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