1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 #include <SketchSolver_ConstraintTangent.h>
4 #include <SketchSolver_Error.h>
6 #include <PlaneGCSSolver_EdgeWrapper.h>
7 #include <PlaneGCSSolver_PointWrapper.h>
8 #include <PlaneGCSSolver_Tools.h>
9 #include <PlaneGCSSolver_UpdateCoincidence.h>
11 #include <GeomAPI_Pnt2d.h>
12 #include <SketchPlugin_Circle.h>
13 #include <SketchPlugin_ConstraintCoincidence.h>
17 #define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(x)
19 /// \brief Obtain tangent features from the constraint
20 static void getTangentFeatures(const ConstraintPtr& theConstraint,
21 FeaturePtr& theFeature1,
22 FeaturePtr& theFeature2);
24 /// \brief Obtain all coincident constraints between features
25 static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2);
27 /// \brief Check whether the entities has only one shared point or less.
28 /// Return list of coincident points.
29 static std::list<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2);
31 /// \brief Check if two connected arcs have centers
32 /// in same direction relatively to connection point
33 static bool isArcArcTangencyInternal(EntityWrapperPtr theArc1,
34 EntityWrapperPtr theArc2);
36 static ConstraintWrapperPtr
37 createArcLineTangency(EntityWrapperPtr theEntity1,
38 EntityWrapperPtr theEntity2,
39 EntityWrapperPtr theShapedPoint = EntityWrapperPtr(),
40 double* theAngle = 0);
42 static ConstraintWrapperPtr
43 createArcArcTangency(EntityWrapperPtr theEntity1,
44 EntityWrapperPtr theEntity2,
45 bool theInternalTangency,
46 EntityWrapperPtr theSharedPoint = EntityWrapperPtr(),
47 double* theAngle = 0);
50 void SketchSolver_ConstraintTangent::process()
53 if (!myBaseConstraint || !myStorage) {
54 // Not enough parameters are assigned
58 EntityWrapperPtr aValue;
59 std::vector<EntityWrapperPtr> anAttributes;
60 SketchSolver_Constraint::getAttributes(aValue, anAttributes);
61 if (!myErrorMsg.empty())
65 if (!myErrorMsg.empty())
68 myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
71 void SketchSolver_ConstraintTangent::rebuild()
73 if (mySolverConstraint)
74 myStorage->removeConstraint(myBaseConstraint);
76 mySolverConstraint = ConstraintWrapperPtr();
77 mySharedPoint = AttributePtr();
79 // Check the quantity of entities of each type and their order (arcs first)
82 std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
83 for (; anEntIt != myAttributes.end(); ++anEntIt) {
84 if (!(*anEntIt).get())
86 if ((*anEntIt)->type() == ENTITY_LINE)
88 else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE)
93 myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE();
96 if (aNbLines == 1 && aNbCircles == 1) {
97 myType = CONSTRAINT_TANGENT_CIRCLE_LINE;
99 else if (aNbCircles == 2) {
100 myType = CONSTRAINT_TANGENT_CIRCLE_CIRCLE;
101 isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back());
104 myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
108 FeaturePtr aFeature1, aFeature2;
109 getTangentFeatures(myBaseConstraint, aFeature1, aFeature2);
111 // check number of coincident points
112 std::list<AttributePtr> aCoincidentPoints = coincidentPoints(aFeature1, aFeature2);
113 if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 1) {
114 myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
118 EntityWrapperPtr aSharedPointEntity;
119 if (!aCoincidentPoints.empty()) {
120 mySharedPoint = aCoincidentPoints.front();
121 aSharedPointEntity = myStorage->entity(mySharedPoint);
124 if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
125 mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(),
126 aSharedPointEntity, &myCurveCurveAngle);
128 mySolverConstraint = createArcArcTangency(myAttributes.front(), myAttributes.back(),
129 isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle);
132 myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
135 void SketchSolver_ConstraintTangent::adjustConstraint()
137 if (myType == CONSTRAINT_TANGENT_CIRCLE_CIRCLE) {
138 EntityWrapperPtr anEntity1 =
139 myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
140 EntityWrapperPtr anEntity2 =
141 myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
143 if (isArcArcInternal != isArcArcTangencyInternal(anEntity1, anEntity2))
148 void SketchSolver_ConstraintTangent::notify(const FeaturePtr& theFeature,
149 PlaneGCSSolver_Update* theUpdater)
151 if (theFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
154 // base constraint may become invalid (for example, during undo)
155 if (!myBaseConstraint->data() || !myBaseConstraint->data()->isValid())
158 FeaturePtr aTgFeat1, aTgFeat2;
159 getTangentFeatures(myBaseConstraint, aTgFeat1, aTgFeat2);
161 bool isRebuild = false;
162 if (theFeature->data() && theFeature->data()->isValid()) {
163 // the constraint has been created
164 if (!mySharedPoint) {
165 // features has no shared point, check whether coincidence constraint binds these features)
166 int aNbCoincidentFeatures = 0;
167 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
168 AttributeRefAttrPtr aRefAttr = theFeature->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
173 if (aRefAttr->isObject())
174 anObj = aRefAttr->object();
176 anObj = aRefAttr->attr()->owner();
178 FeaturePtr aFeature = ModelAPI_Feature::feature(anObj);
179 if (aFeature == aTgFeat1 || aFeature == aTgFeat2)
180 ++aNbCoincidentFeatures;
183 if (aNbCoincidentFeatures == 2)
186 } else if (mySharedPoint) {
187 // The features are tangent in the shared point, but the coincidence has been removed.
188 // Check if the coincidence is the same.
189 std::list<AttributePtr> aCoincidentPoints = coincidentPoints(aTgFeat1, aTgFeat2);
191 std::list<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
192 for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
193 if (*anIt == mySharedPoint)
194 isRebuild = false; // the coincidence is still exists => nothing to change
204 // ================== Auxiliary functions =================================
205 void getTangentFeatures(const ConstraintPtr& theConstraint,
206 FeaturePtr& theFeature1,
207 FeaturePtr& theFeature2)
209 AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
210 theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
211 aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
212 theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
215 std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
217 const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
218 const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
220 std::set<FeaturePtr> aCoincidences;
221 std::set<AttributePtr>::const_iterator anIt;
223 // collect coincidences referred to the first feature
224 for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
225 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
226 if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
227 aCoincidences.insert(aRef);
230 // leave only coincidences referred to the second feature
231 std::set<FeaturePtr> aCoincidencesBetweenFeatures;
232 for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
233 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
234 if (aCoincidences.find(aRef) != aCoincidences.end())
235 aCoincidencesBetweenFeatures.insert(aRef);
238 return aCoincidencesBetweenFeatures;
241 std::list<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
243 std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
244 // collect points only
245 std::list<AttributePtr> aCoincidentPoints;
246 std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
247 for (; aCIt != aCoincidences.end(); ++ aCIt) {
248 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
249 AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
250 if (aRefAttr && !aRefAttr->isObject()) {
251 aCoincidentPoints.push_back(aRefAttr->attr());
256 return aCoincidentPoints;
259 bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
261 std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
262 GCS_EDGE_WRAPPER(theArc1)->entity());
263 std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
264 GCS_EDGE_WRAPPER(theArc2)->entity());
266 if (!aCirc1 || !aCirc2)
269 double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
270 double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
271 double aDist = sqrt(aDX * aDX + aDY * aDY);
273 return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
276 // sets angle to 0 or 180 degrees
277 static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
278 const GCSCurvePtr& theCurve2,
279 const GCSPointPtr& thePoint,
282 double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
283 // bring angle to [-pi..pi]
284 if (anAngle > PI) anAngle -= 2.0 * PI;
285 if (anAngle < -PI) anAngle += 2.0 * PI;
286 // set angle value according to the current angle between curves
287 if (fabs(anAngle) <= PI / 2.)
294 ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
295 EntityWrapperPtr theEntity2,
296 EntityWrapperPtr theSharedPoint,
299 EdgeWrapperPtr anEntLine, anEntCirc;
300 if (theEntity1->type() == ENTITY_LINE) {
301 anEntLine = GCS_EDGE_WRAPPER(theEntity1);
302 anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
304 anEntLine = GCS_EDGE_WRAPPER(theEntity2);
305 anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
308 std::shared_ptr<GCS::Circle> aCirc =
309 std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
310 std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
312 std::shared_ptr<GCS::Line> aLine =
313 std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
315 GCSConstraintPtr aNewConstr;
316 if (theSharedPoint && anArc) { // do not process shared point between circle and line
318 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
320 adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
322 GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
325 GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
328 return ConstraintWrapperPtr(
329 new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
332 ConstraintWrapperPtr createArcArcTangency(EntityWrapperPtr theEntity1,
333 EntityWrapperPtr theEntity2,
334 bool theInternalTangency,
335 EntityWrapperPtr theSharedPoint,
338 std::shared_ptr<GCS::Circle> aCirc1 =
339 std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity1)->entity());
340 std::shared_ptr<GCS::Circle> aCirc2 =
341 std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity2)->entity());
343 GCSConstraintPtr aNewConstr;
344 if (theSharedPoint) {
345 std::shared_ptr<GCS::Arc> anArc1 = std::dynamic_pointer_cast<GCS::Arc>(aCirc1);
346 std::shared_ptr<GCS::Arc> anArc2 = std::dynamic_pointer_cast<GCS::Arc>(aCirc2);
348 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
350 adjustAngleBetweenCurves(anArc1, anArc2, aPoint, theAngle);
352 GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc1, *anArc2, *aPoint, theAngle));
354 aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
355 aCirc1->rad, aCirc2->rad, theInternalTangency));
358 return ConstraintWrapperPtr(
359 new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE));