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