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