Salome HOME
312297733b333c670c75677006ee0d191e121d0e
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_Arc.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:        SketchPlugin_Arc.cpp
4 // Created:     26 Apr 2014
5 // Author:      Artem ZHIDKOV
6
7 #include "SketchPlugin_Arc.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_Session.h>
14
15 #include <GeomAPI_Circ2d.h>
16 #include <GeomAPI_Circ.h>
17 #include <GeomAPI_Pnt2d.h>
18 #include <GeomDataAPI_Point2D.h>
19 #include <GeomDataAPI_Dir.h>
20 #include <GeomAlgoAPI_PointBuilder.h>
21 #include <GeomAlgoAPI_EdgeBuilder.h>
22 #include <GeomAlgoAPI_CompoundBuilder.h>
23 // for sqrt on Linux
24 #include <math.h>
25
26 const double tolerance = 1e-7;
27
28 SketchPlugin_Arc::SketchPlugin_Arc()
29     : SketchPlugin_SketchEntity()
30 {
31   myStartUpdate = false;
32   myEndUpdate = false;
33   // default values
34   myXEndBefore = 0;
35   myYEndBefore = 0;
36 }
37
38 void SketchPlugin_Arc::initAttributes()
39 {
40   SketchPlugin_SketchEntity::initAttributes();
41
42   data()->addAttribute(CENTER_ID(), GeomDataAPI_Point2D::typeId());
43   data()->addAttribute(START_ID(), GeomDataAPI_Point2D::typeId());
44   std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
45     GeomDataAPI_Point2D>(data()->addAttribute(END_ID(), GeomDataAPI_Point2D::typeId()));
46   data()->addAttribute(EXTERNAL_ID(), ModelAPI_AttributeSelection::typeId());
47   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EXTERNAL_ID());
48
49   // get the initial values
50   if (anEndAttr->isInitialized()) {
51     myXEndBefore = anEndAttr->x();
52     myYEndBefore = anEndAttr->y();
53   }
54 }
55
56 void SketchPlugin_Arc::execute()
57 {
58   SketchPlugin_Sketch* aSketch = sketch();
59   // result for the arc is set only when all obligatory attributes are initialized,
60   // otherwise AIS object is used to visualize the arc's preview
61   if (aSketch && isFeatureValid()) {
62     // compute a circle point in 3D view
63     std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
64         GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
65     // compute the arc start point
66     std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
67         GeomDataAPI_Point2D>(data()->attribute(START_ID()));
68
69     std::shared_ptr<GeomAPI_Pnt> aCenter(aSketch->to3D(aCenterAttr->x(), aCenterAttr->y()));
70     // make a visible point
71     std::shared_ptr<GeomAPI_Shape> aCenterPointShape = GeomAlgoAPI_PointBuilder::point(aCenter);
72     std::shared_ptr<ModelAPI_ResultConstruction> aConstr1 = document()->createConstruction(
73         data(), 0);
74     aConstr1->setShape(aCenterPointShape);
75     aConstr1->setIsInHistory(false);
76     setResult(aConstr1, 0);
77
78     // make a visible circle
79     std::shared_ptr<GeomDataAPI_Dir> aNDir = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
80         aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
81     std::shared_ptr<GeomAPI_Dir> aNormal = aNDir->dir();
82     std::shared_ptr<GeomAPI_Pnt> aStartPoint(aSketch->to3D(aStartAttr->x(), aStartAttr->y()));
83
84     // compute and change the arc end point
85     std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
86         GeomDataAPI_Point2D>(data()->attribute(END_ID()));
87     /* must be automatically done in attributeChanged
88     std::shared_ptr<GeomAPI_Circ2d> aCircleForArc(
89         new GeomAPI_Circ2d(aCenterAttr->pnt(), aStartAttr->pnt()));
90     std::shared_ptr<GeomAPI_Pnt2d> aProjection = aCircleForArc->project(anEndAttr->pnt());
91     if (aProjection && anEndAttr->pnt()->distance(aProjection) > tolerance)
92       anEndAttr->setValue(aProjection);
93     */
94     std::shared_ptr<GeomAPI_Pnt> aEndPoint(aSketch->to3D(anEndAttr->x(), anEndAttr->y()));
95
96     std::shared_ptr<GeomAPI_Shape> aCircleShape = GeomAlgoAPI_EdgeBuilder::lineCircleArc(
97         aCenter, aStartPoint, aEndPoint, aNormal);
98     if (aCircleShape) {
99       std::shared_ptr<ModelAPI_ResultConstruction> aConstr2 = document()->createConstruction(
100           data(), 1);
101       aConstr2->setShape(aCircleShape);
102       aConstr2->setIsInHistory(false);
103       setResult(aConstr2, 1);
104     }
105   }
106 }
107
108 AISObjectPtr SketchPlugin_Arc::getAISObject(AISObjectPtr thePrevious)
109 {
110   SketchPlugin_Sketch* aSketch = sketch();
111   if (aSketch) {
112     // if the feature is valid, the execute() method should be performed, AIS object is empty
113     if (!isFeatureValid()) {
114       // compute a circle point in 3D view
115       std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
116           GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
117       if (aCenterAttr->isInitialized()) {
118         std::shared_ptr<GeomAPI_Pnt> aCenter(aSketch->to3D(aCenterAttr->x(), aCenterAttr->y()));
119
120         std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
121             GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::START_ID()));
122         if (aStartAttr->isInitialized()) {
123           // make a visible circle
124           std::shared_ptr<GeomDataAPI_Dir> aNDir = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
125               aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
126           bool aHasPlane = aNDir && !(aNDir->x() == 0 && aNDir->y() == 0 && aNDir->z() == 0);
127           if (aHasPlane) {
128             std::shared_ptr<GeomAPI_Dir> aNormal = aNDir->dir();
129             std::shared_ptr<GeomAPI_Pnt> aStartPoint(aSketch->to3D(aStartAttr->x(), aStartAttr->y()));
130             std::shared_ptr<GeomAPI_Shape> aCircleShape = GeomAlgoAPI_EdgeBuilder::lineCircleArc(
131                                                             aCenter, aStartPoint, aStartPoint, aNormal);
132             if (aCircleShape) {
133               std::list<std::shared_ptr<GeomAPI_Shape> > aShapes;
134               // make a visible point
135               std::shared_ptr<GeomAPI_Shape> aCenterPointShape = GeomAlgoAPI_PointBuilder::point(aCenter);
136               aShapes.push_back(aCenterPointShape);
137
138               aShapes.push_back(aCircleShape);
139               if (!aShapes.empty())
140               {
141                 std::shared_ptr<GeomAPI_Shape> aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
142                 AISObjectPtr anAIS = thePrevious;
143                 if (!anAIS)
144                   anAIS = AISObjectPtr(new GeomAPI_AISObject);
145                 anAIS->createShape(aCompound);
146                 anAIS->setWidth(3);
147                 return anAIS;
148               }
149             }
150           }
151         }
152       }
153     }
154   }
155   return AISObjectPtr();
156 }
157
158 void SketchPlugin_Arc::move(double theDeltaX, double theDeltaY)
159 {
160   std::shared_ptr<ModelAPI_Data> aData = data();
161   if (!aData->isValid())
162     return;
163
164   myStartUpdate = true;
165   myEndUpdate = true;
166   std::shared_ptr<GeomDataAPI_Point2D> aPoint2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
167       aData->attribute(SketchPlugin_Arc::START_ID()));
168   aPoint2->move(theDeltaX, theDeltaY);
169
170   std::shared_ptr<GeomDataAPI_Point2D> aPoint3 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
171       aData->attribute(SketchPlugin_Arc::END_ID()));
172   aPoint3->move(theDeltaX, theDeltaY);
173   myStartUpdate = false;
174   myEndUpdate = false;
175
176   std::shared_ptr<GeomDataAPI_Point2D> aPoint1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
177       aData->attribute(SketchPlugin_Arc::CENTER_ID()));
178   aPoint1->move(theDeltaX, theDeltaY);
179 }
180
181 double SketchPlugin_Arc::distanceToPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
182 {
183   double aDelta = 0;
184   std::shared_ptr<ModelAPI_Data> aData = data();
185
186   std::shared_ptr<GeomDataAPI_Point2D> aPoint1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
187       aData->attribute(SketchPlugin_Arc::CENTER_ID()));
188   aDelta = aPoint1->pnt()->distance(thePoint);
189
190   std::shared_ptr<GeomDataAPI_Point2D> aPoint2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
191       aData->attribute(SketchPlugin_Arc::START_ID()));
192   double aDistance = aPoint2->pnt()->distance(thePoint);
193   if (aDelta < aDistance)
194     aDelta = aDistance;
195
196   std::shared_ptr<GeomDataAPI_Point2D> aPoint3 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
197       aData->attribute(SketchPlugin_Arc::END_ID()));
198   aDistance = aPoint3->pnt()->distance(thePoint);
199   if (aDelta < aDistance)
200     aDelta = aDistance;
201
202   return aDelta;
203 }
204
205 bool SketchPlugin_Arc::isFixed() {
206   return data()->selection(EXTERNAL_ID())->context().get() != NULL;
207 }
208
209 bool SketchPlugin_Arc::isFeatureValid()
210 {
211   std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
212       GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::CENTER_ID()));
213   std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
214       GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::START_ID()));
215   std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
216       GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::END_ID()));
217
218   return aCenterAttr->isInitialized() && aStartAttr->isInitialized() && anEndAttr->isInitialized();
219 }
220
221 void SketchPlugin_Arc::attributeChanged(const std::string& theID)
222 {
223   std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
224       GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
225   std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
226       GeomDataAPI_Point2D>(data()->attribute(START_ID()));
227   std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
228       GeomDataAPI_Point2D>(data()->attribute(END_ID()));
229   // the second condition for unability to move external segments anywhere
230   if (theID == EXTERNAL_ID() || isFixed()) {
231     std::shared_ptr<GeomAPI_Shape> aSelection = data()->selection(EXTERNAL_ID())->value();
232     // update arguments due to the selection value
233     if (aSelection && !aSelection->isNull() && aSelection->isEdge()) {
234       std::shared_ptr<GeomAPI_Edge> anEdge( new GeomAPI_Edge(aSelection));
235       std::shared_ptr<GeomAPI_Circ> aCirc = anEdge->circle();
236       if (aCirc.get()) {
237         aStartAttr->setValue(sketch()->to2D(anEdge->firstPoint()));
238         anEndAttr->setValue(sketch()->to2D(anEdge->lastPoint()));
239         aCenterAttr->setValue(sketch()->to2D(aCirc->center()));
240       }
241     }
242     return;
243   }
244   if (!aCenterAttr->isInitialized())
245     return;
246   if (!aStartAttr->isInitialized())
247     return;
248   if (!anEndAttr->isInitialized())
249     return;
250
251   // update the points in accordance to the changed point changes
252   if (theID == END_ID() && !myEndUpdate) {
253     myEndUpdate = true;
254     // compute and change the arc end point
255     std::shared_ptr<GeomAPI_Circ2d> aCircleForArc(
256         new GeomAPI_Circ2d(aCenterAttr->pnt(), aStartAttr->pnt()));
257     std::shared_ptr<GeomAPI_Pnt2d> aProjection = aCircleForArc->project(anEndAttr->pnt());
258     if (aProjection && anEndAttr->pnt()->distance(aProjection) > tolerance) {
259       // issue #855: trying to update only not-updated coordinate if it is possible
260       if (abs(myXEndBefore - anEndAttr->x()) < 1.e-10) { // keep Y unchanged
261         double aVy = aCenterAttr->y() - anEndAttr->y();
262         double aVy2 = aVy * aVy;
263         double aR2 = aCircleForArc->radius() * aCircleForArc->radius();
264         if (aVy2 <= aR2) {
265           double aDX = sqrt(aR2 - aVy * aVy);
266           if (anEndAttr->x() > aCenterAttr->x())
267             aProjection->setX(aCenterAttr->x() + aDX);
268           else 
269             aProjection->setX(aCenterAttr->x() - aDX);
270           aProjection->setY(anEndAttr->y());
271         }
272       } else if (abs(myYEndBefore - anEndAttr->y()) < 1.e-10) { // keep X unchanged
273         double aVx = aCenterAttr->x() - anEndAttr->x();
274         double aVx2 = aVx * aVx;
275         double aR2 = aCircleForArc->radius() * aCircleForArc->radius();
276         if (aVx2 <= aR2) {
277           double aDY = sqrt(aR2 - aVx * aVx);
278           if (anEndAttr->y() > aCenterAttr->y())
279             aProjection->setY(aCenterAttr->y() + aDY);
280           else 
281             aProjection->setY(aCenterAttr->y() - aDY);
282           aProjection->setX(anEndAttr->x());
283         }
284       }
285
286       anEndAttr->setValue(aProjection);
287     }
288     myXEndBefore = anEndAttr->x();
289     myYEndBefore = anEndAttr->y();
290     myEndUpdate = false;
291   } else if (theID == START_ID() && !myStartUpdate) {
292     myStartUpdate = true;
293     // compute and change the arc start point
294     std::shared_ptr<GeomAPI_Circ2d> aCircleForArc(
295         new GeomAPI_Circ2d(aCenterAttr->pnt(), anEndAttr->pnt()));
296     std::shared_ptr<GeomAPI_Pnt2d> aProjection = aCircleForArc->project(aStartAttr->pnt());
297     if (aProjection && aStartAttr->pnt()->distance(aProjection) > tolerance)
298       aStartAttr->setValue(aProjection);
299     myStartUpdate = false;
300   } else if (theID == CENTER_ID() && !myEndUpdate) {
301     myEndUpdate = true;
302     // compute and change the arc end point
303     std::shared_ptr<GeomAPI_Circ2d> aCircleForArc(
304         new GeomAPI_Circ2d(aCenterAttr->pnt(), aStartAttr->pnt()));
305     std::shared_ptr<GeomAPI_Pnt2d> aProjection = aCircleForArc->project(anEndAttr->pnt());
306     if (aProjection && anEndAttr->pnt()->distance(aProjection) > tolerance)
307       anEndAttr->setValue(aProjection);
308     myEndUpdate = false;
309   }
310 }