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