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