0030853: Visualization, AIS_ViewController - fix 1 pixel Y shift while zooming
[occt.git] / src / AIS / AIS_ViewController.cxx
1 // Copyright (c) 2016-2019 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #include "AIS_ViewController.hxx"
15
16 #include <AIS_AnimationCamera.hxx>
17 #include <AIS_InteractiveContext.hxx>
18 #include <AIS_Manipulator.hxx>
19 #include <AIS_Point.hxx>
20 #include <AIS_RubberBand.hxx>
21 #include <Aspect_Grid.hxx>
22 #include <Geom_CartesianPoint.hxx>
23 #include <Message.hxx>
24 #include <Message_Messenger.hxx>
25 #include <gp_Quaternion.hxx>
26 #include <V3d_View.hxx>
27
28 // =======================================================================
29 // function : AIS_ViewController
30 // purpose  :
31 // =======================================================================
32 AIS_ViewController::AIS_ViewController()
33 : myLastEventsTime    (0.0),
34   myToAskNextFrame    (false),
35   myMinCamDistance    (1.0),
36   myRotationMode      (AIS_RotationMode_BndBoxActive),
37   myNavigationMode    (AIS_NavigationMode_Orbit),
38   myMouseAccel           (1.0f),
39   myOrbitAccel           (1.0f),
40   myToShowPanAnchorPoint (true),
41   myToShowRotateCenter   (true),
42   myToLockOrbitZUp       (false),
43   myToInvertPitch        (false),
44   myToAllowTouchZRotation(false),
45   myToAllowRotation      (true),
46   myToAllowPanning       (true),
47   myToAllowZooming       (true),
48   myToAllowZFocus        (true),
49   myToAllowHighlight     (true),
50   myToAllowDragging      (true),
51   myToStickToRayOnZoom   (true),
52   myToStickToRayOnRotation (true),
53   //
54   myWalkSpeedAbsolute (1.5f),
55   myWalkSpeedRelative (0.1f),
56   myThrustSpeed (0.0f),
57   myHasThrust (false),
58   //
59   myViewAnimation (new AIS_AnimationCamera ("AIS_ViewController_ViewAnimation", Handle(V3d_View)())),
60   myPrevMoveTo (-1, -1),
61   myHasHlrOnBeforeRotation (false),
62   //
63   myMouseClickThreshold (3.0),
64   myMouseDoubleClickInt (0.4),
65   myScrollZoomRatio     (15.0f),
66   myMouseActiveGesture  (AIS_MouseGesture_NONE),
67   myMouseActiveIdleRotation (false),
68   myMouseClickCounter   (0),
69   myMousePressed        (Aspect_VKeyMouse_NONE),
70   myMouseModifiers      (Aspect_VKeyFlags_NONE),
71   myMouseSingleButton   (-1),
72   //
73   myTouchToleranceScale      (1.0f),
74   myTouchRotationThresholdPx (6.0f),
75   myTouchZRotationThreshold  (float(2.0 * M_PI / 180.0)),
76   myTouchPanThresholdPx      (4.0f),
77   myTouchZoomThresholdPx     (6.0f),
78   myTouchZoomRatio           (0.13f),
79   //
80   myNbTouchesLast (0),
81   myUpdateStartPointPan  (true),
82   myUpdateStartPointRot  (true),
83   myUpdateStartPointZRot (true),
84   //
85   myPanPnt3d (Precision::Infinite(), 0.0, 0.0)
86 {
87   myEventTimer.Start();
88
89   myAnchorPointPrs1 = new AIS_Point (new Geom_CartesianPoint (0.0, 0.0, 0.0));
90   myAnchorPointPrs1->SetZLayer (Graphic3d_ZLayerId_Top);
91   myAnchorPointPrs1->SetMutable (true);
92
93   myAnchorPointPrs2 = new AIS_Point (new Geom_CartesianPoint (0.0, 0.0, 0.0));
94   myAnchorPointPrs2->SetZLayer (Graphic3d_ZLayerId_Topmost);
95   myAnchorPointPrs2->SetMutable (true);
96
97   myRubberBand = new AIS_RubberBand (Quantity_NOC_LIGHTBLUE, Aspect_TOL_SOLID, Quantity_NOC_LIGHTBLUE, 0.4, 1.0);
98   myRubberBand->SetZLayer (Graphic3d_ZLayerId_TopOSD);
99   myRubberBand->SetTransformPersistence (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_LEFT_UPPER));
100   myRubberBand->SetDisplayMode (0);
101   myRubberBand->SetMutable (true);
102
103   myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton,                           AIS_MouseGesture_RotateOrbit);
104   myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton | Aspect_VKeyFlags_CTRL,   AIS_MouseGesture_Zoom);
105   myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton | Aspect_VKeyFlags_SHIFT,  AIS_MouseGesture_Pan);
106   myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton | Aspect_VKeyFlags_ALT,    AIS_MouseGesture_SelectRectangle);
107
108   myMouseGestureMap.Bind (Aspect_VKeyMouse_RightButton,                          AIS_MouseGesture_Zoom);
109   myMouseGestureMap.Bind (Aspect_VKeyMouse_RightButton | Aspect_VKeyFlags_CTRL,  AIS_MouseGesture_RotateOrbit);
110
111   myMouseGestureMap.Bind (Aspect_VKeyMouse_MiddleButton,                         AIS_MouseGesture_Pan);
112   myMouseGestureMap.Bind (Aspect_VKeyMouse_MiddleButton | Aspect_VKeyFlags_CTRL, AIS_MouseGesture_Pan);
113 }
114
115 // =======================================================================
116 // function : ResetViewInput
117 // purpose  :
118 // =======================================================================
119 void AIS_ViewController::ResetViewInput()
120 {
121   myKeys.Reset();
122   myMousePressed      = Aspect_VKeyMouse_NONE;
123   myMouseModifiers    = Aspect_VKeyFlags_NONE;
124   myMouseSingleButton = -1;
125   myUI.Dragging.ToAbort = true;
126   myMouseActiveGesture = AIS_MouseGesture_NONE;
127   myMouseClickTimer.Stop();
128   myMouseClickCounter = 0;
129 }
130
131 // =======================================================================
132 // function : FlushViewEvents
133 // purpose  :
134 // =======================================================================
135 void AIS_ViewController::FlushViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
136                                           const Handle(V3d_View)& theView,
137                                           Standard_Boolean theToHandle)
138 {
139   flushBuffers (theCtx, theView);
140   flushGestures(theCtx, theView);
141   if (theToHandle)
142   {
143     HandleViewEvents (theCtx, theView);
144   }
145 }
146
147 // =======================================================================
148 // function : flushBuffers
149 // purpose  :
150 // =======================================================================
151 void AIS_ViewController::flushBuffers (const Handle(AIS_InteractiveContext)& ,
152                                        const Handle(V3d_View)& )
153 {
154   myToAskNextFrame = false;
155
156   myGL.IsNewGesture = myUI.IsNewGesture;
157   myUI.IsNewGesture = false;
158
159   myGL.ZoomActions.Clear();
160   myGL.ZoomActions.Append (myUI.ZoomActions);
161   myUI.ZoomActions.Clear();
162
163   myGL.Orientation.ToFitAll = myUI.Orientation.ToFitAll;
164   myUI.Orientation.ToFitAll = false;
165   if (myUI.Orientation.ToSetViewOrient)
166   {
167     myUI.Orientation.ToSetViewOrient = false;
168     myGL.Orientation.ToSetViewOrient = true;
169     myGL.Orientation.ViewOrient      = myUI.Orientation.ViewOrient;
170   }
171
172   if (myUI.MoveTo.ToHilight)
173   {
174     myUI.MoveTo.ToHilight = false;
175     myGL.MoveTo.ToHilight = true;
176     myGL.MoveTo.Point     = myUI.MoveTo.Point;
177   }
178
179   {
180     myGL.Selection.Tool   = myUI.Selection.Tool;
181     myGL.Selection.IsXOR  = myUI.Selection.IsXOR;
182     myGL.Selection.Points = myUI.Selection.Points;
183     myUI.Selection.IsXOR  = false;
184     if (myUI.Selection.Tool == AIS_ViewSelectionTool_Picking)
185     {
186       myUI.Selection.Points.Clear();
187     }
188   }
189
190   if (myUI.Selection.ToApplyTool)
191   {
192     myGL.Selection.ToApplyTool = true;
193     myUI.Selection.ToApplyTool = false;
194     myUI.Selection.Points.Clear();
195   }
196
197   if (myUI.Panning.ToStart)
198   {
199     myUI.Panning.ToStart = false;
200     myGL.Panning.ToStart = true;
201     myGL.Panning.PointStart = myUI.Panning.PointStart;
202   }
203
204   if (myUI.Panning.ToPan)
205   {
206     myUI.Panning.ToPan = false;
207     myGL.Panning.ToPan = true;
208     myGL.Panning.Delta = myUI.Panning.Delta;
209   }
210
211   if (myUI.Dragging.ToAbort)
212   {
213     myUI.Dragging.ToAbort = false;
214     myGL.Dragging.ToAbort = true;
215   }
216   else if (myUI.Dragging.ToStop)
217   {
218     myUI.Dragging.ToStop = false;
219     myGL.Dragging.ToStop = true;
220   }
221   else if (myUI.Dragging.ToStart)
222   {
223     myUI.Dragging.ToStart = false;
224     myGL.Dragging.ToStart = true;
225     myGL.Dragging.PointStart = myUI.Dragging.PointStart;
226   }
227   myGL.Dragging.PointTo = myUI.Dragging.PointTo;
228
229   if (myUI.OrbitRotation.ToStart)
230   {
231     myUI.OrbitRotation.ToStart    = false;
232     myGL.OrbitRotation.ToStart    = true;
233     myGL.OrbitRotation.PointStart = myUI.OrbitRotation.PointStart;
234   }
235
236   if (myUI.OrbitRotation.ToRotate)
237   {
238     myUI.OrbitRotation.ToRotate = false;
239     myGL.OrbitRotation.ToRotate = true;
240     myGL.OrbitRotation.PointTo  = myUI.OrbitRotation.PointTo;
241   }
242
243   if (myUI.ViewRotation.ToStart)
244   {
245     myUI.ViewRotation.ToStart    = false;
246     myGL.ViewRotation.ToStart    = true;
247     myGL.ViewRotation.PointStart = myUI.ViewRotation.PointStart;
248   }
249
250   if (myUI.ViewRotation.ToRotate)
251   {
252     myUI.ViewRotation.ToRotate = false;
253     myGL.ViewRotation.ToRotate = true;
254     myGL.ViewRotation.PointTo  = myUI.ViewRotation.PointTo;
255   }
256
257   if (myUI.ZRotate.ToRotate)
258   {
259     myGL.ZRotate = myUI.ZRotate;
260     myUI.ZRotate.ToRotate = false;
261   }
262 }
263
264 // =======================================================================
265 // function : flushGestures
266 // purpose  :
267 // =======================================================================
268 void AIS_ViewController::flushGestures (const Handle(AIS_InteractiveContext)& ,
269                                         const Handle(V3d_View)& theView)
270 {
271   const Standard_Real    aTolScale = myTouchToleranceScale;
272   const Standard_Integer aTouchNb  = myTouchPoints.Extent();
273   if (myNbTouchesLast != aTouchNb)
274   {
275     myNbTouchesLast = aTouchNb;
276     myGL.IsNewGesture = true;
277   }
278   if (aTouchNb == 1) // touch
279   {
280     Aspect_Touch& aTouch = myTouchPoints.ChangeFromIndex (1);
281     if (myUpdateStartPointRot)
282     {
283       // skip rotation if have active dragged object
284       if (myNavigationMode == AIS_NavigationMode_Orbit)
285       {
286         myGL.OrbitRotation.ToStart = true;
287         myGL.OrbitRotation.PointStart = myStartRotCoord;
288       }
289       else
290       {
291         myGL.ViewRotation.ToStart = true;
292         myGL.ViewRotation.PointStart = myStartRotCoord;
293       }
294
295       myUpdateStartPointRot = false;
296       theView->Invalidate();
297     }
298
299     // rotation
300     const Standard_Real aRotTouchTol = !aTouch.IsPreciseDevice
301                                      ? aTolScale * myTouchRotationThresholdPx
302                                      : gp::Resolution();
303     if (Abs (aTouch.Delta().x()) + Abs(aTouch.Delta().y()) > aRotTouchTol)
304     {
305       const Standard_Real aRotAccel = myNavigationMode == AIS_NavigationMode_FirstPersonWalk ? myMouseAccel : myOrbitAccel;
306       if (myNavigationMode == AIS_NavigationMode_Orbit)
307       {
308         const Graphic3d_Vec2d aRotDelta = aTouch.To - myGL.OrbitRotation.PointStart;
309         myGL.OrbitRotation.ToRotate = true;
310         myGL.OrbitRotation.PointTo  = myGL.OrbitRotation.PointStart + aRotDelta * aRotAccel;
311         myGL.Dragging.PointTo.SetValues ((int )aTouch.To.x(), (int )aTouch.To.y());
312       }
313       else
314       {
315         const Graphic3d_Vec2d aRotDelta = aTouch.To - myGL.ViewRotation.PointStart;
316         myGL.ViewRotation.ToRotate = true;
317         myGL.ViewRotation.PointTo = myGL.ViewRotation.PointStart + aRotDelta * aRotAccel;
318         myGL.Dragging.PointTo.SetValues ((int )aTouch.To.x(), (int )aTouch.To.y());
319       }
320
321       aTouch.From = aTouch.To;
322     }
323   }
324   else if (aTouchNb == 2) // pinch
325   {
326     Aspect_Touch& aFirstTouch = myTouchPoints.ChangeFromIndex (1);
327     Aspect_Touch& aLastTouch  = myTouchPoints.ChangeFromIndex (2);
328     const Graphic3d_Vec2d aFrom[2] = { aFirstTouch.From, aLastTouch.From };
329     const Graphic3d_Vec2d aTo[2]   = { aFirstTouch.To,   aLastTouch.To   };
330
331     Graphic3d_Vec2d aPinchCenterStart ((aFrom[0].x() + aFrom[1].x()) / 2.0,
332                                        (aFrom[0].y() + aFrom[1].y()) / 2.0);
333
334     Standard_Real aPinchCenterXEnd = (aTo[0].x() + aTo[1].x()) / 2.0;
335     Standard_Real aPinchCenterYEnd = (aTo[0].y() + aTo[1].y()) / 2.0;
336
337     Standard_Real aPinchCenterXDev = aPinchCenterXEnd - aPinchCenterStart.x();
338     Standard_Real aPinchCenterYDev = aPinchCenterYEnd - aPinchCenterStart.y();
339
340     Standard_Real aStartSize = (aFrom[0] - aFrom[1]).Modulus();
341     Standard_Real anEndSize  = (  aTo[0] -   aTo[1]).Modulus();
342
343     Standard_Real aDeltaSize = anEndSize - aStartSize;
344
345     bool anIsClearDev = false;
346
347     if (myToAllowTouchZRotation)
348     {
349       Standard_Real A1 = aFrom[0].y() - aFrom[1].y();
350       Standard_Real B1 = aFrom[1].x() - aFrom[0].x();
351
352       Standard_Real A2 = aTo[0].y() - aTo[1].y();
353       Standard_Real B2 = aTo[1].x() - aTo[0].x();
354
355       Standard_Real aRotAngle = 0.0;
356
357       Standard_Real aDenomenator = A1*A2 + B1*B2;
358       if (aDenomenator <= Precision::Confusion())
359       {
360         aRotAngle = 0.0;
361       }
362       else
363       {
364         Standard_Real aNumerator = A1*B2 - A2*B1;
365         aRotAngle = ATan (aNumerator / aDenomenator);
366       }
367
368       if (Abs(aRotAngle) > Standard_Real(myTouchZRotationThreshold))
369       {
370         myGL.ZRotate.ToRotate = true;
371         myGL.ZRotate.Angle = aRotAngle;
372         anIsClearDev = true;
373       }
374     }
375
376     if (Abs(aDeltaSize) > aTolScale * myTouchZoomThresholdPx)
377     {
378       // zoom
379       aDeltaSize *= Standard_Real(myTouchZoomRatio);
380       Aspect_ScrollDelta aParams (Graphic3d_Vec2i (aPinchCenterStart), aDeltaSize);
381       myGL.ZoomActions.Append (aParams);
382       anIsClearDev = true;
383     }
384
385     const Standard_Real aPanTouchTol = !aFirstTouch.IsPreciseDevice
386                                      ? aTolScale * myTouchPanThresholdPx
387                                      : gp::Resolution();
388     if (Abs(aPinchCenterXDev) + Abs(aPinchCenterYDev) > aPanTouchTol)
389     {
390       // pan
391       if (myUpdateStartPointPan)
392       {
393         myGL.Panning.ToStart = true;
394         myGL.Panning.PointStart = Graphic3d_Vec2i (myStartPanCoord);
395         myUpdateStartPointPan = false;
396         theView->Invalidate();
397       }
398
399       myGL.Panning.ToPan = true;
400       myGL.Panning.Delta.x() = int( aPinchCenterXDev);
401       myGL.Panning.Delta.y() = int(-aPinchCenterYDev);
402       anIsClearDev = true;
403     }
404
405     if (anIsClearDev)
406     {
407       aFirstTouch.From = aFirstTouch.To;
408       aLastTouch .From = aLastTouch.To;
409     }
410   }
411 }
412
413 // =======================================================================
414 // function : UpdateViewOrientation
415 // purpose  :
416 // =======================================================================
417 void AIS_ViewController::UpdateViewOrientation (V3d_TypeOfOrientation theOrientation,
418                                                 bool theToFitAll)
419 {
420   myUI.Orientation.ToFitAll = theToFitAll;
421   myUI.Orientation.ToSetViewOrient = true;
422   myUI.Orientation.ViewOrient = theOrientation;
423 }
424
425 // =======================================================================
426 // function : SelectInViewer
427 // purpose  :
428 // =======================================================================
429 void AIS_ViewController::SelectInViewer (const Graphic3d_Vec2i& thePnt,
430                                          const bool theIsXOR)
431 {
432   if (myUI.Selection.Tool != AIS_ViewSelectionTool_Picking)
433   {
434     myUI.Selection.Tool = AIS_ViewSelectionTool_Picking;
435     myUI.Selection.Points.Clear();
436   }
437
438   myUI.Selection.IsXOR = theIsXOR;
439   myUI.Selection.Points.Append (thePnt);
440 }
441
442 // =======================================================================
443 // function : SelectInViewer
444 // purpose  :
445 // =======================================================================
446 void AIS_ViewController::SelectInViewer (const NCollection_Sequence<Graphic3d_Vec2i>& thePnts,
447                                          const bool theIsXOR)
448 {
449   myUI.Selection.IsXOR = theIsXOR;
450   myUI.Selection.Points = thePnts;
451   myUI.Selection.ToApplyTool = true;
452   if (thePnts.Length() == 1)
453   {
454     myUI.Selection.Tool = AIS_ViewSelectionTool_Picking;
455   }
456   else if (thePnts.Length() == 2)
457   {
458     myUI.Selection.Tool = AIS_ViewSelectionTool_RubberBand;
459   }
460   else
461   {
462     myUI.Selection.Tool = AIS_ViewSelectionTool_Polygon;
463   }
464 }
465
466 // =======================================================================
467 // function : UpdateRubberBand
468 // purpose  :
469 // =======================================================================
470 void AIS_ViewController::UpdateRubberBand (const Graphic3d_Vec2i& thePntFrom,
471                                            const Graphic3d_Vec2i& thePntTo,
472                                            const bool theIsXOR)
473 {
474   myUI.Selection.Tool = AIS_ViewSelectionTool_RubberBand;
475   myUI.Selection.IsXOR = theIsXOR;
476   myUI.Selection.Points.Clear();
477   myUI.Selection.Points.Append (thePntFrom);
478   myUI.Selection.Points.Append (thePntTo);
479 }
480
481 // =======================================================================
482 // function : UpdatePolySelection
483 // purpose  :
484 // =======================================================================
485 void AIS_ViewController::UpdatePolySelection (const Graphic3d_Vec2i& thePnt,
486                                               bool theToAppend)
487 {
488   if (myUI.Selection.Tool != AIS_ViewSelectionTool_Polygon)
489   {
490     myUI.Selection.Tool = AIS_ViewSelectionTool_Polygon;
491     myUI.Selection.Points.Clear();
492   }
493
494   if (myUI.Selection.Points.IsEmpty())
495   {
496     myUI.Selection.Points.Append (thePnt);
497   }
498   else if (theToAppend
499         && myUI.Selection.Points.Last() != thePnt)
500   {
501     myUI.Selection.Points.Append (thePnt);
502   }
503   else
504   {
505     myUI.Selection.Points.ChangeLast() = thePnt;
506   }
507 }
508
509 // =======================================================================
510 // function : UpdateZoom
511 // purpose  :
512 // =======================================================================
513 bool AIS_ViewController::UpdateZoom (const Aspect_ScrollDelta& theDelta)
514 {
515   if (!myUI.ZoomActions.IsEmpty())
516   {
517     if (myUI.ZoomActions.ChangeLast().Point == theDelta.Point)
518     {
519       myUI.ZoomActions.ChangeLast().Delta += theDelta.Delta;
520       return false;
521     }
522   }
523
524   myUI.ZoomActions.Append (theDelta);
525   return true;
526 }
527
528 // =======================================================================
529 // function : UpdateZRotation
530 // purpose  :
531 // =======================================================================
532 bool AIS_ViewController::UpdateZRotation (double theAngle)
533 {
534   if (!ToAllowTouchZRotation())
535   {
536     return false;
537   }
538
539   myUI.ZRotate.Angle = myUI.ZRotate.ToRotate
540                      ? myUI.ZRotate.Angle + theAngle
541                      : theAngle;
542   if (myUI.ZRotate.ToRotate)
543   {
544     return false;
545   }
546   myUI.ZRotate.ToRotate = true;
547   return true;
548 }
549
550 // =======================================================================
551 // function : UpdateMouseScroll
552 // purpose  :
553 // =======================================================================
554 bool AIS_ViewController::UpdateMouseScroll (const Aspect_ScrollDelta& theDelta)
555 {
556   Aspect_ScrollDelta aDelta = theDelta;
557   aDelta.Delta *= myScrollZoomRatio;
558   return UpdateZoom (aDelta);
559 }
560
561 // =======================================================================
562 // function : UpdateMouseClick
563 // purpose  :
564 // =======================================================================
565 bool AIS_ViewController::UpdateMouseClick (const Graphic3d_Vec2i& thePoint,
566                                            Aspect_VKeyMouse theButton,
567                                            Aspect_VKeyFlags theModifiers,
568                                            bool theIsDoubleClick)
569 {
570   (void )theIsDoubleClick;
571   if (theButton == Aspect_VKeyMouse_LeftButton)
572   {
573     SelectInViewer (thePoint, (theModifiers & Aspect_VKeyFlags_SHIFT) != 0);
574     return true;
575   }
576   return false;
577 }
578
579 // =======================================================================
580 // function : UpdateMouseButtons
581 // purpose  :
582 // =======================================================================
583 bool AIS_ViewController::UpdateMouseButtons (const Graphic3d_Vec2i& thePoint,
584                                              Aspect_VKeyMouse theButtons,
585                                              Aspect_VKeyFlags theModifiers,
586                                              bool theIsEmulated)
587 {
588   bool toUpdateView = false;
589   const double aTolClick = (theIsEmulated ? myTouchToleranceScale : 1.0) * myMouseClickThreshold;
590   if (theButtons == Aspect_VKeyMouse_NONE
591    && myMouseSingleButton > 0)
592   {
593     const Graphic3d_Vec2i aDelta = thePoint - myMousePressPoint;
594     if (double(aDelta.cwiseAbs().maxComp()) < aTolClick)
595     {
596       ++myMouseClickCounter;
597       const bool isDoubleClick = myMouseClickCounter == 2
598                               && myMouseClickTimer.IsStarted()
599                               && myMouseClickTimer.ElapsedTime() <= myMouseDoubleClickInt;
600
601       myMouseClickTimer.Stop();
602       myMouseClickTimer.Reset();
603       myMouseClickTimer.Start();
604       if (isDoubleClick)
605       {
606         myMouseClickCounter = 0;
607       }
608       toUpdateView = UpdateMouseClick (thePoint, (Aspect_VKeyMouse )myMouseSingleButton, theModifiers, isDoubleClick) || toUpdateView;
609     }
610     else
611     {
612       myMouseClickTimer.Stop();
613       myMouseClickCounter = 0;
614       myUI.Dragging.ToStop = true;
615       toUpdateView = true;
616     }
617     myMouseSingleButton = -1;
618   }
619   else if (theButtons == Aspect_VKeyMouse_NONE)
620   {
621     myMouseSingleButton = -1;
622   }
623   else if (myMouseSingleButton == -1)
624   {
625     if ((theButtons & Aspect_VKeyMouse_LeftButton) == Aspect_VKeyMouse_LeftButton)
626     {
627       myMouseSingleButton = Aspect_VKeyMouse_LeftButton;
628     }
629     else if ((theButtons & Aspect_VKeyMouse_RightButton) == Aspect_VKeyMouse_RightButton)
630     {
631       myMouseSingleButton = Aspect_VKeyMouse_RightButton;
632     }
633     else if ((theButtons & Aspect_VKeyMouse_MiddleButton) == Aspect_VKeyMouse_MiddleButton)
634     {
635       myMouseSingleButton = Aspect_VKeyMouse_MiddleButton;
636     }
637     else
638     {
639       myMouseSingleButton = 0;
640     }
641     if (myMouseSingleButton != 0)
642     {
643       if (myMouseClickCounter == 1)
644       {
645         const Graphic3d_Vec2i aDelta = thePoint - myMousePressPoint;
646         if (double(aDelta.cwiseAbs().maxComp()) >= aTolClick)
647         {
648           myMouseClickTimer.Stop();
649           myMouseClickCounter = 0;
650         }
651       }
652       myMousePressPoint = thePoint;
653     }
654   }
655   else
656   {
657     myMouseSingleButton = 0;
658
659     myUI.Dragging.ToAbort = true;
660     toUpdateView = true;
661   }
662
663   const AIS_MouseGesture aPrevGesture = myMouseActiveGesture;
664   myMouseModifiers = theModifiers;
665   myMousePressed   = theButtons;
666   if (theIsEmulated
667    || myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
668   {
669     myMouseActiveIdleRotation = false;
670     myMouseActiveGesture = AIS_MouseGesture_NONE;
671     if (theButtons != 0)
672     {
673       myMousePressPoint    = thePoint;
674       myMouseProgressPoint = myMousePressPoint;
675     }
676
677     if (myMouseGestureMap.Find (theButtons | theModifiers, myMouseActiveGesture))
678     {
679       switch (myMouseActiveGesture)
680       {
681         case AIS_MouseGesture_RotateView:
682         case AIS_MouseGesture_RotateOrbit:
683         {
684           if (myToAllowRotation)
685           {
686             myUpdateStartPointRot = true;
687           }
688           else
689           {
690             myMouseActiveGesture = AIS_MouseGesture_NONE;
691           }
692           break;
693         }
694         case AIS_MouseGesture_Pan:
695         {
696           if (myToAllowPanning)
697           {
698             myUpdateStartPointPan = true;
699           }
700           else
701           {
702             myMouseActiveGesture = AIS_MouseGesture_NONE;
703           }
704           break;
705         }
706         case AIS_MouseGesture_Zoom:
707         {
708           if (!myToAllowZooming)
709           {
710             myMouseActiveGesture = AIS_MouseGesture_NONE;
711           }
712           break;
713         }
714         case AIS_MouseGesture_SelectRectangle:
715         {
716           break;
717         }
718         case AIS_MouseGesture_SelectLasso:
719         {
720           UpdatePolySelection (thePoint, true);
721           break;
722         }
723         case AIS_MouseGesture_NONE:
724         {
725           break;
726         }
727       }
728     }
729
730     if (theButtons == Aspect_VKeyMouse_LeftButton
731      && theModifiers == Aspect_VKeyFlags_NONE
732      && myToAllowDragging)
733     {
734       myUI.Dragging.ToStart = true;
735       myUI.Dragging.PointStart = thePoint;
736     }
737   }
738
739   if (aPrevGesture != myMouseActiveGesture)
740   {
741     if (aPrevGesture == AIS_MouseGesture_SelectRectangle
742      || aPrevGesture == AIS_MouseGesture_SelectLasso)
743     {
744       myUI.Selection.ToApplyTool = true;
745     }
746
747     myUI.IsNewGesture = true;
748     toUpdateView = true;
749   }
750   return toUpdateView;
751 }
752
753 // =======================================================================
754 // function : UpdateMousePosition
755 // purpose  :
756 // =======================================================================
757 bool AIS_ViewController::UpdateMousePosition (const Graphic3d_Vec2i& thePoint,
758                                               Aspect_VKeyMouse theButtons,
759                                               Aspect_VKeyFlags theModifiers,
760                                               bool theIsEmulated)
761 {
762   myMousePositionLast = thePoint;
763   if (myMouseSingleButton > 0)
764   {
765     const double aTolClick = (theIsEmulated ? myTouchToleranceScale : 1.0) * myMouseClickThreshold;
766     const Graphic3d_Vec2i aPressDelta = thePoint - myMousePressPoint;
767     if (double(aPressDelta.cwiseAbs().maxComp()) >= aTolClick)
768     {
769       myMouseClickTimer.Stop();
770       myMouseClickCounter = 0;
771       myMouseSingleButton = -1;
772     }
773   }
774
775   bool toUpdateView = false;
776   Graphic3d_Vec2i aDelta = thePoint - myMouseProgressPoint;
777   if (!theIsEmulated
778     && myNavigationMode == AIS_NavigationMode_FirstPersonWalk)
779   {
780     if (!myMouseActiveIdleRotation
781       || myMouseActiveGesture != AIS_MouseGesture_RotateView)
782     {
783       myMouseActiveIdleRotation = true;
784       myMouseActiveGesture = AIS_MouseGesture_RotateView;
785       myMousePressPoint     = thePoint;
786       myMouseProgressPoint  = thePoint;
787       myUpdateStartPointRot = false;
788       myUI.ViewRotation.ToStart = true;
789       myUI.ViewRotation.PointStart.SetValues (thePoint.x(), thePoint.y());
790       myUI.ViewRotation.ToRotate = false;
791       aDelta.SetValues (0, 0);
792     }
793   }
794   else
795   {
796     if (myMouseActiveIdleRotation
797      && myMouseActiveGesture == AIS_MouseGesture_RotateView)
798     {
799       myMouseActiveGesture = AIS_MouseGesture_NONE;
800     }
801     myMouseActiveIdleRotation = false;
802   }
803
804   if (myMouseModifiers != theModifiers
805    && UpdateMouseButtons (thePoint, theButtons, theModifiers, theIsEmulated))
806   {
807     toUpdateView = true;
808   }
809
810   switch (myMouseActiveGesture)
811   {
812     case AIS_MouseGesture_SelectRectangle:
813     {
814       UpdateRubberBand (myMousePressPoint, thePoint);
815       toUpdateView = true;
816       break;
817     }
818     case AIS_MouseGesture_SelectLasso:
819     {
820       UpdatePolySelection (thePoint, true);
821       toUpdateView = true;
822       break;
823     }
824     case AIS_MouseGesture_RotateOrbit:
825     case AIS_MouseGesture_RotateView:
826     {
827       if (!myToAllowRotation)
828       {
829         break;
830       }
831       if (myUpdateStartPointRot)
832       {
833         if (myMouseActiveGesture == AIS_MouseGesture_RotateOrbit)
834         {
835           myUI.OrbitRotation.ToStart = true;
836           myUI.OrbitRotation.PointStart.SetValues (myMousePressPoint.x(), myMousePressPoint.y());
837         }
838         else
839         {
840           myUI.ViewRotation.ToStart = true;
841           myUI.ViewRotation.PointStart.SetValues (myMousePressPoint.x(), myMousePressPoint.y());
842         }
843         myUpdateStartPointRot = false;
844       }
845
846       const double aRotTol = theIsEmulated
847                            ? double(myTouchToleranceScale) * myTouchRotationThresholdPx
848                            : 0.0;
849       if (double (Abs (aDelta.x()) + Abs (aDelta.y())) > aRotTol)
850       {
851         const double aRotAccel = myNavigationMode == AIS_NavigationMode_FirstPersonWalk ? myMouseAccel : myOrbitAccel;
852         const Graphic3d_Vec2i aRotDelta = thePoint - myMousePressPoint;
853         if (myMouseActiveGesture == AIS_MouseGesture_RotateOrbit)
854         {
855           myUI.OrbitRotation.ToRotate = true;
856           myUI.OrbitRotation.PointTo = Graphic3d_Vec2d (myMousePressPoint.x(), myMousePressPoint.y())
857                                      + Graphic3d_Vec2d (aRotDelta.x(), aRotDelta.y()) * aRotAccel;
858         }
859         else
860         {
861           myUI.ViewRotation.ToRotate = true;
862           myUI.ViewRotation.PointTo = Graphic3d_Vec2d (myMousePressPoint.x(), myMousePressPoint.y())
863                                     + Graphic3d_Vec2d (aRotDelta.x(), aRotDelta.y()) * aRotAccel;
864         }
865         myUI.Dragging.PointTo = thePoint;
866
867         myMouseProgressPoint = thePoint;
868         toUpdateView = true;
869       }
870       break;
871     }
872     case AIS_MouseGesture_Zoom:
873     {
874       if (!myToAllowZooming)
875       {
876         break;
877       }
878       const double aZoomTol = theIsEmulated
879                             ? double(myTouchToleranceScale) * myTouchZoomThresholdPx
880                             : 0.0;
881       if (double (Abs (aDelta.x())) > aZoomTol)
882       {
883         if (UpdateZoom (Aspect_ScrollDelta (aDelta.x())))
884         {
885           toUpdateView = true;
886         }
887         myMouseProgressPoint = thePoint;
888       }
889       break;
890     }
891     case AIS_MouseGesture_Pan:
892     {
893       if (!myToAllowPanning)
894       {
895         break;
896       }
897       const double aPanTol = theIsEmulated
898                            ? double(myTouchToleranceScale) * myTouchPanThresholdPx
899                            : 0.0;
900       if (double (Abs (aDelta.x()) + Abs (aDelta.y())) > aPanTol)
901       {
902         if (myUpdateStartPointPan)
903         {
904           myUI.Panning.ToStart = true;
905           myUI.Panning.PointStart.SetValues (myMousePressPoint.x(), myMousePressPoint.y());
906           myUpdateStartPointPan = false;
907         }
908
909         aDelta.y() = -aDelta.y();
910         myMouseProgressPoint = thePoint;
911         if (myUI.Panning.ToPan)
912         {
913           myUI.Panning.Delta += aDelta;
914         }
915         else
916         {
917           myUI.Panning.ToPan = true;
918           myUI.Panning.Delta = aDelta;
919         }
920         toUpdateView = true;
921       }
922       break;
923     }
924     default:
925     {
926       break;
927     }
928   }
929
930   if (theButtons == Aspect_VKeyMouse_NONE
931   &&  myNavigationMode != AIS_NavigationMode_FirstPersonWalk
932   && !theIsEmulated
933   && !HasTouchPoints()
934   &&  myToAllowHighlight)
935   {
936     myUI.MoveTo.ToHilight = true;
937     myUI.MoveTo.Point = thePoint;
938     toUpdateView = true;
939   }
940   return toUpdateView;
941 }
942
943 // =======================================================================
944 // function : AddTouchPoint
945 // purpose  :
946 // =======================================================================
947 void AIS_ViewController::AddTouchPoint (Standard_Size theId,
948                                         const Graphic3d_Vec2d& thePnt,
949                                         Standard_Boolean theClearBefore)
950 {
951   myUI.MoveTo.ToHilight = false;
952   if (theClearBefore)
953   {
954     RemoveTouchPoint ((Standard_Size )-1);
955   }
956
957   myTouchPoints.Add (theId, Aspect_Touch (thePnt, false));
958   if (myTouchPoints.Extent() == 1)
959   {
960     myUpdateStartPointRot = true;
961     myStartRotCoord = thePnt;
962     if (myToAllowDragging)
963     {
964       myUI.Dragging.ToStart = true;
965       myUI.Dragging.PointStart.SetValues ((int )thePnt.x(), (int )thePnt.y());
966     }
967   }
968   else if (myTouchPoints.Extent() == 2)
969   {
970     myUI.Dragging.ToAbort = true;
971
972     myUpdateStartPointPan = true;
973     myStartPanCoord = thePnt;
974   }
975   myUI.IsNewGesture = true;
976 }
977
978 // =======================================================================
979 // function : RemoveTouchPoint
980 // purpose  :
981 // =======================================================================
982 bool AIS_ViewController::RemoveTouchPoint (Standard_Size theId,
983                                            Standard_Boolean theClearSelectPnts)
984 {
985   if (theId == (Standard_Size )-1)
986   {
987     myTouchPoints.Clear (false);
988   }
989   else
990   {
991     const Standard_Integer anOldExtent = myTouchPoints.Extent();
992     myTouchPoints.RemoveKey (theId);
993     if (myTouchPoints.Extent() == anOldExtent)
994     {
995       return false;
996     }
997   }
998
999   if (myTouchPoints.Extent() == 1)
1000   {
1001     // avoid incorrect transition from pinch to one finger
1002     Aspect_Touch& aFirstTouch = myTouchPoints.ChangeFromIndex (1);
1003     aFirstTouch.To = aFirstTouch.From;
1004
1005     myStartRotCoord = aFirstTouch.To;
1006     myUpdateStartPointRot = true;
1007   }
1008   else if (myTouchPoints.Extent() == 2)
1009   {
1010     myStartPanCoord = myTouchPoints.FindFromIndex (1).To;
1011     myUpdateStartPointPan = true;
1012   }
1013   else if (myTouchPoints.IsEmpty())
1014   {
1015     if (theClearSelectPnts)
1016     {
1017       myUI.Selection.ToApplyTool = true;
1018     }
1019
1020     myUI.Dragging.ToStop = true;
1021   }
1022   myUI.IsNewGesture = true;
1023   return true;
1024 }
1025
1026 // =======================================================================
1027 // function : UpdateTouchPoint
1028 // purpose  :
1029 // =======================================================================
1030 void AIS_ViewController::UpdateTouchPoint (Standard_Size theId,
1031                                            const Graphic3d_Vec2d& thePnt)
1032 {
1033   if (Aspect_Touch* aTouch = myTouchPoints.ChangeSeek (theId))
1034   {
1035     aTouch->To = thePnt;
1036   }
1037   else
1038   {
1039     AddTouchPoint (theId, thePnt);
1040   }
1041 }
1042
1043 // =======================================================================
1044 // function : SetNavigationMode
1045 // purpose  :
1046 // =======================================================================
1047 void AIS_ViewController::SetNavigationMode (AIS_NavigationMode theMode)
1048 {
1049   myNavigationMode = theMode;
1050
1051   // abort rotation
1052   myUI.OrbitRotation.ToStart  = false;
1053   myUI.OrbitRotation.ToRotate = false;
1054   myUI.ViewRotation.ToStart   = false;
1055   myUI.ViewRotation.ToRotate  = false;
1056 }
1057
1058 // =======================================================================
1059 // function : KeyDown
1060 // purpose  :
1061 // =======================================================================
1062 void AIS_ViewController::KeyDown (Aspect_VKey theKey,
1063                                   double theTime,
1064                                   double thePressure)
1065 {
1066   myKeys.KeyDown (theKey, theTime, thePressure);
1067 }
1068
1069 // =======================================================================
1070 // function : KeyUp
1071 // purpose  :
1072 // =======================================================================
1073 void AIS_ViewController::KeyUp (Aspect_VKey theKey,
1074                                 double theTime)
1075 {
1076   myKeys.KeyUp (theKey, theTime);
1077 }
1078
1079 // =======================================================================
1080 // function : KeyFromAxis
1081 // purpose  :
1082 // =======================================================================
1083 void AIS_ViewController::KeyFromAxis (Aspect_VKey theNegative,
1084                                       Aspect_VKey thePositive,
1085                                       double theTime,
1086                                       double thePressure)
1087 {
1088   myKeys.KeyFromAxis (theNegative, thePositive, theTime, thePressure);
1089 }
1090
1091 // =======================================================================
1092 // function : FetchNavigationKeys
1093 // purpose  :
1094 // =======================================================================
1095 AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRatio,
1096                                                        Standard_Real theRunRatio)
1097 {
1098   AIS_WalkDelta aWalk;
1099
1100   // navigation keys
1101   double aPrevEventTime = 0.0, aNewEventTime = 0.0;
1102   updateEventsTime (aPrevEventTime, aNewEventTime);
1103
1104   double aDuration = 0.0, aPressure = 1.0;
1105   if (Abs (myThrustSpeed) > gp::Resolution())
1106   {
1107     if (myHasThrust)
1108     {
1109       aWalk[AIS_WalkTranslation_Forward].Value = myThrustSpeed * (aNewEventTime - aPrevEventTime);
1110     }
1111     myHasThrust = true;
1112     myToAskNextFrame = true;
1113   }
1114   else
1115   {
1116     myHasThrust = false;
1117   }
1118
1119   aWalk.SetRunning (theRunRatio > 1.0
1120                  && myKeys.IsKeyDown (Aspect_VKey_Shift));
1121   if (myKeys.HoldDuration (Aspect_VKey_NavJump, aNewEventTime, aDuration))
1122   {
1123     myKeys.KeyUp (Aspect_VKey_NavJump, aNewEventTime);
1124     aWalk.SetJumping (true);
1125   }
1126   if (!aWalk.IsJumping()
1127    && theCrouchRatio < 1.0
1128    && myKeys.HoldDuration (Aspect_VKey_NavCrouch, aNewEventTime, aDuration))
1129   {
1130     aWalk.SetRunning (false);
1131     aWalk.SetCrouching (true);
1132   }
1133
1134   const double aMaxDuration = aNewEventTime - aPrevEventTime;
1135   const double aRunRatio = aWalk.IsRunning()
1136                          ? theRunRatio
1137                          : aWalk.IsCrouching()
1138                           ? theCrouchRatio
1139                           : 1.0;
1140   if (myKeys.HoldDuration (Aspect_VKey_NavForward, aNewEventTime, aDuration, aPressure))
1141   {
1142     double aProgress = Abs (Min (aMaxDuration, aDuration));
1143     aProgress *= aRunRatio;
1144     aWalk[AIS_WalkTranslation_Forward].Value += aProgress;
1145     aWalk[AIS_WalkTranslation_Forward].Pressure = aPressure;
1146     aWalk[AIS_WalkTranslation_Forward].Duration = aDuration;
1147   }
1148   if (myKeys.HoldDuration (Aspect_VKey_NavBackward, aNewEventTime, aDuration, aPressure))
1149   {
1150     double aProgress = Abs (Min (aMaxDuration, aDuration));
1151     aProgress *= aRunRatio;
1152     aWalk[AIS_WalkTranslation_Forward].Value += -aProgress;
1153     aWalk[AIS_WalkTranslation_Forward].Pressure = aPressure;
1154     aWalk[AIS_WalkTranslation_Forward].Duration = aDuration;
1155   }
1156   if (myKeys.HoldDuration (Aspect_VKey_NavSlideLeft, aNewEventTime, aDuration, aPressure))
1157   {
1158     double aProgress = Abs (Min (aMaxDuration, aDuration));
1159     aProgress *= aRunRatio;
1160     aWalk[AIS_WalkTranslation_Side].Value = -aProgress;
1161     aWalk[AIS_WalkTranslation_Side].Pressure = aPressure;
1162     aWalk[AIS_WalkTranslation_Side].Duration = aDuration;
1163   }
1164   if (myKeys.HoldDuration (Aspect_VKey_NavSlideRight, aNewEventTime, aDuration, aPressure))
1165   {
1166     double aProgress = Abs (Min (aMaxDuration, aDuration));
1167     aProgress *= aRunRatio;
1168     aWalk[AIS_WalkTranslation_Side].Value = aProgress;
1169     aWalk[AIS_WalkTranslation_Side].Pressure = aPressure;
1170     aWalk[AIS_WalkTranslation_Side].Duration = aDuration;
1171   }
1172   if (myKeys.HoldDuration (Aspect_VKey_NavLookLeft, aNewEventTime, aDuration, aPressure))
1173   {
1174     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1175     aWalk[AIS_WalkRotation_Yaw].Value = aProgress;
1176     aWalk[AIS_WalkRotation_Yaw].Pressure = aPressure;
1177     aWalk[AIS_WalkRotation_Yaw].Duration = aDuration;
1178   }
1179   if (myKeys.HoldDuration (Aspect_VKey_NavLookRight, aNewEventTime, aDuration, aPressure))
1180   {
1181     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1182     aWalk[AIS_WalkRotation_Yaw].Value = -aProgress;
1183     aWalk[AIS_WalkRotation_Yaw].Pressure = aPressure;
1184     aWalk[AIS_WalkRotation_Yaw].Duration = aDuration;
1185   }
1186   if (myKeys.HoldDuration (Aspect_VKey_NavLookUp, aNewEventTime, aDuration, aPressure))
1187   {
1188     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1189     aWalk[AIS_WalkRotation_Pitch].Value = !myToInvertPitch ? -aProgress : aProgress;
1190     aWalk[AIS_WalkRotation_Pitch].Pressure = aPressure;
1191     aWalk[AIS_WalkRotation_Pitch].Duration = aDuration;
1192   }
1193   if (myKeys.HoldDuration (Aspect_VKey_NavLookDown, aNewEventTime, aDuration, aPressure))
1194   {
1195     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1196     aWalk[AIS_WalkRotation_Pitch].Value = !myToInvertPitch ? aProgress : -aProgress;
1197     aWalk[AIS_WalkRotation_Pitch].Pressure = aPressure;
1198     aWalk[AIS_WalkRotation_Pitch].Duration = aDuration;
1199   }
1200   if (myKeys.HoldDuration (Aspect_VKey_NavRollCCW, aNewEventTime, aDuration, aPressure))
1201   {
1202     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1203     aWalk[AIS_WalkRotation_Roll].Value = -aProgress;
1204     aWalk[AIS_WalkRotation_Roll].Pressure = aPressure;
1205     aWalk[AIS_WalkRotation_Roll].Duration = aDuration;
1206   }
1207   if (myKeys.HoldDuration (Aspect_VKey_NavRollCW, aNewEventTime, aDuration, aPressure))
1208   {
1209     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1210     aWalk[AIS_WalkRotation_Roll].Value = aProgress;
1211     aWalk[AIS_WalkRotation_Roll].Pressure = aPressure;
1212     aWalk[AIS_WalkRotation_Roll].Duration = aDuration;
1213   }
1214   if (myKeys.HoldDuration (Aspect_VKey_NavSlideUp, aNewEventTime, aDuration, aPressure))
1215   {
1216     double aProgress = Abs (Min (aMaxDuration, aDuration));
1217     aWalk[AIS_WalkTranslation_Up].Value = aProgress;
1218     aWalk[AIS_WalkTranslation_Up].Pressure = aPressure;
1219     aWalk[AIS_WalkTranslation_Up].Duration = aDuration;
1220   }
1221   if (myKeys.HoldDuration (Aspect_VKey_NavSlideDown, aNewEventTime, aDuration, aPressure))
1222   {
1223     double aProgress = Abs (Min (aMaxDuration, aDuration));
1224     aWalk[AIS_WalkTranslation_Up].Value = -aProgress;
1225     aWalk[AIS_WalkTranslation_Up].Pressure = aPressure;
1226     aWalk[AIS_WalkTranslation_Up].Duration = aDuration;
1227   }
1228   return aWalk;
1229 }
1230
1231 // =======================================================================
1232 // function : AbortViewAnimation
1233 // purpose  :
1234 // =======================================================================
1235 void AIS_ViewController::AbortViewAnimation()
1236 {
1237   if (!myViewAnimation.IsNull()
1238    && !myViewAnimation->IsStopped())
1239   {
1240     myViewAnimation->Stop();
1241     myViewAnimation->SetView (Handle(V3d_View)());
1242   }
1243 }
1244
1245 // =======================================================================
1246 // function : handlePanning
1247 // purpose  :
1248 // =======================================================================
1249 void AIS_ViewController::handlePanning (const Handle(V3d_View)& theView)
1250 {
1251   if (!myGL.Panning.ToPan
1252    || !myToAllowPanning)
1253   {
1254     return;
1255   }
1256
1257   AbortViewAnimation();
1258
1259   const Handle(Graphic3d_Camera)& aCam = theView->Camera();
1260   if (aCam->IsOrthographic()
1261   || !hasPanningAnchorPoint())
1262   {
1263     theView->Pan (myGL.Panning.Delta.x(), myGL.Panning.Delta.y());
1264     theView->Invalidate();
1265     return;
1266   }
1267
1268   Graphic3d_Vec2i aWinSize;
1269   theView->Window()->Size (aWinSize.x(), aWinSize.y());
1270
1271   const gp_Dir& aDir = aCam->Direction();
1272   const gp_Ax3 aCameraCS (aCam->Center(), aDir.Reversed(), aDir ^ aCam->Up());
1273   const gp_XYZ anEyeToPnt = myPanPnt3d.XYZ() - aCam->Eye().XYZ();
1274   const gp_Pnt aViewDims = aCam->ViewDimensions (anEyeToPnt.Dot (aCam->Direction().XYZ())); // view dimensions at 3D point
1275   const Graphic3d_Vec2d aDxy (-aViewDims.X() * myGL.Panning.Delta.x() / double(aWinSize.x()),
1276                               -aViewDims.X() * myGL.Panning.Delta.y() / double(aWinSize.x()));
1277
1278   //theView->Translate (aCam, aDxy.x(), aDxy.y());
1279   gp_Trsf aPanTrsf;
1280   const gp_Vec aCameraPan = gp_Vec (aCameraCS.XDirection()) * aDxy.x()
1281                           + gp_Vec (aCameraCS.YDirection()) * aDxy.y();
1282   aPanTrsf.SetTranslation (aCameraPan);
1283   aCam->Transform (aPanTrsf);
1284   theView->Invalidate();
1285 }
1286
1287 // =======================================================================
1288 // function : handleZRotate
1289 // purpose  :
1290 // =======================================================================
1291 void AIS_ViewController::handleZRotate (const Handle(V3d_View)& theView)
1292 {
1293   if (!myGL.ZRotate.ToRotate
1294    || !myToAllowRotation)
1295   {
1296     return;
1297   }
1298
1299   AbortViewAnimation();
1300
1301   Graphic3d_Vec2i aViewPort;
1302   theView->Window()->Size (aViewPort.x(), aViewPort.y());
1303   Graphic3d_Vec2d aRotPnt (0.99 * aViewPort.x(),
1304                            0.5  * aViewPort.y());
1305   theView->StartRotation (int(aRotPnt.x()), int(aRotPnt.y()), 0.4);
1306   aRotPnt.y() += myGL.ZRotate.Angle * aViewPort.y();
1307   theView->Rotation (int(aRotPnt.x()), int(aRotPnt.y()));
1308   theView->Invalidate();
1309 }
1310
1311 // =======================================================================
1312 // function : handleZoom
1313 // purpose  :
1314 // =======================================================================
1315 void AIS_ViewController::handleZoom (const Handle(V3d_View)& theView,
1316                                      const Aspect_ScrollDelta& theParams,
1317                                      const gp_Pnt* thePnt)
1318 {
1319   if (!myToAllowZooming)
1320   {
1321     return;
1322   }
1323
1324   AbortViewAnimation();
1325
1326   const Handle(Graphic3d_Camera)& aCam = theView->Camera();
1327   if (thePnt != NULL)
1328   {
1329     const double aViewDist = Max (myMinCamDistance, (thePnt->XYZ() - aCam->Eye().XYZ()).Modulus());
1330     aCam->SetCenter (aCam->Eye().XYZ() + aCam->Direction().XYZ() * aViewDist);
1331   }
1332
1333   if (!theParams.HasPoint())
1334   {
1335     Standard_Real aCoeff = Abs(theParams.Delta) / 100.0 + 1.0;
1336     aCoeff = theParams.Delta > 0.0 ? aCoeff : 1.0 / aCoeff;
1337     theView->SetZoom (aCoeff, true);
1338     theView->Invalidate();
1339     return;
1340   }
1341
1342   // integer delta is too rough for small smooth increments
1343   //theView->StartZoomAtPoint (theParams.Point.x(), theParams.Point.y());
1344   //theView->ZoomAtPoint (0, 0, (int )theParams.Delta, (int )theParams.Delta);
1345
1346   double aDZoom = Abs (theParams.Delta) / 100.0 + 1.0;
1347   aDZoom = (theParams.Delta > 0.0) ? aDZoom : 1.0 / aDZoom;
1348   if (aDZoom <= 0.0)
1349   {
1350     return;
1351   }
1352
1353   const Graphic3d_Vec2d aViewDims (aCam->ViewDimensions().X(), aCam->ViewDimensions().Y());
1354
1355   // ensure that zoom will not be too small or too big
1356   double aCoef = aDZoom;
1357   if (aViewDims.x() < aCoef * Precision::Confusion())
1358   {
1359     aCoef = aViewDims.x() / Precision::Confusion();
1360   }
1361   else if (aViewDims.x() > aCoef * 1e12)
1362   {
1363     aCoef = aViewDims.x() / 1e12;
1364   }
1365   if (aViewDims.y() < aCoef * Precision::Confusion())
1366   {
1367     aCoef = aViewDims.y() / Precision::Confusion();
1368   }
1369   else if (aViewDims.y() > aCoef * 1e12)
1370   {
1371     aCoef = aViewDims.y() / 1e12;
1372   }
1373
1374   Graphic3d_Vec2d aZoomAtPointXYv (0.0, 0.0);
1375   theView->Convert (theParams.Point.x(), theParams.Point.y(),
1376                     aZoomAtPointXYv.x(), aZoomAtPointXYv.y());
1377   Graphic3d_Vec2d aDxy = aZoomAtPointXYv / aCoef;
1378   aCam->SetScale (aCam->Scale() / aCoef);
1379
1380   const gp_Dir& aDir = aCam->Direction();
1381   const gp_Ax3 aCameraCS (aCam->Center(), aDir.Reversed(), aDir ^ aCam->Up());
1382
1383   // pan back to the point
1384   aDxy = aZoomAtPointXYv - aDxy;
1385   if (thePnt != NULL)
1386   {
1387     // zoom at 3D point with perspective projection
1388     const gp_XYZ anEyeToPnt = thePnt->XYZ() - aCam->Eye().XYZ();
1389     aDxy.SetValues (anEyeToPnt.Dot (aCameraCS.XDirection().XYZ()),
1390                     anEyeToPnt.Dot (aCameraCS.YDirection().XYZ()));
1391
1392     // view dimensions at 3D point
1393     const gp_Pnt aViewDims1 = aCam->ViewDimensions (anEyeToPnt.Dot (aCam->Direction().XYZ()));
1394
1395     Graphic3d_Vec2i aWinSize;
1396     theView->Window()->Size (aWinSize.x(), aWinSize.y());
1397     const Graphic3d_Vec2d aPanFromCenterPx (double(theParams.Point.x()) - 0.5 * double(aWinSize.x()),
1398                                             double(aWinSize.y() - theParams.Point.y() - 1) - 0.5 * double(aWinSize.y()));
1399     aDxy.x() += -aViewDims1.X() * aPanFromCenterPx.x() / double(aWinSize.x());
1400     aDxy.y() += -aViewDims1.Y() * aPanFromCenterPx.y() / double(aWinSize.y());
1401   }
1402
1403   //theView->Translate (aCam, aDxy.x(), aDxy.y());
1404   gp_Trsf aPanTrsf;
1405   const gp_Vec aCameraPan = gp_Vec (aCameraCS.XDirection()) * aDxy.x()
1406                           + gp_Vec (aCameraCS.YDirection()) * aDxy.y();
1407   aPanTrsf.SetTranslation (aCameraPan);
1408   aCam->Transform (aPanTrsf);
1409   theView->Invalidate();
1410 }
1411
1412 // =======================================================================
1413 // function : handleZFocusScroll
1414 // purpose  :
1415 // =======================================================================
1416 void AIS_ViewController::handleZFocusScroll (const Handle(V3d_View)& theView,
1417                                              const Aspect_ScrollDelta& theParams)
1418 {
1419   if (!myToAllowZFocus
1420    || !theView->Camera()->IsStereo())
1421   {
1422     return;
1423   }
1424
1425   Standard_Real aFocus = theView->Camera()->ZFocus() + (theParams.Delta > 0.0 ? 0.05 : -0.05);
1426   if (aFocus > 0.2
1427    && aFocus < 2.0)
1428   {
1429     theView->Camera()->SetZFocus (theView->Camera()->ZFocusType(), aFocus);
1430     theView->Redraw();
1431   }
1432 }
1433
1434 // =======================================================================
1435 // function : handleOrbitRotation
1436 // purpose  :
1437 // =======================================================================
1438 void AIS_ViewController::handleOrbitRotation (const Handle(V3d_View)& theView,
1439                                               const gp_Pnt& thePnt,
1440                                               bool theToLockZUp)
1441 {
1442   if (!myToAllowRotation)
1443   {
1444     return;
1445   }
1446
1447   const Handle(Graphic3d_Camera)& aCam = theView->Camera();
1448   if (myGL.OrbitRotation.ToStart)
1449   {
1450     // default alternatives
1451     //if (myRotationMode == AIS_RotationMode_BndBoxActive) theView->StartRotation (myGL.RotateAtPoint.x(), myGL.RotateAtPoint.y());
1452     //theView->Rotate (0.0, 0.0, 0.0, thePnt.X(), thePnt.Y(), thePnt.Z(), true);
1453
1454     myRotatePnt3d      = thePnt;
1455     myCamStartOpUp     = aCam->Up();
1456     myCamStartOpEye    = aCam->Eye();
1457     myCamStartOpCenter = aCam->Center();
1458
1459     gp_Trsf aTrsf;
1460     aTrsf.SetTransformation (gp_Ax3 (myRotatePnt3d, aCam->OrthogonalizedUp(), aCam->Direction()),
1461                              gp_Ax3 (myRotatePnt3d, gp::DZ(), gp::DX()));
1462     const gp_Quaternion aRot = aTrsf.GetRotation();
1463     aRot.GetEulerAngles (gp_YawPitchRoll, myRotateStartYawPitchRoll[0], myRotateStartYawPitchRoll[1], myRotateStartYawPitchRoll[2]);
1464
1465     aTrsf.Invert();
1466     myCamStartOpToEye    = gp_Vec (myRotatePnt3d, aCam->Eye()).Transformed (aTrsf);
1467     myCamStartOpToCenter = gp_Vec (myRotatePnt3d, aCam->Center()).Transformed (aTrsf);
1468
1469     theView->Invalidate();
1470   }
1471
1472   if (!myGL.OrbitRotation.ToRotate)
1473   {
1474     return;
1475   }
1476
1477   AbortViewAnimation();
1478   if (theToLockZUp)
1479   {
1480     // amend camera to exclude roll angle (put camera Up vector to plane containing global Z and view direction)
1481     Graphic3d_Vec2i aWinXY;
1482     theView->Window()->Size (aWinXY.x(), aWinXY.y());
1483     double aYawAngleDelta   =  ((myGL.OrbitRotation.PointStart.x() - myGL.OrbitRotation.PointTo.x()) / double (aWinXY.x())) * (M_PI * 0.5);
1484     double aPitchAngleDelta = -((myGL.OrbitRotation.PointStart.y() - myGL.OrbitRotation.PointTo.y()) / double (aWinXY.y())) * (M_PI * 0.5);
1485     const double aPitchAngleNew = Max (Min (myRotateStartYawPitchRoll[1] + aPitchAngleDelta, M_PI * 0.5 - M_PI / 180.0), -M_PI * 0.5 + M_PI / 180.0);
1486     const double aYawAngleNew   = myRotateStartYawPitchRoll[0] + aYawAngleDelta;
1487     const double aRoll = 0.0;
1488
1489     gp_Quaternion aRot;
1490     aRot.SetEulerAngles (gp_YawPitchRoll, aYawAngleNew, aPitchAngleNew, aRoll);
1491     gp_Trsf aTrsfRot;
1492     aTrsfRot.SetRotation (aRot);
1493
1494     const gp_Dir aNewUp = gp::DZ().Transformed (aTrsfRot);
1495     aCam->SetUp (aNewUp);
1496     aCam->SetCenter(myRotatePnt3d.XYZ() + myCamStartOpToCenter.Transformed (aTrsfRot).XYZ());
1497     aCam->SetEye   (myRotatePnt3d.XYZ() + myCamStartOpToEye   .Transformed (aTrsfRot).XYZ());
1498     aCam->OrthogonalizeUp();
1499   }
1500   else
1501   {
1502     // default alternatives
1503     //if (myRotationMode == AIS_RotationMode_BndBoxActive) theView->Rotation (myGL.RotateToPoint.x(), myGL.RotateToPoint.y());
1504     //theView->Rotate (aDX, aDY, aDZ, myRotatePnt3d.X(), myRotatePnt3d.Y(), myRotatePnt3d.Z(), false);
1505
1506     // restore previous camera state
1507     aCam->SetUp     (myCamStartOpUp);
1508     aCam->SetEye    (myCamStartOpEye);
1509     aCam->SetCenter (myCamStartOpCenter);
1510
1511     Graphic3d_Vec2d aWinXY;
1512     theView->Size (aWinXY.x(), aWinXY.y());
1513     const Standard_Real rx = (Standard_Real )theView->Convert (aWinXY.x());
1514     const Standard_Real ry = (Standard_Real )theView->Convert (aWinXY.y());
1515
1516     const double THE_2PI = M_PI * 2.0;
1517     double aDX = (myGL.OrbitRotation.PointTo.x() - myGL.OrbitRotation.PointStart.x()) * M_PI / rx;
1518     double aDY = (myGL.OrbitRotation.PointStart.y() - myGL.OrbitRotation.PointTo.y()) * M_PI / ry;
1519
1520     if     (aDX > 0.0) { while (aDX >  THE_2PI) { aDX -= THE_2PI; } }
1521     else if(aDX < 0.0) { while (aDX < -THE_2PI) { aDX += THE_2PI; } }
1522     if     (aDY > 0.0) { while (aDY >  THE_2PI) { aDY -= THE_2PI; } }
1523     else if(aDY < 0.0) { while (aDY < -THE_2PI) { aDY += THE_2PI; } }
1524
1525     // rotate camera around 3 initial axes
1526     gp_Dir aCamDir (aCam->Direction().Reversed());
1527     gp_Dir aCamUp  (aCam->Up());
1528     gp_Dir aCamSide(aCamUp.Crossed (aCamDir));
1529
1530     gp_Trsf aRot[2], aTrsf;
1531     aRot[0].SetRotation (gp_Ax1 (myRotatePnt3d, aCamUp),  -aDX);
1532     aRot[1].SetRotation (gp_Ax1 (myRotatePnt3d, aCamSide), aDY);
1533     aTrsf.Multiply (aRot[0]);
1534     aTrsf.Multiply (aRot[1]);
1535
1536     aCam->Transform (aTrsf);
1537   }
1538
1539   theView->Invalidate();
1540 }
1541
1542 // =======================================================================
1543 // function : handleViewRotation
1544 // purpose  :
1545 // =======================================================================
1546 void AIS_ViewController::handleViewRotation (const Handle(V3d_View)& theView,
1547                                              double theYawExtra,
1548                                              double thePitchExtra,
1549                                              double theRoll,
1550                                              bool theToRestartOnIncrement)
1551 {
1552   if (!myToAllowRotation)
1553   {
1554     return;
1555   }
1556
1557   const Handle(Graphic3d_Camera)& aCam = theView->Camera();
1558   const bool toRotateAnyway = Abs (theYawExtra)   > gp::Resolution()
1559                            || Abs (thePitchExtra) > gp::Resolution()
1560                            || Abs (theRoll - myRotateStartYawPitchRoll[2]) > gp::Resolution();
1561   if (toRotateAnyway
1562    && theToRestartOnIncrement)
1563   {
1564     myGL.ViewRotation.ToStart = true;
1565     myGL.ViewRotation.PointTo = myGL.ViewRotation.PointStart;
1566   }
1567   if (myGL.ViewRotation.ToStart)
1568   {
1569     gp_Trsf aTrsf;
1570     aTrsf.SetTransformation (gp_Ax3 (gp::Origin(), aCam->OrthogonalizedUp(), aCam->Direction()),
1571                              gp_Ax3 (gp::Origin(), gp::DZ(), gp::DX()));
1572     const gp_Quaternion aRot = aTrsf.GetRotation();
1573     double aRollDummy = 0.0;
1574     aRot.GetEulerAngles (gp_YawPitchRoll, myRotateStartYawPitchRoll[0], myRotateStartYawPitchRoll[1], aRollDummy);
1575   }
1576   if (toRotateAnyway)
1577   {
1578     myRotateStartYawPitchRoll[0] += theYawExtra;
1579     myRotateStartYawPitchRoll[1] += thePitchExtra;
1580     myRotateStartYawPitchRoll[2]  = theRoll;
1581     myGL.ViewRotation.ToRotate = true;
1582   }
1583
1584   if (!myGL.ViewRotation.ToRotate)
1585   {
1586     return;
1587   }
1588
1589   AbortViewAnimation();
1590
1591   Graphic3d_Vec2i aWinXY;
1592   theView->Window()->Size (aWinXY.x(), aWinXY.y());
1593   double aYawAngleDelta   =  ((myGL.ViewRotation.PointStart.x() - myGL.ViewRotation.PointTo.x()) / double (aWinXY.x())) * (M_PI * 0.5);
1594   double aPitchAngleDelta = -((myGL.ViewRotation.PointStart.y() - myGL.ViewRotation.PointTo.y()) / double (aWinXY.y())) * (M_PI * 0.5);
1595   const double aPitchAngleNew = Max (Min (myRotateStartYawPitchRoll[1] + aPitchAngleDelta, M_PI * 0.5 - M_PI / 180.0), -M_PI * 0.5 + M_PI / 180.0);
1596   const double aYawAngleNew   = myRotateStartYawPitchRoll[0] + aYawAngleDelta;
1597   gp_Quaternion aRot;
1598   aRot.SetEulerAngles (gp_YawPitchRoll, aYawAngleNew, aPitchAngleNew, theRoll);
1599   gp_Trsf aTrsfRot;
1600   aTrsfRot.SetRotation (aRot);
1601
1602   const double aDist   = aCam->Distance();
1603   const gp_Dir aNewUp  = gp::DZ().Transformed (aTrsfRot);
1604   const gp_Dir aNewDir = gp::DX().Transformed (aTrsfRot);
1605   aCam->SetUp     (aNewUp);
1606   aCam->SetCenter (aCam->Eye().Translated (gp_Vec (aNewDir) * aDist));
1607   aCam->OrthogonalizeUp();
1608   theView->Invalidate();
1609 }
1610
1611 // =======================================================================
1612 // function : PickPoint
1613 // purpose  :
1614 // =======================================================================
1615 bool AIS_ViewController::PickPoint (gp_Pnt& thePnt,
1616                                     const Handle(AIS_InteractiveContext)& theCtx,
1617                                     const Handle(V3d_View)& theView,
1618                                     const Graphic3d_Vec2i& theCursor,
1619                                     bool theToStickToPickRay)
1620 {
1621   ResetPreviousMoveTo();
1622
1623   const Handle(StdSelect_ViewerSelector3d)& aSelector = theCtx->MainSelector();
1624   aSelector->Pick (theCursor.x(), theCursor.y(), theView);
1625   if (aSelector->NbPicked() < 1)
1626   {
1627     return false;
1628   }
1629
1630   const SelectMgr_SortCriterion& aPicked = aSelector->PickedData (1);
1631   if (theToStickToPickRay
1632   && !Precision::IsInfinite (aPicked.Depth))
1633   {
1634     thePnt = aSelector->GetManager().DetectedPoint (aPicked.Depth);
1635   }
1636   else
1637   {
1638     thePnt = aSelector->PickedPoint (1);
1639   }
1640   return !Precision::IsInfinite (thePnt.X())
1641       && !Precision::IsInfinite (thePnt.Y())
1642       && !Precision::IsInfinite (thePnt.Z());
1643 }
1644
1645 // =======================================================================
1646 // function : GravityPoint
1647 // purpose  :
1648 // =======================================================================
1649 gp_Pnt AIS_ViewController::GravityPoint (const Handle(AIS_InteractiveContext)& theCtx,
1650                                          const Handle(V3d_View)& theView)
1651 {
1652   switch (myRotationMode)
1653   {
1654     case AIS_RotationMode_PickLast:
1655     case AIS_RotationMode_PickCenter:
1656     {
1657       Graphic3d_Vec2i aCursor ((int )myGL.OrbitRotation.PointStart.x(), (int )myGL.OrbitRotation.PointStart.y());
1658       if (myRotationMode == AIS_RotationMode_PickCenter)
1659       {
1660         Graphic3d_Vec2i aViewPort;
1661         theView->Window()->Size (aViewPort.x(), aViewPort.y());
1662         aCursor = aViewPort / 2;
1663       }
1664
1665       gp_Pnt aPnt;
1666       if (PickPoint (aPnt, theCtx, theView, aCursor, myToStickToRayOnRotation))
1667       {
1668         return aPnt;
1669       }
1670       break;
1671     }
1672     case AIS_RotationMode_CameraAt:
1673     {
1674       const Handle(Graphic3d_Camera)& aCam = theView->Camera();
1675       return aCam->Center();
1676     }
1677     case AIS_RotationMode_BndBoxScene:
1678     {
1679       Bnd_Box aBndBox = theView->View()->MinMaxValues (false);
1680       if (!aBndBox.IsVoid())
1681       {
1682         return (aBndBox.CornerMin().XYZ() + aBndBox.CornerMax().XYZ()) * 0.5;
1683       }
1684       break;
1685     }
1686     case AIS_RotationMode_BndBoxActive:
1687       break;
1688   }
1689
1690   return theCtx ->GravityPoint (theView);
1691 }
1692
1693 // =======================================================================
1694 // function : handleCameraActions
1695 // purpose  :
1696 // =======================================================================
1697 void AIS_ViewController::handleCameraActions (const Handle(AIS_InteractiveContext)& theCtx,
1698                                               const Handle(V3d_View)& theView,
1699                                               const AIS_WalkDelta& theWalk)
1700 {
1701   // apply view actions
1702   if (myGL.Orientation.ToSetViewOrient)
1703   {
1704     theView->SetProj (myGL.Orientation.ViewOrient);
1705     myGL.Orientation.ToFitAll = true;
1706   }
1707
1708   // apply fit all
1709   if (myGL.Orientation.ToFitAll)
1710   {
1711     const double aFitMargin = 0.01;
1712     theView->FitAll (aFitMargin, false);
1713     theView->Invalidate();
1714     myGL.Orientation.ToFitAll = false;
1715   }
1716
1717   if (myGL.IsNewGesture)
1718   {
1719     if (myAnchorPointPrs1->HasInteractiveContext())
1720     {
1721       theCtx->Remove (myAnchorPointPrs1, false);
1722       if (!theView->Viewer()->ZLayerSettings (myAnchorPointPrs1->ZLayer()).IsImmediate())
1723       {
1724         theView->Invalidate();
1725       }
1726       else
1727       {
1728         theView->InvalidateImmediate();
1729       }
1730     }
1731     if (myAnchorPointPrs2->HasInteractiveContext())
1732     {
1733       theCtx->Remove (myAnchorPointPrs2, false);
1734       if (!theView->Viewer()->ZLayerSettings (myAnchorPointPrs2->ZLayer()).IsImmediate())
1735       {
1736         theView->Invalidate();
1737       }
1738       else
1739       {
1740         theView->InvalidateImmediate();
1741       }
1742     }
1743
1744     if (myHasHlrOnBeforeRotation)
1745     {
1746       myHasHlrOnBeforeRotation = false;
1747       theView->SetComputedMode (true);
1748       theView->Invalidate();
1749     }
1750   }
1751
1752   if (myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
1753   {
1754     if (myGL.Panning.ToStart
1755      && myToAllowPanning)
1756     {
1757       gp_Pnt aPanPnt (Precision::Infinite(), 0.0, 0.0);
1758       if (!theView->Camera()->IsOrthographic())
1759       {
1760         bool toStickToRay = false;
1761         if (myGL.Panning.PointStart.x() >= 0
1762          && myGL.Panning.PointStart.y() >= 0)
1763         {
1764           PickPoint (aPanPnt, theCtx, theView, myGL.Panning.PointStart, toStickToRay);
1765         }
1766         if (Precision::IsInfinite (aPanPnt.X()))
1767         {
1768           Graphic3d_Vec2i aWinSize;
1769           theView->Window()->Size (aWinSize.x(), aWinSize.y());
1770           PickPoint (aPanPnt, theCtx, theView, aWinSize / 2, toStickToRay);
1771         }
1772         if (!Precision::IsInfinite (aPanPnt.X())
1773           && myToShowPanAnchorPoint)
1774         {
1775           gp_Trsf aPntTrsf;
1776           aPntTrsf.SetTranslation (gp_Vec (aPanPnt.XYZ()));
1777           theCtx->SetLocation (myAnchorPointPrs2, aPntTrsf);
1778         }
1779       }
1780       setPanningAnchorPoint (aPanPnt);
1781     }
1782
1783     if (myToShowPanAnchorPoint
1784     &&  hasPanningAnchorPoint()
1785     &&  myGL.Panning.ToPan
1786     && !myGL.IsNewGesture
1787     && !myAnchorPointPrs2->HasInteractiveContext())
1788     {
1789       theCtx->Display (myAnchorPointPrs2, 0, -1, false, AIS_DS_Displayed);
1790     }
1791
1792     handlePanning (theView);
1793     handleZRotate (theView);
1794   }
1795
1796   if ((myNavigationMode == AIS_NavigationMode_Orbit
1797     || myGL.OrbitRotation.ToStart
1798     || myGL.OrbitRotation.ToRotate)
1799    && myToAllowRotation)
1800   {
1801     if (myGL.OrbitRotation.ToStart
1802     && !myHasHlrOnBeforeRotation)
1803     {
1804       myHasHlrOnBeforeRotation = theView->ComputedMode();
1805       if (myHasHlrOnBeforeRotation)
1806       {
1807         theView->SetComputedMode (false);
1808       }
1809     }
1810
1811     gp_Pnt aGravPnt;
1812     if (myGL.OrbitRotation.ToStart)
1813     {
1814       aGravPnt = GravityPoint (theCtx, theView);
1815       if (myToShowRotateCenter)
1816       {
1817         gp_Trsf aPntTrsf;
1818         aPntTrsf.SetTranslation (gp_Vec (aGravPnt.XYZ()));
1819         theCtx->SetLocation (myAnchorPointPrs1, aPntTrsf);
1820         theCtx->SetLocation (myAnchorPointPrs2, aPntTrsf);
1821       }
1822     }
1823
1824     if (myToShowRotateCenter
1825     &&  myGL.OrbitRotation.ToRotate
1826     && !myGL.IsNewGesture
1827     && !myAnchorPointPrs1->HasInteractiveContext())
1828     {
1829       theCtx->Display (myAnchorPointPrs1, 0, -1, false, AIS_DS_Displayed);
1830       theCtx->Display (myAnchorPointPrs2, 0, -1, false, AIS_DS_Displayed);
1831     }
1832     handleOrbitRotation (theView, aGravPnt,
1833                          myToLockOrbitZUp || myNavigationMode != AIS_NavigationMode_Orbit);
1834   }
1835
1836   if ((myNavigationMode != AIS_NavigationMode_Orbit
1837     || myGL.ViewRotation.ToStart
1838     || myGL.ViewRotation.ToRotate)
1839    && myToAllowRotation)
1840   {
1841     if (myGL.ViewRotation.ToStart
1842     && !myHasHlrOnBeforeRotation)
1843     {
1844       myHasHlrOnBeforeRotation = theView->ComputedMode();
1845       if (myHasHlrOnBeforeRotation)
1846       {
1847         theView->SetComputedMode (false);
1848       }
1849     }
1850
1851     double aRoll = 0.0;
1852     if (!theWalk[AIS_WalkRotation_Roll].IsEmpty()
1853      && !myToLockOrbitZUp)
1854     {
1855       aRoll = (M_PI / 12.0) * theWalk[AIS_WalkRotation_Roll].Pressure;
1856       aRoll *= Min (1000.0  * theWalk[AIS_WalkRotation_Roll].Duration, 100.0) / 100.0;
1857       if (theWalk[AIS_WalkRotation_Roll].Value < 0.0)
1858       {
1859         aRoll = -aRoll;
1860       }
1861     }
1862
1863     handleViewRotation (theView, theWalk[AIS_WalkRotation_Yaw].Value, theWalk[AIS_WalkRotation_Pitch].Value, aRoll,
1864                         myNavigationMode == AIS_NavigationMode_FirstPersonFlight);
1865   }
1866
1867   if (!myGL.ZoomActions.IsEmpty())
1868   {
1869     for (NCollection_Sequence<Aspect_ScrollDelta>::Iterator aZoomIter (myGL.ZoomActions); aZoomIter.More(); aZoomIter.Next())
1870     {
1871       Aspect_ScrollDelta aZoomParams = aZoomIter.Value();
1872       if (myToAllowZFocus
1873        && (aZoomParams.Flags & Aspect_VKeyFlags_CTRL) != 0
1874        && theView->Camera()->IsStereo())
1875       {
1876         handleZFocusScroll (theView, aZoomParams);
1877         continue;
1878       }
1879
1880       if (!myToAllowZooming)
1881       {
1882         continue;
1883       }
1884
1885       if (!theView->Camera()->IsOrthographic())
1886       {
1887         gp_Pnt aPnt;
1888         if (aZoomParams.HasPoint()
1889          && PickPoint (aPnt, theCtx, theView, aZoomParams.Point, myToStickToRayOnZoom))
1890         {
1891           handleZoom (theView, aZoomParams, &aPnt);
1892           continue;
1893         }
1894
1895         Graphic3d_Vec2i aWinSize;
1896         theView->Window()->Size (aWinSize.x(), aWinSize.y());
1897         if (PickPoint (aPnt, theCtx, theView, aWinSize / 2, myToStickToRayOnZoom))
1898         {
1899           aZoomParams.ResetPoint(); // do not pretend to zoom at 'nothing'
1900           handleZoom (theView, aZoomParams, &aPnt);
1901           continue;
1902         }
1903       }
1904       handleZoom (theView, aZoomParams, NULL);
1905     }
1906     myGL.ZoomActions.Clear();
1907   }
1908 }
1909
1910 // =======================================================================
1911 // function : OnSelectionChanged
1912 // purpose  :
1913 // =======================================================================
1914 void AIS_ViewController::OnSelectionChanged (const Handle(AIS_InteractiveContext)& ,
1915                                              const Handle(V3d_View)& )
1916 {
1917   //
1918 }
1919
1920 // =======================================================================
1921 // function : OnObjectDragged
1922 // purpose  :
1923 // =======================================================================
1924 void AIS_ViewController::OnObjectDragged (const Handle(AIS_InteractiveContext)& theCtx,
1925                                           const Handle(V3d_View)& theView,
1926                                           AIS_DragAction theAction)
1927 {
1928   switch (theAction)
1929   {
1930     case AIS_DragAction_Start:
1931     {
1932       myDragObject.Nullify();
1933       if (!theCtx->HasDetected())
1934       {
1935         return;
1936       }
1937
1938       Handle(AIS_InteractiveObject) aPrs = theCtx->DetectedInteractive();
1939       if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (aPrs))
1940       {
1941         if (aManip->HasActiveMode())
1942         {
1943           myDragObject = aManip;
1944           aManip->StartTransform (myGL.Dragging.PointStart.x(), myGL.Dragging.PointStart.y(), theView);
1945         }
1946       }
1947       return;
1948     }
1949     case AIS_DragAction_Update:
1950     {
1951       if (myDragObject.IsNull())
1952       {
1953         return;
1954       }
1955
1956       if (Handle(SelectMgr_EntityOwner) aGlobOwner = myDragObject->GlobalSelOwner())
1957       {
1958         theCtx->SetSelectedState (aGlobOwner, true);
1959       }
1960       if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (myDragObject))
1961       {
1962         aManip->Transform (myGL.Dragging.PointTo.x(), myGL.Dragging.PointTo.y(), theView);
1963       }
1964       theView->Invalidate();
1965       return;
1966     }
1967     case AIS_DragAction_Abort:
1968     {
1969       if (myDragObject.IsNull())
1970       {
1971         return;
1972       }
1973
1974       myGL.Dragging.PointTo = myGL.Dragging.PointStart;
1975       OnObjectDragged (theCtx, theView, AIS_DragAction_Update);
1976
1977       if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (myDragObject))
1978       {
1979         aManip->StopTransform (false);
1980       }
1981       Standard_FALLTHROUGH
1982     }
1983     case AIS_DragAction_Stop:
1984     {
1985       if (myDragObject.IsNull())
1986       {
1987         return;
1988       }
1989
1990       if (Handle(SelectMgr_EntityOwner) aGlobOwner = myDragObject->GlobalSelOwner())
1991       {
1992         theCtx->SetSelectedState (aGlobOwner, false);
1993       }
1994
1995       theView->Invalidate();
1996       myDragObject.Nullify();
1997       return;
1998     }
1999   }
2000 }
2001
2002 // =======================================================================
2003 // function : contextLazyMoveTo
2004 // purpose  :
2005 // =======================================================================
2006 void AIS_ViewController::contextLazyMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
2007                                             const Handle(V3d_View)& theView,
2008                                             const Graphic3d_Vec2i& thePnt)
2009 {
2010   if (myPrevMoveTo == thePnt)
2011   {
2012     return;
2013   }
2014
2015   myPrevMoveTo = thePnt;
2016
2017   Handle(SelectMgr_EntityOwner) aLastPicked = theCtx->DetectedOwner();
2018   theCtx->MoveTo (thePnt.x(), thePnt.y(), theView, false);
2019   Handle(SelectMgr_EntityOwner) aNewPicked = theCtx->DetectedOwner();
2020
2021   if (theView->Viewer()->Grid()->IsActive()
2022    && theView->Viewer()->GridEcho())
2023   {
2024     if (aNewPicked.IsNull())
2025     {
2026       Graphic3d_Vec3d aPnt3d;
2027       theView->ConvertToGrid (thePnt.x(), thePnt.y(), aPnt3d[0], aPnt3d[1], aPnt3d[2]);
2028       theView->Viewer()->ShowGridEcho (theView, Graphic3d_Vertex (aPnt3d[0], aPnt3d[1], aPnt3d[2]));
2029       theView->InvalidateImmediate();
2030     }
2031     else
2032     {
2033       theView->Viewer()->HideGridEcho (theView);
2034       theView->InvalidateImmediate();
2035     }
2036   }
2037
2038   if (aLastPicked != aNewPicked
2039    || (!aNewPicked.IsNull() && aNewPicked->IsForcedHilight()))
2040   {
2041     // dynamic highlight affects all Views
2042     for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
2043     {
2044       const Handle(V3d_View)& aView = aViewIter.Value();
2045       aView->InvalidateImmediate();
2046     }
2047   }
2048 }
2049
2050 // =======================================================================
2051 // function : handleSelectionPick
2052 // purpose  :
2053 // =======================================================================
2054 void AIS_ViewController::handleSelectionPick (const Handle(AIS_InteractiveContext)& theCtx,
2055                                               const Handle(V3d_View)& theView)
2056 {
2057   if (myGL.Selection.Tool == AIS_ViewSelectionTool_Picking
2058   && !myGL.Selection.Points.IsEmpty())
2059   {
2060     for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aPntIter (myGL.Selection.Points); aPntIter.More(); aPntIter.Next())
2061     {
2062       const bool hadPrevMoveTo = HasPreviousMoveTo();
2063       contextLazyMoveTo (theCtx, theView, aPntIter.Value());
2064       if (!hadPrevMoveTo)
2065       {
2066         ResetPreviousMoveTo();
2067       }
2068
2069       if (myGL.Selection.IsXOR)
2070       {
2071         theCtx->ShiftSelect (false);
2072       }
2073       else
2074       {
2075         theCtx->Select (false);
2076       }
2077
2078       // selection affects all Views
2079       theView->Viewer()->Invalidate();
2080
2081       OnSelectionChanged (theCtx, theView);
2082     }
2083
2084     myGL.Selection.Points.Clear();
2085   }
2086 }
2087
2088 // =======================================================================
2089 // function : handleSelectionPoly
2090 // purpose  :
2091 // =======================================================================
2092 void AIS_ViewController::handleSelectionPoly (const Handle(AIS_InteractiveContext)& theCtx,
2093                                               const Handle(V3d_View)& theView)
2094 {
2095   // rubber-band & window polygon selection
2096   if (myGL.Selection.Tool == AIS_ViewSelectionTool_RubberBand
2097    || myGL.Selection.Tool == AIS_ViewSelectionTool_Polygon)
2098   {
2099     if (!myGL.Selection.Points.IsEmpty())
2100     {
2101       myRubberBand->ClearPoints();
2102       myRubberBand->SetToUpdate();
2103
2104       const bool anIsRubber = myGL.Selection.Tool == AIS_ViewSelectionTool_RubberBand;
2105       if (anIsRubber)
2106       {
2107         myRubberBand->SetRectangle (myGL.Selection.Points.First().x(), -myGL.Selection.Points.First().y(),
2108                                     myGL.Selection.Points.Last().x(),  -myGL.Selection.Points.Last().y());
2109       }
2110       else
2111       {
2112         Graphic3d_Vec2i aPrev (IntegerLast(), IntegerLast());
2113         for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aSelIter (myGL.Selection.Points); aSelIter.More(); aSelIter.Next())
2114         {
2115           Graphic3d_Vec2i aPntNew = Graphic3d_Vec2i (aSelIter.Value().x(), -aSelIter.Value().y());
2116           if (aPntNew != aPrev)
2117           {
2118             aPrev = aPntNew;
2119             myRubberBand->AddPoint (Graphic3d_Vec2i (aSelIter.Value().x(), -aSelIter.Value().y()));
2120           }
2121         }
2122       }
2123
2124       myRubberBand->SetPolygonClosed (anIsRubber);
2125       try
2126       {
2127         theCtx->Display (myRubberBand, 0, -1, false, AIS_DS_Displayed);
2128       }
2129       catch (const Standard_Failure& theEx)
2130       {
2131         Message::DefaultMessenger()->Send (TCollection_AsciiString ("Internal error while displaying rubber-band: ")
2132                                         + theEx.DynamicType()->Name() + ", " + theEx.GetMessageString(), Message_Warning);
2133         myRubberBand->ClearPoints();
2134       }
2135       if (!theView->Viewer()->ZLayerSettings (myRubberBand->ZLayer()).IsImmediate())
2136       {
2137         theView->Invalidate();
2138       }
2139       else
2140       {
2141         theView->InvalidateImmediate();
2142       }
2143     }
2144     else if (!myRubberBand.IsNull()
2145            && myRubberBand->HasInteractiveContext())
2146     {
2147       theCtx->Remove (myRubberBand, false);
2148       myRubberBand->ClearPoints();
2149     }
2150   }
2151
2152   if (myGL.Selection.ToApplyTool)
2153   {
2154     myGL.Selection.ToApplyTool = false;
2155     if (theCtx->IsDisplayed (myRubberBand))
2156     {
2157       theCtx->Remove (myRubberBand, false);
2158       {
2159         const NCollection_Sequence<Graphic3d_Vec2i>& aPoints = myRubberBand->Points();
2160         if (aPoints.Size() == 4
2161          && aPoints.Value (1).x() == aPoints.Value (2).x()
2162          && aPoints.Value (3).x() == aPoints.Value (4).x()
2163          && aPoints.Value (1).y() == aPoints.Value (4).y()
2164          && aPoints.Value (2).y() == aPoints.Value (3).y())
2165         {
2166           const Graphic3d_Vec2i aPnt1 (aPoints.Value (1).x(), -aPoints.Value (1).y());
2167           const Graphic3d_Vec2i aPnt2 (aPoints.Value (3).x(), -aPoints.Value (3).y());
2168           theCtx->MainSelector()->AllowOverlapDetection (aPnt1.y() != Min (aPnt1.y(), aPnt2.y()));
2169           if (myGL.Selection.IsXOR)
2170           {
2171             theCtx->ShiftSelect (Min (aPnt1.x(), aPnt2.x()), Min (aPnt1.y(), aPnt2.y()),
2172                                  Max (aPnt1.x(), aPnt2.x()), Max (aPnt1.y(), aPnt2.y()),
2173                                  theView, false);
2174           }
2175           else
2176           {
2177             theCtx->Select (Min (aPnt1.x(), aPnt2.x()), Min (aPnt1.y(), aPnt2.y()),
2178                             Max (aPnt1.x(), aPnt2.x()), Max (aPnt1.y(), aPnt2.y()),
2179                             theView, false);
2180           }
2181           theCtx->MainSelector()->AllowOverlapDetection (false);
2182         }
2183         else if (aPoints.Length() >= 3)
2184         {
2185           TColgp_Array1OfPnt2d aPolyline (1, aPoints.Length());
2186           TColgp_Array1OfPnt2d::Iterator aPolyIter (aPolyline);
2187           for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aSelIter (aPoints);
2188                aSelIter.More(); aSelIter.Next(), aPolyIter.Next())
2189           {
2190             const Graphic3d_Vec2i& aNewPnt = aSelIter.Value();
2191             aPolyIter.ChangeValue() = gp_Pnt2d (aNewPnt.x(), -aNewPnt.y());
2192           }
2193
2194           theCtx->MainSelector()->AllowOverlapDetection (false);
2195           if (myGL.Selection.IsXOR)
2196           {
2197             theCtx->ShiftSelect (aPolyline, theView, false);
2198           }
2199           else
2200           {
2201             theCtx->Select (aPolyline, theView, false);
2202           }
2203         }
2204       }
2205
2206       // selection affects all Views
2207       theView->Viewer()->Invalidate();
2208
2209       myRubberBand->ClearPoints();
2210       OnSelectionChanged (theCtx, theView);
2211     }
2212   }
2213 }
2214
2215 // =======================================================================
2216 // function : handleDynamicHighlight
2217 // purpose  :
2218 // =======================================================================
2219 void AIS_ViewController::handleDynamicHighlight (const Handle(AIS_InteractiveContext)& theCtx,
2220                                                  const Handle(V3d_View)& theView)
2221 {
2222   if ((myGL.MoveTo.ToHilight || myGL.Dragging.ToStart)
2223    && myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
2224   {
2225     const Graphic3d_Vec2i& aMoveToPnt = myGL.MoveTo.ToHilight ? myGL.MoveTo.Point : myGL.Dragging.PointStart;
2226     if (myGL.Dragging.ToStart && (!myGL.MoveTo.ToHilight || !myToAllowHighlight)
2227      && !HasPreviousMoveTo())
2228     {
2229       contextLazyMoveTo (theCtx, theView, aMoveToPnt);
2230       ResetPreviousMoveTo();
2231       OnObjectDragged (theCtx, theView, AIS_DragAction_Start);
2232       theCtx->ClearDetected();
2233     }
2234     else if (myToAllowHighlight)
2235     {
2236       if (myPrevMoveTo != aMoveToPnt
2237        || myGL.OrbitRotation.ToRotate
2238        || myGL.ViewRotation.ToRotate
2239        || theView->IsInvalidated())
2240       {
2241         ResetPreviousMoveTo();
2242         contextLazyMoveTo (theCtx, theView, aMoveToPnt);
2243       }
2244       if (myGL.Dragging.ToStart)
2245       {
2246         OnObjectDragged (theCtx, theView, AIS_DragAction_Start);
2247       }
2248     }
2249
2250     myGL.MoveTo.ToHilight = false;
2251   }
2252
2253   if (!myDragObject.IsNull())
2254   {
2255     if (myGL.Dragging.ToAbort)
2256     {
2257       OnObjectDragged (theCtx, theView, AIS_DragAction_Abort);
2258       myGL.OrbitRotation.ToRotate = false;
2259       myGL.ViewRotation .ToRotate = false;
2260     }
2261     else if (myGL.Dragging.ToStop)
2262     {
2263       OnObjectDragged (theCtx, theView, AIS_DragAction_Stop);
2264       myGL.OrbitRotation.ToRotate = false;
2265       myGL.ViewRotation .ToRotate = false;
2266     }
2267     else if (myGL.OrbitRotation.ToRotate
2268           || myGL.ViewRotation.ToRotate)
2269     {
2270       OnObjectDragged (theCtx, theView, AIS_DragAction_Update);
2271       myGL.OrbitRotation.ToRotate = false;
2272       myGL.ViewRotation .ToRotate = false;
2273     }
2274   }
2275 }
2276
2277 // =======================================================================
2278 // function : handleMoveTo
2279 // purpose  :
2280 // =======================================================================
2281 void AIS_ViewController::handleMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
2282                                        const Handle(V3d_View)& theView)
2283 {
2284   handleSelectionPick   (theCtx, theView);
2285   handleDynamicHighlight(theCtx, theView);
2286   handleSelectionPoly   (theCtx, theView);
2287 }
2288
2289 // =======================================================================
2290 // function : handleViewRedraw
2291 // purpose  :
2292 // =======================================================================
2293 void AIS_ViewController::handleViewRedraw (const Handle(AIS_InteractiveContext)& ,
2294                                            const Handle(V3d_View)& theView)
2295 {
2296   // manage animation state
2297   if (!myViewAnimation.IsNull()
2298    && !myViewAnimation->IsStopped())
2299   {
2300     myViewAnimation->UpdateTimer();
2301     ResetPreviousMoveTo();
2302     setAskNextFrame();
2303   }
2304
2305   for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
2306   {
2307     const Handle(V3d_View)& aView = aViewIter.Value();
2308     if (aView->IsInvalidated()
2309      || myToAskNextFrame)
2310     {
2311       if (aView->ComputedMode())
2312       {
2313         aView->Update();
2314       }
2315       else
2316       {
2317         aView->Redraw();
2318       }
2319     }
2320     else if (aView->IsInvalidatedImmediate())
2321     {
2322       aView->RedrawImmediate();
2323     }
2324   }
2325
2326   if (myToAskNextFrame)
2327   {
2328     // ask more frames
2329     theView->Window()->InvalidateContent (Handle(Aspect_DisplayConnection)());
2330   }
2331 }
2332
2333 // =======================================================================
2334 // function : HandleViewEvents
2335 // purpose  :
2336 // =======================================================================
2337 void AIS_ViewController::HandleViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
2338                                            const Handle(V3d_View)& theView)
2339 {
2340   handleMoveTo (theCtx, theView);
2341
2342   const AIS_WalkDelta aWalk = FetchNavigationKeys (1.0, 1.0);
2343   handleCameraActions (theCtx, theView, aWalk);
2344   handleViewRedraw (theCtx, theView);
2345
2346   // make sure to not process the same events twice
2347   myGL.Reset();
2348   myToAskNextFrame = false;
2349 }