Salome HOME
10d27ee1c24069b400354b80b2b4404678d6f05b
[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   static Events_ID anId = ModelAPI_EventReentrantMessage::eventId();
136   std::shared_ptr<ModelAPI_EventReentrantMessage> aMessage(
137       new ModelAPI_EventReentrantMessage(anId, this));
138   Events_Loop::loop()->send(aMessage);
139 }
140
141 AISObjectPtr SketchPlugin_ConstraintAngle::getAISObject(AISObjectPtr thePrevious)
142 {
143   if (!sketch())
144     return thePrevious;
145
146   AISObjectPtr anAIS = SketcherPrs_Factory::angleConstraint(this, sketch(),
147                                                             thePrevious);
148   if (anAIS.get() && !thePrevious.get())
149     SketchPlugin_Tools::setDimensionColor(anAIS);
150   return anAIS;
151 }
152
153 // LCOV_EXCL_START
154 std::string SketchPlugin_ConstraintAngle::processEvent(
155     const std::shared_ptr<Events_Message>& theMessage)
156 {
157   std::string aFilledAttributeName;
158
159   std::shared_ptr<ModelAPI_EventReentrantMessage> aReentrantMessage =
160       std::dynamic_pointer_cast<ModelAPI_EventReentrantMessage>(theMessage);
161   if (aReentrantMessage.get()) {
162     aFilledAttributeName = ENTITY_A();
163     refattr(ENTITY_A())->setObject(aReentrantMessage->selectedObject());
164     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(SELECTED_FIRST_POINT_ID()))
165         ->setValue(aReentrantMessage->clickedPoint());
166   }
167   return aFilledAttributeName;
168 }
169 // LCOV_EXCL_STOP
170
171 void SketchPlugin_ConstraintAngle::attributeChanged(const std::string& theID)
172 {
173   if (myFlyoutUpdate)
174     return;
175
176   std::shared_ptr<ModelAPI_Data> aData = data();
177   if (!aData)
178     return;
179
180   if (theID == TYPE_ID())
181     updateAngleValue();
182
183   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
184   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
185   if (!aLineA || !aLineB)
186     return;
187
188   AttributeIntegerPtr aVersion = integer(VERSION_ID());
189   if (!aVersion->isInitialized() || aVersion->value() < THE_VERSION_1)
190     updateVersion();
191
192   if (theID == ENTITY_A() || theID == ENTITY_B() ||
193       theID == TYPE_ID() || theID == ANGLE_VALUE_ID()) {
194     calculateAngle();
195   } else if (theID == FLYOUT_VALUE_PNT()) {
196     compute(theID);
197   }
198 }
199
200 void SketchPlugin_ConstraintAngle::calculateAngle()
201 {
202   // update *_REVERSED_* flags
203   calculateAnglePosition();
204
205   std::shared_ptr<ModelAPI_Data> aData = data();
206   std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
207   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
208   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
209
210   GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
211   GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
212   GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
213   GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
214
215   std::shared_ptr<GeomAPI_Lin2d> aLine1(new GeomAPI_Lin2d(aStartA, aEndA));
216   std::shared_ptr<GeomAPI_Lin2d> aLine2(new GeomAPI_Lin2d(aStartB, aEndB));
217
218   bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
219   bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
220
221   AttributeDoublePtr anAngleValueAttr = real(ANGLE_VALUE_ID());
222   if (!anAngleValueAttr->isInitialized()) {
223     std::shared_ptr<GeomAPI_Angle2d> anAng(
224         new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2));
225     anAngleValueAttr->setValue(getAngleForType(fabs(anAng->angleDegree())));
226   }
227
228   std::shared_ptr<GeomAPI_Angle2d> anAng(new GeomAPI_Angle2d(aLine1, false, aLine2, false));
229   double anAngle = anAng->angleDegree();
230
231   anAngle /= fabs(anAngle);
232   anAngle *= getAngleForType(anAngleValueAttr->value(), isReversed1, isReversed2);
233
234   // update value of the constraint to be passed to the solver
235   real(SketchPlugin_Constraint::VALUE())->setValue(anAngle);
236 }
237
238 void SketchPlugin_ConstraintAngle::calculateAnglePosition()
239 {
240   if (attribute(ANGLE_REVERSED_FIRST_LINE_ID())->isInitialized() &&
241       attribute(ANGLE_REVERSED_SECOND_LINE_ID())->isInitialized())
242     return; // already calculated
243
244   DataPtr aData = data();
245   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
246   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
247
248   GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
249   GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
250   GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
251   GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
252
253   bool isReversed1 = false;
254   bool isReversed2 = false;
255
256   GeomPnt2dPtr aSelected1 = SketcherPrs_Tools::getPoint(this, SELECTED_FIRST_POINT_ID());
257   GeomPnt2dPtr aSelected2 = SketcherPrs_Tools::getPoint(this, SELECTED_SECOND_POINT_ID());
258   if (aSelected1 && aSelected2) {
259     GeomPnt2dPtr anInterPnt = intersect(aLineA, aLineB);
260     if (!anInterPnt)
261       return;
262     std::shared_ptr<GeomAPI_XY> anInterXY = anInterPnt->xy();
263     isReversed1 = aSelected1->xy()->decreased(anInterXY)->dot(
264                   aEndA->xy()->decreased(aStartA->xy())) < -tolerance;
265     isReversed2 = aSelected2->xy()->decreased(anInterXY)->dot(
266                   aEndB->xy()->decreased(aStartB->xy())) < -tolerance;
267   }
268   else {
269     // no point is selected (document opened or Python script is loaded),
270     // calculate basing on the value
271     std::shared_ptr<GeomAPI_Angle2d> anAng(new GeomAPI_Angle2d(aStartA, aEndA, aStartB, aEndB));
272     isReversed1 = anAng->isReversed(0);
273     isReversed2 = anAng->isReversed(1);
274   }
275
276   // adjust reversed flags according to the angle type
277   AttributeIntegerPtr aTypeAttr = integer(TYPE_ID());
278   if (aTypeAttr && aTypeAttr->isInitialized() &&
279      (SketcherPrs_Tools::AngleType)(aTypeAttr->value()) == SketcherPrs_Tools::ANGLE_COMPLEMENTARY)
280     isReversed1 = !isReversed1;
281
282   boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(isReversed1);
283   boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(isReversed2);
284 }
285
286 // Convert angle value from the DIRECT to any given type.
287 static double angleForType(const double theAngle, const int theType)
288 {
289   double anAngle = theAngle;
290   switch ((SketcherPrs_Tools::AngleType)theType) {
291     case SketcherPrs_Tools::ANGLE_DIRECT:
292       anAngle = theAngle;
293       break;
294     case SketcherPrs_Tools::ANGLE_COMPLEMENTARY:
295       anAngle = 180.0 - theAngle;
296       break;
297     case SketcherPrs_Tools::ANGLE_BACKWARD:
298       anAngle = 360.0 - theAngle;
299       break;
300     default:
301       break;
302   }
303   return anAngle;
304 }
305
306 double SketchPlugin_ConstraintAngle::getAngleForType(double theAngle,
307                                                      bool isReversed1,
308                                                      bool isReversed2)
309 {
310   double anAngle = angleForType(theAngle, integer(TYPE_ID())->value());
311   if (isReversed1 != isReversed2)
312     anAngle = 180.0 - anAngle;
313   return anAngle;
314 }
315
316 // Convert angle value or a text expression from one angle type to another
317 static void convertAngle(AttributeDoublePtr theAngle,
318                          const int thePrevType, const int theNewType)
319 {
320   if (theAngle->isInitialized()) {
321     if (theAngle->text().empty()) {
322       // calculate value related to the type twice:
323       // the first time - to return to direct angle,
324       // the second time - to apply new type
325       double aValue = angleForType(theAngle->value(), thePrevType);
326       aValue = angleForType(aValue, theNewType);
327       theAngle->setValue(aValue);
328     }
329     else {
330       // process the parametric value
331       std::string anAngleText = theAngle->text();
332       std::regex anAngleRegex("\\s*([-+]?[0-9]*\\.?[0-9]*)\\s*([-+])\\s*\\((.*)\\)$",
333                               std::regex_constants::ECMAScript);
334
335       double anAnglePrefix = 0.0;
336       static const char aSignPrefix[2] = { '-', '+' };
337       int aSignInd = 1;
338
339       std::smatch aResult;
340       if (std::regex_search(anAngleText, aResult, anAngleRegex)) {
341         anAnglePrefix = std::atof(aResult[1].str().c_str());
342         aSignInd = aResult[2].str()[0] == aSignPrefix[0] ? 0 : 1;
343         anAngleText = aResult[3].str();
344       }
345
346       if (thePrevType != SketcherPrs_Tools::ANGLE_DIRECT)
347         aSignInd = 1 - aSignInd;
348       anAnglePrefix = angleForType(anAnglePrefix, thePrevType);
349
350       if (theNewType != SketcherPrs_Tools::ANGLE_DIRECT)
351         aSignInd = 1 - aSignInd;
352       anAnglePrefix = angleForType(anAnglePrefix, theNewType);
353
354       std::ostringstream aText;
355       bool isPrintSign = true;
356       if (fabs(anAnglePrefix) > tolerance)
357         aText << anAnglePrefix;
358       else
359         isPrintSign = aSignInd == 0;
360       if (isPrintSign)
361         aText << " " << aSignPrefix[aSignInd] << " (";
362       aText << anAngleText << (isPrintSign ? ")" : "");
363       theAngle->setText(aText.str());
364     }
365   }
366 }
367
368 void SketchPlugin_ConstraintAngle::updateAngleValue()
369 {
370   AttributeIntegerPtr anAngleType = integer(TYPE_ID());
371   AttributeIntegerPtr aPrevAngleType = integer(PREV_TYPE_ID());
372   convertAngle(real(ANGLE_VALUE_ID()), aPrevAngleType->value(), anAngleType->value());
373   aPrevAngleType->setValue(anAngleType->value());
374 }
375
376 static GeomPnt2dPtr lineBoundary(const FeaturePtr& theLine, const bool theReversed,
377                                  const GeomPnt2dPtr& thePointToAvoid)
378 {
379   GeomPnt2dPtr aPoint = SketcherPrs_Tools::getPoint(theLine.get(),
380       theReversed ? SketchPlugin_Line::START_ID() : SketchPlugin_Line::END_ID());
381   if (aPoint->distance(thePointToAvoid) < tolerance) {
382     // extremity is equal to the intersection point,
383     // thus recalculate it using another boundary point
384     aPoint = SketcherPrs_Tools::getPoint(theLine.get(),
385         theReversed ? SketchPlugin_Line::END_ID() : SketchPlugin_Line::START_ID());
386     aPoint->setX(thePointToAvoid->x() * 2.0 - aPoint->x());
387     aPoint->setY(thePointToAvoid->y() * 2.0 - aPoint->y());
388   }
389   return aPoint;
390 }
391
392 bool SketchPlugin_ConstraintAngle::compute(const std::string& theAttributeId)
393 {
394   if (theAttributeId != SketchPlugin_Constraint::FLYOUT_VALUE_PNT())
395     return false;
396   if (!sketch())
397     return false;
398
399   std::shared_ptr<GeomDataAPI_Point2D> aFlyOutAttr = std::dynamic_pointer_cast<
400                            GeomDataAPI_Point2D>(attribute(theAttributeId));
401
402   DataPtr aData = data();
403   std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
404   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
405   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
406
407   if ((aLineA.get() == NULL) || (aLineB.get() == NULL))
408     return false;
409
410   // Intersection of lines
411   std::shared_ptr<GeomAPI_Pnt2d> anInter = intersect(aLineA, aLineB);
412   if (!anInter)
413     return false;
414
415   bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
416   bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
417
418   int anAngleType = integer(TYPE_ID())->value();
419
420   bool isSupplementary = anAngleType == (int)SketcherPrs_Tools::ANGLE_COMPLEMENTARY;
421
422   // point on lines to compose an angle
423   GeomPnt2dPtr aPointA = lineBoundary(aLineA, isReversed1 ^ isSupplementary, anInter);
424   GeomPnt2dPtr aPointB = lineBoundary(aLineB, isReversed2, anInter);
425
426   myFlyoutUpdate = true;
427   if (aFlyOutAttr->isInitialized()) {
428     std::shared_ptr<GeomAPI_XY> aFlyoutPoint = aFlyOutAttr->pnt()->xy();
429     std::shared_ptr<GeomAPI_XY> anInterXY = anInter->xy();
430     std::shared_ptr<GeomAPI_XY> aDirIF = aFlyoutPoint->decreased(anInterXY);
431     std::shared_ptr<GeomAPI_XY> aDirIA = aPointA->xy()->decreased(anInterXY);
432     std::shared_ptr<GeomAPI_XY> aDirIB = aPointB->xy()->decreased(anInterXY);
433     double aSign = aDirIA->cross(aDirIB);
434     aSign /= fabs(aSign);
435     if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD)
436       aSign *= -1.0;
437
438     double cross1 = aSign * aDirIA->cross(aDirIF);
439     if (cross1 < -tolerance)
440       boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(!isReversed2);
441     double cross2 = aSign * aDirIF->cross(aDirIB);
442     if (cross2 < -tolerance)
443       boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(!isReversed1);
444
445     // the direction is reversed only once
446     if ((cross1 + tolerance) * (cross2 + tolerance) < 0.0) {
447       if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD) {
448         convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_BACKWARD,
449                      (int)SketcherPrs_Tools::ANGLE_DIRECT);
450       }
451       convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_DIRECT,
452                    (int)SketcherPrs_Tools::ANGLE_COMPLEMENTARY);
453       if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD) {
454         convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_DIRECT,
455                      (int)SketcherPrs_Tools::ANGLE_BACKWARD);
456       }
457     }
458
459     calculateAngle();
460   }
461   else {
462     // default position of the presentation
463     double aX = (aPointA->x() + aPointB->x() + anInter->x()) / 3.;
464     double aY = (aPointA->y() + aPointB->y() + anInter->y()) / 3.;
465     aFlyOutAttr->setValue(aX, aY);
466   }
467   myFlyoutUpdate = false;
468
469   return true;
470 }
471
472 void SketchPlugin_ConstraintAngle::updateVersion()
473 {
474   bool aWasBlocked = data()->blockSendAttributeUpdated(true);
475
476   // Calculate angle value by the old algorithm and
477   // update the corresponding attribute to meet the new requirements.
478   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(data(), ENTITY_A());
479   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(data(), ENTITY_B());
480
481   GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
482   GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
483   GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
484   GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
485
486   std::shared_ptr<GeomAPI_Angle2d> anAng;
487
488   if (boolean(ANGLE_REVERSED_FIRST_LINE_ID())->isInitialized() &&
489       boolean(ANGLE_REVERSED_SECOND_LINE_ID())->isInitialized()) {
490     bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
491     bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
492
493     std::shared_ptr<GeomAPI_Lin2d> aLine1(new GeomAPI_Lin2d(aStartA, aEndA));
494     std::shared_ptr<GeomAPI_Lin2d> aLine2(new GeomAPI_Lin2d(aStartB, aEndB));
495     anAng.reset(new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2));
496   }
497   else {
498     anAng.reset(new GeomAPI_Angle2d(aStartA, aEndA, aStartB, aEndB));
499
500     bool isReversed1 = anAng->isReversed(0);
501     bool isReversed2 = anAng->isReversed(1);
502
503     boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(isReversed1);
504     boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(isReversed2);
505   }
506   double anAngleValue = anAng->angleDegree();
507   double aConstValue = real(ANGLE_VALUE_ID())->value();
508
509   AttributeIntegerPtr aType = integer(TYPE_ID());
510   switch ((SketcherPrs_Tools::AngleType)aType->value()) {
511   case SketcherPrs_Tools::ANGLE_DIRECT:
512     if (anAngleValue < 0.0 && aConstValue > 180.0)
513       convertAngle(real(ANGLE_VALUE_ID()), SketcherPrs_Tools::ANGLE_BACKWARD,
514                                            SketcherPrs_Tools::ANGLE_DIRECT);
515     break;
516   case SketcherPrs_Tools::ANGLE_BACKWARD:
517     if (anAngleValue < 0.0 && aConstValue < 180.0)
518       convertAngle(real(ANGLE_VALUE_ID()), SketcherPrs_Tools::ANGLE_DIRECT,
519                                            SketcherPrs_Tools::ANGLE_BACKWARD);
520     break;
521   default:
522     break;
523   }
524   data()->blockSendAttributeUpdated(aWasBlocked, false);
525   integer(VERSION_ID())->setValue(THE_VERSION_1);
526 }
527
528
529 // ===============   Auxiliary functions   ==================================
530 std::shared_ptr<GeomAPI_Pnt2d> intersect(FeaturePtr theLine1, FeaturePtr theLine2)
531 {
532   // Start and end points of lines
533   const std::string& aLineStartAttr = SketchPlugin_Line::START_ID();
534   const std::string& aLineEndAttr = SketchPlugin_Line::END_ID();
535   GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(theLine1.get(), aLineStartAttr);
536   GeomPnt2dPtr aEndA   = SketcherPrs_Tools::getPoint(theLine1.get(), aLineEndAttr);
537   GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(theLine2.get(), aLineStartAttr);
538   GeomPnt2dPtr aEndB   = SketcherPrs_Tools::getPoint(theLine2.get(), aLineEndAttr);
539   if (aStartA->distance(aEndA) < tolerance || aStartB->distance(aEndB) < tolerance)
540     std::shared_ptr<GeomAPI_Pnt2d>();
541
542   // Lines and their intersection point
543   std::shared_ptr<GeomAPI_Lin2d> aLA(new GeomAPI_Lin2d(aStartA, aEndA));
544   std::shared_ptr<GeomAPI_Lin2d> aLB(new GeomAPI_Lin2d(aStartB, aEndB));
545   return aLA->intersect(aLB);
546 }