Making nested groups work
[dasher.git] / Src / DasherCore / DasherViewSquare.cpp
blobf5bf87cf33efa47d58a2a95ec5c166dae0103a88
1 // DasherViewSquare.cpp
2 //
3 // Copyright (c) 2001-2004 David Ward
5 #include "../Common/Common.h"
7 #include "DasherViewSquare.h"
8 #include "DasherModel.h"
9 #include "DasherView.h"
10 #include "DasherTypes.h"
11 #include "Event.h"
12 #include "EventHandler.h"
13 #include "View/DelayedDraw.h"
15 #include <algorithm>
16 #include <limits>
18 #include <iostream>
20 using namespace Dasher;
22 // Track memory leaks on Windows to the line that new'd the memory
23 #ifdef _WIN32
24 #ifdef _DEBUG
25 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
26 #define new DEBUG_NEW
27 #undef THIS_FILE
28 static char THIS_FILE[] = __FILE__;
29 #endif
30 #endif
32 // FIXME - quite a lot of the code here probably should be moved to
33 // the parent class (DasherView). I think we really should make the
34 // parent class less general - we're probably not going to implement
35 // anything which uses radically different co-ordinate transforms, and
36 // we can always override if necessary.
38 void CDasherViewSquare::RenderNodes() {
40 DASHER_ASSERT(DasherModel()->Root() != 0);
42 // Render nodes to screen object (should use off screen buffer)
44 Screen()->Blank();
46 myint iDasherMinX;
47 myint iDasherMinY;
48 myint iDasherMaxX;
49 myint iDasherMaxY;
51 VisibleRegion(iDasherMinX, iDasherMinY, iDasherMaxX, iDasherMaxY);
53 std::vector<CDasherNode *> vNodeList;
54 std::vector<CDasherNode *> vDeleteList;
56 RecursiveRender(DasherModel()->Root(), DasherModel()->Rootmin(), DasherModel()->Rootmax(), iDasherMaxX, vNodeList, vDeleteList);
58 // DelayDraw the text nodes
59 m_pDelayDraw->Draw(Screen());
61 Crosshair(DasherModel()->DasherOX()); // add crosshair
63 // Push nodes which have newly become large, and delete nodes which
64 // are now too small.
66 for(std::vector<CDasherNode *>::iterator it(vNodeList.begin()); it != vNodeList.end(); ++it)
67 DasherModel()->Push_Node(*it);
69 for(std::vector<CDasherNode *>::iterator it(vDeleteList.begin()); it != vDeleteList.end(); ++it)
70 (*it)->Delete_children();
74 void CDasherViewSquare::HandleEvent(Dasher::CEvent *pEvent) {
75 // Let the parent class do its stuff
76 CDasherView::HandleEvent(pEvent);
78 // And then interpret events for ourself
79 if(pEvent->m_iEventType == 1) {
80 Dasher::CParameterNotificationEvent * pEvt(static_cast < Dasher::CParameterNotificationEvent * >(pEvent));
81 switch (pEvt->m_iParameter) {
83 default:
84 break;
89 int CDasherViewSquare::RecursiveRender(CDasherNode *pRender, myint y1, myint y2, int mostleft, std::vector<CDasherNode *> &vNodeList, std::vector<CDasherNode *> &vDeleteList) {
90 DASHER_ASSERT_VALIDPTR_RW(pRender);
92 // Decide which colour to use when rendering the child
94 int Color;
96 if(GetBoolParameter(BP_COLOUR_MODE) == true) {
97 if(pRender->Colour() != -1) {
98 Color = pRender->Colour();
100 else {
101 if(pRender->Symbol() == DasherModel()->GetSpaceSymbol()) {
102 Color = 9;
104 else if(pRender->Symbol() == DasherModel()->GetControlSymbol()) {
105 Color = 8;
107 else {
108 Color = (pRender->Symbol() % 3) + 10;
112 else {
113 Color = pRender->Phase() % 3;
116 if((pRender->ColorScheme() % 2) == 1 && Color < 130 && GetBoolParameter(BP_COLOUR_MODE) == true) { // We don't loop on high
117 Color += 130; // colours
120 // DASHER_TRACEOUTPUT("%x ",Render);
122 std::string display;
123 // if(pRender->GetControlTree() != NULL)
124 // display = pRender->GetControlTree()->text;
127 display = pRender->m_strDisplayText;
129 if(RenderNode(pRender->Symbol(), Color, pRender->ColorScheme(), y1, y2, mostleft, display, pRender->m_bShove)) {
130 // yuk
131 if(!pRender->ControlChild() && pRender->Symbol() < DasherModel()->GetAlphabet().GetNumberTextSymbols())
132 RenderGroups(pRender, y1, y2, mostleft);
134 else {
135 // std::cout << "Killing Node" << std::endl;
136 vDeleteList.push_back(pRender);
137 pRender->Kill();
138 return 0;
141 if(pRender->ChildCount() == 0) {
142 vNodeList.push_back(pRender);
143 return 0;
146 int norm = GetLongParameter(LP_NORMALIZATION);
147 CDasherNode::ChildMap::const_iterator i;
149 for(i = pRender->GetChildren().begin(); i != pRender->GetChildren().end(); i++) {
150 CDasherNode *pChild = *i;
151 // if(pChild->Alive()) {
152 myint Range = y2 - y1;
153 myint newy1 = y1 + (Range * pChild->Lbnd()) / norm;
154 myint newy2 = y1 + (Range * pChild->Hbnd()) / norm;
156 // FIXME - make the threshold a parameter
158 if((newy2 - newy1 > 50) || (pChild->Alive())) {
159 pChild->Alive(true);
160 RecursiveRender(pChild, newy1, newy2, mostleft, vNodeList, vDeleteList);
162 // }
165 return 1;
168 void CDasherViewSquare::RenderGroups(CDasherNode *Render, myint y1, myint y2, int mostleft) {
169 CDasherNode::ChildMap & Children = Render->Children();
170 if(Children.size() == 0)
171 return;
172 int current = 0;
173 std::string Label = "";
175 myint range = y2 - y1;
177 const CAlphabet & alphabet = DasherModel()->GetAlphabet();
179 // for(int iGroup = 1; iGroup < alphabet.GetGroupCount(); iGroup++) {
180 // int lower = alphabet.GetGroupStart(iGroup);
181 // int upper = alphabet.GetGroupEnd(iGroup);
183 // myint lbnd = Children[lower]->Lbnd();
184 // myint hbnd = Children[upper - 1]->Hbnd();
185 // myint newy1 = y1 + (range * lbnd) / (int)GetLongParameter(LP_NORMALIZATION);
186 // myint newy2 = y1 + (range * hbnd) / (int)GetLongParameter(LP_NORMALIZATION);
188 // if(GetBoolParameter(BP_COLOUR_MODE) == true) {
189 // std::string Label = DasherModel()->GroupLabel(iGroup);
190 // int Colour = DasherModel()->GroupColour(iGroup);
192 // if(Colour != -1) {
193 // RenderNode(0, DasherModel()->GroupColour(iGroup), Opts::Groups, newy1, newy2, mostleft, Label, true);
194 // }
195 // else {
196 // RenderNode(0, (current % 3) + 110, Opts::Groups, newy1, newy2, mostleft, Label, true);
197 // }
198 // }
199 // else {
200 // RenderNode(0, current - 1, Opts::Groups, newy1, newy2, mostleft, Label, true);
201 // }
202 // }
204 SGroupInfo *pCurrentGroup(alphabet.m_pBaseGroup);
206 while(pCurrentGroup) {
207 RecursiveRenderGroups(pCurrentGroup, Children, y1, y2, mostleft);
208 pCurrentGroup = pCurrentGroup->pNext;
212 void CDasherViewSquare::RecursiveRenderGroups(SGroupInfo *pCurrentGroup, std::deque<CDasherNode*>& Children, myint y1, myint y2, int mostleft) {
215 if(pCurrentGroup->bVisible) {
216 myint range = y2 - y1;
218 int lower(pCurrentGroup->iStart);
219 int upper(pCurrentGroup->iEnd);
221 myint lbnd = Children[lower]->Lbnd();
222 myint hbnd = Children[upper - 1]->Hbnd();
224 //std::cout << lbnd << " " << hbnd << std::endl;
226 myint newy1 = y1 + (range * lbnd) / (int)GetLongParameter(LP_NORMALIZATION);
227 myint newy2 = y1 + (range * hbnd) / (int)GetLongParameter(LP_NORMALIZATION);
229 if(GetBoolParameter(BP_COLOUR_MODE) == true) {
230 // std::string Label = DasherModel()->GroupLabel(iGroup);
231 std::string Label=pCurrentGroup->strLabel;
232 int Colour = pCurrentGroup->iColour;
233 if(Colour != -1) {
234 RenderNode(0, pCurrentGroup->iColour, Opts::Groups, newy1, newy2, mostleft, Label, true);
236 else {
237 // RenderNode(0, (current % 3) + 110, Opts::Groups, newy1, newy2, mostleft, Label, true);
242 SGroupInfo *pCurrentChild(pCurrentGroup->pChild);
244 while(pCurrentChild) {
245 RecursiveRenderGroups(pCurrentChild, Children, y1, y2, mostleft);
246 pCurrentChild = pCurrentChild->pNext;
251 CDasherViewSquare::Cymap::Cymap(myint iScale) {
252 double dY1 = 0.25; // Amount of acceleration
253 double dY2 = 0.95; // Accelerate Y movement below this point
254 double dY3 = 0.05; // Accelerate Y movement above this point
256 m_Y2 = myint(dY2 * iScale);
257 m_Y3 = myint(dY3 * iScale);
258 m_Y1 = myint(1.0 / dY1);
261 CDasherViewSquare::CDasherViewSquare(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherScreen *DasherScreen, CDasherModel *DasherModel)
262 :CDasherView(pEventHandler, pSettingsStore, DasherScreen, DasherModel) {
264 // Make sure that the auto calibration is set to zero berfore we start
266 m_yAutoOffset = 0;
268 bInBox = false;
269 m_pDelayDraw = new CDelayedDraw();
270 ChangeScreen(DasherScreen);
272 // tweak these if you know what you are doing
273 m_dXmpa = 0.2; // these are for the x non-linearity
274 m_dXmpb = 0.5;
275 m_dXmpc = 0.9;
276 m_dXmpd = 0.5; // slow X movement when accelerating Y
279 //scale #samples by #samples = m_dSamplesScale / (current bitrate) + m_dSampleOffset
280 m_dSampleScale = 1.5;
281 m_dSampleOffset = 1.3;
282 m_dMinRRate = 80.0;
283 m_dSensitivity = GetLongParameter(LP_AUTOSPEED_SENSITIVITY) / 100.0; //param only, no GUI!
284 //tolerance for automatic speed control
285 m_dTier1 = 0.0005; // should be arranged so that tier4 > tier3 > tier2 > tier1 !!!
286 m_dTier2 = 0.01;
287 m_dTier3 = 0.2;
288 m_dTier4 = 0.31;
289 //bitrate fractional changes for auto-speed control
290 m_dChange1 = 1.1;
291 m_dChange2 = 1.02;
292 m_dChange3 = 0.97;
293 m_dChange4 = 0.94;
294 //cap bitrate at...
295 m_dSpeedMax = 8.0;
296 m_dSpeedMin = 0.1;
297 //variance of two-centred-gaussians for adaptive radius
298 m_dSigma1 = 0.5;
299 m_dSigma2 = 0.05;
300 //Initialise auto-speed control
301 m_nSpeedCounter = 0;
302 m_dBitrate = double(round(GetLongParameter(LP_MAX_BITRATE) / 100.0));
303 UpdateMinRadius();
304 UpdateSampleSize();
305 //KeyControl=false;
306 m_ymap = Cymap(DasherModel->DasherY());
308 CDasherModel::CRange rActive(m_ymap.unmap(0), m_ymap.unmap(DasherModel->DasherY()));
309 DasherModel->SetActive(rActive);
312 CDasherViewSquare::~CDasherViewSquare() {
313 if (m_pDelayDraw != NULL) {
314 delete m_pDelayDraw;
315 m_pDelayDraw = NULL;
319 int CDasherViewSquare::RenderNode(const symbol Character, const int Color, Opts::ColorSchemes ColorScheme, myint y1, myint y2, int &mostleft, const std::string &displaytext, bool bShove) {
321 // Commenting because click mode occasionally fails this assert.
322 // I don't know why. -- cjb.
323 if (!(y2 >= y1)) { return 1; }
325 // FIXME - Get sensibel limits here (to allow for non-linearities)
327 myint iDasherMinX;
328 myint iDasherMinY;
329 myint iDasherMaxX;
330 myint iDasherMaxY;
332 VisibleRegion(iDasherMinX, iDasherMinY, iDasherMaxX, iDasherMaxY);
334 screenint s1, s2;
335 Cint32 iSize = dashery2screen(y1, y2, s1, s2);
337 // Actual height in pixels
338 Cint32 iHeight = Cint32((Cint32) (iSize * CanvasY) / (Cint32) DasherModel()->DasherY());
340 if(iHeight <= 1)
341 return 0; // We're too small to render
343 if((y1 > iDasherMaxY) || (y2 < iDasherMinY)){
344 return 0; // We're entirely off screen, so don't render.
347 myint iDasherSize(y2 - y1);
349 // FIXME - get rid of pointless assignment below
351 int iTruncation(GetLongParameter(LP_TRUNCATION)); // Trucation farction times 100;
352 int iTruncationType(GetLongParameter(LP_TRUNCATIONTYPE));
354 if(iTruncation == 0) { // Regular squares
355 DasherDrawRectangle(std::min(iDasherSize,iDasherMaxX), std::min(y2,iDasherMaxY), 0, std::max(y1,iDasherMinY), Color, -1, ColorScheme, GetBoolParameter(BP_OUTLINE_MODE), true, 1);
357 else {
358 int iDasherY(DasherModel()->DasherY());
360 int iSpacing(iDasherY / 128); // FIXME - assuming that this is an integer below
362 int iXStart = 0;
364 switch (iTruncationType) {
365 case 1:
366 iXStart = iSize - iSize * iTruncation / 200;
367 break;
368 case 2:
369 iXStart = iSize - iSize * iTruncation / 100;
370 break;
373 int iTipMin((y2 - y1) * iTruncation / (200) + y1);
374 int iTipMax(y2 - (y2 - y1) * iTruncation / (200));
376 int iLowerMin(((y1 + 1) / iSpacing) * iSpacing);
377 int iLowerMax(((iTipMin - 1) / iSpacing) * iSpacing);
379 int iUpperMin(((iTipMax + 1) / iSpacing) * iSpacing);
380 int iUpperMax(((y2 - 1) / iSpacing) * iSpacing);
382 if(iLowerMin < 0)
383 iLowerMin = 0;
385 if(iLowerMax < 0)
386 iLowerMax = 0;
388 if(iUpperMin < 0)
389 iUpperMin = 0;
391 if(iUpperMax < 0)
392 iUpperMax = 0;
394 if(iLowerMin > iDasherY)
395 iLowerMin = iDasherY;
397 if(iLowerMax > iDasherY)
398 iLowerMax = iDasherY;
400 if(iUpperMin > iDasherY)
401 iUpperMin = iDasherY;
403 if(iUpperMax > iDasherY)
404 iUpperMax = iDasherY;
406 while(iLowerMin < y1)
407 iLowerMin += iSpacing;
409 while(iLowerMax > iTipMin)
410 iLowerMax -= iSpacing;
412 while(iUpperMin < iTipMax)
413 iUpperMin += iSpacing;
415 while(iUpperMax > y2)
416 iUpperMax -= iSpacing;
418 int iLowerCount((iLowerMax - iLowerMin) / iSpacing + 1);
419 int iUpperCount((iUpperMax - iUpperMin) / iSpacing + 1);
421 if(iLowerCount < 0)
422 iLowerCount = 0;
424 if(iUpperCount < 0)
425 iUpperCount = 0;
427 int iTotalCount(iLowerCount + iUpperCount + 6);
429 myint *x = new myint[iTotalCount];
430 myint *y = new myint[iTotalCount];
432 // Weird duplication here is to make truncated squares possible too
434 x[0] = 0;
435 y[0] = y1;
436 x[1] = iXStart;
437 y[1] = y1;
439 x[iLowerCount + 2] = iDasherSize;
440 y[iLowerCount + 2] = iTipMin;
441 x[iLowerCount + 3] = iDasherSize;
442 y[iLowerCount + 3] = iTipMax;
444 x[iTotalCount - 2] = iXStart;
445 y[iTotalCount - 2] = y2;
446 x[iTotalCount - 1] = 0;
447 y[iTotalCount - 1] = y2;
449 for(int i(0); i < iLowerCount; ++i) {
450 x[i + 2] = (iLowerMin + i * iSpacing - y1) * (iDasherSize - iXStart) / (iTipMin - y1) + iXStart;
451 y[i + 2] = iLowerMin + i * iSpacing;
454 for(int j(0); j < iUpperCount; ++j) {
455 x[j + iLowerCount + 4] = (y2 - (iUpperMin + j * iSpacing)) * (iDasherSize - iXStart) / (y2 - iTipMax) + iXStart;
456 y[j + iLowerCount + 4] = iUpperMin + j * iSpacing;
459 DasherPolygon(x, y, iTotalCount, Color);
461 delete x;
462 delete y;
466 myint iDasherAnchorX(iDasherSize);
468 std::string sDisplayText;
470 if(displaytext != std::string(""))
471 sDisplayText = displaytext;
472 else
473 sDisplayText = DasherModel()->GetDisplayText(Character);
476 if( sDisplayText.size() > 0 )
477 DasherDrawText(iDasherAnchorX, y1, iDasherAnchorX, y2, sDisplayText, mostleft, bShove);
479 return 1;
482 bool CDasherViewSquare::CheckForNewRoot() {
483 CDasherNode *const root = DasherModel()->Root();
484 CDasherNode::ChildMap & children = root->Children();
486 myint y1 = DasherModel()->Rootmin();
487 myint y2 = DasherModel()->Rootmax();
489 // This says that the root node must enclose everything visible.
490 // Tiny probability characters near the bottom will cause a problem
491 // with forcing to reparent to the previous one.
493 myint iDasherMinX;
494 myint iDasherMinY;
495 myint iDasherMaxX;
496 myint iDasherMaxY;
498 VisibleRegion(iDasherMinX, iDasherMinY, iDasherMaxX, iDasherMaxY);
500 if((y1 > iDasherMinY) || (y2 < iDasherMaxY ) || (y2-y1 < iDasherMaxX)) {
501 DasherModel()->Reparent_root(root->Lbnd(), root->Hbnd());
502 return(DasherModel()->Root() != root);
505 if(children.size() == 0)
506 return false;
508 int alive = 0;
509 CDasherNode *theone = 0;
512 // Find whether there is exactly one alive child; if more, we don't care.
513 CDasherNode::ChildMap::iterator i;
514 for(i = children.begin(); i != children.end(); i++) {
515 if((*i)->Alive()) {
516 alive++;
517 theone = *i;
518 if(alive > 1)
519 break;
523 if(alive == 1) {
524 // We must have zoomed sufficiently that only one child of the root node
525 // is still alive. Let's make it the root.
527 y1 = DasherModel()->Rootmin();
528 y2 = DasherModel()->Rootmax();
529 myint range = y2 - y1;
531 myint newy1 = y1 + (range * theone->Lbnd()) / (int)GetLongParameter(LP_NORMALIZATION);
532 myint newy2 = y1 + (range * theone->Hbnd()) / (int)GetLongParameter(LP_NORMALIZATION);
533 if(newy1 < iDasherMinY && newy2 > iDasherMaxY)
534 if( (newy2 - newy1) > iDasherMaxX ) {
535 DasherModel()->Make_root(theone);
536 return false;
540 return false;
543 /// Convert screen co-ordinates to dasher co-ordinates. This doesn't
544 /// include the nonlinear mapping for eyetracking mode etc - it is
545 /// just the inverse of the mapping used to calculate the screen
546 /// positions of boxes etc.
548 void CDasherViewSquare::Screen2Dasher(screenint iInputX, screenint iInputY, myint &iDasherX, myint &iDasherY, bool b1D, bool bNonlinearity) {
550 // Things we're likely to need:
552 myint iDasherWidth = DasherModel()->DasherY();
553 myint iDasherHeight = DasherModel()->DasherY();
555 screenint iScreenWidth = Screen()->GetWidth();
556 screenint iScreenHeight = Screen()->GetHeight();
558 if( b1D ) { // Special case for 1D mode...
559 iDasherX = iInputX * iDasherWidth / iScreenWidth;
560 iDasherY = iInputY * iDasherHeight / iScreenHeight;
561 return;
564 int eOrientation(GetLongParameter(LP_REAL_ORIENTATION));
566 myint iScaleFactorX;
567 myint iScaleFactorY;
569 GetScaleFactor(eOrientation, &iScaleFactorX, &iScaleFactorY);
571 switch(eOrientation) {
572 case Dasher::Opts::LeftToRight:
573 iDasherX = iDasherWidth / 2 - ( iInputX - iScreenWidth / 2 ) * m_iScalingFactor / iScaleFactorX;
574 iDasherY = iDasherHeight / 2 + ( iInputY - iScreenHeight / 2 ) * m_iScalingFactor / iScaleFactorY;
575 break;
576 case Dasher::Opts::RightToLeft:
577 iDasherX = myint(iDasherWidth / 2 + ( iInputX - iScreenWidth / 2 ) * m_iScalingFactor/ iScaleFactorX);
578 iDasherY = myint(iDasherHeight / 2 + ( iInputY - iScreenHeight / 2 ) * m_iScalingFactor/ iScaleFactorY);
579 break;
580 case Dasher::Opts::TopToBottom:
581 iDasherX = myint(iDasherWidth / 2 - ( iInputY - iScreenHeight / 2 ) * m_iScalingFactor/ iScaleFactorY);
582 iDasherY = myint(iDasherHeight / 2 + ( iInputX - iScreenWidth / 2 ) * m_iScalingFactor/ iScaleFactorX);
583 break;
584 case Dasher::Opts::BottomToTop:
585 iDasherX = myint(iDasherWidth / 2 + ( iInputY - iScreenHeight / 2 ) * m_iScalingFactor/ iScaleFactorY);
586 iDasherY = myint(iDasherHeight / 2 + ( iInputX - iScreenWidth / 2 ) * m_iScalingFactor/ iScaleFactorX);
587 break;
590 #ifndef WITH_MAEMO
591 // FIXME - disabled to avoid floating point
592 if( bNonlinearity ) {
593 iDasherX = myint(ixmap(iDasherX / static_cast < double >(DasherModel()->DasherY())) * DasherModel()->DasherY());
594 iDasherY = m_ymap.unmap(iDasherY);
596 #endif
599 void CDasherViewSquare::SetScaleFactor( void )
601 myint iDasherWidth = DasherModel()->DasherY();
602 myint iDasherHeight = iDasherWidth;
604 screenint iScreenWidth = Screen()->GetWidth();
605 screenint iScreenHeight = Screen()->GetHeight();
607 // Try doing this a different way:
609 myint iDasherMargin( 300 ); // Make this a parameter
611 myint iMinX( 0-iDasherMargin );
612 myint iMaxX( iDasherWidth + iDasherMargin );
613 myint iMinY( 0 );
614 myint iMaxY( iDasherHeight );
616 double dLRHScaleFactor;
617 double dLRVScaleFactor;
618 double dTBHScaleFactor;
619 double dTBVScaleFactor;
621 dLRHScaleFactor = iScreenWidth / static_cast<double>( iMaxX - iMinX );
622 dLRVScaleFactor = iScreenHeight / static_cast<double>( iMaxY - iMinY );
623 dTBHScaleFactor = iScreenWidth / static_cast<double>( iMaxY - iMinY );
624 dTBVScaleFactor = iScreenHeight / static_cast<double>( iMaxX - iMinX );
626 iLRScaleFactorX = std::max(std::min(dLRHScaleFactor, dLRVScaleFactor), dLRHScaleFactor / 4.0) * m_iScalingFactor;
627 iLRScaleFactorY = std::max(std::min(dLRHScaleFactor, dLRVScaleFactor), dLRVScaleFactor / 4.0) * m_iScalingFactor;
628 iTBScaleFactorX = std::max(std::min(dTBHScaleFactor, dTBVScaleFactor), dTBVScaleFactor / 4.0) * m_iScalingFactor;
629 iTBScaleFactorY = std::max(std::min(dTBHScaleFactor, dTBVScaleFactor), dTBHScaleFactor / 4.0) * m_iScalingFactor;
632 void CDasherViewSquare::GetScaleFactor( int eOrientation, myint *iScaleFactorX, myint *iScaleFactorY ) {
633 if(( eOrientation == Dasher::Opts::LeftToRight ) || ( eOrientation == Dasher::Opts::RightToLeft )) {
634 *iScaleFactorX = iLRScaleFactorX;
635 *iScaleFactorY = iLRScaleFactorY;
636 } else {
637 *iScaleFactorX = iTBScaleFactorX;
638 *iScaleFactorY = iTBScaleFactorY;
642 /// Convert dasher co-ordinates to screen co-ordinates
644 void CDasherViewSquare::Dasher2Screen(myint iDasherX, myint iDasherY, screenint &iScreenX, screenint &iScreenY) {
646 // Apply the nonlinearities
648 #ifndef WITH_MAEMO
649 // FIXME
650 iDasherX = myint(xmap(iDasherX / static_cast < double >(DasherModel()->DasherY())) * DasherModel()->DasherY());
651 iDasherY = m_ymap.map(iDasherY);
652 #endif
654 // Things we're likely to need:
656 myint iDasherWidth = DasherModel()->DasherY();
657 myint iDasherHeight = DasherModel()->DasherY();
659 screenint iScreenWidth = Screen()->GetWidth();
660 screenint iScreenHeight = Screen()->GetHeight();
662 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION) );
664 myint iScaleFactorX;
665 myint iScaleFactorY;
667 GetScaleFactor( eOrientation, &iScaleFactorX, &iScaleFactorY);
669 switch( eOrientation ) {
670 case Dasher::Opts::LeftToRight:
671 iScreenX = screenint(iScreenWidth / 2 - ( iDasherX - iDasherWidth / 2 ) * iScaleFactorX / m_iScalingFactor);
672 iScreenY = screenint(iScreenHeight / 2 + ( iDasherY - iDasherHeight / 2 ) * iScaleFactorY / m_iScalingFactor);
673 break;
674 case Dasher::Opts::RightToLeft:
675 iScreenX = screenint(iScreenWidth / 2 + ( iDasherX - iDasherWidth / 2 ) * iScaleFactorX / m_iScalingFactor);
676 iScreenY = screenint(iScreenHeight / 2 + ( iDasherY - iDasherHeight / 2 ) * iScaleFactorY / m_iScalingFactor);
677 break;
678 case Dasher::Opts::TopToBottom:
679 iScreenX = screenint(iScreenWidth / 2 + ( iDasherY - iDasherHeight / 2 ) * iScaleFactorX / m_iScalingFactor);
680 iScreenY = screenint(iScreenHeight / 2 - ( iDasherX - iDasherWidth / 2 ) * iScaleFactorY / m_iScalingFactor);
681 break;
682 case Dasher::Opts::BottomToTop:
683 iScreenX = screenint(iScreenWidth / 2 + ( iDasherY - iDasherHeight / 2 ) * iScaleFactorX / m_iScalingFactor);
684 iScreenY = screenint(iScreenHeight / 2 + ( iDasherX - iDasherWidth / 2 ) * iScaleFactorY / m_iScalingFactor);
685 break;
688 // std::cout << iScaleFactorX << " " << iScaleFactorY << " " << iScreenWidth << " " << iScreenHeight << std::endl;
689 //std::cout << iDasherX << " -> " << iScreenX << ", " << iDasherY << " -> " << iScreenY << std::endl;
696 void CDasherViewSquare::VisibleRegion( myint &iDasherMinX, myint &iDasherMinY, myint &iDasherMaxX, myint &iDasherMaxY ) {
697 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION) );
699 switch( eOrientation ) {
700 case Dasher::Opts::LeftToRight:
701 Screen2Dasher(Screen()->GetWidth(),0,iDasherMinX,iDasherMinY,false,true);
702 Screen2Dasher(0,Screen()->GetHeight(),iDasherMaxX,iDasherMaxY,false,true);
703 break;
704 case Dasher::Opts::RightToLeft:
705 Screen2Dasher(0,0,iDasherMinX,iDasherMinY,false,true);
706 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherMaxX,iDasherMaxY,false,true);
707 break;
708 case Dasher::Opts::TopToBottom:
709 Screen2Dasher(0,Screen()->GetHeight(),iDasherMinX,iDasherMinY,false,true);
710 Screen2Dasher(Screen()->GetWidth(),0,iDasherMaxX,iDasherMaxY,false,true);
711 break;
712 case Dasher::Opts::BottomToTop:
713 Screen2Dasher(0,0,iDasherMinX,iDasherMinY,false,true);
714 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherMaxX,iDasherMaxY,false,true);
715 break;
719 /// The minimum Dasher Y co-ordinate which will be visible
721 myint CDasherViewSquare::DasherVisibleMinY() {
723 // Todo - convert all these to a single 'get visible extent' function
725 myint iDasherX;
726 myint iDasherY;
728 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION) );
730 switch( eOrientation ) {
731 case Dasher::Opts::LeftToRight:
732 Screen2Dasher(Screen()->GetWidth(),0,iDasherX,iDasherY,false,true);
733 break;
734 case Dasher::Opts::RightToLeft:
735 Screen2Dasher(0,0,iDasherX,iDasherY,false,true);
736 break;
737 case Dasher::Opts::TopToBottom:
738 Screen2Dasher(0,Screen()->GetHeight(),iDasherX,iDasherY,false,true);
739 break;
740 case Dasher::Opts::BottomToTop:
741 Screen2Dasher(0,0,iDasherX,iDasherY,false,true);
742 break;
745 return iDasherY;
748 /// The maximum Dasher Y co-ordinate which will be visible
750 myint CDasherViewSquare::DasherVisibleMaxY() {
751 // Todo - convert all these to a single 'get visible extent' function
753 myint iDasherX;
754 myint iDasherY;
756 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION) );
758 switch( eOrientation ) {
759 case Dasher::Opts::LeftToRight:
760 Screen2Dasher(0,Screen()->GetHeight(),iDasherX,iDasherY,false,true);
761 break;
762 case Dasher::Opts::RightToLeft:
763 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX,iDasherY,false,true);
764 break;
765 case Dasher::Opts::TopToBottom:
766 Screen2Dasher(Screen()->GetWidth(),0,iDasherX,iDasherY,false,true);
767 break;
768 case Dasher::Opts::BottomToTop:
769 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX,iDasherY,false,true);
770 break;
773 return iDasherY;
776 /// The maximum Dasher X co-ordinate which will be visible
778 myint CDasherViewSquare::DasherVisibleMaxX() {
779 // Todo - convert all these to a single 'get visible extent' function
781 myint iDasherX;
782 myint iDasherY;
784 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION) );
786 switch( eOrientation ) {
787 case Dasher::Opts::LeftToRight:
788 Screen2Dasher(0,Screen()->GetHeight(),iDasherX,iDasherY,false,true);
789 break;
790 case Dasher::Opts::RightToLeft:
791 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX,iDasherY,false,true);
792 break;
793 case Dasher::Opts::TopToBottom:
794 Screen2Dasher(Screen()->GetWidth(),0,iDasherX,iDasherY,false,true);
795 break;
796 case Dasher::Opts::BottomToTop:
797 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX,iDasherY,false,true);
798 break;
801 return iDasherX;
804 /// Convert raw Dasher co-ordinates to the equivalent 1D mode position
806 void CDasherViewSquare::Dasher2OneD(myint &iDasherX, myint &iDasherY) {
808 double disty,circlesize,yfullrange,yforwardrange,angle,ellipse_eccentricity,ybackrange,yb,x;
810 // The distance between the Y coordinate and the centreline in pixels
811 disty=DasherModel()->DasherOY()-iDasherY;
813 circlesize= DasherModel()->DasherY()/2.5;
814 yforwardrange= DasherModel()->DasherY()/3.2; // Was 1.6
815 yfullrange= yforwardrange*1.6;
816 ybackrange= yfullrange-yforwardrange;
817 ellipse_eccentricity=6;
819 if (disty>yforwardrange) {
820 // If the distance between y-coord and centreline is > radius,
821 // we should be going backwards, off the top.
822 yb=(disty-yforwardrange)/ybackrange;
824 if (yb>1) {
825 x=0;
826 iDasherY=myint(DasherModel()->DasherOY());
828 else {
829 angle=(yb*3.14159)*(yb+(1-yb)*(ybackrange/yforwardrange/ellipse_eccentricity));
831 x=(-sin(angle)*circlesize/2)*ellipse_eccentricity;
832 iDasherY=myint(-(1+cos(angle))*circlesize/2+DasherModel()->DasherOY());
835 else if (disty <-(yforwardrange)) {
836 // Backwards, off the bottom.
837 yb=-(disty+yforwardrange)/ybackrange;
839 if (yb>1) {
840 x=0;
841 iDasherY=myint(DasherModel()->DasherOY());
843 else {
844 angle=(yb*3.14159)*(yb+(1-yb)*(ybackrange/yforwardrange/ellipse_eccentricity));
846 x=(-sin(angle)*circlesize/2)*ellipse_eccentricity;
847 iDasherY=myint((1+cos(angle))*circlesize/2+DasherModel()->DasherOY());
851 else {
852 angle=((disty*3.14159/2)/yforwardrange);
853 x=cos(angle)*circlesize;
854 iDasherY=myint(-sin(angle)*circlesize+DasherModel()->DasherOY());
856 x=DasherModel()->DasherOX()-x;
858 iDasherX = myint(x);
861 /// Convert raw Dasher co-ordinates to eyetracker position
863 void CDasherViewSquare::Dasher2Eyetracker(myint &iDasherX, myint &iDasherY) {
865 double disty=DasherModel()->DasherOY()-iDasherY;
867 myint x( iDasherX );
869 myint dasherOX=DasherModel()->DasherOX();
871 // if( iDasherX < dasherOX ) {
873 //cout << "dasherOX: " << dasherOX << endl;
874 myint dasherOY=DasherModel()->DasherOY();
876 // X co-ordinate changes.
877 double double_x = (x/dasherOX); // Fraction of way over to crosshair
878 double double_y = -((iDasherY-dasherOY)/(double)(dasherOY) ); // Fraction above the crosshair
880 // FIXME - I have *no* idea how this is supposed to work - someone else fix it and comment the code please!
882 double xmax_y = xmax(double_x, double_y);
884 if(double_x < xmax_y) {
885 double_x = xmax_y;
888 // std::cout << xmax_y << std::endl;
890 x = myint(dasherOX*double_x);
892 // Finished x-coord changes.
894 double repulsionparameter=0.5;
895 iDasherY = myint(dasherOY - (1.0+ double_y*double_y* repulsionparameter ) * disty);
898 if( iDasherX < x )
899 iDasherX = x;
902 /// Convert abstract 'input coordinates', which may or may not
903 /// correspond to actual screen positions, depending on the settings,
904 /// into dasher co-ordinates. Modes are:
906 /// 0 = Direct (ie mouse)
907 /// 1 = 1D
908 /// 2 = Eyetracker
910 /// This should be done once initially, then we work in Dasher
911 /// co-ordinates for everything else. Input co-ordinates will be
912 /// assumed to range over the extent of the screen.
914 /// TODO: Abstract out modes into an enum
916 void CDasherViewSquare::Input2Dasher(screenint iInputX, screenint iInputY, myint &iDasherX, myint &iDasherY, int iType, int iMode) {
918 // FIXME - need to incorporate one-button mode?
920 // First convert the supplied co-ordinates to 'linear' Dasher co-ordinates
922 switch (iType) {
923 case 0:
924 // Raw secreen coordinates
925 if(iMode == 2) {
926 // First apply the autocalibration offset
927 iInputY += int (m_yAutoOffset); // FIXME - we need more flexible autocalibration to work with orientations other than left-to-right
930 if( iMode == 0 )
931 Screen2Dasher( iInputX, iInputY, iDasherX, iDasherY, false, true );
932 else if( iMode == 1 )
933 Screen2Dasher( iInputX, iInputY, iDasherX, iDasherY, true, false );
934 else
935 Screen2Dasher( iInputX, iInputY, iDasherX, iDasherY, false, true );
936 break;
937 case 1:
938 // Raw dasher coordinates
940 iDasherX = iInputX;
941 iDasherY = iInputY;
942 break;
943 default:
944 // ERROR
945 break;
948 // Apply y scaling
950 if(iMode == 1 ) {
951 if( GetLongParameter(LP_YSCALE) > 0 ) {
953 double dYScale;
955 int eOrientation(GetLongParameter(LP_REAL_ORIENTATION));
957 if(( eOrientation == Dasher::Opts::LeftToRight ) || ( eOrientation == Dasher::Opts::RightToLeft ))
958 dYScale = Screen()->GetHeight() / static_cast<double>(GetLongParameter(LP_YSCALE));
959 else
960 dYScale = Screen()->GetWidth() / static_cast<double>(GetLongParameter(LP_YSCALE));
962 iDasherY = myint((iDasherY - DasherModel()->DasherY()/2) * dYScale + DasherModel()->DasherY()/2);
966 // Then apply any non-linearity
968 switch (iMode) {
969 case 0: // Direct mode
970 // Simply get the dasher co-ordinate under the mouse cursor
971 // Screen2Dasher( iInputX, iInputY, iDasherX, iDasherY );
973 // Don't go off the canvas - FIXME - is this always needed, or just in direct mode?
974 // if(iDasherY > DasherModel()->DasherY())
975 // iDasherY = DasherModel()->DasherY();
976 // if(iDasherY < 0)
977 // iDasherY = 0;
979 break;
980 case 1: // 1D mode
981 // Ignore orientation - iInputY maps directly to the single dimension in this case
982 // iDasherY = iInputY * DasherModel()->DasherY() / Screen()->GetHeight();
984 // Apply non-linear mapping
985 Dasher2OneD(iDasherX, iDasherY);
987 break;
988 case 2: // Eyetracker mode
990 // Then find the dasher co-ordinate under the offset mouse position
992 // Finally apply the non-linear transformation
993 Dasher2Eyetracker(iDasherX, iDasherY);
995 break;
996 default:
997 // Oops!
998 break;
1002 /// Draw a polyline specified in Dasher co-ordinates
1004 void CDasherViewSquare::DasherPolyline(myint *x, myint *y, int n, int iWidth, int iColour) {
1006 CDasherScreen::point * ScreenPoints = new CDasherScreen::point[n];
1008 for(int i(0); i < n; ++i)
1009 Dasher2Screen(x[i], y[i], ScreenPoints[i].x, ScreenPoints[i].y);
1011 if(iColour != -1) {
1012 Screen()->Polyline(ScreenPoints, n, iWidth, iColour);
1014 else {
1015 Screen()->Polyline(ScreenPoints, n, iWidth);
1017 delete[]ScreenPoints;
1020 // Draw a filled polygon specified in Dasher co-ordinates
1022 void CDasherViewSquare::DasherPolygon(myint *x, myint *y, int n, int iColour) {
1024 CDasherScreen::point * ScreenPoints = new CDasherScreen::point[n];
1026 for(int i(0); i < n; ++i)
1027 Dasher2Screen(x[i], y[i], ScreenPoints[i].x, ScreenPoints[i].y);
1029 Screen()->Polygon(ScreenPoints, n, iColour);
1030 delete[]ScreenPoints;
1033 // Draw a box specified in Dasher co-ordinates
1035 void CDasherViewSquare::DasherDrawRectangle(myint iLeft, myint iTop, myint iRight, myint iBottom, const int Color, int iOutlineColour, Opts::ColorSchemes ColorScheme, bool bDrawOutline, bool bFill, int iThickness) {
1037 screenint iScreenLeft;
1038 screenint iScreenTop;
1039 screenint iScreenRight;
1040 screenint iScreenBottom;
1042 Dasher2Screen(iLeft, iTop, iScreenLeft, iScreenTop);
1043 Dasher2Screen(iRight, iBottom, iScreenRight, iScreenBottom);
1045 Screen()->DrawRectangle(iScreenLeft, iScreenTop, iScreenRight, iScreenBottom, Color, iOutlineColour, ColorScheme, bDrawOutline, bFill, iThickness);
1048 /// Draw a rectangle centred on a given dasher co-ordinate, but with a size specified in screen co-ordinates (used for drawing the mouse blob)
1050 void CDasherViewSquare::DasherDrawCentredRectangle(myint iDasherX, myint iDasherY, screenint iSize, const int Color, Opts::ColorSchemes ColorScheme, bool bDrawOutline) {
1052 screenint iScreenX;
1053 screenint iScreenY;
1055 Dasher2Screen(iDasherX, iDasherY, iScreenX, iScreenY);
1057 Screen()->DrawRectangle(iScreenX - iSize, iScreenY - iSize, iScreenX + iSize, iScreenY + iSize, Color, -1, ColorScheme, bDrawOutline, true, 1);
1060 /// Draw text specified in Dasher co-ordinates. The position is
1061 /// specified as two co-ordinates, intended to the be the corners of
1062 /// the leading edge of the containing box.
1064 void CDasherViewSquare::DasherDrawText(myint iAnchorX1, myint iAnchorY1, myint iAnchorX2, myint iAnchorY2, const std::string &sDisplayText, int &mostleft, bool bShove) {
1066 // Don't draw text which will overlap with text in an ancestor.
1068 if(iAnchorX1 > mostleft)
1069 iAnchorX1 = mostleft;
1071 if(iAnchorX2 > mostleft)
1072 iAnchorX2 = mostleft;
1074 myint iDasherMinX;
1075 myint iDasherMinY;
1076 myint iDasherMaxX;
1077 myint iDasherMaxY;
1079 VisibleRegion(iDasherMinX, iDasherMinY, iDasherMaxX, iDasherMaxY);
1081 iAnchorY1 = std::min( iDasherMaxY, std::max( iDasherMinY, iAnchorY1 ) );
1082 iAnchorY2 = std::min( iDasherMaxY, std::max( iDasherMinY, iAnchorY2 ) );
1084 screenint iScreenAnchorX1;
1085 screenint iScreenAnchorY1;
1086 screenint iScreenAnchorX2;
1087 screenint iScreenAnchorY2;
1089 // FIXME - Truncate here before converting - otherwise we risk integer overflow in screen coordinates
1091 Dasher2Screen(iAnchorX1, iAnchorY1, iScreenAnchorX1, iScreenAnchorY1);
1092 Dasher2Screen(iAnchorX2, iAnchorY2, iScreenAnchorX2, iScreenAnchorY2);
1094 // Truncate the ends of the anchor line to be on the screen - this
1095 // prevents us from loosing characters off the top and bottom of the
1096 // screen
1098 // TruncateToScreen(iScreenAnchorX1, iScreenAnchorY1);
1099 // TruncateToScreen(iScreenAnchorX2, iScreenAnchorY2);
1101 // Actual anchor point is the midpoint of the anchor line
1103 screenint iScreenAnchorX((iScreenAnchorX1 + iScreenAnchorX2) / 2);
1104 screenint iScreenAnchorY((iScreenAnchorY1 + iScreenAnchorY2) / 2);
1106 // Compute font size based on position
1107 int Size = GetLongParameter( LP_DASHER_FONTSIZE );
1109 // FIXME - this could be much more elegant, and probably needs a
1110 // rethink anyway - behvaiour here is too dependent on screen size
1112 screenint iLeftTimesFontSize = (DasherModel()->DasherY() - (iAnchorX1 + iAnchorX2)/ 2 )*Size;
1113 if(iLeftTimesFontSize < DasherModel()->DasherY() * 19/ 20)
1114 Size *= 20;
1115 else if(iLeftTimesFontSize < DasherModel()->DasherY() * 159 / 160)
1116 Size *= 14;
1117 else
1118 Size *= 11;
1121 screenint TextWidth, TextHeight;
1123 Screen()->TextSize(sDisplayText, &TextWidth, &TextHeight, Size);
1125 // Poistion of text box relative to anchor depends on orientation
1127 screenint newleft2 = 0;
1128 screenint newtop2 = 0;
1129 screenint newright2 = 0;
1130 screenint newbottom2 = 0;
1132 switch (Dasher::Opts::ScreenOrientations(GetLongParameter(LP_REAL_ORIENTATION))) {
1133 case (Dasher::Opts::LeftToRight):
1134 newleft2 = iScreenAnchorX;
1135 newtop2 = iScreenAnchorY - TextHeight / 2;
1136 newright2 = iScreenAnchorX + TextWidth;
1137 newbottom2 = iScreenAnchorY + TextHeight / 2;
1138 break;
1139 case (Dasher::Opts::RightToLeft):
1140 newleft2 = iScreenAnchorX - TextWidth;
1141 newtop2 = iScreenAnchorY - TextHeight / 2;
1142 newright2 = iScreenAnchorX;
1143 newbottom2 = iScreenAnchorY + TextHeight / 2;
1144 break;
1145 case (Dasher::Opts::TopToBottom):
1146 newleft2 = iScreenAnchorX - TextWidth / 2;
1147 newtop2 = iScreenAnchorY;
1148 newright2 = iScreenAnchorX + TextWidth / 2;
1149 newbottom2 = iScreenAnchorY + TextHeight;
1150 break;
1151 case (Dasher::Opts::BottomToTop):
1152 newleft2 = iScreenAnchorX - TextWidth / 2;
1153 newtop2 = iScreenAnchorY - TextHeight;
1154 newright2 = iScreenAnchorX + TextWidth / 2;
1155 newbottom2 = iScreenAnchorY;
1156 break;
1157 default:
1158 break;
1161 // Update the value of mostleft to take into account the new text
1163 if(bShove) {
1164 myint iDasherNewLeft;
1165 myint iDasherNewTop;
1166 myint iDasherNewRight;
1167 myint iDasherNewBottom;
1169 Screen2Dasher(newleft2, newtop2, iDasherNewLeft, iDasherNewTop,false,true);
1170 Screen2Dasher(newright2, newbottom2, iDasherNewRight, iDasherNewBottom,false,true);
1172 mostleft = std::min(iDasherNewRight, iDasherNewLeft);
1175 // Actually draw the text. We use DelayDrawText as the text should
1176 // be overlayed once all of the boxes have been drawn.
1178 m_pDelayDraw->DelayDrawText(sDisplayText, newleft2, newtop2, Size);
1182 /// Truncate a set of co-ordinates so that they are on the screen
1184 void CDasherViewSquare::TruncateToScreen(screenint &iX, screenint &iY) {
1186 // I think that this function is now obsolete
1188 if(iX < 0)
1189 iX = 0;
1190 if(iX > Screen()->GetWidth())
1191 iX = Screen()->GetWidth();
1193 if(iY < 0)
1194 iY = 0;
1195 if(iY > Screen()->GetHeight())
1196 iY = Screen()->GetHeight();
1199 // work out the next viewpoint
1200 // move the rectangles accordingly
1201 void CDasherViewSquare::TapOnDisplay(screenint mousex,
1202 screenint mousey,
1203 unsigned long Time,
1204 Dasher::VECTOR_SYMBOL_PROB* pAdded,
1205 int* pNumDeleted) {
1207 // NOTE - we now ignore the values which are actually passed to the display
1209 // FIXME - Actually turn autocalibration on and off!
1210 // FIXME - AutoCalibrate should use Dasher co-ordinates, not raw mouse co-ordinates?
1211 // FIXME - Have I broken this by moving it before the offset is applied?
1212 // FIXME - put ymap stuff back in
1214 // FIXME - optimise this
1216 int iCoordinateCount(GetCoordinateCount());
1218 myint *pCoordinates(new myint[iCoordinateCount]);
1220 int iType(GetCoordinates(iCoordinateCount, pCoordinates));
1222 if(iCoordinateCount == 1) {
1223 mousex = 0;
1224 mousey = pCoordinates[0];
1226 else {
1227 mousex = pCoordinates[0];
1228 mousey = pCoordinates[1];
1231 delete[]pCoordinates;
1233 // bool autocalibrate = GetBoolParameter(BP_AUTOCALIBRATE);
1234 if(GetBoolParameter(BP_AUTOCALIBRATE) && GetBoolParameter(BP_EYETRACKER_MODE)) {
1235 AutoCalibrate(&mousex, &mousey);
1238 myint iDasherX;
1239 myint iDasherY;
1241 // Convert the input co-ordinates to dasher co-ordinates
1243 Input2Dasher(mousex, mousey, iDasherX, iDasherY, iType, DasherModel()->GetMode());
1244 m_iDasherXCache = iDasherX;
1245 m_iDasherYCache = iDasherY;
1247 // Request an update at the calculated co-ordinates
1249 DasherModel()->Tap_on_display(iDasherX,iDasherY, Time, pAdded, pNumDeleted);
1251 SpeedControl(iDasherX, iDasherY);
1253 CheckForNewRoot();
1254 // Cache the Dasher Co-ordinates, so we can use them later for things like drawing the mouse position
1255 #ifndef WITH_MAEMO
1256 // FIXME
1257 iDasherX = myint(xmap(iDasherX / static_cast < double >(DasherModel()->DasherY())) * DasherModel()->DasherY());
1258 iDasherY = m_ymap.map(iDasherY);
1259 #endif
1262 void CDasherViewSquare::ClickTo(int x, int y, int width, int height)
1264 myint dasherx, dashery;
1265 Screen2Dasher(x,y,dasherx,dashery,false,false);
1266 if (dasherx < 2) { dasherx = 100; }
1267 int iSteps = GetLongParameter(LP_ZOOMSTEPS);
1268 myint iRxnew = ((DasherModel()->DasherOX()/2) * DasherModel()->DasherOX()) / dasherx;
1269 myint o1, o2, n1, n2;
1270 DasherModel()->Plan_new_goto_coords(iRxnew, dashery, &iSteps, &o1,&o2,&n1,&n2);
1271 int s ;
1272 myint rootMin, rootMax;
1273 rootMin = n1;
1274 rootMax = n2;
1275 myint zoomstep1, zoomstep2;
1276 for (s = 1 ; s <= iSteps ; s ++) {
1277 // use simple linear interpolation. Really should do logarithmic interpolation, but
1278 // this will probably look fine.
1279 zoomstep1 = (s * n1 + (iSteps-s) * o1) / iSteps;
1280 zoomstep2 = (s * n2 + (iSteps-s) * o2) / iSteps;
1281 DasherModel()->NewGoTo(zoomstep1, zoomstep2, 1);
1282 Render();
1283 Display();
1285 DasherModel()->NewGoTo(n1, n2, 2);
1286 Render();
1287 Display();
1291 // move to the specified point
1293 void CDasherViewSquare::GoTo(screenint mousex, screenint mousey) {
1294 // convert mouse (screen) coords into dasher coords
1296 UnMapScreen(&mousex, &mousey);
1297 myint idasherx, idashery;
1298 Screen2Dasher(mousex, mousey, idasherx, idashery, false, false);
1299 DasherModel()->GoTo(idasherx, idashery);
1300 CheckForNewRoot();
1303 void CDasherViewSquare::DrawGoTo(screenint mousex, screenint mousey) {
1304 // Draw a box surrounding the area of the screen that will be zoomed into
1305 UnMapScreen(&mousex, &mousey);
1306 myint idasherx, idashery;
1307 screen2dasher(mousex, mousey, &idasherx, &idashery);
1308 // So, we have a set of coordinates. We need a bunch of points back.
1309 myint height = DasherModel()->PlotGoTo(idasherx, idashery);
1310 myint top, bottom, left, right;
1312 // Convert back to screen coordinates?
1313 top = mousey - height / 2;
1314 bottom = mousey + height / 2;
1315 left = mousex + height / 2;
1316 right = mousex - height / 2;
1317 top = dashery2screen(top);
1318 bottom = dashery2screen(bottom);
1319 left = dasherx2screen(left);
1320 right = dasherx2screen(right);
1322 // Draw the lines
1323 Screen()->DrawRectangle(left, top + 5, right, top - 5, 1, -1, Opts::ColorSchemes(Objects), false, true, 1);
1324 Screen()->DrawRectangle(left + 5, top + 5, left, bottom - 5, 1, -1, Opts::ColorSchemes(Objects), false, true, 1);
1325 Screen()->DrawRectangle(left, bottom + 5, right, bottom - 5, 1, -1, Opts::ColorSchemes(Objects), false, true, 1);
1328 void CDasherViewSquare::DrawMouse(screenint mousex, screenint mousey) {
1330 int iCoordinateCount(GetCoordinateCount());
1332 myint *pCoordinates(new myint[iCoordinateCount]);
1334 int iType(GetCoordinates(iCoordinateCount, pCoordinates));
1336 if(iCoordinateCount == 1) {
1337 mousex = 0;
1338 mousey = pCoordinates[0];
1340 else {
1341 mousex = pCoordinates[0];
1342 mousey = pCoordinates[1];
1345 delete[]pCoordinates;
1346 myint iDasherX;
1347 myint iDasherY;
1349 Input2Dasher(mousex, mousey, iDasherX, iDasherY, iType, DasherModel()->GetMode());
1351 if(GetBoolParameter(BP_COLOUR_MODE) == true) {
1352 DasherDrawCentredRectangle(iDasherX, iDasherY, 5, 2, Opts::ColorSchemes(Objects), false);
1354 else {
1355 DasherDrawCentredRectangle(iDasherX, iDasherY, 5, 1, Opts::ColorSchemes(Objects), false);
1360 /// Draw a line from the crosshair to the mouse position
1362 void CDasherViewSquare::DrawMouseLine(screenint mousex, screenint mousey) {
1364 int iCoordinateCount(GetCoordinateCount());
1366 myint *pCoordinates(new myint[iCoordinateCount]);
1368 int iType(GetCoordinates(iCoordinateCount, pCoordinates));
1370 if(iCoordinateCount == 1) {
1371 mousex = 0;
1372 mousey = pCoordinates[0];
1374 else {
1375 mousex = pCoordinates[0];
1376 mousey = pCoordinates[1];
1379 delete[] pCoordinates;
1381 myint x[2];
1382 myint y[2];
1384 // Start of line is the crosshair location
1386 x[0] = DasherModel()->DasherOX();
1387 y[0] = DasherModel()->DasherOY();
1389 // End of line is the mouse cursor location - note that we should
1390 // probably be using a chached value rather than computing this
1391 // separately to TapOn
1393 Input2Dasher(mousex, mousey, x[1], y[1], iType, DasherModel()->GetMode());
1395 // Actually plot the line
1397 if(GetBoolParameter(BP_COLOUR_MODE)) {
1398 DasherPolyline(x, y, 2, GetLongParameter(LP_LINE_WIDTH), 1);
1400 else {
1401 DasherPolyline(x, y, 2, GetLongParameter(LP_LINE_WIDTH), -1);
1405 void CDasherViewSquare::DrawKeyboard() {
1406 CDasherScreen::point line[2];
1407 line[0].x = 200;
1408 line[0].y = 0;
1409 line[1].x = 200;
1410 line[1].y = CanvasY / 2;
1412 if(GetBoolParameter(BP_COLOUR_MODE)) {
1413 Screen()->Polyline(line, 2, 1, 6);
1415 else {
1416 Screen()->Polyline(line, 2, 1);
1419 line[0].x = 200;
1420 line[0].y = CanvasY / 2;
1421 line[1].x = 0;
1422 line[1].y = CanvasY / 2;
1424 if(GetBoolParameter(BP_COLOUR_MODE)) {
1425 Screen()->Polyline(line, 2, 1, 6);
1427 else {
1428 Screen()->Polyline(line, 2, 1);
1431 line[0].x = 200;
1432 line[0].y = CanvasY / 2;
1433 line[1].x = 200;
1434 line[1].y = CanvasY;
1436 if(GetBoolParameter(BP_COLOUR_MODE)) {
1437 Screen()->Polyline(line, 2, 1, 6);
1439 else {
1440 Screen()->Polyline(line, 2, 1);
1444 void CDasherViewSquare::ResetSum() {
1445 m_ySum = 0;
1448 void CDasherViewSquare::ResetSumCounter() {
1449 m_ySumCounter = 0;
1452 void CDasherViewSquare::ResetYAutoOffset() {
1453 m_yAutoOffset = 0;
1456 void CDasherViewSquare::ChangeScreen(CDasherScreen *NewScreen) {
1457 CDasherView::ChangeScreen(NewScreen);
1458 screenint Width = Screen()->GetWidth();
1459 screenint Height = Screen()->GetHeight();
1460 CanvasX = 9 * Width / 10;
1461 CanvasBorder = Width - CanvasX;
1462 CanvasY = Height;
1463 m_iScalingFactor = 100000000;
1464 SetScaleFactor();
1467 int CDasherViewSquare::GetAutoOffset() const {
1468 return m_yAutoOffset;
1471 /// Convert screen co-ordinates to dasher co-ordinates, possibly
1472 /// involving a non-linear transformation for 1D mode, eyetracking
1473 /// mode etc.
1475 /// FIXME - lots of floating point arithmetic here
1477 void CDasherViewSquare::screen2dasher(screenint imousex, screenint imousey, myint *idasherx, myint *idashery) const {
1478 bool eyetracker = GetBoolParameter(BP_EYETRACKER_MODE);
1479 // bool DasherRunning = DasherModel()->Paused();
1481 // Add the eyetracker autocalibration offset if necessary
1483 if(eyetracker)
1484 imousey += int (m_yAutoOffset);
1486 // Maybe this mousey tweak should take place earlier, elsewhere, and
1487 // have a permanent effect on mousey rather than just local.
1489 //SUMMARY OF Y autocallibrate additions:
1490 // If autocallibrate {
1491 // tweak mousey right now before anyone looks at it.
1492 // If dasher running {
1493 // Adjust our tweak estimate
1494 // }
1495 // }
1496 // end summary
1498 // If autocallibrate {
1499 // tweak mousey right now before anyone looks at it.
1500 // NOTE: yAutoOffset should be set to zero ONCE when Dasher
1501 // first started, then left alone. In principle, if
1502 // someone moves their Dasher window from one locn to another
1503 // then it might be reasonable to re-zero the offset. But don't.
1505 // Convert the Y mouse coordinate to one that's based on the canvas size
1506 double dashery = double (imousey * DasherModel()->DasherY() / CanvasY);
1508 // Convert the X mouse coordinate to one that's based on the canvas size
1509 // - we want this the opposite way round to the mouse coordinate system,
1510 // hence the fudging. ixmap gives us the X nonlinearity.
1511 double x = ixmap(1.0 * (CanvasX - imousex) / CanvasX) * DasherModel()->DasherY();
1513 // Disable one-button mode for now.
1514 // if (eyetracker==true) { dashery=onebutton; }
1516 // If we're in standard mode, fudge things for the vertical acceleration
1517 if(GetBoolParameter(BP_NUMBER_DIMENSIONS) == false && GetBoolParameter(BP_EYETRACKER_MODE) == false) {
1518 dashery = m_ymap.unmap(myint(dashery));
1519 if(dashery > DasherModel()->DasherY()) {
1520 dashery = DasherModel()->DasherY();
1522 if(dashery < 0) {
1523 dashery = 0;
1527 // The X and Y origins.
1528 myint dasherOX = DasherModel()->DasherOX();
1529 //cout << "dasherOX: " << dasherOX << endl;
1530 myint dasherOY = DasherModel()->DasherOY();
1531 // For Y co-ordinate changes.
1532 // disty is the distance between y and centreline.
1533 double disty = double (dasherOY) - dashery;
1534 //cout << "disty: " << disty << endl;
1536 // If we're in one-dimensional mode, make new x,y
1537 if(GetBoolParameter(BP_NUMBER_DIMENSIONS) == true) {
1538 //if (eyetracker==true && !(x<DasherModel()->DasherOX() && pow(pow(DasherModel()->DasherY()/2-dashery,2)+pow(x-DasherModel()->DasherOX(),2),0.5)>DasherModel()->DasherY()/2.5)) {
1539 // *mousex=int(x);
1540 // *mousey=int(dashery);
1541 // return;
1544 double disty, circlesize, yfullrange, yforwardrange, angle, ellipse_eccentricity, ybackrange, yb;
1546 // The distance between the Y coordinate and the centreline in pixels
1547 disty = dasherOY - dashery;
1549 // double rel_dashery=dashery+1726;
1550 // double rel_dasherOY=dasherOY+1726;
1551 //cout << "x: " << x << endl;
1552 //cout << "dashery: " << rel_dashery << endl << endl;
1554 // The radius of the circle transcribed by the one-dimensional mapping
1555 circlesize = DasherModel()->DasherY() / 2.5;
1556 yforwardrange = DasherModel()->DasherY() / 1.6;
1557 yfullrange = yforwardrange * 1.6;
1558 ybackrange = yfullrange - yforwardrange;
1559 ellipse_eccentricity = 6;
1561 if(disty > yforwardrange) {
1562 // If the distance between y-coord and centreline is > radius,
1563 // we should be going backwards, off the top.
1564 yb = (disty - yforwardrange) / ybackrange;
1566 if(yb > 1) {
1567 x = 0;
1568 dashery = double (dasherOY);
1570 else {
1571 angle = (yb * 3.14159) * (yb + (1 - yb) * (ybackrange / yforwardrange / ellipse_eccentricity));
1572 x = (-sin(angle) * circlesize / 2) * ellipse_eccentricity;
1573 dashery = myint(-(1 + cos(angle)) * circlesize / 2 + dasherOY);
1576 else if(disty < -(yforwardrange)) {
1577 // Backwards, off the bottom.
1578 yb = -(disty + yforwardrange) / ybackrange;
1580 if(yb > 1) {
1581 x = 0;
1582 dashery = double (dasherOY);
1584 else {
1585 angle = (yb * 3.14159) * (yb + (1 - yb) * (ybackrange / yforwardrange / ellipse_eccentricity));
1587 x = (-sin(angle) * circlesize / 2) * ellipse_eccentricity;
1588 dashery = myint((1 + cos(angle)) * circlesize / 2 + dasherOY);
1592 else {
1593 angle = ((disty * 3.14159 / 2) / yforwardrange);
1594 x = cos(angle) * circlesize;
1595 dashery = myint(-sin(angle) * circlesize + dasherOY);
1597 x = dasherOX - x;
1599 else if(eyetracker == true) {
1601 myint dasherOX = DasherModel()->DasherOX();
1602 //cout << "dasherOX: " << dasherOX << endl;
1603 myint dasherOY = DasherModel()->DasherOY();
1605 // X co-ordinate changes.
1606 double double_x = (x / dasherOX);
1607 double double_y = -((dashery - dasherOY) / (double)(dasherOY));
1609 double xmax_y = xmax(double_x, double_y);
1611 if(double_x < xmax_y) {
1612 double_x = xmax_y;
1615 x = dasherOX * double_x;
1617 // Finished x-coord changes.
1619 double repulsionparameter = 0.5;
1620 dashery = dasherOY - (1.0 + double_y * double_y * repulsionparameter) * disty;
1623 // Finish the yautocallibrate
1624 // If dasher running, adjust our tweak estimate
1625 if(!DasherRunning==true) {
1626 CDasherView::yFilterTimescale = 60;
1627 CDasherView::ySum += disty;
1628 CDasherView::ySumCounter++;
1630 CDasherView::ySigBiasPercentage=50;
1631 CDasherView::ySigBiasPixels = CDasherView::ySigBiasPercentage * DasherModel()->DasherY() / 100;
1633 // FIXME: screen2dasher appears to be being called thrice per frame.
1634 // I don't know why.
1635 CDasherView::ySigBiasPixels*=3;
1637 cout <<"ySum: " << CDasherView::ySum << " | ySigBiasPixels: " << CDasherView::ySigBiasPixels << " | disty: " << disty << " | yAutoOffset: " << CDasherView::yAutoOffset << endl;
1639 if (CDasherView::ySumCounter > CDasherView::yFilterTimescale) {
1640 CDasherView::ySumCounter = 0;
1642 // 'Conditions A', as specified by DJCM. Only make the auto-offset
1643 // change if we're past the significance boundary.
1645 if (CDasherView::ySum > CDasherView::ySigBiasPixels || CDasherView::ySum < -CDasherView::ySigBiasPixels) {
1646 if (CDasherView::ySum > CDasherView::yFilterTimescale)
1647 CDasherView::yAutoOffset--;
1648 else if (CDasherView::ySum < -CDasherView::yFilterTimescale)
1649 CDasherView::yAutoOffset++;
1650 CDasherView::ySum = 0;
1655 *idasherx = myint(x);
1657 *idashery = myint(dashery);
1660 void CDasherViewSquare::AutoCalibrate(screenint *mousex, screenint *mousey) {
1661 double dashery = double (*mousey) * double (DasherModel()->DasherY()) / double (CanvasY);
1662 myint dasherOY = DasherModel()->DasherOY();
1663 double disty = double (dasherOY) - dashery;
1664 bool DasherRunning = GetBoolParameter(BP_DASHER_PAUSED);
1666 if(!DasherRunning == true) {
1667 m_yFilterTimescale = 20;
1668 m_ySum += (int)disty;
1669 m_ySumCounter++;
1671 m_ySigBiasPercentage = 50;
1672 m_ySigBiasPixels = m_ySigBiasPercentage * DasherModel()->DasherY() / 100;
1674 //cout << "yAutoOffset: " << CDasherView::yAutoOffset << endl;
1676 if(m_ySumCounter > m_yFilterTimescale) {
1677 m_ySumCounter = 0;
1679 // 'Conditions A', as specified by DJCM. Only make the auto-offset
1680 // change if we're past the significance boundary.
1682 if(m_ySum > m_ySigBiasPixels || m_ySum < -m_ySigBiasPixels) {
1683 if(m_ySum > m_yFilterTimescale) {
1684 m_yAutoOffset--;
1686 else if(m_ySum < -m_yFilterTimescale)
1687 m_yAutoOffset++;
1689 m_ySum = 0;
1693 //*mousey=int(dashery);
1697 void CDasherViewSquare::DrawGameModePointer() {
1699 myint loc = DasherModel()->GetGameModePointerLoc();
1701 if(loc == myint(INT64_MIN))
1702 return;
1704 if(loc > DasherModel()->DasherY())
1705 DasherDrawCentredRectangle(-50, DasherModel()->DasherY(), 5, 135, Opts::ColorSchemes(Objects), false);
1707 else if(loc < 0)
1708 DasherDrawCentredRectangle(-50, 0, 5, 135, Opts::ColorSchemes(Objects), false);
1710 else
1711 DasherDrawCentredRectangle(-50, loc, 7, 135, Opts::ColorSchemes(Objects), false);
1715 bool CDasherViewSquare::HandleStartOnMouse(int iTime) {
1717 screenint mousex, mousey;
1719 int iCoordinateCount(GetCoordinateCount());
1721 myint *pCoordinates(new myint[iCoordinateCount]);
1723 int iType(GetCoordinates(iCoordinateCount, pCoordinates));
1725 if(iCoordinateCount == 1) {
1726 mousex = 0;
1727 mousey = pCoordinates[0];
1729 else {
1730 mousex = pCoordinates[0];
1731 mousey = pCoordinates[1];
1734 delete pCoordinates;
1736 // if(GetBoolParameter(BP_AUTOCALIBRATE)) {
1737 // AutoCalibrate(&mousex, &mousey);
1738 // }
1740 myint iDasherX;
1741 myint iDasherY;
1743 // Convert the input co-ordinates to dasher co-ordinates
1745 Input2Dasher(mousex, mousey, iDasherX, iDasherY, iType, DasherModel()->GetMode());
1747 screenint iNewScreenX;
1748 screenint iNewScreenY;
1750 Dasher2Screen( iDasherX, iDasherY, iNewScreenX, iNewScreenY );
1752 int iBoxMax(-1);
1753 int iBoxMin(0);
1755 if(GetLongParameter(LP_MOUSE_POS_BOX) == 1) {
1756 iBoxMax = Screen()->GetHeight() / 2 - (int)GetLongParameter(LP_MOUSEPOSDIST) + 50;
1757 iBoxMin = iBoxMax - 100;
1759 else if(GetLongParameter(LP_MOUSE_POS_BOX) == 2) {
1760 iBoxMin = Screen()->GetHeight() / 2 + (int)GetLongParameter(LP_MOUSEPOSDIST) - 50;
1761 iBoxMax = iBoxMin + 100;
1764 if((iNewScreenY >= iBoxMin) && (iNewScreenY <= iBoxMax)) {
1765 if(!bInBox) {
1766 iBoxEntered = iTime;
1768 else {
1769 if(iTime - iBoxEntered > 2000) {
1771 iBoxStart = iTime;
1773 if(GetLongParameter(LP_MOUSE_POS_BOX) == 1)
1774 SetLongParameter(LP_MOUSE_POS_BOX, 2);
1775 else if(GetLongParameter(LP_MOUSE_POS_BOX) == 2) {
1776 SetLongParameter(LP_MOUSE_POS_BOX, -1);
1777 return true;
1782 bInBox = true;
1784 else {
1785 if((GetLongParameter(LP_MOUSE_POS_BOX) == 2) && (iTime - iBoxStart > 2000))
1786 SetLongParameter(LP_MOUSE_POS_BOX, 1);
1788 bInBox = false;
1791 return false;