Salome HOME
Updated copyright comment
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_MacroEllipticArc.cpp
1 // Copyright (C) 2017-2024  CEA, EDF
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_MacroEllipticArc.h>
21
22 #include <SketchPlugin_EllipticArc.h>
23 #include <SketchPlugin_MacroArcReentrantMessage.h>
24 #include <SketchPlugin_Tools.h>
25 #include <SketchPlugin_Sketch.h>
26
27 #include <ModelAPI_AttributeDouble.h>
28 #include <ModelAPI_AttributeRefAttr.h>
29 #include <ModelAPI_Session.h>
30 #include <ModelAPI_Validator.h>
31 #include <ModelAPI_Events.h>
32
33 #include <GeomDataAPI_Point2D.h>
34
35 #include <GeomAPI_Dir2d.h>
36 #include <GeomAPI_Ellipse.h>
37 #include <GeomAPI_Ellipse2d.h>
38 #include <GeomAPI_Vertex.h>
39
40 #include <GeomAlgoAPI_CompoundBuilder.h>
41 #include <GeomAlgoAPI_EdgeBuilder.h>
42 #include <GeomAlgoAPI_PointBuilder.h>
43
44
45 const double TOLERANCE = 1.e-7;
46 const double paramTolerance = 1.e-4;
47 const double PI = 3.141592653589793238463;
48
49
50 SketchPlugin_MacroEllipticArc::SketchPlugin_MacroEllipticArc()
51   : SketchPlugin_SketchEntity(),
52     myMajorRadius(0.0),
53     myMinorRadius(0.0),
54     myParamDelta(0.0)
55 {
56 }
57
58 void SketchPlugin_MacroEllipticArc::initAttributes()
59 {
60   data()->addAttribute(CENTER_ID(), GeomDataAPI_Point2D::typeId());
61   data()->addAttribute(CENTER_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
62   data()->addAttribute(MAJOR_AXIS_POINT_ID(), GeomDataAPI_Point2D::typeId());
63   data()->addAttribute(MAJOR_AXIS_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
64   data()->addAttribute(START_POINT_ID(), GeomDataAPI_Point2D::typeId());
65   data()->addAttribute(START_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
66   data()->addAttribute(END_POINT_ID(), GeomDataAPI_Point2D::typeId());
67   data()->addAttribute(END_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
68
69   data()->addAttribute(MAJOR_RADIUS_ID(), ModelAPI_AttributeDouble::typeId());
70   data()->addAttribute(MINOR_RADIUS_ID(), ModelAPI_AttributeDouble::typeId());
71
72   data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
73   data()->addAttribute(AUXILIARY_ID(), ModelAPI_AttributeBoolean::typeId());
74
75   boolean(REVERSED_ID())->setValue(false);
76
77   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), CENTER_REF_ID());
78   ModelAPI_Session::get()->validators()
79       ->registerNotObligatory(getKind(), MAJOR_AXIS_POINT_REF_ID());
80   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), START_POINT_REF_ID());
81   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), END_POINT_REF_ID());
82 }
83
84 void SketchPlugin_MacroEllipticArc::execute()
85 {
86   FeaturePtr anEllipse = createEllipticArcFeature();
87   constraintsForEllipticArc(anEllipse);
88
89   // message to init reentrant operation
90   static Events_ID anId = SketchPlugin_MacroArcReentrantMessage::eventId();
91   std::shared_ptr<SketchPlugin_MacroArcReentrantMessage> aMessage = std::shared_ptr
92     <SketchPlugin_MacroArcReentrantMessage>(new SketchPlugin_MacroArcReentrantMessage(anId, this));
93   aMessage->setCreatedFeature(anEllipse);
94   Events_Loop::loop()->send(aMessage);
95 }
96
97 void SketchPlugin_MacroEllipticArc::attributeChanged(const std::string& /*theID*/)
98 {
99   static const int NB_POINTS = 4;
100   std::string aPointAttrName[NB_POINTS] = { CENTER_ID(),
101                                             MAJOR_AXIS_POINT_ID(),
102                                             START_POINT_ID(),
103                                             END_POINT_ID() };
104   std::string aPointRefName[NB_POINTS] = { CENTER_REF_ID(),
105                                            MAJOR_AXIS_POINT_REF_ID(),
106                                            START_POINT_REF_ID(),
107                                            END_POINT_REF_ID() };
108
109   int aNbInitialized = 0;
110   GeomPnt2dPtr anEllipsePoints[NB_POINTS];
111
112   for (int aPntIndex = 0; aPntIndex < NB_POINTS; ++aPntIndex) {
113     AttributePtr aPointAttr = attribute(aPointAttrName[aPntIndex]);
114     if (!aPointAttr->isInitialized())
115       continue;
116
117     AttributeRefAttrPtr aPointRef = refattr(aPointRefName[aPntIndex]);
118     // calculate ellipse parameters
119     GeomPnt2dPtr aPassedPoint =
120         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointAttr)->pnt();
121     GeomShapePtr aTangentCurve;
122     SketchPlugin_Tools::convertRefAttrToPointOrTangentCurve(
123       aPointRef, aPointAttr, aTangentCurve, aPassedPoint);
124
125     anEllipsePoints[aNbInitialized++] = aPassedPoint;
126   }
127
128   if (aNbInitialized <= 1)
129     return; // too few points for the ellipse
130
131   myCenter    = anEllipsePoints[0];
132   myMajorAxis = anEllipsePoints[1];
133   myStartPnt  = anEllipsePoints[2];
134   myEndPnt    = anEllipsePoints[3];
135
136   if (myCenter->distance(myMajorAxis) < TOLERANCE)
137     return; // ellipse is not valid
138
139   std::shared_ptr<GeomAPI_Ellipse2d> anEllipse;
140   if (aNbInitialized == 2) {
141     GeomDir2dPtr aXDir(new GeomAPI_Dir2d(anEllipsePoints[1]->x() - anEllipsePoints[0]->x(),
142                                          anEllipsePoints[1]->y() - anEllipsePoints[0]->y()));
143     double aMajorRad = anEllipsePoints[1]->distance(anEllipsePoints[0]);
144     anEllipse = std::shared_ptr<GeomAPI_Ellipse2d>(
145         new GeomAPI_Ellipse2d(anEllipsePoints[0], aXDir, aMajorRad, 0.5 * aMajorRad));
146   }
147   else if (aNbInitialized >= 3) {
148     anEllipse = std::shared_ptr<GeomAPI_Ellipse2d>(
149       new GeomAPI_Ellipse2d(anEllipsePoints[0], anEllipsePoints[1], anEllipsePoints[2]));
150   }
151
152   if (!anEllipse || anEllipse->implPtr<void>() == 0)
153     return;
154
155   myMajorRadius = anEllipse->majorRadius();
156   myMinorRadius = anEllipse->minorRadius();
157
158   bool aWasBlocked = data()->blockSendAttributeUpdated(true);
159   real(MAJOR_RADIUS_ID())->setValue(myMajorRadius);
160   real(MINOR_RADIUS_ID())->setValue(myMinorRadius);
161   data()->blockSendAttributeUpdated(aWasBlocked, false);
162
163   // update the REVERSED flag
164   if (myEndPnt) {
165     double aParameterEnd = 0.0;
166     GeomPnt2dPtr aEnd = anEllipse->project(myEndPnt);
167     if (anEllipse->parameter(aEnd, paramTolerance, aParameterEnd)) {
168       double aParamStart = 0.0;
169       anEllipse->parameter(myStartPnt, paramTolerance, aParamStart);
170       aParameterEnd -= aParamStart;
171
172       if (myParamDelta > 0.0 && myParamDelta <= PI * 0.5 &&
173           aParameterEnd < 0 && aParameterEnd >= -PI * 0.5) {
174         boolean(REVERSED_ID())->setValue(true);
175       }
176       else if (myParamDelta < 0.0 && myParamDelta >= -PI * 0.5 &&
177                aParameterEnd > 0.0 && aParameterEnd <= PI * 0.5) {
178         boolean(REVERSED_ID())->setValue(false);
179       }
180     }
181     myParamDelta = aParameterEnd;
182   }
183 }
184
185 // LCOV_EXCL_START
186 std::string SketchPlugin_MacroEllipticArc::processEvent(
187                                               const std::shared_ptr<Events_Message>& theMessage)
188 {
189   std::string aFilledAttributeName;
190
191   std::shared_ptr<SketchPlugin_MacroArcReentrantMessage> aReentrantMessage =
192       std::dynamic_pointer_cast<SketchPlugin_MacroArcReentrantMessage>(theMessage);
193   if (aReentrantMessage) {
194     FeaturePtr aCreatedFeature = aReentrantMessage->createdFeature();
195
196     ObjectPtr anObject = aReentrantMessage->selectedObject();
197     AttributePtr anAttribute = aReentrantMessage->selectedAttribute();
198     std::shared_ptr<GeomAPI_Pnt2d> aClickedPoint = aReentrantMessage->clickedPoint();
199
200     if (aClickedPoint && (anObject || anAttribute)) {
201       aFilledAttributeName = CENTER_ID();
202       std::string aReferenceAttributeName = CENTER_REF_ID();
203
204       // fill 2d point attribute
205       AttributePoint2DPtr aPointAttr =
206           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(aFilledAttributeName));
207       aPointAttr->setValue(aClickedPoint);
208
209       // fill reference attribute
210       AttributeRefAttrPtr aRefAttr =
211           std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(attribute(aReferenceAttributeName));
212       if (anAttribute) {
213         if (!anAttribute->owner() || !anAttribute->owner()->data()->isValid()) {
214           if (aCreatedFeature && anAttribute->id() == CENTER_ID())
215             anAttribute = aCreatedFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID());
216         }
217         aRefAttr->setAttr(anAttribute);
218       }
219       else if (anObject.get()) {
220         // if attribute is NULL, only object is defined, it should be processed outside
221         // the feature because it might be an external feature, that will be
222         // removed/created again after restart operation
223         // #2468 - Crash when sketching circles successively on a repetition
224         aFilledAttributeName = "";
225       }
226     }
227     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
228   }
229   return aFilledAttributeName;
230 }
231 // LCOV_EXCL_STOP
232
233 FeaturePtr SketchPlugin_MacroEllipticArc::createEllipticArcFeature()
234 {
235   GeomShapePtr anArc = getArcShape();
236   GeomEllipsePtr anEllipse;
237   GeomPointPtr aStartPoint, aEndPoint;
238   if (anArc->isEdge()) {
239     GeomEdgePtr anArcEdge = anArc->edge();
240     aStartPoint = anArcEdge->firstPoint();
241     aEndPoint = anArcEdge->lastPoint();
242     if (boolean(REVERSED_ID())->value())
243       std::swap(aStartPoint, aEndPoint);
244
245     if (anArcEdge->isEllipse())
246       anEllipse = anArcEdge->ellipse();
247   }
248
249   if (!anEllipse)
250     return FeaturePtr();
251
252   // Create and fill new EllipticArc feature
253   SketchPlugin_Sketch* aSketch = sketch();
254   FeaturePtr aEllipseFeature = aSketch->addFeature(SketchPlugin_EllipticArc::ID());
255
256   AttributePoint2DPtr aCenterAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
257       aEllipseFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()));
258   aCenterAttr->setValue(aSketch->to2D(anEllipse->center()));
259
260   AttributePoint2DPtr aFocusAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
261       aEllipseFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()));
262   aFocusAttr->setValue(aSketch->to2D(anEllipse->firstFocus()));
263
264   AttributePoint2DPtr aStartAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
265       aEllipseFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()));
266   aStartAttr->setValue(aSketch->to2D(aStartPoint));
267
268   AttributePoint2DPtr aEndAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
269       aEllipseFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()));
270   aEndAttr->setValue(aSketch->to2D(aEndPoint));
271
272   aEllipseFeature->real(SketchPlugin_EllipticArc::MAJOR_RADIUS_ID())->setValue(myMajorRadius);
273   aEllipseFeature->real(SketchPlugin_EllipticArc::MINOR_RADIUS_ID())->setValue(myMinorRadius);
274
275   aEllipseFeature->boolean(SketchPlugin_EllipticArc::REVERSED_ID())->setValue(
276       boolean(REVERSED_ID())->value());
277
278   aEllipseFeature->boolean(SketchPlugin_EllipticArc::AUXILIARY_ID())->setValue(
279       boolean(AUXILIARY_ID())->value());
280
281   aEllipseFeature->execute();
282
283   // create auxiliary points
284   SketchPlugin_Tools::createAuxiliaryPointOnEllipse(
285       aEllipseFeature, SketchPlugin_EllipticArc::CENTER_ID());
286   SketchPlugin_Tools::createAuxiliaryPointOnEllipse(
287       aEllipseFeature, SketchPlugin_EllipticArc::FIRST_FOCUS_ID());
288   SketchPlugin_Tools::createAuxiliaryPointOnEllipse(
289       aEllipseFeature, SketchPlugin_EllipticArc::SECOND_FOCUS_ID());
290   SketchPlugin_Tools::createAuxiliaryPointOnEllipse(
291       aEllipseFeature, SketchPlugin_EllipticArc::MAJOR_AXIS_START_ID());
292   SketchPlugin_Tools::createAuxiliaryPointOnEllipse(
293       aEllipseFeature, SketchPlugin_EllipticArc::MAJOR_AXIS_END_ID());
294   SketchPlugin_Tools::createAuxiliaryPointOnEllipse(
295       aEllipseFeature, SketchPlugin_EllipticArc::MINOR_AXIS_START_ID());
296   SketchPlugin_Tools::createAuxiliaryPointOnEllipse(
297       aEllipseFeature, SketchPlugin_EllipticArc::MINOR_AXIS_END_ID());
298   // create auxiliary axes
299   SketchPlugin_Tools::createAuxiliaryAxisOfEllipse(aEllipseFeature,
300                       SketchPlugin_EllipticArc::MAJOR_AXIS_START_ID(),
301                       SketchPlugin_EllipticArc::MAJOR_AXIS_END_ID());
302   SketchPlugin_Tools::createAuxiliaryAxisOfEllipse(aEllipseFeature,
303                       SketchPlugin_EllipticArc::MINOR_AXIS_START_ID(),
304                       SketchPlugin_EllipticArc::MINOR_AXIS_END_ID());
305
306   return aEllipseFeature;
307 }
308
309 void SketchPlugin_MacroEllipticArc::constraintsForEllipticArc(FeaturePtr theEllipticArc)
310 {
311   // tangency on-the-fly is not applicable for elliptic arcs
312   static const bool isTangencyApplicable = false;
313   // Create constraints.
314   SketchPlugin_Tools::createCoincidenceOrTangency(
315       this, CENTER_REF_ID(),
316       theEllipticArc->attribute(SketchPlugin_EllipticArc::CENTER_ID()),
317       ObjectPtr(), isTangencyApplicable);
318   // make coincidence only if PASSED_POINT_REF_ID() refers a point but not an object
319   if (!refattr(MAJOR_AXIS_POINT_REF_ID())->isObject()) {
320     SketchPlugin_Tools::createCoincidenceOrTangency(
321         this, MAJOR_AXIS_POINT_REF_ID(),
322         AttributePtr(),
323         theEllipticArc->lastResult(), isTangencyApplicable);
324   }
325   SketchPlugin_Tools::createCoincidenceOrTangency(
326       this, START_POINT_REF_ID(),
327       theEllipticArc->attribute(SketchPlugin_EllipticArc::START_POINT_ID()),
328       ObjectPtr(), isTangencyApplicable);
329   SketchPlugin_Tools::createCoincidenceOrTangency(
330       this, END_POINT_REF_ID(),
331       theEllipticArc->attribute(SketchPlugin_EllipticArc::END_POINT_ID()),
332       ObjectPtr(), isTangencyApplicable);
333 }
334
335 AISObjectPtr SketchPlugin_MacroEllipticArc::getAISObject(AISObjectPtr thePrevious)
336 {
337   SketchPlugin_Sketch* aSketch = sketch();
338   if (!aSketch || !myCenter || !myMajorAxis)
339     return AISObjectPtr();
340
341   // Compute a elliptic arc in 3D view.
342   GeomPointPtr aCenter(aSketch->to3D(myCenter->x(), myCenter->y()));
343   GeomShapePtr aCenterPointShape = GeomAlgoAPI_PointBuilder::vertex(aCenter);
344   GeomShapePtr anArcShape = getArcShape();
345   if (!anArcShape.get() || !aCenterPointShape.get())
346     return AISObjectPtr();
347
348   std::list<std::shared_ptr<GeomAPI_Shape> > aShapes;
349   aShapes.push_back(anArcShape);
350   aShapes.push_back(aCenterPointShape);
351
352   std::shared_ptr<GeomAPI_Shape> aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
353   AISObjectPtr anAIS = thePrevious;
354   if (!anAIS)
355     anAIS.reset(new GeomAPI_AISObject());
356   anAIS->createShape(aCompound);
357   SketchPlugin_Tools::customizeFeaturePrs(anAIS, boolean(AUXILIARY_ID())->value());
358   return anAIS;
359 }
360
361 GeomShapePtr SketchPlugin_MacroEllipticArc::getArcShape()
362 {
363   if (!myCenter.get() || !myMajorAxis.get())
364     return GeomShapePtr();
365
366   SketchPlugin_Sketch* aSketch = sketch();
367   if (!aSketch || myCenter->distance(myMajorAxis) < 1.e-7)
368     return GeomShapePtr();
369
370   GeomPointPtr aCenter(aSketch->to3D(myCenter->x(), myCenter->y()));
371   GeomPointPtr aMajorAxisPnt(aSketch->to3D(myMajorAxis->x(), myMajorAxis->y()));
372   GeomDirPtr aMajorAxisDir(new GeomAPI_Dir(aMajorAxisPnt->x() - aCenter->x(),
373                                            aMajorAxisPnt->y() - aCenter->y(),
374                                            aMajorAxisPnt->z() - aCenter->z()));
375
376   std::shared_ptr<GeomDataAPI_Dir> aNDir = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
377       aSketch->attribute(SketchPlugin_Sketch::NORM_ID()));
378   GeomDirPtr aNormal(new GeomAPI_Dir(aNDir->x(), aNDir->y(), aNDir->z()));
379
380   GeomPointPtr aStart, anEnd;
381   if (myStartPnt)
382     aStart = aSketch->to3D(myStartPnt->x(), myStartPnt->y());
383   if (myEndPnt)
384     anEnd = aSketch->to3D(myEndPnt->x(), myEndPnt->y());
385
386   GeomShapePtr anArcShape;
387   if (anEnd) {
388     if (boolean(REVERSED_ID())->value())
389       std::swap(aStart, anEnd);
390
391     anArcShape = GeomAlgoAPI_EdgeBuilder::ellipticArc(aCenter, aNormal, aMajorAxisDir,
392         myMajorRadius, myMinorRadius, aStart, anEnd);
393   }
394   else {
395     anArcShape = GeomAlgoAPI_EdgeBuilder::ellipse(aCenter, aNormal, aMajorAxisDir,
396         myMajorRadius, myMinorRadius);
397   }
398
399   return anArcShape;
400 }