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