Salome HOME
Fix compilation error on Linux
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_ConstraintAngle.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "SketchPlugin_ConstraintAngle.h"
21 #include <SketchPlugin_Line.h>
22 #include <SketchPlugin_Tools.h>
23 #include <SketcherPrs_Tools.h>
24
25 #include <ModelAPI_AttributeDouble.h>
26 #include <ModelAPI_AttributeInteger.h>
27 #include <ModelAPI_Session.h>
28 #include <ModelAPI_Validator.h>
29
30 #include <GeomDataAPI_Point2D.h>
31
32 #include <GeomAPI_Angle2d.h>
33 #include <GeomAPI_Dir2d.h>
34 #include <GeomAPI_Lin2d.h>
35 #include <GeomAPI_Pnt2d.h>
36 #include <GeomAPI_XY.h>
37
38 #include <SketcherPrs_Factory.h>
39 #include <SketcherPrs_Tools.h>
40
41 #include <cmath>
42 #include <regex>
43 #include <sstream>
44
45 const double tolerance = 1.e-7;
46 #define PI 3.1415926535897932
47
48 /// \brief Calculate intersection point of two lines
49 static std::shared_ptr<GeomAPI_Pnt2d> intersect(FeaturePtr theLine1, FeaturePtr theLine2);
50
51
52 SketchPlugin_ConstraintAngle::SketchPlugin_ConstraintAngle()
53 {
54   myFlyoutUpdate = false;
55 }
56
57 void SketchPlugin_ConstraintAngle::initAttributes()
58 {
59   ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators();
60
61   data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
62   data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId());
63   data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId());
64   data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId());
65
66   data()->addAttribute(ANGLE_VALUE_ID(), ModelAPI_AttributeDouble::typeId());
67   data()->addAttribute(TYPE_ID(), ModelAPI_AttributeInteger::typeId());
68
69   data()->addAttribute(ANGLE_REVERSED_FIRST_LINE_ID(), ModelAPI_AttributeBoolean::typeId());
70   data()->addAttribute(ANGLE_REVERSED_SECOND_LINE_ID(), ModelAPI_AttributeBoolean::typeId());
71
72   data()->addAttribute(LOCATION_TYPE_ID(), ModelAPI_AttributeInteger::typeId());
73   aValidators->registerNotObligatory(getKind(), LOCATION_TYPE_ID());
74
75   data()->addAttribute(PREV_TYPE_ID(), ModelAPI_AttributeInteger::typeId());
76   data()->attribute(PREV_TYPE_ID())->setIsArgument(false);
77   aValidators->registerNotObligatory(getKind(), PREV_TYPE_ID());
78   if (attribute(TYPE_ID())->isInitialized())
79     integer(PREV_TYPE_ID())->setValue(integer(TYPE_ID())->value());
80
81   data()->addAttribute(SELECTED_FIRST_POINT_ID(), GeomDataAPI_Point2D::typeId());
82   data()->attribute(SELECTED_FIRST_POINT_ID())->setIsArgument(false);
83   aValidators->registerNotObligatory(getKind(), SELECTED_FIRST_POINT_ID());
84
85   data()->addAttribute(SELECTED_SECOND_POINT_ID(), GeomDataAPI_Point2D::typeId());
86   data()->attribute(SELECTED_SECOND_POINT_ID())->setIsArgument(false);
87   aValidators->registerNotObligatory(getKind(), SELECTED_SECOND_POINT_ID());
88
89   AttributeIntegerPtr aVerAttr = std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(
90       data()->addAttribute(VERSION_ID(), ModelAPI_AttributeInteger::typeId()));
91   aVerAttr->setIsArgument(false);
92   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), VERSION_ID());
93   if (!aVerAttr->isInitialized()) {
94     // this is a newly created feature (not read from file),
95     // so, initialize the latest version
96     aVerAttr->setValue(THE_VERSION_1);
97   }
98 }
99
100 void SketchPlugin_ConstraintAngle::colorConfigInfo(std::string& theSection, std::string& theName,
101                                                    std::string& theDefault)
102 {
103   theSection = "Visualization";
104   theName = "sketch_dimension_color";
105   theDefault = SKETCH_DIMENSION_COLOR;
106 }
107
108 void SketchPlugin_ConstraintAngle::execute()
109 {
110   std::shared_ptr<ModelAPI_Data> aData = data();
111
112   AttributeRefAttrPtr anAttrA = aData->refattr(SketchPlugin_Constraint::ENTITY_A());
113   AttributeRefAttrPtr anAttrB = aData->refattr(SketchPlugin_Constraint::ENTITY_B());
114   if (!anAttrA->isInitialized() || !anAttrB->isInitialized())
115     return;
116
117   AttributeIntegerPtr aVersion = integer(VERSION_ID());
118   if (!aVersion->isInitialized() || aVersion->value() < THE_VERSION_1)
119     updateVersion();
120
121   AttributeDoublePtr anAttrValue = real(ANGLE_VALUE_ID());
122   if (!anAttrValue->isInitialized())
123     calculateAngle();
124
125   // the value should to be computed here, not in the
126   // getAISObject in order to change the model value
127   // inside the object transaction. This is important for creating a constraint by preselection.
128   // The display of the presentation in this case happens after the transaction commit
129   std::shared_ptr<GeomDataAPI_Point2D> aFlyOutAttr = std::dynamic_pointer_cast<
130       GeomDataAPI_Point2D>(aData->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()));
131   if(!aFlyOutAttr->isInitialized())
132     compute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT());
133 }
134
135 AISObjectPtr SketchPlugin_ConstraintAngle::getAISObject(AISObjectPtr thePrevious)
136 {
137   if (!sketch())
138     return thePrevious;
139
140   AISObjectPtr anAIS = SketcherPrs_Factory::angleConstraint(this, sketch(),
141                                                             thePrevious);
142   if (anAIS.get() && !thePrevious.get())
143     SketchPlugin_Tools::setDimensionColor(anAIS);
144   return anAIS;
145 }
146
147 void SketchPlugin_ConstraintAngle::attributeChanged(const std::string& theID)
148 {
149   if (myFlyoutUpdate)
150     return;
151
152   std::shared_ptr<ModelAPI_Data> aData = data();
153   if (!aData)
154     return;
155
156   if (theID == TYPE_ID())
157     updateAngleValue();
158
159   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
160   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
161   if (!aLineA || !aLineB)
162     return;
163
164   AttributeIntegerPtr aVersion = integer(VERSION_ID());
165   if (!aVersion->isInitialized() || aVersion->value() < THE_VERSION_1)
166     updateVersion();
167
168   if (theID == ENTITY_A() || theID == ENTITY_B() ||
169       theID == TYPE_ID() || theID == ANGLE_VALUE_ID()) {
170     calculateAngle();
171   } else if (theID == FLYOUT_VALUE_PNT()) {
172     compute(theID);
173   }
174 }
175
176 void SketchPlugin_ConstraintAngle::calculateAngle()
177 {
178   // update *_REVERSED_* flags
179   calculateAnglePosition();
180
181   std::shared_ptr<ModelAPI_Data> aData = data();
182   std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
183   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
184   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
185
186   GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
187   GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
188   GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
189   GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
190
191   std::shared_ptr<GeomAPI_Lin2d> aLine1(new GeomAPI_Lin2d(aStartA, aEndA));
192   std::shared_ptr<GeomAPI_Lin2d> aLine2(new GeomAPI_Lin2d(aStartB, aEndB));
193
194   bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
195   bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
196
197   AttributeDoublePtr anAngleValueAttr = real(ANGLE_VALUE_ID());
198   if (!anAngleValueAttr->isInitialized()) {
199     std::shared_ptr<GeomAPI_Angle2d> anAng(
200         new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2));
201     anAngleValueAttr->setValue(getAngleForType(fabs(anAng->angleDegree())));
202   }
203
204   std::shared_ptr<GeomAPI_Angle2d> anAng(new GeomAPI_Angle2d(aLine1, false, aLine2, false));
205   double anAngle = anAng->angleDegree();
206
207   anAngle /= fabs(anAngle);
208   anAngle *= getAngleForType(anAngleValueAttr->value(), isReversed1, isReversed2);
209
210   // update value of the constraint to be passed to the solver
211   real(SketchPlugin_Constraint::VALUE())->setValue(anAngle);
212 }
213
214 void SketchPlugin_ConstraintAngle::calculateAnglePosition()
215 {
216   if (attribute(ANGLE_REVERSED_FIRST_LINE_ID())->isInitialized() &&
217       attribute(ANGLE_REVERSED_SECOND_LINE_ID())->isInitialized())
218     return; // already calculated
219
220   DataPtr aData = data();
221   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
222   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
223
224   GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
225   GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
226   GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
227   GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
228
229   bool isReversed1 = false;
230   bool isReversed2 = false;
231
232   GeomPnt2dPtr aSelected1 = SketcherPrs_Tools::getPoint(this, SELECTED_FIRST_POINT_ID());
233   GeomPnt2dPtr aSelected2 = SketcherPrs_Tools::getPoint(this, SELECTED_SECOND_POINT_ID());
234   if (aSelected1 && aSelected2) {
235     GeomPnt2dPtr anInterPnt = intersect(aLineA, aLineB);
236     if (!anInterPnt)
237       return;
238     std::shared_ptr<GeomAPI_XY> anInterXY = anInterPnt->xy();
239     isReversed1 = aSelected1->xy()->decreased(anInterXY)->dot(
240                   aEndA->xy()->decreased(aStartA->xy())) < -tolerance;
241     isReversed2 = aSelected2->xy()->decreased(anInterXY)->dot(
242                   aEndB->xy()->decreased(aStartB->xy())) < -tolerance;
243   }
244   else {
245     // no point is selected (document opened or Python script is loaded),
246     // calculate basing on the value
247     std::shared_ptr<GeomAPI_Angle2d> anAng(new GeomAPI_Angle2d(aStartA, aEndA, aStartB, aEndB));
248     isReversed1 = anAng->isReversed(0);
249     isReversed2 = anAng->isReversed(1);
250   }
251
252   // adjust reversed flags according to the angle type
253   AttributeIntegerPtr aTypeAttr = integer(TYPE_ID());
254   if (aTypeAttr && aTypeAttr->isInitialized() &&
255      (SketcherPrs_Tools::AngleType)(aTypeAttr->value()) == SketcherPrs_Tools::ANGLE_COMPLEMENTARY)
256     isReversed1 = !isReversed1;
257
258   boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(isReversed1);
259   boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(isReversed2);
260 }
261
262 // Convert angle value from the DIRECT to any given type.
263 static double angleForType(const double theAngle, const int theType)
264 {
265   double anAngle = theAngle;
266   switch ((SketcherPrs_Tools::AngleType)theType) {
267     case SketcherPrs_Tools::ANGLE_DIRECT:
268       anAngle = theAngle;
269       break;
270     case SketcherPrs_Tools::ANGLE_COMPLEMENTARY:
271       anAngle = 180.0 - theAngle;
272       break;
273     case SketcherPrs_Tools::ANGLE_BACKWARD:
274       anAngle = 360.0 - theAngle;
275       break;
276     default:
277       break;
278   }
279   return anAngle;
280 }
281
282 double SketchPlugin_ConstraintAngle::getAngleForType(double theAngle,
283                                                      bool isReversed1,
284                                                      bool isReversed2)
285 {
286   double anAngle = angleForType(theAngle, integer(TYPE_ID())->value());
287   if (isReversed1 != isReversed2)
288     anAngle = 180.0 - anAngle;
289   return anAngle;
290 }
291
292 // Convert angle value or a text expression from one angle type to another
293 static void convertAngle(AttributeDoublePtr theAngle,
294                          const int thePrevType, const int theNewType)
295 {
296   if (theAngle->isInitialized()) {
297     if (theAngle->text().empty()) {
298       // calculate value related to the type twice:
299       // the first time - to return to direct angle,
300       // the second time - to apply new type
301       double aValue = angleForType(theAngle->value(), thePrevType);
302       aValue = angleForType(aValue, theNewType);
303       theAngle->setValue(aValue);
304     }
305     else {
306       // process the parametric value
307       std::string anAngleText = theAngle->text();
308       std::regex anAngleRegex("\\s*([-+]?[0-9]*\\.?[0-9]*)\\s*([-+])\\s*\\((.*)\\)$",
309                               std::regex_constants::ECMAScript);
310
311       double anAnglePrefix = 0.0;
312       static const char aSignPrefix[2] = { '-', '+' };
313       int aSignInd = 1;
314
315       std::smatch aResult;
316       if (std::regex_search(anAngleText, aResult, anAngleRegex)) {
317         anAnglePrefix = std::atof(aResult[1].str().c_str());
318         aSignInd = aResult[2].str()[0] == aSignPrefix[0] ? 0 : 1;
319         anAngleText = aResult[3].str();
320       }
321
322       if (thePrevType != SketcherPrs_Tools::ANGLE_DIRECT)
323         aSignInd = 1 - aSignInd;
324       anAnglePrefix = angleForType(anAnglePrefix, thePrevType);
325
326       if (theNewType != SketcherPrs_Tools::ANGLE_DIRECT)
327         aSignInd = 1 - aSignInd;
328       anAnglePrefix = angleForType(anAnglePrefix, theNewType);
329
330       std::ostringstream aText;
331       bool isPrintSign = true;
332       if (fabs(anAnglePrefix) > tolerance)
333         aText << anAnglePrefix;
334       else
335         isPrintSign = aSignInd == 0;
336       if (isPrintSign)
337         aText << " " << aSignPrefix[aSignInd] << " (";
338       aText << anAngleText << (isPrintSign ? ")" : "");
339       theAngle->setText(aText.str());
340     }
341   }
342 }
343
344 void SketchPlugin_ConstraintAngle::updateAngleValue()
345 {
346   AttributeIntegerPtr anAngleType = integer(TYPE_ID());
347   AttributeIntegerPtr aPrevAngleType = integer(PREV_TYPE_ID());
348   convertAngle(real(ANGLE_VALUE_ID()), aPrevAngleType->value(), anAngleType->value());
349   aPrevAngleType->setValue(anAngleType->value());
350 }
351
352 static GeomPnt2dPtr lineBoundary(const FeaturePtr& theLine, const bool theReversed,
353                                  const GeomPnt2dPtr& thePointToAvoid)
354 {
355   GeomPnt2dPtr aPoint = SketcherPrs_Tools::getPoint(theLine.get(),
356       theReversed ? SketchPlugin_Line::START_ID() : SketchPlugin_Line::END_ID());
357   if (aPoint->distance(thePointToAvoid) < tolerance) {
358     // extremity is equal to the intersection point,
359     // thus recalculate it using another boundary point
360     aPoint = SketcherPrs_Tools::getPoint(theLine.get(),
361         theReversed ? SketchPlugin_Line::END_ID() : SketchPlugin_Line::START_ID());
362     aPoint->setX(thePointToAvoid->x() * 2.0 - aPoint->x());
363     aPoint->setY(thePointToAvoid->y() * 2.0 - aPoint->y());
364   }
365   return aPoint;
366 }
367
368 bool SketchPlugin_ConstraintAngle::compute(const std::string& theAttributeId)
369 {
370   if (theAttributeId != SketchPlugin_Constraint::FLYOUT_VALUE_PNT())
371     return false;
372   if (!sketch())
373     return false;
374
375   std::shared_ptr<GeomDataAPI_Point2D> aFlyOutAttr = std::dynamic_pointer_cast<
376                            GeomDataAPI_Point2D>(attribute(theAttributeId));
377
378   DataPtr aData = data();
379   std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
380   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
381   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
382
383   if ((aLineA.get() == NULL) || (aLineB.get() == NULL))
384     return false;
385
386   // Intersection of lines
387   std::shared_ptr<GeomAPI_Pnt2d> anInter = intersect(aLineA, aLineB);
388   if (!anInter)
389     return false;
390
391   bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
392   bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
393
394   int anAngleType = integer(TYPE_ID())->value();
395
396   bool isSupplementary = anAngleType == (int)SketcherPrs_Tools::ANGLE_COMPLEMENTARY;
397
398   // point on lines to compose an angle
399   GeomPnt2dPtr aPointA = lineBoundary(aLineA, isReversed1 ^ isSupplementary, anInter);
400   GeomPnt2dPtr aPointB = lineBoundary(aLineB, isReversed2, anInter);
401
402   myFlyoutUpdate = true;
403   if (aFlyOutAttr->isInitialized()) {
404     std::shared_ptr<GeomAPI_XY> aFlyoutPoint = aFlyOutAttr->pnt()->xy();
405     std::shared_ptr<GeomAPI_XY> anInterXY = anInter->xy();
406     std::shared_ptr<GeomAPI_XY> aDirIF = aFlyoutPoint->decreased(anInterXY);
407     std::shared_ptr<GeomAPI_XY> aDirIA = aPointA->xy()->decreased(anInterXY);
408     std::shared_ptr<GeomAPI_XY> aDirIB = aPointB->xy()->decreased(anInterXY);
409     double aSign = aDirIA->cross(aDirIB);
410     aSign /= fabs(aSign);
411     if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD)
412       aSign *= -1.0;
413
414     double cross1 = aSign * aDirIA->cross(aDirIF);
415     if (cross1 < -tolerance)
416       boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(!isReversed2);
417     double cross2 = aSign * aDirIF->cross(aDirIB);
418     if (cross2 < -tolerance)
419       boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(!isReversed1);
420
421     // the direction is reversed only once
422     if ((cross1 + tolerance) * (cross2 + tolerance) < 0.0) {
423       if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD) {
424         convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_BACKWARD,
425                      (int)SketcherPrs_Tools::ANGLE_DIRECT);
426       }
427       convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_DIRECT,
428                    (int)SketcherPrs_Tools::ANGLE_COMPLEMENTARY);
429       if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD) {
430         convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_DIRECT,
431                      (int)SketcherPrs_Tools::ANGLE_BACKWARD);
432       }
433     }
434
435     calculateAngle();
436   }
437   else {
438     // default position of the presentation
439     double aX = (aPointA->x() + aPointB->x() + anInter->x()) / 3.;
440     double aY = (aPointA->y() + aPointB->y() + anInter->y()) / 3.;
441     aFlyOutAttr->setValue(aX, aY);
442   }
443   myFlyoutUpdate = false;
444
445   return true;
446 }
447
448 void SketchPlugin_ConstraintAngle::updateVersion()
449 {
450   bool aWasBlocked = data()->blockSendAttributeUpdated(true);
451
452   // Calculate angle value by the old algorithm and
453   // update the corresponding attribute to meet the new requirements.
454   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(data(), ENTITY_A());
455   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(data(), ENTITY_B());
456
457   GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
458   GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
459   GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
460   GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
461
462   std::shared_ptr<GeomAPI_Angle2d> anAng;
463
464   if (boolean(ANGLE_REVERSED_FIRST_LINE_ID())->isInitialized() &&
465       boolean(ANGLE_REVERSED_SECOND_LINE_ID())->isInitialized()) {
466     bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
467     bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
468
469     std::shared_ptr<GeomAPI_Lin2d> aLine1(new GeomAPI_Lin2d(aStartA, aEndA));
470     std::shared_ptr<GeomAPI_Lin2d> aLine2(new GeomAPI_Lin2d(aStartB, aEndB));
471     anAng.reset(new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2));
472   }
473   else {
474     anAng.reset(new GeomAPI_Angle2d(aStartA, aEndA, aStartB, aEndB));
475
476     bool isReversed1 = anAng->isReversed(0);
477     bool isReversed2 = anAng->isReversed(1);
478
479     boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(isReversed1);
480     boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(isReversed2);
481   }
482   double anAngleValue = anAng->angleDegree();
483   double aConstValue = real(ANGLE_VALUE_ID())->value();
484
485   AttributeIntegerPtr aType = integer(TYPE_ID());
486   switch ((SketcherPrs_Tools::AngleType)aType->value()) {
487   case SketcherPrs_Tools::ANGLE_DIRECT:
488     if (anAngleValue < 0.0 && aConstValue > 180.0)
489       convertAngle(real(ANGLE_VALUE_ID()), SketcherPrs_Tools::ANGLE_BACKWARD,
490                                            SketcherPrs_Tools::ANGLE_DIRECT);
491     break;
492   case SketcherPrs_Tools::ANGLE_BACKWARD:
493     if (anAngleValue < 0.0 && aConstValue < 180.0)
494       convertAngle(real(ANGLE_VALUE_ID()), SketcherPrs_Tools::ANGLE_DIRECT,
495                                            SketcherPrs_Tools::ANGLE_BACKWARD);
496     break;
497   default:
498     break;
499   }
500   data()->blockSendAttributeUpdated(aWasBlocked, false);
501   integer(VERSION_ID())->setValue(THE_VERSION_1);
502 }
503
504
505 // ===============   Auxiliary functions   ==================================
506 std::shared_ptr<GeomAPI_Pnt2d> intersect(FeaturePtr theLine1, FeaturePtr theLine2)
507 {
508   // Start and end points of lines
509   const std::string& aLineStartAttr = SketchPlugin_Line::START_ID();
510   const std::string& aLineEndAttr = SketchPlugin_Line::END_ID();
511   GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(theLine1.get(), aLineStartAttr);
512   GeomPnt2dPtr aEndA   = SketcherPrs_Tools::getPoint(theLine1.get(), aLineEndAttr);
513   GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(theLine2.get(), aLineStartAttr);
514   GeomPnt2dPtr aEndB   = SketcherPrs_Tools::getPoint(theLine2.get(), aLineEndAttr);
515   if (aStartA->distance(aEndA) < tolerance || aStartB->distance(aEndB) < tolerance)
516     std::shared_ptr<GeomAPI_Pnt2d>();
517
518   // Lines and their intersection point
519   std::shared_ptr<GeomAPI_Lin2d> aLA(new GeomAPI_Lin2d(aStartA, aEndA));
520   std::shared_ptr<GeomAPI_Lin2d> aLB(new GeomAPI_Lin2d(aStartB, aEndB));
521   return aLA->intersect(aLB);
522 }