Salome HOME
Issue #2148: Moving an arc displays a circle
[modules/shaper.git] / src / SketchSolver / SketchSolver_Group.cpp
index c02c9a6d03e3722fdc2a61ddbcfb210c2ac9c561..a0a7d362408d8fa2d5b6df058fcbc7d3b1511d0a 100644 (file)
 #include <Events_InfoMessage.h>
 #include <ModelAPI_AttributeString.h>
 #include <ModelAPI_Events.h>
+#include <SketchPlugin_ConstraintMirror.h>
 #include <SketchPlugin_ConstraintRigid.h>
+#include <SketchPlugin_MultiRotation.h>
+#include <SketchPlugin_MultiTranslation.h>
 
 
 static void sendMessage(const char* theMessageName)
@@ -60,7 +63,8 @@ SketchSolver_Group::SketchSolver_Group(const CompositeFeaturePtr& theWorkplane)
   : mySketch(theWorkplane),
     myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN),
     myDOF(0),
-    myIsEventsBlocked(false)
+    myIsEventsBlocked(false),
+    myMultiConstraintUpdateStack(0)
 {
   mySketchSolver = SolverPtr(new PlaneGCSSolver_Solver);
   myStorage = StoragePtr(new PlaneGCSSolver_Storage(mySketchSolver));
@@ -100,6 +104,9 @@ bool SketchSolver_Group::changeConstraint(
   }
   else
     myConstraints[theConstraint]->update();
+
+  // constraint is created/updated => reset stack of "multi" constraints updates
+  myMultiConstraintUpdateStack = 0;
   return true;
 }
 
@@ -110,20 +117,27 @@ bool SketchSolver_Group::updateFeature(FeaturePtr theFeature)
 
 bool SketchSolver_Group::moveFeature(FeaturePtr theFeature)
 {
-  if (myDOF == 0) {
+  bool isFeatureExists = (myStorage->entity(theFeature).get() != 0);
+  if (myDOF == 0 && isFeatureExists) {
     // avoid moving elements of fully constrained sketch
     myStorage->refresh();
     return true;
   }
 
   // Create temporary Fixed constraint
-  SolverConstraintPtr aConstraint = PlaneGCSSolver_Tools::createMovementConstraint(theFeature);
+  std::shared_ptr<SketchSolver_ConstraintFixed> aConstraint =
+      PlaneGCSSolver_Tools::createMovementConstraint(theFeature);
   if (!aConstraint)
     return false;
-  aConstraint->process(myStorage, myIsEventsBlocked);
-  if (aConstraint->error().empty())
+  SolverConstraintPtr(aConstraint)->process(myStorage, myIsEventsBlocked);
+  if (aConstraint->error().empty()) {
     setTemporary(aConstraint);
-  else
+    if (!myStorage->isEmpty())
+      myStorage->setNeedToResolve(true);
+
+    mySketchSolver->initialize();
+    aConstraint->moveFeature();
+  } else
     myStorage->notify(theFeature);
 
   return true;
@@ -136,6 +150,18 @@ bool SketchSolver_Group::moveFeature(FeaturePtr theFeature)
 // ============================================================================
 bool SketchSolver_Group::resolveConstraints()
 {
+  static const int MAX_STACK_SIZE = 5;
+  // check the "Multi" constraints do not drop sketch into infinite loop
+  if (myMultiConstraintUpdateStack > MAX_STACK_SIZE) {
+    myMultiConstraintUpdateStack = 0;
+    myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
+    // generate error message due to loop update of the sketch
+    getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
+      ->setValue(SketchSolver_Error::INFINITE_LOOP());
+    sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
+    return false;
+  }
+
   bool aResolved = false;
   bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
   if (myStorage->isNeedToResolve() &&
@@ -146,6 +172,12 @@ bool SketchSolver_Group::resolveConstraints()
     try {
       if (!isGroupEmpty)
         aResult = mySketchSolver->solve();
+      if (aResult == PlaneGCSSolver_Solver::STATUS_FAILED &&
+          !myTempConstraints.empty()) {
+        mySketchSolver->undo();
+        removeTemporaryConstraints();
+        aResult = mySketchSolver->solve();
+      }
     } catch (...) {
       getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
         ->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
@@ -163,19 +195,26 @@ bool SketchSolver_Group::resolveConstraints()
         aResult == PlaneGCSSolver_Solver::STATUS_EMPTYSET) {
       myStorage->setNeedToResolve(false);
       myStorage->refresh();
-////      updateMultiConstraints(myConstraints);
-////      // multi-constraints updated some parameters, need to store them
-////      if (myStorage->isNeedToResolve())
-////        resolveConstraints();
 
-      if (myPrevResult != PlaneGCSSolver_Solver::STATUS_OK ||
-          myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
-        getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
-        std::set<ObjectPtr> aConflicting = myConflictingConstraints;
-        myConflictingConstraints.clear();
-        myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
-        // the error message should be changed before sending the message
-        sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
+      // additional check that copied entities used in Mirror and other "Multi" constraints
+      // is not connected with their originals by constraints.
+      myMultiConstraintUpdateStack += 1;
+      aResolved = true;
+      if (myStorage->isNeedToResolve())
+        aResolved = resolveConstraints();
+
+      if (aResolved) {
+        myMultiConstraintUpdateStack -= 1;
+
+        if (myPrevResult != PlaneGCSSolver_Solver::STATUS_OK ||
+            myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
+          getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
+          std::set<ObjectPtr> aConflicting = myConflictingConstraints;
+          myConflictingConstraints.clear();
+          myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
+          // the error message should be changed before sending the message
+          sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
+        }
       }
 
       // show degrees of freedom
@@ -209,18 +248,6 @@ bool SketchSolver_Group::resolveConstraints()
       }
     }
 
-    aResolved = true;
-  } else if (!isGroupEmpty) {
-    // Check if the group contains only constraints Fixed, update parameters by stored values
-    aResolved = true;
-    ConstraintConstraintMap::iterator aCIt = myConstraints.begin();
-    for (; aCIt != myConstraints.end(); ++aCIt)
-      if (aCIt->first->getKind() != SketchPlugin_ConstraintRigid::ID()) {
-        aResolved = false;
-        break;
-      }
-    if (aCIt == myConstraints.end())
-      myStorage->refresh();
   } else if (isGroupEmpty && isWorkplaneValid())
     computeDoF();
   removeTemporaryConstraints();
@@ -237,6 +264,8 @@ void SketchSolver_Group::computeDoF()
 {
   std::ostringstream aDoFMsg;
   int aDoF = mySketchSolver->dof();
+  /// "DoF = 0" content of string value is used in PartSet by Sketch edit
+  /// If it is changed, it should be corrected also there
   if (aDoF == 0)
     aDoFMsg << "Sketch is fully fixed (DoF = 0)";
   else
@@ -260,7 +289,7 @@ void SketchSolver_Group::computeDoF()
 // ============================================================================
 void SketchSolver_Group::repairConsistency()
 {
-  if (!myStorage->isConsistent()) {
+  if (!areConstraintsValid() || !myStorage->areFeaturesValid()) {
     // remove invalid constraints
     std::set<ConstraintPtr> anInvalidConstraints;
     ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
@@ -312,6 +341,9 @@ void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
   for (; aCIter != myConstraints.end(); aCIter++)
     if (aCIter->first == theConstraint) {
       aCIter->second->remove(); // the constraint is not fully removed
+
+      // constraint is removed => reset stack of "multi" constraints updates
+      myMultiConstraintUpdateStack = 0;
       break;
     }
   if (aCIter != myConstraints.end())
@@ -348,3 +380,13 @@ void SketchSolver_Group::blockEvents(bool isBlocked)
 
   myIsEventsBlocked = isBlocked;
 }
+
+bool SketchSolver_Group::areConstraintsValid() const
+{
+  // Check the constraints are valid
+  ConstraintConstraintMap::const_iterator aCIter = myConstraints.begin();
+  for (; aCIter != myConstraints.end(); ++aCIter)
+    if (!aCIter->first->data() || !aCIter->first->data()->isValid())
+      return false;
+  return true;
+}