Salome HOME
Task 2.12. New entities: ellipses and arcs of ellipses (issue #3003)
authorazv <azv@opencascade.com>
Mon, 16 Sep 2019 11:33:33 +0000 (14:33 +0300)
committerazv <azv@opencascade.com>
Tue, 17 Sep 2019 13:06:14 +0000 (16:06 +0300)
* Dump auxiliary objects as features produces by the ellipse.
* Unit tests for ellipse creation and movement.

17 files changed:
src/SketchAPI/SketchAPI.i
src/SketchAPI/SketchAPI_Ellipse.cpp
src/SketchAPI/SketchAPI_Ellipse.h
src/SketchAPI/SketchAPI_Sketch.cpp
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_Ellipse.cpp
src/SketchPlugin/SketchPlugin_MacroEllipse.cpp
src/SketchPlugin/Test/TestCreateEllipseByCenterSemiaxisAndPassed.py [new file with mode: 0644]
src/SketchPlugin/Test/TestCreateEllipseByExternal.py [new file with mode: 0644]
src/SketchPlugin/Test/TestCreateEllipseByMajorAxisAndPassed.py [new file with mode: 0644]
src/SketchPlugin/Test/TestMoveEllipse.py [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Solver.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp
src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp
src/SketcherPrs/SketcherPrs_LengthDimension.cpp

index 9cde425da08595ca54d99e3ee6f6e7cfe8d59889..8cbda8d3b7d723547cfd048ca9b4f6943ca4c605 100644 (file)
@@ -44,6 +44,9 @@
 %include "std_pair.i"
 %include "std_shared_ptr.i"
 
+// function with named parameters
+%feature("kwargs") SketchAPI_Ellipse::construction;
+
 // shared pointers
 %shared_ptr(SketchAPI_Arc)
 %shared_ptr(SketchAPI_MacroArc)
index b601e6612cce4acd37e35d9d0230a9f383fba282..7b005014e25f7dd5e027338c9dc18f9b3e33ed3e 100644 (file)
 #include <ModelHighAPI_Selection.h>
 #include <ModelHighAPI_Tools.h>
 
+#include <SketchPlugin_ConstraintCoincidenceInternal.h>
+#include <SketchPlugin_Line.h>
+#include <SketchPlugin_Point.h>
+
+static const std::string AUXILIARY_VALUE = "aux";
+static const std::string MAJOR_AXIS_ID = "majorAxis";
+static const std::string MINOR_AXIS_ID = "minorAxis";
+
 SketchAPI_Ellipse::SketchAPI_Ellipse(const std::shared_ptr<ModelAPI_Feature> & theFeature)
   : SketchAPI_SketchEntity(theFeature)
 {
@@ -149,6 +157,168 @@ ModelHighAPI_Selection SketchAPI_Ellipse::minorAxis() const
   return ModelHighAPI_Selection();
 }
 
+static CompositeFeaturePtr sketchForFeature(FeaturePtr theFeature)
+{
+  const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
+  for (std::set<AttributePtr>::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt)
+    if ((*anIt)->id() == SketchPlugin_Sketch::FEATURES_ID())
+      return std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
+  return CompositeFeaturePtr();
+}
+
+static void createInternalConstraint(const CompositeFeaturePtr& theSketch,
+                                     const AttributePoint2DPtr& thePoint1,
+                                     const AttributePoint2DPtr& thePoint2)
+{
+  FeaturePtr aConstraint = theSketch->addFeature(SketchPlugin_ConstraintCoincidenceInternal::ID());
+  aConstraint->refattr(SketchPlugin_Constraint::ENTITY_A())->setAttr(thePoint1);
+  aConstraint->refattr(SketchPlugin_Constraint::ENTITY_B())->setAttr(thePoint2);
+  aConstraint->execute();
+}
+
+static FeaturePtr createPoint(const CompositeFeaturePtr& theSketch,
+                              const AttributePtr& theCoincident,
+                              const std::string& theAuxOrName)
+{
+  AttributePoint2DPtr anElPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theCoincident);
+
+  FeaturePtr aPointFeature = theSketch->addFeature(SketchPlugin_Point::ID());
+  AttributePoint2DPtr aCoord = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aPointFeature->attribute(SketchPlugin_Point::COORD_ID()));
+  aCoord->setValue(anElPoint->x(), anElPoint->y());
+  aPointFeature->execute();
+
+  if (theAuxOrName == AUXILIARY_VALUE)
+    aPointFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(true);
+  else if (!theAuxOrName.empty()) {
+    aPointFeature->data()->setName(theAuxOrName);
+    aPointFeature->lastResult()->data()->setName(theAuxOrName);
+  }
+
+  createInternalConstraint(theSketch, anElPoint, aCoord);
+
+  return aPointFeature;
+}
+
+static FeaturePtr createAxis(const CompositeFeaturePtr& theSketch,
+                             const AttributePtr& theCoincidentStart,
+                             const AttributePtr& theCoincidentEnd,
+                             const std::string& theAuxOrName)
+{
+  AttributePoint2DPtr aStartPoint =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theCoincidentStart);
+  AttributePoint2DPtr aEndPoint =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theCoincidentEnd);
+
+  FeaturePtr aLineFeature = theSketch->addFeature(SketchPlugin_Line::ID());
+  AttributePoint2DPtr aLineStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aLineFeature->attribute(SketchPlugin_Line::START_ID()));
+  aLineStart->setValue(aStartPoint->x(), aStartPoint->y());
+  AttributePoint2DPtr aLineEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aLineFeature->attribute(SketchPlugin_Line::END_ID()));
+  aLineEnd->setValue(aEndPoint->x(), aEndPoint->y());
+  aLineFeature->execute();
+
+  if (theAuxOrName == AUXILIARY_VALUE)
+    aLineFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(true);
+  else if (!theAuxOrName.empty()) {
+    aLineFeature->data()->setName(theAuxOrName);
+    aLineFeature->lastResult()->data()->setName(theAuxOrName);
+  }
+
+  createInternalConstraint(theSketch, aStartPoint, aLineStart);
+  createInternalConstraint(theSketch, aEndPoint, aLineEnd);
+
+  return aLineFeature;
+}
+
+std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_Ellipse::construction(
+    const std::string& center,
+    const std::string& firstFocus,
+    const std::string& secondFocus,
+    const std::string& majorAxisStart,
+    const std::string& majorAxisEnd,
+    const std::string& minorAxisStart,
+    const std::string& minorAxisEnd,
+    const std::string& majorAxis,
+    const std::string& minorAxis) const
+{
+  FeaturePtr anEllipse = feature();
+  CompositeFeaturePtr aSketch = sketchForFeature(anEllipse);
+
+  std::list<FeaturePtr> anEntities;
+  if (!center.empty()) {
+    AttributePtr aCenterAttr = anEllipse->attribute(SketchPlugin_Ellipse::CENTER_ID());
+    anEntities.push_back(createPoint(aSketch, aCenterAttr, center));
+  }
+  if (!firstFocus.empty()) {
+    AttributePtr aFocusAttr = anEllipse->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID());
+    anEntities.push_back(createPoint(aSketch, aFocusAttr, firstFocus));
+  }
+  if (!secondFocus.empty()) {
+    AttributePtr aFocusAttr = anEllipse->attribute(SketchPlugin_Ellipse::SECOND_FOCUS_ID());
+    anEntities.push_back(createPoint(aSketch, aFocusAttr, secondFocus));
+  }
+  if (!majorAxisStart.empty()) {
+    AttributePtr aStartAttr = anEllipse->attribute(SketchPlugin_Ellipse::MAJOR_AXIS_START_ID());
+    anEntities.push_back(createPoint(aSketch, aStartAttr, majorAxisStart));
+  }
+  if (!majorAxisEnd.empty()) {
+    AttributePtr aEndAttr = anEllipse->attribute(SketchPlugin_Ellipse::MAJOR_AXIS_END_ID());
+    anEntities.push_back(createPoint(aSketch, aEndAttr, majorAxisEnd));
+  }
+  if (!minorAxisStart.empty()) {
+    AttributePtr aStartAttr = anEllipse->attribute(SketchPlugin_Ellipse::MINOR_AXIS_START_ID());
+    anEntities.push_back(createPoint(aSketch, aStartAttr, minorAxisStart));
+  }
+  if (!minorAxisEnd.empty()) {
+    AttributePtr aEndAttr = anEllipse->attribute(SketchPlugin_Ellipse::MINOR_AXIS_END_ID());
+    anEntities.push_back(createPoint(aSketch, aEndAttr, minorAxisEnd));
+  }
+  if (!majorAxis.empty()) {
+    AttributePtr aStartAttr = anEllipse->attribute(SketchPlugin_Ellipse::MAJOR_AXIS_START_ID());
+    AttributePtr aEndAttr = anEllipse->attribute(SketchPlugin_Ellipse::MAJOR_AXIS_END_ID());
+    anEntities.push_back(createAxis(aSketch, aStartAttr, aEndAttr, majorAxis));
+  }
+  if (!minorAxis.empty()) {
+    AttributePtr aStartAttr = anEllipse->attribute(SketchPlugin_Ellipse::MINOR_AXIS_START_ID());
+    AttributePtr aEndAttr = anEllipse->attribute(SketchPlugin_Ellipse::MINOR_AXIS_END_ID());
+    anEntities.push_back(createAxis(aSketch, aStartAttr, aEndAttr, minorAxis));
+  }
+
+  return SketchAPI_SketchEntity::wrap(anEntities);
+}
+
+static void ellipseAttributeAndAuxiliaryFeature(
+    const FeaturePtr& theInternalConstraint,
+    std::map<std::string, FeaturePtr>& theAttrToFeature)
+{
+  AttributeRefAttrPtr aRefAttrA =
+      theInternalConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
+  AttributeRefAttrPtr aRefAttrB =
+      theInternalConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
+  // the first point is usually an ellipse attribute
+  // and the second point is an attribute of the auxiliary feature
+  ObjectPtr anAuxObject;
+  if (aRefAttrB->isObject())
+    anAuxObject = aRefAttrB->object();
+  else
+    anAuxObject = aRefAttrB->attr()->owner();
+
+  FeaturePtr anAuxFeature = ModelAPI_Feature::feature(anAuxObject);
+  if (anAuxFeature->getKind() == SketchPlugin_Point::ID())
+    theAttrToFeature[aRefAttrA->attr()->id()] = anAuxFeature;
+  else {
+    const std::string& anAttrID = aRefAttrA->attr()->id();
+    if (anAttrID == SketchPlugin_Ellipse::MAJOR_AXIS_START_ID() ||
+        anAttrID == SketchPlugin_Ellipse::MAJOR_AXIS_END_ID())
+      theAttrToFeature[MAJOR_AXIS_ID] = anAuxFeature;
+    else if (anAttrID == SketchPlugin_Ellipse::MINOR_AXIS_START_ID() ||
+             anAttrID == SketchPlugin_Ellipse::MINOR_AXIS_END_ID())
+      theAttrToFeature[MINOR_AXIS_ID] = anAuxFeature;
+  }
+}
+
 void SketchAPI_Ellipse::dump(ModelHighAPI_Dumper& theDumper) const
 {
   if (isCopy())
@@ -168,4 +338,61 @@ void SketchAPI_Ellipse::dump(ModelHighAPI_Dumper& theDumper) const
   }
   // dump "auxiliary" flag if necessary
   SketchAPI_SketchEntity::dump(theDumper);
+
+  // dump auxiliary features produced by ellipse
+  std::map<std::string, FeaturePtr> anAuxFeatures;
+  const std::set<AttributePtr>& aRefs = aBase->data()->refsToMe();
+  for (std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
+       aRefIt != aRefs.end(); ++aRefIt) {
+    FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
+    if (anOwner->getKind() != SketchPlugin_ConstraintCoincidenceInternal::ID())
+      continue; // process internal constraints only
+    ellipseAttributeAndAuxiliaryFeature(anOwner, anAuxFeatures);
+  }
+  if (!anAuxFeatures.empty()) {
+    typedef std::pair<std::string, std::string> PairOfStrings;
+    static PairOfStrings anAttributes[] = {
+        PairOfStrings(SketchPlugin_Ellipse::CENTER_ID(), "center"),
+        PairOfStrings(SketchPlugin_Ellipse::FIRST_FOCUS_ID(), "firstFocus"),
+        PairOfStrings(SketchPlugin_Ellipse::SECOND_FOCUS_ID(), "secondFocus"),
+        PairOfStrings(SketchPlugin_Ellipse::MAJOR_AXIS_START_ID(), "majorAxisStart"),
+        PairOfStrings(SketchPlugin_Ellipse::MAJOR_AXIS_END_ID(), "majorAxisEnd"),
+        PairOfStrings(SketchPlugin_Ellipse::MINOR_AXIS_START_ID(), "minorAxisStart"),
+        PairOfStrings(SketchPlugin_Ellipse::MINOR_AXIS_END_ID(), "minorAxisEnd"),
+        PairOfStrings(MAJOR_AXIS_ID, MAJOR_AXIS_ID),
+        PairOfStrings(MINOR_AXIS_ID, MINOR_AXIS_ID)
+    };
+
+    theDumper << "[";
+    bool isFirst = true;
+    for (PairOfStrings* anIt = std::begin(anAttributes);
+         anIt != std::end(anAttributes); ++anIt) {
+      std::map<std::string, FeaturePtr>::iterator aFound = anAuxFeatures.find(anIt->first);
+      if (aFound == anAuxFeatures.end())
+        continue;
+      if (!isFirst)
+        theDumper << ", ";
+      theDumper << theDumper.name(aFound->second, false);
+      theDumper.doNotDumpFeature(aFound->second);
+      isFirst = false;
+    }
+    theDumper << "] = " << theDumper.name(aBase) << ".construction(";
+    isFirst = true;
+    for (PairOfStrings* anIt = std::begin(anAttributes);
+         anIt != std::end(anAttributes); ++anIt) {
+      std::map<std::string, FeaturePtr>::iterator aFound = anAuxFeatures.find(anIt->first);
+      if (aFound == anAuxFeatures.end())
+        continue;
+      if (!isFirst)
+        theDumper << ", ";
+      isFirst = false;
+      theDumper << anIt->second << " = \"";
+      if (aFound->second->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
+        theDumper << AUXILIARY_VALUE;
+      else
+        theDumper << aFound->second->name();
+      theDumper << "\"";
+    }
+    theDumper << ")" << std::endl;
+  }
 }
index 13e344369dcb6b1f29da9a5825636fb5ce13d210..ee79357c866912ef1fce34db7b0023167a452935 100644 (file)
@@ -135,6 +135,22 @@ public:
   SKETCHAPI_EXPORT
   ModelHighAPI_Selection minorAxis() const;
 
+  /// Create construction elements (focuses, axes etc.).
+  /// Empty value for each parameter shows that the corresponding feature has been removed.
+  /// Value "aux" marks this feature as auxiliary.
+  /// And the name of the feature shows that it is a regular feature.
+  SKETCHAPI_EXPORT
+  std::list<std::shared_ptr<SketchAPI_SketchEntity> > construction(
+      const std::string& center = std::string(),
+      const std::string& firstFocus = std::string(),
+      const std::string& secondFocus = std::string(),
+      const std::string& majorAxisStart = std::string(),
+      const std::string& majorAxisEnd = std::string(),
+      const std::string& minorAxisStart = std::string(),
+      const std::string& minorAxisEnd = std::string(),
+      const std::string& majorAxis = std::string(),
+      const std::string& minorAxis = std::string()) const;
+
   /// Dump wrapped feature
   SKETCHAPI_EXPORT
   virtual void dump(ModelHighAPI_Dumper& theDumper) const;
index 4f8b129563b265228e19941ac8b96375971d681f..5a329558fd3e2217782f7aec38c9a864297895c3 100644 (file)
@@ -598,7 +598,7 @@ std::shared_ptr<SketchAPI_MacroEllipse> SketchAPI_Sketch::addEllipse(
   std::shared_ptr<ModelAPI_Feature> aFeature =
       compositeFeature()->addFeature(SketchPlugin_MacroEllipse::ID());
   return MacroEllipsePtr(new SketchAPI_MacroEllipse(aFeature,
-      thePoint1X, thePoint1Y, thePoint2X, thePoint1Y, thePassedX, thePassedY, isPoint1Center));
+      thePoint1X, thePoint1Y, thePoint2X, thePoint2Y, thePassedX, thePassedY, isPoint1Center));
 }
 
 std::shared_ptr<SketchAPI_MacroEllipse> SketchAPI_Sketch::addEllipse(
@@ -1093,6 +1093,13 @@ static std::shared_ptr<GeomAPI_Pnt2d> middlePointOnArc(const FeaturePtr& theFeat
   return std::shared_ptr<GeomAPI_Pnt2d>(new GeomAPI_Pnt2d(x, y));
 }
 
+static std::shared_ptr<GeomAPI_Pnt2d> pointOnEllipse(const FeaturePtr& theFeature)
+{
+  AttributePoint2DPtr aMajorAxisEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Ellipse::MAJOR_AXIS_END_ID()));
+  return aMajorAxisEnd ? aMajorAxisEnd->pnt() : std::shared_ptr<GeomAPI_Pnt2d>();
+}
+
 static std::shared_ptr<GeomAPI_Pnt2d> middlePoint(const ObjectPtr& theObject)
 {
   std::shared_ptr<GeomAPI_Pnt2d> aMiddlePoint;
@@ -1108,6 +1115,8 @@ static std::shared_ptr<GeomAPI_Pnt2d> middlePoint(const ObjectPtr& theObject)
       aMiddlePoint = pointOnCircle(aFeature);
     else if (aFeatureKind == SketchPlugin_Arc::ID())
       aMiddlePoint = middlePointOnArc(aFeature);
+    else if (aFeatureKind == SketchPlugin_Ellipse::ID())
+      aMiddlePoint = pointOnEllipse(aFeature);
   }
   return aMiddlePoint;
 }
index b304b4aad7d7ce7f7871156ad17e30c9c19357e7..80b68f571b84ce5344145f19247a4d3133f4fea6 100644 (file)
@@ -230,6 +230,9 @@ ADD_UNIT_TESTS(
   TestCreateCircleByCenterAndPassed.py
   TestCreateCircleByThreePoints.py
   TestCreateCircleChangeType.py
+  TestCreateEllipseByCenterSemiaxisAndPassed.py
+  TestCreateEllipseByMajorAxisAndPassed.py
+  TestCreateEllipseByExternal.py
   TestDegeneratedGeometry.py
   TestDistanceDump.py
   TestDistanceSignedVsUnsigned01.py
@@ -294,6 +297,7 @@ if(${SKETCHER_CHANGE_RADIUS_WHEN_MOVE})
   ADD_UNIT_TESTS(
     TestMoveArc.py
     TestMoveCircle.py
+    TestMoveEllipse.py
     TestMoveLine.py
     TestMovementComplex.py
     TestMovePoint.py
index af4f1bb4a300cc7e8c66e491c4ab7c8f6f3e9425..098951a853abb74c05e7fee006e6a6e83b5b1b8f 100644 (file)
@@ -77,11 +77,8 @@ void SketchPlugin_Ellipse::execute()
   // Calculate all characteristics of the ellipse.
   fillCharacteristicPoints();
 
-  // Make a visible center of the ellipse.
-  int aResultIndex = 0;
-  SketchPlugin_Sketch::createPoint2DResult(this, aSketch, CENTER_ID(), aResultIndex++);
   // Make a visible ellipse.
-  createEllipse(aSketch, aResultIndex);
+  createEllipse(aSketch, 0);
 }
 
 bool SketchPlugin_Ellipse::isFixed() {
index 2effeb197a8835334e22abcff9225bc0d75ab7fe..fc6e034535bc92a5f12850dfb33a881dd133c61c 100644 (file)
@@ -244,9 +244,12 @@ void SketchPlugin_MacroEllipse::constraintsForEllipseByCenterAxisAndPassed(
       this, SECOND_POINT_REF_ID(),
       theEllipseFeature->attribute(SketchPlugin_Ellipse::MAJOR_AXIS_END_ID()),
       ObjectPtr(), isTangencyApplicable);
-  SketchPlugin_Tools::createCoincidenceOrTangency(
-      this, PASSED_POINT_REF_ID(), AttributePtr(),
-      theEllipseFeature->lastResult(), isTangencyApplicable);
+  // make coincidence only if PASSED_POINT_REF_ID() refers a point but not an object
+  if (!refattr(PASSED_POINT_REF_ID())->isObject()) {
+    SketchPlugin_Tools::createCoincidenceOrTangency(
+        this, PASSED_POINT_REF_ID(), AttributePtr(),
+        theEllipseFeature->lastResult(), isTangencyApplicable);
+  }
 }
 
 void SketchPlugin_MacroEllipse::constraintsForEllipseByMajoxAxisAndPassed(
@@ -263,9 +266,12 @@ void SketchPlugin_MacroEllipse::constraintsForEllipseByMajoxAxisAndPassed(
       this, SECOND_POINT_REF_ID(),
       theEllipseFeature->attribute(SketchPlugin_Ellipse::MAJOR_AXIS_END_ID()),
       ObjectPtr(), isTangencyApplicable);
-  SketchPlugin_Tools::createCoincidenceOrTangency(
-      this, PASSED_POINT_REF_ID(), AttributePtr(),
-      theEllipseFeature->lastResult(), isTangencyApplicable);
+  // make coincidence only if PASSED_POINT_REF_ID() refers a point but not an object
+  if (!refattr(PASSED_POINT_REF_ID())->isObject()) {
+    SketchPlugin_Tools::createCoincidenceOrTangency(
+        this, PASSED_POINT_REF_ID(), AttributePtr(),
+        theEllipseFeature->lastResult(), isTangencyApplicable);
+  }
 }
 
 FeaturePtr SketchPlugin_MacroEllipse::createEllipseFeature()
@@ -289,6 +295,7 @@ FeaturePtr SketchPlugin_MacroEllipse::createEllipseFeature()
   aEllipseFeature->execute();
 
   // create auxiliary points
+  createAuxiliaryPoint(aEllipseFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()));
   createAuxiliaryPoint(aEllipseFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()));
   createAuxiliaryPoint(aEllipseFeature->attribute(SketchPlugin_Ellipse::SECOND_FOCUS_ID()));
   createAuxiliaryPoint(aEllipseFeature->attribute(SketchPlugin_Ellipse::MAJOR_AXIS_START_ID()));
diff --git a/src/SketchPlugin/Test/TestCreateEllipseByCenterSemiaxisAndPassed.py b/src/SketchPlugin/Test/TestCreateEllipseByCenterSemiaxisAndPassed.py
new file mode 100644 (file)
index 0000000..f03f43d
--- /dev/null
@@ -0,0 +1,347 @@
+# 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 creation of ellipse by center, semi-axis and passed point
+"""
+
+import unittest
+from salome.shaper import model
+
+from GeomAPI import *
+from SketchAPI import *
+
+__updated__ = "2019-09-09"
+
+class TestEllipse(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myCenter = GeomAPI_Pnt2d(50., 50.)
+    self.myFocus = GeomAPI_Pnt2d(70., 50.)
+    self.myPassedPoint = GeomAPI_Pnt2d(60., 60.)
+    self.myMinorRadius = 20.
+    self.myDOF = 0
+
+  def tearDown(self):
+    self.checkDOF()
+    model.end()
+
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+  def checkPointsEqual(self, thePoint1, thePoint2):
+    self.assertAlmostEqual(thePoint1.x(), thePoint2.x(), 5)
+    self.assertAlmostEqual(thePoint1.y(), thePoint2.y(), 5)
+
+  def checkPointOnLine(self, theCoordinates, theLine):
+    point = GeomAPI_Pnt2d(theCoordinates.x(), theCoordinates.y())
+    dist = model.distancePointLine(point, theLine)
+    self.assertAlmostEqual(dist, 0, 7)
+
+  def checkPointOnCircle(self, theCoordinates, theCircle):
+    point = GeomAPI_Pnt2d(theCoordinates.x(), theCoordinates.y())
+    dist = model.distancePointPoint(point, theCircle.center())
+    self.assertAlmostEqual(dist , theCircle.radius().value(), 7)
+
+  def checkPointOnEllipse(self, theCoordinates, theEllipse):
+    point = GeomAPI_Pnt2d(theCoordinates.x(), theCoordinates.y())
+    firstFocus2d = GeomAPI_Pnt2d(theEllipse.firstFocus().x(), theEllipse.firstFocus().y())
+    distPF1 = model.distancePointPoint(firstFocus2d,  point)
+    secondFocus2d = GeomAPI_Pnt2d(theEllipse.secondFocus().x(), theEllipse.secondFocus().y())
+    distPF2 = model.distancePointPoint(secondFocus2d,  point)
+    if issubclass(type(theEllipse), SketchAPI_Ellipse):
+      majorRad = theEllipse.majorRadius().value()
+    else:
+      majorRad = theEllipse.majorRadius()
+    self.assertAlmostEqual(distPF1 + distPF2, 2.0 * majorRad, 7)
+
+
+  def test_ellipse_by_center_and_focus(self):
+    """ Test 1. Create ellipse by coordinates of center, focus and minor radius
+    """
+    self.myEllipse1 = self.mySketch.addEllipse(self.myCenter.x(), self.myCenter.y(), self.myFocus.x(), self.myFocus.y(), self.myMinorRadius)
+    self.myDOF += 5
+
+    self.myEllipse2 = self.mySketch.addEllipse(self.myCenter, self.myFocus, self.myMinorRadius)
+    self.myDOF += 5
+    model.do()
+
+    # check both ellipses are equal
+    anEllipse1 = self.myEllipse1.defaultResult().shape().edge().ellipse()
+    anEllipse2 = self.myEllipse2.defaultResult().shape().edge().ellipse()
+    self.checkPointsEqual(anEllipse1.center(), anEllipse2.center())
+    self.checkPointsEqual(anEllipse1.firstFocus(), anEllipse2.firstFocus())
+    self.checkPointsEqual(anEllipse1.secondFocus(), anEllipse2.secondFocus())
+    self.assertAlmostEqual(anEllipse1.minorRadius(), anEllipse2.minorRadius())
+    self.assertAlmostEqual(anEllipse1.majorRadius(), anEllipse2.majorRadius())
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 0)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 0)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 2)
+
+  def test_ellipse_by_semiaxis_and_passed(self):
+    """ Test 2. Create ellipse by coordinates of center, major semi-axis point and a passed point on ellipse
+    """
+    self.myEllipse1 = self.mySketch.addEllipse(self.myCenter.x(), self.myCenter.y(), self.myFocus.x(), self.myFocus.y(), self.myPassedPoint.x(), self.myPassedPoint.y(), True)
+    self.myDOF += 5
+    model.do()
+    anEllipseFeature1 = model.lastSubFeature(self.mySketch, "SketchEllipse")
+
+    self.myEllipse2 = self.mySketch.addEllipse(self.myCenter, self.myFocus, self.myPassedPoint, True)
+    self.myDOF += 5
+    model.do()
+    anEllipseFeature2 = model.lastSubFeature(self.mySketch, "SketchEllipse")
+
+    # check both ellipses are equal
+    anEllipse1 = anEllipseFeature1.lastResult().shape().edge().ellipse()
+    anEllipse2 = anEllipseFeature2.lastResult().shape().edge().ellipse()
+    self.checkPointsEqual(anEllipse1.center(), anEllipse2.center())
+    self.checkPointsEqual(anEllipse1.firstFocus(), anEllipse2.firstFocus())
+    self.checkPointsEqual(anEllipse1.secondFocus(), anEllipse2.secondFocus())
+    self.assertAlmostEqual(anEllipse1.minorRadius(), anEllipse2.minorRadius())
+    self.assertAlmostEqual(anEllipse1.majorRadius(), anEllipse2.majorRadius())
+    # check passed point on ellipse
+    self.checkPointOnEllipse(self.myPassedPoint, anEllipse1)
+    self.checkPointOnEllipse(self.myPassedPoint, anEllipse2)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 14)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 4)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 2)
+
+  def test_ellipse_with_fixed_center(self):
+    """ Test 3. Create ellipse which center is coincident with another point
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(aLine.endPoint(), self.myFocus, self.myPassedPoint, True)
+    self.myDOF += 3
+    model.do()
+    # check ellipse
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    self.checkPointsEqual(anEllipse.center(), aLine.endPoint())
+    self.checkPointOnEllipse(self.myPassedPoint, anEllipse)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_center_on_line(self):
+    """ Test 4. Create ellipse which center is coincident with a line
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse([self.myCenter, aLine.result()], self.myFocus, self.myPassedPoint, True)
+    self.myDOF += 4
+    model.do()
+
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    # check center on line
+    self.checkPointOnLine(anEllipse.center(), aLine)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_center_on_circle(self):
+    """ Test 5. Create ellipse which center is coincident with a circle
+    """
+    aCircle = self.mySketch.addCircle(10, 10, 20)
+    self.myDOF += 3
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse([self.myCenter, aCircle.defaultResult()], self.myFocus, self.myPassedPoint, True)
+    self.myDOF += 4
+    model.do()
+
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    # check center on circle
+    self.checkPointOnCircle(anEllipse.center(), aCircle)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchCircle", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_center_on_ellipse(self):
+    """ Test 6. Create ellipse which center is coincident with another ellipse
+    """
+    anOtherEllipse = self.mySketch.addEllipse(10, 10, 30, 20, 10)
+    self.myDOF += 5
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse([self.myCenter, anOtherEllipse.defaultResult()], self.myFocus, self.myPassedPoint, True)
+    self.myDOF += 4
+    model.do()
+
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    # check center on ellipse
+    self.checkPointOnEllipse(anEllipse.center(), anOtherEllipse)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_fixed_axis(self):
+    """ Test 7. Create ellipse which point on major semi-axis is coincident with another point
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myCenter, aLine.endPoint(), self.myPassedPoint, True)
+    self.myDOF += 3
+    model.do()
+    # check ellipse
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    self.checkPointOnEllipse(self.myPassedPoint, anEllipse)
+    self.checkPointOnEllipse(aLine.endPoint(), anEllipse)
+    # check distance is equal to major semi-axis
+    dist = model.distancePointPoint(GeomAPI_Pnt2d(anEllipse.center().x(), anEllipse.center().y()), aLine.endPoint())
+    self.assertAlmostEqual(dist, anEllipse.majorRadius(), 7)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_axis_on_line(self):
+    """ Test 8. Create ellipse which point on major semi-axis is coincident with a line
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myCenter, [self.myFocus, aLine.result()], self.myPassedPoint, True)
+    self.myDOF += 4
+    model.do()
+
+    anEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse"))
+    # check axis point on line
+    self.checkPointOnLine(anEllipse.majorAxisPositive(), aLine)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_axis_on_circle(self):
+    """ Test 9. Create ellipse which point on major semi-axis is coincident with a circle
+    """
+    aCircle = self.mySketch.addCircle(10, 10, 20)
+    self.myDOF += 3
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myCenter, [self.myFocus, aCircle.defaultResult()], self.myPassedPoint, True)
+    self.myDOF += 4
+    model.do()
+
+    anEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse"))
+    # check center on circle
+    self.checkPointOnCircle(anEllipse.majorAxisPositive(), aCircle)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchCircle", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_axis_on_ellipse(self):
+    """ Test 10. Create ellipse which point on major semi-axis is coincident with another ellipse
+    """
+    anOtherEllipse = self.mySketch.addEllipse(10, 10, 90, 40, 30)
+    self.myDOF += 5
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myCenter, [self.myFocus, anOtherEllipse.defaultResult()], self.myPassedPoint, True)
+    self.myDOF += 4
+    model.do()
+
+    anEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse"))
+    # check center on ellipse
+    self.checkPointOnEllipse(anEllipse.majorAxisPositive(), anOtherEllipse)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_fixed_passed_point(self):
+    """ Test 11. Create ellipse which passed point is coincident with another point
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myCenter, self.myFocus, aLine.endPoint(), True)
+    self.myDOF += 4
+    model.do()
+    # check ellipse
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    self.checkPointOnEllipse(aLine.endPoint(), anEllipse)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_passed_point_on_line(self):
+    """ Test 12. Create ellipse which passed point is placed on a line.
+                 Check no constraints is applied.
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myCenter, self.myFocus, [self.myPassedPoint, aLine.result()], True)
+    self.myDOF += 5
+    model.do()
+
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    self.checkPointOnEllipse(self.myPassedPoint, anEllipse)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    # check neither coincidence nor tangent feature exists
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 0)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintTangent", 0)
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
diff --git a/src/SketchPlugin/Test/TestCreateEllipseByExternal.py b/src/SketchPlugin/Test/TestCreateEllipseByExternal.py
new file mode 100644 (file)
index 0000000..92d33ca
--- /dev/null
@@ -0,0 +1,91 @@
+# 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 creation of ellipse by external feature
+"""
+
+import unittest
+from salome.shaper import model
+
+from GeomAPI import *
+from SketchAPI import *
+
+__updated__ = "2019-09-12"
+
+# reference data
+CENTER_POINT = GeomAPI_Pnt2d(50., 50.)
+FOCUS_POINT = GeomAPI_Pnt2d(70., 60.)
+MINOR_RADIUS = 10.
+
+class TestEllipse(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myDOF = 0
+
+  def tearDown(self):
+    self.checkDOF()
+    model.end()
+
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+  def checkPointsEqual(self, thePoint1, thePoint2):
+    self.assertAlmostEqual(thePoint1.x(), thePoint2.x(), 5)
+    self.assertAlmostEqual(thePoint1.y(), thePoint2.y(), 5)
+
+
+  def test_ellipse_by_external_name(self):
+    """ Test 1. Create ellipse by name of external edge
+    """
+    self.myEllipse = self.mySketch.addEllipse("Sketch_1/SketchEllipse_1")
+    model.do()
+
+    # check ellipse parameters
+    anEllipse = self.myEllipse.defaultResult().shape().edge().ellipse()
+    self.checkPointsEqual(anEllipse.center(), CENTER_POINT)
+    self.checkPointsEqual(anEllipse.firstFocus(), FOCUS_POINT)
+    self.assertAlmostEqual(anEllipse.minorRadius(), MINOR_RADIUS)
+
+  def test_ellipse_by_external_selection(self):
+    """ Test 2. Create ellipse by selected edge
+    """
+    self.myEllipse = self.mySketch.addEllipse(ELLIPSE.results()[-1])
+    model.do()
+
+    # check ellipse parameters
+    anEllipse = self.myEllipse.defaultResult().shape().edge().ellipse()
+    self.checkPointsEqual(anEllipse.center(), CENTER_POINT)
+    self.checkPointsEqual(anEllipse.firstFocus(), FOCUS_POINT)
+    self.assertAlmostEqual(anEllipse.minorRadius(), MINOR_RADIUS)
+
+
+if __name__ == "__main__":
+    model.begin()
+    aDocument = model.moduleDocument()
+    aSketch = model.addSketch(aDocument, model.defaultPlane("XOY"))
+    ELLIPSE = aSketch.addEllipse(CENTER_POINT, FOCUS_POINT, MINOR_RADIUS)
+    model.end()
+
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
diff --git a/src/SketchPlugin/Test/TestCreateEllipseByMajorAxisAndPassed.py b/src/SketchPlugin/Test/TestCreateEllipseByMajorAxisAndPassed.py
new file mode 100644 (file)
index 0000000..401ab72
--- /dev/null
@@ -0,0 +1,319 @@
+# 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 creation of ellipse by majoraxis and passed point
+"""
+
+import unittest
+from salome.shaper import model
+
+from GeomAPI import *
+from SketchAPI import *
+
+__updated__ = "2019-09-12"
+
+class TestEllipse(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myAxisStart = GeomAPI_Pnt2d(30., 60.)
+    self.myAxisEnd = GeomAPI_Pnt2d(80., 50.)
+    self.myPassedPoint = GeomAPI_Pnt2d(60., 60.)
+    self.myDOF = 0
+
+  def tearDown(self):
+    self.checkDOF()
+    model.end()
+
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+  def checkPointsEqual(self, thePoint1, thePoint2):
+    self.assertAlmostEqual(thePoint1.x(), thePoint2.x(), 5)
+    self.assertAlmostEqual(thePoint1.y(), thePoint2.y(), 5)
+
+  def checkPointOnLine(self, theCoordinates, theLine):
+    point = GeomAPI_Pnt2d(theCoordinates.x(), theCoordinates.y())
+    dist = model.distancePointLine(point, theLine)
+    self.assertAlmostEqual(dist, 0, 7)
+
+  def checkPointOnCircle(self, theCoordinates, theCircle):
+    point = GeomAPI_Pnt2d(theCoordinates.x(), theCoordinates.y())
+    dist = model.distancePointPoint(point, theCircle.center())
+    self.assertAlmostEqual(dist , theCircle.radius().value(), 7)
+
+  def checkPointOnEllipse(self, theCoordinates, theEllipse):
+    point = GeomAPI_Pnt2d(theCoordinates.x(), theCoordinates.y())
+    firstFocus2d = GeomAPI_Pnt2d(theEllipse.firstFocus().x(), theEllipse.firstFocus().y())
+    distPF1 = model.distancePointPoint(firstFocus2d,  point)
+    secondFocus2d = GeomAPI_Pnt2d(theEllipse.secondFocus().x(), theEllipse.secondFocus().y())
+    distPF2 = model.distancePointPoint(secondFocus2d,  point)
+    self.assertAlmostEqual(distPF1 + distPF2, 2.0 * theEllipse.majorRadius(), 7)
+
+
+  def test_ellipse_by_axis_and_passed(self):
+    """ Test 1. Create ellipse by points defining major semi-axis and a passed point on ellipse
+    """
+    self.myEllipse1 = self.mySketch.addEllipse(self.myAxisStart.x(), self.myAxisStart.y(), self.myAxisEnd.x(), self.myAxisEnd.y(), self.myPassedPoint.x(), self.myPassedPoint.y(), False)
+    self.myDOF += 5
+    model.do()
+    anEllipseFeature1 = model.lastSubFeature(self.mySketch, "SketchEllipse")
+
+    self.myEllipse2 = self.mySketch.addEllipse(self.myAxisStart, self.myAxisEnd, self.myPassedPoint, False)
+    self.myDOF += 5
+    model.do()
+    anEllipseFeature2 = model.lastSubFeature(self.mySketch, "SketchEllipse")
+
+    # check both ellipses are equal
+    anEllipse1 = anEllipseFeature1.lastResult().shape().edge().ellipse()
+    anEllipse2 = anEllipseFeature2.lastResult().shape().edge().ellipse()
+    self.checkPointsEqual(anEllipse1.center(), anEllipse2.center())
+    self.checkPointsEqual(anEllipse1.firstFocus(), anEllipse2.firstFocus())
+    self.checkPointsEqual(anEllipse1.secondFocus(), anEllipse2.secondFocus())
+    self.assertAlmostEqual(anEllipse1.minorRadius(), anEllipse2.minorRadius())
+    self.assertAlmostEqual(anEllipse1.majorRadius(), anEllipse2.majorRadius())
+    # check passed point on ellipse
+    self.checkPointOnEllipse(self.myPassedPoint, anEllipse1)
+    self.checkPointOnEllipse(self.myPassedPoint, anEllipse2)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 14)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 4)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 2)
+
+  def test_ellipse_with_fixed_axis_start(self):
+    """ Test 2. Create ellipse which negative point on the major axis coincident with another point
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(aLine.startPoint(), self.myAxisEnd, self.myPassedPoint, False)
+    self.myDOF += 3
+    model.do()
+    # check ellipse
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    self.checkPointOnEllipse(self.myPassedPoint, anEllipse)
+    self.checkPointOnEllipse(aLine.startPoint(), anEllipse)
+    # check distance is equal to major semi-axis
+    dist = model.distancePointPoint(GeomAPI_Pnt2d(anEllipse.center().x(), anEllipse.center().y()), aLine.startPoint())
+    self.assertAlmostEqual(dist, anEllipse.majorRadius(), 7)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_axis_start_on_line(self):
+    """ Test 3. Create ellipse which negative point on the major axis coincident with a line
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse([self.myAxisStart, aLine.result()], self.myAxisEnd, self.myPassedPoint, False)
+    self.myDOF += 4
+    model.do()
+
+    anEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse"))
+    # check negative point of major axis on line
+    self.checkPointOnLine(anEllipse.majorAxisNegative(), aLine)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_axis_start_on_circle(self):
+    """ Test 4. Create ellipse which negative point on the major axis coincident with a circle
+    """
+    aCircle = self.mySketch.addCircle(10, 10, 20)
+    self.myDOF += 3
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse([self.myAxisStart, aCircle.defaultResult()], self.myAxisEnd, self.myPassedPoint, False)
+    self.myDOF += 4
+    model.do()
+
+    anEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse"))
+    # check center on circle
+    self.checkPointOnCircle(anEllipse.majorAxisNegative(), aCircle)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchCircle", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_axis_start_on_ellipse(self):
+    """ Test 5. Create ellipse which negative point on the major axis coincident with another ellipse
+    """
+    anOtherEllipse = self.mySketch.addEllipse(10, 10, 30, 20, 10)
+    self.myDOF += 5
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse([self.myAxisStart, anOtherEllipse.defaultResult()], self.myAxisEnd, self.myPassedPoint, False)
+    self.myDOF += 4
+    model.do()
+
+    anEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse"))
+    # check center on ellipse
+    self.checkPointOnEllipse(anEllipse.majorAxisNegative(), anOtherEllipse.defaultResult().shape().edge().ellipse())
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_fixed_axis_end(self):
+    """ Test 6. Create ellipse which positive point on the major axis coincident with another point
+    """
+    aLine = self.mySketch.addLine(10, 10, 90, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myAxisStart, aLine.endPoint(), self.myPassedPoint, False)
+    self.myDOF += 3
+    model.do()
+    # check ellipse
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    self.checkPointOnEllipse(self.myPassedPoint, anEllipse)
+    self.checkPointOnEllipse(aLine.endPoint(), anEllipse)
+    # check distance is equal to major semi-axis
+    dist = model.distancePointPoint(GeomAPI_Pnt2d(anEllipse.center().x(), anEllipse.center().y()), aLine.endPoint())
+    self.assertAlmostEqual(dist, anEllipse.majorRadius(), 7)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_axis_end_on_line(self):
+    """ Test 7. Create ellipse which negative point on the major axis coincident with a line
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myAxisStart, [self.myAxisEnd, aLine.result()], self.myPassedPoint, False)
+    self.myDOF += 4
+    model.do()
+
+    anEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse"))
+    # check axis point on line
+    self.checkPointOnLine(anEllipse.majorAxisPositive(), aLine)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_axis_end_on_circle(self):
+    """ Test 8. Create ellipse which negative point on the major axis coincident with a circle
+    """
+    aCircle = self.mySketch.addCircle(10, 10, 20)
+    self.myDOF += 3
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myAxisStart, [self.myAxisEnd, aCircle.defaultResult()], self.myPassedPoint, False)
+    self.myDOF += 4
+    model.do()
+
+    anEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse"))
+    # check center on circle
+    self.checkPointOnCircle(anEllipse.majorAxisPositive(), aCircle)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchCircle", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_axis_end_on_ellipse(self):
+    """ Test 9. Create ellipse which negative point on the major axis coincident with another ellipse
+    """
+    anOtherEllipse = self.mySketch.addEllipse(10, 10, 90, 40, 30)
+    self.myDOF += 5
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myAxisStart, [self.myAxisEnd, anOtherEllipse.defaultResult()], self.myPassedPoint, False)
+    self.myDOF += 4
+    model.do()
+
+    anEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse"))
+    # check center on ellipse
+    self.checkPointOnEllipse(anEllipse.majorAxisPositive(), anOtherEllipse.defaultResult().shape().edge().ellipse())
+    # check coincidence feature exists
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 2)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_fixed_passed_point(self):
+    """ Test 10. Create ellipse which passed point is coincident with another point
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myAxisStart, self.myAxisEnd, aLine.endPoint(), False)
+    self.myDOF += 4
+    model.do()
+    # check ellipse
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    self.checkPointOnEllipse(aLine.endPoint(), anEllipse)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1)
+
+  def test_ellipse_with_passed_point_on_line(self):
+    """ Test 11. Create ellipse which passed point is placed on a line.
+                 Check no constraints is applied.
+    """
+    aLine = self.mySketch.addLine(10, 10, 30, 40)
+    self.myDOF += 4
+    model.do()
+
+    self.myEllipse1 = self.mySketch.addEllipse(self.myAxisStart, self.myAxisEnd, [self.myPassedPoint, aLine.result()], False)
+    self.myDOF += 5
+    model.do()
+
+    anEllipseFeature = model.lastSubFeature(self.mySketch, "SketchEllipse")
+    anEllipse = anEllipseFeature.lastResult().shape().edge().ellipse()
+    self.checkPointOnEllipse(self.myPassedPoint, anEllipse)
+    # check number of features
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", 7)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", 3)
+    model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1)
+    # check neither coincidence nor tangent feature exists
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 0)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintTangent", 0)
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
diff --git a/src/SketchPlugin/Test/TestMoveEllipse.py b/src/SketchPlugin/Test/TestMoveEllipse.py
new file mode 100644 (file)
index 0000000..3795ee8
--- /dev/null
@@ -0,0 +1,240 @@
+# Copyright (C) 2017-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 movement of the sketch ellipse
+"""
+
+import unittest
+import math
+from GeomAPI import GeomAPI_Pnt2d
+from GeomDataAPI import geomDataAPI_Point2D
+from salome.shaper import model
+
+__updated__ = "2019-09-12"
+
+class TestMoveEllipse(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myCenter = [70., 50.]
+    self.myFocus = [100., 70.]
+    self.myMinorRadius = 20.
+    self.myEllipse = self.mySketch.addEllipse(self.myCenter[0], self.myCenter[1], self.myFocus[0], self.myFocus[1], self.myMinorRadius)
+    self.myDOF = 5
+    model.do()
+    self.checkDOF()
+    self.myMajorRadius = self.myEllipse.majorRadius().value()
+
+  def tearDown(self):
+    self.checkDOF()
+    model.end()
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+  def checkPointCoordinates(self, thePoint, theCoordinates):
+    aCoord = []
+    if issubclass(type(theCoordinates), GeomAPI_Pnt2d):
+      aCoord = [theCoordinates.x(), theCoordinates.y()]
+    else:
+      aCoord = theCoordinates
+    DIGITS = 7 - math.floor(math.log10(math.hypot(aCoord[0], aCoord[1])))
+    self.assertAlmostEqual(thePoint.x(), aCoord[0], DIGITS)
+    self.assertAlmostEqual(thePoint.y(), aCoord[1], DIGITS)
+
+  def checkPointOnEllipse(self, theCoordinates, theEllipse):
+    point = GeomAPI_Pnt2d(theCoordinates.x(), theCoordinates.y())
+    firstFocus2d = GeomAPI_Pnt2d(theEllipse.firstFocus().x(), theEllipse.firstFocus().y())
+    distPF1 = model.distancePointPoint(firstFocus2d,  point)
+    secondFocus2d = GeomAPI_Pnt2d(theEllipse.secondFocus().x(), theEllipse.secondFocus().y())
+    distPF2 = model.distancePointPoint(secondFocus2d,  point)
+    self.assertAlmostEqual(distPF1 + distPF2, 2.0 * theEllipse.majorRadius().value(), 7 - math.floor(math.log10(theEllipse.majorRadius().value())))
+
+  def fixMajorRadius(self):
+    self.mySketch.setDistance(self.myEllipse.center(), self.myEllipse.majorAxisPositive(), self.myMajorRadius)
+    self.myDOF -= 1
+    model.do()
+    self.checkDOF()
+
+  def fixMinorRadius(self):
+    self.mySketch.setDistance(self.myEllipse.center(), self.myEllipse.minorAxisPositive(), self.myMinorRadius)
+    self.myDOF -= 1
+    model.do()
+    self.checkDOF()
+
+  def fixPoint(self, thePoint):
+    self.mySketch.setFixed(thePoint)
+    self.myDOF -= 2
+    model.do()
+    self.checkDOF()
+
+
+  def test_move_center_free_ellipse(self):
+    """ Test 1. Movement of central point of a free ellipse
+    """
+    newPosition = [self.myCenter[0] + 20., self.myCenter[1] + 10.]
+    self.mySketch.move(self.myEllipse.center(), newPosition[0], newPosition[1])
+    model.do()
+    self.checkPointCoordinates(self.myEllipse.center(), newPosition)
+    self.assertAlmostEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertAlmostEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_free_ellipse(self):
+    """ Test 2. Movement of a free ellipse dragging the edge
+    """
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.myEllipse.defaultResult(), newPosition.x(), newPosition.y())
+    model.do()
+    self.checkPointCoordinates(self.myEllipse.center(), self.myCenter)
+    self.checkPointOnEllipse(newPosition, self.myEllipse)
+    self.assertNotEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertNotEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_center_ellipse_fixed_major_radius(self):
+    """ Test 3. Movement of central point of ellipse with fixed major radius
+    """
+    self.fixMajorRadius()
+
+    newPosition = [self.myCenter[0] + 20., self.myCenter[1] + 10.]
+    self.mySketch.move(self.myEllipse.center(), newPosition[0], newPosition[1])
+    model.do()
+    self.checkPointCoordinates(self.myEllipse.center(), newPosition)
+    self.assertAlmostEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertAlmostEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_ellipse_fixed_major_radius(self):
+    """ Test 4. Movement of ellipse with fixed major radius
+    """
+    self.fixMajorRadius()
+
+    newPosition = GeomAPI_Pnt2d(80., 80.)
+    self.mySketch.move(self.myEllipse.defaultResult(), newPosition.x(), newPosition.y())
+    model.do()
+    self.checkPointOnEllipse(newPosition, self.myEllipse)
+    self.assertNotEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertAlmostEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_center_ellipse_fixed_minor_radius(self):
+    """ Test 5. Movement of central point of ellipse with fixed minor radius
+    """
+    self.fixMinorRadius()
+
+    newPosition = [self.myCenter[0] + 20., self.myCenter[1] + 10.]
+    self.mySketch.move(self.myEllipse.center(), newPosition[0], newPosition[1])
+    model.do()
+    self.checkPointCoordinates(self.myEllipse.center(), newPosition)
+    self.assertAlmostEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertAlmostEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_ellipse_fixed_minor_radius(self):
+    """ Test 6. Movement of ellipse with fixed minor radius
+    """
+    self.fixMinorRadius()
+
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.myEllipse.defaultResult(), newPosition.x(), newPosition.y())
+    model.do()
+    self.checkPointOnEllipse(newPosition, self.myEllipse)
+    self.assertAlmostEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertNotEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_center_ellipse_fixed_center(self):
+    """ Test 7. Movement of central point of ellipse with fixed center (nothing should be changed)
+    """
+    self.fixPoint(self.myEllipse.center())
+
+    newPosition = [self.myCenter[0] + 20., self.myCenter[1] + 10.]
+    self.mySketch.move(self.myEllipse.center(), newPosition[0], newPosition[1])
+    model.do()
+    self.checkPointCoordinates(self.myEllipse.center(), self.myCenter)
+    self.assertAlmostEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertAlmostEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_ellipse_fixed_center(self):
+    """ Test 8. Movement of ellipse with fixed center
+    """
+    self.fixPoint(self.myEllipse.center())
+
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.myEllipse.defaultResult(), newPosition.x(), newPosition.y())
+    model.do()
+    self.checkPointOnEllipse(newPosition, self.myEllipse)
+    self.assertNotEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertNotEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_center_ellipse_fixed_focus(self):
+    """ Test 9. Movement of central point of ellipse with fixed focus
+    """
+    self.fixPoint(self.myEllipse.firstFocus())
+
+    newPosition = GeomAPI_Pnt2d(self.myCenter[0] + 20., self.myCenter[1] + 10.)
+    self.mySketch.move(self.myEllipse.center(), newPosition.x(), newPosition.y())
+    model.do()
+    self.checkPointCoordinates(self.myEllipse.center(), newPosition)
+    self.checkPointCoordinates(self.myEllipse.firstFocus(), self.myFocus)
+    self.assertNotEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertNotEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_focus_ellipse_fixed_focus(self):
+    """ Test 10. Movement of a focus point of ellipse with fixed focus (nothing should be changed)
+    """
+    self.fixPoint(self.myEllipse.firstFocus())
+
+    newPosition = GeomAPI_Pnt2d(self.myFocus[0] + 10., self.myFocus[1] + 10.)
+    self.mySketch.move(self.myEllipse.firstFocus(), newPosition.x(), newPosition.y())
+    model.do()
+    self.checkPointCoordinates(self.myEllipse.firstFocus(), self.myFocus)
+    self.assertAlmostEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertAlmostEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_ellipse_fixed_focus(self):
+    """ Test 11. Movement of ellipse with fixed focus
+    """
+    self.fixPoint(self.myEllipse.firstFocus())
+
+    newPosition = GeomAPI_Pnt2d(80., 90.)
+    self.mySketch.move(self.myEllipse.defaultResult(), newPosition.x(), newPosition.y())
+    model.do()
+    self.checkPointOnEllipse(newPosition, self.myEllipse)
+    self.checkPointCoordinates(self.myEllipse.firstFocus(), self.myFocus)
+    self.assertNotEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+    self.assertNotEqual(self.myEllipse.majorRadius().value(), self.myMajorRadius)
+
+  def test_move_fixed_ellipse(self):
+    """ Test 12. Trying to move fully fixed ellipse
+    """
+    self.mySketch.setFixed(self.myEllipse.results()[-1])
+    self.myDOF -= 5
+    model.do()
+    self.checkDOF()
+
+    newPosition = [120., 90.]
+    self.mySketch.move(self.myEllipse.defaultResult(), newPosition[0], newPosition[1])
+    model.do()
+    self.checkPointCoordinates(self.myEllipse.center(), self.myCenter)
+    self.checkPointCoordinates(self.myEllipse.firstFocus(), self.myFocus)
+    self.assertAlmostEqual(self.myEllipse.minorRadius().value(), self.myMinorRadius)
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert(model.checkPythonDump())
index 9c930341cb3ea630209f22454d6e8afafea2fde5..1e51edf26457a80de245ff7d5e7a39a110713b9b 100644 (file)
       <feature id="SketchConstraintRigid" title="Fixed" tooltip="Fix an object" icon="icons/Sketch/fixed.png"
                helpfile="rigidFeature.html">
         <sketch_shape_selector id="ConstraintEntityA" label="Object"
-                        tooltip="Select point, line end point, line, center of circle or arc."
+                        tooltip="Select point, curve or its boundary point."
                         shape_types="edge vertex">
-          <validator id="GeomValidators_ShapeType" parameters="vertex,line,circle"/>
+          <validator id="GeomValidators_ShapeType" parameters="vertex,edge"/>
           <validator id="SketchPlugin_NotFixed"/>
         </sketch_shape_selector>
         <validator id="PartSet_RigidSelection"/>
index e63fa6b91f026513468a6005dc3b94020879e265..37aba1bd8b0c7166afd4fce5998be7e62437f26d 100644 (file)
@@ -62,6 +62,7 @@ enum SketchSolver_ConstraintType {
   CONSTRAINT_PT_PT_COINCIDENT,
   CONSTRAINT_PT_ON_LINE,
   CONSTRAINT_PT_ON_CIRCLE,
+  CONSTRAINT_PT_ON_ELLIPSE,
   CONSTRAINT_MIDDLE_POINT,
   CONSTRAINT_DISTANCE,         // base distance if we don't know the measured objects yet
   CONSTRAINT_PT_PT_DISTANCE,
index 4e34e8badda0541b1bc291469cb213063b7b342a..e3d8f790a4ef7dd4590f198f614384982c1a8098 100644 (file)
@@ -21,7 +21,7 @@
 #include <Events_LongOp.h>
 
 // Multiplier to correlate IDs of SketchPlugin constraint and primitive PlaneGCS constraints
-static const int THE_CONSTRAINT_MULT = 10;
+static const int THE_CONSTRAINT_MULT = 100;
 
 
 PlaneGCSSolver_Solver::PlaneGCSSolver_Solver()
index 5b3572c0221a8b8c97dbbb200d0d2f7627612eab..d0914df1cd9bfa06fd57d73a9fb93dcb7f3a08ce 100644 (file)
@@ -192,6 +192,7 @@ ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint(
     break;
   case CONSTRAINT_PT_ON_LINE:
   case CONSTRAINT_PT_ON_CIRCLE:
+  case CONSTRAINT_PT_ON_ELLIPSE:
     aResult = createConstraintPointOnEntity(theType, aPoint1, GCS_EDGE_WRAPPER(theEntity1));
     break;
   case CONSTRAINT_MIDDLE_POINT:
index 9903b850efdb4ab8bee4b67503465c0bd7419456..f1c32b733a91a101cdf1d6b4a21065d5ef33b1eb 100644 (file)
@@ -102,6 +102,8 @@ void SketchSolver_ConstraintCoincidence::getAttributes(
       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();
 
index 2eecd66c1e8331b9f3e1a77fab5b1640b4f3cf90..b895b6d6be67b50f88ae1e4a81f4e24c345ed297 100644 (file)
@@ -205,6 +205,9 @@ bool SketcherPrs_LengthDimension::readyToDisplay(ModelAPI_Feature* theConstraint
                                                  const std::shared_ptr<GeomAPI_Ax3>& thePlane,
                                                  gp_Pnt& thePnt1, gp_Pnt& thePnt2)
 {
+  if (!thePlane)
+    return false;
+
   DataPtr aData = theConstraint->data();
   if (theConstraint->getKind() == SketchPlugin_ConstraintLength::ID()) {
     // The constraint is length