1 // Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #include "GEOMImpl_SplineDriver.hxx"
25 #include "GEOMImpl_ISpline.hxx"
26 #include "GEOMImpl_Types.hxx"
27 #include "GEOMImpl_ICurveParametric.hxx"
29 #include "GEOM_Function.hxx"
30 #include "GEOMUtils.hxx"
32 #include <BRepBuilderAPI_MakeEdge.hxx>
33 #include <BRepBuilderAPI_MakeVertex.hxx>
34 #include <BRep_Tool.hxx>
39 #include <TopoDS_Shape.hxx>
40 #include <TopoDS_Edge.hxx>
41 #include <TopoDS_Vertex.hxx>
43 #include <Geom_BezierCurve.hxx>
44 #include <GeomAPI_Interpolate.hxx>
48 #include <gp_Circ.hxx>
49 #include <Precision.hxx>
50 #include <TColgp_Array1OfPnt.hxx>
51 #include <TColgp_HArray1OfPnt.hxx>
53 #include <Standard_NullObject.hxx>
55 // Below macro specifies how the closed point set is processed (issue 0022885).
56 // See below for more information.
57 // Currently solution 4 is chosen!
58 #define BSPLINE_PROCESS_CLOSED_PNTSET 2
63 \brief Generate list of points from the list of (x,y,z) coordinates
64 \param coords list of values specifying (x y z) coordinates of points
65 \return list of points
68 Handle(TColgp_HArray1OfPnt) pointsFromCoords(Handle(TColStd_HArray1OfReal) coords)
70 Standard_Integer length = coords->Length() / 3;
72 Handle(TColgp_HArray1OfPnt) points = new TColgp_HArray1OfPnt(1, length);
74 for (int i = 0; i < length; i ++) {
75 Standard_Real x = coords->Value( i*3+1 );
76 Standard_Real y = coords->Value( i*3+2 );
77 Standard_Real z = coords->Value( i*3+3 );
78 points->SetValue(i+1, gp_Pnt(x, y, z));
85 \brief Generate list of points from the sequence of input objects
86 \param coords list of objects as it is stored within the CAF tree
87 \return list of points
90 Handle(TColgp_HArray1OfPnt) pointsFromObjs(Handle(TColStd_HSequenceOfTransient) objects)
92 Standard_Integer length = objects->Length();
94 Handle(TColgp_HArray1OfPnt) points = new TColgp_HArray1OfPnt(1, length);
96 for (int i = 1; i <= length; i ++) {
97 TopoDS_Shape shape = Handle(GEOM_Function)::DownCast(objects->Value(i))->GetValue();
98 if (shape.ShapeType() != TopAbs_VERTEX)
99 // error: only vertices are allowed in the input
100 Standard_ConstructionError::Raise("Input should contain only vertices");
101 points->SetValue(i, BRep_Tool::Pnt(TopoDS::Vertex(shape)));
108 //=======================================================================
111 //=======================================================================
112 const Standard_GUID& GEOMImpl_SplineDriver::GetID()
114 static Standard_GUID aSplineDriver("FF1BBB33-5D14-4df2-980B-3A668264EA16");
115 return aSplineDriver;
119 //=======================================================================
120 //function : GEOMImpl_SplineDriver
122 //=======================================================================
123 GEOMImpl_SplineDriver::GEOMImpl_SplineDriver()
127 //=======================================================================
130 //=======================================================================
131 Standard_Integer GEOMImpl_SplineDriver::Execute(Handle(TFunction_Logbook)& log) const
133 if (Label().IsNull()) return 0;
134 Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(Label());
136 GEOMImpl_ISpline aCI (aFunction);
137 Standard_Integer aType = aFunction->GetType();
141 if (aType == SPLINE_BEZIER ||
142 aType == SPLINE_INTERPOLATION ||
143 aType == SPLINE_INTERPOL_TANGENTS) {
145 bool useCoords = aCI.GetConstructorType() == COORD_CONSTRUCTOR;
147 // collect points from input parameters: objects or coordinates
148 Handle(TColgp_HArray1OfPnt) points = useCoords ? pointsFromCoords(aCI.GetCoordinates()) : pointsFromObjs(aCI.GetPoints());
149 int length = points->Length();
151 if (length < 2) return 0; // error: not enough points in the input list
153 // reorder points if required (bspline only)
154 if ((aType == SPLINE_INTERPOLATION || aType == SPLINE_INTERPOL_TANGENTS) && aCI.GetDoReordering()) {
156 gp_Pnt pPrev = points->Value(1);
157 for (int i = 1; i < length - 1; i++) {
158 gp_Pnt pi = points->Value(i);
160 double minDist = RealLast();
161 for (int j = i+1; j <= length; j++) {
162 double dist = pi.SquareDistance(points->Value(j));
163 if (dist < minDist && (minDist - dist) > Precision::Confusion()) {
168 if (nearest > 0 && nearest != i + 1) {
169 /*====================================================================
170 Keep given order of points to use it in case of equidistant candidates
173 o o o c o->o->o->o->n o o
176 ======================================================================*/
177 gp_Pnt p = points->Value(nearest);
178 for (int j = nearest; j > i+1; j--)
179 points->SetValue(j, points->Value(j-1));
180 points->SetValue(i+1, p);
182 if ( pPrev.Distance(points->Value(i+1)) <= Precision::Confusion() )
185 pPrev = points->Value(i+1);
188 Handle(TColgp_HArray1OfPnt) tmpPoints = new TColgp_HArray1OfPnt(1, length-nbDup);
190 for (int i = 1; i <= length; i++) {
191 if (i == 1 || pPrev.Distance(points->Value(i)) > Precision::Confusion() ) {
192 tmpPoints->SetValue(j++, points->Value(i));
193 pPrev = points->Value(i);
197 length = points->Length();
199 } // end of reordering
201 bool closed = points->Value(1).Distance(points->Value(length)) <= gp::Resolution();
203 if (aType == SPLINE_BEZIER) {
204 // for Bezier curve we should append first point to the list if:
205 // a) "closed" flag is set, and
206 // b) first and last vertices are not too close
207 bool addFirst = aCI.GetIsClosed() && !closed;
209 // re-fill points and check that there's enough points to create a curve
210 bool isValid = false;
211 TColgp_Array1OfPnt curvePoints(1, length + (addFirst ? 1 : 0));
213 for (int i = 1; i <= length; i++) {
214 gp_Pnt p = points->Value(i);
215 if (!isValid && i > 1 && p.Distance(pp) > Precision::Confusion())
217 curvePoints.SetValue(i, p);
222 // error: not enough points to create curve
223 Standard_ConstructionError::Raise("Points for Bezier Curve are too close");
225 // set last point equal to first for the closed Bezier curve
226 if (addFirst) curvePoints.SetValue(length+1, curvePoints.Value(1));
228 // create Bezier curve
229 Handle(Geom_BezierCurve) GBC = new Geom_BezierCurve(curvePoints);
230 aShape = BRepBuilderAPI_MakeEdge(GBC).Edge();
233 // Below described processing of closed points set case
234 // is not done for constrained bsplined
235 bool typeok = aType == SPLINE_INTERPOLATION;
236 #if BSPLINE_PROCESS_CLOSED_PNTSET == 1
237 // Last point is removed from the list if:
238 // a) first and last vertices are equal;
239 // b) "closed" flag is not taken into account.
240 // If first and last points are equal, we force "closed" flag to be set to true.
241 // For the case when first and last vertices are equal, this approach causes
242 // result different that would be if last point had NOT be removed and "closed" flag is false.
243 bool isClosed = typeok && (aCI.GetIsClosed() || closed);
244 bool removeLast = typeok && closed;
245 bool addFirst = false;
246 #elif BSPLINE_PROCESS_CLOSED_PNTSET == 2
247 // Last point is removed from the list if:
248 // a) first and last vertices are equal;
249 // b) "closed" flag is set to true.
250 // Flag "closed" is taken "as is".
251 // For the case when first and last vertices are equal, this approach causes
252 // different results with "closed" flag set to true and false.
253 bool isClosed = typeok && aCI.GetIsClosed();
254 bool removeLast = typeok && aCI.GetIsClosed() && closed;
255 bool addFirst = false;
256 #elif BSPLINE_PROCESS_CLOSED_PNTSET == 3
257 // Points are passed "as is" to the creator.
258 // If first and last points are equal, we force "closed" flag to be set to false.
259 // For the case when first and last vertices are equal, this approach gives
260 // the same results with "closed" flag set to true and false.
261 bool isClosed = typeok && aCI.GetIsClosed() && !closed;
262 bool removeLast = false;
263 bool addFirst = false;
264 #elif BSPLINE_PROCESS_CLOSED_PNTSET == 4
265 // First point is added to the list if:
266 // a) first and last vertices are not equal;
267 // b) "closed" flag is set to true.
268 // In this case "closed" flag is forcidly set to false - bspline creator is
269 // capable to create closed edge in this case.
270 // This approach gives the same results with "closed" flag set to true not
271 // depending on if set of points is closed or no.
272 // Also, it gives equal reqults in both case if set of points is closed or not
273 // and "closed" flag is set to true, in contrast to solution 3 above.
274 bool isClosed = false;
275 bool removeLast = false;
276 bool addFirst = typeok && aCI.GetIsClosed() && !closed;
278 // Points are passed "as is" to the creator.
279 // This causes an error when first point is equal to last one and
280 // "closed" flag is set to true; see bug 0022885.
281 bool isClosed = typeok && aCI.GetIsClosed();
282 bool removeLast = false;
283 bool addFirst = false;
286 // remove last point or append first one if the conditions are observed (see above)
287 if (removeLast || addFirst) {
288 int extra = removeLast ? -1 : (addFirst ? 1 : 0 );
289 int nb = removeLast ? length-1 : length;
290 Handle(TColgp_HArray1OfPnt) curvePoints = new TColgp_HArray1OfPnt (1, length+extra);
291 for (int i = 1; i <= nb; i++)
292 curvePoints->SetValue(i, points->Value(i));
293 if (addFirst) curvePoints->SetValue(length+1, points->Value(1));
294 points = curvePoints;
297 // initial set-up of curve creator
298 GeomAPI_Interpolate GBC(points, isClosed, gp::Resolution());
300 // add tangent vectors constraints
301 if (aType == SPLINE_INTERPOL_TANGENTS) {
302 Handle(GEOM_Function) aVec1Ref = aCI.GetFirstVector();
303 Handle(GEOM_Function) aVec2Ref = aCI.GetLastVector();
305 if (aVec1Ref.IsNull() || aVec2Ref.IsNull())
306 // error: bad vector parameter is specified
307 Standard_NullObject::Raise("Null object is given for a vector");
309 // take orientation of edge into account to avoid regressions, as it was implemented so
310 gp_Vec aV1 = GEOMUtils::GetVector(aVec1Ref->GetValue(), Standard_True);
311 gp_Vec aV2 = GEOMUtils::GetVector(aVec2Ref->GetValue(), Standard_True);
313 // push constraint vectors to the curve creator
314 GBC.Load(aV1, aV2, /*Scale*/Standard_True);
317 // create bspline curve
320 aShape = BRepBuilderAPI_MakeEdge(GBC.Curve()).Edge();
326 if (aShape.IsNull()) return 0; // error: bad result
328 aFunction->SetValue(aShape);
330 log->SetTouched(Label());
335 //================================================================================
337 * \brief Returns a name of creation operation and names and values of creation parameters
339 //================================================================================
341 bool GEOMImpl_SplineDriver::
342 GetCreationInformation(std::string& theOperationName,
343 std::vector<GEOM_Param>& theParams)
345 if (Label().IsNull()) return 0;
346 Handle(GEOM_Function) function = GEOM_Function::GetFunction(Label());
348 GEOMImpl_ISpline aCI( function );
349 GEOMImpl_ICurveParametric aPI( function );
350 Standard_Integer aType = function->GetType();
352 theOperationName = "CURVE";
356 case SPLINE_INTERPOLATION:
357 case SPLINE_INTERPOL_TANGENTS:
359 AddParam( theParams, "Type", ( aType == SPLINE_BEZIER ? "Bezier" : "Interpolation"));
363 AddParam( theParams, "X(t) equation", aPI.GetExprX() );
364 AddParam( theParams, "Y(t) equation", aPI.GetExprY() );
365 AddParam( theParams, "Z(t) equation", aPI.GetExprZ() );
366 AddParam( theParams, "Min t", aPI.GetParamMin() );
367 AddParam( theParams, "Max t", aPI.GetParamMax() );
368 if ( aPI.GetParamNbStep() )
369 AddParam( theParams, "Number of steps", aPI.GetParamNbStep() );
371 AddParam( theParams, "t step", aPI.GetParamStep() );
375 if ( aCI.GetConstructorType() == COORD_CONSTRUCTOR )
377 Handle(TColStd_HArray1OfReal) coords = aCI.GetCoordinates();
378 GEOM_Param& pntParam = AddParam( theParams, "Points");
379 pntParam << ( coords->Length() / 3 ) << " points: ";
380 for ( int i = coords->Lower(), nb = coords->Upper(); i <= nb; i += 3 )
381 pntParam << "( " << coords->Value( i+0 )
382 << ", " << coords->Value( i+1 )
383 << ", " << coords->Value( i+2 ) << " ) ";
387 AddParam( theParams, "Points", aCI.GetPoints() );
389 Handle(GEOM_Function) v1 = aCI.GetFirstVector();
390 Handle(GEOM_Function) v2 = aCI.GetLastVector();
391 if ( !v1.IsNull() ) AddParam( theParams, "First tangent vector", v1 );
392 if ( !v2.IsNull() ) AddParam( theParams, "Last tangent vector", v2 );
402 IMPLEMENT_STANDARD_RTTIEXT (GEOMImpl_SplineDriver,GEOM_BaseDriver)