0031687: Draw Harness, ViewerTest - extend command vrenderparams with option updating...
[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 <AIS_XRTrackedDevice.hxx>
22 #include <Aspect_XRSession.hxx>
23 #include <Aspect_Grid.hxx>
24 #include <Geom_CartesianPoint.hxx>
25 #include <Graphic3d_ArrayOfSegments.hxx>
26 #include <Graphic3d_Texture2Dmanual.hxx>
27 #include <Message.hxx>
28 #include <Message_Messenger.hxx>
29 #include <gp_Quaternion.hxx>
30 #include <V3d_View.hxx>
31 #include <WNT_HIDSpaceMouse.hxx>
32
33 // =======================================================================
34 // function : AIS_ViewController
35 // purpose  :
36 // =======================================================================
37 AIS_ViewController::AIS_ViewController()
38 : myLastEventsTime    (0.0),
39   myToAskNextFrame    (false),
40   myMinCamDistance    (1.0),
41   myRotationMode      (AIS_RotationMode_BndBoxActive),
42   myNavigationMode    (AIS_NavigationMode_Orbit),
43   myMouseAccel           (1.0f),
44   myOrbitAccel           (1.0f),
45   myToShowPanAnchorPoint (true),
46   myToShowRotateCenter   (true),
47   myToLockOrbitZUp       (false),
48   myToInvertPitch        (false),
49   myToAllowTouchZRotation(false),
50   myToAllowRotation      (true),
51   myToAllowPanning       (true),
52   myToAllowZooming       (true),
53   myToAllowZFocus        (true),
54   myToAllowHighlight     (true),
55   myToAllowDragging      (true),
56   myToStickToRayOnZoom   (true),
57   myToStickToRayOnRotation (true),
58   //
59   myWalkSpeedAbsolute (1.5f),
60   myWalkSpeedRelative (0.1f),
61   myThrustSpeed (0.0f),
62   myHasThrust (false),
63   //
64   myViewAnimation (new AIS_AnimationCamera ("AIS_ViewController_ViewAnimation", Handle(V3d_View)())),
65   myPrevMoveTo (-1, -1),
66   myHasHlrOnBeforeRotation (false),
67   //
68   myXRPrsDevices (0, 0),
69   myXRLaserTeleColor (Quantity_NOC_GREEN),
70   myXRLaserPickColor (Quantity_NOC_BLUE),
71   myXRLastTeleportHand(Aspect_XRTrackedDeviceRole_Other),
72   myXRLastPickingHand (Aspect_XRTrackedDeviceRole_Other),
73   myXRLastPickDepthLeft (Precision::Infinite()),
74   myXRLastPickDepthRight(Precision::Infinite()),
75   myXRTurnAngle (M_PI_4),
76   myToDisplayXRAuxDevices (false),
77   myToDisplayXRHands (true),
78   //
79   myMouseClickThreshold (3.0),
80   myMouseDoubleClickInt (0.4),
81   myScrollZoomRatio     (15.0f),
82   myMouseActiveGesture  (AIS_MouseGesture_NONE),
83   myMouseActiveIdleRotation (false),
84   myMouseClickCounter   (0),
85   myMousePressed        (Aspect_VKeyMouse_NONE),
86   myMouseModifiers      (Aspect_VKeyFlags_NONE),
87   myMouseSingleButton   (-1),
88   myMouseStopDragOnUnclick (false),
89   //
90   myTouchToleranceScale      (1.0f),
91   myTouchRotationThresholdPx (6.0f),
92   myTouchZRotationThreshold  (float(2.0 * M_PI / 180.0)),
93   myTouchPanThresholdPx      (4.0f),
94   myTouchZoomThresholdPx     (6.0f),
95   myTouchZoomRatio           (0.13f),
96   //
97   myNbTouchesLast (0),
98   myUpdateStartPointPan  (true),
99   myUpdateStartPointRot  (true),
100   myUpdateStartPointZRot (true),
101   //
102   my3dMouseNoRotate  (false, false, false),
103   my3dMouseToReverse (true,  false, false),
104   my3dMouseAccelTrans  (2.0f),
105   my3dMouseAccelRotate (4.0f),
106   my3dMouseIsQuadric   (true),
107   //
108   myPanPnt3d (Precision::Infinite(), 0.0, 0.0)
109 {
110   memset(my3dMouseButtonState, 0, sizeof(my3dMouseButtonState));
111   myEventTimer.Start();
112   myViewAnimation->SetOwnDuration (0.5);
113
114   myAnchorPointPrs1 = new AIS_Point (new Geom_CartesianPoint (0.0, 0.0, 0.0));
115   myAnchorPointPrs1->SetZLayer (Graphic3d_ZLayerId_Top);
116   myAnchorPointPrs1->SetMutable (true);
117
118   myAnchorPointPrs2 = new AIS_Point (new Geom_CartesianPoint (0.0, 0.0, 0.0));
119   myAnchorPointPrs2->SetZLayer (Graphic3d_ZLayerId_Topmost);
120   myAnchorPointPrs2->SetMutable (true);
121
122   myRubberBand = new AIS_RubberBand (Quantity_NOC_LIGHTBLUE, Aspect_TOL_SOLID, Quantity_NOC_LIGHTBLUE4, 0.5, 1.0);
123   myRubberBand->SetZLayer (Graphic3d_ZLayerId_TopOSD);
124   myRubberBand->SetTransformPersistence (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_LEFT_UPPER));
125   myRubberBand->SetDisplayMode (0);
126   myRubberBand->SetMutable (true);
127
128   myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton,                           AIS_MouseGesture_RotateOrbit);
129   myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton | Aspect_VKeyFlags_CTRL,   AIS_MouseGesture_Zoom);
130   myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton | Aspect_VKeyFlags_SHIFT,  AIS_MouseGesture_Pan);
131   myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton | Aspect_VKeyFlags_ALT,    AIS_MouseGesture_SelectRectangle);
132
133   myMouseGestureMap.Bind (Aspect_VKeyMouse_RightButton,                          AIS_MouseGesture_Zoom);
134   myMouseGestureMap.Bind (Aspect_VKeyMouse_RightButton | Aspect_VKeyFlags_CTRL,  AIS_MouseGesture_RotateOrbit);
135
136   myMouseGestureMap.Bind (Aspect_VKeyMouse_MiddleButton,                         AIS_MouseGesture_Pan);
137   myMouseGestureMap.Bind (Aspect_VKeyMouse_MiddleButton | Aspect_VKeyFlags_CTRL, AIS_MouseGesture_Pan);
138
139   myXRTeleportHaptic.Duration  = 3600.0f;
140   myXRTeleportHaptic.Frequency = 0.1f;
141   myXRTeleportHaptic.Amplitude = 0.2f;
142
143   myXRPickingHaptic.Duration  = 0.1f;
144   myXRPickingHaptic.Frequency = 4.0f;
145   myXRPickingHaptic.Amplitude = 0.1f;
146
147   myXRSelectHaptic.Duration  = 0.2f;
148   myXRSelectHaptic.Frequency = 4.0f;
149   myXRSelectHaptic.Amplitude = 0.5f;
150 }
151
152 // =======================================================================
153 // function : ~AIS_ViewController
154 // purpose  :
155 // =======================================================================
156 AIS_ViewController::~AIS_ViewController()
157 {
158   //
159 }
160
161 // =======================================================================
162 // function : ResetViewInput
163 // purpose  :
164 // =======================================================================
165 void AIS_ViewController::ResetViewInput()
166 {
167   myKeys.Reset();
168   myMousePressed      = Aspect_VKeyMouse_NONE;
169   myMouseModifiers    = Aspect_VKeyFlags_NONE;
170   myMouseSingleButton = -1;
171   myUI.Dragging.ToAbort = true;
172   myMouseActiveGesture = AIS_MouseGesture_NONE;
173   myMouseClickTimer.Stop();
174   myMouseClickCounter = 0;
175 }
176
177 // =======================================================================
178 // function : FlushViewEvents
179 // purpose  :
180 // =======================================================================
181 void AIS_ViewController::FlushViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
182                                           const Handle(V3d_View)& theView,
183                                           Standard_Boolean theToHandle)
184 {
185   flushBuffers (theCtx, theView);
186   flushGestures(theCtx, theView);
187   if (theToHandle)
188   {
189     HandleViewEvents (theCtx, theView);
190   }
191 }
192
193 // =======================================================================
194 // function : flushBuffers
195 // purpose  :
196 // =======================================================================
197 void AIS_ViewController::flushBuffers (const Handle(AIS_InteractiveContext)& ,
198                                        const Handle(V3d_View)& )
199 {
200   myToAskNextFrame = false;
201
202   myGL.IsNewGesture = myUI.IsNewGesture;
203   myUI.IsNewGesture = false;
204
205   myGL.ZoomActions.Clear();
206   myGL.ZoomActions.Append (myUI.ZoomActions);
207   myUI.ZoomActions.Clear();
208
209   myGL.Orientation.ToFitAll = myUI.Orientation.ToFitAll;
210   myUI.Orientation.ToFitAll = false;
211   if (myUI.Orientation.ToSetViewOrient)
212   {
213     myUI.Orientation.ToSetViewOrient = false;
214     myGL.Orientation.ToSetViewOrient = true;
215     myGL.Orientation.ViewOrient      = myUI.Orientation.ViewOrient;
216   }
217
218   if (myUI.MoveTo.ToHilight)
219   {
220     myUI.MoveTo.ToHilight = false;
221     myGL.MoveTo.ToHilight = true;
222     myGL.MoveTo.Point     = myUI.MoveTo.Point;
223   }
224
225   {
226     myGL.Selection.Tool   = myUI.Selection.Tool;
227     myGL.Selection.IsXOR  = myUI.Selection.IsXOR;
228     myGL.Selection.Points = myUI.Selection.Points;
229     myUI.Selection.IsXOR  = false;
230     if (myUI.Selection.Tool == AIS_ViewSelectionTool_Picking)
231     {
232       myUI.Selection.Points.Clear();
233     }
234   }
235
236   if (myUI.Selection.ToApplyTool)
237   {
238     myGL.Selection.ToApplyTool = true;
239     myUI.Selection.ToApplyTool = false;
240     myUI.Selection.Points.Clear();
241   }
242
243   if (myUI.Panning.ToStart)
244   {
245     myUI.Panning.ToStart = false;
246     myGL.Panning.ToStart = true;
247     myGL.Panning.PointStart = myUI.Panning.PointStart;
248   }
249
250   if (myUI.Panning.ToPan)
251   {
252     myUI.Panning.ToPan = false;
253     myGL.Panning.ToPan = true;
254     myGL.Panning.Delta = myUI.Panning.Delta;
255   }
256
257   if (myUI.Dragging.ToAbort)
258   {
259     myUI.Dragging.ToAbort = false;
260     myGL.Dragging.ToAbort = true;
261   }
262   else if (myUI.Dragging.ToStop)
263   {
264     myUI.Dragging.ToStop = false;
265     myGL.Dragging.ToStop = true;
266   }
267   else if (myUI.Dragging.ToStart)
268   {
269     myUI.Dragging.ToStart = false;
270     myGL.Dragging.ToStart = true;
271     myGL.Dragging.PointStart = myUI.Dragging.PointStart;
272   }
273   myGL.Dragging.PointTo = myUI.Dragging.PointTo;
274
275   if (myUI.OrbitRotation.ToStart)
276   {
277     myUI.OrbitRotation.ToStart    = false;
278     myGL.OrbitRotation.ToStart    = true;
279     myGL.OrbitRotation.PointStart = myUI.OrbitRotation.PointStart;
280   }
281
282   if (myUI.OrbitRotation.ToRotate)
283   {
284     myUI.OrbitRotation.ToRotate = false;
285     myGL.OrbitRotation.ToRotate = true;
286     myGL.OrbitRotation.PointTo  = myUI.OrbitRotation.PointTo;
287   }
288
289   if (myUI.ViewRotation.ToStart)
290   {
291     myUI.ViewRotation.ToStart    = false;
292     myGL.ViewRotation.ToStart    = true;
293     myGL.ViewRotation.PointStart = myUI.ViewRotation.PointStart;
294   }
295
296   if (myUI.ViewRotation.ToRotate)
297   {
298     myUI.ViewRotation.ToRotate = false;
299     myGL.ViewRotation.ToRotate = true;
300     myGL.ViewRotation.PointTo  = myUI.ViewRotation.PointTo;
301   }
302
303   if (myUI.ZRotate.ToRotate)
304   {
305     myGL.ZRotate = myUI.ZRotate;
306     myUI.ZRotate.ToRotate = false;
307   }
308 }
309
310 // =======================================================================
311 // function : flushGestures
312 // purpose  :
313 // =======================================================================
314 void AIS_ViewController::flushGestures (const Handle(AIS_InteractiveContext)& ,
315                                         const Handle(V3d_View)& theView)
316 {
317   const Standard_Real    aTolScale = myTouchToleranceScale;
318   const Standard_Integer aTouchNb  = myTouchPoints.Extent();
319   if (myNbTouchesLast != aTouchNb)
320   {
321     myNbTouchesLast = aTouchNb;
322     myGL.IsNewGesture = true;
323   }
324   if (aTouchNb == 1) // touch
325   {
326     Aspect_Touch& aTouch = myTouchPoints.ChangeFromIndex (1);
327     if (myUpdateStartPointRot)
328     {
329       // skip rotation if have active dragged object
330       if (myNavigationMode == AIS_NavigationMode_Orbit)
331       {
332         myGL.OrbitRotation.ToStart = true;
333         myGL.OrbitRotation.PointStart = myStartRotCoord;
334       }
335       else
336       {
337         myGL.ViewRotation.ToStart = true;
338         myGL.ViewRotation.PointStart = myStartRotCoord;
339       }
340
341       myUpdateStartPointRot = false;
342       theView->Invalidate();
343     }
344
345     // rotation
346     const Standard_Real aRotTouchTol = !aTouch.IsPreciseDevice
347                                      ? aTolScale * myTouchRotationThresholdPx
348                                      : gp::Resolution();
349     if (Abs (aTouch.Delta().x()) + Abs(aTouch.Delta().y()) > aRotTouchTol)
350     {
351       const Standard_Real aRotAccel = myNavigationMode == AIS_NavigationMode_FirstPersonWalk ? myMouseAccel : myOrbitAccel;
352       if (myNavigationMode == AIS_NavigationMode_Orbit)
353       {
354         const Graphic3d_Vec2d aRotDelta = aTouch.To - myGL.OrbitRotation.PointStart;
355         myGL.OrbitRotation.ToRotate = true;
356         myGL.OrbitRotation.PointTo  = myGL.OrbitRotation.PointStart + aRotDelta * aRotAccel;
357         myGL.Dragging.PointTo.SetValues ((int )aTouch.To.x(), (int )aTouch.To.y());
358       }
359       else
360       {
361         const Graphic3d_Vec2d aRotDelta = aTouch.To - myGL.ViewRotation.PointStart;
362         myGL.ViewRotation.ToRotate = true;
363         myGL.ViewRotation.PointTo = myGL.ViewRotation.PointStart + aRotDelta * aRotAccel;
364         myGL.Dragging.PointTo.SetValues ((int )aTouch.To.x(), (int )aTouch.To.y());
365       }
366
367       aTouch.From = aTouch.To;
368     }
369   }
370   else if (aTouchNb == 2) // pinch
371   {
372     Aspect_Touch& aFirstTouch = myTouchPoints.ChangeFromIndex (1);
373     Aspect_Touch& aLastTouch  = myTouchPoints.ChangeFromIndex (2);
374     const Graphic3d_Vec2d aFrom[2] = { aFirstTouch.From, aLastTouch.From };
375     const Graphic3d_Vec2d aTo[2]   = { aFirstTouch.To,   aLastTouch.To   };
376
377     Graphic3d_Vec2d aPinchCenterStart ((aFrom[0].x() + aFrom[1].x()) / 2.0,
378                                        (aFrom[0].y() + aFrom[1].y()) / 2.0);
379
380     Standard_Real aPinchCenterXEnd = (aTo[0].x() + aTo[1].x()) / 2.0;
381     Standard_Real aPinchCenterYEnd = (aTo[0].y() + aTo[1].y()) / 2.0;
382
383     Standard_Real aPinchCenterXDev = aPinchCenterXEnd - aPinchCenterStart.x();
384     Standard_Real aPinchCenterYDev = aPinchCenterYEnd - aPinchCenterStart.y();
385
386     Standard_Real aStartSize = (aFrom[0] - aFrom[1]).Modulus();
387     Standard_Real anEndSize  = (  aTo[0] -   aTo[1]).Modulus();
388
389     Standard_Real aDeltaSize = anEndSize - aStartSize;
390
391     bool anIsClearDev = false;
392
393     if (myToAllowTouchZRotation)
394     {
395       Standard_Real A1 = aFrom[0].y() - aFrom[1].y();
396       Standard_Real B1 = aFrom[1].x() - aFrom[0].x();
397
398       Standard_Real A2 = aTo[0].y() - aTo[1].y();
399       Standard_Real B2 = aTo[1].x() - aTo[0].x();
400
401       Standard_Real aRotAngle = 0.0;
402
403       Standard_Real aDenomenator = A1*A2 + B1*B2;
404       if (aDenomenator <= Precision::Confusion())
405       {
406         aRotAngle = 0.0;
407       }
408       else
409       {
410         Standard_Real aNumerator = A1*B2 - A2*B1;
411         aRotAngle = ATan (aNumerator / aDenomenator);
412       }
413
414       if (Abs(aRotAngle) > Standard_Real(myTouchZRotationThreshold))
415       {
416         myGL.ZRotate.ToRotate = true;
417         myGL.ZRotate.Angle = aRotAngle;
418         anIsClearDev = true;
419       }
420     }
421
422     if (Abs(aDeltaSize) > aTolScale * myTouchZoomThresholdPx)
423     {
424       // zoom
425       aDeltaSize *= Standard_Real(myTouchZoomRatio);
426       Aspect_ScrollDelta aParams (Graphic3d_Vec2i (aPinchCenterStart), aDeltaSize);
427       myGL.ZoomActions.Append (aParams);
428       anIsClearDev = true;
429     }
430
431     const Standard_Real aPanTouchTol = !aFirstTouch.IsPreciseDevice
432                                      ? aTolScale * myTouchPanThresholdPx
433                                      : gp::Resolution();
434     if (Abs(aPinchCenterXDev) + Abs(aPinchCenterYDev) > aPanTouchTol)
435     {
436       // pan
437       if (myUpdateStartPointPan)
438       {
439         myGL.Panning.ToStart = true;
440         myGL.Panning.PointStart = Graphic3d_Vec2i (myStartPanCoord);
441         myUpdateStartPointPan = false;
442         theView->Invalidate();
443       }
444
445       myGL.Panning.ToPan = true;
446       myGL.Panning.Delta.x() = int( aPinchCenterXDev);
447       myGL.Panning.Delta.y() = int(-aPinchCenterYDev);
448       anIsClearDev = true;
449     }
450
451     if (anIsClearDev)
452     {
453       aFirstTouch.From = aFirstTouch.To;
454       aLastTouch .From = aLastTouch.To;
455     }
456   }
457 }
458
459 // =======================================================================
460 // function : UpdateViewOrientation
461 // purpose  :
462 // =======================================================================
463 void AIS_ViewController::UpdateViewOrientation (V3d_TypeOfOrientation theOrientation,
464                                                 bool theToFitAll)
465 {
466   myUI.Orientation.ToFitAll = theToFitAll;
467   myUI.Orientation.ToSetViewOrient = true;
468   myUI.Orientation.ViewOrient = theOrientation;
469 }
470
471 // =======================================================================
472 // function : SelectInViewer
473 // purpose  :
474 // =======================================================================
475 void AIS_ViewController::SelectInViewer (const Graphic3d_Vec2i& thePnt,
476                                          const bool theIsXOR)
477 {
478   if (myUI.Selection.Tool != AIS_ViewSelectionTool_Picking)
479   {
480     myUI.Selection.Tool = AIS_ViewSelectionTool_Picking;
481     myUI.Selection.Points.Clear();
482   }
483
484   myUI.Selection.IsXOR = theIsXOR;
485   myUI.Selection.Points.Append (thePnt);
486 }
487
488 // =======================================================================
489 // function : SelectInViewer
490 // purpose  :
491 // =======================================================================
492 void AIS_ViewController::SelectInViewer (const NCollection_Sequence<Graphic3d_Vec2i>& thePnts,
493                                          const bool theIsXOR)
494 {
495   myUI.Selection.IsXOR = theIsXOR;
496   myUI.Selection.Points = thePnts;
497   myUI.Selection.ToApplyTool = true;
498   if (thePnts.Length() == 1)
499   {
500     myUI.Selection.Tool = AIS_ViewSelectionTool_Picking;
501   }
502   else if (thePnts.Length() == 2)
503   {
504     myUI.Selection.Tool = AIS_ViewSelectionTool_RubberBand;
505   }
506   else
507   {
508     myUI.Selection.Tool = AIS_ViewSelectionTool_Polygon;
509   }
510 }
511
512 // =======================================================================
513 // function : UpdateRubberBand
514 // purpose  :
515 // =======================================================================
516 void AIS_ViewController::UpdateRubberBand (const Graphic3d_Vec2i& thePntFrom,
517                                            const Graphic3d_Vec2i& thePntTo,
518                                            const bool theIsXOR)
519 {
520   myUI.Selection.Tool = AIS_ViewSelectionTool_RubberBand;
521   myUI.Selection.IsXOR = theIsXOR;
522   myUI.Selection.Points.Clear();
523   myUI.Selection.Points.Append (thePntFrom);
524   myUI.Selection.Points.Append (thePntTo);
525 }
526
527 // =======================================================================
528 // function : UpdatePolySelection
529 // purpose  :
530 // =======================================================================
531 void AIS_ViewController::UpdatePolySelection (const Graphic3d_Vec2i& thePnt,
532                                               bool theToAppend)
533 {
534   if (myUI.Selection.Tool != AIS_ViewSelectionTool_Polygon)
535   {
536     myUI.Selection.Tool = AIS_ViewSelectionTool_Polygon;
537     myUI.Selection.Points.Clear();
538   }
539
540   if (myUI.Selection.Points.IsEmpty())
541   {
542     myUI.Selection.Points.Append (thePnt);
543   }
544   else if (theToAppend
545         && myUI.Selection.Points.Last() != thePnt)
546   {
547     myUI.Selection.Points.Append (thePnt);
548   }
549   else
550   {
551     myUI.Selection.Points.ChangeLast() = thePnt;
552   }
553 }
554
555 // =======================================================================
556 // function : UpdateZoom
557 // purpose  :
558 // =======================================================================
559 bool AIS_ViewController::UpdateZoom (const Aspect_ScrollDelta& theDelta)
560 {
561   if (!myUI.ZoomActions.IsEmpty())
562   {
563     if (myUI.ZoomActions.ChangeLast().Point == theDelta.Point)
564     {
565       myUI.ZoomActions.ChangeLast().Delta += theDelta.Delta;
566       return false;
567     }
568   }
569
570   myUI.ZoomActions.Append (theDelta);
571   return true;
572 }
573
574 // =======================================================================
575 // function : UpdateZRotation
576 // purpose  :
577 // =======================================================================
578 bool AIS_ViewController::UpdateZRotation (double theAngle)
579 {
580   if (!ToAllowTouchZRotation())
581   {
582     return false;
583   }
584
585   myUI.ZRotate.Angle = myUI.ZRotate.ToRotate
586                      ? myUI.ZRotate.Angle + theAngle
587                      : theAngle;
588   if (myUI.ZRotate.ToRotate)
589   {
590     return false;
591   }
592   myUI.ZRotate.ToRotate = true;
593   return true;
594 }
595
596 // =======================================================================
597 // function : UpdateMouseScroll
598 // purpose  :
599 // =======================================================================
600 bool AIS_ViewController::UpdateMouseScroll (const Aspect_ScrollDelta& theDelta)
601 {
602   Aspect_ScrollDelta aDelta = theDelta;
603   aDelta.Delta *= myScrollZoomRatio;
604   return UpdateZoom (aDelta);
605 }
606
607 // =======================================================================
608 // function : UpdateMouseClick
609 // purpose  :
610 // =======================================================================
611 bool AIS_ViewController::UpdateMouseClick (const Graphic3d_Vec2i& thePoint,
612                                            Aspect_VKeyMouse theButton,
613                                            Aspect_VKeyFlags theModifiers,
614                                            bool theIsDoubleClick)
615 {
616   (void )theIsDoubleClick;
617   if (theButton == Aspect_VKeyMouse_LeftButton)
618   {
619     SelectInViewer (thePoint, (theModifiers & Aspect_VKeyFlags_SHIFT) != 0);
620     return true;
621   }
622   return false;
623 }
624
625 // =======================================================================
626 // function : UpdateMouseButtons
627 // purpose  :
628 // =======================================================================
629 bool AIS_ViewController::UpdateMouseButtons (const Graphic3d_Vec2i& thePoint,
630                                              Aspect_VKeyMouse theButtons,
631                                              Aspect_VKeyFlags theModifiers,
632                                              bool theIsEmulated)
633 {
634   bool toUpdateView = false;
635   const double aTolClick = (theIsEmulated ? myTouchToleranceScale : 1.0) * myMouseClickThreshold;
636   if (theButtons == Aspect_VKeyMouse_NONE
637    && myMouseSingleButton > 0)
638   {
639     const Graphic3d_Vec2i aDelta = thePoint - myMousePressPoint;
640     if (double(aDelta.cwiseAbs().maxComp()) < aTolClick)
641     {
642       ++myMouseClickCounter;
643       const bool isDoubleClick = myMouseClickCounter == 2
644                               && myMouseClickTimer.IsStarted()
645                               && myMouseClickTimer.ElapsedTime() <= myMouseDoubleClickInt;
646
647       myMouseClickTimer.Stop();
648       myMouseClickTimer.Reset();
649       myMouseClickTimer.Start();
650       if (isDoubleClick)
651       {
652         myMouseClickCounter = 0;
653       }
654       toUpdateView = UpdateMouseClick (thePoint, (Aspect_VKeyMouse )myMouseSingleButton, theModifiers, isDoubleClick) || toUpdateView;
655     }
656     else
657     {
658       myMouseClickTimer.Stop();
659       myMouseClickCounter = 0;
660       myMouseStopDragOnUnclick = false;
661       myUI.Dragging.ToStop = true;
662       toUpdateView = true;
663     }
664     myMouseSingleButton = -1;
665   }
666   else if (theButtons == Aspect_VKeyMouse_NONE)
667   {
668     myMouseSingleButton = -1;
669     if (myMouseStopDragOnUnclick)
670     {
671       myMouseStopDragOnUnclick = false;
672       myUI.Dragging.ToStop = true;
673       toUpdateView = true;
674     }
675   }
676   else if (myMouseSingleButton == -1)
677   {
678     if ((theButtons & Aspect_VKeyMouse_LeftButton) == Aspect_VKeyMouse_LeftButton)
679     {
680       myMouseSingleButton = Aspect_VKeyMouse_LeftButton;
681     }
682     else if ((theButtons & Aspect_VKeyMouse_RightButton) == Aspect_VKeyMouse_RightButton)
683     {
684       myMouseSingleButton = Aspect_VKeyMouse_RightButton;
685     }
686     else if ((theButtons & Aspect_VKeyMouse_MiddleButton) == Aspect_VKeyMouse_MiddleButton)
687     {
688       myMouseSingleButton = Aspect_VKeyMouse_MiddleButton;
689     }
690     else
691     {
692       myMouseSingleButton = 0;
693     }
694     if (myMouseSingleButton != 0)
695     {
696       if (myMouseClickCounter == 1)
697       {
698         const Graphic3d_Vec2i aDelta = thePoint - myMousePressPoint;
699         if (double(aDelta.cwiseAbs().maxComp()) >= aTolClick)
700         {
701           myMouseClickTimer.Stop();
702           myMouseClickCounter = 0;
703         }
704       }
705       myMousePressPoint = thePoint;
706     }
707   }
708   else
709   {
710     myMouseSingleButton = 0;
711
712     myUI.Dragging.ToAbort = true;
713     toUpdateView = true;
714   }
715
716   const AIS_MouseGesture aPrevGesture = myMouseActiveGesture;
717   myMouseModifiers = theModifiers;
718   myMousePressed   = theButtons;
719   if (theIsEmulated
720    || myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
721   {
722     myMouseActiveIdleRotation = false;
723     myMouseActiveGesture = AIS_MouseGesture_NONE;
724     if (theButtons != 0)
725     {
726       myMousePressPoint    = thePoint;
727       myMouseProgressPoint = myMousePressPoint;
728     }
729
730     if (myMouseGestureMap.Find (theButtons | theModifiers, myMouseActiveGesture))
731     {
732       switch (myMouseActiveGesture)
733       {
734         case AIS_MouseGesture_RotateView:
735         case AIS_MouseGesture_RotateOrbit:
736         {
737           if (myToAllowRotation)
738           {
739             myUpdateStartPointRot = true;
740           }
741           else
742           {
743             myMouseActiveGesture = AIS_MouseGesture_NONE;
744           }
745           break;
746         }
747         case AIS_MouseGesture_Pan:
748         {
749           if (myToAllowPanning)
750           {
751             myUpdateStartPointPan = true;
752           }
753           else
754           {
755             myMouseActiveGesture = AIS_MouseGesture_NONE;
756           }
757           break;
758         }
759         case AIS_MouseGesture_Zoom:
760         case AIS_MouseGesture_ZoomWindow:
761         {
762           if (!myToAllowZooming)
763           {
764             myMouseActiveGesture = AIS_MouseGesture_NONE;
765           }
766           break;
767         }
768         case AIS_MouseGesture_SelectRectangle:
769         {
770           break;
771         }
772         case AIS_MouseGesture_SelectLasso:
773         {
774           UpdatePolySelection (thePoint, true);
775           break;
776         }
777         case AIS_MouseGesture_NONE:
778         {
779           break;
780         }
781       }
782     }
783
784     if (theButtons == Aspect_VKeyMouse_LeftButton
785      && theModifiers == Aspect_VKeyFlags_NONE
786      && myToAllowDragging)
787     {
788       myUI.Dragging.ToStart = true;
789       myUI.Dragging.PointStart = thePoint;
790     }
791   }
792
793   if (aPrevGesture != myMouseActiveGesture)
794   {
795     if (aPrevGesture == AIS_MouseGesture_SelectRectangle
796      || aPrevGesture == AIS_MouseGesture_SelectLasso
797      || aPrevGesture == AIS_MouseGesture_ZoomWindow)
798     {
799       myUI.Selection.ToApplyTool = true;
800     }
801
802     myUI.IsNewGesture = true;
803     toUpdateView = true;
804   }
805   return toUpdateView;
806 }
807
808 // =======================================================================
809 // function : UpdateMousePosition
810 // purpose  :
811 // =======================================================================
812 bool AIS_ViewController::UpdateMousePosition (const Graphic3d_Vec2i& thePoint,
813                                               Aspect_VKeyMouse theButtons,
814                                               Aspect_VKeyFlags theModifiers,
815                                               bool theIsEmulated)
816 {
817   myMousePositionLast = thePoint;
818   if (myMouseSingleButton > 0)
819   {
820     const double aTolClick = (theIsEmulated ? myTouchToleranceScale : 1.0) * myMouseClickThreshold;
821     const Graphic3d_Vec2i aPressDelta = thePoint - myMousePressPoint;
822     if (double(aPressDelta.cwiseAbs().maxComp()) >= aTolClick)
823     {
824       myMouseClickTimer.Stop();
825       myMouseClickCounter = 0;
826       myMouseSingleButton = -1;
827       myMouseStopDragOnUnclick = true;
828     }
829   }
830
831   bool toUpdateView = false;
832   Graphic3d_Vec2i aDelta = thePoint - myMouseProgressPoint;
833   if (!theIsEmulated
834     && myNavigationMode == AIS_NavigationMode_FirstPersonWalk)
835   {
836     if (!myMouseActiveIdleRotation
837       || myMouseActiveGesture != AIS_MouseGesture_RotateView)
838     {
839       myMouseActiveIdleRotation = true;
840       myMouseActiveGesture = AIS_MouseGesture_RotateView;
841       myMousePressPoint     = thePoint;
842       myMouseProgressPoint  = thePoint;
843       myUpdateStartPointRot = false;
844       myUI.ViewRotation.ToStart = true;
845       myUI.ViewRotation.PointStart.SetValues (thePoint.x(), thePoint.y());
846       myUI.ViewRotation.ToRotate = false;
847       aDelta.SetValues (0, 0);
848     }
849   }
850   else
851   {
852     if (myMouseActiveIdleRotation
853      && myMouseActiveGesture == AIS_MouseGesture_RotateView)
854     {
855       myMouseActiveGesture = AIS_MouseGesture_NONE;
856     }
857     myMouseActiveIdleRotation = false;
858   }
859
860   if (myMouseModifiers != theModifiers
861    && UpdateMouseButtons (thePoint, theButtons, theModifiers, theIsEmulated))
862   {
863     toUpdateView = true;
864   }
865
866   switch (myMouseActiveGesture)
867   {
868     case AIS_MouseGesture_SelectRectangle:
869     case AIS_MouseGesture_ZoomWindow:
870     {
871       UpdateRubberBand (myMousePressPoint, thePoint);
872       if (myMouseActiveGesture == AIS_MouseGesture_ZoomWindow)
873       {
874         myUI.Selection.Tool = AIS_ViewSelectionTool_ZoomWindow;
875       }
876       toUpdateView = true;
877       break;
878     }
879     case AIS_MouseGesture_SelectLasso:
880     {
881       UpdatePolySelection (thePoint, true);
882       toUpdateView = true;
883       break;
884     }
885     case AIS_MouseGesture_RotateOrbit:
886     case AIS_MouseGesture_RotateView:
887     {
888       if (!myToAllowRotation)
889       {
890         break;
891       }
892       if (myUpdateStartPointRot)
893       {
894         if (myMouseActiveGesture == AIS_MouseGesture_RotateOrbit)
895         {
896           myUI.OrbitRotation.ToStart = true;
897           myUI.OrbitRotation.PointStart.SetValues (myMousePressPoint.x(), myMousePressPoint.y());
898         }
899         else
900         {
901           myUI.ViewRotation.ToStart = true;
902           myUI.ViewRotation.PointStart.SetValues (myMousePressPoint.x(), myMousePressPoint.y());
903         }
904         myUpdateStartPointRot = false;
905       }
906
907       const double aRotTol = theIsEmulated
908                            ? double(myTouchToleranceScale) * myTouchRotationThresholdPx
909                            : 0.0;
910       if (double (Abs (aDelta.x()) + Abs (aDelta.y())) > aRotTol)
911       {
912         const double aRotAccel = myNavigationMode == AIS_NavigationMode_FirstPersonWalk ? myMouseAccel : myOrbitAccel;
913         const Graphic3d_Vec2i aRotDelta = thePoint - myMousePressPoint;
914         if (myMouseActiveGesture == AIS_MouseGesture_RotateOrbit)
915         {
916           myUI.OrbitRotation.ToRotate = true;
917           myUI.OrbitRotation.PointTo = Graphic3d_Vec2d (myMousePressPoint.x(), myMousePressPoint.y())
918                                      + Graphic3d_Vec2d (aRotDelta.x(), aRotDelta.y()) * aRotAccel;
919         }
920         else
921         {
922           myUI.ViewRotation.ToRotate = true;
923           myUI.ViewRotation.PointTo = Graphic3d_Vec2d (myMousePressPoint.x(), myMousePressPoint.y())
924                                     + Graphic3d_Vec2d (aRotDelta.x(), aRotDelta.y()) * aRotAccel;
925         }
926         myUI.Dragging.PointTo = thePoint;
927
928         myMouseProgressPoint = thePoint;
929         toUpdateView = true;
930       }
931       break;
932     }
933     case AIS_MouseGesture_Zoom:
934     {
935       if (!myToAllowZooming)
936       {
937         break;
938       }
939       const double aZoomTol = theIsEmulated
940                             ? double(myTouchToleranceScale) * myTouchZoomThresholdPx
941                             : 0.0;
942       if (double (Abs (aDelta.x())) > aZoomTol)
943       {
944         if (UpdateZoom (Aspect_ScrollDelta (aDelta.x())))
945         {
946           toUpdateView = true;
947         }
948         myMouseProgressPoint = thePoint;
949       }
950       break;
951     }
952     case AIS_MouseGesture_Pan:
953     {
954       if (!myToAllowPanning)
955       {
956         break;
957       }
958       const double aPanTol = theIsEmulated
959                            ? double(myTouchToleranceScale) * myTouchPanThresholdPx
960                            : 0.0;
961       if (double (Abs (aDelta.x()) + Abs (aDelta.y())) > aPanTol)
962       {
963         if (myUpdateStartPointPan)
964         {
965           myUI.Panning.ToStart = true;
966           myUI.Panning.PointStart.SetValues (myMousePressPoint.x(), myMousePressPoint.y());
967           myUpdateStartPointPan = false;
968         }
969
970         aDelta.y() = -aDelta.y();
971         myMouseProgressPoint = thePoint;
972         if (myUI.Panning.ToPan)
973         {
974           myUI.Panning.Delta += aDelta;
975         }
976         else
977         {
978           myUI.Panning.ToPan = true;
979           myUI.Panning.Delta = aDelta;
980         }
981         toUpdateView = true;
982       }
983       break;
984     }
985     default:
986     {
987       break;
988     }
989   }
990
991   if (theButtons == Aspect_VKeyMouse_NONE
992   &&  myNavigationMode != AIS_NavigationMode_FirstPersonWalk
993   && !theIsEmulated
994   && !HasTouchPoints()
995   &&  myToAllowHighlight)
996   {
997     myUI.MoveTo.ToHilight = true;
998     myUI.MoveTo.Point = thePoint;
999     toUpdateView = true;
1000   }
1001   return toUpdateView;
1002 }
1003
1004 // =======================================================================
1005 // function : AddTouchPoint
1006 // purpose  :
1007 // =======================================================================
1008 void AIS_ViewController::AddTouchPoint (Standard_Size theId,
1009                                         const Graphic3d_Vec2d& thePnt,
1010                                         Standard_Boolean theClearBefore)
1011 {
1012   myUI.MoveTo.ToHilight = false;
1013   if (theClearBefore)
1014   {
1015     RemoveTouchPoint ((Standard_Size )-1);
1016   }
1017
1018   myTouchPoints.Add (theId, Aspect_Touch (thePnt, false));
1019   if (myTouchPoints.Extent() == 1)
1020   {
1021     myUpdateStartPointRot = true;
1022     myStartRotCoord = thePnt;
1023     if (myToAllowDragging)
1024     {
1025       myUI.Dragging.ToStart = true;
1026       myUI.Dragging.PointStart.SetValues ((int )thePnt.x(), (int )thePnt.y());
1027     }
1028   }
1029   else if (myTouchPoints.Extent() == 2)
1030   {
1031     myUI.Dragging.ToAbort = true;
1032
1033     myUpdateStartPointPan = true;
1034     myStartPanCoord = thePnt;
1035   }
1036   myUI.IsNewGesture = true;
1037 }
1038
1039 // =======================================================================
1040 // function : RemoveTouchPoint
1041 // purpose  :
1042 // =======================================================================
1043 bool AIS_ViewController::RemoveTouchPoint (Standard_Size theId,
1044                                            Standard_Boolean theClearSelectPnts)
1045 {
1046   if (theId == (Standard_Size )-1)
1047   {
1048     myTouchPoints.Clear (false);
1049   }
1050   else
1051   {
1052     const Standard_Integer anOldExtent = myTouchPoints.Extent();
1053     myTouchPoints.RemoveKey (theId);
1054     if (myTouchPoints.Extent() == anOldExtent)
1055     {
1056       return false;
1057     }
1058   }
1059
1060   if (myTouchPoints.Extent() == 1)
1061   {
1062     // avoid incorrect transition from pinch to one finger
1063     Aspect_Touch& aFirstTouch = myTouchPoints.ChangeFromIndex (1);
1064     aFirstTouch.To = aFirstTouch.From;
1065
1066     myStartRotCoord = aFirstTouch.To;
1067     myUpdateStartPointRot = true;
1068   }
1069   else if (myTouchPoints.Extent() == 2)
1070   {
1071     myStartPanCoord = myTouchPoints.FindFromIndex (1).To;
1072     myUpdateStartPointPan = true;
1073   }
1074   else if (myTouchPoints.IsEmpty())
1075   {
1076     if (theClearSelectPnts)
1077     {
1078       myUI.Selection.ToApplyTool = true;
1079     }
1080
1081     myUI.Dragging.ToStop = true;
1082   }
1083   myUI.IsNewGesture = true;
1084   return true;
1085 }
1086
1087 // =======================================================================
1088 // function : UpdateTouchPoint
1089 // purpose  :
1090 // =======================================================================
1091 void AIS_ViewController::UpdateTouchPoint (Standard_Size theId,
1092                                            const Graphic3d_Vec2d& thePnt)
1093 {
1094   if (Aspect_Touch* aTouch = myTouchPoints.ChangeSeek (theId))
1095   {
1096     aTouch->To = thePnt;
1097   }
1098   else
1099   {
1100     AddTouchPoint (theId, thePnt);
1101   }
1102 }
1103
1104 // =======================================================================
1105 // function : Update3dMouse
1106 // purpose  :
1107 // =======================================================================
1108 bool AIS_ViewController::Update3dMouse (const WNT_HIDSpaceMouse& theEvent)
1109 {
1110   bool toUpdate = false;
1111   toUpdate = update3dMouseTranslation (theEvent) || toUpdate;
1112   toUpdate = update3dMouseRotation (theEvent) || toUpdate;
1113   toUpdate = update3dMouseKeys (theEvent) || toUpdate;
1114   return toUpdate;
1115 }
1116
1117 // =======================================================================
1118 // function : update3dMouseTranslation
1119 // purpose  :
1120 // =======================================================================
1121 bool AIS_ViewController::update3dMouseTranslation (const WNT_HIDSpaceMouse& theEvent)
1122 {
1123   if (!theEvent.IsTranslation())
1124   {
1125     return false;
1126   }
1127
1128   bool isIdle = true;
1129   const double aTimeStamp = EventTime();
1130   const Graphic3d_Vec3d aTrans = theEvent.Translation (isIdle, my3dMouseIsQuadric) * my3dMouseAccelTrans;
1131   myKeys.KeyFromAxis (Aspect_VKey_NavSlideLeft, Aspect_VKey_NavSlideRight, aTimeStamp, aTrans.x());
1132   myKeys.KeyFromAxis (Aspect_VKey_NavForward,   Aspect_VKey_NavBackward,   aTimeStamp, aTrans.y());
1133   myKeys.KeyFromAxis (Aspect_VKey_NavSlideUp,   Aspect_VKey_NavSlideDown,  aTimeStamp, aTrans.z());
1134   return true;
1135 }
1136
1137 // =======================================================================
1138 // function : update3dMouseRotation
1139 // purpose  :
1140 // =======================================================================
1141 bool AIS_ViewController::update3dMouseRotation (const WNT_HIDSpaceMouse& theEvent)
1142 {
1143   if (!theEvent.IsRotation()
1144    || !myToAllowRotation)
1145   {
1146     return false;
1147   }
1148
1149   bool isIdle = true, toUpdate = false;
1150   const double aTimeStamp = EventTime();
1151   const Graphic3d_Vec3d aRot3 = theEvent.Rotation (isIdle, my3dMouseIsQuadric) * my3dMouseAccelRotate;
1152   if (!my3dMouseNoRotate.x())
1153   {
1154     KeyFromAxis (Aspect_VKey_NavLookUp,   Aspect_VKey_NavLookDown,  aTimeStamp, !my3dMouseToReverse.x() ? aRot3.x() : -aRot3.x());
1155     toUpdate = true;
1156   }
1157   if (!my3dMouseNoRotate.y())
1158   {
1159     KeyFromAxis (Aspect_VKey_NavRollCW,   Aspect_VKey_NavRollCCW,   aTimeStamp, !my3dMouseToReverse.y() ? aRot3.y() : -aRot3.y());
1160     toUpdate = true;
1161   }
1162   if (!my3dMouseNoRotate.z())
1163   {
1164     KeyFromAxis (Aspect_VKey_NavLookLeft, Aspect_VKey_NavLookRight, aTimeStamp, !my3dMouseToReverse.z() ? aRot3.z() : -aRot3.z());
1165     toUpdate = true;
1166   }
1167   return toUpdate;
1168 }
1169
1170 // =======================================================================
1171 // function : update3dMouseKeys
1172 // purpose  :
1173 // =======================================================================
1174 bool AIS_ViewController::update3dMouseKeys (const WNT_HIDSpaceMouse& theEvent)
1175 {
1176   bool toUpdate = false;
1177   const double aTimeStamp = EventTime();
1178   if (theEvent.IsKeyState())
1179   {
1180     const uint32_t aKeyState = theEvent.KeyState();
1181     for (unsigned short aKeyBit = 0; aKeyBit < 32; ++aKeyBit)
1182     {
1183       const bool isPressed  = (aKeyState & (1 << aKeyBit)) != 0;
1184       const bool isReleased = my3dMouseButtonState[aKeyBit] && !isPressed;
1185       //const bool isRepeated = my3dMouseButtonState[aKeyBit] &&  isPressed;
1186       my3dMouseButtonState[aKeyBit] = isPressed;
1187       if (!isReleased && !isPressed)
1188       {
1189         continue;
1190       }
1191
1192       const Aspect_VKey aVKey = theEvent.HidToSpaceKey (aKeyBit);
1193       if (aVKey != Aspect_VKey_UNKNOWN)
1194       {
1195         toUpdate = true;
1196         if (isPressed)
1197         {
1198           KeyDown (aVKey, aTimeStamp);
1199         }
1200         else
1201         {
1202           KeyUp (aVKey, aTimeStamp);
1203         }
1204       }
1205     }
1206   }
1207   return toUpdate;
1208 }
1209
1210 // =======================================================================
1211 // function : SetNavigationMode
1212 // purpose  :
1213 // =======================================================================
1214 void AIS_ViewController::SetNavigationMode (AIS_NavigationMode theMode)
1215 {
1216   myNavigationMode = theMode;
1217
1218   // abort rotation
1219   myUI.OrbitRotation.ToStart  = false;
1220   myUI.OrbitRotation.ToRotate = false;
1221   myUI.ViewRotation.ToStart   = false;
1222   myUI.ViewRotation.ToRotate  = false;
1223 }
1224
1225 // =======================================================================
1226 // function : KeyDown
1227 // purpose  :
1228 // =======================================================================
1229 void AIS_ViewController::KeyDown (Aspect_VKey theKey,
1230                                   double theTime,
1231                                   double thePressure)
1232 {
1233   myKeys.KeyDown (theKey, theTime, thePressure);
1234 }
1235
1236 // =======================================================================
1237 // function : KeyUp
1238 // purpose  :
1239 // =======================================================================
1240 void AIS_ViewController::KeyUp (Aspect_VKey theKey,
1241                                 double theTime)
1242 {
1243   myKeys.KeyUp (theKey, theTime);
1244 }
1245
1246 // =======================================================================
1247 // function : KeyFromAxis
1248 // purpose  :
1249 // =======================================================================
1250 void AIS_ViewController::KeyFromAxis (Aspect_VKey theNegative,
1251                                       Aspect_VKey thePositive,
1252                                       double theTime,
1253                                       double thePressure)
1254 {
1255   myKeys.KeyFromAxis (theNegative, thePositive, theTime, thePressure);
1256 }
1257
1258 // =======================================================================
1259 // function : FetchNavigationKeys
1260 // purpose  :
1261 // =======================================================================
1262 AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRatio,
1263                                                        Standard_Real theRunRatio)
1264 {
1265   AIS_WalkDelta aWalk;
1266
1267   // navigation keys
1268   double aPrevEventTime = 0.0, aNewEventTime = 0.0;
1269   updateEventsTime (aPrevEventTime, aNewEventTime);
1270
1271   double aDuration = 0.0, aPressure = 1.0;
1272   if (Abs (myThrustSpeed) > gp::Resolution())
1273   {
1274     if (myHasThrust)
1275     {
1276       aWalk[AIS_WalkTranslation_Forward].Value = myThrustSpeed * (aNewEventTime - aPrevEventTime);
1277     }
1278     myHasThrust = true;
1279     myToAskNextFrame = true;
1280   }
1281   else
1282   {
1283     myHasThrust = false;
1284   }
1285
1286   aWalk.SetRunning (theRunRatio > 1.0
1287                  && myKeys.IsKeyDown (Aspect_VKey_Shift));
1288   if (myKeys.HoldDuration (Aspect_VKey_NavJump, aNewEventTime, aDuration))
1289   {
1290     myKeys.KeyUp (Aspect_VKey_NavJump, aNewEventTime);
1291     aWalk.SetJumping (true);
1292   }
1293   if (!aWalk.IsJumping()
1294    && theCrouchRatio < 1.0
1295    && myKeys.HoldDuration (Aspect_VKey_NavCrouch, aNewEventTime, aDuration))
1296   {
1297     aWalk.SetRunning (false);
1298     aWalk.SetCrouching (true);
1299   }
1300
1301   const double aMaxDuration = aNewEventTime - aPrevEventTime;
1302   const double aRunRatio = aWalk.IsRunning()
1303                          ? theRunRatio
1304                          : aWalk.IsCrouching()
1305                           ? theCrouchRatio
1306                           : 1.0;
1307   if (myKeys.HoldDuration (Aspect_VKey_NavForward, aNewEventTime, aDuration, aPressure))
1308   {
1309     double aProgress = Abs (Min (aMaxDuration, aDuration));
1310     aProgress *= aRunRatio;
1311     aWalk[AIS_WalkTranslation_Forward].Value += aProgress;
1312     aWalk[AIS_WalkTranslation_Forward].Pressure = aPressure;
1313     aWalk[AIS_WalkTranslation_Forward].Duration = aDuration;
1314   }
1315   if (myKeys.HoldDuration (Aspect_VKey_NavBackward, aNewEventTime, aDuration, aPressure))
1316   {
1317     double aProgress = Abs (Min (aMaxDuration, aDuration));
1318     aProgress *= aRunRatio;
1319     aWalk[AIS_WalkTranslation_Forward].Value += -aProgress;
1320     aWalk[AIS_WalkTranslation_Forward].Pressure = aPressure;
1321     aWalk[AIS_WalkTranslation_Forward].Duration = aDuration;
1322   }
1323   if (myKeys.HoldDuration (Aspect_VKey_NavSlideLeft, aNewEventTime, aDuration, aPressure))
1324   {
1325     double aProgress = Abs (Min (aMaxDuration, aDuration));
1326     aProgress *= aRunRatio;
1327     aWalk[AIS_WalkTranslation_Side].Value = -aProgress;
1328     aWalk[AIS_WalkTranslation_Side].Pressure = aPressure;
1329     aWalk[AIS_WalkTranslation_Side].Duration = aDuration;
1330   }
1331   if (myKeys.HoldDuration (Aspect_VKey_NavSlideRight, aNewEventTime, aDuration, aPressure))
1332   {
1333     double aProgress = Abs (Min (aMaxDuration, aDuration));
1334     aProgress *= aRunRatio;
1335     aWalk[AIS_WalkTranslation_Side].Value = aProgress;
1336     aWalk[AIS_WalkTranslation_Side].Pressure = aPressure;
1337     aWalk[AIS_WalkTranslation_Side].Duration = aDuration;
1338   }
1339   if (myKeys.HoldDuration (Aspect_VKey_NavLookLeft, aNewEventTime, aDuration, aPressure))
1340   {
1341     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1342     aWalk[AIS_WalkRotation_Yaw].Value = aProgress;
1343     aWalk[AIS_WalkRotation_Yaw].Pressure = aPressure;
1344     aWalk[AIS_WalkRotation_Yaw].Duration = aDuration;
1345   }
1346   if (myKeys.HoldDuration (Aspect_VKey_NavLookRight, aNewEventTime, aDuration, aPressure))
1347   {
1348     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1349     aWalk[AIS_WalkRotation_Yaw].Value = -aProgress;
1350     aWalk[AIS_WalkRotation_Yaw].Pressure = aPressure;
1351     aWalk[AIS_WalkRotation_Yaw].Duration = aDuration;
1352   }
1353   if (myKeys.HoldDuration (Aspect_VKey_NavLookUp, aNewEventTime, aDuration, aPressure))
1354   {
1355     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1356     aWalk[AIS_WalkRotation_Pitch].Value = !myToInvertPitch ? -aProgress : aProgress;
1357     aWalk[AIS_WalkRotation_Pitch].Pressure = aPressure;
1358     aWalk[AIS_WalkRotation_Pitch].Duration = aDuration;
1359   }
1360   if (myKeys.HoldDuration (Aspect_VKey_NavLookDown, aNewEventTime, aDuration, aPressure))
1361   {
1362     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1363     aWalk[AIS_WalkRotation_Pitch].Value = !myToInvertPitch ? aProgress : -aProgress;
1364     aWalk[AIS_WalkRotation_Pitch].Pressure = aPressure;
1365     aWalk[AIS_WalkRotation_Pitch].Duration = aDuration;
1366   }
1367   if (myKeys.HoldDuration (Aspect_VKey_NavRollCCW, aNewEventTime, aDuration, aPressure))
1368   {
1369     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1370     aWalk[AIS_WalkRotation_Roll].Value = -aProgress;
1371     aWalk[AIS_WalkRotation_Roll].Pressure = aPressure;
1372     aWalk[AIS_WalkRotation_Roll].Duration = aDuration;
1373   }
1374   if (myKeys.HoldDuration (Aspect_VKey_NavRollCW, aNewEventTime, aDuration, aPressure))
1375   {
1376     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
1377     aWalk[AIS_WalkRotation_Roll].Value = aProgress;
1378     aWalk[AIS_WalkRotation_Roll].Pressure = aPressure;
1379     aWalk[AIS_WalkRotation_Roll].Duration = aDuration;
1380   }
1381   if (myKeys.HoldDuration (Aspect_VKey_NavSlideUp, aNewEventTime, aDuration, aPressure))
1382   {
1383     double aProgress = Abs (Min (aMaxDuration, aDuration));
1384     aWalk[AIS_WalkTranslation_Up].Value = aProgress;
1385     aWalk[AIS_WalkTranslation_Up].Pressure = aPressure;
1386     aWalk[AIS_WalkTranslation_Up].Duration = aDuration;
1387   }
1388   if (myKeys.HoldDuration (Aspect_VKey_NavSlideDown, aNewEventTime, aDuration, aPressure))
1389   {
1390     double aProgress = Abs (Min (aMaxDuration, aDuration));
1391     aWalk[AIS_WalkTranslation_Up].Value = -aProgress;
1392     aWalk[AIS_WalkTranslation_Up].Pressure = aPressure;
1393     aWalk[AIS_WalkTranslation_Up].Duration = aDuration;
1394   }
1395   return aWalk;
1396 }
1397
1398 // =======================================================================
1399 // function : AbortViewAnimation
1400 // purpose  :
1401 // =======================================================================
1402 void AIS_ViewController::AbortViewAnimation()
1403 {
1404   if (!myViewAnimation.IsNull()
1405    && !myViewAnimation->IsStopped())
1406   {
1407     myViewAnimation->Stop();
1408     myViewAnimation->SetView (Handle(V3d_View)());
1409   }
1410 }
1411
1412 // =======================================================================
1413 // function : handlePanning
1414 // purpose  :
1415 // =======================================================================
1416 void AIS_ViewController::handlePanning (const Handle(V3d_View)& theView)
1417 {
1418   if (!myGL.Panning.ToPan
1419    || !myToAllowPanning)
1420   {
1421     return;
1422   }
1423
1424   AbortViewAnimation();
1425
1426   const Handle(Graphic3d_Camera)& aCam = theView->Camera();
1427   if (aCam->IsOrthographic()
1428   || !hasPanningAnchorPoint())
1429   {
1430     theView->Pan (myGL.Panning.Delta.x(), myGL.Panning.Delta.y());
1431     theView->Invalidate();
1432     theView->View()->SynchronizeXRPosedToBaseCamera();
1433     return;
1434   }
1435
1436   Graphic3d_Vec2i aWinSize;
1437   theView->Window()->Size (aWinSize.x(), aWinSize.y());
1438
1439   const gp_Dir& aDir = aCam->Direction();
1440   const gp_Ax3 aCameraCS (aCam->Center(), aDir.Reversed(), aDir ^ aCam->Up());
1441   const gp_XYZ anEyeToPnt = myPanPnt3d.XYZ() - aCam->Eye().XYZ();
1442   const gp_Pnt aViewDims = aCam->ViewDimensions (anEyeToPnt.Dot (aCam->Direction().XYZ())); // view dimensions at 3D point
1443   const Graphic3d_Vec2d aDxy (-aViewDims.X() * myGL.Panning.Delta.x() / double(aWinSize.x()),
1444                               -aViewDims.X() * myGL.Panning.Delta.y() / double(aWinSize.x()));
1445
1446   //theView->Translate (aCam, aDxy.x(), aDxy.y());
1447   gp_Trsf aPanTrsf;
1448   const gp_Vec aCameraPan = gp_Vec (aCameraCS.XDirection()) * aDxy.x()
1449                           + gp_Vec (aCameraCS.YDirection()) * aDxy.y();
1450   aPanTrsf.SetTranslation (aCameraPan);
1451   aCam->Transform (aPanTrsf);
1452   theView->Invalidate();
1453   theView->View()->SynchronizeXRPosedToBaseCamera();
1454 }
1455
1456 // =======================================================================
1457 // function : handleZRotate
1458 // purpose  :
1459 // =======================================================================
1460 void AIS_ViewController::handleZRotate (const Handle(V3d_View)& theView)
1461 {
1462   if (!myGL.ZRotate.ToRotate
1463    || !myToAllowRotation)
1464   {
1465     return;
1466   }
1467
1468   AbortViewAnimation();
1469
1470   Graphic3d_Vec2i aViewPort;
1471   theView->Window()->Size (aViewPort.x(), aViewPort.y());
1472   Graphic3d_Vec2d aRotPnt (0.99 * aViewPort.x(),
1473                            0.5  * aViewPort.y());
1474   theView->StartRotation (int(aRotPnt.x()), int(aRotPnt.y()), 0.4);
1475   aRotPnt.y() += myGL.ZRotate.Angle * aViewPort.y();
1476   theView->Rotation (int(aRotPnt.x()), int(aRotPnt.y()));
1477   theView->Invalidate();
1478   theView->View()->SynchronizeXRPosedToBaseCamera();
1479 }
1480
1481 // =======================================================================
1482 // function : handleZoom
1483 // purpose  :
1484 // =======================================================================
1485 void AIS_ViewController::handleZoom (const Handle(V3d_View)& theView,
1486                                      const Aspect_ScrollDelta& theParams,
1487                                      const gp_Pnt* thePnt)
1488 {
1489   if (!myToAllowZooming)
1490   {
1491     return;
1492   }
1493
1494   AbortViewAnimation();
1495
1496   const Handle(Graphic3d_Camera)& aCam = theView->Camera();
1497   if (thePnt != NULL)
1498   {
1499     const double aViewDist = Max (myMinCamDistance, (thePnt->XYZ() - aCam->Eye().XYZ()).Modulus());
1500     aCam->SetCenter (aCam->Eye().XYZ() + aCam->Direction().XYZ() * aViewDist);
1501   }
1502
1503   if (!theParams.HasPoint())
1504   {
1505     Standard_Real aCoeff = Abs(theParams.Delta) / 100.0 + 1.0;
1506     aCoeff = theParams.Delta > 0.0 ? aCoeff : 1.0 / aCoeff;
1507     theView->SetZoom (aCoeff, true);
1508     theView->Invalidate();
1509     theView->View()->SynchronizeXRPosedToBaseCamera();
1510     return;
1511   }
1512
1513   // integer delta is too rough for small smooth increments
1514   //theView->StartZoomAtPoint (theParams.Point.x(), theParams.Point.y());
1515   //theView->ZoomAtPoint (0, 0, (int )theParams.Delta, (int )theParams.Delta);
1516
1517   double aDZoom = Abs (theParams.Delta) / 100.0 + 1.0;
1518   aDZoom = (theParams.Delta > 0.0) ? aDZoom : 1.0 / aDZoom;
1519   if (aDZoom <= 0.0)
1520   {
1521     return;
1522   }
1523
1524   const Graphic3d_Vec2d aViewDims (aCam->ViewDimensions().X(), aCam->ViewDimensions().Y());
1525
1526   // ensure that zoom will not be too small or too big
1527   double aCoef = aDZoom;
1528   if (aViewDims.x() < aCoef * Precision::Confusion())
1529   {
1530     aCoef = aViewDims.x() / Precision::Confusion();
1531   }
1532   else if (aViewDims.x() > aCoef * 1e12)
1533   {
1534     aCoef = aViewDims.x() / 1e12;
1535   }
1536   if (aViewDims.y() < aCoef * Precision::Confusion())
1537   {
1538     aCoef = aViewDims.y() / Precision::Confusion();
1539   }
1540   else if (aViewDims.y() > aCoef * 1e12)
1541   {
1542     aCoef = aViewDims.y() / 1e12;
1543   }
1544
1545   Graphic3d_Vec2d aZoomAtPointXYv (0.0, 0.0);
1546   theView->Convert (theParams.Point.x(), theParams.Point.y(),
1547                     aZoomAtPointXYv.x(), aZoomAtPointXYv.y());
1548   Graphic3d_Vec2d aDxy = aZoomAtPointXYv / aCoef;
1549   aCam->SetScale (aCam->Scale() / aCoef);
1550
1551   const gp_Dir& aDir = aCam->Direction();
1552   const gp_Ax3 aCameraCS (aCam->Center(), aDir.Reversed(), aDir ^ aCam->Up());
1553
1554   // pan back to the point
1555   aDxy = aZoomAtPointXYv - aDxy;
1556   if (thePnt != NULL)
1557   {
1558     // zoom at 3D point with perspective projection
1559     const gp_XYZ anEyeToPnt = thePnt->XYZ() - aCam->Eye().XYZ();
1560     aDxy.SetValues (anEyeToPnt.Dot (aCameraCS.XDirection().XYZ()),
1561                     anEyeToPnt.Dot (aCameraCS.YDirection().XYZ()));
1562
1563     // view dimensions at 3D point
1564     const gp_Pnt aViewDims1 = aCam->ViewDimensions (anEyeToPnt.Dot (aCam->Direction().XYZ()));
1565
1566     Graphic3d_Vec2i aWinSize;
1567     theView->Window()->Size (aWinSize.x(), aWinSize.y());
1568     const Graphic3d_Vec2d aPanFromCenterPx (double(theParams.Point.x()) - 0.5 * double(aWinSize.x()),
1569                                             double(aWinSize.y() - theParams.Point.y() - 1) - 0.5 * double(aWinSize.y()));
1570     aDxy.x() += -aViewDims1.X() * aPanFromCenterPx.x() / double(aWinSize.x());
1571     aDxy.y() += -aViewDims1.Y() * aPanFromCenterPx.y() / double(aWinSize.y());
1572   }
1573
1574   //theView->Translate (aCam, aDxy.x(), aDxy.y());
1575   gp_Trsf aPanTrsf;
1576   const gp_Vec aCameraPan = gp_Vec (aCameraCS.XDirection()) * aDxy.x()
1577                           + gp_Vec (aCameraCS.YDirection()) * aDxy.y();
1578   aPanTrsf.SetTranslation (aCameraPan);
1579   aCam->Transform (aPanTrsf);
1580   theView->Invalidate();
1581   theView->View()->SynchronizeXRPosedToBaseCamera();
1582 }
1583
1584 // =======================================================================
1585 // function : handleZFocusScroll
1586 // purpose  :
1587 // =======================================================================
1588 void AIS_ViewController::handleZFocusScroll (const Handle(V3d_View)& theView,
1589                                              const Aspect_ScrollDelta& theParams)
1590 {
1591   if (!myToAllowZFocus
1592    || !theView->Camera()->IsStereo())
1593   {
1594     return;
1595   }
1596
1597   Standard_Real aFocus = theView->Camera()->ZFocus() + (theParams.Delta > 0.0 ? 0.05 : -0.05);
1598   if (aFocus > 0.2
1599    && aFocus < 2.0)
1600   {
1601     theView->Camera()->SetZFocus (theView->Camera()->ZFocusType(), aFocus);
1602     theView->Invalidate();
1603   }
1604 }
1605
1606 // =======================================================================
1607 // function : handleOrbitRotation
1608 // purpose  :
1609 // =======================================================================
1610 void AIS_ViewController::handleOrbitRotation (const Handle(V3d_View)& theView,
1611                                               const gp_Pnt& thePnt,
1612                                               bool theToLockZUp)
1613 {
1614   if (!myToAllowRotation)
1615   {
1616     return;
1617   }
1618
1619   const Handle(Graphic3d_Camera)& aCam = theView->View()->IsActiveXR()
1620                                        ? theView->View()->BaseXRCamera()
1621                                        : theView->Camera();
1622   if (myGL.OrbitRotation.ToStart)
1623   {
1624     // default alternatives
1625     //if (myRotationMode == AIS_RotationMode_BndBoxActive) theView->StartRotation (myGL.RotateAtPoint.x(), myGL.RotateAtPoint.y());
1626     //theView->Rotate (0.0, 0.0, 0.0, thePnt.X(), thePnt.Y(), thePnt.Z(), true);
1627
1628     myRotatePnt3d      = thePnt;
1629     myCamStartOpUp     = aCam->Up();
1630     myCamStartOpDir    = aCam->Direction();
1631     myCamStartOpEye    = aCam->Eye();
1632     myCamStartOpCenter = aCam->Center();
1633
1634     gp_Trsf aTrsf;
1635     aTrsf.SetTransformation (gp_Ax3 (myRotatePnt3d, aCam->OrthogonalizedUp(), aCam->Direction()),
1636                              gp_Ax3 (myRotatePnt3d, gp::DZ(), gp::DX()));
1637     const gp_Quaternion aRot = aTrsf.GetRotation();
1638     aRot.GetEulerAngles (gp_YawPitchRoll, myRotateStartYawPitchRoll[0], myRotateStartYawPitchRoll[1], myRotateStartYawPitchRoll[2]);
1639
1640     aTrsf.Invert();
1641     myCamStartOpToEye    = gp_Vec (myRotatePnt3d, aCam->Eye()).Transformed (aTrsf);
1642     myCamStartOpToCenter = gp_Vec (myRotatePnt3d, aCam->Center()).Transformed (aTrsf);
1643
1644     theView->Invalidate();
1645   }
1646
1647   if (!myGL.OrbitRotation.ToRotate)
1648   {
1649     return;
1650   }
1651
1652   AbortViewAnimation();
1653   if (theToLockZUp)
1654   {
1655     // amend camera to exclude roll angle (put camera Up vector to plane containing global Z and view direction)
1656     Graphic3d_Vec2i aWinXY;
1657     theView->Window()->Size (aWinXY.x(), aWinXY.y());
1658     double aYawAngleDelta   =  ((myGL.OrbitRotation.PointStart.x() - myGL.OrbitRotation.PointTo.x()) / double (aWinXY.x())) * (M_PI * 0.5);
1659     double aPitchAngleDelta = -((myGL.OrbitRotation.PointStart.y() - myGL.OrbitRotation.PointTo.y()) / double (aWinXY.y())) * (M_PI * 0.5);
1660     double aPitchAngleNew = 0.0, aRoll = 0.0;
1661     const double aYawAngleNew = myRotateStartYawPitchRoll[0] + aYawAngleDelta;
1662     if (!theView->View()->IsActiveXR())
1663     {
1664       aPitchAngleNew = Max (Min (myRotateStartYawPitchRoll[1] + aPitchAngleDelta, M_PI * 0.5 - M_PI / 180.0), -M_PI * 0.5 + M_PI / 180.0);
1665       aRoll = 0.0;
1666     }
1667
1668     gp_Quaternion aRot;
1669     aRot.SetEulerAngles (gp_YawPitchRoll, aYawAngleNew, aPitchAngleNew, aRoll);
1670     gp_Trsf aTrsfRot;
1671     aTrsfRot.SetRotation (aRot);
1672
1673     const gp_Dir aNewUp = gp::DZ().Transformed (aTrsfRot);
1674     aCam->SetUp (aNewUp);
1675     aCam->SetEyeAndCenter (myRotatePnt3d.XYZ() + myCamStartOpToEye   .Transformed (aTrsfRot).XYZ(),
1676                            myRotatePnt3d.XYZ() + myCamStartOpToCenter.Transformed (aTrsfRot).XYZ());
1677
1678     aCam->OrthogonalizeUp();
1679   }
1680   else
1681   {
1682     // default alternatives
1683     //if (myRotationMode == AIS_RotationMode_BndBoxActive) theView->Rotation (myGL.RotateToPoint.x(), myGL.RotateToPoint.y());
1684     //theView->Rotate (aDX, aDY, aDZ, myRotatePnt3d.X(), myRotatePnt3d.Y(), myRotatePnt3d.Z(), false);
1685
1686     // restore previous camera state
1687     aCam->SetEyeAndCenter (myCamStartOpEye, myCamStartOpCenter);
1688     aCam->SetUp (myCamStartOpUp);
1689     aCam->SetDirectionFromEye (myCamStartOpDir);
1690
1691     Graphic3d_Vec2d aWinXY;
1692     theView->Size (aWinXY.x(), aWinXY.y());
1693     const Standard_Real rx = (Standard_Real )theView->Convert (aWinXY.x());
1694     const Standard_Real ry = (Standard_Real )theView->Convert (aWinXY.y());
1695
1696     const double THE_2PI = M_PI * 2.0;
1697     double aDX = (myGL.OrbitRotation.PointTo.x() - myGL.OrbitRotation.PointStart.x()) * M_PI / rx;
1698     double aDY = (myGL.OrbitRotation.PointStart.y() - myGL.OrbitRotation.PointTo.y()) * M_PI / ry;
1699
1700     if     (aDX > 0.0) { while (aDX >  THE_2PI) { aDX -= THE_2PI; } }
1701     else if(aDX < 0.0) { while (aDX < -THE_2PI) { aDX += THE_2PI; } }
1702     if     (aDY > 0.0) { while (aDY >  THE_2PI) { aDY -= THE_2PI; } }
1703     else if(aDY < 0.0) { while (aDY < -THE_2PI) { aDY += THE_2PI; } }
1704
1705     // rotate camera around 3 initial axes
1706     gp_Dir aCamDir (aCam->Direction().Reversed());
1707     gp_Dir aCamUp  (aCam->Up());
1708     gp_Dir aCamSide(aCamUp.Crossed (aCamDir));
1709
1710     gp_Trsf aRot[2], aTrsf;
1711     aRot[0].SetRotation (gp_Ax1 (myRotatePnt3d, aCamUp),  -aDX);
1712     aRot[1].SetRotation (gp_Ax1 (myRotatePnt3d, aCamSide), aDY);
1713     aTrsf.Multiply (aRot[0]);
1714     aTrsf.Multiply (aRot[1]);
1715
1716     aCam->Transform (aTrsf);
1717   }
1718
1719   theView->Invalidate();
1720   theView->View()->SynchronizeXRBaseToPosedCamera();
1721 }
1722
1723 // =======================================================================
1724 // function : handleViewRotation
1725 // purpose  :
1726 // =======================================================================
1727 void AIS_ViewController::handleViewRotation (const Handle(V3d_View)& theView,
1728                                              double theYawExtra,
1729                                              double thePitchExtra,
1730                                              double theRoll,
1731                                              bool theToRestartOnIncrement)
1732 {
1733   if (!myToAllowRotation)
1734   {
1735     return;
1736   }
1737
1738   const Handle(Graphic3d_Camera)& aCam = theView->Camera();
1739   const bool toRotateAnyway = Abs (theYawExtra)   > gp::Resolution()
1740                            || Abs (thePitchExtra) > gp::Resolution()
1741                            || Abs (theRoll - myRotateStartYawPitchRoll[2]) > gp::Resolution();
1742   if (toRotateAnyway
1743    && theToRestartOnIncrement)
1744   {
1745     myGL.ViewRotation.ToStart = true;
1746     myGL.ViewRotation.PointTo = myGL.ViewRotation.PointStart;
1747   }
1748   if (myGL.ViewRotation.ToStart)
1749   {
1750     gp_Trsf aTrsf;
1751     aTrsf.SetTransformation (gp_Ax3 (gp::Origin(), aCam->OrthogonalizedUp(), aCam->Direction()),
1752                              gp_Ax3 (gp::Origin(), gp::DZ(), gp::DX()));
1753     const gp_Quaternion aRot = aTrsf.GetRotation();
1754     double aRollDummy = 0.0;
1755     aRot.GetEulerAngles (gp_YawPitchRoll, myRotateStartYawPitchRoll[0], myRotateStartYawPitchRoll[1], aRollDummy);
1756   }
1757   if (toRotateAnyway)
1758   {
1759     myRotateStartYawPitchRoll[0] += theYawExtra;
1760     myRotateStartYawPitchRoll[1] += thePitchExtra;
1761     myRotateStartYawPitchRoll[2]  = theRoll;
1762     myGL.ViewRotation.ToRotate = true;
1763   }
1764
1765   if (!myGL.ViewRotation.ToRotate)
1766   {
1767     return;
1768   }
1769
1770   AbortViewAnimation();
1771
1772   Graphic3d_Vec2i aWinXY;
1773   theView->Window()->Size (aWinXY.x(), aWinXY.y());
1774   double aYawAngleDelta   =  ((myGL.ViewRotation.PointStart.x() - myGL.ViewRotation.PointTo.x()) / double (aWinXY.x())) * (M_PI * 0.5);
1775   double aPitchAngleDelta = -((myGL.ViewRotation.PointStart.y() - myGL.ViewRotation.PointTo.y()) / double (aWinXY.y())) * (M_PI * 0.5);
1776   const double aPitchAngleNew = Max (Min (myRotateStartYawPitchRoll[1] + aPitchAngleDelta, M_PI * 0.5 - M_PI / 180.0), -M_PI * 0.5 + M_PI / 180.0);
1777   const double aYawAngleNew   = myRotateStartYawPitchRoll[0] + aYawAngleDelta;
1778   gp_Quaternion aRot;
1779   aRot.SetEulerAngles (gp_YawPitchRoll, aYawAngleNew, aPitchAngleNew, theRoll);
1780   gp_Trsf aTrsfRot;
1781   aTrsfRot.SetRotation (aRot);
1782
1783   const gp_Dir aNewUp  = gp::DZ().Transformed (aTrsfRot);
1784   const gp_Dir aNewDir = gp::DX().Transformed (aTrsfRot);
1785   aCam->SetUp (aNewUp);
1786   aCam->SetDirectionFromEye (aNewDir);
1787   aCam->OrthogonalizeUp();
1788   theView->Invalidate();
1789 }
1790
1791 // =======================================================================
1792 // function : PickPoint
1793 // purpose  :
1794 // =======================================================================
1795 bool AIS_ViewController::PickPoint (gp_Pnt& thePnt,
1796                                     const Handle(AIS_InteractiveContext)& theCtx,
1797                                     const Handle(V3d_View)& theView,
1798                                     const Graphic3d_Vec2i& theCursor,
1799                                     bool theToStickToPickRay)
1800 {
1801   ResetPreviousMoveTo();
1802
1803   const Handle(StdSelect_ViewerSelector3d)& aSelector = theCtx->MainSelector();
1804   aSelector->Pick (theCursor.x(), theCursor.y(), theView);
1805   if (aSelector->NbPicked() < 1)
1806   {
1807     return false;
1808   }
1809
1810   const SelectMgr_SortCriterion& aPicked = aSelector->PickedData (1);
1811   if (theToStickToPickRay
1812   && !Precision::IsInfinite (aPicked.Depth))
1813   {
1814     thePnt = aSelector->GetManager().DetectedPoint (aPicked.Depth);
1815   }
1816   else
1817   {
1818     thePnt = aSelector->PickedPoint (1);
1819   }
1820   return !Precision::IsInfinite (thePnt.X())
1821       && !Precision::IsInfinite (thePnt.Y())
1822       && !Precision::IsInfinite (thePnt.Z());
1823 }
1824
1825 // =======================================================================
1826 // function : GravityPoint
1827 // purpose  :
1828 // =======================================================================
1829 gp_Pnt AIS_ViewController::GravityPoint (const Handle(AIS_InteractiveContext)& theCtx,
1830                                          const Handle(V3d_View)& theView)
1831 {
1832   switch (myRotationMode)
1833   {
1834     case AIS_RotationMode_PickLast:
1835     case AIS_RotationMode_PickCenter:
1836     {
1837       Graphic3d_Vec2i aCursor ((int )myGL.OrbitRotation.PointStart.x(), (int )myGL.OrbitRotation.PointStart.y());
1838       if (myRotationMode == AIS_RotationMode_PickCenter)
1839       {
1840         Graphic3d_Vec2i aViewPort;
1841         theView->Window()->Size (aViewPort.x(), aViewPort.y());
1842         aCursor = aViewPort / 2;
1843       }
1844
1845       gp_Pnt aPnt;
1846       if (PickPoint (aPnt, theCtx, theView, aCursor, myToStickToRayOnRotation))
1847       {
1848         return aPnt;
1849       }
1850       break;
1851     }
1852     case AIS_RotationMode_CameraAt:
1853     {
1854       const Handle(Graphic3d_Camera)& aCam = theView->Camera();
1855       return aCam->Center();
1856     }
1857     case AIS_RotationMode_BndBoxScene:
1858     {
1859       Bnd_Box aBndBox = theView->View()->MinMaxValues (false);
1860       if (!aBndBox.IsVoid())
1861       {
1862         return (aBndBox.CornerMin().XYZ() + aBndBox.CornerMax().XYZ()) * 0.5;
1863       }
1864       break;
1865     }
1866     case AIS_RotationMode_BndBoxActive:
1867       break;
1868   }
1869
1870   return theCtx ->GravityPoint (theView);
1871 }
1872
1873 // =======================================================================
1874 // function : FitAllAuto
1875 // purpose  :
1876 // =======================================================================
1877 void AIS_ViewController::FitAllAuto (const Handle(AIS_InteractiveContext)& theCtx,
1878                                      const Handle(V3d_View)& theView)
1879 {
1880   const Bnd_Box aBoxSel = theCtx->BoundingBoxOfSelection();
1881   const double aFitMargin = 0.01;
1882   if (aBoxSel.IsVoid())
1883   {
1884     theView->FitAll (aFitMargin, false);
1885     return;
1886   }
1887
1888   // fit all algorithm is not 100% stable - so compute some precision to compare equal camera values
1889   const double  aFitTol = (aBoxSel.CornerMax().XYZ() - aBoxSel.CornerMin().XYZ()).Modulus() * 0.000001;
1890   const Bnd_Box aBoxAll = theView->View()->MinMaxValues();
1891
1892   const Handle(Graphic3d_Camera)& aCam = theView->Camera();
1893   Handle(Graphic3d_Camera) aCameraSel = new Graphic3d_Camera (aCam);
1894   Handle(Graphic3d_Camera) aCameraAll = new Graphic3d_Camera (aCam);
1895   theView->FitMinMax (aCameraSel, aBoxSel, aFitMargin);
1896   theView->FitMinMax (aCameraAll, aBoxAll, aFitMargin);
1897   if (aCameraSel->Center().IsEqual (aCam->Center(),     aFitTol)
1898    && Abs (aCameraSel->Scale()    - aCam->Scale())    < aFitTol
1899    && Abs (aCameraSel->Distance() - aCam->Distance()) < aFitTol)
1900   {
1901     // fit all entire view on second FitALL request
1902     aCam->Copy (aCameraAll);
1903   }
1904   else
1905   {
1906     aCam->Copy (aCameraSel);
1907   }
1908 }
1909
1910 // =======================================================================
1911 // function : handleViewOrientationKeys
1912 // purpose  :
1913 // =======================================================================
1914 void AIS_ViewController::handleViewOrientationKeys (const Handle(AIS_InteractiveContext)& theCtx,
1915                                                     const Handle(V3d_View)& theView)
1916 {
1917   if (myNavigationMode == AIS_NavigationMode_FirstPersonWalk)
1918   {
1919     return;
1920   }
1921
1922   Handle(Graphic3d_Camera) aCameraBack;
1923   struct ViewKeyAction
1924   {
1925     Aspect_VKey Key;
1926     V3d_TypeOfOrientation Orientation;
1927   };
1928   static const ViewKeyAction THE_VIEW_KEYS[] =
1929   {
1930     { Aspect_VKey_ViewTop,          V3d_TypeOfOrientation_Zup_Top },
1931     { Aspect_VKey_ViewBottom,       V3d_TypeOfOrientation_Zup_Bottom },
1932     { Aspect_VKey_ViewLeft,         V3d_TypeOfOrientation_Zup_Left },
1933     { Aspect_VKey_ViewRight,        V3d_TypeOfOrientation_Zup_Right },
1934     { Aspect_VKey_ViewFront,        V3d_TypeOfOrientation_Zup_Front },
1935     { Aspect_VKey_ViewBack,         V3d_TypeOfOrientation_Zup_Back },
1936     { Aspect_VKey_ViewAxoLeftProj,  V3d_TypeOfOrientation_Zup_AxoLeft },
1937     { Aspect_VKey_ViewAxoRightProj, V3d_TypeOfOrientation_Zup_AxoRight },
1938     { Aspect_VKey_ViewRoll90CW,     (V3d_TypeOfOrientation )-1},
1939     { Aspect_VKey_ViewRoll90CCW,    (V3d_TypeOfOrientation )-1},
1940     { Aspect_VKey_ViewFitAll,       (V3d_TypeOfOrientation )-1}
1941   };
1942   {
1943     Standard_Mutex::Sentry aLock (myKeys.Mutex());
1944     const size_t aNbKeys = sizeof(THE_VIEW_KEYS) / sizeof(*THE_VIEW_KEYS);
1945     const double anEventTime = EventTime();
1946     for (size_t aKeyIter = 0; aKeyIter < aNbKeys; ++aKeyIter)
1947     {
1948       const ViewKeyAction& aKeyAction = THE_VIEW_KEYS[aKeyIter];
1949       if (!myKeys.IsKeyDown (aKeyAction.Key))
1950       {
1951         continue;
1952       }
1953
1954       myKeys.KeyUp (aKeyAction.Key, anEventTime);
1955       if (aCameraBack.IsNull())
1956       {
1957         aCameraBack = theView->Camera();
1958         theView->SetCamera (new Graphic3d_Camera (aCameraBack));
1959       }
1960       if (aKeyAction.Orientation != (V3d_TypeOfOrientation )-1)
1961       {
1962         theView->SetProj (aKeyAction.Orientation);
1963         FitAllAuto (theCtx, theView);
1964       }
1965       else if (aKeyAction.Key == Aspect_VKey_ViewRoll90CW)
1966       {
1967         const double aTwist = theView->Twist() + M_PI / 2.0;
1968         theView->SetTwist (aTwist);
1969       }
1970       else if (aKeyAction.Key == Aspect_VKey_ViewRoll90CCW)
1971       {
1972         const double aTwist = theView->Twist() - M_PI / 2.0;
1973         theView->SetTwist (aTwist);
1974       }
1975       else if (aKeyAction.Key == Aspect_VKey_ViewFitAll)
1976       {
1977         FitAllAuto (theCtx, theView);
1978       }
1979     }
1980   }
1981
1982   if (aCameraBack.IsNull())
1983   {
1984     return;
1985   }
1986
1987   Handle(Graphic3d_Camera) aCameraNew = theView->Camera();
1988   theView->SetCamera (aCameraBack);
1989   const Graphic3d_Mat4d anOrientMat1 = aCameraBack->OrientationMatrix();
1990   const Graphic3d_Mat4d anOrientMat2 = aCameraNew ->OrientationMatrix();
1991   if (anOrientMat1 != anOrientMat2)
1992   {
1993     const Handle(AIS_AnimationCamera)& aCamAnim = myViewAnimation;
1994     aCamAnim->SetView (theView);
1995     aCamAnim->SetStartPts (0.0);
1996     aCamAnim->SetCameraStart (new Graphic3d_Camera (aCameraBack));
1997     aCamAnim->SetCameraEnd   (new Graphic3d_Camera (aCameraNew));
1998     aCamAnim->StartTimer (0.0, 1.0, true, false);
1999   }
2000 }
2001
2002 // =======================================================================
2003 // function : handleNavigationKeys
2004 // purpose  :
2005 // =======================================================================
2006 AIS_WalkDelta AIS_ViewController::handleNavigationKeys (const Handle(AIS_InteractiveContext)& ,
2007                                                         const Handle(V3d_View)& theView)
2008 {
2009   // navigation keys
2010   double aCrouchRatio = 1.0, aRunRatio = 1.0;
2011   if (myNavigationMode == AIS_NavigationMode_FirstPersonFlight)
2012   {
2013     aRunRatio = 3.0;
2014   }
2015
2016   const double aRotSpeed = 0.5;
2017   const double aWalkSpeedCoef = WalkSpeedRelative();
2018   AIS_WalkDelta aWalk = FetchNavigationKeys (aCrouchRatio, aRunRatio);
2019   if (aWalk.IsJumping())
2020   {
2021     // ask more frames
2022     setAskNextFrame();
2023     theView->Invalidate();
2024   }
2025   if (aWalk.IsEmpty())
2026   {
2027     return aWalk;
2028   }
2029   else if (myGL.OrbitRotation.ToRotate
2030         || myGL.OrbitRotation.ToStart)
2031   {
2032     return aWalk;
2033   }
2034
2035   gp_XYZ aMin, aMax;
2036   const Bnd_Box aBndBox = theView->View()->MinMaxValues();
2037   if (!aBndBox.IsVoid())
2038   {
2039     aMin = aBndBox.CornerMin().XYZ();
2040     aMax = aBndBox.CornerMax().XYZ();
2041   }
2042   double aBndDiam = Max (Max (aMax.X() - aMin.X(), aMax.Y() - aMin.Y()), aMax.Z() - aMin.Z());
2043   if (aBndDiam <= gp::Resolution())
2044   {
2045     aBndDiam = 0.001;
2046   }
2047
2048   const double aWalkSpeed = myNavigationMode != AIS_NavigationMode_Orbit
2049                          && myNavigationMode != AIS_NavigationMode_FirstPersonFlight
2050                           ? theView->View()->UnitFactor() * WalkSpeedAbsolute()
2051                           : aWalkSpeedCoef * aBndDiam;
2052   const Handle(Graphic3d_Camera)& aCam = theView->View()->IsActiveXR()
2053                                        ? theView->View()->BaseXRCamera()
2054                                        : theView->Camera();
2055
2056   // move forward in plane XY and up along Z
2057   const gp_Dir anUp = ToLockOrbitZUp() ? gp::DZ() : aCam->OrthogonalizedUp();
2058   if (aWalk.ToMove()
2059    && myToAllowPanning)
2060   {
2061     const gp_Vec aSide = -aCam->SideRight();
2062     gp_XYZ aFwd = aCam->Direction().XYZ();
2063     aFwd -= anUp.XYZ() * (anUp.XYZ() * aFwd);
2064
2065     gp_XYZ aMoveVec;
2066     if (!aWalk[AIS_WalkTranslation_Forward].IsEmpty())
2067     {
2068       if (!aCam->IsOrthographic())
2069       {
2070         aMoveVec += aFwd * aWalk[AIS_WalkTranslation_Forward].Value * aWalk[AIS_WalkTranslation_Forward].Pressure * aWalkSpeed;
2071       }
2072     }
2073     if (!aWalk[AIS_WalkTranslation_Side].IsEmpty())
2074     {
2075       aMoveVec += aSide.XYZ() * aWalk[AIS_WalkTranslation_Side].Value * aWalk[AIS_WalkTranslation_Side].Pressure * aWalkSpeed;
2076     }
2077     if (!aWalk[AIS_WalkTranslation_Up].IsEmpty())
2078     {
2079       aMoveVec += anUp.XYZ() * aWalk[AIS_WalkTranslation_Up].Value * aWalk[AIS_WalkTranslation_Up].Pressure * aWalkSpeed;
2080     }
2081     {
2082       if (aCam->IsOrthographic())
2083       {
2084         if (!aWalk[AIS_WalkTranslation_Forward].IsEmpty())
2085         {
2086           const double aZoomDelta = aWalk[AIS_WalkTranslation_Forward].Value * aWalk[AIS_WalkTranslation_Forward].Pressure * aWalkSpeedCoef;
2087           handleZoom (theView, Aspect_ScrollDelta (aZoomDelta * 100.0), NULL);
2088         }
2089       }
2090
2091       gp_Trsf aTrsfTranslate;
2092       aTrsfTranslate.SetTranslation (aMoveVec);
2093       aCam->Transform (aTrsfTranslate);
2094     }
2095   }
2096
2097   if (myNavigationMode == AIS_NavigationMode_Orbit
2098    && myToAllowRotation)
2099   {
2100     if (!aWalk[AIS_WalkRotation_Yaw].IsEmpty())
2101     {
2102       gp_Trsf aTrsfRot;
2103       aTrsfRot.SetRotation (gp_Ax1 (aCam->Eye(), anUp), aWalk[AIS_WalkRotation_Yaw].Value * aRotSpeed);
2104       aCam->Transform (aTrsfRot);
2105     }
2106     if (!aWalk[AIS_WalkRotation_Pitch].IsEmpty())
2107     {
2108       const gp_Vec aSide = -aCam->SideRight();
2109       gp_Trsf aTrsfRot;
2110       aTrsfRot.SetRotation (gp_Ax1 (aCam->Eye(), aSide), -aWalk[AIS_WalkRotation_Pitch].Value * aRotSpeed);
2111       aCam->Transform (aTrsfRot);
2112     }
2113     if (!aWalk[AIS_WalkRotation_Roll].IsEmpty()
2114      && !ToLockOrbitZUp())
2115     {
2116       gp_Trsf aTrsfRot;
2117       aTrsfRot.SetRotation (gp_Ax1 (aCam->Center(), aCam->Direction()), aWalk[AIS_WalkRotation_Roll].Value * aRotSpeed);
2118       aCam->Transform (aTrsfRot);
2119     }
2120   }
2121
2122   // ask more frames
2123   setAskNextFrame();
2124   theView->Invalidate();
2125   theView->View()->SynchronizeXRBaseToPosedCamera();
2126   return aWalk;
2127 }
2128
2129 // =======================================================================
2130 // function : handleCameraActions
2131 // purpose  :
2132 // =======================================================================
2133 void AIS_ViewController::handleCameraActions (const Handle(AIS_InteractiveContext)& theCtx,
2134                                               const Handle(V3d_View)& theView,
2135                                               const AIS_WalkDelta& theWalk)
2136 {
2137   // apply view actions
2138   if (myGL.Orientation.ToSetViewOrient)
2139   {
2140     theView->SetProj (myGL.Orientation.ViewOrient);
2141     myGL.Orientation.ToFitAll = true;
2142   }
2143
2144   // apply fit all
2145   if (myGL.Orientation.ToFitAll)
2146   {
2147     const double aFitMargin = 0.01;
2148     theView->FitAll (aFitMargin, false);
2149     theView->Invalidate();
2150     myGL.Orientation.ToFitAll = false;
2151   }
2152
2153   if (myGL.IsNewGesture)
2154   {
2155     if (myAnchorPointPrs1->HasInteractiveContext())
2156     {
2157       theCtx->Remove (myAnchorPointPrs1, false);
2158       if (!theView->Viewer()->ZLayerSettings (myAnchorPointPrs1->ZLayer()).IsImmediate())
2159       {
2160         theView->Invalidate();
2161       }
2162       else
2163       {
2164         theView->InvalidateImmediate();
2165       }
2166     }
2167     if (myAnchorPointPrs2->HasInteractiveContext())
2168     {
2169       theCtx->Remove (myAnchorPointPrs2, false);
2170       if (!theView->Viewer()->ZLayerSettings (myAnchorPointPrs2->ZLayer()).IsImmediate())
2171       {
2172         theView->Invalidate();
2173       }
2174       else
2175       {
2176         theView->InvalidateImmediate();
2177       }
2178     }
2179
2180     if (myHasHlrOnBeforeRotation)
2181     {
2182       myHasHlrOnBeforeRotation = false;
2183       theView->SetComputedMode (true);
2184       theView->Invalidate();
2185     }
2186   }
2187
2188   if (myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
2189   {
2190     if (myGL.Panning.ToStart
2191      && myToAllowPanning)
2192     {
2193       gp_Pnt aPanPnt (Precision::Infinite(), 0.0, 0.0);
2194       if (!theView->Camera()->IsOrthographic())
2195       {
2196         bool toStickToRay = false;
2197         if (myGL.Panning.PointStart.x() >= 0
2198          && myGL.Panning.PointStart.y() >= 0)
2199         {
2200           PickPoint (aPanPnt, theCtx, theView, myGL.Panning.PointStart, toStickToRay);
2201         }
2202         if (Precision::IsInfinite (aPanPnt.X()))
2203         {
2204           Graphic3d_Vec2i aWinSize;
2205           theView->Window()->Size (aWinSize.x(), aWinSize.y());
2206           PickPoint (aPanPnt, theCtx, theView, aWinSize / 2, toStickToRay);
2207         }
2208         if (!Precision::IsInfinite (aPanPnt.X())
2209           && myToShowPanAnchorPoint)
2210         {
2211           gp_Trsf aPntTrsf;
2212           aPntTrsf.SetTranslation (gp_Vec (aPanPnt.XYZ()));
2213           theCtx->SetLocation (myAnchorPointPrs2, aPntTrsf);
2214         }
2215       }
2216       setPanningAnchorPoint (aPanPnt);
2217     }
2218
2219     if (myToShowPanAnchorPoint
2220     &&  hasPanningAnchorPoint()
2221     &&  myGL.Panning.ToPan
2222     && !myGL.IsNewGesture
2223     && !myAnchorPointPrs2->HasInteractiveContext())
2224     {
2225       theCtx->Display (myAnchorPointPrs2, 0, -1, false, AIS_DS_Displayed);
2226     }
2227
2228     handlePanning (theView);
2229     handleZRotate (theView);
2230   }
2231
2232   if ((myNavigationMode == AIS_NavigationMode_Orbit
2233     || myGL.OrbitRotation.ToStart
2234     || myGL.OrbitRotation.ToRotate)
2235    && myToAllowRotation)
2236   {
2237     if (myGL.OrbitRotation.ToStart
2238     && !myHasHlrOnBeforeRotation)
2239     {
2240       myHasHlrOnBeforeRotation = theView->ComputedMode();
2241       if (myHasHlrOnBeforeRotation)
2242       {
2243         theView->SetComputedMode (false);
2244       }
2245     }
2246
2247     gp_Pnt aGravPnt;
2248     if (myGL.OrbitRotation.ToStart)
2249     {
2250       aGravPnt = GravityPoint (theCtx, theView);
2251       if (myToShowRotateCenter)
2252       {
2253         gp_Trsf aPntTrsf;
2254         aPntTrsf.SetTranslation (gp_Vec (aGravPnt.XYZ()));
2255         theCtx->SetLocation (myAnchorPointPrs1, aPntTrsf);
2256         theCtx->SetLocation (myAnchorPointPrs2, aPntTrsf);
2257       }
2258     }
2259
2260     if (myToShowRotateCenter
2261     &&  myGL.OrbitRotation.ToRotate
2262     && !myGL.IsNewGesture
2263     && !myAnchorPointPrs1->HasInteractiveContext())
2264     {
2265       theCtx->Display (myAnchorPointPrs1, 0, -1, false, AIS_DS_Displayed);
2266       theCtx->Display (myAnchorPointPrs2, 0, -1, false, AIS_DS_Displayed);
2267     }
2268     handleOrbitRotation (theView, aGravPnt,
2269                          myToLockOrbitZUp || myNavigationMode != AIS_NavigationMode_Orbit);
2270   }
2271
2272   if ((myNavigationMode != AIS_NavigationMode_Orbit
2273     || myGL.ViewRotation.ToStart
2274     || myGL.ViewRotation.ToRotate)
2275    && myToAllowRotation)
2276   {
2277     if (myGL.ViewRotation.ToStart
2278     && !myHasHlrOnBeforeRotation)
2279     {
2280       myHasHlrOnBeforeRotation = theView->ComputedMode();
2281       if (myHasHlrOnBeforeRotation)
2282       {
2283         theView->SetComputedMode (false);
2284       }
2285     }
2286
2287     double aRoll = 0.0;
2288     if (!theWalk[AIS_WalkRotation_Roll].IsEmpty()
2289      && !myToLockOrbitZUp)
2290     {
2291       aRoll = (M_PI / 12.0) * theWalk[AIS_WalkRotation_Roll].Pressure;
2292       aRoll *= Min (1000.0  * theWalk[AIS_WalkRotation_Roll].Duration, 100.0) / 100.0;
2293       if (theWalk[AIS_WalkRotation_Roll].Value < 0.0)
2294       {
2295         aRoll = -aRoll;
2296       }
2297     }
2298
2299     handleViewRotation (theView, theWalk[AIS_WalkRotation_Yaw].Value, theWalk[AIS_WalkRotation_Pitch].Value, aRoll,
2300                         myNavigationMode == AIS_NavigationMode_FirstPersonFlight);
2301   }
2302
2303   if (!myGL.ZoomActions.IsEmpty())
2304   {
2305     for (NCollection_Sequence<Aspect_ScrollDelta>::Iterator aZoomIter (myGL.ZoomActions); aZoomIter.More(); aZoomIter.Next())
2306     {
2307       Aspect_ScrollDelta aZoomParams = aZoomIter.Value();
2308       if (myToAllowZFocus
2309        && (aZoomParams.Flags & Aspect_VKeyFlags_CTRL) != 0
2310        && theView->Camera()->IsStereo())
2311       {
2312         handleZFocusScroll (theView, aZoomParams);
2313         continue;
2314       }
2315
2316       if (!myToAllowZooming)
2317       {
2318         continue;
2319       }
2320
2321       if (!theView->Camera()->IsOrthographic())
2322       {
2323         gp_Pnt aPnt;
2324         if (aZoomParams.HasPoint()
2325          && PickPoint (aPnt, theCtx, theView, aZoomParams.Point, myToStickToRayOnZoom))
2326         {
2327           handleZoom (theView, aZoomParams, &aPnt);
2328           continue;
2329         }
2330
2331         Graphic3d_Vec2i aWinSize;
2332         theView->Window()->Size (aWinSize.x(), aWinSize.y());
2333         if (PickPoint (aPnt, theCtx, theView, aWinSize / 2, myToStickToRayOnZoom))
2334         {
2335           aZoomParams.ResetPoint(); // do not pretend to zoom at 'nothing'
2336           handleZoom (theView, aZoomParams, &aPnt);
2337           continue;
2338         }
2339       }
2340       handleZoom (theView, aZoomParams, NULL);
2341     }
2342     myGL.ZoomActions.Clear();
2343   }
2344 }
2345
2346 // =======================================================================
2347 // function : handleXRInput
2348 // purpose  :
2349 // =======================================================================
2350 void AIS_ViewController::handleXRInput (const Handle(AIS_InteractiveContext)& theCtx,
2351                                         const Handle(V3d_View)& theView,
2352                                         const AIS_WalkDelta& )
2353 {
2354   theView->View()->ProcessXRInput();
2355   if (!theView->View()->IsActiveXR())
2356   {
2357     return;
2358   }
2359   if (myXRCameraTmp.IsNull())
2360   {
2361     myXRCameraTmp = new Graphic3d_Camera();
2362   }
2363   handleXRTurnPad (theCtx, theView);
2364   handleXRTeleport(theCtx, theView);
2365   handleXRPicking (theCtx, theView);
2366 }
2367
2368 // =======================================================================
2369 // function : handleXRTurnPad
2370 // purpose  :
2371 // =======================================================================
2372 void AIS_ViewController::handleXRTurnPad (const Handle(AIS_InteractiveContext)& ,
2373                                           const Handle(V3d_View)& theView)
2374 {
2375   if (myXRTurnAngle <= 0.0
2376   || !theView->View()->IsActiveXR())
2377   {
2378     return;
2379   }
2380
2381   // turn left/right at 45 degrees on left/right trackpad clicks
2382   for (int aHand = 0; aHand < 2; ++aHand)
2383   {
2384     const Aspect_XRTrackedDeviceRole aRole = aHand == 0 ? Aspect_XRTrackedDeviceRole_RightHand : Aspect_XRTrackedDeviceRole_LeftHand;
2385     const Handle(Aspect_XRAction)& aPadClickAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadClick);
2386     const Handle(Aspect_XRAction)& aPadPosAct   = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadPosition);
2387     if (aPadClickAct.IsNull()
2388     ||  aPadPosAct.IsNull())
2389     {
2390       continue;
2391     }
2392
2393     const Aspect_XRDigitalActionData aPadClick = theView->View()->XRSession()->GetDigitalActionData (aPadClickAct);
2394     const Aspect_XRAnalogActionData  aPadPos   = theView->View()->XRSession()->GetAnalogActionData (aPadPosAct);
2395     if (aPadClick.IsActive
2396      && aPadClick.IsPressed
2397      && aPadClick.IsChanged
2398      && aPadPos.IsActive
2399      && Abs (aPadPos.VecXYZ.y()) < 0.5f
2400      && Abs (aPadPos.VecXYZ.x()) > 0.7f)
2401     {
2402       gp_Trsf aTrsfTurn;
2403       aTrsfTurn.SetRotation (gp_Ax1 (gp::Origin(), theView->View()->BaseXRCamera()->Up()), aPadPos.VecXYZ.x() < 0.0f ? myXRTurnAngle : -myXRTurnAngle);
2404       theView->View()->TurnViewXRCamera (aTrsfTurn);
2405       break;
2406     }
2407   }
2408 }
2409
2410 // =======================================================================
2411 // function : handleXRTeleport
2412 // purpose  :
2413 // =======================================================================
2414 void AIS_ViewController::handleXRTeleport (const Handle(AIS_InteractiveContext)& theCtx,
2415                                            const Handle(V3d_View)& theView)
2416 {
2417   if (!theView->View()->IsActiveXR())
2418   {
2419     return;
2420   }
2421
2422   // teleport on forward trackpad unclicks
2423   const Aspect_XRTrackedDeviceRole aTeleOld = myXRLastTeleportHand;
2424   myXRLastTeleportHand = Aspect_XRTrackedDeviceRole_Other;
2425   for (int aHand = 0; aHand < 2; ++aHand)
2426   {
2427     const Aspect_XRTrackedDeviceRole aRole = aHand == 0 ? Aspect_XRTrackedDeviceRole_RightHand : Aspect_XRTrackedDeviceRole_LeftHand;
2428     const Standard_Integer aDeviceId = theView->View()->XRSession()->NamedTrackedDevice (aRole);
2429     if (aDeviceId == -1)
2430     {
2431       continue;
2432     }
2433
2434     const Handle(Aspect_XRAction)& aPadClickAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadClick);
2435     const Handle(Aspect_XRAction)& aPadPosAct   = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadPosition);
2436     if (aPadClickAct.IsNull()
2437     ||  aPadPosAct.IsNull())
2438     {
2439       continue;
2440     }
2441
2442     const Aspect_XRDigitalActionData aPadClick = theView->View()->XRSession()->GetDigitalActionData (aPadClickAct);
2443     const Aspect_XRAnalogActionData  aPadPos   = theView->View()->XRSession()->GetAnalogActionData (aPadPosAct);
2444     const bool isPressed =  aPadClick.IsPressed;
2445     const bool isClicked = !aPadClick.IsPressed
2446                         &&  aPadClick.IsChanged;
2447     if (aPadClick.IsActive
2448      && (isPressed || isClicked)
2449      && aPadPos.IsActive
2450      && aPadPos.VecXYZ.y() > 0.6f
2451      && Abs (aPadPos.VecXYZ.x()) < 0.5f)
2452     {
2453       const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceId];
2454       if (!aPose.IsValidPose)
2455       {
2456         continue;
2457       }
2458
2459       myXRLastTeleportHand = aRole;
2460       Standard_Real& aPickDepth = aRole == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
2461       aPickDepth = Precision::Infinite();
2462       Graphic3d_Vec3 aPickNorm;
2463       const gp_Trsf aHandBase = theView->View()->PoseXRToWorld (aPose.Orientation);
2464       const Standard_Real aHeadHeight = theView->View()->XRSession()->HeadPose().TranslationPart().Y();
2465       {
2466         const Standard_Integer aPickedId = handleXRMoveTo (theCtx, theView, aPose.Orientation, false);
2467         if (aPickedId >= 1)
2468         {
2469           const SelectMgr_SortCriterion& aPickedData = theCtx->MainSelector()->PickedData (aPickedId);
2470           aPickNorm = aPickedData.Normal;
2471           if (aPickNorm.SquareModulus() > ShortRealEpsilon())
2472           {
2473             aPickDepth = aPickedData.Point.Distance (aHandBase.TranslationPart());
2474           }
2475         }
2476       }
2477       if (isClicked)
2478       {
2479         myXRLastTeleportHand = Aspect_XRTrackedDeviceRole_Other;
2480         if (!Precision::IsInfinite (aPickDepth))
2481         {
2482           const gp_Dir aTeleDir = -gp::DZ().Transformed (aHandBase);
2483           const gp_Dir anUpDir  = theView->View()->BaseXRCamera()->Up();
2484
2485           bool isHorizontal = false;
2486           gp_Dir aPickNormDir (aPickNorm.x(), aPickNorm.y(), aPickNorm.z());
2487           if (anUpDir.IsEqual ( aPickNormDir, M_PI_4)
2488            || anUpDir.IsEqual (-aPickNormDir, M_PI_4))
2489           {
2490             isHorizontal = true;
2491           }
2492
2493           gp_Pnt aNewEye = aHandBase.TranslationPart();
2494           if (isHorizontal)
2495           {
2496             aNewEye  = aHandBase.TranslationPart()
2497                      + aTeleDir.XYZ() * aPickDepth
2498                      + anUpDir.XYZ() * aHeadHeight;
2499           }
2500           else
2501           {
2502             if (aPickNormDir.Dot (aTeleDir) < 0.0)
2503             {
2504               aPickNormDir.Reverse();
2505             }
2506             aNewEye  = aHandBase.TranslationPart()
2507                      + aTeleDir.XYZ() * aPickDepth
2508                      - aPickNormDir.XYZ() * aHeadHeight / 4;
2509           }
2510
2511           theView->View()->PosedXRCamera()->MoveEyeTo (aNewEye);
2512           theView->View()->ComputeXRBaseCameraFromPosed (theView->View()->PosedXRCamera(), theView->View()->XRSession()->HeadPose());
2513         }
2514       }
2515       break;
2516     }
2517   }
2518
2519   if (myXRLastTeleportHand != aTeleOld)
2520   {
2521     if (aTeleOld != Aspect_XRTrackedDeviceRole_Other)
2522     {
2523       if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (aTeleOld, Aspect_XRGenericAction_OutputHaptic))
2524       {
2525         theView->View()->XRSession()->AbortHapticVibrationAction (aHaptic);
2526       }
2527     }
2528     if (myXRLastTeleportHand != Aspect_XRTrackedDeviceRole_Other)
2529     {
2530       if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (myXRLastTeleportHand, Aspect_XRGenericAction_OutputHaptic))
2531       {
2532         theView->View()->XRSession()->TriggerHapticVibrationAction (aHaptic, myXRTeleportHaptic);
2533       }
2534     }
2535   }
2536 }
2537
2538 // =======================================================================
2539 // function : handleXRPicking
2540 // purpose  :
2541 // =======================================================================
2542 void AIS_ViewController::handleXRPicking (const Handle(AIS_InteractiveContext)& theCtx,
2543                                           const Handle(V3d_View)& theView)
2544 {
2545   if (!theView->View()->IsActiveXR())
2546   {
2547     return;
2548   }
2549
2550   // handle selection on trigger clicks
2551   Aspect_XRTrackedDeviceRole aPickDevOld = myXRLastPickingHand;
2552   myXRLastPickingHand = Aspect_XRTrackedDeviceRole_Other;
2553   for (int aHand = 0; aHand < 2; ++aHand)
2554   {
2555     const Aspect_XRTrackedDeviceRole aRole = aHand == 0 ? Aspect_XRTrackedDeviceRole_RightHand : Aspect_XRTrackedDeviceRole_LeftHand;
2556     const Handle(Aspect_XRAction)& aTrigClickAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTriggerClick);
2557     const Handle(Aspect_XRAction)& aTrigPullAct  = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTriggerPull);
2558     if (aTrigClickAct.IsNull()
2559     ||  aTrigPullAct.IsNull())
2560     {
2561       continue;
2562     }
2563
2564     const Aspect_XRDigitalActionData aTrigClick = theView->View()->XRSession()->GetDigitalActionData (aTrigClickAct);
2565     const Aspect_XRAnalogActionData  aTrigPos   = theView->View()->XRSession()->GetAnalogActionData (aTrigPullAct);
2566     if (aTrigPos.IsActive
2567      && Abs (aTrigPos.VecXYZ.x()) > 0.1f)
2568     {
2569       myXRLastPickingHand = aRole;
2570       handleXRHighlight (theCtx, theView);
2571       if (aTrigClick.IsActive
2572        && aTrigClick.IsPressed
2573        && aTrigClick.IsChanged)
2574       {
2575         theCtx->Select (false);
2576         OnSelectionChanged (theCtx, theView);
2577         if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (myXRLastPickingHand, Aspect_XRGenericAction_OutputHaptic))
2578         {
2579           theView->View()->XRSession()->TriggerHapticVibrationAction (aHaptic, myXRSelectHaptic);
2580         }
2581       }
2582       break;
2583     }
2584   }
2585   if (myXRLastPickingHand != aPickDevOld)
2586   {
2587     theCtx->ClearDetected();
2588   }
2589 }
2590
2591 // =======================================================================
2592 // function : OnSelectionChanged
2593 // purpose  :
2594 // =======================================================================
2595 void AIS_ViewController::OnSelectionChanged (const Handle(AIS_InteractiveContext)& ,
2596                                              const Handle(V3d_View)& )
2597 {
2598   //
2599 }
2600
2601 // =======================================================================
2602 // function : OnObjectDragged
2603 // purpose  :
2604 // =======================================================================
2605 void AIS_ViewController::OnObjectDragged (const Handle(AIS_InteractiveContext)& theCtx,
2606                                           const Handle(V3d_View)& theView,
2607                                           AIS_DragAction theAction)
2608 {
2609   switch (theAction)
2610   {
2611     case AIS_DragAction_Start:
2612     {
2613       myDragObject.Nullify();
2614       if (!theCtx->HasDetected())
2615       {
2616         return;
2617       }
2618
2619       Handle(AIS_InteractiveObject) aPrs = theCtx->DetectedInteractive();
2620       if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (aPrs))
2621       {
2622         if (aManip->HasActiveMode())
2623         {
2624           myDragObject = aManip;
2625           aManip->StartTransform (myGL.Dragging.PointStart.x(), myGL.Dragging.PointStart.y(), theView);
2626         }
2627       }
2628       return;
2629     }
2630     case AIS_DragAction_Update:
2631     {
2632       if (myDragObject.IsNull())
2633       {
2634         return;
2635       }
2636
2637       if (Handle(SelectMgr_EntityOwner) aGlobOwner = myDragObject->GlobalSelOwner())
2638       {
2639         theCtx->SetSelectedState (aGlobOwner, true);
2640       }
2641       if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (myDragObject))
2642       {
2643         aManip->Transform (myGL.Dragging.PointTo.x(), myGL.Dragging.PointTo.y(), theView);
2644       }
2645       theView->Invalidate();
2646       return;
2647     }
2648     case AIS_DragAction_Abort:
2649     {
2650       if (myDragObject.IsNull())
2651       {
2652         return;
2653       }
2654
2655       myGL.Dragging.PointTo = myGL.Dragging.PointStart;
2656       OnObjectDragged (theCtx, theView, AIS_DragAction_Update);
2657
2658       if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (myDragObject))
2659       {
2660         aManip->StopTransform (false);
2661       }
2662       Standard_FALLTHROUGH
2663     }
2664     case AIS_DragAction_Stop:
2665     {
2666       if (myDragObject.IsNull())
2667       {
2668         return;
2669       }
2670
2671       if (Handle(SelectMgr_EntityOwner) aGlobOwner = myDragObject->GlobalSelOwner())
2672       {
2673         theCtx->SetSelectedState (aGlobOwner, false);
2674       }
2675
2676       theView->Invalidate();
2677       myDragObject.Nullify();
2678       return;
2679     }
2680   }
2681 }
2682
2683 // =======================================================================
2684 // function : contextLazyMoveTo
2685 // purpose  :
2686 // =======================================================================
2687 void AIS_ViewController::contextLazyMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
2688                                             const Handle(V3d_View)& theView,
2689                                             const Graphic3d_Vec2i& thePnt)
2690 {
2691   if (myPrevMoveTo == thePnt
2692    || myHasHlrOnBeforeRotation) // ignore highlighting in-between rotation of HLR view
2693   {
2694     return;
2695   }
2696
2697   myPrevMoveTo = thePnt;
2698
2699   Handle(SelectMgr_EntityOwner) aLastPicked = theCtx->DetectedOwner();
2700   theView->AutoZFit();
2701   theCtx->MoveTo (thePnt.x(), thePnt.y(), theView, false);
2702   Handle(SelectMgr_EntityOwner) aNewPicked = theCtx->DetectedOwner();
2703
2704   if (theView->Viewer()->Grid()->IsActive()
2705    && theView->Viewer()->GridEcho())
2706   {
2707     if (aNewPicked.IsNull())
2708     {
2709       Graphic3d_Vec3d aPnt3d;
2710       theView->ConvertToGrid (thePnt.x(), thePnt.y(), aPnt3d[0], aPnt3d[1], aPnt3d[2]);
2711       theView->Viewer()->ShowGridEcho (theView, Graphic3d_Vertex (aPnt3d[0], aPnt3d[1], aPnt3d[2]));
2712       theView->InvalidateImmediate();
2713     }
2714     else
2715     {
2716       theView->Viewer()->HideGridEcho (theView);
2717       theView->InvalidateImmediate();
2718     }
2719   }
2720
2721   if (aLastPicked != aNewPicked
2722    || (!aNewPicked.IsNull() && aNewPicked->IsForcedHilight()))
2723   {
2724     // dynamic highlight affects all Views
2725     for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
2726     {
2727       const Handle(V3d_View)& aView = aViewIter.Value();
2728       aView->InvalidateImmediate();
2729     }
2730   }
2731 }
2732
2733 // =======================================================================
2734 // function : handleSelectionPick
2735 // purpose  :
2736 // =======================================================================
2737 void AIS_ViewController::handleSelectionPick (const Handle(AIS_InteractiveContext)& theCtx,
2738                                               const Handle(V3d_View)& theView)
2739 {
2740   if (myGL.Selection.Tool == AIS_ViewSelectionTool_Picking
2741   && !myGL.Selection.Points.IsEmpty())
2742   {
2743     for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aPntIter (myGL.Selection.Points); aPntIter.More(); aPntIter.Next())
2744     {
2745       const bool hadPrevMoveTo = HasPreviousMoveTo();
2746       contextLazyMoveTo (theCtx, theView, aPntIter.Value());
2747       if (!hadPrevMoveTo)
2748       {
2749         ResetPreviousMoveTo();
2750       }
2751
2752       if (myGL.Selection.IsXOR)
2753       {
2754         theCtx->ShiftSelect (false);
2755       }
2756       else
2757       {
2758         theCtx->Select (false);
2759       }
2760
2761       // selection affects all Views
2762       theView->Viewer()->Invalidate();
2763
2764       OnSelectionChanged (theCtx, theView);
2765     }
2766
2767     myGL.Selection.Points.Clear();
2768   }
2769 }
2770
2771 // =======================================================================
2772 // function : handleSelectionPoly
2773 // purpose  :
2774 // =======================================================================
2775 void AIS_ViewController::handleSelectionPoly (const Handle(AIS_InteractiveContext)& theCtx,
2776                                               const Handle(V3d_View)& theView)
2777 {
2778   // rubber-band & window polygon selection
2779   if (myGL.Selection.Tool == AIS_ViewSelectionTool_RubberBand
2780    || myGL.Selection.Tool == AIS_ViewSelectionTool_Polygon
2781    || myGL.Selection.Tool == AIS_ViewSelectionTool_ZoomWindow)
2782   {
2783     if (!myGL.Selection.Points.IsEmpty())
2784     {
2785       myRubberBand->ClearPoints();
2786       myRubberBand->SetToUpdate();
2787
2788       const bool anIsRubber = myGL.Selection.Tool == AIS_ViewSelectionTool_RubberBand
2789                            || myGL.Selection.Tool == AIS_ViewSelectionTool_ZoomWindow;
2790       if (anIsRubber)
2791       {
2792         myRubberBand->SetRectangle (myGL.Selection.Points.First().x(), -myGL.Selection.Points.First().y(),
2793                                     myGL.Selection.Points.Last().x(),  -myGL.Selection.Points.Last().y());
2794       }
2795       else
2796       {
2797         Graphic3d_Vec2i aPrev (IntegerLast(), IntegerLast());
2798         for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aSelIter (myGL.Selection.Points); aSelIter.More(); aSelIter.Next())
2799         {
2800           Graphic3d_Vec2i aPntNew = Graphic3d_Vec2i (aSelIter.Value().x(), -aSelIter.Value().y());
2801           if (aPntNew != aPrev)
2802           {
2803             aPrev = aPntNew;
2804             myRubberBand->AddPoint (Graphic3d_Vec2i (aSelIter.Value().x(), -aSelIter.Value().y()));
2805           }
2806         }
2807       }
2808
2809       myRubberBand->SetPolygonClosed (anIsRubber);
2810       try
2811       {
2812         theCtx->Display (myRubberBand, 0, -1, false, AIS_DS_Displayed);
2813       }
2814       catch (const Standard_Failure& theEx)
2815       {
2816         Message::SendWarning (TCollection_AsciiString ("Internal error while displaying rubber-band: ")
2817                             + theEx.DynamicType()->Name() + ", " + theEx.GetMessageString());
2818         myRubberBand->ClearPoints();
2819       }
2820       if (!theView->Viewer()->ZLayerSettings (myRubberBand->ZLayer()).IsImmediate())
2821       {
2822         theView->Invalidate();
2823       }
2824       else
2825       {
2826         theView->InvalidateImmediate();
2827       }
2828     }
2829     else if (!myRubberBand.IsNull()
2830            && myRubberBand->HasInteractiveContext())
2831     {
2832       theCtx->Remove (myRubberBand, false);
2833       myRubberBand->ClearPoints();
2834     }
2835   }
2836
2837   if (myGL.Selection.ToApplyTool)
2838   {
2839     myGL.Selection.ToApplyTool = false;
2840     if (theCtx->IsDisplayed (myRubberBand))
2841     {
2842       theCtx->Remove (myRubberBand, false);
2843       {
2844         const NCollection_Sequence<Graphic3d_Vec2i>& aPoints = myRubberBand->Points();
2845         if (aPoints.Size() == 4
2846          && aPoints.Value (1).x() == aPoints.Value (2).x()
2847          && aPoints.Value (3).x() == aPoints.Value (4).x()
2848          && aPoints.Value (1).y() == aPoints.Value (4).y()
2849          && aPoints.Value (2).y() == aPoints.Value (3).y())
2850         {
2851           const Graphic3d_Vec2i aPnt1 (aPoints.Value (1).x(), -aPoints.Value (1).y());
2852           const Graphic3d_Vec2i aPnt2 (aPoints.Value (3).x(), -aPoints.Value (3).y());
2853           if (myGL.Selection.Tool == AIS_ViewSelectionTool_ZoomWindow)
2854           {
2855             theView->WindowFitAll (aPnt1.x(), aPnt1.y(), aPnt2.x(), aPnt2.y());
2856             theView->Invalidate();
2857           }
2858           else
2859           {
2860             theCtx->MainSelector()->AllowOverlapDetection (aPnt1.y() != Min (aPnt1.y(), aPnt2.y()));
2861             if (myGL.Selection.IsXOR)
2862             {
2863               theCtx->ShiftSelect (Min (aPnt1.x(), aPnt2.x()), Min (aPnt1.y(), aPnt2.y()),
2864                                    Max (aPnt1.x(), aPnt2.x()), Max (aPnt1.y(), aPnt2.y()),
2865                                    theView, false);
2866             }
2867             else
2868             {
2869               theCtx->Select (Min (aPnt1.x(), aPnt2.x()), Min (aPnt1.y(), aPnt2.y()),
2870                               Max (aPnt1.x(), aPnt2.x()), Max (aPnt1.y(), aPnt2.y()),
2871                               theView, false);
2872             }
2873             theCtx->MainSelector()->AllowOverlapDetection (false);
2874           }
2875         }
2876         else if (aPoints.Length() >= 3)
2877         {
2878           TColgp_Array1OfPnt2d aPolyline (1, aPoints.Length());
2879           TColgp_Array1OfPnt2d::Iterator aPolyIter (aPolyline);
2880           for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aSelIter (aPoints);
2881                aSelIter.More(); aSelIter.Next(), aPolyIter.Next())
2882           {
2883             const Graphic3d_Vec2i& aNewPnt = aSelIter.Value();
2884             aPolyIter.ChangeValue() = gp_Pnt2d (aNewPnt.x(), -aNewPnt.y());
2885           }
2886
2887           if (myGL.Selection.IsXOR)
2888           {
2889             theCtx->ShiftSelect (aPolyline, theView, false);
2890           }
2891           else
2892           {
2893             theCtx->Select (aPolyline, theView, false);
2894           }
2895           theCtx->MainSelector()->AllowOverlapDetection (false);
2896         }
2897       }
2898
2899       myRubberBand->ClearPoints();
2900       if (myGL.Selection.Tool != AIS_ViewSelectionTool_ZoomWindow)
2901       {
2902         // selection affects all Views
2903         theView->Viewer()->Invalidate();
2904         OnSelectionChanged (theCtx, theView);
2905       }
2906     }
2907   }
2908 }
2909
2910 // =======================================================================
2911 // function : handleDynamicHighlight
2912 // purpose  :
2913 // =======================================================================
2914 void AIS_ViewController::handleDynamicHighlight (const Handle(AIS_InteractiveContext)& theCtx,
2915                                                  const Handle(V3d_View)& theView)
2916 {
2917   if ((myGL.MoveTo.ToHilight || myGL.Dragging.ToStart)
2918    && myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
2919   {
2920     const Graphic3d_Vec2i& aMoveToPnt = myGL.MoveTo.ToHilight ? myGL.MoveTo.Point : myGL.Dragging.PointStart;
2921     if (myGL.Dragging.ToStart && (!myGL.MoveTo.ToHilight || !myToAllowHighlight)
2922      && !HasPreviousMoveTo())
2923     {
2924       contextLazyMoveTo (theCtx, theView, aMoveToPnt);
2925       ResetPreviousMoveTo();
2926       OnObjectDragged (theCtx, theView, AIS_DragAction_Start);
2927       theCtx->ClearDetected();
2928     }
2929     else if (myToAllowHighlight)
2930     {
2931       if (myPrevMoveTo != aMoveToPnt
2932        || (!theView->View()->IsActiveXR()
2933         && (myGL.OrbitRotation.ToRotate
2934          || myGL.ViewRotation.ToRotate
2935          || theView->IsInvalidated())))
2936       {
2937         ResetPreviousMoveTo();
2938         contextLazyMoveTo (theCtx, theView, aMoveToPnt);
2939       }
2940       if (myGL.Dragging.ToStart)
2941       {
2942         OnObjectDragged (theCtx, theView, AIS_DragAction_Start);
2943       }
2944     }
2945
2946     myGL.MoveTo.ToHilight = false;
2947   }
2948
2949   if (!myDragObject.IsNull())
2950   {
2951     if (myGL.Dragging.ToAbort)
2952     {
2953       OnObjectDragged (theCtx, theView, AIS_DragAction_Abort);
2954       myGL.OrbitRotation.ToRotate = false;
2955       myGL.ViewRotation .ToRotate = false;
2956     }
2957     else if (myGL.Dragging.ToStop)
2958     {
2959       OnObjectDragged (theCtx, theView, AIS_DragAction_Stop);
2960       myGL.OrbitRotation.ToRotate = false;
2961       myGL.ViewRotation .ToRotate = false;
2962     }
2963     else if (myGL.OrbitRotation.ToRotate
2964           || myGL.ViewRotation.ToRotate)
2965     {
2966       OnObjectDragged (theCtx, theView, AIS_DragAction_Update);
2967       myGL.OrbitRotation.ToRotate = false;
2968       myGL.ViewRotation .ToRotate = false;
2969     }
2970   }
2971 }
2972
2973 // =======================================================================
2974 // function : handleMoveTo
2975 // purpose  :
2976 // =======================================================================
2977 void AIS_ViewController::handleMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
2978                                        const Handle(V3d_View)& theView)
2979 {
2980   handleSelectionPick   (theCtx, theView);
2981   handleDynamicHighlight(theCtx, theView);
2982   handleSelectionPoly   (theCtx, theView);
2983 }
2984
2985 // =======================================================================
2986 // function : handleViewRedraw
2987 // purpose  :
2988 // =======================================================================
2989 void AIS_ViewController::handleViewRedraw (const Handle(AIS_InteractiveContext)& ,
2990                                            const Handle(V3d_View)& theView)
2991 {
2992   // manage animation state
2993   if (!myViewAnimation.IsNull()
2994    && !myViewAnimation->IsStopped())
2995   {
2996     myViewAnimation->UpdateTimer();
2997     ResetPreviousMoveTo();
2998     setAskNextFrame();
2999   }
3000
3001   if (theView->View()->IsActiveXR())
3002   {
3003     // VR requires continuous rendering
3004     myToAskNextFrame = true;
3005   }
3006
3007   for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
3008   {
3009     const Handle(V3d_View)& aView = aViewIter.Value();
3010     if (aView->IsInvalidated()
3011      || (myToAskNextFrame && aView == theView))
3012     {
3013       if (aView->ComputedMode())
3014       {
3015         aView->Update();
3016       }
3017       else
3018       {
3019         aView->Redraw();
3020       }
3021     }
3022     else if (aView->IsInvalidatedImmediate())
3023     {
3024       aView->RedrawImmediate();
3025     }
3026   }
3027
3028   if (myToAskNextFrame)
3029   {
3030     // ask more frames
3031     theView->Window()->InvalidateContent (Handle(Aspect_DisplayConnection)());
3032   }
3033 }
3034
3035 // =======================================================================
3036 // function : handleXRMoveTo
3037 // purpose  :
3038 // =======================================================================
3039 Standard_Integer AIS_ViewController::handleXRMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
3040                                                      const Handle(V3d_View)& theView,
3041                                                      const gp_Trsf& thePose,
3042                                                      const Standard_Boolean theToHighlight)
3043 {
3044   //ResetPreviousMoveTo();
3045   Standard_Integer aPickResult = 0;
3046
3047   Handle(Graphic3d_Camera) aCamBack = theView->Camera();
3048   myXRCameraTmp->Copy (aCamBack);
3049   theView->View()->ComputeXRPosedCameraFromBase (*myXRCameraTmp, thePose);
3050   theView->SetCamera (myXRCameraTmp);
3051   Graphic3d_Vec2i aPickPixel;
3052   theView->Window()->Size (aPickPixel.x(), aPickPixel.y());
3053   aPickPixel /= 2;
3054   const Standard_Integer aSelTolerBack = theCtx->MainSelector()->CustomPixelTolerance();
3055   theCtx->MainSelector()->SetPixelTolerance (1);
3056   theView->AutoZFit();
3057   if (theToHighlight)
3058   {
3059     theCtx->MoveTo (aPickPixel.x(), aPickPixel.y(), theView, false);
3060     if (!theCtx->DetectedOwner().IsNull())
3061     {
3062       // ignore 2D objects
3063       for (aPickResult = 1; !theCtx->DetectedOwner()->Selectable()->TransformPersistence().IsNull(); ++aPickResult)
3064       {
3065         if (theCtx->HilightNextDetected (theView, false) <= 1)
3066         {
3067           theCtx->ClearDetected();
3068           aPickResult = 0;
3069           break;
3070         }
3071       }
3072     }
3073   }
3074   else
3075   {
3076     theCtx->MainSelector()->Pick (aPickPixel.x(), aPickPixel.y(), theView);
3077     for (Standard_Integer aPickIter = 1; aPickIter <= theCtx->MainSelector()->NbPicked(); ++aPickIter)
3078     {
3079       const SelectMgr_SortCriterion& aPickedData = theCtx->MainSelector()->PickedData (aPickIter);
3080       if (!aPickedData.Entity->OwnerId()->Selectable()->TransformPersistence().IsNull())
3081       {
3082         // skip 2d objects
3083         continue;
3084       }
3085
3086       aPickResult = aPickIter;
3087       break;
3088     }
3089   }
3090   theCtx->MainSelector()->SetPixelTolerance (aSelTolerBack);
3091   theView->SetCamera (aCamBack);
3092   return aPickResult;
3093 }
3094
3095 // =======================================================================
3096 // function : handleXRHighlight
3097 // purpose  :
3098 // =======================================================================
3099 void AIS_ViewController::handleXRHighlight (const Handle(AIS_InteractiveContext)& theCtx,
3100                                             const Handle(V3d_View)& theView)
3101 {
3102   if (myXRLastPickingHand != Aspect_XRTrackedDeviceRole_LeftHand
3103    && myXRLastPickingHand != Aspect_XRTrackedDeviceRole_RightHand)
3104   {
3105     return;
3106   }
3107
3108   const Standard_Integer aDeviceId = theView->View()->XRSession()->NamedTrackedDevice (myXRLastPickingHand);
3109   if (aDeviceId == -1)
3110   {
3111     return;
3112   }
3113
3114   const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceId];
3115   if (!aPose.IsValidPose)
3116   {
3117     return;
3118   }
3119
3120   Handle(SelectMgr_EntityOwner) aDetOld = theCtx->DetectedOwner();
3121   handleXRMoveTo (theCtx, theView, aPose.Orientation, true);
3122   if (!theCtx->DetectedOwner().IsNull()
3123     && theCtx->DetectedOwner() != aDetOld)
3124   {
3125     if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (myXRLastPickingHand, Aspect_XRGenericAction_OutputHaptic))
3126     {
3127       theView->View()->XRSession()->TriggerHapticVibrationAction (aHaptic, myXRPickingHaptic);
3128     }
3129   }
3130
3131   Standard_Real& aPickDepth = myXRLastPickingHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
3132   aPickDepth = Precision::Infinite();
3133   if (theCtx->MainSelector()->NbPicked() > 0)
3134   {
3135     const gp_Trsf aHandBase = theView->View()->PoseXRToWorld (aPose.Orientation);
3136     const SelectMgr_SortCriterion& aPicked = theCtx->MainSelector()->PickedData (1);
3137     aPickDepth = aPicked.Point.Distance (aHandBase.TranslationPart());
3138   }
3139 }
3140
3141 // =======================================================================
3142 // function : handleXRPresentations
3143 // purpose  :
3144 // =======================================================================
3145 void AIS_ViewController::handleXRPresentations (const Handle(AIS_InteractiveContext)& theCtx,
3146                                                 const Handle(V3d_View)& theView)
3147 {
3148   if (!theView->View()->IsActiveXR()
3149    || (!myToDisplayXRAuxDevices
3150     && !myToDisplayXRHands))
3151   {
3152     for (NCollection_Array1<Handle(AIS_XRTrackedDevice)>::Iterator aPrsIter (myXRPrsDevices); aPrsIter.More(); aPrsIter.Next())
3153     {
3154       if (!aPrsIter.Value().IsNull()
3155         && aPrsIter.Value()->HasInteractiveContext())
3156       {
3157         theCtx->Remove (aPrsIter.Value(), false);
3158       }
3159       aPrsIter.ChangeValue().Nullify();
3160     }
3161     return;
3162   }
3163
3164   if (myXRPrsDevices.Length() != theView->View()->XRSession()->TrackedPoses().Length())
3165   {
3166     for (NCollection_Array1<Handle(AIS_XRTrackedDevice)>::Iterator aPrsIter (myXRPrsDevices); aPrsIter.More(); aPrsIter.Next())
3167     {
3168       if (!aPrsIter.Value().IsNull())
3169       {
3170         theCtx->Remove (aPrsIter.Value(), false);
3171       }
3172     }
3173     myXRPrsDevices.Resize (theView->View()->XRSession()->TrackedPoses().Lower(), theView->View()->XRSession()->TrackedPoses().Upper(), false);
3174   }
3175
3176   const Standard_Integer aHeadDevice  = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_Head);
3177   const Standard_Integer aLeftDevice  = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_LeftHand);
3178   const Standard_Integer aRightDevice = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_RightHand);
3179   for (Standard_Integer aDeviceIter = theView->View()->XRSession()->TrackedPoses().Lower(); aDeviceIter <= theView->View()->XRSession()->TrackedPoses().Upper(); ++aDeviceIter)
3180   {
3181     const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceIter];
3182     Handle(AIS_XRTrackedDevice)& aPosePrs = myXRPrsDevices[aDeviceIter];
3183     if (!aPose.IsValidPose)
3184     {
3185       continue;
3186     }
3187
3188     const bool isHand = aDeviceIter == aLeftDevice
3189                      || aDeviceIter == aRightDevice;
3190     if ((!myToDisplayXRHands && isHand)
3191      || (!myToDisplayXRAuxDevices && !isHand))
3192     {
3193       if (!aPosePrs.IsNull()
3194         && aPosePrs->HasInteractiveContext())
3195       {
3196         theCtx->Remove (aPosePrs, false);
3197       }
3198       continue;
3199     }
3200
3201     Aspect_XRTrackedDeviceRole aRole = Aspect_XRTrackedDeviceRole_Other;
3202     if (aDeviceIter == aLeftDevice)
3203     {
3204       aRole = Aspect_XRTrackedDeviceRole_LeftHand;
3205     }
3206     else if (aDeviceIter == aRightDevice)
3207     {
3208       aRole = Aspect_XRTrackedDeviceRole_RightHand;
3209     }
3210
3211     if (!aPosePrs.IsNull()
3212       && aPosePrs->UnitFactor() != (float )theView->View()->UnitFactor())
3213     {
3214       theCtx->Remove (aPosePrs, false);
3215       aPosePrs.Nullify();
3216     }
3217
3218     if (aPosePrs.IsNull())
3219     {
3220       Handle(Image_Texture) aTexture;
3221       Handle(Graphic3d_ArrayOfTriangles) aTris;
3222       if (aDeviceIter != aHeadDevice)
3223       {
3224         aTris = theView->View()->XRSession()->LoadRenderModel (aDeviceIter, aTexture);
3225       }
3226       if (!aTris.IsNull())
3227       {
3228         aPosePrs = new AIS_XRTrackedDevice (aTris, aTexture);
3229       }
3230       else
3231       {
3232         aPosePrs = new AIS_XRTrackedDevice();
3233       }
3234       aPosePrs->SetUnitFactor ((float )theView->View()->UnitFactor());
3235       aPosePrs->SetMutable (true);
3236       aPosePrs->SetInfiniteState (true);
3237     }
3238     aPosePrs->SetRole (aRole);
3239
3240     if (!aPosePrs->HasInteractiveContext())
3241     {
3242       theCtx->Display (aPosePrs, 0, -1, false);
3243     }
3244
3245     gp_Trsf aPoseLocal = aPose.Orientation;
3246     if (aDeviceIter == aHeadDevice)
3247     {
3248       // show headset position on floor level
3249       aPoseLocal.SetTranslationPart (gp_Vec (aPoseLocal.TranslationPart().X(), 0.0, aPoseLocal.TranslationPart().Z()));
3250     }
3251     const gp_Trsf aPoseWorld = theView->View()->PoseXRToWorld (aPoseLocal);
3252     theCtx->SetLocation (aPosePrs, aPoseWorld);
3253
3254     Standard_Real aLaserLen = 0.0;
3255     if (isHand
3256       && aPosePrs->Role() == myXRLastPickingHand)
3257     {
3258       aLaserLen = myXRLastPickingHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
3259       if (Precision::IsInfinite (aLaserLen))
3260       {
3261         const Bnd_Box aViewBox = theView->View()->MinMaxValues (true);
3262         if (!aViewBox.IsVoid())
3263         {
3264           aLaserLen = Sqrt (aViewBox.SquareExtent());
3265         }
3266         else
3267         {
3268           aLaserLen = 100.0;
3269         }
3270       }
3271       aPosePrs->SetLaserColor (myXRLaserPickColor);
3272     }
3273     else if (isHand
3274           && aPosePrs->Role() == myXRLastTeleportHand)
3275     {
3276       aLaserLen = myXRLastTeleportHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
3277       if (Precision::IsInfinite (aLaserLen))
3278       {
3279         const Bnd_Box aViewBox = theView->View()->MinMaxValues (true);
3280         if (!aViewBox.IsVoid())
3281         {
3282           aLaserLen = Sqrt (aViewBox.SquareExtent());
3283         }
3284         else
3285         {
3286           aLaserLen = 100.0;
3287         }
3288       }
3289       aPosePrs->SetLaserColor (myXRLaserTeleColor);
3290     }
3291     aPosePrs->SetLaserLength ((float )aLaserLen);
3292   }
3293 }
3294
3295 // =======================================================================
3296 // function : HandleViewEvents
3297 // purpose  :
3298 // =======================================================================
3299 void AIS_ViewController::HandleViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
3300                                            const Handle(V3d_View)& theView)
3301 {
3302   const bool wasImmediateUpdate = theView->SetImmediateUpdate (false);
3303
3304   handleViewOrientationKeys (theCtx, theView);
3305   const AIS_WalkDelta aWalk = handleNavigationKeys (theCtx, theView);
3306   handleXRInput (theCtx, theView, aWalk);
3307   if (theView->View()->IsActiveXR())
3308   {
3309     theView->View()->SetupXRPosedCamera();
3310   }
3311   handleMoveTo (theCtx, theView);
3312   handleCameraActions (theCtx, theView, aWalk);
3313   theView->View()->SynchronizeXRPosedToBaseCamera(); // handleCameraActions() may modify posed camera position - copy this modifications also to the base camera
3314   handleXRPresentations (theCtx, theView);
3315
3316   handleViewRedraw (theCtx, theView);
3317   theView->View()->UnsetXRPosedCamera();
3318
3319   theView->SetImmediateUpdate (wasImmediateUpdate);
3320
3321   // make sure to not process the same events twice
3322   myGL.Reset();
3323   myToAskNextFrame = false;
3324 }