Salome HOME
Non planar faces 35156 35156_NonPlanarFace 19/head
authorEkaterina Sukhareva <ekaterina.sukhareva@opencascade.com>
Fri, 25 Aug 2023 15:52:12 +0000 (16:52 +0100)
committerEkaterina Sukhareva <ekaterina.sukhareva@opencascade.com>
Tue, 24 Oct 2023 12:02:37 +0000 (13:02 +0100)
13 files changed:
src/BuildPlugin/BuildPlugin_Face.cpp
src/BuildPlugin/BuildPlugin_Face.h
src/BuildPlugin/BuildPlugin_Validators.cpp
src/BuildPlugin/Test/TestNonPlanarFace.py [new file with mode: 0755]
src/BuildPlugin/Test/TestNonPlanarFace_Edges.py [new file with mode: 0644]
src/BuildPlugin/doc/faceFeature.rst
src/BuildPlugin/tests.set
src/GeomAPI/GeomAPI_Shape.cpp
src/GeomAlgoAPI/CMakeLists.txt
src/GeomAlgoAPI/GeomAlgoAPI.i
src/GeomAlgoAPI/GeomAlgoAPI_NonPlanarFace.cpp [new file with mode: 0644]
src/GeomAlgoAPI/GeomAlgoAPI_NonPlanarFace.h [new file with mode: 0644]
src/GeomAlgoAPI/GeomAlgoAPI_swig.h

index ba39fe3be77c9a37a81f2ffa9fcf8282eb061aa5..874ff58f3663b509ededd16b84471677e7ba9f7d 100644 (file)
@@ -32,6 +32,7 @@
 #include <GeomAlgoAPI_ShapeTools.h>
 #include <GeomAlgoAPI_SketchBuilder.h>
 #include <GeomAlgoAPI_Copy.h>
+#include <GeomAlgoAPI_NonPlanarFace.h>
 
 //=================================================================================================
 BuildPlugin_Face::BuildPlugin_Face()
@@ -60,8 +61,10 @@ void BuildPlugin_Face::execute()
 
   // Collect base shapes.
   ListOfShape anEdges;
+  ListOfShape aNonPlanarEdges;
   ListOfShape anOriginalFaces;
   ListOfShape aContexts;
+
   getOriginalShapesAndContexts(BASE_OBJECTS_ID(), anOriginalFaces, aContexts);
   anOriginalFaces.clear();
   std::list< std::shared_ptr<GeomAPI_Dir> > aListOfNormals;
@@ -88,16 +91,29 @@ void BuildPlugin_Face::execute()
       }
     }
 
-    for(GeomAPI_ShapeExplorer anExp(aShape, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
-      GeomShapePtr anEdge = anExp.current();
-      anEdges.push_back(anEdge);
-    }
-
     // check whether the context is a sketch, in this case store its normal for further needs
     std::shared_ptr<GeomAPI_PlanarEdges> aSketch =
         std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(aContext);
     if (aSketch)
       aListOfNormals.push_back(aSketch->norm());
+
+    bool isPlanar(aShape->isPlanar() || aSketch);
+
+    for(GeomAPI_ShapeExplorer anExp(aShape, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
+      GeomShapePtr anEdge = anExp.current();
+      isPlanar? anEdges.push_back(anEdge) : aNonPlanarEdges.push_back(anEdge);
+    }
+  }
+
+  if (!anEdges.empty())
+  {
+    //check is planar objects belong to nke
+    std::shared_ptr<GeomAPI_Pln> aPln = GeomAlgoAPI_ShapeTools::findPlane(anEdges);
+    if(!aPln.get())
+    {
+      aNonPlanarEdges.insert(aNonPlanarEdges.end(), anEdges.begin(), anEdges.end());
+      anEdges.clear();
+    }
   }
 
   // Build faces by edges.
@@ -107,6 +123,18 @@ void BuildPlugin_Face::execute()
     buildFacesByEdges(anEdges, aListOfNormals, aFaces, aFaceBuilder);
   int aNbFacesFromEdges = (int)aFaces.size();
 
+  // Build non-planar faces by edges.
+  GeomMakeShapePtr aNonPlanarFaceBuilder;
+  int aNbNonPlanarFaces = (int)aFaces.size();
+  if(!aNonPlanarEdges.empty())
+  {
+    ListOfShape aNonPlanarFaces;
+    buildNonPlanarFacesByEdges(aNonPlanarEdges, aNonPlanarFaces, aNonPlanarFaceBuilder);
+    // Add non-planar faces to common faces list.
+    aFaces.insert(aFaces.end(), aNonPlanarFaces.begin(), aNonPlanarFaces.end());
+    aNbNonPlanarFaces += (int)aNonPlanarFaces.size();
+  }
+
   // Add faces selected by user.
   aFaces.insert(aFaces.end(), anOriginalFaces.begin(), anOriginalFaces.end());
 
@@ -116,6 +144,8 @@ void BuildPlugin_Face::execute()
     std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList);
     if (anIndex < aNbFacesFromEdges)
       aMakeShapeList->appendAlgo(aFaceBuilder);
+    else if(anIndex < aNbNonPlanarFaces)
+      aMakeShapeList->appendAlgo(aNonPlanarFaceBuilder);
 
     GeomShapePtr aShape = *anIt;
     GeomMakeShapePtr aCopy(new GeomAlgoAPI_Copy(aShape));
@@ -124,6 +154,8 @@ void BuildPlugin_Face::execute()
     ListOfShape aBaseShapes;
     if (anIndex < aNbFacesFromEdges)
       aBaseShapes = anEdges;
+    else if(anIndex < aNbNonPlanarFaces)
+      aBaseShapes = aNonPlanarEdges;
     else
       aBaseShapes.push_back(aShape);
     storeResult(aMakeShapeList, aBaseShapes, aContexts, aCopy->shape(), anIndex++);
@@ -132,6 +164,7 @@ void BuildPlugin_Face::execute()
   removeResults(anIndex);
 }
 
+//==================================================================================================
 void BuildPlugin_Face::buildFacesByEdges(
     const ListOfShape& theEdges,
     const std::list< std::shared_ptr<GeomAPI_Dir> >& theNormals,
@@ -167,3 +200,18 @@ void BuildPlugin_Face::buildFacesByEdges(
   GeomAlgoAPI_ShapeTools::makeFacesWithHoles(aPln->location(), aPln->direction(),
                                              aWires, theFaces);
 }
+
+//==================================================================================================
+void BuildPlugin_Face::buildNonPlanarFacesByEdges(
+    const ListOfShape& theShapes,
+    ListOfShape& theFaces,
+    std::shared_ptr<GeomAlgoAPI_MakeShape>& theBuilderAlgo) const
+{
+
+    // Get faces.
+    std::shared_ptr<GeomAlgoAPI_NonPlanarFace> aNonPlanarFaceBuilder
+            (new GeomAlgoAPI_NonPlanarFace(theShapes));
+    theFaces = aNonPlanarFaceBuilder->faces();
+    theBuilderAlgo = aNonPlanarFaceBuilder;
+
+}
index cbb4e9cbf33ca1d897be97afafb5a430cf517bfd..4653845f7adb0d7cb6748ff0d74c6ede23f36783 100644 (file)
@@ -69,6 +69,10 @@ private:
                          const std::list< std::shared_ptr<GeomAPI_Dir> >& theNormals,
                          std::list< std::shared_ptr<GeomAPI_Shape> >& theFaces,
                          std::shared_ptr<GeomAlgoAPI_MakeShape>& theBuilderAlgo) const;
+
+  void buildNonPlanarFacesByEdges(const ListOfShape& theShapes,
+                                  ListOfShape& theFaces,
+                                  std::shared_ptr<GeomAlgoAPI_MakeShape>& theBuilderAlgo) const;
 };
 
 #endif
index 13063fa0361f545b6a08242dc83bc98322715ce2..81c16c770faee3e8ab8cae572f92a5c9f58eaa76 100644 (file)
@@ -39,6 +39,7 @@
 #include <GeomAlgoAPI_WireBuilder.h>
 #include <GeomAlgoAPI_MakeVolume.h>
 #include <GeomAlgoAPI_Tools.h>
+#include <GeomAlgoAPI_NonPlanarFace.h>
 
 #include <GeomValidators_FeatureKind.h>
 #include <GeomValidators_ShapeType.h>
@@ -292,7 +293,9 @@ bool BuildPlugin_ValidatorBaseForFace::isValid(const std::shared_ptr<ModelAPI_Fe
   bool hasFaces = false;
 
   // Collect base shapes.
-  ListOfShape anEdges;
+  ListOfShape anAllEdges;
+  ListOfShape aPlanarEdges;
+  ListOfShape aNonPlanarEdges;
   for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
     AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
     GeomShapePtr aShape = aSelection->value();
@@ -313,24 +316,26 @@ bool BuildPlugin_ValidatorBaseForFace::isValid(const std::shared_ptr<ModelAPI_Fe
       continue;
     }
 
+    bool isPlanar(aShape->isPlanar());
     for(GeomAPI_ShapeExplorer anExp(aShape, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
       hasEdgesOrWires = true;
       GeomShapePtr anEdge = anExp.current();
-      anEdges.push_back(anEdge);
+      anAllEdges.push_back(anEdge);
+      isPlanar? aPlanarEdges.push_back(anEdge): aNonPlanarEdges.push_back(anEdge);
     }
   }
 
   if (hasFaces && hasEdgesOrWires) {
     theError = "Faces and edges/wires should be selected together.";
     return false;
-  } else if (hasEdgesOrWires && anEdges.empty()) {
+  } else if (hasEdgesOrWires && anAllEdges.empty()) {
     theError = "Objects are not selected.";
     return false;
   }
 
   // Check that edges does not have intersections.
-  if(anEdges.size() > 1) {
-    GeomAlgoAPI_PaveFiller aPaveFiller(anEdges, false);
+  if(anAllEdges.size() > 1) {
+    GeomAlgoAPI_PaveFiller aPaveFiller(anAllEdges, false);
     if(!aPaveFiller.isDone()) {
       theError = "Error while checking if edges intersects.";
       return false;
@@ -342,25 +347,36 @@ bool BuildPlugin_ValidatorBaseForFace::isValid(const std::shared_ptr<ModelAPI_Fe
         anExp(aSectedEdges, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
       anEdgesNum++;
     }
-    if(anEdgesNum != anEdges.size()) {
+    if(anEdgesNum != anAllEdges.size()) {
       theError = "Selected objects have intersections.";
       return false;
     }
   }
 
-  if (!anEdges.empty()) {
+  //check only planar onjects
+  bool isPlanarBelongToOnePlane(false);
+  if (!aPlanarEdges.empty()) {
     // Check that they are planar.
-    std::shared_ptr<GeomAPI_Pln> aPln = GeomAlgoAPI_ShapeTools::findPlane(anEdges);
-    if(!aPln.get()) {
-      theError = "Selected object(s) should belong to only one plane.";
-      return false;
+    std::shared_ptr<GeomAPI_Pln> aPln = GeomAlgoAPI_ShapeTools::findPlane(aPlanarEdges);
+    if(aPln.get()) {
+      // Check that selected objects have closed contours.
+      isPlanarBelongToOnePlane = true;
+      GeomAlgoAPI_SketchBuilder aBuilder(aPln, aPlanarEdges);
+      const ListOfShape& aFaces = aBuilder.faces();
+      if(aFaces.empty()) {
+        theError = "Selected planar objects do not generate closed contour.";
+        return false;
+      }
     }
+  }
 
-    // Check that selected objects have closed contours.
-    GeomAlgoAPI_SketchBuilder aBuilder(aPln, anEdges);
-    const ListOfShape& aFaces = aBuilder.faces();
+  //check non planar objects
+  if(!aNonPlanarEdges.empty() || (!aPlanarEdges.empty() && !isPlanarBelongToOnePlane))
+  {
+    GeomAlgoAPI_NonPlanarFace aNonPlanarFaceBuilder(isPlanarBelongToOnePlane? aNonPlanarEdges : anAllEdges);
+    const ListOfShape& aFaces = aNonPlanarFaceBuilder.faces();
     if(aFaces.empty()) {
-      theError = "Selected objects do not generate closed contour.";
+      theError = "Selected non-planar objects do not generate closed contour.";
       return false;
     }
   }
diff --git a/src/BuildPlugin/Test/TestNonPlanarFace.py b/src/BuildPlugin/Test/TestNonPlanarFace.py
new file mode 100755 (executable)
index 0000000..f0ffbe5
--- /dev/null
@@ -0,0 +1,67 @@
+# Copyright (C) 2017-2023  CEA, EDF
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""Test de la création de faces gauches"""
+
+import salome
+
+salome.salome_init()
+
+from SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+
+### Create Part
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+
+### Create Point
+l_noms = list()
+l_coo = list()
+l_coo.append((0, 0, 0))
+l_coo.append((1, 8, 0))
+l_coo.append((3, 10, 4))
+l_coo.append((5, 5, 5))
+l_coo.append((2, 3, 1))
+for iaux, (coo_x,coo_y,coo_z) in enumerate(l_coo):
+  point = model.addPoint(Part_1_doc, coo_x,coo_y,coo_z)
+  nom = "P_{}".format(iaux)
+  point.setName(nom)
+  point.result().setName(nom)
+  l_noms.append(nom)
+
+### Create Interpolation
+Interpolation_1_objects = list()
+for nom in l_noms:
+  Interpolation_1_objects.append(model.selection("VERTEX", "all-in-{}".format(nom)))
+Interpolation_1 = model.addInterpolation(Part_1_doc, Interpolation_1_objects, True, False)
+
+### Create Wire
+Wire_1 = model.addWire(Part_1_doc, [model.selection("EDGE", "Interpolation_1_1")], False)
+
+### Create Face
+Face_1 = model.addFace(Part_1_doc, [model.selection("WIRE", "Wire_1_1")])
+
+model.end()
+
+if salome.sg.hasDesktop():
+  salome.sg.updateObjBrowser()
diff --git a/src/BuildPlugin/Test/TestNonPlanarFace_Edges.py b/src/BuildPlugin/Test/TestNonPlanarFace_Edges.py
new file mode 100644 (file)
index 0000000..12090f5
--- /dev/null
@@ -0,0 +1,78 @@
+# Copyright (C) 2017-2023  CEA, EDF
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+import sys
+import salome
+
+salome.salome_init()
+import salome_notebook
+notebook = salome_notebook.NoteBook()
+sys.path.insert(0, r'/home/eksu/S2')
+
+###
+### SHAPER component
+###
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+
+### Create Part
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+
+### Create Points
+Point_2 = model.addPoint(Part_1_doc, 0, 0, 0)
+Point_3 = model.addPoint(Part_1_doc, 10, 0, 0)
+Point_4 = model.addPoint(Part_1_doc, 0, 10, 0)
+Point_5 = model.addPoint(Part_1_doc, 10, 10, 3)
+
+### Create Edges
+Edge_1 = model.addEdge(Part_1_doc, model.selection("VERTEX", "Point_1"), model.selection("VERTEX", "Point_2"))
+Edge_2 = model.addEdge(Part_1_doc, model.selection("VERTEX", "Edge_1_1/Modified_Vertex&Point_2/Point_2"), model.selection("VERTEX", "Point_4"))
+Edge_3 = model.addEdge(Part_1_doc, model.selection("VERTEX", "Edge_2_1/Modified_Vertex&Point_4/Point_4"), model.selection("VERTEX", "Point_3"))
+Edge_4 = model.addEdge(Part_1_doc, model.selection("VERTEX", "Point_3"), model.selection("VERTEX", "Point_1"))
+
+### Create Wire
+Wire_1_objects = [model.selection("EDGE", "Edge_1_1"),
+                  model.selection("EDGE", "Edge_2_1"),
+                  model.selection("EDGE", "Edge_4_1"),
+                  model.selection("EDGE", "Edge_3_1")]
+Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False)
+
+### Create Face
+Face_1_objects = [model.selection("EDGE", "Wire_1_1/Modified_Edge&Edge_1_1/Edge_1_1"),
+                  model.selection("EDGE", "Wire_1_1/Modified_Edge&Edge_2_1/Edge_2_1"),
+                  model.selection("EDGE", "Wire_1_1/Modified_Edge&Edge_4_1/Edge_4_1"),
+                  model.selection("EDGE", "Wire_1_1/Modified_Edge&Edge_3_1/Edge_3_1")]
+Face_1 = model.addFace(Part_1_doc, Face_1_objects)
+
+model.end()
+
+###
+### SHAPERSTUDY component
+###
+
+model.publishToShaperStudy()
+import SHAPERSTUDY
+Face_1_1, = SHAPERSTUDY.shape(model.featureStringId(Face_1))
+
+if salome.sg.hasDesktop():
+  salome.sg.updateObjBrowser()
index 57f37737beb14f3a4a397df5e183ab96a1752ada..b37ee7deb7f6628da819982f104511f22d685783 100644 (file)
@@ -20,6 +20,8 @@ The following property panel will be opened:
   
 Select one or several faces in viewer. Additionally, a face can be build by a closed wire or a set of edges composing a closed wire.
 
+It is possible to build a non-planar face by selecting non-planar wire or set of edges composing a closed wire, which do not belong to only one plane.
+
 It is also possible to select a whole sketch result from the object browser. In this case, the smallest closed contour of the sketch will be transformed to the planar face.
 
 **Apply** button creates faces.
index 32ff7447b3fc50ccba0d3f90da1d97baeb431984..c4b39ebc0a3d8aa44e9de8d0763eba1e2aa24926 100644 (file)
@@ -71,4 +71,6 @@ SET(TEST_NAMES
                Test20469.py
                Test20513_1.py
                Test20513_2.py
-)
\ No newline at end of file
+               TestNonPlanarFace.py
+               TestNonPlanarFace_Edges.py
+)
index c03fd9d7aede5337b5164dcc3ff57705ce47d210..5fe8500d07c6cc4c2eae6929003dccc308913b8a 100644 (file)
@@ -326,23 +326,50 @@ bool GeomAPI_Shape::isPlanar() const
     BRepBuilderAPI_FindPlane aFindPlane(aShape);
     bool isFound = aFindPlane.Found() == Standard_True;
 
-    if(!isFound && aShapeType == TopAbs_EDGE) {
-      Standard_Real aFirst, aLast;
-      Handle(Geom_Curve) aCurve = BRep_Tool::Curve(TopoDS::Edge(aShape), aFirst, aLast);
-      Handle(Standard_Type) aType = aCurve->DynamicType();
-
-      if(aType == STANDARD_TYPE(Geom_TrimmedCurve)) {
-        Handle(Geom_TrimmedCurve) aTrimCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve);
-        aType = aTrimCurve->BasisCurve()->DynamicType();
-      }
+    if(!isFound) {
+
+      auto checkEdge = [](const TopoDS_Shape& theShape){
+        if(theShape.ShapeType()!= TopAbs_EDGE) 
+          return false;
+
+        Standard_Real aFirst, aLast;
+        Handle(Geom_Curve) aCurve = BRep_Tool::Curve(TopoDS::Edge(theShape), aFirst, aLast);
+        Handle(Standard_Type) aType = aCurve->DynamicType();
+        if(aType == STANDARD_TYPE(Geom_TrimmedCurve)) {
+          Handle(Geom_TrimmedCurve) aTrimCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve);
+          aType = aTrimCurve->BasisCurve()->DynamicType();
+        }
 
-      if(aType == STANDARD_TYPE(Geom_Line)
-          || aType == STANDARD_TYPE(Geom_Conic)
-          || aType == STANDARD_TYPE(Geom_Circle)
-          || aType == STANDARD_TYPE(Geom_Ellipse)
-          || aType == STANDARD_TYPE(Geom_Hyperbola)
-          || aType == STANDARD_TYPE(Geom_Parabola)) {
-        isFound = true;
+        if(aType == STANDARD_TYPE(Geom_Line)
+            || aType == STANDARD_TYPE(Geom_Conic)
+            || aType == STANDARD_TYPE(Geom_Circle)
+            || aType == STANDARD_TYPE(Geom_Ellipse)
+            || aType == STANDARD_TYPE(Geom_Hyperbola)
+            || aType == STANDARD_TYPE(Geom_Parabola)) {
+          return true;
+        }
+        return false;
+      };
+
+      if(aShapeType == TopAbs_WIRE){
+        //check if wire consist of only one edge
+        int aNbEdges = 0;
+        TopExp_Explorer anExp(aShape, TopAbs_EDGE);
+        for (TopExp_Explorer anExp(aShape, TopAbs_EDGE); anExp.More(); anExp.Next()) {
+          aNbEdges++;
+          if(aNbEdges == 1){
+            const TopoDS_Edge& anEdge = TopoDS::Edge(anExp.Current());
+            isFound = checkEdge(anEdge);
+          }
+          else{
+            //if more than one edge, check is not valid
+            isFound = false;
+            break;
+          }
+        }
+      }
+      else if(aShapeType == TopAbs_EDGE){
+        isFound = checkEdge(aShape);
       }
     }
 
index ea051707fb6a476f69c4a10d2fbf87fbaeed7ced..815f52f3d626ea5ba5de6f31f7cc747a896a1820 100644 (file)
@@ -101,6 +101,7 @@ SET(PROJECT_HEADERS
     GeomAlgoAPI_GlueFaces.h
     GeomAlgoAPI_LimitTolerance.h
     GeomAlgoAPI_Utils.h
+    GeomAlgoAPI_NonPlanarFace.h
 )
 
 SET(PROJECT_SOURCES
@@ -182,6 +183,7 @@ SET(PROJECT_SOURCES
        GeomAlgoAPI_CanonicalRecognition.cpp
     GeomAlgoAPI_LimitTolerance.cpp
     GeomAlgoAPI_Utils.cpp
+    GeomAlgoAPI_NonPlanarFace.cpp
 )
 
 SET(PROJECT_LIBRARIES
index 4eb97f97904a310c1155ee6d24d81666f14a58e0..2dc9bded05e290ce8c16a032e78f6d5fa92b6aa0 100644 (file)
 %shared_ptr(GeomAlgoAPI_Rotation)
 %shared_ptr(GeomAlgoAPI_Sewing)
 %shared_ptr(GeomAlgoAPI_SketchBuilder)
+%shared_ptr(GeomAlgoAPI_NonPlanarFace)
 %shared_ptr(GeomAlgoAPI_ShapeBuilder)
 %shared_ptr(GeomAlgoAPI_Translation)
 %shared_ptr(GeomAlgoAPI_Transform)
 %include "GeomAlgoAPI_WireBuilder.h"
 %include "GeomAlgoAPI_Sewing.h"
 %include "GeomAlgoAPI_ShapeBuilder.h"
+%include "GeomAlgoAPI_NonPlanarFace.h"
 %include "GeomAlgoAPI_Exception.h"
 %include "GeomAlgoAPI_ShapeAPI.h"
 %include "GeomAlgoAPI_Copy.h"
diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_NonPlanarFace.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_NonPlanarFace.cpp
new file mode 100644 (file)
index 0000000..59b07da
--- /dev/null
@@ -0,0 +1,153 @@
+// Copyright (C) 2014-2023  CEA, EDF
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <GeomAlgoAPI_NonPlanarFace.h>
+
+#include <BRep_Builder.hxx>
+#include <BRep_Tool.hxx>
+#include <BRepTools_WireExplorer.hxx>
+#include <BRepBuilderAPI_Copy.hxx>
+#include <BRepOffsetAPI_MakeFilling.hxx>
+#include <BRepBuilderAPI_MakeFace.hxx>
+#include <BOPTools_AlgoTools.hxx>
+
+#include <GeomAPI_Shape.h>
+#include <Geom_Surface.hxx>
+
+#include <ShapeFix_Face.hxx>
+#include <ShapeAnalysis_FreeBounds.hxx>
+#include <ShapeFix_ShapeTolerance.hxx>
+#include <TopTools_HSequenceOfShape.hxx>
+#include <TColStd_IndexedDataMapOfTransientTransient.hxx>
+#include <TopoDS_Edge.hxx>
+#include <TopoDS_Wire.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopoDS.hxx>
+
+//==================================================================================================
+GeomAlgoAPI_NonPlanarFace::GeomAlgoAPI_NonPlanarFace(
+    const ListOfShape& theEdges)
+{
+    build(theEdges);
+}
+
+//==================================================================================================
+void GeomAlgoAPI_NonPlanarFace::build
+    (const ListOfShape& theEdges)
+{
+    myResultFaces.clear();
+    setDone(false);
+    if (theEdges.empty())
+    {
+        return;
+    }
+
+    //prepare edges
+    Handle(TopTools_HSequenceOfShape) aSeqEdgesIn = new TopTools_HSequenceOfShape;
+    TColStd_IndexedDataMapOfTransientTransient aMapTShapes;
+    TopTools_MapOfShape aMapEdges;
+    for (auto anEdge: theEdges) 
+    {
+        const TopoDS_Edge& aTEdge = anEdge->impl<TopoDS_Edge>();
+        if (aMapEdges.Add(aTEdge))
+        {
+            BRepBuilderAPI_Copy aCopy(aTEdge, Standard_False, Standard_False);
+            const TopoDS_Shape& aCopyEdge = aCopy.Shape();
+            aSeqEdgesIn->Append(aCopyEdge);
+        }
+    }
+
+    // no edges
+    if (aSeqEdgesIn->IsEmpty()) 
+    {
+        return;
+    }
+
+    // Connect edges to wires of maximum length
+    Handle(TopTools_HSequenceOfShape) aSeqWiresOut;
+    ShapeAnalysis_FreeBounds::ConnectEdgesToWires(aSeqEdgesIn, Precision::Confusion(), Standard_False, aSeqWiresOut);
+
+    // prepare compound result
+    BRep_Builder aBuilder;
+    TopoDS_Compound aResult;
+    aBuilder.MakeCompound(aResult);
+
+    // try to construct face for each non-planar wire
+    for(int ind = 1; ind <= aSeqWiresOut->Length(); ind++)
+    {
+        if(!aSeqWiresOut->Value(ind).Closed())
+        {
+            continue;
+        }
+        TopoDS_Wire aWire = TopoDS::Wire(aSeqWiresOut->Value(ind));
+
+        // try to construct filling surface
+        BRepOffsetAPI_MakeFilling aMF;
+        BRepTools_WireExplorer aWExp(aWire);
+        for (; aWExp.More(); aWExp.Next()) 
+        {
+            aMF.Add(TopoDS::Edge(aWExp.Current()), GeomAbs_C0);
+        }
+        aMF.Build();
+        if (!aMF.IsDone()) 
+        {
+            return;
+        }
+        // Result of filling
+        TopoDS_Shape aFillRes = aMF.Shape();
+        Handle(Geom_Surface) aGS = BRep_Tool::Surface(TopoDS::Face(aFillRes));
+        BRepBuilderAPI_MakeFace aMakeFace(aGS, aWire);
+        if (aMakeFace.IsDone()) 
+        {
+            TopoDS_Face aNewFace = aMakeFace.Face();
+            Handle(ShapeFix_Face) aFix = new ShapeFix_Face(aNewFace);
+            aFix->Perform();
+            aFix->FixOrientation();
+            aNewFace = aFix->Face();
+
+            //check and fix pcurves, if necessary
+            TopExp_Explorer aExpE;
+            Standard_Real aT, aTolE, aDMax;
+            ShapeFix_ShapeTolerance sat;
+            aExpE.Init(aNewFace, TopAbs_EDGE);
+            for (; aExpE.More(); aExpE.Next())
+            {
+                const TopoDS_Edge& aE = *(TopoDS_Edge*)&aExpE.Current();
+                if (!BOPTools_AlgoTools::ComputeTolerance(aNewFace, aE, aDMax, aT)) continue;
+                aTolE = BRep_Tool::Tolerance(aE);
+                if (aDMax < aTolE) continue;
+
+                sat.LimitTolerance(aE, aDMax);
+            }
+
+            // store face
+            aBuilder.Add(aResult, aNewFace);
+            std::shared_ptr<GeomAPI_Shape> aResFace(new GeomAPI_Shape);
+            aResFace->setImpl(new TopoDS_Face(aNewFace));
+            myResultFaces.push_back(aResFace);
+        }
+    }
+    
+    // update results
+    GeomShapePtr aResShape(new GeomAPI_Shape);
+    aResShape->setImpl(new TopoDS_Shape(aResult));
+    setShape(aResShape);
+    setDone(true);
+}
diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_NonPlanarFace.h b/src/GeomAlgoAPI/GeomAlgoAPI_NonPlanarFace.h
new file mode 100644 (file)
index 0000000..0ffb5e2
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright (C) 2014-2023  CEA, EDF
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef GeomAlgoAPI_NonPlanarFace_H_
+#define GeomAlgoAPI_NonPlanarFace_H_
+
+#include <GeomAlgoAPI.h>
+#include <GeomAlgoAPI_MakeShape.h>
+
+/** \class GeomAlgoAPI_NonPlanarFace
+ *  \ingroup DataAlgo
+ *  \brief Creates non planar faces
+ */
+class GeomAlgoAPI_NonPlanarFace : public GeomAlgoAPI_MakeShape
+{
+public:
+
+   /// Creates list of faces based on the non-planar wires
+   /// \param theEdges list of input non-planar edges.
+  GEOMALGOAPI_EXPORT
+  GeomAlgoAPI_NonPlanarFace(const ListOfShape& theEdges);
+
+   /// Return list of created faces
+  GEOMALGOAPI_EXPORT const ListOfShape& faces() const
+  { return myResultFaces; }
+
+private:
+  /// \brief Perform operation
+  /// \param theEdges list of selected wires.
+  GEOMALGOAPI_EXPORT void build(const ListOfShape& theEdges);
+
+  std::list<std::shared_ptr<GeomAPI_Shape> > myResultFaces;
+};
+
+#endif
index d9776dea952c9482ece4ae1bcabd3ea8e9e2ce26..7a708573b7112979628a8d68117ef6c6f46da9f2 100644 (file)
@@ -43,6 +43,7 @@
   #include "GeomAlgoAPI_Rotation.h"
   #include "GeomAlgoAPI_ShapeTools.h"
   #include "GeomAlgoAPI_SketchBuilder.h"
+  #include "GeomAlgoAPI_NonPlanarFace.h"
   #include "GeomAlgoAPI_BREPExport.h"
   #include "GeomAlgoAPI_IGESExport.h"
   #include "GeomAlgoAPI_STEPExport.h"