]> OCCT Git - occt.git/commitdiff
0033046: Modeling algorithms - improve performance of per-facet shape construction
authoraml <aml@opencascade.com>
Thu, 30 Jun 2022 08:47:41 +0000 (11:47 +0300)
committersmoskvin <smoskvin@opencascade.com>
Thu, 7 Jul 2022 16:26:22 +0000 (19:26 +0300)
Add new class (BRepBuilderAPI_MakeShapeOnMesh) to reconstruct shape from triangulation on per-facet basis.

src/BRepBuilderAPI/BRepBuilderAPI_MakeShapeOnMesh.cxx [new file with mode: 0644]
src/BRepBuilderAPI/BRepBuilderAPI_MakeShapeOnMesh.hxx [new file with mode: 0644]
src/BRepBuilderAPI/FILES
src/StlAPI/StlAPI_Reader.cxx
tests/bugs/fclasses/bug23192_1
tests/bugs/fclasses/bug23192_2
tests/bugs/stlvrml/bug27622
tests/bugs/xde/bug22670_1
tests/bugs/xde/bug22670_2
tests/perf/de/bug33046 [new file with mode: 0644]

diff --git a/src/BRepBuilderAPI/BRepBuilderAPI_MakeShapeOnMesh.cxx b/src/BRepBuilderAPI/BRepBuilderAPI_MakeShapeOnMesh.cxx
new file mode 100644 (file)
index 0000000..5d74c71
--- /dev/null
@@ -0,0 +1,247 @@
+// Created on: 2022-06-30
+// Created by: Alexander MALYSHEV
+// Copyright (c) 2022-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 <BRepBuilderAPI_MakeShapeOnMesh.hxx>
+
+#include <BRep_Builder.hxx>
+#include <BRepAdaptor_Curve.hxx>
+#include <BRepBuilderAPI_MakeEdge.hxx>
+#include <BRepBuilderAPI_MakeFace.hxx>
+#include <BRepBuilderAPI_MakeVertex.hxx>
+#include <BRepBuilderAPI_MakeWire.hxx>
+#include <NCollection_IndexedDataMap.hxx>
+
+namespace
+{
+  //! Structure representing mesh edge.
+  struct Edge
+  {
+    //! Constructor. Sets edge nodes.
+    Edge(const Standard_Integer TheIdx1,
+         const Standard_Integer TheIdx2)
+    : Idx1(Min(TheIdx1, TheIdx2)),
+      Idx2(Max(TheIdx1, TheIdx2))
+    {}
+
+    //! Comparison operator.
+    Standard_Boolean operator<(const Edge& other) const
+    {
+      if (Idx1 <  other.Idx1 ||
+         (Idx1 == other.Idx1 && Idx2 < other.Idx2))
+      {
+        return Standard_True;
+      }
+
+      return Standard_False;
+    }
+
+    //! First index. It is lower or equal than the second.
+    Standard_Integer Idx1;
+
+    //! Second index.
+    Standard_Integer Idx2;
+  };
+
+  //! Hasher of Edge structure.
+  struct EdgeHasher
+  {
+
+    //! Returns hash code for the given edge.
+    static Standard_Integer HashCode(const Edge&            theEdge,
+                                     const Standard_Integer theUpperBound)
+    {
+      // Circle-based collisions.
+      return ::HashCode(theEdge.Idx1 * theEdge.Idx1 + theEdge.Idx2 * theEdge.Idx2, theUpperBound);
+    }
+
+    //! Returns true if two edges are equal.
+    static Standard_Boolean IsEqual(const Edge& theEdge1,
+                                    const Edge& theEdge2)
+    {
+      return theEdge1.Idx1 == theEdge2.Idx1 && theEdge1.Idx2 == theEdge2.Idx2;
+    }
+
+  };
+}
+
+//=======================================================================
+//function : Build
+//purpose  : 
+//=======================================================================
+void BRepBuilderAPI_MakeShapeOnMesh::Build(const Message_ProgressRange& theRange)
+{
+  // Generally, this method guarantees topology sharing by mapping mesh primitives 
+  // into topological counterparts.
+  // mesh points -> topological vertices
+  // mesh edges  -> topological edges
+
+  // Cannot reconstruct anything from null or empty mesh.
+  if (myMesh.IsNull() || myMesh->NbNodes() == 0 || myMesh->NbTriangles() == 0)
+    return;
+
+  const Standard_Integer aNbNodes = myMesh->NbNodes();
+  const Standard_Integer aNbTriangles = myMesh->NbTriangles();
+
+  // We are going to have three loops: iterate once over nodes and iterate twice
+  // over triangles of input mesh.
+  Message_ProgressScope aPS(theRange,
+                            "Per-facet shape construction", 
+                            Standard_Real(aNbNodes + 2 * aNbTriangles));
+
+  // Build shared vertices.
+  NCollection_IndexedDataMap<Standard_Integer, TopoDS_Vertex> aPnt2VertexMap;
+  
+  for (Standard_Integer i = 1; i <= aNbNodes; ++i)
+  {
+    aPS.Next();
+    if (aPS.UserBreak())
+      return;
+
+    const gp_Pnt aP = myMesh->Node(i);
+    const TopoDS_Vertex aV = BRepBuilderAPI_MakeVertex(aP);
+    aPnt2VertexMap.Add(i, aV);
+  }
+
+  // Build shared edges.
+  NCollection_IndexedDataMap<Edge, TopoDS_Edge, EdgeHasher> anEdgeToTEgeMap;
+  for (Standard_Integer i = 1; i <= aNbTriangles; ++i)
+  {
+    aPS.Next();
+    if (aPS.UserBreak())
+      return;
+
+    Standard_Integer anIdx[3];
+    const Poly_Triangle& aTriangle = myMesh->Triangle(i);
+    aTriangle.Get(anIdx[0], anIdx[1], anIdx[2]);
+
+    // Skip degenerated triangles.
+    if (anIdx[0] == anIdx[1] || anIdx[0] == anIdx[2] || anIdx[1] == anIdx[2])
+      continue;
+
+    const gp_Pnt aP1 = myMesh->Node(anIdx[0]);
+    const gp_Pnt aP2 = myMesh->Node(anIdx[1]);
+    const gp_Pnt aP3 = myMesh->Node(anIdx[2]);
+    const Standard_Real aD1 = aP1.SquareDistance(aP2);
+    const Standard_Real aD2 = aP1.SquareDistance(aP3);
+    const Standard_Real aD3 = aP2.SquareDistance(aP3);
+    if (aD1 < gp::Resolution() ||
+        aD2 < gp::Resolution() ||
+        aD3 < gp::Resolution())
+    {
+      continue;
+    }
+
+    // Edges are constructed in forward order for the existing normals orientation.
+    // In Poly_Triangulation, positive direction is defined as cross product:
+    // (aV1, aV2) x (aV1, aV3).
+    const TopoDS_Vertex& aV1 = aPnt2VertexMap.FindFromKey(anIdx[0]);
+    const TopoDS_Vertex& aV2 = aPnt2VertexMap.FindFromKey(anIdx[1]);
+    const TopoDS_Vertex& aV3 = aPnt2VertexMap.FindFromKey(anIdx[2]);
+
+    const Edge aMeshEdge1(anIdx[0], anIdx[1]);
+    const Edge aMeshEdge2(anIdx[1], anIdx[2]);
+    const Edge aMeshEdge3(anIdx[2], anIdx[0]);
+
+    BRepBuilderAPI_MakeEdge aMaker1(aV1, aV2);
+    BRepBuilderAPI_MakeEdge aMaker2(aV2, aV3);
+    BRepBuilderAPI_MakeEdge aMaker3(aV3, aV1);
+
+    TopoDS_Edge aTE1 = aMaker1.Edge();
+    if (anIdx[1] < anIdx[0])
+      aTE1.Reverse();
+
+    TopoDS_Edge aTE2 = aMaker2.Edge();
+    if (anIdx[2] < anIdx[1])
+      aTE2.Reverse();
+
+    TopoDS_Edge aTE3 = aMaker3.Edge();
+    if (anIdx[0] < anIdx[2])
+      aTE3.Reverse();
+
+    anEdgeToTEgeMap.Add(aMeshEdge1, aTE1);
+    anEdgeToTEgeMap.Add(aMeshEdge2, aTE2);
+    anEdgeToTEgeMap.Add(aMeshEdge3, aTE3);
+  }
+
+  // Construct planar faces using shared topology.
+  TopoDS_Compound aResult;
+  BRep_Builder aBB;
+  aBB.MakeCompound(aResult);
+  for (Standard_Integer i = 1; i <= aNbTriangles; ++i)
+  {
+    aPS.Next();
+    if (aPS.UserBreak())
+      return;
+
+    Standard_Integer anIdx[3];
+    const Poly_Triangle& aTriangle = myMesh->Triangle(i);
+    aTriangle.Get(anIdx[0], anIdx[1], anIdx[2]);
+
+    const Edge aMeshEdge1(anIdx[0], anIdx[1]);
+    const Edge aMeshEdge2(anIdx[1], anIdx[2]);
+    const Edge aMeshEdge3(anIdx[2], anIdx[0]);
+    const Standard_Boolean isReversed1 = anIdx[1] < anIdx[0];
+    const Standard_Boolean isReversed2 = anIdx[2] < anIdx[1];
+    const Standard_Boolean isReversed3 = anIdx[0] < anIdx[2];
+
+    // Edges can be skipped in case of mesh defects - topologically or geometrically
+    // degenerated triangles.
+    const Standard_Boolean aHasAllEdges = anEdgeToTEgeMap.Contains(aMeshEdge1) &&
+                                          anEdgeToTEgeMap.Contains(aMeshEdge2) &&
+                                          anEdgeToTEgeMap.Contains(aMeshEdge3) ;
+    if (!aHasAllEdges)
+      continue;
+
+    TopoDS_Edge aTEdge1 = anEdgeToTEgeMap.FindFromKey(aMeshEdge1);
+    if (isReversed1)
+      aTEdge1.Reverse();
+    TopoDS_Edge aTEdge2 = anEdgeToTEgeMap.FindFromKey(aMeshEdge2);
+    if (isReversed2)
+      aTEdge2.Reverse();
+    TopoDS_Edge aTEdge3 = anEdgeToTEgeMap.FindFromKey(aMeshEdge3);
+    if (isReversed3)
+      aTEdge3.Reverse();
+
+    BRepBuilderAPI_MakeWire aWireMaker;
+    aWireMaker.Add(aTEdge1);
+    aWireMaker.Add(aTEdge2);
+    aWireMaker.Add(aTEdge3);
+    const TopoDS_Wire aWire = aWireMaker.Wire();
+
+    // Construct plane explicitly since it is faster than automatic construction
+    // within BRepBuilderAPI_MakeFace.
+    BRepAdaptor_Curve aC1(aTEdge1);
+    BRepAdaptor_Curve aC2(aTEdge2);
+    const gp_Dir aD1 = aC1.Line().Direction();
+    const gp_Dir aD2 = aC2.Line().Direction();
+    gp_XYZ aN = aD1.XYZ().Crossed(aD2.XYZ());
+    if (aN.SquareModulus() < Precision::SquareConfusion())
+      continue;
+    if (aTEdge1.Orientation() == TopAbs_REVERSED)
+      aN.Reverse();
+    if (aTEdge2.Orientation() == TopAbs_REVERSED)
+      aN.Reverse();
+    const gp_Dir aNorm(aN);
+    gp_Pln aPln(myMesh->Node(anIdx[0]), aNorm);
+
+    BRepBuilderAPI_MakeFace aFaceMaker(aPln, aWire);
+    const TopoDS_Face aFace = aFaceMaker.Face();
+
+    aBB.Add(aResult, aFace);
+  }
+
+  this->Done();
+  myShape = aResult;
+}
diff --git a/src/BRepBuilderAPI/BRepBuilderAPI_MakeShapeOnMesh.hxx b/src/BRepBuilderAPI/BRepBuilderAPI_MakeShapeOnMesh.hxx
new file mode 100644 (file)
index 0000000..17a5db8
--- /dev/null
@@ -0,0 +1,46 @@
+// Created on: 2022-06-30
+// Created by: Alexander MALYSHEV
+// Copyright (c) 2022-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 _BRepBuilderAPI_MakeShapeOnMesh_HeaderFile
+#define _BRepBuilderAPI_MakeShapeOnMesh_HeaderFile
+
+#include <BRepBuilderAPI_MakeShape.hxx>
+#include <Poly_Triangulation.hxx>
+
+//! Builds shape on per-facet basis on the input mesh. Resulting shape has shared
+//! edges by construction, but no maximization (unify same domain) is applied.
+//! No generation history is provided.
+class BRepBuilderAPI_MakeShapeOnMesh : public BRepBuilderAPI_MakeShape
+{
+public:
+
+  DEFINE_STANDARD_ALLOC
+
+  //! Ctor. Sets mesh to process.
+  //! @param theMesh [in] - Mesh to construct shape for.
+  BRepBuilderAPI_MakeShapeOnMesh(const Handle(Poly_Triangulation)& theMesh)
+  : myMesh(theMesh)
+  {}
+
+  //! Builds shape on mesh.
+  Standard_EXPORT virtual void Build(const Message_ProgressRange& theRange = Message_ProgressRange()) Standard_OVERRIDE;
+
+private:
+
+  Handle(Poly_Triangulation) myMesh;
+
+};
+
+#endif // _BRepBuilderAPI_MakeShapeOnMesh_HeaderFile
index 4cd5112637445dd61895b3209ca74149b6afe642..aff1334a50248df962b5c82de0b9d088d09f5354 100644 (file)
@@ -26,6 +26,8 @@ BRepBuilderAPI_MakePolygon.cxx
 BRepBuilderAPI_MakePolygon.hxx
 BRepBuilderAPI_MakeShape.cxx
 BRepBuilderAPI_MakeShape.hxx
+BRepBuilderAPI_MakeShapeOnMesh.cxx
+BRepBuilderAPI_MakeShapeOnMesh.hxx
 BRepBuilderAPI_MakeShell.cxx
 BRepBuilderAPI_MakeShell.hxx
 BRepBuilderAPI_MakeSolid.cxx
index 748d6557eba7dd1d3d03398162de977b00ed7ab6..347b89f4033b2b45d616cd5dcc28aeaf67a97630 100644 (file)
 
 #include <StlAPI_Reader.hxx>
 
-#include <BRep_Builder.hxx>
-#include <BRepBuilderAPI_MakeFace.hxx>
-#include <BRepBuilderAPI_MakePolygon.hxx>
-#include <BRepBuilderAPI_MakeVertex.hxx>
+#include <BRepBuilderAPI_MakeShapeOnMesh.hxx>
 #include <BRepBuilderAPI_Sewing.hxx>
-#include <gp_Pnt.hxx>
 #include <RWStl.hxx>
-#include <TopoDS_Compound.hxx>
-#include <TopoDS_Face.hxx>
-#include <TopoDS_Shape.hxx>
-#include <TopoDS_Vertex.hxx>
-#include <TopoDS_Wire.hxx>
 
 //=============================================================================
 //function : Read
@@ -35,55 +26,17 @@ Standard_Boolean StlAPI_Reader::Read (TopoDS_Shape&          theShape,
 {
   Handle(Poly_Triangulation) aMesh = RWStl::ReadFile (theFileName);
   if (aMesh.IsNull())
-  {
     return Standard_False;
-  }
 
-  TopoDS_Vertex aTriVertexes[3];
-  TopoDS_Face aFace;
-  TopoDS_Wire aWire;
-  BRepBuilderAPI_Sewing aSewingTool;
-  aSewingTool.Init (1.0e-06, Standard_True);
-
-  TopoDS_Compound aComp;
-  BRep_Builder BuildTool;
-  BuildTool.MakeCompound (aComp);
-
-  for (Standard_Integer aTriIdx  = 1; aTriIdx <= aMesh->NbTriangles(); ++aTriIdx)
-  {
-    const Poly_Triangle aTriangle = aMesh->Triangle (aTriIdx);
-
-    Standard_Integer anId[3];
-    aTriangle.Get(anId[0], anId[1], anId[2]);
-
-    const gp_Pnt aPnt1 = aMesh->Node (anId[0]);
-    const gp_Pnt aPnt2 = aMesh->Node (anId[1]);
-    const gp_Pnt aPnt3 = aMesh->Node (anId[2]);
-    if (!(aPnt1.IsEqual (aPnt2, 0.0))
-     && !(aPnt1.IsEqual (aPnt3, 0.0)))
-    {
-      aTriVertexes[0] = BRepBuilderAPI_MakeVertex (aPnt1);
-      aTriVertexes[1] = BRepBuilderAPI_MakeVertex (aPnt2);
-      aTriVertexes[2] = BRepBuilderAPI_MakeVertex (aPnt3);
+  BRepBuilderAPI_MakeShapeOnMesh aConverter(aMesh);
+  aConverter.Build();
+  if (!aConverter.IsDone())
+    return Standard_False;
 
-      aWire = BRepBuilderAPI_MakePolygon (aTriVertexes[0], aTriVertexes[1], aTriVertexes[2], Standard_True);
-      if (!aWire.IsNull())
-      {
-        aFace = BRepBuilderAPI_MakeFace (aWire);
-        if (!aFace.IsNull())
-        {
-          BuildTool.Add (aComp, aFace);
-        }
-      }
-    }
-  }
+  TopoDS_Shape aResult = aConverter.Shape();
+  if (aResult.IsNull())
+    return Standard_False;
 
-  aSewingTool.Load (aComp);
-  aSewingTool.Perform();
-  theShape = aSewingTool.SewedShape();
-  if (theShape.IsNull())
-  {
-    theShape = aComp;
-  }
+  theShape = aResult;
   return Standard_True;
 }
index 56a7b162a58314a30987dd47a53965086fd9f37e..00032fc677f0c0ac8c4d9058606401f5f1509425 100755 (executable)
@@ -24,7 +24,7 @@ catch {exec chmod 777 ${aFile}}
 if { [file exists ${aFile}] } {
   readstl result ${aFile} -brep
 
-checknbshapes result -vertex 8 -edge 18 -wire 12 -face 12 -shell 1 -solid 0 -compsolid 0 -compound 0 -shape 51
+checknbshapes result -vertex 8 -edge 18 -wire 12 -face 12 -shell 0 -solid 0 -compsolid 0 -compound 1 -shape 51
   # Check file size
   set size_status 0
   set filesize [ file size ${aFile} ]
index 3081072bd201697b020f0668804450ddb1a2fd4e..53775e0908c62f1f3f1f7cb39c42c5f166406304 100755 (executable)
@@ -24,7 +24,7 @@ catch {exec chmod 777 ${aFile}}
 if { [file exists ${aFile}] } {
   readstl result ${aFile} -brep
 
-checknbshapes result -vertex 8 -edge 18 -wire 12 -face 12 -shell 1 -solid 0 -compsolid 0 -compound 0 -shape 51
+checknbshapes result -vertex 8 -edge 18 -wire 12 -face 12 -shell 0 -solid 0 -compsolid 0 -compound 1 -shape 51
   # Check file size
   set size_status 0
   set filesize [ file size ${aFile} ]
index a19f4d2fc809b06e1c3d0514327afc6697edf542..94b46b50a73b4d86e5163f731190130d8eecd2a8 100644 (file)
@@ -14,10 +14,10 @@ Number of shapes in result
  EDGE      : 5
  WIRE      : 2
  FACE      : 2
- SHELL     : 1
+ SHELL     : 0
  SOLID     : 0
  COMPSOLID : 0
- COMPOUND  : 0
+ COMPOUND  : 1
  SHAPE     : 14
 "
 checknbshapes result -ref ${nbshapes_expected} -t -m "Result of STL-reading operation"
index 1718c22caca41c0c5ca117a81acc005ba69cfb65..24a19a081ed45c60d5b35712932da057dc6f413b 100755 (executable)
@@ -21,5 +21,5 @@ catch {exec chmod 777 ${aFile}}
 
 readstl result ${aFile} -brep
 
-checknbshapes result -vertex 8 -edge 18 -wire 12 -face 12 -shell 1 -solid 0 -compsolid 0 -compound 0 -shape 51
+checknbshapes result -vertex 8 -edge 18 -wire 12 -face 12 -shell 0 -solid 0 -compsolid 0 -compound 1 -shape 51
 checkview -display result -2d -path ${imagedir}/${test_image}.png
index d9d4938f016de310d202ecbec744942c541879e2..2c50324fc5a393e65486b8d1f9d40b23d67502bb 100755 (executable)
@@ -32,7 +32,7 @@ if { [catch { readstl res_mesh $filepath -brep } catch_result] } {
 
     readstl result ${aFile} -brep
 
-    checknbshapes result -vertex 8 -edge 18 -wire 12 -face 12 -shell 1 -solid 0 -compsolid 0 -compound 0 -shape 51
+    checknbshapes result -vertex 8 -edge 18 -wire 12 -face 12 -shell 0 -solid 0 -compsolid 0 -compound 1 -shape 51
 }
 checkprops result -s 600 
 checkshape result
diff --git a/tests/perf/de/bug33046 b/tests/perf/de/bug33046
new file mode 100644 (file)
index 0000000..5cc1831
--- /dev/null
@@ -0,0 +1,17 @@
+puts "========"
+puts "0033046: Modeling algorithms - improve performance of per-facet shape construction"
+puts "Checks execution time of new shape construction method from STL file."
+puts "========" 
+
+cpulimit 25
+
+# Run stl loading and translation to the shape.
+chrono c reset; chrono c start;
+readstl m [locate_data_file model_stl_025.stl] -brep
+chrono c stop; chrono c show;
+
+# Check that model is converted succesfully.
+checknbshapes m -face 54337;
+
+# Visual check
+checkview -display m -2d -path ${imagedir}/${test_image}.png
\ No newline at end of file