0028954: Visualization - implement interactive object for camera manipulations
[occt.git] / src / AIS / AIS_ViewCube.cxx
1 // Created on: 2017-07-25
2 // Created by: Anastasia BOBYLEVA
3 // Copyright (c) 2017-2019 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <AIS_ViewCube.hxx>
17
18 #include <AIS_AnimationCamera.hxx>
19 #include <AIS_InteractiveContext.hxx>
20 #include <gp_Ax2.hxx>
21 #include <Graphic3d_ViewAffinity.hxx>
22 #include <NCollection_Lerp.hxx>
23 #include <Prs3d.hxx>
24 #include <Prs3d_Arrow.hxx>
25 #include <Prs3d_DatumAspect.hxx>
26 #include <Prs3d_Root.hxx>
27 #include <Prs3d_Text.hxx>
28 #include <Prs3d_ToolDisk.hxx>
29 #include <Prs3d_ToolSphere.hxx>
30 #include <Select3D_SensitivePrimitiveArray.hxx>
31 #include <SelectMgr_SequenceOfOwner.hxx>
32 #include <V3d.hxx>
33 #include <V3d_View.hxx>
34
35 IMPLEMENT_STANDARD_RTTIEXT(AIS_ViewCube, AIS_InteractiveObject)
36 IMPLEMENT_STANDARD_RTTIEXT(AIS_ViewCubeOwner, SelectMgr_EntityOwner)
37
38 namespace
39 {
40   static const Standard_Integer THE_NB_ROUND_SPLITS = 8;
41   static const Standard_Integer THE_NB_DISK_SLICES = 20;
42   static const Standard_Integer THE_NB_ARROW_FACETTES = 20;
43
44   //! Return the number of non-zero components.
45   static Standard_Integer nbDirectionComponents (const gp_Dir& theDir)
46   {
47     Standard_Integer aNbComps = 0;
48     for (Standard_Integer aCompIter = 1; aCompIter <= 3; ++aCompIter)
49     {
50       if (Abs (theDir.Coord (aCompIter)) > gp::Resolution())
51       {
52         ++aNbComps;
53       }
54     }
55     return aNbComps;
56   }
57 }
58
59 //! Simple sensitive element for picking by point only.
60 class AIS_ViewCubeSensitive : public Select3D_SensitivePrimitiveArray
61 {
62   DEFINE_STANDARD_RTTI_INLINE(AIS_ViewCubeSensitive, Select3D_SensitivePrimitiveArray)
63 public:
64   //! Constructor.
65   AIS_ViewCubeSensitive (const Handle(SelectMgr_EntityOwner)& theOwner,
66                          const Handle(Graphic3d_ArrayOfTriangles)& theTris)
67   : Select3D_SensitivePrimitiveArray (theOwner)
68   {
69     InitTriangulation (theTris->Attributes(), theTris->Indices(), TopLoc_Location());
70   }
71
72   //! Checks whether element overlaps current selecting volume.
73   virtual Standard_Boolean Matches (SelectBasics_SelectingVolumeManager& theMgr,
74                                     SelectBasics_PickResult& thePickResult) Standard_OVERRIDE
75   {
76     return isValidRay (theMgr)
77         && Select3D_SensitivePrimitiveArray::Matches (theMgr, thePickResult);
78   }
79
80   //! Checks if picking ray can be used for detection.
81   bool isValidRay (const SelectBasics_SelectingVolumeManager& theMgr) const
82   {
83     if (theMgr.GetActiveSelectionType() != SelectBasics_SelectingVolumeManager::Point)
84     {
85       // disallow rectangular selection
86       return false;
87     }
88
89     if (AIS_ViewCubeOwner* anOwner = dynamic_cast<AIS_ViewCubeOwner* >(myOwnerId.get()))
90     {
91       const Standard_Real anAngleToler = 10.0 * M_PI / 180.0;
92       const gp_Vec aRay (theMgr.GetNearPickedPnt(), theMgr.GetFarPickedPnt());
93       const gp_Dir aDir = V3d::GetProjAxis (anOwner->MainOrientation());
94       return !aRay.IsNormal (aDir, anAngleToler);
95     }
96     return true;
97   }
98 };
99
100 //=======================================================================
101 //function : IsBoxSide
102 //purpose  :
103 //=======================================================================
104 bool AIS_ViewCube::IsBoxSide (V3d_TypeOfOrientation theOrient)
105 {
106   return nbDirectionComponents (V3d::GetProjAxis (theOrient)) == 1;
107 }
108
109 //=======================================================================
110 //function : IsBoxEdge
111 //purpose  :
112 //=======================================================================
113 bool AIS_ViewCube::IsBoxEdge (V3d_TypeOfOrientation theOrient)
114 {
115   return nbDirectionComponents (V3d::GetProjAxis (theOrient)) == 2;
116 }
117
118 //=======================================================================
119 //function : IsBoxCorner
120 //purpose  :
121 //=======================================================================
122 bool AIS_ViewCube::IsBoxCorner (V3d_TypeOfOrientation theOrient)
123 {
124   return nbDirectionComponents (V3d::GetProjAxis (theOrient)) == 3;
125 }
126
127 //=======================================================================
128 //function : AIS_ViewCube
129 //purpose  :
130 //=======================================================================
131 AIS_ViewCube::AIS_ViewCube()
132 : myBoxEdgeAspect (new Prs3d_ShadingAspect()),
133   myBoxCornerAspect (new Prs3d_ShadingAspect()),
134   mySize (1.0),
135   myBoxEdgeMinSize (2.0),
136   myBoxEdgeGap (0.0),
137   myBoxFacetExtension (1.0),
138   myAxesPadding (1.0),
139   myCornerMinSize (2.0),
140   myRoundRadius  (0.0),
141   myToDisplayAxes (true),
142   myToDisplayEdges (true),
143   myToDisplayVertices (true),
144   myIsYup (false),
145   myViewAnimation (new AIS_AnimationCamera ("AIS_ViewCube", Handle(V3d_View)())),
146   myStartState(new Graphic3d_Camera()),
147   myEndState  (new Graphic3d_Camera()),
148   myDuration (0.5),
149   myToAutoStartAnim (true),
150   myIsFixedAnimation (true),
151   myToFitSelected (true),
152   myToResetCameraUp (false)
153 {
154   myInfiniteState = true;
155   myIsMutable = true;
156   myDrawer->SetZLayer (Graphic3d_ZLayerId_Topmost);
157   myTransformPersistence = new Graphic3d_TransformPers (Graphic3d_TMF_TriedronPers, Aspect_TOTP_LEFT_LOWER, Graphic3d_Vec2i (100, 100));
158
159   myDrawer->SetTextAspect  (new Prs3d_TextAspect());
160   myDrawer->SetShadingAspect (new Prs3d_ShadingAspect());
161
162   myDynHilightDrawer = new Prs3d_Drawer();
163   myDynHilightDrawer->SetLink (myDrawer);
164   myDynHilightDrawer->SetShadingAspect (new Prs3d_ShadingAspect());
165
166   setDefaultAttributes();
167   setDefaultHighlightAttributes();
168
169   // setup default labels
170   myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Front,  "FRONT");
171   myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Back,   "BACK");
172   myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Top,    "TOP");
173   myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Bottom, "BOTTOM");
174   myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Left,   "LEFT");
175   myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Right,  "RIGHT");
176
177   myAxesLabels.Bind (Prs3d_DP_XAxis, "X");
178   myAxesLabels.Bind (Prs3d_DP_YAxis, "Y");
179   myAxesLabels.Bind (Prs3d_DP_ZAxis, "Z");
180
181   // define default size
182   SetSize (70.0);
183 }
184
185 //=======================================================================
186 //function : setDefaultAttributes
187 //purpose  :
188 //=======================================================================
189 void AIS_ViewCube::setDefaultAttributes()
190 {
191   myDrawer->TextAspect()->SetHorizontalJustification(Graphic3d_HTA_CENTER);
192   myDrawer->TextAspect()->SetVerticalJustification  (Graphic3d_VTA_CENTER);
193   myDrawer->TextAspect()->SetColor (Quantity_NOC_BLACK);
194   myDrawer->TextAspect()->SetFont (Font_NOF_SANS_SERIF);
195   myDrawer->TextAspect()->SetHeight (16.0);
196   // this should be forced back-face culling regardless Closed flag
197   myDrawer->TextAspect()->Aspect()->SetSuppressBackFaces (true);
198
199   Graphic3d_MaterialAspect aMat (Graphic3d_NOM_UserDefined);
200   aMat.SetColor (Quantity_NOC_WHITE);
201   aMat.SetAmbientColor (Quantity_NOC_GRAY60);
202
203   const Handle(Graphic3d_AspectFillArea3d)& aShading = myDrawer->ShadingAspect()->Aspect();
204   aShading->SetInteriorStyle (Aspect_IS_SOLID);
205   // this should be forced back-face culling regardless Closed flag
206   aShading->SetSuppressBackFaces (true);
207   aShading->SetInteriorColor (aMat.Color());
208   aShading->SetFrontMaterial (aMat);
209   myDrawer->SetFaceBoundaryDraw (false);
210
211   *myBoxEdgeAspect  ->Aspect() = *aShading;
212   myBoxEdgeAspect->SetColor (Quantity_NOC_GRAY30);
213   *myBoxCornerAspect->Aspect() = *aShading;
214   myBoxCornerAspect->SetColor (Quantity_NOC_GRAY30);
215 }
216
217 //=======================================================================
218 //function : setDefaultHighlightAttributes
219 //purpose  :
220 //=======================================================================
221 void AIS_ViewCube::setDefaultHighlightAttributes()
222 {
223   Graphic3d_MaterialAspect aHighlightMaterial;
224   aHighlightMaterial.SetReflectionModeOff (Graphic3d_TOR_AMBIENT);
225   aHighlightMaterial.SetReflectionModeOff (Graphic3d_TOR_DIFFUSE);
226   aHighlightMaterial.SetReflectionModeOff (Graphic3d_TOR_SPECULAR);
227   aHighlightMaterial.SetReflectionModeOff (Graphic3d_TOR_EMISSION);
228   aHighlightMaterial.SetMaterialType (Graphic3d_MATERIAL_ASPECT);
229   myDynHilightDrawer->SetShadingAspect (new Prs3d_ShadingAspect());
230   myDynHilightDrawer->ShadingAspect()->SetMaterial (aHighlightMaterial);
231   myDynHilightDrawer->ShadingAspect()->SetColor (Quantity_NOC_CYAN1);
232   myDynHilightDrawer->SetZLayer (Graphic3d_ZLayerId_Topmost);
233   myDynHilightDrawer->SetColor (Quantity_NOC_CYAN1);
234 }
235
236 //=======================================================================
237 //function : SetYup
238 //purpose  :
239 //=======================================================================
240 void AIS_ViewCube::SetYup (Standard_Boolean theIsYup,
241                            Standard_Boolean theToUpdateLabels)
242 {
243   if (myIsYup == theIsYup)
244   {
245     return;
246   }
247
248   myIsYup = theIsYup;
249
250   static const V3d_TypeOfOrientation THE_ZUP_ORI_LIST[6] =
251   {
252     V3d_TypeOfOrientation_Zup_Front, V3d_TypeOfOrientation_Zup_Back,
253     V3d_TypeOfOrientation_Zup_Top,   V3d_TypeOfOrientation_Zup_Bottom,
254     V3d_TypeOfOrientation_Zup_Left,  V3d_TypeOfOrientation_Zup_Right
255   };
256   static const V3d_TypeOfOrientation THE_YUP_ORI_LIST[6] =
257   {
258     V3d_TypeOfOrientation_Yup_Front, V3d_TypeOfOrientation_Yup_Back,
259     V3d_TypeOfOrientation_Yup_Top,   V3d_TypeOfOrientation_Yup_Bottom,
260     V3d_TypeOfOrientation_Yup_Left,  V3d_TypeOfOrientation_Yup_Right
261   };
262   if (theToUpdateLabels)
263   {
264     NCollection_Array1<TCollection_AsciiString> aLabels (0, 5);
265     for (Standard_Integer aLabelIter = 0; aLabelIter < 6; ++aLabelIter)
266     {
267       myBoxSideLabels.Find (!myIsYup ? THE_YUP_ORI_LIST[aLabelIter] : THE_ZUP_ORI_LIST[aLabelIter],
268                             aLabels.ChangeValue (aLabelIter));
269     }
270     for (Standard_Integer aLabelIter = 0; aLabelIter < 6; ++aLabelIter)
271     {
272       myBoxSideLabels.Bind (myIsYup ? THE_YUP_ORI_LIST[aLabelIter] : THE_ZUP_ORI_LIST[aLabelIter],
273                             aLabels.Value (aLabelIter));
274     }
275   }
276
277   SetToUpdate();
278 }
279
280 //=======================================================================
281 //function : ResetStyles
282 //purpose  :
283 //=======================================================================
284 void AIS_ViewCube::ResetStyles()
285 {
286   UnsetAttributes();
287   UnsetHilightAttributes();
288
289   myBoxEdgeMinSize = 2.0;
290   myCornerMinSize  = 2.0;
291   myBoxEdgeGap     = 0.0;
292   myRoundRadius    = 0.0;
293
294   myToDisplayAxes = true;
295   myToDisplayEdges = true;
296   myToDisplayVertices = true;
297
298   myBoxFacetExtension = 1.0;
299   myAxesPadding = 1.0;
300   SetSize (70.0);
301 }
302
303 //=======================================================================
304 //function : SetSize
305 //purpose  :
306 //=======================================================================
307 void AIS_ViewCube::SetSize (Standard_Real theValue,
308                             Standard_Boolean theToAdaptAnother)
309 {
310   const bool isNewSize = Abs (mySize - theValue) > Precision::Confusion();
311   mySize = theValue;
312   if (theToAdaptAnother)
313   {
314     if (myBoxFacetExtension > 0.0)
315     {
316       SetBoxFacetExtension (mySize * 0.15);
317     }
318     if (myAxesPadding > 0.0)
319     {
320       SetAxesPadding (mySize * 0.1);
321     }
322     SetFontHeight (mySize * 0.16);
323   }
324   if (isNewSize)
325   {
326     SetToUpdate();
327   }
328 }
329
330 //=======================================================================
331 //function : SetRoundRadius
332 //purpose  :
333 //=======================================================================
334 void AIS_ViewCube::SetRoundRadius (const Standard_Real theValue)
335 {
336   Standard_OutOfRange_Raise_if (theValue < 0.0 || theValue > 0.5,
337                                 "AIS_ViewCube::SetRoundRadius(): theValue should be in [0; 0.5]");
338   if (Abs (myRoundRadius - theValue) > Precision::Confusion())
339   {
340     myRoundRadius = theValue;
341     SetToUpdate();
342   }
343 }
344
345 //=======================================================================
346 //function : createRoundRectangleTriangles
347 //purpose  :
348 //=======================================================================
349 Handle(Graphic3d_ArrayOfTriangles) AIS_ViewCube::createRoundRectangleTriangles (const gp_XY& theSize,
350                                                                                 Standard_Real theRadius,
351                                                                                 const gp_Trsf& theTrsf)
352 {
353   const Standard_Real aRadius = Min (theRadius, Min (theSize.X(), theSize.Y()) * 0.5);
354   const gp_XY  aHSize (theSize.X() * 0.5 - aRadius, theSize.Y() * 0.5 - aRadius);
355   const gp_Dir aNorm = gp::DZ().Transformed (theTrsf);
356   Handle(Graphic3d_ArrayOfTriangles) aTris;
357   if (aRadius > 0.0)
358   {
359     const Standard_Integer aNbNodes = (THE_NB_ROUND_SPLITS + 1) * 4 + 1;
360     aTris = new Graphic3d_ArrayOfTriangles (aNbNodes, aNbNodes * 3, Graphic3d_ArrayFlags_VertexNormal);
361
362     aTris->AddVertex (gp_Pnt (0.0, 0.0, 0.0).Transformed (theTrsf));
363     for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
364     {
365       const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (M_PI * 0.5, 0.0, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
366       aTris->AddVertex (gp_Pnt (aHSize.X() + aRadius * Cos (anAngle), aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
367     }
368     for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
369     {
370       const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (0.0, -M_PI * 0.5, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
371       aTris->AddVertex (gp_Pnt (aHSize.X() + aRadius * Cos (anAngle), -aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
372     }
373     for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
374     {
375       const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (-M_PI * 0.5, -M_PI, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
376       aTris->AddVertex (gp_Pnt (-aHSize.X() + aRadius * Cos (anAngle), -aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
377     }
378     for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
379     {
380       const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (-M_PI, -M_PI * 1.5, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
381       aTris->AddVertex (gp_Pnt (-aHSize.X() + aRadius * Cos (anAngle), aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
382     }
383
384     // split triangle fan
385     for (Standard_Integer aNodeIter = 2; aNodeIter <= aTris->VertexNumber(); ++aNodeIter)
386     {
387       aTris->AddEdge (1);
388       aTris->AddEdge (aNodeIter - 1);
389       aTris->AddEdge (aNodeIter);
390     }
391     aTris->AddEdge (1);
392     aTris->AddEdge (aTris->VertexNumber());
393     aTris->AddEdge (2);
394   }
395   else
396   {
397     aTris = new Graphic3d_ArrayOfTriangles (4, 6, Graphic3d_ArrayFlags_VertexNormal);
398     aTris->AddVertex (gp_Pnt (-aHSize.X(), -aHSize.Y(), 0.0).Transformed (theTrsf));
399     aTris->AddVertex (gp_Pnt (-aHSize.X(),  aHSize.Y(), 0.0).Transformed (theTrsf));
400     aTris->AddVertex (gp_Pnt ( aHSize.X(),  aHSize.Y(), 0.0).Transformed (theTrsf));
401     aTris->AddVertex (gp_Pnt ( aHSize.X(), -aHSize.Y(), 0.0).Transformed (theTrsf));
402     aTris->AddEdges (3, 1, 2);
403     aTris->AddEdges (1, 3, 4);
404   }
405
406   for (Standard_Integer aVertIter = 1; aVertIter <= aTris->VertexNumber(); ++aVertIter)
407   {
408     aTris->SetVertexNormal (aVertIter, -aNorm);
409   }
410   return aTris;
411 }
412
413 //=======================================================================
414 //function : createBoxPartTriangles
415 //purpose  :
416 //=======================================================================
417 Handle(Graphic3d_ArrayOfTriangles) AIS_ViewCube::createBoxPartTriangles (V3d_TypeOfOrientation theDir) const
418 {
419   if (IsBoxSide (theDir))
420   {
421     return createBoxSideTriangles (theDir);
422   }
423   else if (IsBoxEdge (theDir)
424         && myToDisplayEdges)
425   {
426     return createBoxEdgeTriangles (theDir);
427   }
428   else if (IsBoxCorner (theDir)
429         && myToDisplayVertices)
430   {
431     return createBoxCornerTriangles (theDir);
432   }
433   return Handle(Graphic3d_ArrayOfTriangles)();
434 }
435
436 //=======================================================================
437 //function : createBoxSideTriangles
438 //purpose  :
439 //=======================================================================
440 Handle(Graphic3d_ArrayOfTriangles) AIS_ViewCube::createBoxSideTriangles (V3d_TypeOfOrientation theDirection) const
441 {
442   const gp_Dir aDir = V3d::GetProjAxis (theDirection);
443   const gp_Pnt aPos = aDir.XYZ() * (mySize * 0.5 + myBoxFacetExtension);
444   const gp_Ax2 aPosition (aPos, aDir.Reversed());
445
446   gp_Ax3 aSystem (aPosition);
447   gp_Trsf aTrsf;
448   aTrsf.SetTransformation (aSystem, gp_Ax3());
449
450   return createRoundRectangleTriangles (gp_XY (mySize, mySize), myRoundRadius * mySize, aTrsf);
451 }
452
453 //=======================================================================
454 //function : createBoxEdgeTriangles
455 //purpose  :
456 //=======================================================================
457 Handle(Graphic3d_ArrayOfTriangles) AIS_ViewCube::createBoxEdgeTriangles (V3d_TypeOfOrientation theDirection) const
458 {
459   const Standard_Real aThickness = Max (myBoxFacetExtension * gp_XY (1.0, 1.0).Modulus() - myBoxEdgeGap, myBoxEdgeMinSize);
460
461   const gp_Dir aDir = V3d::GetProjAxis (theDirection);
462   const gp_Pnt aPos = aDir.XYZ() * (mySize * 0.5 * gp_XY (1.0, 1.0).Modulus() + myBoxFacetExtension * Cos (M_PI_4));
463   const gp_Ax2 aPosition (aPos, aDir.Reversed());
464
465   gp_Ax3 aSystem (aPosition);
466   gp_Trsf aTrsf;
467   aTrsf.SetTransformation (aSystem, gp_Ax3());
468
469   return createRoundRectangleTriangles (gp_XY (aThickness, mySize), myRoundRadius * mySize, aTrsf);
470 }
471
472 //=======================================================================
473 //function : createBoxCornerTriangles
474 //purpose  :
475 //=======================================================================
476 Handle(Graphic3d_ArrayOfTriangles) AIS_ViewCube::createBoxCornerTriangles (V3d_TypeOfOrientation theDir) const
477 {
478   const Standard_Real aHSize = mySize * 0.5;
479   const gp_Dir aDir = V3d::GetProjAxis (theDir);
480   const gp_XYZ aHSizeDir = aDir.XYZ() * (aHSize * gp_Vec (1.0, 1.0, 1.0).Magnitude());
481   if (myRoundRadius > 0.0)
482   {
483     const Standard_Real anEdgeHWidth = myBoxFacetExtension * gp_XY (1.0, 1.0).Modulus() * 0.5;
484     const Standard_Real aHeight = anEdgeHWidth * Sqrt (2.0 / 3.0); // tetrahedron height
485     const gp_Pnt aPos = aDir.XYZ() * (aHSize * gp_Vec (1.0, 1.0, 1.0).Magnitude() + aHeight);
486     const gp_Ax2 aPosition (aPos, aDir.Reversed());
487     gp_Ax3 aSystem (aPosition);
488     gp_Trsf aTrsf;
489     aTrsf.SetTransformation (aSystem, gp_Ax3());
490     const Standard_Real aRadius = Max (myBoxFacetExtension * 0.5 / Cos (M_PI_4), myCornerMinSize);
491     return Prs3d_ToolDisk::Create (0.0, aRadius, THE_NB_DISK_SLICES, 1, aTrsf);
492   }
493
494   Handle(Graphic3d_ArrayOfTriangles) aTris = new Graphic3d_ArrayOfTriangles (3, 3, Graphic3d_ArrayFlags_VertexNormal);
495
496   aTris->AddVertex (aHSizeDir + myBoxFacetExtension * gp_Dir (aDir.X(), 0.0, 0.0).XYZ());
497   aTris->AddVertex (aHSizeDir + myBoxFacetExtension * gp_Dir (0.0, aDir.Y(), 0.0).XYZ());
498   aTris->AddVertex (aHSizeDir + myBoxFacetExtension * gp_Dir (0.0, 0.0, aDir.Z()).XYZ());
499
500   const gp_XYZ aNode1 = aTris->Vertice (1).XYZ();
501   const gp_XYZ aNode2 = aTris->Vertice (2).XYZ();
502   const gp_XYZ aNode3 = aTris->Vertice (3).XYZ();
503   const gp_XYZ aNormTri = ((aNode2 - aNode1).Crossed (aNode3 - aNode1));
504   if (aNormTri.Dot (aDir.XYZ()) < 0.0)
505   {
506     aTris->AddEdges (1, 3, 2);
507   }
508   else
509   {
510     aTris->AddEdges (1, 2, 3);
511   }
512
513   for (Standard_Integer aVertIter = 1; aVertIter <= aTris->VertexNumber(); ++aVertIter)
514   {
515     aTris->SetVertexNormal (aVertIter, aDir);
516   }
517   return aTris;
518 }
519
520 //=======================================================================
521 //function : Compute
522 //purpose  :
523 //=======================================================================
524 void AIS_ViewCube::Compute (const Handle(PrsMgr_PresentationManager3d)& ,
525                             const Handle(Prs3d_Presentation)& thePrs,
526                             const Standard_Integer theMode)
527 {
528   thePrs->SetInfiniteState (true);
529   if (theMode != 0)
530   {
531     return;
532   }
533
534   const gp_Pnt aLocation = (mySize * 0.5 + myBoxFacetExtension + myAxesPadding) * gp_XYZ (-1.0, -1.0, -1.0);
535
536   // Display axes
537   if (myToDisplayAxes)
538   {
539     const Standard_Real anAxisSize = mySize + 2.0 * myBoxFacetExtension + myAxesPadding;
540     const Handle(Prs3d_DatumAspect)& aDatumAspect = myDrawer->DatumAspect();
541     for (Standard_Integer anAxisIter = Prs3d_DP_XAxis; anAxisIter <= Prs3d_DP_ZAxis; ++anAxisIter)
542     {
543       const Prs3d_DatumParts aPart = (Prs3d_DatumParts )anAxisIter;
544       if (!aDatumAspect->DrawDatumPart (aPart))
545       {
546         continue;
547       }
548
549       gp_Ax1 anAx1;
550       switch (aPart)
551       {
552         case Prs3d_DP_XAxis: anAx1 = gp_Ax1 (aLocation, gp::DX()); break;
553         case Prs3d_DP_YAxis: anAx1 = gp_Ax1 (aLocation, gp::DY()); break;
554         case Prs3d_DP_ZAxis: anAx1 = gp_Ax1 (aLocation, gp::DZ()); break;
555         default: break;
556       }
557
558       Handle(Graphic3d_Group) anAxisGroup = thePrs->NewGroup();
559       anAxisGroup->SetGroupPrimitivesAspect (aDatumAspect->ShadingAspect (aPart)->Aspect());
560
561       const Standard_Real anArrowLength = 0.2 * anAxisSize;
562       Handle(Graphic3d_ArrayOfTriangles) aTriangleArray = Prs3d_Arrow::DrawShaded (anAx1, 1.0, anAxisSize, 3.0, anArrowLength, THE_NB_ARROW_FACETTES);
563       anAxisGroup->AddPrimitiveArray (aTriangleArray);
564
565       TCollection_AsciiString anAxisLabel;
566       if (aDatumAspect->ToDrawLabels()
567       &&  myAxesLabels.Find (aPart, anAxisLabel)
568       && !anAxisLabel.IsEmpty())
569       {
570         Handle(Graphic3d_Group) anAxisLabelGroup = thePrs->NewGroup();
571         gp_Pnt aTextOrigin = anAx1.Location().Translated (gp_Vec (anAx1.Direction().X() * (anAxisSize + anArrowLength),
572                                                                   anAx1.Direction().Y() * (anAxisSize + anArrowLength),
573                                                                   anAx1.Direction().Z() * (anAxisSize + anArrowLength)));
574         Prs3d_Text::Draw (anAxisLabelGroup, aDatumAspect->TextAspect(), TCollection_ExtendedString (anAxisLabel), aTextOrigin);
575       }
576     }
577
578     // Display center
579     {
580       Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
581       Handle(Prs3d_ShadingAspect) anAspectCen = new Prs3d_ShadingAspect();
582       anAspectCen->SetColor (Quantity_NOC_WHITE);
583       aGroup->SetGroupPrimitivesAspect (anAspectCen->Aspect());
584       Prs3d_ToolSphere aTool (4.0, THE_NB_DISK_SLICES, THE_NB_DISK_SLICES);
585       gp_Trsf aTrsf;
586       aTrsf.SetTranslation (gp_Vec (gp::Origin(), aLocation));
587       Handle(Graphic3d_ArrayOfTriangles) aCenterArray;
588       aTool.FillArray (aCenterArray, aTrsf);
589       aGroup->AddPrimitiveArray (aCenterArray);
590     }
591   }
592
593   // Display box
594   {
595     Handle(Graphic3d_Group) aGroupSides = thePrs->NewGroup(), aGroupEdges = thePrs->NewGroup(), aGroupCorners = thePrs->NewGroup();
596     aGroupSides->SetClosed (true); // should be replaced by forced back-face culling aspect
597     aGroupSides->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
598
599     aGroupEdges->SetClosed (true);
600     aGroupEdges->SetGroupPrimitivesAspect (myBoxEdgeAspect->Aspect());
601
602     aGroupCorners->SetClosed (true);
603     aGroupCorners->SetGroupPrimitivesAspect (myBoxCornerAspect->Aspect());
604
605     Handle(Graphic3d_Group) aTextGroup = thePrs->NewGroup();
606     //aTextGroup->SetClosed (true);
607     aTextGroup->SetGroupPrimitivesAspect (myDrawer->TextAspect()->Aspect());
608     for (Standard_Integer aPartIter = 0; aPartIter <= Standard_Integer(V3d_XnegYnegZneg); ++aPartIter)
609     {
610       const V3d_TypeOfOrientation anOrient = (V3d_TypeOfOrientation )aPartIter;
611       if (Handle(Graphic3d_ArrayOfTriangles) aTris = createBoxPartTriangles (anOrient))
612       {
613         if (IsBoxSide (anOrient))
614         {
615           aGroupSides->AddPrimitiveArray (aTris);
616
617           TCollection_AsciiString aLabel;
618           if (!myBoxSideLabels.Find (anOrient, aLabel)
619             || aLabel.IsEmpty())
620           {
621             continue;
622           }
623
624           const gp_Dir aDir = V3d::GetProjAxis (anOrient);
625           gp_Dir anUp = myIsYup ? gp::DY() : gp::DZ();
626           if (myIsYup)
627           {
628             if (anOrient == V3d_Ypos
629              || anOrient == V3d_Yneg)
630             {
631               anUp = -gp::DZ();
632             }
633           }
634           else
635           {
636             if (anOrient == V3d_Zpos)
637             {
638               anUp = gp::DY();
639             }
640             else if (anOrient == V3d_Zneg)
641             {
642               anUp = -gp::DY();
643             }
644           }
645
646           const Standard_Real anOffset = 2.0; // extra offset to avoid overlapping with triangulation
647           const gp_Pnt aPos = aDir.XYZ() * (mySize * 0.5 + myBoxFacetExtension + anOffset);
648           const gp_Ax2 aPosition (aPos, aDir, anUp.Crossed (aDir));
649           Prs3d_Text::Draw (aTextGroup, myDrawer->TextAspect(), aLabel, aPosition);
650         }
651         else if (IsBoxEdge (anOrient))
652         {
653           aGroupEdges->AddPrimitiveArray (aTris);
654         }
655         else if (IsBoxCorner (anOrient))
656         {
657           aGroupCorners->AddPrimitiveArray (aTris);
658         }
659       }
660     }
661   }
662 }
663
664 //=======================================================================
665 //function : ComputeSelection
666 //purpose  :
667 //=======================================================================
668 void AIS_ViewCube::ComputeSelection (const Handle(SelectMgr_Selection)& theSelection,
669                                      const Standard_Integer theMode)
670 {
671   if (theMode != 0)
672   {
673     return;
674   }
675
676   for (Standard_Integer aPartIter = 0; aPartIter <= Standard_Integer(V3d_XnegYnegZneg); ++aPartIter)
677   {
678     const V3d_TypeOfOrientation anOri = (V3d_TypeOfOrientation )aPartIter;
679     if (Handle(Graphic3d_ArrayOfTriangles) aTris = createBoxPartTriangles (anOri))
680     {
681       Standard_Integer aSensitivity = 2;
682       if (IsBoxCorner (anOri))
683       {
684         aSensitivity = 8;
685       }
686       else if (IsBoxEdge (anOri))
687       {
688         aSensitivity = 4;
689       }
690       Handle(AIS_ViewCubeOwner) anOwner = new AIS_ViewCubeOwner (this, anOri);
691       Handle(AIS_ViewCubeSensitive) aTriSens = new AIS_ViewCubeSensitive (anOwner, aTris);
692       aTriSens->SetSensitivityFactor (aSensitivity);
693       theSelection->Add (aTriSens);
694     }
695   }
696 }
697
698 //=======================================================================
699 //function : HasAnimation
700 //purpose  :
701 //=======================================================================
702 Standard_Boolean AIS_ViewCube::HasAnimation() const
703 {
704   return !myViewAnimation->IsStopped();
705 }
706
707 //=======================================================================
708 //function : StartAnimation
709 //purpose  :
710 //=======================================================================
711 void AIS_ViewCube::StartAnimation (const Handle(AIS_ViewCubeOwner)& theOwner)
712 {
713   Handle(V3d_View) aView = GetContext()->LastActiveView();
714   if (theOwner.IsNull()
715    || aView.IsNull())
716   {
717     return;
718   }
719
720   myStartState->Copy (aView->Camera());
721   myEndState  ->Copy (aView->Camera());
722
723   {
724     Handle(Graphic3d_Camera) aBackupCamera = new Graphic3d_Camera (aView->Camera());
725
726     const bool wasImmediateUpdate = aView->SetImmediateUpdate (false);
727     aView->SetCamera (myEndState);
728     aView->SetProj (theOwner->MainOrientation(), myIsYup);
729
730     const gp_Dir aNewDir = aView->Camera()->Direction();
731     if (!myToResetCameraUp
732      && !aNewDir.IsEqual (aBackupCamera->Direction(), Precision::Angular()))
733     {
734       // find the Up direction closest to current instead of default one
735       const gp_Ax1 aNewDirAx1 (gp::Origin(), aNewDir);
736       const gp_Dir anOldUp = aBackupCamera->Up();
737       const gp_Dir anUpList[4] =
738       {
739         aView->Camera()->Up(),
740         aView->Camera()->Up().Rotated (aNewDirAx1, M_PI_2),
741         aView->Camera()->Up().Rotated (aNewDirAx1, M_PI),
742         aView->Camera()->Up().Rotated (aNewDirAx1, M_PI * 1.5),
743       };
744
745       Standard_Real aBestAngle = Precision::Infinite();
746       gp_Dir anUpBest;
747       for (Standard_Integer anUpIter = 0; anUpIter < 4; ++anUpIter)
748       {
749         Standard_Real anAngle = anUpList[anUpIter].Angle (anOldUp);
750         if (aBestAngle > anAngle)
751         {
752           aBestAngle = anAngle;
753           anUpBest = anUpList[anUpIter];
754         }
755       }
756       aView->Camera()->SetUp (anUpBest);
757     }
758
759     const Bnd_Box aBndSelected = myToFitSelected ? GetContext()->BoundingBoxOfSelection() : Bnd_Box();
760     if (!aBndSelected.IsVoid())
761     {
762       aView->FitAll (aBndSelected, 0.01, false);
763     }
764     else
765     {
766       aView->FitAll (0.01, false);
767     }
768     aView->SetCamera (aBackupCamera);
769     aView->SetImmediateUpdate (wasImmediateUpdate);
770   }
771
772   myViewAnimation->SetView (aView);
773   myViewAnimation->SetCameraStart (myStartState);
774   myViewAnimation->SetCameraEnd   (myEndState);
775   myViewAnimation->SetOwnDuration (myDuration);
776   myViewAnimation->StartTimer (0.0, 1.0, true, false);
777 }
778
779 //=======================================================================
780 //function : updateAnimation
781 //purpose  :
782 //=======================================================================
783 Standard_Boolean AIS_ViewCube::updateAnimation()
784 {
785   const Standard_Real aPts = myViewAnimation->UpdateTimer();
786   if (aPts >= myDuration)
787   {
788     myViewAnimation->Stop();
789     onAnimationFinished();
790     myViewAnimation->SetView (Handle(V3d_View)());
791     return Standard_False;
792   }
793   return Standard_True;
794 }
795
796 //=======================================================================
797 //function : UpdateAnimation
798 //purpose  :
799 //=======================================================================
800 Standard_Boolean AIS_ViewCube::UpdateAnimation (const Standard_Boolean theToUpdate)
801 {
802   Handle(V3d_View) aView = myViewAnimation->View();
803   if (!HasAnimation()
804    || !updateAnimation())
805   {
806     return Standard_False;
807   }
808
809   if (theToUpdate
810   && !aView.IsNull())
811   {
812     aView->IsInvalidated() ? aView->Redraw() : aView->RedrawImmediate();
813   }
814
815   onAfterAnimation();
816   return Standard_True;
817 }
818
819 //=======================================================================
820 //function : HandleClick
821 //purpose  :
822 //=======================================================================
823 void AIS_ViewCube::HandleClick (const Handle(AIS_ViewCubeOwner)& theOwner)
824 {
825   if (!myToAutoStartAnim)
826   {
827     return;
828   }
829
830   StartAnimation (theOwner);
831   if (!myIsFixedAnimation)
832   {
833     return;
834   }
835   for (; HasAnimation(); )
836   {
837     UpdateAnimation (true);
838   }
839 }
840
841 //=======================================================================
842 //function : HilightOwnerWithColor
843 //purpose  :
844 //=======================================================================
845 void AIS_ViewCube::HilightOwnerWithColor (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr,
846                                           const Handle(Prs3d_Drawer)& theStyle,
847                                           const Handle(SelectMgr_EntityOwner)& theOwner)
848 {
849   if (theOwner.IsNull()
850   || !thePrsMgr->IsImmediateModeOn())
851   {
852     return;
853   }
854
855   const Graphic3d_ZLayerId aLayer = theStyle->ZLayer() != Graphic3d_ZLayerId_UNKNOWN ? theStyle->ZLayer() : myDrawer->ZLayer();
856   const AIS_ViewCubeOwner* aCubeOwner = dynamic_cast<AIS_ViewCubeOwner* >(theOwner.get());
857
858   Handle(Prs3d_Presentation) aHiPrs = GetHilightPresentation (thePrsMgr);
859   aHiPrs->Clear();
860   aHiPrs->CStructure()->ViewAffinity = thePrsMgr->StructureManager()->ObjectAffinity (Handle(Standard_Transient)(this));
861   aHiPrs->SetTransformPersistence (TransformPersistence());
862   aHiPrs->SetZLayer (aLayer);
863
864   {
865     Handle(Graphic3d_Group) aGroup = aHiPrs->NewGroup();
866     aGroup->SetGroupPrimitivesAspect (theStyle->ShadingAspect()->Aspect());
867     if (Handle(Graphic3d_ArrayOfTriangles) aTris = createBoxPartTriangles (aCubeOwner->MainOrientation()))
868     {
869       aGroup->AddPrimitiveArray (aTris);
870     }
871   }
872
873   if (thePrsMgr->IsImmediateModeOn())
874   {
875     thePrsMgr->AddToImmediateList (aHiPrs);
876   }
877 }
878
879 //=======================================================================
880 //function : HilightSelected
881 //purpose  :
882 //=======================================================================
883 void AIS_ViewCube::HilightSelected (const Handle(PrsMgr_PresentationManager3d)& ,
884                                     const SelectMgr_SequenceOfOwner& theSeq)
885 {
886   // this method should never be called since AIS_InteractiveObject::HandleClick() has been overridden
887   if (theSeq.Size() == 1)
888   {
889     //HandleClick (Handle(AIS_ViewCubeOwner)::DownCast (theSeq.First()));
890   }
891 }