Salome HOME
[Code coverage GeomAlgoAPI]: Circ2dBuilder refactoring and additional tests
authorazv <azv@opencascade.com>
Wed, 19 Dec 2018 08:13:54 +0000 (11:13 +0300)
committerazv <azv@opencascade.com>
Wed, 19 Dec 2018 08:13:54 +0000 (11:13 +0300)
src/GeomAlgoAPI/GeomAlgoAPI_Circ2dBuilder.cpp
src/SketchPlugin/Test/TestCreateCircleByCenterAndPassed.py
src/SketchPlugin/Test/TestCreateCircleByThreePoints.py

index f0d92c7f75d22d6c043c9c57913bd564f281cdfe..c7db7549852dcf36f07d412629bbdda5e0ee5d96 100644 (file)
@@ -211,29 +211,28 @@ private:
     VectorOfGccLine aTgLine;
     convertTangentCurvesToGccEnt(aTgCirc, aTgLine);
 
-    if (aTgCirc.size() + aTgLine.size() != 3)
-      return Circ2dPtr();
-
     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
-    switch (aTgLine.size()) {
-    case 0:
-      aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
-          *aTgCirc[0], *aTgCirc[1], *aTgCirc[2], Precision::Confusion()));
-      break;
-    case 1:
-      aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
-          *aTgCirc[0], *aTgCirc[1], *aTgLine[0], Precision::Confusion()));
-      break;
-    case 2:
-      aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
-          *aTgCirc[0], *aTgLine[0], *aTgLine[1], Precision::Confusion()));
-      break;
-    case 3:
-      aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
-          *aTgLine[0], *aTgLine[1], *aTgLine[0], Precision::Confusion()));
-      break;
-    default:
-      break;
+    if (aTgCirc.size() + aTgLine.size() == 3) {
+      switch (aTgLine.size()) {
+      case 0:
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+            *aTgCirc[0], *aTgCirc[1], *aTgCirc[2], Precision::Confusion()));
+        break;
+      case 1:
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+            *aTgCirc[0], *aTgCirc[1], *aTgLine[0], Precision::Confusion()));
+        break;
+      case 2:
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+            *aTgCirc[0], *aTgLine[0], *aTgLine[1], Precision::Confusion()));
+        break;
+      case 3:
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+            *aTgLine[0], *aTgLine[1], *aTgLine[2], Precision::Confusion()));
+        break;
+      default:
+        break;
+      }
     }
 
     return getProperCircle(aCircleBuilder);
@@ -242,35 +241,28 @@ private:
   Circ2dPtr circleByPointAndTwoTangentCurves()
   {
     const gp_Pnt2d& aPoint = myPassingPoints[0];
-    CurveAdaptorPtr aCurve1 = myTangentShapes[0];
-    CurveAdaptorPtr aCurve2 = myTangentShapes[1];
-    if (!aCurve1 || !aCurve2)
-      return Circ2dPtr();
+
+    VectorOfGccCirc aTgCirc;
+    VectorOfGccLine aTgLine;
+    convertTangentCurvesToGccEnt(aTgCirc, aTgLine);
 
     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
-    if (aCurve1->GetType() == GeomAbs_Line) {
-      if (aCurve2->GetType() == GeomAbs_Line) {
-        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
-            new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Line()),
-                                  GccEnt::Unqualified(aCurve2->Line()),
-                                  aPoint, Precision::Confusion()));
-      } else if (aCurve2->GetType() == GeomAbs_Circle) {
-        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
-            new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()),
-                                  GccEnt::Unqualified(aCurve1->Line()),
-                                  aPoint, Precision::Confusion()));
-      }
-    } else if (aCurve1->GetType() == GeomAbs_Circle) {
-      if (aCurve2->GetType() == GeomAbs_Line) {
-        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
-            new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Circle()),
-                                  GccEnt::Unqualified(aCurve2->Line()),
-                                  aPoint, Precision::Confusion()));
-      } else if (aCurve2->GetType() == GeomAbs_Circle) {
-        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
-            new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()),
-                                  GccEnt::Unqualified(aCurve1->Circle()),
-                                  aPoint, Precision::Confusion()));
+    if (aTgCirc.size() + aTgLine.size() == 2) {
+      switch (aTgLine.size()) {
+      case 0:
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+            *aTgCirc[0], *aTgCirc[1], aPoint, Precision::Confusion()));
+        break;
+      case 1:
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+            *aTgCirc[0], *aTgLine[0], aPoint, Precision::Confusion()));
+        break;
+      case 2:
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+            *aTgLine[0], *aTgLine[1], aPoint, Precision::Confusion()));
+        break;
+      default:
+        break;
       }
     }
 
@@ -282,16 +274,17 @@ private:
     const gp_Pnt2d& aPoint1 = myPassingPoints[0];
     const gp_Pnt2d& aPoint2 = myPassingPoints[1];
     CurveAdaptorPtr aCurve = myTangentShapes[0];
-    if (!aCurve)
-      return Circ2dPtr();
 
     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
-    if (aCurve->GetType() == GeomAbs_Line) {
-      aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
-        GccEnt::Unqualified(aCurve->Line()), aPoint1, aPoint2, Precision::Confusion()));
-    } else if (aCurve->GetType() == GeomAbs_Circle) {
-      aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
-          GccEnt::Unqualified(aCurve->Circle()), aPoint1, aPoint2, Precision::Confusion()));
+    if (aCurve) {
+      if (aCurve->GetType() == GeomAbs_Line) {
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+            GccEnt::Unqualified(aCurve->Line()), aPoint1, aPoint2, Precision::Confusion()));
+      }
+      else if (aCurve->GetType() == GeomAbs_Circle) {
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+            GccEnt::Unqualified(aCurve->Circle()), aPoint1, aPoint2, Precision::Confusion()));
+      }
     }
 
     return getProperCircle(aCircleBuilder);
@@ -356,25 +349,24 @@ private:
     VectorOfGccLine aTgLine;
     convertTangentCurvesToGccEnt(aTgCirc, aTgLine);
 
-    if (aTgCirc.size() + aTgLine.size() != 2)
-      return Circ2dPtr();
-
     std::shared_ptr<GccAna_Circ2d2TanRad> aCircleBuilder;
-    switch (aTgLine.size()) {
-    case 0:
-      aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
-          *aTgCirc[0], *aTgCirc[1], myRadius, Precision::Confusion()));
-      break;
-    case 1:
-      aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
-          *aTgCirc[0], *aTgLine[0], myRadius, Precision::Confusion()));
-      break;
-    case 2:
-      aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
-          *aTgLine[0], *aTgLine[1], myRadius, Precision::Confusion()));
-      break;
-    default:
-      break;
+    if (aTgCirc.size() + aTgLine.size() == 2) {
+      switch (aTgLine.size()) {
+      case 0:
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
+            *aTgCirc[0], *aTgCirc[1], myRadius, Precision::Confusion()));
+        break;
+      case 1:
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
+            *aTgCirc[0], *aTgLine[0], myRadius, Precision::Confusion()));
+        break;
+      case 2:
+        aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
+            *aTgLine[0], *aTgLine[1], myRadius, Precision::Confusion()));
+        break;
+      default:
+        break;
+      }
     }
 
     return getProperCircle(aCircleBuilder);
index 6fbb6b34a77c87078acfee9dae969f3244fd27c6..b3212903e0f8328e53d427019d880084f1985c6c 100644 (file)
@@ -63,6 +63,12 @@ def verifyTangentCircles(theCircle1, theCircle2):
     aRDiff = math.fabs(aRadius1 - aRadius2)
     assert math.fabs(aRSum - aDistCC) < TOLERANCE or math.fabs(aRDiff - aDistCC) < TOLERANCE, "Circles do not tangent"
 
+def verifyTangentCircleLine(theCircle, theLine):
+    aCenter = geomDataAPI_Point2D(theCircle.attribute("circle_center"))
+    aRadius = theCircle.real("circle_radius").value()
+    aDistCL = model.distancePointLine(aCenter, theLine)
+    assert math.fabs(aDistCL - aRadius) < TOLERANCE, "Circle and line are not tangent"
+
 
 #=========================================================================
 # Start of test
@@ -221,6 +227,35 @@ verifyTangentCircles(aCircle, aPrevCircle)
 model.testNbSubFeatures(aSketch, "SketchConstraintCoincidence", 3)
 model.testNbSubFeatures(aSketch, "SketchConstraintTangent", 1)
 
+#=========================================================================
+# Test 5. Create a circle as a macro-feature by center and passed point on line
+#=========================================================================
+# create new circle
+aSession.startOperation()
+aCircle = aSketchFeature.addFeature("SketchMacroCircle")
+aCenter = geomDataAPI_Point2D(aCircle.attribute("center_point"))
+aCenterRef = aCircle.refattr("center_point_ref")
+aPassed = geomDataAPI_Point2D(aCircle.attribute("passed_point"))
+aPassedRef = aCircle.refattr("passed_point_ref")
+aCircleType = aCircle.string("circle_type")
+# initialize attributes
+aCircleType.setValue("circle_type_by_center_and_passed_points")
+anExpectedCenter = [(aLineStart[0] + aLineEnd[0]) * 0.5 + 10., (aLineStart[1] + aLineEnd[1]) * 0.5]
+aCenter.setValue(anExpectedCenter[0], anExpectedCenter[1])
+aPassedRef.setObject(aLine.lastResult())
+aPassed.setValue(aLineStart[0], aLineStart[1])
+aSession.finishOperation()
+assert (aSketchFeature.numberOfSubs() == 12)
+# check connected features do not change their positions
+model.assertLine(aLine, aLineStart, aLineEnd)
+# verify newly created circle
+aCircle = model.lastSubFeature(aSketchFeature, "SketchCircle")
+aCenter = geomDataAPI_Point2D(aCircle.attribute("circle_center"))
+model.assertPoint(aCenter, anExpectedCenter)
+verifyTangentCircleLine(aCircle, aLine)
+model.testNbSubFeatures(aSketch, "SketchConstraintCoincidence", 3)
+model.testNbSubFeatures(aSketch, "SketchConstraintTangent", 2)
+
 #=========================================================================
 # Test 5. Create a circle as a macro-feature by center and passed point placed on the same line
 #         Check the circle is not created
@@ -244,7 +279,7 @@ assert aLastFeature.getKind() == "SketchMacroCircle", "ERROR: SketchMacroCircle
 aSession.startOperation()
 aDocument.removeFeature(aCircle)
 aSession.finishOperation()
-assert (aSketchFeature.numberOfSubs() == 10)
+assert (aSketchFeature.numberOfSubs() == 12)
 
 #=========================================================================
 # End of test
index 558f2f86f86b6341bc7cea0043c38b14aff7884c..8a352a29c72f3ee66fb1cb932023cce0b23de3f0 100644 (file)
@@ -258,7 +258,9 @@ aCirclePnt3.setValue(aLineEnd[0], aLineEnd[1])
 aSession.finishOperation()
 aLastFeature = aSketchFeature.subFeature(aSketchFeature.numberOfSubs() - 1)
 assert aLastFeature.getKind() == "SketchMacroCircle", "ERROR: SketchMacroCircle has NOT expected to be valid"
+aSession.startOperation()
 aDocument.removeFeature(aCircle)
+aSession.finishOperation()
 assert (aSketchFeature.numberOfSubs() == 12)
 
 #=========================================================================
@@ -285,12 +287,211 @@ aCirclePnt3.setValue(aLineEnd[0], aLineEnd[1])
 aSession.finishOperation()
 aLastFeature = aSketchFeature.subFeature(aSketchFeature.numberOfSubs() - 1)
 assert aLastFeature.getKind() == "SketchMacroCircle", "ERROR: SketchMacroCircle has NOT expected to be valid"
+aSession.startOperation()
 aDocument.removeFeature(aCircle)
+aSession.finishOperation()
 assert (aSketchFeature.numberOfSubs() == 12)
 
+
+#=========================================================================
+# Auxiliary objects for the following tests
+#=========================================================================
+from salome.shaper import model
+
+model.begin()
+Sketch_1 = model.addSketch(aDocument, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(20, -10, 20, 10)
+SketchLine_2 = Sketch_1.addLine(20, 10, 40, 10)
+SketchLine_3 = Sketch_1.addLine(40, 10, 40, -10)
+SketchCircle_1 = Sketch_1.addCircle(-10, 0, 20)
+SketchCircle_2 = Sketch_1.addCircle(30, 25, 5)
+SketchCircle_3 = Sketch_1.addCircle(50, 0, 10)
+model.do()
+model.end()
+aSketchFeature = featureToCompositeFeature(Sketch_1.feature())
+
+#=========================================================================
+# Test 6. Create a circle passing through a point and tangent to 2 lines
+#=========================================================================
+# create new circle
+aSession.startOperation()
+aCircle = aSketchFeature.addFeature("SketchMacroCircle")
+aCirclePnt1 = geomDataAPI_Point2D(aCircle.attribute("first_point"))
+aCirclePnt2 = geomDataAPI_Point2D(aCircle.attribute("second_point"))
+aCirclePnt3 = geomDataAPI_Point2D(aCircle.attribute("third_point"))
+aCirclePnt1Ref = aCircle.refattr("first_point_ref")
+aCirclePnt2Ref = aCircle.refattr("second_point_ref")
+aCirclePnt3Ref = aCircle.refattr("third_point_ref")
+aCircleType = aCircle.string("circle_type")
+# initialize attributes
+aCircleType.setValue("circle_type_by_three_points")
+aCirclePnt1.setValue(0, 0)
+aCirclePnt2Ref.setObject(SketchLine_1.feature().lastResult())
+aCirclePnt2.setValue(SketchLine_1.startPoint().x(), SketchLine_1.startPoint().y())
+aCirclePnt3Ref.setObject(SketchLine_2.feature().lastResult())
+aCirclePnt3.setValue(SketchLine_2.startPoint().x(), SketchLine_2.startPoint().y())
+aSession.finishOperation()
+assert (aSketchFeature.numberOfSubs() == 9)
+# check newly created circle tangent to objects
+aCircle = model.lastSubFeature(aSketchFeature, "SketchCircle")
+verifyTangentCircleLine(aCircle, SketchLine_1.feature())
+verifyTangentCircleLine(aCircle, SketchLine_2.feature())
+model.testNbSubFeatures(Sketch_1, "SketchConstraintCoincidence", 0)
+model.testNbSubFeatures(Sketch_1, "SketchConstraintTangent", 2)
+
+#=========================================================================
+# Test 7. Create a circle passing through a point and tangent to 2 circles
+#=========================================================================
+# create new circle
+aSession.startOperation()
+aCircle = aSketchFeature.addFeature("SketchMacroCircle")
+aCirclePnt1 = geomDataAPI_Point2D(aCircle.attribute("first_point"))
+aCirclePnt2 = geomDataAPI_Point2D(aCircle.attribute("second_point"))
+aCirclePnt3 = geomDataAPI_Point2D(aCircle.attribute("third_point"))
+aCirclePnt1Ref = aCircle.refattr("first_point_ref")
+aCirclePnt2Ref = aCircle.refattr("second_point_ref")
+aCirclePnt3Ref = aCircle.refattr("third_point_ref")
+aCircleType = aCircle.string("circle_type")
+# initialize attributes
+aCircleType.setValue("circle_type_by_three_points")
+aCirclePnt1.setValue(0, 0)
+aCirclePnt2Ref.setObject(SketchCircle_3.feature().lastResult())
+aCirclePnt2.setValue(40, 0)
+aCirclePnt3Ref.setObject(SketchCircle_2.feature().lastResult())
+aCirclePnt3.setValue(30, 20)
+aSession.finishOperation()
+assert (aSketchFeature.numberOfSubs() == 12)
+# check newly created circle tangent to objects
+aCircle = model.lastSubFeature(aSketchFeature, "SketchCircle")
+verifyTangentCircles(aCircle, SketchCircle_3.feature())
+verifyTangentCircles(aCircle, SketchCircle_2.feature())
+model.testNbSubFeatures(Sketch_1, "SketchConstraintCoincidence", 0)
+model.testNbSubFeatures(Sketch_1, "SketchConstraintTangent", 4)
+
+#=========================================================================
+# Test 8. Create a circle passing through a point and tangent to line and circle
+#=========================================================================
+# create new circle
+aSession.startOperation()
+aCircle = aSketchFeature.addFeature("SketchMacroCircle")
+aCirclePnt1 = geomDataAPI_Point2D(aCircle.attribute("first_point"))
+aCirclePnt2 = geomDataAPI_Point2D(aCircle.attribute("second_point"))
+aCirclePnt3 = geomDataAPI_Point2D(aCircle.attribute("third_point"))
+aCirclePnt1Ref = aCircle.refattr("first_point_ref")
+aCirclePnt2Ref = aCircle.refattr("second_point_ref")
+aCirclePnt3Ref = aCircle.refattr("third_point_ref")
+aCircleType = aCircle.string("circle_type")
+# initialize attributes
+aCircleType.setValue("circle_type_by_three_points")
+aCirclePnt1.setValue(0, 0)
+aCirclePnt2Ref.setObject(SketchLine_3.feature().lastResult())
+aCirclePnt2.setValue(30, 0)
+aCirclePnt3Ref.setObject(SketchCircle_2.feature().lastResult())
+aCirclePnt3.setValue(30, 20)
+aSession.finishOperation()
+assert (aSketchFeature.numberOfSubs() == 15)
+# check newly created circle tangent to objects
+aCircle = model.lastSubFeature(aSketchFeature, "SketchCircle")
+verifyTangentCircles(aCircle, SketchCircle_2.feature())
+verifyTangentCircleLine(aCircle, SketchLine_3.feature())
+model.testNbSubFeatures(Sketch_1, "SketchConstraintCoincidence", 0)
+model.testNbSubFeatures(Sketch_1, "SketchConstraintTangent", 6)
+
+#=========================================================================
+# Test 9. Create a circle tangent to 3 lines
+#=========================================================================
+# create new circle
+aSession.startOperation()
+aCircle = aSketchFeature.addFeature("SketchMacroCircle")
+aCirclePnt1 = geomDataAPI_Point2D(aCircle.attribute("first_point"))
+aCirclePnt2 = geomDataAPI_Point2D(aCircle.attribute("second_point"))
+aCirclePnt3 = geomDataAPI_Point2D(aCircle.attribute("third_point"))
+aCirclePnt1Ref = aCircle.refattr("first_point_ref")
+aCirclePnt2Ref = aCircle.refattr("second_point_ref")
+aCirclePnt3Ref = aCircle.refattr("third_point_ref")
+aCircleType = aCircle.string("circle_type")
+# initialize attributes
+aCircleType.setValue("circle_type_by_three_points")
+aCirclePnt1Ref.setObject(SketchLine_1.feature().lastResult())
+aCirclePnt1.setValue(20, 0)
+aCirclePnt2Ref.setObject(SketchLine_2.feature().lastResult())
+aCirclePnt2.setValue(30, 10)
+aCirclePnt3Ref.setObject(SketchLine_3.feature().lastResult())
+aCirclePnt3.setValue(40, 0)
+aSession.finishOperation()
+assert (aSketchFeature.numberOfSubs() == 19)
+# check newly created circle tangent to objects
+aCircle = model.lastSubFeature(aSketchFeature, "SketchCircle")
+verifyTangentCircleLine(aCircle, SketchLine_1.feature())
+verifyTangentCircleLine(aCircle, SketchLine_2.feature())
+verifyTangentCircleLine(aCircle, SketchLine_3.feature())
+model.testNbSubFeatures(Sketch_1, "SketchConstraintCoincidence", 0)
+model.testNbSubFeatures(Sketch_1, "SketchConstraintTangent", 9)
+
+#=========================================================================
+# Test 10. Create a circle tangent to a circle and 2 lines
+#=========================================================================
+# create new circle
+aSession.startOperation()
+aCircle = aSketchFeature.addFeature("SketchMacroCircle")
+aCirclePnt1 = geomDataAPI_Point2D(aCircle.attribute("first_point"))
+aCirclePnt2 = geomDataAPI_Point2D(aCircle.attribute("second_point"))
+aCirclePnt3 = geomDataAPI_Point2D(aCircle.attribute("third_point"))
+aCirclePnt1Ref = aCircle.refattr("first_point_ref")
+aCirclePnt2Ref = aCircle.refattr("second_point_ref")
+aCirclePnt3Ref = aCircle.refattr("third_point_ref")
+aCircleType = aCircle.string("circle_type")
+# initialize attributes
+aCircleType.setValue("circle_type_by_three_points")
+aCirclePnt1Ref.setObject(SketchLine_1.feature().lastResult())
+aCirclePnt1.setValue(20, 0)
+aCirclePnt2Ref.setObject(SketchLine_2.feature().lastResult())
+aCirclePnt2.setValue(30, 10)
+aCirclePnt3Ref.setObject(SketchCircle_3.feature().lastResult())
+aCirclePnt3.setValue(40, 0)
+aSession.finishOperation()
+assert (aSketchFeature.numberOfSubs() == 23)
+# check newly created circle tangent to objects
+aCircle = model.lastSubFeature(aSketchFeature, "SketchCircle")
+verifyTangentCircleLine(aCircle, SketchLine_1.feature())
+verifyTangentCircleLine(aCircle, SketchLine_2.feature())
+verifyTangentCircles(aCircle, SketchCircle_3.feature())
+model.testNbSubFeatures(Sketch_1, "SketchConstraintCoincidence", 0)
+model.testNbSubFeatures(Sketch_1, "SketchConstraintTangent", 12)
+
+#=========================================================================
+# Test 11. Create a circle tangent to 3 circles
+#=========================================================================
+# create new circle
+aSession.startOperation()
+aCircle = aSketchFeature.addFeature("SketchMacroCircle")
+aCirclePnt1 = geomDataAPI_Point2D(aCircle.attribute("first_point"))
+aCirclePnt2 = geomDataAPI_Point2D(aCircle.attribute("second_point"))
+aCirclePnt3 = geomDataAPI_Point2D(aCircle.attribute("third_point"))
+aCirclePnt1Ref = aCircle.refattr("first_point_ref")
+aCirclePnt2Ref = aCircle.refattr("second_point_ref")
+aCirclePnt3Ref = aCircle.refattr("third_point_ref")
+aCircleType = aCircle.string("circle_type")
+# initialize attributes
+aCircleType.setValue("circle_type_by_three_points")
+aCirclePnt1Ref.setObject(SketchCircle_1.feature().lastResult())
+aCirclePnt1.setValue(10, 0)
+aCirclePnt2Ref.setObject(SketchCircle_2.feature().lastResult())
+aCirclePnt2.setValue(30, 20)
+aCirclePnt3Ref.setObject(SketchCircle_3.feature().lastResult())
+aCirclePnt3.setValue(40, 0)
+aSession.finishOperation()
+assert (aSketchFeature.numberOfSubs() == 27)
+# check newly created circle tangent to objects
+aCircle = model.lastSubFeature(aSketchFeature, "SketchCircle")
+verifyTangentCircles(aCircle, SketchCircle_1.feature())
+verifyTangentCircles(aCircle, SketchCircle_2.feature())
+verifyTangentCircles(aCircle, SketchCircle_3.feature())
+model.testNbSubFeatures(Sketch_1, "SketchConstraintCoincidence", 0)
+model.testNbSubFeatures(Sketch_1, "SketchConstraintTangent", 15)
+
 #=========================================================================
 # End of test
 #=========================================================================
 
-from salome.shaper import model
 assert(model.checkPythonDump())