From 042b38afc4aec55ce1868c3025d8a4ca514587e1 Mon Sep 17 00:00:00 2001 From: azv Date: Thu, 27 Feb 2020 16:24:46 +0300 Subject: [PATCH] Issue #3158: Fatal error when create Tangent constraint Forbid by validators the selection of tangent entities if at least on is a B-spline curve and they have no coincident boundary point. --- src/SketchPlugin/SketchPlugin_Tools.cpp | 3 +- src/SketchPlugin/SketchPlugin_Validators.cpp | 88 ++++++++++--- .../Test/TestConstraintTangentBSpline.py | 116 +++++++++--------- .../SketchSolver_ConstraintTangent.cpp | 31 +++-- 4 files changed, 149 insertions(+), 89 deletions(-) diff --git a/src/SketchPlugin/SketchPlugin_Tools.cpp b/src/SketchPlugin/SketchPlugin_Tools.cpp index df8e35cf6..68765a14e 100644 --- a/src/SketchPlugin/SketchPlugin_Tools.cpp +++ b/src/SketchPlugin/SketchPlugin_Tools.cpp @@ -120,7 +120,8 @@ std::set findCoincidentConstraints(const FeaturePtr& theFeature) std::set::const_iterator aIt; for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) { FeaturePtr aConstrFeature = std::dynamic_pointer_cast((*aIt)->owner()); - if (aConstrFeature && aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) + if (aConstrFeature && (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID() || + aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID())) aCoincident.insert(aConstrFeature); } return aCoincident; diff --git a/src/SketchPlugin/SketchPlugin_Validators.cpp b/src/SketchPlugin/SketchPlugin_Validators.cpp index 454d8c871..f4d6b324c 100644 --- a/src/SketchPlugin/SketchPlugin_Validators.cpp +++ b/src/SketchPlugin/SketchPlugin_Validators.cpp @@ -24,6 +24,7 @@ #include "SketchPlugin_BSplinePeriodic.h" #include "SketchPlugin_Circle.h" #include "SketchPlugin_ConstraintCoincidence.h" +#include "SketchPlugin_ConstraintCoincidenceInternal.h" #include "SketchPlugin_ConstraintDistance.h" #include "SketchPlugin_ConstraintRigid.h" #include "SketchPlugin_ConstraintTangent.h" @@ -166,32 +167,81 @@ bool SketchPlugin_TangentAttrValidator::isValid(const AttributePtr& theAttribute bool isObject = aRefAttr->isObject(); ObjectPtr anObject = aRefAttr->object(); - if (isObject && anObject.get()) { - FeaturePtr aRefFea = ModelAPI_Feature::feature(anObject); + if (!isObject || !anObject.get()) { + theError = "It uses an empty object"; + return false; + } - AttributeRefAttrPtr aOtherAttr = anAttributeFeature->data()->refattr(aParamA); - ObjectPtr aOtherObject = aOtherAttr->object(); - FeaturePtr aOtherFea = ModelAPI_Feature::feature(aOtherObject); - if (!aOtherFea) - return true; + FeaturePtr aRefFea = ModelAPI_Feature::feature(anObject); - if (aRefFea->getKind() == SketchPlugin_Line::ID() && - aOtherFea->getKind() == SketchPlugin_Line::ID()) { - theError = "Two segments cannot be tangent"; - return false; - } - else if (isSpline(aRefFea) && isSpline(aOtherFea)) { - theError = "Two B-splines cannot be tangent"; - return false; - } + AttributeRefAttrPtr aOtherAttr = anAttributeFeature->data()->refattr(aParamA); + ObjectPtr aOtherObject = aOtherAttr->object(); + FeaturePtr aOtherFea = ModelAPI_Feature::feature(aOtherObject); + if (!aOtherFea) return true; + + if (aRefFea->getKind() == SketchPlugin_Line::ID() && + aOtherFea->getKind() == SketchPlugin_Line::ID()) { + theError = "Two segments cannot be tangent"; + return false; } - else { - theError = "It uses an empty object"; + else if (isSpline(aRefFea) && isSpline(aOtherFea)) { + theError = "Two B-splines cannot be tangent"; return false; } - return true; + bool isValid = true; + bool hasSpline = isSpline(aRefFea); + if (!hasSpline && isSpline(aOtherFea)) { + hasSpline = true; + std::swap(aRefFea, aOtherFea); + } + if (hasSpline) { + auto isApplicableCoincidence = [](FeaturePtr theFeature, const std::string& theAttrName) { + AttributeRefAttrPtr aRefAttr = theFeature->refattr(theAttrName); + if (aRefAttr->isObject()) + return false; + AttributePtr anAttr = aRefAttr->attr(); + FeaturePtr anOwner = ModelAPI_Feature::feature(anAttr->owner()); + AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast(anAttr); + if (aPointAttr) { + return anOwner->getKind() == SketchPlugin_BSpline::ID() && + (aPointAttr->id() == SketchPlugin_BSpline::START_ID() || + aPointAttr->id() == SketchPlugin_BSpline::END_ID()); + } + + AttributePoint2DArrayPtr aPntArray = + std::dynamic_pointer_cast(anAttr); + if (aPntArray) { + // check index of the pole + AttributeIntegerPtr anIndex = theAttrName == SketchPlugin_Constraint::ENTITY_A() ? + theFeature->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A()) : + theFeature->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B()); + return anIndex && (anIndex->value() == 0 || anIndex->value() == aPntArray->size() - 1); + } + return false; + }; + + isValid = false; + AttributePoint2DArrayPtr aBSplinePoles = std::dynamic_pointer_cast( + aRefFea->attribute(SketchPlugin_BSplineBase::POLES_ID())); + // additional check the B-spline edge and the other edge have a coincident boundary point + std::set aCoincidences = SketchPlugin_Tools::findCoincidentConstraints(aRefFea); + for (std::set::iterator anIt = aCoincidences.begin(); + anIt != aCoincidences.end() && !isValid; ++anIt) { + std::set aCoinc; + if (isApplicableCoincidence(*anIt, SketchPlugin_Constraint::ENTITY_A())) + SketchPlugin_Tools::findCoincidences(*anIt, SketchPlugin_Constraint::ENTITY_B(), + aCoinc, true); + else if (isApplicableCoincidence(*anIt, SketchPlugin_Constraint::ENTITY_B())) + SketchPlugin_Tools::findCoincidences(*anIt, SketchPlugin_Constraint::ENTITY_A(), + aCoinc, true); + + isValid = aCoinc.find(aOtherFea) != aCoinc.end(); + } + } + + return isValid; } bool SketchPlugin_PerpendicularAttrValidator::isValid(const AttributePtr& theAttribute, diff --git a/src/SketchPlugin/Test/TestConstraintTangentBSpline.py b/src/SketchPlugin/Test/TestConstraintTangentBSpline.py index e3e758bb6..f69cd5cf8 100644 --- a/src/SketchPlugin/Test/TestConstraintTangentBSpline.py +++ b/src/SketchPlugin/Test/TestConstraintTangentBSpline.py @@ -118,12 +118,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF += 4 model.do() - self.mySketch.setTangent(self.mySpline.result(), aLine.result()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), aLine.result()) + model.end() - self.assertTangentFeatures(aLine, self.mySpline) + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_circle_tangent(self): """ Test 2. Set tangency between B-spline and a circle @@ -133,12 +133,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF += 3 model.do() - self.mySketch.setTangent(self.mySpline.result(), aCircle.defaultResult()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), aCircle.defaultResult()) + model.end() - self.assertTangentFeatures(aCircle, self.mySpline) + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_arc_tangent(self): """ Test 3. Set tangency between B-spline and an arc @@ -148,12 +148,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF += 5 model.do() - self.mySketch.setTangent(self.mySpline.result(), anArc.defaultResult()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), anArc.defaultResult()) + model.end() - self.assertTangentFeatures(anArc, self.mySpline) + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_ellipse_tangent(self): """ Test 4. Set tangency between B-spline and an ellipse @@ -163,12 +163,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF += 5 model.do() - self.mySketch.setTangent(self.mySpline.result(), anEllipse.defaultResult()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), anEllipse.defaultResult()) + model.end() - self.assertTangentFeatures(anEllipse, self.mySpline) + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_elliptic_arc_tangent(self): """ Test 5. Set tangency between B-spline and an elliptic arc @@ -178,12 +178,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF += 7 model.do() - self.mySketch.setTangent(self.mySpline.result(), anEllipticArc.defaultResult()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), anEllipticArc.defaultResult()) + model.end() - self.assertTangentFeatures(anEllipticArc, self.mySpline) + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_spline_tangent(self): """ Test 6. Set tangency between two B-spline curves @@ -193,13 +193,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF += aSpline.poles().size() * 2 model.do() - self.mySketch.setTangent(self.mySpline.result(), aSpline.result()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), aSpline.result()) + model.end() - #self.assertTangentFeatures(aSpline, self.mySpline) - self.myExpectedFailure = True + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_line_tangent_coincident_by_pole(self): @@ -215,13 +214,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF -= 1 model.do() - self.mySketch.setTangent(self.mySpline.result(), aLine.result()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), aLine.result()) + model.end() - self.assertPointLineDistance(self.mySpline.startPoint(), aLine) - self.assertTangentFeatures(aLine, self.mySpline) + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_circle_tangent_coincident_by_pole(self): """ Test 8. Set tangency between B-spline and a circle coincident with B-spline end point @@ -236,14 +234,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF -= 1 model.do() - self.mySketch.setTangent(self.mySpline.result(), aCircle.defaultResult()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), aCircle.defaultResult()) + model.end() - self.assertTangentFeatures(aCircle, self.mySpline) - dist = model.distancePointPoint(self.mySpline.startPoint(), aCircle.center()) - self.assertAlmostEqual(dist, aCircle.radius().value()) + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_arc_tangent_coincident_by_pole(self): """ Test 9. Set tangency between B-spline and an arc coincident with B-spline end point @@ -258,14 +254,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF -= 1 model.do() - self.mySketch.setTangent(self.mySpline.result(), anArc.defaultResult()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), anArc.defaultResult()) + model.end() - self.assertTangentFeatures(anArc, self.mySpline) - dist = model.distancePointPoint(self.mySpline.endPoint(), anArc.center()) - self.assertAlmostEqual(dist, anArc.radius().value()) + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_ellipse_tangent_coincident_by_pole(self): """ Test 10. Set tangency between B-spline and an ellipse coincident with B-spline start point @@ -280,12 +274,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF -= 1 model.do() - self.mySketch.setTangent(self.mySpline.result(), anEllipse.defaultResult()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), anEllipse.defaultResult()) + model.end() - self.assertTangentLineEllipse(SketchAPI_Line(self.myControlLines[0]), anEllipse) + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_elliptic_arc_tangent_coincident_by_pole(self): """ Test 11. Set tangency between B-spline and an elliptic arc coincident with B-spline start point @@ -300,12 +294,12 @@ class TestTangentBSpline(unittest.TestCase): self.myDOF -= 1 model.do() - self.mySketch.setTangent(self.mySpline.result(), anEllipticArc.defaultResult()) - self.myNbTangency += 1 - self.myDOF -= 1 - model.do() + aTangency = self.mySketch.setTangent(self.mySpline.result(), anEllipticArc.defaultResult()) + model.end() - self.assertTangentLineEllipse(SketchAPI_Line(self.myControlLines[0]), anEllipticArc) + self.assertNotEqual(aTangency.feature().error(), "") + model.undo() + model.begin() def test_line_tangent_coincident_by_boundaries(self): diff --git a/src/SketchSolver/SketchSolver_ConstraintTangent.cpp b/src/SketchSolver/SketchSolver_ConstraintTangent.cpp index f3832c245..90e57e3f4 100644 --- a/src/SketchSolver/SketchSolver_ConstraintTangent.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintTangent.cpp @@ -31,6 +31,8 @@ #include #include +#include + #include #include @@ -423,7 +425,7 @@ std::set coincidentBoundaryPoints(FeaturePtr theFeature1, FeatureP { std::set aCoincidences = collectCoincidences(theFeature1, theFeature2); // collect points only - std::set aCoincidentPoints; + std::map > aCoincidentPoints; std::set::const_iterator aCIt = aCoincidences.begin(); for (; aCIt != aCoincidences.end(); ++ aCIt) { for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) { @@ -435,25 +437,38 @@ std::set coincidentBoundaryPoints(FeaturePtr theFeature1, FeatureP FeaturePtr anOwner = ModelAPI_Feature::feature(anAttr->owner()); if (anOwner == theFeature1 || anOwner == theFeature2) { if (anAttr->id() == SketchPlugin_BSplineBase::POLES_ID()) { - AttributeIntegerPtr anIndex = (*aCIt)->integer(i == 0 ? - SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A() : - SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B()); + AttributePoint2DArrayPtr aPoles = + std::dynamic_pointer_cast(anAttr); + + AttributeIntegerPtr anIndex; + if (anOwner->getKind() == SketchPlugin_BSpline::ID()) { + anIndex = (*aCIt)->integer(i == 0 ? + SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A() : + SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B()); + } if (anIndex) { if (anIndex->value() == 0) anAttr = anOwner->attribute(SketchPlugin_BSpline::START_ID()); - else + else if (anIndex->value() + 1 == aPoles->size()) anAttr = anOwner->attribute(SketchPlugin_BSpline::END_ID()); if (anAttr) - aCoincidentPoints.insert(anAttr); + aCoincidentPoints[anOwner].insert(anAttr); } } else if (anAttr->id() != SketchPlugin_Arc::CENTER_ID() && anAttr->id() != SketchPlugin_Circle::CENTER_ID()) - aCoincidentPoints.insert(anAttr); + aCoincidentPoints[anOwner].insert(anAttr); } } } - return aCoincidentPoints; + + std::set aBoundaryPoints; + if (aCoincidentPoints.size() == 2) { + for (std::map >::iterator anIt = aCoincidentPoints.begin(); + anIt != aCoincidentPoints.end(); ++anIt) + aBoundaryPoints.insert(anIt->second.begin(), anIt->second.end()); + } + return aBoundaryPoints; } static std::set refsToFeatureAndResults(FeaturePtr theFeature) -- 2.39.2