Salome HOME
Copyright update 2021
[modules/geom.git] / src / GEOMImpl / GEOMImpl_SplineDriver.cxx
1 // Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
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.
10 //
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.
15 //
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
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 #include "GEOMImpl_SplineDriver.hxx"
24
25 #include "GEOMImpl_ISpline.hxx"
26 #include "GEOMImpl_Types.hxx"
27 #include "GEOMImpl_ICurveParametric.hxx"
28
29 #include "GEOM_Function.hxx"
30 #include "GEOMUtils.hxx"
31
32 #include <BRepBuilderAPI_MakeEdge.hxx>
33 #include <BRepBuilderAPI_MakeVertex.hxx>
34 #include <BRep_Tool.hxx>
35
36 #include <TopAbs.hxx>
37 #include <TopExp.hxx>
38 #include <TopoDS.hxx>
39 #include <TopoDS_Shape.hxx>
40 #include <TopoDS_Edge.hxx>
41 #include <TopoDS_Vertex.hxx>
42
43 #include <Geom_BezierCurve.hxx>
44 #include <GeomAPI_Interpolate.hxx>
45
46 #include <gp.hxx>
47 #include <gp_Pnt.hxx>
48 #include <gp_Circ.hxx>
49 #include <Precision.hxx>
50 #include <TColgp_Array1OfPnt.hxx>
51 #include <TColgp_HArray1OfPnt.hxx>
52
53 #include <Standard_NullObject.hxx>
54
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
59
60 namespace
61 {
62   /*!
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
66     \internal
67   */
68   Handle(TColgp_HArray1OfPnt) pointsFromCoords(Handle(TColStd_HArray1OfReal) coords)
69   {
70     Standard_Integer length = coords->Length() / 3;
71
72     Handle(TColgp_HArray1OfPnt) points = new TColgp_HArray1OfPnt(1, length);
73     
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));
79     }
80
81     return points;
82   }
83
84   /*!
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
88     \internal
89   */
90   Handle(TColgp_HArray1OfPnt) pointsFromObjs(Handle(TColStd_HSequenceOfTransient) objects)
91   {
92     Standard_Integer length = objects->Length();
93
94     Handle(TColgp_HArray1OfPnt) points = new TColgp_HArray1OfPnt(1, length);
95     
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)));
102     }
103
104     return points;
105   }
106 }
107
108 //=======================================================================
109 //function : GetID
110 //purpose  :
111 //=======================================================================
112 const Standard_GUID& GEOMImpl_SplineDriver::GetID()
113 {
114   static Standard_GUID aSplineDriver("FF1BBB33-5D14-4df2-980B-3A668264EA16");
115   return aSplineDriver;
116 }
117
118
119 //=======================================================================
120 //function : GEOMImpl_SplineDriver
121 //purpose  :
122 //=======================================================================
123 GEOMImpl_SplineDriver::GEOMImpl_SplineDriver()
124 {
125 }
126
127 //=======================================================================
128 //function : Execute
129 //purpose  :
130 //=======================================================================
131 Standard_Integer GEOMImpl_SplineDriver::Execute(Handle(TFunction_Logbook)& log) const
132 {
133   if (Label().IsNull()) return 0;
134   Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(Label());
135
136   GEOMImpl_ISpline aCI (aFunction);
137   Standard_Integer aType = aFunction->GetType();
138
139   TopoDS_Shape aShape;
140
141   if (aType == SPLINE_BEZIER ||
142       aType == SPLINE_INTERPOLATION ||
143       aType == SPLINE_INTERPOL_TANGENTS) {
144
145     bool useCoords = aCI.GetConstructorType() == COORD_CONSTRUCTOR;
146
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();
150
151     if (length < 2) return 0; // error: not enough points in the input list
152
153     // reorder points if required (bspline only)
154     if ((aType == SPLINE_INTERPOLATION || aType == SPLINE_INTERPOL_TANGENTS) && aCI.GetDoReordering()) {
155       int nbDup = 0;
156       gp_Pnt pPrev = points->Value(1);
157       for (int i = 1; i < length - 1; i++) {
158         gp_Pnt pi = points->Value(i);
159         int nearest = 0;
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()) {
164             nearest = j;
165             minDist = dist;
166           }
167         }
168         if (nearest > 0 && nearest != i + 1) {
169           /*====================================================================
170           Keep given order of points to use it in case of equidistant candidates
171                          .-<---<-.
172                         /         \ 
173            o  o  o  c  o->o->o->o->n  o  o
174                     |  |           |
175                     i i+1       nearest
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);
181         }
182         if ( pPrev.Distance(points->Value(i+1)) <= Precision::Confusion() )
183           nbDup++;
184         else
185           pPrev = points->Value(i+1);
186       }
187       if ( nbDup > 0 ) {
188         Handle(TColgp_HArray1OfPnt) tmpPoints = new TColgp_HArray1OfPnt(1, length-nbDup);
189         int j = 1;
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);
194           }
195         }
196         points = tmpPoints;
197         length = points->Length();
198       }
199     } // end of reordering
200
201     bool closed = points->Value(1).Distance(points->Value(length)) <= gp::Resolution();
202
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;
208       
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));
212       gp_Pnt pp;
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())
216           isValid = true;
217         curvePoints.SetValue(i, p);
218         pp = p;
219       }
220
221       if (!isValid)
222         // error: not enough points to create curve
223         Standard_ConstructionError::Raise("Points for Bezier Curve are too close");
224       
225       // set last point equal to first for the closed Bezier curve
226       if (addFirst) curvePoints.SetValue(length+1, curvePoints.Value(1));
227       
228       // create Bezier curve
229       Handle(Geom_BezierCurve) GBC = new Geom_BezierCurve(curvePoints);
230       aShape = BRepBuilderAPI_MakeEdge(GBC).Edge();
231     }
232     else {
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;
277 #else
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;
284 #endif
285
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;
295       }
296
297       // initial set-up of curve creator
298       GeomAPI_Interpolate GBC(points, isClosed, gp::Resolution());
299
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();
304
305         if (aVec1Ref.IsNull() || aVec2Ref.IsNull())
306           // error: bad vector parameter is specified
307           Standard_NullObject::Raise("Null object is given for a vector");
308
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);
312
313         // push constraint vectors to the curve creator
314         GBC.Load(aV1, aV2, /*Scale*/Standard_True);
315       }
316
317       // create bspline curve
318       GBC.Perform();
319       if (GBC.IsDone())
320         aShape = BRepBuilderAPI_MakeEdge(GBC.Curve()).Edge();
321     }
322   }
323   else {
324   }
325
326   if (aShape.IsNull()) return 0; // error: bad result
327
328   aFunction->SetValue(aShape);
329
330   log->SetTouched(Label());
331
332   return 1;
333 }
334
335 //================================================================================
336 /*!
337  * \brief Returns a name of creation operation and names and values of creation parameters
338  */
339 //================================================================================
340
341 bool GEOMImpl_SplineDriver::
342 GetCreationInformation(std::string&             theOperationName,
343                        std::vector<GEOM_Param>& theParams)
344 {
345   if (Label().IsNull()) return 0;
346   Handle(GEOM_Function) function = GEOM_Function::GetFunction(Label());
347
348   GEOMImpl_ISpline          aCI( function );
349   GEOMImpl_ICurveParametric aPI( function );
350   Standard_Integer aType = function->GetType();
351
352   theOperationName = "CURVE";
353
354   switch ( aType ) {
355   case SPLINE_BEZIER:
356   case SPLINE_INTERPOLATION:
357   case SPLINE_INTERPOL_TANGENTS:
358
359     AddParam( theParams, "Type", ( aType == SPLINE_BEZIER ? "Bezier" : "Interpolation"));
360
361     if ( aPI.HasData() )
362     {
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() );
370       else
371         AddParam( theParams, "t step", aPI.GetParamStep() );
372     }
373     else
374     {
375       if ( aCI.GetConstructorType() == COORD_CONSTRUCTOR )
376       {
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 ) << " ) ";
384       }
385       else
386       {
387         AddParam( theParams, "Points", aCI.GetPoints() );
388       }
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 );
393     }
394     break;
395   default:
396     return false;
397   }
398
399   return true;
400 }
401
402 IMPLEMENT_STANDARD_RTTIEXT (GEOMImpl_SplineDriver,GEOM_BaseDriver)