0028954: Visualization - implement interactive object for camera manipulations
authoraba <aba@opencascade.com>
Wed, 3 Jul 2019 09:37:36 +0000 (12:37 +0300)
committerbugmaster <bugmaster@opencascade.com>
Fri, 19 Jul 2019 09:23:01 +0000 (12:23 +0300)
Added new class AIS_ViewCube implementing interactive cube
displaying orientation of the main axes of the model space in the viewer.
Each side, edge, or corner of the cube corresponds to particular orientation of the camera,
and the class provides methods to move the camera to corresponding position (with animation if needed).

AIS_InteractiveContext::LastActiveView(), added new property returning the last View processed by MoveTo() event.
AIS_InteractiveContext::BoundingBoxOfSelection(), added method returning bounding box of selected objects.
SelectMgr_EntityOwner::HandleMouseClick(), added new callback for handling
mouse clicks by owner itself without automatic highlighting and clearing previous selection.
Called by AIS_InteractiveContext::Select() method.

AIS_ViewController::ViewAnimation() has been extened with camera animation propery,
which can be bound to AIS_ViewCube for smooth embedding into event loop.

Prs3d_ToolDisk has been extended with parameters specifying angle range.
Graphic3d_MaterialAspect now initializes all coefficients to 1.0
when Graphic3d_NOM_UserDefined is passed to class constructor.
AIS_AnimationCamera::update() now sets the end camera position if animation duration is 0.
Prs3d_DatumAspect, added missing setters.

New command vviewcube has been added.

22 files changed:
src/AIS/AIS_AnimationCamera.cxx
src/AIS/AIS_AnimationCamera.hxx
src/AIS/AIS_InteractiveContext.cxx
src/AIS/AIS_InteractiveContext.hxx
src/AIS/AIS_InteractiveContext_1.cxx
src/AIS/AIS_ViewController.cxx
src/AIS/AIS_ViewController.hxx
src/AIS/AIS_ViewCube.cxx [new file with mode: 0644]
src/AIS/AIS_ViewCube.hxx [new file with mode: 0644]
src/AIS/FILES
src/Graphic3d/Graphic3d_MaterialAspect.cxx
src/Prs3d/Prs3d_DatumAspect.hxx
src/Prs3d/Prs3d_ToolDisk.cxx
src/Prs3d/Prs3d_ToolDisk.hxx
src/SelectMgr/SelectMgr_EntityOwner.hxx
src/ViewerTest/ViewerTest_EventManager.cxx
src/ViewerTest/ViewerTest_EventManager.hxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
tests/v3d/grids.list
tests/v3d/viewcube/default [new file with mode: 0644]
tests/v3d/viewcube/style [new file with mode: 0644]
tests/v3d/viewcube/view [new file with mode: 0644]

index b5c6935..8ca5e2e 100644 (file)
@@ -48,7 +48,7 @@ void AIS_AnimationCamera::update (const AIS_AnimationProgress& theProgress)
   Handle(Graphic3d_Camera) aCamera = myView->Camera();
 
   Graphic3d_CameraLerp aCamLerp (myCamStart, myCamEnd);
-  aCamLerp.Interpolate (theProgress.LocalNormalized, aCamera);
+  aCamLerp.Interpolate (HasOwnDuration() ? theProgress.LocalNormalized : 1.0, aCamera);
 
   const Standard_Boolean aPrevImmUpdate = myView->SetImmediateUpdate (Standard_False);
   myView->SetCamera (aCamera);
index cd6d7e1..328ce15 100644 (file)
@@ -33,6 +33,9 @@ public:
   //! Return the target view.
   const Handle(V3d_View)& View() const { return myView; }
 
+  //! Set target view.
+  void SetView (const Handle(V3d_View)& theView) { myView = theView; }
+
   //! Return camera start position.
   const Handle(Graphic3d_Camera)& CameraStart() const { return myCamStart; }
 
index af2d09b..2e68266 100644 (file)
@@ -188,6 +188,29 @@ AIS_InteractiveContext::~AIS_InteractiveContext()
 }
 
 //=======================================================================
+//function : LastActiveView
+//purpose  :
+//=======================================================================
+Handle(V3d_View) AIS_InteractiveContext::LastActiveView() const
+{
+  if (myLastActiveView == NULL
+   || myMainVwr.IsNull())
+  {
+    return Handle(V3d_View)();
+  }
+
+  // as a precaution - check that myLastActiveView pointer is a valid active View
+  for (V3d_ListOfViewIterator aViewIter = myMainVwr->ActiveViewIterator(); aViewIter.More(); aViewIter.Next())
+  {
+    if (aViewIter.Value() == myLastActiveView)
+    {
+      return aViewIter.Value();
+    }
+  }
+  return Handle(V3d_View)();
+}
+
+//=======================================================================
 //function : UpdateCurrentViewer
 //purpose  : 
 //=======================================================================
@@ -2447,12 +2470,10 @@ void AIS_InteractiveContext::FitSelected (const Handle(V3d_View)& theView)
 }
 
 //=======================================================================
-//function : FitSelected
-//purpose  : Fits the view corresponding to the bounds of selected objects
+//function : BoundingBoxOfSelection
+//purpose  :
 //=======================================================================
-void AIS_InteractiveContext::FitSelected (const Handle(V3d_View)& theView,
-                                          const Standard_Real theMargin,
-                                          const Standard_Boolean theToUpdate)
+Bnd_Box AIS_InteractiveContext::BoundingBoxOfSelection() const
 {
   Bnd_Box aBndSelected;
   AIS_MapOfObjectOwners anObjectOwnerMap;
@@ -2491,12 +2512,22 @@ void AIS_InteractiveContext::FitSelected (const Handle(V3d_View)& theView,
     aBndSelected.Add (aTmpBox);
   }
 
-  anObjectOwnerMap.Clear();
-
-  if (aBndSelected.IsVoid())
-    return;
+  return aBndSelected;
+}
 
-  theView->FitAll (aBndSelected, theMargin, theToUpdate);
+//=======================================================================
+//function : FitSelected
+//purpose  : Fits the view corresponding to the bounds of selected objects
+//=======================================================================
+void AIS_InteractiveContext::FitSelected (const Handle(V3d_View)& theView,
+                                          const Standard_Real theMargin,
+                                          const Standard_Boolean theToUpdate)
+{
+  Bnd_Box aBndSelected = BoundingBoxOfSelection();
+  if (!aBndSelected.IsVoid())
+  {
+    theView->FitAll (aBndSelected, theMargin, theToUpdate);
+  }
 }
 
 //=======================================================================
index fd1a1f6..38e5a43 100644 (file)
@@ -370,6 +370,9 @@ public: //! @name mouse picking logic (detection and dynamic highlighting of ent
                                                 const Standard_Integer theMode,
                                                 const Standard_Integer theNewSensitivity);
 
+  //! Returns last active View (argument of MoveTo()/Select() methods).
+  Standard_EXPORT Handle(V3d_View) LastActiveView() const;
+
   //! Relays mouse position in pixels theXPix and theYPix to the interactive context selectors.
   //! This is done by the view theView passing this position to the main viewer and updating it.
   //! If theToRedrawOnUpdate is set to false, callee should call RedrawImmediate() to highlight detected object.
@@ -505,6 +508,9 @@ public: //! @name Selection management
                                                 const Handle(V3d_View)& theView,
                                                 const Standard_Boolean  theToUpdateViewer);
 
+  //! Returns bounding box of selected objects.
+  Standard_EXPORT Bnd_Box BoundingBoxOfSelection() const;
+
   //! Fits the view correspondingly to the bounds of selected objects.
   //! Infinite objects are ignored if infinite state of AIS_InteractiveObject is set to true.
   Standard_EXPORT void FitSelected (const Handle(V3d_View)& theView,
@@ -1379,6 +1385,7 @@ protected: //! @name internal fields
   Handle(PrsMgr_PresentationManager3d) myMainPM;
   Handle(V3d_Viewer) myMainVwr;
   Handle(StdSelect_ViewerSelector3d) myMainSel;
+  V3d_View* myLastActiveView;
   Handle(SelectMgr_EntityOwner) myLastPicked;
   Standard_Boolean myToHilightSelected;
   Handle(AIS_Selection) mySelection;
index f63ee3b..6684a35 100644 (file)
@@ -325,6 +325,7 @@ AIS_StatusOfDetection AIS_InteractiveContext::MoveTo (const Standard_Integer  th
   myCurDetected = 0;
   myCurHighlighted = 0;
   myDetectedSeq.Clear();
+  myLastActiveView = theView.get();
 
   // preliminaires
   AIS_StatusOfDetection aStatus        = AIS_SOD_Nothing;
@@ -495,6 +496,7 @@ AIS_StatusOfPick AIS_InteractiveContext::Select (const Standard_Integer  theXPMi
   // all objects detected by the selector are taken, previous current objects are emptied,
   // new objects are put...
   ClearSelected (Standard_False);
+  myLastActiveView = theView.get();
   myMainSel->Pick (theXPMin, theYPMin, theXPMax, theYPMax, theView);
   for (Standard_Integer aPickIter = 1; aPickIter <= myMainSel->NbPicked(); ++aPickIter)
   {
@@ -534,6 +536,7 @@ AIS_StatusOfPick AIS_InteractiveContext::Select (const TColgp_Array1OfPnt2d& the
   // all objects detected by the selector are taken, previous current objects are emptied,
   // new objects are put...
   ClearSelected (Standard_False);
+  myLastActiveView = theView.get();
   myMainSel->Pick (thePolyline, theView);
   for (Standard_Integer aPickIter = 1; aPickIter <= myMainSel->NbPicked(); ++aPickIter)
   {
@@ -565,6 +568,17 @@ AIS_StatusOfPick AIS_InteractiveContext::Select (const Standard_Boolean toUpdate
 {
   if (!myLastPicked.IsNull())
   {
+    Graphic3d_Vec2i aMousePos (-1, -1);
+    if (myMainSel->GetManager().GetActiveSelectionType() == SelectBasics_SelectingVolumeManager::Point)
+    {
+      aMousePos.SetValues ((Standard_Integer )myMainSel->GetManager().GetMousePosition().X(),
+                           (Standard_Integer )myMainSel->GetManager().GetMousePosition().Y());
+    }
+    if (myLastPicked->HandleMouseClick (aMousePos, Aspect_VKeyMouse_LeftButton, Aspect_VKeyFlags_NONE, false))
+    {
+      return AIS_SOP_NothingSelected;
+    }
+
     if (myAutoHilight)
     {
       clearDynamicHighlight();
@@ -630,6 +644,7 @@ AIS_StatusOfPick AIS_InteractiveContext::ShiftSelect (const Standard_Integer the
     throw Standard_ProgramError ("AIS_InteractiveContext::ShiftSelect() - invalid argument");
   }
 
+  myLastActiveView = theView.get();
   if (myAutoHilight)
   {
     UnhilightSelected (Standard_False);
@@ -670,6 +685,7 @@ AIS_StatusOfPick AIS_InteractiveContext::ShiftSelect (const TColgp_Array1OfPnt2d
     throw Standard_ProgramError ("AIS_InteractiveContext::ShiftSelect() - invalid argument");
   }
 
+  myLastActiveView = theView.get();
   if (myAutoHilight)
   {
     UnhilightSelected (Standard_False);
index e440330..8d8f199 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "AIS_ViewController.hxx"
 
+#include <AIS_AnimationCamera.hxx>
 #include <AIS_InteractiveContext.hxx>
 #include <AIS_Manipulator.hxx>
 #include <AIS_Point.hxx>
@@ -53,6 +54,7 @@ AIS_ViewController::AIS_ViewController()
   myThrustSpeed (0.0f),
   myHasThrust (false),
   //
+  myViewAnimation (new AIS_AnimationCamera ("AIS_ViewController_ViewAnimation", Handle(V3d_View)())),
   myPrevMoveTo (-1, -1),
   myHasHlrOnBeforeRotation (false),
   //
@@ -1225,6 +1227,20 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
 }
 
 // =======================================================================
+// function : AbortViewAnimation
+// purpose  :
+// =======================================================================
+void AIS_ViewController::AbortViewAnimation()
+{
+  if (!myViewAnimation.IsNull()
+   && !myViewAnimation->IsStopped())
+  {
+    myViewAnimation->Stop();
+    myViewAnimation->SetView (Handle(V3d_View)());
+  }
+}
+
+// =======================================================================
 // function : handlePanning
 // purpose  :
 // =======================================================================
@@ -1236,6 +1252,8 @@ void AIS_ViewController::handlePanning (const Handle(V3d_View)& theView)
     return;
   }
 
+  AbortViewAnimation();
+
   const Handle(Graphic3d_Camera)& aCam = theView->Camera();
   if (aCam->IsOrthographic()
   || !hasPanningAnchorPoint())
@@ -1276,6 +1294,8 @@ void AIS_ViewController::handleZRotate (const Handle(V3d_View)& theView)
     return;
   }
 
+  AbortViewAnimation();
+
   Graphic3d_Vec2i aViewPort;
   theView->Window()->Size (aViewPort.x(), aViewPort.y());
   Graphic3d_Vec2d aRotPnt (0.99 * aViewPort.x(),
@@ -1299,6 +1319,8 @@ void AIS_ViewController::handleZoom (const Handle(V3d_View)& theView,
     return;
   }
 
+  AbortViewAnimation();
+
   const Handle(Graphic3d_Camera)& aCam = theView->Camera();
   if (thePnt != NULL)
   {
@@ -1450,6 +1472,7 @@ void AIS_ViewController::handleOrbitRotation (const Handle(V3d_View)& theView,
     return;
   }
 
+  AbortViewAnimation();
   if (theToLockZUp)
   {
     // amend camera to exclude roll angle (put camera Up vector to plane containing global Z and view direction)
@@ -1561,6 +1584,8 @@ void AIS_ViewController::handleViewRotation (const Handle(V3d_View)& theView,
     return;
   }
 
+  AbortViewAnimation();
+
   Graphic3d_Vec2i aWinXY;
   theView->Window()->Size (aWinXY.x(), aWinXY.y());
   double aYawAngleDelta   =  ((myGL.ViewRotation.PointStart.x() - myGL.ViewRotation.PointTo.x()) / double (aWinXY.x())) * (M_PI * 0.5);
@@ -2035,11 +2060,6 @@ void AIS_ViewController::handleSelectionPick (const Handle(AIS_InteractiveContex
   {
     for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aPntIter (myGL.Selection.Points); aPntIter.More(); aPntIter.Next())
     {
-      if (!myGL.Selection.IsXOR)
-      {
-        theCtx->ClearSelected (false);
-      }
-
       const bool hadPrevMoveTo = HasPreviousMoveTo();
       contextLazyMoveTo (theCtx, theView, aPntIter.Value());
       if (!hadPrevMoveTo)
@@ -2274,6 +2294,15 @@ void AIS_ViewController::handleMoveTo (const Handle(AIS_InteractiveContext)& the
 void AIS_ViewController::handleViewRedraw (const Handle(AIS_InteractiveContext)& ,
                                            const Handle(V3d_View)& theView)
 {
+  // manage animation state
+  if (!myViewAnimation.IsNull()
+   && !myViewAnimation->IsStopped())
+  {
+    myViewAnimation->UpdateTimer();
+    ResetPreviousMoveTo();
+    setAskNextFrame();
+  }
+
   for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
   {
     const Handle(V3d_View)& aView = aViewIter.Value();
index e82027d..7cec4ce 100644 (file)
@@ -30,6 +30,7 @@
 #include <Precision.hxx>
 #include <Standard_Mutex.hxx>
 
+class AIS_AnimationCamera;
 class AIS_InteractiveObject;
 class AIS_InteractiveContext;
 class AIS_Point;
@@ -56,6 +57,15 @@ public:
   //! Return input buffer.
   AIS_ViewInputBuffer& ChangeInputBuffer (AIS_ViewInputBufferType theType)       { return theType == AIS_ViewInputBufferType_UI ? myUI : myGL; }
 
+  //! Return view animation; empty (but not NULL) animation by default.
+  const Handle(AIS_AnimationCamera)& ViewAnimation() const { return myViewAnimation; }
+
+  //! Set view animation to be handled within handleViewRedraw().
+  void SetViewAnimation (const Handle(AIS_AnimationCamera)& theAnimation) { myViewAnimation = theAnimation; }
+
+  //! Interrupt active view animation.
+  Standard_EXPORT void AbortViewAnimation();
+
 public: //! @name global parameters
 
   //! Return camera rotation mode, AIS_RotationMode_BndBoxActive by default.
@@ -596,6 +606,7 @@ protected:
   Standard_ShortReal  myThrustSpeed;              //!< active thrust value
   Standard_Boolean    myHasThrust;                //!< flag indicating active thrust
 
+  Handle(AIS_AnimationCamera) myViewAnimation;    //!< view animation
   Handle(AIS_RubberBand) myRubberBand;            //!< Rubber-band presentation
   Handle(AIS_InteractiveObject) myDragObject;     //!< currently dragged object
   Graphic3d_Vec2i     myPrevMoveTo;               //!< previous position of MoveTo event in 3D viewer
diff --git a/src/AIS/AIS_ViewCube.cxx b/src/AIS/AIS_ViewCube.cxx
new file mode 100644 (file)
index 0000000..80652f5
--- /dev/null
@@ -0,0 +1,891 @@
+// Created on: 2017-07-25
+// Created by: Anastasia BOBYLEVA
+// Copyright (c) 2017-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <AIS_ViewCube.hxx>
+
+#include <AIS_AnimationCamera.hxx>
+#include <AIS_InteractiveContext.hxx>
+#include <gp_Ax2.hxx>
+#include <Graphic3d_ViewAffinity.hxx>
+#include <NCollection_Lerp.hxx>
+#include <Prs3d.hxx>
+#include <Prs3d_Arrow.hxx>
+#include <Prs3d_DatumAspect.hxx>
+#include <Prs3d_Root.hxx>
+#include <Prs3d_Text.hxx>
+#include <Prs3d_ToolDisk.hxx>
+#include <Prs3d_ToolSphere.hxx>
+#include <Select3D_SensitivePrimitiveArray.hxx>
+#include <SelectMgr_SequenceOfOwner.hxx>
+#include <V3d.hxx>
+#include <V3d_View.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(AIS_ViewCube, AIS_InteractiveObject)
+IMPLEMENT_STANDARD_RTTIEXT(AIS_ViewCubeOwner, SelectMgr_EntityOwner)
+
+namespace
+{
+  static const Standard_Integer THE_NB_ROUND_SPLITS = 8;
+  static const Standard_Integer THE_NB_DISK_SLICES = 20;
+  static const Standard_Integer THE_NB_ARROW_FACETTES = 20;
+
+  //! Return the number of non-zero components.
+  static Standard_Integer nbDirectionComponents (const gp_Dir& theDir)
+  {
+    Standard_Integer aNbComps = 0;
+    for (Standard_Integer aCompIter = 1; aCompIter <= 3; ++aCompIter)
+    {
+      if (Abs (theDir.Coord (aCompIter)) > gp::Resolution())
+      {
+        ++aNbComps;
+      }
+    }
+    return aNbComps;
+  }
+}
+
+//! Simple sensitive element for picking by point only.
+class AIS_ViewCubeSensitive : public Select3D_SensitivePrimitiveArray
+{
+  DEFINE_STANDARD_RTTI_INLINE(AIS_ViewCubeSensitive, Select3D_SensitivePrimitiveArray)
+public:
+  //! Constructor.
+  AIS_ViewCubeSensitive (const Handle(SelectMgr_EntityOwner)& theOwner,
+                         const Handle(Graphic3d_ArrayOfTriangles)& theTris)
+  : Select3D_SensitivePrimitiveArray (theOwner)
+  {
+    InitTriangulation (theTris->Attributes(), theTris->Indices(), TopLoc_Location());
+  }
+
+  //! Checks whether element overlaps current selecting volume.
+  virtual Standard_Boolean Matches (SelectBasics_SelectingVolumeManager& theMgr,
+                                    SelectBasics_PickResult& thePickResult) Standard_OVERRIDE
+  {
+    return isValidRay (theMgr)
+        && Select3D_SensitivePrimitiveArray::Matches (theMgr, thePickResult);
+  }
+
+  //! Checks if picking ray can be used for detection.
+  bool isValidRay (const SelectBasics_SelectingVolumeManager& theMgr) const
+  {
+    if (theMgr.GetActiveSelectionType() != SelectBasics_SelectingVolumeManager::Point)
+    {
+      // disallow rectangular selection
+      return false;
+    }
+
+    if (AIS_ViewCubeOwner* anOwner = dynamic_cast<AIS_ViewCubeOwner* >(myOwnerId.get()))
+    {
+      const Standard_Real anAngleToler = 10.0 * M_PI / 180.0;
+      const gp_Vec aRay (theMgr.GetNearPickedPnt(), theMgr.GetFarPickedPnt());
+      const gp_Dir aDir = V3d::GetProjAxis (anOwner->MainOrientation());
+      return !aRay.IsNormal (aDir, anAngleToler);
+    }
+    return true;
+  }
+};
+
+//=======================================================================
+//function : IsBoxSide
+//purpose  :
+//=======================================================================
+bool AIS_ViewCube::IsBoxSide (V3d_TypeOfOrientation theOrient)
+{
+  return nbDirectionComponents (V3d::GetProjAxis (theOrient)) == 1;
+}
+
+//=======================================================================
+//function : IsBoxEdge
+//purpose  :
+//=======================================================================
+bool AIS_ViewCube::IsBoxEdge (V3d_TypeOfOrientation theOrient)
+{
+  return nbDirectionComponents (V3d::GetProjAxis (theOrient)) == 2;
+}
+
+//=======================================================================
+//function : IsBoxCorner
+//purpose  :
+//=======================================================================
+bool AIS_ViewCube::IsBoxCorner (V3d_TypeOfOrientation theOrient)
+{
+  return nbDirectionComponents (V3d::GetProjAxis (theOrient)) == 3;
+}
+
+//=======================================================================
+//function : AIS_ViewCube
+//purpose  :
+//=======================================================================
+AIS_ViewCube::AIS_ViewCube()
+: myBoxEdgeAspect (new Prs3d_ShadingAspect()),
+  myBoxCornerAspect (new Prs3d_ShadingAspect()),
+  mySize (1.0),
+  myBoxEdgeMinSize (2.0),
+  myBoxEdgeGap (0.0),
+  myBoxFacetExtension (1.0),
+  myAxesPadding (1.0),
+  myCornerMinSize (2.0),
+  myRoundRadius  (0.0),
+  myToDisplayAxes (true),
+  myToDisplayEdges (true),
+  myToDisplayVertices (true),
+  myIsYup (false),
+  myViewAnimation (new AIS_AnimationCamera ("AIS_ViewCube", Handle(V3d_View)())),
+  myStartState(new Graphic3d_Camera()),
+  myEndState  (new Graphic3d_Camera()),
+  myDuration (0.5),
+  myToAutoStartAnim (true),
+  myIsFixedAnimation (true),
+  myToFitSelected (true),
+  myToResetCameraUp (false)
+{
+  myInfiniteState = true;
+  myIsMutable = true;
+  myDrawer->SetZLayer (Graphic3d_ZLayerId_Topmost);
+  myTransformPersistence = new Graphic3d_TransformPers (Graphic3d_TMF_TriedronPers, Aspect_TOTP_LEFT_LOWER, Graphic3d_Vec2i (100, 100));
+
+  myDrawer->SetTextAspect  (new Prs3d_TextAspect());
+  myDrawer->SetShadingAspect (new Prs3d_ShadingAspect());
+
+  myDynHilightDrawer = new Prs3d_Drawer();
+  myDynHilightDrawer->SetLink (myDrawer);
+  myDynHilightDrawer->SetShadingAspect (new Prs3d_ShadingAspect());
+
+  setDefaultAttributes();
+  setDefaultHighlightAttributes();
+
+  // setup default labels
+  myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Front,  "FRONT");
+  myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Back,   "BACK");
+  myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Top,    "TOP");
+  myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Bottom, "BOTTOM");
+  myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Left,   "LEFT");
+  myBoxSideLabels.Bind (V3d_TypeOfOrientation_Zup_Right,  "RIGHT");
+
+  myAxesLabels.Bind (Prs3d_DP_XAxis, "X");
+  myAxesLabels.Bind (Prs3d_DP_YAxis, "Y");
+  myAxesLabels.Bind (Prs3d_DP_ZAxis, "Z");
+
+  // define default size
+  SetSize (70.0);
+}
+
+//=======================================================================
+//function : setDefaultAttributes
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::setDefaultAttributes()
+{
+  myDrawer->TextAspect()->SetHorizontalJustification(Graphic3d_HTA_CENTER);
+  myDrawer->TextAspect()->SetVerticalJustification  (Graphic3d_VTA_CENTER);
+  myDrawer->TextAspect()->SetColor (Quantity_NOC_BLACK);
+  myDrawer->TextAspect()->SetFont (Font_NOF_SANS_SERIF);
+  myDrawer->TextAspect()->SetHeight (16.0);
+  // this should be forced back-face culling regardless Closed flag
+  myDrawer->TextAspect()->Aspect()->SetSuppressBackFaces (true);
+
+  Graphic3d_MaterialAspect aMat (Graphic3d_NOM_UserDefined);
+  aMat.SetColor (Quantity_NOC_WHITE);
+  aMat.SetAmbientColor (Quantity_NOC_GRAY60);
+
+  const Handle(Graphic3d_AspectFillArea3d)& aShading = myDrawer->ShadingAspect()->Aspect();
+  aShading->SetInteriorStyle (Aspect_IS_SOLID);
+  // this should be forced back-face culling regardless Closed flag
+  aShading->SetSuppressBackFaces (true);
+  aShading->SetInteriorColor (aMat.Color());
+  aShading->SetFrontMaterial (aMat);
+  myDrawer->SetFaceBoundaryDraw (false);
+
+  *myBoxEdgeAspect  ->Aspect() = *aShading;
+  myBoxEdgeAspect->SetColor (Quantity_NOC_GRAY30);
+  *myBoxCornerAspect->Aspect() = *aShading;
+  myBoxCornerAspect->SetColor (Quantity_NOC_GRAY30);
+}
+
+//=======================================================================
+//function : setDefaultHighlightAttributes
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::setDefaultHighlightAttributes()
+{
+  Graphic3d_MaterialAspect aHighlightMaterial;
+  aHighlightMaterial.SetReflectionModeOff (Graphic3d_TOR_AMBIENT);
+  aHighlightMaterial.SetReflectionModeOff (Graphic3d_TOR_DIFFUSE);
+  aHighlightMaterial.SetReflectionModeOff (Graphic3d_TOR_SPECULAR);
+  aHighlightMaterial.SetReflectionModeOff (Graphic3d_TOR_EMISSION);
+  aHighlightMaterial.SetMaterialType (Graphic3d_MATERIAL_ASPECT);
+  myDynHilightDrawer->SetShadingAspect (new Prs3d_ShadingAspect());
+  myDynHilightDrawer->ShadingAspect()->SetMaterial (aHighlightMaterial);
+  myDynHilightDrawer->ShadingAspect()->SetColor (Quantity_NOC_CYAN1);
+  myDynHilightDrawer->SetZLayer (Graphic3d_ZLayerId_Topmost);
+  myDynHilightDrawer->SetColor (Quantity_NOC_CYAN1);
+}
+
+//=======================================================================
+//function : SetYup
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::SetYup (Standard_Boolean theIsYup,
+                           Standard_Boolean theToUpdateLabels)
+{
+  if (myIsYup == theIsYup)
+  {
+    return;
+  }
+
+  myIsYup = theIsYup;
+
+  static const V3d_TypeOfOrientation THE_ZUP_ORI_LIST[6] =
+  {
+    V3d_TypeOfOrientation_Zup_Front, V3d_TypeOfOrientation_Zup_Back,
+    V3d_TypeOfOrientation_Zup_Top,   V3d_TypeOfOrientation_Zup_Bottom,
+    V3d_TypeOfOrientation_Zup_Left,  V3d_TypeOfOrientation_Zup_Right
+  };
+  static const V3d_TypeOfOrientation THE_YUP_ORI_LIST[6] =
+  {
+    V3d_TypeOfOrientation_Yup_Front, V3d_TypeOfOrientation_Yup_Back,
+    V3d_TypeOfOrientation_Yup_Top,   V3d_TypeOfOrientation_Yup_Bottom,
+    V3d_TypeOfOrientation_Yup_Left,  V3d_TypeOfOrientation_Yup_Right
+  };
+  if (theToUpdateLabels)
+  {
+    NCollection_Array1<TCollection_AsciiString> aLabels (0, 5);
+    for (Standard_Integer aLabelIter = 0; aLabelIter < 6; ++aLabelIter)
+    {
+      myBoxSideLabels.Find (!myIsYup ? THE_YUP_ORI_LIST[aLabelIter] : THE_ZUP_ORI_LIST[aLabelIter],
+                            aLabels.ChangeValue (aLabelIter));
+    }
+    for (Standard_Integer aLabelIter = 0; aLabelIter < 6; ++aLabelIter)
+    {
+      myBoxSideLabels.Bind (myIsYup ? THE_YUP_ORI_LIST[aLabelIter] : THE_ZUP_ORI_LIST[aLabelIter],
+                            aLabels.Value (aLabelIter));
+    }
+  }
+
+  SetToUpdate();
+}
+
+//=======================================================================
+//function : ResetStyles
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::ResetStyles()
+{
+  UnsetAttributes();
+  UnsetHilightAttributes();
+
+  myBoxEdgeMinSize = 2.0;
+  myCornerMinSize  = 2.0;
+  myBoxEdgeGap     = 0.0;
+  myRoundRadius    = 0.0;
+
+  myToDisplayAxes = true;
+  myToDisplayEdges = true;
+  myToDisplayVertices = true;
+
+  myBoxFacetExtension = 1.0;
+  myAxesPadding = 1.0;
+  SetSize (70.0);
+}
+
+//=======================================================================
+//function : SetSize
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::SetSize (Standard_Real theValue,
+                            Standard_Boolean theToAdaptAnother)
+{
+  const bool isNewSize = Abs (mySize - theValue) > Precision::Confusion();
+  mySize = theValue;
+  if (theToAdaptAnother)
+  {
+    if (myBoxFacetExtension > 0.0)
+    {
+      SetBoxFacetExtension (mySize * 0.15);
+    }
+    if (myAxesPadding > 0.0)
+    {
+      SetAxesPadding (mySize * 0.1);
+    }
+    SetFontHeight (mySize * 0.16);
+  }
+  if (isNewSize)
+  {
+    SetToUpdate();
+  }
+}
+
+//=======================================================================
+//function : SetRoundRadius
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::SetRoundRadius (const Standard_Real theValue)
+{
+  Standard_OutOfRange_Raise_if (theValue < 0.0 || theValue > 0.5,
+                                "AIS_ViewCube::SetRoundRadius(): theValue should be in [0; 0.5]");
+  if (Abs (myRoundRadius - theValue) > Precision::Confusion())
+  {
+    myRoundRadius = theValue;
+    SetToUpdate();
+  }
+}
+
+//=======================================================================
+//function : createRoundRectangleTriangles
+//purpose  :
+//=======================================================================
+Handle(Graphic3d_ArrayOfTriangles) AIS_ViewCube::createRoundRectangleTriangles (const gp_XY& theSize,
+                                                                                Standard_Real theRadius,
+                                                                                const gp_Trsf& theTrsf)
+{
+  const Standard_Real aRadius = Min (theRadius, Min (theSize.X(), theSize.Y()) * 0.5);
+  const gp_XY  aHSize (theSize.X() * 0.5 - aRadius, theSize.Y() * 0.5 - aRadius);
+  const gp_Dir aNorm = gp::DZ().Transformed (theTrsf);
+  Handle(Graphic3d_ArrayOfTriangles) aTris;
+  if (aRadius > 0.0)
+  {
+    const Standard_Integer aNbNodes = (THE_NB_ROUND_SPLITS + 1) * 4 + 1;
+    aTris = new Graphic3d_ArrayOfTriangles (aNbNodes, aNbNodes * 3, Graphic3d_ArrayFlags_VertexNormal);
+
+    aTris->AddVertex (gp_Pnt (0.0, 0.0, 0.0).Transformed (theTrsf));
+    for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
+    {
+      const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (M_PI * 0.5, 0.0, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
+      aTris->AddVertex (gp_Pnt (aHSize.X() + aRadius * Cos (anAngle), aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
+    }
+    for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
+    {
+      const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (0.0, -M_PI * 0.5, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
+      aTris->AddVertex (gp_Pnt (aHSize.X() + aRadius * Cos (anAngle), -aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
+    }
+    for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
+    {
+      const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (-M_PI * 0.5, -M_PI, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
+      aTris->AddVertex (gp_Pnt (-aHSize.X() + aRadius * Cos (anAngle), -aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
+    }
+    for (Standard_Integer aNodeIter = 0; aNodeIter <= THE_NB_ROUND_SPLITS; ++aNodeIter)
+    {
+      const Standard_Real anAngle = NCollection_Lerp<Standard_Real>::Interpolate (-M_PI, -M_PI * 1.5, Standard_Real(aNodeIter) / Standard_Real(THE_NB_ROUND_SPLITS));
+      aTris->AddVertex (gp_Pnt (-aHSize.X() + aRadius * Cos (anAngle), aHSize.Y() + aRadius * Sin (anAngle), 0.0).Transformed (theTrsf));
+    }
+
+    // split triangle fan
+    for (Standard_Integer aNodeIter = 2; aNodeIter <= aTris->VertexNumber(); ++aNodeIter)
+    {
+      aTris->AddEdge (1);
+      aTris->AddEdge (aNodeIter - 1);
+      aTris->AddEdge (aNodeIter);
+    }
+    aTris->AddEdge (1);
+    aTris->AddEdge (aTris->VertexNumber());
+    aTris->AddEdge (2);
+  }
+  else
+  {
+    aTris = new Graphic3d_ArrayOfTriangles (4, 6, Graphic3d_ArrayFlags_VertexNormal);
+    aTris->AddVertex (gp_Pnt (-aHSize.X(), -aHSize.Y(), 0.0).Transformed (theTrsf));
+    aTris->AddVertex (gp_Pnt (-aHSize.X(),  aHSize.Y(), 0.0).Transformed (theTrsf));
+    aTris->AddVertex (gp_Pnt ( aHSize.X(),  aHSize.Y(), 0.0).Transformed (theTrsf));
+    aTris->AddVertex (gp_Pnt ( aHSize.X(), -aHSize.Y(), 0.0).Transformed (theTrsf));
+    aTris->AddEdges (3, 1, 2);
+    aTris->AddEdges (1, 3, 4);
+  }
+
+  for (Standard_Integer aVertIter = 1; aVertIter <= aTris->VertexNumber(); ++aVertIter)
+  {
+    aTris->SetVertexNormal (aVertIter, -aNorm);
+  }
+  return aTris;
+}
+
+//=======================================================================
+//function : createBoxPartTriangles
+//purpose  :
+//=======================================================================
+Handle(Graphic3d_ArrayOfTriangles) AIS_ViewCube::createBoxPartTriangles (V3d_TypeOfOrientation theDir) const
+{
+  if (IsBoxSide (theDir))
+  {
+    return createBoxSideTriangles (theDir);
+  }
+  else if (IsBoxEdge (theDir)
+        && myToDisplayEdges)
+  {
+    return createBoxEdgeTriangles (theDir);
+  }
+  else if (IsBoxCorner (theDir)
+        && myToDisplayVertices)
+  {
+    return createBoxCornerTriangles (theDir);
+  }
+  return Handle(Graphic3d_ArrayOfTriangles)();
+}
+
+//=======================================================================
+//function : createBoxSideTriangles
+//purpose  :
+//=======================================================================
+Handle(Graphic3d_ArrayOfTriangles) AIS_ViewCube::createBoxSideTriangles (V3d_TypeOfOrientation theDirection) const
+{
+  const gp_Dir aDir = V3d::GetProjAxis (theDirection);
+  const gp_Pnt aPos = aDir.XYZ() * (mySize * 0.5 + myBoxFacetExtension);
+  const gp_Ax2 aPosition (aPos, aDir.Reversed());
+
+  gp_Ax3 aSystem (aPosition);
+  gp_Trsf aTrsf;
+  aTrsf.SetTransformation (aSystem, gp_Ax3());
+
+  return createRoundRectangleTriangles (gp_XY (mySize, mySize), myRoundRadius * mySize, aTrsf);
+}
+
+//=======================================================================
+//function : createBoxEdgeTriangles
+//purpose  :
+//=======================================================================
+Handle(Graphic3d_ArrayOfTriangles) AIS_ViewCube::createBoxEdgeTriangles (V3d_TypeOfOrientation theDirection) const
+{
+  const Standard_Real aThickness = Max (myBoxFacetExtension * gp_XY (1.0, 1.0).Modulus() - myBoxEdgeGap, myBoxEdgeMinSize);
+
+  const gp_Dir aDir = V3d::GetProjAxis (theDirection);
+  const gp_Pnt aPos = aDir.XYZ() * (mySize * 0.5 * gp_XY (1.0, 1.0).Modulus() + myBoxFacetExtension * Cos (M_PI_4));
+  const gp_Ax2 aPosition (aPos, aDir.Reversed());
+
+  gp_Ax3 aSystem (aPosition);
+  gp_Trsf aTrsf;
+  aTrsf.SetTransformation (aSystem, gp_Ax3());
+
+  return createRoundRectangleTriangles (gp_XY (aThickness, mySize), myRoundRadius * mySize, aTrsf);
+}
+
+//=======================================================================
+//function : createBoxCornerTriangles
+//purpose  :
+//=======================================================================
+Handle(Graphic3d_ArrayOfTriangles) AIS_ViewCube::createBoxCornerTriangles (V3d_TypeOfOrientation theDir) const
+{
+  const Standard_Real aHSize = mySize * 0.5;
+  const gp_Dir aDir = V3d::GetProjAxis (theDir);
+  const gp_XYZ aHSizeDir = aDir.XYZ() * (aHSize * gp_Vec (1.0, 1.0, 1.0).Magnitude());
+  if (myRoundRadius > 0.0)
+  {
+    const Standard_Real anEdgeHWidth = myBoxFacetExtension * gp_XY (1.0, 1.0).Modulus() * 0.5;
+    const Standard_Real aHeight = anEdgeHWidth * Sqrt (2.0 / 3.0); // tetrahedron height
+    const gp_Pnt aPos = aDir.XYZ() * (aHSize * gp_Vec (1.0, 1.0, 1.0).Magnitude() + aHeight);
+    const gp_Ax2 aPosition (aPos, aDir.Reversed());
+    gp_Ax3 aSystem (aPosition);
+    gp_Trsf aTrsf;
+    aTrsf.SetTransformation (aSystem, gp_Ax3());
+    const Standard_Real aRadius = Max (myBoxFacetExtension * 0.5 / Cos (M_PI_4), myCornerMinSize);
+    return Prs3d_ToolDisk::Create (0.0, aRadius, THE_NB_DISK_SLICES, 1, aTrsf);
+  }
+
+  Handle(Graphic3d_ArrayOfTriangles) aTris = new Graphic3d_ArrayOfTriangles (3, 3, Graphic3d_ArrayFlags_VertexNormal);
+
+  aTris->AddVertex (aHSizeDir + myBoxFacetExtension * gp_Dir (aDir.X(), 0.0, 0.0).XYZ());
+  aTris->AddVertex (aHSizeDir + myBoxFacetExtension * gp_Dir (0.0, aDir.Y(), 0.0).XYZ());
+  aTris->AddVertex (aHSizeDir + myBoxFacetExtension * gp_Dir (0.0, 0.0, aDir.Z()).XYZ());
+
+  const gp_XYZ aNode1 = aTris->Vertice (1).XYZ();
+  const gp_XYZ aNode2 = aTris->Vertice (2).XYZ();
+  const gp_XYZ aNode3 = aTris->Vertice (3).XYZ();
+  const gp_XYZ aNormTri = ((aNode2 - aNode1).Crossed (aNode3 - aNode1));
+  if (aNormTri.Dot (aDir.XYZ()) < 0.0)
+  {
+    aTris->AddEdges (1, 3, 2);
+  }
+  else
+  {
+    aTris->AddEdges (1, 2, 3);
+  }
+
+  for (Standard_Integer aVertIter = 1; aVertIter <= aTris->VertexNumber(); ++aVertIter)
+  {
+    aTris->SetVertexNormal (aVertIter, aDir);
+  }
+  return aTris;
+}
+
+//=======================================================================
+//function : Compute
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::Compute (const Handle(PrsMgr_PresentationManager3d)& ,
+                            const Handle(Prs3d_Presentation)& thePrs,
+                            const Standard_Integer theMode)
+{
+  thePrs->SetInfiniteState (true);
+  if (theMode != 0)
+  {
+    return;
+  }
+
+  const gp_Pnt aLocation = (mySize * 0.5 + myBoxFacetExtension + myAxesPadding) * gp_XYZ (-1.0, -1.0, -1.0);
+
+  // Display axes
+  if (myToDisplayAxes)
+  {
+    const Standard_Real anAxisSize = mySize + 2.0 * myBoxFacetExtension + myAxesPadding;
+    const Handle(Prs3d_DatumAspect)& aDatumAspect = myDrawer->DatumAspect();
+    for (Standard_Integer anAxisIter = Prs3d_DP_XAxis; anAxisIter <= Prs3d_DP_ZAxis; ++anAxisIter)
+    {
+      const Prs3d_DatumParts aPart = (Prs3d_DatumParts )anAxisIter;
+      if (!aDatumAspect->DrawDatumPart (aPart))
+      {
+        continue;
+      }
+
+      gp_Ax1 anAx1;
+      switch (aPart)
+      {
+        case Prs3d_DP_XAxis: anAx1 = gp_Ax1 (aLocation, gp::DX()); break;
+        case Prs3d_DP_YAxis: anAx1 = gp_Ax1 (aLocation, gp::DY()); break;
+        case Prs3d_DP_ZAxis: anAx1 = gp_Ax1 (aLocation, gp::DZ()); break;
+        default: break;
+      }
+
+      Handle(Graphic3d_Group) anAxisGroup = thePrs->NewGroup();
+      anAxisGroup->SetGroupPrimitivesAspect (aDatumAspect->ShadingAspect (aPart)->Aspect());
+
+      const Standard_Real anArrowLength = 0.2 * anAxisSize;
+      Handle(Graphic3d_ArrayOfTriangles) aTriangleArray = Prs3d_Arrow::DrawShaded (anAx1, 1.0, anAxisSize, 3.0, anArrowLength, THE_NB_ARROW_FACETTES);
+      anAxisGroup->AddPrimitiveArray (aTriangleArray);
+
+      TCollection_AsciiString anAxisLabel;
+      if (aDatumAspect->ToDrawLabels()
+      &&  myAxesLabels.Find (aPart, anAxisLabel)
+      && !anAxisLabel.IsEmpty())
+      {
+        Handle(Graphic3d_Group) anAxisLabelGroup = thePrs->NewGroup();
+        gp_Pnt aTextOrigin = anAx1.Location().Translated (gp_Vec (anAx1.Direction().X() * (anAxisSize + anArrowLength),
+                                                                  anAx1.Direction().Y() * (anAxisSize + anArrowLength),
+                                                                  anAx1.Direction().Z() * (anAxisSize + anArrowLength)));
+        Prs3d_Text::Draw (anAxisLabelGroup, aDatumAspect->TextAspect(), TCollection_ExtendedString (anAxisLabel), aTextOrigin);
+      }
+    }
+
+    // Display center
+    {
+      Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
+      Handle(Prs3d_ShadingAspect) anAspectCen = new Prs3d_ShadingAspect();
+      anAspectCen->SetColor (Quantity_NOC_WHITE);
+      aGroup->SetGroupPrimitivesAspect (anAspectCen->Aspect());
+      Prs3d_ToolSphere aTool (4.0, THE_NB_DISK_SLICES, THE_NB_DISK_SLICES);
+      gp_Trsf aTrsf;
+      aTrsf.SetTranslation (gp_Vec (gp::Origin(), aLocation));
+      Handle(Graphic3d_ArrayOfTriangles) aCenterArray;
+      aTool.FillArray (aCenterArray, aTrsf);
+      aGroup->AddPrimitiveArray (aCenterArray);
+    }
+  }
+
+  // Display box
+  {
+    Handle(Graphic3d_Group) aGroupSides = thePrs->NewGroup(), aGroupEdges = thePrs->NewGroup(), aGroupCorners = thePrs->NewGroup();
+    aGroupSides->SetClosed (true); // should be replaced by forced back-face culling aspect
+    aGroupSides->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
+
+    aGroupEdges->SetClosed (true);
+    aGroupEdges->SetGroupPrimitivesAspect (myBoxEdgeAspect->Aspect());
+
+    aGroupCorners->SetClosed (true);
+    aGroupCorners->SetGroupPrimitivesAspect (myBoxCornerAspect->Aspect());
+
+    Handle(Graphic3d_Group) aTextGroup = thePrs->NewGroup();
+    //aTextGroup->SetClosed (true);
+    aTextGroup->SetGroupPrimitivesAspect (myDrawer->TextAspect()->Aspect());
+    for (Standard_Integer aPartIter = 0; aPartIter <= Standard_Integer(V3d_XnegYnegZneg); ++aPartIter)
+    {
+      const V3d_TypeOfOrientation anOrient = (V3d_TypeOfOrientation )aPartIter;
+      if (Handle(Graphic3d_ArrayOfTriangles) aTris = createBoxPartTriangles (anOrient))
+      {
+        if (IsBoxSide (anOrient))
+        {
+          aGroupSides->AddPrimitiveArray (aTris);
+
+          TCollection_AsciiString aLabel;
+          if (!myBoxSideLabels.Find (anOrient, aLabel)
+            || aLabel.IsEmpty())
+          {
+            continue;
+          }
+
+          const gp_Dir aDir = V3d::GetProjAxis (anOrient);
+          gp_Dir anUp = myIsYup ? gp::DY() : gp::DZ();
+          if (myIsYup)
+          {
+            if (anOrient == V3d_Ypos
+             || anOrient == V3d_Yneg)
+            {
+              anUp = -gp::DZ();
+            }
+          }
+          else
+          {
+            if (anOrient == V3d_Zpos)
+            {
+              anUp = gp::DY();
+            }
+            else if (anOrient == V3d_Zneg)
+            {
+              anUp = -gp::DY();
+            }
+          }
+
+          const Standard_Real anOffset = 2.0; // extra offset to avoid overlapping with triangulation
+          const gp_Pnt aPos = aDir.XYZ() * (mySize * 0.5 + myBoxFacetExtension + anOffset);
+          const gp_Ax2 aPosition (aPos, aDir, anUp.Crossed (aDir));
+          Prs3d_Text::Draw (aTextGroup, myDrawer->TextAspect(), aLabel, aPosition);
+        }
+        else if (IsBoxEdge (anOrient))
+        {
+          aGroupEdges->AddPrimitiveArray (aTris);
+        }
+        else if (IsBoxCorner (anOrient))
+        {
+          aGroupCorners->AddPrimitiveArray (aTris);
+        }
+      }
+    }
+  }
+}
+
+//=======================================================================
+//function : ComputeSelection
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::ComputeSelection (const Handle(SelectMgr_Selection)& theSelection,
+                                     const Standard_Integer theMode)
+{
+  if (theMode != 0)
+  {
+    return;
+  }
+
+  for (Standard_Integer aPartIter = 0; aPartIter <= Standard_Integer(V3d_XnegYnegZneg); ++aPartIter)
+  {
+    const V3d_TypeOfOrientation anOri = (V3d_TypeOfOrientation )aPartIter;
+    if (Handle(Graphic3d_ArrayOfTriangles) aTris = createBoxPartTriangles (anOri))
+    {
+      Standard_Integer aSensitivity = 2;
+      if (IsBoxCorner (anOri))
+      {
+        aSensitivity = 8;
+      }
+      else if (IsBoxEdge (anOri))
+      {
+        aSensitivity = 4;
+      }
+      Handle(AIS_ViewCubeOwner) anOwner = new AIS_ViewCubeOwner (this, anOri);
+      Handle(AIS_ViewCubeSensitive) aTriSens = new AIS_ViewCubeSensitive (anOwner, aTris);
+      aTriSens->SetSensitivityFactor (aSensitivity);
+      theSelection->Add (aTriSens);
+    }
+  }
+}
+
+//=======================================================================
+//function : HasAnimation
+//purpose  :
+//=======================================================================
+Standard_Boolean AIS_ViewCube::HasAnimation() const
+{
+  return !myViewAnimation->IsStopped();
+}
+
+//=======================================================================
+//function : StartAnimation
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::StartAnimation (const Handle(AIS_ViewCubeOwner)& theOwner)
+{
+  Handle(V3d_View) aView = GetContext()->LastActiveView();
+  if (theOwner.IsNull()
+   || aView.IsNull())
+  {
+    return;
+  }
+
+  myStartState->Copy (aView->Camera());
+  myEndState  ->Copy (aView->Camera());
+
+  {
+    Handle(Graphic3d_Camera) aBackupCamera = new Graphic3d_Camera (aView->Camera());
+
+    const bool wasImmediateUpdate = aView->SetImmediateUpdate (false);
+    aView->SetCamera (myEndState);
+    aView->SetProj (theOwner->MainOrientation(), myIsYup);
+
+    const gp_Dir aNewDir = aView->Camera()->Direction();
+    if (!myToResetCameraUp
+     && !aNewDir.IsEqual (aBackupCamera->Direction(), Precision::Angular()))
+    {
+      // find the Up direction closest to current instead of default one
+      const gp_Ax1 aNewDirAx1 (gp::Origin(), aNewDir);
+      const gp_Dir anOldUp = aBackupCamera->Up();
+      const gp_Dir anUpList[4] =
+      {
+        aView->Camera()->Up(),
+        aView->Camera()->Up().Rotated (aNewDirAx1, M_PI_2),
+        aView->Camera()->Up().Rotated (aNewDirAx1, M_PI),
+        aView->Camera()->Up().Rotated (aNewDirAx1, M_PI * 1.5),
+      };
+
+      Standard_Real aBestAngle = Precision::Infinite();
+      gp_Dir anUpBest;
+      for (Standard_Integer anUpIter = 0; anUpIter < 4; ++anUpIter)
+      {
+        Standard_Real anAngle = anUpList[anUpIter].Angle (anOldUp);
+        if (aBestAngle > anAngle)
+        {
+          aBestAngle = anAngle;
+          anUpBest = anUpList[anUpIter];
+        }
+      }
+      aView->Camera()->SetUp (anUpBest);
+    }
+
+    const Bnd_Box aBndSelected = myToFitSelected ? GetContext()->BoundingBoxOfSelection() : Bnd_Box();
+    if (!aBndSelected.IsVoid())
+    {
+      aView->FitAll (aBndSelected, 0.01, false);
+    }
+    else
+    {
+      aView->FitAll (0.01, false);
+    }
+    aView->SetCamera (aBackupCamera);
+    aView->SetImmediateUpdate (wasImmediateUpdate);
+  }
+
+  myViewAnimation->SetView (aView);
+  myViewAnimation->SetCameraStart (myStartState);
+  myViewAnimation->SetCameraEnd   (myEndState);
+  myViewAnimation->SetOwnDuration (myDuration);
+  myViewAnimation->StartTimer (0.0, 1.0, true, false);
+}
+
+//=======================================================================
+//function : updateAnimation
+//purpose  :
+//=======================================================================
+Standard_Boolean AIS_ViewCube::updateAnimation()
+{
+  const Standard_Real aPts = myViewAnimation->UpdateTimer();
+  if (aPts >= myDuration)
+  {
+    myViewAnimation->Stop();
+    onAnimationFinished();
+    myViewAnimation->SetView (Handle(V3d_View)());
+    return Standard_False;
+  }
+  return Standard_True;
+}
+
+//=======================================================================
+//function : UpdateAnimation
+//purpose  :
+//=======================================================================
+Standard_Boolean AIS_ViewCube::UpdateAnimation (const Standard_Boolean theToUpdate)
+{
+  Handle(V3d_View) aView = myViewAnimation->View();
+  if (!HasAnimation()
+   || !updateAnimation())
+  {
+    return Standard_False;
+  }
+
+  if (theToUpdate
+  && !aView.IsNull())
+  {
+    aView->IsInvalidated() ? aView->Redraw() : aView->RedrawImmediate();
+  }
+
+  onAfterAnimation();
+  return Standard_True;
+}
+
+//=======================================================================
+//function : HandleClick
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::HandleClick (const Handle(AIS_ViewCubeOwner)& theOwner)
+{
+  if (!myToAutoStartAnim)
+  {
+    return;
+  }
+
+  StartAnimation (theOwner);
+  if (!myIsFixedAnimation)
+  {
+    return;
+  }
+  for (; HasAnimation(); )
+  {
+    UpdateAnimation (true);
+  }
+}
+
+//=======================================================================
+//function : HilightOwnerWithColor
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::HilightOwnerWithColor (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr,
+                                          const Handle(Prs3d_Drawer)& theStyle,
+                                          const Handle(SelectMgr_EntityOwner)& theOwner)
+{
+  if (theOwner.IsNull()
+  || !thePrsMgr->IsImmediateModeOn())
+  {
+    return;
+  }
+
+  const Graphic3d_ZLayerId aLayer = theStyle->ZLayer() != Graphic3d_ZLayerId_UNKNOWN ? theStyle->ZLayer() : myDrawer->ZLayer();
+  const AIS_ViewCubeOwner* aCubeOwner = dynamic_cast<AIS_ViewCubeOwner* >(theOwner.get());
+
+  Handle(Prs3d_Presentation) aHiPrs = GetHilightPresentation (thePrsMgr);
+  aHiPrs->Clear();
+  aHiPrs->CStructure()->ViewAffinity = thePrsMgr->StructureManager()->ObjectAffinity (Handle(Standard_Transient)(this));
+  aHiPrs->SetTransformPersistence (TransformPersistence());
+  aHiPrs->SetZLayer (aLayer);
+
+  {
+    Handle(Graphic3d_Group) aGroup = aHiPrs->NewGroup();
+    aGroup->SetGroupPrimitivesAspect (theStyle->ShadingAspect()->Aspect());
+    if (Handle(Graphic3d_ArrayOfTriangles) aTris = createBoxPartTriangles (aCubeOwner->MainOrientation()))
+    {
+      aGroup->AddPrimitiveArray (aTris);
+    }
+  }
+
+  if (thePrsMgr->IsImmediateModeOn())
+  {
+    thePrsMgr->AddToImmediateList (aHiPrs);
+  }
+}
+
+//=======================================================================
+//function : HilightSelected
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::HilightSelected (const Handle(PrsMgr_PresentationManager3d)& ,
+                                    const SelectMgr_SequenceOfOwner& theSeq)
+{
+  // this method should never be called since AIS_InteractiveObject::HandleClick() has been overridden
+  if (theSeq.Size() == 1)
+  {
+    //HandleClick (Handle(AIS_ViewCubeOwner)::DownCast (theSeq.First()));
+  }
+}
diff --git a/src/AIS/AIS_ViewCube.hxx b/src/AIS/AIS_ViewCube.hxx
new file mode 100644 (file)
index 0000000..7222c89
--- /dev/null
@@ -0,0 +1,645 @@
+// Created on: 2017-07-25
+// Created by: Anastasia BOBYLEVA
+// Copyright (c) 2017-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _AIS_ViewCube_HeaderFile
+#define _AIS_ViewCube_HeaderFile
+
+#include <AIS_InteractiveObject.hxx>
+#include <Graphic3d_Camera.hxx>
+#include <Graphic3d_Vec2.hxx>
+#include <Prs3d_DatumParts.hxx>
+#include <Prs3d_ShadingAspect.hxx>
+#include <Prs3d_TextAspect.hxx>
+#include <SelectMgr_EntityOwner.hxx>
+#include <V3d_TypeOfOrientation.hxx>
+
+class AIS_AnimationCamera;
+class AIS_ViewCubeOwner;
+class Graphic3d_ArrayOfTriangles;
+
+//! Interactive object for displaying the view manipulation cube.
+//!
+//! View cube consists of several parts that are responsible for different camera manipulations:
+//! @li Cube sides represent main views: top, bottom, left, right, front and back.
+//! @li Edges represent rotation of one of main views on 45 degrees.
+//! @li Vertices represent rotation of one of man views in two directions.
+//!
+//! The object is expected to behave like a trihedron in the view corner,
+//! therefore its position should be defined using transformation persistence flags:
+//! @code SetTransformPersistence (new Graphic3d_TransformPers (Graphic3d_TMF_TriedronPers, Aspect_TOTP_LEFT_LOWER, Graphic3d_Vec2i (100, 100)); @endcode
+//!
+//! View Cube parts are sensitive to detection, or dynamic highlighting (but not selection),
+//! and every its owner AIS_ViewCubeOwner corresponds to camera transformation.
+//! @code
+//!   for (aViewCube->StartAnimation (aDetectedOwner); aViewCube->HasAnimation(); )
+//!   {
+//!     aViewCube->UpdateAnimation();
+//!     ... // updating of application window
+//!   }
+//! @endcode
+//! or
+//! @code aViewCube->HandleClick (aDetectedOwner); @endcode
+//! that includes transformation loop.
+//! This loop allows external actions like application updating. For this purpose AIS_ViewCube has virtual interface onAfterAnimation(),
+//! that is to be redefined on application level.
+class AIS_ViewCube : public AIS_InteractiveObject
+{
+  DEFINE_STANDARD_RTTIEXT(AIS_ViewCube, AIS_InteractiveObject)
+public:
+
+  //! Return TRUE if specified orientation belongs to box side.
+  Standard_EXPORT static bool IsBoxSide (V3d_TypeOfOrientation theOrient);
+
+  //! Return TRUE if specified orientation belongs to box edge.
+  Standard_EXPORT static bool IsBoxEdge (V3d_TypeOfOrientation theOrient);
+
+  //! Return TRUE if specified orientation belongs to box corner (vertex).
+  Standard_EXPORT static bool IsBoxCorner (V3d_TypeOfOrientation theOrient);
+
+public:
+
+  //! Empty constructor.
+  Standard_EXPORT AIS_ViewCube();
+
+  //! Return view animation.
+  const Handle(AIS_AnimationCamera)& ViewAnimation() const { return myViewAnimation; }
+
+  //! Set view animation.
+  void SetViewAnimation (const Handle(AIS_AnimationCamera)& theAnimation) { myViewAnimation = theAnimation; }
+
+  //! Return TRUE if automatic camera transformation on selection (highlighting) is enabled; TRUE by default.
+  Standard_Boolean ToAutoStartAnimation() const { return myToAutoStartAnim; }
+
+  //! Enable/disable automatic camera transformation on selection (highlighting).
+  //! The automatic logic can be disabled if application wants performing action manually
+  //! basing on picking results (AIS_ViewCubeOwner).
+  void SetAutoStartAnimation (bool theToEnable) { myToAutoStartAnim = theToEnable; }
+
+  //! Return TRUE if camera animation should be done in uninterruptible loop; TRUE by default.
+  Standard_Boolean IsFixedAnimationLoop() const { return myIsFixedAnimation; }
+
+  //! Set if camera animation should be done in uninterruptible loop.
+  void SetFixedAnimationLoop (bool theToEnable) { myIsFixedAnimation = theToEnable; }
+
+  //! Reset all size and style parameters to default.
+  //! @warning It doesn't reset position of View Cube
+  Standard_EXPORT void ResetStyles();
+
+protected:
+
+  //! Set default visual attributes
+  Standard_EXPORT void setDefaultAttributes();
+
+  //! Set default dynamic highlight properties
+  Standard_EXPORT void setDefaultHighlightAttributes();
+
+public: //! @name Geometry management API
+
+  //! @return size (width and height) of View cube sides; 100 by default.
+  Standard_Real Size() const { return mySize; }
+
+  //! Sets size (width and height) of View cube sides.
+  //! @param theToAdaptAnother if TRUE, then other parameters will be adapted to specified size
+  Standard_EXPORT void SetSize (Standard_Real theValue,
+                                Standard_Boolean theToAdaptAnother = true);
+
+  //! Return box facet extension to edge/corner facet split; 10 by default.
+  Standard_Real BoxFacetExtension() const { return myBoxFacetExtension; }
+
+  //! Set new value of box facet extension.
+  void SetBoxFacetExtension (Standard_Real theValue)
+  {
+    if (Abs (myBoxFacetExtension - theValue) > Precision::Confusion())
+    {
+      myBoxFacetExtension = theValue;
+      SetToUpdate();
+    }
+  }
+
+  //! Return padding between axes and 3D part (box); 10 by default.
+  Standard_Real AxesPadding() const { return myAxesPadding; }
+
+  //! Set new value of padding between axes and 3D part (box).
+  void SetAxesPadding (Standard_Real theValue)
+  {
+    if (Abs (myAxesPadding - theValue) > Precision::Confusion())
+    {
+      myAxesPadding = theValue;
+      SetToUpdate();
+    }
+  }
+
+  //! Return gap between box edges and box sides; 0 by default.
+  Standard_Real BoxEdgeGap() const { return myBoxEdgeGap; }
+
+  //! Set new value of box edges gap.
+  void SetBoxEdgeGap (Standard_Real theValue)
+  {
+    if (Abs (myBoxEdgeGap - theValue) > Precision::Confusion())
+    {
+      myBoxEdgeGap = theValue;
+      SetToUpdate();
+    }
+  }
+
+  //! Return minimal size of box edge; 2 by default.
+  Standard_Real BoxEdgeMinSize() const { return myBoxEdgeMinSize; }
+
+  //! Set new value of box edge minimal size.
+  void SetBoxEdgeMinSize (Standard_Real theValue)
+  {
+    if (Abs (myBoxEdgeMinSize - theValue) > Precision::Confusion())
+    {
+      myBoxEdgeMinSize = theValue;
+      SetToUpdate();
+    }
+  }
+
+  //! Return minimal size of box corner; 2 by default.
+  Standard_Real BoxCornerMinSize() const { return myCornerMinSize; }
+
+  //! Set new value of box corner minimal size.
+  void SetBoxCornerMinSize (Standard_Real theValue)
+  {
+    if (Abs (myCornerMinSize - theValue) > Precision::Confusion())
+    {
+      myCornerMinSize = theValue;
+      SetToUpdate();
+    }
+  }
+
+  //! Return relative radius of side corners (round rectangle); 0.0 by default.
+  //! The value in within [0, 0.5] range meaning absolute radius = RoundRadius() / Size().
+  Standard_Real RoundRadius() const { return myRoundRadius; }
+
+  //! Set relative radius of View Cube sides corners (round rectangle).
+  //! The value should be within [0, 0.5] range.
+  Standard_EXPORT void SetRoundRadius (const Standard_Real theValue);
+
+  //! @return TRUE if trihedron is drawn; TRUE by default.
+  Standard_Boolean ToDrawAxes() const { return myToDisplayAxes; }
+
+  //! Enable/disable drawing of trihedron.
+  void SetDrawAxes (Standard_Boolean theValue)
+  {
+    if (myToDisplayAxes != theValue)
+    {
+      myToDisplayAxes = theValue;
+      SetToUpdate();
+    }
+  }
+
+  //! @return TRUE if edges of View Cube is drawn; TRUE by default.
+  Standard_Boolean ToDrawEdges() const { return myToDisplayEdges; }
+
+  //! Enable/disable drawing of edges of View Cube.
+  void SetDrawEdges (Standard_Boolean theValue)
+  {
+    if (myToDisplayEdges != theValue)
+    {
+      myToDisplayEdges = theValue;
+      SetToUpdate();
+    }
+  }
+
+  //! Return TRUE if vertices (vertex) of View Cube is drawn; TRUE by default.
+  Standard_Boolean ToDrawVertices() const { return myToDisplayVertices; }
+
+  //! Enable/disable drawing of vertices (corners) of View Cube.
+  void SetDrawVertices (Standard_Boolean theValue)
+  {
+    if (myToDisplayVertices != theValue)
+    {
+      myToDisplayVertices = theValue;
+      SetToUpdate();
+    }
+  }
+
+  //! Return TRUE if application expects Y-up viewer orientation instead of Z-up; FALSE by default.
+  Standard_Boolean IsYup() const { return myIsYup; }
+
+  //! Set if application expects Y-up viewer orientation instead of Z-up.
+  Standard_EXPORT void SetYup (Standard_Boolean theIsYup,
+                               Standard_Boolean theToUpdateLabels = Standard_True);
+
+public: //! @name Style management API
+
+  //! Return shading style of box sides.
+  const Handle(Prs3d_ShadingAspect)& BoxSideStyle() const { return myDrawer->ShadingAspect(); }
+
+  //! Return shading style of box edges.
+  const Handle(Prs3d_ShadingAspect)& BoxEdgeStyle() const { return myBoxEdgeAspect; }
+
+  //! Return shading style of box corners.
+  const Handle(Prs3d_ShadingAspect)& BoxCornerStyle() const { return myBoxCornerAspect; }
+
+  //! Return value of front color for the 3D part of object.
+  const Quantity_Color& BoxColor() const { return myDrawer->ShadingAspect()->Color(); }
+
+  //! Set new value of front color for the 3D part of object.
+  //! @param theColor [in] input color value.
+  void SetBoxColor (const Quantity_Color& theColor)
+  {
+    if (!myDrawer->ShadingAspect()->Color().IsEqual (theColor)
+     || !myBoxEdgeAspect  ->Color().IsEqual (theColor)
+     || !myBoxCornerAspect->Color().IsEqual (theColor))
+    {
+      myDrawer->ShadingAspect()->SetColor (theColor);
+      myBoxEdgeAspect->SetColor (theColor);
+      myBoxCornerAspect->SetColor (theColor);
+      SynchronizeAspects();
+    }
+  }
+
+  //! Return transparency for 3D part of object.
+  Standard_Real BoxTransparency() const { return myDrawer->ShadingAspect()->Transparency(); }
+
+  //! Set new value of transparency for 3D part of object.
+  //! @param theValue [in] input transparency value
+  void SetBoxTransparency (Standard_Real theValue)
+  {
+    if (Abs (myDrawer->ShadingAspect()->Transparency() - theValue) > Precision::Confusion()
+     || Abs (myBoxEdgeAspect  ->Transparency() - theValue) > Precision::Confusion()
+     || Abs (myBoxCornerAspect->Transparency() - theValue) > Precision::Confusion())
+    {
+      myDrawer->ShadingAspect()->SetTransparency (theValue);
+      myBoxEdgeAspect->SetTransparency (theValue);
+      myBoxCornerAspect->SetTransparency (theValue);
+      SynchronizeAspects();
+    }
+  }
+
+  //! Return color of sides back material.
+  const Quantity_Color& InnerColor() const { return myDrawer->ShadingAspect()->Color (Aspect_TOFM_BACK_SIDE); }
+
+  //! Set color of sides back material. Alias for:
+  //! @code Attributes()->ShadingAspect()->Aspect()->ChangeBackMaterial().SetColor() @endcode
+  void SetInnerColor (const Quantity_Color& theColor)
+  {
+    myDrawer->ShadingAspect()->SetColor (theColor, Aspect_TOFM_BACK_SIDE);
+    SynchronizeAspects();
+  }
+
+  //! Return box side label or empty string if undefined.
+  //! Default labels: FRONT, BACK, LEFT, RIGHT, TOP, BOTTOM.
+  TCollection_AsciiString BoxSideLabel (V3d_TypeOfOrientation theSide) const
+  {
+    const TCollection_AsciiString* aLabel = myBoxSideLabels.Seek (theSide);
+    return aLabel != NULL ? *aLabel : TCollection_AsciiString();
+  }
+
+  //! Set box side label.
+  void SetBoxSideLabel (const V3d_TypeOfOrientation theSide,
+                        const TCollection_AsciiString& theLabel)
+  {
+    if (!IsBoxSide (theSide))
+    {
+      throw Standard_ProgramError ("AIS_ViewCube::SetBoxSideLabel(), invalid enumeration value");
+    }
+    myBoxSideLabels.Bind (theSide, theLabel);
+    SetToUpdate();
+  }
+
+  //! Return text color of labels of box sides; BLACK by default.
+  const Quantity_Color& TextColor() const { return myDrawer->TextAspect()->Aspect()->Color(); }
+
+  //! Set color of text labels on box sides. Alias for:
+  //! @code Attributes()->TextAspect()->SetColor() @endcode
+  void SetTextColor (const Quantity_Color& theColor)
+  {
+    myDrawer->TextAspect()->SetColor (theColor);
+    SynchronizeAspects();
+  }
+
+  //! Return font name that is used for displaying of sides and axes text. Alias for:
+  //! @code Attributes()->TextAspect()->Aspect()->SetFont() @endcode
+  const TCollection_AsciiString& Font() const { return myDrawer->TextAspect()->Aspect()->Font(); }
+
+  //! Set font name that is used for displaying of sides and axes text. Alias for:
+  //! @code Attributes()->TextAspect()->SetFont() @endcode
+  void SetFont (const TCollection_AsciiString& theFont)
+  {
+    myDrawer->TextAspect()->Aspect()->SetFont (theFont);
+    SynchronizeAspects();
+  }
+
+  //! Return height of font
+  Standard_Real FontHeight() const { return myDrawer->TextAspect()->Height(); }
+
+  //! Change font height. Alias for:
+  //! @code Attributes()->TextAspect()->SetHeight() @endcode
+  void SetFontHeight (Standard_Real theValue)
+  {
+    if (Abs (myDrawer->TextAspect()->Height() - theValue) > Precision::Confusion())
+    {
+      myDrawer->TextAspect()->SetHeight (theValue);
+      SetToUpdate();
+    }
+  }
+
+  //! Return axes labels or empty string if undefined.
+  //! Default labels: X, Y, Z.
+  TCollection_AsciiString AxisLabel (Prs3d_DatumParts theAxis) const
+  {
+    const TCollection_AsciiString* aLabel = myAxesLabels.Seek (theAxis);
+    return aLabel != NULL ? *aLabel : TCollection_AsciiString();
+  }
+
+  //! Set axes labels.
+  void SetAxesLabels (const TCollection_AsciiString& theX,
+                      const TCollection_AsciiString& theY,
+                      const TCollection_AsciiString& theZ)
+  {
+    myAxesLabels.Bind (Prs3d_DP_XAxis, theX);
+    myAxesLabels.Bind (Prs3d_DP_YAxis, theY);
+    myAxesLabels.Bind (Prs3d_DP_ZAxis, theZ);
+    SetToUpdate();
+  }
+
+public:
+
+  //! Set new value of color for the whole object.
+  //! @param theColor [in] input color value.
+  virtual void SetColor (const Quantity_Color& theColor) Standard_OVERRIDE
+  {
+    SetBoxColor (theColor);
+  }
+
+  //! Reset color for the whole object.
+  virtual void UnsetColor() Standard_OVERRIDE
+  {
+    myDrawer->ShadingAspect()->SetColor (Quantity_NOC_WHITE);
+    myBoxEdgeAspect  ->SetColor (Quantity_NOC_GRAY30);
+    myBoxCornerAspect->SetColor (Quantity_NOC_GRAY30);
+    SynchronizeAspects();
+  }
+
+  //! Set new value of transparency for the whole object.
+  //! @param theValue [in] input transparency value.
+  virtual void SetTransparency (const Standard_Real theValue) Standard_OVERRIDE
+  {
+    SetBoxTransparency (theValue);
+  }
+
+  //! Reset transparency for the whole object.
+  virtual void UnsetTransparency() Standard_OVERRIDE
+  {
+    SetBoxTransparency (0.0f);
+  }
+
+  //! Sets the material for the interactive object.
+  virtual void SetMaterial (const Graphic3d_MaterialAspect& theMat) Standard_OVERRIDE
+  {
+    myDrawer->ShadingAspect()->SetMaterial (theMat);
+    myBoxEdgeAspect  ->SetMaterial (theMat);
+    myBoxCornerAspect->SetMaterial (theMat);
+    SynchronizeAspects();
+  }
+
+  //! Sets the material for the interactive object.
+  virtual void UnsetMaterial() Standard_OVERRIDE
+  {
+    Graphic3d_MaterialAspect aMat (Graphic3d_NOM_UserDefined);
+    aMat.SetColor (Quantity_NOC_WHITE);
+    aMat.SetAmbientColor (Quantity_NOC_GRAY60);
+    myDrawer->ShadingAspect()->SetMaterial (aMat);
+    myBoxEdgeAspect  ->SetMaterial (aMat);
+    myBoxEdgeAspect  ->SetColor (Quantity_NOC_GRAY30);
+    myBoxCornerAspect->SetMaterial (aMat);
+    myBoxCornerAspect->SetColor (Quantity_NOC_GRAY30);
+    SynchronizeAspects();
+  }
+
+public: //! @name animation methods
+
+  //! Return duration of animation in seconds; 0.5 sec by default
+  Standard_Real Duration() const { return myDuration; }
+
+  //! Set duration of animation.
+  //! @param theValue [in] input value of duration in seconds
+  void SetDuration (Standard_Real theValue) { myDuration = theValue; }
+
+  //! Return TRUE if new camera Up direction should be always set to default value for a new camera Direction; FALSE by default.
+  //! When this flag is FALSE, the new camera Up will be set as current Up orthogonalized to the new camera Direction,
+  //! and will set to default Up on second click.
+  Standard_Boolean ToResetCameraUp() const { return myToResetCameraUp; }
+
+  //! Set if new camera Up direction should be always set to default value for a new camera Direction.
+  void SetResetCamera (Standard_Boolean theToReset) { myToResetCameraUp = theToReset; }
+
+  //! Return TRUE if animation should fit selected objects and FALSE to fit entire scene; TRUE by default.
+  Standard_Boolean ToFitSelected() const { return myToFitSelected; }
+
+  //! Set if animation should fit selected objects or to fit entire scene.
+  void SetFitSelected (Standard_Boolean theToFitSelected) { myToFitSelected = theToFitSelected; }
+
+  //! @return TRUE if View Cube has unfinished animation of view camera.
+  Standard_EXPORT Standard_Boolean HasAnimation() const;
+
+  //! Start camera transformation corresponding to the input detected owner.
+  //! @param theOwner [in] detected owner.
+  Standard_EXPORT virtual void StartAnimation (const Handle(AIS_ViewCubeOwner)& theOwner);
+
+  //! Perform one step of current camera transformation.
+  //! theToUpdate [in] enable/disable update of view.
+  //! @return TRUE if animation is not stopped.
+  Standard_EXPORT virtual Standard_Boolean UpdateAnimation (const Standard_Boolean theToUpdate);
+
+  //! Perform camera transformation corresponding to the input detected owner.
+  Standard_EXPORT virtual void HandleClick (const Handle(AIS_ViewCubeOwner)& theOwner);
+
+protected:
+
+  //! Perform internal single step of animation.
+  //! @return FALSE if animation has been finished
+  Standard_EXPORT Standard_Boolean updateAnimation();
+
+protected: //! @name protected virtual API
+
+  //! Method that is called after one step of transformation.
+  virtual void onAfterAnimation() {}
+
+  //! Method that is called after transformation finish.
+  virtual void onAnimationFinished() {}
+
+public: //! @name Presentation computation
+
+  //! Return TRUE for supported display mode.
+  virtual Standard_Boolean AcceptDisplayMode (const Standard_Integer theMode) const Standard_OVERRIDE { return theMode == 0; }
+
+  //! Global selection has no meaning for this class.
+  virtual Handle(SelectMgr_EntityOwner) GlobalSelOwner() const Standard_OVERRIDE { return Handle(SelectMgr_EntityOwner)(); }
+
+  //! Compute 3D part of View Cube.
+  //! @param thePrsMgr [in] presentation manager.
+  //! @param thePrs [in] input presentation that is to be filled with flat presentation primitives.
+  //! @param theMode [in] display mode.
+  //! @warning this object accept only 0 display mode.
+  Standard_EXPORT virtual void Compute (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr,
+                                        const Handle(Prs3d_Presentation)& thePrs,
+                                        const Standard_Integer theMode = 0) Standard_OVERRIDE;
+
+  //! Redefine computing of sensitive entities for View Cube.
+  //! @param theSelection [in] input selection object that is to be filled with sensitive entities.
+  //! @param theMode [in] selection mode.
+  //! @warning object accepts only 0 selection mode.
+  Standard_EXPORT virtual void ComputeSelection (const Handle(SelectMgr_Selection)& theSelection,
+                                                 const Standard_Integer theMode) Standard_OVERRIDE;
+
+  //! Disables auto highlighting to use HilightSelected() and HilightOwnerWithColor() overridden methods.
+  virtual Standard_Boolean IsAutoHilight() const Standard_OVERRIDE { return Standard_False; }
+
+  //! Method which clear all selected owners belonging to this selectable object.
+  //! @warning this object does not support selection.
+  virtual void ClearSelected() Standard_OVERRIDE {}
+
+  //! Method which highlights input owner belonging to this selectable object.
+  //! @param thePM [in] presentation manager
+  //! @param theStyle [in] style for dynamic highlighting.
+  //! @param theOwner [in] input entity owner.
+  Standard_EXPORT virtual void HilightOwnerWithColor (const Handle(PrsMgr_PresentationManager3d)& thePM,
+                                                      const Handle(Prs3d_Drawer)& theStyle,
+                                                      const Handle(SelectMgr_EntityOwner)& theOwner) Standard_OVERRIDE;
+
+  //! Method which draws selected owners.
+  Standard_EXPORT virtual void HilightSelected (const Handle(PrsMgr_PresentationManager3d)& thePM,
+                                                const SelectMgr_SequenceOfOwner& theSeq) Standard_OVERRIDE;
+
+  //! Set default parameters for visual attributes
+  //! @sa Attributes()
+  virtual void UnsetAttributes() Standard_OVERRIDE
+  {
+    setDefaultAttributes();
+    SetToUpdate();
+  }
+
+  //! Set default parameters for dynamic highlighting attributes, reset highlight attributes
+  virtual void UnsetHilightAttributes() Standard_OVERRIDE
+  {
+    myHilightDrawer.Nullify();
+    setDefaultHighlightAttributes();
+    SetToUpdate();
+  }
+
+protected: //! @name Auxiliary classes to fill presentation with proper primitives
+
+  //! Create triangulation for a box part - for presentation and selection purposes.
+  Standard_EXPORT virtual Handle(Graphic3d_ArrayOfTriangles) createBoxPartTriangles (V3d_TypeOfOrientation theDir) const;
+
+  //! Create triangulation for a box side.
+  Standard_EXPORT virtual Handle(Graphic3d_ArrayOfTriangles) createBoxSideTriangles (V3d_TypeOfOrientation theDir) const;
+
+  //! Create triangulation for a box edge.
+  Standard_EXPORT virtual Handle(Graphic3d_ArrayOfTriangles) createBoxEdgeTriangles (V3d_TypeOfOrientation theDir) const;
+
+  //! Create triangulation for a box corner (vertex).
+  Standard_EXPORT virtual Handle(Graphic3d_ArrayOfTriangles) createBoxCornerTriangles (V3d_TypeOfOrientation theDir) const;
+
+protected:
+
+  //! Create triangulation for a rectangle with round corners.
+  //! @param theSize   rectangle dimensions
+  //! @param theRadius radius at corners
+  //! @param theTrsf   transformation
+  Standard_EXPORT static Handle(Graphic3d_ArrayOfTriangles) createRoundRectangleTriangles (const gp_XY& theSize,
+                                                                                           Standard_Real theRadius,
+                                                                                           const gp_Trsf& theTrsf);
+
+protected:
+
+  //! Trivial hasher to avoid ambiguity with enumeration type.
+  struct IntegerHasher
+  {
+    static Standard_Integer HashCode (Standard_Integer theValue, Standard_Integer theUpper) { return ::HashCode (theValue, theUpper); }
+    static Standard_Boolean IsEqual (Standard_Integer theA, Standard_Integer theB) { return theA == theB; }
+  };
+
+protected:
+
+  NCollection_DataMap<V3d_TypeOfOrientation, TCollection_AsciiString, IntegerHasher>
+                                myBoxSideLabels;     //!< map with box side labels
+  NCollection_DataMap<Prs3d_DatumParts, TCollection_AsciiString, IntegerHasher>
+                                myAxesLabels;        //!< map with axes labels
+  Handle(Prs3d_ShadingAspect)   myBoxEdgeAspect;     //!< style for box edges
+  Handle(Prs3d_ShadingAspect)   myBoxCornerAspect;   //!< style for box corner
+
+  Standard_Real                 mySize;              //!< size of box side, length of one axis
+  Standard_Real                 myBoxEdgeMinSize;    //!< minimal size of box edge
+  Standard_Real                 myBoxEdgeGap;        //!< gap between box side and box edge
+  Standard_Real                 myBoxFacetExtension; //!< box facet extension
+  Standard_Real                 myAxesPadding;       //!< Padding between box and axes
+  Standard_Real                 myCornerMinSize;     //!< minimal size of box corner
+  Standard_Real                 myRoundRadius;       //!< relative round radius within [0; 0.5] range
+  Standard_Boolean              myToDisplayAxes;     //!< trihedron visibility
+  Standard_Boolean              myToDisplayEdges;    //!< box edges visibility
+  Standard_Boolean              myToDisplayVertices; //!< box corners (vertices) visibility
+  Standard_Boolean              myIsYup;             //!< flag indicating that application expects Y-up viewer orientation instead of Z-up
+
+protected: //! @name Animation options
+
+  Handle(AIS_AnimationCamera)   myViewAnimation;     //!< Camera animation object
+  Handle(Graphic3d_Camera)      myStartState;        //!< Start state of view camera
+  Handle(Graphic3d_Camera)      myEndState;          //!< End state of view camera
+  Standard_Real                 myDuration;          //!< Duration of animation. By default it is half a second
+  Standard_Boolean              myToAutoStartAnim;   //!< start animation automatically on click
+  Standard_Boolean              myIsFixedAnimation;  //!< fixed-loop animation
+  Standard_Boolean              myToFitSelected;     //!< fit selected or fit entire scene
+  Standard_Boolean              myToResetCameraUp;   //!< always reset camera up direction to default
+
+};
+
+//! Redefined entity owner that is highlighted when owner is detected,
+//! even if Interactive Context highlighted on last detection procedure.
+class AIS_ViewCubeOwner : public SelectMgr_EntityOwner
+{
+  DEFINE_STANDARD_RTTIEXT(AIS_ViewCubeOwner, SelectMgr_EntityOwner)
+public:
+
+  //! Main constructor.
+  AIS_ViewCubeOwner (const Handle(AIS_ViewCube)& theObject,
+                     V3d_TypeOfOrientation theOrient,
+                     Standard_Integer thePriority = 5)
+  : SelectMgr_EntityOwner ((const Handle(SelectMgr_SelectableObject)& )theObject, thePriority),
+    myMainOrient (theOrient)
+  {
+    myFromDecomposition = true;
+  }
+
+  //! @return TRUE. This owner will always call method
+  //! Hilight for its Selectable Object when the owner is detected.
+  virtual Standard_Boolean IsForcedHilight() const Standard_OVERRIDE { return Standard_True; }
+
+  //! Return new orientation to set.
+  V3d_TypeOfOrientation MainOrientation() const { return myMainOrient; }
+
+  //! Handle mouse button click event.
+  virtual Standard_Boolean HandleMouseClick (const Graphic3d_Vec2i& thePoint,
+                                             Aspect_VKeyMouse theButton,
+                                             Aspect_VKeyFlags theModifiers,
+                                             bool theIsDoubleClick) Standard_OVERRIDE
+  {
+    (void )thePoint; (void )theButton; (void )theModifiers; (void )theIsDoubleClick;
+    AIS_ViewCube* aCubePrs = dynamic_cast<AIS_ViewCube* >(mySelectable);
+    aCubePrs->HandleClick (this);
+    return Standard_True;
+  }
+
+protected:
+
+  V3d_TypeOfOrientation myMainOrient; //!< new orientation to set
+
+};
+
+#endif // _AIS_ViewCube_HeaderFile
index 7d225a6..6aae413 100644 (file)
@@ -182,4 +182,6 @@ AIS_TypeOfPlane.hxx
 AIS_ViewController.cxx
 AIS_ViewController.hxx
 AIS_ViewInputBuffer.hxx
+AIS_ViewCube.cxx
+AIS_ViewCube.hxx
 AIS_WalkDelta.hxx
index 3b3f6db..8888c83 100644 (file)
@@ -462,6 +462,15 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
       Colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (0.970f, 0.970f, 0.970f));
       break;
     case Graphic3d_NOM_UserDefined:
+      MaterialType = Graphic3d_MATERIAL_PHYSIC;
+      ColorCoef[Graphic3d_TOR_AMBIENT]  = 1.00f;
+      ColorCoef[Graphic3d_TOR_DIFFUSE]  = 1.00f;
+      ColorCoef[Graphic3d_TOR_SPECULAR] = 1.00f;
+      ColorCoef[Graphic3d_TOR_EMISSION] = 1.00f;
+      Colors[Graphic3d_TOR_AMBIENT]  = Quantity_Color (Graphic3d_Vec3 (0.1f, 0.1f, 0.1f));
+      Colors[Graphic3d_TOR_DIFFUSE]  = Quantity_Color (Graphic3d_Vec3 (0.8f, 0.8f, 0.8f));
+      Colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (0.2f, 0.2f, 0.2f));
+      Colors[Graphic3d_TOR_EMISSION] = Quantity_Color (Graphic3d_Vec3 (0.0f, 0.0f, 0.0f));
       break;
     case Graphic3d_NOM_DEFAULT:
       break;
index 42e90b8..69bb3c2 100644 (file)
@@ -43,15 +43,24 @@ public:
   //! Returns the right-handed coordinate system set in SetComponent.
   Standard_EXPORT Handle(Prs3d_ShadingAspect) ShadingAspect (Prs3d_DatumParts thePart) const;
 
-  //! Returns the right-handed coordinate system set in SetComponent.
+  //! Returns the text attributes for rendering labels.
   const Handle(Prs3d_TextAspect)& TextAspect() const { return myTextAspect; }
 
+  //! Sets text attributes for rendering labels.
+  void SetTextAspect (const Handle(Prs3d_TextAspect)& theTextAspect) { myTextAspect = theTextAspect; }
+
   //! Returns the point aspect of origin wireframe presentation
   const Handle(Prs3d_PointAspect)& PointAspect() const { return myPointAspect; }
 
+  //! Returns the point aspect of origin wireframe presentation
+  void SetPointAspect (const Handle(Prs3d_PointAspect)& theAspect) { myPointAspect = theAspect; }
+
   //! Returns the arrow aspect of presentation
   const Handle(Prs3d_ArrowAspect)& ArrowAspect() const { return myArrowAspect; }
 
+  //! Sets the arrow aspect of presentation
+  void SetArrowAspect (const Handle(Prs3d_ArrowAspect)& theAspect) { myArrowAspect = theAspect; }
+
   //! Returns the attributes for display of the first axis.
   Standard_DEPRECATED("This method is deprecated - LineAspect() should be called instead")
   const Handle(Prs3d_LineAspect)& FirstAxisAspect() const { return myLineAspects.Find (Prs3d_DP_XAxis); }
index 3857d15..5eec91b 100644 (file)
@@ -28,7 +28,9 @@ Prs3d_ToolDisk::Prs3d_ToolDisk (const Standard_Real    theInnerRadius,
                                 const Standard_Integer theNbSlices,
                                 const Standard_Integer theNbStacks)
 : myInnerRadius (theInnerRadius),
-  myOuterRadius (theOuterRadius)
+  myOuterRadius (theOuterRadius),
+  myStartAngle  (0.0),
+  myEndAngle    (M_PI * 2.0)
 {
   mySlicesNb = theNbSlices;
   myStacksNb = theNbStacks;
@@ -40,7 +42,7 @@ Prs3d_ToolDisk::Prs3d_ToolDisk (const Standard_Real    theInnerRadius,
 //=======================================================================
 gp_Pnt Prs3d_ToolDisk::Vertex (const Standard_Real theU, const Standard_Real theV)
 {
-  const Standard_Real aU      = theU * M_PI * 2.0;
+  const Standard_Real aU      = myStartAngle + theU * (myEndAngle - myStartAngle);
   const Standard_Real aRadius = myInnerRadius + (myOuterRadius - myInnerRadius) * theV;
   return gp_Pnt (Cos (aU) * aRadius,
                  Sin (aU) * aRadius,
@@ -48,15 +50,6 @@ gp_Pnt Prs3d_ToolDisk::Vertex (const Standard_Real theU, const Standard_Real the
 }
 
 //=======================================================================
-//function : Add
-//purpose  :
-//=======================================================================
-gp_Dir Prs3d_ToolDisk::Normal (const Standard_Real /*theU*/, const Standard_Real /*theV*/)
-{
-  return gp_Dir (0.0, 0.0, -1.0);
-}
-
-//=======================================================================
 //function : Perform
 //purpose  :
 //=======================================================================
index 1e66093..6897083 100644 (file)
@@ -37,18 +37,31 @@ public:
                                   const Standard_Real    theOuterRadius,
                                   const Standard_Integer theNbSlices,
                                   const Standard_Integer theNbStacks);
+
+  //! Set angle range in radians [0, 2*PI] by default.
+  //! @param theStartAngle [in] Start angle in counter clockwise order
+  //! @param theEndAngle   [in] End   angle in counter clockwise order
+  void SetAngleRange (Standard_Real theStartAngle,
+                      Standard_Real theEndAngle)
+  {
+    myStartAngle = theStartAngle;
+    myEndAngle   = theEndAngle;
+  }
+
 protected:
 
   //! Computes vertex at given parameter location of the surface.
   Standard_EXPORT virtual gp_Pnt Vertex (const Standard_Real theU, const Standard_Real theV) Standard_OVERRIDE;
 
   //! Computes normal at given parameter location of the surface.
-  Standard_EXPORT virtual gp_Dir Normal (const Standard_Real theU, const Standard_Real theV) Standard_OVERRIDE;
+  virtual gp_Dir Normal (const Standard_Real , const Standard_Real ) Standard_OVERRIDE { return gp_Dir (0.0, 0.0, -1.0); }
 
 protected:
 
   Standard_Real myInnerRadius;
   Standard_Real myOuterRadius;
+  Standard_Real myStartAngle;  //!< Start angle in counter clockwise order
+  Standard_Real myEndAngle;    //!< End   angle in counter clockwise order
 
 };
 
index eec5e12..3e96e0f 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef _SelectMgr_EntityOwner_HeaderFile
 #define _SelectMgr_EntityOwner_HeaderFile
 
+#include <Aspect_VKey.hxx>
 #include <PrsMgr_PresentationManager.hxx>
 #include <SelectMgr_SelectableObject.hxx>
 #include <TopLoc_Location.hxx>
@@ -60,6 +61,22 @@ public:
   //! Sets the selectable object.
   virtual void SetSelectable (const Handle(SelectMgr_SelectableObject)& theSelObj) { mySelectable = theSelObj.get(); }
 
+  //! Handle mouse button click event.
+  //! Does nothing by default and returns FALSE.
+  //! @param thePoint      mouse cursor position
+  //! @param theButton     clicked button
+  //! @param theModifiers  key modifiers
+  //! @param theIsDoubleClick flag indicating double mouse click
+  //! @return TRUE if object handled click
+  virtual Standard_Boolean HandleMouseClick (const Graphic3d_Vec2i& thePoint,
+                                             Aspect_VKeyMouse theButton,
+                                             Aspect_VKeyFlags theModifiers,
+                                             bool theIsDoubleClick)
+  {
+    (void )thePoint; (void )theButton; (void )theModifiers; (void )theIsDoubleClick;
+    return Standard_False;
+  }
+
   //! Returns true if the presentation manager highlights selections corresponding to the selection mode.
   virtual Standard_Boolean IsHilighted (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                                         const Standard_Integer theMode = 0) const
index d2e8105..4dffd89 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <ViewerTest_EventManager.hxx>
 
+#include <AIS_AnimationCamera.hxx>
 #include <AIS_InteractiveContext.hxx>
 #include <AIS_Shape.hxx>
 #include <Aspect_Grid.hxx>
@@ -27,6 +28,16 @@ Standard_IMPORT Standard_Boolean Draw_Interprete (const char* theCommand);
 IMPLEMENT_STANDARD_RTTIEXT(ViewerTest_EventManager,Standard_Transient)
 
 //=======================================================================
+//function : GlobalViewAnimation
+//purpose  :
+//=======================================================================
+const Handle(AIS_AnimationCamera)& ViewerTest_EventManager::GlobalViewAnimation()
+{
+  static Handle(AIS_AnimationCamera) THE_CAMERA_ANIM = new AIS_AnimationCamera ("ViewerTest_EventManager_ViewAnimation", Handle(V3d_View)());
+  return THE_CAMERA_ANIM;
+}
+
+//=======================================================================
 //function : ViewerTest_EventManager
 //purpose  :
 //=======================================================================
@@ -35,7 +46,23 @@ ViewerTest_EventManager::ViewerTest_EventManager (const Handle(V3d_View)&
 : myCtx  (theCtx),
   myView (theView),
   myToPickPnt (Standard_False)
-{}
+{
+  myViewAnimation = GlobalViewAnimation();
+}
+
+//=======================================================================
+//function : ~ViewerTest_EventManager
+//purpose  :
+//=======================================================================
+ViewerTest_EventManager::~ViewerTest_EventManager()
+{
+  if (!myViewAnimation.IsNull()
+    && myViewAnimation->View() == myView)
+  {
+    myViewAnimation->Stop();
+    myViewAnimation->SetView (Handle(V3d_View)());
+  }
+}
 
 //=======================================================================
 //function : UpdateMouseButtons
index 25f23db..6d2fed7 100644 (file)
@@ -47,11 +47,17 @@ public:
     return Draw_ToExitOnCloseView;
   }
 
+  //! Use global camera animation object shared across all Views in ViewerTest.
+  Standard_EXPORT static const Handle(AIS_AnimationCamera)& GlobalViewAnimation();
+
 public:
 
   //! Main constructor.
   Standard_EXPORT ViewerTest_EventManager(const Handle(V3d_View)& aView, const Handle(AIS_InteractiveContext)& aCtx);
-  
+
+  //! Destructor.
+  Standard_EXPORT virtual ~ViewerTest_EventManager();
+
   //! Return interactive context.
   const Handle(AIS_InteractiveContext)& Context() const { return myCtx; }
 
index b131a07..79ed5dd 100644 (file)
@@ -25,6 +25,7 @@
 #include <AIS_ListOfInteractive.hxx>
 #include <AIS_ListIteratorOfListOfInteractive.hxx>
 #include <AIS_Manipulator.hxx>
+#include <AIS_ViewCube.hxx>
 #include <AIS_Shape.hxx>
 #include <Aspect_DisplayConnection.hxx>
 #include <Aspect_Grid.hxx>
@@ -13084,6 +13085,258 @@ static int VDumpSelectionImage (Draw_Interpretor& /*theDi*/,
   return 0;
 }
 
+//===============================================================================================
+//function : VViewCube
+//purpose  :
+//===============================================================================================
+static int VViewCube (Draw_Interpretor& ,
+                      Standard_Integer  theNbArgs,
+                      const char**      theArgVec)
+{
+  const Handle(AIS_InteractiveContext)& aContext = ViewerTest::GetAISContext();
+  const Handle(V3d_View)& aView = ViewerTest::CurrentView();
+  if (aContext.IsNull() || aView.IsNull())
+  {
+    std::cout << "Error: no active view.\n";
+    return 1;
+  }
+  else if (theNbArgs < 2)
+  {
+    std::cout << "Syntax error: wrong number arguments\n";
+    return 1;
+  }
+
+  Handle(AIS_ViewCube) aViewCube;
+  ViewerTest_AutoUpdater anUpdateTool (aContext, aView);
+  Quantity_Color aColorRgb;
+  TCollection_AsciiString aName;
+  for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
+  {
+    TCollection_AsciiString anArg (theArgVec[anArgIter]);
+    anArg.LowerCase();
+    if (anUpdateTool.parseRedrawMode (anArg))
+    {
+      //
+    }
+    else if (aViewCube.IsNull())
+    {
+      aName = theArgVec[anArgIter];
+      if (aName.StartsWith ("-"))
+      {
+        std::cout << "Syntax error: object name should be specified.\n";
+        return 1;
+      }
+      Handle(AIS_InteractiveObject) aPrs;
+      GetMapOfAIS().Find2 (aName, aPrs);
+      aViewCube = Handle(AIS_ViewCube)::DownCast (aPrs);
+      if (aViewCube.IsNull())
+      {
+        aViewCube = new AIS_ViewCube();
+        aViewCube->SetBoxColor (Quantity_NOC_GRAY50);
+        aViewCube->SetViewAnimation (ViewerTest::CurrentEventManager()->ViewAnimation());
+        aViewCube->SetFixedAnimationLoop (false);
+      }
+    }
+    else if (anArg == "-reset")
+    {
+      aViewCube->ResetStyles();
+    }
+    else if (anArg == "-color"
+          || anArg == "-boxcolor"
+          || anArg == "-boxsidecolor"
+          || anArg == "-sidecolor"
+          || anArg == "-boxedgecolor"
+          || anArg == "-edgecolor"
+          || anArg == "-boxcornercolor"
+          || anArg == "-cornercolor"
+          || anArg == "-innercolor"
+          || anArg == "-textcolor")
+    {
+      Standard_Integer aNbParsed = ViewerTest::ParseColor (theNbArgs - anArgIter - 1,
+                                                           theArgVec + anArgIter + 1,
+                                                           aColorRgb);
+      if (aNbParsed == 0)
+      {
+        std::cerr << "Error: wrong syntax at '" << anArg << "'\n";
+        return 1;
+      }
+      anArgIter += aNbParsed;
+      if (anArg == "-boxcolor")
+      {
+        aViewCube->SetBoxColor (aColorRgb);
+      }
+      else if (anArg == "-boxsidecolor"
+            || anArg == "-sidecolor")
+      {
+        aViewCube->BoxSideStyle()->SetColor (aColorRgb);
+        aViewCube->SynchronizeAspects();
+      }
+      else if (anArg == "-boxedgecolor"
+            || anArg == "-edgecolor")
+      {
+        aViewCube->BoxEdgeStyle()->SetColor (aColorRgb);
+        aViewCube->SynchronizeAspects();
+      }
+      else if (anArg == "-boxcornercolor"
+            || anArg == "-cornercolor")
+      {
+        aViewCube->BoxCornerStyle()->SetColor (aColorRgb);
+        aViewCube->SynchronizeAspects();
+      }
+      else if (anArg == "-innercolor")
+      {
+        aViewCube->SetInnerColor (aColorRgb);
+      }
+      else if (anArg == "-textcolor")
+      {
+        aViewCube->SetTextColor (aColorRgb);
+      }
+      else
+      {
+        aViewCube->SetColor (aColorRgb);
+      }
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && (anArg == "-transparency"
+           || anArg == "-boxtransparency"))
+    {
+      const Standard_Real aValue = Draw::Atof (theArgVec[++anArgIter]);
+      if (aValue < 0.0 || aValue > 1.0)
+      {
+        std::cout << "Syntax error: invalid transparency value " << theArgVec[anArgIter] << "\n";
+        return 1;
+      }
+
+      if (anArg == "-boxtransparency")
+      {
+        aViewCube->SetBoxTransparency (aValue);
+      }
+      else
+      {
+        aViewCube->SetTransparency (aValue);
+      }
+    }
+    else if (anArg == "-axes"
+          || anArg == "-edges"
+          || anArg == "-vertices"
+          || anArg == "-vertexes"
+          || anArg == "-fixedanimation")
+    {
+      bool toShow = true;
+      if (anArgIter + 1 < theNbArgs
+       && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], toShow))
+      {
+        ++anArgIter;
+      }
+      if (anArg == "-fixedanimation")
+      {
+        aViewCube->SetFixedAnimationLoop (toShow);
+      }
+      else if (anArg == "-axes")
+      {
+        aViewCube->SetDrawAxes (toShow);
+      }
+      else if (anArg == "-edges")
+      {
+        aViewCube->SetDrawEdges (toShow);
+      }
+      else
+      {
+        aViewCube->SetDrawVertices (toShow);
+      }
+    }
+    else if (anArg == "-yup"
+          || anArg == "-zup")
+    {
+      bool isOn = true;
+      if (anArgIter + 1 < theNbArgs
+       && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], isOn))
+      {
+        ++anArgIter;
+      }
+      if (anArg == "-yup")
+      {
+        aViewCube->SetYup (isOn);
+      }
+      else
+      {
+        aViewCube->SetYup (!isOn);
+      }
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && anArg == "-font")
+    {
+      aViewCube->SetFont (theArgVec[++anArgIter]);
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && anArg == "-fontheight")
+    {
+      aViewCube->SetFontHeight (Draw::Atof (theArgVec[++anArgIter]));
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && (anArg == "-size"
+           || anArg == "-boxsize"))
+    {
+      aViewCube->SetSize (Draw::Atof (theArgVec[++anArgIter]),
+                          anArg != "-boxsize");
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && (anArg == "-boxfacet"
+           || anArg == "-boxfacetextension"
+           || anArg == "-facetextension"
+           || anArg == "-extension"))
+    {
+      aViewCube->SetBoxFacetExtension (Draw::Atof (theArgVec[++anArgIter]));
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && (anArg == "-boxedgegap"
+           || anArg == "-edgegap"))
+    {
+      aViewCube->SetBoxEdgeGap (Draw::Atof (theArgVec[++anArgIter]));
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && (anArg == "-boxedgeminsize"
+           || anArg == "-edgeminsize"))
+    {
+      aViewCube->SetBoxEdgeMinSize (Draw::Atof (theArgVec[++anArgIter]));
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && (anArg == "-boxcornerminsize"
+           || anArg == "-cornerminsize"))
+    {
+      aViewCube->SetBoxCornerMinSize (Draw::Atof (theArgVec[++anArgIter]));
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && anArg == "-axespadding")
+    {
+      aViewCube->SetAxesPadding (Draw::Atof (theArgVec[++anArgIter]));
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && anArg == "-roundradius")
+    {
+      aViewCube->SetRoundRadius (Draw::Atof (theArgVec[++anArgIter]));
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && anArg == "-duration")
+    {
+      aViewCube->SetDuration (Draw::Atof (theArgVec[++anArgIter]));
+    }
+    else
+    {
+      std::cout << "Syntax error: unknown argument '" << anArg << "'\n";
+      return 1;
+    }
+  }
+  if (aViewCube.IsNull())
+  {
+    std::cout << "Syntax error: wrong number of arguments\n";
+    return 1;
+  }
+
+  ViewerTest::Display (aName, aViewCube, false);
+  return 0;
+}
+
 //=======================================================================
 //function : ViewerCommands
 //purpose  :
@@ -13880,5 +14133,37 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
                    "\n\t\t:   selMode     color of selection mode"
                    "\n\t\t:   entity      color of etected entity",
                    __FILE__, VDumpSelectionImage, group);
-}
 
+  theCommands.Add ("vviewcube",
+                   "vviewcube name"
+                   "\n\t\t: Displays interactive view manipualtion object."
+                   "\n\t\t: Options: "
+                   "\n\t\t:   -reset                   reset geomertical and visual attributes'"
+                   "\n\t\t:   -size Size               adapted size of View Cube"
+                   "\n\t\t:   -boxSize Size            box size"
+                   "\n\t\t:   -axes {0|1 }             show/hide axes (trihedron)"
+                   "\n\t\t:   -edges {0|1}             show/hide edges of View Cube"
+                   "\n\t\t:   -vertices {0|1}          show/hide vertices of View Cube"
+                   "\n\t\t:   -Yup {0|1} -Zup {0|1}    set Y-up or Z-up view orientation"
+                   "\n\t\t:   -color Color             color of View Cube"
+                   "\n\t\t:   -boxColor Color          box color"
+                   "\n\t\t:   -boxSideColor Color      box sides color"
+                   "\n\t\t:   -boxEdgeColor Color      box edges color"
+                   "\n\t\t:   -boxCornerColor Color    box corner color"
+                   "\n\t\t:   -textColor Color         color of side text of view cube"
+                   "\n\t\t:   -innerColor Color        inner box color"
+                   "\n\t\t:   -transparency Value      transparency of object within [0, 1] range"
+                   "\n\t\t:   -boxTransparency Value   transparency of box    within [0, 1] range"
+                   "\n\t\t:   -font Name               font name"
+                   "\n\t\t:   -fontHeight Value        font height"
+                   "\n\t\t:   -boxFacetExtension Value box facet extension"
+                   "\n\t\t:   -boxEdgeGap Value        gap between box edges and box sides"
+                   "\n\t\t:   -boxEdgeMinSize Value    minimal box edge size"
+                   "\n\t\t:   -boxCornerMinSize Value  minimal box corner size"
+                   "\n\t\t:   -axesPadding Value       padding between box and arrows"
+                   "\n\t\t:   -roundRadius Value       relative radius of corners of sides within [0.0, 0.5] range"
+                   "\n\t\t:   -fixedanimation {0|1}    uninterruptible animation loop"
+                   "\n\t\t:   -duration Seconds        animation duration in seconds",
+    __FILE__, VViewCube, group);
+
+}
index 9db6b25..39189a3 100755 (executable)
@@ -19,3 +19,4 @@
 020 anim
 021 dimensions
 022 transparency
+023 viewcube
diff --git a/tests/v3d/viewcube/default b/tests/v3d/viewcube/default
new file mode 100644 (file)
index 0000000..b577a59
--- /dev/null
@@ -0,0 +1,36 @@
+puts "=================================="
+puts "0028954: Visualization - implement interactive object AIS_ViewCube for camera manipulations"
+puts "Display and erase with default settings"
+puts "=================================="
+
+vclear
+vinit View1
+
+box b 15 20 70
+vdisplay -dispMode 1 b
+vaxo
+vfit
+vviewcube vc -fixedAnimation 1 -duration 0
+
+vmoveto 70 340
+if {[vreadpixel 70 340 name rgb] != "CYAN1"} { puts "Error: Highlighting of view cube Side is wrong." }
+vmoveto 0 0
+vdump $imagedir/${casename}_axo.png
+
+# check FRONT side
+vselect 70 340
+if {[vreadpixel 255 300 name rgb] != "BLACK"} { puts "Error: Position of FRONT camera is wrong." }
+vdump $imagedir/${casename}_side.png
+
+# check FRONT/TOP edge
+vselect 100 270
+if {[vreadpixel 100 300 name rgb] != "GRAY51"} { puts "Error: Position of FRONT-TOP camera is wrong." }
+if {[vreadpixel 100 310 name rgb] != "CYAN1"}  { puts "Error: Position of FRONT-TOP camera is wrong." }
+vdump $imagedir/${casename}_edge.png
+
+# Check vertex
+vselect 140 310
+if {[vreadpixel 100 290 name rgb] != "GRAY42"}    { puts "Error: Position of TOP-FRONT-RIGHT camera is wrong." }
+if {[vreadpixel 100 310 name rgb] != "CYAN1"}     { puts "Error: Position of TOP-FRONT-RIGHT camera is wrong." }
+if {[vreadpixel 100 320 name rgb] != "MATRAGRAY"} { puts "Error: Position of TOP-FRONT-RIGHT camera is wrong." }
+vdump $imagedir/${casename}_corner.png
diff --git a/tests/v3d/viewcube/style b/tests/v3d/viewcube/style
new file mode 100644 (file)
index 0000000..881d373
--- /dev/null
@@ -0,0 +1,44 @@
+puts "=================================="
+puts "0028954: Visualization - implement interactive object AIS_ViewCube for camera manipulations"
+puts "Display custom styled View Cube"
+puts "=================================="
+
+vclear
+vinit View1
+
+vviewcube vc -edges 0
+if {[vreadpixel 70 295 name rgb] != "BLACK"} { puts "Error: Invalid display of View Cube without edges." }
+vdump $imagedir/${casename}_noedges.png
+
+vviewcube vc -edges 1 -vertices 0
+if {[vreadpixel 100 308 name rgb] != "BLACK"} { puts "Error: Invalid display of View Cube without vertices." }
+vdump $imagedir/${casename}_noverts.png
+
+vviewcube vc -edges 0 -vertices 0
+
+if {[vreadpixel 70 295 name rgb] != "BLACK" || [vreadpixel 100 308 name rgb] != "BLACK"} {
+  puts "Error: Invalid display of View Cube without edges & vertices."
+}
+vdump $imagedir/${casename}_noedgeandvert.png
+vclear
+
+# Color
+vviewcube vc1 -boxColor 0.69 0.88 1 -textColor 0 0.4 0.54
+vdisplay vc1 -trihedron bottomLeft 100 100
+
+# Transparency
+vviewcube vc2 -transparency 0.5
+vdisplay vc2 -trihedron topLeft 100 100
+
+# Font
+vviewcube vc3 -reset -boxSideColor WHITE -font "monospace" -fontHeight 16
+vdisplay vc3 -trihedron bottomRight 100 100
+
+# Corner radius
+vviewcube vc4 -reset -boxSideColor WHITE -roundRadius 0.2 -boxEdgeGap 2
+vdisplay vc4 -trihedron topRight 100 100
+
+# Padding
+vviewcube vc5 -reset -boxFacetExtension 0 -axesPadding 0
+vdisplay vc5 -trihedron center
+vdump $imagedir/${casename}_styles.png
diff --git a/tests/v3d/viewcube/view b/tests/v3d/viewcube/view
new file mode 100644 (file)
index 0000000..2a84e99
--- /dev/null
@@ -0,0 +1,22 @@
+puts "=================================="
+puts "0028954: Visualization - implement interactive object AIS_ViewCube for camera manipulations"
+puts "Check view affinity"
+puts "=================================="
+
+vclear
+vclose ALL
+vinit View1
+vinit View2
+
+vviewcube vc
+verase vc -view
+
+if {[vreadpixel 100 350 name rgb] != "BLACK"} { puts "Error: hiding Cube in View2 fails." }
+vdump $imagedir/${casename}_v2.png
+
+vactivate View1
+
+if {[vreadpixel 100 350 name rgb] == "BLACK"} { puts "Error: showing Cube in View1 fails." }
+vdump $imagedir/${casename}_v1.png
+
+vactivate View2