1 // Copyright (C) 2020-2023 CEA, EDF
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 <SketchPlugin_CurveFitting.h>
22 #include <SketchPlugin_BSpline.h>
23 #include <SketchPlugin_BSplinePeriodic.h>
24 #include <SketchPlugin_ConstraintCoincidence.h>
25 #include <SketchPlugin_MacroBSpline.h>
26 #include <SketchPlugin_Point.h>
27 #include <SketchPlugin_Tools.h>
28 #include <SketchPlugin_Sketch.h>
30 #include <ModelAPI_AttributeBoolean.h>
31 #include <ModelAPI_AttributeDouble.h>
32 #include <ModelAPI_AttributeDoubleArray.h>
33 #include <ModelAPI_AttributeInteger.h>
34 #include <ModelAPI_AttributeRefAttrList.h>
35 #include <ModelAPI_AttributeString.h>
36 #include <ModelAPI_Session.h>
37 #include <ModelAPI_Validator.h>
39 #include <Events_InfoMessage.h>
41 #include <GeomDataAPI_Point2D.h>
42 #include <GeomDataAPI_Point2DArray.h>
44 #include <GeomAlgoAPI_CurveBuilder.h>
45 #include <GeomAlgoAPI_EdgeBuilder.h>
47 #include <GeomAPI_BSpline.h>
49 static void convertTo3D(SketchPlugin_Sketch* theSketch,
50 const AttributeRefAttrListPtr& theAttribute,
51 bool theClosedButNotPeriodic,
52 std::list<GeomPointPtr>& thePoints);
54 static GeomEdgePtr buildInterpolationCurve(SketchPlugin_Sketch* theSketch,
55 AttributeRefAttrListPtr thePoints,
58 static GeomEdgePtr buildApproximationCurve(SketchPlugin_Sketch* theSketch,
59 AttributeRefAttrListPtr thePoints,
65 SketchPlugin_CurveFitting::SketchPlugin_CurveFitting()
66 : SketchPlugin_SketchEntity()
70 void SketchPlugin_CurveFitting::initDerivedClassAttributes()
72 data()->addAttribute(POINTS_ID(), ModelAPI_AttributeRefAttrList::typeId());
74 data()->addAttribute(TYPE_ID(), ModelAPI_AttributeString::typeId());
76 data()->addAttribute(PRECISION_ID(), ModelAPI_AttributeDouble::typeId());
77 ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PRECISION_ID());
79 data()->addAttribute(NEED_CONTROL_POLYGON_ID(), ModelAPI_AttributeBoolean::typeId());
80 ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(),
81 NEED_CONTROL_POLYGON_ID());
83 data()->addAttribute(PERIODIC_ID(), ModelAPI_AttributeBoolean::typeId());
84 data()->addAttribute(CLOSED_ID(), ModelAPI_AttributeBoolean::typeId());
87 void SketchPlugin_CurveFitting::execute()
89 FeaturePtr aBSpline = createBSplineFeature();
90 // create control polygon
91 AttributeBooleanPtr aNeedControlPoly = boolean(NEED_CONTROL_POLYGON_ID());
92 if (aNeedControlPoly && aNeedControlPoly->isInitialized() && aNeedControlPoly->value()) {
93 bool isPeriodic = boolean(PERIODIC_ID())->value();
94 std::list<FeaturePtr> aControlPoles;
95 SketchPlugin_MacroBSpline::createControlPolygon(aBSpline, isPeriodic, aControlPoles);
97 // constraints for the selected points
98 createConstraints(aBSpline);
101 FeaturePtr SketchPlugin_CurveFitting::createBSplineFeature()
103 // create transient curve if not created yet
104 if (!myTransientResult) {
105 getAISObject(AISObjectPtr());
106 if (!myTransientResult)
110 SketchPlugin_Sketch* aSketch = sketch();
114 bool isPeriodic = boolean(PERIODIC_ID())->value();
116 FeaturePtr aBSplineFeature = aSketch->addFeature(
117 isPeriodic ? SketchPlugin_BSplinePeriodic::ID() : SketchPlugin_BSpline::ID());
119 // Convert transient edge to B-spline curve.
120 GeomCurvePtr aCurve = std::make_shared<GeomAPI_Curve>(myTransientResult);
121 std::shared_ptr<GeomAPI_BSpline> aBSpline = std::make_shared<GeomAPI_BSpline>(aCurve);
123 // Fill attributes of B-spline feature:
124 bool aWasBlocked = aBSplineFeature->data()->blockSendAttributeUpdated(true, false);
126 aBSplineFeature->integer(SketchPlugin_BSplineBase::DEGREE_ID())->setValue(aBSpline->degree());
128 std::list<GeomPointPtr> aPoles = aBSpline->poles();
129 AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
130 aBSplineFeature->attribute(SketchPlugin_BSplineBase::POLES_ID()));
131 aPolesAttr->setSize((int)aPoles.size());
133 for (auto it = aPoles.begin(); it != aPoles.end(); ++it, ++anIndex)
134 aPolesAttr->setPnt(anIndex, aSketch->to2D(*it));
136 std::list<double> aWeights = aBSpline->weights();
137 AttributeDoubleArrayPtr aWeightsAttr =
138 aBSplineFeature->data()->realArray(SketchPlugin_BSplineBase::WEIGHTS_ID());
139 if (aWeights.empty()) {
140 aWeightsAttr->setSize((int)aPoles.size());
141 for (anIndex = 0; anIndex < (int)aPoles.size(); ++anIndex)
142 aWeightsAttr->setValue(anIndex, 1.0);
145 aWeightsAttr->setSize((int)aWeights.size());
147 for (auto it = aWeights.begin(); it != aWeights.end(); ++it, ++anIndex)
148 aWeightsAttr->setValue(anIndex, *it);
150 // 4. Knots (normalized from 0 to 1)
151 std::list<double> aKnots = aBSpline->knots();
152 AttributeDoubleArrayPtr aKnotsAttr =
153 aBSplineFeature->data()->realArray(SketchPlugin_BSplineBase::KNOTS_ID());
154 aKnotsAttr->setSize((int)aKnots.size());
155 double aFirstKnot = aKnots.front();
156 double aLastKnot = aKnots.back();
158 for (auto it = aKnots.begin(); it != aKnots.end(); ++it, ++anIndex)
159 aKnotsAttr->setValue(anIndex, (*it - aFirstKnot) / (aLastKnot - aFirstKnot));
161 std::list<int> aMults = aBSpline->mults();
162 AttributeIntArrayPtr aMultsAttr =
163 aBSplineFeature->data()->intArray(SketchPlugin_BSplineBase::MULTS_ID());
164 aMultsAttr->setSize((int)aMults.size());
166 for (auto it = aMults.begin(); it != aMults.end(); ++it, ++anIndex)
167 aMultsAttr->setValue(anIndex, *it);
170 AttributePoint2DPtr aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
171 aBSplineFeature->attribute(SketchPlugin_BSpline::START_ID()));
172 aStartPoint->setValue(aPolesAttr->pnt(0));
174 AttributePoint2DPtr aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
175 aBSplineFeature->attribute(SketchPlugin_BSpline::END_ID()));
176 aEndPoint->setValue(aPolesAttr->pnt(aPolesAttr->size() - 1));
179 aBSplineFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(
180 boolean(AUXILIARY_ID())->value());
182 aBSplineFeature->data()->blockSendAttributeUpdated(aWasBlocked);
184 aBSplineFeature->execute();
186 return aBSplineFeature;
189 void SketchPlugin_CurveFitting::createConstraints(FeaturePtr theProducedFeature)
191 if (!theProducedFeature)
194 SketchPlugin_Sketch* aSketch = sketch();
195 ResultPtr aResult = theProducedFeature->lastResult();
196 bool isPeriodic = boolean(PERIODIC_ID())->value();
197 bool isClosed = boolean(CLOSED_ID())->value();
198 bool isApproximation = string(TYPE_ID())->value() == TYPE_APPROXIMATION_ID();
200 AttributeRefAttrListPtr aPointsAttr = refattrlist(POINTS_ID());
201 std::list<std::pair<ObjectPtr, AttributePtr> > aPointsList = aPointsAttr->list();
202 std::list<std::pair<ObjectPtr, AttributePtr> >::iterator aLastIt = --aPointsList.end();
203 for (auto it = aPointsList.begin(); it != aPointsList.end(); ++it) {
204 AttributePtr anAttr = it->second;
206 // maybe the SketchPoint is selected
207 FeaturePtr aFeature = ModelAPI_Feature::feature(it->first);
208 if (aFeature && aFeature->getKind() == SketchPlugin_Point::ID())
209 anAttr = aFeature->attribute(SketchPlugin_Point::COORD_ID());
214 if (!isPeriodic && it == aPointsList.begin()) {
215 SketchPlugin_Tools::createConstraintAttrAttr(aSketch,
216 SketchPlugin_ConstraintCoincidence::ID(),
217 anAttr, theProducedFeature->attribute(SketchPlugin_BSpline::START_ID()));
219 // end of B-spline curve should be coincident with the first selected point
220 SketchPlugin_Tools::createConstraintAttrAttr(aSketch,
221 SketchPlugin_ConstraintCoincidence::ID(),
222 anAttr, theProducedFeature->attribute(SketchPlugin_BSpline::END_ID()));
225 else if (!isPeriodic && !isClosed && it == aLastIt) {
226 SketchPlugin_Tools::createConstraintAttrAttr(aSketch,
227 SketchPlugin_ConstraintCoincidence::ID(),
228 anAttr, theProducedFeature->attribute(SketchPlugin_BSpline::END_ID()));
230 else if (!isApproximation) {
231 SketchPlugin_Tools::createConstraintAttrObject(aSketch,
232 SketchPlugin_ConstraintCoincidence::ID(),
238 bool SketchPlugin_CurveFitting::customAction(const std::string& theActionId)
241 if (theActionId == REORDER_POINTS_ACTION_ID()) {
244 static const std::string MESSAGE("Error: Feature \"%1\" does not support action \"%2\".");
245 Events_InfoMessage("SketchPlugin_CurveFitting", MESSAGE)
246 .arg(getKind()).arg(theActionId).send();
253 void SketchPlugin_CurveFitting::reorderPoints()
255 AttributeRefAttrListPtr aPointsAttr = refattrlist(POINTS_ID());
256 bool isPeriodic = boolean(PERIODIC_ID())->value();
257 bool isClosed = boolean(CLOSED_ID())->value();
259 std::list<GeomPointPtr> aCoordinates;
260 convertTo3D(sketch(), aPointsAttr, !isPeriodic && isClosed, aCoordinates);
262 // to keep mapping between points and attributes
263 std::map<GeomPointPtr, std::pair<ObjectPtr, AttributePtr> > aMap;
264 std::list<std::pair<ObjectPtr, AttributePtr> > aPointsList = aPointsAttr->list();
266 std::list<GeomPointPtr>::iterator aCoordIt = aCoordinates.begin();
267 std::list<std::pair<ObjectPtr, AttributePtr> >::iterator anAttrIt = aPointsList.begin();
268 for (; aCoordIt != aCoordinates.end() && anAttrIt != aPointsList.end(); ++aCoordIt, ++anAttrIt)
269 aMap[*aCoordIt] = *anAttrIt;
272 GeomAlgoAPI_CurveBuilder::reorderPoints(aCoordinates);
274 // re-compose the attribute
275 bool aWasBlocked = data()->blockSendAttributeUpdated(true);
276 aPointsAttr->clear();
277 for (aCoordIt = aCoordinates.begin(); aCoordIt != aCoordinates.end(); ++aCoordIt) {
278 const std::pair<ObjectPtr, AttributePtr>& aValue = aMap.at(*aCoordIt);
280 aPointsAttr->append(aValue.second);
282 aPointsAttr->append(aValue.first);
284 data()->blockSendAttributeUpdated(aWasBlocked);
287 AISObjectPtr SketchPlugin_CurveFitting::getAISObject(AISObjectPtr thePrevious)
289 SketchPlugin_Sketch* aSketch = sketch();
291 return AISObjectPtr();
293 std::string aType = string(TYPE_ID())->value();
294 if (aType == TYPE_INTERPOLATION_ID()) {
295 myTransientResult = buildInterpolationCurve(aSketch,
296 refattrlist(POINTS_ID()),
297 boolean(PERIODIC_ID())->value(),
298 boolean(CLOSED_ID())->value());
300 else if (aType == TYPE_APPROXIMATION_ID()) {
301 myTransientResult = buildApproximationCurve(aSketch,
302 refattrlist(POINTS_ID()),
303 real(PRECISION_ID())->value(),
304 boolean(PERIODIC_ID())->value(),
305 boolean(CLOSED_ID())->value());
307 if (!myTransientResult)
308 return AISObjectPtr();
310 AISObjectPtr anAIS = thePrevious;
312 anAIS.reset(new GeomAPI_AISObject());
313 anAIS->createShape(myTransientResult);
316 SketchPlugin_Tools::customizeFeaturePrs(anAIS, boolean(AUXILIARY_ID())->value());
322 // =============================== Auxiliary functions ====================================
324 void convertTo3D(SketchPlugin_Sketch* theSketch,
325 const AttributeRefAttrListPtr& theAttribute,
326 bool theClosedButNotPeriodic,
327 std::list<GeomPointPtr>& thePoints)
329 std::list<std::pair<ObjectPtr, AttributePtr> > aPointsList = theAttribute->list();
330 for (auto it = aPointsList.begin(); it != aPointsList.end(); ++it) {
331 AttributePtr anAttr = it->second;
333 // maybe the SketchPoint is selected
334 FeaturePtr aFeature = ModelAPI_Feature::feature(it->first);
335 if (aFeature && aFeature->getKind() == SketchPlugin_Point::ID())
336 anAttr = aFeature->attribute(SketchPlugin_Point::COORD_ID());
343 AttributePoint2DPtr aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
345 GeomPointPtr aPnt3D = theSketch->to3D(aPoint->x(), aPoint->y());
346 thePoints.push_back(aPnt3D);
350 if (theClosedButNotPeriodic && !thePoints.empty() &&
351 thePoints.front()->distance(thePoints.back()) > 1.e-7)
352 thePoints.push_back(thePoints.front()); // close the curve
355 GeomEdgePtr buildInterpolationCurve(SketchPlugin_Sketch* theSketch,
356 AttributeRefAttrListPtr thePoints,
360 std::list<GeomPointPtr> aCoordinates;
361 convertTo3D(theSketch, thePoints, !thePeriodic && theClosed, aCoordinates);
364 if (aCoordinates.size() > 1) {
365 static const bool isReorder = false;
366 static GeomDirPtr aStartEndDir;
367 aResult = GeomAlgoAPI_CurveBuilder::edge(aCoordinates, thePeriodic,
368 isReorder, aStartEndDir, aStartEndDir);
373 GeomEdgePtr buildApproximationCurve(SketchPlugin_Sketch* theSketch,
374 AttributeRefAttrListPtr thePoints,
379 std::list<GeomPointPtr> aCoordinates;
380 convertTo3D(theSketch, thePoints, !thePeriodic && theClosed, aCoordinates);
383 if (aCoordinates.size() > 1)
384 aResult = GeomAlgoAPI_CurveBuilder::approximate(aCoordinates, thePeriodic, thePrecision);