Salome HOME
Issue #2024: Redesign of circle and arc of circle
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_MacroArc.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:        SketchPlugin_MacroArc.cpp
4 // Created:     26 Apr 2014
5 // Author:      Artem ZHIDKOV
6
7 #include "SketchPlugin_MacroArc.h"
8
9 #include "SketchPlugin_Sketch.h"
10 #include "SketchPlugin_Tools.h"
11
12 #include <ModelAPI_AttributeDouble.h>
13 #include <ModelAPI_AttributeRefAttr.h>
14 #include <ModelAPI_AttributeString.h>
15 #include <ModelAPI_Session.h>
16 #include <ModelAPI_Validator.h>
17
18 #include <GeomAPI_Circ.h>
19 #include <GeomAPI_Circ2d.h>
20 #include <GeomAPI_Dir2d.h>
21 #include <GeomAPI_Edge.h>
22 #include <GeomAPI_Lin.h>
23 #include <GeomAPI_Lin2d.h>
24 #include <GeomAPI_Pnt2d.h>
25 #include <GeomAPI_Vertex.h>
26 #include <GeomAPI_XY.h>
27
28 #include <GeomDataAPI_Point2D.h>
29 #include <GeomDataAPI_Dir.h>
30 #include <GeomAlgoAPI_PointBuilder.h>
31 #include <GeomAlgoAPI_EdgeBuilder.h>
32 #include <GeomAlgoAPI_CompoundBuilder.h>
33
34 // for sqrt on Linux
35 #include <math.h>
36
37 const double tolerance = 1e-7;
38 const double paramTolerance = 1.e-4;
39 const double PI = 3.141592653589793238463;
40
41
42 SketchPlugin_MacroArc::SketchPlugin_MacroArc()
43 : SketchPlugin_SketchEntity(),
44   myIsInversed(false),
45   myParamBefore(0.0)
46 {
47 }
48
49 void SketchPlugin_MacroArc::initAttributes()
50 {
51   data()->addAttribute(ARC_TYPE(), ModelAPI_AttributeString::typeId());
52
53   data()->addAttribute(CENTER_POINT_ID(), GeomDataAPI_Point2D::typeId());
54   data()->addAttribute(START_POINT_1_ID(), GeomDataAPI_Point2D::typeId());
55   data()->addAttribute(END_POINT_1_ID(), GeomDataAPI_Point2D::typeId());
56
57   data()->addAttribute(START_POINT_2_ID(), GeomDataAPI_Point2D::typeId());
58   data()->addAttribute(END_POINT_2_ID(), GeomDataAPI_Point2D::typeId());
59   data()->addAttribute(PASSED_POINT_ID(), GeomDataAPI_Point2D::typeId());
60
61   data()->addAttribute(TANGENT_POINT_ID(), ModelAPI_AttributeRefAttr::typeId());
62   data()->addAttribute(END_POINT_3_ID(), GeomDataAPI_Point2D::typeId());
63
64   data()->addAttribute(RADIUS_ID(), ModelAPI_AttributeDouble::typeId());
65   data()->addAttribute(ANGLE_ID(), ModelAPI_AttributeDouble::typeId());
66
67   data()->addAttribute(AUXILIARY_ID(), ModelAPI_AttributeBoolean::typeId());
68
69   data()->addAttribute(CENTER_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
70   data()->addAttribute(START_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
71   data()->addAttribute(END_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
72   data()->addAttribute(PASSED_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId());
73
74   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), CENTER_POINT_REF_ID());
75   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), START_POINT_REF_ID());
76   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), END_POINT_REF_ID());
77   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PASSED_POINT_REF_ID());
78 }
79
80 void SketchPlugin_MacroArc::attributeChanged(const std::string& theID)
81 {
82   std::string anArcType = string(ARC_TYPE())->value();
83
84   // If arc type switched reset according attributes.
85   if(theID == ARC_TYPE()) {
86     std::string aType = string(ARC_TYPE())->value();
87     if(aType == ARC_TYPE_BY_CENTER_AND_POINTS()) {
88       SketchPlugin_Tools::resetAttribute(this, CENTER_POINT_ID());
89       SketchPlugin_Tools::resetAttribute(this, CENTER_POINT_REF_ID());
90       SketchPlugin_Tools::resetAttribute(this, START_POINT_1_ID());
91       SketchPlugin_Tools::resetAttribute(this, START_POINT_REF_ID());
92       SketchPlugin_Tools::resetAttribute(this, END_POINT_1_ID());
93       SketchPlugin_Tools::resetAttribute(this, END_POINT_REF_ID());
94     } else if(aType == ARC_TYPE_BY_THREE_POINTS()) {
95       SketchPlugin_Tools::resetAttribute(this, START_POINT_2_ID());
96       SketchPlugin_Tools::resetAttribute(this, START_POINT_REF_ID());
97       SketchPlugin_Tools::resetAttribute(this, END_POINT_2_ID());
98       SketchPlugin_Tools::resetAttribute(this, END_POINT_REF_ID());
99       SketchPlugin_Tools::resetAttribute(this, PASSED_POINT_ID());
100       SketchPlugin_Tools::resetAttribute(this, PASSED_POINT_REF_ID());
101     } else if(aType == ARC_TYPE_BY_TANGENT_EDGE()) {
102       SketchPlugin_Tools::resetAttribute(this, TANGENT_POINT_ID());
103       SketchPlugin_Tools::resetAttribute(this, END_POINT_3_ID());
104       SketchPlugin_Tools::resetAttribute(this, END_POINT_REF_ID());
105     }
106
107     myCenter.reset();
108     myStart.reset();
109     myEnd.reset();
110     myIsInversed = false;
111     myParamBefore = 0.0;
112   } else if(anArcType == ARC_TYPE_BY_CENTER_AND_POINTS()) {
113     std::shared_ptr<GeomDataAPI_Point2D> aCenterPointAttr =
114         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(CENTER_POINT_ID()));
115     if(!aCenterPointAttr->isInitialized()) {
116       return;
117     }
118     std::shared_ptr<GeomDataAPI_Point2D> aStartPointAttr =
119         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(START_POINT_1_ID()));
120     if(!aStartPointAttr->isInitialized()) {
121       return;
122     }
123
124     myCenter = aCenterPointAttr->pnt();
125     myStart = aStartPointAttr->pnt();
126     myEnd = myStart;
127
128     std::shared_ptr<GeomDataAPI_Point2D> anEndPointAttr =
129         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(END_POINT_1_ID()));
130     if(anEndPointAttr->isInitialized()) {
131       // End point should be a projection on circle.
132       GeomAPI_Circ2d aCircleForArc(myCenter, myStart);
133       std::shared_ptr<GeomAPI_Pnt2d> aProjection = aCircleForArc.project(anEndPointAttr->pnt());
134       if(aProjection.get()) {
135         bool aWasBlocked = data()->blockSendAttributeUpdated(true);
136         anEndPointAttr->setValue(aProjection);
137         data()->blockSendAttributeUpdated(aWasBlocked, false);
138       }
139       myEnd = anEndPointAttr->pnt();
140
141       double aParameterNew = 0.0;
142       if(aCircleForArc.parameter(myEnd, paramTolerance, aParameterNew)) {
143         if(myParamBefore <= PI / 2.0 && aParameterNew >= PI * 1.5) {
144           myIsInversed = true;
145         } else if(myParamBefore >= PI * 1.5 && aParameterNew <= PI / 2.0) {
146           myIsInversed = false;
147         }
148       }
149       myParamBefore = aParameterNew;
150     }
151   } else if(anArcType == ARC_TYPE_BY_THREE_POINTS()) {
152     std::shared_ptr<GeomDataAPI_Point2D> aStartPointAttr =
153         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(START_POINT_2_ID()));
154     if(!aStartPointAttr->isInitialized()) {
155       return;
156     }
157     std::shared_ptr<GeomDataAPI_Point2D> anEndPointAttr =
158         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(END_POINT_2_ID()));
159     if(!anEndPointAttr->isInitialized()) {
160       return;
161     }
162
163     myStart = aStartPointAttr->pnt();
164     myEnd = anEndPointAttr->pnt();
165
166     std::shared_ptr<GeomDataAPI_Point2D> aPassedPointAttr =
167         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(PASSED_POINT_ID()));
168     if(aPassedPointAttr->isInitialized()) {
169       std::shared_ptr<GeomAPI_Pnt2d> aPassed = aPassedPointAttr->pnt();
170       GeomAPI_Circ2d aCircle(myStart, myEnd, aPassed);
171       myCenter = aCircle.center();
172       aCircle = GeomAPI_Circ2d(myCenter, myStart);
173       double anEndParam, aPassedParam;
174       aCircle.parameter(myEnd, paramTolerance, anEndParam);
175       aCircle.parameter(aPassed, paramTolerance, aPassedParam);
176       if(aPassedParam > anEndParam) {
177         myIsInversed = true;
178       } else {
179         myIsInversed = false;
180       }
181     } else {
182       std::shared_ptr<GeomAPI_XY> aDir = myEnd->xy()->decreased(myStart->xy())->multiplied(0.5);
183       double x = aDir->x();
184       double y = aDir->y();
185       aDir->setX(x - y);
186       aDir->setY(y + x);
187       myCenter.reset(new GeomAPI_Pnt2d(myStart->xy()->added(aDir)));
188     }
189   } else if(anArcType == ARC_TYPE_BY_TANGENT_EDGE()) {
190     AttributeRefAttrPtr aTangentAttr = refattr(TANGENT_POINT_ID());
191     if(!aTangentAttr->isInitialized()) {
192       return;
193     }
194     std::shared_ptr<GeomDataAPI_Point2D> aTangentPointAttr =
195         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aTangentAttr->attr());
196     if(!aTangentPointAttr->isInitialized()) {
197       return;
198     }
199     std::shared_ptr<GeomDataAPI_Point2D> anEndPointAttr =
200         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(END_POINT_3_ID()));
201     if(!anEndPointAttr->isInitialized()) {
202       return;
203     }
204
205     myStart = aTangentPointAttr->pnt();
206     myEnd = anEndPointAttr->pnt();
207
208     if(myStart->isEqual(myEnd)) {
209       return;
210     }
211
212     SketchPlugin_Sketch* aSketch = sketch();
213     if(!aSketch) {
214       return;
215     }
216
217     std::shared_ptr<GeomAPI_Dir2d> anOrthoDir;
218     FeaturePtr aTangFeature = ModelAPI_Feature::feature(aTangentPointAttr->owner());
219     std::shared_ptr<GeomAPI_Edge> aTangEdge =
220       std::dynamic_pointer_cast<GeomAPI_Edge>(aTangFeature->lastResult()->shape());
221     if(aTangEdge->isLine()) {
222       std::shared_ptr<GeomAPI_Dir> aDir = aTangEdge->line()->direction();
223       std::shared_ptr<GeomAPI_Pnt> aPnt(new GeomAPI_Pnt(aDir->x(), aDir->y(), aDir->z()));
224       std::shared_ptr<GeomAPI_Pnt2d> aPnt2d = aSketch->to2D(aPnt);
225       anOrthoDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(-aPnt2d->y(), aPnt2d->x()));
226     }
227     else if (aTangEdge->isArc()) {
228       std::shared_ptr<GeomAPI_Pnt> aCenter = aTangEdge->circle()->center();
229       std::shared_ptr<GeomAPI_Pnt2d> aCenter2d = aSketch->to2D(aCenter);
230       anOrthoDir = std::shared_ptr<GeomAPI_Dir2d>(
231           new GeomAPI_Dir2d(myStart->xy()->decreased(aCenter2d->xy())));
232     }
233
234     // compute parameters of the middle perpendicular
235     std::shared_ptr<GeomAPI_XY> aEndPntCoord = myEnd->xy();
236     std::shared_ptr<GeomAPI_XY> aTempDir = aEndPntCoord->decreased(myStart->xy());
237     std::shared_ptr<GeomAPI_Dir2d> aMidDir(new GeomAPI_Dir2d(-aTempDir->y(), aTempDir->x()));
238     std::shared_ptr<GeomAPI_Pnt2d> aMidPnt(
239         new GeomAPI_Pnt2d(aEndPntCoord->added(myStart->xy())->multiplied(0.5)));
240
241     // compute center of arc by calculating intersection of
242     // orthogonal line and middle perpendicular
243     std::shared_ptr<GeomAPI_Lin2d> anOrthoLine(new GeomAPI_Lin2d(myStart, anOrthoDir));
244     std::shared_ptr<GeomAPI_Lin2d> aMiddleLine(new GeomAPI_Lin2d(aMidPnt, aMidDir));
245     std::shared_ptr<GeomAPI_Pnt2d> aCenter = anOrthoLine->intersect(aMiddleLine);
246     if(aCenter) {
247       myCenter = aCenter;
248     }
249
250     GeomAPI_Circ2d aCircleForArc(myCenter, myStart);
251     double aParameterNew = 0.0;
252     if(aCircleForArc.parameter(myEnd, paramTolerance, aParameterNew)) {
253       if(myParamBefore <= PI / 2.0 && aParameterNew >= PI * 1.5) {
254         if(!myIsInversed) {
255           myIsInversed = true;
256         }
257       } else if(myParamBefore >= PI * 1.5 && aParameterNew <= PI / 2.0) {
258         if(myIsInversed) {
259           myIsInversed = false;
260         }
261       }
262     }
263     myParamBefore = aParameterNew;
264   }
265
266   double aRadius = 0;
267   double anAngle = 0;
268   if(myCenter.get() && myStart.get()) {
269     aRadius = myCenter->distance(myStart);
270     if(myEnd.get()) {
271       if(myStart->isEqual(myEnd)) {
272         anAngle = 360;
273       } else {
274         GeomAPI_Circ2d aCircleForArc(myCenter, myStart);
275         double aStartParam, anEndParam;
276         aCircleForArc.parameter(myStart, paramTolerance, aStartParam);
277         aCircleForArc.parameter(myEnd, paramTolerance, anEndParam);
278         anAngle = (anEndParam - aStartParam) / PI * 180.0;
279         if(myIsInversed) anAngle = 360.0 - anAngle;
280       }
281     }
282   }
283
284   bool aWasBlocked = data()->blockSendAttributeUpdated(true);
285   real(RADIUS_ID())->setValue(aRadius);
286   real(ANGLE_ID())->setValue(anAngle);
287   data()->blockSendAttributeUpdated(aWasBlocked, false);
288 }
289
290 AISObjectPtr SketchPlugin_MacroArc::getAISObject(AISObjectPtr thePrevious)
291 {
292   if(!myStart.get() || !myEnd.get() || !myCenter.get()) {
293     return AISObjectPtr();
294   }
295
296   SketchPlugin_Sketch* aSketch = sketch();
297   if(!aSketch) {
298     return AISObjectPtr();
299   }
300
301   std::shared_ptr<GeomAPI_Pnt> aStart = aSketch->to3D(myStart->x(), myStart->y());
302   std::shared_ptr<GeomAPI_Pnt> anEnd = aSketch->to3D(myEnd->x(), myEnd->y());
303   std::shared_ptr<GeomAPI_Pnt> aCenter = aSketch->to3D(myCenter->x(), myCenter->y());;
304
305   std::shared_ptr<GeomDataAPI_Dir> aNDir =
306       std::dynamic_pointer_cast<GeomDataAPI_Dir>(
307           aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
308   std::shared_ptr<GeomAPI_Dir> aNormal = aNDir->dir();
309   GeomShapePtr anArcShape = myIsInversed ?
310       GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenter, anEnd, aStart, aNormal)
311     : GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenter, aStart, anEnd, aNormal);
312   GeomShapePtr aCenterPointShape = GeomAlgoAPI_PointBuilder::vertex(aCenter);
313
314   if(!anArcShape.get() || !aCenterPointShape.get()) {
315     return AISObjectPtr();
316   }
317
318   std::list<std::shared_ptr<GeomAPI_Shape> > aShapes;
319   aShapes.push_back(anArcShape);
320   aShapes.push_back(aCenterPointShape);
321
322   std::shared_ptr<GeomAPI_Shape> aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
323   AISObjectPtr anAIS = thePrevious;
324   if(!anAIS.get()) {
325     anAIS.reset(new GeomAPI_AISObject());
326   }
327   anAIS->createShape(aCompound);
328   return anAIS;
329 }
330
331 void SketchPlugin_MacroArc::execute()
332 {
333 }