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