]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
[bos #29470][EDF](2022-T1) Advanced geometry features: cloud point on a face
authorjgv <jgv@opencascade.com>
Fri, 8 Jul 2022 07:59:45 +0000 (10:59 +0300)
committerjfa <jfa@opencascade.com>
Thu, 18 Aug 2022 15:00:28 +0000 (18:00 +0300)
16 files changed:
src/FeaturesAPI/CMakeLists.txt
src/FeaturesAPI/FeaturesAPI.i
src/FeaturesAPI/FeaturesAPI_PointCloudOnFace.cpp [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_PointCloudOnFace.h [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_swig.h
src/FeaturesPlugin/CMakeLists.txt
src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp
src/FeaturesPlugin/FeaturesPlugin_PointCloudOnFace.cpp [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_PointCloudOnFace.h [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPointCloud.py [new file with mode: 0644]
src/FeaturesPlugin/plugin-Features.xml
src/FeaturesPlugin/tests.set
src/GeomAlgoAPI/CMakeLists.txt
src/GeomAlgoAPI/GeomAlgoAPI_PointCloudOnFace.cpp [new file with mode: 0644]
src/GeomAlgoAPI/GeomAlgoAPI_PointCloudOnFace.h [new file with mode: 0644]
src/PythonAPI/model/features/__init__.py

index 2d641033f9cfc898b4ac8fe351e9e86373d8c82d..5ced6f8c9026a309412f4a4d295a4a8ab9f90e80 100644 (file)
@@ -38,6 +38,7 @@ SET(PROJECT_HEADERS
   FeaturesAPI_Partition.h
   FeaturesAPI_Pipe.h
   FeaturesAPI_Placement.h
+  FeaturesAPI_PointCloudOnFace.h
   FeaturesAPI_Recover.h
   FeaturesAPI_RemoveResults.h
   FeaturesAPI_RemoveSubShapes.h
@@ -75,6 +76,7 @@ SET(PROJECT_SOURCES
   FeaturesAPI_Partition.cpp
   FeaturesAPI_Pipe.cpp
   FeaturesAPI_Placement.cpp
+  FeaturesAPI_PointCloudOnFace.cpp
   FeaturesAPI_Recover.cpp
   FeaturesAPI_RemoveResults.cpp
   FeaturesAPI_RemoveSubShapes.cpp
index d2b6dbfce82eedcc949596c188641f34ae35bf0f..d9d5367b828a425f3b17219d37a0e5cb5368aabc 100644 (file)
@@ -77,6 +77,7 @@
 %shared_ptr(FeaturesAPI_Partition)
 %shared_ptr(FeaturesAPI_Pipe)
 %shared_ptr(FeaturesAPI_Placement)
+%shared_ptr(FeaturesAPI_PointCloudOnFace)
 %shared_ptr(FeaturesAPI_Recover)
 %shared_ptr(FeaturesAPI_RemoveSubShapes)
 %shared_ptr(FeaturesAPI_Revolution)
 %include "FeaturesAPI_Partition.h"
 %include "FeaturesAPI_Pipe.h"
 %include "FeaturesAPI_Placement.h"
+%include "FeaturesAPI_PointCloudOnFace.h"
 %include "FeaturesAPI_Recover.h"
 %include "FeaturesAPI_RemoveSubShapes.h"
 %include "FeaturesAPI_Revolution.h"
diff --git a/src/FeaturesAPI/FeaturesAPI_PointCloudOnFace.cpp b/src/FeaturesAPI/FeaturesAPI_PointCloudOnFace.cpp
new file mode 100644 (file)
index 0000000..da7ae7c
--- /dev/null
@@ -0,0 +1,84 @@
+// Copyright (C) 2014-2022  CEA/DEN, EDF R&D
+//
+// 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 "FeaturesAPI_PointCloudOnFace.h"
+
+#include <ModelHighAPI_Dumper.h>
+#include <ModelHighAPI_Selection.h>
+#include <ModelHighAPI_Tools.h>
+
+//=================================================================================================
+FeaturesAPI_PointCloudOnFace::FeaturesAPI_PointCloudOnFace(const std::shared_ptr<ModelAPI_Feature>& theFeature)
+  : ModelHighAPI_Interface(theFeature)
+{
+  initialize();
+}
+
+//=================================================================================================
+FeaturesAPI_PointCloudOnFace::FeaturesAPI_PointCloudOnFace(
+                                    const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                                    const ModelHighAPI_Selection& theFace,
+                                    const ModelHighAPI_Integer& theNumber)
+:ModelHighAPI_Interface(theFeature)
+{
+  if (initialize()) {
+    fillAttribute(theFace, myfaceSelected);
+    setNumberOfPoints(theNumber);
+  }
+}
+
+
+//=================================================================================================
+FeaturesAPI_PointCloudOnFace::~FeaturesAPI_PointCloudOnFace()
+{
+}
+
+//==================================================================================================
+void FeaturesAPI_PointCloudOnFace::setNumberOfPoints(const ModelHighAPI_Integer& theNumber)
+{
+  fillAttribute(theNumber, nbPoints());
+
+  execute();
+}
+
+//=================================================================================================
+void FeaturesAPI_PointCloudOnFace::dump(ModelHighAPI_Dumper& theDumper) const
+{
+  FeaturePtr aBase = feature();
+  const std::string& aDocName = theDumper.name(aBase->document());
+
+  AttributeSelectionPtr anAttrObject =
+    aBase->selection(FeaturesPlugin_PointCloudOnFace::FACE_SELECTED_ID());
+  int aNbPnts = aBase->integer(FeaturesPlugin_PointCloudOnFace::NUMBER_ID())->value();
+
+  theDumper << aBase << " = model.makeVertexInsideFace(" << aDocName
+            << ", " << anAttrObject << ", " << aNbPnts << ")" << std::endl;
+}
+
+//==================================================================================================
+PointCloudPtr makeVertexInsideFace(const std::shared_ptr<ModelAPI_Document>& thePart,
+                                   const ModelHighAPI_Selection& theFace,
+                                   const ModelHighAPI_Integer& theNumber)
+{
+  FeaturePtr aFeature = thePart->addFeature(FeaturesPlugin_PointCloudOnFace::ID());
+  PointCloudPtr aPointCloud;
+  aPointCloud.reset(new FeaturesAPI_PointCloudOnFace(aFeature, theFace, theNumber));
+
+  return aPointCloud;
+}
diff --git a/src/FeaturesAPI/FeaturesAPI_PointCloudOnFace.h b/src/FeaturesAPI/FeaturesAPI_PointCloudOnFace.h
new file mode 100644 (file)
index 0000000..323a751
--- /dev/null
@@ -0,0 +1,83 @@
+// Copyright (C) 2014-2022  CEA/DEN, EDF R&D
+//
+// 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 FeaturesAPI_PointCloudOnFace_H_
+#define FeaturesAPI_PointCloudOnFace_H_
+
+#include "FeaturesAPI.h"
+
+#include <FeaturesPlugin_PointCloudOnFace.h>
+
+#include <ModelHighAPI_Interface.h>
+#include <ModelHighAPI_Macro.h>
+#include <ModelHighAPI_Integer.h>
+
+
+class ModelHighAPI_Selection;
+
+/// \class FeaturesAPI_PointCloudOnFace
+/// \ingroup CPPHighAPI
+/// \brief Interface for PointCloudOnFace feature.
+class FeaturesAPI_PointCloudOnFace: public ModelHighAPI_Interface
+{
+public:
+  /// Constructor without values.
+  FEATURESAPI_EXPORT
+  explicit FeaturesAPI_PointCloudOnFace(const std::shared_ptr<ModelAPI_Feature>& theFeature);
+
+  /// Constructor with values.
+  FEATURESAPI_EXPORT
+  explicit FeaturesAPI_PointCloudOnFace(const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                                       const ModelHighAPI_Selection& theFace,
+                                       const ModelHighAPI_Integer& theNumber);
+
+  /// Destructor.
+  FEATURESAPI_EXPORT
+  virtual ~FeaturesAPI_PointCloudOnFace();
+
+  INTERFACE_2(FeaturesPlugin_PointCloudOnFace::ID(),
+              faceSelected, FeaturesPlugin_PointCloudOnFace::FACE_SELECTED_ID(),
+              ModelAPI_AttributeSelection, /** face */,
+              nbPoints, FeaturesPlugin_PointCloudOnFace::NUMBER_ID(),
+              ModelAPI_AttributeInteger, /** Number of points */)
+
+  /// Modify CreationMethod, nb_points attribute of the feature.
+  FEATURESAPI_EXPORT
+  void setNumberOfPoints(const ModelHighAPI_Integer& theNumber);
+
+  /// Dump wrapped feature
+  FEATURESAPI_EXPORT
+  virtual void dump(ModelHighAPI_Dumper& theDumper) const;
+
+};
+
+/// Pointer on the PointCloudOnFace object.
+typedef std::shared_ptr<FeaturesAPI_PointCloudOnFace> PointCloudPtr;
+
+/// \ingroup CPPHighAPI
+/// \brief Create point cloud on a face feature
+/// \param thePart the part
+/// \param theFace the selected face
+/// \param theNumberOfPoints the selected point
+FEATURESAPI_EXPORT
+PointCloudPtr makeVertexInsideFace(const std::shared_ptr<ModelAPI_Document>& thePart,
+                                   const ModelHighAPI_Selection& theFace,
+                                   const ModelHighAPI_Integer& theNumber);
+
+#endif // FeaturesAPI_PointCloudOnFace_H_
index 066aa098f53a3d4d562b1f052ef9b943f5196ca8..a761ff93a27367c2f95329d8b5f0be88a6d84c95 100644 (file)
@@ -41,6 +41,7 @@
   #include "FeaturesAPI_Partition.h"
   #include "FeaturesAPI_Pipe.h"
   #include "FeaturesAPI_Placement.h"
+  #include "FeaturesAPI_PointCloudOnFace.h"
   #include "FeaturesAPI_Recover.h"
   #include "FeaturesAPI_RemoveSubShapes.h"
   #include "FeaturesAPI_Revolution.h"
index 1d031c8ce0cabdbb9f24d20fd516b71ad9610921..4f2148191992e867617c6114a21e83e577929bb7 100644 (file)
@@ -40,6 +40,7 @@ SET(PROJECT_HEADERS
     FeaturesPlugin_Partition.h
     FeaturesPlugin_Pipe.h
     FeaturesPlugin_Placement.h
+    FeaturesPlugin_PointCloudOnFace.h
     FeaturesPlugin_CompositeBoolean.h
     FeaturesPlugin_CompositeSketch.h
     FeaturesPlugin_ExtrusionBoolean.h
@@ -94,6 +95,7 @@ SET(PROJECT_SOURCES
     FeaturesPlugin_Partition.cpp
     FeaturesPlugin_Pipe.cpp
     FeaturesPlugin_Placement.cpp
+    FeaturesPlugin_PointCloudOnFace.cpp
     FeaturesPlugin_CompositeBoolean.cpp
     FeaturesPlugin_CompositeSketch.cpp
     FeaturesPlugin_ExtrusionBoolean.cpp
index 795bc77b51be6f05123c6d623600672985bc882f..24442767f66da642bec549d0bf1fb07ba176e65d 100644 (file)
@@ -44,6 +44,7 @@
 #include <FeaturesPlugin_Partition.h>
 #include <FeaturesPlugin_Pipe.h>
 #include <FeaturesPlugin_Placement.h>
+#include <FeaturesPlugin_PointCloudOnFace.h>
 #include <FeaturesPlugin_Recover.h>
 #include <FeaturesPlugin_RemoveSubShapes.h>
 #include <FeaturesPlugin_Revolution.h>
@@ -215,6 +216,8 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID)
     return FeaturePtr(new FeaturesPlugin_InspectNormalToFace);
   } else if (theFeatureID == FeaturesPlugin_NormalToFace::ID()) {
     return FeaturePtr(new FeaturesPlugin_NormalToFace);
+  } else if (theFeatureID == FeaturesPlugin_PointCloudOnFace::ID()) {
+    return FeaturePtr(new FeaturesPlugin_PointCloudOnFace);
   }
 
 
diff --git a/src/FeaturesPlugin/FeaturesPlugin_PointCloudOnFace.cpp b/src/FeaturesPlugin/FeaturesPlugin_PointCloudOnFace.cpp
new file mode 100644 (file)
index 0000000..e9a6c64
--- /dev/null
@@ -0,0 +1,84 @@
+// Copyright (C) 2014-2022  CEA/DEN, EDF R&D
+//
+// 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 "FeaturesPlugin_PointCloudOnFace.h"
+
+#include <ModelAPI_AttributeSelection.h>
+#include <ModelAPI_AttributeString.h>
+#include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_Data.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_Validator.h>
+#include <Config_PropManager.h>
+#include <GeomAlgoAPI_PointCloudOnFace.h>
+#include <ModelAPI_ResultConstruction.h>
+
+#include <iomanip>
+#include <sstream>
+
+//=================================================================================================
+FeaturesPlugin_PointCloudOnFace::FeaturesPlugin_PointCloudOnFace()
+{
+}
+
+//=================================================================================================
+void FeaturesPlugin_PointCloudOnFace::initAttributes()
+{
+  // attribute for object selected
+  data()->addAttribute(FACE_SELECTED_ID(), ModelAPI_AttributeSelection::typeId());
+  data()->addAttribute(NUMBER_ID(), ModelAPI_AttributeInteger::typeId());
+}
+
+//=================================================================================================
+void FeaturesPlugin_PointCloudOnFace::attributeChanged(const std::string& /*theID*/)
+{
+}
+
+//=================================================================================================
+void FeaturesPlugin_PointCloudOnFace::execute()
+{
+
+  AttributeSelectionPtr aSelectionFace = selection(FACE_SELECTED_ID());
+
+  GeomShapePtr aShape;
+
+  if (aSelectionFace && aSelectionFace->isInitialized()) {
+    aShape = aSelectionFace->value();
+    if (!aShape && aSelectionFace->context())
+      aShape = aSelectionFace->context()->shape();
+  }
+
+  // Getting number of points
+  int aNbPnts = integer(FeaturesPlugin_PointCloudOnFace::NUMBER_ID())->value();
+
+  if (aShape && aNbPnts >= 2) {
+    std::string aError;
+    std::shared_ptr<GeomAPI_Shape> aPoints(new GeomAPI_Shape);
+    if (!GeomAlgoAPI_PointCloudOnFace::PointCloud(aShape,
+                                                  aNbPnts,
+                                                  aPoints,
+                                                  aError))
+      setError("Error in points calculation :" +  aError);
+
+    ResultConstructionPtr aConstr = document()->createConstruction(data());
+    aConstr->setInfinite(true);
+    aConstr->setShape(aPoints);
+    setResult(aConstr);
+  }
+}
diff --git a/src/FeaturesPlugin/FeaturesPlugin_PointCloudOnFace.h b/src/FeaturesPlugin/FeaturesPlugin_PointCloudOnFace.h
new file mode 100644 (file)
index 0000000..83d1b84
--- /dev/null
@@ -0,0 +1,77 @@
+// Copyright (C) 2014-2022  CEA/DEN, EDF R&D
+//
+// 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 FeaturesPlugin_PointCloudOnFace_H_
+#define FeaturesPlugin_PointCloudOnFace_H_
+
+#include "FeaturesPlugin.h"
+#include <ModelAPI_Feature.h>
+
+#include <GeomAPI_IPresentable.h>
+#include <GeomAPI_IScreenParams.h>
+
+/// \class FeaturesPlugin_PointCloudOnFace
+/// \ingroup Plugins
+/// \brief Persistence feature to construct normal to face
+class FeaturesPlugin_PointCloudOnFace : public ModelAPI_Feature
+{
+public:
+  /// Point Cloud on face kind.
+  inline static const std::string& ID()
+  {
+    static const std::string MY_ID("Point_cloud");
+    return MY_ID;
+  }
+
+  /// Attribute name for face selected.
+  inline static const std::string& FACE_SELECTED_ID()
+  {
+    static const std::string MY_FACE_ID("face");
+    return MY_FACE_ID;
+  }
+
+  /// \return the kind of a feature.
+  virtual const std::string& getKind()
+  {
+    return ID();
+  }
+
+  /// Attribute name of number of points in the point cloud.
+  inline static const std::string& NUMBER_ID()
+  {
+    static const std::string MY_NUMBER_OF_POINTS_ID("number_of_points");
+    return MY_NUMBER_OF_POINTS_ID;
+  }
+
+  /// Performs the algorithm and stores results it in the data structure.
+  FEATURESPLUGIN_EXPORT virtual void execute();
+
+  /// Request for initialization of data model of the feature: adding all attributes
+  FEATURESPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Called on change of any argument-attribute of this object
+  /// \param theID identifier of changed attribute
+  FEATURESPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID);
+
+  /// Use plugin manager for features creation
+  FeaturesPlugin_PointCloudOnFace();
+
+};
+
+#endif
diff --git a/src/FeaturesPlugin/Test/TestPointCloud.py b/src/FeaturesPlugin/Test/TestPointCloud.py
new file mode 100644 (file)
index 0000000..69fb706
--- /dev/null
@@ -0,0 +1,56 @@
+# Copyright (C) 2014-2022  CEA/DEN, EDF R&D
+#
+# 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
+#
+
+"""
+      Unit test of makeVertexInsideFace
+"""
+
+from salome.shaper import model
+from GeomAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+
+### Create Part
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+
+### Create Sphere
+Sphere_1 = model.addSphere(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), 10)
+
+### Get Points Cloud
+PC_1 = model.makeVertexInsideFace(Part_1_doc, model.selection("FACE", "Sphere_1_1/Face_1"), 100)
+
+model.end()
+
+### Check result
+err = PC_1.feature().error()
+if err != "":
+    print(err)
+    # this test should not fail with old OCCT
+    assert("Improper OCCT version" in err)
+else:
+    model.testNbResults(PC_1, 1)
+    model.testNbSubResults(PC_1, [0])
+    model.testNbSubShapes(PC_1, GeomAPI_Shape.SOLID, [0])
+    model.testNbSubShapes(PC_1, GeomAPI_Shape.FACE, [0])
+    model.testNbSubShapes(PC_1, GeomAPI_Shape.EDGE, [0])
+    model.testNbSubShapes(PC_1, GeomAPI_Shape.VERTEX, [100])
+
+    assert(model.checkPythonDump())
index 87e47c378a6753e27f3297a13ee4f79bbe2fb6a8..0292c038ce228d2c71bdcf967359867000744ec9 100644 (file)
                icon="icons/Features/measurement.png" helpfile="measurementFeature.html" abort_confirmation="false">
         <source path="measurement_widget.xml"/>
       </feature>
+      <feature id="Point_cloud" title="Cloud of points"
+               tooltip="Create points laying on the face"
+               icon="" internal="1">
+        <shape_selector id="face" label="Face"
+                        icon="" tooltip="Select objects"
+                        shape_types="face"/>
+        <integervalue id="number_of_points" label="Nb points"
+                      step="1" default="2"
+                      icon="" tooltip="Number of points"/>
+      </feature>
     </group>
     <group id="Face">
       <feature id="NormalMacro" title="Normal to a face" tooltip="Calculate the normal to a face" auto_preview="true"
index 393bc27e372859449568357273c7ea34f4f3fcc0..f239bdc761c6488c15316ed5d9ad2c809ec6fefe 100644 (file)
@@ -522,6 +522,7 @@ SET(TEST_NAMES
                Test20247.py
                Test22847.py
                TestPointCoordinates.py
+               TestPointCloud.py
                TestGeometryCalculation.py
                TestBoundingBox.py
                Test23885.py
index 86ee8fe7ca671ceaa56f8d6231e5615e2efee1ee..fef2d06fc794554f4a3898a4f96a830571f1e029 100644 (file)
@@ -58,6 +58,7 @@ SET(PROJECT_HEADERS
     GeomAlgoAPI_ShapeTools.h
     GeomAlgoAPI_Partition.h
     GeomAlgoAPI_PaveFiller.h
+    GeomAlgoAPI_PointCloudOnFace.h
     GeomAlgoAPI_Intersection.h
     GeomAlgoAPI_Pipe.h
     GeomAlgoAPI_WireBuilder.h
@@ -132,6 +133,7 @@ SET(PROJECT_SOURCES
     GeomAlgoAPI_ShapeTools.cpp
     GeomAlgoAPI_Partition.cpp
     GeomAlgoAPI_PaveFiller.cpp
+    GeomAlgoAPI_PointCloudOnFace.cpp
     GeomAlgoAPI_Intersection.cpp
     GeomAlgoAPI_Pipe.cpp
     GeomAlgoAPI_WireBuilder.cpp
diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_PointCloudOnFace.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_PointCloudOnFace.cpp
new file mode 100644 (file)
index 0000000..2e71f82
--- /dev/null
@@ -0,0 +1,672 @@
+// Copyright (C) 2014-2022  CEA/DEN, EDF R&D
+//
+// 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_PointCloudOnFace.h"
+
+#include <TopoDS_Shape.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopoDS.hxx>
+#include <BRep_Tool.hxx>
+#include <BRep_Builder.hxx>
+#include <BRepTools.hxx>
+#include <BRepTools_WireExplorer.hxx>
+#include <BRepTopAdaptor_FClass2d.hxx>
+#include <BRepLib_MakeVertex.hxx>
+#include <GeomLib.hxx>
+#include <gp_Ax3.hxx>
+#include <Geom_Plane.hxx>
+#include <gp_Pln.hxx>
+#include <TopoDS_Iterator.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopExp.hxx>
+#include <GProp_GProps.hxx>
+#include <BRepGProp.hxx>
+#include <ShapeAnalysis.hxx>
+#include <ShapeAnalysis_Surface.hxx>
+#include <ShapeUpgrade_UnifySameDomain.hxx>
+#include <ShapeUpgrade_ShapeDivideArea.hxx>
+#include <BRepAdaptor_Surface.hxx>
+#include <BRepAdaptor_Curve2d.hxx>
+#include <BRepBndLib.hxx>
+#include <GeomLProp_SLProps.hxx>
+#include <BRepBuilderAPI_MakeEdge.hxx>
+#include <TopTools_DataMapOfShapeReal.hxx>
+
+#include <Standard_Version.hxx>
+// code from KERNEL_SRC/src/Basics/Basics_OCCTVersion.hxx
+#ifdef OCC_VERSION_SERVICEPACK
+#  define OCC_VERSION_LARGE (OCC_VERSION_MAJOR << 24 | OCC_VERSION_MINOR << 16 | OCC_VERSION_MAINTENANCE << 8 | OCC_VERSION_SERVICEPACK)
+#else
+#  ifdef OCC_VERSION_DEVELOPMENT
+#    define OCC_VERSION_LARGE ((OCC_VERSION_MAJOR << 24 | OCC_VERSION_MINOR << 16 | OCC_VERSION_MAINTENANCE << 8)-1)
+#  else
+#    define OCC_VERSION_LARGE (OCC_VERSION_MAJOR << 24 | OCC_VERSION_MINOR << 16 | OCC_VERSION_MAINTENANCE << 8)
+#  endif
+#endif
+
+#include <algorithm>
+
+Standard_Boolean comp(const std::pair<TopoDS_Shape, Standard_Real>& theA,
+                      const std::pair<TopoDS_Shape, Standard_Real>& theB)
+{
+  return (theA.second < theB.second);
+}
+
+Standard_Boolean IsUiso (const TopoDS_Edge& theEdge,
+                         const TopoDS_Face& theFace)
+{
+  BRepAdaptor_Curve2d aBAcurve2d (theEdge, theFace);
+  gp_Pnt2d aP2d;
+  gp_Vec2d aVec;
+  aBAcurve2d.D1 (aBAcurve2d.FirstParameter(), aP2d, aVec);
+  return (Abs(aVec.Y()) > Abs(aVec.X()));
+}
+
+void CorrectShell (const TopoDS_Shape& theShell,
+                   const TopoDS_Face&  theFace)
+{
+  BRepAdaptor_Surface aBAsurf (theFace, Standard_False);
+  GeomAbs_SurfaceType aType = aBAsurf.GetType();
+  if (aType <= GeomAbs_Torus) //elementary surfaces
+    return;
+
+  TopLoc_Location anInputLoc;
+  const Handle(Geom_Surface)& anInputSurf = BRep_Tool::Surface (theFace, anInputLoc);
+
+  BRep_Builder aBB;
+
+  TopoDS_Iterator anIter (theShell);
+  for (; anIter.More(); anIter.Next())
+  {
+    const TopoDS_Face& aFace = TopoDS::Face (anIter.Value());
+    TopLoc_Location aLoc;
+    const Handle(Geom_Surface)& aSurf = BRep_Tool::Surface (aFace, aLoc);
+    if (aSurf == anInputSurf)
+      continue;
+
+    TopExp_Explorer anExplo (aFace, TopAbs_EDGE);
+    for (; anExplo.More(); anExplo.Next())
+    {
+      const TopoDS_Edge& anEdge = TopoDS::Edge (anExplo.Current());
+      Standard_Real aFirst, aLast;
+      Handle(Geom2d_Curve) aPCurve = BRep_Tool::CurveOnSurface (anEdge, aFace, aFirst, aLast);
+      aBB.UpdateEdge (anEdge, aPCurve, anInputSurf, anInputLoc, 0.);
+    }
+    Standard_Real aTol = BRep_Tool::Tolerance (aFace);
+    aBB.UpdateFace (aFace, anInputSurf, anInputLoc, aTol);
+  }
+}
+
+gp_Pnt GetMidPnt2d(const TopoDS_Face&     theFace,
+                   const Standard_Boolean theIsNaturalRestrictions)
+{
+  gp_Pnt aResPnt;
+
+  if (theIsNaturalRestrictions)
+  {
+    BRepAdaptor_Surface aBAsurf (theFace);
+    Standard_Real aUmin, aUmax, aVmin, aVmax;
+    aUmin = aBAsurf.FirstUParameter();
+    aUmax = aBAsurf.LastUParameter();
+    aVmin = aBAsurf.FirstVParameter();
+    aVmax = aBAsurf.LastVParameter();
+    aResPnt = aBAsurf.Value ((aUmin + aUmax)/2, (aVmin + aVmax)/2);
+  }
+  else
+  {
+    const Standard_Integer aNbSamples = 4;
+    TopoDS_Wire aWire = BRepTools::OuterWire (theFace);
+    TopTools_IndexedMapOfShape aEmap;
+    TopExp::MapShapes (aWire, TopAbs_EDGE, aEmap);
+    Standard_Integer aNbPointsOnContour = aNbSamples * aEmap.Extent();
+    TColgp_Array1OfPnt anArray (1, aNbPointsOnContour);
+
+    BRepTools_WireExplorer aWexp (aWire, theFace);
+    Standard_Integer anInd = 0;
+    for (; aWexp.More(); aWexp.Next())
+    {
+      const TopoDS_Edge& anEdge = aWexp.Current();
+      BRepAdaptor_Curve2d aBAcurve2d (anEdge, theFace);
+      Standard_Real aDelta = (aBAcurve2d.LastParameter() - aBAcurve2d.FirstParameter())/aNbSamples;
+      for (Standard_Integer ii = 0; ii < aNbSamples; ii++)
+      {
+        Standard_Real aParam = aBAcurve2d.FirstParameter() + ii * aDelta;
+        gp_Pnt2d aP2d = aBAcurve2d.Value (aParam);
+        gp_Pnt aPnt (aP2d.X(), aP2d.Y(), 0.);
+        anArray (++anInd) = aPnt;
+      }
+    }
+
+    gp_Ax2 anAxis;
+    Standard_Boolean anIsSingular;
+    GeomLib::AxeOfInertia (anArray, anAxis, anIsSingular);
+    gp_Pnt aBaryCentre = anAxis.Location();
+    gp_Pnt2d aCentre2d (aBaryCentre.X(), aBaryCentre.Y());
+    BRepTopAdaptor_FClass2d aClassifier (theFace, Precision::Confusion());
+    BRepAdaptor_Surface aBAsurf (theFace, Standard_False);
+
+    TopAbs_State aStatus = aClassifier.Perform (aCentre2d);
+    gp_Pnt2d aP2d = aCentre2d;
+    Standard_Integer anIndVertex = 0;
+    const Standard_Integer aNbIter = 10;
+    while (aStatus != TopAbs_IN && anIndVertex < aNbPointsOnContour)
+    {
+      gp_Pnt aVertexPnt = anArray (anIndVertex+1);
+      gp_Pnt2d aVertexP2d (aVertexPnt.X(), aVertexPnt.Y());
+      TColgp_SequenceOfPnt2d aPseq;
+      aPseq.Append (aCentre2d);
+      aPseq.Append (aVertexP2d);
+      for (Standard_Integer ii = 1; ii <= aNbIter; ii++)
+      {
+        for (Standard_Integer jj = 1; jj < aPseq.Length(); jj++)
+        {
+          aP2d.SetXY ((aPseq(jj).XY() + aPseq(jj+1).XY())/2);
+          aStatus = aClassifier.Perform (aP2d);
+          if (aStatus == TopAbs_IN)
+            break;
+          else
+          {
+            aPseq.InsertAfter (jj, aP2d);
+            jj++;
+          }
+        }
+        if (aStatus == TopAbs_IN)
+          break;
+      }
+      anIndVertex += aNbSamples;
+    }
+    aResPnt = aBAsurf.Value (aP2d.X(), aP2d.Y());
+  } //case of complex boundaries
+
+  return aResPnt;
+}
+
+void ModifyFacesForGlobalResult(const TopoDS_Face&     theInputFace,
+                                const Standard_Real    theAverageArea,
+                                const Standard_Boolean theIsToAddFaces,
+                                Standard_Integer&      theNbExtremalFaces,
+                                TopTools_MapOfShape&   theExtremalFaces,
+                                const std::vector<std::pair<TopoDS_Shape, Standard_Real>> theFacesAndAreas,
+                                const TopTools_DataMapOfShapeReal& theFaceAreaMap,
+                                const TopTools_IndexedDataMapOfShapeListOfShape& theEFmap,
+                                TopoDS_Shape&          theRes,
+                                TopoDS_Shape&          theGlobalRes,
+                                TopTools_MapOfShape&   theRemovedFaces)
+{
+  BRep_Builder aBB;
+  const Standard_Integer aNbFaces = (Standard_Integer) theFacesAndAreas.size();
+
+  const Standard_Integer aDiff = theNbExtremalFaces - theRemovedFaces.Extent();
+
+  Standard_Integer aSum = 0;
+  while (aSum < aDiff) //global loop
+  {
+    Standard_Integer aNbFacesDone = 0, aNbFacesInTape = 0;
+    TopoDS_Face aStartFace;
+
+    Standard_Integer aStartIndex = (theIsToAddFaces)? aNbFaces-1 : 0;
+    Standard_Integer anEndIndex  = (theIsToAddFaces)? 0 : aNbFaces-1;
+    Standard_Integer aStep = (theIsToAddFaces)? -1 : 1;
+
+    for (Standard_Integer ii = aStartIndex; ii != anEndIndex; ii += aStep)
+    {
+      const TopoDS_Face& aFace = TopoDS::Face (theFacesAndAreas[ii].first);
+      if (!theRemovedFaces.Contains(aFace))
+      {
+        aStartFace = aFace;
+        break;
+      }
+    }
+    if (aStartFace.IsNull())
+      break;
+
+    theRemovedFaces.Add (aStartFace);
+
+    TopoDS_Edge aCommonEdge;
+    TopoDS_Face aNextFace;
+    Standard_Real anExtremalArea = (theIsToAddFaces)? 0. : Precision::Infinite();
+    for (TopExp_Explorer anExplo(aStartFace, TopAbs_EDGE); anExplo.More(); anExplo.Next())
+    {
+      const TopoDS_Edge& anEdge = TopoDS::Edge (anExplo.Current());
+      const TopTools_ListOfShape& aFaceList = theEFmap.FindFromKey (anEdge);
+      TopTools_ListIteratorOfListOfShape anItl (aFaceList);
+      for (; anItl.More(); anItl.Next())
+      {
+        const TopoDS_Face& aFace = TopoDS::Face (anItl.Value());
+        if (aFace.IsSame (aStartFace) ||
+            theRemovedFaces.Contains(aFace))
+          continue;
+        Standard_Real anArea = theFaceAreaMap(aFace);
+        Standard_Boolean anIsToExchange = (theIsToAddFaces)? (anArea > anExtremalArea) : (anArea < anExtremalArea);
+        if (anIsToExchange)
+        {
+          anExtremalArea = anArea;
+          aCommonEdge = anEdge;
+          aNextFace = aFace;
+        }
+      }
+    }
+    if (aCommonEdge.IsNull()) //all adjacent faces are already removed
+    {
+      theExtremalFaces.Add (theFacesAndAreas[theNbExtremalFaces].first);
+      theNbExtremalFaces++;
+      continue;
+    }
+
+    //Start filling the shell
+    aBB.Remove (theRes, aStartFace);
+    aNbFacesDone++;
+    TopoDS_Shell aShell;
+    aBB.MakeShell (aShell);
+    Standard_Real anAreaOfTape = 0.;
+    aBB.Add (aShell, aStartFace);
+    aNbFacesInTape++;
+    anAreaOfTape += theFaceAreaMap (aStartFace);
+
+    Standard_Boolean anIsUiso = IsUiso (aCommonEdge, aStartFace);
+    //Find another faces on this level
+    TopoDS_Face aCurrentFace = aNextFace;
+    TopoDS_Edge aCurrentEdge = aCommonEdge;
+    Standard_Boolean anIsFirstDirection = Standard_True;
+    aBB.Remove (theRes, aCurrentFace);
+    theRemovedFaces.Add (aCurrentFace);
+    if (theExtremalFaces.Contains (aCurrentFace))
+    {
+      aNbFacesDone++;
+    }
+    aBB.Add (aShell, aCurrentFace);
+    aNbFacesInTape++;
+    anAreaOfTape += theFaceAreaMap (aCurrentFace);
+    Standard_Boolean anIsRound = Standard_False;
+    for (;;) //local loop
+    {
+      TopoDS_Edge aNextEdge;
+      for (TopExp_Explorer anExplo(aCurrentFace, TopAbs_EDGE); anExplo.More(); anExplo.Next())
+      {
+        const TopoDS_Edge& anEdge = TopoDS::Edge (anExplo.Current());
+        if (anEdge.IsSame (aCurrentEdge))
+          continue;
+        const TopTools_ListOfShape& aFaceList = theEFmap.FindFromKey (anEdge);
+        TopTools_ListIteratorOfListOfShape anItl (aFaceList);
+        for (; anItl.More(); anItl.Next())
+        {
+          const TopoDS_Face& aFace = TopoDS::Face (anItl.Value());
+          if (aFace.IsSame (aCurrentFace))
+            continue;
+          if (aFace.IsSame (aStartFace))
+          {
+            anIsRound = Standard_True;
+            break;
+          }
+          if (theRemovedFaces.Contains(aFace))
+            continue;
+          if (anIsUiso == IsUiso (anEdge, aFace))
+          {
+            aNextEdge = anEdge;
+            aNextFace = aFace;
+            break;
+          }
+        }
+        if (anIsRound || !aNextEdge.IsNull())
+          break;
+      }
+      if (anIsRound) //round tape: returned to start face
+        break;
+      if (aNextEdge.IsNull())
+      {
+        if (anIsFirstDirection)
+        {
+          aCurrentFace = aStartFace;
+          aCurrentEdge = aCommonEdge;
+          anIsFirstDirection = Standard_False;
+          continue;
+        }
+        else
+          break;
+      }
+
+      aBB.Add (aShell, aNextFace);
+      aNbFacesInTape++;
+      anAreaOfTape += theFaceAreaMap (aNextFace);
+      aBB.Remove (theRes, aNextFace);
+      theRemovedFaces.Add (aNextFace);
+      if (theExtremalFaces.Contains (aNextFace))
+      {
+        aNbFacesDone++;
+      }
+      aCurrentEdge = aNextEdge;
+      aNextEdge.Nullify();
+      aCurrentFace = aNextFace;
+    } //end of local loop
+
+    //Tape is formed
+    Standard_Integer aNumberToSplit = (theIsToAddFaces)? aNbFacesInTape + aNbFacesDone : aNbFacesInTape - aNbFacesDone;
+    if (!theIsToAddFaces && aNbFacesDone > 1)
+    {
+      Standard_Integer aRealNumberToSplit = (aNumberToSplit > 0)? aNumberToSplit : 1;
+      Standard_Real anAverageAreaInTape = anAreaOfTape / aRealNumberToSplit;
+      if (anAverageAreaInTape > theAverageArea)
+      {
+        Standard_Integer aNewNumberToSplit = RealToInt(round(anAreaOfTape / theAverageArea));
+        if (aNewNumberToSplit < aNbFacesInTape)
+        {
+          Standard_Integer aNumberToIncrease = aNewNumberToSplit - aNumberToSplit;
+          for (Standard_Integer jj = theNbExtremalFaces; jj < theNbExtremalFaces + aNumberToIncrease; jj++)
+            theExtremalFaces.Add (theFacesAndAreas[jj].first);
+          theNbExtremalFaces += aNumberToIncrease;
+          aNumberToSplit = aNewNumberToSplit;
+        }
+      }
+    }
+    if (anIsRound && aNumberToSplit <= 1)
+    {
+      Standard_Integer aNumberToIncrease = 3 - aNumberToSplit;
+      for (Standard_Integer jj = theNbExtremalFaces; jj < theNbExtremalFaces + aNumberToIncrease; jj++)
+        theExtremalFaces.Add (theFacesAndAreas[jj].first);
+      theNbExtremalFaces += aNumberToIncrease;
+      aNumberToSplit = 3;
+    }
+    CorrectShell (aShell, theInputFace);
+    ShapeUpgrade_UnifySameDomain aUnifier;
+    aUnifier.Initialize (aShell, Standard_True, Standard_True);
+    aUnifier.Build();
+    TopoDS_Shape aUnifiedShape = aUnifier.Shape();
+    //Splitting
+    TopoDS_Shape aLocalResult = aUnifiedShape;
+    Standard_Integer aNbFacesInLocalResult = 1;
+    if (aNumberToSplit > 1)
+    {
+#if OCC_VERSION_LARGE < 0x07050304
+      aNbFacesInLocalResult = 1;
+#else
+      ShapeUpgrade_ShapeDivideArea aLocalTool (aUnifiedShape);
+      aLocalTool.SetSplittingByNumber (Standard_True);
+      aLocalTool.MaxArea() = -1;
+      if (anIsUiso)
+        aLocalTool.SetNumbersUVSplits (aNumberToSplit, 1);
+      else
+        aLocalTool.SetNumbersUVSplits (1, aNumberToSplit);
+      aLocalTool.Perform();
+      aLocalResult = aLocalTool.Result();
+      aNbFacesInLocalResult = aNumberToSplit;
+#endif
+    }
+    else
+    {
+      if (aNumberToSplit == 0)
+      {
+        theExtremalFaces.Add (theFacesAndAreas[theNbExtremalFaces].first);
+        theNbExtremalFaces++;
+      }
+    }
+    aBB.Add (theGlobalRes, aLocalResult);
+
+    aSum += Abs(aNbFacesInTape - aNbFacesInLocalResult);
+  } //end of global loop
+
+  //Second global loop
+  TopoDS_Compound aSecondComp;
+  aBB.MakeCompound (aSecondComp);
+  while (aSum < aDiff)
+  {
+    TopoDS_Shape aMaxShell;
+    Standard_Integer aMaxNbFaces = 0;
+    TopoDS_Iterator anIter (theGlobalRes);
+    for (; anIter.More(); anIter.Next())
+    {
+      const TopoDS_Shape& aShell = anIter.Value();
+      TopTools_IndexedMapOfShape aFaceMap;
+      TopExp::MapShapes (aShell, TopAbs_FACE, aFaceMap);
+      if (aFaceMap.Extent() > aMaxNbFaces)
+      {
+        aMaxNbFaces = aFaceMap.Extent();
+        aMaxShell = aShell;
+      }
+    }
+
+    if (aMaxNbFaces == 1)
+      break;
+
+    aBB.Remove (theGlobalRes, aMaxShell);
+    //Find iso
+    Standard_Boolean anIsUiso = Standard_True;
+    TopTools_IndexedDataMapOfShapeListOfShape aLocalEFmap;
+    TopExp::MapShapesAndAncestors (aMaxShell, TopAbs_EDGE, TopAbs_FACE, aLocalEFmap);
+    for (Standard_Integer jj = 1; jj <= aLocalEFmap.Extent(); jj++)
+    {
+      const TopoDS_Edge& anEdge = TopoDS::Edge (aLocalEFmap.FindKey(jj));
+      const TopTools_ListOfShape& aFaceList = aLocalEFmap(jj);
+      if (aFaceList.Extent() == 2)
+      {
+        const TopoDS_Face& aFace = TopoDS::Face (aFaceList.First());
+        anIsUiso = IsUiso (anEdge, aFace);
+        break;
+      }
+    }
+    CorrectShell (aMaxShell, theInputFace);
+    ShapeUpgrade_UnifySameDomain aUnifier;
+    aUnifier.Initialize (aMaxShell, Standard_True, Standard_True);
+    aUnifier.Build();
+    TopoDS_Shape aUnifiedShape = aUnifier.Shape();
+    TopoDS_Shape aLocalResult = aUnifiedShape;
+
+    Standard_Integer aNumberToSplit = (theIsToAddFaces)? aMaxNbFaces + (aDiff-aSum) : aMaxNbFaces - (aDiff-aSum);
+    if (aNumberToSplit > 1)
+    {
+#if OCC_VERSION_LARGE < 0x07050304
+      aNumberToSplit = 1;
+#else
+      ShapeUpgrade_ShapeDivideArea aLocalTool (aUnifiedShape);
+      aLocalTool.SetSplittingByNumber (Standard_True);
+      aLocalTool.MaxArea() = -1;
+      if (anIsUiso)
+        aLocalTool.SetNumbersUVSplits (aNumberToSplit, 1);
+      else
+        aLocalTool.SetNumbersUVSplits (1, aNumberToSplit);
+      aLocalTool.Perform();
+      aLocalResult = aLocalTool.Result();
+#endif
+    }
+    else
+      aNumberToSplit = 1;
+
+    aBB.Add (aSecondComp, aLocalResult);
+
+    if (theIsToAddFaces)
+      break;
+    aSum += aMaxNbFaces - aNumberToSplit;
+  }
+  aBB.Add (theGlobalRes, aSecondComp);
+}
+
+//=================================================================================================
+bool GeomAlgoAPI_PointCloudOnFace::PointCloud(GeomShapePtr theFace,
+                                              const int    theNumberOfPoints,
+                                              GeomShapePtr thePoints,
+                                              std::string& theError)
+{
+#ifdef _DEBUG
+  std::cout << "GeomAlgoAPI_PointCloudOnFace::PointCloud" << std::endl;
+#endif
+
+#if OCC_VERSION_LARGE < 0x07050304
+  theError = "Improper OCCT version: please, use OCCT 7.5.3p4 or newer.";
+  return false;
+#else
+
+  if (!theFace.get()) {
+    theError = "Face for point cloud calculation is null";
+    return false;
+  }
+
+  TopoDS_Shape anInputShape = theFace->impl<TopoDS_Shape>();
+
+  if (anInputShape.ShapeType() != TopAbs_FACE) {
+    theError = "Shape for normale calculation is not a face";
+    return false;
+  }
+
+  TopoDS_Face anInputFace = TopoDS::Face (anInputShape);
+
+  ShapeUpgrade_ShapeDivideArea tool (anInputFace);
+  tool.SetSplittingByNumber (Standard_True);
+  tool.NbParts() = theNumberOfPoints;
+  tool.Perform();
+  TopoDS_Shape res = tool.Result();
+
+  BRep_Builder aBB;
+  TopoDS_Compound aGlobalRes;
+  aBB.MakeCompound (aGlobalRes);
+
+  TopTools_IndexedMapOfShape aFaceMap;
+  TopExp::MapShapes (res, TopAbs_FACE, aFaceMap);
+  Standard_Integer aNbFaces = aFaceMap.Extent();
+
+  TopTools_IndexedDataMapOfShapeListOfShape aEFmap;
+  TopExp::MapShapesAndAncestors (res, TopAbs_EDGE, TopAbs_FACE, aEFmap);
+
+  TopTools_MapOfShape aBiggestFaces, aSmallestFaces;
+  Standard_Boolean aUseTriangulation = Standard_True;
+  Standard_Boolean aSkipShared = Standard_False;
+  if (aNbFaces != theNumberOfPoints)
+  {
+    Standard_Real aTotalArea = 0.;
+    std::vector<std::pair<TopoDS_Shape, Standard_Real>> aFacesAndAreas (aNbFaces);
+    for (Standard_Integer ii = 1; ii <= aNbFaces; ii++)
+    {
+      GProp_GProps aProps;
+      BRepGProp::SurfaceProperties (aFaceMap(ii), aProps, aSkipShared, aUseTriangulation);
+      Standard_Real anArea = aProps.Mass();
+      aTotalArea += anArea;
+      std::pair<TopoDS_Shape, Standard_Real> aFaceWithArea (aFaceMap(ii), anArea);
+      aFacesAndAreas[ii-1] = aFaceWithArea;
+    }
+    std::sort (aFacesAndAreas.begin(), aFacesAndAreas.end(), comp);
+
+    Standard_Real anAverageArea = aTotalArea / theNumberOfPoints;
+
+    TopTools_DataMapOfShapeReal aFaceAreaMap;
+    for (Standard_Integer ii = 0; ii < aNbFaces; ii++)
+      aFaceAreaMap.Bind (aFacesAndAreas[ii].first, aFacesAndAreas[ii].second);
+
+    TopTools_MapOfShape aRemovedFaces;
+
+    if (aNbFaces < theNumberOfPoints)
+    {
+      Standard_Integer aNbMissingFaces = theNumberOfPoints - aNbFaces;
+      for (Standard_Integer ii = aNbFaces-1; ii > aNbFaces - aNbMissingFaces - 1; ii--)
+        aBiggestFaces.Add (aFacesAndAreas[ii].first);
+
+      ModifyFacesForGlobalResult (anInputFace, anAverageArea,
+                                  Standard_True, //to add faces
+                                  aNbMissingFaces, aBiggestFaces,
+                                  aFacesAndAreas, aFaceAreaMap, aEFmap,
+                                  res, aGlobalRes,
+                                  aRemovedFaces);
+    }
+    else //aNbFaces > theNumberOfPoints
+    {
+      Standard_Integer aNbExcessFaces = aNbFaces - theNumberOfPoints;
+      for (Standard_Integer ii = 0; ii < aNbExcessFaces; ii++)
+        aSmallestFaces.Add (aFacesAndAreas[ii].first);
+
+      TopTools_IndexedDataMapOfShapeListOfShape aVFmap;
+      TopExp::MapShapesAndAncestors (res, TopAbs_VERTEX, TopAbs_FACE, aVFmap);
+
+      //Remove smallest faces with free boundaries
+      for (Standard_Integer ii = 0; ii < aNbExcessFaces; ii++)
+      {
+        const TopoDS_Face& aFace = TopoDS::Face (aFacesAndAreas[ii].first);
+        Standard_Boolean anIsFreeBoundFound = Standard_False;
+        TopExp_Explorer anExplo (aFace, TopAbs_EDGE);
+        for (; anExplo.More(); anExplo.Next())
+        {
+          const TopoDS_Edge& anEdge = TopoDS::Edge (anExplo.Current());
+          if (!BRep_Tool::Degenerated (anEdge) &&
+              aEFmap.FindFromKey(anEdge).Extent() < 2)
+          {
+            anIsFreeBoundFound = Standard_True;
+            break;
+          }
+        }
+        if (anIsFreeBoundFound)
+        {
+          Standard_Real aMaxArea = 0.;
+          for (anExplo.Init(aFace, TopAbs_VERTEX); anExplo.More(); anExplo.Next())
+          {
+            const TopoDS_Shape& aVertex = anExplo.Current();
+            const TopTools_ListOfShape& aFaceList = aVFmap.FindFromKey (aVertex);
+            TopTools_ListIteratorOfListOfShape anItl (aFaceList);
+            for (; anItl.More(); anItl.Next())
+            {
+              Standard_Real anArea = aFaceAreaMap (anItl.Value());
+              if (anArea > aMaxArea)
+                aMaxArea = anArea;
+            }
+          }
+          Standard_Real anArreaOfSmallestFace = aFaceAreaMap (aFace);
+          if (anArreaOfSmallestFace < aMaxArea / 16)
+          {
+            aBB.Remove (res, aFace);
+            aRemovedFaces.Add (aFace);
+          }
+        }
+      }
+
+      ModifyFacesForGlobalResult (anInputFace, anAverageArea,
+                                  Standard_False, //to decrease number of faces
+                                  aNbExcessFaces, aSmallestFaces,
+                                  aFacesAndAreas, aFaceAreaMap, aEFmap,
+                                  res, aGlobalRes,
+                                  aRemovedFaces);
+    }
+  }
+
+  aBB.Add (aGlobalRes, res);
+
+  TopoDS_Compound aCompound;
+  aBB.MakeCompound (aCompound);
+  for (TopExp_Explorer aGlobalExplo(aGlobalRes, TopAbs_FACE); aGlobalExplo.More(); aGlobalExplo.Next())
+  {
+    const TopoDS_Face& aFace = TopoDS::Face (aGlobalExplo.Current());
+    Standard_Boolean anIsNaturalRestrictions = Standard_True;
+    TopExp_Explorer anExplo (aFace, TopAbs_EDGE);
+    for (; anExplo.More(); anExplo.Next())
+    {
+      const TopoDS_Edge& anEdge = TopoDS::Edge (anExplo.Current());
+      if (BRep_Tool::Degenerated (anEdge))
+        continue;
+      if (!aEFmap.Contains(anEdge) ||
+          aEFmap.FindFromKey(anEdge).Extent() < 2)
+      {
+        anIsNaturalRestrictions = Standard_False;
+        break;
+      }
+    }
+
+    gp_Pnt aPnt = GetMidPnt2d (aFace, anIsNaturalRestrictions);
+    TopoDS_Vertex aVertex = BRepLib_MakeVertex (aPnt);
+    aBB.Add (aCompound, aVertex);
+  }
+
+  thePoints->setImpl(new TopoDS_Shape(aCompound));
+
+  return true;
+#endif
+}
diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_PointCloudOnFace.h b/src/GeomAlgoAPI/GeomAlgoAPI_PointCloudOnFace.h
new file mode 100644 (file)
index 0000000..df63db5
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright (C) 2014-2022  CEA/DEN, EDF R&D
+//
+// 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_PointCloudOnFace_H_
+#define GeomAlgoAPI_PointCloudOnFace_H_
+
+#include <GeomAlgoAPI.h>
+#include <GeomAPI_Shape.h>
+
+/**\class GeomAlgoAPI_PointCloudOnFace
+ * \ingroup DataAlgo
+ * \brief Allows to create a cloud of points on face
+ */
+
+class GeomAlgoAPI_PointCloudOnFace
+{
+ public:
+  /// Get the set of points on face
+  /// \param theFace   the face
+  /// \param thePoints the compound of vertices
+  /// \param theError the error
+  GEOMALGOAPI_EXPORT  static bool PointCloud(GeomShapePtr theFace,
+                                            const int    theNumberOfPoints,
+                                            GeomShapePtr thePoints,
+                                            std::string& theError);
+};
+
+#endif
index e316358829e21eb9c1d4283a7fe55d7da7a57941..c0faaacf68f1a13834a7cf23583d15200659ea3f 100644 (file)
@@ -32,6 +32,7 @@ from FeaturesAPI import addFusionFaces
 from FeaturesAPI import measureLength, measureDistance, measureRadius, measureAngle
 from FeaturesAPI import getPointCoordinates, getGeometryCalculation, getBoundingBox
 from FeaturesAPI import getNormal
+from FeaturesAPI import makeVertexInsideFace
 from FeaturesAPI import addRemoveResults
 from FeaturesAPI import addCopy, addImportResult
 from FeaturesAPI import addDefeaturing