Make it build on Windows again (don't expect it to actually work sensibly
[dasher.git] / Src / DasherCore / DasherViewSquare.cpp
blob6b45f17fcf6f6a57102e7fd0a8d03eb61e031f9f
1 // DasherViewSquare.cpp
2 //
3 // Copyright (c) 2001-2004 David Ward
5 // TODO - there's no real reason to distinguish between groups and nodes here - they're all just boxes to be rendered, with the distinction being at the level of the alphabet manager
7 #include "../Common/Common.h"
11 #include "..\Win32\Common\WinCommon.h"
13 #include "DasherViewSquare.h"
14 #include "DasherModel.h"
15 #include "DasherView.h"
16 #include "DasherTypes.h"
17 #include "Event.h"
18 #include "EventHandler.h"
19 #include "View/DelayedDraw.h"
21 #include <algorithm>
22 #include <limits>
24 #include <iostream>
26 using namespace Dasher;
28 // Track memory leaks on Windows to the line that new'd the memory
29 #ifdef _WIN32
30 #ifdef _DEBUG
31 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
32 #define new DEBUG_NEW
33 #undef THIS_FILE
34 static char THIS_FILE[] = __FILE__;
35 #endif
36 #endif
38 // FIXME - quite a lot of the code here probably should be moved to
39 // the parent class (DasherView). I think we really should make the
40 // parent class less general - we're probably not going to implement
41 // anything which uses radically different co-ordinate transforms, and
42 // we can always override if necessary.
44 // FIXME - duplicated 'mode' code throught - needs to be fixed (actually, mode related stuff, Input2Dasher etc should probably be at least partially in some other class)
46 CDasherViewSquare::CDasherViewSquare(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherScreen *DasherScreen)
47 : CDasherView(pEventHandler, pSettingsStore, DasherScreen) {
49 // TODO - AutoOffset should be part of the eyetracker input filter
50 // Make sure that the auto calibration is set to zero berfore we start
51 m_yAutoOffset = 0;
53 m_pDelayDraw = new CDelayedDraw();
54 ChangeScreen(DasherScreen);
56 // TODO - Make these parameters
57 // tweak these if you know what you are doing
58 m_dXmpa = 0.2; // these are for the x non-linearity
59 m_dXmpb = 0.5;
60 m_dXmpc = 0.9;
61 m_dXmpd = 0.5; // slow X movement when accelerating Y
63 m_ymap = Cymap((myint)GetLongParameter(LP_MAX_Y));
65 m_bVisibleRegionValid = false;
68 CDasherViewSquare::~CDasherViewSquare() {
69 if (m_pDelayDraw != NULL) {
70 delete m_pDelayDraw;
71 m_pDelayDraw = NULL;
75 void CDasherViewSquare::HandleEvent(Dasher::CEvent *pEvent) {
76 // Let the parent class do its stuff
77 CDasherView::HandleEvent(pEvent);
79 // And then interpret events for ourself
80 if(pEvent->m_iEventType == 1) {
81 Dasher::CParameterNotificationEvent * pEvt(static_cast < Dasher::CParameterNotificationEvent * >(pEvent));
82 switch (pEvt->m_iParameter) {
83 case LP_REAL_ORIENTATION:
84 m_bVisibleRegionValid = false;
85 break;
86 default:
87 break;
92 void CDasherViewSquare::RenderNodes(CDasherNode *pRoot, myint iRootMin, myint iRootMax, std::vector<CDasherNode *> &vNodeList, std::vector<CDasherNode *> &vDeleteList, myint *iGamePointer) {
93 DASHER_ASSERT(pRoot != 0);
95 Screen()->Blank();
97 myint iDasherMinX;
98 myint iDasherMinY;
99 myint iDasherMaxX;
100 myint iDasherMaxY;
101 VisibleRegion(iDasherMinX, iDasherMinY, iDasherMaxX, iDasherMaxY);
103 RecursiveRender(pRoot, iRootMin, iRootMax, iDasherMaxX, vNodeList, vDeleteList, iGamePointer, true);
105 // DelayDraw the text nodes
106 m_pDelayDraw->Draw(Screen());
108 Crosshair((myint)GetLongParameter(LP_OX)); // add crosshair
112 int CDasherViewSquare::RecursiveRender(CDasherNode *pRender, myint y1, myint y2, int mostleft, std::vector<CDasherNode *> &vNodeList, std::vector<CDasherNode *> &vDeleteList, myint *iGamePointer, bool bDraw) {
113 DASHER_ASSERT_VALIDPTR_RW(pRender);
115 // TODO: We need an overhall of the node creation/deletion logic - make sure that we only maintain the minimum number of nodes which are actually needed.
116 // This is especially true at the moment in Game mode, which feels very sluggish. Node creation also feels slower in Windows than Linux, especially
117 // if many nodes are created at once (eg untrained Hiragana)
119 ++m_iRenderCount;
121 if(bDraw && !RenderNode(pRender->Colour(), y1, y2, mostleft, pRender->m_strDisplayText, pRender->m_bShove) && !(pRender->GetGame())) {
122 vDeleteList.push_back(pRender);
123 pRender->Kill();
124 return 0;
127 if(pRender->ChildCount() == 0) {
128 vNodeList.push_back(pRender);
129 return 0;
132 if(pRender->GetGame())
133 *iGamePointer = (y1 + y2) / 2;
135 // Render groups
136 RenderGroups(pRender, y1, y2, mostleft);
138 // Render children
139 int norm = (myint)GetLongParameter(LP_NORMALIZATION);
141 CDasherNode::ChildMap::const_iterator i;
142 for(i = pRender->GetChildren().begin(); i != pRender->GetChildren().end(); i++) {
143 CDasherNode *pChild = *i;
145 myint Range = y2 - y1;
146 myint newy1 = y1 + (Range * pChild->Lbnd()) / norm;
147 myint newy2 = y1 + (Range * pChild->Hbnd()) / norm;
149 // FIXME - make the threshold a parameter
151 if((newy2 - newy1 > 50) || (pChild->Alive())) {
152 pChild->Alive(true);
153 RecursiveRender(pChild, newy1, newy2, mostleft, vNodeList, vDeleteList, iGamePointer, true);
155 else if(pRender->GetGame()) {
156 RecursiveRender(pChild, newy1, newy2, mostleft, vNodeList, vDeleteList, iGamePointer, false);
160 return 1;
163 void CDasherViewSquare::RenderGroups(CDasherNode *Render, myint y1, myint y2, int mostleft) {
164 SGroupInfo *pCurrentGroup(Render->m_pBaseGroup);
166 while(pCurrentGroup) {
167 RecursiveRenderGroups(pCurrentGroup, Render, y1, y2, mostleft);
168 pCurrentGroup = pCurrentGroup->pNext;
172 void CDasherViewSquare::RecursiveRenderGroups(SGroupInfo *pCurrentGroup, CDasherNode *pNode, myint y1, myint y2, int mostleft) {
174 if(pCurrentGroup->bVisible) {
175 myint range = y2 - y1;
177 int lower(pCurrentGroup->iStart);
178 int upper(pCurrentGroup->iEnd);
180 myint lbnd = pNode->Children()[lower]->Lbnd();
181 myint hbnd = pNode->Children()[upper - 1]->Hbnd();
183 myint newy1 = y1 + (range * lbnd) / (int)GetLongParameter(LP_NORMALIZATION);
184 myint newy2 = y1 + (range * hbnd) / (int)GetLongParameter(LP_NORMALIZATION);
186 RenderNode(pCurrentGroup->iColour, newy1, newy2, mostleft, pCurrentGroup->strLabel, true);
189 // Iterate through child groups
190 SGroupInfo *pCurrentChild(pCurrentGroup->pChild);
192 while(pCurrentChild) {
193 RecursiveRenderGroups(pCurrentChild, pNode, y1, y2, mostleft);
194 pCurrentChild = pCurrentChild->pNext;
199 CDasherViewSquare::Cymap::Cymap(myint iScale) {
200 double dY1 = 0.25; // Amount of acceleration
201 double dY2 = 0.95; // Accelerate Y movement below this point
202 double dY3 = 0.05; // Accelerate Y movement above this point
204 m_Y2 = myint(dY2 * iScale);
205 m_Y3 = myint(dY3 * iScale);
206 m_Y1 = myint(1.0 / dY1);
211 int CDasherViewSquare::RenderNode(const int Color, myint y1, myint y2, int &mostleft, const std::string &sDisplayText, bool bShove) {
213 // Commenting because click mode occasionally fails this assert.
214 // I don't know why. -- cjb.
215 if (!(y2 >= y1)) { return 1; }
217 // TODO - Get sensibel limits here (to allow for non-linearities)
218 myint iDasherMinX;
219 myint iDasherMinY;
220 myint iDasherMaxX;
221 myint iDasherMaxY;
222 VisibleRegion(iDasherMinX, iDasherMinY, iDasherMaxX, iDasherMaxY);
224 screenint s1, s2;
225 // TODO - use new versions of functions
226 Cint32 iSize = dashery2screen(y1, y2, s1, s2);
228 // Actual height in pixels
229 Cint32 iHeight = Cint32((Cint32) (iSize * CanvasY) / (Cint32) GetLongParameter(LP_MAX_Y));
231 if(iHeight <= 1)
232 return 0; // We're too small to render
234 if((y1 > iDasherMaxY) || (y2 < iDasherMinY)){
235 return 0; // We're entirely off screen, so don't render.
238 myint iDasherSize(y2 - y1);
240 // FIXME - get rid of pointless assignment below
242 int iTruncation(GetLongParameter(LP_TRUNCATION)); // Trucation farction times 100;
243 int iTruncationType(GetLongParameter(LP_TRUNCATIONTYPE));
245 if(iTruncation == 0) { // Regular squares
246 // DasherDrawRectangle(std::min(iDasherSize,iDasherMaxX), std::min(y2,iDasherMaxY), 0, std::max(y1,iDasherMinY), Color, -1, Nodes1, GetBoolParameter(BP_OUTLINE_MODE), true, 1);
248 else {
249 int iDasherY((myint)GetLongParameter(LP_MAX_Y));
251 int iSpacing(iDasherY / 128); // FIXME - assuming that this is an integer below
253 int iXStart = 0;
255 switch (iTruncationType) {
256 case 1:
257 iXStart = iSize - iSize * iTruncation / 200;
258 break;
259 case 2:
260 iXStart = iSize - iSize * iTruncation / 100;
261 break;
264 int iTipMin((y2 - y1) * iTruncation / (200) + y1);
265 int iTipMax(y2 - (y2 - y1) * iTruncation / (200));
267 int iLowerMin(((y1 + 1) / iSpacing) * iSpacing);
268 int iLowerMax(((iTipMin - 1) / iSpacing) * iSpacing);
270 int iUpperMin(((iTipMax + 1) / iSpacing) * iSpacing);
271 int iUpperMax(((y2 - 1) / iSpacing) * iSpacing);
273 if(iLowerMin < 0)
274 iLowerMin = 0;
276 if(iLowerMax < 0)
277 iLowerMax = 0;
279 if(iUpperMin < 0)
280 iUpperMin = 0;
282 if(iUpperMax < 0)
283 iUpperMax = 0;
285 if(iLowerMin > iDasherY)
286 iLowerMin = iDasherY;
288 if(iLowerMax > iDasherY)
289 iLowerMax = iDasherY;
291 if(iUpperMin > iDasherY)
292 iUpperMin = iDasherY;
294 if(iUpperMax > iDasherY)
295 iUpperMax = iDasherY;
297 while(iLowerMin < y1)
298 iLowerMin += iSpacing;
300 while(iLowerMax > iTipMin)
301 iLowerMax -= iSpacing;
303 while(iUpperMin < iTipMax)
304 iUpperMin += iSpacing;
306 while(iUpperMax > y2)
307 iUpperMax -= iSpacing;
309 int iLowerCount((iLowerMax - iLowerMin) / iSpacing + 1);
310 int iUpperCount((iUpperMax - iUpperMin) / iSpacing + 1);
312 if(iLowerCount < 0)
313 iLowerCount = 0;
315 if(iUpperCount < 0)
316 iUpperCount = 0;
318 int iTotalCount(iLowerCount + iUpperCount + 6);
320 myint *x = new myint[iTotalCount];
321 myint *y = new myint[iTotalCount];
323 // Weird duplication here is to make truncated squares possible too
325 x[0] = 0;
326 y[0] = y1;
327 x[1] = iXStart;
328 y[1] = y1;
330 x[iLowerCount + 2] = iDasherSize;
331 y[iLowerCount + 2] = iTipMin;
332 x[iLowerCount + 3] = iDasherSize;
333 y[iLowerCount + 3] = iTipMax;
335 x[iTotalCount - 2] = iXStart;
336 y[iTotalCount - 2] = y2;
337 x[iTotalCount - 1] = 0;
338 y[iTotalCount - 1] = y2;
340 for(int i(0); i < iLowerCount; ++i) {
341 x[i + 2] = (iLowerMin + i * iSpacing - y1) * (iDasherSize - iXStart) / (iTipMin - y1) + iXStart;
342 y[i + 2] = iLowerMin + i * iSpacing;
345 for(int j(0); j < iUpperCount; ++j) {
346 x[j + iLowerCount + 4] = (y2 - (iUpperMin + j * iSpacing)) * (iDasherSize - iXStart) / (y2 - iTipMax) + iXStart;
347 y[j + iLowerCount + 4] = iUpperMin + j * iSpacing;
350 DasherPolygon(x, y, iTotalCount, Color);
352 delete x;
353 delete y;
357 myint iDasherAnchorX(iDasherSize);
359 if( sDisplayText.size() > 0 )
360 DasherDrawText(iDasherAnchorX, y1, iDasherAnchorX, y2, sDisplayText, mostleft, bShove);
362 return 1;
365 bool CDasherViewSquare::IsNodeVisible(myint y1, myint y2) {
367 myint iDasherMinX;
368 myint iDasherMinY;
369 myint iDasherMaxX;
370 myint iDasherMaxY;
372 VisibleRegion(iDasherMinX, iDasherMinY, iDasherMaxX, iDasherMaxY);
374 return (y1 > iDasherMinY) || (y2 < iDasherMaxY ) || (y2-y1 < iDasherMaxX);
377 /// Convert screen co-ordinates to dasher co-ordinates. This doesn't
378 /// include the nonlinear mapping for eyetracking mode etc - it is
379 /// just the inverse of the mapping used to calculate the screen
380 /// positions of boxes etc.
382 void CDasherViewSquare::Screen2Dasher(screenint iInputX, screenint iInputY, myint &iDasherX, myint &iDasherY, bool b1D, bool bNonlinearity) {
384 // Things we're likely to need:
386 myint iDasherWidth = (myint)GetLongParameter(LP_MAX_Y);
387 myint iDasherHeight = (myint)GetLongParameter(LP_MAX_Y);
389 screenint iScreenWidth = Screen()->GetWidth();
390 screenint iScreenHeight = Screen()->GetHeight();
392 if( b1D ) { // Special case for 1D mode...
393 iDasherX = iInputX * iDasherWidth / iScreenWidth;
394 iDasherY = iInputY * iDasherHeight / iScreenHeight;
395 return;
398 int eOrientation(GetLongParameter(LP_REAL_ORIENTATION));
400 myint iScaleFactorX;
401 myint iScaleFactorY;
403 GetScaleFactor(eOrientation, &iScaleFactorX, &iScaleFactorY);
405 switch(eOrientation) {
406 case Dasher::Opts::LeftToRight:
407 iDasherX = iDasherWidth / 2 - ( iInputX - iScreenWidth / 2 ) * m_iScalingFactor / iScaleFactorX;
408 iDasherY = iDasherHeight / 2 + ( iInputY - iScreenHeight / 2 ) * m_iScalingFactor / iScaleFactorY;
409 break;
410 case Dasher::Opts::RightToLeft:
411 iDasherX = myint(iDasherWidth / 2 + ( iInputX - iScreenWidth / 2 ) * m_iScalingFactor/ iScaleFactorX);
412 iDasherY = myint(iDasherHeight / 2 + ( iInputY - iScreenHeight / 2 ) * m_iScalingFactor/ iScaleFactorY);
413 break;
414 case Dasher::Opts::TopToBottom:
415 iDasherX = myint(iDasherWidth / 2 - ( iInputY - iScreenHeight / 2 ) * m_iScalingFactor/ iScaleFactorY);
416 iDasherY = myint(iDasherHeight / 2 + ( iInputX - iScreenWidth / 2 ) * m_iScalingFactor/ iScaleFactorX);
417 break;
418 case Dasher::Opts::BottomToTop:
419 iDasherX = myint(iDasherWidth / 2 + ( iInputY - iScreenHeight / 2 ) * m_iScalingFactor/ iScaleFactorY);
420 iDasherY = myint(iDasherHeight / 2 + ( iInputX - iScreenWidth / 2 ) * m_iScalingFactor/ iScaleFactorX);
421 break;
424 #ifndef WITH_MAEMO
425 // FIXME - disabled to avoid floating point
426 if( bNonlinearity ) {
427 iDasherX = myint(ixmap(iDasherX / static_cast < double >(GetLongParameter(LP_MAX_Y))) * (myint)GetLongParameter(LP_MAX_Y));
428 iDasherY = m_ymap.unmap(iDasherY);
430 #endif
433 void CDasherViewSquare::SetScaleFactor( void )
435 myint iDasherWidth = (myint)GetLongParameter(LP_MAX_Y);
436 myint iDasherHeight = iDasherWidth;
438 screenint iScreenWidth = Screen()->GetWidth();
439 screenint iScreenHeight = Screen()->GetHeight();
441 // Try doing this a different way:
443 myint iDasherMargin( 300 ); // Make this a parameter
445 myint iMinX( 0-iDasherMargin );
446 myint iMaxX( iDasherWidth + iDasherMargin );
447 myint iMinY( 0 );
448 myint iMaxY( iDasherHeight );
450 double dLRHScaleFactor;
451 double dLRVScaleFactor;
452 double dTBHScaleFactor;
453 double dTBVScaleFactor;
455 dLRHScaleFactor = iScreenWidth / static_cast<double>( iMaxX - iMinX );
456 dLRVScaleFactor = iScreenHeight / static_cast<double>( iMaxY - iMinY );
457 dTBHScaleFactor = iScreenWidth / static_cast<double>( iMaxY - iMinY );
458 dTBVScaleFactor = iScreenHeight / static_cast<double>( iMaxX - iMinX );
460 iLRScaleFactorX = myint(std::max(std::min(dLRHScaleFactor, dLRVScaleFactor), dLRHScaleFactor / 4.0) * m_iScalingFactor);
461 iLRScaleFactorY = myint(std::max(std::min(dLRHScaleFactor, dLRVScaleFactor), dLRVScaleFactor / 4.0) * m_iScalingFactor);
462 iTBScaleFactorX = myint(std::max(std::min(dTBHScaleFactor, dTBVScaleFactor), dTBVScaleFactor / 4.0) * m_iScalingFactor);
463 iTBScaleFactorY = myint(std::max(std::min(dTBHScaleFactor, dTBVScaleFactor), dTBHScaleFactor / 4.0) * m_iScalingFactor);
466 void CDasherViewSquare::GetScaleFactor( int eOrientation, myint *iScaleFactorX, myint *iScaleFactorY ) {
467 if(( eOrientation == Dasher::Opts::LeftToRight ) || ( eOrientation == Dasher::Opts::RightToLeft )) {
468 *iScaleFactorX = iLRScaleFactorX;
469 *iScaleFactorY = iLRScaleFactorY;
470 } else {
471 *iScaleFactorX = iTBScaleFactorX;
472 *iScaleFactorY = iTBScaleFactorY;
476 /// Convert dasher co-ordinates to screen co-ordinates
478 void CDasherViewSquare::Dasher2Screen(myint iDasherX, myint iDasherY, screenint &iScreenX, screenint &iScreenY) {
480 // Apply the nonlinearities
482 #ifndef WITH_MAEMO
483 // FIXME
484 iDasherX = myint(xmap(iDasherX / static_cast < double >(GetLongParameter(LP_MAX_Y))) * (myint)GetLongParameter(LP_MAX_Y));
485 iDasherY = m_ymap.map(iDasherY);
486 #endif
488 // Things we're likely to need:
490 myint iDasherWidth = (myint)GetLongParameter(LP_MAX_Y);
491 myint iDasherHeight = (myint)GetLongParameter(LP_MAX_Y);
493 screenint iScreenWidth = Screen()->GetWidth();
494 screenint iScreenHeight = Screen()->GetHeight();
496 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION) );
498 myint iScaleFactorX;
499 myint iScaleFactorY;
501 GetScaleFactor( eOrientation, &iScaleFactorX, &iScaleFactorY);
503 switch( eOrientation ) {
504 case Dasher::Opts::LeftToRight:
505 iScreenX = screenint(iScreenWidth / 2 - ( iDasherX - iDasherWidth / 2 ) * iScaleFactorX / m_iScalingFactor);
506 iScreenY = screenint(iScreenHeight / 2 + ( iDasherY - iDasherHeight / 2 ) * iScaleFactorY / m_iScalingFactor);
507 break;
508 case Dasher::Opts::RightToLeft:
509 iScreenX = screenint(iScreenWidth / 2 + ( iDasherX - iDasherWidth / 2 ) * iScaleFactorX / m_iScalingFactor);
510 iScreenY = screenint(iScreenHeight / 2 + ( iDasherY - iDasherHeight / 2 ) * iScaleFactorY / m_iScalingFactor);
511 break;
512 case Dasher::Opts::TopToBottom:
513 iScreenX = screenint(iScreenWidth / 2 + ( iDasherY - iDasherHeight / 2 ) * iScaleFactorX / m_iScalingFactor);
514 iScreenY = screenint(iScreenHeight / 2 - ( iDasherX - iDasherWidth / 2 ) * iScaleFactorY / m_iScalingFactor);
515 break;
516 case Dasher::Opts::BottomToTop:
517 iScreenX = screenint(iScreenWidth / 2 + ( iDasherY - iDasherHeight / 2 ) * iScaleFactorX / m_iScalingFactor);
518 iScreenY = screenint(iScreenHeight / 2 + ( iDasherX - iDasherWidth / 2 ) * iScaleFactorY / m_iScalingFactor);
519 break;
523 void CDasherViewSquare::VisibleRegion( myint &iDasherMinX, myint &iDasherMinY, myint &iDasherMaxX, myint &iDasherMaxY ) {
525 if(!m_bVisibleRegionValid) {
527 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION) );
529 switch( eOrientation ) {
530 case Dasher::Opts::LeftToRight:
531 Screen2Dasher(Screen()->GetWidth(),0,m_iDasherMinX,m_iDasherMinY,false,true);
532 Screen2Dasher(0,Screen()->GetHeight(),m_iDasherMaxX,m_iDasherMaxY,false,true);
533 break;
534 case Dasher::Opts::RightToLeft:
535 Screen2Dasher(0,0,m_iDasherMinX,m_iDasherMinY,false,true);
536 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),m_iDasherMaxX,m_iDasherMaxY,false,true);
537 break;
538 case Dasher::Opts::TopToBottom:
539 Screen2Dasher(0,Screen()->GetHeight(),m_iDasherMinX,m_iDasherMinY,false,true);
540 Screen2Dasher(Screen()->GetWidth(),0,m_iDasherMaxX,m_iDasherMaxY,false,true);
541 break;
542 case Dasher::Opts::BottomToTop:
543 Screen2Dasher(0,0,m_iDasherMinX,m_iDasherMinY,false,true);
544 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),m_iDasherMaxX,m_iDasherMaxY,false,true);
545 break;
548 m_bVisibleRegionValid = true;
551 iDasherMinX = m_iDasherMinX;
552 iDasherMaxX = m_iDasherMaxX;
553 iDasherMinY = m_iDasherMinY;
554 iDasherMaxY = m_iDasherMaxY;
557 /// The minimum Dasher Y co-ordinate which will be visible
559 myint CDasherViewSquare::DasherVisibleMinY() {
561 // Todo - convert all these to a single 'get visible extent' function
563 myint iDasherX;
564 myint iDasherY;
566 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION) );
568 switch( eOrientation ) {
569 case Dasher::Opts::LeftToRight:
570 Screen2Dasher(Screen()->GetWidth(),0,iDasherX,iDasherY,false,true);
571 break;
572 case Dasher::Opts::RightToLeft:
573 Screen2Dasher(0,0,iDasherX,iDasherY,false,true);
574 break;
575 case Dasher::Opts::TopToBottom:
576 Screen2Dasher(0,Screen()->GetHeight(),iDasherX,iDasherY,false,true);
577 break;
578 case Dasher::Opts::BottomToTop:
579 Screen2Dasher(0,0,iDasherX,iDasherY,false,true);
580 break;
583 return iDasherY;
586 /// The maximum Dasher Y co-ordinate which will be visible
588 myint CDasherViewSquare::DasherVisibleMaxY() {
589 // Todo - convert all these to a single 'get visible extent' function
591 myint iDasherX;
592 myint iDasherY;
594 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION) );
596 switch( eOrientation ) {
597 case Dasher::Opts::LeftToRight:
598 Screen2Dasher(0,Screen()->GetHeight(),iDasherX,iDasherY,false,true);
599 break;
600 case Dasher::Opts::RightToLeft:
601 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX,iDasherY,false,true);
602 break;
603 case Dasher::Opts::TopToBottom:
604 Screen2Dasher(Screen()->GetWidth(),0,iDasherX,iDasherY,false,true);
605 break;
606 case Dasher::Opts::BottomToTop:
607 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX,iDasherY,false,true);
608 break;
611 return iDasherY;
614 /// The maximum Dasher X co-ordinate which will be visible
616 myint CDasherViewSquare::DasherVisibleMaxX() {
617 // Todo - convert all these to a single 'get visible extent' function
619 myint iDasherX;
620 myint iDasherY;
622 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION) );
624 switch( eOrientation ) {
625 case Dasher::Opts::LeftToRight:
626 Screen2Dasher(0,Screen()->GetHeight(),iDasherX,iDasherY,false,true);
627 break;
628 case Dasher::Opts::RightToLeft:
629 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX,iDasherY,false,true);
630 break;
631 case Dasher::Opts::TopToBottom:
632 Screen2Dasher(Screen()->GetWidth(),0,iDasherX,iDasherY,false,true);
633 break;
634 case Dasher::Opts::BottomToTop:
635 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX,iDasherY,false,true);
636 break;
639 return iDasherX;
643 /// Convert abstract 'input coordinates', which may or may not
644 /// correspond to actual screen positions, depending on the settings,
645 /// into dasher co-ordinates. Modes are:
647 /// 0 = Direct (ie mouse)
648 /// 1 = 1D
649 /// 2 = Eyetracker
651 /// This should be done once initially, then we work in Dasher
652 /// co-ordinates for everything else. Input co-ordinates will be
653 /// assumed to range over the extent of the screen.
655 /// TODO: Abstract out modes into an enum
657 void CDasherViewSquare::Input2Dasher(screenint iInputX, screenint iInputY, myint &iDasherX, myint &iDasherY, int iType, int iMode) {
659 // FIXME - need to incorporate one-button mode?
661 // First convert the supplied co-ordinates to 'linear' Dasher co-ordinates
663 // std::cout << "iType: " << iType << " iMode: " << iMode << std::endl;
665 switch (iType) {
666 case 0:
667 // Raw secreen coordinates
669 // TODO - autocalibration should be at the level of the eyetracker filter
670 if(iMode == 2) {
671 // First apply the autocalibration offset
672 iInputY += int (m_yAutoOffset); // FIXME - we need more flexible autocalibration to work with orientations other than left-to-right
675 if( iMode == 0 )
676 Screen2Dasher( iInputX, iInputY, iDasherX, iDasherY, false, true );
677 else if( iMode == 1 )
678 Screen2Dasher( iInputX, iInputY, iDasherX, iDasherY, true, false );
679 else
680 Screen2Dasher( iInputX, iInputY, iDasherX, iDasherY, false, true );
681 break;
682 case 1:
683 // Raw dasher coordinates
685 iDasherX = iInputX;
686 iDasherY = iInputY;
687 break;
688 default:
689 // ERROR
690 break;
693 // Apply y scaling
695 // TODO: Check that this is still doing something vaguely sensible - I think it isn't
697 if(iMode == 1 ) {
698 if( GetLongParameter(LP_YSCALE) > 0 ) {
700 double dYScale;
702 int eOrientation(GetLongParameter(LP_REAL_ORIENTATION));
704 if(( eOrientation == Dasher::Opts::LeftToRight ) || ( eOrientation == Dasher::Opts::RightToLeft ))
705 dYScale = Screen()->GetHeight() / static_cast<double>(GetLongParameter(LP_YSCALE));
706 else
707 dYScale = Screen()->GetWidth() / static_cast<double>(GetLongParameter(LP_YSCALE));
709 iDasherY = myint((iDasherY - (myint)GetLongParameter(LP_MAX_Y)/2) * dYScale + (myint)GetLongParameter(LP_MAX_Y)/2);
714 /// Truncate a set of co-ordinates so that they are on the screen
716 void CDasherViewSquare::TruncateToScreen(screenint &iX, screenint &iY) {
718 // I think that this function is now obsolete
720 if(iX < 0)
721 iX = 0;
722 if(iX > Screen()->GetWidth())
723 iX = Screen()->GetWidth();
725 if(iY < 0)
726 iY = 0;
727 if(iY > Screen()->GetHeight())
728 iY = Screen()->GetHeight();
731 void CDasherViewSquare::GetCoordinates(unsigned long Time, myint &iDasherX, myint &iDasherY) {
733 // FIXME - Actually turn autocalibration on and off!
734 // FIXME - AutoCalibrate should use Dasher co-ordinates, not raw mouse co-ordinates?
735 // FIXME - Have I broken this by moving it before the offset is applied?
736 // FIXME - put ymap stuff back in
737 // FIXME - optimise this
739 int iCoordinateCount(GetCoordinateCount());
741 myint *pCoordinates(new myint[iCoordinateCount]);
743 int iType(GetInputCoordinates(iCoordinateCount, pCoordinates));
745 screenint mousex;
746 screenint mousey;
748 if(iCoordinateCount == 1) {
749 mousex = 0;
750 mousey = pCoordinates[0];
752 else {
753 mousex = pCoordinates[0];
754 mousey = pCoordinates[1];
757 delete[]pCoordinates;
759 // bool autocalibrate = GetBoolParameter(BP_AUTOCALIBRATE);
760 if(GetBoolParameter(BP_AUTOCALIBRATE) && (GetStringParameter(SP_INPUT_FILTER) == "Eyetracker Mode")) {
761 AutoCalibrate(&mousex, &mousey);
765 // TODO: Mode probably isn't being used any more
767 // Convert the input co-ordinates to dasher co-ordinates
769 int mode;
771 mode = 0;
773 Input2Dasher(mousex, mousey, iDasherX, iDasherY, iType, mode);
774 m_iDasherXCache = iDasherX;
775 m_iDasherYCache = iDasherY;
777 // Request an update at the calculated co-ordinates
780 // Cache the Dasher Co-ordinates, so we can use them later for things like drawing the mouse position
781 #ifndef WITH_MAEMO
782 // FIXME
783 // iDasherX = myint(xmap(iDasherX / static_cast < double >(GetLongParameter(LP_MAX_Y))) * GetLongParameter(LP_MAX_Y));
784 // iDasherY = m_ymap.map(iDasherY);
785 #endif
788 void CDasherViewSquare::NewDrawGoTo(myint iDasherMin, myint iDasherMax, bool bActive) {
789 myint iHeight(iDasherMax - iDasherMin);
791 int iColour;
792 int iWidth;
794 if(bActive) {
795 iColour = 1;
796 iWidth = 3;
798 else {
799 iColour = 2;
800 iWidth = 1;
803 CDasherScreen::point p[4];
805 Dasher2Screen( 0, iDasherMin, p[0].x, p[0].y);
806 Dasher2Screen( iHeight, iDasherMin, p[1].x, p[1].y);
807 Dasher2Screen( iHeight, iDasherMax, p[2].x, p[2].y);
808 Dasher2Screen( 0, iDasherMax, p[3].x, p[3].y);
810 Screen()->Polyline(p, 4, iWidth, iColour);
813 // TODO: Autocalibration should be in the eyetracker filter class
815 void CDasherViewSquare::ResetSum() {
816 m_ySum = 0;
819 void CDasherViewSquare::ResetSumCounter() {
820 m_ySumCounter = 0;
823 void CDasherViewSquare::ResetYAutoOffset() {
824 m_yAutoOffset = 0;
827 void CDasherViewSquare::ChangeScreen(CDasherScreen *NewScreen) {
828 CDasherView::ChangeScreen(NewScreen);
829 m_bVisibleRegionValid = false;
830 screenint Width = Screen()->GetWidth();
831 screenint Height = Screen()->GetHeight();
832 CanvasX = 9 * Width / 10;
833 CanvasBorder = Width - CanvasX;
834 CanvasY = Height;
835 m_iScalingFactor = 100000000;
836 SetScaleFactor();
839 int CDasherViewSquare::GetAutoOffset() const {
840 return m_yAutoOffset;
843 void CDasherViewSquare::AutoCalibrate(screenint *mousex, screenint *mousey) {
844 return;
845 // Y value in dasher coordinates
846 double dashery = double (*mousey) * double ((myint)GetLongParameter(LP_MAX_Y)) / double (CanvasY);
848 // Origin in dasher coordinates
849 myint dasherOY = (myint)GetLongParameter(LP_OY);
851 // Distance above origin in dasher coordinates
852 double disty = double (dasherOY) - dashery;
854 // Whether we're paused or not (sensible choice of name!)
855 bool DasherRunning = GetBoolParameter(BP_DASHER_PAUSED);
857 if(!DasherRunning == true) {
858 // Only update every this number of timesteps
859 m_yFilterTimescale = 20;
860 m_ySum += (int)disty;
861 m_ySumCounter++;
863 m_ySigBiasPercentage = 50;
865 // Despite the name, this is actually measured in dasher coordinates
866 m_ySigBiasPixels = m_ySigBiasPercentage * (myint)GetLongParameter(LP_MAX_Y) / 100;
869 if(m_ySumCounter > m_yFilterTimescale) {
870 m_ySumCounter = 0;
872 // 'Conditions A', as specified by DJCM. Only make the auto-offset
873 // change if we're past the significance boundary.
875 if(m_ySum > m_ySigBiasPixels || m_ySum < -m_ySigBiasPixels) {
876 if(m_ySum > m_yFilterTimescale) {
877 m_yAutoOffset--;
879 else if(m_ySum < -m_yFilterTimescale)
880 m_yAutoOffset++;
882 m_ySum = 0;
886 //*mousey=int(dashery);
890 // TODO - should be elsewhere
892 void CDasherViewSquare::DrawGameModePointer(myint iPosition) {
893 // TODO: Horrible code duplication here
896 // Circular version:
898 double dOffset = (iPosition - 2048) / 2048.0;
900 double dCos = (1 - pow(dOffset,2.0)) / (1 + pow(dOffset,2.0));
901 double dSin = 2 * dOffset / (1 + pow(dOffset,2.0));
904 CDasherScreen::point p[2];
906 myint iDasherX;
907 myint iDasherY;
909 iDasherX = 2048;
910 iDasherY = 2048;
911 Dasher2Screen(iDasherX, iDasherY, p[0].x, p[0].y);
913 iDasherX = static_cast<int>(2048 - 500 * dCos);
914 iDasherY = static_cast<int>(2048 + 500 * dSin);
915 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
917 Screen()->Polyline(p, 2, 4, 136);
920 int iOtherPosition = static_cast<int>((1 - (1 / dOffset)) * 2048);
922 int iRadius = abs((int)(iPosition - iOtherPosition)) / 2;
923 int iCentre = (iPosition + iOtherPosition) / 2;
926 CDasherScreen::point p[2];
928 myint iDasherX;
929 myint iDasherY;
931 iDasherX = 0;
932 iDasherY = iCentre;
933 Dasher2Screen(iDasherX, iDasherY, p[0].x, p[0].y);
935 iDasherX = 0;
936 iDasherY = iCentre + iRadius;
937 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
939 Screen()->DrawCircle(p[0].x, p[0].y, abs(p[1].y - p[0].y), 136, 0, 1, false);
942 if(iPosition > (myint)GetLongParameter(LP_MAX_Y)) {
943 CDasherScreen::point p[2];
945 myint iDasherX;
946 myint iDasherY;
948 iDasherX = -400;
949 iDasherY = GetLongParameter(LP_MAX_Y);
951 Dasher2Screen(iDasherX, iDasherY, p[0].x, p[0].y);
953 iDasherX = -400;
954 iDasherY = GetLongParameter(LP_MAX_Y) - 500;
956 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
958 Screen()->Polyline(p, 2, 1, 2);
960 iDasherX = -300;
961 iDasherY = GetLongParameter(LP_MAX_Y) - 100;
963 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
965 Screen()->Polyline(p, 2, 1, 2);
967 iDasherX = -500;
968 iDasherY = GetLongParameter(LP_MAX_Y) - 100;
970 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
972 Screen()->Polyline(p, 2, 1, 2);
975 else if(iPosition < 0) {
976 CDasherScreen::point p[2];
978 myint iDasherX;
979 myint iDasherY;
981 iDasherX = -400;
982 iDasherY = 0;
984 Dasher2Screen(iDasherX, iDasherY, p[0].x, p[0].y);
986 iDasherX = -400;
987 iDasherY = 500;
989 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
991 Screen()->Polyline(p, 2, 1, 2);
993 iDasherX = -300;
994 iDasherY = 100;
996 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
998 Screen()->Polyline(p, 2, 1, 2);
1000 iDasherX = -500;
1001 iDasherY = 100;
1003 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
1005 Screen()->Polyline(p, 2, 1, 2);
1007 else {
1008 CDasherScreen::point p[2];
1010 myint iDasherX;
1011 myint iDasherY;
1013 iDasherX = -200;
1014 iDasherY = iPosition;
1016 Dasher2Screen(iDasherX, iDasherY, p[0].x, p[0].y);
1018 iDasherX = -1000;
1019 iDasherY = iPosition;
1021 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
1023 Screen()->Polyline(p, 2, 1, 2);
1025 iDasherX = -300;
1026 iDasherY = iPosition + 100;
1028 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
1030 Screen()->Polyline(p, 2, 1, 2);
1032 iDasherX = -300;
1033 iDasherY = iPosition - 100;
1035 Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
1037 Screen()->Polyline(p, 2, 1, 2);