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