Salome HOME
updated copyright message
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_MacroBSpline.cpp
1 // Copyright (C) 2019-2023  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 <SketchPlugin_MacroBSpline.h>
21
22 #include <SketchPlugin_BSpline.h>
23 #include <SketchPlugin_BSplinePeriodic.h>
24 #include <SketchPlugin_ConstraintCoincidenceInternal.h>
25 #include <SketchPlugin_Line.h>
26 #include <SketchPlugin_Point.h>
27 #include <SketchPlugin_Tools.h>
28 #include <SketchPlugin_Sketch.h>
29
30 #include <Locale_Convert.h>
31
32 #include <ModelAPI_AttributeDoubleArray.h>
33 #include <ModelAPI_AttributeInteger.h>
34 #include <ModelAPI_AttributeRefAttrList.h>
35 #include <ModelAPI_Events.h>
36 #include <ModelAPI_Session.h>
37 #include <ModelAPI_Validator.h>
38 #include <ModelAPI_Tools.h>
39
40 #include <GeomDataAPI_Point2DArray.h>
41
42 #include <GeomAlgoAPI_CompoundBuilder.h>
43 #include <GeomAlgoAPI_EdgeBuilder.h>
44 #include <GeomAlgoAPI_PointBuilder.h>
45
46 #include <GeomAPI_BSpline2d.h>
47
48 #include <sstream>
49
50 /// Create internal coincidence constraint with B-spline pole
51 static void createInternalConstraint(SketchPlugin_Sketch* theSketch,
52                                      AttributePtr thePoint,
53                                      AttributePtr theBSplinePoles,
54                                      const int thePoleIndex);
55
56
57 SketchPlugin_MacroBSpline::SketchPlugin_MacroBSpline()
58   : SketchPlugin_SketchEntity(),
59     myDegree(3),
60     myIsPeriodic(false)
61 {
62 }
63
64 SketchPlugin_MacroBSpline::SketchPlugin_MacroBSpline(bool isPeriodic)
65   : SketchPlugin_SketchEntity(),
66     myDegree(3),
67     myIsPeriodic(isPeriodic)
68 {
69 }
70
71 void SketchPlugin_MacroBSpline::initAttributes()
72 {
73   data()->addAttribute(POLES_ID(), GeomDataAPI_Point2DArray::typeId());
74   data()->addAttribute(WEIGHTS_ID(), ModelAPI_AttributeDoubleArray::typeId());
75
76   data()->addAttribute(REF_POLES_ID(), ModelAPI_AttributeRefAttrList::typeId());
77   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), REF_POLES_ID());
78
79   data()->addAttribute(CONTROL_POLYGON_ID(), ModelAPI_AttributeBoolean::typeId());
80
81   data()->addAttribute(AUXILIARY_ID(), ModelAPI_AttributeBoolean::typeId());
82 }
83
84 void SketchPlugin_MacroBSpline::execute()
85 {
86   FeaturePtr aBSpline = createBSplineFeature();
87
88   if (boolean(CONTROL_POLYGON_ID())->value()) {
89     std::list<FeaturePtr> aControlPoles;
90     createControlPolygon(aBSpline, myIsPeriodic, aControlPoles);
91     constraintsForPoles(aControlPoles);
92   }
93 }
94
95 FeaturePtr SketchPlugin_MacroBSpline::createBSplineFeature()
96 {
97   if (myKnots.empty() || myMultiplicities.empty())
98     getAISObject(AISObjectPtr()); // fill B-spline parameters
99
100   FeaturePtr aBSpline = sketch()->addFeature(
101       myIsPeriodic ? SketchPlugin_BSplinePeriodic::ID() : SketchPlugin_BSpline::ID());
102
103   aBSpline->integer(SketchPlugin_BSplineBase::DEGREE_ID())->setValue(myDegree);
104
105   AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
106       aBSpline->attribute(SketchPlugin_BSplineBase::POLES_ID()));
107   AttributePoint2DArrayPtr aPolesMacro =
108       std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(attribute(POLES_ID()));
109   aPoles->assign(aPolesMacro);
110
111   AttributeDoubleArrayPtr aWeights =
112       aBSpline->data()->realArray(SketchPlugin_BSplineBase::WEIGHTS_ID());
113   AttributeDoubleArrayPtr aWeightsMacro = data()->realArray(WEIGHTS_ID());
114   int aSize = aWeightsMacro->size();
115   aWeights->setSize(aSize);
116   for (int index = 0; index < aSize; ++index)
117     aWeights->setValue(index, aWeightsMacro->value(index));
118
119   AttributeDoubleArrayPtr aKnots =
120       aBSpline->data()->realArray(SketchPlugin_BSplineBase::KNOTS_ID());
121   aSize = (int)myKnots.size();
122   aKnots->setSize(aSize);
123   std::list<double>::iterator aKIt = myKnots.begin();
124   for (int index = 0; index < aSize; ++index, ++aKIt)
125     aKnots->setValue(index, *aKIt);
126
127   AttributeIntArrayPtr aMults = aBSpline->data()->intArray(SketchPlugin_BSplineBase::MULTS_ID());
128   aSize = (int)myMultiplicities.size();
129   aMults->setSize(aSize);
130   std::list<int>::iterator aMIt = myMultiplicities.begin();
131   for (int index = 0; index < aSize; ++index, ++aMIt)
132     aMults->setValue(index, *aMIt);
133
134   if (!myIsPeriodic) {
135     AttributePoint2DPtr aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
136         aBSpline->attribute(SketchPlugin_BSpline::START_ID()));
137     aStartPoint->setValue(aPoles->pnt(0));
138
139     AttributePoint2DPtr aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
140        aBSpline->attribute(SketchPlugin_BSpline::END_ID()));
141     aEndPoint->setValue(aPoles->pnt(aPoles->size() - 1));
142   }
143
144   aBSpline->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(
145       boolean(AUXILIARY_ID())->value());
146
147   aBSpline->execute();
148
149   return aBSpline;
150 }
151
152 void SketchPlugin_MacroBSpline::createControlPolygon(FeaturePtr theBSpline,
153                                                      bool thePeriodic,
154                                                      std::list<FeaturePtr>& thePoles)
155 {
156   AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
157       theBSpline->attribute(SketchPlugin_BSpline::POLES_ID()));
158   int aSize = aPoles->size();
159   // poles
160   for (int index = 0; index < aSize; ++index)
161     thePoles.push_back(createAuxiliaryPole(aPoles, index));
162   // segments
163   for (int index = 1; index < aSize; ++index)
164     createAuxiliarySegment(aPoles, index - 1, index);
165   if (thePeriodic) {
166     // additional segment to close the control polygon
167     createAuxiliarySegment(aPoles, aSize - 1, 0);
168   }
169 }
170
171 void SketchPlugin_MacroBSpline::constraintsForPoles(const std::list<FeaturePtr>& thePoles)
172 {
173   AttributeRefAttrListPtr aRefAttrList = data()->refattrlist(REF_POLES_ID());
174   std::list<std::pair<ObjectPtr, AttributePtr> > aList;
175   if (aRefAttrList)
176     aList = aRefAttrList->list();
177
178   SketchPlugin_Sketch* aSketch = sketch();
179
180   std::list<std::pair<ObjectPtr, AttributePtr> >::iterator aLIt = aList.begin();
181   std::list<FeaturePtr>::const_iterator aPIt = thePoles.begin();
182   for (; aLIt != aList.end() && aPIt != thePoles.end(); ++aPIt, ++aLIt) {
183     // firstly, check the attribute (in this case the object will be not empty too)
184     if (aLIt->second) {
185       SketchPlugin_Tools::createConstraintAttrAttr(aSketch,
186           SketchPlugin_ConstraintCoincidence::ID(),
187           (*aPIt)->attribute(SketchPlugin_Point::COORD_ID()), aLIt->second);
188     }
189     // now add coincidence with the result
190     else if (aLIt->first) {
191       SketchPlugin_Tools::createConstraintAttrObject(aSketch,
192           SketchPlugin_ConstraintCoincidence::ID(),
193           (*aPIt)->attribute(SketchPlugin_Point::COORD_ID()), aLIt->first);
194     }
195   }
196 }
197
198 AISObjectPtr SketchPlugin_MacroBSpline::getAISObject(AISObjectPtr thePrevious)
199 {
200   SketchPlugin_Sketch* aSketch = sketch();
201   if (!aSketch)
202     return AISObjectPtr();
203
204   AttributePoint2DArrayPtr aPolesArray =
205       std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(attribute(POLES_ID()));
206   AttributeDoubleArrayPtr aWeightsArray = data()->realArray(WEIGHTS_ID());
207
208   if (aPolesArray->size() < 2)
209     return AISObjectPtr();
210
211   std::list<GeomShapePtr> aShapes;
212
213   // convert poles to vertices and collect weights
214   std::list<GeomPnt2dPtr> aPoles2D;
215   std::list<double> aWeights;
216   for (int anIndex = 0; anIndex < aPolesArray->size(); ++anIndex) {
217     double aWeight = aWeightsArray->value(anIndex);
218     if (aWeight < 1.e-10)
219       continue; // skip poles with zero weights
220
221     aWeights.push_back(aWeight);
222
223     GeomPnt2dPtr aPole = aPolesArray->pnt(anIndex);
224     aPoles2D.push_back(aPole);
225     GeomPointPtr aPole3D = aSketch->to3D(aPole->x(), aPole->y());
226     aShapes.push_back(GeomAlgoAPI_PointBuilder::vertex(aPole3D));
227   }
228
229   // create result non-periodic B-spline curve
230   std::shared_ptr<GeomAPI_BSpline2d> aBSplineCurve;
231   try {
232     aBSplineCurve.reset(new GeomAPI_BSpline2d(aPoles2D, aWeights, myIsPeriodic));
233   } catch (...) {
234     // cannot build a B-spline curve
235     return AISObjectPtr();
236   }
237   GeomShapePtr anEdge =
238       GeomAlgoAPI_EdgeBuilder::bsplineOnPlane(aSketch->coordinatePlane(), aBSplineCurve);
239   if (!anEdge)
240     return AISObjectPtr();
241
242   // store transient parameters of B-spline curve
243   myDegree = aBSplineCurve->degree();
244   myKnots = aBSplineCurve->knots();
245   myMultiplicities = aBSplineCurve->mults();
246
247   aShapes.push_back(anEdge);
248   GeomShapePtr aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
249
250   AISObjectPtr anAIS = thePrevious;
251   if (!anAIS)
252     anAIS.reset(new GeomAPI_AISObject());
253   anAIS->createShape(aCompound);
254
255   // Modify attributes
256   SketchPlugin_Tools::customizeFeaturePrs(anAIS, boolean(AUXILIARY_ID())->value());
257
258   return anAIS;
259 }
260
261
262
263 // ==========================     Auxiliary functions    ===========================================
264
265 void SketchPlugin_MacroBSpline::assignDefaultNameForAux(FeaturePtr theAuxFeature,
266                                                         AttributePoint2DArrayPtr theBSplinePoles,
267                                                         const int thePoleIndex1,
268                                                         const int thePoleIndex2)
269 {
270   FeaturePtr aBSpline = ModelAPI_Feature::feature(theBSplinePoles->owner());
271
272   std::wostringstream aName;
273   aName << aBSpline->name();
274   if (theAuxFeature->getKind() == SketchPlugin_Point::ID())
275     aName << "_" << Locale::Convert::toWString(theBSplinePoles->id()) << "_" << thePoleIndex1;
276   else
277     aName << "_segment_" << thePoleIndex1 << "_" << thePoleIndex2;
278
279   theAuxFeature->data()->setName(aName.str());
280   theAuxFeature->lastResult()->data()->setName(aName.str());
281 }
282
283 FeaturePtr SketchPlugin_MacroBSpline::createAuxiliaryPole(AttributePoint2DArrayPtr theBSplinePoles,
284                                                           const int thePoleIndex)
285 {
286   FeaturePtr aBSpline = ModelAPI_Feature::feature(theBSplinePoles->owner());
287
288   SketchPlugin_Sketch* aSketch =
289       std::dynamic_pointer_cast<SketchPlugin_Feature>(aBSpline)->sketch();
290
291   // create child point equal to the B-spline's pole
292   FeaturePtr aPointFeature = aSketch->addFeature(SketchPlugin_Point::ID());
293   aPointFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(true);
294   aPointFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(aBSpline);
295
296   GeomPnt2dPtr aPole = theBSplinePoles->pnt(thePoleIndex);
297
298   AttributePoint2DPtr aCoord = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
299       aPointFeature->attribute(SketchPlugin_Point::COORD_ID()));
300   aCoord->setValue(aPole);
301
302   aPointFeature->execute();
303   assignDefaultNameForAux(aPointFeature, theBSplinePoles, thePoleIndex);
304
305   // internal constraint to keep position of the point
306   createInternalConstraint(aSketch, aCoord, theBSplinePoles, thePoleIndex);
307
308   return aPointFeature;
309 }
310
311 void SketchPlugin_MacroBSpline::createAuxiliarySegment(AttributePoint2DArrayPtr theBSplinePoles,
312                                                        const int thePoleIndex1,
313                                                        const int thePoleIndex2)
314 {
315   FeaturePtr aBSpline = ModelAPI_Feature::feature(theBSplinePoles->owner());
316
317   SketchPlugin_Sketch* aSketch =
318       std::dynamic_pointer_cast<SketchPlugin_Feature>(aBSpline)->sketch();
319
320   // create child segment between B-spline poles
321   FeaturePtr aLineFeature = aSketch->addFeature(SketchPlugin_Line::ID());
322   aLineFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(true);
323   aLineFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(aBSpline);
324
325   AttributePoint2DPtr aLineStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
326     aLineFeature->attribute(SketchPlugin_Line::START_ID()));
327   aLineStart->setValue(theBSplinePoles->pnt(thePoleIndex1));
328
329   AttributePoint2DPtr aLineEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
330     aLineFeature->attribute(SketchPlugin_Line::END_ID()));
331   aLineEnd->setValue(theBSplinePoles->pnt(thePoleIndex2));
332
333   aLineFeature->execute();
334   assignDefaultNameForAux(aLineFeature, theBSplinePoles, thePoleIndex1, thePoleIndex2);
335
336   // internal constraints to keep the segment position
337   createInternalConstraint(aSketch, aLineStart, theBSplinePoles, thePoleIndex1);
338   createInternalConstraint(aSketch, aLineEnd, theBSplinePoles, thePoleIndex2);
339 }
340
341 void createInternalConstraint(SketchPlugin_Sketch* theSketch,
342                               AttributePtr thePoint,
343                               AttributePtr theBSplinePoles,
344                               const int thePoleIndex)
345 {
346   std::shared_ptr<SketchPlugin_ConstraintCoincidenceInternal> aConstraint =
347       std::dynamic_pointer_cast<SketchPlugin_ConstraintCoincidenceInternal>(
348       theSketch->addFeature(SketchPlugin_ConstraintCoincidenceInternal::ID()));
349   aConstraint->refattr(SketchPlugin_Constraint::ENTITY_A())->setAttr(thePoint);
350   aConstraint->refattr(SketchPlugin_Constraint::ENTITY_B())->setAttr(theBSplinePoles);
351   aConstraint->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B())
352       ->setValue(thePoleIndex);
353 }