Salome HOME
Issue #901 - It is possible to define empty name for parameter
[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 bool SketchPlugin_Arc::isFixed() {
182   return data()->selection(EXTERNAL_ID())->context().get() != NULL;
183 }
184
185 bool SketchPlugin_Arc::isFeatureValid()
186 {
187   std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
188       GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::CENTER_ID()));
189   std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
190       GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::START_ID()));
191   std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
192       GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::END_ID()));
193
194   return aCenterAttr->isInitialized() && aStartAttr->isInitialized() && anEndAttr->isInitialized();
195 }
196
197 void SketchPlugin_Arc::attributeChanged(const std::string& theID)
198 {
199   std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
200       GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
201   std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
202       GeomDataAPI_Point2D>(data()->attribute(START_ID()));
203   std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
204       GeomDataAPI_Point2D>(data()->attribute(END_ID()));
205   // the second condition for unability to move external segments anywhere
206   if (theID == EXTERNAL_ID() || isFixed()) {
207     std::shared_ptr<GeomAPI_Shape> aSelection = data()->selection(EXTERNAL_ID())->value();
208     // update arguments due to the selection value
209     if (aSelection && !aSelection->isNull() && aSelection->isEdge()) {
210       std::shared_ptr<GeomAPI_Edge> anEdge( new GeomAPI_Edge(aSelection));
211       std::shared_ptr<GeomAPI_Circ> aCirc = anEdge->circle();
212       if (aCirc.get()) {
213         aStartAttr->setValue(sketch()->to2D(anEdge->firstPoint()));
214         anEndAttr->setValue(sketch()->to2D(anEdge->lastPoint()));
215         aCenterAttr->setValue(sketch()->to2D(aCirc->center()));
216       }
217     }
218     return;
219   }
220   if (!aCenterAttr->isInitialized())
221     return;
222   if (!aStartAttr->isInitialized())
223     return;
224   if (!anEndAttr->isInitialized())
225     return;
226
227   // update the points in accordance to the changed point changes
228   if (theID == END_ID() && !myEndUpdate) {
229     myEndUpdate = true;
230     // compute and change the arc end point
231     std::shared_ptr<GeomAPI_Circ2d> aCircleForArc(
232         new GeomAPI_Circ2d(aCenterAttr->pnt(), aStartAttr->pnt()));
233     std::shared_ptr<GeomAPI_Pnt2d> aProjection = aCircleForArc->project(anEndAttr->pnt());
234     if (aProjection && anEndAttr->pnt()->distance(aProjection) > tolerance) {
235       // issue #855: trying to update only not-updated coordinate if it is possible
236       if (abs(myXEndBefore - anEndAttr->x()) < 1.e-10) { // keep Y unchanged
237         double aVy = aCenterAttr->y() - anEndAttr->y();
238         double aVy2 = aVy * aVy;
239         double aR2 = aCircleForArc->radius() * aCircleForArc->radius();
240         if (aVy2 <= aR2) {
241           double aDX = sqrt(aR2 - aVy * aVy);
242           if (anEndAttr->x() > aCenterAttr->x())
243             aProjection->setX(aCenterAttr->x() + aDX);
244           else 
245             aProjection->setX(aCenterAttr->x() - aDX);
246           aProjection->setY(anEndAttr->y());
247         }
248       } else if (abs(myYEndBefore - anEndAttr->y()) < 1.e-10) { // keep X unchanged
249         double aVx = aCenterAttr->x() - anEndAttr->x();
250         double aVx2 = aVx * aVx;
251         double aR2 = aCircleForArc->radius() * aCircleForArc->radius();
252         if (aVx2 <= aR2) {
253           double aDY = sqrt(aR2 - aVx * aVx);
254           if (anEndAttr->y() > aCenterAttr->y())
255             aProjection->setY(aCenterAttr->y() + aDY);
256           else 
257             aProjection->setY(aCenterAttr->y() - aDY);
258           aProjection->setX(anEndAttr->x());
259         }
260       }
261
262       anEndAttr->setValue(aProjection);
263     }
264     myXEndBefore = anEndAttr->x();
265     myYEndBefore = anEndAttr->y();
266     myEndUpdate = false;
267   } else if (theID == START_ID() && !myStartUpdate) {
268     myStartUpdate = true;
269     // compute and change the arc start point
270     std::shared_ptr<GeomAPI_Circ2d> aCircleForArc(
271         new GeomAPI_Circ2d(aCenterAttr->pnt(), anEndAttr->pnt()));
272     std::shared_ptr<GeomAPI_Pnt2d> aProjection = aCircleForArc->project(aStartAttr->pnt());
273     if (aProjection && aStartAttr->pnt()->distance(aProjection) > tolerance)
274       aStartAttr->setValue(aProjection);
275     myStartUpdate = false;
276   } else if (theID == CENTER_ID() && !myEndUpdate) {
277     myEndUpdate = true;
278     // compute and change the arc end point
279     std::shared_ptr<GeomAPI_Circ2d> aCircleForArc(
280         new GeomAPI_Circ2d(aCenterAttr->pnt(), aStartAttr->pnt()));
281     std::shared_ptr<GeomAPI_Pnt2d> aProjection = aCircleForArc->project(anEndAttr->pnt());
282     if (aProjection && anEndAttr->pnt()->distance(aProjection) > tolerance)
283       anEndAttr->setValue(aProjection);
284     myEndUpdate = false;
285   }
286 }