]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Redesign Tangency constraint processing to set the Angle constraint for tangent featu...
authorazv <azv@opencascade.com>
Fri, 17 Mar 2017 10:27:36 +0000 (13:27 +0300)
committerazv <azv@opencascade.com>
Fri, 17 Mar 2017 12:57:43 +0000 (15:57 +0300)
12 files changed:
src/PythonAPI/Test/TestSketcherSetFillet.py
src/SketchPlugin/Test/TestConstraintEqual.py
src/SketchPlugin/Test/TestFillet.py
src/SketchPlugin/Test/TestFilletInteracting.py
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Solver.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Solver.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h
src/SketchSolver/SketchSolver_ConstraintTangent.cpp
src/SketchSolver/SketchSolver_ConstraintTangent.h

index 6b2b469da88ef045a55ef7ed4ebba2de45e412d4..f121be4db8b00e56d0861ae4e4b0d5cbb439189a 100644 (file)
@@ -3,12 +3,6 @@ from salome.shaper import model
 from TestSketcher import SketcherTestCase
 
 class SketcherSetFillet(SketcherTestCase):
-    # TODO: Remove tearDown method to check Python dump in super-class
-    def tearDown(self):
-        model.end()
-        #assert(model.checkPythonDump())
-        model.reset()
-
     def test_fillet(self):
         l1 = self.sketch.addLine(0, 0, 0, 1)
         l2 = self.sketch.addLine(0, 1, 1, 1)
index c8d4917b4c36ec9900225bfb6e763f1a22a0df55..49bf176586198358d9f30b70e2ac1ad7cc200760 100644 (file)
@@ -209,6 +209,13 @@ assert (math.fabs(aLine1Len - anExtLineLen) < 1.e-10)
 assert (math.fabs(aLine2Len - anExtLineLen) < 1.e-10)
 assert (model.dof(aSketchFeature) == 12)
 #=========================================================================
+# Remove costraint to check the DOF
+#=========================================================================
+aSession.startOperation()
+aDocument.removeFeature(aConstraintEqLen2)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 13)
+#=========================================================================
 # End of test
 #=========================================================================
 
index 39762ed778d19ded02d0327425bad2adb1d5d321..59f353cb8f1e6c792f0216a3aa6ce315a2980190 100644 (file)
@@ -197,7 +197,17 @@ aSession.finishOperation()
 # Verify the objects of fillet are created
 #=========================================================================
 checkSmoothness(aSketchFeature)
-assert model.dof(aSketchFeature) == 14, "PlaneGCS limitation: if you see this message, then maybe PlaneGCS has solved DoF for sketch with fillet correctly (expected DoF = 10, observed = {0}".format(model.dof(aSketchFeature))
+assert (model.dof(aSketchFeature) == 10)
+#=========================================================================
+# Move a line and check the fillet is correct
+#=========================================================================
+DELTA_X = DELTA_Y = 10.
+aSession.startOperation()
+aEndPoint1.setValue(aEndPoint1.x() + DELTA_X, aEndPoint1.y() + DELTA_Y)
+aSession.finishOperation()
+checkSmoothness(aSketchFeature)
+assert (model.dof(aSketchFeature) == 10)
+
 
 #=========================================================================
 # Create another sketch
@@ -228,12 +238,21 @@ aSession.finishOperation()
 # Verify the objects of fillet are created
 #=========================================================================
 checkSmoothness(aSketchFeature)
-assert model.dof(aSketchFeature) == 10, "PlaneGCS limitation: if you see this message, then maybe PlaneGCS has solved DoF for sketch with fillet correctly (expected DoF = 8, observed = {0}".format(model.dof(aSketchFeature))
+assert (model.dof(aSketchFeature) == 8)
+#=========================================================================
+# Move a line and check the fillet is correct
+#=========================================================================
+DELTA_X = 1.
+DELTA_Y = -2.
+aSession.startOperation()
+aStartPoint1.setValue(aStartPoint1.x() + DELTA_X, aStartPoint1.y() + DELTA_Y)
+aSession.finishOperation()
+checkSmoothness(aSketchFeature)
+assert (model.dof(aSketchFeature) == 8)
 #=========================================================================
 # End of test
 #=========================================================================
 
 # TODO: Improve Fillet test case by moving one of filleted objectes and check coincidence and tangency are correct
 
-# TODO: Checking of Python dump has been disabled until the Fillet redesigned.
-#assert(model.checkPythonDump())
+assert(model.checkPythonDump())
index 58a222b68d0678b63696b366458c774a15b3d37e..15b101e444e24833e6a0a23ef2e2c0e2b66c006b 100644 (file)
@@ -58,8 +58,7 @@ class TestFilletInteracting(unittest.TestCase):
 
   def tearDown(self):
     model.end()
-    # TODO: Revert commented line to check Python dump
-    #self.assertTrue(model.checkPythonDump())
+    self.assertTrue(model.checkPythonDump())
     model.reset()
 
 
@@ -115,7 +114,6 @@ class TestFilletInteracting(unittest.TestCase):
     return [aFeatureA, aFeatureB]
 
 
-  @unittest.expectedFailure
   def test_fillet_two_lines(self):
     """ Test 1. Fillet on two connected lines
     """
@@ -173,7 +171,6 @@ class TestFilletInteracting(unittest.TestCase):
     # remove fillet for correct python dump
     self.myDocument.removeFeature(aFillet.feature())
 
-  @unittest.expectedFailure
   def test_fillet_arc_line(self):
     """ Test 3. Fillet on connected arc and line
     """
@@ -202,7 +199,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
-  @unittest.expectedFailure
   def test_fillet_two_arcs(self):
     """ Test 4. Fillet on two connected arcs
     """
@@ -231,7 +227,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
-  @unittest.expectedFailure
   def test_fillet_with_horizontal_vertical(self):
     """ Test 5. Fillet on two connected lines in case of Horizontal or Vertical constraints applied
     """
@@ -267,7 +262,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
-  @unittest.expectedFailure
   def test_fillet_with_orthogonal(self):
     """ Test 6. Fillet on two connected lines in case of Perpendicular constraint applied
     """
@@ -301,7 +295,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
-  @unittest.expectedFailure
   def test_fillet_with_parallel(self):
     """ Test 7. Fillet on two connected lines in case of Parallel constraint applied
     """
@@ -338,7 +331,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [4])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [8])
 
-  @unittest.expectedFailure
   def test_fillet_with_equal_lines(self):
     """ Test 8. Fillet on two connected lines in case of Equal constraint applied
     """
@@ -355,7 +347,7 @@ class TestFilletInteracting(unittest.TestCase):
     model.do()
     self.checkDOF()
     self.mySketch.setFillet(aSketchLineA.startPoint())
-    self.myDOF += 2
+    self.myDOF += 2 # Equal has been removed
     model.do()
     self.checkFillet()
     self.checkDOF()
@@ -372,7 +364,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
-  @unittest.expectedFailure
   def test_fillet_with_equal_arcs(self):
     """ Test 9. Fillet on two connected arcs in case of Equal constraint applied
     """
@@ -389,7 +380,7 @@ class TestFilletInteracting(unittest.TestCase):
     model.do()
     self.checkDOF()
     self.mySketch.setFillet(aSketchArc1.endPoint())
-    self.myDOF += 1
+    self.myDOF += 2 # Equal has been removed
     model.do()
     self.checkFillet()
     self.checkDOF()
@@ -406,7 +397,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
-  @unittest.expectedFailure
   def test_fillet_with_length(self):
     """ Test 10. Fillet on two connected lines in case of Length constraint applied
     """
@@ -440,7 +430,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
-  @unittest.expectedFailure
   def test_fillet_with_radius(self):
     """ Test 11. Fillet on connected arc and line in case of Radius constraint is applied to arc
     """
@@ -474,7 +463,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
-  @unittest.expectedFailure
   def test_fillet_with_distance(self):
     """ Test 12. Fillet on two connected lines in case of Distance constraint applied
     """
@@ -495,7 +483,7 @@ class TestFilletInteracting(unittest.TestCase):
     model.do()
     self.checkDOF()
     self.mySketch.setFillet(aSketchLineA.startPoint())
-    self.myDOF += 1
+    self.myDOF += 2 # Distance has been removed
     model.do()
     self.checkFillet()
     self.checkDOF()
@@ -512,7 +500,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [4])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [8])
 
-  @unittest.expectedFailure
   def test_fillet_with_fixed_point(self):
     """ Test 13. Fillet on two connected lines in case of Fixed constraint applied to the fillet point
     """
@@ -529,7 +516,7 @@ class TestFilletInteracting(unittest.TestCase):
     model.do()
     self.checkDOF()
     self.mySketch.setFillet(aSketchLineA.startPoint())
-    self.myDOF += 1
+    self.myDOF += 3 # Fixed constraint has been removed
     model.do()
     self.checkFillet()
     self.checkDOF()
@@ -546,7 +533,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
-  @unittest.expectedFailure
   def test_fillet_with_fixed_line(self):
     """ Test 14. Fillet on two connected lines in case of Fixed constraint applied to one of lines
     """
@@ -580,7 +566,6 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
-  @unittest.expectedFailure
   def test_fillet_with_angle(self):
     """ Test 15. Fillet on two connected lines in case of Perpendicular constraint applied
     """
@@ -615,6 +600,5 @@ class TestFilletInteracting(unittest.TestCase):
     model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
 
 
-# TODO: Remove unittest.expectedFailure if the Tangency in PlaneGCS will be updated
 if __name__ == '__main__':
   unittest.main()
index dd843e2cb6598f31cb124188c1004aeb725fa60c..206c8a4f492f64452ea171643e5822a8101d53e7 100644 (file)
@@ -34,4 +34,6 @@ private:
   GCSCurvePtr myEntity;
 };
 
+typedef std::shared_ptr<PlaneGCSSolver_EdgeWrapper> EdgeWrapperPtr;
+
 #endif
index e9be8a299f31ccd34f080f5eb6185b7489d27f8e..2be2d62b5ac9bc0472d6ecc262b4f4d06c8c9037 100644 (file)
@@ -29,26 +29,16 @@ void PlaneGCSSolver_Solver::clear()
   myDOF = 0;
 }
 
-void PlaneGCSSolver_Solver::addConstraint(GCSConstraintPtr theConstraint,
-                                          const SketchSolver_ConstraintType theType)
+void PlaneGCSSolver_Solver::addConstraint(GCSConstraintPtr theConstraint)
 {
   myEquationSystem->addConstraint(theConstraint.get());
   myConstraints[theConstraint->getTag()].insert(theConstraint);
   myDOF = -1;
-
-  // Workaround: avoid tangent constraint in the list of redundant
-  if (theType == CONSTRAINT_TANGENT_CIRCLE_LINE ||
-      theType == CONSTRAINT_TANGENT_CIRCLE_CIRCLE ||
-      theType == CONSTRAINT_PT_PT_COINCIDENT ||
-      theType == CONSTRAINT_PT_ON_CIRCLE ||
-      theType == CONSTRAINT_PT_ON_LINE)
-    myConstraintIDsNotRedundant.insert(theConstraint->getTag());
 }
 
 void PlaneGCSSolver_Solver::removeConstraint(ConstraintID theID)
 {
   myConstraints.erase(theID);
-  myConstraintIDsNotRedundant.erase(theID);
   if (myConstraints.empty()) {
     myEquationSystem->clear();
     myDOF = (int)myParameters.size();
@@ -132,12 +122,6 @@ void PlaneGCSSolver_Solver::collectConflicting()
   myConflictingIDs.insert(aConflict.begin(), aConflict.end());
 
   myEquationSystem->getRedundant(aConflict);
-  // Workaround: avoid conflicting tangent constraints
-  GCS::VEC_I aTemp = aConflict;
-  aConflict.clear();
-  for (GCS::VEC_I::iterator anIt = aTemp.begin(); anIt != aTemp.end(); ++anIt)
-    if (myConstraintIDsNotRedundant.find(*anIt) == myConstraintIDsNotRedundant.end())
-      aConflict.push_back(*anIt);
   myConflictingIDs.insert(aConflict.begin(), aConflict.end());
 
   myConfCollected = true;
index 7037b1a5e0bba618837b54bd802cf893853a3175..4da9e60dc5aa116ed4db176f28dc0b1d6f52b323 100644 (file)
@@ -32,7 +32,7 @@ public:
   void clear();
 
   /// \brief Add constraint to the system of equations
-  void addConstraint(GCSConstraintPtr theConstraint, const SketchSolver_ConstraintType theType);
+  void addConstraint(GCSConstraintPtr theConstraint);
 
   /// \brief Remove constraint from the system of equations
   void removeConstraint(ConstraintID theID);
@@ -64,10 +64,6 @@ private:
   GCS::VEC_pD                  myParameters;     ///< list of unknowns
   ConstraintMap                myConstraints;    ///< list of constraints
 
-  /// IDs of constraints (coincidence, tangency) which will not be treated as conflicting
-  /// if they are reported as redundant
-  GCS::SET_I                   myConstraintIDsNotRedundant;
-
   std::shared_ptr<GCS::System> myEquationSystem; ///< set of equations for solving in FreeGCS
 
   GCS::SET_I                   myConflictingIDs; ///< list of IDs of conflicting constraints
index bec4445be6794ad323e58bdf6cbfba694bff95dd..472f4add7ccb80c7e4041f1171191d00e225c9ca 100644 (file)
@@ -31,7 +31,7 @@ static void constraintsToSolver(const ConstraintWrapperPtr& theConstraint,
       std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(theConstraint)->constraints();
   std::list<GCSConstraintPtr>::const_iterator anIt = aConstraints.begin();
   for (; anIt != aConstraints.end(); ++anIt)
-    theSolver->addConstraint(*anIt, theConstraint->type());
+    theSolver->addConstraint(*anIt);
 }
 
 
@@ -259,9 +259,20 @@ bool PlaneGCSSolver_Storage::removeConstraint(ConstraintPtr theConstraint)
   std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator
       aFound = myConstraintMap.find(theConstraint);
   if (aFound != myConstraintMap.end()) {
-    ConstraintID anID = aFound->second->id();
+    ConstraintWrapperPtr aCW = aFound->second;
+    ConstraintID anID = aCW->id();
+
     // Remove solver's constraints
     mySketchSolver->removeConstraint(anID);
+
+    // Remove value if exists
+    const ScalarWrapperPtr& aValue = aCW->valueParameter();
+    if (aValue) {
+      GCS::SET_pD aParToRemove;
+      aParToRemove.insert(aValue->scalar());
+      removeParameters(aParToRemove);
+    }
+
     // Remove constraint
     myConstraintMap.erase(aFound);
 
@@ -295,7 +306,8 @@ void PlaneGCSSolver_Storage::removeInvalidEntities()
   for (; aFIter != myFeatureMap.end(); aFIter++)
     if (!aFIter->first->data() || !aFIter->first->data()->isValid()) {
       anInvalidFeatures.push_back(aFIter->first);
-      aDestroyer.remove(aFIter->second);
+      if (aFIter->second)
+        aDestroyer.remove(aFIter->second);
 
       // remove invalid arc
       std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator
index f90aa3172c8298ba1eedeb9f6e38d621acc652fc..52408859619c66430373162997115b711760c8a7 100644 (file)
@@ -84,10 +84,6 @@ static ConstraintWrapperPtr
                         std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity1,
                         std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity2,
                         std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theIntermed);
-static ConstraintWrapperPtr
-  createConstraintTangent(const SketchSolver_ConstraintType& theType,
-                          std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity1,
-                          std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity2);
 static ConstraintWrapperPtr
   createConstraintMiddlePoint(std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
                               std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity);
@@ -200,15 +196,6 @@ ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint(
                                     GCS_EDGE_WRAPPER(theEntity2),
                                     anIntermediate);
     break;
-  case CONSTRAINT_TANGENT_CIRCLE_LINE:
-  case CONSTRAINT_TANGENT_CIRCLE_CIRCLE:
-    aResult = createConstraintTangent(theType,
-                                      GCS_EDGE_WRAPPER(theEntity1),
-                                      GCS_EDGE_WRAPPER(theEntity2));
-    break;
-  case CONSTRAINT_MULTI_TRANSLATION:
-  case CONSTRAINT_MULTI_ROTATION:
-  case CONSTRAINT_SYMMETRIC:
   default:
     break;
   }
@@ -465,60 +452,3 @@ ConstraintWrapperPtr createConstraintEqual(
     aResult->setValueParameter(theIntermed);
   return aResult;
 }
-
-ConstraintWrapperPtr createConstraintTangent(
-    const SketchSolver_ConstraintType& theType,
-    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity1,
-    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity2)
-{
-  GCSConstraintPtr aNewConstr;
-  if (theType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
-    GCSCurvePtr anEntCirc, anEntLine;
-    if (theEntity1->type() == ENTITY_LINE) {
-      anEntLine = theEntity1->entity();
-      anEntCirc = theEntity2->entity();
-    } else {
-      anEntLine = theEntity2->entity();
-      anEntCirc = theEntity1->entity();
-    }
-
-    std::shared_ptr<GCS::Circle> aCirc =
-      std::dynamic_pointer_cast<GCS::Circle>(anEntCirc);
-    std::shared_ptr<GCS::Line> aLine =
-      std::dynamic_pointer_cast<GCS::Line>(anEntLine);
-
-    aNewConstr =
-      GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
-  } else {
-    std::shared_ptr<GCS::Circle> aCirc1 =
-      std::dynamic_pointer_cast<GCS::Circle>(theEntity1->entity());
-    std::shared_ptr<GCS::Circle> aCirc2 =
-      std::dynamic_pointer_cast<GCS::Circle>(theEntity2->entity());
-
-    double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
-    double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
-    double aDist = sqrt(aDX * aDX + aDY * aDY);
-    aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
-        aCirc1->rad, aCirc2->rad, (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad))));
-  }
-
-  return ConstraintWrapperPtr(new PlaneGCSSolver_ConstraintWrapper(aNewConstr, theType));
-}
-
-bool PlaneGCSSolver_Tools::isArcArcTangencyInternal(
-    EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
-{
-  std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
-      GCS_EDGE_WRAPPER(theArc1)->entity());
-  std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
-      GCS_EDGE_WRAPPER(theArc2)->entity());
-
-  if (!aCirc1 || !aCirc2)
-    return false;
-
-  double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
-  double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
-  double aDist = sqrt(aDX * aDX + aDY * aDY);
-
-  return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
-}
index 56dfbbdbbb8793db3312707ee9eee472f5337546..db4820107eba0eb4c1e6f7983941cf2780cab9da 100644 (file)
@@ -54,11 +54,6 @@ namespace PlaneGCSSolver_Tools
   /// \brief Convert entity to line
   /// \return empty pointer if the entity is not a line
   std::shared_ptr<GeomAPI_Lin2d> line(FeaturePtr theFeature);
-
-  /// \brief Check if two connected arcs have centers
-  ///        in same direction relatively to connection point
-  bool isArcArcTangencyInternal(EntityWrapperPtr theArc1,
-                                EntityWrapperPtr theArc2);
 };
 
 #endif
index 8a61e1a993834fb60894db047030f289565d6b44..791de4cc2f3d470cbf1fd8ab32061bc49da1e028 100644 (file)
@@ -3,7 +3,10 @@
 #include <SketchSolver_ConstraintTangent.h>
 #include <SketchSolver_Error.h>
 
+#include <PlaneGCSSolver_EdgeWrapper.h>
+#include <PlaneGCSSolver_PointWrapper.h>
 #include <PlaneGCSSolver_Tools.h>
+#include <PlaneGCSSolver_UpdateCoincidence.h>
 
 #include <GeomAPI_Pnt2d.h>
 #include <SketchPlugin_Circle.h>
 
 #include <cmath>
 
+#define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(x)
 
-/// \brief Check whether the entities has only one shared point or less
-static bool hasSingleCoincidence(FeaturePtr theFeature1, FeaturePtr theFeature2)
-{
-  const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
-  const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
+/// \brief Obtain tangent features from the constraint
+static void getTangentFeatures(const ConstraintPtr& theConstraint,
+                               FeaturePtr& theFeature1,
+                               FeaturePtr& theFeature2);
 
-  // collect all shared coincidendes
-  std::set<FeaturePtr> aCoincidences;
-  std::set<AttributePtr>::const_iterator anIt;
-  for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
-    FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
-    if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
-      aCoincidences.insert(aRef);
-  }
-  int aNbCoinCidentPoints = 0;
-  for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
-    FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
-    if (aCoincidences.find(aRef) != aCoincidences.end())
-      ++aNbCoinCidentPoints;
-  }
+/// \brief Obtain all coincident constraints between features
+static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2);
 
-  return aNbCoinCidentPoints <= 1;
-}
+/// \brief Check whether the entities has only one shared point or less.
+///        Return list of coincident points.
+static std::list<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,
+                                     EntityWrapperPtr theArc2);
+
+static ConstraintWrapperPtr
+  createArcLineTangency(EntityWrapperPtr theEntity1,
+                        EntityWrapperPtr theEntity2,
+                        EntityWrapperPtr theShapedPoint = EntityWrapperPtr(),
+                        double*          theAngle = 0);
+
+static ConstraintWrapperPtr
+  createArcArcTangency(EntityWrapperPtr theEntity1,
+                       EntityWrapperPtr theEntity2,
+                       bool             theInternalTangency,
+                       EntityWrapperPtr theSharedPoint = EntityWrapperPtr(),
+                       double*          theAngle = 0);
 
-void SketchSolver_ConstraintTangent::getAttributes(
-    EntityWrapperPtr& theValue,
-    std::vector<EntityWrapperPtr>& theAttributes)
+
+void SketchSolver_ConstraintTangent::process()
 {
-  SketchSolver_Constraint::getAttributes(theValue, theAttributes);
-  if (!myErrorMsg.empty() || !theAttributes[2] || !theAttributes[3]) {
-    theAttributes.clear();
+  cleanErrorMsg();
+  if (!myBaseConstraint || !myStorage) {
+    // Not enough parameters are assigned
     return;
   }
 
+  EntityWrapperPtr aValue;
+  std::vector<EntityWrapperPtr> anAttributes;
+  SketchSolver_Constraint::getAttributes(aValue, anAttributes);
+  if (!myErrorMsg.empty())
+    return;
+
+  rebuild();
+  if (!myErrorMsg.empty())
+    return;
+
+  myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
+}
+
+void SketchSolver_ConstraintTangent::rebuild()
+{
+  if (mySolverConstraint)
+    myStorage->removeConstraint(myBaseConstraint);
+
+  mySolverConstraint = ConstraintWrapperPtr();
+  mySharedPoint = AttributePtr();
+
   // Check the quantity of entities of each type and their order (arcs first)
   int aNbLines = 0;
   int aNbCircles = 0;
-  std::vector<EntityWrapperPtr>::iterator anEntIt = theAttributes.begin() + 2;
-  for (; anEntIt != theAttributes.end(); ++anEntIt) {
+  std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
+  for (; anEntIt != myAttributes.end(); ++anEntIt) {
+    if (!(*anEntIt).get())
+      continue;
     if ((*anEntIt)->type() == ENTITY_LINE)
       ++aNbLines;
     else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE)
@@ -66,23 +98,38 @@ void SketchSolver_ConstraintTangent::getAttributes(
   }
   else if (aNbCircles == 2) {
     myType = CONSTRAINT_TANGENT_CIRCLE_CIRCLE;
-    isArcArcInternal =
-        PlaneGCSSolver_Tools::isArcArcTangencyInternal(theAttributes[2], theAttributes[3]);
+    isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back());
   }
   else {
     myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
     return;
   }
 
-  if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
-    AttributeRefAttrPtr aRefAttr = myBaseConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
-    FeaturePtr aFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
-    aRefAttr = myBaseConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
-    FeaturePtr aFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
+  FeaturePtr aFeature1, aFeature2;
+  getTangentFeatures(myBaseConstraint, aFeature1, aFeature2);
 
-    if (!hasSingleCoincidence(aFeature1, aFeature2))
-      myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
+  // check number of coincident points
+  std::list<AttributePtr> aCoincidentPoints = coincidentPoints(aFeature1, aFeature2);
+  if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 1) {
+    myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
+    return;
   }
+
+  EntityWrapperPtr aSharedPointEntity;
+  if (!aCoincidentPoints.empty()) {
+    mySharedPoint = aCoincidentPoints.front();
+    aSharedPointEntity = myStorage->entity(mySharedPoint);
+  }
+
+  if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
+    mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(),
+                                           aSharedPointEntity, &myCurveCurveAngle);
+  } else {
+    mySolverConstraint = createArcArcTangency(myAttributes.front(), myAttributes.back(),
+                            isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle);
+  }
+
+  myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
 }
 
 void SketchSolver_ConstraintTangent::adjustConstraint()
@@ -93,10 +140,220 @@ void SketchSolver_ConstraintTangent::adjustConstraint()
     EntityWrapperPtr anEntity2 =
         myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
 
-    if (isArcArcInternal != PlaneGCSSolver_Tools::isArcArcTangencyInternal(anEntity1, anEntity2)) {
-      // fully rebuld constraint, because it is unable to access attributes of PlaneGCS constraint
-      remove();
-      process();
+    if (isArcArcInternal != isArcArcTangencyInternal(anEntity1, anEntity2))
+      rebuild();
+  }
+}
+
+void SketchSolver_ConstraintTangent::notify(const FeaturePtr&      theFeature,
+                                            PlaneGCSSolver_Update* theUpdater)
+{
+  if (theFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
+    return;
+
+  // base constraint may become invalid (for example, during undo)
+  if (!myBaseConstraint->data() || !myBaseConstraint->data()->isValid())
+    return;
+
+  FeaturePtr aTgFeat1, aTgFeat2;
+  getTangentFeatures(myBaseConstraint, aTgFeat1, aTgFeat2);
+
+  bool isRebuild = false;
+  if (theFeature->data() && theFeature->data()->isValid()) {
+    // the constraint has been created
+    if (!mySharedPoint) {
+      // features has no shared point, check whether coincidence constraint binds these features)
+      int aNbCoincidentFeatures = 0;
+      for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+        AttributeRefAttrPtr aRefAttr = theFeature->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
+        if (!aRefAttr)
+          continue;
+
+        ObjectPtr anObj;
+        if (aRefAttr->isObject())
+          anObj = aRefAttr->object();
+        else
+          anObj = aRefAttr->attr()->owner();
+
+        FeaturePtr aFeature = ModelAPI_Feature::feature(anObj);
+        if (aFeature == aTgFeat1 || aFeature == aTgFeat2)
+          ++aNbCoincidentFeatures;
+      }
+
+      if (aNbCoincidentFeatures == 2)
+        isRebuild = true;
+    }
+  } else if (mySharedPoint) {
+    // The features are tangent in the shared point, but the coincidence has been removed.
+    // Check if the coincidence is the same.
+    std::list<AttributePtr> aCoincidentPoints = coincidentPoints(aTgFeat1, aTgFeat2);
+    isRebuild = true;
+    std::list<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)
+    rebuild();
+}
+
+
+
+
+// ==================   Auxiliary functions   =================================
+void getTangentFeatures(const ConstraintPtr& theConstraint,
+                              FeaturePtr&    theFeature1,
+                              FeaturePtr&    theFeature2)
+{
+  AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
+  theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
+  aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
+  theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
+}
+
+std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
+{
+  const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
+  const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
+
+  std::set<FeaturePtr> aCoincidences;
+  std::set<AttributePtr>::const_iterator anIt;
+
+  // collect coincidences referred to the first feature
+  for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
+    FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
+    if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
+      aCoincidences.insert(aRef);
+  }
+
+  // leave only coincidences referred to the second feature
+  std::set<FeaturePtr> aCoincidencesBetweenFeatures;
+  for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
+    FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
+    if (aCoincidences.find(aRef) != aCoincidences.end())
+      aCoincidencesBetweenFeatures.insert(aRef);
+  }
+
+  return aCoincidencesBetweenFeatures;
+}
+
+std::list<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
+{
+  std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
+  // collect points only
+  std::list<AttributePtr> aCoincidentPoints;
+  std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
+  for (; aCIt != aCoincidences.end(); ++ aCIt) {
+    for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+      AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
+      if (aRefAttr && !aRefAttr->isObject()) {
+        aCoincidentPoints.push_back(aRefAttr->attr());
+        break;
+      }
     }
   }
+  return aCoincidentPoints;
+}
+
+bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
+{
+  std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
+      GCS_EDGE_WRAPPER(theArc1)->entity());
+  std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
+      GCS_EDGE_WRAPPER(theArc2)->entity());
+
+  if (!aCirc1 || !aCirc2)
+    return false;
+
+  double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
+  double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
+  double aDist = sqrt(aDX * aDX + aDY * aDY);
+
+  return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
+}
+
+// sets angle to 0 or 180 degrees
+static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
+                                     const GCSCurvePtr& theCurve2,
+                                     const GCSPointPtr& thePoint,
+                                     double*            theAngle)
+{
+  double anAngle = GCS::System::calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
+  // bring angle to [-pi..pi]
+  if (anAngle >  PI) anAngle -= 2.0 * PI;
+  if (anAngle < -PI) anAngle += 2.0 * PI;
+  // set angle value according to the current angle between curves
+  if (fabs(anAngle) <= PI / 2.)
+    *theAngle = 0.0;
+  else
+    *theAngle = PI;
+}
+
+
+ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
+                                           EntityWrapperPtr theEntity2,
+                                           EntityWrapperPtr theSharedPoint,
+                                           double*          theAngle)
+{
+  EdgeWrapperPtr anEntLine, anEntCirc;
+  if (theEntity1->type() == ENTITY_LINE) {
+    anEntLine = GCS_EDGE_WRAPPER(theEntity1);
+    anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
+  } else {
+    anEntLine = GCS_EDGE_WRAPPER(theEntity2);
+    anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
+  }
+
+  std::shared_ptr<GCS::Circle> aCirc =
+      std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
+  std::shared_ptr<GCS::Line> aLine =
+      std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
+
+  GCSConstraintPtr aNewConstr;
+  if (theSharedPoint) {
+    std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
+    GCSPointPtr aPoint =
+        std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
+
+    adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
+    aNewConstr =
+        GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
+  } else {
+    aNewConstr =
+      GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
+  }
+
+  return  ConstraintWrapperPtr(
+      new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
+}
+
+ConstraintWrapperPtr createArcArcTangency(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());
+
+  GCSConstraintPtr aNewConstr;
+  if (theSharedPoint) {
+    std::shared_ptr<GCS::Arc> anArc1 = std::dynamic_pointer_cast<GCS::Arc>(aCirc1);
+    std::shared_ptr<GCS::Arc> anArc2 = std::dynamic_pointer_cast<GCS::Arc>(aCirc2);
+    GCSPointPtr aPoint =
+        std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
+
+    adjustAngleBetweenCurves(anArc1, anArc2, aPoint, theAngle);
+    aNewConstr =
+        GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc1, *anArc2, *aPoint, theAngle));
+  } else {
+    aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
+                                  aCirc1->rad, aCirc2->rad, theInternalTangency));
+  }
+
+  return ConstraintWrapperPtr(
+      new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE));
 }
index 96428b47fcce2ee688a2d4b9f49c0fd00b0fb267..3f8524c340ac0b7d1a5261293eeab1c0c0fcbd5f 100644 (file)
@@ -19,15 +19,20 @@ public:
   /// Constructor based on SketchPlugin constraint
   SketchSolver_ConstraintTangent(ConstraintPtr theConstraint) :
       SketchSolver_Constraint(theConstraint),
-      isArcArcInternal(false)
+      isArcArcInternal(false),
+      myCurveCurveAngle(0.0)
   {}
 
+  /// \brief Notify this object about the feature is changed somewhere
+  virtual void notify(const FeaturePtr&      theFeature,
+                      PlaneGCSSolver_Update* theUpdater);
+
 protected:
-  /// \brief Generate list of attributes of constraint in order useful for constraints
-  /// \param[out] theValue      numerical characteristic of constraint (e.g. distance)
-  /// \param[out] theAttributes list of attributes to be filled
-  virtual void getAttributes(EntityWrapperPtr&              theValue,
-                             std::vector<EntityWrapperPtr>& theAttributes);
+  /// \brief Converts SketchPlugin constraint to a list of solver constraints
+  virtual void process();
+
+  /// \brief Remove current constraint from the storage and build is again
+  void rebuild();
 
   /// \brief This method is used in derived objects to check consistency of constraint.
   ///        E.g. the distance between line and point may be signed.
@@ -35,6 +40,8 @@ protected:
 
 private:
   bool isArcArcInternal;
+  double myCurveCurveAngle;
+  AttributePtr mySharedPoint;
 };
 
 #endif