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, 0));
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           aRefAttr->setAttr(anAttribute);
145         else if (anObject.get()) {
146           // if presentation of previous reentrant macro arc is used, the object is invalid,
147           // we should use result of previous feature of the message(Arc)
148           if (!anObject->data()->isValid()) {
149             FeaturePtr aCreatedFeature = aReentrantMessage->createdFeature();
150             anObject = aCreatedFeature->lastResult();
151           }
152           aRefAttr->setObject(anObject);
153         }
154       }
155     }
156     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
157   }
158   return aFilledAttributeName;
159 }
160
161 void SketchPlugin_MacroCircle::constraintsForCircleByCenterAndPassed(FeaturePtr theCircleFeature)
162 {
163   // Create constraints.
164   SketchPlugin_Tools::createConstraint(
165       this, CENTER_POINT_REF_ID(),
166       theCircleFeature->attribute(SketchPlugin_Circle::CENTER_ID()),
167       ObjectPtr(), false);
168   SketchPlugin_Tools::createConstraint(
169       this, PASSED_POINT_REF_ID(), AttributePtr(),
170       theCircleFeature->lastResult(), true);
171 }
172
173 void SketchPlugin_MacroCircle::constraintsForCircleByThreePoints(FeaturePtr theCircleFeature)
174 {
175   std::string aPointRef[3] = { FIRST_POINT_REF_ID(),
176                                SECOND_POINT_REF_ID(),
177                                THIRD_POINT_REF_ID() };
178
179   // Create constraints.
180   ResultPtr aCircleResult = theCircleFeature->lastResult();
181   for (int i = 0; i < 3; ++i)
182     SketchPlugin_Tools::createConstraint(this, aPointRef[i], AttributePtr(), aCircleResult, true);
183 }
184
185 FeaturePtr SketchPlugin_MacroCircle::createCircleFeature()
186 {
187   FeaturePtr aCircleFeature = sketch()->addFeature(SketchPlugin_Circle::ID());
188   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
189       aCircleFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(myCenter->x(),
190                                                                              myCenter->y());
191   aCircleFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(myRadius);
192   aCircleFeature->boolean(SketchPlugin_Circle::AUXILIARY_ID())
193                 ->setValue(boolean(AUXILIARY_ID())->value());
194   aCircleFeature->execute();
195   return aCircleFeature;
196 }
197
198 void SketchPlugin_MacroCircle::fillByCenterAndPassed()
199 {
200   AttributePtr aCenterAttr = attribute(CENTER_POINT_ID());
201   AttributePtr aPassedAttr = attribute(PASSED_POINT_ID());
202   if (!aCenterAttr->isInitialized() || !aPassedAttr->isInitialized())
203     return;
204
205   AttributeRefAttrPtr aPassedRef = refattr(PASSED_POINT_REF_ID());
206   // Calculate circle parameters
207   std::shared_ptr<GeomAPI_Pnt2d> aCenter =
208       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aCenterAttr)->pnt();
209   std::shared_ptr<GeomAPI_Pnt2d> aPassedPoint;
210   std::shared_ptr<GeomAPI_Shape> aTangentCurve;
211   SketchPlugin_Tools::convertRefAttrToPointOrTangentCurve(
212       aPassedRef, aPassedAttr, aTangentCurve, aPassedPoint);
213
214   // Build a circle
215   GeomAlgoAPI_Circ2dBuilder aCircBuilder(SketchPlugin_Sketch::plane(sketch()));
216   aCircBuilder.setCenter(aCenter);
217   if (aTangentCurve) {
218     aCircBuilder.addTangentCurve(aTangentCurve);
219
220     AttributePoint2DPtr aPassedPntAttr =
221         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPassedAttr);
222     if (aPassedPntAttr)
223       aCircBuilder.setClosestPoint(aPassedPntAttr->pnt());
224   } else
225     aCircBuilder.addPassingPoint(aPassedPoint);
226
227   std::shared_ptr<GeomAPI_Circ2d> aCircle = aCircBuilder.circle();
228   if (aCircle) {
229     myCenter = aCircle->center();
230     myRadius = aCircle->radius();
231   }
232 }
233
234 void SketchPlugin_MacroCircle::fillByThreePoints()
235 {
236   std::string aPointAttr[3] = { FIRST_POINT_ID(),
237                                 SECOND_POINT_ID(),
238                                 THIRD_POINT_ID() };
239   std::string aPointRef[3] = { FIRST_POINT_REF_ID(),
240                                SECOND_POINT_REF_ID(),
241                                THIRD_POINT_REF_ID() };
242
243   GeomAlgoAPI_Circ2dBuilder aCircBuilder(SketchPlugin_Sketch::plane(sketch()));
244
245   for (int aPntIndex = 0; aPntIndex < 3; ++aPntIndex) {
246     AttributePtr aPassedAttr = attribute(aPointAttr[aPntIndex]);
247     if (!aPassedAttr->isInitialized())
248       break;
249
250     AttributeRefAttrPtr aPassedRef = refattr(aPointRef[aPntIndex]);
251     // calculate circle parameters
252     std::shared_ptr<GeomAPI_Pnt2d> aPassedPoint;
253     std::shared_ptr<GeomAPI_Shape> aTangentCurve;
254     SketchPlugin_Tools::convertRefAttrToPointOrTangentCurve(
255         aPassedRef, aPassedAttr, aTangentCurve, aPassedPoint);
256
257     if (aPassedPoint)
258       aCircBuilder.addPassingPoint(aPassedPoint);
259     else {
260       aCircBuilder.addTangentCurve(aTangentCurve);
261       AttributePoint2DPtr aPassedPoint =
262           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPassedAttr);
263       if (aPassedPoint)
264         aCircBuilder.setClosestPoint(aPassedPoint->pnt());
265     }
266   }
267
268   std::shared_ptr<GeomAPI_Circ2d> aCircle = aCircBuilder.circle();
269   if (aCircle) {
270     myCenter = aCircle->center();
271     myRadius = aCircle->radius();
272   }
273 }
274
275 void SketchPlugin_MacroCircle::fillByTwoPassedPoints()
276 {
277   std::string aPointAttr[2] = { FIRST_POINT_ID(),
278                                 SECOND_POINT_ID() };
279   std::string aPointRef[2] = { FIRST_POINT_REF_ID(),
280                                SECOND_POINT_REF_ID() };
281
282   GeomAlgoAPI_Circ2dBuilder aCircBuilder(SketchPlugin_Sketch::plane(sketch()));
283
284   std::shared_ptr<GeomAPI_Pnt2d> aPassedPoints[2]; // there is possible only two passed points
285   bool hasTangentCurve = false;
286   int aPntIndex = 0;
287   for (; aPntIndex < 2; ++aPntIndex) {
288     AttributePtr aPassedAttr = attribute(aPointAttr[aPntIndex]);
289     if (!aPassedAttr->isInitialized())
290       break;
291
292     AttributeRefAttrPtr aPassedRef = refattr(aPointRef[aPntIndex]);
293     // calculate circle parameters
294     std::shared_ptr<GeomAPI_Pnt2d> aPassedPoint;
295     std::shared_ptr<GeomAPI_Shape> aTangentCurve;
296     SketchPlugin_Tools::convertRefAttrToPointOrTangentCurve(
297         aPassedRef, aPassedAttr, aTangentCurve, aPassedPoint);
298
299     if (aPassedPoint) {
300       aCircBuilder.addPassingPoint(aPassedPoint);
301       aPassedPoints[aPntIndex] = aPassedPoint;
302     } else {
303       hasTangentCurve = true;
304       aCircBuilder.addTangentCurve(aTangentCurve);
305       // if the circle is tangent to any curve,
306       // the third point will be initialized by the tangent point
307       aCircBuilder.addPassingPoint(
308           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPassedAttr)->pnt());
309     }
310   }
311   if (aPntIndex <= 1)
312     return;
313
314   std::shared_ptr<GeomAPI_Circ2d> aCircle;
315
316   if (hasTangentCurve)
317     aCircle = aCircBuilder.circle();
318   else {
319     // the circle is defined by two points, calculate its parameters manually
320     std::shared_ptr<GeomAPI_Pnt2d> aCenter(new GeomAPI_Pnt2d(
321         (aPassedPoints[0]->x() + aPassedPoints[1]->x()) * 0.5,
322         (aPassedPoints[0]->y() + aPassedPoints[1]->y()) * 0.5));
323     aCircle = std::shared_ptr<GeomAPI_Circ2d>(new GeomAPI_Circ2d(aCenter, aPassedPoints[0]));
324   }
325
326   if (aCircle) {
327     myCenter = aCircle->center();
328     myRadius = aCircle->radius();
329   }
330 }
331
332 AISObjectPtr SketchPlugin_MacroCircle::getAISObject(AISObjectPtr thePrevious)
333 {
334   SketchPlugin_Sketch* aSketch = sketch();
335   if(!aSketch || !myCenter || myRadius == 0) {
336     return AISObjectPtr();
337   }
338
339   // Compute a circle in 3D view.
340   std::shared_ptr<GeomAPI_Pnt> aCenter(aSketch->to3D(myCenter->x(), myCenter->y()));
341   std::shared_ptr<GeomDataAPI_Dir> aNDir =
342       std::dynamic_pointer_cast<GeomDataAPI_Dir>(
343           aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
344   std::shared_ptr<GeomAPI_Dir> aNormal = aNDir->dir();
345   GeomShapePtr aCircleShape = GeomAlgoAPI_EdgeBuilder::lineCircle(aCenter, aNormal, myRadius);
346   GeomShapePtr aCenterPointShape = GeomAlgoAPI_PointBuilder::vertex(aCenter);
347   if(!aCircleShape.get() || !aCenterPointShape.get()) {
348     return AISObjectPtr();
349   }
350
351   std::list<std::shared_ptr<GeomAPI_Shape> > aShapes;
352   aShapes.push_back(aCircleShape);
353   aShapes.push_back(aCenterPointShape);
354
355   std::shared_ptr<GeomAPI_Shape> aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
356   AISObjectPtr anAIS = thePrevious;
357   if(!anAIS.get()) {
358     anAIS.reset(new GeomAPI_AISObject());
359   }
360   anAIS->createShape(aCompound);
361   return anAIS;
362 }
363
364 void SketchPlugin_MacroCircle::attributeChanged(const std::string& theID) {
365   double aRadius = 0.0;
366   // If circle type switched reset all attributes.
367   if(theID == CIRCLE_TYPE()) {
368     SketchPlugin_Tools::resetAttribute(this, CENTER_POINT_ID());
369     SketchPlugin_Tools::resetAttribute(this, CENTER_POINT_REF_ID());
370     SketchPlugin_Tools::resetAttribute(this, PASSED_POINT_ID());
371     SketchPlugin_Tools::resetAttribute(this, PASSED_POINT_REF_ID());
372     SketchPlugin_Tools::resetAttribute(this, FIRST_POINT_ID());
373     SketchPlugin_Tools::resetAttribute(this, FIRST_POINT_REF_ID());
374     SketchPlugin_Tools::resetAttribute(this, SECOND_POINT_ID());
375     SketchPlugin_Tools::resetAttribute(this, SECOND_POINT_REF_ID());
376     SketchPlugin_Tools::resetAttribute(this, THIRD_POINT_ID());
377     SketchPlugin_Tools::resetAttribute(this, THIRD_POINT_REF_ID());
378   } else if(theID == CENTER_POINT_ID() || theID == PASSED_POINT_ID() ||
379             theID == CENTER_POINT_REF_ID() || theID == PASSED_POINT_REF_ID())
380     fillByCenterAndPassed();
381   else if(theID == FIRST_POINT_ID() || theID == FIRST_POINT_REF_ID() ||
382           theID == SECOND_POINT_ID() || theID == SECOND_POINT_REF_ID() ||
383           theID == THIRD_POINT_ID() || theID == THIRD_POINT_REF_ID()) {
384     std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
385     int aNbInitialized = 0;
386     for(int i = 1; i <= 3; ++i) {
387       std::shared_ptr<GeomDataAPI_Point2D> aCurPnt =
388           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(POINT_ID(i)));
389       if(aCurPnt->isInitialized())
390         aPoints[aNbInitialized++] = aCurPnt->pnt();
391     }
392
393     if(aNbInitialized == 1)
394       return;
395     else if(aNbInitialized == 2)
396       fillByTwoPassedPoints();
397     else
398       fillByThreePoints();
399   }
400
401   AttributeDoublePtr aRadiusAttr = real(CIRCLE_RADIUS_ID());
402   bool aWasBlocked = data()->blockSendAttributeUpdated(true);
403   aRadiusAttr->setValue(myRadius);
404   data()->blockSendAttributeUpdated(aWasBlocked, false);
405 }