1 // Copyright (C) 2014-2024 CEA, EDF
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "GeomAlgoAPI_CurveBuilder.h"
22 #include <GeomAPI_Edge.h>
23 #include <GeomAPI_Pnt.h>
24 #include <GeomAPI_Vertex.h>
25 #include <GeomAPI_ShapeExplorer.h>
27 #include <Approx_ParametrizationType.hxx>
28 #include <BRepBuilderAPI_MakeEdge.hxx>
29 #include <BSplSLib.hxx>
30 #include <Geom_BSplineCurve.hxx>
31 #include <Geom_BSplineSurface.hxx>
32 #include <GeomAPI_Interpolate.hxx>
33 #include <GeomAPI_PointsToBSplineSurface.hxx>
35 #include <Precision.hxx>
36 #include <TColgp_Array2OfPnt.hxx>
37 #include <TColgp_HArray1OfPnt.hxx>
38 #include <TopExp_Explorer.hxx>
40 #include <TopoDS_Edge.hxx>
43 GeomEdgePtr GeomAlgoAPI_CurveBuilder::edge(const std::list<GeomPointPtr>& thePoints,
44 const bool thePeriodic,
45 const bool theIsToReorder,
46 const GeomDirPtr& theStartTangent,
47 const GeomDirPtr& theEndTangent)
49 std::list<GeomPointPtr> aPointsCopy = thePoints;
51 // If the curve to be closed - remove last point if it is too close to the first one
52 bool isClose = aPointsCopy.front()->distance(aPointsCopy.back()) <= gp::Resolution();
53 if (isClose && thePeriodic) {
54 aPointsCopy.pop_back();
57 // Reorder points if required
59 reorderPoints(aPointsCopy);
62 // Prepare points array
63 Handle(TColgp_HArray1OfPnt) aPoints = new TColgp_HArray1OfPnt(1, (int)aPointsCopy.size());
64 std::list<GeomPointPtr>::const_iterator anIt = aPointsCopy.begin();
65 for (int i = 1; anIt != aPointsCopy.end(); anIt++, i++) {
66 GeomPointPtr aPoint = *anIt;
67 aPoints->SetValue(i, aPoint->impl<gp_Pnt>());
70 // Initialize interpolator
71 GeomAPI_Interpolate anInterp(aPoints, thePeriodic, gp::Resolution());
73 // Assign tangents if defined
74 if (theStartTangent && theEndTangent) {
75 gp_Dir aDir = theStartTangent->impl<gp_Dir>();
76 gp_Vec anInitialTangent(aDir.XYZ());
77 aDir = theEndTangent->impl<gp_Dir>();
78 gp_Vec aFinalTangent(aDir.XYZ());
80 anInterp.Load(anInitialTangent, aFinalTangent);
84 if (aPoints->Length() > 1) {
88 // Set result in form of edge
90 if (anInterp.IsDone()) {
91 anEdge = BRepBuilderAPI_MakeEdge(anInterp.Curve()).Edge();
94 GeomEdgePtr aResultShape(new GeomAPI_Edge);
95 aResultShape->setImpl(new TopoDS_Shape(anEdge));
100 GeomEdgePtr GeomAlgoAPI_CurveBuilder::approximate(const std::list<GeomPointPtr>& thePoints,
101 const bool thePeriodic,
102 const double thePrecision)
104 // Prepare points array to be able to build a surface.
105 // This surface is based on two sets of points: the first is an original and
106 // the second is shifted along orthogonal direction.
107 // This is a workaround, because GeomAPI_PointsToBSpline algorithm cannot produce
108 // the periodic curve, but GeomAPI_PointsToBSplineSurface can.
109 TColgp_Array2OfPnt aPoints(1, (int)thePoints.size(), 1, 2);
110 gp_Pnt aPlaneBase[3]; // base points to calculate the normal direction
111 int aNbPlanePoints = 0;
113 std::list<GeomPointPtr>::const_iterator anIt = thePoints.begin();
114 for (int i = 1; anIt != thePoints.end(); anIt++, i++) {
115 const gp_Pnt& aPoint = (*anIt)->impl<gp_Pnt>();
116 aPoints.SetValue(i, 1, aPoint);
117 aPoints.SetValue(i, 2, aPoint);
118 if (aNbPlanePoints < 3) {
119 if (aNbPlanePoints == 0 ||
120 aPoint.SquareDistance(aPlaneBase[0]) > Precision::SquareConfusion())
121 aPlaneBase[aNbPlanePoints++] = aPoint;
122 if (aNbPlanePoints == 3) {
123 gp_Vec aVec12(aPlaneBase[0], aPlaneBase[1]);
124 gp_Vec aVec13(aPlaneBase[0], aPlaneBase[2]);
125 if (aVec12.CrossSquareMagnitude(aVec13) > Precision::SquareConfusion())
126 aNormal = gp_Dir(aVec12 ^ aVec13);
132 if (aNbPlanePoints < 3)
135 for (int i = aPoints.LowerRow(); i <= aPoints.UpperRow(); i++)
136 aPoints.ChangeValue(i, 2).ChangeCoord() += aNormal.XYZ();
138 // If the curve to be closed - remove last point if it is too close to the first one
139 bool isClose = aPoints.Value(aPoints.LowerRow(), 1).Distance(
140 aPoints.Value(aPoints.UpperRow(), 1)) <= gp::Resolution();
141 if (isClose && thePeriodic) {
142 aPoints.Resize(aPoints.LowerRow(), aPoints.UpperRow() - 1,
143 aPoints.LowerCol(), aPoints.UpperCol(), Standard_True);
146 // Initialize and perform approximator
147 static const Standard_Integer DEGREE_MIN = 3;
148 static const Standard_Integer DEGREE_MAX = 8;
149 GeomAPI_PointsToBSplineSurface anApprox;
150 anApprox.Init(aPoints, Approx_ChordLength, DEGREE_MIN, DEGREE_MAX,
151 GeomAbs_C2, thePrecision, thePeriodic);
153 // Set result in form of edge
155 if (anApprox.IsDone()) {
156 // build a curve along U-direction of the surface
157 Handle(Geom_BSplineSurface) aSurface = anApprox.Surface();
158 Handle(Geom_Curve) aCurve = aSurface->VIso(aSurface->VKnots().First());
160 anEdge = BRepBuilderAPI_MakeEdge(aCurve).Edge();
163 GeomEdgePtr aResultShape(new GeomAPI_Edge);
164 aResultShape->setImpl(new TopoDS_Shape(anEdge));
169 void GeomAlgoAPI_CurveBuilder::reorderPoints(std::list<GeomPointPtr>& thePoints)
171 if (thePoints.size() < 3) {
175 std::list<GeomPointPtr>::iterator aPIt = thePoints.begin();
176 GeomPointPtr aPrevPnt = *aPIt;
177 for (; aPIt != thePoints.end(); ++aPIt) {
178 GeomPointPtr aPnt = *aPIt;
179 std::list<GeomPointPtr>::iterator aNextIt = aPIt;
180 std::list<GeomPointPtr>::iterator aNearestIt = ++aNextIt;
181 double aMinDist = RealLast();
182 while (aNextIt != thePoints.end()) {
183 double aDist = aPnt->distance(*aNextIt);
184 if (aDist < Precision::Confusion()) {
186 std::list<GeomPointPtr>::iterator aRemoveIt = aNextIt++;
187 thePoints.erase(aRemoveIt);
188 // update iterator showing the nearest point, because it may become invalid
189 aNearestIt = aPIt; ++aNearestIt;
192 if (aDist < aMinDist && (aMinDist - aDist) > Precision::Confusion()) {
193 aNearestIt = aNextIt;
198 aNextIt = aPIt; ++aNextIt;
199 if (aNearestIt != aNextIt) {
200 // Keep given order of points to use it in case of equidistant candidates
203 // o o o c o->o->o->o->n o o
206 GeomPointPtr aPointToMove = *aNearestIt;
207 thePoints.erase(aNearestIt);
208 thePoints.insert(aNextIt, aPointToMove);