diff --git a/src/DBRep/DBRep.cxx b/src/DBRep/DBRep.cxx index b37942f4c0..dfe37e3648 100644 --- a/src/DBRep/DBRep.cxx +++ b/src/DBRep/DBRep.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1118,73 +1119,124 @@ static Standard_Integer check(Draw_Interpretor& , //======================================================================= // normals //======================================================================= - -static Standard_Integer normals(Draw_Interpretor& di, - Standard_Integer n, const char** a) +static Standard_Integer normals (Draw_Interpretor& theDI, + Standard_Integer theArgNum, + const char** theArgs) { - if (n <= 1) return 1; - Standard_Real l = 1.; - if (n > 2) - l = Draw::Atof(a[2]); + if (theArgNum < 2) + { + std::cout << "Syntax error: wrong number of arguments!\n"; + theDI.PrintHelp (theArgs[0]); + return 1; + } - TopoDS_Shape S = DBRep::Get(a[1]); - if (S.IsNull()) return 1; + TopoDS_Shape aShape = DBRep::Get (theArgs[1]); + if (aShape.IsNull()) + { + std::cout << "Error: shape with name " << theArgs[1] << " is not found\n"; + return 1; + } + + Standard_Boolean toUseMesh = Standard_False; + Standard_Real aLength = 10.0; + Standard_Integer aNbAlongU = 1, aNbAlongV = 1; + for (Standard_Integer anArgIter = 2; anArgIter< theArgNum; ++anArgIter) + { + TCollection_AsciiString aParam (theArgs[anArgIter]); + aParam.LowerCase(); + if (anArgIter == 2 + && aParam.IsRealValue()) + { + aLength = aParam.RealValue(); + if (Abs (aLength) <= gp::Resolution()) + { + std::cout << "Syntax error: length should not be zero\n"; + return 1; + } + } + else if (aParam == "-usemesh" + || aParam == "-mesh") + { + toUseMesh = Standard_True; + } + else if (aParam == "-length" + || aParam == "-len") + { + ++anArgIter; + aLength = anArgIter < theArgNum ? Draw::Atof(theArgs[anArgIter]) : 0.0; + if (Abs(aLength) <= gp::Resolution()) + { + std::cout << "Syntax error: length should not be zero\n"; + return 1; + } + } + else if (aParam == "-nbalongu" + || aParam == "-nbu") + { + ++anArgIter; + aNbAlongU = anArgIter< theArgNum ? Draw::Atoi (theArgs[anArgIter]) : 0; + if (aNbAlongU < 1) + { + std::cout << "Syntax error: NbAlongU should be >=1\n"; + return 1; + } + } + else if (aParam == "-nbalongv" + || aParam == "-nbv") + { + ++anArgIter; + aNbAlongV = anArgIter< theArgNum ? Draw::Atoi (theArgs[anArgIter]) : 0; + if (aNbAlongV < 1) + { + std::cout << "Syntax error: NbAlongV should be >=1\n"; + return 1; + } + } + else if (aParam == "-nbalong" + || aParam == "-nbuv") + { + ++anArgIter; + aNbAlongU = anArgIter< theArgNum ? Draw::Atoi (theArgs[anArgIter]) : 0; + aNbAlongV = aNbAlongU; + if (aNbAlongU < 1) + { + std::cout << "Syntax error: NbAlong should be >=1\n"; + return 1; + } + } + else + { + std::cout << "Syntax error: unknwon argument '" << aParam << "'\n"; + return 1; + } + } DBRep_WriteColorOrientation(); - gp_Pnt P1,P2; - gp_Vec V,V1,V2; - Draw_Color col; - - TopExp_Explorer ex(S,TopAbs_FACE); - while (ex.More()) { - - const TopoDS_Face& F = TopoDS::Face(ex.Current()); - - // find the center of the minmax - BRepAdaptor_Surface SF(F); - - Standard_Real u, v, x; - - u = SF.FirstUParameter(); - x = SF.LastUParameter(); - if (Precision::IsInfinite(u)) - u = (Precision::IsInfinite(x)) ? 0. : x; - else if (!Precision::IsInfinite(x)) - u = (u+x) / 2.; - - v = SF.FirstVParameter(); - x = SF.LastVParameter(); - if (Precision::IsInfinite(v)) - v = (Precision::IsInfinite(x)) ? 0. : x; - else if (!Precision::IsInfinite(x)) - v = (v+x) / 2.; - - SF.D1(u,v,P1,V1,V2); - V = V1.Crossed(V2); - x = V.Magnitude(); - if (x > 1.e-10) - V.Multiply(l/x); - else { - V.SetCoord(l/2.,0,0); - di << "Null normal\n"; - } - - P2 = P1; - P2.Translate(V); - - col = DBRep_ColorOrientation(F.Orientation()); - - Handle(Draw_Segment3D) seg = new Draw_Segment3D(P1,P2,col); - dout << seg; - - - ex.Next(); + NCollection_DataMap > > aNormals; + if (toUseMesh) + { + DBRep_DrawableShape::addMeshNormals (aNormals, aShape, aLength); } + else + { + DBRep_DrawableShape::addSurfaceNormals (aNormals, aShape, aLength, aNbAlongU, aNbAlongV); + } + + for (NCollection_DataMap > >::Iterator aFaceIt (aNormals); aFaceIt.More(); aFaceIt.Next()) + { + const Draw_Color aColor = DBRep_ColorOrientation (aFaceIt.Key().Orientation()); + for (NCollection_Vector >::Iterator aNormalsIt (aFaceIt.Value()); aNormalsIt.More(); aNormalsIt.Next()) + { + const std::pair& aVec = aNormalsIt.Value(); + Handle(Draw_Segment3D) aSeg = new Draw_Segment3D(aVec.first, aVec.second, aColor); + dout << aSeg; + } + } + return 0; } - //======================================================================= //function : Set //purpose : @@ -1354,7 +1406,7 @@ void DBRep::BasicCommands(Draw_Interpretor& theCommands) theCommands.Add("treverse","treverse name1 name2 ...",__FILE__,orientation,g); theCommands.Add("complement","complement name1 name2 ...",__FILE__,orientation,g); theCommands.Add("invert","invert name, reverse subshapes",__FILE__,invert,g); - theCommands.Add("normals","normals s (length = 10), disp normals",__FILE__,normals,g); + theCommands.Add("normals","normals shape [Length {10}] [-NbAlongU {1}] [-NbAlongV {1}] [-UseMesh], display normals",__FILE__,normals,g); theCommands.Add("nbshapes", "\n nbshapes s - shows the number of sub-shapes in ;\n nbshapes s -t - shows the number of sub-shapes in counting the same sub-shapes with different location as different sub-shapes.", __FILE__,nbshapes,g); diff --git a/src/DBRep/DBRep_DrawableShape.cxx b/src/DBRep/DBRep_DrawableShape.cxx index 05581002c9..9165396c32 100644 --- a/src/DBRep/DBRep_DrawableShape.cxx +++ b/src/DBRep/DBRep_DrawableShape.cxx @@ -1174,3 +1174,161 @@ void DBRep_DrawableShape::display(const Handle(Poly_Triangulation)& T, } } +//======================================================================= +//function : addMeshNormals +//purpose : +//======================================================================= +Standard_Boolean DBRep_DrawableShape::addMeshNormals (NCollection_Vector >& theNormals, + const TopoDS_Face& theFace, + const Standard_Real theLength) +{ + TopLoc_Location aLoc; + const Handle(Poly_Triangulation)& aTriangulation = BRep_Tool::Triangulation (theFace, aLoc); + const Standard_Boolean hasNormals = aTriangulation->HasNormals(); + if (aTriangulation.IsNull() + || (!hasNormals && !aTriangulation->HasUVNodes())) + { + return Standard_False; + } + + const TColgp_Array1OfPnt& aNodes = aTriangulation->Nodes(); + BRepAdaptor_Surface aSurface (theFace); + for (Standard_Integer aNodeIter = aNodes.Lower(); aNodeIter <= aNodes.Upper(); ++aNodeIter) + { + gp_Pnt aP1 = aNodes (aNodeIter); + if (!aLoc.IsIdentity()) + { + aP1.Transform (aLoc.Transformation()); + } + + gp_Vec aNormal; + if (hasNormals) + { + aNormal = aTriangulation->Normal (aNodeIter); + } + else + { + const gp_Pnt2d& aUVNode = aTriangulation->UVNode (aNodeIter); + gp_Pnt aDummyPnt; + gp_Vec aV1, aV2; + aSurface.D1 (aUVNode.X(), aUVNode.Y(), aDummyPnt, aV1, aV2); + aNormal = aV1.Crossed (aV2); + } + + const Standard_Real aNormalLen = aNormal.Magnitude(); + if (aNormalLen > 1.e-10) + { + aNormal.Multiply (theLength / aNormalLen); + } + else + { + aNormal.SetCoord (aNormalLen / 2.0, 0.0, 0.0); + std::cout << "Null normal at node X = " << aP1.X() << ", Y = " << aP1.Y() << ", Z = " << aP1.Z() << "\n"; + } + + const gp_Pnt aP2 = aP1.Translated (aNormal); + theNormals.Append (std::pair (aP1, aP2)); + } + return Standard_True; +} + +//======================================================================= +//function : addMeshNormals +//purpose : +//======================================================================= +void DBRep_DrawableShape::addMeshNormals (NCollection_DataMap > > & theNormals, + const TopoDS_Shape& theShape, + const Standard_Real theLength) +{ + TopLoc_Location aLoc; + for (TopExp_Explorer aFaceIt(theShape, TopAbs_FACE); aFaceIt.More(); aFaceIt.Next()) + { + const TopoDS_Face& aFace = TopoDS::Face(aFaceIt.Current()); + NCollection_Vector >* aFaceNormals = theNormals.ChangeSeek(aFace); + if (aFaceNormals == NULL) + { + aFaceNormals = theNormals.Bound(aFace, NCollection_Vector >()); + } + + addMeshNormals (*aFaceNormals, aFace, theLength); + } +} + +//======================================================================= +//function : addSurfaceNormals +//purpose : +//======================================================================= +Standard_Boolean DBRep_DrawableShape::addSurfaceNormals (NCollection_Vector >& theNormals, + const TopoDS_Face& theFace, + const Standard_Real theLength, + const Standard_Integer theNbAlongU, + const Standard_Integer theNbAlongV) +{ + { + TopLoc_Location aLoc; + const Handle(Geom_Surface)& aSurface = BRep_Tool::Surface (theFace, aLoc); + if (aSurface.IsNull()) + { + return Standard_False; + } + } + + Standard_Real aUmin = 0.0, aVmin = 0.0, aUmax = 0.0, aVmax = 0.0; + BRepTools::UVBounds (theFace, aUmin, aUmax, aVmin, aVmax); + const Standard_Boolean isUseMidU = (theNbAlongU == 1); + const Standard_Boolean isUseMidV = (theNbAlongV == 1); + const Standard_Real aDU = (aUmax - aUmin) / (isUseMidU ? 2 : (theNbAlongU - 1)); + const Standard_Real aDV = (aVmax - aVmin) / (isUseMidV ? 2 : (theNbAlongV - 1)); + + BRepAdaptor_Surface aSurface (theFace); + for (Standard_Integer aUIter = 0; aUIter < theNbAlongU; ++aUIter) + { + const Standard_Real aU = aUmin + (isUseMidU ? 1 : aUIter) * aDU; + for (Standard_Integer aVIter = 0; aVIter < theNbAlongV; ++aVIter) + { + const Standard_Real aV = aVmin + (isUseMidV ? 1 : aVIter) * aDV; + + gp_Pnt aP1; + gp_Vec aV1, aV2; + aSurface.D1 (aU, aV, aP1, aV1, aV2); + + gp_Vec aVec = aV1.Crossed (aV2); + Standard_Real aNormalLen = aVec.Magnitude(); + if (aNormalLen > 1.e-10) + { + aVec.Multiply (theLength / aNormalLen); + } + else + { + aVec.SetCoord (aNormalLen / 2.0, 0.0, 0.0); + std::cout << "Null normal at U = " << aU << ", V = " << aV << "\n"; + } + + const gp_Pnt aP2 = aP1.Translated(aVec); + theNormals.Append (std::pair (aP1, aP2)); + } + } + return Standard_True; +} + +//======================================================================= +//function : addSurfaceNormals +//purpose : +//======================================================================= +void DBRep_DrawableShape::addSurfaceNormals (NCollection_DataMap > >& theNormals, + const TopoDS_Shape& theShape, + const Standard_Real theLength, + const Standard_Integer theNbAlongU, + const Standard_Integer theNbAlongV) +{ + for (TopExp_Explorer aFaceIt (theShape, TopAbs_FACE); aFaceIt.More(); aFaceIt.Next()) + { + const TopoDS_Face& aFace = TopoDS::Face (aFaceIt.Current()); + NCollection_Vector >* aFaceNormals = theNormals.ChangeSeek (aFace); + if (aFaceNormals == NULL) + { + aFaceNormals = theNormals.Bound (aFace, NCollection_Vector >()); + } + addSurfaceNormals (*aFaceNormals, aFace, theLength, theNbAlongU, theNbAlongV); + } +} diff --git a/src/DBRep/DBRep_DrawableShape.hxx b/src/DBRep/DBRep_DrawableShape.hxx index cc7b487410..1176bca039 100644 --- a/src/DBRep/DBRep_DrawableShape.hxx +++ b/src/DBRep/DBRep_DrawableShape.hxx @@ -17,39 +17,27 @@ #ifndef _DBRep_DrawableShape_HeaderFile #define _DBRep_DrawableShape_HeaderFile -#include -#include - -#include #include #include #include -#include -#include #include -#include #include -#include #include -class Standard_DomainError; -class TopoDS_Shape; -class Draw_Color; +#include +#include +#include +#include + class Draw_Display; class Poly_Triangulation; class gp_Trsf; -class Draw_Drawable3D; - - -class DBRep_DrawableShape; -DEFINE_STANDARD_HANDLE(DBRep_DrawableShape, Draw_Drawable3D) //! Drawable structure to display a shape. Contains a //! list of edges and a list of faces. class DBRep_DrawableShape : public Draw_Drawable3D { - + DEFINE_STANDARD_RTTIEXT(DBRep_DrawableShape, Draw_Drawable3D) public: - Standard_EXPORT DBRep_DrawableShape(const TopoDS_Shape& C, const Draw_Color& FreeCol, const Draw_Color& ConnCol, const Draw_Color& EdgeCol, const Draw_Color& IsosCol, const Standard_Real size, const Standard_Integer nbisos, const Standard_Integer discret); @@ -106,10 +94,50 @@ public: //! u,v are the parameters of the closest point. Standard_EXPORT static void LastPick (TopoDS_Shape& S, Standard_Real& u, Standard_Real& v); +public: + //! Auxiliary method computing nodal normals for presentation purposes. + //! @param theNormals [out] vector of computed normals (pair of points [from, to]) + //! @param theFace [in] input face + //! @param theLength [in] normal length + //! @return FALSE if normals can not be computed + Standard_EXPORT static Standard_Boolean addMeshNormals (NCollection_Vector >& theNormals, + const TopoDS_Face& theFace, + const Standard_Real theLength); + //! Auxiliary method computing nodal normals for presentation purposes. + //! @param theNormals [out] map of computed normals (grouped per Face) + //! @param theShape [in] input shape which will be exploded into Faces + //! @param theLength [in] normal length + Standard_EXPORT static void addMeshNormals (NCollection_DataMap > > & theNormals, + const TopoDS_Shape& theShape, + const Standard_Real theLength); - DEFINE_STANDARD_RTTIEXT(DBRep_DrawableShape,Draw_Drawable3D) + //! Auxiliary method computing surface normals distributed within the Face for presentation purposes. + //! @param theNormals [out] vector of computed normals (pair of points [from, to]) + //! @param theFace [in] input face + //! @param theLength [in] normal length + //! @param theNbAlongU [in] number along U + //! @param theNbAlongV [in] number along V + //! @return FALSE if normals can not be computed + Standard_EXPORT static Standard_Boolean addSurfaceNormals (NCollection_Vector >& theNormals, + const TopoDS_Face& theFace, + const Standard_Real theLength, + const Standard_Integer theNbAlongU, + const Standard_Integer theNbAlongV); + + //! Auxiliary method computing surface normals distributed within the Face for presentation purposes. + //! @param theNormals [out] map of computed normals (grouped per Face) + //! @param theShape [in] input shape which will be exploded into Faces + //! @param theLength [in] normal length + //! @param theNbAlongU [in] number along U + //! @param theNbAlongV [in] number along V + //! @return FALSE if normals can not be computed + Standard_EXPORT static void addSurfaceNormals (NCollection_DataMap > >& theNormals, + const TopoDS_Shape& theShape, + const Standard_Real theLength, + const Standard_Integer theNbAlongU, + const Standard_Integer theNbAlongV); private: @@ -142,13 +170,8 @@ private: Standard_Boolean myHid; Standard_Real myAng; - }; - - - - - +DEFINE_STANDARD_HANDLE(DBRep_DrawableShape, Draw_Drawable3D) #endif // _DBRep_DrawableShape_HeaderFile diff --git a/src/ViewerTest/ViewerTest_ObjectCommands.cxx b/src/ViewerTest/ViewerTest_ObjectCommands.cxx index 24092370e0..2795ce9b61 100644 --- a/src/ViewerTest/ViewerTest_ObjectCommands.cxx +++ b/src/ViewerTest/ViewerTest_ObjectCommands.cxx @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -129,6 +130,8 @@ #include #include +#include +#include #include #include #include @@ -5934,6 +5937,217 @@ static int VPriority (Draw_Interpretor& theDI, return 0; } +//! Auxiliary class for command vnormals. +class MyShapeWithNormals : public AIS_Shape +{ + DEFINE_STANDARD_RTTI_INLINE(MyShapeWithNormals, AIS_Shape); +public: + + Standard_Real NormalLength; + Standard_Integer NbAlongU; + Standard_Integer NbAlongV; + Standard_Boolean ToUseMesh; + Standard_Boolean ToOrient; + +public: + + //! Main constructor. + MyShapeWithNormals (const TopoDS_Shape& theShape) + : AIS_Shape (theShape), + NormalLength(10), + NbAlongU (1), + NbAlongV (1), + ToUseMesh (Standard_False), + ToOrient (Standard_False) {} + +protected: + + //! Comnpute presentation. + virtual void Compute (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) Standard_OVERRIDE + { + AIS_Shape::Compute (thePrsMgr, thePrs, theMode); + + NCollection_DataMap > > aNormalMap; + if (ToUseMesh) + { + DBRep_DrawableShape::addMeshNormals (aNormalMap, myshape, NormalLength); + } + else + { + DBRep_DrawableShape::addSurfaceNormals (aNormalMap, myshape, NormalLength, NbAlongU, NbAlongV); + } + + Handle(Graphic3d_Group) aPrsGroup = Prs3d_Root::NewGroup (thePrs); + aPrsGroup->SetGroupPrimitivesAspect (myDrawer->ArrowAspect()->Aspect()); + + const Standard_Real aArrowAngle = myDrawer->ArrowAspect()->Angle(); + const Standard_Real aArrowLength = myDrawer->ArrowAspect()->Length(); + for (NCollection_DataMap > >::Iterator aFaceIt (aNormalMap); + aFaceIt.More(); aFaceIt.Next()) + { + const Standard_Boolean toReverse = ToOrient && aFaceIt.Key().Orientation() == TopAbs_REVERSED; + Handle(Graphic3d_ArrayOfSegments) aSegments = new Graphic3d_ArrayOfSegments (2 * aFaceIt.Value().Size()); + for (NCollection_Vector >::Iterator aPntIt (aFaceIt.Value()); aPntIt.More(); aPntIt.Next()) + { + std::pair aPair = aPntIt.Value(); + if (toReverse) + { + const gp_Vec aDir = aPair.first.XYZ() - aPair.second.XYZ(); + aPair.second = aPair.first.XYZ() + aDir.XYZ(); + } + + aSegments->AddVertex (aPair.first); + aSegments->AddVertex (aPair.second); + Prs3d_Arrow::Draw (aPrsGroup, aPair.second, gp_Vec(aPair.first, aPair.second), aArrowAngle, aArrowLength); + } + + aPrsGroup->AddPrimitiveArray (aSegments); + } + } + +}; + +//======================================================================= +//function : VNormals +//purpose : Displays/Hides normals calculated on shape geometry or retrieved from triangulation +//======================================================================= +static int VNormals (Draw_Interpretor& theDI, + Standard_Integer theArgNum, + const char** theArgs) +{ + Handle(AIS_InteractiveContext) aContext = ViewerTest::GetAISContext(); + if (aContext.IsNull()) + { + std::cout << "Error: no view available, call 'vinit' before!\n"; + return 1; + } + else if (theArgNum < 2) + { + std::cout << "Error: wrong number of arguments! See usage:\n"; + theDI.PrintHelp (theArgs[0]); + return 1; + } + + Standard_Integer anArgIter = 1; + Standard_CString aShapeName = theArgs[anArgIter++]; + TopoDS_Shape aShape = DBRep::Get (aShapeName); + Standard_Boolean isOn = Standard_True; + if (aShape.IsNull()) + { + std::cout << "Error: shape with name '" << aShapeName << "' is not found\n"; + return 1; + } + + ViewerTest_DoubleMapOfInteractiveAndName& aMap = GetMapOfAIS(); + Handle(MyShapeWithNormals) aShapePrs; + if (aMap.IsBound2 (aShapeName)) + { + aShapePrs = Handle(MyShapeWithNormals)::DownCast (aMap.Find2 (aShapeName)); + } + + Standard_Boolean isUseMesh = Standard_False; + Standard_Real aLength = 10.0; + Standard_Integer aNbAlongU = 1, aNbAlongV = 1; + Standard_Boolean isOriented = Standard_False; + for (; anArgIter < theArgNum; ++anArgIter) + { + TCollection_AsciiString aParam (theArgs[anArgIter]); + aParam.LowerCase(); + if (anArgIter == 2 + && ViewerTest::ParseOnOff (aParam.ToCString(), isOn)) + { + continue; + } + else if (aParam == "-usemesh" + || aParam == "-mesh") + { + isUseMesh = Standard_True; + } + else if (aParam == "-length" + || aParam == "-len") + { + ++anArgIter; + aLength = anArgIter < theArgNum ? Draw::Atof (theArgs[anArgIter]) : 0.0; + if (Abs (aLength) <= gp::Resolution()) + { + std::cout << "Syntax error: length should not be zero\n"; + return 1; + } + } + else if (aParam == "-orient" + || aParam == "-oriented") + { + isOriented = Standard_True; + if (anArgIter + 1 < theArgNum + && ViewerTest::ParseOnOff (theArgs[anArgIter + 1], isOriented)) + { + ++anArgIter; + } + } + else if (aParam == "-nbalongu" + || aParam == "-nbu") + { + ++anArgIter; + aNbAlongU = anArgIter < theArgNum ? Draw::Atoi (theArgs[anArgIter]) : 0; + if (aNbAlongU < 1) + { + std::cout << "Syntax error: NbAlongU should be >=1\n"; + return 1; + } + } + else if (aParam == "-nbalongv" + || aParam == "-nbv") + { + ++anArgIter; + aNbAlongV = anArgIter < theArgNum ? Draw::Atoi (theArgs[anArgIter]) : 0; + if (aNbAlongV < 1) + { + std::cout << "Syntax error: NbAlongV should be >=1\n"; + return 1; + } + } + else if (aParam == "-nbalong" + || aParam == "-nbuv") + { + ++anArgIter; + aNbAlongU = anArgIter < theArgNum ? Draw::Atoi (theArgs[anArgIter]) : 0; + aNbAlongV = aNbAlongU; + if (aNbAlongU < 1) + { + std::cout << "Syntax error: NbAlong should be >=1\n"; + return 1; + } + } + else + { + std::cout << "Syntax error: unknwon argument '" << aParam << "'\n"; + return 1; + } + } + + if (isOn) + { + if (aShapePrs.IsNull()) + { + aShapePrs = new MyShapeWithNormals (aShape); + } + aShapePrs->ToUseMesh = isUseMesh; + aShapePrs->ToOrient = isOriented; + aShapePrs->NormalLength = aLength; + aShapePrs->NbAlongU = aNbAlongU; + aShapePrs->NbAlongV = aNbAlongV; + VDisplayAISObject (aShapeName, aShapePrs); + } + else if (!aShapePrs.IsNull()) + { + VDisplayAISObject (aShapeName, new AIS_Shape (aShape)); + } + + return 0; +} + //======================================================================= //function : ObjectsCommands //purpose : @@ -6232,4 +6446,11 @@ void ViewerTest::ObjectCommands(Draw_Interpretor& theCommands) "vpriority [-noupdate|-update] name [value]\n\t\t prints or sets the display priority for an object", __FILE__, VPriority, group); + + theCommands.Add ("vnormals", + "vnormals usage:\n" + "vnormals Shape [{on|off}=on] [-length {10}] [-nbAlongU {1}] [-nbAlongV {1}] [-nbAlong {1}]" + "\n\t\t: [-useMesh] [-oriented {0}1}=0]" + "\n\t\t: Displays/Hides normals calculated on shape geometry or retrieved from triangulation", + __FILE__, VNormals, group); }