1 // Copyright (C) 2014-2020 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_ConstraintPerpendicular.h>
21 #include <SketchSolver_Error.h>
23 #include <PlaneGCSSolver_EdgeWrapper.h>
24 #include <PlaneGCSSolver_PointWrapper.h>
25 #include <PlaneGCSSolver_Storage.h>
26 #include <PlaneGCSSolver_Tools.h>
27 #include <PlaneGCSSolver_UpdateCoincidence.h>
29 #include <GeomAPI_Circ2d.h>
30 #include <GeomAPI_Lin2d.h>
31 #include <GeomAPI_Pnt2d.h>
32 #include <GeomAPI_Ellipse2d.h>
34 #include <SketchPlugin_Arc.h>
35 #include <SketchPlugin_Circle.h>
36 #include <SketchPlugin_ConstraintCoincidence.h>
37 #include <SketchPlugin_ConstraintMiddle.h>
41 #define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(x)
42 #define GCS_POINT_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(x)
44 /// \brief Obtain constrained features
45 static void getFeatures(const ConstraintPtr& theConstraint,
46 FeaturePtr& theFeature1,
47 FeaturePtr& theFeature2);
49 /// \brief Check whether the entities has only one shared point or less.
50 /// Return list of coincident points.
51 static std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1,
52 FeaturePtr theFeature2);
54 /// \brief Collect points coincident with each of two features
55 static std::set<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2);
57 static void calculateIntersectionPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2,
58 GCSPointPtr& theIntersectionPoint);
60 /// sets angle to 90 or -90 degrees
61 static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
62 const GCSCurvePtr& theCurve2,
63 const GCSPointPtr& thePoint,
67 void SketchSolver_ConstraintPerpendicular::process()
70 if (!myBaseConstraint || !myStorage) {
71 // Not enough parameters are assigned
75 EntityWrapperPtr aValue;
76 std::vector<EntityWrapperPtr> anAttributes;
77 SketchSolver_Constraint::getAttributes(aValue, anAttributes);
78 if (!myErrorMsg.empty())
82 if (!myErrorMsg.empty())
85 myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
88 void SketchSolver_ConstraintPerpendicular::rebuild()
90 if (mySolverConstraint)
91 myStorage->removeConstraint(myBaseConstraint);
93 std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
94 std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
96 mySolverConstraint = ConstraintWrapperPtr();
97 mySharedPoint = AttributePtr();
99 GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(myAuxPoint);
100 aStorage->removeParameters(aParams);
101 myAuxPoint = EntityWrapperPtr();
104 // Check the quantity of entities of each type and their order (arcs first)
108 std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
109 for (; anEntIt != myAttributes.end(); ++anEntIt) {
110 if (!(*anEntIt).get())
112 if ((*anEntIt)->type() == ENTITY_LINE)
114 else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE)
116 else if ((*anEntIt)->type() != ENTITY_POINT)
120 if (aNbLines + aNbCircles + aNbOther == 2) {
122 myType = CONSTRAINT_PERPENDICULAR_CURVES;
125 myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
129 FeaturePtr aFeature1, aFeature2;
130 getFeatures(myBaseConstraint, aFeature1, aFeature2);
132 // check number of coincident points
133 std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2);
134 // Try to find non-boundary points coincident with both features.
135 // It is necesasry to create perpendicularity with ellipse
136 if (aCoincidentPoints.empty() && aNbOther > 0)
137 aCoincidentPoints = coincidentPoints(aFeature1, aFeature2);
139 EntityWrapperPtr aSharedPointEntity;
140 std::list<GCSConstraintPtr> anAuxConstraints;
141 if (!aCoincidentPoints.empty()) {
142 mySharedPoint = *aCoincidentPoints.begin();
143 aSharedPointEntity = myStorage->entity(mySharedPoint);
145 else if (aNbOther > 0) {
146 // create auxiliary point
147 GCSPointPtr aPoint(new GCS::Point);
148 aPoint->x = aStorage->createParameter();
149 aPoint->y = aStorage->createParameter();
150 calculateIntersectionPoint(myAttributes.front(), myAttributes.back(), aPoint);
152 myAuxPoint.reset(new PlaneGCSSolver_PointWrapper(aPoint));
153 aSharedPointEntity = myAuxPoint;
155 // create auxiliary coincident constraints for tangency with ellipse
156 EntityWrapperPtr aDummy;
157 ConstraintWrapperPtr aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
158 CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.front(), aDummy);
159 anAuxConstraints = aCoincidence->constraints();
160 aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
161 CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.back(), aDummy);
162 anAuxConstraints.insert(anAuxConstraints.end(),
163 aCoincidence->constraints().begin(), aCoincidence->constraints().end());
166 ScalarWrapperPtr anAngleWrapper;
167 if (aSharedPointEntity) {
168 adjustAngleBetweenCurves(GCS_EDGE_WRAPPER(myAttributes.front())->entity(),
169 GCS_EDGE_WRAPPER(myAttributes.back())->entity(),
170 GCS_POINT_WRAPPER(aSharedPointEntity)->point(), &myCurveCurveAngle);
171 anAngleWrapper.reset(new PlaneGCSSolver_ScalarWrapper(&myCurveCurveAngle));
174 mySolverConstraint = PlaneGCSSolver_Tools::createConstraint(myBaseConstraint, myType,
175 anAngleWrapper, aSharedPointEntity, EntityWrapperPtr(),
176 myAttributes.front(), myAttributes.back());
178 if (!anAuxConstraints.empty()) {
179 anAuxConstraints.insert(anAuxConstraints.end(), mySolverConstraint->constraints().begin(),
180 mySolverConstraint->constraints().end());
181 mySolverConstraint->setConstraints(anAuxConstraints);
184 myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
187 void SketchSolver_ConstraintPerpendicular::notify(const FeaturePtr& theFeature,
188 PlaneGCSSolver_Update* theUpdater)
190 if (theFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
193 // base constraint may become invalid (for example, during undo)
194 if (!myBaseConstraint->data() || !myBaseConstraint->data()->isValid())
197 FeaturePtr aTgFeat1, aTgFeat2;
198 getFeatures(myBaseConstraint, aTgFeat1, aTgFeat2);
200 bool isRebuild = false;
201 if (theFeature->data() && theFeature->data()->isValid()) {
202 // the constraint has been created
203 if (!mySharedPoint) {
204 // features has no shared point, check whether coincidence constraint binds these features)
205 int aNbCoincidentFeatures = 0;
206 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
207 AttributeRefAttrPtr aRefAttr = theFeature->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
212 if (aRefAttr->isObject())
213 anObj = aRefAttr->object();
215 anObj = aRefAttr->attr()->owner();
217 FeaturePtr aFeature = ModelAPI_Feature::feature(anObj);
218 if (aFeature == aTgFeat1 || aFeature == aTgFeat2)
219 ++aNbCoincidentFeatures;
222 if (aNbCoincidentFeatures == 2)
229 // The features are tangent in the shared point, but the coincidence has been removed/updated.
230 // Check if the coincidence is the same.
231 std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
233 std::set<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
234 for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
235 if (*anIt == mySharedPoint)
236 isRebuild = false; // the coincidence is still exists => nothing to change
239 // check both features have a coincident point
240 std::set<AttributePtr> aCoincidentPoints = coincidentPoints(aTgFeat1, aTgFeat2);
241 isRebuild = (bool)(myAuxPoint.get()) == (!aCoincidentPoints.empty());
252 // ================== Auxiliary functions =================================
253 void getFeatures(const ConstraintPtr& theConstraint,
254 FeaturePtr& theFeature1,
255 FeaturePtr& theFeature2)
257 AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
258 theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
259 aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
260 theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
263 static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
265 const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
266 const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
268 std::set<FeaturePtr> aCoincidences;
269 std::set<AttributePtr>::const_iterator anIt;
271 // collect coincidences referred to the first feature
272 for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
273 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
274 if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
275 aCoincidences.insert(aRef);
278 // leave only coincidences referred to the second feature
279 std::set<FeaturePtr> aCoincidencesBetweenFeatures;
280 for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
281 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
282 if (aCoincidences.find(aRef) != aCoincidences.end())
283 aCoincidencesBetweenFeatures.insert(aRef);
286 return aCoincidencesBetweenFeatures;
289 std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
291 std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
292 // collect points only
293 std::set<AttributePtr> aCoincidentPoints;
294 std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
295 for (; aCIt != aCoincidences.end(); ++ aCIt) {
296 AttributeRefAttrPtr aRefAttrA = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
297 AttributeRefAttrPtr aRefAttrB = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_B());
298 if (!aRefAttrA || aRefAttrA->isObject() ||
299 !aRefAttrB || aRefAttrB->isObject())
302 AttributePtr anAttrA = aRefAttrA->attr();
303 AttributePtr anAttrB = aRefAttrB->attr();
304 if (anAttrA->id() != SketchPlugin_Arc::CENTER_ID() &&
305 anAttrA->id() != SketchPlugin_Circle::CENTER_ID() &&
306 anAttrB->id() != SketchPlugin_Arc::CENTER_ID() &&
307 anAttrB->id() != SketchPlugin_Circle::CENTER_ID()) {
308 aCoincidentPoints.insert(anAttrA);
309 aCoincidentPoints.insert(anAttrB);
312 return aCoincidentPoints;
315 static std::set<AttributePtr> refsToFeatureAndResults(FeaturePtr theFeature)
317 std::set<AttributePtr> aRefs = theFeature->data()->refsToMe();
318 const std::list<ResultPtr>& aResults = theFeature->results();
319 for (std::list<ResultPtr>::const_iterator anIt = aResults.begin();
320 anIt != aResults.end(); ++anIt) {
321 const std::set<AttributePtr>& aResRefs = (*anIt)->data()->refsToMe();
322 aRefs.insert(aResRefs.begin(), aResRefs.end());
327 // collect all points coincident with the feature
328 static std::set<AttributePtr> pointsOnFeature(FeaturePtr theFeature)
330 std::set<AttributePtr> aPoints;
332 std::set<AttributePtr> aRefs = refsToFeatureAndResults(theFeature);
333 for (std::set<AttributePtr>::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) {
334 FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
335 if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() ||
336 aRef->getKind() == SketchPlugin_ConstraintMiddle::ID())) {
337 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
338 AttributeRefAttrPtr aRefAttr = aRef->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
340 AttributePtr anAttr = aRefAttr->attr();
341 if (anAttr && anAttr->id() != SketchPlugin_Arc::CENTER_ID() &&
342 anAttr->id() != SketchPlugin_Circle::CENTER_ID())
343 aPoints.insert(anAttr);
351 std::set<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
353 std::set<AttributePtr> aPointsOnF1 = pointsOnFeature(theFeature1);
354 std::set<AttributePtr> aPointsOnF2 = pointsOnFeature(theFeature2);
356 std::set<AttributePtr> aCommonPoints;
357 for (std::set<AttributePtr>::iterator anIt = aPointsOnF1.begin();
358 anIt != aPointsOnF1.end(); ++anIt)
359 if (aPointsOnF2.find(*anIt) != aPointsOnF2.end())
360 aCommonPoints.insert(*anIt);
361 return aCommonPoints;
364 void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
365 const GCSCurvePtr& theCurve2,
366 const GCSPointPtr& thePoint,
369 double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
370 // bring angle to [-pi..pi]
371 if (anAngle > PI) anAngle -= 2.0 * PI;
372 if (anAngle < -PI) anAngle += 2.0 * PI;
373 // set angle value according to the current angle between curves
377 *theAngle = -PI / 2.;
380 void calculateIntersectionPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2,
381 GCSPointPtr& theIntersectionPoint)
383 std::shared_ptr<GeomAPI_Ellipse2d> anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve1);
384 EntityWrapperPtr aCurve2 = theCurve2;
386 // try converting to ellipse the second curve
387 anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve2);
389 return; // no one curve is ellipse
393 GeomPnt2dPtr aP1, aP2;
394 if (aCurve2->type() == ENTITY_LINE) {
395 std::shared_ptr<GeomAPI_Lin2d> aLine = PlaneGCSSolver_Tools::line(aCurve2);
396 anEllipse->distance(aLine, aP1, aP2);
398 else if (aCurve2->type() == ENTITY_ARC || aCurve2->type() == ENTITY_CIRCLE) {
399 std::shared_ptr<GeomAPI_Circ2d> aCircle = PlaneGCSSolver_Tools::circle(aCurve2);
400 anEllipse->distance(aCircle, aP1, aP2);
402 else if (aCurve2->type() == ENTITY_ELLIPSE || aCurve2->type() == ENTITY_ELLIPTIC_ARC) {
403 std::shared_ptr<GeomAPI_Ellipse2d> anEl2 = PlaneGCSSolver_Tools::ellipse(aCurve2);
404 anEllipse->distance(anEl2, aP1, aP2);
408 *theIntersectionPoint->x = 0.5 * (aP1->x() + aP2->x());
409 *theIntersectionPoint->y = 0.5 * (aP1->y() + aP2->y());