1 // DasherViewSquare.cpp
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"
18 #include "EventHandler.h"
19 #include "View/DelayedDraw.h"
26 using namespace Dasher
;
28 // Track memory leaks on Windows to the line that new'd the memory
31 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
34 static char THIS_FILE
[] = __FILE__
;
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
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
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
) {
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;
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);
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)
121 if(bDraw
&& !RenderNode(pRender
->Colour(), y1
, y2
, mostleft
, pRender
->m_strDisplayText
, pRender
->m_bShove
) && !(pRender
->GetGame())) {
122 vDeleteList
.push_back(pRender
);
127 if(pRender
->ChildCount() == 0) {
128 vNodeList
.push_back(pRender
);
132 if(pRender
->GetGame())
133 *iGamePointer
= (y1
+ y2
) / 2;
136 RenderGroups(pRender
, y1
, y2
, mostleft
);
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())) {
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);
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)
222 VisibleRegion(iDasherMinX
, iDasherMinY
, iDasherMaxX
, iDasherMaxY
);
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
));
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);
249 int iDasherY((myint
)GetLongParameter(LP_MAX_Y
));
251 int iSpacing(iDasherY
/ 128); // FIXME - assuming that this is an integer below
255 switch (iTruncationType
) {
257 iXStart
= iSize
- iSize
* iTruncation
/ 200;
260 iXStart
= iSize
- iSize
* iTruncation
/ 100;
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
);
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);
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
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
);
357 myint
iDasherAnchorX(iDasherSize
);
359 if( sDisplayText
.size() > 0 )
360 DasherDrawText(iDasherAnchorX
, y1
, iDasherAnchorX
, y2
, sDisplayText
, mostleft
, bShove
);
365 bool CDasherViewSquare::IsNodeVisible(myint y1
, myint y2
) {
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
;
398 int eOrientation(GetLongParameter(LP_REAL_ORIENTATION
));
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
;
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
);
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
);
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
);
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
);
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
);
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
;
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
484 iDasherX
= myint(xmap(iDasherX
/ static_cast < double >(GetLongParameter(LP_MAX_Y
))) * (myint
)GetLongParameter(LP_MAX_Y
));
485 iDasherY
= m_ymap
.map(iDasherY
);
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
) );
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
);
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
);
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
);
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
);
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);
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);
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);
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);
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
566 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION
) );
568 switch( eOrientation
) {
569 case Dasher::Opts::LeftToRight
:
570 Screen2Dasher(Screen()->GetWidth(),0,iDasherX
,iDasherY
,false,true);
572 case Dasher::Opts::RightToLeft
:
573 Screen2Dasher(0,0,iDasherX
,iDasherY
,false,true);
575 case Dasher::Opts::TopToBottom
:
576 Screen2Dasher(0,Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
578 case Dasher::Opts::BottomToTop
:
579 Screen2Dasher(0,0,iDasherX
,iDasherY
,false,true);
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
594 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION
) );
596 switch( eOrientation
) {
597 case Dasher::Opts::LeftToRight
:
598 Screen2Dasher(0,Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
600 case Dasher::Opts::RightToLeft
:
601 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
603 case Dasher::Opts::TopToBottom
:
604 Screen2Dasher(Screen()->GetWidth(),0,iDasherX
,iDasherY
,false,true);
606 case Dasher::Opts::BottomToTop
:
607 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
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
622 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION
) );
624 switch( eOrientation
) {
625 case Dasher::Opts::LeftToRight
:
626 Screen2Dasher(0,Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
628 case Dasher::Opts::RightToLeft
:
629 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
631 case Dasher::Opts::TopToBottom
:
632 Screen2Dasher(Screen()->GetWidth(),0,iDasherX
,iDasherY
,false,true);
634 case Dasher::Opts::BottomToTop
:
635 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
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)
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;
667 // Raw secreen coordinates
669 // TODO - autocalibration should be at the level of the eyetracker filter
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
676 Screen2Dasher( iInputX
, iInputY
, iDasherX
, iDasherY
, false, true );
677 else if( iMode
== 1 )
678 Screen2Dasher( iInputX
, iInputY
, iDasherX
, iDasherY
, true, false );
680 Screen2Dasher( iInputX
, iInputY
, iDasherX
, iDasherY
, false, true );
683 // Raw dasher coordinates
695 // TODO: Check that this is still doing something vaguely sensible - I think it isn't
698 if( GetLongParameter(LP_YSCALE
) > 0 ) {
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
));
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
722 if(iX
> Screen()->GetWidth())
723 iX
= Screen()->GetWidth();
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
));
748 if(iCoordinateCount
== 1) {
750 mousey
= pCoordinates
[0];
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
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
783 // iDasherX = myint(xmap(iDasherX / static_cast < double >(GetLongParameter(LP_MAX_Y))) * GetLongParameter(LP_MAX_Y));
784 // iDasherY = m_ymap.map(iDasherY);
788 void CDasherViewSquare::NewDrawGoTo(myint iDasherMin
, myint iDasherMax
, bool bActive
) {
789 myint
iHeight(iDasherMax
- iDasherMin
);
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() {
819 void CDasherViewSquare::ResetSumCounter() {
823 void CDasherViewSquare::ResetYAutoOffset() {
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
;
835 m_iScalingFactor
= 100000000;
839 int CDasherViewSquare::GetAutoOffset() const {
840 return m_yAutoOffset
;
843 void CDasherViewSquare::AutoCalibrate(screenint
*mousex
, screenint
*mousey
) {
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
;
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
) {
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
) {
879 else if(m_ySum
< -m_yFilterTimescale
)
886 //*mousey=int(dashery);
890 // TODO - should be elsewhere
892 void CDasherViewSquare::DrawGameModePointer(myint iPosition
) {
893 // TODO: Horrible code duplication here
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];
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];
933 Dasher2Screen(iDasherX
, iDasherY
, p
[0].x
, p
[0].y
);
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];
949 iDasherY
= GetLongParameter(LP_MAX_Y
);
951 Dasher2Screen(iDasherX
, iDasherY
, p
[0].x
, p
[0].y
);
954 iDasherY
= GetLongParameter(LP_MAX_Y
) - 500;
956 Dasher2Screen(iDasherX
, iDasherY
, p
[1].x
, p
[1].y
);
958 Screen()->Polyline(p
, 2, 1, 2);
961 iDasherY
= GetLongParameter(LP_MAX_Y
) - 100;
963 Dasher2Screen(iDasherX
, iDasherY
, p
[1].x
, p
[1].y
);
965 Screen()->Polyline(p
, 2, 1, 2);
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];
984 Dasher2Screen(iDasherX
, iDasherY
, p
[0].x
, p
[0].y
);
989 Dasher2Screen(iDasherX
, iDasherY
, p
[1].x
, p
[1].y
);
991 Screen()->Polyline(p
, 2, 1, 2);
996 Dasher2Screen(iDasherX
, iDasherY
, p
[1].x
, p
[1].y
);
998 Screen()->Polyline(p
, 2, 1, 2);
1003 Dasher2Screen(iDasherX
, iDasherY
, p
[1].x
, p
[1].y
);
1005 Screen()->Polyline(p
, 2, 1, 2);
1008 CDasherScreen::point p
[2];
1014 iDasherY
= iPosition
;
1016 Dasher2Screen(iDasherX
, iDasherY
, p
[0].x
, p
[0].y
);
1019 iDasherY
= iPosition
;
1021 Dasher2Screen(iDasherX
, iDasherY
, p
[1].x
, p
[1].y
);
1023 Screen()->Polyline(p
, 2, 1, 2);
1026 iDasherY
= iPosition
+ 100;
1028 Dasher2Screen(iDasherX
, iDasherY
, p
[1].x
, p
[1].y
);
1030 Screen()->Polyline(p
, 2, 1, 2);
1033 iDasherY
= iPosition
- 100;
1035 Dasher2Screen(iDasherX
, iDasherY
, p
[1].x
, p
[1].y
);
1037 Screen()->Polyline(p
, 2, 1, 2);