Salome HOME
Merge V8_4_BR branch.
[modules/geom.git] / src / GEOMImpl / GEOMImpl_SplineDriver.cxx
1 // Copyright (C) 2007-2016  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           // Keep given order of points to use it in case of equidistant candidates
170           //               .-<---<-.
171           //              /         \
172           // o  o  o  c  o->o->o->o->n  o  o
173           //          |  |           |
174           //          i i+1       nearest
175           gp_Pnt p = points->Value(nearest);
176           for (int j = nearest; j > i+1; j--)
177             points->SetValue(j, points->Value(j-1));
178           points->SetValue(i+1, p);
179         }
180         if ( pPrev.Distance(points->Value(i+1)) <= Precision::Confusion() )
181           nbDup++;
182         else
183           pPrev = points->Value(i+1);
184       }
185       if ( nbDup > 0 ) {
186         Handle(TColgp_HArray1OfPnt) tmpPoints = new TColgp_HArray1OfPnt(1, length-nbDup);
187         int j = 1;
188         for (int i = 1; i <= length; i++) {
189           if (i == 1 || pPrev.Distance(points->Value(i)) > Precision::Confusion() ) {
190             tmpPoints->SetValue(j++, points->Value(i));
191             pPrev = points->Value(i);
192           }
193         }
194         points = tmpPoints;
195         length = points->Length();
196       }
197     } // end of reordering
198
199     bool closed = points->Value(1).Distance(points->Value(length)) <= gp::Resolution();
200
201     if (aType == SPLINE_BEZIER) {
202       // for Bezier curve we should append first point to the list if:
203       // a) "closed" flag is set, and
204       // b) first and last vertices are not too close
205       bool addFirst = aCI.GetIsClosed() && !closed;
206       
207       // re-fill points and check that there's enough points to create a curve
208       bool isValid = false;
209       TColgp_Array1OfPnt curvePoints(1, length + (addFirst ? 1 : 0));
210       gp_Pnt pp;
211       for (int i = 1; i <= length; i++) {
212         gp_Pnt p = points->Value(i);
213         if (!isValid && i > 1 && p.Distance(pp) > Precision::Confusion())
214           isValid = true;
215         curvePoints.SetValue(i, p);
216         pp = p;
217       }
218
219       if (!isValid)
220         // error: not enough points to create curve
221         Standard_ConstructionError::Raise("Points for Bezier Curve are too close");
222       
223       // set last point equal to first for the closed Bezier curve
224       if (addFirst) curvePoints.SetValue(length+1, curvePoints.Value(1));
225       
226       // create Bezier curve
227       Handle(Geom_BezierCurve) GBC = new Geom_BezierCurve(curvePoints);
228       aShape = BRepBuilderAPI_MakeEdge(GBC).Edge();
229     }
230     else {
231       // Below described processing of closed points set case
232       // is not done for constrained bsplined
233       bool typeok = aType == SPLINE_INTERPOLATION;
234 #if BSPLINE_PROCESS_CLOSED_PNTSET == 1
235       // Last point is removed from the list if:
236       // a) first and last vertices are equal;
237       // b) "closed" flag is not taken into account.
238       // If first and last points are equal, we force "closed" flag to be set to true.
239       // For the case when first and last vertices are equal, this approach causes
240       // result different that would be if last point had NOT be removed and "closed" flag is false.
241       bool isClosed = typeok && (aCI.GetIsClosed() || closed);
242       bool removeLast = typeok && closed;
243       bool addFirst = false;
244 #elif BSPLINE_PROCESS_CLOSED_PNTSET == 2
245       // Last point is removed from the list if:
246       // a) first and last vertices are equal;
247       // b) "closed" flag is set to true.
248       // Flag "closed" is taken "as is".
249       // For the case when first and last vertices are equal, this approach causes
250       // different results with "closed" flag set to true and false.
251       bool isClosed = typeok && aCI.GetIsClosed();
252       bool removeLast = typeok && aCI.GetIsClosed() && closed;
253       bool addFirst = false;
254 #elif BSPLINE_PROCESS_CLOSED_PNTSET == 3
255       // Points are passed "as is" to the creator.
256       // If first and last points are equal, we force "closed" flag to be set to false.
257       // For the case when first and last vertices are equal, this approach gives
258       // the same results with "closed" flag set to true and false.
259       bool isClosed = typeok && aCI.GetIsClosed() && !closed;
260       bool removeLast = false;
261       bool addFirst = false;
262 #elif BSPLINE_PROCESS_CLOSED_PNTSET == 4
263       // First point is added to the list if:
264       // a) first and last vertices are not equal;
265       // b) "closed" flag is set to true.
266       // In this case "closed" flag is forcidly set to false - bspline creator is 
267       // capable to create closed edge in this case.
268       // This approach gives the same results with "closed" flag set to true not
269       // depending on if set of points is closed or no.
270       // Also, it gives equal reqults in both case if set of points is closed or not
271       // and "closed" flag is set to true, in contrast to solution 3 above.
272       bool isClosed = false;
273       bool removeLast = false;
274       bool addFirst = typeok && aCI.GetIsClosed() && !closed;
275 #else
276       // Points are passed "as is" to the creator.
277       // This causes an error when first point is equal to last one and
278       // "closed" flag is set to true; see bug 0022885.
279       bool isClosed = typeok && aCI.GetIsClosed();
280       bool removeLast = false;
281       bool addFirst = false;
282 #endif
283
284       // remove last point or append first one if the conditions are observed (see above)
285       if (removeLast || addFirst) {
286         int extra = removeLast ? -1 : (addFirst ? 1 : 0 );
287         int nb = removeLast ? length-1 : length;
288         Handle(TColgp_HArray1OfPnt) curvePoints = new TColgp_HArray1OfPnt (1, length+extra);
289         for (int i = 1; i <= nb; i++)
290           curvePoints->SetValue(i, points->Value(i));
291         if (addFirst) curvePoints->SetValue(length+1, points->Value(1));
292         points = curvePoints;
293       }
294
295       // initial set-up of curve creator
296       GeomAPI_Interpolate GBC(points, isClosed, gp::Resolution());
297
298       // add tangent vectors constraints
299       if (aType == SPLINE_INTERPOL_TANGENTS) {
300         Handle(GEOM_Function) aVec1Ref = aCI.GetFirstVector();
301         Handle(GEOM_Function) aVec2Ref = aCI.GetLastVector();
302
303         if (aVec1Ref.IsNull() || aVec2Ref.IsNull())
304           // error: bad vector parameter is specified
305           Standard_NullObject::Raise("Null object is given for a vector");
306
307         // take orientation of edge into account to avoid regressions, as it was implemented so
308         gp_Vec aV1 = GEOMUtils::GetVector(aVec1Ref->GetValue(), Standard_True);
309         gp_Vec aV2 = GEOMUtils::GetVector(aVec2Ref->GetValue(), Standard_True);
310
311         // push constraint vectors to the curve creator
312         GBC.Load(aV1, aV2, /*Scale*/Standard_True);
313       }
314
315       // create bspline curve
316       GBC.Perform();
317       if (GBC.IsDone())
318         aShape = BRepBuilderAPI_MakeEdge(GBC.Curve()).Edge();
319     }
320   }
321   else {
322   }
323
324   if (aShape.IsNull()) return 0; // error: bad result
325
326   aFunction->SetValue(aShape);
327
328   log->SetTouched(Label());
329
330   return 1;
331 }
332
333 //================================================================================
334 /*!
335  * \brief Returns a name of creation operation and names and values of creation parameters
336  */
337 //================================================================================
338
339 bool GEOMImpl_SplineDriver::
340 GetCreationInformation(std::string&             theOperationName,
341                        std::vector<GEOM_Param>& theParams)
342 {
343   if (Label().IsNull()) return 0;
344   Handle(GEOM_Function) function = GEOM_Function::GetFunction(Label());
345
346   GEOMImpl_ISpline          aCI( function );
347   GEOMImpl_ICurveParametric aPI( function );
348   Standard_Integer aType = function->GetType();
349
350   theOperationName = "CURVE";
351
352   switch ( aType ) {
353   case SPLINE_BEZIER:
354   case SPLINE_INTERPOLATION:
355   case SPLINE_INTERPOL_TANGENTS:
356
357     AddParam( theParams, "Type", ( aType == SPLINE_BEZIER ? "Bezier" : "Interpolation"));
358
359     if ( aPI.HasData() )
360     {
361       AddParam( theParams, "X(t) equation", aPI.GetExprX() );
362       AddParam( theParams, "Y(t) equation", aPI.GetExprY() );
363       AddParam( theParams, "Z(t) equation", aPI.GetExprZ() );
364       AddParam( theParams, "Min t", aPI.GetParamMin() );
365       AddParam( theParams, "Max t", aPI.GetParamMax() );
366       if ( aPI.GetParamNbStep() )
367         AddParam( theParams, "Number of steps", aPI.GetParamNbStep() );
368       else
369         AddParam( theParams, "t step", aPI.GetParamStep() );
370     }
371     else
372     {
373       if ( aCI.GetConstructorType() == COORD_CONSTRUCTOR )
374       {
375         Handle(TColStd_HArray1OfReal) coords = aCI.GetCoordinates();
376         GEOM_Param& pntParam = AddParam( theParams, "Points");
377         pntParam << ( coords->Length() ) / 3 << " points: ";
378         for ( int i = coords->Lower(), nb = coords->Upper(); i <= nb; )
379           pntParam << "( " << coords->Value( i++ )
380                    << ", " << coords->Value( i++ )
381                    << ", " << coords->Value( i++ ) << " ) ";
382       }
383       else
384       {
385         AddParam( theParams, "Points", aCI.GetPoints() );
386       }
387       Handle(GEOM_Function) v1 = aCI.GetFirstVector();
388       Handle(GEOM_Function) v2 = aCI.GetLastVector();
389       if ( !v1.IsNull() ) AddParam( theParams, "First tangent vector", v1 );
390       if ( !v2.IsNull() ) AddParam( theParams, "Last tangent vector", v2 );
391     }
392     break;
393   default:
394     return false;
395   }
396
397   return true;
398 }
399
400 IMPLEMENT_STANDARD_RTTIEXT (GEOMImpl_SplineDriver,GEOM_BaseDriver);