Salome HOME
Initial implementation of work of "To add all elements that share the same topology...
authormpv <mpv@opencascade.com>
Mon, 12 Nov 2018 14:21:52 +0000 (17:21 +0300)
committermpv <mpv@opencascade.com>
Mon, 19 Nov 2018 08:45:52 +0000 (11:45 +0300)
14 files changed:
doc/gui/Introduction.rst
src/CollectionAPI/CollectionAPI_Group.cpp
src/CollectionAPI/CollectionAPI_Group.h
src/Model/Model_AttributeSelection.cpp
src/Model/Model_AttributeSelection.h
src/Model/Model_AttributeSelectionList.cpp
src/Model/Model_AttributeSelectionList.h
src/Model/Model_ResultPart.cpp
src/Model/Model_ResultPart.h
src/ModelAPI/ModelAPI_AttributeSelection.h
src/ModelAPI/ModelAPI_ResultPart.h
src/ModuleBase/ModuleBase_WidgetMultiSelector.cpp
src/Selector/Selector_Selector.cpp
src/Selector/Selector_Selector.h

index 613bd11aa980c96e06a21de09d15c712fc3da13f..ae5c997e9acaf47d8600c95fe0e373f922a2bbf5 100644 (file)
@@ -241,7 +241,7 @@ Each feature, result, construction, group, field, parameter can be renamed using
 .. centered::\r
    Construction pop-up menu\r
 \r
-The order of features can be changed using *Move to the end* pop-up menu command. The selected feature will be moved to the end of feature list.\r
+The order of features can be changed using *Move to the end* pop-up menu command. It works only for Group features. The selected group will be moved to the end of features list.\r
 \r
 Folders can be used to arrange long Tree View for features.\r
 \r
index e6660beec193d03309016dfeceed7ef0d8bc018e..19fd1a44e7d77247a3f794db1a84a2db793a6195 100644 (file)
@@ -64,13 +64,19 @@ void CollectionAPI_Group::dump(ModelHighAPI_Dumper& theDumper) const
 
   AttributeSelectionListPtr anAttrList = aBase->selectionList(CollectionPlugin_Group::LIST_ID());
 
-  theDumper << aBase << " = model.addGroup(" << aDocName << ", " << anAttrList << ")" << std::endl;
+  theDumper << aBase << " = model.addGroup(" << aDocName << ", " << anAttrList;
+  if (anAttrList->isGeometricalSelection())
+    theDumper <<", True";
+  theDumper << ")" << std::endl;
 }
 
 //==================================================================================================
 GroupPtr addGroup(const std::shared_ptr<ModelAPI_Document>& thePart,
-                  const std::list<ModelHighAPI_Selection>& theGroupList)
+                  const std::list<ModelHighAPI_Selection>& theGroupList,
+                  const bool theShareSameTopology)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature = thePart->addFeature(CollectionAPI_Group::ID());
+  if (theShareSameTopology)
+    aFeature->selectionList(CollectionPlugin_Group::LIST_ID())->setGeometricalSelection(true);
   return GroupPtr(new CollectionAPI_Group(aFeature, theGroupList));
 }
index bb92ff1b81a5e64c0fc2b0359ac716a47ded3335..fe28fc2f4074c06afe35cb18665cc80502c3ab14 100644 (file)
@@ -70,6 +70,7 @@ typedef std::shared_ptr<CollectionAPI_Group> GroupPtr;
 /// \brief Create Group feature.
 COLLECTIONAPI_EXPORT
 GroupPtr addGroup(const std::shared_ptr<ModelAPI_Document>& thePart,
-                  const std::list<ModelHighAPI_Selection>& theGroupList);
+                  const std::list<ModelHighAPI_Selection>& theGroupList,
+                  const bool theShareSameTopology = false);
 
 #endif // CollectionAPI_Group_H_
index a59e90a0d8d825be7486d149bfd22bc6f59fa62b..7ca2ca08983c15ca488215d8e1b1e67db6c04067 100644 (file)
@@ -1770,3 +1770,49 @@ ResultPtr Model_AttributeSelection::newestContext(
     aResult = theCurrent;
   return aResult;
 }
+
+void Model_AttributeSelection::combineGeometrical()
+{
+  if (myTmpContext.get() || myTmpSubShape.get())
+    return;
+  TDF_Label aSelLab = selectionLabel();
+  if (aSelLab.IsAttribute(kINVALID_SELECTION) || !myRef.isInitialized())
+    return;
+
+  if (aSelLab.IsAttribute(kCIRCLE_CENTER) || aSelLab.IsAttribute(kELLIPSE_CENTER1) ||
+      aSelLab.IsAttribute(kELLIPSE_CENTER2) || aSelLab.IsAttribute(kSIMPLE_REF_ID))
+    return;
+
+  if (aSelLab.IsAttribute(kPART_REF_ID)) {
+    ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(context());
+    if (!aPart.get() || !aPart->isActivated())
+      return; // postponed naming needed
+    Handle(TDataStd_Integer) anIndex;
+    if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
+      if (anIndex->Get()) { // special selection attribute was created, use it
+        std::string aNewName;
+        aPart->combineGeometrical(anIndex->Get(), aNewName);
+        TDataStd_Name::Set(aSelLab, aNewName.c_str());
+      }
+    }
+    return;
+  }
+
+  std::shared_ptr<Model_ResultConstruction> aConstr =
+    std::dynamic_pointer_cast<Model_ResultConstruction>(context());
+  if (aConstr.get())
+    return;
+  FeaturePtr aFeature = contextFeature();
+  if (aFeature.get())
+    return;
+
+  Selector_Selector aSelector(aSelLab);
+  if (ModelAPI_Session::get()->moduleDocument() != owner()->document()) {
+    aSelector.setBaseDocument(std::dynamic_pointer_cast<Model_Document>
+      (ModelAPI_Session::get()->moduleDocument())->extConstructionsLabel());
+  }
+  if (aSelector.restore()) {
+    TopoDS_Shape aContextShape = context()->shape()->impl<TopoDS_Shape>();
+    aSelector.combineGeometrical(aContextShape);
+  }
+}
index 0e98757fbbdfa12ffbafb389d91bf78d58b6cea4..a624e5cf08976a531e1c6f948a71fb5f372079a0 100644 (file)
@@ -150,6 +150,9 @@ public:
   /// Returns the name by context. Adds the part name if the context is located in other document
   MODEL_EXPORT virtual std::string contextName(const ResultPtr& theContext) const;
 
+  /// Makes the current local selection becomes all sub-shapes with same base geometry.
+  MODEL_EXPORT virtual void combineGeometrical();
+
 protected:
   /// Objects are created for features automatically
   MODEL_EXPORT Model_AttributeSelection(TDF_Label& theLabel);
index f32fbcf6396c0b6b8cd01a24cd9a6a819d99c203..617007a5e1b9b0605ec8d52e4c4e56ac67d5c36e 100644 (file)
 
 #include <GeomAPI_Pnt.h>
 #include <GeomAPI_Shape.h>
+#include <GeomAPI_ShapeIterator.h>
 
 #include <TDF_AttributeIterator.hxx>
 #include <TDF_ChildIterator.hxx>
 #include <TDF_RelocationTable.hxx>
 #include <TDF_DeltaOnAddition.hxx>
+#include <TDataStd_UAttribute.hxx>
 #include <TopAbs_ShapeEnum.hxx>
 #include <TopoDS.hxx>
 #include <TopoDS_Shape.hxx>
 #include <TNaming_NamedShape.hxx>
 #include <NCollection_List.hxx>
 
+/// GUID for UAttribute that indicates the list has "To add all elements that share the same
+/// topology" flag enabled
+static const Standard_GUID kIS_GEOMETRICAL_SELECTION("f16987b6-e6c8-435c-99fa-03a7e0b06e83");
+
 void Model_AttributeSelectionList::append(
     const ObjectPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape,
     const bool theTemporarily)
@@ -231,7 +237,7 @@ void Model_AttributeSelectionList::remove(const std::set<int>& theIndices)
 {
   int anOldSize = mySize->Get();
   int aRemoved = 0;
-  // iterate one by one and shifting the removed indicies
+  // iterate one by one and shifting the removed indices
   for(int aCurrent = 0; aCurrent < anOldSize; aCurrent++) {
     if (theIndices.find(aCurrent) == theIndices.end()) { // not removed
       if (aRemoved) { // but must be shifted to the removed position
@@ -263,6 +269,19 @@ int Model_AttributeSelectionList::size()
   return mySize->Get();
 }
 
+// returns true if theShape is same with theInList or is contained in it (a compound)
+static bool isIn(GeomShapePtr theInList, GeomShapePtr theShape) {
+  if (theShape->isSame(theInList))
+    return true;
+  if (theInList.get() && theInList->shapeType() == GeomAPI_Shape::COMPOUND) {
+    for(GeomAPI_ShapeIterator anIter(theInList); anIter.more(); anIter.next()) {
+      if (!anIter.current()->isNull() && anIter.current()->isSame(theShape))
+        return true;
+    }
+  }
+  return false;
+}
+
 bool Model_AttributeSelectionList::isInList(const ObjectPtr& theContext,
                                             const std::shared_ptr<GeomAPI_Shape>& theSubShape,
                                             const bool theTemporarily)
@@ -281,7 +300,7 @@ bool Model_AttributeSelectionList::isInList(const ObjectPtr& theContext,
               return true;
           } else {
             // we need to call here isSame instead of isEqual to do not check shapes orientation
-            if (theSubShape->isSame(*aShapes))
+            if (isIn(*aShapes, theSubShape))
               return true;
           }
         }
@@ -301,7 +320,7 @@ bool Model_AttributeSelectionList::isInList(const ObjectPtr& theContext,
           }
         } else {
           // we need to call here isSame instead of isEqual to do not check shapes orientation
-          if (theSubShape->isSame(aValue)) // shapes are equal
+          if (isIn(aValue, theSubShape)) // shapes are equal
             return true;
         }
       }
@@ -328,8 +347,8 @@ std::shared_ptr<ModelAPI_AttributeSelection>
   }
   TDF_Label aLabel = mySize->Label().FindChild(theIndex + 1);
   // create a new attribute each time, by demand
-  // supporting of old attributes is too slow (synch each time) and buggy on redo
-  // (if attribute is deleted and created, the abort updates attriute and makes the Attr invalid)
+  // supporting of old attributes is too slow (sync each time) and buggy on redo
+  // (if attribute is deleted and created, the abort updates attribute and makes the Attr invalid)
   std::shared_ptr<Model_AttributeSelection> aNewAttr =
     std::shared_ptr<Model_AttributeSelection>(new Model_AttributeSelection(aLabel));
   if (owner()) {
@@ -371,8 +390,7 @@ bool Model_AttributeSelectionList::isInitialized()
 }
 
 Model_AttributeSelectionList::Model_AttributeSelectionList(TDF_Label& theLabel)
-: myLab(theLabel),
-  myIsGeometricalSelection(false)
+: myLab(theLabel)
 {
   reinit();
 }
@@ -404,10 +422,58 @@ void Model_AttributeSelectionList::cashValues(const bool theEnabled)
   }
 }
 
+bool Model_AttributeSelectionList::isGeometricalSelection() const
+{
+  return myLab.IsAttribute(kIS_GEOMETRICAL_SELECTION);
+}
+
 void Model_AttributeSelectionList::setGeometricalSelection(const bool theIsGeometricalSelection)
 {
-  myIsGeometricalSelection = theIsGeometricalSelection;
-  // TODO: update list accodring to the flag:
-  // false - all objects with same geometry must be splited in separate.
-  // true - all objets with same geometry must be combined into single.
+  if (isGeometricalSelection() == theIsGeometricalSelection)
+    return; // nothing to do
+  if (theIsGeometricalSelection) // store the state
+    TDataStd_UAttribute::Set(myLab, kIS_GEOMETRICAL_SELECTION);
+  else
+    myLab.ForgetAttribute(kIS_GEOMETRICAL_SELECTION);
+  std::set<int> anIndiciesToRemove;  // Update list according to the flag
+  if (theIsGeometricalSelection) { // all objects with same geometry must be combined into single
+    std::list<AttributeSelectionPtr> anAttributes; // collect attributes with geometrical compounds
+    for(int anIndex = 0; anIndex < size(); anIndex++) {
+      AttributeSelectionPtr anAttr = value(anIndex);
+      if (!anAttr.get() || !anAttr->context().get())
+        continue;
+      anAttr->combineGeometrical();
+      if (!anAttr->value().get() || anAttr->value()->shapeType() != GeomAPI_Shape::COMPOUND)
+        continue;
+      // check it is equal to some other attribute already presented in the list
+      std::list<AttributeSelectionPtr>::iterator anAttrIter = anAttributes.begin();
+      for(; anAttrIter != anAttributes.end(); anAttrIter++) {
+        if (anAttr->context() == (*anAttrIter)->context() &&
+            anAttr->namingName() == (*anAttrIter)->namingName()) {
+          anIndiciesToRemove.insert(anIndex);
+          break;
+        }
+      }
+      if (anAttrIter == anAttributes.end()) // not removed, so, add to the compare-list
+        anAttributes.push_back(anAttr);
+    }
+  } else { // all objects with same geometry must be divided into separated sub-attributes
+    int anInitialSize = size();
+    for(int anIndex = 0; anIndex < anInitialSize; anIndex++) {
+      AttributeSelectionPtr anAttr = value(anIndex);
+      if (!anAttr.get() || !anAttr->context().get())
+        continue;
+      GeomShapePtr aValue = anAttr->value();
+      if (!aValue.get() || aValue->shapeType() != GeomAPI_Shape::COMPOUND)
+        continue;
+      for(GeomAPI_ShapeIterator anIter(aValue); anIter.more(); anIter.next()) {
+        append(anAttr->context(), anIter.current());
+      }
+      anIndiciesToRemove.insert(anIndex);
+    }
+  }
+  remove(anIndiciesToRemove);
+  myIsCashed = false;
+  myCash.clear(); // empty list as indicator that cash is not used
+  owner()->data()->sendAttributeUpdated(this);
 }
index 1870d95d4a677971d1bc9f26c6e2ca0edb0076da..d84ca1ff4187b766745b8e7262242aa73e6ec8df 100644 (file)
@@ -45,14 +45,12 @@ class Model_AttributeSelectionList : public ModelAPI_AttributeSelectionList
   /// the cashed shapes to optimize isInList method: from context to set of shapes in this context
   std::map<ResultPtr, std::list<std::shared_ptr<GeomAPI_Shape> > > myCash;
   bool myIsCashed; ///< true if cashing is performed
-  /// If true attribute selects geometry instead of shape.
-  bool myIsGeometricalSelection;
 public:
   /// Adds the new reference to the end of the list
   /// \param theContext object where the sub-shape was selected
   /// \param theSubShape selected sub-shape (if null, the whole context is selected)
   /// \param theTemporarily if it is true, do not store and name the added in the data framework
-  ///           (used to remove immideately, without the following updates)
+  ///           (used to remove immediately, without the following updates)
   MODEL_EXPORT virtual void append(
     const ObjectPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape,
     const bool theTemporarily = false);
@@ -79,14 +77,14 @@ public:
   /// \param theIndices a list of indices of elements to be removed
   MODEL_EXPORT virtual void remove(const std::set<int>& theIndices);
 
-  /// Returns the number ofselection attributes in the list
+  /// Returns the number of selection attributes in the list
   MODEL_EXPORT virtual int size();
 
   /// Returns true if the object with the shape are in list
   /// \param theContext object where the sub-shape was selected
   /// \param theSubShape selected sub-shape (if null, the whole context is selected)
   /// \param theTemporarily if it is true, it checks also the temporary added item
-  /// \returns true if the pair is found in the attirbute
+  /// \returns true if the pair is found in the attribute
   MODEL_EXPORT virtual bool isInList(
     const ObjectPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape,
     const bool theTemporarily = false);
@@ -116,9 +114,7 @@ public:
   MODEL_EXPORT virtual void setGeometricalSelection(const bool theIsGeometricalSelection) override;
 
   /// Returns true if is geometrical selection.
-  MODEL_EXPORT virtual bool isGeometricalSelection() const override {
-    return myIsGeometricalSelection;
-  };
+  MODEL_EXPORT virtual bool isGeometricalSelection() const override;
 
 protected:
   /// Objects are created for features automatically
index c4f6f309e207b762e1d00d5c4b41ad94b417ebf0..dbad51c7be3630204a22249cccb871b745d5c82a 100644 (file)
@@ -323,6 +323,24 @@ gp_Trsf Model_ResultPart::sumTrsf() {
   return aResult;
 }
 
+bool Model_ResultPart::combineGeometrical(const int theIndex, std::string& theNewName)
+{
+  std::shared_ptr<Model_Document> aDoc = std::dynamic_pointer_cast<Model_Document>(partDoc());
+  if (aDoc.get()) {
+    AttributeSelectionListPtr aSelAttr = aDoc->selectionInPartFeature();
+    AttributeSelectionPtr aThisAttr = aSelAttr->value(theIndex - 1);
+    if (aThisAttr.get()) {
+      aThisAttr->combineGeometrical();
+      if (aThisAttr->value().get()) {
+        int anIndex;
+        theNewName = nameInPart(aThisAttr->value(), anIndex);
+        return true;
+      }
+    }
+  }
+  return false; // something is wrong
+}
+
 std::shared_ptr<GeomAPI_Shape> Model_ResultPart::shapeInPart(
   const std::string& theName, const std::string& theType, int& theIndex)
 {
index b6a1305115ef082accb27186ce9e4342fec6dd2c..d632ae187b7184f7e41b333e48099de74cfbaea9 100644 (file)
@@ -79,6 +79,8 @@ class Model_ResultPart : public ModelAPI_ResultPart
   /// Returns the shape by the name in the part
   MODEL_EXPORT virtual std::shared_ptr<GeomAPI_Shape> shapeInPart(
     const std::string& theName, const std::string& theType, int& theIndex);
+  /// Updates the selection inside of the part as a geometrical selection
+  MODEL_EXPORT virtual bool combineGeometrical(const int theIndex, std::string& theNewName);
   /// Updates the shape-result of the part (called on Part feature execution)
   MODEL_EXPORT virtual void updateShape();
   /// Applies the additional transformation of the part
index 93ad948d584751b277e1e7daac3128061dfdf0f6..d2dec5bc49e02e0e4b0ceb595b33acd1c849f9f6 100644 (file)
@@ -124,6 +124,8 @@ class ModelAPI_AttributeSelection : public ModelAPI_Attribute
   /// Returns the name by context. Adds the part name if the context is located in other document
   MODELAPI_EXPORT virtual std::string contextName(const ResultPtr& theContext) const = 0;
 
+  /// Makes the current local selection becomes all sub-shapes with same base geometry.
+  MODELAPI_EXPORT virtual void combineGeometrical() = 0;
 
  protected:
   /// Objects are created for features automatically
index 418e2d9d9f3aed4d8bda51447a0d4c511ee61b14..db0baf99ca3ae07a774872deb992d4f84671ae2f 100644 (file)
@@ -87,6 +87,9 @@ class ModelAPI_ResultPart : public ModelAPI_Result
   virtual std::shared_ptr<GeomAPI_Shape> shapeInPart(
     const std::string& theName, const std::string& theType, int& theIndex) = 0;
 
+  /// Updates the selection inside of the part as a geometrical selection
+  virtual bool combineGeometrical(const int theIndex, std::string& theNewName) = 0;
+
   /// Returns the shape selected in the selection index
   virtual std::shared_ptr<GeomAPI_Shape> selectionValue(const int theIndex) = 0;
 
index 9a2007cd9f97174a00c3c898e953887035b85b6f..b17d97413b321aafc59d465afe27464f514f355d 100755 (executable)
@@ -160,9 +160,6 @@ ModuleBase_WidgetMultiSelector::ModuleBase_WidgetMultiSelector(QWidget* theParen
 
   aMainLay->addWidget(myListView->getControl(), 2, 0, 1, -1);
   aMainLay->setRowStretch(2, 1);
-  //aMainLay->addWidget(new QLabel(this)); //FIXME(sbh)???
-  //aMainLay->setRowMinimumHeight(3, 20);
-  //this->setLayout(aMainLay);
   connect(myTypeCtrl, SIGNAL(valueChanged(int)), this, SLOT(onSelectionTypeChanged()));
 
   bool aSameTop = theData->getBooleanAttribute("same_topology", false);
@@ -250,9 +247,9 @@ bool ModuleBase_WidgetMultiSelector::restoreValueCustom()
     return false;
 
   AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
+  AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
   std::string aType = anAttribute->attributeType();
   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
-    AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
     // Restore shape type
     std::string aSelectionType = aSelectionListAttr->selectionType().c_str();
     if (!aSelectionType.empty()) {
@@ -261,6 +258,7 @@ bool ModuleBase_WidgetMultiSelector::restoreValueCustom()
       myIsFirst = false;
     }
   }
+  myGeomCheck->setChecked(aSelectionListAttr->isGeometricalSelection());
   updateSelectionList();
   return true;
 }
@@ -1058,7 +1056,6 @@ void ModuleBase_WidgetMultiSelector::onSameTopology(bool theOn)
   std::string aType = anAttribute->attributeType();
   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
-    //TODO: set same topology flag
     aSelectionListAttr->setGeometricalSelection(theOn);
     updateObject(myFeature);
   }
index 4f5f62f2e130e9e2a734b12a5d4dca88d1f79412..140294004a7058bb65c2ab028dc533b1144b6328 100644 (file)
@@ -75,6 +75,7 @@ static const std::string kPUREWEAK_NAME_IDENTIFIER = "_weak_name_";
 Selector_Selector::Selector_Selector(TDF_Label theLab) : myLab(theLab)
 {
   myWeakIndex = -1;
+  myAlwaysGeometricalNaming = false;
 }
 
 TDF_Label Selector_Selector::label()
@@ -362,7 +363,7 @@ bool Selector_Selector::select(const TopoDS_Shape theContext, const TopoDS_Shape
     { // iterate all sub-shapes and select them on sublabels
       for(TopoDS_Iterator aSubIter(theValue); aSubIter.More(); aSubIter.Next()) {
         if (!selectBySubSelector(theContext, aSubIter.Value(),
-            theGeometricalNaming, theUseNeighbors, theUseIntersections)) {
+            false, theUseNeighbors, theUseIntersections)) {//for subs no geometrical naming allowed
           return false; // if some selector is failed, everything is failed
         }
       }
@@ -461,8 +462,20 @@ bool Selector_Selector::select(const TopoDS_Shape theContext, const TopoDS_Shape
       }
     }
 
-    // weak naming to distinguish commons coming from intersection
     if (aLastCommon.Extent() > 1) {
+      if (myAlwaysGeometricalNaming) {
+        TopoDS_ListOfShape::Iterator aCommonIter(aLastCommon);
+        TopoDS_Shape aFirst = aCommonIter.Value();
+        for(aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
+          if (!sameGeometry(aFirst, aCommonIter.Value()))
+            break;
+        }
+        if (!aCommonIter.More()) { // all geometry is same, result is a compound
+          myType = SELTYPE_INTERSECT;
+          return true;
+        }
+      }
+      // weak naming to distinguish commons coming from intersection
       Selector_NExplode aNexp(aLastCommon);
       myWeakIndex = aNexp.index(theValue);
       if (myWeakIndex != -1) {
@@ -580,6 +593,18 @@ bool Selector_Selector::select(const TopoDS_Shape theContext, const TopoDS_Shape
       findModificationResult(aCommon);
       // trying to search by neighbors
       if (aCommon.Extent() > 1) { // more complicated selection
+        if (myAlwaysGeometricalNaming) {
+          TopoDS_ListOfShape::Iterator aCommonIter(aCommon);
+          TopoDS_Shape aFirst = aCommonIter.Value();
+          for(aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
+            if (!sameGeometry(aFirst, aCommonIter.Value()))
+              break;
+          }
+          if (!aCommonIter.More()) { // all geometry is same, result is a compound
+            myType = SELTYPE_MODIFICATION;
+            return true;
+          }
+        }
         if (!theUseNeighbors)
           return false;
 
@@ -622,7 +647,20 @@ bool Selector_Selector::select(const TopoDS_Shape theContext, const TopoDS_Shape
           }
         }
         // filter by neighbors did not help
-        if (aCommon.Extent() > 1) { // weak naming between the common results
+        if (aCommon.Extent() > 1) {
+          if (myAlwaysGeometricalNaming) {
+            TopoDS_ListOfShape::Iterator aCommonIter(aCommon);
+            TopoDS_Shape aFirst = aCommonIter.Value();
+            for(aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
+              if (!sameGeometry(aFirst, aCommonIter.Value()))
+                break;
+            }
+            if (!aCommonIter.More()) { // all geometry is same, result is a compound
+              myType = SELTYPE_FILTER_BY_NEIGHBOR;
+              return true;
+            }
+          }
+          // weak naming between the common results
           Selector_NExplode aNexp(aCommon);
           myWeakIndex = aNexp.index(theValue);
           if (myWeakIndex == -1)
@@ -1361,3 +1399,40 @@ bool Selector_Selector::selectBySubSelector(const TopoDS_Shape theContext,
   }
   return true;
 }
+
+void Selector_Selector::combineGeometrical(const TopoDS_Shape theContext)
+{
+  TopoDS_Shape aValue = value();
+  if (aValue.IsNull() || aValue.ShapeType() == TopAbs_COMPOUND)
+    return;
+  myAlwaysGeometricalNaming = true;
+  mySubSelList.clear();
+  myBases.Clear();
+  myWeakIndex = -1;
+  if (select(theContext, aValue, true)) {
+    store();
+    solve(theContext);
+    return;
+  }
+  // if can not select, select the compound in a custom way
+  TopTools_MapOfShape aMap;
+  TopoDS_ListOfShape aList;
+  for(TopExp_Explorer anExp(theContext, aValue.ShapeType()); anExp.More(); anExp.Next()) {
+    if (aMap.Add(anExp.Current())) {
+      if (sameGeometry(aValue, anExp.Current()))
+        aList.Append(anExp.Current());
+    }
+  }
+  if (aList.Size() > 1) {
+    TopoDS_Builder aBuilder;
+    TopoDS_Compound aCompound;
+    aBuilder.MakeCompound(aCompound);
+    for(TopoDS_ListIteratorOfListOfShape aListIter(aList); aListIter.More(); aListIter.Next()) {
+      aBuilder.Add(aCompound, aListIter.Value());
+    }
+    if (select(theContext, aCompound, true)) {
+      store();
+      solve(theContext);
+    }
+  }
+}
index fa069c4aa09d31b5e72e8f898226b3aff2b46b2d..46f2db6e53a0effdda3fbbc4cbd86063b70cd035 100644 (file)
@@ -62,6 +62,7 @@ class Selector_Selector
   TDF_Label myBaseDocumentLab; ///< an access-label to the document that may contain initial shapes
 
   bool myGeometricalNaming; ///< flag that indicates that geometrical naming selection is enabled
+  bool myAlwaysGeometricalNaming; /// to enable geometrical naming from beginning, at select
 
 public:
   /// Initializes selector on the label
@@ -105,6 +106,9 @@ public:
   /// Returns the naming name of the selection
   SELECTOR_EXPORT std::string name(Selector_NameGenerator* theNameGenerator);
 
+  /// Makes the current local selection becomes all sub-shapes with same base geometry.
+  SELECTOR_EXPORT void combineGeometrical(const TopoDS_Shape theContext);
+
 private:
 
   /// Create and keep in the list the sub-selector that select the given value.