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