Salome HOME
971591e0a30ded40c271661de727a0c8f3f2f77f
[modules/shaper.git] / src / SketchAPI / SketchAPI_BSpline.cpp
1 // Copyright (C) 2019-2022  CEA/DEN, EDF R&D
2 //
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.
7 //
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.
12 //
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
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "SketchAPI_BSpline.h"
21
22 #include <GeomAPI_BSpline2d.h>
23 #include <GeomAPI_Pnt2d.h>
24
25 #include <GeomAlgoAPI_EdgeBuilder.h>
26
27 #include <Locale_Convert.h>
28
29 #include <ModelHighAPI_Double.h>
30 #include <ModelHighAPI_Dumper.h>
31 #include <ModelHighAPI_Integer.h>
32 #include <ModelHighAPI_Selection.h>
33 #include <ModelHighAPI_Tools.h>
34
35 #include <ModelAPI_Tools.h>
36
37 #include <SketchPlugin_ConstraintCoincidenceInternal.h>
38 #include <SketchPlugin_Line.h>
39 #include <SketchPlugin_Point.h>
40
41 #include <cmath>
42
43
44 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature> & theFeature)
45   : SketchAPI_SketchEntity(theFeature)
46 {
47   initialize();
48 }
49
50 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature,
51                                      bool theInitialize)
52   : SketchAPI_SketchEntity(theFeature)
53 {
54   if (theInitialize)
55     initialize();
56 }
57
58 SketchAPI_BSpline::~SketchAPI_BSpline()
59 {
60 }
61
62 void SketchAPI_BSpline::setByDegreePolesAndWeights(const ModelHighAPI_Integer& theDegree,
63                                                    const std::list<GeomPnt2dPtr>& thePoles,
64                                                    const std::list<ModelHighAPI_Double>& theWeights)
65 {
66   std::list<ModelHighAPI_Double> aWeights;
67   if (theWeights.size() <= 1) {
68     // prepare array of equal weights
69     aWeights.assign(thePoles.size(),
70         theWeights.empty() ? ModelHighAPI_Double(1.0) : theWeights.front());
71   }
72   else
73     aWeights = theWeights;
74
75   ModelHighAPI_Integer aDegree = theDegree;
76   std::list<ModelHighAPI_Double> aKnots;
77   std::list<ModelHighAPI_Integer> aMults;
78   getDefaultParameters(thePoles, aWeights, aDegree, aKnots, aMults);
79
80   setByParameters(aDegree, thePoles, aWeights, aKnots, aMults);
81 }
82
83 void SketchAPI_BSpline::setByParameters(const ModelHighAPI_Integer& theDegree,
84                                         const std::list<GeomPnt2dPtr>& thePoles,
85                                         const std::list<ModelHighAPI_Double>& theWeights,
86                                         const std::list<ModelHighAPI_Double>& theKnots,
87                                         const std::list<ModelHighAPI_Integer>& theMults)
88 {
89   fillAttribute(theDegree, degree());
90
91   fillAttribute(thePoles, poles());
92   if (theWeights.size() <= 1) {
93     // prepare array of equal weights
94     std::list<ModelHighAPI_Double> aWeights(thePoles.size(),
95         theWeights.empty() ? ModelHighAPI_Double(1.0) : theWeights.front());
96     fillAttribute(aWeights, weights());
97   }
98   else
99     fillAttribute(theWeights, weights());
100
101   fillAttribute(theKnots, knots());
102   fillAttribute(theMults, multiplicities());
103
104   if (feature()->getKind() != SketchPlugin_BSplinePeriodic::ID())
105     setStartAndEndPoints();
106   execute();
107 }
108
109 void SketchAPI_BSpline::setStartAndEndPoints()
110 {
111   fillAttribute(poles()->pnt(0), startPoint());
112   fillAttribute(poles()->pnt(poles()->size() - 1), endPoint());
113 }
114
115 void SketchAPI_BSpline::setByExternal(const ModelHighAPI_Selection & theExternal)
116 {
117   fillAttribute(theExternal, external());
118   execute();
119 }
120
121 static CompositeFeaturePtr sketchForFeature(FeaturePtr theFeature)
122 {
123   const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
124   for (std::set<AttributePtr>::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt)
125     if ((*anIt)->id() == SketchPlugin_Sketch::FEATURES_ID())
126       return std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
127   return CompositeFeaturePtr();
128 }
129
130 static void createInternalConstraint(const CompositeFeaturePtr& theSketch,
131                                      const AttributePoint2DPtr& thePoint,
132                                      const AttributePoint2DArrayPtr& thePoles,
133                                      const int thePoleIndex)
134 {
135   FeaturePtr aConstraint = theSketch->addFeature(SketchPlugin_ConstraintCoincidenceInternal::ID());
136   aConstraint->refattr(SketchPlugin_Constraint::ENTITY_A())->setAttr(thePoint);
137   aConstraint->refattr(SketchPlugin_Constraint::ENTITY_B())->setAttr(thePoles);
138   aConstraint->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B())
139       ->setValue(thePoleIndex);
140   aConstraint->execute();
141 }
142
143 static void createPole(const CompositeFeaturePtr& theSketch,
144                        const FeaturePtr& theBSpline,
145                        const AttributePoint2DArrayPtr& thePoles,
146                        const int thePoleIndex,
147                        const bool theAuxiliary,
148                        std::list<FeaturePtr>& theEntities)
149 {
150   GeomPnt2dPtr aPole = thePoles->pnt(thePoleIndex);
151
152   FeaturePtr aPointFeature = theSketch->addFeature(SketchPlugin_Point::ID());
153   AttributePoint2DPtr aCoord = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
154       aPointFeature->attribute(SketchPlugin_Point::COORD_ID()));
155   aCoord->setValue(aPole);
156   aPointFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(theBSpline);
157   aPointFeature->execute();
158
159   std::wostringstream aName;
160   aName << theBSpline->name() << "_" << Locale::Convert::toWString(thePoles->id())
161                               << "_" << thePoleIndex;
162   aPointFeature->data()->setName(aName.str());
163   aPointFeature->lastResult()->data()->setName(aName.str());
164
165   aPointFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(theAuxiliary);
166
167   createInternalConstraint(theSketch, aCoord, thePoles, thePoleIndex);
168
169   theEntities.push_back(aPointFeature);
170 }
171
172 static void createSegment(const CompositeFeaturePtr& theSketch,
173                           const FeaturePtr& theBSpline,
174                           const AttributePoint2DArrayPtr& thePoles,
175                           const int theStartPoleIndex,
176                           const bool theAuxiliary,
177                           std::list<FeaturePtr>& theEntities)
178 {
179   int aEndPoleIndex = (theStartPoleIndex + 1) % thePoles->size();
180   GeomPnt2dPtr aStartPoint = thePoles->pnt(theStartPoleIndex);
181   GeomPnt2dPtr aEndPoint = thePoles->pnt(aEndPoleIndex);
182
183   FeaturePtr aLineFeature = theSketch->addFeature(SketchPlugin_Line::ID());
184   AttributePoint2DPtr aLineStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
185       aLineFeature->attribute(SketchPlugin_Line::START_ID()));
186   aLineStart->setValue(aStartPoint);
187   AttributePoint2DPtr aLineEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
188       aLineFeature->attribute(SketchPlugin_Line::END_ID()));
189   aLineEnd->setValue(aEndPoint);
190   aLineFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(theBSpline);
191   aLineFeature->execute();
192
193   std::wostringstream aName;
194   aName << theBSpline->name() << "_segment_" << theStartPoleIndex << "_" << aEndPoleIndex;
195   aLineFeature->data()->setName(aName.str());
196   aLineFeature->lastResult()->data()->setName(aName.str());
197
198   aLineFeature->boolean(SketchPlugin_Line::AUXILIARY_ID())->setValue(theAuxiliary);
199
200   createInternalConstraint(theSketch, aLineStart, thePoles, theStartPoleIndex);
201   createInternalConstraint(theSketch, aLineEnd, thePoles, aEndPoleIndex);
202
203   theEntities.push_back(aLineFeature);
204 }
205
206 static void toMapOfAuxIndices(const std::list<int>& theRegular,
207                               const std::list<int>& theAuxiliary,
208                               std::map<int, bool>& theIndices)
209 {
210   for (auto it = theRegular.begin(); it != theRegular.end(); ++it)
211     theIndices[*it] = false;
212   for (auto it = theAuxiliary.begin(); it != theAuxiliary.end(); ++it)
213     theIndices[*it] = true;
214 }
215
216 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_BSpline::controlPoles(
217     const std::list<int>& regular,
218     const std::list<int>& auxiliary) const
219 {
220   std::map<int, bool> anAux;
221   toMapOfAuxIndices(regular, auxiliary, anAux);
222
223   std::list<FeaturePtr> anEntities;
224
225   FeaturePtr aBSpline = feature();
226   CompositeFeaturePtr aSketch = sketchForFeature(aBSpline);
227   AttributePoint2DArrayPtr aPoles = poles();
228
229   for (auto it = anAux.begin(); it != anAux.end(); ++it)
230     createPole(aSketch, aBSpline, aPoles, it->first, it->second, anEntities);
231
232   return SketchAPI_SketchEntity::wrap(anEntities);
233 }
234
235 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_BSpline::controlPolygon(
236     const std::list<int>& regular,
237     const std::list<int>& auxiliary) const
238 {
239   std::map<int, bool> anAux;
240   toMapOfAuxIndices(regular, auxiliary, anAux);
241
242   std::list<FeaturePtr> anEntities;
243
244   FeaturePtr aBSpline = feature();
245   CompositeFeaturePtr aSketch = sketchForFeature(aBSpline);
246   AttributePoint2DArrayPtr aPoles = poles();
247
248   for (auto it = anAux.begin(); it != anAux.end(); ++it)
249     createSegment(aSketch, aBSpline, aPoles, it->first, it->second, anEntities);
250
251   return SketchAPI_SketchEntity::wrap(anEntities);
252 }
253
254
255 void SketchAPI_BSpline::getDefaultParameters(
256     const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
257     const std::list<ModelHighAPI_Double>& theWeights,
258     ModelHighAPI_Integer& theDegree,
259     std::list<ModelHighAPI_Double>& theKnots,
260     std::list<ModelHighAPI_Integer>& theMults) const
261 {
262   std::shared_ptr<GeomAPI_BSpline2d> aBSplineCurve;
263   try {
264     std::list<double> aWeights;
265     for (std::list<ModelHighAPI_Double>::const_iterator it = theWeights.begin();
266          it != theWeights.end(); ++it)
267       aWeights.push_back(it->value());
268
269     bool isPeriodic = feature()->getKind() == SketchPlugin_BSplinePeriodic::ID();
270     if (theDegree.intValue() < 0)
271       aBSplineCurve.reset(new GeomAPI_BSpline2d(thePoles, aWeights, isPeriodic));
272     else {
273       aBSplineCurve.reset(new GeomAPI_BSpline2d(theDegree.intValue(), thePoles, aWeights,
274                                                 std::list<double>(), std::list<int>(), isPeriodic));
275     }
276   }
277   catch (...) {
278     // cannot build a B-spline curve
279     return;
280   }
281
282   theDegree = aBSplineCurve->degree();
283   std::list<double> aKnots = aBSplineCurve->knots();
284   std::list<int> aMults = aBSplineCurve->mults();
285   theKnots.assign(aKnots.begin(), aKnots.end());
286   theMults.assign(aMults.begin(), aMults.end());
287 }
288
289 void SketchAPI_BSpline::checkDefaultParameters(bool& isDefaultDegree,
290                                                bool& isDefaultWeights,
291                                                bool& isDefaultKnotsMults) const
292 {
293   static const double TOLERANCE = 1.e-7;
294
295   AttributePoint2DArrayPtr aPolesAttr = poles();
296   AttributeDoubleArrayPtr aWeightsAttr = weights();
297   AttributeDoubleArrayPtr aKnotsAttr = knots();
298   AttributeIntArrayPtr aMultsAttr = multiplicities();
299
300   std::list<GeomPnt2dPtr> aPoles;
301   std::list<ModelHighAPI_Double> aWeights;
302   isDefaultWeights = true;
303   for (int anIndex = 0; anIndex < aPolesAttr->size(); ++anIndex) {
304     aPoles.push_back(aPolesAttr->pnt(anIndex));
305     double aCurWeight = aWeightsAttr->value(anIndex);
306     isDefaultWeights = isDefaultWeights && fabs(aCurWeight - 1.0) < TOLERANCE;
307     aWeights.push_back(aCurWeight);
308   }
309
310   ModelHighAPI_Integer aDegree(-1);
311   std::list<ModelHighAPI_Double> aKnots;
312   std::list<ModelHighAPI_Integer> aMults;
313   getDefaultParameters(aPoles, aWeights, aDegree, aKnots, aMults);
314   isDefaultDegree = aDegree.intValue() == degree()->value();
315   if (!isDefaultDegree) {
316     // recalculate knots and multiplicities with the actual degree
317     aDegree = degree()->value();
318     getDefaultParameters(aPoles, aWeights, aDegree, aKnots, aMults);
319   }
320
321   isDefaultKnotsMults = aKnotsAttr->size() == (int)aKnots.size()
322                      && aMultsAttr->size() == (int)aMults.size();
323   if (isDefaultKnotsMults) {
324     std::list<ModelHighAPI_Double>::iterator anIt = aKnots.begin();
325     for (int anIndex = 0; isDefaultKnotsMults && anIt != aKnots.end(); ++anIt, ++anIndex)
326       isDefaultKnotsMults = fabs(anIt->value() - aKnotsAttr->value(anIndex)) < TOLERANCE;
327   }
328   if (isDefaultKnotsMults) {
329     std::list<ModelHighAPI_Integer>::iterator anIt = aMults.begin();
330     for (int anIndex = 0; isDefaultKnotsMults && anIt != aMults.end(); ++anIt, ++anIndex)
331       isDefaultKnotsMults = anIt->intValue() == aMultsAttr->value(anIndex);
332   }
333
334   isDefaultDegree = isDefaultDegree && isDefaultKnotsMults;
335   isDefaultWeights = isDefaultWeights && isDefaultKnotsMults;
336 }
337
338
339 static void bsplineAuxiliaryFeature(const AttributeRefAttrPtr& theReference,
340                                     FeaturePtr& thePoint,
341                                     FeaturePtr& theSegment)
342 {
343   ObjectPtr anAuxObject;
344   if (theReference->isObject())
345     anAuxObject = theReference->object();
346   else
347     anAuxObject = theReference->attr()->owner();
348
349   FeaturePtr anAuxFeature = ModelAPI_Feature::feature(anAuxObject);
350   if (anAuxFeature->getKind() == SketchPlugin_Point::ID())
351     thePoint = anAuxFeature;
352   else if (anAuxFeature->getKind() == SketchPlugin_Line::ID() &&
353            theReference->attr()->id() == SketchPlugin_Line::START_ID()) {
354     // process only coincidence with start point
355     theSegment = anAuxFeature;
356   }
357 }
358
359 static void collectAuxiliaryFeatures(FeaturePtr theBSpline,
360                                      std::map<int, FeaturePtr>& thePoints,
361                                      std::map<int, FeaturePtr>& theSegments)
362 {
363   const std::set<AttributePtr>& aRefs = theBSpline->data()->refsToMe();
364   for (std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
365        aRefIt != aRefs.end(); ++aRefIt) {
366     FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
367     if (anOwner->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) {
368       // process internal constraints only
369       AttributeRefAttrPtr aRefAttrA = anOwner->refattr(SketchPlugin_Constraint::ENTITY_A());
370       AttributeRefAttrPtr aRefAttrB = anOwner->refattr(SketchPlugin_Constraint::ENTITY_B());
371       AttributePtr anAttrA = aRefAttrA->attr();
372       AttributePtr anAttrB = aRefAttrB->attr();
373
374       AttributeIntegerPtr aPoleIndex;
375       FeaturePtr aPoint, aLine;
376       if (anAttrA && anAttrA->attributeType() == GeomDataAPI_Point2DArray::typeId()) {
377         aPoleIndex = anOwner->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A());
378         bsplineAuxiliaryFeature(aRefAttrB, aPoint, aLine);
379       }
380       else if (anAttrB && anAttrB->attributeType() == GeomDataAPI_Point2DArray::typeId()) {
381         aPoleIndex = anOwner->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
382         bsplineAuxiliaryFeature(aRefAttrA, aPoint, aLine);
383       }
384
385       if (aPoint)
386         thePoints[aPoleIndex->value()] = aPoint;
387       else if (aLine)
388         theSegments[aPoleIndex->value()] = aLine;
389     }
390   }
391 }
392
393 void SketchAPI_BSpline::dump(ModelHighAPI_Dumper& theDumper) const
394 {
395   if (isCopy())
396     return; // no need to dump copied feature
397
398   FeaturePtr aBase = feature();
399   const std::string& aSketchName = theDumper.parentName(aBase);
400
401   AttributeSelectionPtr anExternal = aBase->selection(SketchPlugin_SketchEntity::EXTERNAL_ID());
402   if (anExternal->context()) {
403     // B-spline is external
404     theDumper << aBase << " = " << aSketchName << ".addSpline(" << anExternal << ")" << std::endl;
405   } else {
406     // check if some B-spline parameters are default and should not be dumped
407     bool isDefaultDegree, isDefaultWeights, isDefaultKnotsMults;
408     checkDefaultParameters(isDefaultDegree, isDefaultWeights, isDefaultKnotsMults);
409
410     theDumper << aBase << " = " << aSketchName << ".addSpline(";
411     if (!isDefaultDegree)
412       theDumper << "degree = " << degree() << ", ";
413     theDumper << "poles = " << poles();
414     if (!isDefaultWeights)
415       theDumper << ", weights = " << weights();
416     if (!isDefaultKnotsMults)
417       theDumper << ", knots = " << knots() << ", multiplicities = " << multiplicities();
418     if (aBase->getKind() == SketchPlugin_BSplinePeriodic::ID())
419       theDumper << ", periodic = True";
420     theDumper << ")" << std::endl;
421   }
422   // dump "auxiliary" flag if necessary
423   SketchAPI_SketchEntity::dump(theDumper);
424
425   // dump control polygon
426   std::map<int, FeaturePtr> anAuxPoles, anAuxSegments;
427   collectAuxiliaryFeatures(aBase, anAuxPoles, anAuxSegments);
428
429   if (!anAuxPoles.empty())
430     dumpControlPolygon(theDumper, aBase, "controlPoles", anAuxPoles);
431   if (!anAuxSegments.empty())
432     dumpControlPolygon(theDumper, aBase, "controlPolygon", anAuxSegments);
433 }
434
435 static void dumpList(ModelHighAPI_Dumper& theDumper,
436                      const std::string& theAttrName,
437                      const std::set<int>& theIndices)
438 {
439   theDumper << theAttrName << " = [";
440   std::set<int>::const_iterator it = theIndices.begin();
441   theDumper << *it;
442   for (++it; it != theIndices.end(); ++it)
443     theDumper << ", " << *it;
444   theDumper << "]";
445 }
446
447 void SketchAPI_BSpline::dumpControlPolygon(
448     ModelHighAPI_Dumper& theDumper,
449     const FeaturePtr& theBSpline,
450     const std::string& theMethod,
451     const std::map<int, FeaturePtr>& theAuxFeatures) const
452 {
453   theDumper << "[";
454   bool isFirst = true;
455   // dump features and split them to auxiliary and regular
456   std::set<int> aRegular, anAuxiliary;
457   for (std::map<int, FeaturePtr>::const_iterator it = theAuxFeatures.begin();
458        it != theAuxFeatures.end(); ++it) {
459     if (!isFirst)
460       theDumper << ", ";
461     theDumper << theDumper.name(it->second, false);
462     theDumper.doNotDumpFeature(it->second);
463     isFirst = false;
464
465     if (it->second->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
466       anAuxiliary.insert(it->first);
467     else
468       aRegular.insert(it->first);
469   }
470   theDumper << "] = " << theDumper.name(theBSpline) << "." << theMethod << "(";
471   if (!aRegular.empty()) {
472     dumpList(theDumper, "regular", aRegular);
473     if (!anAuxiliary.empty())
474       theDumper << ", ";
475   }
476   if (!anAuxiliary.empty())
477     dumpList(theDumper, "auxiliary", anAuxiliary);
478   theDumper << ")" << std::endl;
479 }
480
481 static void setCoordinates(const FeaturePtr& theFeature,
482                            const std::string& theAttrName,
483                            const GeomPnt2dPtr& theCoordinates)
484 {
485   AttributePoint2DPtr aPoint =
486       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theFeature->attribute(theAttrName));
487   aPoint->setValue(theCoordinates);
488 }
489
490 bool SketchAPI_BSpline::insertPole(const int theIndex,
491                                    const GeomPnt2dPtr& theCoordinates,
492                                    const ModelHighAPI_Double& theWeight)
493 {
494   std::ostringstream anActionName;
495   anActionName << SketchPlugin_BSplineBase::ADD_POLE_ACTION_ID() << "#" << theIndex;
496   bool isOk = feature()->customAction(anActionName.str());
497   if (isOk) {
498     int anIndex = theIndex + 1;
499     if (feature()->getKind() == SketchPlugin_BSpline::ID() && anIndex + 1 >= poles()->size())
500       anIndex = poles()->size() - 2;
501     // initialize coordinates and weight of new pole
502     poles()->setPnt(anIndex, theCoordinates);
503     weights()->setValue(anIndex, theWeight.value());
504
505     // update coordinates of points of control polygon
506     std::map<int, FeaturePtr> aPoints, aLines;
507     collectAuxiliaryFeatures(feature(), aPoints, aLines);
508     std::map<int, FeaturePtr>::iterator aFound = aPoints.find(anIndex);
509     if (aFound != aPoints.end())
510       setCoordinates(aFound->second, SketchPlugin_Point::COORD_ID(), theCoordinates);
511     aFound = aLines.find(anIndex);
512     if (aFound != aLines.end())
513       setCoordinates(aFound->second, SketchPlugin_Line::START_ID(), theCoordinates);
514     aFound = aLines.find(anIndex - 1);
515     if (aFound != aLines.end())
516       setCoordinates(aFound->second, SketchPlugin_Line::END_ID(), theCoordinates);
517   }
518   return isOk;
519 }
520
521
522
523 // =================================================================================================
524 SketchAPI_BSplinePeriodic::SketchAPI_BSplinePeriodic(const FeaturePtr& theFeature)
525   : SketchAPI_BSpline(theFeature, false)
526 {
527   initialize();
528 }