1 // DasherViewSquare.cpp
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"
12 #include "EventHandler.h"
13 #include "View/DelayedDraw.h"
20 using namespace Dasher
;
22 // Track memory leaks on Windows to the line that new'd the memory
25 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
28 static char THIS_FILE
[] = __FILE__
;
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)
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
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
) {
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
96 if(GetBoolParameter(BP_COLOUR_MODE
) == true) {
97 if(pRender
->Colour() != -1) {
98 Color
= pRender
->Colour();
101 if(pRender
->Symbol() == DasherModel()->GetSpaceSymbol()) {
104 else if(pRender
->Symbol() == DasherModel()->GetControlSymbol()) {
108 Color
= (pRender
->Symbol() % 3) + 10;
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);
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
)) {
131 if(!pRender
->ControlChild() && pRender
->Symbol() < DasherModel()->GetAlphabet().GetNumberTextSymbols())
132 RenderGroups(pRender
, y1
, y2
, mostleft
);
135 // std::cout << "Killing Node" << std::endl;
136 vDeleteList
.push_back(pRender
);
141 if(pRender
->ChildCount() == 0) {
142 vNodeList
.push_back(pRender
);
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())) {
160 RecursiveRender(pChild
, newy1
, newy2
, mostleft
, vNodeList
, vDeleteList
);
168 void CDasherViewSquare::RenderGroups(CDasherNode
*Render
, myint y1
, myint y2
, int mostleft
) {
169 CDasherNode::ChildMap
& Children
= Render
->Children();
170 if(Children
.size() == 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);
196 // RenderNode(0, (current % 3) + 110, Opts::Groups, newy1, newy2, mostleft, Label, true);
200 // RenderNode(0, current - 1, Opts::Groups, newy1, newy2, mostleft, Label, true);
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
;
234 RenderNode(0, pCurrentGroup
->iColour
, Opts::Groups
, newy1
, newy2
, mostleft
, Label
, true);
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
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
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;
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 !!!
289 //bitrate fractional changes for auto-speed control
297 //variance of two-centred-gaussians for adaptive radius
300 //Initialise auto-speed control
302 m_dBitrate
= double(round(GetLongParameter(LP_MAX_BITRATE
) / 100.0));
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
) {
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)
332 VisibleRegion(iDasherMinX
, iDasherMinY
, iDasherMaxX
, iDasherMaxY
);
335 Cint32 iSize
= dashery2screen(y1
, y2
, s1
, s2
);
337 // Actual height in pixels
338 Cint32 iHeight
= Cint32((Cint32
) (iSize
* CanvasY
) / (Cint32
) DasherModel()->DasherY());
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);
358 int iDasherY(DasherModel()->DasherY());
360 int iSpacing(iDasherY
/ 128); // FIXME - assuming that this is an integer below
364 switch (iTruncationType
) {
366 iXStart
= iSize
- iSize
* iTruncation
/ 200;
369 iXStart
= iSize
- iSize
* iTruncation
/ 100;
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
);
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);
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
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
);
466 myint
iDasherAnchorX(iDasherSize
);
468 std::string sDisplayText
;
470 if(displaytext
!= std::string(""))
471 sDisplayText
= displaytext
;
473 sDisplayText
= DasherModel()->GetDisplayText(Character
);
476 if( sDisplayText
.size() > 0 )
477 DasherDrawText(iDasherAnchorX
, y1
, iDasherAnchorX
, y2
, sDisplayText
, mostleft
, bShove
);
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.
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)
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
++) {
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
);
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
;
564 int eOrientation(GetLongParameter(LP_REAL_ORIENTATION
));
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
;
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
);
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
);
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
);
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
);
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
);
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
;
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
650 iDasherX
= myint(xmap(iDasherX
/ static_cast < double >(DasherModel()->DasherY())) * DasherModel()->DasherY());
651 iDasherY
= m_ymap
.map(iDasherY
);
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
) );
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
);
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
);
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
);
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
);
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);
704 case Dasher::Opts::RightToLeft
:
705 Screen2Dasher(0,0,iDasherMinX
,iDasherMinY
,false,true);
706 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherMaxX
,iDasherMaxY
,false,true);
708 case Dasher::Opts::TopToBottom
:
709 Screen2Dasher(0,Screen()->GetHeight(),iDasherMinX
,iDasherMinY
,false,true);
710 Screen2Dasher(Screen()->GetWidth(),0,iDasherMaxX
,iDasherMaxY
,false,true);
712 case Dasher::Opts::BottomToTop
:
713 Screen2Dasher(0,0,iDasherMinX
,iDasherMinY
,false,true);
714 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherMaxX
,iDasherMaxY
,false,true);
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
728 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION
) );
730 switch( eOrientation
) {
731 case Dasher::Opts::LeftToRight
:
732 Screen2Dasher(Screen()->GetWidth(),0,iDasherX
,iDasherY
,false,true);
734 case Dasher::Opts::RightToLeft
:
735 Screen2Dasher(0,0,iDasherX
,iDasherY
,false,true);
737 case Dasher::Opts::TopToBottom
:
738 Screen2Dasher(0,Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
740 case Dasher::Opts::BottomToTop
:
741 Screen2Dasher(0,0,iDasherX
,iDasherY
,false,true);
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
756 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION
) );
758 switch( eOrientation
) {
759 case Dasher::Opts::LeftToRight
:
760 Screen2Dasher(0,Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
762 case Dasher::Opts::RightToLeft
:
763 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
765 case Dasher::Opts::TopToBottom
:
766 Screen2Dasher(Screen()->GetWidth(),0,iDasherX
,iDasherY
,false,true);
768 case Dasher::Opts::BottomToTop
:
769 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
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
784 int eOrientation( GetLongParameter(LP_REAL_ORIENTATION
) );
786 switch( eOrientation
) {
787 case Dasher::Opts::LeftToRight
:
788 Screen2Dasher(0,Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
790 case Dasher::Opts::RightToLeft
:
791 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
793 case Dasher::Opts::TopToBottom
:
794 Screen2Dasher(Screen()->GetWidth(),0,iDasherX
,iDasherY
,false,true);
796 case Dasher::Opts::BottomToTop
:
797 Screen2Dasher(Screen()->GetWidth(),Screen()->GetHeight(),iDasherX
,iDasherY
,false,true);
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
;
826 iDasherY
=myint(DasherModel()->DasherOY());
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
;
841 iDasherY
=myint(DasherModel()->DasherOY());
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());
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
;
861 /// Convert raw Dasher co-ordinates to eyetracker position
863 void CDasherViewSquare::Dasher2Eyetracker(myint
&iDasherX
, myint
&iDasherY
) {
865 double disty
=DasherModel()->DasherOY()-iDasherY
;
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
) {
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
);
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)
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
924 // Raw secreen coordinates
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
931 Screen2Dasher( iInputX
, iInputY
, iDasherX
, iDasherY
, false, true );
932 else if( iMode
== 1 )
933 Screen2Dasher( iInputX
, iInputY
, iDasherX
, iDasherY
, true, false );
935 Screen2Dasher( iInputX
, iInputY
, iDasherX
, iDasherY
, false, true );
938 // Raw dasher coordinates
951 if( GetLongParameter(LP_YSCALE
) > 0 ) {
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
));
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
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();
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
);
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
);
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
);
1012 Screen()->Polyline(ScreenPoints
, n
, iWidth
, iColour
);
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
) {
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
;
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
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)
1115 else if(iLeftTimesFontSize
< DasherModel()->DasherY() * 159 / 160)
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;
1139 case (Dasher::Opts::RightToLeft
):
1140 newleft2
= iScreenAnchorX
- TextWidth
;
1141 newtop2
= iScreenAnchorY
- TextHeight
/ 2;
1142 newright2
= iScreenAnchorX
;
1143 newbottom2
= iScreenAnchorY
+ TextHeight
/ 2;
1145 case (Dasher::Opts::TopToBottom
):
1146 newleft2
= iScreenAnchorX
- TextWidth
/ 2;
1147 newtop2
= iScreenAnchorY
;
1148 newright2
= iScreenAnchorX
+ TextWidth
/ 2;
1149 newbottom2
= iScreenAnchorY
+ TextHeight
;
1151 case (Dasher::Opts::BottomToTop
):
1152 newleft2
= iScreenAnchorX
- TextWidth
/ 2;
1153 newtop2
= iScreenAnchorY
- TextHeight
;
1154 newright2
= iScreenAnchorX
+ TextWidth
/ 2;
1155 newbottom2
= iScreenAnchorY
;
1161 // Update the value of mostleft to take into account the new text
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
1190 if(iX
> Screen()->GetWidth())
1191 iX
= Screen()->GetWidth();
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
,
1204 Dasher::VECTOR_SYMBOL_PROB
* pAdded
,
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) {
1224 mousey
= pCoordinates
[0];
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
);
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
);
1254 // Cache the Dasher Co-ordinates, so we can use them later for things like drawing the mouse position
1257 iDasherX
= myint(xmap(iDasherX
/ static_cast < double >(DasherModel()->DasherY())) * DasherModel()->DasherY());
1258 iDasherY
= m_ymap
.map(iDasherY
);
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
);
1272 myint rootMin
, rootMax
;
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);
1285 DasherModel()->NewGoTo(n1
, n2
, 2);
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
);
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
);
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) {
1338 mousey
= pCoordinates
[0];
1341 mousex
= pCoordinates
[0];
1342 mousey
= pCoordinates
[1];
1345 delete[]pCoordinates
;
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);
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) {
1372 mousey
= pCoordinates
[0];
1375 mousex
= pCoordinates
[0];
1376 mousey
= pCoordinates
[1];
1379 delete[] pCoordinates
;
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);
1401 DasherPolyline(x
, y
, 2, GetLongParameter(LP_LINE_WIDTH
), -1);
1405 void CDasherViewSquare::DrawKeyboard() {
1406 CDasherScreen::point line
[2];
1410 line
[1].y
= CanvasY
/ 2;
1412 if(GetBoolParameter(BP_COLOUR_MODE
)) {
1413 Screen()->Polyline(line
, 2, 1, 6);
1416 Screen()->Polyline(line
, 2, 1);
1420 line
[0].y
= CanvasY
/ 2;
1422 line
[1].y
= CanvasY
/ 2;
1424 if(GetBoolParameter(BP_COLOUR_MODE
)) {
1425 Screen()->Polyline(line
, 2, 1, 6);
1428 Screen()->Polyline(line
, 2, 1);
1432 line
[0].y
= CanvasY
/ 2;
1434 line
[1].y
= CanvasY
;
1436 if(GetBoolParameter(BP_COLOUR_MODE
)) {
1437 Screen()->Polyline(line
, 2, 1, 6);
1440 Screen()->Polyline(line
, 2, 1);
1444 void CDasherViewSquare::ResetSum() {
1448 void CDasherViewSquare::ResetSumCounter() {
1452 void CDasherViewSquare::ResetYAutoOffset() {
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
;
1463 m_iScalingFactor
= 100000000;
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
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
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
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();
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)) {
1540 // *mousey=int(dashery);
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
;
1568 dashery
= double (dasherOY
);
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
;
1582 dashery
= double (dasherOY
);
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
);
1593 angle
= ((disty
* 3.14159 / 2) / yforwardrange
);
1594 x
= cos(angle
) * circlesize
;
1595 dashery
= myint(-sin(angle
) * circlesize
+ dasherOY
);
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
) {
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
;
1671 m_ySigBiasPercentage
= 50;
1672 m_ySigBiasPixels
= m_ySigBiasPercentage
* DasherModel()->DasherY() / 100;
1674 //cout << "yAutoOffset: " << CDasherView::yAutoOffset << endl;
1676 if(m_ySumCounter
> m_yFilterTimescale
) {
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
) {
1686 else if(m_ySum
< -m_yFilterTimescale
)
1693 //*mousey=int(dashery);
1697 void CDasherViewSquare::DrawGameModePointer() {
1699 myint loc
= DasherModel()->GetGameModePointerLoc();
1701 if(loc
== myint(INT64_MIN
))
1704 if(loc
> DasherModel()->DasherY())
1705 DasherDrawCentredRectangle(-50, DasherModel()->DasherY(), 5, 135, Opts::ColorSchemes(Objects
), false);
1708 DasherDrawCentredRectangle(-50, 0, 5, 135, Opts::ColorSchemes(Objects
), false);
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) {
1727 mousey
= pCoordinates
[0];
1730 mousex
= pCoordinates
[0];
1731 mousey
= pCoordinates
[1];
1734 delete pCoordinates
;
1736 // if(GetBoolParameter(BP_AUTOCALIBRATE)) {
1737 // AutoCalibrate(&mousex, &mousey);
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
);
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
)) {
1766 iBoxEntered
= iTime
;
1769 if(iTime
- iBoxEntered
> 2000) {
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);
1785 if((GetLongParameter(LP_MOUSE_POS_BOX
) == 2) && (iTime
- iBoxStart
> 2000))
1786 SetLongParameter(LP_MOUSE_POS_BOX
, 1);