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