0031632: Draw Harness - handle 3d mouse Raw HID input on Windows
[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_LIGHTBLUE, 0.4, 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   {
2693     return;
2694   }
2695
2696   myPrevMoveTo = thePnt;
2697
2698   Handle(SelectMgr_EntityOwner) aLastPicked = theCtx->DetectedOwner();
2699   theView->AutoZFit();
2700   theCtx->MoveTo (thePnt.x(), thePnt.y(), theView, false);
2701   Handle(SelectMgr_EntityOwner) aNewPicked = theCtx->DetectedOwner();
2702
2703   if (theView->Viewer()->Grid()->IsActive()
2704    && theView->Viewer()->GridEcho())
2705   {
2706     if (aNewPicked.IsNull())
2707     {
2708       Graphic3d_Vec3d aPnt3d;
2709       theView->ConvertToGrid (thePnt.x(), thePnt.y(), aPnt3d[0], aPnt3d[1], aPnt3d[2]);
2710       theView->Viewer()->ShowGridEcho (theView, Graphic3d_Vertex (aPnt3d[0], aPnt3d[1], aPnt3d[2]));
2711       theView->InvalidateImmediate();
2712     }
2713     else
2714     {
2715       theView->Viewer()->HideGridEcho (theView);
2716       theView->InvalidateImmediate();
2717     }
2718   }
2719
2720   if (aLastPicked != aNewPicked
2721    || (!aNewPicked.IsNull() && aNewPicked->IsForcedHilight()))
2722   {
2723     // dynamic highlight affects all Views
2724     for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
2725     {
2726       const Handle(V3d_View)& aView = aViewIter.Value();
2727       aView->InvalidateImmediate();
2728     }
2729   }
2730 }
2731
2732 // =======================================================================
2733 // function : handleSelectionPick
2734 // purpose  :
2735 // =======================================================================
2736 void AIS_ViewController::handleSelectionPick (const Handle(AIS_InteractiveContext)& theCtx,
2737                                               const Handle(V3d_View)& theView)
2738 {
2739   if (myGL.Selection.Tool == AIS_ViewSelectionTool_Picking
2740   && !myGL.Selection.Points.IsEmpty())
2741   {
2742     for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aPntIter (myGL.Selection.Points); aPntIter.More(); aPntIter.Next())
2743     {
2744       const bool hadPrevMoveTo = HasPreviousMoveTo();
2745       contextLazyMoveTo (theCtx, theView, aPntIter.Value());
2746       if (!hadPrevMoveTo)
2747       {
2748         ResetPreviousMoveTo();
2749       }
2750
2751       if (myGL.Selection.IsXOR)
2752       {
2753         theCtx->ShiftSelect (false);
2754       }
2755       else
2756       {
2757         theCtx->Select (false);
2758       }
2759
2760       // selection affects all Views
2761       theView->Viewer()->Invalidate();
2762
2763       OnSelectionChanged (theCtx, theView);
2764     }
2765
2766     myGL.Selection.Points.Clear();
2767   }
2768 }
2769
2770 // =======================================================================
2771 // function : handleSelectionPoly
2772 // purpose  :
2773 // =======================================================================
2774 void AIS_ViewController::handleSelectionPoly (const Handle(AIS_InteractiveContext)& theCtx,
2775                                               const Handle(V3d_View)& theView)
2776 {
2777   // rubber-band & window polygon selection
2778   if (myGL.Selection.Tool == AIS_ViewSelectionTool_RubberBand
2779    || myGL.Selection.Tool == AIS_ViewSelectionTool_Polygon
2780    || myGL.Selection.Tool == AIS_ViewSelectionTool_ZoomWindow)
2781   {
2782     if (!myGL.Selection.Points.IsEmpty())
2783     {
2784       myRubberBand->ClearPoints();
2785       myRubberBand->SetToUpdate();
2786
2787       const bool anIsRubber = myGL.Selection.Tool == AIS_ViewSelectionTool_RubberBand
2788                            || myGL.Selection.Tool == AIS_ViewSelectionTool_ZoomWindow;
2789       if (anIsRubber)
2790       {
2791         myRubberBand->SetRectangle (myGL.Selection.Points.First().x(), -myGL.Selection.Points.First().y(),
2792                                     myGL.Selection.Points.Last().x(),  -myGL.Selection.Points.Last().y());
2793       }
2794       else
2795       {
2796         Graphic3d_Vec2i aPrev (IntegerLast(), IntegerLast());
2797         for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aSelIter (myGL.Selection.Points); aSelIter.More(); aSelIter.Next())
2798         {
2799           Graphic3d_Vec2i aPntNew = Graphic3d_Vec2i (aSelIter.Value().x(), -aSelIter.Value().y());
2800           if (aPntNew != aPrev)
2801           {
2802             aPrev = aPntNew;
2803             myRubberBand->AddPoint (Graphic3d_Vec2i (aSelIter.Value().x(), -aSelIter.Value().y()));
2804           }
2805         }
2806       }
2807
2808       myRubberBand->SetPolygonClosed (anIsRubber);
2809       try
2810       {
2811         theCtx->Display (myRubberBand, 0, -1, false, AIS_DS_Displayed);
2812       }
2813       catch (const Standard_Failure& theEx)
2814       {
2815         Message::SendWarning (TCollection_AsciiString ("Internal error while displaying rubber-band: ")
2816                             + theEx.DynamicType()->Name() + ", " + theEx.GetMessageString());
2817         myRubberBand->ClearPoints();
2818       }
2819       if (!theView->Viewer()->ZLayerSettings (myRubberBand->ZLayer()).IsImmediate())
2820       {
2821         theView->Invalidate();
2822       }
2823       else
2824       {
2825         theView->InvalidateImmediate();
2826       }
2827     }
2828     else if (!myRubberBand.IsNull()
2829            && myRubberBand->HasInteractiveContext())
2830     {
2831       theCtx->Remove (myRubberBand, false);
2832       myRubberBand->ClearPoints();
2833     }
2834   }
2835
2836   if (myGL.Selection.ToApplyTool)
2837   {
2838     myGL.Selection.ToApplyTool = false;
2839     if (theCtx->IsDisplayed (myRubberBand))
2840     {
2841       theCtx->Remove (myRubberBand, false);
2842       {
2843         const NCollection_Sequence<Graphic3d_Vec2i>& aPoints = myRubberBand->Points();
2844         if (aPoints.Size() == 4
2845          && aPoints.Value (1).x() == aPoints.Value (2).x()
2846          && aPoints.Value (3).x() == aPoints.Value (4).x()
2847          && aPoints.Value (1).y() == aPoints.Value (4).y()
2848          && aPoints.Value (2).y() == aPoints.Value (3).y())
2849         {
2850           const Graphic3d_Vec2i aPnt1 (aPoints.Value (1).x(), -aPoints.Value (1).y());
2851           const Graphic3d_Vec2i aPnt2 (aPoints.Value (3).x(), -aPoints.Value (3).y());
2852           if (myGL.Selection.Tool == AIS_ViewSelectionTool_ZoomWindow)
2853           {
2854             theView->WindowFitAll (aPnt1.x(), aPnt1.y(), aPnt2.x(), aPnt2.y());
2855             theView->Invalidate();
2856           }
2857           else
2858           {
2859             theCtx->MainSelector()->AllowOverlapDetection (aPnt1.y() != Min (aPnt1.y(), aPnt2.y()));
2860             if (myGL.Selection.IsXOR)
2861             {
2862               theCtx->ShiftSelect (Min (aPnt1.x(), aPnt2.x()), Min (aPnt1.y(), aPnt2.y()),
2863                                    Max (aPnt1.x(), aPnt2.x()), Max (aPnt1.y(), aPnt2.y()),
2864                                    theView, false);
2865             }
2866             else
2867             {
2868               theCtx->Select (Min (aPnt1.x(), aPnt2.x()), Min (aPnt1.y(), aPnt2.y()),
2869                               Max (aPnt1.x(), aPnt2.x()), Max (aPnt1.y(), aPnt2.y()),
2870                               theView, false);
2871             }
2872             theCtx->MainSelector()->AllowOverlapDetection (false);
2873           }
2874         }
2875         else if (aPoints.Length() >= 3)
2876         {
2877           TColgp_Array1OfPnt2d aPolyline (1, aPoints.Length());
2878           TColgp_Array1OfPnt2d::Iterator aPolyIter (aPolyline);
2879           for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aSelIter (aPoints);
2880                aSelIter.More(); aSelIter.Next(), aPolyIter.Next())
2881           {
2882             const Graphic3d_Vec2i& aNewPnt = aSelIter.Value();
2883             aPolyIter.ChangeValue() = gp_Pnt2d (aNewPnt.x(), -aNewPnt.y());
2884           }
2885
2886           if (myGL.Selection.IsXOR)
2887           {
2888             theCtx->ShiftSelect (aPolyline, theView, false);
2889           }
2890           else
2891           {
2892             theCtx->Select (aPolyline, theView, false);
2893           }
2894           theCtx->MainSelector()->AllowOverlapDetection (false);
2895         }
2896       }
2897
2898       myRubberBand->ClearPoints();
2899       if (myGL.Selection.Tool != AIS_ViewSelectionTool_ZoomWindow)
2900       {
2901         // selection affects all Views
2902         theView->Viewer()->Invalidate();
2903         OnSelectionChanged (theCtx, theView);
2904       }
2905     }
2906   }
2907 }
2908
2909 // =======================================================================
2910 // function : handleDynamicHighlight
2911 // purpose  :
2912 // =======================================================================
2913 void AIS_ViewController::handleDynamicHighlight (const Handle(AIS_InteractiveContext)& theCtx,
2914                                                  const Handle(V3d_View)& theView)
2915 {
2916   if ((myGL.MoveTo.ToHilight || myGL.Dragging.ToStart)
2917    && myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
2918   {
2919     const Graphic3d_Vec2i& aMoveToPnt = myGL.MoveTo.ToHilight ? myGL.MoveTo.Point : myGL.Dragging.PointStart;
2920     if (myGL.Dragging.ToStart && (!myGL.MoveTo.ToHilight || !myToAllowHighlight)
2921      && !HasPreviousMoveTo())
2922     {
2923       contextLazyMoveTo (theCtx, theView, aMoveToPnt);
2924       ResetPreviousMoveTo();
2925       OnObjectDragged (theCtx, theView, AIS_DragAction_Start);
2926       theCtx->ClearDetected();
2927     }
2928     else if (myToAllowHighlight)
2929     {
2930       if (myPrevMoveTo != aMoveToPnt
2931        || (!theView->View()->IsActiveXR()
2932         && (myGL.OrbitRotation.ToRotate
2933          || myGL.ViewRotation.ToRotate
2934          || theView->IsInvalidated())))
2935       {
2936         ResetPreviousMoveTo();
2937         contextLazyMoveTo (theCtx, theView, aMoveToPnt);
2938       }
2939       if (myGL.Dragging.ToStart)
2940       {
2941         OnObjectDragged (theCtx, theView, AIS_DragAction_Start);
2942       }
2943     }
2944
2945     myGL.MoveTo.ToHilight = false;
2946   }
2947
2948   if (!myDragObject.IsNull())
2949   {
2950     if (myGL.Dragging.ToAbort)
2951     {
2952       OnObjectDragged (theCtx, theView, AIS_DragAction_Abort);
2953       myGL.OrbitRotation.ToRotate = false;
2954       myGL.ViewRotation .ToRotate = false;
2955     }
2956     else if (myGL.Dragging.ToStop)
2957     {
2958       OnObjectDragged (theCtx, theView, AIS_DragAction_Stop);
2959       myGL.OrbitRotation.ToRotate = false;
2960       myGL.ViewRotation .ToRotate = false;
2961     }
2962     else if (myGL.OrbitRotation.ToRotate
2963           || myGL.ViewRotation.ToRotate)
2964     {
2965       OnObjectDragged (theCtx, theView, AIS_DragAction_Update);
2966       myGL.OrbitRotation.ToRotate = false;
2967       myGL.ViewRotation .ToRotate = false;
2968     }
2969   }
2970 }
2971
2972 // =======================================================================
2973 // function : handleMoveTo
2974 // purpose  :
2975 // =======================================================================
2976 void AIS_ViewController::handleMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
2977                                        const Handle(V3d_View)& theView)
2978 {
2979   handleSelectionPick   (theCtx, theView);
2980   handleDynamicHighlight(theCtx, theView);
2981   handleSelectionPoly   (theCtx, theView);
2982 }
2983
2984 // =======================================================================
2985 // function : handleViewRedraw
2986 // purpose  :
2987 // =======================================================================
2988 void AIS_ViewController::handleViewRedraw (const Handle(AIS_InteractiveContext)& ,
2989                                            const Handle(V3d_View)& theView)
2990 {
2991   // manage animation state
2992   if (!myViewAnimation.IsNull()
2993    && !myViewAnimation->IsStopped())
2994   {
2995     myViewAnimation->UpdateTimer();
2996     ResetPreviousMoveTo();
2997     setAskNextFrame();
2998   }
2999
3000   if (theView->View()->IsActiveXR())
3001   {
3002     // VR requires continuous rendering
3003     myToAskNextFrame = true;
3004   }
3005
3006   for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
3007   {
3008     const Handle(V3d_View)& aView = aViewIter.Value();
3009     if (aView->IsInvalidated()
3010      || (myToAskNextFrame && aView == theView))
3011     {
3012       if (aView->ComputedMode())
3013       {
3014         aView->Update();
3015       }
3016       else
3017       {
3018         aView->Redraw();
3019       }
3020     }
3021     else if (aView->IsInvalidatedImmediate())
3022     {
3023       aView->RedrawImmediate();
3024     }
3025   }
3026
3027   if (myToAskNextFrame)
3028   {
3029     // ask more frames
3030     theView->Window()->InvalidateContent (Handle(Aspect_DisplayConnection)());
3031   }
3032 }
3033
3034 // =======================================================================
3035 // function : handleXRMoveTo
3036 // purpose  :
3037 // =======================================================================
3038 Standard_Integer AIS_ViewController::handleXRMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
3039                                                      const Handle(V3d_View)& theView,
3040                                                      const gp_Trsf& thePose,
3041                                                      const Standard_Boolean theToHighlight)
3042 {
3043   //ResetPreviousMoveTo();
3044   Standard_Integer aPickResult = 0;
3045
3046   Handle(Graphic3d_Camera) aCamBack = theView->Camera();
3047   myXRCameraTmp->Copy (aCamBack);
3048   theView->View()->ComputeXRPosedCameraFromBase (*myXRCameraTmp, thePose);
3049   theView->SetCamera (myXRCameraTmp);
3050   Graphic3d_Vec2i aPickPixel;
3051   theView->Window()->Size (aPickPixel.x(), aPickPixel.y());
3052   aPickPixel /= 2;
3053   const Standard_Integer aSelTolerBack = theCtx->MainSelector()->CustomPixelTolerance();
3054   theCtx->MainSelector()->SetPixelTolerance (1);
3055   theView->AutoZFit();
3056   if (theToHighlight)
3057   {
3058     theCtx->MoveTo (aPickPixel.x(), aPickPixel.y(), theView, false);
3059     if (!theCtx->DetectedOwner().IsNull())
3060     {
3061       // ignore 2D objects
3062       for (aPickResult = 1; !theCtx->DetectedOwner()->Selectable()->TransformPersistence().IsNull(); ++aPickResult)
3063       {
3064         if (theCtx->HilightNextDetected (theView, false) <= 1)
3065         {
3066           theCtx->ClearDetected();
3067           aPickResult = 0;
3068           break;
3069         }
3070       }
3071     }
3072   }
3073   else
3074   {
3075     theCtx->MainSelector()->Pick (aPickPixel.x(), aPickPixel.y(), theView);
3076     for (Standard_Integer aPickIter = 1; aPickIter <= theCtx->MainSelector()->NbPicked(); ++aPickIter)
3077     {
3078       const SelectMgr_SortCriterion& aPickedData = theCtx->MainSelector()->PickedData (aPickIter);
3079       if (!aPickedData.Entity->OwnerId()->Selectable()->TransformPersistence().IsNull())
3080       {
3081         // skip 2d objects
3082         continue;
3083       }
3084
3085       aPickResult = aPickIter;
3086       break;
3087     }
3088   }
3089   theCtx->MainSelector()->SetPixelTolerance (aSelTolerBack);
3090   theView->SetCamera (aCamBack);
3091   return aPickResult;
3092 }
3093
3094 // =======================================================================
3095 // function : handleXRHighlight
3096 // purpose  :
3097 // =======================================================================
3098 void AIS_ViewController::handleXRHighlight (const Handle(AIS_InteractiveContext)& theCtx,
3099                                             const Handle(V3d_View)& theView)
3100 {
3101   if (myXRLastPickingHand != Aspect_XRTrackedDeviceRole_LeftHand
3102    && myXRLastPickingHand != Aspect_XRTrackedDeviceRole_RightHand)
3103   {
3104     return;
3105   }
3106
3107   const Standard_Integer aDeviceId = theView->View()->XRSession()->NamedTrackedDevice (myXRLastPickingHand);
3108   if (aDeviceId == -1)
3109   {
3110     return;
3111   }
3112
3113   const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceId];
3114   if (!aPose.IsValidPose)
3115   {
3116     return;
3117   }
3118
3119   Handle(SelectMgr_EntityOwner) aDetOld = theCtx->DetectedOwner();
3120   handleXRMoveTo (theCtx, theView, aPose.Orientation, true);
3121   if (!theCtx->DetectedOwner().IsNull()
3122     && theCtx->DetectedOwner() != aDetOld)
3123   {
3124     if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (myXRLastPickingHand, Aspect_XRGenericAction_OutputHaptic))
3125     {
3126       theView->View()->XRSession()->TriggerHapticVibrationAction (aHaptic, myXRPickingHaptic);
3127     }
3128   }
3129
3130   Standard_Real& aPickDepth = myXRLastPickingHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
3131   aPickDepth = Precision::Infinite();
3132   if (theCtx->MainSelector()->NbPicked() > 0)
3133   {
3134     const gp_Trsf aHandBase = theView->View()->PoseXRToWorld (aPose.Orientation);
3135     const SelectMgr_SortCriterion& aPicked = theCtx->MainSelector()->PickedData (1);
3136     aPickDepth = aPicked.Point.Distance (aHandBase.TranslationPart());
3137   }
3138 }
3139
3140 // =======================================================================
3141 // function : handleXRPresentations
3142 // purpose  :
3143 // =======================================================================
3144 void AIS_ViewController::handleXRPresentations (const Handle(AIS_InteractiveContext)& theCtx,
3145                                                 const Handle(V3d_View)& theView)
3146 {
3147   if (!theView->View()->IsActiveXR()
3148    || (!myToDisplayXRAuxDevices
3149     && !myToDisplayXRHands))
3150   {
3151     for (NCollection_Array1<Handle(AIS_XRTrackedDevice)>::Iterator aPrsIter (myXRPrsDevices); aPrsIter.More(); aPrsIter.Next())
3152     {
3153       if (!aPrsIter.Value().IsNull()
3154         && aPrsIter.Value()->HasInteractiveContext())
3155       {
3156         theCtx->Remove (aPrsIter.Value(), false);
3157       }
3158       aPrsIter.ChangeValue().Nullify();
3159     }
3160     return;
3161   }
3162
3163   if (myXRPrsDevices.Length() != theView->View()->XRSession()->TrackedPoses().Length())
3164   {
3165     for (NCollection_Array1<Handle(AIS_XRTrackedDevice)>::Iterator aPrsIter (myXRPrsDevices); aPrsIter.More(); aPrsIter.Next())
3166     {
3167       if (!aPrsIter.Value().IsNull())
3168       {
3169         theCtx->Remove (aPrsIter.Value(), false);
3170       }
3171     }
3172     myXRPrsDevices.Resize (theView->View()->XRSession()->TrackedPoses().Lower(), theView->View()->XRSession()->TrackedPoses().Upper(), false);
3173   }
3174
3175   const Standard_Integer aHeadDevice  = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_Head);
3176   const Standard_Integer aLeftDevice  = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_LeftHand);
3177   const Standard_Integer aRightDevice = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_RightHand);
3178   for (Standard_Integer aDeviceIter = theView->View()->XRSession()->TrackedPoses().Lower(); aDeviceIter <= theView->View()->XRSession()->TrackedPoses().Upper(); ++aDeviceIter)
3179   {
3180     const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceIter];
3181     Handle(AIS_XRTrackedDevice)& aPosePrs = myXRPrsDevices[aDeviceIter];
3182     if (!aPose.IsValidPose)
3183     {
3184       continue;
3185     }
3186
3187     const bool isHand = aDeviceIter == aLeftDevice
3188                      || aDeviceIter == aRightDevice;
3189     if ((!myToDisplayXRHands && isHand)
3190      || (!myToDisplayXRAuxDevices && !isHand))
3191     {
3192       if (!aPosePrs.IsNull()
3193         && aPosePrs->HasInteractiveContext())
3194       {
3195         theCtx->Remove (aPosePrs, false);
3196       }
3197       continue;
3198     }
3199
3200     Aspect_XRTrackedDeviceRole aRole = Aspect_XRTrackedDeviceRole_Other;
3201     if (aDeviceIter == aLeftDevice)
3202     {
3203       aRole = Aspect_XRTrackedDeviceRole_LeftHand;
3204     }
3205     else if (aDeviceIter == aRightDevice)
3206     {
3207       aRole = Aspect_XRTrackedDeviceRole_RightHand;
3208     }
3209
3210     if (!aPosePrs.IsNull()
3211       && aPosePrs->UnitFactor() != (float )theView->View()->UnitFactor())
3212     {
3213       theCtx->Remove (aPosePrs, false);
3214       aPosePrs.Nullify();
3215     }
3216
3217     if (aPosePrs.IsNull())
3218     {
3219       Handle(Image_Texture) aTexture;
3220       Handle(Graphic3d_ArrayOfTriangles) aTris;
3221       if (aDeviceIter != aHeadDevice)
3222       {
3223         aTris = theView->View()->XRSession()->LoadRenderModel (aDeviceIter, aTexture);
3224       }
3225       if (!aTris.IsNull())
3226       {
3227         aPosePrs = new AIS_XRTrackedDevice (aTris, aTexture);
3228       }
3229       else
3230       {
3231         aPosePrs = new AIS_XRTrackedDevice();
3232       }
3233       aPosePrs->SetUnitFactor ((float )theView->View()->UnitFactor());
3234       aPosePrs->SetMutable (true);
3235       aPosePrs->SetInfiniteState (true);
3236     }
3237     aPosePrs->SetRole (aRole);
3238
3239     if (!aPosePrs->HasInteractiveContext())
3240     {
3241       theCtx->Display (aPosePrs, 0, -1, false);
3242     }
3243
3244     gp_Trsf aPoseLocal = aPose.Orientation;
3245     if (aDeviceIter == aHeadDevice)
3246     {
3247       // show headset position on floor level
3248       aPoseLocal.SetTranslationPart (gp_Vec (aPoseLocal.TranslationPart().X(), 0.0, aPoseLocal.TranslationPart().Z()));
3249     }
3250     const gp_Trsf aPoseWorld = theView->View()->PoseXRToWorld (aPoseLocal);
3251     theCtx->SetLocation (aPosePrs, aPoseWorld);
3252
3253     Standard_Real aLaserLen = 0.0;
3254     if (isHand
3255       && aPosePrs->Role() == myXRLastPickingHand)
3256     {
3257       aLaserLen = myXRLastPickingHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
3258       if (Precision::IsInfinite (aLaserLen))
3259       {
3260         const Bnd_Box aViewBox = theView->View()->MinMaxValues (true);
3261         if (!aViewBox.IsVoid())
3262         {
3263           aLaserLen = Sqrt (aViewBox.SquareExtent());
3264         }
3265         else
3266         {
3267           aLaserLen = 100.0;
3268         }
3269       }
3270       aPosePrs->SetLaserColor (myXRLaserPickColor);
3271     }
3272     else if (isHand
3273           && aPosePrs->Role() == myXRLastTeleportHand)
3274     {
3275       aLaserLen = myXRLastTeleportHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
3276       if (Precision::IsInfinite (aLaserLen))
3277       {
3278         const Bnd_Box aViewBox = theView->View()->MinMaxValues (true);
3279         if (!aViewBox.IsVoid())
3280         {
3281           aLaserLen = Sqrt (aViewBox.SquareExtent());
3282         }
3283         else
3284         {
3285           aLaserLen = 100.0;
3286         }
3287       }
3288       aPosePrs->SetLaserColor (myXRLaserTeleColor);
3289     }
3290     aPosePrs->SetLaserLength ((float )aLaserLen);
3291   }
3292 }
3293
3294 // =======================================================================
3295 // function : HandleViewEvents
3296 // purpose  :
3297 // =======================================================================
3298 void AIS_ViewController::HandleViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
3299                                            const Handle(V3d_View)& theView)
3300 {
3301   const bool wasImmediateUpdate = theView->SetImmediateUpdate (false);
3302
3303   handleViewOrientationKeys (theCtx, theView);
3304   const AIS_WalkDelta aWalk = handleNavigationKeys (theCtx, theView);
3305   handleXRInput (theCtx, theView, aWalk);
3306   if (theView->View()->IsActiveXR())
3307   {
3308     theView->View()->SetupXRPosedCamera();
3309   }
3310   handleMoveTo (theCtx, theView);
3311   handleCameraActions (theCtx, theView, aWalk);
3312   theView->View()->SynchronizeXRPosedToBaseCamera(); // handleCameraActions() may modify posed camera position - copy this modifications also to the base camera
3313   handleXRPresentations (theCtx, theView);
3314
3315   handleViewRedraw (theCtx, theView);
3316   theView->View()->UnsetXRPosedCamera();
3317
3318   theView->SetImmediateUpdate (wasImmediateUpdate);
3319
3320   // make sure to not process the same events twice
3321   myGL.Reset();
3322   myToAskNextFrame = false;
3323 }