]> SALOME platform Git repositories - modules/shaper.git/blobdiff - src/SketchSolver/SketchSolver_Manager.cpp
Salome HOME
Correct calculation of DoF for the "multi" constraints
[modules/shaper.git] / src / SketchSolver / SketchSolver_Manager.cpp
index 82f0c826bb9956b5973bf8e32e0a6824dd9d33e1..839f97736e9f9c18ca8cef1a52172d47d29ac349 100644 (file)
@@ -110,6 +110,10 @@ void SketchSolver_Manager::processEvent(
   if (myIsComputed)
     return;
   myIsComputed = true;
+
+  // Shows that the message has at least one feature applicable for solver
+  bool hasProperFeature = false;
+
   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
@@ -118,8 +122,6 @@ void SketchSolver_Manager::processEvent(
     std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
 
     bool isUpdateFlushed = stopSendUpdate();
-    // Shows the message has at least one feature applicable for solver
-    bool hasProperFeature = false;
 
     bool isMovedEvt = theMessage->eventID()
           == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
@@ -128,11 +130,16 @@ void SketchSolver_Manager::processEvent(
       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
         std::shared_ptr<SketchPlugin_Feature> aSFeature = 
             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
-        if (aSFeature) {
-          moveEntity(aSFeature);
+        if (aSFeature && moveEntity(aSFeature)) {
+          // Want to avoid recalculation of DoF too frequently.
+          // So, set the flag when the feature is really moved.
           hasProperFeature = true;
         }
       }
+      if (!hasProperFeature) // in this iteration it will compute nothing, so, no problem with recursion
+        // it is important that solver flushes signal updated after processing move signal as there is
+        // optimization that relies on this update, might be found by key "optimization"
+        myIsComputed = false;
     } else {
       std::list<FeaturePtr> aSketchFeatures = SketchSolver_Group::selectApplicableFeatures(aFeatures);
       std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
@@ -176,6 +183,7 @@ void SketchSolver_Manager::processEvent(
         break;
 
     if (aFGrIter != aFeatureGroups.end()) {
+      hasProperFeature = true;
       std::list<SketchSolver_Group*> aGroupsToResolve;
       std::list<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
       std::list<SketchSolver_Group*> aSeparatedGroups;
@@ -188,8 +196,9 @@ void SketchSolver_Manager::processEvent(
         }
         if (!(*aGroupIter)->isConsistent()) {  // some constraints were removed, try to split the group
           (*aGroupIter)->splitGroup(aSeparatedGroups);
-          //if (!(*aGroupIter)->getWorkplane()->string(
-          //    SketchPlugin_Sketch::SOLVER_ERROR())->value().empty())
+          if (!(*aGroupIter)->getWorkplane()->string(
+              SketchPlugin_Sketch::SOLVER_ERROR())->value().empty() ||
+              (*aGroupIter)->isFailed())
             aGroupsToResolve.push_back(*aGroupIter);
         }
         aGroupIter++;
@@ -204,7 +213,9 @@ void SketchSolver_Manager::processEvent(
         resolveConstraints(aGroupsToResolve);
     }
   }
-  degreesOfFreedom();
+
+  if (hasProperFeature)
+    degreesOfFreedom();
   myIsComputed = false;
 }
 
@@ -355,23 +366,22 @@ bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> t
 //  Function: moveEntity
 //  Purpose:  update element moved on the sketch, which is used by constraints
 // ============================================================================
-void SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
+bool SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
 {
   bool isMoved = false;
   std::list<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
   for (; aGroupIt != myGroups.end(); aGroupIt++)
-    if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature)) {
-      (*aGroupIt)->moveFeature(theFeature);
-      isMoved = true;
-    }
+    if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
+      isMoved = (*aGroupIt)->moveFeature(theFeature) || isMoved;
 
   if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) {
     // Workaround to move arc.
     // If the arc has not been constrained, we will push it into empty group and apply movement.
     for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++)
       if ((*aGroupIt)->isEmpty())
-        (*aGroupIt)->moveFeature(theFeature);
+        isMoved = (*aGroupIt)->moveFeature(theFeature) || isMoved;
   }
+  return isMoved;
 }
 
 // ============================================================================
@@ -446,6 +456,87 @@ bool SketchSolver_Manager::resolveConstraints(const std::list<SketchSolver_Group
   return needToUpdate;
 }
 
+
+// Obtain points and their copies for Mirror, Multi-Rotation and Multi-Translation constraints
+static void collectPointsAndCopies(FeaturePtr theConstraint, std::list<std::set<AttributePtr> >& thePoints)
+{
+  typedef std::list<std::string> strlist;
+  static strlist aPointAttributes(1, SketchPlugin_Point::COORD_ID());
+  static strlist aLineAttributes;
+  if (aLineAttributes.empty()) {
+    aLineAttributes.push_back(SketchPlugin_Line::START_ID());
+    aLineAttributes.push_back(SketchPlugin_Line::END_ID());
+  };
+  static strlist aCircleAttributes(1, SketchPlugin_Circle::CENTER_ID());
+  static strlist anArcAttributes;
+  if (anArcAttributes.empty()) {
+    anArcAttributes.push_back(SketchPlugin_Arc::CENTER_ID());
+    anArcAttributes.push_back(SketchPlugin_Arc::START_ID());
+    anArcAttributes.push_back(SketchPlugin_Arc::END_ID());
+  };
+
+  static std::map<std::string, strlist> aFeatureAttributes;
+  if (aFeatureAttributes.empty()) {
+    aFeatureAttributes[SketchPlugin_Point::ID()] = aPointAttributes;
+    aFeatureAttributes[SketchPlugin_Line::ID()] = aLineAttributes;
+    aFeatureAttributes[SketchPlugin_Circle::ID()] = aCircleAttributes;
+    aFeatureAttributes[SketchPlugin_Arc::ID()] = anArcAttributes;
+  }
+
+
+  std::set<AttributePtr> aPoints;
+  if (theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID()) {
+    AttributeRefListPtr aBaseRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B());
+    AttributeRefListPtr aMirrRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_C());
+
+    std::list<ObjectPtr> aBaseList = aBaseRefList->list();
+    std::list<ObjectPtr> aMirrList = aMirrRefList->list();
+    std::list<ObjectPtr>::const_iterator aBIt, aMIt;
+    for (aBIt = aBaseList.begin(), aMIt = aMirrList.begin();
+         aBIt != aBaseList.end() && aMIt != aMirrList.end(); ++aBIt, ++aMIt) {
+      FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aBIt);
+      FeaturePtr aMirrFeature = ModelAPI_Feature::feature(*aMIt);
+
+      strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()];
+      strlist::iterator anIt = anAttrList.begin();
+      for (; anIt != anAttrList.end(); ++anIt) {
+        aPoints.clear();
+        aPoints.insert(aBaseFeature->attribute(*anIt));
+        aPoints.insert(aMirrFeature->attribute(*anIt));
+        thePoints.push_back(aPoints);
+      }
+    }
+  }
+  else { // the "Multi" constraints
+    std::string aNbObjName;
+    if (theConstraint->getKind() == SketchPlugin_MultiRotation::ID())
+      aNbObjName = SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID();
+    else
+      aNbObjName = SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID();
+    int aNbCopies = theConstraint->integer(aNbObjName)->value();
+
+    AttributeRefListPtr aRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B());
+    std::list<ObjectPtr> aFullList = aRefList->list();
+    std::list<ObjectPtr>::const_iterator anObjIt = aFullList.begin();
+    std::list<ObjectPtr>::const_iterator aCopyIt;
+    while (anObjIt != aFullList.end()) {
+      FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*anObjIt);
+      strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()];
+      strlist::iterator anIt = anAttrList.begin();
+      for (; anIt != anAttrList.end(); ++anIt) {
+        aPoints.clear();
+        aCopyIt = anObjIt;
+        for (int i = 0; i < aNbCopies; ++i, ++aCopyIt) {
+          FeaturePtr aFeature = ModelAPI_Feature::feature(*aCopyIt);
+          aPoints.insert(aFeature->attribute(*anIt));
+        }
+        thePoints.push_back(aPoints);
+      }
+      anObjIt = aCopyIt;
+    }
+  }
+}
+
 // ============================================================================
 //  Function: degreesOfFreedom
 //  Purpose:  calculate DoFs for each sketch
@@ -481,7 +572,15 @@ void SketchSolver_Manager::degreesOfFreedom()
   std::list<SketchSolver_Group*>::const_iterator aGroupIt = myGroups.begin();
   for (; aGroupIt != myGroups.end(); ++aGroupIt) {
     CompositeFeaturePtr aSketch = (*aGroupIt)->getWorkplane();
-    if (!aSketch->data()->isValid()) {
+    bool isSketchValid = aSketch->data() && aSketch->data()->isValid();
+
+    if (isSketchValid) {
+      std::shared_ptr<GeomDataAPI_Dir> aNormal =
+          std::dynamic_pointer_cast<GeomDataAPI_Dir>(aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
+      isSketchValid = aNormal && aNormal->isInitialized();
+    }
+
+    if (!isSketchValid) {
       myDoF.erase(aSketch);
       continue;
     }
@@ -493,6 +592,8 @@ void SketchSolver_Manager::degreesOfFreedom()
     if (aSketchDoF.find(aSketch) != aSketchDoF.end() || aSketchDoF[aSketch] < 0)
       continue;
 
+    std::set<AttributePtr> aCoincidentPoints;
+    std::list<std::set<AttributePtr> > aPointsInMultiConstraints;
     int aDoF = 0;
     int aNbSubs = aSketch->numberOfSubs();
     for (int i = 0; i < aNbSubs; ++i) {
@@ -506,19 +607,43 @@ void SketchSolver_Manager::degreesOfFreedom()
 
       // DoF delta in specific cases
       if (aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
+        AttributePtr aCoincPoint[2] = {AttributePtr(), AttributePtr()};
         for (int j = 0; j < 2; ++j) {
           AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
               aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
           if (!aRefAttr)
             continue;
           bool isPoint = !aRefAttr->isObject();
-          if (!isPoint) {
+          if (isPoint)
+            aCoincPoint[j] = aRefAttr->attr();
+          else {
             FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
             isPoint = anAttr && anAttr->getKind() == SketchPlugin_Point::ID();
+            if (isPoint)
+              aCoincPoint[j] = anAttr->attribute(SketchPlugin_Point::COORD_ID());
           }
-          if (isPoint)
-            aDoF -= 1;
         }
+        if (aCoincPoint[0] && aCoincPoint[1]) {
+          // point-point coincidence
+          if (aCoincidentPoints.find(aCoincPoint[0]) == aCoincidentPoints.end() ||
+              aCoincidentPoints.find(aCoincPoint[1]) == aCoincidentPoints.end())
+            aDoF -= 2;
+          // check the coincident point is used in "multi" constraints
+          std::list<std::set<AttributePtr> >::const_iterator
+              aPtIt = aPointsInMultiConstraints.begin();
+          bool isFound[2] = {false, false};
+          for (; aPtIt != aPointsInMultiConstraints.end(); ++aPtIt) {
+            if ((!isFound[0] && (isFound[0] = (aPtIt->find(aCoincPoint[0]) != aPtIt->end())))
+             || (!isFound[1] && (isFound[1] = (aPtIt->find(aCoincPoint[1]) != aPtIt->end()))))
+              aCoincidentPoints.insert(aPtIt->begin(), aPtIt->end());
+            if (isFound[0] && isFound[1])
+              break;
+          }
+        } else 
+          aDoF -= 1;
+        for (int j = 0; j < 2; ++j)
+          if (aCoincPoint[j])
+            aCoincidentPoints.insert(aCoincPoint[j]);
       }
       else if (aFeature->getKind() == SketchPlugin_ConstraintRigid::ID()) {
         AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
@@ -528,8 +653,8 @@ void SketchSolver_Manager::degreesOfFreedom()
           aDoF -= 2; // attribute is a point
         else {
           FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
-          assert(anAttr);
-          aDoF -= aDoFDelta[anAttr->getKind()];
+          if (anAttr)
+            aDoF -= aDoFDelta[anAttr->getKind()];
         }
       }
       else if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID() ||
@@ -555,6 +680,8 @@ void SketchSolver_Manager::degreesOfFreedom()
           FeaturePtr aSub = ModelAPI_Feature::feature(*anObjIt);
           aDoF -= aDoFDelta[aSub->getKind()] * aNbCopies;
         }
+        // collect points and their copies for correct calculation of DoF for coincident points
+        collectPointsAndCopies(aFeature, aPointsInMultiConstraints);
       }
     }
 
@@ -573,7 +700,10 @@ void SketchSolver_Manager::degreesOfFreedom()
     myDoF[aDoFIt->first] = aDoFIt->second;
     // change attribute value
     std::ostringstream aStream;
-    aStream << "DOF(degree of freedom) = " << aDoFIt->second;
+    if (aDoFIt->second == 0)
+      aStream << "Sketch fully fixed (DOF = " << aDoFIt->second << ")";
+    else
+      aStream << "DOF (degree of freedom) = " << aDoFIt->second;
     aDoFIt->first->data()->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aStream.str());
   }
 }