1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
3 // File: SketchPlugin_Arc.cpp
4 // Created: 26 Apr 2014
5 // Author: Artem ZHIDKOV
7 #include "SketchPlugin_Arc.h"
8 #include "SketchPlugin_Sketch.h"
9 #include <SketchPlugin_ConstraintCoincidence.h>
10 #include <SketchPlugin_ConstraintTangent.h>
12 #include <Events_Loop.h>
13 #include <ModelAPI_Data.h>
14 #include <ModelAPI_ResultConstruction.h>
15 #include <ModelAPI_AttributeDouble.h>
16 #include <ModelAPI_AttributeRefAttr.h>
17 #include <ModelAPI_AttributeSelection.h>
18 #include <ModelAPI_AttributeString.h>
19 #include <ModelAPI_Events.h>
20 #include <ModelAPI_Validator.h>
21 #include <ModelAPI_Session.h>
22 #include <ModelAPI_Tools.h>
24 #include <GeomAPI_Ax2.h>
25 #include <GeomAPI_Circ2d.h>
26 #include <GeomAPI_Circ.h>
27 #include <GeomAPI_Dir2d.h>
28 #include <GeomAPI_Dir.h>
29 #include <GeomAPI_Lin2d.h>
30 #include <GeomAPI_Lin.h>
31 #include <GeomAPI_Pnt2d.h>
32 #include <GeomAPI_XY.h>
33 #include <GeomDataAPI_Point2D.h>
34 #include <GeomDataAPI_Dir.h>
35 #include <GeomAlgoAPI_PointBuilder.h>
36 #include <GeomAlgoAPI_EdgeBuilder.h>
37 #include <GeomAlgoAPI_CompoundBuilder.h>
41 const double tolerance = 1e-7;
42 const double paramTolerance = 1.e-4;
43 const double PI =3.141592653589793238463;
46 static const std::string& ARC_TYPE_CENTER_START_END()
48 static const std::string TYPE("CenterStartEnd");
51 static const std::string& ARC_TYPE_THREE_POINTS()
53 static const std::string TYPE("ThreePoints");
57 static const std::string& PASSED_POINT_ID()
59 static const std::string PASSED_PNT("ArcPassedPoint");
62 static const std::string& RADIUS_ID()
64 static const std::string RADIUS("ArcRadius");
67 static const std::string& ANGLE_ID()
69 static const std::string ANGLE("ArcAngle");
73 static const std::string& POINT_ID(int theIndex)
76 case 1: return SketchPlugin_Arc::START_ID();
77 case 2: return SketchPlugin_Arc::END_ID();
78 case 3: return PASSED_POINT_ID();
81 static const std::string DUMMY;
88 SketchPlugin_Arc::SketchPlugin_Arc()
89 : SketchPlugin_SketchEntity()
91 myStartUpdate = false;
100 void SketchPlugin_Arc::initDerivedClassAttributes()
102 data()->addAttribute(CENTER_ID(), GeomDataAPI_Point2D::typeId());
103 data()->addAttribute(START_ID(), GeomDataAPI_Point2D::typeId());
104 std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
105 GeomDataAPI_Point2D>(data()->addAttribute(END_ID(), GeomDataAPI_Point2D::typeId()));
106 data()->addAttribute(EXTERNAL_ID(), ModelAPI_AttributeSelection::typeId());
107 ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EXTERNAL_ID());
109 data()->addAttribute(INVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
110 AttributeBooleanPtr isInversed =
111 std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(attribute(INVERSED_ID()));
112 if (!isInversed->isInitialized())
113 isInversed->setValue(false);
115 // get the initial values
116 if (anEndAttr->isInitialized()) {
117 myXEndBefore = anEndAttr->x();
118 myYEndBefore = anEndAttr->y();
121 data()->addAttribute(ARC_TYPE(), ModelAPI_AttributeString::typeId());
122 std::dynamic_pointer_cast<ModelAPI_AttributeString>(
123 data()->attribute(ARC_TYPE()))->setValue(ARC_TYPE_CENTER_START_END());
125 data()->addAttribute(PASSED_POINT_ID(), GeomDataAPI_Point2D::typeId());
126 data()->addAttribute(TANGENT_POINT_ID(), ModelAPI_AttributeRefAttr::typeId());
127 data()->addAttribute(RADIUS_ID(), ModelAPI_AttributeDouble::typeId());
128 data()->addAttribute(ANGLE_ID(), ModelAPI_AttributeDouble::typeId());
131 void SketchPlugin_Arc::execute()
133 SketchPlugin_Sketch* aSketch = sketch();
134 // result for the arc is set only when all obligatory attributes are initialized,
135 // otherwise AIS object is used to visualize the arc's preview
136 if (aSketch && isFeatureValid()) {
137 ResultPtr aLastResult = lastResult();
138 bool hasResult = aLastResult && aLastResult.get();
140 // compute a circle point in 3D view
141 std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
142 GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
143 // compute the arc start point
144 std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
145 GeomDataAPI_Point2D>(data()->attribute(START_ID()));
147 std::shared_ptr<GeomAPI_Pnt> aCenter(aSketch->to3D(aCenterAttr->x(), aCenterAttr->y()));
148 // make a visible point
149 std::shared_ptr<GeomAPI_Shape> aCenterPointShape = GeomAlgoAPI_PointBuilder::point(aCenter);
150 std::shared_ptr<ModelAPI_ResultConstruction> aConstr1 = document()->createConstruction(
152 aConstr1->setShape(aCenterPointShape);
153 aConstr1->setIsInHistory(false);
154 setResult(aConstr1, 0);
156 // make a visible circle
157 std::shared_ptr<GeomDataAPI_Dir> aNDir = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
158 aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
159 std::shared_ptr<GeomAPI_Dir> aNormal = aNDir->dir();
160 std::shared_ptr<GeomAPI_Pnt> aStartPoint(aSketch->to3D(aStartAttr->x(), aStartAttr->y()));
162 // compute and change the arc end point
163 std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
164 GeomDataAPI_Point2D>(data()->attribute(END_ID()));
165 std::shared_ptr<GeomAPI_Circ2d> aCircleForArc(
166 new GeomAPI_Circ2d(aCenterAttr->pnt(), aStartAttr->pnt()));
167 std::shared_ptr<GeomAPI_Pnt2d> aProjection = aCircleForArc->project(anEndAttr->pnt());
168 if (aProjection && anEndAttr->pnt()->distance(aProjection) > tolerance)
169 anEndAttr->setValue(aProjection);
170 std::shared_ptr<GeomAPI_Pnt> aEndPoint(aSketch->to3D(anEndAttr->x(), anEndAttr->y()));
171 AttributeBooleanPtr isInversed =
172 std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(attribute(INVERSED_ID()));
174 std::shared_ptr<GeomAPI_Dir> anXDir(new GeomAPI_Dir(aStartPoint->xyz()->decreased(aCenter->xyz())));
175 std::shared_ptr<GeomAPI_Ax2> anAx2(new GeomAPI_Ax2(aCenter, aNormal, anXDir));
176 std::shared_ptr<GeomAPI_Circ> aCirc(new GeomAPI_Circ(anAx2, aCenter->distance(aStartPoint)));
177 double aParameterNew = 0.0;
178 if(aCirc->parameter(aEndPoint, paramTolerance, aParameterNew)) {
179 if(0 <= myParamBefore && myParamBefore <= PI / 2.0
180 && PI * 1.5 <= aParameterNew && aParameterNew <= PI * 2.0) {
181 isInversed->setValue(true);
182 } else if(PI * 1.5 <= myParamBefore && myParamBefore <= PI * 2.0
183 && 0 <= aParameterNew && aParameterNew <= PI / 2.0) {
184 isInversed->setValue(false);
187 myParamBefore = aParameterNew;
189 std::shared_ptr<GeomAPI_Shape> aCircleShape;
190 if(!isInversed->value()) {
191 aCircleShape = GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenter, aStartPoint, aEndPoint, aNormal);
193 aCircleShape = GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenter, aEndPoint, aStartPoint, aNormal);
197 std::shared_ptr<ModelAPI_ResultConstruction> aConstr2 = document()->createConstruction(
199 aConstr2->setShape(aCircleShape);
200 aConstr2->setIsInHistory(false);
201 setResult(aConstr2, 1);
204 // update radius and angle
205 updateDependentAttributes();
209 AISObjectPtr SketchPlugin_Arc::getAISObject(AISObjectPtr thePrevious)
211 SketchPlugin_Sketch* aSketch = sketch();
213 // if the feature is valid, the execute() method should be performed, AIS object is empty
214 if (!isFeatureValid()) {
215 // compute a circle point in 3D view
216 std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
217 GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
219 std::list<std::shared_ptr<GeomAPI_Shape> > aShapes;
220 if (aCenterAttr->isInitialized()) {
221 std::shared_ptr<GeomAPI_Pnt> aCenter(aSketch->to3D(aCenterAttr->x(), aCenterAttr->y()));
222 // make a visible point
223 std::shared_ptr<GeomAPI_Shape> aCenterPointShape = GeomAlgoAPI_PointBuilder::point(aCenter);
224 aShapes.push_back(aCenterPointShape);
226 std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
227 GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::START_ID()));
228 std::shared_ptr<GeomDataAPI_Point2D> aEndAttr = std::dynamic_pointer_cast<
229 GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::END_ID()));
230 AttributeStringPtr aTypeAttr = std::dynamic_pointer_cast<ModelAPI_AttributeString>(
231 data()->attribute(ARC_TYPE()));
233 if (aStartAttr->isInitialized()) {
234 // make a visible circle
235 std::shared_ptr<GeomDataAPI_Dir> aNDir = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
236 aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
237 bool aHasPlane = aNDir && !(aNDir->x() == 0 && aNDir->y() == 0 && aNDir->z() == 0);
239 std::shared_ptr<GeomAPI_Dir> aNormal = aNDir->dir();
240 std::shared_ptr<GeomAPI_Pnt> aStartPoint(aSketch->to3D(aStartAttr->x(), aStartAttr->y()));
241 std::shared_ptr<GeomAPI_Pnt> aEndPoint = aStartPoint;
242 if (aTypeAttr && aTypeAttr->isInitialized() &&
243 aTypeAttr->value() == ARC_TYPE_THREE_POINTS() && aEndAttr->isInitialized())
244 aEndPoint = aSketch->to3D(aEndAttr->x(), aEndAttr->y());
246 std::shared_ptr<GeomAPI_Shape> aCircleShape = GeomAlgoAPI_EdgeBuilder::lineCircleArc(
247 aCenter, aStartPoint, aEndPoint, aNormal);
249 aShapes.push_back(aCircleShape);
253 if (!aShapes.empty()) {
254 std::shared_ptr<GeomAPI_Shape> aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
255 AISObjectPtr anAIS = thePrevious;
257 anAIS = AISObjectPtr(new GeomAPI_AISObject);
258 anAIS->createShape(aCompound);
264 return AISObjectPtr();
267 void SketchPlugin_Arc::move(double theDeltaX, double theDeltaY)
269 std::shared_ptr<ModelAPI_Data> aData = data();
270 if (!aData->isValid())
273 aData->blockSendAttributeUpdated(true);
275 myStartUpdate = true;
277 std::shared_ptr<GeomDataAPI_Point2D> aPoint2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
278 aData->attribute(SketchPlugin_Arc::START_ID()));
279 aPoint2->move(theDeltaX, theDeltaY);
281 std::shared_ptr<GeomDataAPI_Point2D> aPoint3 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
282 aData->attribute(SketchPlugin_Arc::END_ID()));
283 aPoint3->move(theDeltaX, theDeltaY);
284 myStartUpdate = false;
287 std::shared_ptr<GeomDataAPI_Point2D> aPoint1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
288 aData->attribute(SketchPlugin_Arc::CENTER_ID()));
289 aPoint1->move(theDeltaX, theDeltaY);
291 std::shared_ptr<GeomDataAPI_Point2D> aPassedPoint =
292 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aData->attribute(PASSED_POINT_ID()));
293 aPassedPoint->move(theDeltaX, theDeltaY);
294 aData->blockSendAttributeUpdated(false);
297 bool SketchPlugin_Arc::isFixed() {
298 return data()->selection(EXTERNAL_ID())->context().get() != NULL;
301 bool SketchPlugin_Arc::isFeatureValid()
303 AttributeStringPtr anArcTypeAttr =
304 std::dynamic_pointer_cast<ModelAPI_AttributeString>(data()->attribute(ARC_TYPE()));
307 std::string anArcType = anArcTypeAttr->value();
309 std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
310 GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::CENTER_ID()));
311 std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
312 GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::START_ID()));
313 std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
314 GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::END_ID()));
315 std::shared_ptr<GeomDataAPI_Point2D> aPassedAttr = std::dynamic_pointer_cast<
316 GeomDataAPI_Point2D>(data()->attribute(PASSED_POINT_ID()));
318 bool isValid = false;
319 if (anArcType == ARC_TYPE_THREE_POINTS())
320 isValid = aStartAttr->isInitialized() && anEndAttr->isInitialized() && aPassedAttr->isInitialized();
322 isValid = aCenterAttr->isInitialized() && aStartAttr->isInitialized() && anEndAttr->isInitialized();
327 static inline void adjustPeriod(double& theParam)
329 static const double PERIOD = 2.0 * PI;
330 while (theParam < 0.0) theParam += PERIOD;
331 while (theParam >= PERIOD) theParam -= PERIOD;
334 static inline void calculateArcAngleRadius(
335 const std::shared_ptr<GeomAPI_Circ2d>& theCircle,
336 const std::shared_ptr<GeomAPI_Pnt2d>& theStartPoint,
337 const std::shared_ptr<GeomAPI_Pnt2d>& theEndPoint,
338 const std::shared_ptr<GeomAPI_Pnt2d>& thePassedPoint,
339 AttributeDoublePtr theAngleAttr,
340 AttributeDoublePtr theRadiusAttr)
342 double aStartParam, aEndParam, aPassedParam;
343 theCircle->parameter(theStartPoint, paramTolerance, aStartParam);
344 theCircle->parameter(theEndPoint, paramTolerance, aEndParam);
345 theCircle->parameter(thePassedPoint, paramTolerance, aPassedParam);
346 adjustPeriod(aStartParam);
347 adjustPeriod(aEndParam);
348 adjustPeriod(aPassedParam);
350 if (aPassedParam >= aStartParam && aPassedParam <= aEndParam)
351 theAngleAttr->setValue((aEndParam - aStartParam) * 180.0 / PI);
353 theAngleAttr->setValue((aEndParam - aStartParam - 2.0 * PI) * 180.0 / PI);
354 theRadiusAttr->setValue(theCircle->radius());
357 static inline bool calculatePassedPoint(
358 const std::shared_ptr<GeomAPI_Pnt2d>& theCenter,
359 const std::shared_ptr<GeomAPI_Pnt2d>& theStartPoint,
360 const std::shared_ptr<GeomAPI_Pnt2d>& theEndPoint,
362 std::shared_ptr<GeomDataAPI_Point2D> thePassedPoint)
364 if (theCenter->distance(theStartPoint) < tolerance ||
365 theCenter->distance(theEndPoint) < tolerance)
368 std::shared_ptr<GeomAPI_Dir2d> aStartDir(new GeomAPI_Dir2d(
369 theStartPoint->xy()->decreased(theCenter->xy())));
370 std::shared_ptr<GeomAPI_Dir2d> aEndDir(new GeomAPI_Dir2d(
371 theEndPoint->xy()->decreased(theCenter->xy())));
372 std::shared_ptr<GeomAPI_XY> aMidDirXY = aStartDir->xy()->added(aEndDir->xy());
373 if (aMidDirXY->dot(aMidDirXY) < tolerance * tolerance) {
374 // start and end directions are opposite, so middle direction will be orthogonal
375 aMidDirXY->setX(-aStartDir->y());
376 aMidDirXY->setY(aStartDir->x());
378 std::shared_ptr<GeomAPI_Dir2d> aMidDir(new GeomAPI_Dir2d(aMidDirXY));
379 if ((aStartDir->cross(aMidDir) > 0) ^ !theArcReversed)
382 double aRadius = theCenter->distance(theStartPoint);
383 std::shared_ptr<GeomAPI_XY> aPassedPnt = theCenter->xy()->added( aMidDir->xy()->multiplied(aRadius) );
384 thePassedPoint->setValue(aPassedPnt->x(), aPassedPnt->y());
388 void SketchPlugin_Arc::updateDependentAttributes()
390 std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
391 GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
392 std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
393 GeomDataAPI_Point2D>(data()->attribute(START_ID()));
394 std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
395 GeomDataAPI_Point2D>(data()->attribute(END_ID()));
396 std::shared_ptr<GeomDataAPI_Point2D> aPassedPoint =
397 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(PASSED_POINT_ID()));
398 AttributeDoublePtr aRadiusAttr = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
399 data()->attribute(RADIUS_ID()));
400 AttributeDoublePtr anAngleAttr = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
401 data()->attribute(ANGLE_ID()));
406 data()->blockSendAttributeUpdated(true);
408 bool isOk = calculatePassedPoint(aCenterAttr->pnt(), aStartAttr->pnt(), anEndAttr->pnt(),
409 isReversed(), aPassedPoint);
410 if (isOk && aRadiusAttr && anAngleAttr) {
411 std::shared_ptr<GeomAPI_Circ2d> aCircle(
412 new GeomAPI_Circ2d(aStartAttr->pnt(), anEndAttr->pnt(), aPassedPoint->pnt()));
413 if (aCircle->implPtr<void*>())
414 calculateArcAngleRadius(aCircle, aStartAttr->pnt(), anEndAttr->pnt(), aPassedPoint->pnt(),
415 anAngleAttr, aRadiusAttr);
417 data()->blockSendAttributeUpdated(false);
421 void SketchPlugin_Arc::attributeChanged(const std::string& theID)
423 std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
424 GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
425 std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
426 GeomDataAPI_Point2D>(data()->attribute(START_ID()));
427 std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
428 GeomDataAPI_Point2D>(data()->attribute(END_ID()));
429 // the second condition for unability to move external segments anywhere
430 if (theID == EXTERNAL_ID() || isFixed()) {
431 std::shared_ptr<GeomAPI_Shape> aSelection = data()->selection(EXTERNAL_ID())->value();
432 // update arguments due to the selection value
433 if (aSelection && !aSelection->isNull() && aSelection->isEdge()) {
434 std::shared_ptr<GeomAPI_Edge> anEdge( new GeomAPI_Edge(aSelection));
435 std::shared_ptr<GeomAPI_Circ> aCirc = anEdge->circle();
437 aStartAttr->setValue(sketch()->to2D(anEdge->firstPoint()));
438 anEndAttr->setValue(sketch()->to2D(anEdge->lastPoint()));
439 aCenterAttr->setValue(sketch()->to2D(aCirc->center()));
445 AttributeDoublePtr aRadiusAttr = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
446 data()->attribute(RADIUS_ID()));
447 AttributeDoublePtr anAngleAttr = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
448 data()->attribute(ANGLE_ID()));
450 if (theID == RADIUS_ID()) {
451 if (!aStartAttr->isInitialized() || !anEndAttr->isInitialized())
453 // move center and passed point
454 std::shared_ptr<GeomAPI_XY> aStartPnt = aStartAttr->pnt()->xy();
455 std::shared_ptr<GeomAPI_XY> aEndPnt = anEndAttr->pnt()->xy();
456 double aDist = aStartPnt->distance(aEndPnt);
457 if (fabs(aDist) < tolerance)
459 std::shared_ptr<GeomAPI_Dir2d> aDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt)));
460 std::shared_ptr<GeomAPI_Dir2d> aMidPerpDir(new GeomAPI_Dir2d(-aDir->y(), aDir->x()));
461 std::shared_ptr<GeomAPI_XY> aMidPnt = aStartPnt->added(aEndPnt)->multiplied(0.5);
463 double anAngle = anAngleAttr->value() * PI / 180.0;
464 adjustPeriod(anAngle);
466 aMidPerpDir->reverse();
468 double aRadius = aRadiusAttr->value();
469 // The center is placed on a perpendicular bisector of a start-end points segment.
470 // If the radius is smaller that necessary, start and end points are moved too.
471 double aDist2 = aRadius * aRadius - aDist * aDist / 4.0;
472 aDist = aDist2 > 0.0 ? sqrt(aDist2) : 0.0;
473 // distance between middle point and start point (does not changed if the arc diameter is greater than start-end distance)
474 aDist2 = sqrt(aRadius * aRadius - aDist * aDist);
476 std::shared_ptr<GeomAPI_XY> aCenter = aMidPnt->added(aMidPerpDir->xy()->multiplied(aDist));
477 aStartPnt = aMidPnt->added(aDir->xy()->multiplied(-aDist2));
478 aEndPnt = aMidPnt->added(aDir->xy()->multiplied(aDist2));
480 data()->blockSendAttributeUpdated(true);
481 aCenterAttr->setValue(aCenter->x(), aCenter->y());
482 aStartAttr->setValue(aStartPnt->x(), aStartPnt->y());
483 anEndAttr->setValue(aEndPnt->x(), aEndPnt->y());
484 updateDependentAttributes();
485 data()->blockSendAttributeUpdated(false);
488 if (theID == ANGLE_ID()) {
489 if (!aStartAttr->isInitialized() || !aCenterAttr->isInitialized())
491 data()->blockSendAttributeUpdated(true);
492 // move end point and passed point
493 std::shared_ptr<GeomAPI_XY> aCenter = aCenterAttr->pnt()->xy();
494 double anAngle = anAngleAttr->value() * PI / 180.0;
495 double sinA = sin(anAngle);
496 double cosA = cos(anAngle);
497 std::shared_ptr<GeomAPI_XY> aStartDir = aStartAttr->pnt()->xy()->decreased(aCenter);
498 std::shared_ptr<GeomAPI_XY> aDir(new GeomAPI_XY(
499 aStartDir->x() * cosA - aStartDir->y() * sinA,
500 aStartDir->x() * sinA + aStartDir->y() * cosA));
501 anEndAttr->setValue(aCenter->x() + aDir->x(), aCenter->y() + aDir->y());
506 aDir = std::shared_ptr<GeomAPI_XY>(new GeomAPI_XY(
507 aStartDir->x() * cosA - aStartDir->y() * sinA,
508 aStartDir->x() * sinA + aStartDir->y() * cosA));
509 std::shared_ptr<GeomDataAPI_Point2D> aPassedPoint =
510 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(PASSED_POINT_ID()));
511 aPassedPoint->setValue(aCenter->x() + aDir->x(), aCenter->y() + aDir->y());
513 std::shared_ptr<GeomAPI_Circ2d> aCircle(
514 new GeomAPI_Circ2d(aStartAttr->pnt(), anEndAttr->pnt(), aPassedPoint->pnt()));
515 calculateArcAngleRadius(aCircle, aStartAttr->pnt(), anEndAttr->pnt(), aPassedPoint->pnt(),
516 anAngleAttr, aRadiusAttr);
517 data()->blockSendAttributeUpdated(false);
521 if (theID == CENTER_ID()) {
522 if (isFeatureValid())
527 AttributeStringPtr aTypeAttr =
528 std::dynamic_pointer_cast<ModelAPI_AttributeString>(attribute(ARC_TYPE()));
531 std::string anArcType = aTypeAttr->value();
533 // update the points in accordance to the changed point changes
534 if (anArcType == ARC_TYPE_CENTER_START_END()) {
535 if (!isFeatureValid())
537 if (theID == END_ID() && isStable()) {
538 // The arc is under construction, so its end point projected
539 // on the circle formed by center and start points
542 updateDependentAttributes();
544 else if (anArcType == ARC_TYPE_THREE_POINTS() &&
545 (theID == START_ID() || theID == END_ID() || theID == PASSED_POINT_ID())) {
546 data()->blockSendAttributeUpdated(true);
548 std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
549 int aNbInitialized = 0;
550 for (int i = 1; i <= 3; ++i) {
551 std::shared_ptr<GeomDataAPI_Point2D> aCurPnt =
552 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(POINT_ID(i)));
553 if (aCurPnt->isInitialized())
554 aPoints[aNbInitialized++] = aCurPnt->pnt();
557 if (aNbInitialized == 1)
558 aCenterAttr->setValue(aPoints[0]->x(), aPoints[0]->y());
559 else if (aNbInitialized == 2) {
560 // calculate center point, which gives a quarter of circle for the given start and end points
561 std::shared_ptr<GeomAPI_Pnt2d> aStartPnt = aPoints[0];
562 std::shared_ptr<GeomAPI_Pnt2d> aEndPnt = aPoints[1];
563 std::shared_ptr<GeomAPI_XY> aDir = aEndPnt->xy()->decreased(aStartPnt->xy())->multiplied(0.5);
564 double x = aDir->x();
565 double y = aDir->y();
568 std::shared_ptr<GeomAPI_XY> aCenter = aStartPnt->xy()->added(aDir);
569 double aRadius = sqrt(aDir->dot(aDir));
571 aCenterAttr->setValue(aCenter->x(), aCenter->y());
572 aRadiusAttr->setValue(aRadius);
573 anAngleAttr->setValue(90.0);
576 std::shared_ptr<GeomAPI_Circ2d> aCircle(
577 new GeomAPI_Circ2d(aPoints[0], aPoints[1], aPoints[2]));
579 std::shared_ptr<GeomAPI_Pnt2d> aCenter = aCircle->center();
581 aCenterAttr->setValue(aCenter);
582 if (theID == START_ID() || theID == END_ID())
583 updateDependentAttributes();
585 calculateArcAngleRadius(aCircle, aPoints[0], aPoints[1], aPoints[2],
586 anAngleAttr, aRadiusAttr);
590 data()->blockSendAttributeUpdated(false);
592 else if (anArcType == ARC_TYPE_TANGENT() && (theID == TANGENT_POINT_ID() || theID == END_ID())) {
593 SketchPlugin_Sketch* aSketch = sketch();
594 AttributeRefAttrPtr aTangPtAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
595 data()->attribute(TANGENT_POINT_ID()));
597 if (aTangPtAttr->isInitialized() && anEndAttr->isInitialized()) {
598 data()->blockSendAttributeUpdated(true);
599 // compute orthogonal direction
600 std::shared_ptr<GeomAPI_Dir2d> anOrthoDir;
601 std::shared_ptr<GeomDataAPI_Point2D> aTangentPoint =
602 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aTangPtAttr->attr());
603 std::shared_ptr<GeomAPI_Pnt2d> aTangPnt2d = aTangentPoint->pnt();
604 FeaturePtr aTangFeature = ModelAPI_Feature::feature(aTangentPoint->owner());
605 std::shared_ptr<GeomAPI_Edge> aTangEdge = std::dynamic_pointer_cast<GeomAPI_Edge>(
606 aTangFeature->lastResult()->shape());
607 if (aTangEdge->isLine()) {
608 std::shared_ptr<GeomAPI_Dir> aDir = aTangEdge->line()->direction();
609 std::shared_ptr<GeomAPI_Pnt> aPnt(new GeomAPI_Pnt(aDir->x(), aDir->y(), aDir->z()));
610 std::shared_ptr<GeomAPI_Pnt2d> aPnt2d = aSketch->to2D(aPnt);
611 anOrthoDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(-aPnt2d->y(), aPnt2d->x()));
613 else if (aTangEdge->isArc()) {
614 std::shared_ptr<GeomAPI_Pnt> aCenter = aTangEdge->circle()->center();
615 std::shared_ptr<GeomAPI_Pnt2d> aCenter2d = aSketch->to2D(aCenter);
616 anOrthoDir = std::shared_ptr<GeomAPI_Dir2d>(
617 new GeomAPI_Dir2d(aTangPnt2d->xy()->decreased(aCenter2d->xy())));
620 // compute parameters of the middle perpendicular
621 std::shared_ptr<GeomAPI_XY> aEndPntCoord = anEndAttr->pnt()->xy();
622 std::shared_ptr<GeomAPI_XY> aTempDir = aEndPntCoord->decreased(aTangPnt2d->xy());
623 std::shared_ptr<GeomAPI_Dir2d> aMidDir(new GeomAPI_Dir2d(-aTempDir->y(), aTempDir->x()));
624 std::shared_ptr<GeomAPI_Pnt2d> aMidPnt(
625 new GeomAPI_Pnt2d(aEndPntCoord->added(aTangPnt2d->xy())->multiplied(0.5)));
627 // compute center of arc by calculating intersection of orthogonal line and middle perpendicular
628 std::shared_ptr<GeomAPI_Lin2d> anOrthoLine(new GeomAPI_Lin2d(aTangPnt2d, anOrthoDir));
629 std::shared_ptr<GeomAPI_Lin2d> aMiddleLine(new GeomAPI_Lin2d(aMidPnt, aMidDir));
630 std::shared_ptr<GeomAPI_Pnt2d> aCenter = anOrthoLine->intersect(aMiddleLine);
632 aCenterAttr->setValue(aCenter);
633 aStartAttr->setValue(aTangPnt2d);
634 updateDependentAttributes();
637 data()->blockSendAttributeUpdated(false);
638 tangencyArcConstraints();
643 void SketchPlugin_Arc::setReversed(bool isReversed)
645 std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(attribute(INVERSED_ID()))->setValue(isReversed);
649 bool SketchPlugin_Arc::isReversed()
651 return std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(attribute(INVERSED_ID()))->value();
654 void SketchPlugin_Arc::tangencyArcConstraints()
659 std::shared_ptr<GeomDataAPI_Point2D> aStartAttr =
660 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(START_ID()));
661 AttributeRefAttrPtr aTangPtAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
662 attribute(TANGENT_POINT_ID()));
663 if (!aTangPtAttr->attr())
666 FeaturePtr aFeature = ModelAPI_Feature::feature(aStartAttr->owner());
667 ObjectPtr aThisArc = aFeature->lastResult();
668 aFeature = ModelAPI_Feature::feature(aTangPtAttr->attr()->owner());
669 ObjectPtr aTangFeature = aFeature->lastResult();
671 // trying to find constraints to fix the tangency of the arc
672 std::set<FeaturePtr> aCoincidence;
673 std::set<FeaturePtr> aTangency;
675 AttributeRefAttrPtr aRefAttrA, aRefAttrB;
676 std::set<AttributePtr> aRefs = data()->refsToMe();
677 const std::set<AttributePtr>& aRefsToResult = lastResult()->data()->refsToMe();
678 aRefs.insert(aRefsToResult.begin(), aRefsToResult.end());
679 std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
680 for (; aRefIt != aRefs.end(); ++aRefIt) {
681 FeaturePtr aConstrFeature = ModelAPI_Feature::feature((*aRefIt)->owner());
682 if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
683 aRefAttrA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
684 aConstrFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
685 aRefAttrB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
686 aConstrFeature->attribute(SketchPlugin_Constraint::ENTITY_B()));
687 if ((aRefAttrA && aRefAttrA->attr() == aStartAttr) ||
688 (aRefAttrB && aRefAttrB->attr() == aStartAttr))
689 aCoincidence.insert(aConstrFeature);
691 else if (aConstrFeature->getKind() == SketchPlugin_ConstraintTangent::ID()) {
692 aRefAttrA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
693 aConstrFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
694 aRefAttrB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
695 aConstrFeature->attribute(SketchPlugin_Constraint::ENTITY_B()));
696 if ((aRefAttrA && aRefAttrA->object() == aThisArc) ||
697 (aRefAttrB && aRefAttrB->object() == aThisArc))
698 aTangency.insert(aConstrFeature);
701 // search applicable pair of constraints
702 bool isFound = false;
703 FeaturePtr aPrevCoincidence, aPrevTangency;
704 std::set<FeaturePtr>::const_iterator aCIt, aTIt;
705 for (aCIt = aCoincidence.begin(); aCIt != aCoincidence.end() && !isFound; ++aCIt) {
706 aRefAttrA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
707 (*aCIt)->attribute(SketchPlugin_Constraint::ENTITY_A()));
708 aRefAttrB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
709 (*aCIt)->attribute(SketchPlugin_Constraint::ENTITY_B()));
710 AttributePtr anOtherPoint =
711 aRefAttrA->attr() == aStartAttr ? aRefAttrB->attr() : aRefAttrA->attr();
712 for (aTIt = aTangency.begin(); aTIt != aTangency.end() && !isFound; ++aTIt) {
713 aRefAttrA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
714 (*aTIt)->attribute(SketchPlugin_Constraint::ENTITY_A()));
715 aRefAttrB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
716 (*aTIt)->attribute(SketchPlugin_Constraint::ENTITY_B()));
717 ObjectPtr anOtherObject = aRefAttrA->object() == aThisArc ?
718 aRefAttrB->object() : aRefAttrA->object();
719 if (anOtherPoint->owner() == anOtherObject) {
721 aPrevCoincidence = *aCIt;
722 aPrevTangency = *aTIt;
728 // update previous constraints
729 aRefAttrA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
730 aPrevCoincidence->attribute(SketchPlugin_Constraint::ENTITY_A()));
731 aRefAttrB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
732 aPrevCoincidence->attribute(SketchPlugin_Constraint::ENTITY_B()));
733 if (aRefAttrA->attr() == aStartAttr)
734 aRefAttrB->setAttr(aTangPtAttr->attr());
736 aRefAttrA->setAttr(aTangPtAttr->attr());
738 aRefAttrA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
739 aPrevTangency->attribute(SketchPlugin_Constraint::ENTITY_A()));
740 aRefAttrB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
741 aPrevTangency->attribute(SketchPlugin_Constraint::ENTITY_B()));
742 if (aRefAttrA->object() == aThisArc)
743 aRefAttrB->setObject(aTangFeature);
745 aRefAttrA->setObject(aTangFeature);
747 // Wait all constraints being removed, then send update events
748 static Events_ID aDeleteEvent = Events_Loop::eventByName(EVENT_OBJECT_DELETED);
749 bool isDeleteFlushed = Events_Loop::loop()->isFlushed(aDeleteEvent);
751 Events_Loop::loop()->setFlushed(aDeleteEvent, false);
752 // Remove all obtained constraints which use current arc, because
753 // there is no information which of them were used to build tangency arc.
754 DocumentPtr aDoc = sketch()->document();
755 std::set<FeaturePtr> aFeaturesToBeRemoved;
756 for (aCIt = aCoincidence.begin(); aCIt != aCoincidence.end(); ++aCIt)
757 aFeaturesToBeRemoved.insert(*aCIt);
758 for (aTIt = aTangency.begin(); aTIt != aTangency.end(); ++aTIt)
759 aFeaturesToBeRemoved.insert(*aTIt);
760 ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved);
761 // Send events to update the sub-features by the solver.
763 Events_Loop::loop()->setFlushed(aDeleteEvent, true);
765 Events_Loop::loop()->flush(aDeleteEvent);
767 // Wait all constraints being created, then send update events
768 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
769 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
771 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
773 // Create new constraints
774 FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
775 aRefAttrA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
776 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
777 aRefAttrB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
778 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
779 aRefAttrA->setAttr(aStartAttr);
780 aRefAttrB->setAttr(aTangPtAttr->attr());
781 aConstraint->execute();
782 ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
784 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
785 aRefAttrA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
786 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
787 aRefAttrB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
788 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
789 aRefAttrA->setObject(aThisArc);
790 aRefAttrB->setObject(aTangFeature);
791 aConstraint->execute();
792 ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
794 // Send events to update the sub-features by the solver.
796 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
800 void SketchPlugin_Arc::projectEndPoint()
802 std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<
803 GeomDataAPI_Point2D>(data()->attribute(CENTER_ID()));
804 std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<
805 GeomDataAPI_Point2D>(data()->attribute(START_ID()));
806 std::shared_ptr<GeomDataAPI_Point2D> anEndAttr = std::dynamic_pointer_cast<
807 GeomDataAPI_Point2D>(data()->attribute(END_ID()));
809 if (aCenterAttr->pnt()->distance(aStartAttr->pnt()) < tolerance)
811 data()->blockSendAttributeUpdated(true);
812 // compute and change the arc end point
813 std::shared_ptr<GeomAPI_Circ2d> aCircleForArc(
814 new GeomAPI_Circ2d(aCenterAttr->pnt(), aStartAttr->pnt()));
815 std::shared_ptr<GeomAPI_Pnt2d> aProjection = aCircleForArc->project(anEndAttr->pnt());
816 if (aProjection && anEndAttr->pnt()->distance(aProjection) > tolerance)
817 anEndAttr->setValue(aProjection);
818 updateDependentAttributes();
819 data()->blockSendAttributeUpdated(false);