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