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