Fixing a couple of bugs
[dasher.git] / Src / DasherCore / DasherModel.cpp
blobd532e25ae2337e904b863c78e2ee21f39893622f
1 // DasherModel.h
2 //
3 // Copyright (c) 2001-2005 David Ward
5 #include "../Common/Common.h"
7 #include <iostream>
8 #include "../Common/Random.h"
9 #include "DasherModel.h"
10 #include "DasherView.h"
11 #include "Parameters.h"
13 using namespace Dasher;
14 using namespace std;
16 #include "Event.h"
17 #include "DasherInterfaceBase.h"
18 #include "LanguageModelling/PPMLanguageModel.h"
19 #include "LanguageModelling/WordLanguageModel.h"
20 #include "LanguageModelling/DictLanguageModel.h"
21 #include "LanguageModelling/MixtureLanguageModel.h"
22 #include "NodeCreationManager.h"
24 using namespace Dasher;
25 using namespace std;
27 // Track memory leaks on Windows to the line that new'd the memory
28 #ifdef _WIN32
29 #ifdef _DEBUG
30 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
31 #define new DEBUG_NEW
32 #undef THIS_FILE
33 static char THIS_FILE[] = __FILE__;
34 #endif
35 #endif
37 // FIXME - need to get node deletion working properly and implement reference counting
39 // CDasherModel
41 CDasherModel::CDasherModel(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CNodeCreationManager *pNCManager, CDasherInterfaceBase *pDashIface, bool bGameMode, const std::string &strGameModeText)
42 :CDasherComponent(pEventHandler, pSettingsStore), m_pDasherInterface(pDashIface) {
44 m_Root = 0;
45 m_pLanguageModel =NULL;
47 m_Rootmin = 0;
48 m_Rootmax = 0;
49 m_Rootmin_min = 0;
50 m_Rootmax_max = 0;
51 m_dAddProb = 0.0;
52 m_dMaxRate = 0.0;
53 m_iTargetOffset = 0;
54 m_dTotalNats = 0.0;
55 m_bGameMode = bGameMode;
57 // TODO: Need to rationalise the require conversion methods
59 #ifdef JAPANESE
60 m_bRequireConversion = true;
61 #else
62 m_bRequireConversion = false;
63 #endif
65 SetBoolParameter(BP_CONVERSION_MODE, m_bRequireConversion);
67 // Set max bitrate in the FrameRate class
68 m_dMaxRate = GetLongParameter(LP_MAX_BITRATE) / 100.0;
69 m_fr.SetMaxBitrate(m_dMaxRate);
70 m_dAddProb = 0.003;
72 int iNormalization = GetLongParameter(LP_NORMALIZATION);
73 m_Rootmin_min = int64_min / iNormalization / 2;
74 m_Rootmax_max = int64_max / iNormalization / 2;
76 m_pNodeCreationManager = pNCManager;
78 m_pLanguageModel = m_pNodeCreationManager->GetLanguageModel();
79 // LearnContext = m_pNodeCreationManager->GetLearnContext();
81 // m_bContextSensitive = true;
84 CDasherModel::~CDasherModel() {
86 if(oldroots.size() > 0) {
87 delete oldroots[0];
88 oldroots.clear();
89 // At this point we have also deleted the root - so better NULL pointer
90 m_Root = NULL;
93 if(m_Root)
94 delete m_Root;
96 // m_pLanguageModel->ReleaseContext(LearnContext);
97 // delete m_pLanguageModel;
100 void CDasherModel::HandleEvent(Dasher::CEvent *pEvent) {
102 if(pEvent->m_iEventType == 1) {
103 Dasher::CParameterNotificationEvent * pEvt(static_cast < Dasher::CParameterNotificationEvent * >(pEvent));
105 switch (pEvt->m_iParameter) {
106 case LP_MAX_BITRATE: // Delibarate fallthrough
107 case LP_BOOSTFACTOR: // Deliberate fallthrough
108 case LP_SPEED_DIVISOR:
109 m_dMaxRate = GetLongParameter(LP_MAX_BITRATE) * GetLongParameter(LP_BOOSTFACTOR) / 100 / static_cast<double>(GetLongParameter(LP_SPEED_DIVISOR));
110 m_fr.SetMaxBitrate(m_dMaxRate);
111 break;
112 case BP_CONTROL_MODE: // Rebuild the model if control mode is switched on/off
113 RebuildAroundNode(Get_node_under_crosshair());
114 break;
115 case BP_DELAY_VIEW:
116 MatchTarget(GetBoolParameter(BP_DELAY_VIEW));
117 break;
118 case BP_DASHER_PAUSED:
119 if(GetBoolParameter(BP_SLOW_START))
120 m_iStartTime = 0;
121 else
122 m_iStartTime = 1;
123 break;
124 default:
125 break;
131 void CDasherModel::Make_root(CDasherNode *whichchild)
132 // find a new root node
134 // TODO: Need to undo this when root is reparented
135 m_Root->SetFlag(NF_COMMITTED, true);
137 m_Root->DeleteNephews(whichchild);
139 oldroots.push_back(m_Root);
141 m_Root = whichchild;
143 while((oldroots.size() > 10) && (!m_bRequireConversion || (oldroots[0]->GetConverted()))) {
144 oldroots[0]->OrphanChild(oldroots[1]);
145 delete oldroots[0];
146 oldroots.pop_front();
149 myint range = m_Rootmax - m_Rootmin;
150 m_Rootmax = m_Rootmin + (range * m_Root->Hbnd()) / (int)GetLongParameter(LP_NORMALIZATION);
151 m_Rootmin = m_Rootmin + (range * m_Root->Lbnd()) / (int)GetLongParameter(LP_NORMALIZATION);
153 for(std::deque<SGotoItem>::iterator it(m_deGotoQueue.begin()); it != m_deGotoQueue.end(); ++it) {
154 myint r = it->iN2 - it->iN1;
155 it->iN2 = it->iN1 + (r * m_Root->Hbnd()) / (int)GetLongParameter(LP_NORMALIZATION);
156 it->iN1 = it->iN1 + (r * m_Root->Lbnd()) / (int)GetLongParameter(LP_NORMALIZATION);
159 // myint iTargetRange = m_iTargetMax - m_iTargetMin;
160 // m_iTargetMax = m_iTargetMin + (iTargetRange * m_Root->Hbnd()) / (int)GetLongParameter(LP_NORMALIZATION);
161 // m_iTargetMin = m_iTargetMin + (iTargetRange * m_Root->Lbnd()) / (int)GetLongParameter(LP_NORMALIZATION);
164 void CDasherModel::ClearRootQueue() {
165 while(oldroots.size() > 0) {
166 if(oldroots.size() > 1) {
167 oldroots[0]->OrphanChild(oldroots[1]);
169 else {
170 oldroots[0]->OrphanChild(m_Root);
172 delete oldroots[0];
173 oldroots.pop_front();
177 void CDasherModel::RecursiveMakeRoot(CDasherNode *pNewRoot) {
178 if(!pNewRoot)
179 return;
181 if(pNewRoot == m_Root)
182 return;
184 // FIXME - we really ought to check that pNewRoot is actually a
185 // descendent of the root, although that should be guaranteed
187 if(!pNewRoot->NodeIsParent(m_Root))
188 RecursiveMakeRoot(pNewRoot->Parent());
190 Make_root(pNewRoot);
193 void CDasherModel::RebuildAroundNode(CDasherNode *pNode) {
194 RecursiveMakeRoot(pNode);
195 ClearRootQueue();
196 pNode->Delete_children();
197 pNode->m_pNodeManager->PopulateChildren(pNode);
200 void CDasherModel::Reparent_root(int lower, int upper) {
203 /* Change the root node to the parent of the existing node
204 We need to recalculate the coordinates for the "new" root as the
205 user may have moved around within the current root */
207 // TODO: Reimplement this
209 // if(m_Root->Symbol() == 0)
210 // return; // Don't try to reparent the root symbol
212 CDasherNode *pNewRoot;
214 if(oldroots.size() == 0) {
215 CDasherNode *pCurrentNode(Get_node_under_crosshair());
216 int iGenerations(0);
218 while(pCurrentNode != m_Root) {
219 ++iGenerations;
220 pCurrentNode = pCurrentNode->Parent();
223 pNewRoot = m_Root->m_pNodeManager->RebuildParent(m_Root, iGenerations);
225 // std::cout << pNewRoot->m_pNodeManager << std::endl;
226 // pNewRoot->m_bWatchDelete = true;
228 lower = m_Root->Lbnd();
229 upper = m_Root->Hbnd();
232 else {
233 pNewRoot = oldroots.back();
234 oldroots.pop_back();
237 // Return if there's no existing parent and no way of recreating one
239 if(!pNewRoot) {
240 return;
243 /* Determine how zoomed in we are */
245 myint iWidth = upper - lower;
247 myint iRootWidth;
248 iRootWidth = m_Rootmax - m_Rootmin;
250 if(((myint((GetLongParameter(LP_NORMALIZATION) - upper)) / static_cast<double>(iWidth)) > (m_Rootmax_max - m_Rootmax)/static_cast<double>(iRootWidth)) || ((myint(lower) / static_cast<double>(iWidth)) > (m_Rootmin - m_Rootmin_min) / static_cast<double>(iRootWidth))) {
251 pNewRoot->OrphanChild(m_Root);
252 delete pNewRoot;
253 return;
256 m_Root = pNewRoot;
258 m_Rootmax = m_Rootmax + (myint((GetLongParameter(LP_NORMALIZATION) - upper)) * iRootWidth / iWidth);
259 m_Rootmin = m_Rootmin - (myint(lower) * iRootWidth / iWidth);
261 for(std::deque<SGotoItem>::iterator it(m_deGotoQueue.begin()); it != m_deGotoQueue.end(); ++it) {
262 iRootWidth = it->iN2 - it->iN1;
263 it->iN2 = it->iN2 + (myint((GetLongParameter(LP_NORMALIZATION) - upper)) * iRootWidth / iWidth);
264 it->iN1 = it->iN1 - (myint(lower) * iRootWidth / iWidth);
268 CDasherNode *CDasherModel::Get_node_under_crosshair() {
269 return m_Root->Get_node_under(GetLongParameter(LP_NORMALIZATION), m_Rootmin + m_iTargetOffset, m_Rootmax + m_iTargetOffset, GetLongParameter(LP_OX), GetLongParameter(LP_OY));
272 CDasherNode *CDasherModel::Get_node_under_mouse(myint Mousex, myint Mousey) {
273 return m_Root->Get_node_under(GetLongParameter(LP_NORMALIZATION), m_Rootmin + m_iTargetOffset, m_Rootmax + m_iTargetOffset, Mousex, Mousey);
276 void CDasherModel::Start() {
278 // FIXME - re-evaluate this function and SetContext...
280 // m_pEditbox->get_new_context(ContextString,5);
282 std::string strNewContext("");
284 SetContext(strNewContext); // FIXME - REALLY REALLY broken!
286 CEditContextEvent oEvent(5);
288 InsertEvent(&oEvent);
290 // FIXME - what if we don't get a reply?
292 // m_pLanguageModel->ReleaseNodeContext(therootcontext);
293 // ppmmodel->dump();
294 // dump();
298 void CDasherModel::SetContext(std::string &sNewContext) {
299 // TODO: This is probably the area most significantly in need of a
300 // rethink. Get the language model out of here!
301 m_deGotoQueue.clear();
303 if(oldroots.size() > 0) {
304 delete oldroots[0];
305 oldroots.clear();
306 // At this point we have also deleted the root - so better NULL pointer
307 m_Root = NULL;
309 delete m_Root;
312 CLanguageModel::Context therootcontext = m_pLanguageModel->CreateEmptyContext();
314 if(sNewContext.size() == 0) {
315 m_Root = GetRoot(0, NULL, 0,GetLongParameter(LP_NORMALIZATION), NULL);
317 EnterText(therootcontext, ". ");
319 else {
320 std::vector<symbol> vSymbols;
321 m_pLanguageModel->SymbolAlphabet().GetAlphabetPointer()->GetSymbols(&vSymbols, &sNewContext, false);
323 int iRootSymbol(vSymbols[vSymbols.size()-1]);
325 m_Root = GetRoot(0, NULL, 0,GetLongParameter(LP_NORMALIZATION), &iRootSymbol);
327 EnterText(therootcontext, sNewContext);
330 // TODO: Reimplement
331 // m_pLanguageModel->ReleaseContext(LearnContext);
332 // LearnContext = m_pLanguageModel->CloneContext(therootcontext);
334 // TODO: Reimplement
335 // m_Root->SetContext(therootcontext); // node takes control of the context
336 Recursive_Push_Node(m_Root, 0);
338 double dFraction( 1 - (1 - m_Root->MostProbableChild() / static_cast<double>(GetLongParameter(LP_NORMALIZATION))) / 2.0 );
340 int iWidth( static_cast<int>( (GetLongParameter(LP_MAX_Y) / (2.0*dFraction)) ) );
342 m_Rootmin = GetLongParameter(LP_MAX_Y) / 2 - iWidth / 2;
343 m_Rootmax = GetLongParameter(LP_MAX_Y) / 2 + iWidth / 2;
345 m_iTargetOffset = 0;
347 // m_iTargetMin = m_Rootmin;
348 // m_iTargetMax = m_Rootmax;
351 /////////////////////////////////////////////////////////////////////////////
354 /// CDasherModel::Get_new_root_coords( myint Mousex,myint Mousey )
355 ///
356 /// Calculate the new co-ordinates for the root node after a single
357 /// update step. For further information, see Doc/geometry.tex.
358 ///
359 /// \param Mousex x mouse co-ordinate measured right to left.
360 /// \param Mousey y mouse co-ordinate measured top to bottom.
361 /// \return Returns the number of nats entered
364 void CDasherModel::Get_new_root_coords(myint Mousex, myint Mousey,
365 myint &iNewMin, myint &iNewMax, unsigned long iTime) {
366 // Comments refer to the code immedialtely before them
368 // Avoid Mousex=0, as this corresponds to infinite zoom
369 if(Mousex <= 0) {
370 Mousex = 1;
373 if(m_iStartTime == 0)
374 m_iStartTime = iTime;
376 int iSteps = m_fr.Steps();
378 double dFactor;
380 if(IsSlowdown(iTime))
381 dFactor = 0.1 * (1 + 9 * ((iTime - m_iStartTime) / static_cast<double>(GetLongParameter(LP_SLOW_START_TIME))));
382 else
383 dFactor = 1.0;
385 iSteps = static_cast<int>(iSteps / dFactor);
387 // If Mousex is too large we risk overflow errors, so limit it
388 int iMaxX = (1 << 29) / iSteps;
390 if(Mousex > iMaxX)
391 Mousex = iMaxX;
393 // Cache some results so we don't do a huge number of parameter lookups
395 myint iMaxY(GetLongParameter(LP_MAX_Y));
396 myint iOX(GetLongParameter(LP_OX));
397 myint iOY(GetLongParameter(LP_OY));
399 int iTargetMin(Mousey - ((myint)iMaxY * Mousex) / (2 * (myint)iOX));
400 int iTargetMax(Mousey + ((myint)iMaxY * Mousex) / (2 * (myint)iOY));
402 // Calculate what the extremes of the viewport will be when the
403 // point under the cursor is at the cross-hair. This is where
404 // we want to be in iSteps updates
406 // std::cout << iTargetMin << " " << iTargetMax << std::endl;
408 DASHER_ASSERT(iSteps > 0);
410 // iSteps is the number of update steps we need to get the point
411 // under the cursor over to the cross hair. Calculated in order to
412 // keep a constant bit-rate.
414 int iNewTargetMin;
415 int iNewTargetMax;
417 iNewTargetMin = (iTargetMin * iMaxY / (iMaxY + (iSteps - 1) * (iTargetMax - iTargetMin)));
419 iNewTargetMax = ((iTargetMax * iSteps - iTargetMin * (iSteps - 1)) * iMaxY) / (iMaxY + (iSteps - 1) * (iTargetMax - iTargetMin));
421 iTargetMin = iNewTargetMin;
422 iTargetMax = iNewTargetMax;
424 // Calculate the new values of iTargetMin and iTargetMax required to
425 // perform a single update step. Note that the slightly awkward
426 // expressions are in order to reproduce the behaviour of the old
427 // algorithm
429 myint iMinSize(m_fr.MinSize(iMaxY, dFactor));
431 // std::cout << iTargetMax - iTargetMin << " " << iMinSize << std::endl;
433 // Calculate the minimum size of the viewport corresponding to the
434 // maximum zoom.
436 if((iTargetMax - iTargetMin) < iMinSize) {
437 iNewTargetMin = iTargetMin * (iMaxY - iMinSize) / (iMaxY - (iTargetMax - iTargetMin));
438 iNewTargetMax = iNewTargetMin + iMinSize;
440 iTargetMin = iNewTargetMin;
441 iTargetMax = iNewTargetMax;
444 iNewMin = (((m_Rootmin - iTargetMin) * (myint)GetLongParameter(LP_MAX_Y)) / (iTargetMax - iTargetMin));
445 iNewMax = (((m_Rootmax - iTargetMax) * (myint)GetLongParameter(LP_MAX_Y)) / (iTargetMax - iTargetMin) + (myint)GetLongParameter(LP_MAX_Y));
448 bool CDasherModel::UpdatePosition(myint miMousex, myint miMousey, unsigned long iTime, Dasher::VECTOR_SYMBOL_PROB* pAdded, int* pNumDeleted) {
449 // Clear out parameters that might get passed in to track user activity
450 if (pAdded != NULL)
451 pAdded->clear();
452 if (pNumDeleted != NULL)
453 *pNumDeleted = 0;
455 if(GetBoolParameter(BP_DASHER_PAUSED) && (m_deGotoQueue.size() == 0))
456 return false;
458 myint iNewMin;
459 myint iNewMax;
461 if(m_deGotoQueue.size() == 0) {
462 // works out next viewpoint
463 Get_new_root_coords(miMousex, miMousey, iNewMin, iNewMax, iTime);
465 if(GetBoolParameter(BP_OLD_STYLE_PUSH))
466 OldPush(miMousex, miMousey);
468 else {
469 iNewMin = m_deGotoQueue.front().iN1;
470 iNewMax = m_deGotoQueue.front().iN2;
471 m_deGotoQueue.pop_front();
474 m_dTotalNats += log((iNewMax - iNewMin) / static_cast<double>(m_Rootmax - m_Rootmin));
476 // Now actually zoom to the new location
477 NewGoTo(iNewMin, iNewMax, pAdded, pNumDeleted);
479 return true;
482 void CDasherModel::OldPush(myint iMousex, myint iMousey) {
483 // push node under mouse
484 CDasherNode *pUnderMouse = Get_node_under_mouse(iMousex, iMousey);
486 Push_Node(pUnderMouse);
488 if(Framerate() > 4) {
489 // push node under mouse but with x coord on RHS
490 CDasherNode *pRight = Get_node_under_mouse(50, iMousey);
491 Push_Node(pRight);
494 if(Framerate() > 8) {
495 // push node under the crosshair
496 CDasherNode *pUnderCross = Get_node_under_crosshair();
497 Push_Node(pUnderCross);
500 int iRandom = RandomInt();
502 if(Framerate() > 8) {
503 // add some noise and push another node
504 CDasherNode *pRight = Get_node_under_mouse(50, iMousey + iRandom % 500 - 250);
505 Push_Node(pRight);
508 iRandom = RandomInt();
510 if(Framerate() > 15) {
511 // add some noise and push another node
512 CDasherNode *pRight = Get_node_under_mouse(50, iMousey + iRandom % 500 - 250);
513 Push_Node(pRight);
516 // only do this is Dasher is flying
517 if(Framerate() > 30) {
518 for(int i = 1; i < int (Framerate() - 30) / 3; i++) {
520 int iRandom = RandomInt();
522 if(Framerate() > 8) {
523 // add some noise and push another node
524 CDasherNode *pRight = Get_node_under_mouse(50, iMousey + iRandom % 500 - 250);
525 Push_Node(pRight);
528 iRandom = RandomInt();
529 // push at a random node on the RHS
530 CDasherNode *pRight = Get_node_under_mouse(50, iMousey + iRandom % 1000 - 500);
531 Push_Node(pRight);
537 void CDasherModel::RecursiveOutput(CDasherNode *pNode, Dasher::VECTOR_SYMBOL_PROB* pAdded) {
538 if(pNode->Parent() && (!pNode->Parent()->isSeen()))
539 RecursiveOutput(pNode->Parent(), pAdded);
541 if(pNode->Parent())
542 pNode->Parent()->m_pNodeManager->Leave(pNode->Parent());
544 pNode->m_pNodeManager->Enter(pNode);
546 pNode->Seen(true);
547 pNode->m_pNodeManager->Output(pNode, pAdded, GetLongParameter(LP_NORMALIZATION));
551 // This is similar to Get_new_goto_coords, but doesn't actually change Rootmax and Rootmin.
552 // Instead it gives information for NewGoTo to make direct changes in the root coordinates.
553 #define ZOOMDENOM (1<<10)
554 #define STEPNUM 48
555 #define STEPDENOM 64
556 double CDasherModel::Plan_new_goto_coords(int iRxnew, myint mousey, int *iSteps, myint *o1, myint *o2 , myint *n1, myint *n2)
558 m_Stepnum = GetLongParameter(LP_ZOOMSTEPS);
559 int iRxnew_dup = iRxnew;
560 // note -- iRxnew is the zoom factor in units of ZOOMDENOM
561 *o1 = m_Rootmin ;
562 *o2 = m_Rootmax ;
563 DASHER_ASSERT(iRxnew > 0);
564 if (iRxnew < ZOOMDENOM && m_Rootmax<(myint)GetLongParameter(LP_MAX_Y) && m_Rootmin>0 ) {
565 // refuse to zoom backwards if the entire root node is visible.
566 *iSteps = 0 ;
567 *n1 = m_Rootmin;
568 *n2 = m_Rootmax;
570 else {
571 myint above=(mousey-*o1);
572 myint below=(*o2-mousey);
574 myint miNewrootzoom= GetLongParameter(LP_MAX_Y)/2 ;
575 myint newRootmax=miNewrootzoom+(below*iRxnew/ZOOMDENOM); // is there a risk of overflow in this multiply?
576 myint newRootmin=miNewrootzoom-(above*iRxnew/ZOOMDENOM);
578 *n1 = newRootmin;
579 *n2 = newRootmax;
581 *iSteps = 1;
583 // We might be moving at zoomfactor one vertically, in which case the below invention won't
584 // come up with more than one step. Look for a mousey difference and use an iSteps concordant
585 // to that if it would be larger than the iSteps created by taking the log of the zoomfactor.
586 int distance = mousey - ((myint)GetLongParameter(LP_MAX_Y)/2);
588 double s = (log(2.0) * 2 / log( (STEPDENOM*1.0)/(m_Stepnum*1.0)) ) / 4096;
590 double alpha = 2 * (2 * s);
591 int alternateSteps = int(alpha * abs(distance));
593 // Take log of iRxnew to base ( STEPDENOM / STEPNUM ):
594 if ( STEPDENOM > m_Stepnum && m_Stepnum > 0 ) { // check that the following loop will terminate.
595 //cout << "iRxnew is " << iRxnew << " and ZOOMDENOM is" << ZOOMDENOM << endl;
596 if ( iRxnew > ZOOMDENOM ) {
597 while ( iRxnew > ZOOMDENOM ) {
598 *iSteps += 1;
599 iRxnew = iRxnew * m_Stepnum / STEPDENOM;
601 } else {
602 while ( iRxnew < ZOOMDENOM ) {
603 *iSteps += 1;
604 iRxnew = iRxnew * STEPDENOM / m_Stepnum;
609 // Done taking log of iRxnew.
610 if (alternateSteps > *iSteps) {
611 *iSteps = alternateSteps;
615 double iRxnew_ratio = (double) iRxnew_dup / ZOOMDENOM;
616 double iRxnew_log = log(iRxnew_ratio);
617 return iRxnew_log;
620 void CDasherModel::NewGoTo(myint newRootmin, myint newRootmax, Dasher::VECTOR_SYMBOL_PROB* pAdded, int* pNumDeleted) {
621 // Find out the current node under the crosshair
622 CDasherNode *old_under_cross=Get_node_under_crosshair();
624 // Update the max and min of the root node to make iTargetMin and iTargetMax the edges of the viewport.
626 if(newRootmin + m_iTargetOffset > (myint)GetLongParameter(LP_MAX_Y) / 2 - 1)
627 newRootmin = (myint)GetLongParameter(LP_MAX_Y) / 2 - 1 - m_iTargetOffset;
629 if(newRootmax + m_iTargetOffset < (myint)GetLongParameter(LP_MAX_Y) / 2 + 1)
630 newRootmax = (myint)GetLongParameter(LP_MAX_Y) / 2 + 1 - m_iTargetOffset;
632 // Check that we haven't drifted too far. The rule is that we're not
633 // allowed to let the root max and min cross the midpoint of the
634 // screen.
636 if(newRootmax < m_Rootmax_max && newRootmin > m_Rootmin_min && (newRootmax - newRootmin) > (myint)GetLongParameter(LP_MAX_Y) / 4) {
637 // Only update if we're not making things big enough to risk
638 // overflow. In theory we should have reparented the root well
639 // before getting this far.
641 // Also don't allow the update if it will result in making the
642 // root too small. Again, we should have re-generated a deeper
643 // root in most cases, but the original root is an exception.
645 m_Rootmax = newRootmax;
646 m_Rootmin = newRootmin;
648 m_iTargetOffset = (m_iTargetOffset * 90) / 100;
650 // m_iTargetMax = m_iTargetMax + 0.1 * (m_Rootmax - m_iTargetMax);
651 // m_iTargetMin = m_iTargetMin + 0.1 * (m_Rootmin - m_iTargetMin);
653 else {
654 // TODO - force a new root to be chosen, so that we get better
655 // behaviour than just having Dasher stop at this point.
658 // push node under crosshair
659 CDasherNode* new_under_cross = Get_node_under_crosshair();
660 Push_Node(new_under_cross);
662 HandleOutput(new_under_cross, old_under_cross, pAdded, pNumDeleted);
665 void CDasherModel::HandleOutput(CDasherNode *pNewNode, CDasherNode *pOldNode, Dasher::VECTOR_SYMBOL_PROB* pAdded, int* pNumDeleted) {
666 if(pNewNode != pOldNode)
667 DeleteCharacters(pNewNode, pOldNode, pNumDeleted);
669 if(pNewNode->isSeen())
670 return;
672 // TODO: Reimplement second parameter
673 RecursiveOutput(pNewNode, pAdded);
677 // TODO - is this used any more
678 //void CDasherModel::OutputCharacters(CDasherNode *node) {
679 // if(node->Parent() != NULL && node->Parent()->isSeen() != true) {
680 // node->Parent()->Seen(true);
681 // OutputCharacters(node->Parent());
682 // }
683 // symbol t = node->Symbol();
684 // if(t) // SYM0
685 // {
686 // Dasher::CEditEvent oEvent(1, GetAlphabet().GetText(t));
687 // InsertEvent(&oEvent);
688 // }
692 bool CDasherModel::DeleteCharacters(CDasherNode *newnode, CDasherNode *oldnode, int* pNumDeleted) {
693 // DJW cant see how either of these can ever be NULL
694 DASHER_ASSERT_VALIDPTR_RW(newnode);
695 DASHER_ASSERT_VALIDPTR_RW(oldnode);
697 if(newnode == NULL || oldnode == NULL)
698 return false;
700 // This deals with the trivial instance - we're reversing back over
701 // text that we've seen already
702 if(newnode->isSeen() == true) {
703 if(oldnode->Parent() == newnode) {
704 oldnode->m_pNodeManager->Undo(oldnode);
705 oldnode->Parent()->m_pNodeManager->Enter(oldnode->Parent());
706 if (pNumDeleted != NULL)
707 (*pNumDeleted)++;
708 oldnode->Seen(false);
709 return true;
711 if(DeleteCharacters(newnode, oldnode->Parent(), pNumDeleted) == true) {
712 oldnode->m_pNodeManager->Undo(oldnode);
713 oldnode->Parent()->m_pNodeManager->Enter(oldnode->Parent());
714 if (pNumDeleted != NULL)
715 (*pNumDeleted)++;
716 oldnode->Seen(false);
717 return true;
720 else {
721 // This one's more complicated - the user may have moved onto a new branch
722 // Find the last seen node on the new branch
723 CDasherNode *lastseen = newnode->Parent();
725 while(lastseen != NULL && lastseen->isSeen() == false) {
726 lastseen = lastseen->Parent();
728 // Delete back to last seen node
729 while(oldnode != lastseen) {
731 oldnode->Seen(false);
733 oldnode->m_pNodeManager->Undo(oldnode);
734 oldnode->Parent()->m_pNodeManager->Enter(oldnode->Parent());
735 if (pNumDeleted != NULL)
736 (*pNumDeleted)++;
737 oldnode = oldnode->Parent();
738 if(oldnode == NULL) {
739 return false;
743 return false;
746 /////////////////////////////////////////////////////////////////////////////
748 // // Diagnostic trace
749 // void CDasherModel::Trace() const {
750 // // OutputDebugString(TEXT(" ptr symbol context Next Child pushme pushed cscheme lbnd hbnd \n"));
751 // m_Root->Trace();
752 // }
754 ///////////////////////////////////////////////////////////////////
756 void CDasherModel::GetProbs(CLanguageModel::Context context, vector <symbol >&NewSymbols, vector <unsigned int >&Probs, int iNorm) const {
757 m_pNodeCreationManager->GetProbs(context, NewSymbols, Probs, iNorm);
760 void CDasherModel::LearnText(CLanguageModel::Context context, string *TheText, bool IsMore) {
761 m_pNodeCreationManager->LearnText(context, TheText, IsMore);
764 void CDasherModel::EnterText(CLanguageModel::Context context, string TheText) const {
765 m_pNodeCreationManager->EnterText(context, TheText);
768 void CDasherModel::Push_Node(CDasherNode *pNode) {
770 if(pNode->HasAllChildren()) {
771 DASHER_ASSERT(pNode->Children().size() > 0);
772 // if there are children just give them a poke
773 CDasherNode::ChildMap::iterator i;
774 for(i = pNode->Children().begin(); i != pNode->Children().end(); i++)
775 (*i)->Alive(true);
776 return;
779 // TODO: Do we really need to delete all of the children at this point?
780 pNode->Delete_children();
782 pNode->Alive(true);
783 pNode->m_pNodeManager->PopulateChildren(pNode);
784 pNode->SetHasAllChildren(true);
787 void CDasherModel::Recursive_Push_Node(CDasherNode *pNode, int iDepth) {
789 if(pNode->Range() < 0.1 * GetLongParameter(LP_NORMALIZATION)) {
790 return;
793 Push_Node(pNode);
795 if(iDepth == 0)
796 return;
798 for(unsigned int i(0); i < pNode->ChildCount(); i++) {
799 Recursive_Push_Node(pNode->Children()[i], iDepth - 1);
803 // FIXME - annoying code duplication below
805 // void CDasherModel::RenderToView(CDasherView *pView) {
806 // std::vector<CDasherNode *> vNodeList;
807 // std::vector<CDasherNode *> vDeleteList;
809 // pView->Render(m_Root, m_Rootmin, m_Rootmax, vNodeList, vDeleteList);
811 // if(!GetBoolParameter(BP_OLD_STYLE_PUSH)) {
812 // for(std::vector<CDasherNode *>::iterator it(vNodeList.begin()); it != vNodeList.end(); ++it)
813 // Push_Node(*it);
814 // }
816 // for(std::vector<CDasherNode *>::iterator it(vDeleteList.begin()); it != vDeleteList.end(); ++it)
817 // (*it)->Delete_children();
818 // }
820 bool CDasherModel::RenderToView(CDasherView *pView, bool bRedrawDisplay) {
821 std::vector<CDasherNode *> vNodeList;
822 std::vector<CDasherNode *> vDeleteList;
824 bool bReturnValue;
826 //if(GetBoolParameter(BP_DELAY_VIEW))
827 // bReturnValue = pView->Render(m_Root, m_iTargetMin, m_iTargetMax, vNodeList, vDeleteList, bRedrawDisplay, m_bGameMode);
828 //else
829 bReturnValue = pView->Render(m_Root, m_Rootmin + m_iTargetOffset, m_Rootmax + m_iTargetOffset, vNodeList, vDeleteList, bRedrawDisplay, m_bGameMode);
831 if(!GetBoolParameter(BP_OLD_STYLE_PUSH)) {
832 for(std::vector<CDasherNode *>::iterator it(vNodeList.begin()); it != vNodeList.end(); ++it)
833 Push_Node(*it);
836 for(std::vector<CDasherNode *>::iterator it(vDeleteList.begin()); it != vDeleteList.end(); ++it)
837 (*it)->Delete_children();
839 return bReturnValue;
842 bool CDasherModel::CheckForNewRoot(CDasherView *pView) {
844 // if(m_deGotoQueue.size() > 0)
845 // return false;
847 CDasherNode *root(m_Root);
848 CDasherNode::ChildMap & children = m_Root->Children();
850 if(pView->IsNodeVisible(m_Rootmin,m_Rootmax)) {
851 Reparent_root(root->Lbnd(), root->Hbnd());
852 return(m_Root != root);
855 if(children.size() == 0)
856 return false;
858 int alive = 0;
859 CDasherNode *theone = 0;
861 // Find whether there is exactly one alive child; if more, we don't care.
862 CDasherNode::ChildMap::iterator i;
863 for(i = children.begin(); i != children.end(); i++) {
864 if((*i)->Alive()) {
865 alive++;
866 theone = *i;
867 if(alive > 1)
868 break;
872 if(alive == 1) {
873 // We must have zoomed sufficiently that only one child of the root node
874 // is still alive. Let's make it the root.
876 myint y1 = m_Rootmin;
877 myint y2 = m_Rootmax;
878 myint range = y2 - y1;
880 myint newy1 = y1 + (range * theone->Lbnd()) / (int)GetLongParameter(LP_NORMALIZATION);
881 myint newy2 = y1 + (range * theone->Hbnd()) / (int)GetLongParameter(LP_NORMALIZATION);
882 if(!pView->IsNodeVisible(newy1, newy2)) {
883 Make_root(theone);
884 return false;
888 return false;
891 double CDasherModel::CorrectionFactor(int dasherx, int dashery) {
892 double dX = 1 - dasherx/2048.0;
893 double dY = dashery/2048.0 - 1;
895 double dR = sqrt(pow(dX, 2.0) + pow(dY, 2.0));
897 if(fabs(dX) < 0.1)
898 return dR * (1 + dX /2.0+ pow(dX, 2.0) / 3.0 + pow(dX, 3.0) / 4.0 + pow(dX, 4.0) / 5.0);
899 else
900 return -dR * log(1 - dX) / dX;
903 void CDasherModel::ScheduleZoom(int dasherx, int dashery) {
905 // TODO: What is the following line for?
906 if (dasherx < 2) { dasherx = 100; }
908 double dCFactor(CorrectionFactor(dasherx, dashery));
910 int iSteps = static_cast<int>(GetLongParameter(LP_ZOOMSTEPS) * dCFactor);
911 // myint iRxnew = ((GetLongParameter(LP_OX)/2) * GetLongParameter(LP_OX)) / dasherx;
912 myint n1, n2, iTarget1, iTarget2;
913 //Plan_new_goto_coords(iRxnew, dashery, &iSteps, &o1,&o2,&n1,&n2);
915 iTarget1 = dashery - dasherx;
916 iTarget2 = dashery + dasherx;
918 double dZ = 4096 / static_cast<double>(iTarget2 - iTarget1);
920 n1 = static_cast<int>((m_Rootmin - iTarget1) * dZ);
921 n2 = static_cast<int>((m_Rootmax - iTarget2) * dZ + 4096);
923 m_deGotoQueue.clear();
924 SGotoItem sNewItem;
926 for(int s(1); s < iSteps; ++s) {
927 // use simple linear interpolation. Really should do logarithmic interpolation, but
928 // this will probably look fine.
930 sNewItem.iN1 = (s * n1 + (iSteps-s) * m_Rootmin) / iSteps;
931 sNewItem.iN2 = (s * n2 + (iSteps-s) * m_Rootmax) / iSteps;
932 sNewItem.iStyle = 1;
934 m_deGotoQueue.push_back(sNewItem);
937 sNewItem.iN1 = n1;
938 sNewItem.iN2 = n2;
939 sNewItem.iStyle = 2;
941 m_deGotoQueue.push_back(sNewItem);
944 void CDasherModel::Offset(int iOffset) {
945 m_Rootmin += iOffset;
946 m_Rootmax += iOffset;
948 m_iTargetOffset -= iOffset;
951 void CDasherModel::MatchTarget(bool bReverse) {
952 // TODO: Does anything need to happen wrt bReverse here?
954 m_Rootmin += m_iTargetOffset;
955 m_Rootmax += m_iTargetOffset;
957 m_iTargetOffset = 0;
960 void CDasherModel::LimitRoot(int iMaxWidth) {
961 m_Rootmin = GetLongParameter(LP_MAX_Y) / 2 - iMaxWidth / 2;
962 m_Rootmax = GetLongParameter(LP_MAX_Y) / 2 + iMaxWidth / 2;
966 CAlphabetManagerFactory::CTrainer *CDasherModel::GetTrainer() {
967 return m_pNodeCreationManager->GetTrainer();