From 3627271f1f98d2a04cb1fcbfa0e51cdc9700ff09 Mon Sep 17 00:00:00 2001 From: azv Date: Sun, 22 Sep 2019 12:20:50 +0300 Subject: [PATCH] Task 2.12. New entities: ellipses and arcs of ellipses (issue #3003) Constraint Tangent for ellipses --- src/GeomAPI/GeomAPI_Circ2d.cpp | 6 + src/GeomAPI/GeomAPI_Circ2d.h | 6 +- src/GeomAPI/GeomAPI_Ellipse.cpp | 32 ++ src/GeomAPI/GeomAPI_Ellipse.h | 6 + src/GeomAPI/GeomAPI_Ellipse2d.cpp | 57 +++ src/GeomAPI/GeomAPI_Ellipse2d.h | 22 +- src/PartSet/PartSet_Validators.cpp | 29 +- src/SketchPlugin/CMakeLists.txt | 1 + src/SketchPlugin/SketchPlugin_Validators.cpp | 28 +- .../Test/TestConstraintTangentEllipse.py | 354 ++++++++++++++++++ .../PlaneGCSSolver/PlaneGCSSolver_Defs.h | 6 +- .../PlaneGCSSolver/PlaneGCSSolver_Tools.cpp | 43 ++- .../PlaneGCSSolver/PlaneGCSSolver_Tools.h | 12 +- .../SketchSolver_ConstraintCoincidence.cpp | 12 +- .../SketchSolver_ConstraintMirror.cpp | 2 + .../SketchSolver_ConstraintTangent.cpp | 208 ++++++++-- .../SketchSolver_ConstraintTangent.h | 1 + src/SketcherPrs/SketcherPrs_PositionMgr.cpp | 33 +- 18 files changed, 749 insertions(+), 109 deletions(-) create mode 100644 src/SketchPlugin/Test/TestConstraintTangentEllipse.py diff --git a/src/GeomAPI/GeomAPI_Circ2d.cpp b/src/GeomAPI/GeomAPI_Circ2d.cpp index 5204f25e5..25c2e32f8 100644 --- a/src/GeomAPI/GeomAPI_Circ2d.cpp +++ b/src/GeomAPI/GeomAPI_Circ2d.cpp @@ -54,6 +54,12 @@ static gp_Circ2d* newCirc2d(const double theCenterX, const double theCenterY, } +GeomAPI_Circ2d::GeomAPI_Circ2d(const double theCenterX, + const double theCenterY, + const double theRadius) + : GeomAPI_Interface(newCirc2d(theCenterX, theCenterY, gp::DX2d(), theRadius)) +{ +} GeomAPI_Circ2d::GeomAPI_Circ2d(const std::shared_ptr& theCenter, const std::shared_ptr& theCirclePoint) diff --git a/src/GeomAPI/GeomAPI_Circ2d.h b/src/GeomAPI/GeomAPI_Circ2d.h index 479f6edf6..efa65b1d3 100644 --- a/src/GeomAPI/GeomAPI_Circ2d.h +++ b/src/GeomAPI/GeomAPI_Circ2d.h @@ -35,7 +35,11 @@ class GeomAPI_Shape; class GeomAPI_Circ2d : public GeomAPI_Interface { - public: +public: + /// Creation of circle defined by center point and circle radius + GEOMAPI_EXPORT + GeomAPI_Circ2d(const double theCenterX, const double theCenterY, const double theRadius); + /// Creation of circle defined by center point and circle radius GEOMAPI_EXPORT GeomAPI_Circ2d(const std::shared_ptr& theCenter, diff --git a/src/GeomAPI/GeomAPI_Ellipse.cpp b/src/GeomAPI/GeomAPI_Ellipse.cpp index c9384724a..243be630e 100644 --- a/src/GeomAPI/GeomAPI_Ellipse.cpp +++ b/src/GeomAPI/GeomAPI_Ellipse.cpp @@ -23,8 +23,11 @@ #include "GeomAPI_Ellipse.h" #include "GeomAPI_Ax2.h" +#include "GeomAPI_Curve.h" #include "GeomAPI_Pnt.h" +#include +#include #include #define MY_ELIPS implPtr() @@ -35,6 +38,15 @@ GeomAPI_Ellipse::GeomAPI_Ellipse(const std::shared_ptr& theAx2, { } +GeomAPI_Ellipse::GeomAPI_Ellipse(GeomCurvePtr theCurve) +{ + Handle(Geom_Curve) aCurve = theCurve->impl(); + Handle(Geom_Ellipse) anEllipse = Handle(Geom_Ellipse)::DownCast(aCurve); + if (anEllipse.IsNull()) + throw Standard_ConstructionError("GeomAPI_Ellipse: Curve is not an ellipse"); + setImpl(new gp_Elips(anEllipse->Elips())); +} + std::shared_ptr GeomAPI_Ellipse::center() const { const gp_Pnt& aCenter = MY_ELIPS->Location(); @@ -69,3 +81,23 @@ double GeomAPI_Ellipse::majorRadius() const { return MY_ELIPS->MajorRadius(); } + +const std::shared_ptr GeomAPI_Ellipse::project( + const std::shared_ptr& thePoint) const +{ + std::shared_ptr aResult; + if (!MY_ELIPS) + return aResult; + + Handle(Geom_Ellipse) aEllipse = new Geom_Ellipse(*MY_ELIPS); + + const gp_Pnt& aPoint = thePoint->impl(); + + GeomAPI_ProjectPointOnCurve aProj(aPoint, aEllipse); + Standard_Integer aNbPoint = aProj.NbPoints(); + if (aNbPoint > 0) { + gp_Pnt aNearest = aProj.NearestPoint(); + aResult = GeomPointPtr(new GeomAPI_Pnt(aNearest.X(), aNearest.Y(), aNearest.Z())); + } + return aResult; +} diff --git a/src/GeomAPI/GeomAPI_Ellipse.h b/src/GeomAPI/GeomAPI_Ellipse.h index ac63dd97e..dc41993a0 100644 --- a/src/GeomAPI/GeomAPI_Ellipse.h +++ b/src/GeomAPI/GeomAPI_Ellipse.h @@ -28,6 +28,7 @@ #include class GeomAPI_Ax2; +class GeomAPI_Curve; class GeomAPI_Dir; class GeomAPI_Pnt; @@ -53,6 +54,8 @@ public: GEOMAPI_EXPORT GeomAPI_Ellipse(const std::shared_ptr& theAx2, double theMajorRadius, double theMinorRadius); + GEOMAPI_EXPORT GeomAPI_Ellipse(std::shared_ptr theCurve); + /// Returns center of the ellipse GEOMAPI_EXPORT std::shared_ptr center() const; @@ -71,6 +74,9 @@ public: /// Returns major radius of the ellipse GEOMAPI_EXPORT double majorRadius() const; + /// Project point on ellipse + GEOMAPI_EXPORT const std::shared_ptr project( + const std::shared_ptr& thePoint) const; }; //! Pointer on the object diff --git a/src/GeomAPI/GeomAPI_Ellipse2d.cpp b/src/GeomAPI/GeomAPI_Ellipse2d.cpp index 203382304..4a4c9950d 100644 --- a/src/GeomAPI/GeomAPI_Ellipse2d.cpp +++ b/src/GeomAPI/GeomAPI_Ellipse2d.cpp @@ -22,9 +22,15 @@ // Author: Artem ZHIDKOV #include +#include #include +#include #include +#include +#include +#include +#include #include #include #include @@ -112,3 +118,54 @@ double GeomAPI_Ellipse2d::majorRadius() const { return MY_ELLIPSE->MajorRadius(); } + +template +static double extrema(EXTREMAPTR theAlgo, + GeomPnt2dPtr& thePoint1, + GeomPnt2dPtr& thePoint2) +{ + double aDistance = Precision::Infinite(); + if (theAlgo->IsDone() && theAlgo->NbExt() > 0) { + Extrema_POnCurv2d aP1, aP2; + for (int i = 1; i <= theAlgo->NbExt(); ++i) + if (theAlgo->SquareDistance(i) < aDistance) { + aDistance = Sqrt(theAlgo->SquareDistance(i)); + theAlgo->Points(i, aP1, aP2); + } + + aDistance = Sqrt(aDistance); + thePoint1 = GeomPnt2dPtr(new GeomAPI_Pnt2d(aP1.Value().X(), aP1.Value().Y())); + thePoint2 = GeomPnt2dPtr(new GeomAPI_Pnt2d(aP2.Value().X(), aP2.Value().Y())); + } + return aDistance; +} + +double GeomAPI_Ellipse2d::distance(const std::shared_ptr& theLine, + std::shared_ptr& thePointOnMe, + std::shared_ptr& thePointOnLine) +{ + Extrema_ExtElC2d anExtema(theLine->impl(), *MY_ELLIPSE); + return extrema(&anExtema, thePointOnLine, thePointOnMe); +} + +double GeomAPI_Ellipse2d::distance(const std::shared_ptr& theCircle, + std::shared_ptr& thePointOnMe, + std::shared_ptr& thePointOnCircle) +{ + Extrema_ExtElC2d anExtema(theCircle->impl(), *MY_ELLIPSE); + return extrema(&anExtema, thePointOnCircle, thePointOnMe); +} + +double GeomAPI_Ellipse2d::distance(const std::shared_ptr& theEllipse, + std::shared_ptr& thePointOnMe, + std::shared_ptr& thePointOnEllipse) +{ + Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(theEllipse->impl()); + Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(*MY_ELLIPSE); + + Extrema_ExtCC2d* anExtema = + new Extrema_ExtCC2d(Geom2dAdaptor_Curve(anEllipse1), Geom2dAdaptor_Curve(anEllipse2)); + double aDistance = extrema(anExtema, thePointOnEllipse, thePointOnMe); + delete anExtema; + return aDistance; +} diff --git a/src/GeomAPI/GeomAPI_Ellipse2d.h b/src/GeomAPI/GeomAPI_Ellipse2d.h index 11c11f934..bda3221e1 100644 --- a/src/GeomAPI/GeomAPI_Ellipse2d.h +++ b/src/GeomAPI/GeomAPI_Ellipse2d.h @@ -26,8 +26,10 @@ #include -class GeomAPI_Pnt2d; +class GeomAPI_Circ2d; class GeomAPI_Dir2d; +class GeomAPI_Lin2d; +class GeomAPI_Pnt2d; /**\class GeomAPI_Ellipse2d * \ingroup DataModel @@ -63,6 +65,24 @@ public: /// Returns major radius of the ellipse GEOMAPI_EXPORT double majorRadius() const; + + /// Calculate minimal distance between the ellipse and a line. + /// Return corresponding points on the ellipse and on the line. + GEOMAPI_EXPORT double distance(const std::shared_ptr& theLine, + std::shared_ptr& thePointOnMe, + std::shared_ptr& thePointOnLine); + + /// Calculate minimal distance between the ellipse and a circle. + /// Return corresponding points on the ellipse and on the circle. + GEOMAPI_EXPORT double distance(const std::shared_ptr& theCircle, + std::shared_ptr& thePointOnMe, + std::shared_ptr& thePointOnCircle); + + /// Calculate minimal distance between two ellipses. + /// Return corresponding points on the ellipses. + GEOMAPI_EXPORT double distance(const std::shared_ptr& theEllipse, + std::shared_ptr& thePointOnMe, + std::shared_ptr& thePointOnEllipse); }; #endif diff --git a/src/PartSet/PartSet_Validators.cpp b/src/PartSet/PartSet_Validators.cpp index 0b5a42bd5..f8d05fe0a 100644 --- a/src/PartSet/PartSet_Validators.cpp +++ b/src/PartSet/PartSet_Validators.cpp @@ -282,26 +282,19 @@ bool PartSet_TangentSelection::isValid(const ModuleBase_ISelection* theSelection return false; GeomAPI_Edge aEdge1(aShape); - if (aEdge1.isLine() || aEdge1.isArc()) { - if (aList.size() == 2) { - // Check second selection - aPrs = aList.last(); - const GeomShapePtr& aShape2 = aPrs->shape(); - if (!aShape2.get() || aShape2->isNull() || aShape2->shapeType() != GeomAPI_Shape::EDGE) - return false; - GeomAPI_Edge aEdge2(aShape2); - - if (aEdge1.isLine() && aEdge2.isArc()) - return true; - else if (aEdge1.isArc() && aEdge2.isLine()) - return true; - else - return false; - } else - return true; + if (aList.size() == 2) { + // Check second selection + aPrs = aList.last(); + const GeomShapePtr& aShape2 = aPrs->shape(); + if (!aShape2.get() || aShape2->isNull() || aShape2->shapeType() != GeomAPI_Shape::EDGE) + return false; + GeomAPI_Edge aEdge2(aShape2); + + if (aEdge1.isLine() && aEdge2.isLine()) + return false; } - return false; } + return true; } bool PartSet_AngleSelection::isValid(const ModuleBase_ISelection* theSelection, diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 55a9aa143..24745e8a1 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -227,6 +227,7 @@ ADD_UNIT_TESTS( TestConstraintRadius.py TestConstraintRadiusFailure.py TestConstraintTangent.py + TestConstraintTangentEllipse.py TestConstraintVertical.py TestCreateArcByCenterStartEnd.py TestCreateArcByTangentEdge.py diff --git a/src/SketchPlugin/SketchPlugin_Validators.cpp b/src/SketchPlugin/SketchPlugin_Validators.cpp index af85ea6f1..31f217868 100644 --- a/src/SketchPlugin/SketchPlugin_Validators.cpp +++ b/src/SketchPlugin/SketchPlugin_Validators.cpp @@ -162,31 +162,9 @@ bool SketchPlugin_TangentAttrValidator::isValid(const AttributePtr& theAttribute if (!aOtherFea) return true; - if (aRefFea->getKind() == SketchPlugin_Line::ID()) { - if (aOtherFea->getKind() != SketchPlugin_Arc::ID() && - aOtherFea->getKind() != SketchPlugin_Circle::ID()) { - theError = "It refers to a %1, but %2 is neither an %3 nor %4"; - theError.arg(SketchPlugin_Line::ID()).arg(aParamA) - .arg(SketchPlugin_Arc::ID()).arg(SketchPlugin_Circle::ID()); - return false; - } - } - else if (aRefFea->getKind() == SketchPlugin_Arc::ID() || - aRefFea->getKind() == SketchPlugin_Circle::ID()) { - if (aOtherFea->getKind() != SketchPlugin_Line::ID() && - aOtherFea->getKind() != SketchPlugin_Arc::ID() && - aOtherFea->getKind() != SketchPlugin_Circle::ID()) { - theError = "It refers to an %1, but %2 is not a %3 or an %4 or a %5"; - theError.arg(SketchPlugin_Arc::ID()).arg(aParamA) - .arg(SketchPlugin_Line::ID()).arg(SketchPlugin_Arc::ID()) - .arg(SketchPlugin_Circle::ID()); - return false; - } - } - else { - theError = "It refers to %1, but should refer to %2 or %3 or %4"; - theError.arg(aRefFea->getKind()).arg(SketchPlugin_Line::ID()) - .arg(SketchPlugin_Arc::ID()).arg(SketchPlugin_Circle::ID()); + if (aRefFea->getKind() == SketchPlugin_Line::ID() && + aOtherFea->getKind() == SketchPlugin_Line::ID()) { + theError = "Two segments cannot be tangent"; return false; } return true; diff --git a/src/SketchPlugin/Test/TestConstraintTangentEllipse.py b/src/SketchPlugin/Test/TestConstraintTangentEllipse.py new file mode 100644 index 000000000..04f9a18d0 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintTangentEllipse.py @@ -0,0 +1,354 @@ +# Copyright (C) 2019 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +""" + Test constraint "Tangent" applied to ellipse and another entity +""" + +import unittest +import math + +from salome.shaper import model + +from GeomAPI import * +from SketchAPI import * + +__updated__ = "2019-09-20" + +class TestTangentEllipse(unittest.TestCase): + def setUp(self): + axisStart = GeomAPI_Pnt2d(20., 60.) + axisEnd = GeomAPI_Pnt2d(80., 50.) + passedPoint = GeomAPI_Pnt2d(60., 70.) + + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + macroEllipse = self.mySketch.addEllipse(axisStart, axisEnd, passedPoint, False) + model.do() + self.myEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse")) + self.myCenter = macroEllipse.center() + self.myFocus1 = macroEllipse.focus1() + self.myFocus2 = macroEllipse.focus2() + self.myMajorAxis = macroEllipse.majorAxis() + self.myMajorStart = macroEllipse.majorAxisStart() + self.myMajorEnd = macroEllipse.majorAxisEnd() + self.myMinorAxis = macroEllipse.minorAxis() + self.myMinorStart = macroEllipse.minorAxisStart() + self.myMinorEnd = macroEllipse.minorAxisEnd() + + self.myDOF = 5 + self.myNbPoints = 7 + self.myNbLines = 2 + self.myNbArcs = 0 + self.myNbCircles = 0 + self.myNbEllipses = 1 + self.myNbInternals = 11 + self.myNbCoincidence = 0 + self.myNbTangency = 0 + + def tearDown(self): + model.end() + self.checkDOF() + self.assertPoints(self.myCenter.coordinates(), self.myEllipse.center()) + self.assertPoints(self.myFocus1.coordinates(), self.myEllipse.firstFocus()) + self.assertPoints(self.myFocus2.coordinates(), self.myEllipse.secondFocus()) + self.assertPoints(self.myMajorStart.coordinates(), self.myEllipse.majorAxisNegative()) + self.assertPoints(self.myMajorEnd.coordinates(), self.myEllipse.majorAxisPositive()) + self.assertPoints(self.myMajorAxis.startPoint(), self.myEllipse.majorAxisNegative()) + self.assertPoints(self.myMajorAxis.endPoint(), self.myEllipse.majorAxisPositive()) + self.assertPoints(self.myMinorStart.coordinates(), self.myEllipse.minorAxisNegative()) + self.assertPoints(self.myMinorEnd.coordinates(), self.myEllipse.minorAxisPositive()) + self.assertPoints(self.myMinorAxis.startPoint(), self.myEllipse.minorAxisNegative()) + self.assertPoints(self.myMinorAxis.endPoint(), self.myEllipse.minorAxisPositive()) + model.testNbSubFeatures(self.mySketch, "SketchPoint", self.myNbPoints) + model.testNbSubFeatures(self.mySketch, "SketchLine", self.myNbLines) + model.testNbSubFeatures(self.mySketch, "SketchArc", self.myNbArcs) + model.testNbSubFeatures(self.mySketch, "SketchCircle", self.myNbCircles) + model.testNbSubFeatures(self.mySketch, "SketchEllipse", self.myNbEllipses) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidenceInternal", self.myNbInternals) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", self.myNbCoincidence) + model.testNbSubFeatures(self.mySketch, "SketchConstraintTangent", self.myNbTangency) + + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def assertTangentLineEllipse(self, theLine, theEllipse): + aLine = GeomAPI_Lin2d(theLine.startPoint().pnt(), theLine.endPoint().pnt()) + projF1 = aLine.project(theEllipse.firstFocus().pnt()) + projF2 = aLine.project(theEllipse.secondFocus().pnt()) + + distF1P1 = model.distancePointPoint(theEllipse.firstFocus(), projF1) + distF2P2 = model.distancePointPoint(theEllipse.secondFocus(), projF2) + + tgPoint = GeomAPI_Pnt2d((projF1.x() * distF2P2 + projF2.x() * distF1P1) / (distF1P1 + distF2P2), (projF1.y() * distF2P2 + projF2.y() * distF1P1) / (distF1P1 + distF2P2)) + distF1T = model.distancePointPoint(theEllipse.firstFocus(), tgPoint) + distF2T = model.distancePointPoint(theEllipse.secondFocus(), tgPoint) + self.assertAlmostEqual(distF1T + distF2T, 2 * theEllipse.majorRadius().value()) + + def assertTangentCircleEllipse(self, theCircle, theEllipse): + axis = GeomAPI_Dir2d(theEllipse.firstFocus().x() - theEllipse.center().x(), theEllipse.firstFocus().y() - theEllipse.center().y()) + anEllipse = GeomAPI_Ellipse2d(theEllipse.center().pnt(), axis, theEllipse.majorRadius().value(), theEllipse.minorRadius().value()) + aCircle = GeomAPI_Circ2d(theCircle.center().pnt(), GeomAPI_Dir2d(1, 0), theCircle.radius().value()) + + pOnE = GeomAPI_Pnt2d(0, 0) + pOnC = GeomAPI_Pnt2d(0, 0) + anEllipse.distance(aCircle, pOnE, pOnC) + self.assertAlmostEqual(model.distancePointPoint(pOnE, theCircle.center()), theCircle.radius().value()) + + dist1 = model.distancePointPoint(pOnC, theEllipse.firstFocus()) + dist2 = model.distancePointPoint(pOnC, theEllipse.secondFocus()) + self.assertAlmostEqual(dist1 + dist2, 2 * theEllipse.majorRadius().value()) + + def assertTangentEllipses(self, theEllipse1, theEllipse2): + axis1 = GeomAPI_Dir2d(theEllipse1.firstFocus().x() - theEllipse1.center().x(), theEllipse1.firstFocus().y() - theEllipse1.center().y()) + anEllipse1 = GeomAPI_Ellipse2d(theEllipse1.center().pnt(), axis1, theEllipse1.majorRadius().value(), theEllipse1.minorRadius().value()) + axis2 = GeomAPI_Dir2d(theEllipse2.firstFocus().x() - theEllipse2.center().x(), theEllipse2.firstFocus().y() - theEllipse2.center().y()) + anEllipse2 = GeomAPI_Ellipse2d(theEllipse2.center().pnt(), axis2, theEllipse2.majorRadius().value(), theEllipse2.minorRadius().value()) + + p1 = GeomAPI_Pnt2d(0, 0) + p2 = GeomAPI_Pnt2d(0, 0) + anEllipse1.distance(anEllipse2, p1, p2) + + dist1 = model.distancePointPoint(p2, theEllipse1.firstFocus()) + dist2 = model.distancePointPoint(p2, theEllipse1.secondFocus()) + self.assertAlmostEqual(dist1 + dist2, 2 * theEllipse1.majorRadius().value()) + + dist1 = model.distancePointPoint(p1, theEllipse2.firstFocus()) + dist2 = model.distancePointPoint(p1, theEllipse2.secondFocus()) + self.assertAlmostEqual(dist1 + dist2, 2 * theEllipse2.majorRadius().value()) + + def assertPoints(self, thePoint1, thePoint2): + self.assertAlmostEqual(thePoint1.x(), thePoint2.x()) + self.assertAlmostEqual(thePoint1.y(), thePoint2.y()) + + + def test_line_tangent(self): + """ Test 1. Set tangency between ellipse and a line + """ + aLine = self.mySketch.addLine(10, 10, 90, 40) + self.myNbLines += 1 + self.myDOF += 4 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipse) + + + def test_line_coincident_then_tangent(self): + """ Test 2. Set tangency between ellipse and a line, if the extremity of the line is coincident with the ellipse + """ + aLine = self.mySketch.addLine(10, 10, 90, 40) + self.mySketch.setCoincident(aLine.endPoint(), self.myEllipse.result()) + self.myNbLines += 1 + self.myNbCoincidence += 1 + self.myDOF += 3 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipse) + + + def test_line_tangent_then_coincident(self): + """ Test 3. Set tangency between ellipse and a line, after that apply coincidence of extremity of the line and the ellipse's curve + """ + aLine = self.mySketch.addLine(10, 10, 90, 40) + self.myNbLines += 1 + self.myDOF += 4 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.mySketch.setCoincident(aLine.startPoint(), self.myEllipse.result()) + self.myNbCoincidence += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipse) + + + def test_line_tangent_then_remove_coincidence(self): + """ Test 4. Set tangency between ellipse and a line, which have a coincident point, then remove this coincidence + """ + aLine = self.mySketch.addLine(10, 10, 90, 40) + aCoincidence = self.mySketch.setCoincident(aLine.endPoint(), self.myEllipse.result()) + self.myNbLines += 1 + self.myNbCoincidence += 1 + self.myDOF += 3 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.myDocument.removeFeature(aCoincidence.feature()) + self.myNbCoincidence -= 1 + self.myDOF += 1 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipse) + + + def test_circle_tangent(self): + """ Test 5. Set tangency between ellipse and a circle + """ + aCircle = self.mySketch.addCircle(30, 10, 20) + self.myNbCircles += 1 + self.myDOF += 3 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), aCircle.defaultResult()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentCircleEllipse(aCircle, self.myEllipse) + + + def test_circle_coincident_then_tangent(self): + """ Test 6. Set tangency between ellipse and a circle, if the circle is coincident with start point of ellipse's minor axis + """ + aCircle = self.mySketch.addCircle(30, 10, 20) + self.mySketch.setCoincident(self.myMinorStart.coordinates(), aCircle.defaultResult()) + self.myNbCircles += 1 + self.myNbCoincidence += 1 + self.myDOF += 2 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), aCircle.defaultResult()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentCircleEllipse(aCircle, self.myEllipse) + self.assertAlmostEqual(model.distancePointPoint(aCircle.center(), self.myMinorStart.coordinates()), aCircle.radius().value()) + + + def test_arc_tangent(self): + """ Test 7. Set tangency between ellipse and a circular arc + """ + anArc = self.mySketch.addArc(30, 10, 20, 10, 40, 10, False) + self.myNbArcs += 1 + self.myDOF += 5 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), anArc.results()[-1]) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentCircleEllipse(anArc, self.myEllipse) + + + def test_arc_coincident_then_tangent(self): + """ Test 8. Set tangency between ellipse and an arc, if the extremity of the arc is coincident with the ellipse + """ + anArc = self.mySketch.addArc(30, 10, 20, 10, 40, 10, False) + self.mySketch.setCoincident(anArc.endPoint(), self.myEllipse.result()) + self.myNbArcs += 1 + self.myNbCoincidence += 1 + self.myDOF += 4 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), anArc.results()[-1]) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentCircleEllipse(anArc, self.myEllipse) + + + def test_arc_tangent_then_coincident(self): + """ Test 9. Set tangency between ellipse and an arc, after that apply coincidence of extremity of the arc and the ellipse's curve + """ + anArc = self.mySketch.addArc(30, 10, 20, 10, 40, 10, False) + self.myNbArcs += 1 + self.myDOF += 5 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), anArc.results()[-1]) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.mySketch.setCoincident(anArc.startPoint(), self.myEllipse.result()) + self.myNbCoincidence += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentCircleEllipse(anArc, self.myEllipse) + + + def test_arc_tangent_then_remove_coincidence(self): + """ Test 10. Set tangency between ellipse and an arc, which have a coincident point, then remove this coincidence + """ + anArc = self.mySketch.addArc(30, 10, 20, 10, 40, 10, False) + aCoincidence = self.mySketch.setCoincident(anArc.endPoint(), self.myEllipse.result()) + self.myNbArcs += 1 + self.myNbCoincidence += 1 + self.myDOF += 4 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), anArc.results()[-1]) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.myDocument.removeFeature(aCoincidence.feature()) + self.myNbCoincidence -= 1 + self.myDOF += 1 + model.do() + + self.assertTangentCircleEllipse(anArc, self.myEllipse) + + + def test_ellipse_tangent(self): + """ Test 11. Set tangency between two ellipses + """ + anEllipse = self.mySketch.addEllipse(10, 10, 20, -50, 20) + self.myNbEllipses += 1 + self.myDOF += 5 + model.do() + + self.mySketch.setTangent(self.myEllipse.result(), anEllipse.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentEllipses(anEllipse, self.myEllipse) + + + +if __name__ == "__main__": + test_program = unittest.main(exit=False) + assert test_program.result.wasSuccessful(), "Test failed" + assert model.checkPythonDump() diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h index c2893b1f8..264191603 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h @@ -60,9 +60,7 @@ enum SketchSolver_ConstraintType { CONSTRAINT_UNKNOWN = 0, CONSTRAINT_COINCIDENCE, // base coincidence if we don't know exact type yet CONSTRAINT_PT_PT_COINCIDENT, - CONSTRAINT_PT_ON_LINE, - CONSTRAINT_PT_ON_CIRCLE, - CONSTRAINT_PT_ON_ELLIPSE, + CONSTRAINT_PT_ON_CURVE, CONSTRAINT_MIDDLE_POINT, CONSTRAINT_DISTANCE, // base distance if we don't know the measured objects yet CONSTRAINT_PT_PT_DISTANCE, @@ -83,7 +81,7 @@ enum SketchSolver_ConstraintType { CONSTRAINT_EQUAL_RADIUS, CONSTRAINT_TANGENT, // base tangency if we don't know the measured objects yet CONSTRAINT_TANGENT_CIRCLE_LINE, - CONSTRAINT_TANGENT_CIRCLE_CIRCLE, + CONSTRAINT_TANGENT_CURVE_CURVE, CONSTRAINT_COLLINEAR, CONSTRAINT_MULTI_TRANSLATION, CONSTRAINT_MULTI_ROTATION diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp index d0914df1c..11c265dd0 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp @@ -54,6 +54,12 @@ #include #include +#include +#include +#include +#include +#include + #include @@ -190,9 +196,7 @@ ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint( case CONSTRAINT_PT_PT_COINCIDENT: aResult = createConstraintCoincidence(aPoint1, aPoint2); break; - case CONSTRAINT_PT_ON_LINE: - case CONSTRAINT_PT_ON_CIRCLE: - case CONSTRAINT_PT_ON_ELLIPSE: + case CONSTRAINT_PT_ON_CURVE: aResult = createConstraintPointOnEntity(theType, aPoint1, GCS_EDGE_WRAPPER(theEntity1)); break; case CONSTRAINT_MIDDLE_POINT: @@ -285,6 +289,39 @@ std::shared_ptr PlaneGCSSolver_Tools::line(FeaturePtr theFeature) return std::shared_ptr(new GeomAPI_Lin2d(aStart->pnt(), aEnd->pnt())); } +std::shared_ptr PlaneGCSSolver_Tools::circle(EntityWrapperPtr theEntity) +{ + if (theEntity->type() != ENTITY_CIRCLE && theEntity->type() != ENTITY_ARC) + return std::shared_ptr(); + + std::shared_ptr anEntity = + std::dynamic_pointer_cast(theEntity); + std::shared_ptr aCirc = std::dynamic_pointer_cast(anEntity->entity()); + return std::shared_ptr( + new GeomAPI_Circ2d(*(aCirc->center.x), *(aCirc->center.y), *(aCirc->rad))); +} + +std::shared_ptr PlaneGCSSolver_Tools::ellipse(EntityWrapperPtr theEntity) +{ + if (theEntity->type() != ENTITY_ELLIPSE && theEntity->type() != ENTITY_ELLIPTICAL_ARC) + return std::shared_ptr(); + + std::shared_ptr anEntity = + std::dynamic_pointer_cast(theEntity); + std::shared_ptr anEllipse = + std::dynamic_pointer_cast(anEntity->entity()); + + std::shared_ptr aCenter( + new GeomAPI_Pnt2d(*(anEllipse->center.x), *(anEllipse->center.y))); + std::shared_ptr anAxis(new GeomAPI_Dir2d( + *(anEllipse->focus1.x) - *(anEllipse->center.x), + *(anEllipse->focus1.y) - *(anEllipse->center.y))); + + return std::shared_ptr( + new GeomAPI_Ellipse2d(aCenter, anAxis, anEllipse->getRadMaj(), *anEllipse->radmin)); +} + + GCS::SET_pD PlaneGCSSolver_Tools::parameters(const EntityWrapperPtr& theEntity) { diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h index 4e5b666b6..1eb95be4f 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h @@ -24,8 +24,10 @@ #include #include -#include -#include +class GeomAPI_Circ2d; +class GeomAPI_Ellipse2d; +class GeomAPI_Lin2d; +class GeomAPI_Pnt2d; /** \namespace PlaneGCSSolver_Tools * \ingroup Plugins @@ -68,6 +70,12 @@ namespace PlaneGCSSolver_Tools /// \brief Convert entity to line /// \return empty pointer if the entity is not a line std::shared_ptr line(EntityWrapperPtr theEntity); + /// \brief Convert entity to circle + /// \return empty pointer if the entity is not a circle + std::shared_ptr circle(EntityWrapperPtr theEntity); + /// \brief Convert entity to ellipse + /// \return empty pointer if the entity is not an ellipse + std::shared_ptr ellipse(EntityWrapperPtr theEntity); /// \brief Convert entity to line /// \return empty pointer if the entity is not a line diff --git a/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp b/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp index f1c32b733..b0520c45a 100644 --- a/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp @@ -96,17 +96,7 @@ void SketchSolver_ConstraintCoincidence::getAttributes( if (theAttributes[1]) myType = CONSTRAINT_PT_PT_COINCIDENT; else if (theAttributes[2]) { - // check the type of entity (line or circle) - SketchSolver_EntityType anEntType = theAttributes[2]->type(); - if (anEntType == ENTITY_LINE) - myType = CONSTRAINT_PT_ON_LINE; - else if (anEntType == ENTITY_CIRCLE || anEntType == ENTITY_ARC) - myType = CONSTRAINT_PT_ON_CIRCLE; - else if (anEntType == ENTITY_ELLIPSE || anEntType == ENTITY_ELLIPTICAL_ARC) - myType = CONSTRAINT_PT_ON_ELLIPSE; - else - myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE(); - + myType = CONSTRAINT_PT_ON_CURVE; // obtain extremity points of the coincident feature for further checking of multi-coincidence getCoincidentFeatureExtremities(myBaseConstraint, myStorage, myFeatureExtremities); } else diff --git a/src/SketchSolver/SketchSolver_ConstraintMirror.cpp b/src/SketchSolver/SketchSolver_ConstraintMirror.cpp index cbf025ed5..6be506b25 100644 --- a/src/SketchSolver/SketchSolver_ConstraintMirror.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintMirror.cpp @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include #include diff --git a/src/SketchSolver/SketchSolver_ConstraintTangent.cpp b/src/SketchSolver/SketchSolver_ConstraintTangent.cpp index eaa0e2c41..16e6d11da 100644 --- a/src/SketchSolver/SketchSolver_ConstraintTangent.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintTangent.cpp @@ -22,13 +22,19 @@ #include #include +#include #include #include +#include +#include #include +#include + #include #include #include +#include #include @@ -47,6 +53,9 @@ static std::set collectCoincidences(FeaturePtr theFeature1, FeatureP static std::set coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2); +/// \brief Collect points coincident with each of two features +static std::set coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2); + /// \brief Check if two connected arcs have centers /// in same direction relatively to connection point static bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, @@ -59,11 +68,14 @@ static ConstraintWrapperPtr double* theAngle = 0); static ConstraintWrapperPtr - createArcArcTangency(EntityWrapperPtr theEntity1, - EntityWrapperPtr theEntity2, - bool theInternalTangency, - EntityWrapperPtr theSharedPoint = EntityWrapperPtr(), - double* theAngle = 0); + createCurveCurveTangency(EntityWrapperPtr theEntity1, + EntityWrapperPtr theEntity2, + bool theInternalTangency, + EntityWrapperPtr theSharedPoint = EntityWrapperPtr(), + double* theAngle = 0); + +static void calculateTangencyPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2, + GCSPointPtr& theTangencyPoint); void SketchSolver_ConstraintTangent::process() @@ -92,12 +104,21 @@ void SketchSolver_ConstraintTangent::rebuild() if (mySolverConstraint) myStorage->removeConstraint(myBaseConstraint); + std::shared_ptr aStorage = + std::dynamic_pointer_cast(myStorage); + mySolverConstraint = ConstraintWrapperPtr(); mySharedPoint = AttributePtr(); + if (myAuxPoint) { + GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(myAuxPoint); + aStorage->removeParameters(aParams); + myAuxPoint = EntityWrapperPtr(); + } // Check the quantity of entities of each type and their order (arcs first) int aNbLines = 0; int aNbCircles = 0; + int aNbEllipses = 0; std::list::iterator anEntIt = myAttributes.begin(); for (; anEntIt != myAttributes.end(); ++anEntIt) { if (!(*anEntIt).get()) @@ -106,17 +127,19 @@ void SketchSolver_ConstraintTangent::rebuild() ++aNbLines; else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE) ++aNbCircles; + else if ((*anEntIt)->type() == ENTITY_ELLIPSE || (*anEntIt)->type() == ENTITY_ELLIPTICAL_ARC) + ++aNbEllipses; } - if (aNbCircles < 1) { + if (aNbCircles + aNbEllipses < 1) { myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE(); return; } if (aNbLines == 1 && aNbCircles == 1) { myType = CONSTRAINT_TANGENT_CIRCLE_LINE; } - else if (aNbCircles == 2) { - myType = CONSTRAINT_TANGENT_CIRCLE_CIRCLE; + else if (aNbLines + aNbCircles + aNbEllipses == 2) { + myType = CONSTRAINT_TANGENT_CURVE_CURVE; isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back()); } else { @@ -134,26 +157,58 @@ void SketchSolver_ConstraintTangent::rebuild() return; } + // Try to find non-boundary points coincident with both features. + // It is necesasry to create tangency with ellipse + if (aCoincidentPoints.empty() && aNbEllipses > 0) + aCoincidentPoints = coincidentPoints(aFeature1, aFeature2); + EntityWrapperPtr aSharedPointEntity; + std::list anAuxConstraints; if (!aCoincidentPoints.empty()) { mySharedPoint = *aCoincidentPoints.begin(); aSharedPointEntity = myStorage->entity(mySharedPoint); } + else if (aNbEllipses > 0) { + // create auxiliary point + GCSPointPtr aPoint(new GCS::Point); + aPoint->x = aStorage->createParameter(); + aPoint->y = aStorage->createParameter(); + calculateTangencyPoint(myAttributes.front(), myAttributes.back(), aPoint); + + myAuxPoint.reset(new PlaneGCSSolver_PointWrapper(aPoint)); + aSharedPointEntity = myAuxPoint; + + // create auxiliary coincident constraints for tangency with ellipse + EntityWrapperPtr aDummy; + ConstraintWrapperPtr aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(), + CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.front(), aDummy); + anAuxConstraints = aCoincidence->constraints(); + aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(), + CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.back(), aDummy); + anAuxConstraints.insert(anAuxConstraints.end(), + aCoincidence->constraints().begin(), aCoincidence->constraints().end()); + } if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) { mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(), aSharedPointEntity, &myCurveCurveAngle); } else { - mySolverConstraint = createArcArcTangency(myAttributes.front(), myAttributes.back(), + mySolverConstraint = createCurveCurveTangency(myAttributes.front(), myAttributes.back(), isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle); } + if (!anAuxConstraints.empty()) { + anAuxConstraints.insert(anAuxConstraints.end(), mySolverConstraint->constraints().begin(), + mySolverConstraint->constraints().end()); + mySolverConstraint->setConstraints(anAuxConstraints); + } + myStorage->addConstraint(myBaseConstraint, mySolverConstraint); } void SketchSolver_ConstraintTangent::adjustConstraint() { - if (myType == CONSTRAINT_TANGENT_CIRCLE_CIRCLE) { + if (myType == CONSTRAINT_TANGENT_CURVE_CURVE) { EntityWrapperPtr anEntity1 = myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); EntityWrapperPtr anEntity2 = @@ -204,15 +259,22 @@ void SketchSolver_ConstraintTangent::notify(const FeaturePtr& theFeature, } } - if (mySharedPoint && !isRebuild) { - // The features are tangent in the shared point, but the coincidence has been removed/updated. - // Check if the coincidence is the same. - std::set aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2); - isRebuild = true; - std::set::iterator anIt = aCoincidentPoints.begin(); - for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt) - if (*anIt == mySharedPoint) - isRebuild = false; // the coincidence is still exists => nothing to change + if (!isRebuild) { + if (mySharedPoint) { + // The features are tangent in the shared point, but the coincidence has been removed/updated. + // Check if the coincidence is the same. + std::set aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2); + isRebuild = true; + std::set::iterator anIt = aCoincidentPoints.begin(); + for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt) + if (*anIt == mySharedPoint) + isRebuild = false; // the coincidence is still exists => nothing to change + } + else { + // check both features have a coincident point + std::set aCoincidentPoints = coincidentPoints(aTgFeat1, aTgFeat2); + isRebuild = (bool)(myAuxPoint.get()) == (!aCoincidentPoints.empty()); + } } if (isRebuild) @@ -285,6 +347,55 @@ std::set coincidentBoundaryPoints(FeaturePtr theFeature1, FeatureP return aCoincidentPoints; } +static std::set refsToFeatureAndResults(FeaturePtr theFeature) +{ + std::set aRefs = theFeature->data()->refsToMe(); + const std::list& aResults = theFeature->results(); + for (std::list::const_iterator anIt = aResults.begin(); + anIt != aResults.end(); ++anIt) { + const std::set& aResRefs = (*anIt)->data()->refsToMe(); + aRefs.insert(aResRefs.begin(), aResRefs.end()); + } + return aRefs; +} + +// collect all points coincident with the feature +static std::set pointsOnFeature(FeaturePtr theFeature) +{ + std::set aPoints; + + std::set aRefs = refsToFeatureAndResults(theFeature); + for (std::set::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) { + FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner()); + if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() || + aRef->getKind() == SketchPlugin_ConstraintMiddle::ID())) { + for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) { + AttributeRefAttrPtr aRefAttr = aRef->refattr(SketchPlugin_Constraint::ATTRIBUTE(i)); + if (aRefAttr) { + AttributePtr anAttr = aRefAttr->attr(); + if (anAttr && anAttr->id() != SketchPlugin_Arc::CENTER_ID() && + anAttr->id() != SketchPlugin_Circle::CENTER_ID()) + aPoints.insert(anAttr); + } + } + } + } + return aPoints; +} + +std::set coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2) +{ + std::set aPointsOnF1 = pointsOnFeature(theFeature1); + std::set aPointsOnF2 = pointsOnFeature(theFeature2); + + std::set aCommonPoints; + for (std::set::iterator anIt = aPointsOnF1.begin(); + anIt != aPointsOnF1.end(); ++anIt) + if (aPointsOnF2.find(*anIt) != aPointsOnF2.end()) + aCommonPoints.insert(*anIt); + return aCommonPoints; +} + bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2) { std::shared_ptr aCirc1 = std::dynamic_pointer_cast( @@ -358,30 +469,65 @@ ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1, new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE)); } -ConstraintWrapperPtr createArcArcTangency(EntityWrapperPtr theEntity1, - EntityWrapperPtr theEntity2, - bool theInternalTangency, - EntityWrapperPtr theSharedPoint, - double* theAngle) +ConstraintWrapperPtr createCurveCurveTangency(EntityWrapperPtr theEntity1, + EntityWrapperPtr theEntity2, + bool theInternalTangency, + EntityWrapperPtr theSharedPoint, + double* theAngle) { - std::shared_ptr aCirc1 = - std::dynamic_pointer_cast(GCS_EDGE_WRAPPER(theEntity1)->entity()); - std::shared_ptr aCirc2 = - std::dynamic_pointer_cast(GCS_EDGE_WRAPPER(theEntity2)->entity()); + GCSCurvePtr aCurve1 = + std::dynamic_pointer_cast(GCS_EDGE_WRAPPER(theEntity1)->entity()); + GCSCurvePtr aCurve2 = + std::dynamic_pointer_cast(GCS_EDGE_WRAPPER(theEntity2)->entity()); GCSConstraintPtr aNewConstr; if (theSharedPoint) { GCSPointPtr aPoint = std::dynamic_pointer_cast(theSharedPoint)->point(); - adjustAngleBetweenCurves(aCirc1, aCirc2, aPoint, theAngle); + adjustAngleBetweenCurves(aCurve1, aCurve2, aPoint, theAngle); aNewConstr = - GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*aCirc1, *aCirc2, *aPoint, theAngle)); + GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*aCurve1, *aCurve2, *aPoint, theAngle)); } else { + std::shared_ptr aCirc1 = std::dynamic_pointer_cast(aCurve1); + std::shared_ptr aCirc2 = std::dynamic_pointer_cast(aCurve2); aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center, aCirc1->rad, aCirc2->rad, theInternalTangency)); } return ConstraintWrapperPtr( - new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE)); + new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CURVE_CURVE)); +} + +void calculateTangencyPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2, + GCSPointPtr& theTangencyPoint) +{ + std::shared_ptr anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve1); + EntityWrapperPtr aCurve2 = theCurve2; + if (!anEllipse) { + // try converting to ellipse the second curve + anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve2); + if (!anEllipse) + return; // no one curve is ellipse + aCurve2 = theCurve1; + } + + GeomPnt2dPtr aP1, aP2; + if (aCurve2->type() == ENTITY_LINE) { + std::shared_ptr aLine = PlaneGCSSolver_Tools::line(aCurve2); + anEllipse->distance(aLine, aP1, aP2); + } + else if (aCurve2->type() == ENTITY_ARC || aCurve2->type() == ENTITY_CIRCLE) { + std::shared_ptr aCircle = PlaneGCSSolver_Tools::circle(aCurve2); + anEllipse->distance(aCircle, aP1, aP2); + } + else if (aCurve2->type() == ENTITY_ELLIPSE || aCurve2->type() == ENTITY_ELLIPTICAL_ARC) { + std::shared_ptr anEl2 = PlaneGCSSolver_Tools::ellipse(aCurve2); + anEllipse->distance(anEl2, aP1, aP2); + } + + if (aP1 && aP2) { + *theTangencyPoint->x = 0.5 * (aP1->x() + aP2->x()); + *theTangencyPoint->y = 0.5 * (aP1->y() + aP2->y()); + } } diff --git a/src/SketchSolver/SketchSolver_ConstraintTangent.h b/src/SketchSolver/SketchSolver_ConstraintTangent.h index 6af491ec7..57424b293 100644 --- a/src/SketchSolver/SketchSolver_ConstraintTangent.h +++ b/src/SketchSolver/SketchSolver_ConstraintTangent.h @@ -55,6 +55,7 @@ private: bool isArcArcInternal; double myCurveCurveAngle; AttributePtr mySharedPoint; + EntityWrapperPtr myAuxPoint; }; #endif diff --git a/src/SketcherPrs/SketcherPrs_PositionMgr.cpp b/src/SketcherPrs/SketcherPrs_PositionMgr.cpp index d803256d8..7aacd2002 100644 --- a/src/SketcherPrs/SketcherPrs_PositionMgr.cpp +++ b/src/SketcherPrs/SketcherPrs_PositionMgr.cpp @@ -20,21 +20,23 @@ #include "SketcherPrs_PositionMgr.h" #include "SketcherPrs_Tools.h" -#include -#include -#include -#include #include #include +#include +#include +#include +#include #include +#include #include -#include -#include #include -#include +#include +#include +#include #include +#include #include #include @@ -328,8 +330,7 @@ std::list getCurves(const GeomPointPtr& thePnt, const SketcherPrs_Sym if (aDist <= Precision::Confusion()) aList.push_back(aFeature->firstResult()); } - } else if ((aFeature->getKind() == SketchPlugin_Circle::ID()) || - (aFeature->getKind() == SketchPlugin_Arc::ID())) { + } else { GeomCurvePtr aCurve; ObjectPtr aResObj; std::list aResults = aFeature->results(); @@ -343,10 +344,16 @@ std::list getCurves(const GeomPointPtr& thePnt, const SketcherPrs_Sym } } if (aCurve.get()) { - double aStart = aCurve->startParam(); - double aEnd = aCurve->endParam(); - GeomCirclePtr aCircle = GeomCirclePtr(new GeomAPI_Circ(aCurve)); - GeomPointPtr aProjPnt = aCircle->project(thePnt); + GeomPointPtr aProjPnt; + if (aFeature->getKind() == SketchPlugin_Circle::ID() || + aFeature->getKind() == SketchPlugin_Arc::ID()) { + GeomCirclePtr aCircle = GeomCirclePtr(new GeomAPI_Circ(aCurve)); + aProjPnt = aCircle->project(thePnt); + } + else if (aFeature->getKind() == SketchPlugin_Ellipse::ID()) { + GeomEllipsePtr anEllipse = GeomEllipsePtr(new GeomAPI_Ellipse(aCurve)); + aProjPnt = anEllipse->project(thePnt); + } if (aProjPnt && thePnt->distance(aProjPnt) <= Precision::Confusion()) aList.push_back(aResObj); } -- 2.30.2