3 // Copyright (c) 2001-2005 David Ward
5 #include "../Common/Common.h"
8 #include "../Common/Random.h"
9 #include "DasherModel.h"
10 #include "DasherView.h"
11 #include "Parameters.h"
13 using namespace Dasher
;
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
;
27 // Track memory leaks on Windows to the line that new'd the memory
30 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
33 static char THIS_FILE
[] = __FILE__
;
37 // FIXME - need to get node deletion working properly and implement reference counting
41 CDasherModel::CDasherModel(CEventHandler
*pEventHandler
, CSettingsStore
*pSettingsStore
, CNodeCreationManager
*pNCManager
, CDasherInterfaceBase
*pDashIface
, bool bGameMode
, const std::string
&strGameModeText
)
42 :CDasherComponent(pEventHandler
, pSettingsStore
), m_pDasherInterface(pDashIface
) {
45 m_pLanguageModel
=NULL
;
55 m_bGameMode
= bGameMode
;
57 // TODO: Need to rationalise the require conversion methods
60 m_bRequireConversion
= true;
62 m_bRequireConversion
= false;
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
);
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) {
89 // At this point we have also deleted the root - so better NULL pointer
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
);
112 case BP_CONTROL_MODE
: // Rebuild the model if control mode is switched on/off
113 RebuildAroundNode(Get_node_under_crosshair());
116 MatchTarget(GetBoolParameter(BP_DELAY_VIEW
));
118 case BP_DASHER_PAUSED
:
119 if(GetBoolParameter(BP_SLOW_START
))
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
);
143 while((oldroots
.size() > 10) && (!m_bRequireConversion
|| (oldroots
[0]->GetConverted()))) {
144 oldroots
[0]->OrphanChild(oldroots
[1]);
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]);
170 oldroots
[0]->OrphanChild(m_Root
);
173 oldroots
.pop_front();
177 void CDasherModel::RecursiveMakeRoot(CDasherNode
*pNewRoot
) {
181 if(pNewRoot
== m_Root
)
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());
193 void CDasherModel::RebuildAroundNode(CDasherNode
*pNode
) {
194 RecursiveMakeRoot(pNode
);
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());
218 while(pCurrentNode
!= m_Root
) {
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();
233 pNewRoot
= oldroots
.back();
237 // Return if there's no existing parent and no way of recreating one
243 /* Determine how zoomed in we are */
245 myint iWidth
= upper
- lower
;
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
);
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);
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) {
306 // At this point we have also deleted the root - so better NULL pointer
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
, ". ");
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
);
331 // m_pLanguageModel->ReleaseContext(LearnContext);
332 // LearnContext = m_pLanguageModel->CloneContext(therootcontext);
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;
347 // m_iTargetMin = m_Rootmin;
348 // m_iTargetMax = m_Rootmax;
351 /////////////////////////////////////////////////////////////////////////////
354 /// CDasherModel::Get_new_root_coords( myint Mousex,myint Mousey )
356 /// Calculate the new co-ordinates for the root node after a single
357 /// update step. For further information, see Doc/geometry.tex.
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
373 if(m_iStartTime
== 0)
374 m_iStartTime
= iTime
;
376 int iSteps
= m_fr
.Steps();
380 if(IsSlowdown(iTime
))
381 dFactor
= 0.1 * (1 + 9 * ((iTime
- m_iStartTime
) / static_cast<double>(GetLongParameter(LP_SLOW_START_TIME
))));
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
;
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.
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
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
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
452 if (pNumDeleted
!= NULL
)
455 if(GetBoolParameter(BP_DASHER_PAUSED
) && (m_deGotoQueue
.size() == 0))
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
);
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
);
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
);
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);
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);
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);
528 iRandom
= RandomInt();
529 // push at a random node on the RHS
530 CDasherNode
*pRight
= Get_node_under_mouse(50, iMousey
+ iRandom
% 1000 - 500);
537 void CDasherModel::RecursiveOutput(CDasherNode
*pNode
, Dasher::VECTOR_SYMBOL_PROB
* pAdded
) {
538 if(pNode
->Parent() && (!pNode
->Parent()->isSeen()))
539 RecursiveOutput(pNode
->Parent(), pAdded
);
542 pNode
->Parent()->m_pNodeManager
->Leave(pNode
->Parent());
544 pNode
->m_pNodeManager
->Enter(pNode
);
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)
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
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.
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
);
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
) {
599 iRxnew
= iRxnew
* m_Stepnum
/ STEPDENOM
;
602 while ( iRxnew
< ZOOMDENOM
) {
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
);
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
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);
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())
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());
683 // symbol t = node->Symbol();
686 // Dasher::CEditEvent oEvent(1, GetAlphabet().GetText(t));
687 // InsertEvent(&oEvent);
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
)
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
)
708 oldnode
->Seen(false);
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
)
716 oldnode
->Seen(false);
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
)
737 oldnode
= oldnode
->Parent();
738 if(oldnode
== NULL
) {
746 /////////////////////////////////////////////////////////////////////////////
748 // // Diagnostic trace
749 // void CDasherModel::Trace() const {
750 // // OutputDebugString(TEXT(" ptr symbol context Next Child pushme pushed cscheme lbnd hbnd \n"));
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
++)
779 // TODO: Do we really need to delete all of the children at this point?
780 pNode
->Delete_children();
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
)) {
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)
816 // for(std::vector<CDasherNode *>::iterator it(vDeleteList.begin()); it != vDeleteList.end(); ++it)
817 // (*it)->Delete_children();
820 bool CDasherModel::RenderToView(CDasherView
*pView
, bool bRedrawDisplay
) {
821 std::vector
<CDasherNode
*> vNodeList
;
822 std::vector
<CDasherNode
*> vDeleteList
;
826 //if(GetBoolParameter(BP_DELAY_VIEW))
827 // bReturnValue = pView->Render(m_Root, m_iTargetMin, m_iTargetMax, vNodeList, vDeleteList, bRedrawDisplay, m_bGameMode);
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
)
836 for(std::vector
<CDasherNode
*>::iterator
it(vDeleteList
.begin()); it
!= vDeleteList
.end(); ++it
)
837 (*it
)->Delete_children();
842 bool CDasherModel::CheckForNewRoot(CDasherView
*pView
) {
844 // if(m_deGotoQueue.size() > 0)
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)
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
++) {
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
)) {
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));
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);
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();
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
;
934 m_deGotoQueue
.push_back(sNewItem
);
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
;
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();