Added PLY writing tools RWPly_CafWriter and RWPly_PlyWriterContext.
Added tool BRepLib_PointCloudShape generating point cloud from shape in two ways:
- random points on surface with specified density;
- points from triangulation nodes.
StdPrs_ToolTriangulatedShape::ComputeNormals() has been moved to
BRepLib_ToolTriangulatedShape for reusing outside of AIS.
Command vpointcloud has been extended to use new generation tool.
Command writeply has been added to write triangulation or point set into PLY format.
n RWGltf
n RWMesh
n RWObj
+n RWPly
n DFBrowser
n DFBrowserPane
n DFBrowserPaneXDE
--- /dev/null
+// Copyright (c) 2021 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 <BRepLib_PointCloudShape.hxx>
+
+#include <BRep_Tool.hxx>
+#include <BRepGProp.hxx>
+#include <BRepLib_ToolTriangulatedShape.hxx>
+#include <BRepTools.hxx>
+#include <BRepTopAdaptor_FClass2d.hxx>
+#include <Geom_Surface.hxx>
+#include <GProp_GProps.hxx>
+#include <gp_Pnt.hxx>
+#include <gp_Vec.hxx>
+#include <Precision.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopoDS_Shape.hxx>
+
+#include <random>
+
+// =======================================================================
+// function : BRepLib_PointCloudShape
+// purpose :
+// =======================================================================
+BRepLib_PointCloudShape::BRepLib_PointCloudShape (const TopoDS_Shape& theShape,
+ const Standard_Real theTol)
+: myShape (theShape),
+ myDist (0.0),
+ myTol (theTol),
+ myNbPoints (0)
+{
+ //
+}
+
+// =======================================================================
+// function : ~BRepLib_PointCloudShape
+// purpose :
+// =======================================================================
+BRepLib_PointCloudShape::~BRepLib_PointCloudShape()
+{
+ //
+}
+
+// =======================================================================
+// function : NbPointsByDensity
+// purpose :
+// =======================================================================
+Standard_Integer BRepLib_PointCloudShape::NbPointsByDensity (const Standard_Real theDensity)
+{
+ clear();
+ Standard_Real aDensity = (theDensity < Precision::Confusion() ? computeDensity() : theDensity);
+ if (aDensity < Precision::Confusion())
+ {
+ return 0;
+ }
+
+ Standard_Integer aNbPoints = 0;
+ for (TopExp_Explorer aExpF(myShape, TopAbs_FACE); aExpF.More(); aExpF.Next())
+ {
+ Standard_Real anArea = faceArea(aExpF.Current());
+
+ Standard_Integer aNbPnts = Max ((Standard_Integer)std::ceil(anArea / theDensity), 1);
+ myFacePoints.Bind(aExpF.Current(), aNbPnts);
+ aNbPoints+= aNbPnts;
+ }
+ return aNbPoints;
+}
+
+// =======================================================================
+// function : GeneratePointsByDensity
+// purpose :
+// =======================================================================
+Standard_Boolean BRepLib_PointCloudShape::GeneratePointsByDensity (const Standard_Real theDensity)
+{
+ if (myFacePoints.IsEmpty())
+ {
+ if (NbPointsByDensity (theDensity) == 0)
+ {
+ return Standard_False;
+ }
+ }
+
+ Standard_Integer aNbAdded = 0;
+ for (TopExp_Explorer aExpF (myShape, TopAbs_FACE); aExpF.More(); aExpF.Next())
+ {
+ if (addDensityPoints (aExpF.Current()))
+ {
+ aNbAdded++;
+ }
+ }
+ return (aNbAdded > 0);
+}
+
+// =======================================================================
+// function : GeneratePointsByTriangulation
+// purpose :
+// =======================================================================
+Standard_Boolean BRepLib_PointCloudShape::GeneratePointsByTriangulation()
+{
+ clear();
+
+ Standard_Integer aNbAdded = 0;
+ for (TopExp_Explorer aExpF (myShape, TopAbs_FACE); aExpF.More(); aExpF.Next())
+ {
+ if (addTriangulationPoints (aExpF.Current()))
+ {
+ aNbAdded++;
+ }
+ }
+ return (aNbAdded > 0);
+}
+
+// =======================================================================
+// function : faceArea
+// purpose :
+// =======================================================================
+Standard_Real BRepLib_PointCloudShape::faceArea (const TopoDS_Shape& theShape)
+{
+ Standard_Real anArea = 0.0;
+ if (myFaceArea.Find (theShape, anArea))
+ {
+ return anArea;
+ }
+
+ GProp_GProps aFaceProps;
+ BRepGProp::SurfaceProperties (theShape, aFaceProps);
+ anArea = aFaceProps.Mass();
+ myFaceArea.Bind (theShape, anArea);
+ return anArea;
+}
+
+// =======================================================================
+// function : computeDensity
+// purpose :
+// =======================================================================
+Standard_Real BRepLib_PointCloudShape::computeDensity()
+{
+ // at first step find the face with smallest area
+ Standard_Real anAreaMin = Precision::Infinite();
+ for (TopExp_Explorer aExpF (myShape, TopAbs_FACE); aExpF.More(); aExpF.Next())
+ {
+ Standard_Real anArea = faceArea (aExpF.Current());
+ if (anArea < myTol * myTol)
+ {
+ continue;
+ }
+
+ if (anArea < anAreaMin)
+ {
+ anAreaMin = anArea;
+ }
+ }
+ return anAreaMin * 0.1;
+}
+
+// =======================================================================
+// function : NbPointsByTriangulation
+// purpose :
+// =======================================================================
+Standard_Integer BRepLib_PointCloudShape::NbPointsByTriangulation() const
+{
+ // at first step find the face with smallest area
+ Standard_Integer aNbPoints = 0;
+ for (TopExp_Explorer aExpF (myShape, TopAbs_FACE); aExpF.More(); aExpF.Next())
+ {
+ TopLoc_Location aLoc;
+ Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (TopoDS::Face (aExpF.Current()), aLoc);
+ if (aTriangulation.IsNull())
+ {
+ continue;
+ }
+
+ aNbPoints += aTriangulation->NbNodes();
+ }
+ return aNbPoints;
+}
+
+// =======================================================================
+// function : addDensityPoints
+// purpose :
+// =======================================================================
+Standard_Boolean BRepLib_PointCloudShape::addDensityPoints (const TopoDS_Shape& theFace)
+{
+ //addition of the points with specified density on the face by random way
+ Standard_Integer aNbPnts = (myFacePoints.IsBound (theFace) ? myFacePoints.Find (theFace) : 0);
+ if (aNbPnts == 0)
+ {
+ return Standard_False;
+ }
+
+ TopoDS_Face aFace = TopoDS::Face (theFace);
+ Standard_Real anUMin = 0.0, anUMax = 0.0, aVMin = 0.0, aVMax = 0.0;
+ BRepTools::UVBounds (aFace, anUMin, anUMax, aVMin, aVMax);
+ BRepTopAdaptor_FClass2d aClassifier (aFace, Precision::Confusion());
+
+ TopLoc_Location aLoc = theFace.Location();
+ const gp_Trsf& aTrsf = aLoc.Transformation();
+ TopLoc_Location aLoc1;
+ Handle(Geom_Surface) aSurf = BRep_Tool::Surface (aFace, aLoc1);
+ if (aSurf.IsNull())
+ {
+ return Standard_False;
+ }
+
+ std::mt19937 aRandomGenerator(0);
+ std::uniform_real_distribution<> anUDistrib(anUMin, anUMax);
+ std::uniform_real_distribution<> aVDistrib (aVMin, aVMax);
+ for (Standard_Integer nbCurPnts = 1; nbCurPnts <= aNbPnts;)
+ {
+ const Standard_Real aU = anUDistrib(aRandomGenerator);
+ const Standard_Real aV = aVDistrib (aRandomGenerator);
+ gp_Pnt2d aUVNode (aU, aV);
+ const TopAbs_State aState = aClassifier.Perform (aUVNode);
+ if (aState == TopAbs_OUT)
+ {
+ continue;
+ }
+
+ nbCurPnts++;
+
+ gp_Pnt aP1;
+ gp_Vec dU, dV;
+ aSurf->D1 (aU, aV, aP1, dU, dV);
+
+ gp_Vec aNorm = dU ^ dV;
+ if (aFace.Orientation() == TopAbs_REVERSED)
+ {
+ aNorm.Reverse();
+ }
+ const Standard_Real aNormMod = aNorm.Magnitude();
+ if (aNormMod > gp::Resolution())
+ {
+ aNorm /= aNormMod;
+ }
+ if (myDist > Precision::Confusion())
+ {
+ std::uniform_real_distribution<> aDistanceDistrib (0.0, myDist);
+ gp_XYZ aDeflPoint = aP1.XYZ() + aNorm.XYZ() * aDistanceDistrib (aRandomGenerator);
+ aP1.SetXYZ (aDeflPoint);
+ }
+ aP1.Transform (aTrsf);
+ if (aNormMod > gp::Resolution())
+ {
+ aNorm = gp_Dir (aNorm).Transformed (aTrsf);
+ }
+ addPoint (aP1, aNorm, aUVNode, aFace);
+ }
+ return Standard_True;
+}
+
+// =======================================================================
+// function : addTriangulationPoints
+// purpose :
+// =======================================================================
+Standard_Boolean BRepLib_PointCloudShape::addTriangulationPoints (const TopoDS_Shape& theFace)
+{
+ TopLoc_Location aLoc;
+ TopoDS_Face aFace = TopoDS::Face (theFace);
+ Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (aFace, aLoc);
+ if (aTriangulation.IsNull())
+ {
+ return Standard_False;
+ }
+
+ TopLoc_Location aLoc1;
+ Handle(Geom_Surface) aSurf = BRep_Tool::Surface (aFace, aLoc1);
+ const gp_Trsf& aTrsf = aLoc.Transformation();
+
+ BRepLib_ToolTriangulatedShape::ComputeNormals (aFace, aTriangulation);
+ Standard_Boolean aHasUVNode = aTriangulation->HasUVNodes();
+ for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter)
+ {
+ gp_Pnt aP1 = aTriangulation->Node (aNodeIter);
+ gp_Dir aNormal = aTriangulation->Normal(aNodeIter);
+ if (!aLoc.IsIdentity())
+ {
+ aP1 .Transform (aTrsf);
+ aNormal.Transform (aTrsf);
+ }
+
+ const gp_Pnt2d anUVNode = aHasUVNode ? aTriangulation->UVNode (aNodeIter) : gp_Pnt2d();
+ addPoint (aP1, aNormal, anUVNode, aFace);
+ }
+ return Standard_True;
+}
+
+// =======================================================================
+// function : clear
+// purpose :
+// =======================================================================
+void BRepLib_PointCloudShape::clear()
+{
+ myFaceArea.Clear();
+ myFacePoints.Clear();
+}
--- /dev/null
+// Copyright (c) 2021 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 _BRepLib_PointCloudShape_HeaderFile
+#define _BRepLib_PointCloudShape_HeaderFile
+
+#include <TopTools_DataMapOfShapeInteger.hxx>
+#include <TopTools_DataMapOfShapeReal.hxx>
+#include <Quantity_Color.hxx>
+#include <Precision.hxx>
+
+//! This tool is intended to get points from shape with specified distance from shape along normal.
+//! Can be used to simulation of points obtained in result of laser scan of shape.
+//! There are 2 ways for generation points by shape:
+//! 1. Generation points with specified density
+//! 2. Generation points using triangulation Nodes
+//! Generation of points by density using the GeneratePointsByDensity() function is not thread safe.
+class BRepLib_PointCloudShape
+{
+public:
+
+ DEFINE_STANDARD_ALLOC
+
+ //! Constructor initialized by shape
+ Standard_EXPORT BRepLib_PointCloudShape (const TopoDS_Shape& theShape = TopoDS_Shape(),
+ const Standard_Real theTol = Precision::Confusion());
+
+ //! Virtual destructor
+ Standard_EXPORT virtual ~BRepLib_PointCloudShape();
+
+ //! Return loaded shape.
+ const TopoDS_Shape& Shape() const { return myShape; }
+
+ //! Set shape.
+ void SetShape (const TopoDS_Shape& theShape) { myShape = theShape; }
+
+ //! Return tolerance.
+ Standard_Real Tolerance() const { return myTol; }
+
+ //! Set tolerance.
+ void SetTolerance (Standard_Real theTol) { myTol = theTol; }
+
+ //! Returns value of the distance to define deflection of points from shape along normal to shape; 0.0 by default.
+ Standard_Real GetDistance() const { return myDist; }
+
+ //! Sets value of the distance to define deflection of points from shape along normal to shape.
+ //! Negative values of theDist parameter are ignored.
+ void SetDistance (const Standard_Real theDist) { myDist = theDist; }
+
+ //! Returns size of the point cloud for specified density.
+ Standard_EXPORT Standard_Integer NbPointsByDensity (const Standard_Real theDensity = 0.0);
+
+ //! Returns size of the point cloud for using triangulation.
+ Standard_EXPORT Standard_Integer NbPointsByTriangulation() const;
+
+ //! Computes points with specified density for initial shape.
+ //! If parameter Density is equal to 0 then density will be computed automatically by criterion:
+ //! - 10 points per minimal unreduced face area.
+ //!
+ //! Note: this function should not be called from concurrent threads without external lock.
+ Standard_EXPORT Standard_Boolean GeneratePointsByDensity (const Standard_Real theDensity = 0.0);
+
+ //! Get points from triangulation existing in the shape.
+ Standard_EXPORT Standard_Boolean GeneratePointsByTriangulation();
+
+protected:
+
+ //! Compute area of the specified face.
+ Standard_EXPORT Standard_Real faceArea (const TopoDS_Shape& theShape);
+
+ //! Computes default density points per face.
+ Standard_EXPORT Standard_Real computeDensity();
+
+ //! Adds points to face in accordance with the specified density randomly in the specified range [0, Dist].
+ Standard_EXPORT Standard_Boolean addDensityPoints (const TopoDS_Shape& theFace);
+
+ //! Adds points to face by nodes of the existing triangulation randomly in the specified range [0, Dist].
+ Standard_EXPORT Standard_Boolean addTriangulationPoints (const TopoDS_Shape& theFace);
+
+protected:
+
+ //! Method to clear maps.
+ Standard_EXPORT virtual void clear();
+
+ //! Method to add point, normal to surface in this point and face for which point computed.
+ //! @param[in] thePoint 3D point on the surface
+ //! @param[in] theNorm surface normal at this point
+ //! @param[in] theUV surface UV parameters
+ //! @param[in] theFace surface (face) definition
+ Standard_EXPORT virtual void addPoint (const gp_Pnt& thePoint,
+ const gp_Vec& theNorm,
+ const gp_Pnt2d& theUV,
+ const TopoDS_Shape& theFace) = 0;
+
+protected:
+
+ TopoDS_Shape myShape;
+ Standard_Real myDist;
+ Standard_Real myTol;
+ TopTools_DataMapOfShapeReal myFaceArea;
+ TopTools_DataMapOfShapeInteger myFacePoints;
+ Standard_Integer myNbPoints;
+
+};
+
+#endif // _BRepLib_PointCloudShape_HeaderFile
--- /dev/null
+// Copyright (c) 2021 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 <BRepLib_ToolTriangulatedShape.hxx>
+
+#include <BRep_Tool.hxx>
+#include <GeomLib.hxx>
+#include <Poly.hxx>
+#include <Poly_Connect.hxx>
+#include <Precision.hxx>
+#include <TopLoc_Location.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Face.hxx>
+
+// =======================================================================
+// function : ComputeNormals
+// purpose :
+// =======================================================================
+void BRepLib_ToolTriangulatedShape::ComputeNormals (const TopoDS_Face& theFace,
+ const Handle(Poly_Triangulation)& theTris,
+ Poly_Connect& thePolyConnect)
+{
+ if (theTris.IsNull()
+ || theTris->HasNormals())
+ {
+ return;
+ }
+
+ // take in face the surface location
+ const TopoDS_Face aZeroFace = TopoDS::Face (theFace.Located (TopLoc_Location()));
+ Handle(Geom_Surface) aSurf = BRep_Tool::Surface (aZeroFace);
+ if (!theTris->HasUVNodes() || aSurf.IsNull())
+ {
+ // compute normals by averaging triangulation normals sharing the same vertex
+ Poly::ComputeNormals (theTris);
+ return;
+ }
+
+ const Standard_Real aTol = Precision::Confusion();
+ Standard_Integer aTri[3];
+ gp_Dir aNorm;
+ theTris->AddNormals();
+ for (Standard_Integer aNodeIter = 1; aNodeIter <= theTris->NbNodes(); ++aNodeIter)
+ {
+ // try to retrieve normal from real surface first, when UV coordinates are available
+ if (GeomLib::NormEstim (aSurf, theTris->UVNode (aNodeIter), aTol, aNorm) > 1)
+ {
+ if (thePolyConnect.Triangulation() != theTris)
+ {
+ thePolyConnect.Load (theTris);
+ }
+
+ // compute flat normals
+ gp_XYZ eqPlan (0.0, 0.0, 0.0);
+ for (thePolyConnect.Initialize (aNodeIter); thePolyConnect.More(); thePolyConnect.Next())
+ {
+ theTris->Triangle (thePolyConnect.Value()).Get (aTri[0], aTri[1], aTri[2]);
+ const gp_XYZ v1 (theTris->Node (aTri[1]).Coord() - theTris->Node (aTri[0]).Coord());
+ const gp_XYZ v2 (theTris->Node (aTri[2]).Coord() - theTris->Node (aTri[1]).Coord());
+ const gp_XYZ vv = v1 ^ v2;
+ const Standard_Real aMod = vv.Modulus();
+ if (aMod >= aTol)
+ {
+ eqPlan += vv / aMod;
+ }
+ }
+ const Standard_Real aModMax = eqPlan.Modulus();
+ aNorm = (aModMax > aTol) ? gp_Dir (eqPlan) : gp::DZ();
+ }
+
+ theTris->SetNormal (aNodeIter, aNorm);
+ }
+}
--- /dev/null
+// Copyright (c) 2021 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 _BrepLib_ToolTriangulatedShape_HeaderFile
+#define _BrepLib_ToolTriangulatedShape_HeaderFile
+
+#include <Poly_Connect.hxx>
+#include <Poly_Triangulation.hxx>
+
+class TopoDS_Face;
+class Poly_Triangulation;
+
+//! Provides methods for calculating normals to Poly_Triangulation of TopoDS_Face.
+class BRepLib_ToolTriangulatedShape
+{
+public:
+
+ //! Computes nodal normals for Poly_Triangulation structure using UV coordinates and surface.
+ //! Does nothing if triangulation already defines normals.
+ //! @param[in] theFace the face
+ //! @param[in] theTris the definition of a face triangulation
+ static void ComputeNormals (const TopoDS_Face& theFace,
+ const Handle(Poly_Triangulation)& theTris)
+ {
+ Poly_Connect aPolyConnect;
+ ComputeNormals (theFace, theTris, aPolyConnect);
+ }
+
+ //! Computes nodal normals for Poly_Triangulation structure using UV coordinates and surface.
+ //! Does nothing if triangulation already defines normals.
+ //! @param[in] theFace the face
+ //! @param[in] theTris the definition of a face triangulation
+ //! @param[in,out] thePolyConnect optional, initialized tool for exploring triangulation
+ Standard_EXPORT static void ComputeNormals (const TopoDS_Face& theFace,
+ const Handle(Poly_Triangulation)& theTris,
+ Poly_Connect& thePolyConnect);
+
+};
+
+#endif
BRepLib_MakeWire.cxx
BRepLib_MakeWire.hxx
BRepLib_MakeWire_1.cxx
+BRepLib_PointCloudShape.hxx
+BRepLib_PointCloudShape.cxx
BRepLib_ShapeModification.hxx
BRepLib_ShellError.hxx
+BRepLib_ToolTriangulatedShape.hxx
+BRepLib_ToolTriangulatedShape.cxx
BRepLib_ValidateEdge.cxx
BRepLib_ValidateEdge.hxx
BRepLib_WireError.hxx
--- /dev/null
+RWPly_CafWriter.cxx
+RWPly_CafWriter.hxx
+RWPly_PlyWriterContext.cxx
+RWPly_PlyWriterContext.hxx
--- /dev/null
+// Copyright (c) 2022 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 <RWPly_CafWriter.hxx>
+
+#include <Message.hxx>
+#include <Message_LazyProgressScope.hxx>
+#include <OSD_Path.hxx>
+#include <RWMesh_FaceIterator.hxx>
+#include <RWMesh_MaterialMap.hxx>
+#include <RWPly_PlyWriterContext.hxx>
+#include <Standard_CLocaleSentry.hxx>
+#include <TDocStd_Document.hxx>
+#include <TDataStd_Name.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+#include <XCAFPrs_DocumentExplorer.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(RWPly_CafWriter, Standard_Transient)
+
+//================================================================
+// Function : Constructor
+// Purpose :
+//================================================================
+RWPly_CafWriter::RWPly_CafWriter (const TCollection_AsciiString& theFile)
+: myFile (theFile),
+ myIsDoublePrec (false),
+ myHasNormals (true),
+ myHasColors (true),
+ myHasTexCoords (false),
+ myHasPartId (true),
+ myHasFaceId (false)
+{
+ //
+}
+
+//================================================================
+// Function : Destructor
+// Purpose :
+//================================================================
+RWPly_CafWriter::~RWPly_CafWriter()
+{
+ //
+}
+
+//================================================================
+// Function : toSkipFaceMesh
+// Purpose :
+//================================================================
+Standard_Boolean RWPly_CafWriter::toSkipFaceMesh (const RWMesh_FaceIterator& theFaceIter)
+{
+ return theFaceIter.IsEmptyMesh();
+}
+
+// =======================================================================
+// function : Perform
+// purpose :
+// =======================================================================
+bool RWPly_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo,
+ const Message_ProgressRange& theProgress)
+{
+ TDF_LabelSequence aRoots;
+ Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (theDocument->Main());
+ aShapeTool->GetFreeShapes (aRoots);
+ return Perform (theDocument, aRoots, NULL, theFileInfo, theProgress);
+}
+
+// =======================================================================
+// function : Perform
+// purpose :
+// =======================================================================
+bool RWPly_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
+ const TDF_LabelSequence& theRootLabels,
+ const TColStd_MapOfAsciiString* theLabelFilter,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo,
+ const Message_ProgressRange& theProgress)
+{
+ TCollection_AsciiString aFolder, aFileName, aFullFileNameBase, aShortFileNameBase, aFileExt;
+ OSD_Path::FolderAndFileFromPath (myFile, aFolder, aFileName);
+ OSD_Path::FileNameAndExtension (aFileName, aShortFileNameBase, aFileExt);
+
+ Standard_Real aLengthUnit = 1.;
+ if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument, aLengthUnit))
+ {
+ myCSTrsf.SetInputLengthUnit(aLengthUnit);
+ }
+
+ if (theRootLabels.IsEmpty()
+ || (theLabelFilter != NULL && theLabelFilter->IsEmpty()))
+ {
+ Message::SendFail ("Nothing to export into PLY file");
+ return false;
+ }
+
+ Standard_Integer aNbNodesAll = 0, aNbElemsAll = 0;
+ Standard_Real aNbPEntities = 0; // steps for progress range
+ for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
+ aDocExplorer.More(); aDocExplorer.Next())
+ {
+ const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
+ if (theLabelFilter != NULL
+ && !theLabelFilter->Contains (aDocNode.Id))
+ {
+ continue;
+ }
+
+ for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, aDocNode.Location, true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
+ {
+ if (toSkipFaceMesh (aFaceIter))
+ {
+ continue;
+ }
+
+ addFaceInfo (aFaceIter, aNbNodesAll, aNbElemsAll);
+ aNbPEntities += aNbNodesAll + aNbElemsAll;
+ }
+ }
+ if (aNbNodesAll == 0)
+ {
+ Message::SendFail ("No mesh data to save");
+ return false;
+ }
+
+ Standard_CLocaleSentry aLocaleSentry;
+ RWPly_PlyWriterContext aPlyCtx;
+ aPlyCtx.SetDoublePrecision (myIsDoublePrec);
+ aPlyCtx.SetNormals (myHasNormals);
+ aPlyCtx.SetColors (myHasColors);
+ aPlyCtx.SetTexCoords (myHasTexCoords);
+ aPlyCtx.SetSurfaceId (myHasPartId || myHasFaceId);
+ if (!aPlyCtx.Open (myFile)
+ || !aPlyCtx.WriteHeader (aNbNodesAll, aNbElemsAll, theFileInfo))
+ {
+ return false;
+ }
+
+ // simple global progress sentry
+ const Standard_Real aPatchStep = 2048.0;
+ Message_LazyProgressScope aPSentry (theProgress, "PLY export", aNbPEntities, aPatchStep);
+
+ bool isDone = true;
+ for (Standard_Integer aStepIter = 0; aStepIter < 2; ++aStepIter)
+ {
+ aPlyCtx.SetSurfaceId (0);
+ for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
+ aDocExplorer.More() && !aPSentry.IsAborted(); aDocExplorer.Next())
+ {
+ const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
+ if (theLabelFilter != NULL
+ && !theLabelFilter->Contains (aDocNode.Id))
+ {
+ continue;
+ }
+
+ if (myHasPartId)
+ {
+ aPlyCtx.SetSurfaceId (aPlyCtx.SurfaceId() + 1);
+ }
+ if (!writeShape (aPlyCtx, aPSentry, aStepIter, aDocNode.RefLabel, aDocNode.Location, aDocNode.Style))
+ {
+ isDone = false;
+ break;
+ }
+ }
+ }
+
+ const bool isClosed = aPlyCtx.Close();
+ if (isDone && !isClosed)
+ {
+ Message::SendFail (TCollection_AsciiString ("Failed to write PLY file\n") + myFile);
+ return false;
+ }
+ return isDone && !aPSentry.IsAborted();
+}
+
+// =======================================================================
+// function : addFaceInfo
+// purpose :
+// =======================================================================
+void RWPly_CafWriter::addFaceInfo (const RWMesh_FaceIterator& theFace,
+ Standard_Integer& theNbNodes,
+ Standard_Integer& theNbElems)
+{
+ theNbNodes += theFace.NbNodes();
+ theNbElems += theFace.NbTriangles();
+}
+
+// =======================================================================
+// function : writeShape
+// purpose :
+// =======================================================================
+bool RWPly_CafWriter::writeShape (RWPly_PlyWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const Standard_Integer theWriteStep,
+ const TDF_Label& theLabel,
+ const TopLoc_Location& theParentTrsf,
+ const XCAFPrs_Style& theParentStyle)
+{
+ for (RWMesh_FaceIterator aFaceIter (theLabel, theParentTrsf, true, theParentStyle); aFaceIter.More() && !thePSentry.IsAborted(); aFaceIter.Next())
+ {
+ if (toSkipFaceMesh (aFaceIter))
+ {
+ continue;
+ }
+
+ if (theWriteStep == 0
+ && !writeNodes (theWriter, thePSentry, aFaceIter))
+ {
+ return false;
+ }
+ if (theWriteStep == 1
+ && !writeIndices (theWriter, thePSentry, aFaceIter))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// =======================================================================
+// function : writeNodes
+// purpose :
+// =======================================================================
+bool RWPly_CafWriter::writeNodes (RWPly_PlyWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace)
+{
+ const Standard_Integer aNodeUpper = theFace.NodeUpper();
+ Graphic3d_Vec3 aNormVec;
+ Graphic3d_Vec2 aTexVec;
+ Graphic3d_Vec4ub aColorVec (255);
+ if (theFace.HasFaceColor())
+ {
+ //Graphic3d_Vec4 aColorF = Quantity_ColorRGBA::Convert_LinearRGB_To_sRGB (theFace.FaceColor());
+ Graphic3d_Vec4 aColorF = theFace.FaceColor();
+ aColorVec.SetValues ((unsigned char )int(aColorF.r() * 255.0f),
+ (unsigned char )int(aColorF.g() * 255.0f),
+ (unsigned char )int(aColorF.b() * 255.0f),
+ (unsigned char )int(aColorF.a() * 255.0f));
+ }
+ for (Standard_Integer aNodeIter = theFace.NodeLower(); aNodeIter <= aNodeUpper && thePSentry.More(); ++aNodeIter, thePSentry.Next())
+ {
+ gp_XYZ aNode = theFace.NodeTransformed (aNodeIter).XYZ();
+ myCSTrsf.TransformPosition (aNode);
+ if (theFace.HasNormals())
+ {
+ gp_Dir aNorm = theFace.NormalTransformed (aNodeIter);
+ aNormVec.SetValues ((float )aNorm.X(), (float )aNorm.Y(), (float )aNorm.Z());
+ myCSTrsf.TransformNormal (aNormVec);
+ }
+ if (theFace.HasTexCoords())
+ {
+ const gp_Pnt2d aUV = theFace.NodeTexCoord (aNodeIter);
+ aTexVec.SetValues ((float )aUV.X(), (float )aUV.Y());
+ }
+
+ if (!theWriter.WriteVertex (aNode, aNormVec, aTexVec, aColorVec))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// =======================================================================
+// function : writeIndices
+// purpose :
+// =======================================================================
+bool RWPly_CafWriter::writeIndices (RWPly_PlyWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace)
+{
+ if (myHasFaceId)
+ {
+ theWriter.SetSurfaceId (theWriter.SurfaceId() + 1);
+ }
+
+ const Standard_Integer anElemLower = theFace.ElemLower();
+ const Standard_Integer anElemUpper = theFace.ElemUpper();
+ for (Standard_Integer anElemIter = anElemLower; anElemIter <= anElemUpper && thePSentry.More(); ++anElemIter, thePSentry.Next())
+ {
+ const Poly_Triangle aTri = theFace.TriangleOriented (anElemIter);
+ if (!theWriter.WriteTriangle (Graphic3d_Vec3i (aTri(1), aTri(2), aTri(3)) - Graphic3d_Vec3i (anElemLower)))
+ {
+ return false;
+ }
+ }
+
+ theWriter.SetVertexOffset (theWriter.VertexOffset() + theFace.NbNodes());
+ return true;
+}
--- /dev/null
+// Copyright (c) 2022 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 _RWPly_CafWriter_HeaderFiler
+#define _RWPly_CafWriter_HeaderFiler
+
+#include <TColStd_IndexedDataMapOfStringString.hxx>
+#include <TColStd_MapOfAsciiString.hxx>
+#include <TDF_LabelSequence.hxx>
+#include <TopTools_ShapeMapHasher.hxx>
+#include <RWMesh_CoordinateSystemConverter.hxx>
+#include <XCAFPrs_Style.hxx>
+
+#include <memory>
+
+class Message_ProgressRange;
+class RWMesh_FaceIterator;
+class TDocStd_Document;
+
+class Message_LazyProgressScope;
+class RWPly_PlyWriterContext;
+
+//! PLY writer context from XCAF document.
+class RWPly_CafWriter : public Standard_Transient
+{
+ DEFINE_STANDARD_RTTIEXT(RWPly_CafWriter, Standard_Transient)
+public:
+
+ //! Main constructor.
+ //! @param[in] theFile path to output PLY file
+ Standard_EXPORT RWPly_CafWriter (const TCollection_AsciiString& theFile);
+
+ //! Destructor.
+ Standard_EXPORT virtual ~RWPly_CafWriter();
+
+ //! Return transformation from OCCT to PLY coordinate system.
+ const RWMesh_CoordinateSystemConverter& CoordinateSystemConverter() const { return myCSTrsf; }
+
+ //! Return transformation from OCCT to PLY coordinate system.
+ RWMesh_CoordinateSystemConverter& ChangeCoordinateSystemConverter() { return myCSTrsf; }
+
+ //! Set transformation from OCCT to PLY coordinate system.
+ void SetCoordinateSystemConverter (const RWMesh_CoordinateSystemConverter& theConverter) { myCSTrsf = theConverter; }
+
+ //! Return default material definition to be used for nodes with only color defined.
+ const XCAFPrs_Style& DefaultStyle() const { return myDefaultStyle; }
+
+ //! Set default material definition to be used for nodes with only color defined.
+ void SetDefaultStyle (const XCAFPrs_Style& theStyle) { myDefaultStyle = theStyle; }
+
+public:
+
+ //! Return TRUE if vertex position should be stored with double floating point precision; FALSE by default.
+ bool IsDoublePrecision() const { return myIsDoublePrec; }
+
+ //! Set if vertex position should be stored with double floating point precision.
+ void SetDoublePrecision (bool theDoublePrec) { myIsDoublePrec = theDoublePrec; }
+
+ //! Return TRUE if normals should be written; TRUE by default.
+ bool HasNormals() const { return myHasNormals; }
+
+ //! Set if normals are defined.
+ void SetNormals (const bool theHasNormals) { myHasNormals = theHasNormals; }
+
+ //! Return TRUE if UV / texture coordinates should be written; FALSE by default.
+ bool HasTexCoords() const { return myHasTexCoords; }
+
+ //! Set if UV / texture coordinates should be written.
+ void SetTexCoords (const bool theHasTexCoords) { myHasTexCoords = theHasTexCoords; }
+
+ //! Return TRUE if point colors should be written; TRUE by default.
+ bool HasColors() const { return myHasColors; }
+
+ //! Set if point colors should be written.
+ void SetColors (bool theToWrite) { myHasColors = theToWrite; }
+
+ //! Return TRUE if part Id should be written as element attribute; TRUE by default.
+ bool HasPartId() const { return myHasPartId; }
+
+ //! Set if part Id should be written as element attribute; FALSE by default.
+ //! Cannot be combined with HasFaceId().
+ void SetPartId (bool theSurfId)
+ {
+ myHasPartId = theSurfId;
+ myHasFaceId = myHasFaceId && !myHasPartId;
+ }
+
+ //! Return TRUE if face Id should be written as element attribute; FALSE by default.
+ bool HasFaceId() const { return myHasFaceId; }
+
+ //! Set if face Id should be written as element attribute; FALSE by default.
+ //! Cannot be combined with HasPartId().
+ void SetFaceId (bool theSurfId)
+ {
+ myHasFaceId = theSurfId;
+ myHasPartId = myHasPartId && !myHasFaceId;
+ }
+
+public:
+
+ //! Write PLY file and associated MTL material file.
+ //! Triangulation data should be precomputed within shapes!
+ //! @param[in] theDocument input document
+ //! @param[in] theRootLabels list of root shapes to export
+ //! @param[in] theLabelFilter optional filter with document nodes to export,
+ //! with keys defined by XCAFPrs_DocumentExplorer::DefineChildId() and filled recursively
+ //! (leaves and parent assembly nodes at all levels);
+ //! when not NULL, all nodes not included into the map will be ignored
+ //! @param[in] theFileInfo map with file metadata to put into PLY header section
+ //! @param[in] theProgress optional progress indicator
+ //! @return FALSE on file writing failure
+ Standard_EXPORT virtual bool Perform (const Handle(TDocStd_Document)& theDocument,
+ const TDF_LabelSequence& theRootLabels,
+ const TColStd_MapOfAsciiString* theLabelFilter,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo,
+ const Message_ProgressRange& theProgress);
+
+ //! Write PLY file and associated MTL material file.
+ //! Triangulation data should be precomputed within shapes!
+ //! @param[in] theDocument input document
+ //! @param[in] theFileInfo map with file metadata to put into PLY header section
+ //! @param[in] theProgress optional progress indicator
+ //! @return FALSE on file writing failure
+ Standard_EXPORT virtual bool Perform (const Handle(TDocStd_Document)& theDocument,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo,
+ const Message_ProgressRange& theProgress);
+
+protected:
+
+ //! Return TRUE if face mesh should be skipped (e.g. because it is invalid or empty).
+ Standard_EXPORT virtual Standard_Boolean toSkipFaceMesh (const RWMesh_FaceIterator& theFaceIter);
+
+ //! Collect face triangulation info.
+ //! @param[in] theFace face to process
+ //! @param[in,out] theNbNodes overall number of triangulation nodes (should be appended)
+ //! @param[in,out] theNbElems overall number of triangulation elements (should be appended)
+ Standard_EXPORT virtual void addFaceInfo (const RWMesh_FaceIterator& theFace,
+ Standard_Integer& theNbNodes,
+ Standard_Integer& theNbElems);
+
+ //! Write the shape.
+ //! @param[in] theWriter PLY writer context
+ //! @param[in] thePSentry progress sentry
+ //! @param[in] theWriteStep export step, 0 for vertex attributes, 1 for elements
+ //! @param[in] theLabel document label to process
+ //! @param[in] theParentTrsf parent node transformation
+ //! @param[in] theParentStyle parent node style
+ Standard_EXPORT virtual bool writeShape (RWPly_PlyWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const Standard_Integer theWriteStep,
+ const TDF_Label& theLabel,
+ const TopLoc_Location& theParentTrsf,
+ const XCAFPrs_Style& theParentStyle);
+
+ //! Write face triangle vertices and attributes.
+ //! @param[in] theWriter PLY writer context
+ //! @param[in] thePSentry progress sentry
+ //! @param[in] theFace current face
+ //! @return FALSE on writing file error
+ Standard_EXPORT virtual bool writeNodes (RWPly_PlyWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace);
+
+ //! Write face triangles indices.
+ //! @param[in] theWriter PLY writer context
+ //! @param[in] thePSentry progress sentry
+ //! @param[in] theFace current face
+ //! @return FALSE on writing file error
+ Standard_EXPORT virtual bool writeIndices (RWPly_PlyWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace);
+
+
+protected:
+
+ TCollection_AsciiString myFile; //!< output PLY file
+ RWMesh_CoordinateSystemConverter myCSTrsf; //!< transformation from OCCT to PLY coordinate system
+ XCAFPrs_Style myDefaultStyle; //!< default material definition to be used for nodes with only color defined
+ Standard_Boolean myIsDoublePrec;
+ Standard_Boolean myHasNormals;
+ Standard_Boolean myHasColors;
+ Standard_Boolean myHasTexCoords;
+ Standard_Boolean myHasPartId;
+ Standard_Boolean myHasFaceId;
+
+};
+
+#endif // _RWPly_CafWriter_HeaderFiler
--- /dev/null
+// Copyright (c) 2022 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 <RWPly_PlyWriterContext.hxx>
+
+#include <Message.hxx>
+#include <NCollection_IndexedMap.hxx>
+#include <OSD_FileSystem.hxx>
+
+// =======================================================================
+// function : splitLines
+// purpose :
+// =======================================================================
+static void splitLines (const TCollection_AsciiString& theString,
+ NCollection_IndexedMap<TCollection_AsciiString>& theLines)
+{
+ if (theString.IsEmpty())
+ {
+ return;
+ }
+
+ Standard_Integer aLineFrom = 1;
+ for (Standard_Integer aCharIter = 1;; ++aCharIter)
+ {
+ const char aChar = theString.Value (aCharIter);
+ if (aChar != '\r'
+ && aChar != '\n'
+ && aCharIter != theString.Length())
+ {
+ continue;
+ }
+
+ if (aLineFrom != aCharIter)
+ {
+ TCollection_AsciiString aLine = theString.SubString (aLineFrom, aCharIter);
+ aLine.RightAdjust();
+ theLines.Add (aLine);
+ }
+
+ if (aCharIter == theString.Length())
+ {
+ break;
+ }
+ else if (aChar == '\r'
+ && theString.Value (aCharIter + 1) == '\n')
+ {
+ // CRLF
+ ++aCharIter;
+ }
+ aLineFrom = aCharIter + 1;
+ }
+}
+
+// ================================================================
+// Function : RWPly_PlyWriterContext
+// Purpose :
+// ================================================================
+RWPly_PlyWriterContext::RWPly_PlyWriterContext()
+: myNbHeaderVerts (0),
+ myNbHeaderElems (0),
+ myNbVerts (0),
+ myNbElems (0),
+ mySurfId (0),
+ myVertOffset (0),
+ myIsDoublePrec (false),
+ myHasNormals (false),
+ myHasColors (false),
+ myHasTexCoords (false),
+ myHasSurfId (false)
+{
+ //
+}
+
+// ================================================================
+// Function : ~RWPly_PlyWriterContext
+// Purpose :
+// ================================================================
+RWPly_PlyWriterContext::~RWPly_PlyWriterContext()
+{
+ Close();
+}
+
+// ================================================================
+// Function : Open
+// Purpose :
+// ================================================================
+bool RWPly_PlyWriterContext::Open (const TCollection_AsciiString& theName,
+ const std::shared_ptr<std::ostream>& theStream)
+{
+ myName = theName;
+ myNbHeaderVerts = myNbHeaderElems = 0;
+ myNbVerts = myNbElems = 0;
+ if (theStream.get() != nullptr)
+ {
+ myStream = theStream;
+ return true;
+ }
+
+ const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
+ myStream = aFileSystem->OpenOStream (theName, std::ios::out | std::ios::binary);
+ if (myStream.get() == NULL || !myStream->good())
+ {
+ myStream.reset();
+ Message::SendFail() << "Error: file cannot be created\n" << theName;
+ return false;
+ }
+ return true;
+}
+
+// ================================================================
+// Function : Close
+// Purpose :
+// ================================================================
+bool RWPly_PlyWriterContext::Close (bool theIsAborted)
+{
+ if (myStream.get() == nullptr)
+ {
+ return false;
+ }
+
+ myStream->flush();
+ bool aResult = myStream->good();
+ if (!aResult)
+ {
+ Message::SendFail() << "Error: file cannot be written\n" << myName;
+ }
+ else if (!theIsAborted)
+ {
+ if (myNbVerts != myNbHeaderVerts)
+ {
+ Message::SendFail() << "Error: written less number of vertices (" << myNbVerts << ") than specified in PLY header (" << myNbHeaderVerts << ")";
+ }
+ else if (myNbElems != myNbHeaderElems)
+ {
+ Message::SendFail() << "Error: written less number of elements (" << myNbElems << ") than specified in PLY header (" << myNbHeaderElems << ")";
+ }
+ }
+ myStream.reset();
+ return aResult;
+}
+
+// ================================================================
+// Function : WriteHeader
+// Purpose :
+// ================================================================
+bool RWPly_PlyWriterContext::WriteHeader (const Standard_Integer theNbNodes,
+ const Standard_Integer theNbElems,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo)
+{
+ if (myStream.get() == nullptr)
+ {
+ return false;
+ }
+
+ myNbHeaderVerts = theNbNodes;
+ myNbHeaderElems = theNbElems;
+ *myStream << "ply\n"
+ "format ascii 1.0\n"
+ "comment Exported by Open CASCADE Technology [dev.opencascade.org]\n";
+ for (TColStd_IndexedDataMapOfStringString::Iterator aKeyValueIter (theFileInfo); aKeyValueIter.More(); aKeyValueIter.Next())
+ {
+ NCollection_IndexedMap<TCollection_AsciiString> aKeyLines, aValLines;
+ splitLines (aKeyValueIter.Key(), aKeyLines);
+ splitLines (aKeyValueIter.Value(), aValLines);
+ for (Standard_Integer aLineIter = 1; aLineIter <= aKeyLines.Extent(); ++aLineIter)
+ {
+ const TCollection_AsciiString& aLine = aKeyLines.FindKey (aLineIter);
+ *myStream << (aLineIter > 1 ? "\n" : "") << "comment " << aLine;
+ }
+ *myStream << (!aKeyLines.IsEmpty() ? ":" : "comment ");
+ for (Standard_Integer aLineIter = 1; aLineIter <= aValLines.Extent(); ++aLineIter)
+ {
+ const TCollection_AsciiString& aLine = aValLines.FindKey (aLineIter);
+ *myStream << (aLineIter > 1 ? "\n" : "") << "comment " << aLine;
+ }
+ *myStream << "\n";
+ }
+
+ *myStream << "element vertex " << theNbNodes<< "\n";
+ if (myIsDoublePrec)
+ {
+ *myStream << "property double x\n"
+ "property double y\n"
+ "property double z\n";
+ }
+ else
+ {
+ *myStream << "property float x\n"
+ "property float y\n"
+ "property float z\n";
+ }
+ if (myHasNormals)
+ {
+ *myStream << "property float nx\n"
+ "property float ny\n"
+ "property float nz\n";
+ }
+ if (myHasTexCoords)
+ {
+ *myStream << "property float s\n"
+ "property float t\n";
+ }
+ if (myHasColors)
+ {
+ *myStream << "property uchar red\n"
+ "property uchar green\n"
+ "property uchar blue\n";
+ }
+
+ if (theNbElems > 0)
+ {
+ *myStream << "element face " << theNbElems << "\n"
+ "property list uchar uint vertex_indices\n";
+ if (myHasSurfId)
+ {
+ *myStream << "property uint SurfaceID\n";
+ }
+ }
+
+ *myStream << "end_header\n";
+ return myStream->good();
+}
+
+// ================================================================
+// Function : WriteVertex
+// Purpose :
+// ================================================================
+bool RWPly_PlyWriterContext::WriteVertex (const gp_Pnt& thePoint,
+ const Graphic3d_Vec3& theNorm,
+ const Graphic3d_Vec2& theUV,
+ const Graphic3d_Vec4ub& theColor)
+{
+ if (myStream.get() == nullptr)
+ {
+ return false;
+ }
+
+ if (myIsDoublePrec)
+ {
+ *myStream << (double )thePoint.X() << " " << (double )thePoint.Y() << " " << (double )thePoint.Z();
+ }
+ else
+ {
+ *myStream << (float )thePoint.X() << " " << (float )thePoint.Y() << " " << (float )thePoint.Z();
+ }
+ if (myHasNormals)
+ {
+ *myStream << " " << (float )theNorm.x() << " " << (float )theNorm.y() << " " << (float )theNorm.z();
+ }
+ if (myHasTexCoords)
+ {
+ *myStream << " " << (float )theUV.x() << " " << (float )theUV.y();
+ }
+ if (myHasColors)
+ {
+ *myStream << " " << (int )theColor.r() << " " << (int )theColor.g() << " " << (int )theColor.b();
+ }
+ *myStream << "\n";
+ if (++myNbVerts > myNbHeaderVerts)
+ {
+ throw Standard_OutOfRange ("RWPly_PlyWriterContext::WriteVertex() - number of vertices is greater than defined");
+ }
+ return myStream->good();
+}
+
+// ================================================================
+// Function : WriteTriangle
+// Purpose :
+// ================================================================
+bool RWPly_PlyWriterContext::WriteTriangle (const Graphic3d_Vec3i& theTri)
+{
+ if (myStream.get() == nullptr)
+ {
+ return false;
+ }
+
+ const Graphic3d_Vec3i aTri = Graphic3d_Vec3i(myVertOffset) + theTri;
+ *myStream << "3 " << aTri[0] << " " << aTri[1] << " " << aTri[2];
+ if (myHasSurfId)
+ {
+ *myStream << " " << mySurfId;
+ }
+ *myStream << "\n";
+ if (++myNbElems > myNbHeaderElems)
+ {
+ throw Standard_OutOfRange ("RWPly_PlyWriterContext::WriteTriangle() - number of elements is greater than defined");
+ }
+ return myStream->good();
+}
+
+// ================================================================
+// Function : WriteQuad
+// Purpose :
+// ================================================================
+bool RWPly_PlyWriterContext::WriteQuad (const Graphic3d_Vec4i& theQuad)
+{
+ if (myStream.get() == nullptr)
+ {
+ return false;
+ }
+
+ const Graphic3d_Vec4i aQuad = Graphic3d_Vec4i(myVertOffset) + theQuad;
+ *myStream << "4 " << aQuad[0] << " " << aQuad[1] << " " << aQuad[2] << " " << aQuad[3];
+ if (myHasSurfId)
+ {
+ *myStream << " " << mySurfId;
+ }
+ *myStream << "\n";
+ if (++myNbElems > myNbHeaderElems)
+ {
+ throw Standard_OutOfRange ("RWPly_PlyWriterContext::WriteQuad() - number of elements is greater than defined");
+ }
+ return myStream->good();
+}
--- /dev/null
+// Copyright (c) 2022 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 _RWPly_PlyWriterContext_HeaderFiler
+#define _RWPly_PlyWriterContext_HeaderFiler
+
+#include <Graphic3d_Vec.hxx>
+#include <gp_Pnt.hxx>
+#include <TCollection_AsciiString.hxx>
+#include <TColStd_IndexedDataMapOfStringString.hxx>
+
+#include <memory>
+
+//! Auxiliary low-level tool writing PLY file.
+class RWPly_PlyWriterContext
+{
+public:
+
+ //! Empty constructor.
+ Standard_EXPORT RWPly_PlyWriterContext();
+
+ //! Destructor, will emit error message if file was not closed.
+ Standard_EXPORT ~RWPly_PlyWriterContext();
+
+public: //! @name vertex attributes parameters
+
+ //! Return TRUE if vertex position should be stored with double floating point precision; FALSE by default.
+ bool IsDoublePrecision() const { return myIsDoublePrec; }
+
+ //! Set if vertex position should be stored with double floating point precision.
+ void SetDoublePrecision (bool theDoublePrec) { myIsDoublePrec = theDoublePrec; }
+
+ //! Return TRUE if normals should be written as vertex attribute; FALSE by default.
+ bool HasNormals() const { return myHasNormals; }
+
+ //! Set if normals should be written.
+ void SetNormals (const bool theHasNormals) { myHasNormals = theHasNormals; }
+
+ //! Return TRUE if UV / texture coordinates should be written as vertex attribute; FALSE by default.
+ bool HasTexCoords() const { return myHasTexCoords; }
+
+ //! Set if UV / texture coordinates should be written.
+ void SetTexCoords (const bool theHasTexCoords) { myHasTexCoords = theHasTexCoords; }
+
+ //! Return TRUE if point colors should be written as vertex attribute; FALSE by default.
+ bool HasColors() const { return myHasColors; }
+
+ //! Set if point colors should be written.
+ void SetColors (bool theToWrite) { myHasColors = theToWrite; }
+
+public: //! @name element attributes parameters
+
+ //! Return TRUE if surface Id should be written as element attribute; FALSE by default.
+ bool HasSurfaceId() const { return myHasSurfId; }
+
+ //! Set if surface Id should be written as element attribute; FALSE by default.
+ void SetSurfaceId (bool theSurfId) { myHasSurfId = theSurfId; }
+
+public: //! @name writing into file
+
+ //! Return TRUE if file has been opened.
+ bool IsOpened() const { return myStream.get() != nullptr; }
+
+ //! Open file for writing.
+ Standard_EXPORT bool Open (const TCollection_AsciiString& theName,
+ const std::shared_ptr<std::ostream>& theStream = std::shared_ptr<std::ostream>());
+
+ //! Write the header.
+ //! @param[in] theNbNodes number of vertex nodes
+ //! @param[in] theNbElems number of mesh elements
+ //! @param[in] theFileInfo optional comments
+ Standard_EXPORT bool WriteHeader (const Standard_Integer theNbNodes,
+ const Standard_Integer theNbElems,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo);
+
+ //! Write single point with all attributes.
+ //! @param[in] thePoint 3D point coordinates
+ //! @param[in] theNorm surface normal direction at the point
+ //! @param[in] theUV surface/texture UV coordinates
+ //! @param[in] theColor RGB color values
+ Standard_EXPORT bool WriteVertex (const gp_Pnt& thePoint,
+ const Graphic3d_Vec3& theNorm,
+ const Graphic3d_Vec2& theUV,
+ const Graphic3d_Vec4ub& theColor);
+
+ //! Return number of written vertices.
+ Standard_Integer NbWrittenVertices() const { return myNbVerts; }
+
+ //! Return vertex offset to be applied to element indices; 0 by default.
+ Standard_Integer VertexOffset() const { return myVertOffset; }
+
+ //! Set vertex offset to be applied to element indices.
+ void SetVertexOffset (Standard_Integer theOffset) { myVertOffset = theOffset; }
+
+ //! Return surface id to write with element; 0 by default.
+ Standard_Integer SurfaceId() const { return mySurfId; }
+
+ //! Set surface id to write with element.
+ void SetSurfaceId (Standard_Integer theSurfId) { mySurfId = theSurfId; }
+
+ //! Writing a triangle.
+ Standard_EXPORT bool WriteTriangle (const Graphic3d_Vec3i& theTri);
+
+ //! Writing a quad.
+ Standard_EXPORT bool WriteQuad (const Graphic3d_Vec4i& theQuad);
+
+ //! Return number of written elements.
+ Standard_Integer NbWrittenElements() const { return myNbElems; }
+
+ //! Correctly close the file.
+ //! @return FALSE in case of writing error
+ Standard_EXPORT bool Close (bool theIsAborted = false);
+
+private:
+
+ std::shared_ptr<std::ostream> myStream;
+ TCollection_AsciiString myName;
+ Standard_Integer myNbHeaderVerts;
+ Standard_Integer myNbHeaderElems;
+ Standard_Integer myNbVerts;
+ Standard_Integer myNbElems;
+ Standard_Integer mySurfId;
+ Standard_Integer myVertOffset;
+ bool myIsDoublePrec;
+ bool myHasNormals;
+ bool myHasColors;
+ bool myHasTexCoords;
+ bool myHasSurfId;
+
+};
+
+#endif // _RWPly_PlyWriterContext_HeaderFiler
#include <BRepBndLib.hxx>
#include <BRepMesh_DiscretFactory.hxx>
-#include <BRepMesh_DiscretRoot.hxx>
#include <BRepTools.hxx>
#include <BRep_Tool.hxx>
-#include <GeomAbs_SurfaceType.hxx>
-#include <GeomLib.hxx>
-#include <gp_XYZ.hxx>
-#include <Poly.hxx>
-#include <Poly_Connect.hxx>
-#include <Poly_Triangulation.hxx>
-#include <Precision.hxx>
#include <Prs3d.hxx>
#include <Prs3d_Drawer.hxx>
-#include <TColgp_Array1OfPnt.hxx>
-#include <TColgp_Array1OfPnt2d.hxx>
-#include <TopAbs_Orientation.hxx>
#include <TopLoc_Location.hxx>
-#include <TShort_HArray1OfShortReal.hxx>
-#include <TShort_Array1OfShortReal.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Face.hxx>
}
}
-//=======================================================================
-//function : ComputeNormals
-//purpose :
-//=======================================================================
-void StdPrs_ToolTriangulatedShape::ComputeNormals (const TopoDS_Face& theFace,
- const Handle(Poly_Triangulation)& theTris,
- Poly_Connect& thePolyConnect)
-{
- if (theTris.IsNull()
- || theTris->HasNormals())
- {
- return;
- }
-
- // take in face the surface location
- const TopoDS_Face aZeroFace = TopoDS::Face (theFace.Located (TopLoc_Location()));
- Handle(Geom_Surface) aSurf = BRep_Tool::Surface (aZeroFace);
- if (!theTris->HasUVNodes() || aSurf.IsNull())
- {
- // compute normals by averaging triangulation normals sharing the same vertex
- Poly::ComputeNormals (theTris);
- return;
- }
-
- const Standard_Real aTol = Precision::Confusion();
- Standard_Integer aTri[3];
- gp_Dir aNorm;
- theTris->AddNormals();
- for (Standard_Integer aNodeIter = 1; aNodeIter <= theTris->NbNodes(); ++aNodeIter)
- {
- // try to retrieve normal from real surface first, when UV coordinates are available
- if (GeomLib::NormEstim (aSurf, theTris->UVNode (aNodeIter), aTol, aNorm) > 1)
- {
- if (thePolyConnect.Triangulation() != theTris)
- {
- thePolyConnect.Load (theTris);
- }
-
- // compute flat normals
- gp_XYZ eqPlan (0.0, 0.0, 0.0);
- for (thePolyConnect.Initialize (aNodeIter); thePolyConnect.More(); thePolyConnect.Next())
- {
- theTris->Triangle (thePolyConnect.Value()).Get (aTri[0], aTri[1], aTri[2]);
- const gp_XYZ v1 (theTris->Node (aTri[1]).Coord() - theTris->Node (aTri[0]).Coord());
- const gp_XYZ v2 (theTris->Node (aTri[2]).Coord() - theTris->Node (aTri[1]).Coord());
- const gp_XYZ vv = v1 ^ v2;
- const Standard_Real aMod = vv.Modulus();
- if (aMod >= aTol)
- {
- eqPlan += vv / aMod;
- }
- }
- const Standard_Real aModMax = eqPlan.Modulus();
- aNorm = (aModMax > aTol) ? gp_Dir (eqPlan) : gp::DZ();
- }
-
- theTris->SetNormal (aNodeIter, aNorm);
- }
-}
-
//=======================================================================
//function : Normal
//purpose :
#ifndef _StdPrs_ToolTriangulatedShape_HeaderFile
#define _StdPrs_ToolTriangulatedShape_HeaderFile
-#include <Poly_Connect.hxx>
-#include <Poly_Triangulation.hxx>
-#include <Prs3d_Drawer.hxx>
-#include <Standard.hxx>
-#include <Standard_Macro.hxx>
+#include <BRepLib_ToolTriangulatedShape.hxx>
#include <TColgp_Array1OfDir.hxx>
-class TopoDS_Face;
class TopoDS_Shape;
class Prs3d_Drawer;
-class Poly_Triangulation;
-class StdPrs_ToolTriangulatedShape
+class StdPrs_ToolTriangulatedShape: public BRepLib_ToolTriangulatedShape
{
public:
//! @return true if shape is closed manifold Solid or compound of such Solids. <br>
Standard_EXPORT static Standard_Boolean IsClosed (const TopoDS_Shape& theShape);
- //! Computes nodal normals for Poly_Triangulation structure using UV coordinates and surface.
- //! Does nothing if triangulation already defines normals.
- //! @param theFace [in] the face
- //! @param theTris [in] the definition of a face triangulation
- static void ComputeNormals (const TopoDS_Face& theFace,
- const Handle(Poly_Triangulation)& theTris)
- {
- Poly_Connect aPolyConnect;
- ComputeNormals (theFace, theTris, aPolyConnect);
- }
-
- //! Computes nodal normals for Poly_Triangulation structure using UV coordinates and surface.
- //! Does nothing if triangulation already defines normals.
- //! @param theFace [in] the face
- //! @param theTris [in] the definition of a face triangulation
- //! @param thePolyConnect [in,out] optional, initialized tool for exploring triangulation
- Standard_EXPORT static void ComputeNormals (const TopoDS_Face& theFace,
- const Handle(Poly_Triangulation)& theTris,
- Poly_Connect& thePolyConnect);
-
//! Evaluate normals for a triangle of a face.
//! @param[in] theFace the face.
//! @param[in] thePolyConnect the definition of a face triangulation.
RWGltf
RWMesh
RWObj
+RWPly
#include <AIS_Shape.hxx>
#include <AIS_DisplayMode.hxx>
#include <AIS_PointCloud.hxx>
+#include <BRepLib_PointCloudShape.hxx>
#include <TColStd_MapOfInteger.hxx>
#include <ViewerTest_AutoUpdater.hxx>
#include <ViewerTest_DoubleMapOfInteractiveAndName.hxx>
Standard_Integer theArgNum,
const char** theArgs)
{
+ if (theArgNum < 2)
+ {
+ Message::SendFail ("Syntax error: wrong number of arguments");
+ return 1;
+ }
+
Handle(AIS_InteractiveContext) anAISContext = ViewerTest::GetAISContext();
if (anAISContext.IsNull())
{
return 1;
}
- // command to execute
- enum Command
- {
- CloudForShape, // generate point cloud for shape
- CloudSphere, // generate point cloud for generic sphere
- Unknow
- };
+ TCollection_AsciiString aName;
+ TopoDS_Shape aShape;
- // count number of non-optional command arguments
- Command aCmd = Unknow;
- Standard_Integer aCmdArgs = 0;
- for (Standard_Integer anArgIter = 1; anArgIter < theArgNum; ++anArgIter)
- {
- Standard_CString anArg = theArgs[anArgIter];
- TCollection_AsciiString aFlag (anArg);
- aFlag.LowerCase();
- if (aFlag.IsRealValue (Standard_True) || aFlag.Search ("-") != 1)
- {
- aCmdArgs++;
- }
- }
- switch (aCmdArgs)
- {
- case 2 : aCmd = CloudForShape; break;
- case 7 : aCmd = CloudSphere; break;
- default :
- Message::SendFail ("Syntax error: wrong number of arguments! See usage:");
- theDI.PrintHelp (theArgs[0]);
- return 1;
- }
+ TCollection_AsciiString aDistribution;
+ gp_Pnt aDistCenter;
+ Standard_Real aDistRadius = 0.0;
+ Standard_Integer aDistNbPoints = 0;
// parse options
- Standard_Boolean toRandColors = Standard_False;
- Standard_Boolean hasNormals = Standard_True;
- Standard_Boolean isSetArgNorm = Standard_False;
- Standard_Boolean hasUV = Standard_False;
+ bool toRandColors = false;
+ bool hasNormals = true, hasUV = false;
+ bool isDensityPoints = false;
+ Standard_Real aDensity = 0.0, aDist = 0.0;
+ Standard_Real aTol = Precision::Confusion();
for (Standard_Integer anArgIter = 1; anArgIter < theArgNum; ++anArgIter)
{
- Standard_CString anArg = theArgs[anArgIter];
- TCollection_AsciiString aFlag (anArg);
+ TCollection_AsciiString aFlag (theArgs[anArgIter]);
aFlag.LowerCase();
if (aFlag == "-randcolors"
|| aFlag == "-randcolor")
{
- if (isSetArgNorm && hasNormals)
- {
- Message::SendFail ("Syntax error: normals can not be enabled with colors at the same time");
- return 1;
- }
- toRandColors = Standard_True;
- hasNormals = Standard_False;
+ toRandColors = Draw::ParseOnOffIterator (theArgNum, theArgs, anArgIter);
}
else if (aFlag == "-normals"
|| aFlag == "-normal")
{
- if (toRandColors)
- {
- Message::SendFail ("Syntax error: normals can not be enabled with colors at the same time");
- return 1;
- }
- isSetArgNorm = Standard_True;
- hasNormals = Standard_True;
+ hasNormals = Draw::ParseOnOffIterator (theArgNum, theArgs, anArgIter);
}
else if (aFlag == "-nonormals"
|| aFlag == "-nonormal")
{
- isSetArgNorm = Standard_True;
- hasNormals = Standard_False;
+ hasNormals = !Draw::ParseOnOffIterator (theArgNum, theArgs, anArgIter);
}
else if (aFlag == "-uv"
|| aFlag == "-texels")
{
- hasUV = Standard_True;
+ hasUV = Draw::ParseOnOffIterator (theArgNum, theArgs, anArgIter);
+ }
+ else if ((aFlag == "-dist"
+ || aFlag == "-distance")
+ && anArgIter + 1 < theArgNum
+ && Draw::ParseReal (theArgs[anArgIter + 1], aDist))
+ {
+ ++anArgIter;
+ if (aDist < 0.0)
+ {
+ theDI << "Syntax error: -distance value should be >= 0.0";
+ return 1;
+ }
+ aDist = Max (aDist, Precision::Confusion());
+ }
+ else if ((aFlag == "-dens"
+ || aFlag == "-density")
+ && anArgIter + 1 < theArgNum
+ && Draw::ParseReal (theArgs[anArgIter + 1], aDensity))
+ {
+ ++anArgIter;
+ isDensityPoints = Standard_True;
+ if (aDensity <= 0.0)
+ {
+ theDI << "Syntax error: -density value should be > 0.0";
+ return 1;
+ }
+ }
+ else if ((aFlag == "-tol"
+ || aFlag == "-tolerance")
+ && anArgIter + 1 < theArgNum
+ && Draw::ParseReal (theArgs[anArgIter + 1], aTol))
+ {
+ ++anArgIter;
+ if (aTol < Precision::Confusion())
+ {
+ theDI << "Syntax error: -tol value should be >= " << Precision::Confusion();
+ return 1;
+ }
+ }
+ else if ((aFlag == "-surface"
+ || aFlag == "-volume")
+ && anArgIter + 5 < theArgNum)
+ {
+ aDistribution = aFlag;
+ aDistCenter.SetCoord (Draw::Atof (theArgs[anArgIter + 1]),
+ Draw::Atof (theArgs[anArgIter + 2]),
+ Draw::Atof (theArgs[anArgIter + 3]));
+ aDistRadius = Draw::Atof (theArgs[anArgIter + 4]);
+ aDistNbPoints = Draw::Atoi (theArgs[anArgIter + 5]);
+ anArgIter += 5;
+ }
+ else if (aName.IsEmpty())
+ {
+ aName = theArgs[anArgIter];
+ }
+ else if (aShape.IsNull())
+ {
+ aShape = DBRep::Get (theArgs[anArgIter]);
+ if (aShape.IsNull())
+ {
+ theDI << "Syntax error: invalid shape '" << theArgs[anArgIter] << "'";
+ return 1;
+ }
+ }
+ else
+ {
+ theDI << "Syntax error at '" << theArgs[anArgIter] << "'";
+ return 1;
}
}
- Standard_CString aName = theArgs[1];
Graphic3d_ArrayFlags aFlags = Graphic3d_ArrayFlags_None;
if (hasNormals)
{
// generate arbitrary set of points
Handle(Graphic3d_ArrayOfPoints) anArrayPoints;
- if (aCmd == CloudForShape)
+ if (!aShape.IsNull())
{
- Standard_CString aShapeName = theArgs[2];
- TopoDS_Shape aShape = DBRep::Get (aShapeName);
-
- if (aShape.IsNull())
- {
- Message::SendFail() << "Error: no shape with name '" << aShapeName << "' found";
- return 1;
- }
-
- // calculate number of points
- TopLoc_Location aLocation;
- Standard_Integer aNbPoints = 0;
- for (TopExp_Explorer aFaceIt (aShape, TopAbs_FACE); aFaceIt.More(); aFaceIt.Next())
- {
- const TopoDS_Face& aFace = TopoDS::Face (aFaceIt.Current());
- Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (aFace, aLocation);
- if (!aTriangulation.IsNull())
- {
- aNbPoints += aTriangulation->NbNodes();
- }
- }
- if (aNbPoints < 3)
+ class PointCloudPntFiller : public BRepLib_PointCloudShape
{
- Message::SendFail ("Error: shape should be triangulated");
- return 1;
- }
-
- anArrayPoints = new Graphic3d_ArrayOfPoints (aNbPoints, aFlags);
- for (TopExp_Explorer aFaceIt (aShape, TopAbs_FACE); aFaceIt.More(); aFaceIt.Next())
- {
- const TopoDS_Face& aFace = TopoDS::Face (aFaceIt.Current());
- Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (aFace, aLocation);
- if (aTriangulation.IsNull())
- {
- continue;
- }
-
- const gp_Trsf& aTrsf = aLocation.Transformation();
-
- // extract normals from nodes
- TColgp_Array1OfDir aNormals (1, hasNormals ? aTriangulation->NbNodes() : 1);
- if (hasNormals)
- {
- Poly_Connect aPolyConnect (aTriangulation);
- StdPrs_ToolTriangulatedShape::Normal (aFace, aPolyConnect, aNormals);
- }
+ public:
+ PointCloudPntFiller (Standard_Real theTol) : BRepLib_PointCloudShape (TopoDS_Shape(), theTol) {}
+ void SetPointArray (const Handle(Graphic3d_ArrayOfPoints)& thePoints) { myPoints = thePoints; }
- for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter)
+ protected:
+ virtual void addPoint (const gp_Pnt& thePoint,
+ const gp_Vec& theNorm,
+ const gp_Pnt2d& theUV,
+ const TopoDS_Shape& ) Standard_OVERRIDE
{
- gp_Pnt aPoint = aTriangulation->Node (aNodeIter);
- if (!aLocation.IsIdentity())
+ const Standard_Integer aPntIndex = myPoints->AddVertex (thePoint, theUV);
+ if (theNorm.SquareMagnitude() > gp::Resolution())
{
- aPoint.Transform (aTrsf);
- if (hasNormals)
- {
- aNormals (aNodeIter).Transform (aTrsf);
- }
+ myPoints->SetVertexNormal (aPntIndex, theNorm);
}
-
- // add vertex into array of points
- const Standard_Integer anIndexOfPoint = anArrayPoints->AddVertex (aPoint);
- if (toRandColors)
+ if (myPoints->HasVertexColors())
{
- Quantity_Color aColor (360.0 * Standard_Real(anIndexOfPoint) / Standard_Real(aNbPoints),
+ Quantity_Color aColor (360.0 * Standard_Real(aPntIndex) / Standard_Real(myPoints->VertexNumberAllocated()),
1.0, 0.5, Quantity_TOC_HLS);
- anArrayPoints->SetVertexColor (anIndexOfPoint, aColor);
- }
-
- if (hasNormals)
- {
- anArrayPoints->SetVertexNormal (anIndexOfPoint, aNormals (aNodeIter));
- }
- if (hasUV
- && aTriangulation->HasUVNodes())
- {
- anArrayPoints->SetVertexTexel (anIndexOfPoint, aTriangulation->UVNode (aNodeIter));
+ myPoints->SetVertexColor (aPntIndex, aColor);
}
}
- }
- }
- else if (aCmd == CloudSphere)
- {
- Standard_Real aCenterX = Draw::Atof (theArgs[2]);
- Standard_Real aCenterY = Draw::Atof (theArgs[3]);
- Standard_Real aCenterZ = Draw::Atof (theArgs[4]);
- Standard_Real aRadius = Draw::Atof (theArgs[5]);
- Standard_Integer aNbPoints = Draw::Atoi (theArgs[6]);
- TCollection_AsciiString aDistribution = TCollection_AsciiString(theArgs[7]);
- aDistribution.LowerCase();
- if ( aDistribution != "surface" && aDistribution != "volume" )
+ private:
+ Handle(Graphic3d_ArrayOfPoints) myPoints;
+ };
+
+ PointCloudPntFiller aPoitCloudTool (aTol);
+ aPoitCloudTool.SetShape (aShape);
+ aPoitCloudTool.SetDistance (aDist);
+
+ Standard_Integer aNbPoints = isDensityPoints
+ ? aPoitCloudTool.NbPointsByDensity (aDensity)
+ : aPoitCloudTool.NbPointsByTriangulation();
+ theDI << "Number of the generated points : " << aNbPoints << "\n";
+ anArrayPoints = new Graphic3d_ArrayOfPoints (aNbPoints, aFlags);
+ aPoitCloudTool.SetPointArray (anArrayPoints);
+ Standard_Boolean isDone = isDensityPoints
+ ? aPoitCloudTool.GeneratePointsByDensity (aDensity)
+ : aPoitCloudTool.GeneratePointsByTriangulation();
+ if (!isDone)
{
- Message::SendFail ("Syntax error: wrong arguments. See usage:");
- theDI.PrintHelp (theArgs[0]);
+ Message::SendFail() << "Error: Point cloud was not generated";
return 1;
}
- Standard_Boolean isSurface = aDistribution == "surface";
-
- gp_Pnt aCenter(aCenterX, aCenterY, aCenterZ);
+ }
+ else if (!aDistribution.IsEmpty())
+ {
+ const bool isSurface = aDistribution == "-surface";
- anArrayPoints = new Graphic3d_ArrayOfPoints (aNbPoints, aFlags);
- for (Standard_Integer aPntIt = 0; aPntIt < aNbPoints; ++aPntIt)
+ anArrayPoints = new Graphic3d_ArrayOfPoints (aDistNbPoints, aFlags);
+ std::mt19937 aRandomGenerator(0);
+ std::uniform_real_distribution<> anAlphaDistrib(0.0, 2.0 * M_PI);
+ std::uniform_real_distribution<> aBetaDistrib (0.0, 2.0 * M_PI);
+ std::uniform_real_distribution<> aRadiusDistrib(0.0, aDistRadius);
+ for (Standard_Integer aPntIt = 0; aPntIt < aDistNbPoints; ++aPntIt)
{
- Standard_Real anAlpha = (Standard_Real (rand() % 2000) / 1000.0) * M_PI;
- Standard_Real aBeta = (Standard_Real (rand() % 2000) / 1000.0) * M_PI;
- Standard_Real aDistance = isSurface ?
- aRadius : (Standard_Real (rand() % aNbPoints) / aNbPoints) * aRadius;
+ Standard_Real anAlpha = anAlphaDistrib(aRandomGenerator);
+ Standard_Real aBeta = aBetaDistrib (aRandomGenerator);
+ Standard_Real aDistance = isSurface ? aDistRadius : aRadiusDistrib (aRandomGenerator);
gp_Dir aDir (Cos (anAlpha) * Sin (aBeta),
Sin (anAlpha),
Cos (anAlpha) * Cos (aBeta));
- gp_Pnt aPoint = aCenter.Translated (aDir.XYZ() * aDistance);
+ gp_Pnt aPoint = aDistCenter.Translated (aDir.XYZ() * aDistance);
const Standard_Integer anIndexOfPoint = anArrayPoints->AddVertex (aPoint);
if (toRandColors)
{
- Quantity_Color aColor (360.0 * Standard_Real (anIndexOfPoint) / Standard_Real (aNbPoints),
+ Quantity_Color aColor (360.0 * Standard_Real (anIndexOfPoint) / Standard_Real (aDistNbPoints),
1.0, 0.5, Quantity_TOC_HLS);
anArrayPoints->SetVertexColor (anIndexOfPoint, aColor);
}
}
}
}
+ else
+ {
+ Message::SendFail ("Error: wrong number of arguments");
+ return 1;
+ }
// set array of points in point cloud object
Handle(AIS_PointCloud) aPointCloud = new AIS_PointCloud();
aPointCloud->SetPoints (anArrayPoints);
- VDisplayAISObject (aName, aPointCloud);
+ ViewerTest::Display (aName, aPointCloud);
return 0;
}
)" /* [vvertexmode] */);
addCmd ("vpointcloud", VPointCloud, /* [vpointcloud] */ R"(
-vpointcloud name shape [-randColor] [-normals] [-noNormals] [-uv]
+vpointcloud name shape [-randColor {0|1}]=0 [-normals {0|1}]=1 [-uv {0|1}]=0
+ [-distance Value]=0.0 [-density Value] [-tolerance Value]
Create an interactive object for arbitrary set of points from triangulated shape.
-vpointcloud name x y z r npts {surface|volume}
- ... [-randColor] [-normals] [-noNormals] [-uv]
+vpointcloud name {-surface|-volume} x y z r npts
+ [-randColor] [-normals] [-uv]
Create arbitrary set of points (npts) randomly distributed
on spheric surface or within spheric volume (x y z r).
Additional options:
- -randColor - generate random color per point
- -normals - generate normal per point (default)
- -noNormals - do not generate normal per point
+ -normals generate or not normal per point
+ -uv generate UV (texel) coordinates per point
+ -randColor generate random color per point
+ -distance distance from shape into the range [0, Value];
+ -density density of points to generate randomly on surface;
+ -tolerance cloud generator's tolerance; default value is Precision::Confusion();
+
)" /* [vpointcloud] */);
addCmd ("vpriority", VPriority, /* [vpriority] */ R"(
#include <Aspect_TypeOfMarker.hxx>
#include <Bnd_Box.hxx>
#include <BRep_Builder.hxx>
+#include <BRepLib_PointCloudShape.hxx>
#include <DBRep.hxx>
#include <DDocStd.hxx>
#include <DDocStd_DrawDocument.hxx>
#include <Quantity_NameOfColor.hxx>
#include <RWGltf_CafReader.hxx>
#include <RWGltf_CafWriter.hxx>
+#include <RWMesh_FaceIterator.hxx>
#include <RWStl.hxx>
#include <RWObj.hxx>
#include <RWObj_CafReader.hxx>
#include <RWObj_CafWriter.hxx>
+#include <RWPly_CafWriter.hxx>
+#include <RWPly_PlyWriterContext.hxx>
#include <SelectMgr_SelectionManager.hxx>
#include <Standard_ErrorHandler.hxx>
#include <StdSelect_ViewerSelector3d.hxx>
#include <TDataStd_Name.hxx>
#include <TDocStd_Application.hxx>
#include <TDocStd_Document.hxx>
+#include <TopoDS.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <UnitsAPI.hxx>
#include <VrmlData_ShapeConvert.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
+#include <XCAFPrs_DocumentExplorer.hxx>
#include <XSAlgo.hxx>
#include <XSAlgo_AlgoContainer.hxx>
#include <XSDRAW.hxx>
return 0;
}
+//=======================================================================
+//function : writeply
+//purpose : write PLY file
+//=======================================================================
+static Standard_Integer WritePly (Draw_Interpretor& theDI,
+ Standard_Integer theNbArgs,
+ const char** theArgVec)
+{
+ Handle(TDocStd_Document) aDoc;
+ Handle(TDocStd_Application) anApp = DDocStd::GetApplication();
+ TCollection_AsciiString aShapeName, aFileName;
+
+ Standard_Real aDist = 0.0;
+ Standard_Real aDens = Precision::Infinite();
+ Standard_Real aTol = Precision::Confusion();
+ bool hasColors = true, hasNormals = true, hasTexCoords = false, hasPartId = true, hasFaceId = false;
+ bool isPntSet = false, isDensityPoints = false;
+ TColStd_IndexedDataMapOfStringString aFileInfo;
+ for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
+ {
+ TCollection_AsciiString anArg (theArgVec[anArgIter]);
+ anArg.LowerCase();
+ if (anArg == "-normal")
+ {
+ hasNormals = Draw::ParseOnOffIterator (theNbArgs, theArgVec, anArgIter);
+ }
+ else if (anArg == "-nonormal")
+ {
+ hasNormals = !Draw::ParseOnOffIterator (theNbArgs, theArgVec, anArgIter);
+ }
+ else if (anArg == "-color"
+ || anArg == "-nocolor"
+ || anArg == "-colors"
+ || anArg == "-nocolors")
+ {
+ hasColors = Draw::ParseOnOffNoIterator (theNbArgs, theArgVec, anArgIter);
+ }
+ else if (anArg == "-uv"
+ || anArg == "-nouv")
+ {
+ hasTexCoords = Draw::ParseOnOffNoIterator (theNbArgs, theArgVec, anArgIter);
+ }
+ else if (anArg == "-partid")
+ {
+ hasPartId = Draw::ParseOnOffNoIterator (theNbArgs, theArgVec, anArgIter);
+ hasFaceId = hasFaceId && !hasPartId;
+ }
+ else if (anArg == "-surfid"
+ || anArg == "-surfaceid"
+ || anArg == "-faceid")
+ {
+ hasFaceId = Draw::ParseOnOffNoIterator (theNbArgs, theArgVec, anArgIter);
+ hasPartId = hasPartId && !hasFaceId;
+ }
+ else if (anArg == "-pntset"
+ || anArg == "-pntcloud"
+ || anArg == "-pointset"
+ || anArg == "-pointcloud"
+ || anArg == "-cloud"
+ || anArg == "-points")
+ {
+ isPntSet = Draw::ParseOnOffIterator (theNbArgs, theArgVec, anArgIter);
+ }
+ else if ((anArg == "-dist"
+ || anArg == "-distance")
+ && anArgIter + 1 < theNbArgs
+ && Draw::ParseReal (theArgVec[anArgIter + 1], aDist))
+ {
+ ++anArgIter;
+ isPntSet = true;
+ if (aDist < 0.0)
+ {
+ theDI << "Syntax error: -distance value should be >= 0.0";
+ return 1;
+ }
+ aDist = Max (aDist, Precision::Confusion());
+ }
+ else if ((anArg == "-dens"
+ || anArg == "-density")
+ && anArgIter + 1 < theNbArgs
+ && Draw::ParseReal (theArgVec[anArgIter + 1], aDens))
+ {
+ ++anArgIter;
+ isDensityPoints = Standard_True;
+ isPntSet = true;
+ if (aDens <= 0.0)
+ {
+ theDI << "Syntax error: -density value should be > 0.0";
+ return 1;
+ }
+ }
+ else if ((anArg == "-tol"
+ || anArg == "-tolerance")
+ && anArgIter + 1 < theNbArgs
+ && Draw::ParseReal (theArgVec[anArgIter + 1], aTol))
+ {
+ ++anArgIter;
+ isPntSet = true;
+ if (aTol < Precision::Confusion())
+ {
+ theDI << "Syntax error: -tol value should be >= " << Precision::Confusion();
+ return 1;
+ }
+ }
+ else if (anArg == "-comments"
+ && anArgIter + 1 < theNbArgs)
+ {
+ aFileInfo.Add ("Comments", theArgVec[++anArgIter]);
+ }
+ else if (anArg == "-author"
+ && anArgIter + 1 < theNbArgs)
+ {
+ aFileInfo.Add ("Author", theArgVec[++anArgIter]);
+ }
+ else if (aDoc.IsNull())
+ {
+ if (aShapeName.IsEmpty())
+ {
+ aShapeName = theArgVec[anArgIter];
+ }
+
+ Standard_CString aNameVar = theArgVec[anArgIter];
+ DDocStd::GetDocument (aNameVar, aDoc, false);
+ if (aDoc.IsNull())
+ {
+ TopoDS_Shape aShape = DBRep::Get (aNameVar);
+ if (!aShape.IsNull())
+ {
+ anApp->NewDocument (TCollection_ExtendedString ("BinXCAF"), aDoc);
+ Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (aDoc->Main());
+ aShapeTool->AddShape (aShape);
+ }
+ }
+ }
+ else if (aFileName.IsEmpty())
+ {
+ aFileName = theArgVec[anArgIter];
+ }
+ else
+ {
+ theDI << "Syntax error at '" << theArgVec[anArgIter] << "'";
+ return 1;
+ }
+ }
+ if (aDoc.IsNull()
+ && !aShapeName.IsEmpty())
+ {
+ theDI << "Syntax error: '" << aShapeName << "' is not a shape nor document";
+ return 1;
+ }
+ else if (aDoc.IsNull()
+ || aFileName.IsEmpty())
+ {
+ theDI << "Syntax error: wrong number of arguments";
+ return 1;
+ }
+
+ TDF_LabelSequence aRootLabels;
+ Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (aDoc->Main());
+ aShapeTool->GetFreeShapes (aRootLabels);
+ if (aRootLabels.IsEmpty())
+ {
+ theDI << "Error: empty document";
+ return 1;
+ }
+
+ if (isPntSet)
+ {
+ class PointCloudPlyWriter : public BRepLib_PointCloudShape, public RWPly_PlyWriterContext
+ {
+ public:
+ PointCloudPlyWriter (Standard_Real theTol)
+ : BRepLib_PointCloudShape (TopoDS_Shape(), theTol) {}
+
+ void AddFaceColor (const TopoDS_Shape& theFace, const Graphic3d_Vec4ub& theColor)
+ { myFaceColor.Bind (theFace, theColor); }
+
+ protected:
+ virtual void addPoint (const gp_Pnt& thePoint,
+ const gp_Vec& theNorm,
+ const gp_Pnt2d& theUV,
+ const TopoDS_Shape& theFace)
+ {
+ Graphic3d_Vec4ub aColor;
+ myFaceColor.Find (theFace, aColor);
+ RWPly_PlyWriterContext::WriteVertex (thePoint,
+ Graphic3d_Vec3 ((float )theNorm.X(), (float )theNorm.Y(), (float )theNorm.Z()),
+ Graphic3d_Vec2 ((float )theUV.X(), (float )theUV.Y()),
+ aColor);
+ }
+
+ private:
+ NCollection_DataMap<TopoDS_Shape, Graphic3d_Vec4ub> myFaceColor;
+ };
+
+ PointCloudPlyWriter aPlyCtx (aTol);
+ aPlyCtx.SetNormals (hasNormals);
+ aPlyCtx.SetColors (hasColors);
+ aPlyCtx.SetTexCoords (hasTexCoords);
+
+ TopoDS_Compound aComp;
+ BRep_Builder().MakeCompound (aComp);
+ for (XCAFPrs_DocumentExplorer aDocExplorer (aDoc, aRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
+ aDocExplorer.More(); aDocExplorer.Next())
+ {
+ const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
+ for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, aDocNode.Location, true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
+ {
+ BRep_Builder().Add (aComp, aFaceIter.Face());
+ Graphic3d_Vec4ub aColorVec (255);
+ if (aFaceIter.HasFaceColor())
+ {
+ Graphic3d_Vec4 aColorF = aFaceIter.FaceColor();
+ aColorVec.SetValues ((unsigned char )int(aColorF.r() * 255.0f),
+ (unsigned char )int(aColorF.g() * 255.0f),
+ (unsigned char )int(aColorF.b() * 255.0f),
+ (unsigned char )int(aColorF.a() * 255.0f));
+ }
+ aPlyCtx.AddFaceColor (aFaceIter.Face(), aColorVec);
+ }
+ }
+ aPlyCtx.SetShape (aComp);
+
+ Standard_Integer aNbPoints = isDensityPoints
+ ? aPlyCtx.NbPointsByDensity (aDens)
+ : aPlyCtx.NbPointsByTriangulation();
+ if (aNbPoints <= 0)
+ {
+ theDI << "Error: unable to generate points";
+ return 0;
+ }
+
+ if (!aPlyCtx.Open (aFileName)
+ || !aPlyCtx.WriteHeader (aNbPoints, 0, TColStd_IndexedDataMapOfStringString()))
+ {
+ theDI << "Error: unable to create file '" << aFileName << "'";
+ return 0;
+ }
+
+ Standard_Boolean isDone = isDensityPoints
+ ? aPlyCtx.GeneratePointsByDensity (aDens)
+ : aPlyCtx.GeneratePointsByTriangulation();
+ if (!isDone)
+ {
+ theDI << "Error: Point cloud was not generated in file '" << aFileName << "'";
+ }
+ else if (!aPlyCtx.Close())
+ {
+ theDI << "Error: Point cloud file '" << aFileName << "' was not written";
+ }
+ else
+ {
+ theDI << aNbPoints;
+ }
+ }
+ else
+ {
+ Handle(Draw_ProgressIndicator) aProgress = new Draw_ProgressIndicator (theDI, 1);
+ RWPly_CafWriter aPlyCtx (aFileName);
+ aPlyCtx.SetNormals (hasNormals);
+ aPlyCtx.SetColors (hasColors);
+ aPlyCtx.SetTexCoords (hasTexCoords);
+ aPlyCtx.SetPartId (hasPartId);
+ aPlyCtx.SetFaceId (hasFaceId);
+ aPlyCtx.Perform (aDoc, aFileInfo, aProgress->Start());
+ }
+ return 0;
+}
+
//-----------------------------------------------------------------------------
void XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands)
theCommands.Add ("meshdeform", "display deformed mesh", __FILE__, meshdeform, g );
theCommands.Add ("mesh_edge_width", "set width of edges", __FILE__, mesh_edge_width, g );
theCommands.Add ("meshinfo", "displays the number of nodes and triangles", __FILE__, meshinfo, g );
+ theCommands.Add ("WritePly", R"(
+WritePly Doc file [-normals {0|1}]=1 [-colors {0|1}]=1 [-uv {0|1}]=0 [-partId {0|1}]=1 [-faceId {0|1}]=0
+ [-pointCloud {0|1}]=0 [-distance Value]=0.0 [-density Value] [-tolerance Value]
+Write document or triangulated shape into PLY file.
+ -normals write per-vertex normals
+ -colors write per-vertex colors
+ -uv write per-vertex UV coordinates
+ -partId write per-element part index (alternative to -faceId)
+ -faceId write per-element face index (alternative to -partId)
+
+Generate point cloud out of the shape and write it into PLY file.
+ -pointCloud write point cloud instead without triangulation indices
+ -distance sets distance from shape into the range [0, Value];
+ -density sets density of points to generate randomly on surface;
+ -tolerance sets tolerance; default value is Precision::Confusion();
+)", __FILE__, WritePly, g);
+ theCommands.Add ("writeply",
+ "writeply shape file",
+ __FILE__, WritePly, g);
}
//==============================================================================
005 gltf_lateload
006 obj_read
007 obj_write
+008 ply_write
--- /dev/null
+puts "============"
+puts "0029325: Modeling Algorithms - add tool BRepLib_PointCloudShape for generation point cloud for specified shape"
+puts "============"
+puts ""
+
+pload XDE OCAF MODELING VISUALIZATION
+
+set aNbPntsExpected 32581
+set aTmpPly ${imagedir}/${casename}_tmp.ply
+lappend occ_tmp_files $aTmpPly
+
+restore [locate_data_file bug29325_EQUERRE.brep] aShape
+set aNbPnts [writeply aShape $aTmpPly -pointCloud -dist 0.0 -dens 0.1 -colors 0]
+if {$aNbPnts != $aNbPntsExpected} { puts "Error: ($aNbPnts) generated while expected ($aNbPntsExpected)" }
--- /dev/null
+puts "============"
+puts "0029325: Modeling Algorithms - add tool BRepLib_PointCloudShape for generation point cloud for specified shape"
+puts "============"
+puts ""
+
+pload XDE OCAF MODELING VISUALIZATION
+
+set aNbPntsExpected 27890
+set aTmpPly ${imagedir}/${casename}_tmp.ply
+lappend occ_tmp_files $aTmpPly
+
+restore [locate_data_file bug29325_SANGLE_DE_FIXATION.brep] aShape
+set aNbPnts [writeply aShape $aTmpPly -pointCloud -dist 0.0 -dens 0.5 -colors 0]
+if {$aNbPnts != $aNbPntsExpected} { puts "Error: ($aNbPnts) generated while expected ($aNbPntsExpected)" }
vdump $::imagedir/${::casename}_green.png
# random colors mode
-vpointcloud p s -randcolors
+vpointcloud p s -randcolors -nonormals
vdump $::imagedir/${::casename}_rand.png
# texture mapping