Salome HOME
Creating a circle by 3 points
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_Circle.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:        SketchPlugin_Circle.cpp
4 // Created:     26 May 2014
5 // Author:      Artem ZHIDKOV
6
7 #include "SketchPlugin_Circle.h"
8 #include "SketchPlugin_Sketch.h"
9 #include <ModelAPI_Data.h>
10 #include <ModelAPI_ResultConstruction.h>
11 #include <ModelAPI_AttributeSelection.h>
12 #include <ModelAPI_Validator.h>
13 #include <ModelAPI_AttributeDouble.h>
14 #include <ModelAPI_AttributeString.h>
15 #include <ModelAPI_Session.h>
16
17 #include <GeomAPI_Pnt2d.h>
18 #include <GeomAPI_Circ.h>
19 #include <GeomAPI_XY.h>
20 #include <GeomDataAPI_Point2D.h>
21 #include <GeomDataAPI_Dir.h>
22 #include <GeomAlgoAPI_PointBuilder.h>
23 #include <GeomAlgoAPI_EdgeBuilder.h>
24 #include <GeomAlgoAPI_CompoundBuilder.h>
25
26 namespace {
27   static const std::string& CIRCLE_TYPE()
28   {
29     static const std::string TYPE("CircleType");
30     return TYPE;
31   }
32   static const std::string CIRCLE_TYPE_CENTER_AND_RADIUS()
33   {
34     static const std::string TYPE("CenterRadius");
35     return TYPE;
36   }
37   static const std::string CIRCLE_TYPE_THREE_POINTS()
38   {
39     static const std::string TYPE("ThreePoints");
40     return TYPE;
41   }
42
43   static const std::string& FIRST_POINT_ID()
44   {
45     static const std::string FIRST_PNT("FirstPoint");
46     return FIRST_PNT;
47   }
48   static const std::string& SECOND_POINT_ID()
49   {
50     static const std::string SECOND_PNT("SecondPoint");
51     return SECOND_PNT;
52   }
53   static const std::string& THIRD_POINT_ID()
54   {
55     static const std::string THIRD_PNT("ThirdPoint");
56     return THIRD_PNT;
57   }
58   static const std::string& POINT_ID(int theIndex)
59   {
60     switch (theIndex) {
61     case 1: return FIRST_POINT_ID();
62     case 2: return SECOND_POINT_ID();
63     case 3: return THIRD_POINT_ID();
64     }
65
66     static const std::string DUMMY;
67     return DUMMY;
68   }
69 }
70
71 static void calculateCircleOnThreePoints(const std::shared_ptr<GeomAPI_Pnt2d>& theFirstPnt,
72                                          const std::shared_ptr<GeomAPI_Pnt2d>& theSecondPnt,
73                                          const std::shared_ptr<GeomAPI_Pnt2d>& theThirdPnt,
74                                                std::shared_ptr<GeomAPI_Pnt2d>& theCenter,
75                                                double&                         theRadius);
76
77
78 SketchPlugin_Circle::SketchPlugin_Circle()
79     : SketchPlugin_SketchEntity()
80 {
81 }
82
83 void SketchPlugin_Circle::initDerivedClassAttributes()
84 {
85   data()->addAttribute(CENTER_ID(), GeomDataAPI_Point2D::typeId());
86   data()->addAttribute(RADIUS_ID(), ModelAPI_AttributeDouble::typeId());
87   data()->addAttribute(EXTERNAL_ID(), ModelAPI_AttributeSelection::typeId());
88   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EXTERNAL_ID());
89
90   data()->addAttribute(CIRCLE_TYPE(), ModelAPI_AttributeString::typeId());
91   data()->addAttribute(FIRST_POINT_ID(), GeomDataAPI_Point2D::typeId());
92   data()->addAttribute(SECOND_POINT_ID(), GeomDataAPI_Point2D::typeId());
93   data()->addAttribute(THIRD_POINT_ID(), GeomDataAPI_Point2D::typeId());
94   std::dynamic_pointer_cast<ModelAPI_AttributeString>(
95       data()->attribute(CIRCLE_TYPE()))->setValue(CIRCLE_TYPE_CENTER_AND_RADIUS());
96 }
97
98 void SketchPlugin_Circle::execute()
99 {
100   SketchPlugin_Sketch* aSketch = sketch();
101   if (aSketch) {
102     std::list<std::shared_ptr<GeomAPI_Shape> > aShapes;
103
104     // compute a circle point in 3D view
105     std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
106         GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
107     AttributeDoublePtr aRadiusAttr = 
108       std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(data()->attribute(RADIUS_ID()));
109     if (aCenterAttr->isInitialized() && aRadiusAttr->isInitialized()) {
110       std::shared_ptr<GeomAPI_Pnt> aCenter(aSketch->to3D(aCenterAttr->x(), aCenterAttr->y()));
111       //std::cout<<"Execute circle "<<aCenter->x()<<" "<<aCenter->y()<<" "<<aCenter->z()<<std::endl;
112       // make a visible point
113       SketchPlugin_Sketch::createPoint2DResult(this, sketch(), CENTER_ID(), 0);
114
115       // make a visible circle
116       std::shared_ptr<GeomDataAPI_Dir> aNDir = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
117         aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
118       std::shared_ptr<GeomAPI_Dir> aNormal(new GeomAPI_Dir(aNDir->x(), aNDir->y(), aNDir->z()));
119       // compute the circle radius
120       double aRadius = aRadiusAttr->value();
121
122       std::shared_ptr<GeomAPI_Shape> aCircleShape = GeomAlgoAPI_EdgeBuilder::lineCircle(
123         aCenter, aNormal, aRadius);
124       aShapes.push_back(aCircleShape);
125       std::shared_ptr<ModelAPI_ResultConstruction> aConstr2 = document()->createConstruction(
126         data(), 1);
127       aConstr2->setShape(aCircleShape);
128       aConstr2->setIsInHistory(false);
129       setResult(aConstr2, 1);
130     }
131   }
132 }
133
134 AISObjectPtr SketchPlugin_Circle::getAISObject(AISObjectPtr thePrevious)
135 {
136   SketchPlugin_Sketch* aSketch = sketch();
137   if (aSketch) {
138     // compute a circle point in 3D view
139     std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
140         GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
141     AttributeDoublePtr aRadiusAttr = 
142         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(attribute(RADIUS_ID()));
143     if (aCenterAttr->isInitialized() && aRadiusAttr->isInitialized()) {
144         std::shared_ptr<GeomAPI_Pnt> aCenter(aSketch->to3D(aCenterAttr->x(), aCenterAttr->y()));
145
146         // make a visible circle
147         std::shared_ptr<GeomDataAPI_Dir> aNDir = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
148             aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
149         std::shared_ptr<GeomAPI_Dir> aNormal = aNDir->dir();
150
151         double aRadius = aRadiusAttr->value();
152         std::shared_ptr<GeomAPI_Shape> aCircleShape = GeomAlgoAPI_EdgeBuilder::lineCircle(
153             aCenter, aNormal, aRadius);
154         if (aCircleShape && aRadius != 0) {
155           std::list<std::shared_ptr<GeomAPI_Shape> > aShapes;
156           // make a visible point
157           std::shared_ptr<GeomAPI_Shape> aCenterPointShape = GeomAlgoAPI_PointBuilder::point(aCenter);
158           aShapes.push_back(aCenterPointShape);
159           aShapes.push_back(aCircleShape);
160
161           std::shared_ptr<GeomAPI_Shape> aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
162           AISObjectPtr anAIS = thePrevious;
163           if (!anAIS)
164             anAIS = AISObjectPtr(new GeomAPI_AISObject);
165           anAIS->createShape(aCompound);
166           anAIS->setWidth(3);
167           return anAIS;
168         }
169     }
170   }
171   return AISObjectPtr();
172 }
173
174 void SketchPlugin_Circle::move(double theDeltaX, double theDeltaY)
175 {
176   std::shared_ptr<ModelAPI_Data> aData = data();
177   if (!aData->isValid())
178     return;
179
180   std::shared_ptr<GeomDataAPI_Point2D> aPoint1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
181       aData->attribute(CENTER_ID()));
182   aPoint1->move(theDeltaX, theDeltaY);
183 }
184
185 bool SketchPlugin_Circle::isFixed() {
186   return data()->selection(EXTERNAL_ID())->context().get() != NULL;
187 }
188
189 void SketchPlugin_Circle::attributeChanged(const std::string& theID) {
190   // the second condition for unability to move external segments anywhere
191   if (theID == EXTERNAL_ID() || isFixed()) {
192     std::shared_ptr<GeomAPI_Shape> aSelection = data()->selection(EXTERNAL_ID())->value();
193     // update arguments due to the selection value
194     if (aSelection && !aSelection->isNull() && aSelection->isEdge()) {
195       std::shared_ptr<GeomAPI_Edge> anEdge( new GeomAPI_Edge(aSelection));
196       std::shared_ptr<GeomAPI_Circ> aCirc = anEdge->circle();
197       std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = 
198         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(CENTER_ID()));
199       aCenterAttr->setValue(sketch()->to2D(aCirc->center()));
200       real(RADIUS_ID())->setValue(aCirc->radius());
201     }
202   }
203   else if (theID == CENTER_ID() || theID == RADIUS_ID()) {
204     std::string aType = std::dynamic_pointer_cast<ModelAPI_AttributeString>(
205       data()->attribute(CIRCLE_TYPE()))->value();
206     if (aType == CIRCLE_TYPE_THREE_POINTS())
207       return;
208
209     std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr =
210         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(CENTER_ID()));
211     if (!aCenterAttr->isInitialized())
212       return;
213     AttributeDoublePtr aRadiusAttr = 
214       std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(attribute(RADIUS_ID()));
215     if (!aRadiusAttr->isInitialized())
216       return;
217
218     // check the execute() was called and the shape was built
219     if (!lastResult())
220       return;
221
222     data()->blockSendAttributeUpdated(true);
223     std::shared_ptr<GeomDataAPI_Point2D> aFirstPnt =
224         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(FIRST_POINT_ID()));
225     std::shared_ptr<GeomDataAPI_Point2D> aSecondPnt =
226         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(SECOND_POINT_ID()));
227     std::shared_ptr<GeomDataAPI_Point2D> aThirdPnt =
228         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(THIRD_POINT_ID()));
229     double aRadius = aRadiusAttr->value();
230     aFirstPnt->setValue(aCenterAttr->x() + aRadius, aCenterAttr->y());
231     aSecondPnt->setValue(aCenterAttr->x(), aCenterAttr->y() + aRadius);
232     aThirdPnt->setValue(aCenterAttr->x() - aRadius, aCenterAttr->y());
233     data()->blockSendAttributeUpdated(false);
234   }
235   else if (theID == FIRST_POINT_ID() || theID == SECOND_POINT_ID() || theID == THIRD_POINT_ID()) {
236     std::string aType = std::dynamic_pointer_cast<ModelAPI_AttributeString>(
237       data()->attribute(CIRCLE_TYPE()))->value();
238     if (aType == CIRCLE_TYPE_CENTER_AND_RADIUS())
239       return;
240
241     data()->blockSendAttributeUpdated(true);
242
243     std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
244     int aNbInitialized = 0;
245     for (int i = 1; i <= 3; ++i) {
246       std::shared_ptr<GeomDataAPI_Point2D> aCurPnt =
247           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(POINT_ID(i)));
248       if (aCurPnt->isInitialized())
249         aPoints[aNbInitialized++] = aCurPnt->pnt();
250     }
251
252     std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
253         GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
254     AttributeDoublePtr aRadiusAttr = 
255       std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(data()->attribute(RADIUS_ID()));
256
257     if (aNbInitialized == 1)
258       aCenterAttr->setValue(aPoints[0]->x(), aPoints[0]->y());
259     else if (aNbInitialized == 2) {
260       std::shared_ptr<GeomAPI_XY> aCoord =
261           aPoints[0]->xy()->added(aPoints[1]->xy())->multiplied(0.5);
262       double aRadius = aPoints[0]->distance(aPoints[1]) * 0.5;
263       aCenterAttr->setValue(aCoord->x(), aCoord->y());
264       aRadiusAttr->setValue(aRadius);
265     } else {
266       std::shared_ptr<GeomAPI_Pnt2d> aCenter;
267       double aRadius;
268       calculateCircleOnThreePoints(aPoints[0], aPoints[1], aPoints[2], aCenter, aRadius);
269       if (aCenter) {
270         aCenterAttr->setValue(aCenter->x(), aCenter->y());
271         aRadiusAttr->setValue(aRadius);
272       }
273     }
274
275     data()->blockSendAttributeUpdated(false);
276   }
277 }
278
279
280
281
282 // ==========   Auxiliary functions   =========================
283 void calculateCircleOnThreePoints(const std::shared_ptr<GeomAPI_Pnt2d>& theFirstPnt,
284                                   const std::shared_ptr<GeomAPI_Pnt2d>& theSecondPnt,
285                                   const std::shared_ptr<GeomAPI_Pnt2d>& theThirdPnt,
286                                         std::shared_ptr<GeomAPI_Pnt2d>& theCenter,
287                                         double&                         theRadius)
288 {
289   std::shared_ptr<GeomAPI_XY> aVec12 = theSecondPnt->xy()->decreased(theFirstPnt->xy());
290   std::shared_ptr<GeomAPI_XY> aVec23 = theThirdPnt->xy()->decreased(theSecondPnt->xy());
291   std::shared_ptr<GeomAPI_XY> aVec31 = theFirstPnt->xy()->decreased(theThirdPnt->xy());
292   // square of parallelogram
293   double aSquare2 = aVec12->cross(aVec23);
294   aSquare2 *= aSquare2 * 2.0;
295   if (aSquare2 < 1.e-20)
296     return;
297   // coefficients to calculate center
298   double aCoeff1 = aVec23->dot(aVec23) / aSquare2 * aVec12->dot(aVec31->multiplied(-1.0));
299   double aCoeff2 = aVec31->dot(aVec31) / aSquare2 * aVec23->dot(aVec12->multiplied(-1.0));
300   double aCoeff3 = aVec12->dot(aVec12) / aSquare2 * aVec31->dot(aVec23->multiplied(-1.0));
301   // center
302   std::shared_ptr<GeomAPI_XY> aCenter = theFirstPnt->xy()->multiplied(aCoeff1)->added(
303       theSecondPnt->xy()->multiplied(aCoeff2))->added(theThirdPnt->xy()->multiplied(aCoeff3));
304   theCenter = std::shared_ptr<GeomAPI_Pnt2d>(new GeomAPI_Pnt2d(aCenter));
305   // radius
306   theRadius = theFirstPnt->distance(theCenter);
307 }