Salome HOME
Merge branch 'master' into cgt/devCEA
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_MacroCircle.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:        SketchPlugin_MacroCircle.cpp
4 // Created:     26 May 2014
5 // Author:      Artem ZHIDKOV
6
7 #include "SketchPlugin_MacroCircle.h"
8
9 #include "SketchPlugin_Circle.h"
10 #include "SketchPlugin_Point.h"
11 #include "SketchPlugin_Tools.h"
12 #include "SketchPlugin_MacroArcReentrantMessage.h"
13
14 #include <ModelAPI_AttributeDouble.h>
15 #include <ModelAPI_AttributeRefAttr.h>
16 #include <ModelAPI_AttributeString.h>
17 #include <ModelAPI_Session.h>
18 #include <ModelAPI_Validator.h>
19 #include <ModelAPI_Events.h>
20
21 #include <GeomDataAPI_Dir.h>
22 #include <GeomDataAPI_Point2D.h>
23
24 #include <GeomAPI_Circ2d.h>
25 #include <GeomAPI_Pnt2d.h>
26 #include <GeomAPI_Vertex.h>
27
28 #include <GeomAlgoAPI_Circ2dBuilder.h>
29 #include <GeomAlgoAPI_CompoundBuilder.h>
30 #include <GeomAlgoAPI_EdgeBuilder.h>
31 #include <GeomAlgoAPI_PointBuilder.h>
32
33
34 const double tolerance = 1e-7;
35
36 namespace {
37   static const std::string& POINT_ID(int theIndex)
38   {
39     switch (theIndex) {
40       case 1: return SketchPlugin_MacroCircle::FIRST_POINT_ID();
41       case 2: return SketchPlugin_MacroCircle::SECOND_POINT_ID();
42       case 3: return SketchPlugin_MacroCircle::THIRD_POINT_ID();
43     }
44
45     static const std::string DUMMY;
46     return DUMMY;
47   }
48 }
49
50
51 SketchPlugin_MacroCircle::SketchPlugin_MacroCircle()
52 : SketchPlugin_SketchEntity(),
53   myRadius(0.0)
54 {
55 }
56
57 void SketchPlugin_MacroCircle::initAttributes()
58 {
59   data()->addAttribute(CIRCLE_TYPE(), ModelAPI_AttributeString::typeId());
60   data()->addAttribute(EDIT_CIRCLE_TYPE(), ModelAPI_AttributeString::typeId());
61
62   data()->addAttribute(CENTER_POINT_ID(), GeomDataAPI_Point2D::typeId());
63   data()->addAttribute(CENTER_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
64   data()->addAttribute(PASSED_POINT_ID(), GeomDataAPI_Point2D::typeId());
65   data()->addAttribute(PASSED_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
66
67   data()->addAttribute(FIRST_POINT_ID(), GeomDataAPI_Point2D::typeId());
68   data()->addAttribute(SECOND_POINT_ID(), GeomDataAPI_Point2D::typeId());
69   data()->addAttribute(THIRD_POINT_ID(), GeomDataAPI_Point2D::typeId());
70   data()->addAttribute(FIRST_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
71   data()->addAttribute(SECOND_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
72   data()->addAttribute(THIRD_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
73
74   data()->addAttribute(CIRCLE_RADIUS_ID(), ModelAPI_AttributeDouble::typeId());
75   data()->addAttribute(AUXILIARY_ID(), ModelAPI_AttributeBoolean::typeId());
76
77   string(EDIT_CIRCLE_TYPE())->setValue("");
78
79   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), CENTER_POINT_REF_ID());
80   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PASSED_POINT_REF_ID());
81   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), FIRST_POINT_REF_ID());
82   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SECOND_POINT_REF_ID());
83   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), THIRD_POINT_REF_ID());
84   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EDIT_CIRCLE_TYPE());
85 }
86
87 void SketchPlugin_MacroCircle::execute()
88 {
89   FeaturePtr aCircle = createCircleFeature();
90
91   std::string aType = string(CIRCLE_TYPE())->value();
92   if (aType == CIRCLE_TYPE_BY_CENTER_AND_PASSED_POINTS())
93     constraintsForCircleByCenterAndPassed(aCircle);
94   else if (aType == CIRCLE_TYPE_BY_THREE_POINTS())
95     constraintsForCircleByThreePoints(aCircle);
96
97   // message to init reentrant operation
98   static Events_ID anId = SketchPlugin_MacroArcReentrantMessage::eventId();
99   std::shared_ptr<SketchPlugin_MacroArcReentrantMessage> aMessage = std::shared_ptr
100     <SketchPlugin_MacroArcReentrantMessage>(new SketchPlugin_MacroArcReentrantMessage(anId, this));
101
102   std::string anEditType = string(EDIT_CIRCLE_TYPE())->value();
103   aMessage->setTypeOfCreation(!anEditType.empty() ? anEditType : aType);
104   aMessage->setCreatedFeature(aCircle);
105   Events_Loop::loop()->send(aMessage);
106 }
107
108 std::string SketchPlugin_MacroCircle::processEvent(
109                                               const std::shared_ptr<Events_Message>& theMessage)
110 {
111   std::string aFilledAttributeName;
112   std::shared_ptr<SketchPlugin_MacroArcReentrantMessage> aReentrantMessage =
113         std::dynamic_pointer_cast<SketchPlugin_MacroArcReentrantMessage>(theMessage);
114   if (aReentrantMessage.get()) {
115     FeaturePtr aCreatedFeature = aReentrantMessage->createdFeature();
116     std::string aCircleType = aReentrantMessage->typeOfCreation();
117
118     string(CIRCLE_TYPE())->setValue(aCircleType);
119
120     aFilledAttributeName = CIRCLE_TYPE();
121     ObjectPtr anObject = aReentrantMessage->selectedObject();
122     AttributePtr anAttribute = aReentrantMessage->selectedAttribute();
123     std::shared_ptr<GeomAPI_Pnt2d> aClickedPoint = aReentrantMessage->clickedPoint();
124
125     if (aClickedPoint.get() && (anObject.get() || anAttribute.get())) {
126       std::string aReferenceAttributeName;
127       if (aCircleType == CIRCLE_TYPE_BY_CENTER_AND_PASSED_POINTS()) {
128         aFilledAttributeName = CENTER_POINT_ID();
129         aReferenceAttributeName = CENTER_POINT_REF_ID();
130       }
131       else {
132         aFilledAttributeName = FIRST_POINT_ID();
133         aReferenceAttributeName = FIRST_POINT_REF_ID();
134       }
135       // fill 2d point attribute
136       AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
137                                                         attribute(aFilledAttributeName));
138       aPointAttr->setValue(aClickedPoint);
139       // fill reference attribute
140       AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
141                                                         attribute(aReferenceAttributeName));
142       if (aRefAttr.get()) {
143         if (anAttribute.get()) {
144           if (!anAttribute->owner().get() || !anAttribute->owner()->data()->isValid()) {
145             FeaturePtr aCreatedFeature = aReentrantMessage->createdFeature();
146             if (aCreatedFeature.get()) {
147               std::string anID = anAttribute->id();
148               std::string anArcID;
149               if (anID == CENTER_POINT_ID())
150                 anArcID = SketchPlugin_Circle::CENTER_ID();
151               anAttribute = aCreatedFeature->attribute(anArcID);
152             }
153           }
154           aRefAttr->setAttr(anAttribute);
155         }
156         else if (anObject.get()) {
157           // if presentation of previous reentrant macro arc is used, the object is invalid,
158           // we should use result of previous feature of the message(Arc)
159           if (!anObject->data()->isValid()) {
160             FeaturePtr aCreatedFeature = aReentrantMessage->createdFeature();
161             anObject = aCreatedFeature->lastResult();
162           }
163           aRefAttr->setObject(anObject);
164         }
165       }
166     }
167     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
168   }
169   return aFilledAttributeName;
170 }
171
172 void SketchPlugin_MacroCircle::constraintsForCircleByCenterAndPassed(FeaturePtr theCircleFeature)
173 {
174   // Create constraints.
175   SketchPlugin_Tools::createConstraint(
176       this, CENTER_POINT_REF_ID(),
177       theCircleFeature->attribute(SketchPlugin_Circle::CENTER_ID()),
178       ObjectPtr(), false);
179   SketchPlugin_Tools::createConstraint(
180       this, PASSED_POINT_REF_ID(), AttributePtr(),
181       theCircleFeature->lastResult(), true);
182 }
183
184 void SketchPlugin_MacroCircle::constraintsForCircleByThreePoints(FeaturePtr theCircleFeature)
185 {
186   std::string aPointRef[3] = { FIRST_POINT_REF_ID(),
187                                SECOND_POINT_REF_ID(),
188                                THIRD_POINT_REF_ID() };
189
190   // Create constraints.
191   ResultPtr aCircleResult = theCircleFeature->lastResult();
192   for (int i = 0; i < 3; ++i)
193     SketchPlugin_Tools::createConstraint(this, aPointRef[i], AttributePtr(), aCircleResult, true);
194 }
195
196 FeaturePtr SketchPlugin_MacroCircle::createCircleFeature()
197 {
198   FeaturePtr aCircleFeature = sketch()->addFeature(SketchPlugin_Circle::ID());
199   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
200       aCircleFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(myCenter->x(),
201                                                                              myCenter->y());
202   aCircleFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(myRadius);
203   aCircleFeature->boolean(SketchPlugin_Circle::AUXILIARY_ID())
204                 ->setValue(boolean(AUXILIARY_ID())->value());
205   aCircleFeature->execute();
206   return aCircleFeature;
207 }
208
209 void SketchPlugin_MacroCircle::fillByCenterAndPassed()
210 {
211   AttributePtr aCenterAttr = attribute(CENTER_POINT_ID());
212   AttributePtr aPassedAttr = attribute(PASSED_POINT_ID());
213   if (!aCenterAttr->isInitialized() || !aPassedAttr->isInitialized())
214     return;
215
216   AttributeRefAttrPtr aPassedRef = refattr(PASSED_POINT_REF_ID());
217   // Calculate circle parameters
218   std::shared_ptr<GeomAPI_Pnt2d> aCenter =
219       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aCenterAttr)->pnt();
220   std::shared_ptr<GeomAPI_Pnt2d> aPassedPoint;
221   std::shared_ptr<GeomAPI_Shape> aTangentCurve;
222   SketchPlugin_Tools::convertRefAttrToPointOrTangentCurve(
223       aPassedRef, aPassedAttr, aTangentCurve, aPassedPoint);
224
225   // Build a circle
226   GeomAlgoAPI_Circ2dBuilder aCircBuilder(SketchPlugin_Sketch::plane(sketch()));
227   aCircBuilder.setCenter(aCenter);
228   if (aTangentCurve) {
229     aCircBuilder.addTangentCurve(aTangentCurve);
230
231     AttributePoint2DPtr aPassedPntAttr =
232         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPassedAttr);
233     if (aPassedPntAttr)
234       aCircBuilder.setClosestPoint(aPassedPntAttr->pnt());
235   } else
236     aCircBuilder.addPassingPoint(aPassedPoint);
237
238   std::shared_ptr<GeomAPI_Circ2d> aCircle = aCircBuilder.circle();
239   if (aCircle) {
240     myCenter = aCircle->center();
241     myRadius = aCircle->radius();
242   }
243 }
244
245 void SketchPlugin_MacroCircle::fillByThreePoints()
246 {
247   std::string aPointAttr[3] = { FIRST_POINT_ID(),
248                                 SECOND_POINT_ID(),
249                                 THIRD_POINT_ID() };
250   std::string aPointRef[3] = { FIRST_POINT_REF_ID(),
251                                SECOND_POINT_REF_ID(),
252                                THIRD_POINT_REF_ID() };
253
254   GeomAlgoAPI_Circ2dBuilder aCircBuilder(SketchPlugin_Sketch::plane(sketch()));
255
256   for (int aPntIndex = 0; aPntIndex < 3; ++aPntIndex) {
257     AttributePtr aPassedAttr = attribute(aPointAttr[aPntIndex]);
258     if (!aPassedAttr->isInitialized())
259       break;
260
261     AttributeRefAttrPtr aPassedRef = refattr(aPointRef[aPntIndex]);
262     // calculate circle parameters
263     std::shared_ptr<GeomAPI_Pnt2d> aPassedPoint;
264     std::shared_ptr<GeomAPI_Shape> aTangentCurve;
265     SketchPlugin_Tools::convertRefAttrToPointOrTangentCurve(
266         aPassedRef, aPassedAttr, aTangentCurve, aPassedPoint);
267
268     if (aPassedPoint)
269       aCircBuilder.addPassingPoint(aPassedPoint);
270     else {
271       aCircBuilder.addTangentCurve(aTangentCurve);
272       AttributePoint2DPtr aPassedPoint =
273           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPassedAttr);
274       if (aPassedPoint)
275         aCircBuilder.setClosestPoint(aPassedPoint->pnt());
276     }
277   }
278
279   std::shared_ptr<GeomAPI_Circ2d> aCircle = aCircBuilder.circle();
280   if (aCircle) {
281     myCenter = aCircle->center();
282     myRadius = aCircle->radius();
283   }
284 }
285
286 void SketchPlugin_MacroCircle::fillByTwoPassedPoints()
287 {
288   std::string aPointAttr[2] = { FIRST_POINT_ID(),
289                                 SECOND_POINT_ID() };
290   std::string aPointRef[2] = { FIRST_POINT_REF_ID(),
291                                SECOND_POINT_REF_ID() };
292
293   GeomAlgoAPI_Circ2dBuilder aCircBuilder(SketchPlugin_Sketch::plane(sketch()));
294
295   std::shared_ptr<GeomAPI_Pnt2d> aPassedPoints[2]; // there is possible only two passed points
296   bool hasTangentCurve = false;
297   int aPntIndex = 0;
298   for (; aPntIndex < 2; ++aPntIndex) {
299     AttributePtr aPassedAttr = attribute(aPointAttr[aPntIndex]);
300     if (!aPassedAttr->isInitialized())
301       break;
302
303     AttributeRefAttrPtr aPassedRef = refattr(aPointRef[aPntIndex]);
304     // calculate circle parameters
305     std::shared_ptr<GeomAPI_Pnt2d> aPassedPoint;
306     std::shared_ptr<GeomAPI_Shape> aTangentCurve;
307     SketchPlugin_Tools::convertRefAttrToPointOrTangentCurve(
308         aPassedRef, aPassedAttr, aTangentCurve, aPassedPoint);
309
310     if (aPassedPoint) {
311       aCircBuilder.addPassingPoint(aPassedPoint);
312       aPassedPoints[aPntIndex] = aPassedPoint;
313     } else {
314       hasTangentCurve = true;
315       aCircBuilder.addTangentCurve(aTangentCurve);
316       // if the circle is tangent to any curve,
317       // the third point will be initialized by the tangent point
318       aCircBuilder.addPassingPoint(
319           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPassedAttr)->pnt());
320     }
321   }
322   if (aPntIndex <= 1)
323     return;
324
325   std::shared_ptr<GeomAPI_Circ2d> aCircle;
326
327   if (hasTangentCurve)
328     aCircle = aCircBuilder.circle();
329   else {
330     // the circle is defined by two points, calculate its parameters manually
331     std::shared_ptr<GeomAPI_Pnt2d> aCenter(new GeomAPI_Pnt2d(
332         (aPassedPoints[0]->x() + aPassedPoints[1]->x()) * 0.5,
333         (aPassedPoints[0]->y() + aPassedPoints[1]->y()) * 0.5));
334     aCircle = std::shared_ptr<GeomAPI_Circ2d>(new GeomAPI_Circ2d(aCenter, aPassedPoints[0]));
335   }
336
337   if (aCircle) {
338     myCenter = aCircle->center();
339     myRadius = aCircle->radius();
340   }
341 }
342
343 AISObjectPtr SketchPlugin_MacroCircle::getAISObject(AISObjectPtr thePrevious)
344 {
345   SketchPlugin_Sketch* aSketch = sketch();
346   if(!aSketch || !myCenter || myRadius == 0) {
347     return AISObjectPtr();
348   }
349
350   // Compute a circle in 3D view.
351   std::shared_ptr<GeomAPI_Pnt> aCenter(aSketch->to3D(myCenter->x(), myCenter->y()));
352   std::shared_ptr<GeomDataAPI_Dir> aNDir =
353       std::dynamic_pointer_cast<GeomDataAPI_Dir>(
354           aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
355   std::shared_ptr<GeomAPI_Dir> aNormal = aNDir->dir();
356   GeomShapePtr aCircleShape = GeomAlgoAPI_EdgeBuilder::lineCircle(aCenter, aNormal, myRadius);
357   GeomShapePtr aCenterPointShape = GeomAlgoAPI_PointBuilder::vertex(aCenter);
358   if(!aCircleShape.get() || !aCenterPointShape.get()) {
359     return AISObjectPtr();
360   }
361
362   std::list<std::shared_ptr<GeomAPI_Shape> > aShapes;
363   aShapes.push_back(aCircleShape);
364   aShapes.push_back(aCenterPointShape);
365
366   std::shared_ptr<GeomAPI_Shape> aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
367   AISObjectPtr anAIS = thePrevious;
368   if(!anAIS.get()) {
369     anAIS.reset(new GeomAPI_AISObject());
370   }
371   anAIS->createShape(aCompound);
372   return anAIS;
373 }
374
375 void SketchPlugin_MacroCircle::attributeChanged(const std::string& theID) {
376   double aRadius = 0.0;
377   // If circle type switched reset all attributes.
378   if(theID == CIRCLE_TYPE()) {
379     SketchPlugin_Tools::resetAttribute(this, CENTER_POINT_ID());
380     SketchPlugin_Tools::resetAttribute(this, CENTER_POINT_REF_ID());
381     SketchPlugin_Tools::resetAttribute(this, PASSED_POINT_ID());
382     SketchPlugin_Tools::resetAttribute(this, PASSED_POINT_REF_ID());
383     SketchPlugin_Tools::resetAttribute(this, FIRST_POINT_ID());
384     SketchPlugin_Tools::resetAttribute(this, FIRST_POINT_REF_ID());
385     SketchPlugin_Tools::resetAttribute(this, SECOND_POINT_ID());
386     SketchPlugin_Tools::resetAttribute(this, SECOND_POINT_REF_ID());
387     SketchPlugin_Tools::resetAttribute(this, THIRD_POINT_ID());
388     SketchPlugin_Tools::resetAttribute(this, THIRD_POINT_REF_ID());
389   } else if(theID == CENTER_POINT_ID() || theID == PASSED_POINT_ID() ||
390             theID == CENTER_POINT_REF_ID() || theID == PASSED_POINT_REF_ID())
391     fillByCenterAndPassed();
392   else if(theID == FIRST_POINT_ID() || theID == FIRST_POINT_REF_ID() ||
393           theID == SECOND_POINT_ID() || theID == SECOND_POINT_REF_ID() ||
394           theID == THIRD_POINT_ID() || theID == THIRD_POINT_REF_ID()) {
395     std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
396     int aNbInitialized = 0;
397     for(int i = 1; i <= 3; ++i) {
398       std::shared_ptr<GeomDataAPI_Point2D> aCurPnt =
399           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(POINT_ID(i)));
400       if(aCurPnt->isInitialized())
401         aPoints[aNbInitialized++] = aCurPnt->pnt();
402     }
403
404     if(aNbInitialized == 1)
405       return;
406     else if(aNbInitialized == 2)
407       fillByTwoPassedPoints();
408     else
409       fillByThreePoints();
410   }
411
412   AttributeDoublePtr aRadiusAttr = real(CIRCLE_RADIUS_ID());
413   bool aWasBlocked = data()->blockSendAttributeUpdated(true);
414   if(myCenter.get()) {
415     // center attribute is used in processEvent() to set reference to reentrant arc
416     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(CENTER_POINT_ID()))
417         ->setValue(myCenter);
418   }
419   aRadiusAttr->setValue(myRadius);
420   data()->blockSendAttributeUpdated(aWasBlocked, false);
421 }