]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Task 2.12. New entities: ellipses and arcs of ellipses (issue #3003)
authorazv <azv@opencascade.com>
Sun, 22 Sep 2019 09:20:50 +0000 (12:20 +0300)
committerazv <azv@opencascade.com>
Sun, 22 Sep 2019 13:14:10 +0000 (16:14 +0300)
Constraint Tangent for ellipses

18 files changed:
src/GeomAPI/GeomAPI_Circ2d.cpp
src/GeomAPI/GeomAPI_Circ2d.h
src/GeomAPI/GeomAPI_Ellipse.cpp
src/GeomAPI/GeomAPI_Ellipse.h
src/GeomAPI/GeomAPI_Ellipse2d.cpp
src/GeomAPI/GeomAPI_Ellipse2d.h
src/PartSet/PartSet_Validators.cpp
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_Validators.cpp
src/SketchPlugin/Test/TestConstraintTangentEllipse.py [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h
src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp
src/SketchSolver/SketchSolver_ConstraintMirror.cpp
src/SketchSolver/SketchSolver_ConstraintTangent.cpp
src/SketchSolver/SketchSolver_ConstraintTangent.h
src/SketcherPrs/SketcherPrs_PositionMgr.cpp

index 5204f25e5ca5def7ac0d599552ca98c73737a5fd..25c2e32f851d9b614c5fb1f9cd31a3e3f8651241 100644 (file)
@@ -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<GeomAPI_Pnt2d>& theCenter,
                                const std::shared_ptr<GeomAPI_Pnt2d>& theCirclePoint)
index 479f6edf63feb4963033899e02f46caa74d0774c..efa65b1d35230e7077372c9e62c6e3670508c406 100644 (file)
@@ -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<GeomAPI_Pnt2d>& theCenter,
index c9384724ac380a8ea90abedfbdb278e172b37652..243be630e507b5ae4ff99c22285dd24a06983e79 100644 (file)
 
 #include "GeomAPI_Ellipse.h"
 #include "GeomAPI_Ax2.h"
+#include "GeomAPI_Curve.h"
 #include "GeomAPI_Pnt.h"
 
+#include <Geom_Ellipse.hxx>
+#include <GeomAPI_ProjectPointOnCurve.hxx>
 #include <gp_Elips.hxx>
 
 #define MY_ELIPS implPtr<gp_Elips>()
@@ -35,6 +38,15 @@ GeomAPI_Ellipse::GeomAPI_Ellipse(const std::shared_ptr<GeomAPI_Ax2>& theAx2,
 {
 }
 
+GeomAPI_Ellipse::GeomAPI_Ellipse(GeomCurvePtr theCurve)
+{
+  Handle(Geom_Curve) aCurve = theCurve->impl<Handle(Geom_Curve)>();
+  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_Pnt> 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_Pnt> GeomAPI_Ellipse::project(
+    const std::shared_ptr<GeomAPI_Pnt>& thePoint) const
+{
+  std::shared_ptr<GeomAPI_Pnt> aResult;
+  if (!MY_ELIPS)
+    return aResult;
+
+  Handle(Geom_Ellipse) aEllipse = new Geom_Ellipse(*MY_ELIPS);
+
+  const gp_Pnt& aPoint = thePoint->impl<gp_Pnt>();
+
+  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;
+}
index ac63dd97ec3bfb80d8f60cef0af4512d0a53898a..dc41993a05e472d0e160352659c8797c50dec465 100644 (file)
@@ -28,6 +28,7 @@
 #include <memory>
 
 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<GeomAPI_Ax2>& theAx2,
                                  double theMajorRadius, double theMinorRadius);
 
+  GEOMAPI_EXPORT GeomAPI_Ellipse(std::shared_ptr<GeomAPI_Curve> theCurve);
+
   /// Returns center of the ellipse
   GEOMAPI_EXPORT std::shared_ptr<GeomAPI_Pnt> 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<GeomAPI_Pnt> project(
+      const std::shared_ptr<GeomAPI_Pnt>& thePoint) const;
 };
 
 //! Pointer on the object
index 20338230448d0cc057133aa0b7b643e9a6658cc9..4a4c9950dbcd2e3f39094e24c7f9df39b222e0a3 100644 (file)
 // Author:      Artem ZHIDKOV
 
 #include <GeomAPI_Ellipse2d.h>
+#include <GeomAPI_Circ2d.h>
 #include <GeomAPI_Dir2d.h>
+#include <GeomAPI_Lin2d.h>
 #include <GeomAPI_Pnt2d.h>
 
+#include <Extrema_ExtCC2d.hxx>
+#include <Extrema_ExtElC2d.hxx>
+#include <Geom2d_Ellipse.hxx>
+#include <Geom2dAdaptor_Curve.hxx>
 #include <gp_Ax22d.hxx>
 #include <gp_Elips2d.hxx>
 #include <Precision.hxx>
@@ -112,3 +118,54 @@ double GeomAPI_Ellipse2d::majorRadius() const
 {
   return MY_ELLIPSE->MajorRadius();
 }
+
+template <typename EXTREMAPTR>
+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<GeomAPI_Lin2d>& theLine,
+                                   std::shared_ptr<GeomAPI_Pnt2d>& thePointOnMe,
+                                   std::shared_ptr<GeomAPI_Pnt2d>& thePointOnLine)
+{
+  Extrema_ExtElC2d anExtema(theLine->impl<gp_Lin2d>(), *MY_ELLIPSE);
+  return extrema(&anExtema, thePointOnLine, thePointOnMe);
+}
+
+double GeomAPI_Ellipse2d::distance(const std::shared_ptr<GeomAPI_Circ2d>& theCircle,
+                                   std::shared_ptr<GeomAPI_Pnt2d>& thePointOnMe,
+                                   std::shared_ptr<GeomAPI_Pnt2d>& thePointOnCircle)
+{
+  Extrema_ExtElC2d anExtema(theCircle->impl<gp_Circ2d>(), *MY_ELLIPSE);
+  return extrema(&anExtema, thePointOnCircle, thePointOnMe);
+}
+
+double GeomAPI_Ellipse2d::distance(const std::shared_ptr<GeomAPI_Ellipse2d>& theEllipse,
+                                   std::shared_ptr<GeomAPI_Pnt2d>& thePointOnMe,
+                                   std::shared_ptr<GeomAPI_Pnt2d>& thePointOnEllipse)
+{
+  Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(theEllipse->impl<gp_Elips2d>());
+  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;
+}
index 11c11f93492a49c0e8827d3b0dbb6c0542f43c7b..bda3221e12b70840ae6d50fe71ef6259333d5919 100644 (file)
 
 #include <GeomAPI_Interface.h>
 
-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<GeomAPI_Lin2d>& theLine,
+                                 std::shared_ptr<GeomAPI_Pnt2d>& thePointOnMe,
+                                 std::shared_ptr<GeomAPI_Pnt2d>& 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<GeomAPI_Circ2d>& theCircle,
+                                 std::shared_ptr<GeomAPI_Pnt2d>& thePointOnMe,
+                                 std::shared_ptr<GeomAPI_Pnt2d>& thePointOnCircle);
+
+  /// Calculate minimal distance between two ellipses.
+  /// Return corresponding points on the ellipses.
+  GEOMAPI_EXPORT double distance(const std::shared_ptr<GeomAPI_Ellipse2d>& theEllipse,
+                                 std::shared_ptr<GeomAPI_Pnt2d>& thePointOnMe,
+                                 std::shared_ptr<GeomAPI_Pnt2d>& thePointOnEllipse);
 };
 
 #endif
index 0b5a42bd5e9613775ef46c7f02a74037bec0a1fa..f8d05fe0a6c011c731d314ee19bc06739368b871 100644 (file)
@@ -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,
index 55a9aa143553f01ae988ac3df9d5bf3a586db35e..24745e8a13713b5b1a12f22b11a3cfa6d914574b 100644 (file)
@@ -227,6 +227,7 @@ ADD_UNIT_TESTS(
   TestConstraintRadius.py
   TestConstraintRadiusFailure.py
   TestConstraintTangent.py
+  TestConstraintTangentEllipse.py
   TestConstraintVertical.py
   TestCreateArcByCenterStartEnd.py
   TestCreateArcByTangentEdge.py
index af85ea6f1da8348d20ab4f2b369a240901549500..31f2178687e7d9e3d012574aa7a1304b42ce8062 100644 (file)
@@ -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 (file)
index 0000000..04f9a18
--- /dev/null
@@ -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()
index c2893b1f8faf4a5e3c5d39f97dfef3ca2f87e59e..26419160333086bc08383a9fdfcb2aa0da910f45 100644 (file)
@@ -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
index d0914df1cd9bfa06fd57d73a9fb93dcb7f3a08ce..11c265dd0eed1751bafd9071abe41181595a0b31 100644 (file)
 #include <SketchPlugin_MultiRotation.h>
 #include <SketchPlugin_MultiTranslation.h>
 
+#include <GeomAPI_Circ2d.h>
+#include <GeomAPI_Dir2d.h>
+#include <GeomAPI_Ellipse2d.h>
+#include <GeomAPI_Lin2d.h>
+#include <GeomAPI_Pnt2d.h>
+
 #include <cmath>
 
 
@@ -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<GeomAPI_Lin2d> PlaneGCSSolver_Tools::line(FeaturePtr theFeature)
   return std::shared_ptr<GeomAPI_Lin2d>(new GeomAPI_Lin2d(aStart->pnt(), aEnd->pnt()));
 }
 
+std::shared_ptr<GeomAPI_Circ2d> PlaneGCSSolver_Tools::circle(EntityWrapperPtr theEntity)
+{
+  if (theEntity->type() != ENTITY_CIRCLE && theEntity->type() != ENTITY_ARC)
+    return std::shared_ptr<GeomAPI_Circ2d>();
+
+  std::shared_ptr<PlaneGCSSolver_EdgeWrapper> anEntity =
+    std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(theEntity);
+  std::shared_ptr<GCS::Circle> aCirc = std::dynamic_pointer_cast<GCS::Circle>(anEntity->entity());
+  return std::shared_ptr<GeomAPI_Circ2d>(
+    new GeomAPI_Circ2d(*(aCirc->center.x), *(aCirc->center.y), *(aCirc->rad)));
+}
+
+std::shared_ptr<GeomAPI_Ellipse2d> PlaneGCSSolver_Tools::ellipse(EntityWrapperPtr theEntity)
+{
+  if (theEntity->type() != ENTITY_ELLIPSE && theEntity->type() != ENTITY_ELLIPTICAL_ARC)
+    return std::shared_ptr<GeomAPI_Ellipse2d>();
+
+  std::shared_ptr<PlaneGCSSolver_EdgeWrapper> anEntity =
+    std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(theEntity);
+  std::shared_ptr<GCS::Ellipse> anEllipse =
+      std::dynamic_pointer_cast<GCS::Ellipse>(anEntity->entity());
+
+  std::shared_ptr<GeomAPI_Pnt2d> aCenter(
+      new GeomAPI_Pnt2d(*(anEllipse->center.x), *(anEllipse->center.y)));
+  std::shared_ptr<GeomAPI_Dir2d> anAxis(new GeomAPI_Dir2d(
+      *(anEllipse->focus1.x) - *(anEllipse->center.x),
+      *(anEllipse->focus1.y) - *(anEllipse->center.y)));
+
+  return std::shared_ptr<GeomAPI_Ellipse2d>(
+      new GeomAPI_Ellipse2d(aCenter, anAxis, anEllipse->getRadMaj(), *anEllipse->radmin));
+}
+
+
 
 GCS::SET_pD PlaneGCSSolver_Tools::parameters(const EntityWrapperPtr& theEntity)
 {
index 4e5b666b67268986f7f77026cfd8058f12e10a1c..1eb95be4fcb27370a0ba457e77c4bfff66e32850 100644 (file)
 #include <SketchSolver_ConstraintMovement.h>
 #include <SketchPlugin_Constraint.h>
 
-#include <GeomAPI_Lin2d.h>
-#include <GeomAPI_Pnt2d.h>
+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<GeomAPI_Lin2d> line(EntityWrapperPtr theEntity);
+  /// \brief Convert entity to circle
+  /// \return empty pointer if the entity is not a circle
+  std::shared_ptr<GeomAPI_Circ2d> circle(EntityWrapperPtr theEntity);
+  /// \brief Convert entity to ellipse
+  /// \return empty pointer if the entity is not an ellipse
+  std::shared_ptr<GeomAPI_Ellipse2d> ellipse(EntityWrapperPtr theEntity);
 
   /// \brief Convert entity to line
   /// \return empty pointer if the entity is not a line
index f1c32b733a91a101cdf1d6b4a21065d5ef33b1eb..b0520c45a0bb713705f89d5e8b905b02b3865ebd 100644 (file)
@@ -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
index cbf025ed5d6e154a7831988e6c4a7ff152dfb5ad..6be506b2591e4eff1b09c806061fb3096f7a223e 100644 (file)
@@ -23,6 +23,8 @@
 #include <PlaneGCSSolver_Tools.h>
 #include <PlaneGCSSolver_UpdateFeature.h>
 
+#include <GeomAPI_Lin2d.h>
+#include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_XY.h>
 #include <GeomDataAPI_Point2D.h>
 #include <ModelAPI_AttributeRefList.h>
index eaa0e2c41696068e8d5507a1b17dc759beccc3b8..16e6d11da200fd6d0c8acb959b7460cb417fa4b7 100644 (file)
 
 #include <PlaneGCSSolver_EdgeWrapper.h>
 #include <PlaneGCSSolver_PointWrapper.h>
+#include <PlaneGCSSolver_Storage.h>
 #include <PlaneGCSSolver_Tools.h>
 #include <PlaneGCSSolver_UpdateCoincidence.h>
 
+#include <GeomAPI_Circ2d.h>
+#include <GeomAPI_Lin2d.h>
 #include <GeomAPI_Pnt2d.h>
+#include <GeomAPI_Ellipse2d.h>
+
 #include <SketchPlugin_Arc.h>
 #include <SketchPlugin_Circle.h>
 #include <SketchPlugin_ConstraintCoincidence.h>
+#include <SketchPlugin_ConstraintMiddle.h>
 
 #include <cmath>
 
@@ -47,6 +53,9 @@ static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeatureP
 static std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1,
                                                        FeaturePtr theFeature2);
 
+/// \brief Collect points coincident with each of two features
+static std::set<AttributePtr> 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<PlaneGCSSolver_Storage> aStorage =
+      std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(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<EntityWrapperPtr>::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<GCSConstraintPtr> 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<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
-    isRebuild = true;
-    std::set<AttributePtr>::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<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
+      isRebuild = true;
+      std::set<AttributePtr>::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<AttributePtr> aCoincidentPoints = coincidentPoints(aTgFeat1, aTgFeat2);
+      isRebuild = (bool)(myAuxPoint.get()) == (!aCoincidentPoints.empty());
+    }
   }
 
   if (isRebuild)
@@ -285,6 +347,55 @@ std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeatureP
   return aCoincidentPoints;
 }
 
+static std::set<AttributePtr> refsToFeatureAndResults(FeaturePtr theFeature)
+{
+  std::set<AttributePtr> aRefs = theFeature->data()->refsToMe();
+  const std::list<ResultPtr>& aResults = theFeature->results();
+  for (std::list<ResultPtr>::const_iterator anIt = aResults.begin();
+      anIt != aResults.end(); ++anIt) {
+    const std::set<AttributePtr>& aResRefs = (*anIt)->data()->refsToMe();
+    aRefs.insert(aResRefs.begin(), aResRefs.end());
+  }
+  return aRefs;
+}
+
+// collect all points coincident with the feature
+static std::set<AttributePtr> pointsOnFeature(FeaturePtr theFeature)
+{
+  std::set<AttributePtr> aPoints;
+
+  std::set<AttributePtr> aRefs = refsToFeatureAndResults(theFeature);
+  for (std::set<AttributePtr>::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<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
+{
+  std::set<AttributePtr> aPointsOnF1 = pointsOnFeature(theFeature1);
+  std::set<AttributePtr> aPointsOnF2 = pointsOnFeature(theFeature2);
+
+  std::set<AttributePtr> aCommonPoints;
+  for (std::set<AttributePtr>::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<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
@@ -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<GCS::Circle> aCirc1 =
-    std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity1)->entity());
-  std::shared_ptr<GCS::Circle> aCirc2 =
-    std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity2)->entity());
+  GCSCurvePtr aCurve1 =
+    std::dynamic_pointer_cast<GCS::Curve>(GCS_EDGE_WRAPPER(theEntity1)->entity());
+  GCSCurvePtr aCurve2 =
+    std::dynamic_pointer_cast<GCS::Curve>(GCS_EDGE_WRAPPER(theEntity2)->entity());
 
   GCSConstraintPtr aNewConstr;
   if (theSharedPoint) {
     GCSPointPtr aPoint =
         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(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<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(aCurve1);
+    std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(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<GeomAPI_Ellipse2d> 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<GeomAPI_Lin2d> aLine = PlaneGCSSolver_Tools::line(aCurve2);
+    anEllipse->distance(aLine, aP1, aP2);
+  }
+  else if (aCurve2->type() == ENTITY_ARC || aCurve2->type() == ENTITY_CIRCLE) {
+    std::shared_ptr<GeomAPI_Circ2d> aCircle = PlaneGCSSolver_Tools::circle(aCurve2);
+    anEllipse->distance(aCircle, aP1, aP2);
+  }
+  else if (aCurve2->type() == ENTITY_ELLIPSE || aCurve2->type() == ENTITY_ELLIPTICAL_ARC) {
+    std::shared_ptr<GeomAPI_Ellipse2d> 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());
+  }
 }
index 6af491ec770e5452bb4550a2da9a729b6685d33d..57424b293492e0284afa606dc63c178c4499d1c7 100644 (file)
@@ -55,6 +55,7 @@ private:
   bool isArcArcInternal;
   double myCurveCurveAngle;
   AttributePtr mySharedPoint;
+  EntityWrapperPtr myAuxPoint;
 };
 
 #endif
index d803256d8baab56e89e6fe17ab1511f25f40cf58..7aacd2002093c332c992a444deaac12f75898e03 100644 (file)
 #include "SketcherPrs_PositionMgr.h"
 #include "SketcherPrs_Tools.h"
 
-#include <GeomAPI_Edge.h>
-#include <GeomAPI_Curve.h>
-#include <GeomAPI_Vertex.h>
-#include <GeomAPI_Dir.h>
 #include <GeomAPI_Ax3.h>
 #include <GeomAPI_Circ.h>
+#include <GeomAPI_Curve.h>
+#include <GeomAPI_Edge.h>
+#include <GeomAPI_Ellipse.h>
+#include <GeomAPI_Dir.h>
 #include <GeomAPI_Lin2d.h>
+#include <GeomAPI_Vertex.h>
 
 #include <GeomDataAPI_Point2D.h>
 
-#include <SketchPlugin_Line.h>
-#include <SketchPlugin_Circle.h>
 #include <SketchPlugin_Arc.h>
-#include <SketchPlugin_ConstraintTangent.h>
+#include <SketchPlugin_Circle.h>
+#include <SketchPlugin_Ellipse.h>
+#include <SketchPlugin_Line.h>
 #include <SketchPlugin_ConstraintPerpendicular.h>
+#include <SketchPlugin_ConstraintTangent.h>
 
 #include <TopoDS_Vertex.hxx>
 #include <Geom_Curve.hxx>
@@ -328,8 +330,7 @@ std::list<ObjectPtr> 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<ResultPtr> aResults = aFeature->results();
@@ -343,10 +344,16 @@ std::list<ObjectPtr> 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);
       }