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::set<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::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2);
115 if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 2) {
116 myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
120 EntityWrapperPtr aSharedPointEntity;
121 if (!aCoincidentPoints.empty()) {
122 mySharedPoint = *aCoincidentPoints.begin();
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)
190 if (mySharedPoint && !isRebuild) {
191 // The features are tangent in the shared point, but the coincidence has been removed/updated.
192 // Check if the coincidence is the same.
193 std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
195 std::set<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
196 for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
197 if (*anIt == mySharedPoint)
198 isRebuild = false; // the coincidence is still exists => nothing to change
208 // ================== Auxiliary functions =================================
209 void getTangentFeatures(const ConstraintPtr& theConstraint,
210 FeaturePtr& theFeature1,
211 FeaturePtr& theFeature2)
213 AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
214 theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
215 aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
216 theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
219 std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
221 const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
222 const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
224 std::set<FeaturePtr> aCoincidences;
225 std::set<AttributePtr>::const_iterator anIt;
227 // collect coincidences referred to the first feature
228 for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
229 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
230 if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
231 aCoincidences.insert(aRef);
234 // leave only coincidences referred to the second feature
235 std::set<FeaturePtr> aCoincidencesBetweenFeatures;
236 for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
237 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
238 if (aCoincidences.find(aRef) != aCoincidences.end())
239 aCoincidencesBetweenFeatures.insert(aRef);
242 return aCoincidencesBetweenFeatures;
245 std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
247 std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
248 // collect points only
249 std::set<AttributePtr> aCoincidentPoints;
250 std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
251 for (; aCIt != aCoincidences.end(); ++ aCIt) {
252 AttributeRefAttrPtr aRefAttrA = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
253 AttributeRefAttrPtr aRefAttrB = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_B());
254 if (!aRefAttrA || aRefAttrA->isObject() ||
255 !aRefAttrB || aRefAttrB->isObject())
258 AttributePtr anAttrA = aRefAttrA->attr();
259 AttributePtr anAttrB = aRefAttrB->attr();
260 if (anAttrA->id() != SketchPlugin_Arc::CENTER_ID() &&
261 anAttrA->id() != SketchPlugin_Circle::CENTER_ID() &&
262 anAttrB->id() != SketchPlugin_Arc::CENTER_ID() &&
263 anAttrB->id() != SketchPlugin_Circle::CENTER_ID()) {
264 aCoincidentPoints.insert(anAttrA);
265 aCoincidentPoints.insert(anAttrB);
268 return aCoincidentPoints;
271 bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
273 std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
274 GCS_EDGE_WRAPPER(theArc1)->entity());
275 std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
276 GCS_EDGE_WRAPPER(theArc2)->entity());
278 if (!aCirc1 || !aCirc2)
281 double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
282 double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
283 double aDist = sqrt(aDX * aDX + aDY * aDY);
285 return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
288 // sets angle to 0 or 180 degrees
289 static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
290 const GCSCurvePtr& theCurve2,
291 const GCSPointPtr& thePoint,
294 double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
295 // bring angle to [-pi..pi]
296 if (anAngle > PI) anAngle -= 2.0 * PI;
297 if (anAngle < -PI) anAngle += 2.0 * PI;
298 // set angle value according to the current angle between curves
299 if (fabs(anAngle) <= PI / 2.)
306 ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
307 EntityWrapperPtr theEntity2,
308 EntityWrapperPtr theSharedPoint,
311 EdgeWrapperPtr anEntLine, anEntCirc;
312 if (theEntity1->type() == ENTITY_LINE) {
313 anEntLine = GCS_EDGE_WRAPPER(theEntity1);
314 anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
316 anEntLine = GCS_EDGE_WRAPPER(theEntity2);
317 anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
320 std::shared_ptr<GCS::Circle> aCirc =
321 std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
322 std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
324 std::shared_ptr<GCS::Line> aLine =
325 std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
327 GCSConstraintPtr aNewConstr;
328 if (theSharedPoint && anArc) { // do not process shared point between circle and line
330 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
332 adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
334 GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
337 GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
340 return ConstraintWrapperPtr(
341 new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
344 ConstraintWrapperPtr createArcArcTangency(EntityWrapperPtr theEntity1,
345 EntityWrapperPtr theEntity2,
346 bool theInternalTangency,
347 EntityWrapperPtr theSharedPoint,
350 std::shared_ptr<GCS::Circle> aCirc1 =
351 std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity1)->entity());
352 std::shared_ptr<GCS::Circle> aCirc2 =
353 std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity2)->entity());
355 GCSConstraintPtr aNewConstr;
356 if (theSharedPoint) {
358 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
360 adjustAngleBetweenCurves(aCirc1, aCirc2, aPoint, theAngle);
362 GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*aCirc1, *aCirc2, *aPoint, theAngle));
364 aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
365 aCirc1->rad, aCirc2->rad, theInternalTangency));
368 return ConstraintWrapperPtr(
369 new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE));