Salome HOME
Make edition transaction where actually nothing was changed as empty one and do not...
[modules/shaper.git] / src / Model / Model_AttributeSelection.cpp
index f3f4d27a3c46d34ea92dfa5c3577716004bee615..232ff61b558ead55e47049e9d23208513d06c975 100644 (file)
@@ -15,6 +15,7 @@
 #include <ModelAPI_ResultConstruction.h>
 #include <ModelAPI_ResultPart.h>
 #include <ModelAPI_CompositeFeature.h>
+#include <ModelAPI_Tools.h>
 #include <GeomAPI_Shape.h>
 #include <GeomAPI_PlanarEdges.h>
 #include <Events_Error.h>
@@ -54,6 +55,7 @@
 #include <TopAbs_ShapeEnum.hxx>
 #include <TopoDS_Iterator.hxx>
 #include <TNaming_Iterator.hxx>
+#include <BRep_Builder.hxx>
 using namespace std;
 //#define DEB_NAMING 1
 #ifdef DEB_NAMING
@@ -68,6 +70,8 @@ Standard_GUID kSIMPLE_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb29");
 Standard_GUID kCONSTUCTION_SIMPLE_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb28");
 // reference to Part sub-object
 Standard_GUID kPART_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb27");
+// selection is invalid after recomputation
+Standard_GUID kINVALID_SELECTION("bce47fd7-80fa-4462-9d63-2f58acddd49d");
 
 // on this label is stored:
 // TNaming_NamedShape - selected shape
@@ -76,8 +80,18 @@ Standard_GUID kPART_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb27");
 // TDataStd_Integer - type of the selected shape (for construction)
 // TDF_Reference - from ReferenceAttribute, the context
 void Model_AttributeSelection::setValue(const ResultPtr& theContext,
-  const std::shared_ptr<GeomAPI_Shape>& theSubShape)
+  const std::shared_ptr<GeomAPI_Shape>& theSubShape, const bool theTemporarily)
 {
+  if (theTemporarily) { // just keep the stored without DF update
+    myTmpContext = theContext;
+    myTmpSubShape = theSubShape;
+    owner()->data()->sendAttributeUpdated(this);
+    return;
+  } else {
+    myTmpContext.reset();
+    myTmpSubShape.reset();
+  }
+
   const std::shared_ptr<GeomAPI_Shape>& anOldShape = value();
   bool isOldContext = theContext == myRef.value();
   bool isOldShape = isOldContext &&
@@ -91,6 +105,7 @@ void Model_AttributeSelection::setValue(const ResultPtr& theContext,
   TDF_Label aSelLab = selectionLabel();
   aSelLab.ForgetAttribute(kSIMPLE_REF_ID);
   aSelLab.ForgetAttribute(kCONSTUCTION_SIMPLE_REF_ID);
+  aSelLab.ForgetAttribute(kINVALID_SELECTION);
 
   bool isDegeneratedEdge = false;
   // do not use the degenerated edge as a shape, a null context and shape is used in the case
@@ -109,7 +124,6 @@ void Model_AttributeSelection::setValue(const ResultPtr& theContext,
   if (theContext->groupName() == ModelAPI_ResultBody::group()) {
     // do not select the whole shape for body:it is already must be in the data framework
     // equal and null selected objects mean the same: object is equal to context,
-    // TODO: synchronize with GUI later that it must be null always
     if (theContext->shape().get() && 
         (theContext->shape()->isEqual(theSubShape) || !theSubShape.get())) {
       aSelLab.ForgetAllAttributes(true);
@@ -122,6 +136,30 @@ void Model_AttributeSelection::setValue(const ResultPtr& theContext,
       // to sub, so the whole result is selected
       aSelLab.ForgetAllAttributes(true);
       TDataStd_UAttribute::Set(aSelLab, kCONSTUCTION_SIMPLE_REF_ID);
+      ResultConstructionPtr aConstruction = 
+        std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(theContext);
+      if (aConstruction->isInfinite()) {
+        // For correct naming selection, put the shape into the naming structure.
+        // It seems sub-shapes are not needed: only this shape is (and can be ) selected.
+        TNaming_Builder aBuilder(aSelLab);
+        aBuilder.Generated(theContext->shape()->impl<TopoDS_Shape>());
+        std::shared_ptr<Model_Document> aMyDoc = 
+          std::dynamic_pointer_cast<Model_Document>(owner()->document());
+        std::string aName = theContext->data()->name();
+        aMyDoc->addNamingName(aSelLab, aName);
+        TDataStd_Name::Set(aSelLab, aName.c_str());
+      } else {  // for sketch the naming is needed in DS
+        BRep_Builder aCompoundBuilder;
+        TopoDS_Compound aComp;
+        aCompoundBuilder.MakeCompound(aComp);
+        for(int a = 0; a < aConstruction->facesNum(); a++) {
+          TopoDS_Shape aFace = aConstruction->face(a)->impl<TopoDS_Shape>();
+          aCompoundBuilder.Add(aComp, aFace);
+        }
+        std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape);
+        aShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aComp));
+        selectConstruction(theContext, aShape);
+      }
     } else {
       selectConstruction(theContext, theSubShape);
     }
@@ -142,9 +180,16 @@ void Model_AttributeSelection::setValue(const ResultPtr& theContext,
 
 std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
 {
+  if (myTmpContext.get() || myTmpSubShape.get()) {
+    return myTmpSubShape;
+  }
+
   std::shared_ptr<GeomAPI_Shape> aResult;
+  TDF_Label aSelLab = selectionLabel();
+  if (aSelLab.IsAttribute(kINVALID_SELECTION))
+    return aResult;
+
   if (myRef.isInitialized()) {
-    TDF_Label aSelLab = selectionLabel();
     if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
       ResultPtr aContext = context();
       if (!aContext.get()) 
@@ -155,7 +200,6 @@ std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
         return aResult; // empty result
     }
     if (aSelLab.IsAttribute(kPART_REF_ID)) {
-      /* TODO: implement used text here
       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(context());
       if (!aPart.get() || !aPart->isActivated())
         return std::shared_ptr<GeomAPI_Shape>(); // postponed naming needed
@@ -163,11 +207,12 @@ std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
       if (selectionLabel().FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
         return aPart->selectionValue(anIndex->Get());
       }
+      /*
       Handle(TDataStd_Name) aName;
       if (!selectionLabel().FindAttribute(TDataStd_Name::GetID(), aName)) {
         return std::shared_ptr<GeomAPI_Shape>(); // something is wrong
       }
-      return aPart->shapeInPart(TCollection_AsciiString(aName).ToCString());
+      return aPart->shapeInPart(TCollection_AsciiString(aName->Get()).ToCString());
       */
     }
 
@@ -187,6 +232,11 @@ std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
   return aResult;
 }
 
+bool Model_AttributeSelection::isInvalid()
+{
+  return selectionLabel().IsAttribute(kINVALID_SELECTION) == Standard_True;
+}
+
 bool Model_AttributeSelection::isInitialized()
 {
   if (ModelAPI_AttributeSelection::isInitialized()) { // additional checkings if it is initialized
@@ -229,6 +279,10 @@ void Model_AttributeSelection::setID(const std::string theID)
 }
 
 ResultPtr Model_AttributeSelection::context() {
+  if (myTmpContext.get() || myTmpSubShape.get()) {
+    return myTmpContext;
+  }
+
   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
   // for parts there could be same-data result, so take the last enabled
   if (aResult.get() && aResult->groupName() == ModelAPI_ResultPart::group()) {
@@ -262,9 +316,32 @@ TDF_LabelMap& Model_AttributeSelection::scope()
     DocumentPtr aMyDoc = owner()->document();
     std::list<std::shared_ptr<ModelAPI_Feature> > allFeatures = aMyDoc->allFeatures();
     std::list<std::shared_ptr<ModelAPI_Feature> >::iterator aFIter = allFeatures.begin();
+    bool aMePassed = false;
+    CompositeFeaturePtr aComposite = 
+      std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(owner());
+    FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
+    CompositeFeaturePtr aCompositeOwner, aCompositeOwnerOwner;
+    if (aFeature.get()) {
+      aCompositeOwner = ModelAPI_Tools::compositeOwner(aFeature);
+      if (aCompositeOwner.get()) {
+         aCompositeOwnerOwner = ModelAPI_Tools::compositeOwner(aCompositeOwner);
+      }
+    }
     for(; aFIter != allFeatures.end(); aFIter++) {
-      if (*aFIter == owner()) break; // the left features are created later
-      if (aFIter->get() && (*aFIter)->data()->isValid()) {
+      if (*aFIter == owner()) {  // the left features are created later (except subs of composite)
+        aMePassed = true;
+        continue;
+      }
+      bool isInScope = !aMePassed;
+      if (!isInScope && aComposite.get()) { // try to add sub-elements of composite if this is composite
+        if (aComposite->isSub(*aFIter))
+          isInScope = true;
+      }
+      // remove the composite-owner of this feature (sketch in extrusion-cut)
+      if (isInScope && (aCompositeOwner == *aFIter || aCompositeOwnerOwner == *aFIter))
+        isInScope = false;
+
+      if (isInScope && aFIter->get() && (*aFIter)->data()->isValid()) {
         TDF_Label aFeatureLab = std::dynamic_pointer_cast<Model_Data>(
           (*aFIter)->data())->label().Father();
         TDF_ChildIDIterator aNSIter(aFeatureLab, TNaming_NamedShape::GetID(), 1);
@@ -293,21 +370,48 @@ int edgeOrientation(const TopoDS_Shape& theContext, TopoDS_Edge& theEdge)
   return 0; // unknown
 }
 
+/// Sets the invalid flag if flag is false, or removes it if "true"
+/// Returns theFlag
+static bool setInvalidIfFalse(TDF_Label& theLab, const bool theFlag) {
+  if (theFlag) {
+    theLab.ForgetAttribute(kINVALID_SELECTION);
+  } else {
+    TDataStd_UAttribute::Set(theLab, kINVALID_SELECTION);
+  }
+  return theFlag;
+}
+
 bool Model_AttributeSelection::update()
 {
-  ResultPtr aContext = context();
-  if (!aContext.get()) return false;
   TDF_Label aSelLab = selectionLabel();
+  ResultPtr aContext = context();
+  if (!aContext.get()) 
+    return setInvalidIfFalse(aSelLab, false);
   if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
-    return aContext->shape() && !aContext->shape()->isNull();
+    return setInvalidIfFalse(aSelLab, aContext->shape() && !aContext->shape()->isNull());
   }
   if (aSelLab.IsAttribute(kCONSTUCTION_SIMPLE_REF_ID)) { // it is just reference to construction, not sub-shape
-    return aContext->shape() && !aContext->shape()->isNull();
+    // if there is a sketch, the sketch-naming must be updated
+    ResultConstructionPtr aConstruction = 
+      std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
+    if (!aConstruction->isInfinite()) {
+      BRep_Builder aCompoundBuilder;
+      TopoDS_Compound aComp;
+      aCompoundBuilder.MakeCompound(aComp);
+      for(int a = 0; a < aConstruction->facesNum(); a++) {
+        TopoDS_Shape aFace = aConstruction->face(a)->impl<TopoDS_Shape>();
+        aCompoundBuilder.Add(aComp, aFace);
+      }
+      std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape);
+      aShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aComp));
+      selectConstruction(aContext, aShape);
+    }
+    return setInvalidIfFalse(aSelLab, aContext->shape() && !aContext->shape()->isNull());
   }
 
   if (aSelLab.IsAttribute(kPART_REF_ID)) { // it is reference to the part object
     std::shared_ptr<GeomAPI_Shape> aNoSelection;
-    return selectPart(aContext, aNoSelection, true);
+    return setInvalidIfFalse(aSelLab, selectPart(aContext, aNoSelection, true));
   }
 
   if (aContext->groupName() == ModelAPI_ResultBody::group()) {
@@ -315,7 +419,7 @@ bool Model_AttributeSelection::update()
     TNaming_Selector aSelector(aSelLab);
     bool aResult = aSelector.Solve(scope()) == Standard_True;
     owner()->data()->sendAttributeUpdated(this);
-    return aResult;
+    return setInvalidIfFalse(aSelLab, aResult);
   } else if (aContext->groupName() == ModelAPI_ResultConstruction::group()) {
     // construction: identification by the results indexes, recompute faces and
     // take the face that more close by the indexes
@@ -330,7 +434,7 @@ bool Model_AttributeSelection::update()
       // getting a type of selected shape
       Handle(TDataStd_Integer) aTypeAttr;
       if (!aLab.FindAttribute(TDataStd_Integer::GetID(), aTypeAttr)) {
-        return false;
+        return setInvalidIfFalse(aSelLab, false);
       }
       TopAbs_ShapeEnum aShapeType = (TopAbs_ShapeEnum)(aTypeAttr->Get());
       // selected indexes will be needed in each "if"
@@ -342,13 +446,13 @@ bool Model_AttributeSelection::update()
       CompositeFeaturePtr aComposite = 
         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aContextFeature);
       if (!aComposite.get() || aComposite->numberOfSubs() == 0) {
-        return false;
+        return setInvalidIfFalse(aSelLab, false);
       }
 
       if (aShapeType == TopAbs_FACE) { // compound is for the whole sketch selection
         // If this is a wire with plane defined thin it is a sketch-like object
         if (!aConstructionContext->facesNum()) // no faces, update can not work correctly
-          return false;
+          return setInvalidIfFalse(aSelLab, false);
         // if there is no edges indexes, any face can be used: take the first
         std::shared_ptr<GeomAPI_Shape> aNewSelected;
         if (aNoIndexes) {
@@ -384,7 +488,7 @@ bool Model_AttributeSelection::update()
               }
             }
           }
-          double aBestFound = 0; // best percentage of found edges
+          int aBestFound = 0; // best number of found edges (not percentage: issue 1019)
           int aBestOrient = 0; // for the equal "BestFound" additional parameter is orientation
           for(int aFaceIndex = 0; aFaceIndex < aConstructionContext->facesNum(); aFaceIndex++) {
             int aFound = 0, aNotFound = 0, aSameOrientation = 0;
@@ -413,12 +517,9 @@ bool Model_AttributeSelection::update()
               }
             }
             if (aFound + aNotFound != 0) {
-              double aSum = aFound + aNotFound;
-               // aSameOrientation: if edges are same, take where orientation is better
-              double aPercentage = double(aFound) / double(aFound + aNotFound);
-              if (aPercentage > aBestFound || 
-                  (aPercentage == aBestFound && aSameOrientation > aBestOrient)) {
-                aBestFound = aPercentage;
+              if (aFound > aBestFound || 
+                  (aFound == aBestFound && aSameOrientation > aBestOrient)) {
+                aBestFound = aFound;
                 aBestOrient = aSameOrientation;
                 aNewSelected = aConstructionContext->face(aFaceIndex);
               }
@@ -428,7 +529,10 @@ bool Model_AttributeSelection::update()
         if (aNewSelected) { // store this new selection
           selectConstruction(aContext, aNewSelected);
           owner()->data()->sendAttributeUpdated(this);
-          return true;
+          return setInvalidIfFalse(aSelLab, true);
+        } else { // if the selection is not found, put the empty shape: it's better to have disappeared shape, than the old, the lost one
+          TNaming_Builder anEmptyBuilder(selectionLabel());
+          return setInvalidIfFalse(aSelLab, false);
         }
       } else if (aShapeType == TopAbs_EDGE) {
         // just reselect the edge by the id
@@ -446,7 +550,7 @@ bool Model_AttributeSelection::update()
               if (aRes && aRes->shape() && aRes->shape()->isEdge()) { // found!
                 selectConstruction(aContext, aRes->shape());
                 owner()->data()->sendAttributeUpdated(this);
-                return true;
+                return setInvalidIfFalse(aSelLab, true);
               }
             }
           }
@@ -475,7 +579,7 @@ bool Model_AttributeSelection::update()
                   if (aRes->shape()->isVertex() && aVertexNum == 0) { // found!
                     selectConstruction(aContext, aRes->shape());
                     owner()->data()->sendAttributeUpdated(this);
-                    return true;
+                    return setInvalidIfFalse(aSelLab, true);
                   } else if (aRes->shape()->isEdge() && aVertexNum > 0) {
                     const TopoDS_Shape& anEdge = aRes->shape()->impl<TopoDS_Shape>();
                     int aVIndex = 1;
@@ -485,7 +589,7 @@ bool Model_AttributeSelection::update()
                         aVertex->setImpl(new TopoDS_Shape(aVExp.Current()));
                         selectConstruction(aContext, aVertex);
                         owner()->data()->sendAttributeUpdated(this);
-                        return true;
+                        return setInvalidIfFalse(aSelLab, true);
                       }
                       aVIndex++;
                     }
@@ -498,10 +602,10 @@ bool Model_AttributeSelection::update()
     } else { // simple construction element: the selected is that needed
       selectConstruction(aContext, aContext->shape());
       owner()->data()->sendAttributeUpdated(this);
-      return true;
+      return setInvalidIfFalse(aSelLab, true);
     }
   }
-  return false; // unknown case
+  return setInvalidIfFalse(aSelLab, false); // unknown case
 }
 
 
@@ -527,11 +631,9 @@ void Model_AttributeSelection::selectBody(
   }
   TopoDS_Shape aNewShape = theSubShape ? theSubShape->impl<TopoDS_Shape>() : aContext;
   /// fix for issue 411: result modified shapes must not participate in this selection mechanism
-  /*
   FeaturePtr aFeatureOwner = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
   if (aFeatureOwner.get())
     aFeatureOwner->eraseResults();
-    */
   if (!aContext.IsNull()) {
     aSel.Select(aNewShape, aContext); 
   }
@@ -665,7 +767,7 @@ void Model_AttributeSelection::selectConstruction(
             if (allCurves.Contains(aCurve)) {
               int anID = aComposite->subFeatureId(a);
               aRefs->Add(anID);
-              if (aShapeType != TopAbs_EDGE) { // face nneds the sub-edges on sub-labels
+              if (aShapeType != TopAbs_EDGE) { // face needs the sub-edges on sub-labels
                 // add edges to sub-label to support naming for edges selection
                 TopExp_Explorer anEdgeExp(aSubShape, TopAbs_EDGE);
                 for(; anEdgeExp.More(); anEdgeExp.Next()) {
@@ -727,7 +829,7 @@ bool Model_AttributeSelection::selectPart(
   // store the shape (in case part is not loaded it should be usefull
   TopoDS_Shape aShape;
   std::string aName = theContext->data()->name();
-  if (theSubShape->isNull()) {// the whole part shape is selected
+  if (!theSubShape.get() || theSubShape->isNull()) {// the whole part shape is selected
     aShape = theContext->shape()->impl<TopoDS_Shape>();
   } else {
     aShape = theSubShape->impl<TopoDS_Shape>();
@@ -771,6 +873,24 @@ void Model_AttributeSelection::selectSubShape(
 {
   if(theSubShapeName.empty() || theType.empty()) return;
 
+  // check this is Part-name: 2 delimiters in the name
+  std::size_t aPartEnd = theSubShapeName.find('/');
+  if (aPartEnd != string::npos && aPartEnd != theSubShapeName.rfind('/')) {
+    std::string aPartName = theSubShapeName.substr(0, aPartEnd);
+    ObjectPtr aFound = owner()->document()->objectByName(ModelAPI_ResultPart::group(), aPartName);
+    if (aFound.get()) { // found such part, so asking it for the name
+      ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aFound);
+      string aNameInPart = theSubShapeName.substr(aPartEnd + 1);
+      int anIndex;
+      std::shared_ptr<GeomAPI_Shape> aSelected = aPart->shapeInPart(aNameInPart, theType, anIndex);
+      if (aSelected.get()) {
+        setValue(aPart, aSelected);
+        TDataStd_Integer::Set(selectionLabel(), anIndex);
+        return;
+      }
+    }
+  }
+
   Model_SelectionNaming aSelNaming(selectionLabel());
   std::shared_ptr<Model_Document> aDoc = 
     std::dynamic_pointer_cast<Model_Document>(owner()->document());