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