]> SALOME platform Git repositories - modules/shaper.git/blobdiff - src/Model/Model_AttributeSelection.cpp
Salome HOME
Fix for the issue #3116 : Create Edge, Wire all-in is missed for feature in Partset
[modules/shaper.git] / src / Model / Model_AttributeSelection.cpp
index e7a974e2e9f10be824427173820da54894455d5d..9d1e6cb65311873de13c46cde27684fe2a2bdcc5 100644 (file)
@@ -185,10 +185,11 @@ bool Model_AttributeSelection::setValue(const ObjectPtr& theContext,
   } else if (theContext->groupName() == ModelAPI_ResultGroup::group()) {
     aSelLab.ForgetAllAttributes(true);
     TDataStd_UAttribute::Set(aSelLab, kSIMPLE_REF_ID);
-  } else { // check the feature context: parent-Part of this feature should not be used
+  } else { // check the feature context: only construction features of PartSet could be selected
     FeaturePtr aFeatureContext = std::dynamic_pointer_cast<ModelAPI_Feature>(theContext);
-    if (aFeatureContext.get()) {
-      if (owner()->document() != aFeatureContext->document()) {
+    if (aFeatureContext.get() && owner()->document() != aFeatureContext->document()) {
+      if (aFeatureContext->results().empty() ||
+          aFeatureContext->firstResult()->groupName() != ModelAPI_ResultConstruction::group()) {
         aSelLab.ForgetAllAttributes(true);
         myRef.setValue(ObjectPtr());
         if (aToUnblock)
@@ -302,7 +303,7 @@ GeomShapePtr centerByEdge(GeomShapePtr theEdge, ModelAPI_AttributeSelection::Cen
 
 std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
 {
-  if (!ModelAPI_AttributeSelection::isInitialized() && !myTmpContext.get() && !myTmpSubShape.get())
+  if (!myRef.isInitialized() && !myTmpContext.get() && !myTmpSubShape.get())
     return std::shared_ptr<GeomAPI_Shape>();
   CenterType aType = NOT_CENTER;
   std::shared_ptr<GeomAPI_Shape> aResult = internalValue(aType);
@@ -421,32 +422,29 @@ bool Model_AttributeSelection::isInvalid()
 
 bool Model_AttributeSelection::isInitialized()
 {
-  if (ModelAPI_AttributeSelection::isInitialized()) { // additional checks if it is initialized
-    std::shared_ptr<GeomAPI_Shape> aResult;
-    if (myRef.isInitialized()) {
-      TDF_Label aSelLab = selectionLabel();
-      // it is just reference to shape, not sub-shape
-      if (aSelLab.IsAttribute(kSIMPLE_REF_ID) || aSelLab.IsAttribute(kPART_REF_ID)) {
-        ResultPtr aContext = context();
-        return aContext.get() != NULL;
-      }
-      Handle(TNaming_NamedShape) aSelection;
-      if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
-        return !aSelection->Get().IsNull();
-      } else { // for simple construction element: just shape of this construction element
-        if (myRef.value().get())
-          return true;
-        // check that this is on open of document, so, results are not initialized yet
-        TDF_Label aRefLab = myRef.myRef->Get();
-        if (aRefLab.IsNull() || !owner().get())
-          return false;
-        std::shared_ptr<Model_Document> aMyDoc =
-          std::dynamic_pointer_cast<Model_Document>(owner()->document());
-        if (!aMyDoc.get())
-          return false;
-        // check at least the feature exists
-        return aMyDoc->featureByLab(aRefLab).get() != NULL;
-      }
+  if (myRef.isInitialized()) {
+    TDF_Label aSelLab = selectionLabel();
+    // it is just reference to shape, not sub-shape
+    if (aSelLab.IsAttribute(kSIMPLE_REF_ID) || aSelLab.IsAttribute(kPART_REF_ID)) {
+      ResultPtr aContext = context();
+      return aContext.get() != NULL;
+    }
+    Handle(TNaming_NamedShape) aSelection;
+    if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
+      return !aSelection->Get().IsNull();
+    } else { // for simple construction element: just shape of this construction element
+      if (myRef.value().get())
+        return true;
+      // check that this is on open of document, so, results are not initialized yet
+      TDF_Label aRefLab = myRef.myRef->Get();
+      if (aRefLab.IsNull() || !owner().get())
+        return false;
+      std::shared_ptr<Model_Document> aMyDoc =
+        std::dynamic_pointer_cast<Model_Document>(owner()->document());
+      if (!aMyDoc.get())
+        return false;
+      // check at least the feature exists
+      return aMyDoc->featureByLab(aRefLab).get() != NULL;
     }
   }
   return false;
@@ -476,7 +474,7 @@ void Model_AttributeSelection::setID(const std::string theID)
 
 ResultPtr Model_AttributeSelection::context()
 {
-  if (!ModelAPI_AttributeSelection::isInitialized() && !myTmpContext.get() && !myTmpSubShape.get())
+  if (!myRef.isInitialized() && !myTmpContext.get() && !myTmpSubShape.get())
     return ResultPtr();
 
   if (myTmpContext.get() || myTmpSubShape.get()) {
@@ -602,19 +600,32 @@ bool Model_AttributeSelection::update()
     TopoDS_Shape aContextShape = aContext->shape()->impl<TopoDS_Shape>();
     Selector_Selector aSelector(aSelLab, baseDocumentLab());
     aResult = aSelector.restore(aContextShape);
+    bool aWasInvalid = aSelLab.IsAttribute(kINVALID_SELECTION);
     setInvalidIfFalse(aSelLab, aResult);
 
     TopoDS_Shape aNewShape;
     if (aSelLab.FindAttribute(TNaming_NamedShape::GetID(), aNS))
       aNewShape = aNS->Get();
 
-    if (anOldShape.IsNull() || aNewShape.IsNull() || !anOldShape.IsEqual(aNewShape)) {
+    if (anOldShape.IsNull() || aNewShape.IsNull() || !anOldShape.IsEqual(aNewShape) || aWasInvalid)
+    {
       // shape type should not be changed: if shape becomes compound of such shapes, then split
       if (myParent && !anOldShape.IsNull() && !aNewShape.IsNull() &&
           anOldShape.ShapeType() != aNewShape.ShapeType() &&
           aNewShape.ShapeType() == TopAbs_COMPOUND) {
         split(aContext, aNewShape, anOldShape.ShapeType());
       }
+      // for issue #3076 check that the new value belongs to the new context
+      if (!aNewShape.IsNull() && !aContextShape.IsNull() &&
+          (aNewShape.ShapeType() == TopAbs_VERTEX || aNewShape.ShapeType() == TopAbs_EDGE ||
+           aNewShape.ShapeType() == TopAbs_FACE)) {
+        TopExp_Explorer anExp(aContextShape, aNewShape.ShapeType());
+        for(; anExp.More(); anExp.Next()) {
+          if (anExp.Current().IsSame(aNewShape))
+            break;
+        }
+        aResult = setInvalidIfFalse(aSelLab, anExp.More());
+      }
       owner()->data()->sendAttributeUpdated(this);  // send updated if shape is changed
     }
     return aResult;
@@ -763,19 +774,22 @@ std::string Model_AttributeSelection::namingName(const std::string& theDefaultNa
 
   CenterType aCenterType = NOT_CENTER;
   std::shared_ptr<GeomAPI_Shape> aSubSh = internalValue(aCenterType);
-  ResultPtr aCont = context();
 
-  if (!aCont.get() ||
-      (aCont->groupName() == ModelAPI_ResultConstruction::group() && contextFeature().get())) {
+  FeaturePtr aContFeature = contextFeature();
+  if (aContFeature.get()) {
+    std::string aResName;
+    // checking part-owner
+    if (aContFeature->document() != owner()->document())
+        aResName += aContFeature->document()->kind() + "/";
     // selection of a full feature
-    FeaturePtr aFeatureCont = contextFeature();
-    if (aFeatureCont.get()) {
-      return kWHOLE_FEATURE + aFeatureCont->name();
+    if (aContFeature.get()) {
+      return aResName + kWHOLE_FEATURE + aContFeature->name();
     }
     // in case of selection of removed result
     return "";
   }
 
+  ResultPtr aCont = context();
   TDF_Label aSelLab = selectionLabel();
   if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // whole context, no value
     return contextName(aCont);
@@ -1282,14 +1296,21 @@ void Model_AttributeSelection::computeValues(
 
 
 void Model_AttributeSelection::concealedFeature(
-  const FeaturePtr theFeature, const FeaturePtr theStop, std::list<FeaturePtr>& theConcealers)
+  const FeaturePtr theFeature, const FeaturePtr theStop, const bool theCheckCopy,
+  std::list<FeaturePtr>& theConcealers, const ResultPtr theResultOfFeature)
 {
   std::set<FeaturePtr> alreadyProcessed;
   alreadyProcessed.insert(theFeature);
   if (theStop.get())
     alreadyProcessed.insert(theStop);
   /// iterate all results to find the concealment-attribute
-  const std::list<ResultPtr>& aRootRes = theFeature->results();
+  std::list<ResultPtr> aRootRes;
+  if (theResultOfFeature.get()) {
+    ResultPtr aRoot = ModelAPI_Tools::bodyOwner(theResultOfFeature, true);
+    aRootRes.push_back(aRoot ? aRoot : theResultOfFeature);
+  } else { // all results of a feature
+   aRootRes = theFeature->results();
+  }
   std::list<ResultPtr>::const_iterator aRootIter = aRootRes.cbegin();
   for(; aRootIter != aRootRes.cend(); aRootIter++) {
     std::list<ResultPtr> allRes;
@@ -1311,7 +1332,9 @@ void Model_AttributeSelection::concealedFeature(
         if (alreadyProcessed.find(aRefFeat) != alreadyProcessed.end()) // optimization
           continue;
         alreadyProcessed.insert(aRefFeat);
-        if (ModelAPI_Session::get()->validators()->isConcealed(aRefFeat->getKind(), (*aRef)->id()))
+        if (ModelAPI_Session::get()->validators()->isConcealed(aRefFeat->getKind(), (*aRef)->id())
+          || (theCheckCopy &&
+              std::dynamic_pointer_cast<ModelAPI_FeatureCopyInterface>(aRefFeat).get()))
         {
           // for extrusion cut in python script the nested sketch reference may be concealed before
           // it is nested, so, check this composite feature is valid
@@ -1393,12 +1416,13 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document>
     } else aResIter++;
   }
 
+  bool aStaySame = false;
   if (aResults.empty()) {
     // check the context become concealed by operation which is earlier than this selection
     FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
     FeaturePtr aContextOwner = theDoc->feature(theContext);
     std::list<FeaturePtr> aConcealers;
-    concealedFeature(aContextOwner, aThisFeature, aConcealers);
+    concealedFeature(aContextOwner, aThisFeature, false, aConcealers, theContext);
     std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
     for(; aConcealer != aConcealers.end(); aConcealer++) {
       std::list<ResultPtr> aRefResults;
@@ -1419,26 +1443,87 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document>
       if (aResults.empty())
         return true; // feature conceals result, return true, so the context will be removed
     }
-    if (aResults.empty())
-      return false; // no modifications found, must stay the same
+    aStaySame = aResults.empty();
+  }
+  if (myParent && myParent->isMakeCopy()) {
+    // check there are copies before the new results, so, make a copy
+    std::set<ResultPtr>::iterator aResIter = aResults.begin();
+    std::list<ObjectPtr> aCopyContext;
+    std::list<GeomShapePtr> aCopyVals;
+    // features between the new and the old: check the "Move" interface to get a copy
+    FeaturePtr aRootOwner = theDoc->feature(theContext);
+    FeaturePtr anOwner = ModelAPI_Tools::compositeOwner(aRootOwner);
+    for(; anOwner.get(); anOwner = ModelAPI_Tools::compositeOwner(anOwner))
+      aRootOwner = anOwner;
+    FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
+    // iterate all results to find a "Copy" features between the new and one and to add the
+    // copy-results also to results if this attribute refers to the copied shape
+    int anIndex = kUNDEFINED_FEATURE_INDEX;
+    for(FeaturePtr aFeat = theDoc->objects()->nextFeature(aRootOwner, anIndex); aFeat.get() &&
+        aFeat != aThisFeature; aFeat = theDoc->objects()->nextFeature(aFeat, anIndex)) {
+      std::shared_ptr<ModelAPI_FeatureCopyInterface> aCopier =
+        std::dynamic_pointer_cast<ModelAPI_FeatureCopyInterface>(aFeat);
+      if (aCopier.get()) {
+        GeomShapePtr aValShape(new GeomAPI_Shape);
+        aValShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(
+          theValShape.IsNull() ? theContShape : theValShape));
+        aCopier->getCopies(theContext, aValShape, aCopyContext, aCopyVals);
+      }
+    }
+    // check for the further modifications of the copy contexts and values
+    std::list<ObjectPtr>::iterator aCopyContIter = aCopyContext.begin();
+    std::list<GeomShapePtr>::iterator aCopyValIter = aCopyVals.begin();
+    for(; aCopyContIter != aCopyContext.end(); aCopyContIter++, aCopyValIter++) {
+      ResultPtr aNewCont = std::dynamic_pointer_cast<ModelAPI_Result>(*aCopyContIter);
+      TopoDS_Shape aNewContShape = aNewCont->shape()->impl<TopoDS_Shape>();
+      GeomShapePtr aNewVal = *aCopyValIter;
+      TopoDS_Shape aNewValShape;
+      if (aNewVal.get() && !aNewVal->isNull())
+        aNewValShape = aNewVal->impl<TopoDS_Shape>();
+      std::list<ResultPtr> aNewRes;
+      TopTools_ListOfShape aNewUpdatedVal;
+      if (searchNewContext(theDoc, aNewContShape, aNewCont, aNewValShape,
+          theAccessLabel, aNewRes, aNewUpdatedVal)) {
+        // append new results instead of the current ones
+        std::list<ResultPtr>::iterator aNewIter = aNewRes.begin();
+        TopTools_ListIteratorOfListOfShape aNewUpdVal(aNewUpdatedVal);
+        for(; aNewIter != aNewRes.end(); aNewIter++, aNewUpdVal.Next()) {
+          theResults.push_back(*aNewIter);
+          theValShapes.Append(aNewUpdVal.Value());
+        }
+      } else { // the current result is good
+        theResults.push_back(aNewCont);
+        theValShapes.Append(aNewValShape);
+      }
+    }
+    if (aStaySame && !theResults.empty()) { // no changes except copy, so, keep the origin as first
+      theResults.push_front(theContext);
+      theValShapes.Prepend(theValShape);
+      return true;
+    }
   }
+  if (aStaySame)
+    return false;
+
   // iterate all results to find further modifications
   std::set<ResultPtr>::iterator aResIter = aResults.begin();
-  for(; aResIter != aResults.end(); aResIter++) {
+  for(aResIter = aResults.begin(); aResIter != aResults.end(); aResIter++) {
     if (aResIter->get() != NULL) {
+      ResultPtr aNewResObj = *aResIter;
       // compute new values by two contexts: the old and the new
       TopTools_ListOfShape aValShapes;
-      computeValues(theContext, *aResIter, theValShape, aValShapes);
+      computeValues(theContext, aNewResObj, theValShape, aValShapes);
 
       TopTools_ListIteratorOfListOfShape aNewVal(aValShapes);
       for(; aNewVal.More(); aNewVal.Next()) {
         std::list<ResultPtr> aNewRes;
         TopTools_ListOfShape aNewUpdatedVal;
         TopoDS_Shape aNewValSh = aNewVal.Value();
-        TopoDS_Shape aNewContShape = (*aResIter)->shape()->impl<TopoDS_Shape>();
+        TopoDS_Shape aNewContShape = aNewResObj->shape()->impl<TopoDS_Shape>();
+
         if (theValShape.IsNull() && aNewContShape.IsSame(aNewValSh))
           aNewValSh.Nullify();
-        if (searchNewContext(theDoc, aNewContShape, *aResIter, aNewValSh,
+        if (searchNewContext(theDoc, aNewContShape, aNewResObj, aNewValSh,
                              theAccessLabel, aNewRes, aNewUpdatedVal))
         {
           // append new results instead of the current ones
@@ -1449,7 +1534,7 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document>
             theValShapes.Append(aNewUpdVal.Value());
           }
         } else { // the current result is good
-          theResults.push_back(*aResIter);
+          theResults.push_back(aNewResObj);
           theValShapes.Append(aNewValSh);
         }
       }
@@ -1471,25 +1556,40 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove)
       if (aFeature.get()) {
         FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
         std::list<FeaturePtr> aConcealers;
-        concealedFeature(aFeature, aThisFeature, aConcealers);
+        bool aCopyPossible = myParent && myParent->isMakeCopy();
+        concealedFeature(aFeature, aThisFeature, aCopyPossible, aConcealers, ResultPtr());
         if (aConcealers.empty())
           return;
+        // if there are copies, but no direct modification, keep the original
+        bool aKeepOrigin = false;
+        if (aCopyPossible) {
+          std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
+          for(aKeepOrigin = true; aConcealer != aConcealers.end(); aConcealer++)
+            if (!std::dynamic_pointer_cast<ModelAPI_FeatureCopyInterface>(*aConcealer).get()) {
+              aKeepOrigin = false;
+              break;
+            }
+          if (aKeepOrigin) {
+            aConcealers.push_front(aFeature);
+          }
+        }
         bool aChanged = false;
         std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
         for(; aConcealer != aConcealers.end(); aConcealer++)
-          if (!myParent->isInList(*aConcealer, anEmptyShape)) {// avoid addition of duplicates
-            setValue(*aConcealer, anEmptyShape);
-            aChanged = true;
-          }
-        if (aConcealer == aConcealers.end()) {
-          if (!aChanged) // remove this
-            theRemove = true;
-        } else { // append new
-          for(aConcealer++; aConcealer != aConcealers.end(); aConcealer++)
-            if (!myParent->isInList(*aConcealer, anEmptyShape)) // avoid addition of duplicates
+          if (aChanged) {
+            if (aKeepOrigin || !myParent->isInList(*aConcealer, anEmptyShape))
               myParent->append(*aConcealer, anEmptyShape);
-        }
-        if (aChanged) // searching for the further modifications
+          } else {
+            if (!myParent->isInList(*aConcealer, anEmptyShape)) {// avoid addition of duplicates
+              setValue(*aConcealer, anEmptyShape);
+              aChanged = true;
+            } else if (aCopyPossible && *aConcealer == aFeature) {// keep origin in case of copy
+              aChanged = true;
+            }
+          }
+        if (!aChanged) // remove this
+          theRemove = true;
+        else if (!aKeepOrigin) // searching further modifications only if current changed
           updateInHistory(theRemove);
       }
     }
@@ -1598,6 +1698,42 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove)
         aListShapeType = GeomAPI_Shape::FACE;
     }
 
+    // issue #3031: skip topology if there is more convenient shape type presents in the
+    // same context as a result of this
+    bool isWholeResult = myParent && myParent->isWholeResultAllowed() && !aSubShape.get();
+    GeomAPI_Shape::ShapeType allowedType = GeomAPI_Shape::SHAPE;
+    if (isWholeResult) {
+      std::list<ResultPtr>::iterator aNewCont = aNewContexts.begin();
+      TopTools_ListIteratorOfListOfShape aNewValues(aValShapes);
+      for(; aNewCont != aNewContexts.end(); aNewCont++, aNewValues.Next()) {
+        if (aNewValues.Value().IsNull()) { // only for the whole context
+          GeomAPI_Shape::ShapeType aShapeType = (*aNewCont)->shape()->shapeType();
+          if (allowedType == GeomAPI_Shape::SHAPE) { // just set this one
+            allowedType = aShapeType;
+          } else {
+            GeomAPI_Shape::ShapeType anAllowed = allowedType;
+            if (anAllowed != aShapeType) { // select the best, nearest to the origin
+              GeomAPI_Shape::ShapeType anOldShapeType = aContext->shape()->shapeType();
+              GeomAPI_Shape::ShapeType aDeltaAllowed =
+                (GeomAPI_Shape::ShapeType)(anOldShapeType - anAllowed);
+              if (aDeltaAllowed < 0)
+                aDeltaAllowed = (GeomAPI_Shape::ShapeType)(-aDeltaAllowed);
+              GeomAPI_Shape::ShapeType aDeltaThis =
+                (GeomAPI_Shape::ShapeType)(anOldShapeType - aShapeType);
+              if (aDeltaThis < 0)
+                aDeltaThis = (GeomAPI_Shape::ShapeType)(-aDeltaThis);
+              if (aDeltaThis == aDeltaAllowed) { // equal distance to context, select complicated
+                if (anOldShapeType < anAllowed)
+                  allowedType = aShapeType;
+              } else if (aDeltaAllowed > aDeltaThis) { // this wins
+                allowedType = aShapeType;
+              }
+            }
+          }
+        }
+      }
+    }
+
     std::list<ResultPtr>::iterator aNewCont = aNewContexts.begin();
     TopTools_ListIteratorOfListOfShape aNewValues(aValShapes);
     bool aFirst = true; // first is set to this, next are appended to parent
@@ -1605,6 +1741,11 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove)
       if (aSkippedContext.count(*aNewCont))
         continue;
 
+      if (isWholeResult && aNewValues.Value().IsNull())
+        if (allowedType != GeomAPI_Shape::SHAPE &&
+            (*aNewCont)->shape()->shapeType() != allowedType)
+          continue; // there is better result exists with the better shape type (issue #3031)
+
       GeomShapePtr aValueShape;
       if (!aNewValues.Value().IsNull()) {
         aValueShape = std::make_shared<GeomAPI_Shape>();
@@ -1619,7 +1760,7 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove)
       }
       if (aListShapeType != GeomAPI_Shape::SHAPE && aListShapeType != aShapeShapeType) {
         // exception is for whole results selected
-        if (!myParent || !myParent->isWholeResultAllowed() || aSubShape.get()) {
+        if (!isWholeResult) {
           continue;
         }
       }
@@ -1646,6 +1787,9 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove)
         if (!myParent || !myParent->isInList(aNewContext, aValueShape)) { // avoid duplicates
           setValue(aNewContext, aValueShape);
           aFirst = false;
+        } else if (aNewContext == aContext && myParent && myParent->isMakeCopy()) {
+          // this may be exactly the old one, not modified in case of copy
+          aFirst = false;
         }
       } else if (myParent) {
         if (!myParent->isInList(aNewContext, aValueShape)) // avoid addition of duplicates
@@ -1973,3 +2117,9 @@ TDF_Label Model_AttributeSelection::baseDocumentLab()
   static TDF_Label anEmpty;
   return anEmpty;
 }
+
+void Model_AttributeSelection::reset()
+{
+  ModelAPI_AttributeSelection::reset();
+  myRef.reset();
+}