Salome HOME
e1d15b68ce2099b623fed5f9aee8329237646884
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_ConstraintAngle.cpp
1 // Copyright (C) 2014-2017  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
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
19 //
20
21 #include "SketchPlugin_ConstraintAngle.h"
22 #include <SketchPlugin_Line.h>
23
24 #include <ModelAPI_AttributeDouble.h>
25 #include <ModelAPI_AttributeInteger.h>
26 #include <ModelAPI_Session.h>
27 #include <ModelAPI_Validator.h>
28
29 #include <GeomDataAPI_Point2D.h>
30
31 #include <GeomAPI_Angle2d.h>
32 #include <GeomAPI_Dir2d.h>
33 #include <GeomAPI_Lin2d.h>
34 #include <GeomAPI_Pnt2d.h>
35 #include <GeomAPI_XY.h>
36
37 #include <SketcherPrs_Factory.h>
38 #include <SketcherPrs_Tools.h>
39
40 #include <math.h>
41
42 const double tolerance = 1.e-7;
43 #define PI 3.1415926535897932
44
45 /// \brief Calculate intersection point of two lines
46 static std::shared_ptr<GeomAPI_Pnt2d> intersect(FeaturePtr theLine1, FeaturePtr theLine2);
47
48
49 SketchPlugin_ConstraintAngle::SketchPlugin_ConstraintAngle()
50 {
51   myFlyoutUpdate = false;
52 }
53
54 void SketchPlugin_ConstraintAngle::initAttributes()
55 {
56   data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
57   data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId());
58   data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId());
59   data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId());
60
61   data()->addAttribute(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID(),
62                        ModelAPI_AttributeDouble::typeId());
63   data()->addAttribute(SketchPlugin_ConstraintAngle::TYPE_ID(),
64                        ModelAPI_AttributeInteger::typeId());
65
66   data()->addAttribute(SketchPlugin_ConstraintAngle::ANGLE_REVERSED_FIRST_LINE_ID(),
67                        ModelAPI_AttributeBoolean::typeId());
68   data()->addAttribute(SketchPlugin_ConstraintAngle::ANGLE_REVERSED_SECOND_LINE_ID(),
69                        ModelAPI_AttributeBoolean::typeId());
70
71   data()->addAttribute(SketchPlugin_ConstraintAngle::LOCATION_TYPE_ID(),
72                        ModelAPI_AttributeInteger::typeId());
73   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), LOCATION_TYPE_ID());
74 }
75
76 void SketchPlugin_ConstraintAngle::colorConfigInfo(std::string& theSection, std::string& theName,
77                                                    std::string& theDefault)
78 {
79   theSection = "Visualization";
80   theName = "sketch_dimension_color";
81   theDefault = SKETCH_DIMENSION_COLOR;
82 }
83
84 void SketchPlugin_ConstraintAngle::execute()
85 {
86   std::shared_ptr<ModelAPI_Data> aData = data();
87
88   std::shared_ptr<ModelAPI_AttributeRefAttr> anAttrA =
89     aData->refattr(SketchPlugin_Constraint::ENTITY_A());
90   std::shared_ptr<ModelAPI_AttributeRefAttr> anAttrB =
91     aData->refattr(SketchPlugin_Constraint::ENTITY_B());
92   if (!anAttrA->isInitialized() || !anAttrB->isInitialized())
93     return;
94
95   AttributeDoublePtr anAttrValue = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
96       aData->attribute(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()));
97
98   if (!anAttrValue->isInitialized()) {
99     double anAngle = calculateAngle();
100     anAttrValue->setValue(anAngle);
101     updateConstraintValueByAngleValue();
102   }
103   // the value should to be computed here, not in the
104   // getAISObject in order to change the model value
105   // inside the object transaction. This is important for creating a constraint by preselection.
106   // The display of the presentation in this case happens after the transaction commit
107   std::shared_ptr<GeomDataAPI_Point2D> aFlyOutAttr = std::dynamic_pointer_cast<
108       GeomDataAPI_Point2D>(aData->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()));
109   if(!aFlyOutAttr->isInitialized())
110     compute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT());
111 }
112
113 AISObjectPtr SketchPlugin_ConstraintAngle::getAISObject(AISObjectPtr thePrevious)
114 {
115   if (!sketch())
116     return thePrevious;
117
118   AISObjectPtr anAIS = SketcherPrs_Factory::angleConstraint(this, sketch()->coordinatePlane(),
119                                                             thePrevious);
120   return anAIS;
121 }
122
123 void SketchPlugin_ConstraintAngle::attributeChanged(const std::string& theID)
124 {
125   std::shared_ptr<ModelAPI_Data> aData = data();
126   if (!aData)
127     return;
128   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A());
129   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B());
130   if (!aLineA || !aLineB)
131     return;
132
133   if (theID == SketchPlugin_Constraint::ENTITY_A() ||
134       theID == SketchPlugin_Constraint::ENTITY_B()) {
135     AttributeDoublePtr aValueAttr = real(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID());
136     AttributeDoublePtr aConstrValueAttr = real(SketchPlugin_Constraint::VALUE());
137     // only if one of attributes is not initialized, try to compute the current value
138     if (!aValueAttr->isInitialized() || !aConstrValueAttr->isInitialized()) {
139       if (aValueAttr->isInitialized() && !aConstrValueAttr->isInitialized())
140         // initialize base value of constraint
141         updateConstraintValueByAngleValue();
142       double anAngle = calculateAngle();
143       aValueAttr->setValue(anAngle);
144       updateConstraintValueByAngleValue();
145     }
146   } else if (theID == SketchPlugin_Constraint::FLYOUT_VALUE_PNT() && !myFlyoutUpdate) {
147     // Recalculate flyout point in local coordinates
148     // coordinates are calculated according to the center of shapes intersection
149     std::shared_ptr<GeomDataAPI_Point2D> aFlyoutAttr =
150       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
151       attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()));
152
153     std::shared_ptr<ModelAPI_Data> aData = data();
154     std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
155     FeaturePtr aLineA =
156       SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A());
157     FeaturePtr aLineB =
158       SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B());
159
160     // Intersection of lines
161     std::shared_ptr<GeomAPI_Pnt2d> anInter = intersect(aLineA, aLineB);
162     if (!anInter)
163       return;
164
165     myFlyoutUpdate = true;
166     std::shared_ptr<GeomAPI_XY> aFlyoutDir = aFlyoutAttr->pnt()->xy()->decreased(anInter->xy());
167     if (aFlyoutDir->dot(aFlyoutDir) < tolerance * tolerance)
168       aFlyoutAttr->setValue(aFlyoutAttr->x() + tolerance, aFlyoutAttr->y());
169     myFlyoutUpdate = false;
170   }
171   else if (theID == SketchPlugin_ConstraintAngle::TYPE_ID()) {
172     std::shared_ptr<ModelAPI_AttributeDouble> aValueAttr = std::dynamic_pointer_cast<
173       ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()));
174     double anAngle = calculateAngle();
175     if (aValueAttr->text().empty())
176       aValueAttr->setValue(anAngle);
177     else {
178       aValueAttr = std::dynamic_pointer_cast<
179         ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_ConstraintAngle::VALUE()));
180       aValueAttr->setValue(anAngle);
181     }
182   }
183   else if (theID == SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()) {
184     updateConstraintValueByAngleValue();
185   }
186 }
187
188 double SketchPlugin_ConstraintAngle::calculateAngle()
189 {
190   std::shared_ptr<ModelAPI_Data> aData = data();
191   std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
192   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A());
193   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B());
194
195   std::shared_ptr<GeomDataAPI_Point2D> aStartA = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
196       aLineA->attribute(SketchPlugin_Line::START_ID()));
197   std::shared_ptr<GeomDataAPI_Point2D> aEndA = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
198       aLineA->attribute(SketchPlugin_Line::END_ID()));
199   std::shared_ptr<GeomDataAPI_Point2D> aStartB = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
200       aLineB->attribute(SketchPlugin_Line::START_ID()));
201   std::shared_ptr<GeomDataAPI_Point2D> aEndB = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
202       aLineB->attribute(SketchPlugin_Line::END_ID()));
203
204   std::shared_ptr<GeomAPI_Angle2d> anAng;
205   if (!attribute(ANGLE_REVERSED_FIRST_LINE_ID())->isInitialized() ||
206       !attribute(ANGLE_REVERSED_SECOND_LINE_ID())->isInitialized())
207     anAng = std::shared_ptr<GeomAPI_Angle2d>(new GeomAPI_Angle2d(
208         aStartA->pnt(), aEndA->pnt(), aStartB->pnt(), aEndB->pnt()));
209   else {
210     std::shared_ptr<GeomAPI_Lin2d> aLine1(new GeomAPI_Lin2d(aStartA->pnt(), aEndA->pnt()));
211     bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
212     std::shared_ptr<GeomAPI_Lin2d> aLine2(new GeomAPI_Lin2d(aStartB->pnt(), aEndB->pnt()));
213     bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
214     anAng = std::shared_ptr<GeomAPI_Angle2d>(
215       new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2));
216   }
217   double anAngle = anAng->angleDegree();
218   std::shared_ptr<ModelAPI_AttributeDouble> aValueAttr = std::dynamic_pointer_cast<
219       ModelAPI_AttributeDouble>(data()->attribute(VALUE()));
220   std::shared_ptr<ModelAPI_AttributeDouble> anAngleValueAttr = std::dynamic_pointer_cast<
221       ModelAPI_AttributeDouble>(data()->attribute(ANGLE_VALUE_ID()));
222   if (!aValueAttr->isInitialized())
223     aValueAttr->setValue(anAngle);
224   /// an angle value should be corrected by the current angle type
225   anAngle = getAngleForType(anAngleValueAttr->text().empty() ?
226                             anAngle : anAngleValueAttr->value());
227   boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(anAng->isReversed(0));
228   boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(anAng->isReversed(1));
229   return anAngle;
230 }
231
232 double SketchPlugin_ConstraintAngle::getAngleForType(double theAngle, bool isPreviousValueObtuse)
233 {
234   double anAngle = theAngle;
235
236   std::shared_ptr<ModelAPI_Data> aData = data();
237   std::shared_ptr<ModelAPI_AttributeInteger> aTypeAttr = std::dynamic_pointer_cast<
238       ModelAPI_AttributeInteger>(aData->attribute(SketchPlugin_ConstraintAngle::TYPE_ID()));
239   SketcherPrs_Tools::AngleType anAngleType = (SketcherPrs_Tools::AngleType)(aTypeAttr->value());
240   switch (anAngleType) {
241     case SketcherPrs_Tools::ANGLE_DIRECT:
242       anAngle = theAngle;
243     break;
244     case SketcherPrs_Tools::ANGLE_COMPLEMENTARY: {
245       if (theAngle > 180 || isPreviousValueObtuse)
246         anAngle = theAngle - 180.0;
247       else
248         anAngle = 180.0 - theAngle;
249
250       if (anAngle < 0.0)
251         anAngle += 360.0;
252     }
253     break;
254     case SketcherPrs_Tools::ANGLE_BACKWARD:
255       anAngle = 360.0 - theAngle;
256     break;
257     default:
258       break;
259   }
260   return anAngle;
261 }
262
263 void SketchPlugin_ConstraintAngle::updateConstraintValueByAngleValue()
264 {
265   std::shared_ptr<ModelAPI_AttributeDouble> aValueAttr = std::dynamic_pointer_cast<
266     ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()));
267   double anAngle = aValueAttr->value();
268
269   /// an angle value should be corrected by the current angle type
270   aValueAttr = std::dynamic_pointer_cast<
271                   ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_Constraint::VALUE()));
272   if (!aValueAttr->isInitialized())
273     calculateAngle();
274   anAngle = getAngleForType(anAngle, aValueAttr->value() > 180.0);
275   aValueAttr->setValue(anAngle);
276 }
277
278 bool SketchPlugin_ConstraintAngle::compute(const std::string& theAttributeId)
279 {
280   if (theAttributeId != SketchPlugin_Constraint::FLYOUT_VALUE_PNT())
281     return false;
282   if (!sketch())
283     return false;
284
285   std::shared_ptr<GeomDataAPI_Point2D> aFlyOutAttr = std::dynamic_pointer_cast<
286                            GeomDataAPI_Point2D>(attribute(theAttributeId));
287   if (aFlyOutAttr->isInitialized() &&
288       (fabs(aFlyOutAttr->x()) >= tolerance || fabs(aFlyOutAttr->y()) >= tolerance))
289     return false;
290
291   DataPtr aData = data();
292   std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
293   FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A());
294   FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B());
295
296   if ((aLineA.get() == NULL) || (aLineB.get() == NULL))
297     return false;
298
299   // Start and end points of lines
300   std::shared_ptr<GeomDataAPI_Point2D> aPointA1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
301       aLineA->attribute(SketchPlugin_Line::START_ID()));
302   std::shared_ptr<GeomDataAPI_Point2D> aPointA2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
303       aLineA->attribute(SketchPlugin_Line::END_ID()));
304
305   std::shared_ptr<GeomAPI_Pnt2d> aStartA = aPointA1->pnt();
306   std::shared_ptr<GeomAPI_Pnt2d> aEndA   = aPointA2->pnt();
307   if (aStartA->distance(aEndA) < tolerance)
308     return false;
309
310   myFlyoutUpdate = true;
311   double aX = (aStartA->x() + aEndA->x()) / 2.;
312   double aY = (aStartA->y() + aEndA->y()) / 2.;
313
314   aFlyOutAttr->setValue(aX, aY);
315   myFlyoutUpdate = false;
316
317   return true;
318 }
319
320
321 // ===============   Auxiliary functions   ==================================
322 std::shared_ptr<GeomAPI_Pnt2d> intersect(FeaturePtr theLine1, FeaturePtr theLine2)
323 {
324   // Start and end points of lines
325   std::shared_ptr<GeomDataAPI_Point2D> aPointA1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
326       theLine1->attribute(SketchPlugin_Line::START_ID()));
327   std::shared_ptr<GeomDataAPI_Point2D> aPointA2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
328       theLine1->attribute(SketchPlugin_Line::END_ID()));
329
330   std::shared_ptr<GeomDataAPI_Point2D> aPointB1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
331       theLine2->attribute(SketchPlugin_Line::START_ID()));
332   std::shared_ptr<GeomDataAPI_Point2D> aPointB2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
333       theLine2->attribute(SketchPlugin_Line::END_ID()));
334
335   std::shared_ptr<GeomAPI_Pnt2d> aStartA = aPointA1->pnt();
336   std::shared_ptr<GeomAPI_Pnt2d> aEndA   = aPointA2->pnt();
337   std::shared_ptr<GeomAPI_Pnt2d> aStartB = aPointB1->pnt();
338   std::shared_ptr<GeomAPI_Pnt2d> aEndB   = aPointB2->pnt();
339   if (aStartA->distance(aEndA) < tolerance || aStartB->distance(aEndB) < tolerance)
340     std::shared_ptr<GeomAPI_Pnt2d>();
341
342   // Lines and their intersection point
343   std::shared_ptr<GeomAPI_Lin2d> aLA(new GeomAPI_Lin2d(aStartA, aEndA));
344   std::shared_ptr<GeomAPI_Lin2d> aLB(new GeomAPI_Lin2d(aStartB, aEndB));
345   return aLA->intersect(aLB);
346 }