]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchPlugin/SketchPlugin_BSplineBase.cpp
Salome HOME
Unit test for adding pole to B-spline curve
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_BSplineBase.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 <SketchPlugin_BSplineBase.h>
21
22 #include <SketchPlugin_ConstraintCoincidenceInternal.h>
23 #include <SketchPlugin_Line.h>
24 #include <SketchPlugin_MacroBSpline.h>
25 #include <SketchPlugin_Point.h>
26 #include <SketchPlugin_Sketch.h>
27
28 #include <Events_InfoMessage.h>
29
30 #include <GeomAlgoAPI_EdgeBuilder.h>
31
32 #include <GeomAPI_BSpline2d.h>
33 #include <GeomAPI_Pnt2d.h>
34
35 #include <GeomDataAPI_Point2D.h>
36 #include <GeomDataAPI_Point2DArray.h>
37
38 #include <ModelAPI_AttributeDouble.h>
39 #include <ModelAPI_AttributeDoubleArray.h>
40 #include <ModelAPI_AttributeIntArray.h>
41 #include <ModelAPI_AttributeInteger.h>
42 #include <ModelAPI_ResultConstruction.h>
43 #include <ModelAPI_Session.h>
44 #include <ModelAPI_Validator.h>
45
46
47 SketchPlugin_BSplineBase::SketchPlugin_BSplineBase()
48   : SketchPlugin_SketchEntity()
49 {
50 }
51
52 void SketchPlugin_BSplineBase::initDerivedClassAttributes()
53 {
54   data()->addAttribute(POLES_ID(), GeomDataAPI_Point2DArray::typeId());
55   data()->addAttribute(WEIGHTS_ID(), ModelAPI_AttributeDoubleArray::typeId());
56   data()->addAttribute(KNOTS_ID(), ModelAPI_AttributeDoubleArray::typeId());
57   data()->addAttribute(MULTS_ID(), ModelAPI_AttributeIntArray::typeId());
58   data()->addAttribute(DEGREE_ID(), ModelAPI_AttributeInteger::typeId());
59
60   data()->addAttribute(EXTERNAL_ID(), ModelAPI_AttributeSelection::typeId());
61   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EXTERNAL_ID());
62 }
63
64 void SketchPlugin_BSplineBase::execute()
65 {
66   SketchPlugin_Sketch* aSketch = sketch();
67   if(!aSketch) {
68     return;
69   }
70
71   AttributePoint2DArrayPtr aPolesArray =
72       std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(attribute(POLES_ID()));
73   AttributeDoubleArrayPtr aWeightsArray = data()->realArray(WEIGHTS_ID());
74   AttributeDoubleArrayPtr aKnotsArray = data()->realArray(KNOTS_ID());
75   AttributeIntArrayPtr aMultsArray = data()->intArray(MULTS_ID());
76   AttributeIntegerPtr aDegreeAttr = data()->integer(DEGREE_ID());
77
78   // collect poles
79   std::list<GeomPnt2dPtr> aPoles2D;
80   for (int anIndex = 0; anIndex < aPolesArray->size(); ++anIndex) {
81     GeomPnt2dPtr aPole = aPolesArray->pnt(anIndex);
82     aPoles2D.push_back(aPole);
83   }
84   // collect weights
85   std::list<double> aWeights;
86   for (int anIndex = 0; anIndex < aWeightsArray->size(); ++anIndex)
87     aWeights.push_back(aWeightsArray->value(anIndex));
88   // collect knots
89   std::list<double> aKnots;
90   for (int anIndex = 0; anIndex < aKnotsArray->size(); ++anIndex)
91     aKnots.push_back(aKnotsArray->value(anIndex));
92   // collect multiplicities
93   std::list<int> aMults;
94   for (int anIndex = 0; anIndex < aMultsArray->size(); ++anIndex)
95     aMults.push_back(aMultsArray->value(anIndex));
96
97   // create result B-spline curve
98   GeomShapePtr anEdge = GeomAlgoAPI_EdgeBuilder::bsplineOnPlane(aSketch->coordinatePlane(),
99       aPoles2D, aWeights, aKnots, aMults, aDegreeAttr->value(), isPeriodic());
100
101   ResultConstructionPtr aResult = document()->createConstruction(data(), 0);
102   aResult->setShape(anEdge);
103   aResult->setIsInHistory(false);
104   setResult(aResult, 0);
105 }
106
107 bool SketchPlugin_BSplineBase::isFixed() {
108   return data()->selection(EXTERNAL_ID())->context().get() != NULL;
109 }
110
111 void SketchPlugin_BSplineBase::attributeChanged(const std::string& theID) {
112 }
113
114 bool SketchPlugin_BSplineBase::customAction(const std::string& theActionId)
115 {
116   // parse for the action and an index divided by '#'
117   std::string anAction;
118   int anIndex = -1;
119   size_t pos = theActionId.find('#');
120   if (pos != std::string::npos) {
121     anAction = theActionId.substr(0, pos);
122     anIndex = std::stoi(theActionId.substr(pos + 1));
123   }
124
125   if (anAction == ADD_POLE_ACTION_ID()) {
126     return addPole(anIndex);
127   }
128
129   std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
130   Events_InfoMessage("SketchPlugin_BSplineBase", aMsg).arg(getKind()).arg(theActionId).send();
131   return false;
132 }
133
134 bool SketchPlugin_BSplineBase::addPole(const int theAfter)
135 {
136   AttributePoint2DArrayPtr aPolesArray =
137       std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(attribute(POLES_ID()));
138   AttributeDoubleArrayPtr aWeightsArray = data()->realArray(WEIGHTS_ID());
139
140   int anAfter = (!isPeriodic() && theAfter == aPolesArray->size() - 1) ? theAfter - 1 : theAfter;
141
142   // find internal coincidences applied to the poles with greater indices
143   std::list<AttributeIntegerPtr> aCoincidentPoleIndex;
144   std::map<int, FeaturePtr> aControlPoles, aControlSegments;
145   bool hasAuxSegment = false;
146   const std::set<AttributePtr>& aRefs = data()->refsToMe();
147   for (std::set<AttributePtr>::iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) {
148     FeaturePtr aFeature = ModelAPI_Feature::feature((*anIt)->owner());
149     if (aFeature->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) {
150       AttributeIntegerPtr anIndex;
151       AttributeRefAttrPtr aNonSplinePoint;
152       if ((*anIt)->id() == SketchPlugin_ConstraintCoincidenceInternal::ENTITY_A()) {
153         anIndex = aFeature->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A());
154         aNonSplinePoint = aFeature->refattr(SketchPlugin_Constraint::ENTITY_B());
155       }
156       else if ((*anIt)->id() == SketchPlugin_ConstraintCoincidenceInternal::ENTITY_B()) {
157         anIndex = aFeature->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
158         aNonSplinePoint = aFeature->refattr(SketchPlugin_Constraint::ENTITY_A());
159       }
160
161       if (anIndex && anIndex->isInitialized()) {
162         if (anIndex->value() > anAfter) {
163           aCoincidentPoleIndex.push_back(anIndex);
164           FeaturePtr aParent = ModelAPI_Feature::feature(aNonSplinePoint->attr()->owner());
165           if (aParent->getKind() == SketchPlugin_Point::ID())
166             aControlPoles[anIndex->value()] = aParent;
167           else if (aParent->getKind() == SketchPlugin_Line::ID() &&
168                    aNonSplinePoint->attr()->id() == SketchPlugin_Line::START_ID())
169             aControlSegments[anIndex->value()] = aParent;
170         }
171         else if (anIndex->value() == anAfter && !hasAuxSegment) {
172           // check the constrained object is a segment of the control polygon
173           if (aNonSplinePoint && !aNonSplinePoint->isObject() &&
174               aNonSplinePoint->attr()->id() == SketchPlugin_Line::START_ID()) {
175             hasAuxSegment = true;
176             aCoincidentPoleIndex.push_back(anIndex);
177             aControlSegments[anIndex->value()] =
178                 ModelAPI_Feature::feature(aNonSplinePoint->attr()->owner());
179           }
180         }
181       }
182     }
183   }
184
185   bool aWasBlocked = data()->blockSendAttributeUpdated(true);
186
187   // add new pole and default weight
188   std::list<GeomPnt2dPtr> aPoles;
189   aPolesArray->setSize(aPolesArray->size() + 1);
190   aPolesArray->setPnt(aPolesArray->size() - 1, aPolesArray->pnt(0)); // for periodic spline
191   for (int i = aPolesArray->size() - 2; i > anAfter; --i) {
192     aPoles.push_front(aPolesArray->pnt(i));
193     aPolesArray->setPnt(i + 1, aPoles.front());
194   }
195
196   GeomPnt2dPtr aCurPole = aPolesArray->pnt(anAfter);
197   GeomPnt2dPtr aNextPole = aPolesArray->pnt(anAfter + 1);
198   aPolesArray->setPnt(anAfter + 1, (aCurPole->x() + aNextPole->x()) * 0.5,
199                                    (aCurPole->y() + aNextPole->y()) * 0.5);
200   for (int i = anAfter + 1; i >= 0; --i)
201     aPoles.push_front(aPolesArray->pnt(i));
202
203   std::list<double> aWeights;
204   for (int i = 0; i < aWeightsArray->size(); ++i) {
205     aWeights.push_back(aWeightsArray->value(i));
206     if (i == anAfter)
207       aWeights.push_back(1.0); // default weight
208   }
209   aWeightsArray->setSize(aWeightsArray->size() + 1);
210   std::list<double>::iterator aWIt = aWeights.begin();
211   for (int i = 0; i < aWeightsArray->size(); ++i, ++aWIt)
212     aWeightsArray->setValue(i, *aWIt);
213
214   // recalculate knots and multiplicities
215   std::shared_ptr<GeomAPI_BSpline2d> aBSplineCurve(
216       new GeomAPI_BSpline2d(aPoles, aWeights, isPeriodic()));
217
218   integer(DEGREE_ID())->setValue(aBSplineCurve->degree());
219
220   AttributeDoubleArrayPtr aKnotsAttr = data()->realArray(SketchPlugin_BSplineBase::KNOTS_ID());
221   std::list<double> aKnots = aBSplineCurve->knots();
222   int aSize = (int)aKnots.size();
223   aKnotsAttr->setSize(aSize);
224   std::list<double>::iterator aKIt = aKnots.begin();
225   for (int index = 0; index < aSize; ++index, ++aKIt)
226     aKnotsAttr->setValue(index, *aKIt);
227
228   AttributeIntArrayPtr aMultsAttr = data()->intArray(SketchPlugin_BSplineBase::MULTS_ID());
229   std::list<int> aMults = aBSplineCurve->mults();
230   aSize = (int)aMults.size();
231   aMultsAttr->setSize(aSize);
232   std::list<int>::iterator aMIt = aMults.begin();
233   for (int index = 0; index < aSize; ++index, ++aMIt)
234     aMultsAttr->setValue(index, *aMIt);
235
236   data()->blockSendAttributeUpdated(aWasBlocked, true);
237
238   // update indices of internal coincidences
239   for (std::list<AttributeIntegerPtr>::iterator aCIt = aCoincidentPoleIndex.begin();
240        aCIt != aCoincidentPoleIndex.end(); ++aCIt)
241     (*aCIt)->setValue((*aCIt)->value() + 1);
242
243   // create auxiliary segment and pole updating the control polygon
244   SketchPlugin_MacroBSpline::createAuxiliaryPole(aPolesArray, anAfter + 1);
245   if (hasAuxSegment)
246     SketchPlugin_MacroBSpline::createAuxiliarySegment(aPolesArray, anAfter, anAfter + 1);
247
248   // update names of features representing control polygon
249   for (std::map<int, FeaturePtr>::iterator anIt = aControlPoles.begin();
250        anIt != aControlPoles.end(); ++anIt) {
251     SketchPlugin_MacroBSpline::assignDefaultNameForAux(anIt->second, aPolesArray, anIt->first + 1);
252   }
253   for (std::map<int, FeaturePtr>::iterator anIt = aControlSegments.begin();
254        anIt != aControlSegments.end(); ++anIt) {
255     SketchPlugin_MacroBSpline::assignDefaultNameForAux(anIt->second, aPolesArray,
256         anIt->first + 1, (anIt->first + 2) % aPolesArray->size());
257   }
258
259   return true;
260 }