Salome HOME
Add ExchangeAPI, fillAttribute in ModelHighAPI_Tools, ModelHighAPI_Selection and...
[modules/shaper.git] / src / SketchSolver / SketchSolver_Group.cpp
index 1da845930f9b433fe7eec9afc982b1c0141d01f4..a39aab6e6a535330fec2df414fbec7dfc3e6fd19 100644 (file)
@@ -12,7 +12,7 @@
 #include <SketchSolver_Error.h>
 #include <SketchSolver_Manager.h>
 
-#include <Events_Error.h>
+#include <Events_InfoMessage.h>
 #include <Events_Loop.h>
 #include <ModelAPI_AttributeString.h>
 #include <ModelAPI_Events.h>
@@ -98,6 +98,11 @@ SketchSolver_Group::~SketchSolver_Group()
 {
   myConstraints.clear();
   GroupIndexer::REMOVE_GROUP(myID);
+  // send the message that there is no more conflicting constraints
+  if (!myConflictingConstraints.empty()) {
+    sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
+    myConflictingConstraints.clear();
+  }
 }
 
 // ============================================================================
@@ -121,7 +126,13 @@ bool SketchSolver_Group::isInteract(FeaturePtr theFeature) const
   if (isEmpty())
     return true;
   // Check interaction with the storage
-  return myStorage->isInteract(theFeature);
+  bool isInteracted = myStorage->isInteract(theFeature);
+  ConstraintConstraintMap::const_iterator anIt = myConstraints.begin();
+  for (; !isInteracted && anIt != myConstraints.end(); ++anIt)
+    if (anIt->first->getKind() == SketchPlugin_MultiRotation::ID() ||
+        anIt->first->getKind() == SketchPlugin_MultiTranslation::ID())
+      isInteracted = anIt->second->isUsed(theFeature);
+  return isInteracted;
 }
 
 // ============================================================================
@@ -155,9 +166,12 @@ bool SketchSolver_Group::changeConstraint(
     if (!aConstraint->error().empty()) {
       if (aConstraint->error() == SketchSolver_Error::NOT_INITIALIZED())
         return false; // some attribute are not initialized yet, don't show message
-      Events_Error::send(aConstraint->error(), this);
+      Events_InfoMessage("SketchSolver_Group", aConstraint->error(), this).send();
     }
     myConstraints[theConstraint] = aConstraint;
+
+    if (theConstraint->getKind() == SketchPlugin_ConstraintCoincidence::ID())
+      notifyCoincidenceChanged(myConstraints[theConstraint]);
   }
   else
     myConstraints[theConstraint]->update();
@@ -182,20 +196,35 @@ bool SketchSolver_Group::changeConstraint(
   return true;
 }
 
+// Update constraints if they contain specific feature
 static void updateMultiConstraints(ConstraintConstraintMap& theConstraints, FeaturePtr theFeature)
 {
   ConstraintConstraintMap::iterator aCIt = theConstraints.begin();
   for (; aCIt != theConstraints.end(); ++aCIt) {
-    if ((aCIt->second->getType() == CONSTRAINT_MULTI_ROTATION ||
-         aCIt->second->getType() == CONSTRAINT_MULTI_TRANSLATION)
+    SketchSolver_ConstraintType aType = aCIt->second->getType();
+    if ((aType == CONSTRAINT_MULTI_ROTATION ||
+         aType == CONSTRAINT_MULTI_TRANSLATION)
         && aCIt->second->isUsed(theFeature))
       std::dynamic_pointer_cast<SketchSolver_ConstraintMulti>(aCIt->second)->update(true);
-    else if (aCIt->second->getType() == CONSTRAINT_TANGENT_CIRCLE_LINE
+    else if ((aType == CONSTRAINT_TANGENT_CIRCLE_LINE ||
+              aType == CONSTRAINT_SYMMETRIC || aType == CONSTRAINT_ANGLE)
              && aCIt->second->isUsed(theFeature))
       aCIt->second->update();
   }
 }
 
+// Recalculate slave features of the Multi constraints
+static void updateMultiConstraints(ConstraintConstraintMap& theConstraints)
+{
+  ConstraintConstraintMap::iterator aCIt = theConstraints.begin();
+  for (; aCIt != theConstraints.end(); ++aCIt) {
+    SketchSolver_ConstraintType aType = aCIt->second->getType();
+    if ((aType == CONSTRAINT_MULTI_ROTATION ||
+         aType == CONSTRAINT_MULTI_TRANSLATION))
+      std::dynamic_pointer_cast<SketchSolver_ConstraintMulti>(aCIt->second)->update(true);
+  }
+}
+
 bool SketchSolver_Group::updateFeature(FeaturePtr theFeature)
 {
   if (!checkFeatureValidity(theFeature))
@@ -225,6 +254,9 @@ void SketchSolver_Group::moveFeature(FeaturePtr theFeature)
   myStorage->blockEvents(true);
   myStorage->refresh(true);
 
+  // Secondly, search attributes of the feature in the list of the Multi constraints and update them
+  updateMultiConstraints(myConstraints, theFeature);
+
   // Then, create temporary Fixed constraint
   SolverConstraintPtr aConstraint = aBuilder->createMovementConstraint(theFeature);
   if (!aConstraint)
@@ -233,17 +265,18 @@ void SketchSolver_Group::moveFeature(FeaturePtr theFeature)
   if (aConstraint->error().empty())
     setTemporary(aConstraint);
 
-  // Secondly, search attributes of the feature in the list of the Multi constraints and update them
-  updateMultiConstraints(myConstraints, theFeature);
-
   // Workaround to process arcs.
   // When move unconstrained arc, add temporary constraint to fix radius.
   if (theFeature->getKind() == SketchPlugin_Arc::ID()) {
+    bool hasDup = myStorage->hasDuplicatedConstraint();
     SolverConstraintPtr aFixedRadius = aBuilder->createFixedArcRadiusConstraint(theFeature);
     if (aFixedRadius) {
       aFixedRadius->process(myStorage, getId(), getWorkplaneId());
-      if (aFixedRadius->error().empty())
+      hasDup = myStorage->hasDuplicatedConstraint() && !hasDup;
+      if (aFixedRadius->error().empty() && !hasDup)
         setTemporary(aFixedRadius);
+      else
+        aFixedRadius->remove();
     }
   }
 }
@@ -295,7 +328,8 @@ bool SketchSolver_Group::resolveConstraints()
 {
   bool aResolved = false;
   bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
-  if (myStorage->isNeedToResolve() && !isGroupEmpty) {
+  if (myStorage->isNeedToResolve() &&
+      (!isGroupEmpty || !myConflictingConstraints.empty() || myPrevResult == STATUS_FAILED)) {
     if (!mySketchSolver)
       mySketchSolver = SketchSolver_Manager::instance()->builder()->createSolver();
 
@@ -308,7 +342,7 @@ bool SketchSolver_Group::resolveConstraints()
     try {
       if (myStorage->hasDuplicatedConstraint())
         aResult = STATUS_INCONSISTENT;
-      else {
+      else if (!isGroupEmpty) {
         // To avoid overconstraint situation, we will remove temporary constraints one-by-one
         // and try to find the case without overconstraint
         bool isLastChance = false;
@@ -340,23 +374,36 @@ bool SketchSolver_Group::resolveConstraints()
     }
     if (aResult == STATUS_OK || aResult == STATUS_EMPTYSET) {  // solution succeeded, store results into correspondent attributes
       myStorage->refresh();
+      updateMultiConstraints(myConstraints);
       if (myPrevResult != STATUS_OK || myPrevResult == STATUS_UNKNOWN) {
         getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
-        // the error message should be changed before sending the message
-        sendMessage(EVENT_SOLVER_REPAIRED);
+        std::set<ObjectPtr> aConflicting = myConflictingConstraints;
+        myConflictingConstraints.clear();
         myPrevResult = STATUS_OK;
+        // the error message should be changed before sending the message
+        sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
       }
     } else {
       mySketchSolver->undo();
       if (!myConstraints.empty()) {
-//      Events_Error::send(SketchSolver_Error::CONSTRAINTS(), this);
+        // the error message should be changed before sending the message
         getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(SketchSolver_Error::CONSTRAINTS());
         if (myPrevResult != aResult || myPrevResult == STATUS_UNKNOWN) {
           // Obtain list of conflicting constraints
           std::set<ObjectPtr> aConflicting = myStorage->getConflictingConstraints(mySketchSolver);
 
-          // the error message should be changed before sending the message
-          sendMessage(EVENT_SOLVER_FAILED, aConflicting);
+          if (myConflictingConstraints.empty())
+            sendMessage(EVENT_SOLVER_FAILED, aConflicting);
+          else {
+            std::set<ObjectPtr>::iterator anIt = aConflicting.begin();
+            for (; anIt != aConflicting.end(); ++anIt)
+              myConflictingConstraints.erase(*anIt);
+            if (!myConflictingConstraints.empty()) {
+              // some constraints does not conflict, send corresponding message
+              sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
+            }
+          }
+          myConflictingConstraints = aConflicting;
           myPrevResult = aResult;
         }
       }
@@ -440,27 +487,39 @@ void SketchSolver_Group::splitGroup(std::list<SketchSolver_Group*>& theCuts)
   }
 
   std::list<SketchSolver_Group*>::iterator aCutsIter;
-  aUnuseIt = anUnusedConstraints.begin();
-  for ( ; aUnuseIt != anUnusedConstraints.end(); ++aUnuseIt) {
-    // Remove unused constraints
+  // Remove unused constraints
+  for (aUnuseIt = anUnusedConstraints.begin(); aUnuseIt != anUnusedConstraints.end(); ++aUnuseIt)
     removeConstraint(*aUnuseIt);
-    // Try to append constraint to already existent group
-    for (aCutsIter = theCuts.begin(); aCutsIter != theCuts.end(); ++aCutsIter)
-      if ((*aCutsIter)->isInteract(*aUnuseIt)) {
-        (*aCutsIter)->changeConstraint(*aUnuseIt);
-        break;
-      }
-    if (aCutsIter == theCuts.end()) {
+
+  SketchSolver_Group* aBaseGroup;
+  for (aUnuseIt = anUnusedConstraints.begin(); aUnuseIt != anUnusedConstraints.end(); ++aUnuseIt) {
+    aBaseGroup = 0;
+    aCutsIter = theCuts.begin();
+    // Try to append constraint to the current group
+    if (isInteract(*aUnuseIt)) {
+      changeConstraint(*aUnuseIt);
+      aBaseGroup = this;
+    } else {
+      // Try to append constraint to already existent group
+      for (; aCutsIter != theCuts.end(); ++aCutsIter)
+        if ((*aCutsIter)->isInteract(*aUnuseIt)) {
+          (*aCutsIter)->changeConstraint(*aUnuseIt);
+          break;
+        }
+    }
+
+    if (aCutsIter == theCuts.end() && !aBaseGroup) {
       // Add new group
       SketchSolver_Group* aGroup = new SketchSolver_Group(mySketch);
       aGroup->changeConstraint(*aUnuseIt);
       theCuts.push_back(aGroup);
     } else {
+      if (!aBaseGroup)
+        aBaseGroup = *aCutsIter++;
       // Find other groups interacting with constraint
-      std::list<SketchSolver_Group*>::iterator aBaseGroupIt = aCutsIter;
-      for (++aCutsIter; aCutsIter != theCuts.end(); ++aCutsIter)
+      for (; aCutsIter != theCuts.end(); ++aCutsIter)
         if ((*aCutsIter)->isInteract(*aUnuseIt)) {
-          (*aBaseGroupIt)->mergeGroups(**aCutsIter);
+          aBaseGroup->mergeGroups(**aCutsIter);
           std::list<SketchSolver_Group*>::iterator aRemoveIt = aCutsIter--;
           theCuts.erase(aRemoveIt);
         }
@@ -537,6 +596,8 @@ void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
   for (; aCIter != myConstraints.end(); aCIter++)
     if (aCIter->first == theConstraint) {
       aCIter->second->remove(); // the constraint is not fully removed
+      if (aCIter->first->getKind() == SketchPlugin_ConstraintCoincidence::ID())
+        notifyCoincidenceChanged(aCIter->second);
       break;
     }
   if (aCIter != myConstraints.end())
@@ -650,3 +711,13 @@ std::list<FeaturePtr> SketchSolver_Group::selectApplicableFeatures(const std::se
   return aResult;
 }
 
+void SketchSolver_Group::notifyCoincidenceChanged(SolverConstraintPtr theCoincidence)
+{
+  const std::list<EntityWrapperPtr>& aCoincident = theCoincidence->attributes();
+  EntityWrapperPtr anAttr1 = aCoincident.front();
+  EntityWrapperPtr anAttr2 = aCoincident.back();
+
+  ConstraintConstraintMap::iterator anIt = myConstraints.begin();
+  for (; anIt != myConstraints.end(); ++anIt)
+    anIt->second->notifyCoincidenceChanged(anAttr1, anAttr2);
+}