Salome HOME
Improve behavior of MacroCircle
[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
13 #include <ModelAPI_AttributeDouble.h>
14 #include <ModelAPI_AttributeRefAttr.h>
15 #include <ModelAPI_AttributeString.h>
16 #include <ModelAPI_Session.h>
17 #include <ModelAPI_Validator.h>
18
19 #include <GeomDataAPI_Dir.h>
20 #include <GeomDataAPI_Point2D.h>
21
22 #include <GeomAPI_Circ2d.h>
23 #include <GeomAPI_Pnt2d.h>
24 #include <GeomAPI_Vertex.h>
25
26 #include <GeomAlgoAPI_CompoundBuilder.h>
27 #include <GeomAlgoAPI_EdgeBuilder.h>
28 #include <GeomAlgoAPI_PointBuilder.h>
29
30
31 const double tolerance = 1e-7;
32
33 namespace {
34   static const std::string& POINT_ID(int theIndex)
35   {
36     switch (theIndex) {
37       case 1: return SketchPlugin_MacroCircle::FIRST_POINT_ID();
38       case 2: return SketchPlugin_MacroCircle::SECOND_POINT_ID();
39       case 3: return SketchPlugin_MacroCircle::THIRD_POINT_ID();
40     }
41
42     static const std::string DUMMY;
43     return DUMMY;
44   }
45 }
46
47
48 SketchPlugin_MacroCircle::SketchPlugin_MacroCircle()
49 : SketchPlugin_SketchEntity(),
50   myRadius(0.0)
51 {
52 }
53
54 void SketchPlugin_MacroCircle::initAttributes()
55 {
56   data()->addAttribute(CIRCLE_TYPE(), ModelAPI_AttributeString::typeId());
57
58   data()->addAttribute(CENTER_POINT_ID(), GeomDataAPI_Point2D::typeId());
59   data()->addAttribute(CENTER_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
60   data()->addAttribute(PASSED_POINT_ID(), GeomDataAPI_Point2D::typeId());
61   data()->addAttribute(PASSED_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
62
63   data()->addAttribute(FIRST_POINT_ID(), GeomDataAPI_Point2D::typeId());
64   data()->addAttribute(SECOND_POINT_ID(), GeomDataAPI_Point2D::typeId());
65   data()->addAttribute(THIRD_POINT_ID(), GeomDataAPI_Point2D::typeId());
66   data()->addAttribute(FIRST_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
67   data()->addAttribute(SECOND_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
68   data()->addAttribute(THIRD_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
69
70   data()->addAttribute(CIRCLE_RADIUS_ID(), ModelAPI_AttributeDouble::typeId());
71   data()->addAttribute(AUXILIARY_ID(), ModelAPI_AttributeBoolean::typeId());
72
73   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), CENTER_POINT_REF_ID());
74   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PASSED_POINT_REF_ID());
75   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), FIRST_POINT_REF_ID());
76   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SECOND_POINT_REF_ID());
77   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), THIRD_POINT_REF_ID());
78 }
79
80 void SketchPlugin_MacroCircle::execute()
81 {
82   FeaturePtr aCircle = createCircleFeature();
83
84   std::string aType = string(CIRCLE_TYPE())->value();
85   if (aType == CIRCLE_TYPE_BY_CENTER_AND_PASSED_POINTS())
86     constraintsForCircleByCenterAndPassed(aCircle);
87   else if (aType == CIRCLE_TYPE_BY_THREE_POINTS())
88     constraintsForCircleByThreePoints(aCircle);
89 }
90
91 void SketchPlugin_MacroCircle::constraintsForCircleByCenterAndPassed(FeaturePtr theCircleFeature)
92 {
93   // Create constraints.
94   SketchPlugin_Tools::createConstraint(
95       this, CENTER_POINT_REF_ID(),
96       theCircleFeature->attribute(SketchPlugin_Circle::CENTER_ID()),
97       ObjectPtr(), false);
98   SketchPlugin_Tools::createConstraint(
99       this, PASSED_POINT_REF_ID(), AttributePtr(),
100       theCircleFeature->lastResult(), true);
101 }
102
103 void SketchPlugin_MacroCircle::constraintsForCircleByThreePoints(FeaturePtr theCircleFeature)
104 {
105   std::string aPointRef[3] = { FIRST_POINT_REF_ID(),
106                                SECOND_POINT_REF_ID(),
107                                THIRD_POINT_REF_ID() };
108
109   // Create constraints.
110   ResultPtr aCircleResult = theCircleFeature->lastResult();
111   for (int i = 0; i < 3; ++i)
112     SketchPlugin_Tools::createConstraint(this, aPointRef[i], AttributePtr(), aCircleResult, true);
113 }
114
115 FeaturePtr SketchPlugin_MacroCircle::createCircleFeature()
116 {
117   FeaturePtr aCircleFeature = sketch()->addFeature(SketchPlugin_Circle::ID());
118   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
119       aCircleFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(myCenter->x(),
120                                                                              myCenter->y());
121   aCircleFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(myRadius);
122   aCircleFeature->boolean(SketchPlugin_Circle::AUXILIARY_ID())
123                 ->setValue(boolean(AUXILIARY_ID())->value());
124   aCircleFeature->execute();
125   return aCircleFeature;
126 }
127
128 void SketchPlugin_MacroCircle::fillByCenterAndPassed()
129 {
130   AttributePtr aCenterAttr = attribute(CENTER_POINT_ID());
131   AttributePtr aPassedAttr = attribute(PASSED_POINT_ID());
132   if (!aCenterAttr->isInitialized() || !aPassedAttr->isInitialized())
133     return;
134
135   AttributeRefAttrPtr aPassedRef = refattr(PASSED_POINT_REF_ID());
136   // Calculate circle parameters
137   std::shared_ptr<GeomAPI_Pnt2d> aCenter =
138       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aCenterAttr)->pnt();
139   std::shared_ptr<GeomAPI_Pnt2d> aPassedPoint;
140   std::shared_ptr<GeomAPI_Shape> aTangentCurve;
141   SketchPlugin_Tools::convertRefAttrToPointOrTangentCurve(
142       aPassedRef, aPassedAttr, aTangentCurve, aPassedPoint);
143
144   // Build a circle
145   std::shared_ptr<GeomAPI_Circ2d> aCircle;
146   if (aTangentCurve) {
147     std::shared_ptr<GeomAPI_Ax3> anAxis = SketchPlugin_Sketch::plane(sketch());
148     aCircle = std::shared_ptr<GeomAPI_Circ2d>(new GeomAPI_Circ2d(aCenter, aTangentCurve, anAxis));
149   } else
150     aCircle = std::shared_ptr<GeomAPI_Circ2d>(new GeomAPI_Circ2d(aCenter, aPassedPoint));
151   if (aCircle->implPtr<char>()) {
152     myCenter = aCircle->center();
153     myRadius = aCircle->radius();
154   }
155 }
156
157 void SketchPlugin_MacroCircle::fillByThreePoints()
158 {
159   std::string aPointAttr[3] = { FIRST_POINT_ID(),
160                                 SECOND_POINT_ID(),
161                                 THIRD_POINT_ID() };
162   std::string aPointRef[3] = { FIRST_POINT_REF_ID(),
163                                SECOND_POINT_REF_ID(),
164                                THIRD_POINT_REF_ID() };
165   std::shared_ptr<GeomAPI_Interface> aPassedEntities[3];
166   for (int aPntIndex = 0; aPntIndex < 3; ++aPntIndex) {
167     AttributePtr aPassedAttr = attribute(aPointAttr[aPntIndex]);
168     if (!aPassedAttr->isInitialized())
169       break;
170
171     AttributeRefAttrPtr aPassedRef = refattr(aPointRef[aPntIndex]);
172     // calculate circle parameters
173     std::shared_ptr<GeomAPI_Pnt2d> aPassedPoint;
174     std::shared_ptr<GeomAPI_Shape> aTangentCurve;
175     SketchPlugin_Tools::convertRefAttrToPointOrTangentCurve(
176         aPassedRef, aPassedAttr, aTangentCurve, aPassedPoint);
177
178     if (aPassedPoint)
179       aPassedEntities[aPntIndex] = aPassedPoint;
180     else
181       aPassedEntities[aPntIndex] = aTangentCurve;
182   }
183
184   std::shared_ptr<GeomAPI_Ax3> anAxis = SketchPlugin_Sketch::plane(sketch());
185   std::shared_ptr<GeomAPI_Circ2d> aCircle = std::shared_ptr<GeomAPI_Circ2d>(
186       new GeomAPI_Circ2d(aPassedEntities[0], aPassedEntities[1], aPassedEntities[2], anAxis));
187   if (aCircle->implPtr<char>()) {
188     myCenter = aCircle->center();
189     myRadius = aCircle->radius();
190   }
191 }
192
193 void SketchPlugin_MacroCircle::fillByTwoPassedPoints()
194 {
195   std::string aPointAttr[2] = { FIRST_POINT_ID(),
196                                 SECOND_POINT_ID() };
197   std::string aPointRef[2] = { FIRST_POINT_REF_ID(),
198                                SECOND_POINT_REF_ID() };
199   std::shared_ptr<GeomAPI_Pnt2d> aPassedPoints[2]; // there is possible only two passed points
200   std::shared_ptr<GeomAPI_Interface> aPassedEntities[3];
201   int aPntIndex = 0;
202   for (; aPntIndex < 2; ++aPntIndex) {
203     AttributePtr aPassedAttr = attribute(aPointAttr[aPntIndex]);
204     if (!aPassedAttr->isInitialized())
205       break;
206
207     AttributeRefAttrPtr aPassedRef = refattr(aPointRef[aPntIndex]);
208     // calculate circle parameters
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     if (aPassedPoint) {
215       aPassedEntities[aPntIndex] = aPassedPoint;
216       aPassedPoints[aPntIndex] = aPassedPoint;
217     } else {
218       aPassedEntities[aPntIndex] = aTangentCurve;
219       // if the circle is tangent to any curve,
220       // the third point will be initialized by the tangent point
221       aPassedEntities[2] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPassedAttr)->pnt();
222     }
223   }
224   if (aPntIndex <= 1)
225     return;
226
227   std::shared_ptr<GeomAPI_Circ2d> aCircle;
228   if (aPassedEntities[2]) {
229     std::shared_ptr<GeomAPI_Ax3> anAxis = SketchPlugin_Sketch::plane(sketch());
230     aCircle = std::shared_ptr<GeomAPI_Circ2d>(
231         new GeomAPI_Circ2d(aPassedEntities[0], aPassedEntities[1], aPassedEntities[2], anAxis));
232   } else {
233     // the circle is defined by two points, calculate its parameters manually
234     std::shared_ptr<GeomAPI_Pnt2d> aCenter(new GeomAPI_Pnt2d(
235         (aPassedPoints[0]->x() + aPassedPoints[1]->x()) * 0.5,
236         (aPassedPoints[0]->y() + aPassedPoints[1]->y()) * 0.5));
237     aCircle = std::shared_ptr<GeomAPI_Circ2d>(new GeomAPI_Circ2d(aCenter, aPassedPoints[0]));
238   }
239   if (aCircle->implPtr<char>()) {
240     myCenter = aCircle->center();
241     myRadius = aCircle->radius();
242   }
243 }
244
245 AISObjectPtr SketchPlugin_MacroCircle::getAISObject(AISObjectPtr thePrevious)
246 {
247   SketchPlugin_Sketch* aSketch = sketch();
248   if(!aSketch || !myCenter || myRadius == 0) {
249     return AISObjectPtr();
250   }
251
252   // Compute a circle in 3D view.
253   std::shared_ptr<GeomAPI_Pnt> aCenter(aSketch->to3D(myCenter->x(), myCenter->y()));
254   std::shared_ptr<GeomDataAPI_Dir> aNDir =
255       std::dynamic_pointer_cast<GeomDataAPI_Dir>(
256           aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
257   std::shared_ptr<GeomAPI_Dir> aNormal = aNDir->dir();
258   GeomShapePtr aCircleShape = GeomAlgoAPI_EdgeBuilder::lineCircle(aCenter, aNormal, myRadius);
259   GeomShapePtr aCenterPointShape = GeomAlgoAPI_PointBuilder::vertex(aCenter);
260   if(!aCircleShape.get() || !aCenterPointShape.get()) {
261     return AISObjectPtr();
262   }
263
264   std::list<std::shared_ptr<GeomAPI_Shape> > aShapes;
265   aShapes.push_back(aCircleShape);
266   aShapes.push_back(aCenterPointShape);
267
268   std::shared_ptr<GeomAPI_Shape> aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
269   AISObjectPtr anAIS = thePrevious;
270   if(!anAIS.get()) {
271     anAIS.reset(new GeomAPI_AISObject());
272   }
273   anAIS->createShape(aCompound);
274   return anAIS;
275 }
276
277 void SketchPlugin_MacroCircle::attributeChanged(const std::string& theID) {
278   double aRadius = 0.0;
279   // If circle type switched reset all attributes.
280   if(theID == CIRCLE_TYPE()) {
281     SketchPlugin_Tools::resetAttribute(this, CENTER_POINT_ID());
282     SketchPlugin_Tools::resetAttribute(this, CENTER_POINT_REF_ID());
283     SketchPlugin_Tools::resetAttribute(this, PASSED_POINT_ID());
284     SketchPlugin_Tools::resetAttribute(this, PASSED_POINT_REF_ID());
285     SketchPlugin_Tools::resetAttribute(this, FIRST_POINT_ID());
286     SketchPlugin_Tools::resetAttribute(this, FIRST_POINT_REF_ID());
287     SketchPlugin_Tools::resetAttribute(this, SECOND_POINT_ID());
288     SketchPlugin_Tools::resetAttribute(this, SECOND_POINT_REF_ID());
289     SketchPlugin_Tools::resetAttribute(this, THIRD_POINT_ID());
290     SketchPlugin_Tools::resetAttribute(this, THIRD_POINT_REF_ID());
291   } else if(theID == CENTER_POINT_ID() || theID == PASSED_POINT_ID() ||
292             theID == CENTER_POINT_REF_ID() || theID == PASSED_POINT_REF_ID())
293     fillByCenterAndPassed();
294   else if(theID == FIRST_POINT_ID() || theID == FIRST_POINT_REF_ID() ||
295           theID == SECOND_POINT_ID() || theID == SECOND_POINT_REF_ID() ||
296           theID == THIRD_POINT_ID() || theID == THIRD_POINT_REF_ID()) {
297     std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
298     int aNbInitialized = 0;
299     for(int i = 1; i <= 3; ++i) {
300       std::shared_ptr<GeomDataAPI_Point2D> aCurPnt =
301           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(POINT_ID(i)));
302       if(aCurPnt->isInitialized())
303         aPoints[aNbInitialized++] = aCurPnt->pnt();
304     }
305
306     if(aNbInitialized == 1)
307       return;
308     else if(aNbInitialized == 2)
309       fillByTwoPassedPoints();
310     else
311       fillByThreePoints();
312   }
313
314   AttributeDoublePtr aRadiusAttr = real(CIRCLE_RADIUS_ID());
315   bool aWasBlocked = data()->blockSendAttributeUpdated(true);
316   aRadiusAttr->setValue(myRadius);
317   data()->blockSendAttributeUpdated(aWasBlocked, false);
318 }