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