1 // Copyright (C) 2019-2023 CEA, EDF
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "SketchAPI_BSpline.h"
22 #include <GeomAPI_BSpline2d.h>
23 #include <GeomAPI_Pnt2d.h>
25 #include <GeomAlgoAPI_EdgeBuilder.h>
27 #include <Locale_Convert.h>
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>
35 #include <ModelAPI_Tools.h>
37 #include <SketchPlugin_ConstraintCoincidenceInternal.h>
38 #include <SketchPlugin_Line.h>
39 #include <SketchPlugin_Point.h>
44 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature> & theFeature)
45 : SketchAPI_SketchEntity(theFeature)
50 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature,
52 : SketchAPI_SketchEntity(theFeature)
58 SketchAPI_BSpline::~SketchAPI_BSpline()
62 void SketchAPI_BSpline::setByDegreePolesAndWeights(const ModelHighAPI_Integer& theDegree,
63 const std::list<GeomPnt2dPtr>& thePoles,
64 const std::list<ModelHighAPI_Double>& theWeights)
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());
73 aWeights = theWeights;
75 ModelHighAPI_Integer aDegree = theDegree;
76 std::list<ModelHighAPI_Double> aKnots;
77 std::list<ModelHighAPI_Integer> aMults;
78 getDefaultParameters(thePoles, aWeights, aDegree, aKnots, aMults);
80 setByParameters(aDegree, thePoles, aWeights, aKnots, aMults);
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)
89 fillAttribute(theDegree, degree());
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());
99 fillAttribute(theWeights, weights());
101 fillAttribute(theKnots, knots());
102 fillAttribute(theMults, multiplicities());
104 if (feature()->getKind() != SketchPlugin_BSplinePeriodic::ID())
105 setStartAndEndPoints();
109 void SketchAPI_BSpline::setStartAndEndPoints()
111 fillAttribute(poles()->pnt(0), startPoint());
112 fillAttribute(poles()->pnt(poles()->size() - 1), endPoint());
115 void SketchAPI_BSpline::setByExternal(const ModelHighAPI_Selection & theExternal)
117 fillAttribute(theExternal, external());
121 static CompositeFeaturePtr sketchForFeature(FeaturePtr theFeature)
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();
130 static void createInternalConstraint(const CompositeFeaturePtr& theSketch,
131 const AttributePoint2DPtr& thePoint,
132 const AttributePoint2DArrayPtr& thePoles,
133 const int thePoleIndex)
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();
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)
150 GeomPnt2dPtr aPole = thePoles->pnt(thePoleIndex);
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();
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());
165 aPointFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(theAuxiliary);
167 createInternalConstraint(theSketch, aCoord, thePoles, thePoleIndex);
169 theEntities.push_back(aPointFeature);
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)
179 int aEndPoleIndex = (theStartPoleIndex + 1) % thePoles->size();
180 GeomPnt2dPtr aStartPoint = thePoles->pnt(theStartPoleIndex);
181 GeomPnt2dPtr aEndPoint = thePoles->pnt(aEndPoleIndex);
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();
193 std::wostringstream aName;
194 aName << theBSpline->name() << "_segment_" << theStartPoleIndex << "_" << aEndPoleIndex;
195 aLineFeature->data()->setName(aName.str());
196 aLineFeature->lastResult()->data()->setName(aName.str());
198 aLineFeature->boolean(SketchPlugin_Line::AUXILIARY_ID())->setValue(theAuxiliary);
200 createInternalConstraint(theSketch, aLineStart, thePoles, theStartPoleIndex);
201 createInternalConstraint(theSketch, aLineEnd, thePoles, aEndPoleIndex);
203 theEntities.push_back(aLineFeature);
206 static void toMapOfAuxIndices(const std::list<int>& theRegular,
207 const std::list<int>& theAuxiliary,
208 std::map<int, bool>& theIndices)
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;
216 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_BSpline::controlPoles(
217 const std::list<int>& regular,
218 const std::list<int>& auxiliary) const
220 std::map<int, bool> anAux;
221 toMapOfAuxIndices(regular, auxiliary, anAux);
223 std::list<FeaturePtr> anEntities;
225 FeaturePtr aBSpline = feature();
226 CompositeFeaturePtr aSketch = sketchForFeature(aBSpline);
227 AttributePoint2DArrayPtr aPoles = poles();
229 for (auto it = anAux.begin(); it != anAux.end(); ++it)
230 createPole(aSketch, aBSpline, aPoles, it->first, it->second, anEntities);
232 return SketchAPI_SketchEntity::wrap(anEntities);
235 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_BSpline::controlPolygon(
236 const std::list<int>& regular,
237 const std::list<int>& auxiliary) const
239 std::map<int, bool> anAux;
240 toMapOfAuxIndices(regular, auxiliary, anAux);
242 std::list<FeaturePtr> anEntities;
244 FeaturePtr aBSpline = feature();
245 CompositeFeaturePtr aSketch = sketchForFeature(aBSpline);
246 AttributePoint2DArrayPtr aPoles = poles();
248 for (auto it = anAux.begin(); it != anAux.end(); ++it)
249 createSegment(aSketch, aBSpline, aPoles, it->first, it->second, anEntities);
251 return SketchAPI_SketchEntity::wrap(anEntities);
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
262 std::shared_ptr<GeomAPI_BSpline2d> aBSplineCurve;
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());
269 bool isPeriodic = feature()->getKind() == SketchPlugin_BSplinePeriodic::ID();
270 if (theDegree.intValue() < 0)
271 aBSplineCurve.reset(new GeomAPI_BSpline2d(thePoles, aWeights, isPeriodic));
273 aBSplineCurve.reset(new GeomAPI_BSpline2d(theDegree.intValue(), thePoles, aWeights,
274 std::list<double>(), std::list<int>(), isPeriodic));
278 // cannot build a B-spline curve
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());
289 void SketchAPI_BSpline::checkDefaultParameters(bool& isDefaultDegree,
290 bool& isDefaultWeights,
291 bool& isDefaultKnotsMults) const
293 static const double TOLERANCE = 1.e-7;
295 AttributePoint2DArrayPtr aPolesAttr = poles();
296 AttributeDoubleArrayPtr aWeightsAttr = weights();
297 AttributeDoubleArrayPtr aKnotsAttr = knots();
298 AttributeIntArrayPtr aMultsAttr = multiplicities();
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);
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);
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;
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);
334 isDefaultDegree = isDefaultDegree && isDefaultKnotsMults;
335 isDefaultWeights = isDefaultWeights && isDefaultKnotsMults;
339 static void bsplineAuxiliaryFeature(const AttributeRefAttrPtr& theReference,
340 FeaturePtr& thePoint,
341 FeaturePtr& theSegment)
343 ObjectPtr anAuxObject;
344 if (theReference->isObject())
345 anAuxObject = theReference->object();
347 anAuxObject = theReference->attr()->owner();
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;
359 static void collectAuxiliaryFeatures(FeaturePtr theBSpline,
360 std::map<int, FeaturePtr>& thePoints,
361 std::map<int, FeaturePtr>& theSegments)
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();
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);
380 else if (anAttrB && anAttrB->attributeType() == GeomDataAPI_Point2DArray::typeId()) {
381 aPoleIndex = anOwner->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
382 bsplineAuxiliaryFeature(aRefAttrA, aPoint, aLine);
386 thePoints[aPoleIndex->value()] = aPoint;
388 theSegments[aPoleIndex->value()] = aLine;
393 void SketchAPI_BSpline::dump(ModelHighAPI_Dumper& theDumper) const
396 return; // no need to dump copied feature
398 FeaturePtr aBase = feature();
399 const std::string& aSketchName = theDumper.parentName(aBase);
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;
406 // check if some B-spline parameters are default and should not be dumped
407 bool isDefaultDegree, isDefaultWeights, isDefaultKnotsMults;
408 checkDefaultParameters(isDefaultDegree, isDefaultWeights, isDefaultKnotsMults);
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;
422 // dump "auxiliary" flag if necessary
423 SketchAPI_SketchEntity::dump(theDumper);
425 // dump control polygon
426 std::map<int, FeaturePtr> anAuxPoles, anAuxSegments;
427 collectAuxiliaryFeatures(aBase, anAuxPoles, anAuxSegments);
429 if (!anAuxPoles.empty())
430 dumpControlPolygon(theDumper, aBase, "controlPoles", anAuxPoles);
431 if (!anAuxSegments.empty())
432 dumpControlPolygon(theDumper, aBase, "controlPolygon", anAuxSegments);
435 static void dumpList(ModelHighAPI_Dumper& theDumper,
436 const std::string& theAttrName,
437 const std::set<int>& theIndices)
439 theDumper << theAttrName << " = [";
440 std::set<int>::const_iterator it = theIndices.begin();
442 for (++it; it != theIndices.end(); ++it)
443 theDumper << ", " << *it;
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
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) {
461 theDumper << theDumper.name(it->second, false);
462 theDumper.doNotDumpFeature(it->second);
465 if (it->second->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
466 anAuxiliary.insert(it->first);
468 aRegular.insert(it->first);
470 theDumper << "] = " << theDumper.name(theBSpline) << "." << theMethod << "(";
471 if (!aRegular.empty()) {
472 dumpList(theDumper, "regular", aRegular);
473 if (!anAuxiliary.empty())
476 if (!anAuxiliary.empty())
477 dumpList(theDumper, "auxiliary", anAuxiliary);
478 theDumper << ")" << std::endl;
481 static void setCoordinates(const FeaturePtr& theFeature,
482 const std::string& theAttrName,
483 const GeomPnt2dPtr& theCoordinates)
485 AttributePoint2DPtr aPoint =
486 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theFeature->attribute(theAttrName));
487 aPoint->setValue(theCoordinates);
490 bool SketchAPI_BSpline::insertPole(const int theIndex,
491 const GeomPnt2dPtr& theCoordinates,
492 const ModelHighAPI_Double& theWeight)
494 std::ostringstream anActionName;
495 anActionName << SketchPlugin_BSplineBase::ADD_POLE_ACTION_ID() << "#" << theIndex;
496 bool isOk = feature()->customAction(anActionName.str());
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());
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);
523 // =================================================================================================
524 SketchAPI_BSplinePeriodic::SketchAPI_BSplinePeriodic(const FeaturePtr& theFeature)
525 : SketchAPI_BSpline(theFeature, false)