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