736f0acb5d0c7ef105d7e5afcb510040a280f0e8
[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 : handleNavigationKeys
1760 // purpose  :
1761 // =======================================================================
1762 AIS_WalkDelta AIS_ViewController::handleNavigationKeys (const Handle(AIS_InteractiveContext)& ,
1763                                                         const Handle(V3d_View)& theView)
1764 {
1765   // navigation keys
1766   double aCrouchRatio = 1.0, aRunRatio = 1.0;
1767   if (myNavigationMode == AIS_NavigationMode_FirstPersonFlight)
1768   {
1769     aRunRatio = 3.0;
1770   }
1771
1772   const double aRotSpeed = 0.5;
1773   const double aWalkSpeedCoef = WalkSpeedRelative();
1774   AIS_WalkDelta aWalk = FetchNavigationKeys (aCrouchRatio, aRunRatio);
1775   if (aWalk.IsJumping())
1776   {
1777     // ask more frames
1778     setAskNextFrame();
1779     theView->Invalidate();
1780   }
1781   if (aWalk.IsEmpty())
1782   {
1783     return aWalk;
1784   }
1785   else if (myGL.OrbitRotation.ToRotate
1786         || myGL.OrbitRotation.ToStart)
1787   {
1788     return aWalk;
1789   }
1790
1791   gp_XYZ aMin, aMax;
1792   const Bnd_Box aBndBox = theView->View()->MinMaxValues();
1793   if (!aBndBox.IsVoid())
1794   {
1795     aMin = aBndBox.CornerMin().XYZ();
1796     aMax = aBndBox.CornerMax().XYZ();
1797   }
1798   double aBndDiam = Max (Max (aMax.X() - aMin.X(), aMax.Y() - aMin.Y()), aMax.Z() - aMin.Z());
1799   if (aBndDiam <= gp::Resolution())
1800   {
1801     aBndDiam = 0.001;
1802   }
1803
1804   const double aWalkSpeed = myNavigationMode != AIS_NavigationMode_Orbit
1805                          && myNavigationMode != AIS_NavigationMode_FirstPersonFlight
1806                           ? theView->View()->UnitFactor() * WalkSpeedAbsolute()
1807                           : aWalkSpeedCoef * aBndDiam;
1808   const Handle(Graphic3d_Camera)& aCam = theView->View()->IsActiveXR()
1809                                        ? theView->View()->BaseXRCamera()
1810                                        : theView->Camera();
1811
1812   // move forward in plane XY and up along Z
1813   const gp_Dir anUp = ToLockOrbitZUp() ? gp::DZ() : aCam->OrthogonalizedUp();
1814   if (aWalk.ToMove()
1815    && myToAllowPanning)
1816   {
1817     const gp_Vec aSide = -aCam->SideRight();
1818     gp_XYZ aFwd = aCam->Direction().XYZ();
1819     aFwd -= anUp.XYZ() * (anUp.XYZ() * aFwd);
1820
1821     gp_XYZ aMoveVec;
1822     if (!aWalk[AIS_WalkTranslation_Forward].IsEmpty())
1823     {
1824       if (!aCam->IsOrthographic())
1825       {
1826         aMoveVec += aFwd * aWalk[AIS_WalkTranslation_Forward].Value * aWalk[AIS_WalkTranslation_Forward].Pressure * aWalkSpeed;
1827       }
1828     }
1829     if (!aWalk[AIS_WalkTranslation_Side].IsEmpty())
1830     {
1831       aMoveVec += aSide.XYZ() * aWalk[AIS_WalkTranslation_Side].Value * aWalk[AIS_WalkTranslation_Side].Pressure * aWalkSpeed;
1832     }
1833     if (!aWalk[AIS_WalkTranslation_Up].IsEmpty())
1834     {
1835       aMoveVec += anUp.XYZ() * aWalk[AIS_WalkTranslation_Up].Value * aWalk[AIS_WalkTranslation_Up].Pressure * aWalkSpeed;
1836     }
1837     {
1838       if (aCam->IsOrthographic())
1839       {
1840         if (!aWalk[AIS_WalkTranslation_Forward].IsEmpty())
1841         {
1842           const double aZoomDelta = aWalk[AIS_WalkTranslation_Forward].Value * aWalk[AIS_WalkTranslation_Forward].Pressure * aWalkSpeedCoef;
1843           handleZoom (theView, Aspect_ScrollDelta (aZoomDelta * 100.0), NULL);
1844         }
1845       }
1846
1847       gp_Trsf aTrsfTranslate;
1848       aTrsfTranslate.SetTranslation (aMoveVec);
1849       aCam->Transform (aTrsfTranslate);
1850     }
1851   }
1852
1853   if (myNavigationMode == AIS_NavigationMode_Orbit
1854    && myToAllowRotation)
1855   {
1856     if (!aWalk[AIS_WalkRotation_Yaw].IsEmpty())
1857     {
1858       gp_Trsf aTrsfRot;
1859       aTrsfRot.SetRotation (gp_Ax1 (aCam->Eye(), anUp), aWalk[AIS_WalkRotation_Yaw].Value * aRotSpeed);
1860       aCam->Transform (aTrsfRot);
1861     }
1862     if (!aWalk[AIS_WalkRotation_Pitch].IsEmpty())
1863     {
1864       const gp_Vec aSide = -aCam->SideRight();
1865       gp_Trsf aTrsfRot;
1866       aTrsfRot.SetRotation (gp_Ax1 (aCam->Eye(), aSide), -aWalk[AIS_WalkRotation_Pitch].Value * aRotSpeed);
1867       aCam->Transform (aTrsfRot);
1868     }
1869     if (!aWalk[AIS_WalkRotation_Roll].IsEmpty()
1870      && !ToLockOrbitZUp())
1871     {
1872       gp_Trsf aTrsfRot;
1873       aTrsfRot.SetRotation (gp_Ax1 (aCam->Center(), aCam->Direction()), aWalk[AIS_WalkRotation_Roll].Value * aRotSpeed);
1874       aCam->Transform (aTrsfRot);
1875     }
1876   }
1877
1878   // ask more frames
1879   setAskNextFrame();
1880   theView->Invalidate();
1881   theView->View()->SynchronizeXRBaseToPosedCamera();
1882   return aWalk;
1883 }
1884
1885 // =======================================================================
1886 // function : handleCameraActions
1887 // purpose  :
1888 // =======================================================================
1889 void AIS_ViewController::handleCameraActions (const Handle(AIS_InteractiveContext)& theCtx,
1890                                               const Handle(V3d_View)& theView,
1891                                               const AIS_WalkDelta& theWalk)
1892 {
1893   // apply view actions
1894   if (myGL.Orientation.ToSetViewOrient)
1895   {
1896     theView->SetProj (myGL.Orientation.ViewOrient);
1897     myGL.Orientation.ToFitAll = true;
1898   }
1899
1900   // apply fit all
1901   if (myGL.Orientation.ToFitAll)
1902   {
1903     const double aFitMargin = 0.01;
1904     theView->FitAll (aFitMargin, false);
1905     theView->Invalidate();
1906     myGL.Orientation.ToFitAll = false;
1907   }
1908
1909   if (myGL.IsNewGesture)
1910   {
1911     if (myAnchorPointPrs1->HasInteractiveContext())
1912     {
1913       theCtx->Remove (myAnchorPointPrs1, false);
1914       if (!theView->Viewer()->ZLayerSettings (myAnchorPointPrs1->ZLayer()).IsImmediate())
1915       {
1916         theView->Invalidate();
1917       }
1918       else
1919       {
1920         theView->InvalidateImmediate();
1921       }
1922     }
1923     if (myAnchorPointPrs2->HasInteractiveContext())
1924     {
1925       theCtx->Remove (myAnchorPointPrs2, false);
1926       if (!theView->Viewer()->ZLayerSettings (myAnchorPointPrs2->ZLayer()).IsImmediate())
1927       {
1928         theView->Invalidate();
1929       }
1930       else
1931       {
1932         theView->InvalidateImmediate();
1933       }
1934     }
1935
1936     if (myHasHlrOnBeforeRotation)
1937     {
1938       myHasHlrOnBeforeRotation = false;
1939       theView->SetComputedMode (true);
1940       theView->Invalidate();
1941     }
1942   }
1943
1944   if (myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
1945   {
1946     if (myGL.Panning.ToStart
1947      && myToAllowPanning)
1948     {
1949       gp_Pnt aPanPnt (Precision::Infinite(), 0.0, 0.0);
1950       if (!theView->Camera()->IsOrthographic())
1951       {
1952         bool toStickToRay = false;
1953         if (myGL.Panning.PointStart.x() >= 0
1954          && myGL.Panning.PointStart.y() >= 0)
1955         {
1956           PickPoint (aPanPnt, theCtx, theView, myGL.Panning.PointStart, toStickToRay);
1957         }
1958         if (Precision::IsInfinite (aPanPnt.X()))
1959         {
1960           Graphic3d_Vec2i aWinSize;
1961           theView->Window()->Size (aWinSize.x(), aWinSize.y());
1962           PickPoint (aPanPnt, theCtx, theView, aWinSize / 2, toStickToRay);
1963         }
1964         if (!Precision::IsInfinite (aPanPnt.X())
1965           && myToShowPanAnchorPoint)
1966         {
1967           gp_Trsf aPntTrsf;
1968           aPntTrsf.SetTranslation (gp_Vec (aPanPnt.XYZ()));
1969           theCtx->SetLocation (myAnchorPointPrs2, aPntTrsf);
1970         }
1971       }
1972       setPanningAnchorPoint (aPanPnt);
1973     }
1974
1975     if (myToShowPanAnchorPoint
1976     &&  hasPanningAnchorPoint()
1977     &&  myGL.Panning.ToPan
1978     && !myGL.IsNewGesture
1979     && !myAnchorPointPrs2->HasInteractiveContext())
1980     {
1981       theCtx->Display (myAnchorPointPrs2, 0, -1, false, AIS_DS_Displayed);
1982     }
1983
1984     handlePanning (theView);
1985     handleZRotate (theView);
1986   }
1987
1988   if ((myNavigationMode == AIS_NavigationMode_Orbit
1989     || myGL.OrbitRotation.ToStart
1990     || myGL.OrbitRotation.ToRotate)
1991    && myToAllowRotation)
1992   {
1993     if (myGL.OrbitRotation.ToStart
1994     && !myHasHlrOnBeforeRotation)
1995     {
1996       myHasHlrOnBeforeRotation = theView->ComputedMode();
1997       if (myHasHlrOnBeforeRotation)
1998       {
1999         theView->SetComputedMode (false);
2000       }
2001     }
2002
2003     gp_Pnt aGravPnt;
2004     if (myGL.OrbitRotation.ToStart)
2005     {
2006       aGravPnt = GravityPoint (theCtx, theView);
2007       if (myToShowRotateCenter)
2008       {
2009         gp_Trsf aPntTrsf;
2010         aPntTrsf.SetTranslation (gp_Vec (aGravPnt.XYZ()));
2011         theCtx->SetLocation (myAnchorPointPrs1, aPntTrsf);
2012         theCtx->SetLocation (myAnchorPointPrs2, aPntTrsf);
2013       }
2014     }
2015
2016     if (myToShowRotateCenter
2017     &&  myGL.OrbitRotation.ToRotate
2018     && !myGL.IsNewGesture
2019     && !myAnchorPointPrs1->HasInteractiveContext())
2020     {
2021       theCtx->Display (myAnchorPointPrs1, 0, -1, false, AIS_DS_Displayed);
2022       theCtx->Display (myAnchorPointPrs2, 0, -1, false, AIS_DS_Displayed);
2023     }
2024     handleOrbitRotation (theView, aGravPnt,
2025                          myToLockOrbitZUp || myNavigationMode != AIS_NavigationMode_Orbit);
2026   }
2027
2028   if ((myNavigationMode != AIS_NavigationMode_Orbit
2029     || myGL.ViewRotation.ToStart
2030     || myGL.ViewRotation.ToRotate)
2031    && myToAllowRotation)
2032   {
2033     if (myGL.ViewRotation.ToStart
2034     && !myHasHlrOnBeforeRotation)
2035     {
2036       myHasHlrOnBeforeRotation = theView->ComputedMode();
2037       if (myHasHlrOnBeforeRotation)
2038       {
2039         theView->SetComputedMode (false);
2040       }
2041     }
2042
2043     double aRoll = 0.0;
2044     if (!theWalk[AIS_WalkRotation_Roll].IsEmpty()
2045      && !myToLockOrbitZUp)
2046     {
2047       aRoll = (M_PI / 12.0) * theWalk[AIS_WalkRotation_Roll].Pressure;
2048       aRoll *= Min (1000.0  * theWalk[AIS_WalkRotation_Roll].Duration, 100.0) / 100.0;
2049       if (theWalk[AIS_WalkRotation_Roll].Value < 0.0)
2050       {
2051         aRoll = -aRoll;
2052       }
2053     }
2054
2055     handleViewRotation (theView, theWalk[AIS_WalkRotation_Yaw].Value, theWalk[AIS_WalkRotation_Pitch].Value, aRoll,
2056                         myNavigationMode == AIS_NavigationMode_FirstPersonFlight);
2057   }
2058
2059   if (!myGL.ZoomActions.IsEmpty())
2060   {
2061     for (NCollection_Sequence<Aspect_ScrollDelta>::Iterator aZoomIter (myGL.ZoomActions); aZoomIter.More(); aZoomIter.Next())
2062     {
2063       Aspect_ScrollDelta aZoomParams = aZoomIter.Value();
2064       if (myToAllowZFocus
2065        && (aZoomParams.Flags & Aspect_VKeyFlags_CTRL) != 0
2066        && theView->Camera()->IsStereo())
2067       {
2068         handleZFocusScroll (theView, aZoomParams);
2069         continue;
2070       }
2071
2072       if (!myToAllowZooming)
2073       {
2074         continue;
2075       }
2076
2077       if (!theView->Camera()->IsOrthographic())
2078       {
2079         gp_Pnt aPnt;
2080         if (aZoomParams.HasPoint()
2081          && PickPoint (aPnt, theCtx, theView, aZoomParams.Point, myToStickToRayOnZoom))
2082         {
2083           handleZoom (theView, aZoomParams, &aPnt);
2084           continue;
2085         }
2086
2087         Graphic3d_Vec2i aWinSize;
2088         theView->Window()->Size (aWinSize.x(), aWinSize.y());
2089         if (PickPoint (aPnt, theCtx, theView, aWinSize / 2, myToStickToRayOnZoom))
2090         {
2091           aZoomParams.ResetPoint(); // do not pretend to zoom at 'nothing'
2092           handleZoom (theView, aZoomParams, &aPnt);
2093           continue;
2094         }
2095       }
2096       handleZoom (theView, aZoomParams, NULL);
2097     }
2098     myGL.ZoomActions.Clear();
2099   }
2100 }
2101
2102 // =======================================================================
2103 // function : handleXRInput
2104 // purpose  :
2105 // =======================================================================
2106 void AIS_ViewController::handleXRInput (const Handle(AIS_InteractiveContext)& theCtx,
2107                                         const Handle(V3d_View)& theView,
2108                                         const AIS_WalkDelta& )
2109 {
2110   theView->View()->ProcessXRInput();
2111   if (!theView->View()->IsActiveXR())
2112   {
2113     return;
2114   }
2115   if (myXRCameraTmp.IsNull())
2116   {
2117     myXRCameraTmp = new Graphic3d_Camera();
2118   }
2119   handleXRTurnPad (theCtx, theView);
2120   handleXRTeleport(theCtx, theView);
2121   handleXRPicking (theCtx, theView);
2122 }
2123
2124 // =======================================================================
2125 // function : handleXRTurnPad
2126 // purpose  :
2127 // =======================================================================
2128 void AIS_ViewController::handleXRTurnPad (const Handle(AIS_InteractiveContext)& ,
2129                                           const Handle(V3d_View)& theView)
2130 {
2131   if (myXRTurnAngle <= 0.0
2132   || !theView->View()->IsActiveXR())
2133   {
2134     return;
2135   }
2136
2137   // turn left/right at 45 degrees on left/right trackpad clicks
2138   for (int aHand = 0; aHand < 2; ++aHand)
2139   {
2140     const Aspect_XRTrackedDeviceRole aRole = aHand == 0 ? Aspect_XRTrackedDeviceRole_RightHand : Aspect_XRTrackedDeviceRole_LeftHand;
2141     const Handle(Aspect_XRAction)& aPadClickAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadClick);
2142     const Handle(Aspect_XRAction)& aPadPosAct   = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadPosition);
2143     if (aPadClickAct.IsNull()
2144     ||  aPadPosAct.IsNull())
2145     {
2146       continue;
2147     }
2148
2149     const Aspect_XRDigitalActionData aPadClick = theView->View()->XRSession()->GetDigitalActionData (aPadClickAct);
2150     const Aspect_XRAnalogActionData  aPadPos   = theView->View()->XRSession()->GetAnalogActionData (aPadPosAct);
2151     if (aPadClick.IsActive
2152      && aPadClick.IsPressed
2153      && aPadClick.IsChanged
2154      && aPadPos.IsActive
2155      && Abs (aPadPos.VecXYZ.y()) < 0.5f
2156      && Abs (aPadPos.VecXYZ.x()) > 0.7f)
2157     {
2158       gp_Trsf aTrsfTurn;
2159       aTrsfTurn.SetRotation (gp_Ax1 (gp::Origin(), theView->View()->BaseXRCamera()->Up()), aPadPos.VecXYZ.x() < 0.0f ? myXRTurnAngle : -myXRTurnAngle);
2160       theView->View()->TurnViewXRCamera (aTrsfTurn);
2161       break;
2162     }
2163   }
2164 }
2165
2166 // =======================================================================
2167 // function : handleXRTeleport
2168 // purpose  :
2169 // =======================================================================
2170 void AIS_ViewController::handleXRTeleport (const Handle(AIS_InteractiveContext)& theCtx,
2171                                            const Handle(V3d_View)& theView)
2172 {
2173   if (!theView->View()->IsActiveXR())
2174   {
2175     return;
2176   }
2177
2178   // teleport on forward trackpad unclicks
2179   const Aspect_XRTrackedDeviceRole aTeleOld = myXRLastTeleportHand;
2180   myXRLastTeleportHand = Aspect_XRTrackedDeviceRole_Other;
2181   for (int aHand = 0; aHand < 2; ++aHand)
2182   {
2183     const Aspect_XRTrackedDeviceRole aRole = aHand == 0 ? Aspect_XRTrackedDeviceRole_RightHand : Aspect_XRTrackedDeviceRole_LeftHand;
2184     const Standard_Integer aDeviceId = theView->View()->XRSession()->NamedTrackedDevice (aRole);
2185     if (aDeviceId == -1)
2186     {
2187       continue;
2188     }
2189
2190     const Handle(Aspect_XRAction)& aPadClickAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadClick);
2191     const Handle(Aspect_XRAction)& aPadPosAct   = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadPosition);
2192     if (aPadClickAct.IsNull()
2193     ||  aPadPosAct.IsNull())
2194     {
2195       continue;
2196     }
2197
2198     const Aspect_XRDigitalActionData aPadClick = theView->View()->XRSession()->GetDigitalActionData (aPadClickAct);
2199     const Aspect_XRAnalogActionData  aPadPos   = theView->View()->XRSession()->GetAnalogActionData (aPadPosAct);
2200     const bool isPressed =  aPadClick.IsPressed;
2201     const bool isClicked = !aPadClick.IsPressed
2202                         &&  aPadClick.IsChanged;
2203     if (aPadClick.IsActive
2204      && (isPressed || isClicked)
2205      && aPadPos.IsActive
2206      && aPadPos.VecXYZ.y() > 0.6f
2207      && Abs (aPadPos.VecXYZ.x()) < 0.5f)
2208     {
2209       const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceId];
2210       if (!aPose.IsValidPose)
2211       {
2212         continue;
2213       }
2214
2215       myXRLastTeleportHand = aRole;
2216       Standard_Real& aPickDepth = aRole == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
2217       aPickDepth = Precision::Infinite();
2218       Graphic3d_Vec3 aPickNorm;
2219       const gp_Trsf aHandBase = theView->View()->PoseXRToWorld (aPose.Orientation);
2220       const Standard_Real aHeadHeight = theView->View()->XRSession()->HeadPose().TranslationPart().Y();
2221       {
2222         const Standard_Integer aPickedId = handleXRMoveTo (theCtx, theView, aPose.Orientation, false);
2223         if (aPickedId >= 1)
2224         {
2225           const SelectMgr_SortCriterion& aPickedData = theCtx->MainSelector()->PickedData (aPickedId);
2226           aPickNorm = aPickedData.Normal;
2227           if (aPickNorm.SquareModulus() > ShortRealEpsilon())
2228           {
2229             aPickDepth = aPickedData.Point.Distance (aHandBase.TranslationPart());
2230           }
2231         }
2232       }
2233       if (isClicked)
2234       {
2235         myXRLastTeleportHand = Aspect_XRTrackedDeviceRole_Other;
2236         if (!Precision::IsInfinite (aPickDepth))
2237         {
2238           const gp_Dir aTeleDir = -gp::DZ().Transformed (aHandBase);
2239           const gp_Dir anUpDir  = theView->View()->BaseXRCamera()->Up();
2240
2241           bool isHorizontal = false;
2242           gp_Dir aPickNormDir (aPickNorm.x(), aPickNorm.y(), aPickNorm.z());
2243           if (anUpDir.IsEqual ( aPickNormDir, M_PI_4)
2244            || anUpDir.IsEqual (-aPickNormDir, M_PI_4))
2245           {
2246             isHorizontal = true;
2247           }
2248
2249           gp_Pnt aNewEye = aHandBase.TranslationPart();
2250           if (isHorizontal)
2251           {
2252             aNewEye  = aHandBase.TranslationPart()
2253                      + aTeleDir.XYZ() * aPickDepth
2254                      + anUpDir.XYZ() * aHeadHeight;
2255           }
2256           else
2257           {
2258             if (aPickNormDir.Dot (aTeleDir) < 0.0)
2259             {
2260               aPickNormDir.Reverse();
2261             }
2262             aNewEye  = aHandBase.TranslationPart()
2263                      + aTeleDir.XYZ() * aPickDepth
2264                      - aPickNormDir.XYZ() * aHeadHeight / 4;
2265           }
2266
2267           theView->View()->PosedXRCamera()->MoveEyeTo (aNewEye);
2268           theView->View()->ComputeXRBaseCameraFromPosed (theView->View()->PosedXRCamera(), theView->View()->XRSession()->HeadPose());
2269         }
2270       }
2271       break;
2272     }
2273   }
2274
2275   if (myXRLastTeleportHand != aTeleOld)
2276   {
2277     if (aTeleOld != Aspect_XRTrackedDeviceRole_Other)
2278     {
2279       if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (aTeleOld, Aspect_XRGenericAction_OutputHaptic))
2280       {
2281         theView->View()->XRSession()->AbortHapticVibrationAction (aHaptic);
2282       }
2283     }
2284     if (myXRLastTeleportHand != Aspect_XRTrackedDeviceRole_Other)
2285     {
2286       if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (myXRLastTeleportHand, Aspect_XRGenericAction_OutputHaptic))
2287       {
2288         theView->View()->XRSession()->TriggerHapticVibrationAction (aHaptic, myXRTeleportHaptic);
2289       }
2290     }
2291   }
2292 }
2293
2294 // =======================================================================
2295 // function : handleXRPicking
2296 // purpose  :
2297 // =======================================================================
2298 void AIS_ViewController::handleXRPicking (const Handle(AIS_InteractiveContext)& theCtx,
2299                                           const Handle(V3d_View)& theView)
2300 {
2301   if (!theView->View()->IsActiveXR())
2302   {
2303     return;
2304   }
2305
2306   // handle selection on trigger clicks
2307   Aspect_XRTrackedDeviceRole aPickDevOld = myXRLastPickingHand;
2308   myXRLastPickingHand = Aspect_XRTrackedDeviceRole_Other;
2309   for (int aHand = 0; aHand < 2; ++aHand)
2310   {
2311     const Aspect_XRTrackedDeviceRole aRole = aHand == 0 ? Aspect_XRTrackedDeviceRole_RightHand : Aspect_XRTrackedDeviceRole_LeftHand;
2312     const Handle(Aspect_XRAction)& aTrigClickAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTriggerClick);
2313     const Handle(Aspect_XRAction)& aTrigPullAct  = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTriggerPull);
2314     if (aTrigClickAct.IsNull()
2315     ||  aTrigPullAct.IsNull())
2316     {
2317       continue;
2318     }
2319
2320     const Aspect_XRDigitalActionData aTrigClick = theView->View()->XRSession()->GetDigitalActionData (aTrigClickAct);
2321     const Aspect_XRAnalogActionData  aTrigPos   = theView->View()->XRSession()->GetAnalogActionData (aTrigPullAct);
2322     if (aTrigPos.IsActive
2323      && Abs (aTrigPos.VecXYZ.x()) > 0.1f)
2324     {
2325       myXRLastPickingHand = aRole;
2326       handleXRHighlight (theCtx, theView);
2327       if (aTrigClick.IsActive
2328        && aTrigClick.IsPressed
2329        && aTrigClick.IsChanged)
2330       {
2331         theCtx->Select (false);
2332         OnSelectionChanged (theCtx, theView);
2333         if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (myXRLastPickingHand, Aspect_XRGenericAction_OutputHaptic))
2334         {
2335           theView->View()->XRSession()->TriggerHapticVibrationAction (aHaptic, myXRSelectHaptic);
2336         }
2337       }
2338       break;
2339     }
2340   }
2341   if (myXRLastPickingHand != aPickDevOld)
2342   {
2343     theCtx->ClearDetected();
2344   }
2345 }
2346
2347 // =======================================================================
2348 // function : OnSelectionChanged
2349 // purpose  :
2350 // =======================================================================
2351 void AIS_ViewController::OnSelectionChanged (const Handle(AIS_InteractiveContext)& ,
2352                                              const Handle(V3d_View)& )
2353 {
2354   //
2355 }
2356
2357 // =======================================================================
2358 // function : OnObjectDragged
2359 // purpose  :
2360 // =======================================================================
2361 void AIS_ViewController::OnObjectDragged (const Handle(AIS_InteractiveContext)& theCtx,
2362                                           const Handle(V3d_View)& theView,
2363                                           AIS_DragAction theAction)
2364 {
2365   switch (theAction)
2366   {
2367     case AIS_DragAction_Start:
2368     {
2369       myDragObject.Nullify();
2370       if (!theCtx->HasDetected())
2371       {
2372         return;
2373       }
2374
2375       Handle(AIS_InteractiveObject) aPrs = theCtx->DetectedInteractive();
2376       if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (aPrs))
2377       {
2378         if (aManip->HasActiveMode())
2379         {
2380           myDragObject = aManip;
2381           aManip->StartTransform (myGL.Dragging.PointStart.x(), myGL.Dragging.PointStart.y(), theView);
2382         }
2383       }
2384       return;
2385     }
2386     case AIS_DragAction_Update:
2387     {
2388       if (myDragObject.IsNull())
2389       {
2390         return;
2391       }
2392
2393       if (Handle(SelectMgr_EntityOwner) aGlobOwner = myDragObject->GlobalSelOwner())
2394       {
2395         theCtx->SetSelectedState (aGlobOwner, true);
2396       }
2397       if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (myDragObject))
2398       {
2399         aManip->Transform (myGL.Dragging.PointTo.x(), myGL.Dragging.PointTo.y(), theView);
2400       }
2401       theView->Invalidate();
2402       return;
2403     }
2404     case AIS_DragAction_Abort:
2405     {
2406       if (myDragObject.IsNull())
2407       {
2408         return;
2409       }
2410
2411       myGL.Dragging.PointTo = myGL.Dragging.PointStart;
2412       OnObjectDragged (theCtx, theView, AIS_DragAction_Update);
2413
2414       if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (myDragObject))
2415       {
2416         aManip->StopTransform (false);
2417       }
2418       Standard_FALLTHROUGH
2419     }
2420     case AIS_DragAction_Stop:
2421     {
2422       if (myDragObject.IsNull())
2423       {
2424         return;
2425       }
2426
2427       if (Handle(SelectMgr_EntityOwner) aGlobOwner = myDragObject->GlobalSelOwner())
2428       {
2429         theCtx->SetSelectedState (aGlobOwner, false);
2430       }
2431
2432       theView->Invalidate();
2433       myDragObject.Nullify();
2434       return;
2435     }
2436   }
2437 }
2438
2439 // =======================================================================
2440 // function : contextLazyMoveTo
2441 // purpose  :
2442 // =======================================================================
2443 void AIS_ViewController::contextLazyMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
2444                                             const Handle(V3d_View)& theView,
2445                                             const Graphic3d_Vec2i& thePnt)
2446 {
2447   if (myPrevMoveTo == thePnt)
2448   {
2449     return;
2450   }
2451
2452   myPrevMoveTo = thePnt;
2453
2454   Handle(SelectMgr_EntityOwner) aLastPicked = theCtx->DetectedOwner();
2455   theView->AutoZFit();
2456   theCtx->MoveTo (thePnt.x(), thePnt.y(), theView, false);
2457   Handle(SelectMgr_EntityOwner) aNewPicked = theCtx->DetectedOwner();
2458
2459   if (theView->Viewer()->Grid()->IsActive()
2460    && theView->Viewer()->GridEcho())
2461   {
2462     if (aNewPicked.IsNull())
2463     {
2464       Graphic3d_Vec3d aPnt3d;
2465       theView->ConvertToGrid (thePnt.x(), thePnt.y(), aPnt3d[0], aPnt3d[1], aPnt3d[2]);
2466       theView->Viewer()->ShowGridEcho (theView, Graphic3d_Vertex (aPnt3d[0], aPnt3d[1], aPnt3d[2]));
2467       theView->InvalidateImmediate();
2468     }
2469     else
2470     {
2471       theView->Viewer()->HideGridEcho (theView);
2472       theView->InvalidateImmediate();
2473     }
2474   }
2475
2476   if (aLastPicked != aNewPicked
2477    || (!aNewPicked.IsNull() && aNewPicked->IsForcedHilight()))
2478   {
2479     // dynamic highlight affects all Views
2480     for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
2481     {
2482       const Handle(V3d_View)& aView = aViewIter.Value();
2483       aView->InvalidateImmediate();
2484     }
2485   }
2486 }
2487
2488 // =======================================================================
2489 // function : handleSelectionPick
2490 // purpose  :
2491 // =======================================================================
2492 void AIS_ViewController::handleSelectionPick (const Handle(AIS_InteractiveContext)& theCtx,
2493                                               const Handle(V3d_View)& theView)
2494 {
2495   if (myGL.Selection.Tool == AIS_ViewSelectionTool_Picking
2496   && !myGL.Selection.Points.IsEmpty())
2497   {
2498     for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aPntIter (myGL.Selection.Points); aPntIter.More(); aPntIter.Next())
2499     {
2500       const bool hadPrevMoveTo = HasPreviousMoveTo();
2501       contextLazyMoveTo (theCtx, theView, aPntIter.Value());
2502       if (!hadPrevMoveTo)
2503       {
2504         ResetPreviousMoveTo();
2505       }
2506
2507       if (myGL.Selection.IsXOR)
2508       {
2509         theCtx->ShiftSelect (false);
2510       }
2511       else
2512       {
2513         theCtx->Select (false);
2514       }
2515
2516       // selection affects all Views
2517       theView->Viewer()->Invalidate();
2518
2519       OnSelectionChanged (theCtx, theView);
2520     }
2521
2522     myGL.Selection.Points.Clear();
2523   }
2524 }
2525
2526 // =======================================================================
2527 // function : handleSelectionPoly
2528 // purpose  :
2529 // =======================================================================
2530 void AIS_ViewController::handleSelectionPoly (const Handle(AIS_InteractiveContext)& theCtx,
2531                                               const Handle(V3d_View)& theView)
2532 {
2533   // rubber-band & window polygon selection
2534   if (myGL.Selection.Tool == AIS_ViewSelectionTool_RubberBand
2535    || myGL.Selection.Tool == AIS_ViewSelectionTool_Polygon
2536    || myGL.Selection.Tool == AIS_ViewSelectionTool_ZoomWindow)
2537   {
2538     if (!myGL.Selection.Points.IsEmpty())
2539     {
2540       myRubberBand->ClearPoints();
2541       myRubberBand->SetToUpdate();
2542
2543       const bool anIsRubber = myGL.Selection.Tool == AIS_ViewSelectionTool_RubberBand
2544                            || myGL.Selection.Tool == AIS_ViewSelectionTool_ZoomWindow;
2545       if (anIsRubber)
2546       {
2547         myRubberBand->SetRectangle (myGL.Selection.Points.First().x(), -myGL.Selection.Points.First().y(),
2548                                     myGL.Selection.Points.Last().x(),  -myGL.Selection.Points.Last().y());
2549       }
2550       else
2551       {
2552         Graphic3d_Vec2i aPrev (IntegerLast(), IntegerLast());
2553         for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aSelIter (myGL.Selection.Points); aSelIter.More(); aSelIter.Next())
2554         {
2555           Graphic3d_Vec2i aPntNew = Graphic3d_Vec2i (aSelIter.Value().x(), -aSelIter.Value().y());
2556           if (aPntNew != aPrev)
2557           {
2558             aPrev = aPntNew;
2559             myRubberBand->AddPoint (Graphic3d_Vec2i (aSelIter.Value().x(), -aSelIter.Value().y()));
2560           }
2561         }
2562       }
2563
2564       myRubberBand->SetPolygonClosed (anIsRubber);
2565       try
2566       {
2567         theCtx->Display (myRubberBand, 0, -1, false, AIS_DS_Displayed);
2568       }
2569       catch (const Standard_Failure& theEx)
2570       {
2571         Message::SendWarning (TCollection_AsciiString ("Internal error while displaying rubber-band: ")
2572                             + theEx.DynamicType()->Name() + ", " + theEx.GetMessageString());
2573         myRubberBand->ClearPoints();
2574       }
2575       if (!theView->Viewer()->ZLayerSettings (myRubberBand->ZLayer()).IsImmediate())
2576       {
2577         theView->Invalidate();
2578       }
2579       else
2580       {
2581         theView->InvalidateImmediate();
2582       }
2583     }
2584     else if (!myRubberBand.IsNull()
2585            && myRubberBand->HasInteractiveContext())
2586     {
2587       theCtx->Remove (myRubberBand, false);
2588       myRubberBand->ClearPoints();
2589     }
2590   }
2591
2592   if (myGL.Selection.ToApplyTool)
2593   {
2594     myGL.Selection.ToApplyTool = false;
2595     if (theCtx->IsDisplayed (myRubberBand))
2596     {
2597       theCtx->Remove (myRubberBand, false);
2598       {
2599         const NCollection_Sequence<Graphic3d_Vec2i>& aPoints = myRubberBand->Points();
2600         if (aPoints.Size() == 4
2601          && aPoints.Value (1).x() == aPoints.Value (2).x()
2602          && aPoints.Value (3).x() == aPoints.Value (4).x()
2603          && aPoints.Value (1).y() == aPoints.Value (4).y()
2604          && aPoints.Value (2).y() == aPoints.Value (3).y())
2605         {
2606           const Graphic3d_Vec2i aPnt1 (aPoints.Value (1).x(), -aPoints.Value (1).y());
2607           const Graphic3d_Vec2i aPnt2 (aPoints.Value (3).x(), -aPoints.Value (3).y());
2608           if (myGL.Selection.Tool == AIS_ViewSelectionTool_ZoomWindow)
2609           {
2610             theView->WindowFitAll (aPnt1.x(), aPnt1.y(), aPnt2.x(), aPnt2.y());
2611             theView->Invalidate();
2612           }
2613           else
2614           {
2615             theCtx->MainSelector()->AllowOverlapDetection (aPnt1.y() != Min (aPnt1.y(), aPnt2.y()));
2616             if (myGL.Selection.IsXOR)
2617             {
2618               theCtx->ShiftSelect (Min (aPnt1.x(), aPnt2.x()), Min (aPnt1.y(), aPnt2.y()),
2619                                    Max (aPnt1.x(), aPnt2.x()), Max (aPnt1.y(), aPnt2.y()),
2620                                    theView, false);
2621             }
2622             else
2623             {
2624               theCtx->Select (Min (aPnt1.x(), aPnt2.x()), Min (aPnt1.y(), aPnt2.y()),
2625                               Max (aPnt1.x(), aPnt2.x()), Max (aPnt1.y(), aPnt2.y()),
2626                               theView, false);
2627             }
2628             theCtx->MainSelector()->AllowOverlapDetection (false);
2629           }
2630         }
2631         else if (aPoints.Length() >= 3)
2632         {
2633           TColgp_Array1OfPnt2d aPolyline (1, aPoints.Length());
2634           TColgp_Array1OfPnt2d::Iterator aPolyIter (aPolyline);
2635           for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aSelIter (aPoints);
2636                aSelIter.More(); aSelIter.Next(), aPolyIter.Next())
2637           {
2638             const Graphic3d_Vec2i& aNewPnt = aSelIter.Value();
2639             aPolyIter.ChangeValue() = gp_Pnt2d (aNewPnt.x(), -aNewPnt.y());
2640           }
2641
2642           if (myGL.Selection.IsXOR)
2643           {
2644             theCtx->ShiftSelect (aPolyline, theView, false);
2645           }
2646           else
2647           {
2648             theCtx->Select (aPolyline, theView, false);
2649           }
2650           theCtx->MainSelector()->AllowOverlapDetection (false);
2651         }
2652       }
2653
2654       myRubberBand->ClearPoints();
2655       if (myGL.Selection.Tool != AIS_ViewSelectionTool_ZoomWindow)
2656       {
2657         // selection affects all Views
2658         theView->Viewer()->Invalidate();
2659         OnSelectionChanged (theCtx, theView);
2660       }
2661     }
2662   }
2663 }
2664
2665 // =======================================================================
2666 // function : handleDynamicHighlight
2667 // purpose  :
2668 // =======================================================================
2669 void AIS_ViewController::handleDynamicHighlight (const Handle(AIS_InteractiveContext)& theCtx,
2670                                                  const Handle(V3d_View)& theView)
2671 {
2672   if ((myGL.MoveTo.ToHilight || myGL.Dragging.ToStart)
2673    && myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
2674   {
2675     const Graphic3d_Vec2i& aMoveToPnt = myGL.MoveTo.ToHilight ? myGL.MoveTo.Point : myGL.Dragging.PointStart;
2676     if (myGL.Dragging.ToStart && (!myGL.MoveTo.ToHilight || !myToAllowHighlight)
2677      && !HasPreviousMoveTo())
2678     {
2679       contextLazyMoveTo (theCtx, theView, aMoveToPnt);
2680       ResetPreviousMoveTo();
2681       OnObjectDragged (theCtx, theView, AIS_DragAction_Start);
2682       theCtx->ClearDetected();
2683     }
2684     else if (myToAllowHighlight)
2685     {
2686       if (myPrevMoveTo != aMoveToPnt
2687        || (!theView->View()->IsActiveXR()
2688         && (myGL.OrbitRotation.ToRotate
2689          || myGL.ViewRotation.ToRotate
2690          || theView->IsInvalidated())))
2691       {
2692         ResetPreviousMoveTo();
2693         contextLazyMoveTo (theCtx, theView, aMoveToPnt);
2694       }
2695       if (myGL.Dragging.ToStart)
2696       {
2697         OnObjectDragged (theCtx, theView, AIS_DragAction_Start);
2698       }
2699     }
2700
2701     myGL.MoveTo.ToHilight = false;
2702   }
2703
2704   if (!myDragObject.IsNull())
2705   {
2706     if (myGL.Dragging.ToAbort)
2707     {
2708       OnObjectDragged (theCtx, theView, AIS_DragAction_Abort);
2709       myGL.OrbitRotation.ToRotate = false;
2710       myGL.ViewRotation .ToRotate = false;
2711     }
2712     else if (myGL.Dragging.ToStop)
2713     {
2714       OnObjectDragged (theCtx, theView, AIS_DragAction_Stop);
2715       myGL.OrbitRotation.ToRotate = false;
2716       myGL.ViewRotation .ToRotate = false;
2717     }
2718     else if (myGL.OrbitRotation.ToRotate
2719           || myGL.ViewRotation.ToRotate)
2720     {
2721       OnObjectDragged (theCtx, theView, AIS_DragAction_Update);
2722       myGL.OrbitRotation.ToRotate = false;
2723       myGL.ViewRotation .ToRotate = false;
2724     }
2725   }
2726 }
2727
2728 // =======================================================================
2729 // function : handleMoveTo
2730 // purpose  :
2731 // =======================================================================
2732 void AIS_ViewController::handleMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
2733                                        const Handle(V3d_View)& theView)
2734 {
2735   handleSelectionPick   (theCtx, theView);
2736   handleDynamicHighlight(theCtx, theView);
2737   handleSelectionPoly   (theCtx, theView);
2738 }
2739
2740 // =======================================================================
2741 // function : handleViewRedraw
2742 // purpose  :
2743 // =======================================================================
2744 void AIS_ViewController::handleViewRedraw (const Handle(AIS_InteractiveContext)& ,
2745                                            const Handle(V3d_View)& theView)
2746 {
2747   // manage animation state
2748   if (!myViewAnimation.IsNull()
2749    && !myViewAnimation->IsStopped())
2750   {
2751     myViewAnimation->UpdateTimer();
2752     ResetPreviousMoveTo();
2753     setAskNextFrame();
2754   }
2755
2756   if (theView->View()->IsActiveXR())
2757   {
2758     // VR requires continuous rendering
2759     myToAskNextFrame = true;
2760   }
2761
2762   for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
2763   {
2764     const Handle(V3d_View)& aView = aViewIter.Value();
2765     if (aView->IsInvalidated()
2766      || myToAskNextFrame)
2767     {
2768       if (aView->ComputedMode())
2769       {
2770         aView->Update();
2771       }
2772       else
2773       {
2774         aView->Redraw();
2775       }
2776     }
2777     else if (aView->IsInvalidatedImmediate())
2778     {
2779       aView->RedrawImmediate();
2780     }
2781   }
2782
2783   if (myToAskNextFrame)
2784   {
2785     // ask more frames
2786     theView->Window()->InvalidateContent (Handle(Aspect_DisplayConnection)());
2787   }
2788 }
2789
2790 // =======================================================================
2791 // function : handleXRMoveTo
2792 // purpose  :
2793 // =======================================================================
2794 Standard_Integer AIS_ViewController::handleXRMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
2795                                                      const Handle(V3d_View)& theView,
2796                                                      const gp_Trsf& thePose,
2797                                                      const Standard_Boolean theToHighlight)
2798 {
2799   //ResetPreviousMoveTo();
2800   Standard_Integer aPickResult = 0;
2801
2802   Handle(Graphic3d_Camera) aCamBack = theView->Camera();
2803   myXRCameraTmp->Copy (aCamBack);
2804   theView->View()->ComputeXRPosedCameraFromBase (*myXRCameraTmp, thePose);
2805   theView->SetCamera (myXRCameraTmp);
2806   Graphic3d_Vec2i aPickPixel;
2807   theView->Window()->Size (aPickPixel.x(), aPickPixel.y());
2808   aPickPixel /= 2;
2809   const Standard_Integer aSelTolerBack = theCtx->MainSelector()->CustomPixelTolerance();
2810   theCtx->MainSelector()->SetPixelTolerance (1);
2811   theView->AutoZFit();
2812   if (theToHighlight)
2813   {
2814     theCtx->MoveTo (aPickPixel.x(), aPickPixel.y(), theView, false);
2815     if (!theCtx->DetectedOwner().IsNull())
2816     {
2817       // ignore 2D objects
2818       for (aPickResult = 1; !theCtx->DetectedOwner()->Selectable()->TransformPersistence().IsNull(); ++aPickResult)
2819       {
2820         if (theCtx->HilightNextDetected (theView, false) <= 1)
2821         {
2822           theCtx->ClearDetected();
2823           aPickResult = 0;
2824           break;
2825         }
2826       }
2827     }
2828   }
2829   else
2830   {
2831     theCtx->MainSelector()->Pick (aPickPixel.x(), aPickPixel.y(), theView);
2832     for (Standard_Integer aPickIter = 1; aPickIter <= theCtx->MainSelector()->NbPicked(); ++aPickIter)
2833     {
2834       const SelectMgr_SortCriterion& aPickedData = theCtx->MainSelector()->PickedData (aPickIter);
2835       if (!aPickedData.Entity->OwnerId()->Selectable()->TransformPersistence().IsNull())
2836       {
2837         // skip 2d objects
2838         continue;
2839       }
2840
2841       aPickResult = aPickIter;
2842       break;
2843     }
2844   }
2845   theCtx->MainSelector()->SetPixelTolerance (aSelTolerBack);
2846   theView->SetCamera (aCamBack);
2847   return aPickResult;
2848 }
2849
2850 // =======================================================================
2851 // function : handleXRHighlight
2852 // purpose  :
2853 // =======================================================================
2854 void AIS_ViewController::handleXRHighlight (const Handle(AIS_InteractiveContext)& theCtx,
2855                                             const Handle(V3d_View)& theView)
2856 {
2857   if (myXRLastPickingHand != Aspect_XRTrackedDeviceRole_LeftHand
2858    && myXRLastPickingHand != Aspect_XRTrackedDeviceRole_RightHand)
2859   {
2860     return;
2861   }
2862
2863   const Standard_Integer aDeviceId = theView->View()->XRSession()->NamedTrackedDevice (myXRLastPickingHand);
2864   if (aDeviceId == -1)
2865   {
2866     return;
2867   }
2868
2869   const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceId];
2870   if (!aPose.IsValidPose)
2871   {
2872     return;
2873   }
2874
2875   Handle(SelectMgr_EntityOwner) aDetOld = theCtx->DetectedOwner();
2876   handleXRMoveTo (theCtx, theView, aPose.Orientation, true);
2877   if (!theCtx->DetectedOwner().IsNull()
2878     && theCtx->DetectedOwner() != aDetOld)
2879   {
2880     if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (myXRLastPickingHand, Aspect_XRGenericAction_OutputHaptic))
2881     {
2882       theView->View()->XRSession()->TriggerHapticVibrationAction (aHaptic, myXRPickingHaptic);
2883     }
2884   }
2885
2886   Standard_Real& aPickDepth = myXRLastPickingHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
2887   aPickDepth = Precision::Infinite();
2888   if (theCtx->MainSelector()->NbPicked() > 0)
2889   {
2890     const gp_Trsf aHandBase = theView->View()->PoseXRToWorld (aPose.Orientation);
2891     const SelectMgr_SortCriterion& aPicked = theCtx->MainSelector()->PickedData (1);
2892     aPickDepth = aPicked.Point.Distance (aHandBase.TranslationPart());
2893   }
2894 }
2895
2896 // =======================================================================
2897 // function : handleXRPresentations
2898 // purpose  :
2899 // =======================================================================
2900 void AIS_ViewController::handleXRPresentations (const Handle(AIS_InteractiveContext)& theCtx,
2901                                                 const Handle(V3d_View)& theView)
2902 {
2903   if (!theView->View()->IsActiveXR()
2904    || (!myToDisplayXRAuxDevices
2905     && !myToDisplayXRHands))
2906   {
2907     for (NCollection_Array1<Handle(AIS_XRTrackedDevice)>::Iterator aPrsIter (myXRPrsDevices); aPrsIter.More(); aPrsIter.Next())
2908     {
2909       if (!aPrsIter.Value().IsNull()
2910         && aPrsIter.Value()->HasInteractiveContext())
2911       {
2912         theCtx->Remove (aPrsIter.Value(), false);
2913       }
2914       aPrsIter.ChangeValue().Nullify();
2915     }
2916     return;
2917   }
2918
2919   if (myXRPrsDevices.Length() != theView->View()->XRSession()->TrackedPoses().Length())
2920   {
2921     for (NCollection_Array1<Handle(AIS_XRTrackedDevice)>::Iterator aPrsIter (myXRPrsDevices); aPrsIter.More(); aPrsIter.Next())
2922     {
2923       if (!aPrsIter.Value().IsNull())
2924       {
2925         theCtx->Remove (aPrsIter.Value(), false);
2926       }
2927     }
2928     myXRPrsDevices.Resize (theView->View()->XRSession()->TrackedPoses().Lower(), theView->View()->XRSession()->TrackedPoses().Upper(), false);
2929   }
2930
2931   const Standard_Integer aHeadDevice  = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_Head);
2932   const Standard_Integer aLeftDevice  = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_LeftHand);
2933   const Standard_Integer aRightDevice = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_RightHand);
2934   for (Standard_Integer aDeviceIter = theView->View()->XRSession()->TrackedPoses().Lower(); aDeviceIter <= theView->View()->XRSession()->TrackedPoses().Upper(); ++aDeviceIter)
2935   {
2936     const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceIter];
2937     Handle(AIS_XRTrackedDevice)& aPosePrs = myXRPrsDevices[aDeviceIter];
2938     if (!aPose.IsValidPose)
2939     {
2940       continue;
2941     }
2942
2943     const bool isHand = aDeviceIter == aLeftDevice
2944                      || aDeviceIter == aRightDevice;
2945     if ((!myToDisplayXRHands && isHand)
2946      || (!myToDisplayXRAuxDevices && !isHand))
2947     {
2948       if (!aPosePrs.IsNull()
2949         && aPosePrs->HasInteractiveContext())
2950       {
2951         theCtx->Remove (aPosePrs, false);
2952       }
2953       continue;
2954     }
2955
2956     Aspect_XRTrackedDeviceRole aRole = Aspect_XRTrackedDeviceRole_Other;
2957     if (aDeviceIter == aLeftDevice)
2958     {
2959       aRole = Aspect_XRTrackedDeviceRole_LeftHand;
2960     }
2961     else if (aDeviceIter == aRightDevice)
2962     {
2963       aRole = Aspect_XRTrackedDeviceRole_RightHand;
2964     }
2965
2966     if (!aPosePrs.IsNull()
2967       && aPosePrs->UnitFactor() != (float )theView->View()->UnitFactor())
2968     {
2969       theCtx->Remove (aPosePrs, false);
2970       aPosePrs.Nullify();
2971     }
2972
2973     if (aPosePrs.IsNull())
2974     {
2975       Handle(Image_Texture) aTexture;
2976       Handle(Graphic3d_ArrayOfTriangles) aTris;
2977       if (aDeviceIter != aHeadDevice)
2978       {
2979         aTris = theView->View()->XRSession()->LoadRenderModel (aDeviceIter, aTexture);
2980       }
2981       if (!aTris.IsNull())
2982       {
2983         aPosePrs = new AIS_XRTrackedDevice (aTris, aTexture);
2984       }
2985       else
2986       {
2987         aPosePrs = new AIS_XRTrackedDevice();
2988       }
2989       aPosePrs->SetUnitFactor ((float )theView->View()->UnitFactor());
2990       aPosePrs->SetMutable (true);
2991       aPosePrs->SetInfiniteState (true);
2992     }
2993     aPosePrs->SetRole (aRole);
2994
2995     if (!aPosePrs->HasInteractiveContext())
2996     {
2997       theCtx->Display (aPosePrs, 0, -1, false);
2998     }
2999
3000     gp_Trsf aPoseLocal = aPose.Orientation;
3001     if (aDeviceIter == aHeadDevice)
3002     {
3003       // show headset position on floor level
3004       aPoseLocal.SetTranslationPart (gp_Vec (aPoseLocal.TranslationPart().X(), 0.0, aPoseLocal.TranslationPart().Z()));
3005     }
3006     const gp_Trsf aPoseWorld = theView->View()->PoseXRToWorld (aPoseLocal);
3007     theCtx->SetLocation (aPosePrs, aPoseWorld);
3008
3009     Standard_Real aLaserLen = 0.0;
3010     if (isHand
3011       && aPosePrs->Role() == myXRLastPickingHand)
3012     {
3013       aLaserLen = myXRLastPickingHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
3014       if (Precision::IsInfinite (aLaserLen))
3015       {
3016         const Bnd_Box aViewBox = theView->View()->MinMaxValues (true);
3017         if (!aViewBox.IsVoid())
3018         {
3019           aLaserLen = Sqrt (aViewBox.SquareExtent());
3020         }
3021         else
3022         {
3023           aLaserLen = 100.0;
3024         }
3025       }
3026       aPosePrs->SetLaserColor (myXRLaserPickColor);
3027     }
3028     else if (isHand
3029           && aPosePrs->Role() == myXRLastTeleportHand)
3030     {
3031       aLaserLen = myXRLastTeleportHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
3032       if (Precision::IsInfinite (aLaserLen))
3033       {
3034         const Bnd_Box aViewBox = theView->View()->MinMaxValues (true);
3035         if (!aViewBox.IsVoid())
3036         {
3037           aLaserLen = Sqrt (aViewBox.SquareExtent());
3038         }
3039         else
3040         {
3041           aLaserLen = 100.0;
3042         }
3043       }
3044       aPosePrs->SetLaserColor (myXRLaserTeleColor);
3045     }
3046     aPosePrs->SetLaserLength ((float )aLaserLen);
3047   }
3048 }
3049
3050 // =======================================================================
3051 // function : HandleViewEvents
3052 // purpose  :
3053 // =======================================================================
3054 void AIS_ViewController::HandleViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
3055                                            const Handle(V3d_View)& theView)
3056 {
3057   const bool wasImmediateUpdate = theView->SetImmediateUpdate (false);
3058
3059   const AIS_WalkDelta aWalk = handleNavigationKeys (theCtx, theView);
3060   handleXRInput (theCtx, theView, aWalk);
3061   if (theView->View()->IsActiveXR())
3062   {
3063     theView->View()->SetupXRPosedCamera();
3064   }
3065   handleMoveTo (theCtx, theView);
3066   handleCameraActions (theCtx, theView, aWalk);
3067   theView->View()->SynchronizeXRPosedToBaseCamera(); // handleCameraActions() may modify posed camera position - copy this modifications also to the base camera
3068   handleXRPresentations (theCtx, theView);
3069
3070   handleViewRedraw (theCtx, theView);
3071   theView->View()->UnsetXRPosedCamera();
3072
3073   theView->SetImmediateUpdate (wasImmediateUpdate);
3074
3075   // make sure to not process the same events twice
3076   myGL.Reset();
3077   myToAskNextFrame = false;
3078 }