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