Salome HOME
#1802 Crash when I create a circle on projected axis
[modules/shaper.git] / src / SketchSolver / SketchSolver_Group.cpp
index d62f995e15c507145b5363f6d26dd8f503028df6..67e0d6cccf0e80d795d331a98bc07d529159a4db 100644 (file)
 #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>
 #include <ModelAPI_Session.h>
 #include <ModelAPI_Validator.h>
 
+#include <SketchPlugin_Arc.h>
 #include <SketchPlugin_ConstraintAngle.h>
 #include <SketchPlugin_ConstraintCoincidence.h>
 #include <SketchPlugin_ConstraintDistance.h>
@@ -31,6 +32,7 @@
 #include <SketchPlugin_ConstraintPerpendicular.h>
 #include <SketchPlugin_ConstraintRadius.h>
 #include <SketchPlugin_ConstraintRigid.h>
+#include <SketchPlugin_ConstraintSplit.h>
 #include <SketchPlugin_ConstraintTangent.h>
 #include <SketchPlugin_ConstraintVertical.h>
 #include <SketchPlugin_MultiRotation.h>
@@ -68,6 +70,15 @@ static void sendMessage(const char* theMessageName)
   Events_Loop::loop()->send(aMessage);
 }
 
+static void sendMessage(const char* theMessageName, const std::set<ObjectPtr>& theConflicting)
+{
+  std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
+      std::shared_ptr<ModelAPI_SolverFailedMessage>(
+      new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName)));
+  aMessage->setObjects(theConflicting);
+  Events_Loop::loop()->send(aMessage);
+}
+
 
 
 // ========================================================
@@ -77,7 +88,7 @@ static void sendMessage(const char* theMessageName)
 SketchSolver_Group::SketchSolver_Group(
     std::shared_ptr<ModelAPI_CompositeFeature> theWorkplane)
     : myID(GroupIndexer::NEW_GROUP()),
-      myPrevSolved(true)
+      myPrevResult(STATUS_UNKNOWN)
 {
   // Initialize workplane
   myWorkplaneID = EID_UNKNOWN;
@@ -88,6 +99,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();
+  }
 }
 
 // ============================================================================
@@ -111,7 +127,26 @@ 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);
+      if (isInteracted)
+        break;
+      // if theFeature is a constraint, check its attributes
+      ConstraintPtr aConstraint = std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
+      if (!aConstraint)
+        continue;
+      for (int i = 0; i < 4 && !isInteracted; ++i) {
+        AttributeRefAttrPtr aRefAttr = aConstraint->refattr(aConstraint->ATTRIBUTE(i));
+        if (!aRefAttr)
+          continue;
+        isInteracted = anIt->second->isUsed((AttributePtr)aRefAttr);
+      }
+    }
+  return isInteracted;
 }
 
 // ============================================================================
@@ -145,9 +180,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();
@@ -172,37 +210,31 @@ bool SketchSolver_Group::changeConstraint(
   return true;
 }
 
-
-void SketchSolver_Group::updateConstraints()
+// Update constraints if they contain specific feature
+static void updateMultiConstraints(ConstraintConstraintMap& theConstraints, FeaturePtr theFeature)
 {
-  std::set<SolverConstraintPtr> aPostponed; // postponed constraints Multi-Rotation and Multi-Translation
-
-  ConstraintConstraintMap::iterator anIt = myConstraints.begin();
-  for (; anIt != myConstraints.end(); ++anIt) {
-    if (myChangedConstraints.find(anIt->first) == myChangedConstraints.end())
-      continue;
-    if (anIt->first->getKind() == SketchPlugin_MultiRotation::ID() ||
-        anIt->first->getKind() == SketchPlugin_MultiTranslation::ID())
-      aPostponed.insert(anIt->second);
-    else
-      anIt->second->update();
+  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)
+        && aCIt->second->isUsed(theFeature))
+      std::dynamic_pointer_cast<SketchSolver_ConstraintMulti>(aCIt->second)->update(true);
+    else if ((aType == CONSTRAINT_TANGENT_CIRCLE_LINE || aType == CONSTRAINT_TANGENT_ARC_ARC ||
+              aType == CONSTRAINT_SYMMETRIC || aType == CONSTRAINT_ANGLE)
+             && aCIt->second->isUsed(theFeature))
+      aCIt->second->update();
   }
-
-  // Update postponed constraints
-  std::set<SolverConstraintPtr>::iterator aSCIter = aPostponed.begin();
-  for (; aSCIter != aPostponed.end(); ++aSCIter)
-    (*aSCIter)->update();
-
-  myChangedConstraints.clear();
 }
 
-static void updateMultiConstraints(ConstraintConstraintMap& theConstraints, FeaturePtr theFeature)
+// Recalculate slave features of the Multi constraints
+static void updateMultiConstraints(ConstraintConstraintMap& theConstraints)
 {
   ConstraintConstraintMap::iterator aCIt = theConstraints.begin();
   for (; aCIt != theConstraints.end(); ++aCIt) {
-    if ((aCIt->second->getType() == CONSTRAINT_MULTI_ROTATION ||
-         aCIt->second->getType() == CONSTRAINT_MULTI_TRANSLATION)
-        && aCIt->second->isUsed(theFeature))
+    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);
   }
 }
@@ -212,15 +244,23 @@ bool SketchSolver_Group::updateFeature(FeaturePtr theFeature)
   if (!checkFeatureValidity(theFeature))
     return false;
 
-  myStorage->blockEvents(true);
+  bool isBlocked = myStorage->isEventsBlocked();
+  if (!isBlocked)
+    myStorage->blockEvents(true);
+
   myStorage->refresh(true);
   bool isUpdated = myStorage->update(theFeature);
 
   updateMultiConstraints(myConstraints, theFeature);
+
+  // events were not blocked before, the feature has not been updated,
+  // so it is necessary to revert blocking
+  if (!isUpdated && !isBlocked)
+    myStorage->blockEvents(false);
   return isUpdated;
 }
 
-void SketchSolver_Group::moveFeature(FeaturePtr theFeature)
+bool SketchSolver_Group::moveFeature(FeaturePtr theFeature)
 {
   BuilderPtr aBuilder = SketchSolver_Manager::instance()->builder();
 
@@ -228,16 +268,32 @@ 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)
-    return;
+    return false;
   aConstraint->process(myStorage, getId(), getWorkplaneId());
   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());
+      hasDup = myStorage->hasDuplicatedConstraint() && !hasDup;
+      if (aFixedRadius->error().empty() && !hasDup)
+        setTemporary(aFixedRadius);
+      else
+        aFixedRadius->remove();
+    }
+  }
+  return true;
 }
 
 // ============================================================================
@@ -285,12 +341,10 @@ bool SketchSolver_Group::updateWorkplane()
 // ============================================================================
 bool SketchSolver_Group::resolveConstraints()
 {
-  if (!myChangedConstraints.empty())
-    updateConstraints();
-
   bool aResolved = false;
-  bool isGroupEmpty = isEmpty();
-  if (myStorage->isNeedToResolve() && !isGroupEmpty) {
+  bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
+  if (myStorage->isNeedToResolve() &&
+      (!isGroupEmpty || !myConflictingConstraints.empty() || myPrevResult == STATUS_FAILED)) {
     if (!mySketchSolver)
       mySketchSolver = SketchSolver_Manager::instance()->builder()->createSolver();
 
@@ -303,7 +357,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;
@@ -311,10 +365,10 @@ bool SketchSolver_Group::resolveConstraints()
           aResult = mySketchSolver->solve();
           if (aResult == STATUS_OK || aResult == STATUS_EMPTYSET || isLastChance)
             break;
-          // try to update parameters and resolve once again
-          ConstraintConstraintMap::iterator aConstrIt = myConstraints.begin();
-          for (; aConstrIt != myConstraints.end(); ++aConstrIt)
-            aConstrIt->second->update();
+////          // try to update parameters and resolve once again
+////          ConstraintConstraintMap::iterator aConstrIt = myConstraints.begin();
+////          for (; aConstrIt != myConstraints.end(); ++aConstrIt)
+////            aConstrIt->second->update();
           isLastChance = true;
 
           removeTemporaryConstraints();
@@ -324,46 +378,73 @@ bool SketchSolver_Group::resolveConstraints()
       }
     } catch (...) {
 //      Events_Error::send(SketchSolver_Error::SOLVESPACE_CRASH(), this);
-      getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
-      if (myPrevSolved) {
+      getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
+        ->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
+      if (myPrevResult == STATUS_OK || myPrevResult == STATUS_UNKNOWN) {
         // the error message should be changed before sending the message
         sendMessage(EVENT_SOLVER_FAILED);
-        myPrevSolved = false;
+        myPrevResult = STATUS_FAILED;
       }
       mySketchSolver->undo();
       return false;
     }
-    if (aResult == STATUS_OK || aResult == STATUS_EMPTYSET) {  // solution succeeded, store results into correspondent attributes
+    // solution succeeded, store results into correspondent attributes
+    if (aResult == STATUS_OK || aResult == STATUS_EMPTYSET) {  
+      myStorage->setNeedToResolve(false);
       myStorage->refresh();
-      if (!myPrevSolved) {
+      updateMultiConstraints(myConstraints);
+      // multi-constraints updated some parameters, need to store them
+      if (myStorage->isNeedToResolve()) 
+        resolveConstraints();
+
+      if (myPrevResult != STATUS_OK || myPrevResult == STATUS_UNKNOWN) {
         getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
+        std::set<ObjectPtr> aConflicting = myConflictingConstraints;
+        myConflictingConstraints.clear();
+        myPrevResult = STATUS_OK;
         // the error message should be changed before sending the message
-        sendMessage(EVENT_SOLVER_REPAIRED);
-        myPrevSolved = true;
+        sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
       }
     } else {
       mySketchSolver->undo();
       if (!myConstraints.empty()) {
-//      Events_Error::send(SketchSolver_Error::CONSTRAINTS(), this);
-        getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(SketchSolver_Error::CONSTRAINTS());
-        if (myPrevSolved) {
-          // the error message should be changed before sending the message
-          sendMessage(EVENT_SOLVER_FAILED);
-          myPrevSolved = false;
+        // 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 || 
+            myPrevResult == STATUS_FAILED) {
+          // Obtain list of conflicting constraints
+          std::set<ObjectPtr> aConflicting = myStorage->getConflictingConstraints(mySketchSolver);
+
+          if (!myConflictingConstraints.empty()) {
+            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;
+          if (!myConflictingConstraints.empty())
+            sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
+          myPrevResult = aResult;
         }
       }
     }
 
     aResolved = true;
   } else if (!isGroupEmpty) {
-    // Check there are constraints Fixed. If they exist, update parameters by stored values
+    // 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 = true;
+      if (aCIt->first->getKind() != SketchPlugin_ConstraintRigid::ID()) {
+        aResolved = false;
         break;
       }
-    if (aCIt != myConstraints.end())
+    if (aCIt == myConstraints.end())
       myStorage->refresh();
   }
   removeTemporaryConstraints();
@@ -407,7 +488,8 @@ void SketchSolver_Group::splitGroup(std::list<SketchSolver_Group*>& theCuts)
 {
   // New storage will be used in trimmed way to store the list of constraint interacted together.
   StoragePtr aNewStorage = SketchSolver_Manager::instance()->builder()->createStorage(getId());
-  std::list<ConstraintWrapperPtr> aDummyVec; // empty vector to avoid creation of solver's constraints
+  // empty vector to avoid creation of solver's constraints
+  std::list<ConstraintWrapperPtr> aDummyVec; 
 
   // Obtain constraints, which should be separated
   std::list<ConstraintPtr> anUnusedConstraints;
@@ -419,7 +501,8 @@ void SketchSolver_Group::splitGroup(std::list<SketchSolver_Group*>& theCuts)
       anUnusedConstraints.push_back(aCIter->first);
   }
 
-  // Check the unused constraints once again, because they may become interacted with new storage since adding constraints
+  // Check the unused constraints once again, 
+  // because they may become interacted with new storage since adding constraints
   std::list<ConstraintPtr>::iterator aUnuseIt = anUnusedConstraints.begin();
   while (aUnuseIt != anUnusedConstraints.end()) {
     if (aNewStorage->isInteract(FeaturePtr(*aUnuseIt))) {
@@ -432,27 +515,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);
         }
@@ -525,27 +620,22 @@ void SketchSolver_Group::removeTemporaryConstraints()
 // ============================================================================
 void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
 {
-  bool isFullyRemoved = true;
   ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
   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())
     myConstraints.erase(aCIter);
-}
-
-// ============================================================================
-//  Function: isComplexConstraint
-//  Class:    SketchSolver_Group
-//  Purpose:  verifies the constraint is complex, i.e. it needs another constraints to be created before
-// ============================================================================
-bool SketchSolver_Group::isComplexConstraint(FeaturePtr theConstraint)
-{
-  return theConstraint->getKind() == SketchPlugin_ConstraintFillet::ID() ||
-         theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID() ||
-         theConstraint->getKind() == SketchPlugin_ConstraintTangent::ID();
+  // empty group => clear storage
+  if (myConstraints.empty()) {
+    myStorage = StoragePtr();
+    mySketchSolver = SolverPtr();
+    updateWorkplane();
+  }
 }
 
 // ============================================================================
@@ -593,7 +683,8 @@ static double featureToVal(FeaturePtr theFeature)
     AttributeRefAttrPtr anAttrB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
     if (anAttrA && anAttrB && (anAttrA->isObject() || anAttrB->isObject()))
-      return 2.0; // point-on-line and point-on-circle should go before points coincidence constraint
+      // point-on-line and point-on-circle should go before points coincidence constraint
+      return 2.0; 
     return 2.5;
   }
   if (anID == SketchPlugin_ConstraintDistance::ID() ||
@@ -613,7 +704,7 @@ static double featureToVal(FeaturePtr theFeature)
       anID == SketchPlugin_ConstraintMirror::ID())
     return 6.0;
   if (anID == SketchPlugin_ConstraintRigid::ID())
-    return 7.0;
+    return 0.5;
   if (anID == SketchPlugin_MultiRotation::ID() ||
       anID == SketchPlugin_MultiTranslation::ID())
     return 8.0;
@@ -627,7 +718,8 @@ static bool isLess(FeaturePtr theFeature1, FeaturePtr theFeature2)
   return featureToVal(theFeature1) < featureToVal(theFeature2);
 }
 
-std::list<FeaturePtr> SketchSolver_Group::selectApplicableFeatures(const std::set<ObjectPtr>& theObjects)
+std::list<FeaturePtr> SketchSolver_Group::
+  selectApplicableFeatures(const std::set<ObjectPtr>& theObjects)
 {
   std::list<FeaturePtr> aResult;
   std::list<FeaturePtr>::iterator aResIt;
@@ -635,14 +727,16 @@ std::list<FeaturePtr> SketchSolver_Group::selectApplicableFeatures(const std::se
   std::set<ObjectPtr>::const_iterator anObjIter = theObjects.begin();
   for (; anObjIter != theObjects.end(); ++anObjIter) {
     // Operate sketch itself and SketchPlugin features only.
-    // Also, the Fillet need to be skipped, because there are several separated constraints composing it.
+    // Also, the Fillet and Split need to be skipped, 
+    // because there are several separated constraints composing it.
     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIter);
     if (!aFeature)
       continue;
     std::shared_ptr<SketchPlugin_Feature> aSketchFeature = 
         std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
     if ((aFeature->getKind() != SketchPlugin_Sketch::ID() && !aSketchFeature) ||
-        aFeature->getKind() == SketchPlugin_ConstraintFillet::ID())
+        aFeature->getKind() == SketchPlugin_ConstraintFillet::ID() ||
+        aFeature->getKind() == SketchPlugin_ConstraintSplit::ID())
       continue;
 
     // Find the place where to insert a feature
@@ -655,3 +749,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);
+}