1 // Copyright (C) 2014-2019 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "SketchPlugin_ConstraintAngle.h"
21 #include <SketchPlugin_Line.h>
22 #include <SketchPlugin_Tools.h>
23 #include <SketcherPrs_Tools.h>
25 #include <ModelAPI_AttributeDouble.h>
26 #include <ModelAPI_AttributeInteger.h>
27 #include <ModelAPI_Session.h>
28 #include <ModelAPI_Validator.h>
30 #include <GeomDataAPI_Point2D.h>
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>
38 #include <SketcherPrs_Factory.h>
39 #include <SketcherPrs_Tools.h>
45 const double tolerance = 1.e-7;
46 #define PI 3.1415926535897932
48 /// \brief Calculate intersection point of two lines
49 static std::shared_ptr<GeomAPI_Pnt2d> intersect(FeaturePtr theLine1, FeaturePtr theLine2);
52 SketchPlugin_ConstraintAngle::SketchPlugin_ConstraintAngle()
54 myFlyoutUpdate = false;
57 void SketchPlugin_ConstraintAngle::initAttributes()
59 ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators();
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());
66 data()->addAttribute(ANGLE_VALUE_ID(), ModelAPI_AttributeDouble::typeId());
67 data()->addAttribute(TYPE_ID(), ModelAPI_AttributeInteger::typeId());
69 data()->addAttribute(ANGLE_REVERSED_FIRST_LINE_ID(), ModelAPI_AttributeBoolean::typeId());
70 data()->addAttribute(ANGLE_REVERSED_SECOND_LINE_ID(), ModelAPI_AttributeBoolean::typeId());
72 data()->addAttribute(LOCATION_TYPE_ID(), ModelAPI_AttributeInteger::typeId());
73 aValidators->registerNotObligatory(getKind(), LOCATION_TYPE_ID());
75 data()->addAttribute(SELECTED_FIRST_POINT_ID(), GeomDataAPI_Point2D::typeId());
76 data()->attribute(SELECTED_FIRST_POINT_ID())->setIsArgument(false);
77 aValidators->registerNotObligatory(getKind(), SELECTED_FIRST_POINT_ID());
79 data()->addAttribute(SELECTED_SECOND_POINT_ID(), GeomDataAPI_Point2D::typeId());
80 data()->attribute(SELECTED_SECOND_POINT_ID())->setIsArgument(false);
81 aValidators->registerNotObligatory(getKind(), SELECTED_SECOND_POINT_ID());
83 if (attribute(TYPE_ID())->isInitialized())
84 myPrevAngleType = integer(TYPE_ID())->value();
86 myPrevAngleType = (int)SketcherPrs_Tools::ANGLE_DIRECT;
89 void SketchPlugin_ConstraintAngle::colorConfigInfo(std::string& theSection, std::string& theName,
90 std::string& theDefault)
92 theSection = "Visualization";
93 theName = "sketch_dimension_color";
94 theDefault = SKETCH_DIMENSION_COLOR;
97 void SketchPlugin_ConstraintAngle::execute()
99 std::shared_ptr<ModelAPI_Data> aData = data();
101 AttributeRefAttrPtr anAttrA = aData->refattr(SketchPlugin_Constraint::ENTITY_A());
102 AttributeRefAttrPtr anAttrB = aData->refattr(SketchPlugin_Constraint::ENTITY_B());
103 if (!anAttrA->isInitialized() || !anAttrB->isInitialized())
106 AttributeDoublePtr anAttrValue = real(ANGLE_VALUE_ID());
107 if (!anAttrValue->isInitialized())
110 // the value should to be computed here, not in the
111 // getAISObject in order to change the model value
112 // inside the object transaction. This is important for creating a constraint by preselection.
113 // The display of the presentation in this case happens after the transaction commit
114 std::shared_ptr<GeomDataAPI_Point2D> aFlyOutAttr = std::dynamic_pointer_cast<
115 GeomDataAPI_Point2D>(aData->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()));
116 if(!aFlyOutAttr->isInitialized())
117 compute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT());
120 AISObjectPtr SketchPlugin_ConstraintAngle::getAISObject(AISObjectPtr thePrevious)
125 AISObjectPtr anAIS = SketcherPrs_Factory::angleConstraint(this, sketch(),
127 if (anAIS.get() && !thePrevious.get())
128 SketchPlugin_Tools::setDimensionColor(anAIS);
132 void SketchPlugin_ConstraintAngle::attributeChanged(const std::string& theID)
134 std::shared_ptr<ModelAPI_Data> aData = data();
138 if (theID == TYPE_ID())
141 FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
142 FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
143 if (!aLineA || !aLineB)
146 if (theID == ENTITY_A() || theID == ENTITY_B() ||
147 theID == TYPE_ID() || theID == ANGLE_VALUE_ID()) {
149 } else if (theID == FLYOUT_VALUE_PNT() && !myFlyoutUpdate) {
150 // Recalculate flyout point in local coordinates
151 // coordinates are calculated according to the center of shapes intersection
152 std::shared_ptr<GeomDataAPI_Point2D> aFlyoutAttr =
153 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(FLYOUT_VALUE_PNT()));
155 std::shared_ptr<ModelAPI_Data> aData = data();
156 std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
158 // Intersection of lines
159 std::shared_ptr<GeomAPI_Pnt2d> anInter = intersect(aLineA, aLineB);
163 myFlyoutUpdate = true;
164 std::shared_ptr<GeomAPI_XY> aFlyoutDir = aFlyoutAttr->pnt()->xy()->decreased(anInter->xy());
165 if (aFlyoutDir->dot(aFlyoutDir) < tolerance * tolerance)
166 aFlyoutAttr->setValue(aFlyoutAttr->x() + tolerance, aFlyoutAttr->y());
167 myFlyoutUpdate = false;
171 void SketchPlugin_ConstraintAngle::calculateAngle()
173 // update *_REVERSED_* flags
174 calculateAnglePosition();
176 std::shared_ptr<ModelAPI_Data> aData = data();
177 std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
178 FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
179 FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
181 GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
182 GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
183 GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
184 GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
186 std::shared_ptr<GeomAPI_Lin2d> aLine1(new GeomAPI_Lin2d(aStartA, aEndA));
187 std::shared_ptr<GeomAPI_Lin2d> aLine2(new GeomAPI_Lin2d(aStartB, aEndB));
189 bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
190 bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
192 std::shared_ptr<GeomAPI_Angle2d> anAng(
193 new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2));
194 double anAngle = anAng->angleDegree();
196 AttributeDoublePtr anAngleValueAttr = real(ANGLE_VALUE_ID());
197 if (!anAngleValueAttr->isInitialized())
198 anAngleValueAttr->setValue(getAngleForType(fabs(anAngle)));
200 anAngle /= fabs(anAngle);
201 anAngle *= getAngleForType(anAngleValueAttr->value());
203 // update value of the constraint to be passed to the solver
204 real(SketchPlugin_Constraint::VALUE())->setValue(anAngle);
207 void SketchPlugin_ConstraintAngle::calculateAnglePosition()
209 if (attribute(ANGLE_REVERSED_FIRST_LINE_ID())->isInitialized() &&
210 attribute(ANGLE_REVERSED_SECOND_LINE_ID())->isInitialized())
211 return; // already calculated
213 DataPtr aData = data();
214 FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
215 FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
217 GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
218 GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
219 GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
220 GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
222 bool isReversed1 = false;
223 bool isReversed2 = false;
225 GeomPnt2dPtr aSelected1 = SketcherPrs_Tools::getPoint(this, SELECTED_FIRST_POINT_ID());
226 GeomPnt2dPtr aSelected2 = SketcherPrs_Tools::getPoint(this, SELECTED_SECOND_POINT_ID());
227 if (aSelected1 && aSelected2) {
228 GeomPnt2dPtr anInterPnt = intersect(aLineA, aLineB);
231 std::shared_ptr<GeomAPI_XY> anInterXY = anInterPnt->xy();
232 isReversed1 = aSelected1->xy()->decreased(anInterXY)->dot(
233 aEndA->xy()->decreased(aStartA->xy())) < -tolerance;
234 isReversed2 = aSelected2->xy()->decreased(anInterXY)->dot(
235 aEndB->xy()->decreased(aStartB->xy())) < -tolerance;
238 // no point is selected (document opened or Python script is loaded),
239 // calculate basing on the value
240 std::shared_ptr<GeomAPI_Angle2d> anAng(new GeomAPI_Angle2d(aStartA, aEndA, aStartB, aEndB));
241 isReversed1 = anAng->isReversed(0);
242 isReversed2 = anAng->isReversed(1);
245 // adjust reversed flags according to the angle type
246 AttributeIntegerPtr aTypeAttr = integer(TYPE_ID());
247 if (aTypeAttr && aTypeAttr->isInitialized() &&
248 (SketcherPrs_Tools::AngleType)(aTypeAttr->value()) == SketcherPrs_Tools::ANGLE_COMPLEMENTARY)
249 isReversed1 = !isReversed1;
251 boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(isReversed1);
252 boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(isReversed2);
255 static double angleForType(const double theAngle, const int theType)
257 double anAngle = theAngle;
258 switch ((SketcherPrs_Tools::AngleType)theType) {
259 case SketcherPrs_Tools::ANGLE_DIRECT:
262 case SketcherPrs_Tools::ANGLE_COMPLEMENTARY:
263 anAngle = 180.0 - theAngle;
265 case SketcherPrs_Tools::ANGLE_BACKWARD:
266 anAngle = 360.0 - theAngle;
274 double SketchPlugin_ConstraintAngle::getAngleForType(double theAngle)
276 return angleForType(theAngle, integer(TYPE_ID())->value());
279 void SketchPlugin_ConstraintAngle::updateAngleValue()
281 AttributeIntegerPtr anAngleType = integer(TYPE_ID());
282 AttributeDoublePtr anAngleValueAttr = real(ANGLE_VALUE_ID());
283 if (anAngleValueAttr->isInitialized()) {
284 if (anAngleValueAttr->text().empty()) {
285 // calculate value related to the type twice:
286 // the first time - to return to direct angle,
287 // the second time - to apply new type
288 double aValue = angleForType(anAngleValueAttr->value(), myPrevAngleType);
289 aValue = angleForType(aValue, anAngleType->value());
290 anAngleValueAttr->setValue(aValue);
293 // process the parametric value
294 std::string anAngleText = anAngleValueAttr->text();
295 std::regex anAngleRegex("\\s*([-+]?[0-9]*\\.?[0-9]*)\\s*([-+])\\s*\\((.*)\\)",
296 std::regex_constants::ECMAScript);
298 double anAnglePrefix = 0.0;
299 static const char aSignPrefix[2] = { '-', '+' };
303 if (std::regex_search(anAngleText, aResult, anAngleRegex)) {
304 anAnglePrefix = std::atof(aResult[1].str().c_str());
305 aSignInd = aResult[2].str()[0] == aSignPrefix[0] ? 0 : 1;
306 anAngleText = aResult[3].str();
309 if (myPrevAngleType != SketcherPrs_Tools::ANGLE_DIRECT)
310 aSignInd = 1 - aSignInd;
311 anAnglePrefix = angleForType(anAnglePrefix, myPrevAngleType);
313 if (anAngleType->value() != SketcherPrs_Tools::ANGLE_DIRECT)
314 aSignInd = 1 - aSignInd;
315 anAnglePrefix = angleForType(anAnglePrefix, anAngleType->value());
317 std::ostringstream aText;
318 bool isPrintSign = true;
319 if (fabs(anAnglePrefix) > tolerance)
320 aText << anAnglePrefix;
322 isPrintSign = aSignInd == 0;
324 aText << " " << aSignPrefix[aSignInd] << " (";
325 aText << anAngleText << (isPrintSign ? ")" : "");
326 anAngleValueAttr->setText(aText.str());
329 myPrevAngleType = anAngleType->value();
332 bool SketchPlugin_ConstraintAngle::compute(const std::string& theAttributeId)
334 if (theAttributeId != SketchPlugin_Constraint::FLYOUT_VALUE_PNT())
339 std::shared_ptr<GeomDataAPI_Point2D> aFlyOutAttr = std::dynamic_pointer_cast<
340 GeomDataAPI_Point2D>(attribute(theAttributeId));
341 if (aFlyOutAttr->isInitialized() &&
342 (fabs(aFlyOutAttr->x()) >= tolerance || fabs(aFlyOutAttr->y()) >= tolerance))
345 DataPtr aData = data();
346 std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
347 FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A());
348 FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B());
350 if ((aLineA.get() == NULL) || (aLineB.get() == NULL))
353 // Start and end points of lines
354 GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
355 GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
356 if (aStartA->distance(aEndA) < tolerance)
359 GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
360 GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
361 if (aStartB->distance(aEndB) < tolerance)
364 myFlyoutUpdate = true;
365 double aX = (aStartA->x() + aEndA->x() + aStartB->x() + aEndB->x()) / 4.;
366 double aY = (aStartA->y() + aEndA->y() + aStartB->y() + aEndB->y()) / 4.;
368 aFlyOutAttr->setValue(aX, aY);
369 myFlyoutUpdate = false;
375 // =============== Auxiliary functions ==================================
376 std::shared_ptr<GeomAPI_Pnt2d> intersect(FeaturePtr theLine1, FeaturePtr theLine2)
378 // Start and end points of lines
379 const std::string& aLineStartAttr = SketchPlugin_Line::START_ID();
380 const std::string& aLineEndAttr = SketchPlugin_Line::END_ID();
381 GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(theLine1.get(), aLineStartAttr);
382 GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(theLine1.get(), aLineEndAttr);
383 GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(theLine2.get(), aLineStartAttr);
384 GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(theLine2.get(), aLineEndAttr);
385 if (aStartA->distance(aEndA) < tolerance || aStartB->distance(aEndB) < tolerance)
386 std::shared_ptr<GeomAPI_Pnt2d>();
388 // Lines and their intersection point
389 std::shared_ptr<GeomAPI_Lin2d> aLA(new GeomAPI_Lin2d(aStartA, aEndA));
390 std::shared_ptr<GeomAPI_Lin2d> aLB(new GeomAPI_Lin2d(aStartB, aEndB));
391 return aLA->intersect(aLB);