// 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 <NCollection_Vec2.hxx>
#include <Standard_TypeDef.hxx>
#include <Prs3d_DatumParts.hxx>
#include <Prs3d_ShadingAspect.hxx>
#include <Prs3d_TextAspect.hxx>
#include <SelectMgr_EntityOwner.hxx>
#include <V3d_TypeOfOrientation.hxx>
#include <Select3D_SensitivePrimitiveArray.hxx>

class AIS_AnimationCamera;
class AIS_ViewCubeOwner;
class Graphic3d_ArrayOfTriangles;
class V3d_View;

//! 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, NCollection_Vec2<int> (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 occ::handle<AIS_AnimationCamera>& ViewAnimation() const { return myViewAnimation; }

  //! Set view animation.
  void SetViewAnimation(const occ::handle<AIS_AnimationCamera>& theAnimation)
  {
    myViewAnimation = theAnimation;
  }

  //! Return TRUE if automatic camera transformation on selection (highlighting) is enabled; TRUE by
  //! default.
  bool 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.
  bool 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.
  double 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(double theValue, bool theToAdaptAnother = true);

  //! Return box facet extension to edge/corner facet split; 10 by default.
  double BoxFacetExtension() const { return myBoxFacetExtension; }

  //! Set new value of box facet extension.
  void SetBoxFacetExtension(double theValue)
  {
    if (std::abs(myBoxFacetExtension - theValue) > Precision::Confusion())
    {
      myBoxFacetExtension = theValue;
      SetToUpdate();
    }
  }

  //! Return padding between axes and 3D part (box); 10 by default.
  double AxesPadding() const { return myAxesPadding; }

  //! Set new value of padding between axes and 3D part (box).
  void SetAxesPadding(double theValue)
  {
    if (std::abs(myAxesPadding - theValue) > Precision::Confusion())
    {
      myAxesPadding = theValue;
      SetToUpdate();
    }
  }

  //! Return gap between box edges and box sides; 0 by default.
  double BoxEdgeGap() const { return myBoxEdgeGap; }

  //! Set new value of box edges gap.
  void SetBoxEdgeGap(double theValue)
  {
    if (std::abs(myBoxEdgeGap - theValue) > Precision::Confusion())
    {
      myBoxEdgeGap = theValue;
      SetToUpdate();
    }
  }

  //! Return minimal size of box edge; 2 by default.
  double BoxEdgeMinSize() const { return myBoxEdgeMinSize; }

  //! Set new value of box edge minimal size.
  void SetBoxEdgeMinSize(double theValue)
  {
    if (std::abs(myBoxEdgeMinSize - theValue) > Precision::Confusion())
    {
      myBoxEdgeMinSize = theValue;
      SetToUpdate();
    }
  }

  //! Return minimal size of box corner; 2 by default.
  double BoxCornerMinSize() const { return myCornerMinSize; }

  //! Set new value of box corner minimal size.
  void SetBoxCornerMinSize(double theValue)
  {
    if (std::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().
  double 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 double theValue);

  //! Returns radius of axes of the trihedron; 1.0 by default.
  double AxesRadius() const { return myAxesRadius; }

  //! Sets radius of axes of the trihedron.
  void SetAxesRadius(const double theRadius)
  {
    if (std::abs(myAxesRadius - theRadius) > Precision::Confusion())
    {
      myAxesRadius = theRadius;
      SetToUpdate();
    }
  }

  //! Returns radius of cone of axes of the trihedron; 3.0 by default.
  double AxesConeRadius() const { return myAxesConeRadius; }

  //! Sets radius of cone of axes of the trihedron.
  void SetAxesConeRadius(double theRadius)
  {
    if (std::abs(myAxesConeRadius - theRadius) > Precision::Confusion())
    {
      myAxesConeRadius = theRadius;
      SetToUpdate();
    }
  }

  //! Returns radius of sphere (central point) of the trihedron; 4.0 by default.
  double AxesSphereRadius() const { return myAxesSphereRadius; }

  //! Sets radius of sphere (central point) of the trihedron.
  void SetAxesSphereRadius(double theRadius)
  {
    if (std::abs(myAxesSphereRadius - theRadius) > Precision::Confusion())
    {
      myAxesSphereRadius = theRadius;
      SetToUpdate();
    }
  }

  //! @return TRUE if trihedron is drawn; TRUE by default.
  bool ToDrawAxes() const { return myToDisplayAxes; }

  //! Enable/disable drawing of trihedron.
  void SetDrawAxes(bool theValue)
  {
    if (myToDisplayAxes != theValue)
    {
      myToDisplayAxes = theValue;
      SetToUpdate();
    }
  }

  //! @return TRUE if edges of View Cube is drawn; TRUE by default.
  bool ToDrawEdges() const { return myToDisplayEdges; }

  //! Enable/disable drawing of edges of View Cube.
  void SetDrawEdges(bool theValue)
  {
    if (myToDisplayEdges != theValue)
    {
      myToDisplayEdges = theValue;
      SetToUpdate();
    }
  }

  //! Return TRUE if vertices (vertex) of View Cube is drawn; TRUE by default.
  bool ToDrawVertices() const { return myToDisplayVertices; }

  //! Enable/disable drawing of vertices (corners) of View Cube.
  void SetDrawVertices(bool theValue)
  {
    if (myToDisplayVertices != theValue)
    {
      myToDisplayVertices = theValue;
      SetToUpdate();
    }
  }

  //! Return TRUE if application expects Y-up viewer orientation instead of Z-up; FALSE by default.
  bool IsYup() const { return myIsYup; }

  //! Set if application expects Y-up viewer orientation instead of Z-up.
  Standard_EXPORT void SetYup(bool theIsYup, bool theToUpdateLabels = true);

public: //! @name Style management API
  //! Return shading style of box sides.
  const occ::handle<Prs3d_ShadingAspect>& BoxSideStyle() const { return myDrawer->ShadingAspect(); }

  //! Return shading style of box edges.
  const occ::handle<Prs3d_ShadingAspect>& BoxEdgeStyle() const { return myBoxEdgeAspect; }

  //! Return shading style of box corners.
  const occ::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[in] theColor  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.
  double BoxTransparency() const { return myDrawer->ShadingAspect()->Transparency(); }

  //! Set new value of transparency for 3D part of object.
  //! @param[in] theValue  input transparency value
  void SetBoxTransparency(double theValue)
  {
    if (std::abs(myDrawer->ShadingAspect()->Transparency() - theValue) > Precision::Confusion()
        || std::abs(myBoxEdgeAspect->Transparency() - theValue) > Precision::Confusion()
        || std::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 != nullptr ? *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
  double FontHeight() const { return myDrawer->TextAspect()->Height(); }

  //! Change font height. Alias for:
  //! @code Attributes()->TextAspect()->SetHeight() @endcode
  void SetFontHeight(double theValue)
  {
    if (std::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 != nullptr ? *aLabel : TCollection_AsciiString();
  }

  //! Set axes labels.
  void SetAxesLabels(const TCollection_AsciiString& theX,
                     const TCollection_AsciiString& theY,
                     const TCollection_AsciiString& theZ)
  {
    myAxesLabels.Bind(Prs3d_DatumParts_XAxis, theX);
    myAxesLabels.Bind(Prs3d_DatumParts_YAxis, theY);
    myAxesLabels.Bind(Prs3d_DatumParts_ZAxis, theZ);
    SetToUpdate();
  }

public:
  //! Set new value of color for the whole object.
  //! @param[in] theColor  input color value.
  void SetColor(const Quantity_Color& theColor) override { SetBoxColor(theColor); }

  //! Reset color for the whole object.
  void UnsetColor() 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[in] theValue  input transparency value.
  void SetTransparency(const double theValue) override { SetBoxTransparency(theValue); }

  //! Reset transparency for the whole object.
  void UnsetTransparency() override { SetBoxTransparency(0.0f); }

  //! Sets the material for the interactive object.
  void SetMaterial(const Graphic3d_MaterialAspect& theMat) override
  {
    myDrawer->ShadingAspect()->SetMaterial(theMat);
    myBoxEdgeAspect->SetMaterial(theMat);
    myBoxCornerAspect->SetMaterial(theMat);
    SynchronizeAspects();
  }

  //! Sets the material for the interactive object.
  void UnsetMaterial() override
  {
    Graphic3d_MaterialAspect aMat(Graphic3d_NameOfMaterial_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_EXPORT double Duration() const;

  //! Set duration of animation.
  //! @param[in] theValue  input value of duration in seconds
  Standard_EXPORT void SetDuration(double 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.
  bool ToResetCameraUp() const { return myToResetCameraUp; }

  //! Set if new camera Up direction should be always set to default value for a new camera
  //! Direction.
  void SetResetCamera(bool theToReset) { myToResetCameraUp = theToReset; }

  //! Return TRUE if animation should fit selected objects and FALSE to fit entire scene; TRUE by
  //! default.
  bool ToFitSelected() const { return myToFitSelected; }

  //! Set if animation should fit selected objects or to fit entire scene.
  void SetFitSelected(bool theToFitSelected) { myToFitSelected = theToFitSelected; }

  //! @return TRUE if View Cube has unfinished animation of view camera.
  Standard_EXPORT bool HasAnimation() const;

  //! Start camera transformation corresponding to the input detected owner.
  //! @param[in] theOwner  detected owner.
  Standard_EXPORT virtual void StartAnimation(const occ::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 bool UpdateAnimation(const bool theToUpdate);

  //! Perform camera transformation corresponding to the input detected owner.
  Standard_EXPORT virtual void HandleClick(const occ::handle<AIS_ViewCubeOwner>& theOwner);

protected:
  //! Perform internal single step of animation.
  //! @return FALSE if animation has been finished
  Standard_EXPORT bool updateAnimation();

  //! Fit selected/all into view.
  //! @param[in] theView  view definition to retrieve scene bounding box
  //! @param theCamera [in,out] camera definition
  Standard_EXPORT virtual void viewFitAll(const occ::handle<V3d_View>&         theView,
                                          const occ::handle<Graphic3d_Camera>& theCamera);

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.
  bool AcceptDisplayMode(const int theMode) const override { return theMode == 0; }

  //! Global selection has no meaning for this class.
  occ::handle<SelectMgr_EntityOwner> GlobalSelOwner() const override
  {
    return occ::handle<SelectMgr_EntityOwner>();
  }

  //! Compute 3D part of View Cube.
  //! @param[in] thePrsMgr  presentation manager.
  //! @param[in] thePrs  input presentation that is to be filled with flat presentation primitives.
  //! @param[in] theMode  display mode.
  //! @warning this object accept only 0 display mode.
  Standard_EXPORT void Compute(const occ::handle<PrsMgr_PresentationManager>& thePrsMgr,
                               const occ::handle<Prs3d_Presentation>&         thePrs,
                               const int                                      theMode = 0) override;

  //! Redefine computing of sensitive entities for View Cube.
  //! @param[in] theSelection  input selection object that is to be filled with sensitive entities.
  //! @param[in] theMode  selection mode.
  //! @warning object accepts only 0 selection mode.
  Standard_EXPORT void ComputeSelection(const occ::handle<SelectMgr_Selection>& theSelection,
                                        const int                               theMode) override;

  //! Disables auto highlighting to use HilightSelected() and HilightOwnerWithColor() overridden
  //! methods.
  bool IsAutoHilight() const override { return false; }

  //! Method which clear all selected owners belonging to this selectable object.
  //! @warning this object does not support selection.
  void ClearSelected() override {}

  //! Method which highlights input owner belonging to this selectable object.
  //! @param[in] thePM  presentation manager
  //! @param[in] theStyle  style for dynamic highlighting.
  //! @param[in] theOwner  input entity owner.
  Standard_EXPORT void HilightOwnerWithColor(
    const occ::handle<PrsMgr_PresentationManager>& thePM,
    const occ::handle<Prs3d_Drawer>&               theStyle,
    const occ::handle<SelectMgr_EntityOwner>&      theOwner) override;

  //! Method which draws selected owners.
  Standard_EXPORT void HilightSelected(
    const occ::handle<PrsMgr_PresentationManager>&                  thePM,
    const NCollection_Sequence<occ::handle<SelectMgr_EntityOwner>>& theSeq) override;

  //! Set default parameters for visual attributes
  //! @sa Attributes()
  void UnsetAttributes() override
  {
    setDefaultAttributes();
    SetToUpdate();
  }

  //! Set default parameters for dynamic highlighting attributes, reset highlight attributes
  void UnsetHilightAttributes() 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.
  //! @param theTris    [in,out] triangulation to fill, or NULL to return size
  //! @param theNbNodes [in,out] should be incremented by a number of nodes defining this
  //! triangulation
  //! @param theNbTris  [in,out] should be incremented by a number of triangles defining this
  //! triangulation
  //! @param[in] theDir      part to define
  Standard_EXPORT virtual void createBoxPartTriangles(
    const occ::handle<Graphic3d_ArrayOfTriangles>& theTris,
    int&                                           theNbNodes,
    int&                                           theNbTris,
    V3d_TypeOfOrientation                          theDir) const;

  //! Create triangulation for a box side.
  //! @param theTris    [in,out] triangulation to fill, or NULL to return size
  //! @param theNbNodes [in,out] should be incremented by a number of nodes defining this
  //! triangulation
  //! @param theNbTris  [in,out] should be incremented by a number of triangles defining this
  //! triangulation
  //! @param[in] theDir      part to define
  Standard_EXPORT virtual void createBoxSideTriangles(
    const occ::handle<Graphic3d_ArrayOfTriangles>& theTris,
    int&                                           theNbNodes,
    int&                                           theNbTris,
    V3d_TypeOfOrientation                          theDir) const;

  //! Create triangulation for a box edge.
  //! @param theTris    [in,out] triangulation to fill, or NULL to return size
  //! @param theNbNodes [in,out] should be incremented by a number of nodes defining this
  //! triangulation
  //! @param theNbTris  [in,out] should be incremented by a number of triangles defining this
  //! triangulation
  //! @param[in] theDir      part to define
  Standard_EXPORT virtual void createBoxEdgeTriangles(
    const occ::handle<Graphic3d_ArrayOfTriangles>& theTris,
    int&                                           theNbNodes,
    int&                                           theNbTris,
    V3d_TypeOfOrientation                          theDir) const;

  //! Create triangulation for a box corner (vertex).
  //! @param theTris    [in,out] triangulation to fill, or NULL to return size
  //! @param theNbNodes [in,out] should be incremented by a number of nodes defining this
  //! triangulation
  //! @param theNbTris  [in,out] should be incremented by a number of triangles defining this
  //! triangulation
  //! @param[in] theDir      part to define
  Standard_EXPORT virtual void createBoxCornerTriangles(
    const occ::handle<Graphic3d_ArrayOfTriangles>& theTris,
    int&                                           theNbNodes,
    int&                                           theNbTris,
    V3d_TypeOfOrientation                          theDir) const;

protected:
  //! Create triangulation for a rectangle with round corners.
  //! @param theTris    [in,out] triangulation to fill, or NULL to return size
  //! @param theNbNodes [in,out] should be incremented by a number of nodes defining this
  //! triangulation
  //! @param theNbTris  [in,out] should be incremented by a number of triangles defining this
  //! triangulation
  //! @param[in] theSize     rectangle dimensions
  //! @param[in] theRadius   radius at corners
  //! @param[in] theTrsf     transformation
  Standard_EXPORT static void createRoundRectangleTriangles(
    const occ::handle<Graphic3d_ArrayOfTriangles>& theTris,
    int&                                           theNbNodes,
    int&                                           theNbTris,
    const gp_XY&                                   theSize,
    double                                         theRadius,
    const gp_Trsf&                                 theTrsf);

protected:
  NCollection_DataMap<V3d_TypeOfOrientation, TCollection_AsciiString>
    myBoxSideLabels; //!< map with box side labels
  NCollection_DataMap<Prs3d_DatumParts, TCollection_AsciiString>
                                   myAxesLabels;      //!< map with axes labels
  occ::handle<Prs3d_ShadingAspect> myBoxEdgeAspect;   //!< style for box edges
  occ::handle<Prs3d_ShadingAspect> myBoxCornerAspect; //!< style for box corner

  double mySize;              //!< size of box side, length of one axis
  double myBoxEdgeMinSize;    //!< minimal size of box edge
  double myBoxEdgeGap;        //!< gap between box side and box edge
  double myBoxFacetExtension; //!< box facet extension
  double myAxesPadding;       //!< Padding between box and axes
  // clang-format off
  double                 myAxesRadius;        //!< radius of axes of the trihedron; 1.0 by default
  double                 myAxesConeRadius;    //!< radius of cone of axes of the trihedron; 3.0 by default
  double                 myAxesSphereRadius;  //!< radius of sphere (central point) of the trihedron; 4.0 by default
  double                 myCornerMinSize;     //!< minimal size of box corner
  double                 myRoundRadius;       //!< relative round radius within [0; 0.5] range
  bool              myToDisplayAxes;     //!< trihedron visibility
  bool              myToDisplayEdges;    //!< box edges visibility
  bool              myToDisplayVertices; //!< box corners (vertices) visibility
  bool              myIsYup;             //!< flag indicating that application expects Y-up viewer orientation instead of Z-up
  // clang-format on

protected:                                             //! @name Animation options
  occ::handle<AIS_AnimationCamera> myViewAnimation;    //!< Camera animation object
  occ::handle<Graphic3d_Camera>    myStartState;       //!< Start state of view camera
  occ::handle<Graphic3d_Camera>    myEndState;         //!< End state of view camera
  bool                             myToAutoStartAnim;  //!< start animation automatically on click
  bool                             myIsFixedAnimation; //!< fixed-loop animation
  bool                             myToFitSelected;    //!< fit selected or fit entire scene
  // clang-format off
  bool              myToResetCameraUp;   //!< always reset camera up direction to default
  // clang-format on
};

//! 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 occ::handle<AIS_ViewCube>& theObject,
                    V3d_TypeOfOrientation            theOrient,
                    int                              thePriority = 5)
      : SelectMgr_EntityOwner((const occ::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.
  bool IsForcedHilight() const override { return true; }

  //! Return new orientation to set.
  V3d_TypeOfOrientation MainOrientation() const { return myMainOrient; }

  //! Handle mouse button click event.
  bool HandleMouseClick(const NCollection_Vec2<int>& thePoint,
                        Aspect_VKeyMouse             theButton,
                        Aspect_VKeyFlags             theModifiers,
                        bool                         theIsDoubleClick) override
  {
    (void)thePoint;
    (void)theButton;
    (void)theModifiers;
    (void)theIsDoubleClick;
    AIS_ViewCube* aCubePrs = dynamic_cast<AIS_ViewCube*>(mySelectable);
    aCubePrs->HandleClick(this);
    return true;
  }

protected:
  V3d_TypeOfOrientation myMainOrient; //!< new orientation to set
};

//! Simple sensitive element for picking by point only.
class AIS_ViewCubeSensitive : public Select3D_SensitivePrimitiveArray
{
  DEFINE_STANDARD_RTTIEXT(AIS_ViewCubeSensitive, Select3D_SensitivePrimitiveArray)
public:
  //! Constructor.
  Standard_EXPORT AIS_ViewCubeSensitive(const occ::handle<SelectMgr_EntityOwner>&      theOwner,
                                        const occ::handle<Graphic3d_ArrayOfTriangles>& theTris);

  //! Checks whether element overlaps current selecting volume.
  Standard_EXPORT bool Matches(SelectBasics_SelectingVolumeManager& theMgr,
                               SelectBasics_PickResult&             thePickResult) override;

protected:
  //! Checks if picking ray can be used for detection.
  Standard_EXPORT bool isValidRay(const SelectBasics_SelectingVolumeManager& theMgr) const;
};

#endif // _AIS_ViewCube_HeaderFile
