7db8842ccf8ccf1835960a72eec833945ee8f760
[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 void AIS_ViewCube::createRoundRectangleTriangles (const Handle(Graphic3d_ArrayOfTriangles)& theTris,
350                                                   Standard_Integer& theNbNodes,
351                                                   Standard_Integer& theNbTris,
352                                                   const gp_XY& theSize,
353                                                   Standard_Real theRadius,
354                                                   const gp_Trsf& theTrsf)
355 {
356   const Standard_Real aRadius = Min (theRadius, Min (theSize.X(), theSize.Y()) * 0.5);
357   const gp_XY  aHSize (theSize.X() * 0.5 - aRadius, theSize.Y() * 0.5 - aRadius);
358   const gp_Dir aNorm = gp::DZ().Transformed (theTrsf);
359   const Standard_Integer aVertFirst = !theTris.IsNull() ? theTris->VertexNumber() : 0;
360   if (aRadius > 0.0)
361   {
362     const Standard_Integer aNbNodes = (THE_NB_ROUND_SPLITS + 1) * 4 + 1;
363     theNbNodes += aNbNodes;
364     theNbTris  += aNbNodes;
365     if (theTris.IsNull())
366     {
367       return;
368     }
369
370     theTris->AddVertex (gp_Pnt (0.0, 0.0, 0.0).Transformed (theTrsf));
371     for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
372     {
373       const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (M_PI * 0.5, 0.0, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
374       theTris->AddVertex (gp_Pnt (aHSize.X() + aRadius * Cos (anAngle), aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
375     }
376     for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
377     {
378       const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (0.0, -M_PI * 0.5, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
379       theTris->AddVertex (gp_Pnt (aHSize.X() + aRadius * Cos (anAngle), -aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
380     }
381     for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
382     {
383       const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (-M_PI * 0.5, -M_PI, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
384       theTris->AddVertex (gp_Pnt (-aHSize.X() + aRadius * Cos (anAngle), -aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
385     }
386     for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
387     {
388       const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (-M_PI, -M_PI * 1.5, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
389       theTris->AddVertex (gp_Pnt (-aHSize.X() + aRadius * Cos (anAngle), aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
390     }
391
392     // split triangle fan
393     theTris->AddTriangleFanEdges (aVertFirst + 1, theTris->VertexNumber(), true);
394   }
395   else
396   {
397     theNbNodes += 4;
398     theNbTris  += 2;
399     if (theTris.IsNull())
400     {
401       return;
402     }
403
404     theTris->AddVertex (gp_Pnt (-aHSize.X(), -aHSize.Y(), 0.0).Transformed (theTrsf));
405     theTris->AddVertex (gp_Pnt (-aHSize.X(),  aHSize.Y(), 0.0).Transformed (theTrsf));
406     theTris->AddVertex (gp_Pnt ( aHSize.X(),  aHSize.Y(), 0.0).Transformed (theTrsf));
407     theTris->AddVertex (gp_Pnt ( aHSize.X(), -aHSize.Y(), 0.0).Transformed (theTrsf));
408     theTris->AddQuadTriangleEdges (aVertFirst + 1, aVertFirst + 2, aVertFirst + 3, aVertFirst + 4);
409   }
410
411   for (Standard_Integer aVertIter = aVertFirst + 1; aVertIter <= theTris->VertexNumber(); ++aVertIter)
412   {
413     theTris->SetVertexNormal (aVertIter, -aNorm);
414   }
415 }
416
417 //=======================================================================
418 //function : createBoxPartTriangles
419 //purpose  :
420 //=======================================================================
421 void AIS_ViewCube::createBoxPartTriangles (const Handle(Graphic3d_ArrayOfTriangles)& theTris,
422                                            Standard_Integer& theNbNodes,
423                                            Standard_Integer& theNbTris,
424                                            V3d_TypeOfOrientation theDir) const
425 {
426   if (IsBoxSide (theDir))
427   {
428     createBoxSideTriangles (theTris, theNbNodes, theNbTris, theDir);
429   }
430   else if (IsBoxEdge (theDir)
431         && myToDisplayEdges)
432   {
433     createBoxEdgeTriangles (theTris, theNbNodes, theNbTris, theDir);
434   }
435   else if (IsBoxCorner (theDir)
436         && myToDisplayVertices)
437   {
438     createBoxCornerTriangles (theTris, theNbNodes, theNbTris, theDir);
439   }
440 }
441
442 //=======================================================================
443 //function : createBoxSideTriangles
444 //purpose  :
445 //=======================================================================
446 void AIS_ViewCube::createBoxSideTriangles (const Handle(Graphic3d_ArrayOfTriangles)& theTris,
447                                            Standard_Integer& theNbNodes,
448                                            Standard_Integer& theNbTris,
449                                            V3d_TypeOfOrientation theDirection) const
450 {
451   const gp_Dir aDir = V3d::GetProjAxis (theDirection);
452   const gp_Pnt aPos = aDir.XYZ() * (mySize * 0.5 + myBoxFacetExtension);
453   const gp_Ax2 aPosition (aPos, aDir.Reversed());
454
455   gp_Ax3 aSystem (aPosition);
456   gp_Trsf aTrsf;
457   aTrsf.SetTransformation (aSystem, gp_Ax3());
458
459   createRoundRectangleTriangles (theTris, theNbNodes, theNbTris,
460                                  gp_XY (mySize, mySize), myRoundRadius * mySize, aTrsf);
461 }
462
463 //=======================================================================
464 //function : createBoxEdgeTriangles
465 //purpose  :
466 //=======================================================================
467 void AIS_ViewCube::createBoxEdgeTriangles (const Handle(Graphic3d_ArrayOfTriangles)& theTris,
468                                            Standard_Integer& theNbNodes,
469                                            Standard_Integer& theNbTris,
470                                            V3d_TypeOfOrientation theDirection) const
471 {
472   const Standard_Real aThickness = Max (myBoxFacetExtension * gp_XY (1.0, 1.0).Modulus() - myBoxEdgeGap, myBoxEdgeMinSize);
473
474   const gp_Dir aDir = V3d::GetProjAxis (theDirection);
475   const gp_Pnt aPos = aDir.XYZ() * (mySize * 0.5 * gp_XY (1.0, 1.0).Modulus() + myBoxFacetExtension * Cos (M_PI_4));
476   const gp_Ax2 aPosition (aPos, aDir.Reversed());
477
478   gp_Ax3 aSystem (aPosition);
479   gp_Trsf aTrsf;
480   aTrsf.SetTransformation (aSystem, gp_Ax3());
481
482   createRoundRectangleTriangles (theTris, theNbNodes, theNbTris,
483                                  gp_XY (aThickness, mySize), myRoundRadius * mySize, aTrsf);
484 }
485
486 //=======================================================================
487 //function : createBoxCornerTriangles
488 //purpose  :
489 //=======================================================================
490 void AIS_ViewCube::createBoxCornerTriangles (const Handle(Graphic3d_ArrayOfTriangles)& theTris,
491                                              Standard_Integer& theNbNodes,
492                                              Standard_Integer& theNbTris,
493                                              V3d_TypeOfOrientation theDir) const
494 {
495   const Standard_Real aHSize = mySize * 0.5;
496   const gp_Dir aDir = V3d::GetProjAxis (theDir);
497   const gp_XYZ aHSizeDir = aDir.XYZ() * (aHSize * gp_Vec (1.0, 1.0, 1.0).Magnitude());
498   const Standard_Integer aVertFirst = !theTris.IsNull() ? theTris->VertexNumber() : 0;
499   if (myRoundRadius > 0.0)
500   {
501     theNbNodes += THE_NB_DISK_SLICES + 1;
502     theNbTris  += THE_NB_DISK_SLICES + 1;
503     if (theTris.IsNull())
504     {
505       return;
506     }
507
508     const Standard_Real anEdgeHWidth = myBoxFacetExtension * gp_XY (1.0, 1.0).Modulus() * 0.5;
509     const Standard_Real aHeight = anEdgeHWidth * Sqrt (2.0 / 3.0); // tetrahedron height
510     const gp_Pnt aPos = aDir.XYZ() * (aHSize * gp_Vec (1.0, 1.0, 1.0).Magnitude() + aHeight);
511     const gp_Ax2 aPosition (aPos, aDir.Reversed());
512     gp_Ax3 aSystem (aPosition);
513     gp_Trsf aTrsf;
514     aTrsf.SetTransformation (aSystem, gp_Ax3());
515     const Standard_Real aRadius = Max (myBoxFacetExtension * 0.5 / Cos (M_PI_4), myCornerMinSize);
516
517     theTris->AddVertex (gp_Pnt (0.0, 0.0, 0.0).Transformed (aTrsf));
518     for (Standard_Integer aNodeIter = 0; aNodeIter < THE_NB_DISK_SLICES; ++aNodeIter)
519     {
520       const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (2.0 * M_PI, 0.0, Standard_Real(aNodeIter) / Standard_Real(THE_NB_DISK_SLICES));
521       theTris->AddVertex (gp_Pnt (aRadius * Cos (anAngle), aRadius * Sin (anAngle), 0.0).Transformed (aTrsf));
522     }
523     theTris->AddTriangleFanEdges (aVertFirst + 1, theTris->VertexNumber(), true);
524   }
525   else
526   {
527     theNbNodes += 3;
528     theNbTris  += 1;
529     if (theTris.IsNull())
530     {
531       return;
532     }
533
534     theTris->AddVertex (aHSizeDir + myBoxFacetExtension * gp_Dir (aDir.X(), 0.0, 0.0).XYZ());
535     theTris->AddVertex (aHSizeDir + myBoxFacetExtension * gp_Dir (0.0, aDir.Y(), 0.0).XYZ());
536     theTris->AddVertex (aHSizeDir + myBoxFacetExtension * gp_Dir (0.0, 0.0, aDir.Z()).XYZ());
537
538     const gp_XYZ aNode1 = theTris->Vertice (aVertFirst + 1).XYZ();
539     const gp_XYZ aNode2 = theTris->Vertice (aVertFirst + 2).XYZ();
540     const gp_XYZ aNode3 = theTris->Vertice (aVertFirst + 3).XYZ();
541     const gp_XYZ aNormTri = ((aNode2 - aNode1).Crossed (aNode3 - aNode1));
542     if (aNormTri.Dot (aDir.XYZ()) < 0.0)
543     {
544       theTris->AddTriangleEdges (aVertFirst + 1, aVertFirst + 3, aVertFirst + 2);
545     }
546     else
547     {
548       theTris->AddTriangleEdges (aVertFirst + 1, aVertFirst + 2, aVertFirst + 3);
549     }
550   }
551
552   for (Standard_Integer aVertIter = aVertFirst + 1; aVertIter <= theTris->VertexNumber(); ++aVertIter)
553   {
554     theTris->SetVertexNormal (aVertIter, aDir);
555   }
556 }
557
558 //=======================================================================
559 //function : Compute
560 //purpose  :
561 //=======================================================================
562 void AIS_ViewCube::Compute (const Handle(PrsMgr_PresentationManager3d)& ,
563                             const Handle(Prs3d_Presentation)& thePrs,
564                             const Standard_Integer theMode)
565 {
566   thePrs->SetInfiniteState (true);
567   if (theMode != 0)
568   {
569     return;
570   }
571
572   const gp_Pnt aLocation = (mySize * 0.5 + myBoxFacetExtension + myAxesPadding) * gp_XYZ (-1.0, -1.0, -1.0);
573
574   // Display axes
575   if (myToDisplayAxes)
576   {
577     const Standard_Real anAxisSize = mySize + 2.0 * myBoxFacetExtension + myAxesPadding;
578     const Handle(Prs3d_DatumAspect)& aDatumAspect = myDrawer->DatumAspect();
579     for (Standard_Integer anAxisIter = Prs3d_DP_XAxis; anAxisIter <= Prs3d_DP_ZAxis; ++anAxisIter)
580     {
581       const Prs3d_DatumParts aPart = (Prs3d_DatumParts )anAxisIter;
582       if (!aDatumAspect->DrawDatumPart (aPart))
583       {
584         continue;
585       }
586
587       gp_Ax1 anAx1;
588       switch (aPart)
589       {
590         case Prs3d_DP_XAxis: anAx1 = gp_Ax1 (aLocation, gp::DX()); break;
591         case Prs3d_DP_YAxis: anAx1 = gp_Ax1 (aLocation, gp::DY()); break;
592         case Prs3d_DP_ZAxis: anAx1 = gp_Ax1 (aLocation, gp::DZ()); break;
593         default: break;
594       }
595
596       Handle(Graphic3d_Group) anAxisGroup = thePrs->NewGroup();
597       anAxisGroup->SetGroupPrimitivesAspect (aDatumAspect->ShadingAspect (aPart)->Aspect());
598
599       const Standard_Real anArrowLength = 0.2 * anAxisSize;
600       Handle(Graphic3d_ArrayOfTriangles) aTriangleArray = Prs3d_Arrow::DrawShaded (anAx1, 1.0, anAxisSize, 3.0, anArrowLength, THE_NB_ARROW_FACETTES);
601       anAxisGroup->AddPrimitiveArray (aTriangleArray);
602
603       TCollection_AsciiString anAxisLabel;
604       if (aDatumAspect->ToDrawLabels()
605       &&  myAxesLabels.Find (aPart, anAxisLabel)
606       && !anAxisLabel.IsEmpty())
607       {
608         Handle(Graphic3d_Group) anAxisLabelGroup = thePrs->NewGroup();
609         gp_Pnt aTextOrigin = anAx1.Location().Translated (gp_Vec (anAx1.Direction().X() * (anAxisSize + anArrowLength),
610                                                                   anAx1.Direction().Y() * (anAxisSize + anArrowLength),
611                                                                   anAx1.Direction().Z() * (anAxisSize + anArrowLength)));
612         Prs3d_Text::Draw (anAxisLabelGroup, aDatumAspect->TextAspect(), TCollection_ExtendedString (anAxisLabel), aTextOrigin);
613       }
614     }
615
616     // Display center
617     {
618       Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
619       Handle(Prs3d_ShadingAspect) anAspectCen = new Prs3d_ShadingAspect();
620       anAspectCen->SetColor (Quantity_NOC_WHITE);
621       aGroup->SetGroupPrimitivesAspect (anAspectCen->Aspect());
622       Prs3d_ToolSphere aTool (4.0, THE_NB_DISK_SLICES, THE_NB_DISK_SLICES);
623       gp_Trsf aTrsf;
624       aTrsf.SetTranslation (gp_Vec (gp::Origin(), aLocation));
625       Handle(Graphic3d_ArrayOfTriangles) aCenterArray;
626       aTool.FillArray (aCenterArray, aTrsf);
627       aGroup->AddPrimitiveArray (aCenterArray);
628     }
629   }
630
631   // Display box sides
632   {
633     Standard_Integer aNbNodes = 0, aNbTris = 0;
634     for (Standard_Integer aPartIter = V3d_Xpos; aPartIter <= Standard_Integer(V3d_Zneg); ++aPartIter)
635     {
636       createBoxPartTriangles (Handle(Graphic3d_ArrayOfTriangles)(), aNbNodes, aNbTris, (V3d_TypeOfOrientation )aPartIter);
637     }
638     if (aNbNodes > 0)
639     {
640       Handle(Graphic3d_ArrayOfTriangles) aTris = new Graphic3d_ArrayOfTriangles (aNbNodes, aNbTris * 3, Graphic3d_ArrayFlags_VertexNormal);
641       Handle(Graphic3d_ArrayOfSegments) aSegs;
642       if (myDrawer->FaceBoundaryDraw())
643       {
644         aSegs = new Graphic3d_ArrayOfSegments (aNbNodes, aNbNodes * 2, Graphic3d_ArrayFlags_None);
645       }
646       aNbNodes = aNbTris = 0;
647       for (Standard_Integer aPartIter = V3d_Xpos; aPartIter <= Standard_Integer(V3d_Zneg); ++aPartIter)
648       {
649         Standard_Integer aTriNodesFrom = aTris->VertexNumber();
650         const Standard_Integer aTriFrom = aNbTris;
651         createBoxPartTriangles (aTris, aNbNodes, aNbTris, (V3d_TypeOfOrientation )aPartIter);
652         if (aSegs.IsNull())
653         {
654           continue;
655         }
656
657         const Standard_Integer aFirstNode = aSegs->VertexNumber();
658         for (Standard_Integer aVertIter = (aNbTris - aTriFrom) > 2 ? aTriNodesFrom + 2 : aTriNodesFrom + 1; // skip triangle fan center
659              aVertIter <= aTris->VertexNumber(); ++aVertIter)
660         {
661           aSegs->AddVertex (aTris->Vertice (aVertIter));
662         }
663         aSegs->AddPolylineEdges (aFirstNode + 1, aSegs->VertexNumber(), true);
664       }
665
666       {
667         Handle(Graphic3d_Group) aGroupSides = thePrs->NewGroup();
668         aGroupSides->SetClosed (true); // should be replaced by forced back-face culling aspect
669         aGroupSides->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
670         aGroupSides->AddPrimitiveArray (aTris);
671       }
672
673       if (!aSegs.IsNull())
674       {
675         Handle(Graphic3d_Group) aGroupSegs = thePrs->NewGroup();
676         aGroupSegs->SetGroupPrimitivesAspect (myDrawer->FaceBoundaryAspect()->Aspect());
677         aGroupSegs->AddPrimitiveArray (aSegs);
678       }
679     }
680
681     // Display box sides labels
682     Handle(Graphic3d_Group) aTextGroup = thePrs->NewGroup();
683     aTextGroup->SetGroupPrimitivesAspect (myDrawer->TextAspect()->Aspect());
684     for (Standard_Integer aPartIter = V3d_Xpos; aPartIter <= Standard_Integer(V3d_Zneg); ++aPartIter)
685     {
686       const V3d_TypeOfOrientation anOrient = (V3d_TypeOfOrientation )aPartIter;
687
688       TCollection_AsciiString aLabel;
689       if (!myBoxSideLabels.Find (anOrient, aLabel)
690         || aLabel.IsEmpty())
691       {
692         continue;
693       }
694
695       const gp_Dir aDir = V3d::GetProjAxis (anOrient);
696       gp_Dir anUp = myIsYup ? gp::DY() : gp::DZ();
697       if (myIsYup)
698       {
699         if (anOrient == V3d_Ypos
700           || anOrient == V3d_Yneg)
701         {
702           anUp = -gp::DZ();
703         }
704       }
705       else
706       {
707         if (anOrient == V3d_Zpos)
708         {
709           anUp = gp::DY();
710         }
711         else if (anOrient == V3d_Zneg)
712         {
713           anUp = -gp::DY();
714         }
715       }
716
717       const Standard_Real anOffset = 2.0; // extra offset to avoid overlapping with triangulation
718       const gp_Pnt aPos = aDir.XYZ() * (mySize * 0.5 + myBoxFacetExtension + anOffset);
719       const gp_Ax2 aPosition (aPos, aDir, anUp.Crossed (aDir));
720       Prs3d_Text::Draw (aTextGroup, myDrawer->TextAspect(), aLabel, aPosition);
721     }
722   }
723
724   // Display box edges
725   {
726     Standard_Integer aNbNodes = 0, aNbTris = 0;
727     for (Standard_Integer aPartIter = V3d_XposYpos; aPartIter <= Standard_Integer(V3d_YposZneg); ++aPartIter)
728     {
729       createBoxPartTriangles (Handle(Graphic3d_ArrayOfTriangles)(), aNbNodes, aNbTris, (V3d_TypeOfOrientation )aPartIter);
730     }
731     if (aNbNodes > 0)
732     {
733       Handle(Graphic3d_ArrayOfTriangles) aTris = new Graphic3d_ArrayOfTriangles (aNbNodes, aNbTris * 3, Graphic3d_ArrayFlags_VertexNormal);
734       aNbNodes = aNbTris = 0;
735       for (Standard_Integer aPartIter = V3d_XposYpos; aPartIter <= Standard_Integer(V3d_YposZneg); ++aPartIter)
736       {
737         const V3d_TypeOfOrientation anOrient = (V3d_TypeOfOrientation )aPartIter;
738         createBoxPartTriangles (aTris, aNbNodes, aNbTris, anOrient);
739       }
740
741       Handle(Graphic3d_Group) aGroupEdges = thePrs->NewGroup();
742       aGroupEdges->SetClosed (true);
743       aGroupEdges->SetGroupPrimitivesAspect (myBoxEdgeAspect->Aspect());
744       aGroupEdges->AddPrimitiveArray (aTris);
745     }
746   }
747
748   // Display box corners
749   {
750     Standard_Integer aNbNodes = 0, aNbTris = 0;
751     for (Standard_Integer aPartIter = V3d_XposYposZpos; aPartIter <= Standard_Integer(V3d_XnegYnegZneg); ++aPartIter)
752     {
753       createBoxPartTriangles (Handle(Graphic3d_ArrayOfTriangles)(), aNbNodes, aNbTris, (V3d_TypeOfOrientation )aPartIter);
754     }
755     if (aNbNodes > 0)
756     {
757       Handle(Graphic3d_ArrayOfTriangles) aTris = new Graphic3d_ArrayOfTriangles (aNbNodes, aNbTris * 3, Graphic3d_ArrayFlags_VertexNormal);
758       aNbNodes = aNbTris = 0;
759       for (Standard_Integer aPartIter = V3d_XposYposZpos; aPartIter <= Standard_Integer(V3d_XnegYnegZneg); ++aPartIter)
760       {
761         const V3d_TypeOfOrientation anOrient = (V3d_TypeOfOrientation )aPartIter;
762         createBoxPartTriangles (aTris, aNbNodes, aNbTris, anOrient);
763       }
764
765       Handle(Graphic3d_Group) aGroupCorners = thePrs->NewGroup();
766       aGroupCorners->SetClosed (true);
767       aGroupCorners->SetGroupPrimitivesAspect (myBoxCornerAspect->Aspect());
768       aGroupCorners->AddPrimitiveArray (aTris);
769     }
770   }
771 }
772
773 //=======================================================================
774 //function : ComputeSelection
775 //purpose  :
776 //=======================================================================
777 void AIS_ViewCube::ComputeSelection (const Handle(SelectMgr_Selection)& theSelection,
778                                      const Standard_Integer theMode)
779 {
780   if (theMode != 0)
781   {
782     return;
783   }
784
785   for (Standard_Integer aPartIter = 0; aPartIter <= Standard_Integer(V3d_XnegYnegZneg); ++aPartIter)
786   {
787     const V3d_TypeOfOrientation anOri = (V3d_TypeOfOrientation )aPartIter;
788     Standard_Integer aNbNodes = 0, aNbTris = 0;
789     createBoxPartTriangles (Handle(Graphic3d_ArrayOfTriangles)(), aNbNodes, aNbTris, anOri);
790     if (aNbNodes <= 0)
791     {
792       continue;
793     }
794
795     Handle(Graphic3d_ArrayOfTriangles) aTris = new Graphic3d_ArrayOfTriangles (aNbNodes, aNbTris * 3, Graphic3d_ArrayFlags_None);
796     aNbNodes = aNbTris = 0;
797     createBoxPartTriangles (aTris, aNbNodes, aNbTris, anOri);
798
799     Standard_Integer aSensitivity = 2;
800     if (IsBoxCorner (anOri))
801     {
802       aSensitivity = 8;
803     }
804     else if (IsBoxEdge (anOri))
805     {
806       aSensitivity = 4;
807     }
808     Handle(AIS_ViewCubeOwner) anOwner = new AIS_ViewCubeOwner (this, anOri);
809     Handle(AIS_ViewCubeSensitive) aTriSens = new AIS_ViewCubeSensitive (anOwner, aTris);
810     aTriSens->SetSensitivityFactor (aSensitivity);
811     theSelection->Add (aTriSens);
812   }
813 }
814
815 //=======================================================================
816 //function : HasAnimation
817 //purpose  :
818 //=======================================================================
819 Standard_Boolean AIS_ViewCube::HasAnimation() const
820 {
821   return !myViewAnimation->IsStopped();
822 }
823
824 //=======================================================================
825 //function : viewFitAll
826 //purpose  :
827 //=======================================================================
828 void AIS_ViewCube::viewFitAll (const Handle(V3d_View)& theView,
829                                const Handle(Graphic3d_Camera)& theCamera)
830 {
831   Bnd_Box aBndBox = myToFitSelected ? GetContext()->BoundingBoxOfSelection() : theView->View()->MinMaxValues();
832   if (aBndBox.IsVoid()
833    && myToFitSelected)
834   {
835     aBndBox = theView->View()->MinMaxValues();
836   }
837   if (!aBndBox.IsVoid())
838   {
839     theView->FitMinMax (theCamera, aBndBox, 0.01, 10.0 * Precision::Confusion());
840   }
841 }
842
843 //=======================================================================
844 //function : StartAnimation
845 //purpose  :
846 //=======================================================================
847 void AIS_ViewCube::StartAnimation (const Handle(AIS_ViewCubeOwner)& theOwner)
848 {
849   Handle(V3d_View) aView = GetContext()->LastActiveView();
850   if (theOwner.IsNull()
851    || aView.IsNull())
852   {
853     return;
854   }
855
856   myStartState->Copy (aView->Camera());
857   myEndState  ->Copy (aView->Camera());
858
859   {
860     {
861       Handle(Graphic3d_Camera) aBackupCamera = aView->Camera();
862       const bool wasImmediateUpdate = aView->SetImmediateUpdate (false);
863       aView->SetCamera (myEndState);
864       aView->SetProj (theOwner->MainOrientation(), myIsYup);
865       aView->SetCamera (aBackupCamera);
866       aView->SetImmediateUpdate (wasImmediateUpdate);
867     }
868
869     const gp_Dir aNewDir = myEndState->Direction();
870     if (!myToResetCameraUp
871      && !aNewDir.IsEqual (myStartState->Direction(), Precision::Angular()))
872     {
873       // find the Up direction closest to current instead of default one
874       const gp_Ax1 aNewDirAx1 (gp::Origin(), aNewDir);
875       const gp_Dir anOldUp = myStartState->Up();
876       const gp_Dir anUpList[4] =
877       {
878         myEndState->Up(),
879         myEndState->Up().Rotated (aNewDirAx1, M_PI_2),
880         myEndState->Up().Rotated (aNewDirAx1, M_PI),
881         myEndState->Up().Rotated (aNewDirAx1, M_PI * 1.5),
882       };
883
884       Standard_Real aBestAngle = Precision::Infinite();
885       gp_Dir anUpBest;
886       for (Standard_Integer anUpIter = 0; anUpIter < 4; ++anUpIter)
887       {
888         Standard_Real anAngle = anUpList[anUpIter].Angle (anOldUp);
889         if (aBestAngle > anAngle)
890         {
891           aBestAngle = anAngle;
892           anUpBest = anUpList[anUpIter];
893         }
894       }
895       myEndState->SetUp (anUpBest);
896     }
897
898     viewFitAll (aView, myEndState);
899   }
900
901   myViewAnimation->SetView (aView);
902   myViewAnimation->SetCameraStart (myStartState);
903   myViewAnimation->SetCameraEnd   (myEndState);
904   myViewAnimation->SetOwnDuration (myDuration);
905   myViewAnimation->StartTimer (0.0, 1.0, true, false);
906 }
907
908 //=======================================================================
909 //function : updateAnimation
910 //purpose  :
911 //=======================================================================
912 Standard_Boolean AIS_ViewCube::updateAnimation()
913 {
914   const Standard_Real aPts = myViewAnimation->UpdateTimer();
915   if (aPts >= myDuration)
916   {
917     myViewAnimation->Stop();
918     onAnimationFinished();
919     myViewAnimation->SetView (Handle(V3d_View)());
920     return Standard_False;
921   }
922   return Standard_True;
923 }
924
925 //=======================================================================
926 //function : UpdateAnimation
927 //purpose  :
928 //=======================================================================
929 Standard_Boolean AIS_ViewCube::UpdateAnimation (const Standard_Boolean theToUpdate)
930 {
931   Handle(V3d_View) aView = myViewAnimation->View();
932   if (!HasAnimation()
933    || !updateAnimation())
934   {
935     return Standard_False;
936   }
937
938   if (theToUpdate
939   && !aView.IsNull())
940   {
941     aView->IsInvalidated() ? aView->Redraw() : aView->RedrawImmediate();
942   }
943
944   onAfterAnimation();
945   return Standard_True;
946 }
947
948 //=======================================================================
949 //function : HandleClick
950 //purpose  :
951 //=======================================================================
952 void AIS_ViewCube::HandleClick (const Handle(AIS_ViewCubeOwner)& theOwner)
953 {
954   if (!myToAutoStartAnim)
955   {
956     return;
957   }
958
959   StartAnimation (theOwner);
960   if (!myIsFixedAnimation)
961   {
962     return;
963   }
964   for (; HasAnimation(); )
965   {
966     UpdateAnimation (true);
967   }
968 }
969
970 //=======================================================================
971 //function : HilightOwnerWithColor
972 //purpose  :
973 //=======================================================================
974 void AIS_ViewCube::HilightOwnerWithColor (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr,
975                                           const Handle(Prs3d_Drawer)& theStyle,
976                                           const Handle(SelectMgr_EntityOwner)& theOwner)
977 {
978   if (theOwner.IsNull()
979   || !thePrsMgr->IsImmediateModeOn())
980   {
981     return;
982   }
983
984   const Graphic3d_ZLayerId aLayer = theStyle->ZLayer() != Graphic3d_ZLayerId_UNKNOWN ? theStyle->ZLayer() : myDrawer->ZLayer();
985   const AIS_ViewCubeOwner* aCubeOwner = dynamic_cast<AIS_ViewCubeOwner* >(theOwner.get());
986
987   Handle(Prs3d_Presentation) aHiPrs = GetHilightPresentation (thePrsMgr);
988   aHiPrs->Clear();
989   aHiPrs->CStructure()->ViewAffinity = thePrsMgr->StructureManager()->ObjectAffinity (Handle(Standard_Transient)(this));
990   aHiPrs->SetTransformPersistence (TransformPersistence());
991   aHiPrs->SetZLayer (aLayer);
992
993   {
994     Handle(Graphic3d_Group) aGroup = aHiPrs->NewGroup();
995     aGroup->SetGroupPrimitivesAspect (theStyle->ShadingAspect()->Aspect());
996     Standard_Integer aNbNodes = 0, aNbTris = 0;
997     createBoxPartTriangles (Handle(Graphic3d_ArrayOfTriangles)(), aNbNodes, aNbTris, aCubeOwner->MainOrientation());
998     if (aNbNodes > 0)
999     {
1000       Handle(Graphic3d_ArrayOfTriangles) aTris = new Graphic3d_ArrayOfTriangles (aNbNodes, aNbTris * 3, Graphic3d_ArrayFlags_None);
1001       aNbNodes = aNbTris = 0;
1002       createBoxPartTriangles (aTris, aNbNodes, aNbTris, aCubeOwner->MainOrientation());
1003       aGroup->AddPrimitiveArray (aTris);
1004     }
1005   }
1006
1007   if (thePrsMgr->IsImmediateModeOn())
1008   {
1009     thePrsMgr->AddToImmediateList (aHiPrs);
1010   }
1011 }
1012
1013 //=======================================================================
1014 //function : HilightSelected
1015 //purpose  :
1016 //=======================================================================
1017 void AIS_ViewCube::HilightSelected (const Handle(PrsMgr_PresentationManager3d)& ,
1018                                     const SelectMgr_SequenceOfOwner& theSeq)
1019 {
1020   // this method should never be called since AIS_InteractiveObject::HandleClick() has been overridden
1021   if (theSeq.Size() == 1)
1022   {
1023     //HandleClick (Handle(AIS_ViewCubeOwner)::DownCast (theSeq.First()));
1024   }
1025 }