1 // Copyright (C) 2019-2020 CEA/DEN, EDF R&D
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 <ModelHighAPI_Double.h>
28 #include <ModelHighAPI_Dumper.h>
29 #include <ModelHighAPI_Integer.h>
30 #include <ModelHighAPI_Selection.h>
31 #include <ModelHighAPI_Tools.h>
33 #include <SketchPlugin_ConstraintCoincidenceInternal.h>
34 #include <SketchPlugin_Line.h>
35 #include <SketchPlugin_Point.h>
40 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature> & theFeature)
41 : SketchAPI_SketchEntity(theFeature)
46 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature,
47 const std::list<GeomPnt2dPtr>& thePoles,
48 const std::list<ModelHighAPI_Double>& theWeights)
49 : SketchAPI_SketchEntity(theFeature)
52 setByDegreePolesAndWeights(ModelHighAPI_Integer(-1), thePoles, theWeights);
56 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature,
58 const std::list<GeomPnt2dPtr>& thePoles,
59 const std::list<ModelHighAPI_Double>& theWeights,
60 const std::list<ModelHighAPI_Double>& theKnots,
61 const std::list<ModelHighAPI_Integer>& theMults)
62 : SketchAPI_SketchEntity(theFeature)
65 if (theKnots.empty() || theMults.empty())
66 setByDegreePolesAndWeights(theDegree, thePoles, theWeights);
68 setByParameters(theDegree, thePoles, theWeights, theKnots, theMults);
72 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature,
73 const ModelHighAPI_Selection& theExternal)
74 : SketchAPI_SketchEntity(theFeature)
77 setByExternal(theExternal);
81 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature,
82 const std::string& theExternalName)
83 : SketchAPI_SketchEntity(theFeature)
86 setByExternalName(theExternalName);
90 SketchAPI_BSpline::~SketchAPI_BSpline()
94 void SketchAPI_BSpline::setByDegreePolesAndWeights(const ModelHighAPI_Integer& theDegree,
95 const std::list<GeomPnt2dPtr>& thePoles,
96 const std::list<ModelHighAPI_Double>& theWeights)
98 std::list<ModelHighAPI_Double> aWeights;
99 if (theWeights.size() <= 1) {
100 // prepare array of equal weights
101 aWeights.assign(thePoles.size(),
102 theWeights.empty() ? ModelHighAPI_Double(1.0) : theWeights.front());
105 aWeights = theWeights;
107 ModelHighAPI_Integer aDegree = theDegree;
108 std::list<ModelHighAPI_Double> aKnots;
109 std::list<ModelHighAPI_Integer> aMults;
110 getDefaultParameters(thePoles, aWeights, aDegree, aKnots, aMults);
112 setByParameters(aDegree, thePoles, aWeights, aKnots, aMults);
115 void SketchAPI_BSpline::setByParameters(const ModelHighAPI_Integer& theDegree,
116 const std::list<GeomPnt2dPtr>& thePoles,
117 const std::list<ModelHighAPI_Double>& theWeights,
118 const std::list<ModelHighAPI_Double>& theKnots,
119 const std::list<ModelHighAPI_Integer>& theMults)
121 fillAttribute(theDegree, degree());
123 fillAttribute(thePoles, poles());
124 if (theWeights.size() <= 1) {
125 // prepare array of equal weights
126 std::list<ModelHighAPI_Double> aWeights(thePoles.size(),
127 theWeights.empty() ? ModelHighAPI_Double(1.0) : theWeights.front());
128 fillAttribute(aWeights, weights());
131 fillAttribute(theWeights, weights());
133 fillAttribute(theKnots, knots());
134 fillAttribute(theMults, multiplicities());
136 setStartAndEndPoints();
140 void SketchAPI_BSpline::setStartAndEndPoints()
142 fillAttribute(poles()->pnt(0), startPoint());
143 fillAttribute(poles()->pnt(poles()->size() - 1), endPoint());
146 void SketchAPI_BSpline::setByExternal(const ModelHighAPI_Selection & theExternal)
148 fillAttribute(theExternal, external());
152 void SketchAPI_BSpline::setByExternalName(const std::string & theExternalName)
154 fillAttribute(ModelHighAPI_Selection("EDGE", theExternalName), external());
158 static CompositeFeaturePtr sketchForFeature(FeaturePtr theFeature)
160 const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
161 for (std::set<AttributePtr>::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt)
162 if ((*anIt)->id() == SketchPlugin_Sketch::FEATURES_ID())
163 return std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
164 return CompositeFeaturePtr();
167 static void createInternalConstraint(const CompositeFeaturePtr& theSketch,
168 const AttributePoint2DPtr& thePoint,
169 const AttributePoint2DArrayPtr& thePoles,
170 const int thePoleIndex)
172 FeaturePtr aConstraint = theSketch->addFeature(SketchPlugin_ConstraintCoincidenceInternal::ID());
173 aConstraint->refattr(SketchPlugin_Constraint::ENTITY_A())->setAttr(thePoint);
174 aConstraint->refattr(SketchPlugin_Constraint::ENTITY_B())->setAttr(thePoles);
175 aConstraint->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B())
176 ->setValue(thePoleIndex);
177 aConstraint->execute();
180 static void createPole(const CompositeFeaturePtr& theSketch,
181 const FeaturePtr& theBSpline,
182 const AttributePoint2DArrayPtr& thePoles,
183 const int thePoleIndex,
184 const bool theAuxiliary,
185 std::list<FeaturePtr>& theEntities)
187 GeomPnt2dPtr aPole = thePoles->pnt(thePoleIndex);
189 FeaturePtr aPointFeature = theSketch->addFeature(SketchPlugin_Point::ID());
190 AttributePoint2DPtr aCoord = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
191 aPointFeature->attribute(SketchPlugin_Point::COORD_ID()));
192 aCoord->setValue(aPole);
193 aPointFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(theBSpline);
194 aPointFeature->execute();
196 std::ostringstream aName;
197 aName << theBSpline->name() << "_" << thePoles->id() << "_" << thePoleIndex;
198 aPointFeature->data()->setName(aName.str());
199 aPointFeature->lastResult()->data()->setName(aName.str());
201 aPointFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(theAuxiliary);
203 createInternalConstraint(theSketch, aCoord, thePoles, thePoleIndex);
205 theEntities.push_back(aPointFeature);
208 static void createSegment(const CompositeFeaturePtr& theSketch,
209 const FeaturePtr& theBSpline,
210 const AttributePoint2DArrayPtr& thePoles,
211 const int theStartPoleIndex,
212 const bool theAuxiliary,
213 std::list<FeaturePtr>& theEntities)
215 GeomPnt2dPtr aStartPoint = thePoles->pnt(theStartPoleIndex);
216 GeomPnt2dPtr aEndPoint = thePoles->pnt(theStartPoleIndex + 1);
218 FeaturePtr aLineFeature = theSketch->addFeature(SketchPlugin_Line::ID());
219 AttributePoint2DPtr aLineStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
220 aLineFeature->attribute(SketchPlugin_Line::START_ID()));
221 aLineStart->setValue(aStartPoint);
222 AttributePoint2DPtr aLineEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
223 aLineFeature->attribute(SketchPlugin_Line::END_ID()));
224 aLineEnd->setValue(aEndPoint);
225 aLineFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(theBSpline);
226 aLineFeature->execute();
228 std::ostringstream aName;
229 aName << theBSpline->name() << "_segment_" << theStartPoleIndex << "_" << theStartPoleIndex + 1;
230 aLineFeature->data()->setName(aName.str());
231 aLineFeature->lastResult()->data()->setName(aName.str());
233 aLineFeature->boolean(SketchPlugin_Line::AUXILIARY_ID())->setValue(theAuxiliary);
235 createInternalConstraint(theSketch, aLineStart, thePoles, theStartPoleIndex);
236 createInternalConstraint(theSketch, aLineEnd, thePoles, theStartPoleIndex + 1);
238 theEntities.push_back(aLineFeature);
241 static void toMapOfAuxIndices(const std::list<int>& theRegular,
242 const std::list<int>& theAuxiliary,
243 std::map<int, bool>& theIndices)
245 for (auto it = theRegular.begin(); it != theRegular.end(); ++it)
246 theIndices[*it] = false;
247 for (auto it = theAuxiliary.begin(); it != theAuxiliary.end(); ++it)
248 theIndices[*it] = true;
251 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_BSpline::controlPoles(
252 const std::list<int>& regular,
253 const std::list<int>& auxiliary) const
255 std::map<int, bool> anAux;
256 toMapOfAuxIndices(regular, auxiliary, anAux);
258 std::list<FeaturePtr> anEntities;
260 FeaturePtr aBSpline = feature();
261 CompositeFeaturePtr aSketch = sketchForFeature(aBSpline);
262 AttributePoint2DArrayPtr aPoles = poles();
264 for (auto it = anAux.begin(); it != anAux.end(); ++it)
265 createPole(aSketch, aBSpline, aPoles, it->first, it->second, anEntities);
267 return SketchAPI_SketchEntity::wrap(anEntities);
270 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_BSpline::controlPolygon(
271 const std::list<int>& regular,
272 const std::list<int>& auxiliary) const
274 std::map<int, bool> anAux;
275 toMapOfAuxIndices(regular, auxiliary, anAux);
277 std::list<FeaturePtr> anEntities;
279 FeaturePtr aBSpline = feature();
280 CompositeFeaturePtr aSketch = sketchForFeature(aBSpline);
281 AttributePoint2DArrayPtr aPoles = poles();
283 for (auto it = anAux.begin(); it != anAux.end(); ++it)
284 createSegment(aSketch, aBSpline, aPoles, it->first, it->second, anEntities);
286 return SketchAPI_SketchEntity::wrap(anEntities);
290 void SketchAPI_BSpline::getDefaultParameters(
291 const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
292 const std::list<ModelHighAPI_Double>& theWeights,
293 ModelHighAPI_Integer& theDegree,
294 std::list<ModelHighAPI_Double>& theKnots,
295 std::list<ModelHighAPI_Integer>& theMults) const
297 std::shared_ptr<GeomAPI_BSpline2d> aBSplineCurve;
299 std::list<double> aWeights;
300 for (std::list<ModelHighAPI_Double>::const_iterator it = theWeights.begin();
301 it != theWeights.end(); ++it)
302 aWeights.push_back(it->value());
304 if (theDegree.intValue() < 0)
305 aBSplineCurve.reset(new GeomAPI_BSpline2d(thePoles, aWeights));
307 aBSplineCurve.reset(new GeomAPI_BSpline2d(theDegree.intValue(), thePoles, aWeights));
310 // cannot build a B-spline curve
314 theDegree = aBSplineCurve->degree();
315 std::list<double> aKnots = aBSplineCurve->knots();
316 std::list<int> aMults = aBSplineCurve->mults();
317 theKnots.assign(aKnots.begin(), aKnots.end());
318 theMults.assign(aMults.begin(), aMults.end());
321 void SketchAPI_BSpline::checkDefaultParameters(bool& isDefaultDegree,
322 bool& isDefaultWeights,
323 bool& isDefaultKnotsMults) const
325 static const double TOLERANCE = 1.e-7;
327 AttributePoint2DArrayPtr aPolesAttr = poles();
328 AttributeDoubleArrayPtr aWeightsAttr = weights();
329 AttributeDoubleArrayPtr aKnotsAttr = knots();
330 AttributeIntArrayPtr aMultsAttr = multiplicities();
332 std::list<GeomPnt2dPtr> aPoles;
333 std::list<ModelHighAPI_Double> aWeights;
334 isDefaultWeights = true;
335 for (int anIndex = 0; anIndex < aPolesAttr->size(); ++anIndex) {
336 aPoles.push_back(aPolesAttr->pnt(anIndex));
337 double aCurWeight = aWeightsAttr->value(anIndex);
338 isDefaultWeights = isDefaultWeights && fabs(aCurWeight - 1.0) < TOLERANCE;
339 aWeights.push_back(aCurWeight);
342 ModelHighAPI_Integer aDegree(-1);
343 std::list<ModelHighAPI_Double> aKnots;
344 std::list<ModelHighAPI_Integer> aMults;
345 getDefaultParameters(aPoles, aWeights, aDegree, aKnots, aMults);
346 isDefaultDegree = aDegree.intValue() == degree()->value();
347 if (!isDefaultDegree) {
348 // recalculate knots and multiplicities with the actual degree
349 aDegree = degree()->value();
350 getDefaultParameters(aPoles, aWeights, aDegree, aKnots, aMults);
353 isDefaultKnotsMults = aKnotsAttr->size() == (int)aKnots.size()
354 && aMultsAttr->size() == (int)aMults.size();
355 if (isDefaultKnotsMults) {
356 std::list<ModelHighAPI_Double>::iterator anIt = aKnots.begin();
357 for (int anIndex = 0; isDefaultKnotsMults && anIt != aKnots.end(); ++anIt, ++anIndex)
358 isDefaultKnotsMults = fabs(anIt->value() - aKnotsAttr->value(anIndex)) < TOLERANCE;
360 if (isDefaultKnotsMults) {
361 std::list<ModelHighAPI_Integer>::iterator anIt = aMults.begin();
362 for (int anIndex = 0; isDefaultKnotsMults && anIt != aMults.end(); ++anIt, ++anIndex)
363 isDefaultKnotsMults = anIt->intValue() == aMultsAttr->value(anIndex);
366 isDefaultDegree = isDefaultDegree && isDefaultKnotsMults;
367 isDefaultWeights = isDefaultWeights && isDefaultKnotsMults;
371 static void bsplineAuxiliaryFeature(const AttributeRefAttrPtr& theReference,
372 FeaturePtr& thePoint,
373 FeaturePtr& theSegment)
375 ObjectPtr anAuxObject;
376 if (theReference->isObject())
377 anAuxObject = theReference->object();
379 anAuxObject = theReference->attr()->owner();
381 FeaturePtr anAuxFeature = ModelAPI_Feature::feature(anAuxObject);
382 if (anAuxFeature->getKind() == SketchPlugin_Point::ID())
383 thePoint = anAuxFeature;
384 else if (anAuxFeature->getKind() == SketchPlugin_Line::ID() &&
385 theReference->attr()->id() == SketchPlugin_Line::START_ID()) {
386 // process only coincidence with start point
387 theSegment = anAuxFeature;
391 static void collectAuxiliaryFeatures(FeaturePtr theBSpline,
392 std::map<int, FeaturePtr>& thePoints,
393 std::map<int, FeaturePtr>& theSegments)
395 const std::set<AttributePtr>& aRefs = theBSpline->data()->refsToMe();
396 for (std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
397 aRefIt != aRefs.end(); ++aRefIt) {
398 FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
399 if (anOwner->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) {
400 // process internal constraints only
401 AttributeRefAttrPtr aRefAttrA = anOwner->refattr(SketchPlugin_Constraint::ENTITY_A());
402 AttributeRefAttrPtr aRefAttrB = anOwner->refattr(SketchPlugin_Constraint::ENTITY_B());
403 AttributePtr anAttrA = aRefAttrA->attr();
404 AttributePtr anAttrB = aRefAttrB->attr();
406 AttributeIntegerPtr aPoleIndex;
407 FeaturePtr aPoint, aLine;
408 if (anAttrA && anAttrA->attributeType() == GeomDataAPI_Point2DArray::typeId()) {
409 aPoleIndex = anOwner->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A());
410 bsplineAuxiliaryFeature(aRefAttrB, aPoint, aLine);
412 else if (anAttrB && anAttrB->attributeType() == GeomDataAPI_Point2DArray::typeId()) {
413 aPoleIndex = anOwner->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
414 bsplineAuxiliaryFeature(aRefAttrA, aPoint, aLine);
418 thePoints[aPoleIndex->value()] = aPoint;
420 theSegments[aPoleIndex->value()] = aLine;
425 void SketchAPI_BSpline::dump(ModelHighAPI_Dumper& theDumper) const
428 return; // no need to dump copied feature
430 FeaturePtr aBase = feature();
431 const std::string& aSketchName = theDumper.parentName(aBase);
433 AttributeSelectionPtr anExternal = aBase->selection(SketchPlugin_SketchEntity::EXTERNAL_ID());
434 if (anExternal->context()) {
435 // B-spline is external
436 theDumper << aBase << " = " << aSketchName << ".addSpline(" << anExternal << ")" << std::endl;
438 // check if some B-spline parameters are default and should not be dumped
439 bool isDefaultDegree, isDefaultWeights, isDefaultKnotsMults;
440 checkDefaultParameters(isDefaultDegree, isDefaultWeights, isDefaultKnotsMults);
442 theDumper << aBase << " = " << aSketchName << ".addSpline(";
443 if (!isDefaultDegree)
444 theDumper << degree() << ", ";
445 theDumper << poles();
446 if (!isDefaultWeights)
447 theDumper << ", " << weights();
448 if (!isDefaultKnotsMults)
449 theDumper << ", " << knots() << ", " << multiplicities();
450 theDumper << ")" << std::endl;
452 // dump "auxiliary" flag if necessary
453 SketchAPI_SketchEntity::dump(theDumper);
455 // dump control polygon
456 std::map<int, FeaturePtr> anAuxPoles, anAuxSegments;
457 collectAuxiliaryFeatures(aBase, anAuxPoles, anAuxSegments);
459 if (!anAuxPoles.empty())
460 dumpControlPolygon(theDumper, aBase, "controlPoles", anAuxPoles);
461 if (!anAuxSegments.empty())
462 dumpControlPolygon(theDumper, aBase, "controlPolygon", anAuxSegments);
465 static void dumpList(ModelHighAPI_Dumper& theDumper,
466 const std::string& theAttrName,
467 const std::set<int>& theIndices)
469 theDumper << theAttrName << " = [";
470 std::set<int>::const_iterator it = theIndices.begin();
472 for (++it; it != theIndices.end(); ++it)
473 theDumper << ", " << *it;
477 void SketchAPI_BSpline::dumpControlPolygon(
478 ModelHighAPI_Dumper& theDumper,
479 const FeaturePtr& theBSpline,
480 const std::string& theMethod,
481 const std::map<int, FeaturePtr>& theAuxFeatures) const
485 // dump features and split them to auxiliary and regular
486 std::set<int> aRegular, anAuxiliary;
487 for (std::map<int, FeaturePtr>::const_iterator it = theAuxFeatures.begin();
488 it != theAuxFeatures.end(); ++it) {
491 theDumper << theDumper.name(it->second, false);
492 theDumper.doNotDumpFeature(it->second);
495 if (it->second->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
496 anAuxiliary.insert(it->first);
498 aRegular.insert(it->first);
500 theDumper << "] = " << theDumper.name(theBSpline) << "." << theMethod << "(";
501 if (!aRegular.empty()) {
502 dumpList(theDumper, "regular", aRegular);
503 if (!anAuxiliary.empty())
506 if (!anAuxiliary.empty())
507 dumpList(theDumper, "auxiliary", anAuxiliary);
508 theDumper << ")" << std::endl;