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 <SketchSolver_ConstraintTangent.h>
21 #include <SketchSolver_Error.h>
23 #include <PlaneGCSSolver_EdgeWrapper.h>
24 #include <PlaneGCSSolver_PointWrapper.h>
25 #include <PlaneGCSSolver_Tools.h>
26 #include <PlaneGCSSolver_UpdateCoincidence.h>
28 #include <GeomAPI_Pnt2d.h>
29 #include <SketchPlugin_Arc.h>
30 #include <SketchPlugin_Circle.h>
31 #include <SketchPlugin_ConstraintCoincidence.h>
35 #define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(x)
37 /// \brief Obtain tangent features from the constraint
38 static void getTangentFeatures(const ConstraintPtr& theConstraint,
39 FeaturePtr& theFeature1,
40 FeaturePtr& theFeature2);
42 /// \brief Obtain all coincident constraints between features
43 static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2);
45 /// \brief Check whether the entities has only one shared point or less.
46 /// Return list of coincident points.
47 static std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1,
48 FeaturePtr theFeature2);
50 /// \brief Check if two connected arcs have centers
51 /// in same direction relatively to connection point
52 static bool isArcArcTangencyInternal(EntityWrapperPtr theArc1,
53 EntityWrapperPtr theArc2);
55 static ConstraintWrapperPtr
56 createArcLineTangency(EntityWrapperPtr theEntity1,
57 EntityWrapperPtr theEntity2,
58 EntityWrapperPtr theShapedPoint = EntityWrapperPtr(),
59 double* theAngle = 0);
61 static ConstraintWrapperPtr
62 createArcArcTangency(EntityWrapperPtr theEntity1,
63 EntityWrapperPtr theEntity2,
64 bool theInternalTangency,
65 EntityWrapperPtr theSharedPoint = EntityWrapperPtr(),
66 double* theAngle = 0);
69 void SketchSolver_ConstraintTangent::process()
72 if (!myBaseConstraint || !myStorage) {
73 // Not enough parameters are assigned
77 EntityWrapperPtr aValue;
78 std::vector<EntityWrapperPtr> anAttributes;
79 SketchSolver_Constraint::getAttributes(aValue, anAttributes);
80 if (!myErrorMsg.empty())
84 if (!myErrorMsg.empty())
87 myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
90 void SketchSolver_ConstraintTangent::rebuild()
92 if (mySolverConstraint)
93 myStorage->removeConstraint(myBaseConstraint);
95 mySolverConstraint = ConstraintWrapperPtr();
96 mySharedPoint = AttributePtr();
98 // Check the quantity of entities of each type and their order (arcs first)
101 std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
102 for (; anEntIt != myAttributes.end(); ++anEntIt) {
103 if (!(*anEntIt).get())
105 if ((*anEntIt)->type() == ENTITY_LINE)
107 else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE)
111 if (aNbCircles < 1) {
112 myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE();
115 if (aNbLines == 1 && aNbCircles == 1) {
116 myType = CONSTRAINT_TANGENT_CIRCLE_LINE;
118 else if (aNbCircles == 2) {
119 myType = CONSTRAINT_TANGENT_CIRCLE_CIRCLE;
120 isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back());
123 myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
127 FeaturePtr aFeature1, aFeature2;
128 getTangentFeatures(myBaseConstraint, aFeature1, aFeature2);
130 // check number of coincident points
131 std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2);
132 if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 2) {
133 myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
137 EntityWrapperPtr aSharedPointEntity;
138 if (!aCoincidentPoints.empty()) {
139 mySharedPoint = *aCoincidentPoints.begin();
140 aSharedPointEntity = myStorage->entity(mySharedPoint);
143 if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
144 mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(),
145 aSharedPointEntity, &myCurveCurveAngle);
147 mySolverConstraint = createArcArcTangency(myAttributes.front(), myAttributes.back(),
148 isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle);
151 myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
154 void SketchSolver_ConstraintTangent::adjustConstraint()
156 if (myType == CONSTRAINT_TANGENT_CIRCLE_CIRCLE) {
157 EntityWrapperPtr anEntity1 =
158 myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
159 EntityWrapperPtr anEntity2 =
160 myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
162 if (isArcArcInternal != isArcArcTangencyInternal(anEntity1, anEntity2))
167 void SketchSolver_ConstraintTangent::notify(const FeaturePtr& theFeature,
168 PlaneGCSSolver_Update* theUpdater)
170 if (theFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
173 // base constraint may become invalid (for example, during undo)
174 if (!myBaseConstraint->data() || !myBaseConstraint->data()->isValid())
177 FeaturePtr aTgFeat1, aTgFeat2;
178 getTangentFeatures(myBaseConstraint, aTgFeat1, aTgFeat2);
180 bool isRebuild = false;
181 if (theFeature->data() && theFeature->data()->isValid()) {
182 // the constraint has been created
183 if (!mySharedPoint) {
184 // features has no shared point, check whether coincidence constraint binds these features)
185 int aNbCoincidentFeatures = 0;
186 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
187 AttributeRefAttrPtr aRefAttr = theFeature->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
192 if (aRefAttr->isObject())
193 anObj = aRefAttr->object();
195 anObj = aRefAttr->attr()->owner();
197 FeaturePtr aFeature = ModelAPI_Feature::feature(anObj);
198 if (aFeature == aTgFeat1 || aFeature == aTgFeat2)
199 ++aNbCoincidentFeatures;
202 if (aNbCoincidentFeatures == 2)
207 if (mySharedPoint && !isRebuild) {
208 // The features are tangent in the shared point, but the coincidence has been removed/updated.
209 // Check if the coincidence is the same.
210 std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
212 std::set<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
213 for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
214 if (*anIt == mySharedPoint)
215 isRebuild = false; // the coincidence is still exists => nothing to change
225 // ================== Auxiliary functions =================================
226 void getTangentFeatures(const ConstraintPtr& theConstraint,
227 FeaturePtr& theFeature1,
228 FeaturePtr& theFeature2)
230 AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
231 theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
232 aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
233 theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
236 std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
238 const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
239 const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
241 std::set<FeaturePtr> aCoincidences;
242 std::set<AttributePtr>::const_iterator anIt;
244 // collect coincidences referred to the first feature
245 for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
246 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
247 if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
248 aCoincidences.insert(aRef);
251 // leave only coincidences referred to the second feature
252 std::set<FeaturePtr> aCoincidencesBetweenFeatures;
253 for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
254 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
255 if (aCoincidences.find(aRef) != aCoincidences.end())
256 aCoincidencesBetweenFeatures.insert(aRef);
259 return aCoincidencesBetweenFeatures;
262 std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
264 std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
265 // collect points only
266 std::set<AttributePtr> aCoincidentPoints;
267 std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
268 for (; aCIt != aCoincidences.end(); ++ aCIt) {
269 AttributeRefAttrPtr aRefAttrA = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
270 AttributeRefAttrPtr aRefAttrB = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_B());
271 if (!aRefAttrA || aRefAttrA->isObject() ||
272 !aRefAttrB || aRefAttrB->isObject())
275 AttributePtr anAttrA = aRefAttrA->attr();
276 AttributePtr anAttrB = aRefAttrB->attr();
277 if (anAttrA->id() != SketchPlugin_Arc::CENTER_ID() &&
278 anAttrA->id() != SketchPlugin_Circle::CENTER_ID() &&
279 anAttrB->id() != SketchPlugin_Arc::CENTER_ID() &&
280 anAttrB->id() != SketchPlugin_Circle::CENTER_ID()) {
281 aCoincidentPoints.insert(anAttrA);
282 aCoincidentPoints.insert(anAttrB);
285 return aCoincidentPoints;
288 bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
290 std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
291 GCS_EDGE_WRAPPER(theArc1)->entity());
292 std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
293 GCS_EDGE_WRAPPER(theArc2)->entity());
295 if (!aCirc1 || !aCirc2)
298 double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
299 double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
300 double aDist = sqrt(aDX * aDX + aDY * aDY);
302 return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
305 // sets angle to 0 or 180 degrees
306 static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
307 const GCSCurvePtr& theCurve2,
308 const GCSPointPtr& thePoint,
311 double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
312 // bring angle to [-pi..pi]
313 if (anAngle > PI) anAngle -= 2.0 * PI;
314 if (anAngle < -PI) anAngle += 2.0 * PI;
315 // set angle value according to the current angle between curves
316 if (fabs(anAngle) <= PI / 2.)
323 ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
324 EntityWrapperPtr theEntity2,
325 EntityWrapperPtr theSharedPoint,
328 EdgeWrapperPtr anEntLine, anEntCirc;
329 if (theEntity1->type() == ENTITY_LINE) {
330 anEntLine = GCS_EDGE_WRAPPER(theEntity1);
331 anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
333 anEntLine = GCS_EDGE_WRAPPER(theEntity2);
334 anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
337 std::shared_ptr<GCS::Circle> aCirc =
338 std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
339 std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
341 std::shared_ptr<GCS::Line> aLine =
342 std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
344 GCSConstraintPtr aNewConstr;
345 if (theSharedPoint && anArc) { // do not process shared point between circle and line
347 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
349 adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
351 GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
354 GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
357 return ConstraintWrapperPtr(
358 new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
361 ConstraintWrapperPtr createArcArcTangency(EntityWrapperPtr theEntity1,
362 EntityWrapperPtr theEntity2,
363 bool theInternalTangency,
364 EntityWrapperPtr theSharedPoint,
367 std::shared_ptr<GCS::Circle> aCirc1 =
368 std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity1)->entity());
369 std::shared_ptr<GCS::Circle> aCirc2 =
370 std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity2)->entity());
372 GCSConstraintPtr aNewConstr;
373 if (theSharedPoint) {
375 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
377 adjustAngleBetweenCurves(aCirc1, aCirc2, aPoint, theAngle);
379 GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*aCirc1, *aCirc2, *aPoint, theAngle));
381 aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
382 aCirc1->rad, aCirc2->rad, theInternalTangency));
385 return ConstraintWrapperPtr(
386 new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE));