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_Arc.h>
13 #include <SketchPlugin_Circle.h>
14 #include <SketchPlugin_ConstraintCoincidence.h>
18 #define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(x)
20 /// \brief Obtain tangent features from the constraint
21 static void getTangentFeatures(const ConstraintPtr& theConstraint,
22 FeaturePtr& theFeature1,
23 FeaturePtr& theFeature2);
25 /// \brief Obtain all coincident constraints between features
26 static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2);
28 /// \brief Check whether the entities has only one shared point or less.
29 /// Return list of coincident points.
30 static std::list<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1,
31 FeaturePtr theFeature2);
33 /// \brief Check if two connected arcs have centers
34 /// in same direction relatively to connection point
35 static bool isArcArcTangencyInternal(EntityWrapperPtr theArc1,
36 EntityWrapperPtr theArc2);
38 static ConstraintWrapperPtr
39 createArcLineTangency(EntityWrapperPtr theEntity1,
40 EntityWrapperPtr theEntity2,
41 EntityWrapperPtr theShapedPoint = EntityWrapperPtr(),
42 double* theAngle = 0);
44 static ConstraintWrapperPtr
45 createArcArcTangency(EntityWrapperPtr theEntity1,
46 EntityWrapperPtr theEntity2,
47 bool theInternalTangency,
48 EntityWrapperPtr theSharedPoint = EntityWrapperPtr(),
49 double* theAngle = 0);
52 void SketchSolver_ConstraintTangent::process()
55 if (!myBaseConstraint || !myStorage) {
56 // Not enough parameters are assigned
60 EntityWrapperPtr aValue;
61 std::vector<EntityWrapperPtr> anAttributes;
62 SketchSolver_Constraint::getAttributes(aValue, anAttributes);
63 if (!myErrorMsg.empty())
67 if (!myErrorMsg.empty())
70 myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
73 void SketchSolver_ConstraintTangent::rebuild()
75 if (mySolverConstraint)
76 myStorage->removeConstraint(myBaseConstraint);
78 mySolverConstraint = ConstraintWrapperPtr();
79 mySharedPoint = AttributePtr();
81 // Check the quantity of entities of each type and their order (arcs first)
84 std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
85 for (; anEntIt != myAttributes.end(); ++anEntIt) {
86 if (!(*anEntIt).get())
88 if ((*anEntIt)->type() == ENTITY_LINE)
90 else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE)
95 myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE();
98 if (aNbLines == 1 && aNbCircles == 1) {
99 myType = CONSTRAINT_TANGENT_CIRCLE_LINE;
101 else if (aNbCircles == 2) {
102 myType = CONSTRAINT_TANGENT_CIRCLE_CIRCLE;
103 isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back());
106 myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
110 FeaturePtr aFeature1, aFeature2;
111 getTangentFeatures(myBaseConstraint, aFeature1, aFeature2);
113 // check number of coincident points
114 std::list<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2);
115 if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 1) {
116 myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
120 EntityWrapperPtr aSharedPointEntity;
121 if (!aCoincidentPoints.empty()) {
122 mySharedPoint = aCoincidentPoints.front();
123 aSharedPointEntity = myStorage->entity(mySharedPoint);
126 if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
127 mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(),
128 aSharedPointEntity, &myCurveCurveAngle);
130 mySolverConstraint = createArcArcTangency(myAttributes.front(), myAttributes.back(),
131 isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle);
134 myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
137 void SketchSolver_ConstraintTangent::adjustConstraint()
139 if (myType == CONSTRAINT_TANGENT_CIRCLE_CIRCLE) {
140 EntityWrapperPtr anEntity1 =
141 myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
142 EntityWrapperPtr anEntity2 =
143 myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
145 if (isArcArcInternal != isArcArcTangencyInternal(anEntity1, anEntity2))
150 void SketchSolver_ConstraintTangent::notify(const FeaturePtr& theFeature,
151 PlaneGCSSolver_Update* theUpdater)
153 if (theFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
156 // base constraint may become invalid (for example, during undo)
157 if (!myBaseConstraint->data() || !myBaseConstraint->data()->isValid())
160 FeaturePtr aTgFeat1, aTgFeat2;
161 getTangentFeatures(myBaseConstraint, aTgFeat1, aTgFeat2);
163 bool isRebuild = false;
164 if (theFeature->data() && theFeature->data()->isValid()) {
165 // the constraint has been created
166 if (!mySharedPoint) {
167 // features has no shared point, check whether coincidence constraint binds these features)
168 int aNbCoincidentFeatures = 0;
169 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
170 AttributeRefAttrPtr aRefAttr = theFeature->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
175 if (aRefAttr->isObject())
176 anObj = aRefAttr->object();
178 anObj = aRefAttr->attr()->owner();
180 FeaturePtr aFeature = ModelAPI_Feature::feature(anObj);
181 if (aFeature == aTgFeat1 || aFeature == aTgFeat2)
182 ++aNbCoincidentFeatures;
185 if (aNbCoincidentFeatures == 2)
188 } else if (mySharedPoint) {
189 // The features are tangent in the shared point, but the coincidence has been removed.
190 // Check if the coincidence is the same.
191 std::list<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
193 std::list<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
194 for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
195 if (*anIt == mySharedPoint)
196 isRebuild = false; // the coincidence is still exists => nothing to change
206 // ================== Auxiliary functions =================================
207 void getTangentFeatures(const ConstraintPtr& theConstraint,
208 FeaturePtr& theFeature1,
209 FeaturePtr& theFeature2)
211 AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
212 theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
213 aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
214 theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
217 std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
219 const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
220 const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
222 std::set<FeaturePtr> aCoincidences;
223 std::set<AttributePtr>::const_iterator anIt;
225 // collect coincidences referred to the first feature
226 for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
227 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
228 if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
229 aCoincidences.insert(aRef);
232 // leave only coincidences referred to the second feature
233 std::set<FeaturePtr> aCoincidencesBetweenFeatures;
234 for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
235 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
236 if (aCoincidences.find(aRef) != aCoincidences.end())
237 aCoincidencesBetweenFeatures.insert(aRef);
240 return aCoincidencesBetweenFeatures;
243 std::list<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
245 std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
246 // collect points only
247 std::list<AttributePtr> aCoincidentPoints;
248 std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
249 for (; aCIt != aCoincidences.end(); ++ aCIt) {
250 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
251 AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
252 if (!aRefAttr || aRefAttr->isObject())
255 AttributePtr anAttr = aRefAttr->attr();
256 if (anAttr->id() != SketchPlugin_Arc::CENTER_ID() &&
257 anAttr->id() != SketchPlugin_Circle::CENTER_ID()) {
258 aCoincidentPoints.push_back(aRefAttr->attr());
263 return aCoincidentPoints;
266 bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
268 std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
269 GCS_EDGE_WRAPPER(theArc1)->entity());
270 std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
271 GCS_EDGE_WRAPPER(theArc2)->entity());
273 if (!aCirc1 || !aCirc2)
276 double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
277 double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
278 double aDist = sqrt(aDX * aDX + aDY * aDY);
280 return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
283 // sets angle to 0 or 180 degrees
284 static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
285 const GCSCurvePtr& theCurve2,
286 const GCSPointPtr& thePoint,
289 double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
290 // bring angle to [-pi..pi]
291 if (anAngle > PI) anAngle -= 2.0 * PI;
292 if (anAngle < -PI) anAngle += 2.0 * PI;
293 // set angle value according to the current angle between curves
294 if (fabs(anAngle) <= PI / 2.)
301 ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
302 EntityWrapperPtr theEntity2,
303 EntityWrapperPtr theSharedPoint,
306 EdgeWrapperPtr anEntLine, anEntCirc;
307 if (theEntity1->type() == ENTITY_LINE) {
308 anEntLine = GCS_EDGE_WRAPPER(theEntity1);
309 anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
311 anEntLine = GCS_EDGE_WRAPPER(theEntity2);
312 anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
315 std::shared_ptr<GCS::Circle> aCirc =
316 std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
317 std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
319 std::shared_ptr<GCS::Line> aLine =
320 std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
322 GCSConstraintPtr aNewConstr;
323 if (theSharedPoint && anArc) { // do not process shared point between circle and line
325 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
327 adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
329 GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
332 GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
335 return ConstraintWrapperPtr(
336 new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
339 ConstraintWrapperPtr createArcArcTangency(EntityWrapperPtr theEntity1,
340 EntityWrapperPtr theEntity2,
341 bool theInternalTangency,
342 EntityWrapperPtr theSharedPoint,
345 std::shared_ptr<GCS::Circle> aCirc1 =
346 std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity1)->entity());
347 std::shared_ptr<GCS::Circle> aCirc2 =
348 std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity2)->entity());
350 GCSConstraintPtr aNewConstr;
351 if (theSharedPoint) {
352 std::shared_ptr<GCS::Arc> anArc1 = std::dynamic_pointer_cast<GCS::Arc>(aCirc1);
353 std::shared_ptr<GCS::Arc> anArc2 = std::dynamic_pointer_cast<GCS::Arc>(aCirc2);
355 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
357 adjustAngleBetweenCurves(anArc1, anArc2, aPoint, theAngle);
359 GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc1, *anArc2, *aPoint, theAngle));
361 aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
362 aCirc1->rad, aCirc2->rad, theInternalTangency));
365 return ConstraintWrapperPtr(
366 new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE));