Salome HOME
[EDF] (2023-T3) Creation of a non planar face from a list of edges and points
[modules/geom.git] / src / GEOMImpl / GEOMImpl_WrappingDriver.cxx
diff --git a/src/GEOMImpl/GEOMImpl_WrappingDriver.cxx b/src/GEOMImpl/GEOMImpl_WrappingDriver.cxx
new file mode 100644 (file)
index 0000000..3d3037f
--- /dev/null
@@ -0,0 +1,452 @@
+// Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// 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 <GEOMImpl_WrappingDriver.hxx>
+
+#include <GEOMAlgo_Splitter.hxx>
+#include <GEOMUtils.hxx>
+
+#include <GEOMImpl_ShapeDriver.hxx>
+#include <GEOMImpl_IWrap.hxx>
+#include <GEOMImpl_Types.hxx>
+#include <GEOMImpl_Block6Explorer.hxx>
+#include <GEOM_Object.hxx>
+
+#include <BRep_Tool.hxx>
+#include <BRepBuilderAPI_MakeFace.hxx>
+#include <BRepPrimAPI_MakeSphere.hxx>
+#if OCC_VERSION_LARGE < 0x07070000
+#include <BRepAdaptor_HSurface.hxx>
+#else
+#include <BRepAdaptor_Surface.hxx>
+#endif
+
+#include <Geom_Curve.hxx>
+#include <Geom_Surface.hxx>
+#include <Geom_Plane.hxx>
+#include <Geom_BSplineSurface.hxx>
+#include <GeomPlate_Surface.hxx>
+#include <GeomPlate_BuildPlateSurface.hxx>
+#include <GeomPlate_PointConstraint.hxx>
+#include <GeomPlate_MakeApprox.hxx>
+#include <GeomPlate_BuildAveragePlane.hxx>
+
+#include <gp_Pnt.hxx>
+#include <gp_Sphere.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Shape.hxx>
+#include <TopoDS_Wire.hxx>
+#include <TopoDS_Vertex.hxx>
+#include <TColgp_HArray1OfPnt.hxx>
+#include <TColStd_HSequenceOfTransient.hxx>
+#include <ShapeFix_Face.hxx>
+
+#include <Standard_NullObject.hxx>
+
+#include <numeric>
+#include <vector>
+
+#include <Eigen/Dense>
+
+//=======================================================================
+// function : GetID
+// purpose  :
+//=======================================================================
+const Standard_GUID &GEOMImpl_WrappingDriver::GetID()
+{
+  static Standard_GUID aWrapDriver("FF1BBB71-729D-4E83-8232-78E74FC5637C");
+  return aWrapDriver;
+}
+
+//=======================================================================
+// function : GEOMImpl_WrappingDriver
+// purpose  :
+//=======================================================================
+GEOMImpl_WrappingDriver::GEOMImpl_WrappingDriver()
+{
+}
+
+//=======================================================================
+// function : createPointsOnEdges
+// purpose  : create points on edges 
+//=======================================================================
+static void createPointsOnEdges(const Handle(TColStd_HSequenceOfTransient) & theEdgesFuncs,
+                                std::vector<gp_Pnt> &thePoints)
+{
+  for (int i = 1; i <= theEdgesFuncs->Length(); i++)
+  {
+    Handle(GEOM_Function) aRefShape = Handle(GEOM_Function)::DownCast(theEdgesFuncs->Value(i));
+    TopoDS_Shape aShape_i = aRefShape->GetValue();
+    if (aShape_i.IsNull())
+    {
+      Standard_NullObject::Raise("Edge for face construction is null");
+    }
+    TopExp_Explorer exp(aShape_i, TopAbs_EDGE);
+    for (; exp.More(); exp.Next())
+    {
+      Standard_Real aFP, aLP, aP;
+      Handle(Geom_Curve) aCurve = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), aFP, aLP);
+      if (!aCurve.IsNull())
+      {
+        for (int aPar = 1; aPar <= 9; aPar++)
+        {
+          aP = aFP + (aLP - aFP) * (aPar * 0.1);
+          gp_Pnt aPnt = aCurve->Value(aP);
+          thePoints.push_back(aPnt);
+        }
+      }
+    }
+  }
+}
+
+//================================================================================
+// function : maxDistanceToFace
+// purpose  : finds max distanse between points and a face
+//================================================================================
+static Standard_Real maxDistanceToFace(const std::vector<gp_Pnt>& thePoints,
+                                       const TopoDS_Face& theFace,
+                                       const Standard_Real theTolerance)
+{
+  Standard_Real U, V;
+  Standard_Real aMaxDist = 0.;
+  for(auto& aPnt : thePoints)
+  {
+    gp_Pnt aProj = GEOMUtils::ProjectPointOnFace(aPnt, theFace,U,V,theTolerance);
+    Standard_Real aDist = aProj.Distance(aPnt);
+    if(aDist > aMaxDist)
+    {
+      aMaxDist = aDist;
+    }
+  }
+  return aMaxDist;
+}
+
+//================================================================================
+// function : divideSphericalShape
+// purpose  : divide spherical shape into two piece 
+//            and choose that part that has the points
+//================================================================================
+static void divideSphericalShape(TopoDS_Shape &theShape,
+                                 const TopoDS_Wire &theWire,
+                                 const std::vector<gp_Pnt> &thePoints,
+                                 const Standard_Real theTolerance)
+{
+    TopExp_Explorer anExp(theShape, TopAbs_FACE);
+    const TopoDS_Face& aFace = TopoDS::Face(anExp.Current());
+    TopoDS_Face aToolFace;
+    GEOMImpl_Block6Explorer::MakeFace(theWire, false, aToolFace);
+    if(!aToolFace.IsNull())
+    {
+        //split sphere and choose right part 
+        GEOMAlgo_Splitter PS;
+        PS.AddArgument(aFace);
+        PS.AddTool(aToolFace);
+        PS.SetLimit(TopAbs_FACE);
+        PS.Perform();
+        TopoDS_Shape aResultShape = PS.Shape();
+        if(!aResultShape.IsNull())
+        {
+            anExp.Init(aResultShape, TopAbs_FACE);
+            for (; anExp.More(); anExp.Next()) 
+            {
+                Standard_Real aDist = maxDistanceToFace(thePoints, TopoDS::Face(anExp.Current()), theTolerance);
+                if(aDist < theTolerance)
+                {
+                    theShape = TopoDS::Face(anExp.Current());
+                    break;
+                }
+            }
+        }
+    }
+}
+
+//================================================================================
+// function : makeFaceFromPointsAndWire
+// purpose  : Create face from set of points with the same approach as for 
+//            geompy.makeSmoothingSurface. Cut resulting surface with a wire
+//================================================================================
+static TopoDS_Shape makeFaceFromPointsAndWire(const TopoDS_Wire &theWire,
+                                              const std::vector<gp_Pnt> &theAllPoints)
+{
+  int nbPoints = theAllPoints.size();
+  Handle(TColgp_HArray1OfPnt) anArrayofPnt = new TColgp_HArray1OfPnt(1, nbPoints);
+  for (int i = 0; i < nbPoints; i++) {
+    gp_Pnt aP = theAllPoints[i];
+    anArrayofPnt->SetValue(i+1, aP);
+  }
+
+  // Try to build smoothing surface
+  GeomPlate_BuildAveragePlane gpbap(anArrayofPnt,anArrayofPnt->Length(),Precision::Confusion(),1,1);
+  Handle(Geom_Plane) plane(gpbap.Plane());
+
+  Standard_Real Umin, Umax, Vmin, Vmax;
+  gpbap.MinMaxBox(Umin,Umax,Vmin,Vmax);
+  
+  TopoDS_Face aInitShape;
+  BRepBuilderAPI_MakeFace mf(plane,Umin,Umax,Vmin,Vmax,Precision::Confusion());
+  aInitShape =  mf.Face();
+
+  GeomPlate_BuildPlateSurface aBuilder(3,10);
+  // ** Initialization of surface
+#if OCC_VERSION_LARGE < 0x07070000
+  Handle(BRepAdaptor_HSurface) HSI = new BRepAdaptor_HSurface();
+  HSI->ChangeSurface().Initialize(aInitShape);
+  aBuilder.LoadInitSurface( BRep_Tool::Surface(HSI->ChangeSurface().Face()));
+#else
+  Handle(BRepAdaptor_Surface) HSI = new BRepAdaptor_Surface();
+  HSI->Initialize(aInitShape);
+  aBuilder.LoadInitSurface( BRep_Tool::Surface(HSI->Face()) );
+#endif
+
+  Standard_Integer j, j1, j2;
+  j1 = anArrayofPnt->Lower();
+  j2 = anArrayofPnt->Upper();
+  for (j=j1; j<=j2 ; j++)
+  {
+    gp_Pnt aPnt = anArrayofPnt->Value(j); 
+    Handle(GeomPlate_PointConstraint) PCont = new GeomPlate_PointConstraint(aPnt,0);
+    aBuilder.Add(PCont);
+  }
+  aBuilder.Perform();
+  Handle(GeomPlate_Surface) gpPlate = aBuilder.Surface();
+  if(gpPlate.IsNull())
+  {
+    Standard_ConstructionError::Raise("Not possible to build a face with given input, GeomPlate_Surface failed");
+  }
+  
+  Standard_Real seuil = Max(0.0001,10*aBuilder.G0Error());
+  GeomPlate_MakeApprox Mapp(gpPlate,0.0001,2,8,seuil);
+  Handle(Geom_Surface) aSurf(Mapp.Surface());
+  
+  //cut surface with a face
+  TopoDS_Shape aShape;
+  BRepBuilderAPI_MakeFace aMkFace(aSurf, theWire);
+  if (aMkFace.IsDone())
+  {
+    aShape = aMkFace.Shape();
+  }
+
+  TopoDS_Face aFace = TopoDS::Face(aShape);
+  Handle(ShapeFix_Face) aFix = new ShapeFix_Face(aFace);
+  aFix->Perform();
+  aFix->FixOrientation();
+  aFace = aFix->Face();
+
+  return aFace;
+}
+
+//=======================================================================
+// function : Execute
+// purpose  :
+//=======================================================================
+Standard_Integer GEOMImpl_WrappingDriver::Execute(Handle(TFunction_Logbook) & log) const
+{
+  if (Label().IsNull())
+    return 0;
+  Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(Label());
+
+  GEOMImpl_IWrap aCI(aFunction);
+  Standard_Integer aType = aFunction->GetType();
+
+  TopoDS_Shape aShape;
+
+  if (aType == WRAPPING_FACE)
+  {
+    Handle(TColStd_HSequenceOfTransient) anEdges = aCI.GetEdges();
+    Handle(TColStd_HSequenceOfTransient) aVertices = aCI.GetVertices();
+
+    int nbEdge = anEdges->Length();
+    int nbVertices = aVertices->Length();
+
+    if (nbEdge < 1 || nbVertices < 1)
+    {
+      Standard_ConstructionError::Raise("No edges or vertices given");
+    }
+
+    Standard_Real aTolerance = aCI.GetTolerance();
+    if (aTolerance < Precision::Confusion())
+      aTolerance = Precision::Confusion();
+
+    std::vector<gp_Pnt> anAllPoints, aPoints;
+    createPointsOnEdges(anEdges, anAllPoints);
+    for (int i = 1; i <= aVertices->Length(); i++)
+    {
+      Handle(GEOM_Function) aRefShape = Handle(GEOM_Function)::DownCast(aVertices->Value(i));
+      TopoDS_Shape aShape_i = aRefShape->GetValue();
+      if (aShape_i.IsNull())
+      {
+        Standard_NullObject::Raise("Vertex for face construction is null");
+      }
+      TopExp_Explorer exp (aShape_i, TopAbs_VERTEX);
+      for (; exp.More(); exp.Next())
+      {
+        gp_Pnt aP = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current()));
+        anAllPoints.push_back(aP);
+        aPoints.push_back(aP);
+      }
+    }
+
+    if(anAllPoints.size() == 0)
+    {
+      Standard_NullObject::Raise("Shape creation imposible, no valid objects were given");
+    }
+
+    //create wire from edges
+    TopoDS_Wire aWire = GEOMImpl_ShapeDriver::MakeWireFromEdges(anEdges, aTolerance);
+    if(aWire.IsNull())
+    {
+      Standard_NullObject::Raise("Given edges does not create closed contour");
+    }
+
+    aShape = createWrappedFace(aWire, anAllPoints, aPoints, aTolerance);
+  }
+
+  if (aShape.IsNull())
+    return 0;
+
+  aFunction->SetValue(aShape);
+
+  log->SetTouched(Label());
+
+  return 1;
+}
+
+//================================================================================
+/*!
+ * \brief Create wrapped face from vector of points and wire
+ */
+//================================================================================
+TopoDS_Shape GEOMImpl_WrappingDriver::createWrappedFace(const TopoDS_Wire &theWire,
+                                                        const std::vector<gp_Pnt> &theAllPoints,
+                                                        const std::vector<gp_Pnt> &thePoints,
+                                                        const Standard_Real theTolerance) const
+{
+  TopoDS_Shape aShape;
+
+  gp_Pnt aCenter(0,0,0);
+  Standard_Real aRadius = 100;
+  if(isSphere(theAllPoints, aCenter, aRadius, theTolerance))
+  {
+    aShape = BRepPrimAPI_MakeSphere(aCenter, aRadius).Shape();
+    divideSphericalShape(aShape, theWire, thePoints, theTolerance);
+    return aShape;
+  }
+
+  aShape = makeFaceFromPointsAndWire(theWire, theAllPoints);
+  return aShape;
+}
+
+//================================================================================
+/*!
+ * \brief Returns a name of creation operation and names and values of creation parameters
+ */
+//================================================================================
+Standard_Boolean GEOMImpl_WrappingDriver::isSphere(const std::vector<gp_Pnt>& thePoints,
+                                 gp_Pnt& theCenter, 
+                                 Standard_Real& theRadius,
+                                 const Standard_Real theTolerance)const
+{
+  int aNumPoints = thePoints.size();
+  if(aNumPoints == 0)
+  {
+    return false;
+  }
+
+  // Create coefficient matrix A and right-hand side vector f
+  Eigen::MatrixXd A(aNumPoints, 4);
+  Eigen::VectorXd f(aNumPoints);
+
+  Standard_Real X(.0);
+  Standard_Real Y(.0);
+  Standard_Real Z(.0);
+
+  for (int i = 0; i < aNumPoints; ++i) 
+  {
+    X = thePoints[i].X();
+    Y = thePoints[i].Y();
+    Z = thePoints[i].Z();
+    A(i, 0) = X * 2;
+    A(i, 1) = Y * 2;
+    A(i, 2) = Z * 2;
+    A(i, 3) = 1.0;
+    f(i) = X * X + Y * Y + Z * Z;
+  }
+
+  // Solve linear equations to get coefficients
+  Eigen::VectorXd c = A.colPivHouseholderQr().solve(f);
+
+  Standard_Real t = c[0] * c[0] + c[1] * c[1] + c[2] * c[2] + c[3];
+  theRadius = std::sqrt(t);
+  theCenter.SetCoord(c[0], c[1], c[2]);
+
+  //check that all points belong to the sphere within theTolerance
+  std::vector<Standard_Real> aDists;
+  for(const auto& aPnt : thePoints)
+  {
+    aDists.push_back(aPnt.Distance(theCenter));
+  }
+  if (!aDists.empty()) 
+  {
+    Standard_Real sumDistances = std::accumulate(aDists.begin(), aDists.end(), 0.0);
+    Standard_Real averageDistance = sumDistances / aDists.size();
+
+    if (std::fabs(averageDistance - theRadius) > theTolerance) {
+        return false;
+    }
+  }
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Returns a name of creation operation and names and values of creation parameters
+ */
+//================================================================================
+
+bool GEOMImpl_WrappingDriver::
+    GetCreationInformation(std::string &theOperationName,
+                           std::vector<GEOM_Param> &theParams)
+{
+  if (Label().IsNull()) return 0;
+  Handle(GEOM_Function) function = GEOM_Function::GetFunction(Label());
+
+  GEOMImpl_IWrap aCI( function );
+  Standard_Integer aType = function->GetType();
+
+  theOperationName = "WRAPPEDFACE";
+
+  switch ( aType ) {
+  case WRAPPING_FACE:
+    AddParam(theParams, "Edges", aCI.GetEdges());
+    AddParam(theParams, "Vertices", aCI.GetVertices());
+    AddParam(theParams, "Tolerance", aCI.GetTolerance());
+    break;
+  default:
+    return false;
+  }
+  
+  return true;
+}
+
+IMPLEMENT_STANDARD_RTTIEXT(GEOMImpl_WrappingDriver, GEOM_BaseDriver)
\ No newline at end of file