1 // Copyright (C) 2014-2017 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
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
21 #include <SketchSolver_ConstraintTangent.h>
22 #include <SketchSolver_Error.h>
24 #include <PlaneGCSSolver_EdgeWrapper.h>
25 #include <PlaneGCSSolver_PointWrapper.h>
26 #include <PlaneGCSSolver_Tools.h>
27 #include <PlaneGCSSolver_UpdateCoincidence.h>
29 #include <GeomAPI_Pnt2d.h>
30 #include <SketchPlugin_Arc.h>
31 #include <SketchPlugin_Circle.h>
32 #include <SketchPlugin_ConstraintCoincidence.h>
36 #define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(x)
38 /// \brief Obtain tangent features from the constraint
39 static void getTangentFeatures(const ConstraintPtr& theConstraint,
40 FeaturePtr& theFeature1,
41 FeaturePtr& theFeature2);
43 /// \brief Obtain all coincident constraints between features
44 static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2);
46 /// \brief Check whether the entities has only one shared point or less.
47 /// Return list of coincident points.
48 static std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1,
49 FeaturePtr theFeature2);
51 /// \brief Check if two connected arcs have centers
52 /// in same direction relatively to connection point
53 static bool isArcArcTangencyInternal(EntityWrapperPtr theArc1,
54 EntityWrapperPtr theArc2);
56 static ConstraintWrapperPtr
57 createArcLineTangency(EntityWrapperPtr theEntity1,
58 EntityWrapperPtr theEntity2,
59 EntityWrapperPtr theShapedPoint = EntityWrapperPtr(),
60 double* theAngle = 0);
62 static ConstraintWrapperPtr
63 createArcArcTangency(EntityWrapperPtr theEntity1,
64 EntityWrapperPtr theEntity2,
65 bool theInternalTangency,
66 EntityWrapperPtr theSharedPoint = EntityWrapperPtr(),
67 double* theAngle = 0);
70 void SketchSolver_ConstraintTangent::process()
73 if (!myBaseConstraint || !myStorage) {
74 // Not enough parameters are assigned
78 EntityWrapperPtr aValue;
79 std::vector<EntityWrapperPtr> anAttributes;
80 SketchSolver_Constraint::getAttributes(aValue, anAttributes);
81 if (!myErrorMsg.empty())
85 if (!myErrorMsg.empty())
88 myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
91 void SketchSolver_ConstraintTangent::rebuild()
93 if (mySolverConstraint)
94 myStorage->removeConstraint(myBaseConstraint);
96 mySolverConstraint = ConstraintWrapperPtr();
97 mySharedPoint = AttributePtr();
99 // Check the quantity of entities of each type and their order (arcs first)
102 std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
103 for (; anEntIt != myAttributes.end(); ++anEntIt) {
104 if (!(*anEntIt).get())
106 if ((*anEntIt)->type() == ENTITY_LINE)
108 else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE)
112 if (aNbCircles < 1) {
113 myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE();
116 if (aNbLines == 1 && aNbCircles == 1) {
117 myType = CONSTRAINT_TANGENT_CIRCLE_LINE;
119 else if (aNbCircles == 2) {
120 myType = CONSTRAINT_TANGENT_CIRCLE_CIRCLE;
121 isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back());
124 myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
128 FeaturePtr aFeature1, aFeature2;
129 getTangentFeatures(myBaseConstraint, aFeature1, aFeature2);
131 // check number of coincident points
132 std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2);
133 if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 2) {
134 myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
138 EntityWrapperPtr aSharedPointEntity;
139 if (!aCoincidentPoints.empty()) {
140 mySharedPoint = *aCoincidentPoints.begin();
141 aSharedPointEntity = myStorage->entity(mySharedPoint);
144 if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
145 mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(),
146 aSharedPointEntity, &myCurveCurveAngle);
148 mySolverConstraint = createArcArcTangency(myAttributes.front(), myAttributes.back(),
149 isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle);
152 myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
155 void SketchSolver_ConstraintTangent::adjustConstraint()
157 if (myType == CONSTRAINT_TANGENT_CIRCLE_CIRCLE) {
158 EntityWrapperPtr anEntity1 =
159 myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
160 EntityWrapperPtr anEntity2 =
161 myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
163 if (isArcArcInternal != isArcArcTangencyInternal(anEntity1, anEntity2))
168 void SketchSolver_ConstraintTangent::notify(const FeaturePtr& theFeature,
169 PlaneGCSSolver_Update* theUpdater)
171 if (theFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
174 // base constraint may become invalid (for example, during undo)
175 if (!myBaseConstraint->data() || !myBaseConstraint->data()->isValid())
178 FeaturePtr aTgFeat1, aTgFeat2;
179 getTangentFeatures(myBaseConstraint, aTgFeat1, aTgFeat2);
181 bool isRebuild = false;
182 if (theFeature->data() && theFeature->data()->isValid()) {
183 // the constraint has been created
184 if (!mySharedPoint) {
185 // features has no shared point, check whether coincidence constraint binds these features)
186 int aNbCoincidentFeatures = 0;
187 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
188 AttributeRefAttrPtr aRefAttr = theFeature->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
193 if (aRefAttr->isObject())
194 anObj = aRefAttr->object();
196 anObj = aRefAttr->attr()->owner();
198 FeaturePtr aFeature = ModelAPI_Feature::feature(anObj);
199 if (aFeature == aTgFeat1 || aFeature == aTgFeat2)
200 ++aNbCoincidentFeatures;
203 if (aNbCoincidentFeatures == 2)
208 if (mySharedPoint && !isRebuild) {
209 // The features are tangent in the shared point, but the coincidence has been removed/updated.
210 // Check if the coincidence is the same.
211 std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
213 std::set<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
214 for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
215 if (*anIt == mySharedPoint)
216 isRebuild = false; // the coincidence is still exists => nothing to change
226 // ================== Auxiliary functions =================================
227 void getTangentFeatures(const ConstraintPtr& theConstraint,
228 FeaturePtr& theFeature1,
229 FeaturePtr& theFeature2)
231 AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
232 theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
233 aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
234 theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
237 std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
239 const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
240 const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
242 std::set<FeaturePtr> aCoincidences;
243 std::set<AttributePtr>::const_iterator anIt;
245 // collect coincidences referred to the first feature
246 for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
247 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
248 if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
249 aCoincidences.insert(aRef);
252 // leave only coincidences referred to the second feature
253 std::set<FeaturePtr> aCoincidencesBetweenFeatures;
254 for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
255 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
256 if (aCoincidences.find(aRef) != aCoincidences.end())
257 aCoincidencesBetweenFeatures.insert(aRef);
260 return aCoincidencesBetweenFeatures;
263 std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
265 std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
266 // collect points only
267 std::set<AttributePtr> aCoincidentPoints;
268 std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
269 for (; aCIt != aCoincidences.end(); ++ aCIt) {
270 AttributeRefAttrPtr aRefAttrA = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
271 AttributeRefAttrPtr aRefAttrB = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_B());
272 if (!aRefAttrA || aRefAttrA->isObject() ||
273 !aRefAttrB || aRefAttrB->isObject())
276 AttributePtr anAttrA = aRefAttrA->attr();
277 AttributePtr anAttrB = aRefAttrB->attr();
278 if (anAttrA->id() != SketchPlugin_Arc::CENTER_ID() &&
279 anAttrA->id() != SketchPlugin_Circle::CENTER_ID() &&
280 anAttrB->id() != SketchPlugin_Arc::CENTER_ID() &&
281 anAttrB->id() != SketchPlugin_Circle::CENTER_ID()) {
282 aCoincidentPoints.insert(anAttrA);
283 aCoincidentPoints.insert(anAttrB);
286 return aCoincidentPoints;
289 bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
291 std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
292 GCS_EDGE_WRAPPER(theArc1)->entity());
293 std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
294 GCS_EDGE_WRAPPER(theArc2)->entity());
296 if (!aCirc1 || !aCirc2)
299 double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
300 double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
301 double aDist = sqrt(aDX * aDX + aDY * aDY);
303 return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
306 // sets angle to 0 or 180 degrees
307 static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
308 const GCSCurvePtr& theCurve2,
309 const GCSPointPtr& thePoint,
312 double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
313 // bring angle to [-pi..pi]
314 if (anAngle > PI) anAngle -= 2.0 * PI;
315 if (anAngle < -PI) anAngle += 2.0 * PI;
316 // set angle value according to the current angle between curves
317 if (fabs(anAngle) <= PI / 2.)
324 ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
325 EntityWrapperPtr theEntity2,
326 EntityWrapperPtr theSharedPoint,
329 EdgeWrapperPtr anEntLine, anEntCirc;
330 if (theEntity1->type() == ENTITY_LINE) {
331 anEntLine = GCS_EDGE_WRAPPER(theEntity1);
332 anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
334 anEntLine = GCS_EDGE_WRAPPER(theEntity2);
335 anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
338 std::shared_ptr<GCS::Circle> aCirc =
339 std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
340 std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
342 std::shared_ptr<GCS::Line> aLine =
343 std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
345 GCSConstraintPtr aNewConstr;
346 if (theSharedPoint && anArc) { // do not process shared point between circle and line
348 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
350 adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
352 GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
355 GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
358 return ConstraintWrapperPtr(
359 new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
362 ConstraintWrapperPtr createArcArcTangency(EntityWrapperPtr theEntity1,
363 EntityWrapperPtr theEntity2,
364 bool theInternalTangency,
365 EntityWrapperPtr theSharedPoint,
368 std::shared_ptr<GCS::Circle> aCirc1 =
369 std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity1)->entity());
370 std::shared_ptr<GCS::Circle> aCirc2 =
371 std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity2)->entity());
373 GCSConstraintPtr aNewConstr;
374 if (theSharedPoint) {
376 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
378 adjustAngleBetweenCurves(aCirc1, aCirc2, aPoint, theAngle);
380 GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*aCirc1, *aCirc2, *aPoint, theAngle));
382 aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
383 aCirc1->rad, aCirc2->rad, theInternalTangency));
386 return ConstraintWrapperPtr(
387 new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE));