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