Salome HOME
9023d703bf3ccbd65b8551cc8731226b6bebed3c
[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 <SketchPlugin_ConstraintCoincidenceInternal.h>
34 #include <SketchPlugin_Line.h>
35 #include <SketchPlugin_Point.h>
36
37 #include <cmath>
38
39
40 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature> & theFeature)
41   : SketchAPI_SketchEntity(theFeature)
42 {
43   initialize();
44 }
45
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)
50 {
51   if (initialize()) {
52     setByDegreePolesAndWeights(ModelHighAPI_Integer(-1), thePoles, theWeights);
53   }
54 }
55
56 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature,
57                                      const int theDegree,
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)
63 {
64   if (initialize()) {
65     if (theKnots.empty() || theMults.empty())
66       setByDegreePolesAndWeights(theDegree, thePoles, theWeights);
67     else
68       setByParameters(theDegree, thePoles, theWeights, theKnots, theMults);
69   }
70 }
71
72 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature,
73                                      const ModelHighAPI_Selection& theExternal)
74   : SketchAPI_SketchEntity(theFeature)
75 {
76   if (initialize()) {
77     setByExternal(theExternal);
78   }
79 }
80
81 SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature,
82                                      const std::string& theExternalName)
83   : SketchAPI_SketchEntity(theFeature)
84 {
85   if (initialize()) {
86     setByExternalName(theExternalName);
87   }
88 }
89
90 SketchAPI_BSpline::~SketchAPI_BSpline()
91 {
92 }
93
94 void SketchAPI_BSpline::setByDegreePolesAndWeights(const ModelHighAPI_Integer& theDegree,
95                                                    const std::list<GeomPnt2dPtr>& thePoles,
96                                                    const std::list<ModelHighAPI_Double>& theWeights)
97 {
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());
103   }
104   else
105     aWeights = theWeights;
106
107   ModelHighAPI_Integer aDegree = theDegree;
108   std::list<ModelHighAPI_Double> aKnots;
109   std::list<ModelHighAPI_Integer> aMults;
110   getDefaultParameters(thePoles, aWeights, aDegree, aKnots, aMults);
111
112   setByParameters(aDegree, thePoles, aWeights, aKnots, aMults);
113 }
114
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)
120 {
121   fillAttribute(theDegree, degree());
122
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());
129   }
130   else
131     fillAttribute(theWeights, weights());
132
133   fillAttribute(theKnots, knots());
134   fillAttribute(theMults, multiplicities());
135
136   setStartAndEndPoints();
137   execute();
138 }
139
140 void SketchAPI_BSpline::setStartAndEndPoints()
141 {
142   fillAttribute(poles()->pnt(0), startPoint());
143   fillAttribute(poles()->pnt(poles()->size() - 1), endPoint());
144 }
145
146 void SketchAPI_BSpline::setByExternal(const ModelHighAPI_Selection & theExternal)
147 {
148   fillAttribute(theExternal, external());
149   execute();
150 }
151
152 void SketchAPI_BSpline::setByExternalName(const std::string & theExternalName)
153 {
154   fillAttribute(ModelHighAPI_Selection("EDGE", theExternalName), external());
155   execute();
156 }
157
158 static CompositeFeaturePtr sketchForFeature(FeaturePtr theFeature)
159 {
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();
165 }
166
167 static void createInternalConstraint(const CompositeFeaturePtr& theSketch,
168                                      const AttributePoint2DPtr& thePoint,
169                                      const AttributePoint2DArrayPtr& thePoles,
170                                      const int thePoleIndex)
171 {
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();
178 }
179
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)
186 {
187   GeomPnt2dPtr aPole = thePoles->pnt(thePoleIndex);
188
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();
195
196   std::ostringstream aName;
197   aName << theBSpline->name() << "_" << thePoles->id() << "_" << thePoleIndex;
198   aPointFeature->data()->setName(aName.str());
199   aPointFeature->lastResult()->data()->setName(aName.str());
200
201   aPointFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(theAuxiliary);
202
203   createInternalConstraint(theSketch, aCoord, thePoles, thePoleIndex);
204
205   theEntities.push_back(aPointFeature);
206 }
207
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)
214 {
215   GeomPnt2dPtr aStartPoint = thePoles->pnt(theStartPoleIndex);
216   GeomPnt2dPtr aEndPoint = thePoles->pnt(theStartPoleIndex + 1);
217
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();
227
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());
232
233   aLineFeature->boolean(SketchPlugin_Line::AUXILIARY_ID())->setValue(theAuxiliary);
234
235   createInternalConstraint(theSketch, aLineStart, thePoles, theStartPoleIndex);
236   createInternalConstraint(theSketch, aLineEnd, thePoles, theStartPoleIndex + 1);
237
238   theEntities.push_back(aLineFeature);
239 }
240
241 static void toMapOfAuxIndices(const std::list<int>& theRegular,
242                               const std::list<int>& theAuxiliary,
243                               std::map<int, bool>& theIndices)
244 {
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;
249 }
250
251 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_BSpline::controlPoles(
252     const std::list<int>& regular,
253     const std::list<int>& auxiliary) const
254 {
255   std::map<int, bool> anAux;
256   toMapOfAuxIndices(regular, auxiliary, anAux);
257
258   std::list<FeaturePtr> anEntities;
259
260   FeaturePtr aBSpline = feature();
261   CompositeFeaturePtr aSketch = sketchForFeature(aBSpline);
262   AttributePoint2DArrayPtr aPoles = poles();
263
264   for (auto it = anAux.begin(); it != anAux.end(); ++it)
265     createPole(aSketch, aBSpline, aPoles, it->first, it->second, anEntities);
266
267   return SketchAPI_SketchEntity::wrap(anEntities);
268 }
269
270 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_BSpline::controlPolygon(
271     const std::list<int>& regular,
272     const std::list<int>& auxiliary) const
273 {
274   std::map<int, bool> anAux;
275   toMapOfAuxIndices(regular, auxiliary, anAux);
276
277   std::list<FeaturePtr> anEntities;
278
279   FeaturePtr aBSpline = feature();
280   CompositeFeaturePtr aSketch = sketchForFeature(aBSpline);
281   AttributePoint2DArrayPtr aPoles = poles();
282
283   for (auto it = anAux.begin(); it != anAux.end(); ++it)
284     createSegment(aSketch, aBSpline, aPoles, it->first, it->second, anEntities);
285
286   return SketchAPI_SketchEntity::wrap(anEntities);
287 }
288
289
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
296 {
297   std::shared_ptr<GeomAPI_BSpline2d> aBSplineCurve;
298   try {
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());
303
304     if (theDegree.intValue() < 0)
305       aBSplineCurve.reset(new GeomAPI_BSpline2d(thePoles, aWeights));
306     else
307       aBSplineCurve.reset(new GeomAPI_BSpline2d(theDegree.intValue(), thePoles, aWeights));
308   }
309   catch (...) {
310     // cannot build a B-spline curve
311     return;
312   }
313
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());
319 }
320
321 void SketchAPI_BSpline::checkDefaultParameters(bool& isDefaultDegree,
322                                                bool& isDefaultWeights,
323                                                bool& isDefaultKnotsMults) const
324 {
325   static const double TOLERANCE = 1.e-7;
326
327   AttributePoint2DArrayPtr aPolesAttr = poles();
328   AttributeDoubleArrayPtr aWeightsAttr = weights();
329   AttributeDoubleArrayPtr aKnotsAttr = knots();
330   AttributeIntArrayPtr aMultsAttr = multiplicities();
331
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);
340   }
341
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);
351   }
352
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;
359   }
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);
364   }
365
366   isDefaultDegree = isDefaultDegree && isDefaultKnotsMults;
367   isDefaultWeights = isDefaultWeights && isDefaultKnotsMults;
368 }
369
370
371 static void bsplineAuxiliaryFeature(const AttributeRefAttrPtr& theReference,
372                                     FeaturePtr& thePoint,
373                                     FeaturePtr& theSegment)
374 {
375   ObjectPtr anAuxObject;
376   if (theReference->isObject())
377     anAuxObject = theReference->object();
378   else
379     anAuxObject = theReference->attr()->owner();
380
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;
388   }
389 }
390
391 static void collectAuxiliaryFeatures(FeaturePtr theBSpline,
392                                      std::map<int, FeaturePtr>& thePoints,
393                                      std::map<int, FeaturePtr>& theSegments)
394 {
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();
405
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);
411       }
412       else if (anAttrB && anAttrB->attributeType() == GeomDataAPI_Point2DArray::typeId()) {
413         aPoleIndex = anOwner->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
414         bsplineAuxiliaryFeature(aRefAttrA, aPoint, aLine);
415       }
416
417       if (aPoint)
418         thePoints[aPoleIndex->value()] = aPoint;
419       else if (aLine)
420         theSegments[aPoleIndex->value()] = aLine;
421     }
422   }
423 }
424
425 void SketchAPI_BSpline::dump(ModelHighAPI_Dumper& theDumper) const
426 {
427   if (isCopy())
428     return; // no need to dump copied feature
429
430   FeaturePtr aBase = feature();
431   const std::string& aSketchName = theDumper.parentName(aBase);
432
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;
437   } else {
438     // check if some B-spline parameters are default and should not be dumped
439     bool isDefaultDegree, isDefaultWeights, isDefaultKnotsMults;
440     checkDefaultParameters(isDefaultDegree, isDefaultWeights, isDefaultKnotsMults);
441
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;
451   }
452   // dump "auxiliary" flag if necessary
453   SketchAPI_SketchEntity::dump(theDumper);
454
455   // dump control polygon
456   std::map<int, FeaturePtr> anAuxPoles, anAuxSegments;
457   collectAuxiliaryFeatures(aBase, anAuxPoles, anAuxSegments);
458
459   if (!anAuxPoles.empty())
460     dumpControlPolygon(theDumper, aBase, "controlPoles", anAuxPoles);
461   if (!anAuxSegments.empty())
462     dumpControlPolygon(theDumper, aBase, "controlPolygon", anAuxSegments);
463 }
464
465 static void dumpList(ModelHighAPI_Dumper& theDumper,
466                      const std::string& theAttrName,
467                      const std::set<int>& theIndices)
468 {
469   theDumper << theAttrName << " = [";
470   std::set<int>::const_iterator it = theIndices.begin();
471   theDumper << *it;
472   for (++it; it != theIndices.end(); ++it)
473     theDumper << ", " << *it;
474   theDumper << "]";
475 }
476
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
482 {
483   theDumper << "[";
484   bool isFirst = true;
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) {
489     if (!isFirst)
490       theDumper << ", ";
491     theDumper << theDumper.name(it->second, false);
492     theDumper.doNotDumpFeature(it->second);
493     isFirst = false;
494
495     if (it->second->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
496       anAuxiliary.insert(it->first);
497     else
498       aRegular.insert(it->first);
499   }
500   theDumper << "] = " << theDumper.name(theBSpline) << "." << theMethod << "(";
501   if (!aRegular.empty()) {
502     dumpList(theDumper, "regular", aRegular);
503     if (!anAuxiliary.empty())
504       theDumper << ", ";
505   }
506   if (!anAuxiliary.empty())
507     dumpList(theDumper, "auxiliary", anAuxiliary);
508   theDumper << ")" << std::endl;
509 }