]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Issue #3141 implementation : Allow usage of the same filters many times
authormpv <mpv@opencascade.com>
Wed, 19 Feb 2020 09:54:17 +0000 (12:54 +0300)
committermpv <mpv@opencascade.com>
Wed, 19 Feb 2020 09:54:17 +0000 (12:54 +0300)
21 files changed:
src/Config/Config_WidgetAPI.cpp
src/Config/Config_WidgetAPI.h
src/FiltersAPI/FiltersAPI_Feature.cpp
src/FiltersPlugin/CMakeLists.txt
src/FiltersPlugin/FiltersPlugin_OnGeometry.h
src/FiltersPlugin/FiltersPlugin_OnLine.h
src/FiltersPlugin/FiltersPlugin_OnPlane.h
src/FiltersPlugin/FiltersPlugin_OnPlaneSide.h
src/FiltersPlugin/FiltersPlugin_RelativeToSolid.h
src/FiltersPlugin/FiltersPlugin_Selection.cpp
src/FiltersPlugin/FiltersPlugin_Selection.h
src/FiltersPlugin/Test/TestFilter_OnLine_Multi.py [new file with mode: 0644]
src/FiltersPlugin/Test/TestFilter_OnPlaneSide_Multi.py [new file with mode: 0644]
src/FiltersPlugin/Test/TestFilter_OnPlane_Multi.py [new file with mode: 0644]
src/FiltersPlugin/Test/TestFilter_RelativeToSolid_Multi.py [new file with mode: 0644]
src/FiltersPlugin/doc/FiltersPlugin.rst
src/Model/Model_FiltersFactory.cpp
src/ModelAPI/ModelAPI_FiltersFeature.h
src/ModuleBase/ModuleBase_WidgetFactory.cpp
src/ModuleBase/ModuleBase_WidgetFactory.h
src/ModuleBase/ModuleBase_WidgetSelectionFilter.cpp

index cc1dfa98629fbcf9116bdee0297b621728861d3b..5ca092bf8f198429b7760371005e9770bc0c5ec1 100644 (file)
 
 #include <string>
 
-Config_WidgetAPI::Config_WidgetAPI(std::string theRawXml)
+Config_WidgetAPI::Config_WidgetAPI(std::string theRawXml, const std::string theAttributePrefix)
 {
   myDoc = xmlParseDoc(BAD_CAST theRawXml.c_str());
   myCurrentNode = xmlDocGetRootElement(myDoc);
   myFeatureId = getProperty(_ID);
+  myAttributePrefix = theAttributePrefix;
 }
 
 Config_WidgetAPI::~Config_WidgetAPI()
@@ -119,7 +120,7 @@ std::string Config_WidgetAPI::featureId() const
 
 std::string Config_WidgetAPI::widgetId() const
 {
-  return getProperty(_ID);
+  return myAttributePrefix + getProperty(_ID);
 }
 
 std::string Config_WidgetAPI::widgetIcon() const
index 055097cb7a2e9f1ea382950e340a0e451e489cee..6ef588bd6cff7cfab417f8bb06e53a744c5e278d 100644 (file)
@@ -78,7 +78,7 @@ class Config_WidgetAPI
   CONFIG_EXPORT bool getBooleanAttribute(const char* theAttributeName, bool theDefault) const;
 
   /// These fields are accessible for ModuleBase_WidgetFactory only
-  CONFIG_EXPORT Config_WidgetAPI(std::string theRawXml);
+  CONFIG_EXPORT Config_WidgetAPI(std::string theRawXml, const std::string theAttributePrefix = "");
   //! Pass to the next (sibling) node of widget's xml definition. If impossible, returns false
   CONFIG_EXPORT bool toNextWidget();
   //! Pass into the child node of widget's xml definition. If impossible, returns false
@@ -90,6 +90,7 @@ class Config_WidgetAPI
   xmlDocPtr myDoc; //!< Pointer to the root of widget's xml definition
   xmlNodePtr myCurrentNode; //!< Pointer to the current node in the widget's xml definition
   std::string myFeatureId;
+  std::string myAttributePrefix; //!< prefix that must be added to the attribute name
 
   friend class ModuleBase_WidgetFactory;
 };
index 14bdbf6658c60cd8c39246a5c5428455b47cb201..5fe14fd73362f36d0f21a34ae326d9b7425794fc 100644 (file)
@@ -23,6 +23,8 @@
 
 #include <ModelHighAPI_Dumper.h>
 #include <ModelHighAPI_Tools.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_FiltersFactory.h>
 
 FiltersAPI_Feature::FiltersAPI_Feature(
     const std::shared_ptr<ModelAPI_Feature> & theFeature)
@@ -57,8 +59,8 @@ void FiltersAPI_Feature::setFilters(const std::list<FilterAPIPtr>& theFilters)
   FiltersFeaturePtr aBase = std::dynamic_pointer_cast<ModelAPI_FiltersFeature>(feature());
   for (std::list<FilterAPIPtr>::const_iterator anIt = theFilters.begin();
        anIt != theFilters.end(); ++anIt) {
-    aBase->addFilter((*anIt)->name());
-    aBase->setReversed((*anIt)->name(), (*anIt)->isReversed());
+    std::string aFilterID = aBase->addFilter((*anIt)->name());
+    aBase->setReversed(aFilterID, (*anIt)->isReversed());
 
     const std::list<FiltersAPI_Argument>& anArgs = (*anIt)->arguments();
     if (!anArgs.empty()) {
@@ -68,7 +70,7 @@ void FiltersAPI_Feature::setFilters(const std::list<FilterAPIPtr>& theFilters)
       std::list<bool> aBools;
       separateArguments(anArgs, aSelections, aTexts, aBools);
 
-      std::list<AttributePtr> aFilterArgs = aBase->filterArgs((*anIt)->name());
+      std::list<AttributePtr> aFilterArgs = aBase->filterArgs(aFilterID);
       std::list<AttributePtr>::iterator aFIt = aFilterArgs.begin();
       // first boolean argument is always "Reversed" flag
       AttributeBooleanPtr aReversedFlag =
@@ -115,9 +117,12 @@ void FiltersAPI_Feature::dump(ModelHighAPI_Dumper& theDumper) const
   const std::string& aDocName = theDumper.name(aBase->document());
   theDumper << "model.filters(" << aDocName << ", [";
 
+  ModelAPI_FiltersFactory* aFFactory = ModelAPI_Session::get()->filters();
   std::list<std::string> aFilters = aBase->filters();
   for (std::list<std::string>::iterator aFIt = aFilters.begin(); aFIt != aFilters.end(); ++aFIt) {
-    FiltersAPI_Filter aFilter(*aFIt, aBase->filterArgs(*aFIt));
+    // for multiple filters get original id
+    std::string aFilterKind = aFFactory->id(aFFactory->filter(*aFIt));
+    FiltersAPI_Filter aFilter(aFilterKind, aBase->filterArgs(*aFIt));
     if (aFIt != aFilters.begin())
       theDumper << ", ";
     aFilter.dump(theDumper);
index d80b525159d47c74ec9c1f85be48c46b12d995f1..69f98f31f2e4cb631c9315e560d6f652b6c9ecea 100644 (file)
@@ -193,4 +193,8 @@ ADD_UNIT_TESTS(
   Test2951.py
   Test17924.py
   Test17962.py
+  TestFilter_OnLine_Multi.py
+  TestFilter_OnPlane_Multi.py
+  TestFilter_OnPlaneSide_Multi.py
+  TestFilter_RelativeToSolid_Multi.py
 )
index 2d25d426616380d1e46336def9c29cb8202602eb..c90e5dd87076b8aca8162a3587a30832e08df84b 100644 (file)
@@ -48,9 +48,6 @@ public:
   virtual bool isOk(const GeomShapePtr& theShape, const ResultPtr&,
                     const ModelAPI_FiltersArgs& theArgs) const override;
 
-  /// Returns True if the filter can be used several times within one filtering
-  virtual bool isMultiple() const { return true; }
-
   /// Returns XML string which represents GUI of the filter
   virtual std::string xmlRepresentation() const override;
 
index b609bd940ae0697d0660021b8ad1393ab1705941..773af38eb4cf8e30bb0ea401c27ec8e4b0541bb1 100644 (file)
@@ -41,6 +41,9 @@ public:
   /// Returns true for any type because it supports all selection types
   virtual bool isSupported(GeomAPI_Shape::ShapeType theType) const override;
 
+  /// Returns True if the filter can be used several times within one selection
+  virtual bool isMultiple() const { return true; }
+
   /// This method should contain the filter logic. It returns true if the given shape
   /// is accepted by the filter.
   /// \param theShape the given shape
index 62fccd1fc7bb4f56b8dee528054ef5eb20b3a9bf..94c0914894714cc9095c5fc656c00cda468221dd 100644 (file)
@@ -41,6 +41,9 @@ public:
   /// Returns true for any type because it supports all selection types
   virtual bool isSupported(GeomAPI_Shape::ShapeType theType) const override;
 
+  /// Returns True if the filter can be used several times within one selection
+  virtual bool isMultiple() const { return true; }
+
   /// This method should contain the filter logic. It returns true if the given shape
   /// is accepted by the filter.
   /// \param theShape the given shape
index 9939e45888c5b51fbdc2770abca5ea8b5bc3e3bc..eae3fee509a5e8faa3407997803620a972ac3a6c 100644 (file)
@@ -41,6 +41,9 @@ public:
   /// Returns true for any type because it supports all selection types
   virtual bool isSupported(GeomAPI_Shape::ShapeType theType) const override;
 
+  /// Returns True if the filter can be used several times within one selection
+  virtual bool isMultiple() const { return true; }
+
   /// This method should contain the filter logic. It returns true if the given shape
   /// is accepted by the filter.
   /// \param theShape the given shape
index 821b9a7ec243b649f19c9459f05889c368efc0c4..70b616cc013f7604bd99932bd241bc80917d0a3d 100644 (file)
@@ -41,6 +41,9 @@ public:
   /// Returns true for any type because it supports all selection types
   virtual bool isSupported(GeomAPI_Shape::ShapeType theType) const override;
 
+  /// Returns True if the filter can be used several times within one selection
+  virtual bool isMultiple() const { return true; }
+
   /// This method should contain the filter logic. It returns true if the given shape
   /// is accepted by the filter.
   /// \param theShape the given shape
index 7fe056aba5aee9fadbf2b5d2f65023ac9b21a4e8..23343cf617bd5ce7bda89e65c0769dfe518233c8 100644 (file)
 #include <ModelAPI_AttributeSelectionList.h>
 #include <ModelAPI_FiltersFactory.h>
 #include <ModelAPI_Session.h>
+#include <sstream>
 
 // identifier of the reverse flag of a filter
 static const std::string kReverseAttrID("");
 
-void FiltersPlugin_Selection::addFilter(const std::string theFilterID)
+std::string FiltersPlugin_Selection::addFilter(const std::string theFilterID)
 {
   ModelAPI_FiltersFactory* aFactory = ModelAPI_Session::get()->filters();
   FilterPtr aFilter = aFactory->filter(theFilterID);
+
+  std::string aFilterID = theFilterID;
+  if (aFilter->isMultiple()) { // check that there is already such filter, so, increment ID
+    std::list<std::string> aFilters;
+    data()->allGroups(aFilters);
+    for(int anID = 0; true; anID++) {
+      if (anID != 0) {
+        std::ostringstream aStream;
+        aStream<<"_"<<anID<<"_"<<theFilterID;
+        aFilterID = aStream.str();
+      }
+      std::list<std::string>::iterator aFiltersIDs = aFilters.begin();
+      for(; aFiltersIDs != aFilters.end(); aFiltersIDs++) {
+        if (*aFiltersIDs == aFilterID)
+          break;
+      }
+      if (aFiltersIDs == aFilters.end())
+        break;
+    }
+  }
+
   if (aFilter.get()) {
     std::shared_ptr<ModelAPI_AttributeBoolean> aBool =
       std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(data()->addFloatingAttribute(
-        kReverseAttrID, ModelAPI_AttributeBoolean::typeId(), theFilterID));
+        kReverseAttrID, ModelAPI_AttributeBoolean::typeId(), aFilterID));
     aBool->setValue(false); // not reversed by default
     // to add attributes related to the filter
     ModelAPI_FiltersArgs anArgs;
     anArgs.setFeature(std::dynamic_pointer_cast<ModelAPI_FiltersFeature>(data()->owner()));
-    anArgs.setFilter(theFilterID);
+    anArgs.setFilter(aFilterID);
     aFilter->initAttributes(anArgs);
   }
+  return aFilterID;
 }
 
 void FiltersPlugin_Selection::removeFilter(const std::string theFilterID)
index 8fe3850a72d2eab2d200d6c6469a19c640ec7b23..1c494582d5a72150784f0e48fad3f6e1396ceb40 100644 (file)
@@ -59,7 +59,8 @@ public:
   // methods related to the filters management
 
   /// Adds a filter to the feature. Also initializes arguments of this filter.
-  FILTERS_EXPORT virtual void addFilter(const std::string theFilterID) override;
+  /// Returns the real identifier of the filter.
+  FILTERS_EXPORT virtual std::string addFilter(const std::string theFilterID) override;
 
   /// Removes an existing filter from the feature.
   FILTERS_EXPORT virtual void removeFilter(const std::string theFilterID) override;
diff --git a/src/FiltersPlugin/Test/TestFilter_OnLine_Multi.py b/src/FiltersPlugin/Test/TestFilter_OnLine_Multi.py
new file mode 100644 (file)
index 0000000..9e23a85
--- /dev/null
@@ -0,0 +1,46 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+# two filters with two edges each
+Filters = model.filters(Part_1_doc, [
+  model.addFilter(name = "OnLine", args = [model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]")]),
+  model.addFilter(name = "OnLine", args = [model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"), model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]")])])
+model.end()
+
+# 2 vertices on intersection of edges
+Reference = {
+    model.selection("VERTEX", "[Box_1_1/Front][Box_1_1/Left][Box_1_1/Top]"): True,
+    model.selection("VERTEX", "[Box_1_1/Back][Box_1_1/Left][Box_1_1/Top]"): False,
+    model.selection("VERTEX", "[Box_1_1/Front][Box_1_1/Left][Box_1_1/Bottom]"): False,
+    model.selection("VERTEX", "[Box_1_1/Front][Box_1_1/Right][Box_1_1/Top]"): False,
+    model.selection("VERTEX", "[Box_1_1/Back][Box_1_1/Right][Box_1_1/Top]"): False,
+    model.selection("VERTEX", "[Box_1_1/Back][Box_1_1/Left][Box_1_1/Bottom]"): False,
+    model.selection("VERTEX", "[Box_1_1/Front][Box_1_1/Right][Box_1_1/Bottom]"): True,
+    model.selection("VERTEX", "[Box_1_1/Back][Box_1_1/Right][Box_1_1/Bottom]"): False
+}
+
+model.checkFilter(Part_1_doc, model, Filters, Reference)
diff --git a/src/FiltersPlugin/Test/TestFilter_OnPlaneSide_Multi.py b/src/FiltersPlugin/Test/TestFilter_OnPlaneSide_Multi.py
new file mode 100644 (file)
index 0000000..49f94d8
--- /dev/null
@@ -0,0 +1,88 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(20, 23, -19, 23)
+SketchLine_2 = Sketch_1.addLine(-19, 23, -19, -21)
+SketchLine_3 = Sketch_1.addLine(-19, -21, 20, -21)
+SketchLine_4 = Sketch_1.addLine(20, -21, 20, 23)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_4f")], model.selection(), 30, 0)
+ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [], model.selection(), 0, 20, [model.selection("SOLID", "Extrusion_1_1")])
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_5 = Sketch_2.addLine(34, 36, -11, 36)
+SketchLine_6 = Sketch_2.addLine(-11, 36, -11, -10)
+SketchLine_7 = Sketch_2.addLine(-11, -10, 34, -10)
+SketchLine_8 = Sketch_2.addLine(34, -10, 34, 36)
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchLine_8.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_6 = Sketch_2.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintCoincidence_7 = Sketch_2.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint())
+SketchConstraintCoincidence_8 = Sketch_2.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint())
+SketchConstraintHorizontal_3 = Sketch_2.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_3 = Sketch_2.setVertical(SketchLine_6.result())
+SketchConstraintHorizontal_4 = Sketch_2.setHorizontal(SketchLine_7.result())
+SketchConstraintVertical_4 = Sketch_2.setVertical(SketchLine_8.result())
+ExtrusionCut_1.setNestedSketch(Sketch_2)
+# two filters with different faces each, the second is reversed
+Filters = model.filters(Part_1_doc, [
+  model.addFilter(name = "OnPlaneSide", args = [model.selection("FACE", "ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7")]),
+  model.addFilter(name = "OnPlaneSide", exclude = True, args = [model.selection("FACE", "ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6")]),
+  model.addFilter(name = "OnPlaneSide", args = [model.selection("FACE", "ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face")])])
+model.end()
+
+# 5 edges are in result
+Reference = {
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_2]"): True,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Modified_Face&Extrusion_1_1/To_Face]"): True,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6]"): True,
+    model.selection("EDGE", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_2][ExtrusionCut_1_1/Modified_Face&Extrusion_1_1/To_Face]"): True,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Extrusion_1_1/To_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6]"): True,
+    model.selection("EDGE", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_2][Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_3]"): False,
+    model.selection("EDGE", "[Extrusion_1_1/From_Face][Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_3]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Extrusion_1_1/To_Face][Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_3]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4][Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_3]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Extrusion_1_1/To_Face][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Extrusion_1_1/To_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7]"): False,
+    model.selection("EDGE", "([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4])([ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7])([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7])"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6][ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face]"): False,
+    model.selection("EDGE", "[Extrusion_1_1/From_Face][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4]"): False,
+    model.selection("EDGE", "[Extrusion_1_1/From_Face][Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_2]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][Extrusion_1_1/From_Face]"): False,
+    model.selection("EDGE", "([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6])([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4])([ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6][ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face])"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4]"): False,
+}
+
+model.checkFilter(Part_1_doc, model, Filters, Reference)
diff --git a/src/FiltersPlugin/Test/TestFilter_OnPlane_Multi.py b/src/FiltersPlugin/Test/TestFilter_OnPlane_Multi.py
new file mode 100644 (file)
index 0000000..dbe25bc
--- /dev/null
@@ -0,0 +1,143 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [], model.selection(), 20, 0)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(40, 30, 5, 30)
+SketchLine_2 = Sketch_1.addLine(5, 30, 5, 3)
+SketchLine_3 = Sketch_1.addLine(5, 3, 40, 3)
+SketchLine_4 = Sketch_1.addLine(40, 3, 40, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+Extrusion_1.setNestedSketch(Sketch_1)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_5 = Sketch_2.addLine(18, 39, 29, 39)
+SketchConstraintHorizontal_3 = Sketch_2.setHorizontal(SketchLine_5.result())
+SketchLine_6 = Sketch_2.addLine(29, 39, 29, 21)
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintVertical_3 = Sketch_2.setVertical(SketchLine_6.result())
+SketchLine_7 = Sketch_2.addLine(29, 21, 46, 21)
+SketchConstraintCoincidence_6 = Sketch_2.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint())
+SketchConstraintHorizontal_4 = Sketch_2.setHorizontal(SketchLine_7.result())
+SketchLine_8 = Sketch_2.addLine(46, 21, 46, 11)
+SketchConstraintCoincidence_7 = Sketch_2.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint())
+SketchConstraintVertical_4 = Sketch_2.setVertical(SketchLine_8.result())
+SketchLine_9 = Sketch_2.addLine(46, 11, 29, 11)
+SketchConstraintCoincidence_8 = Sketch_2.setCoincident(SketchLine_8.endPoint(), SketchLine_9.startPoint())
+SketchConstraintHorizontal_5 = Sketch_2.setHorizontal(SketchLine_9.result())
+SketchLine_10 = Sketch_2.addLine(29, 11, 29, -3)
+SketchConstraintCoincidence_9 = Sketch_2.setCoincident(SketchLine_9.endPoint(), SketchLine_10.startPoint())
+SketchConstraintVertical_5 = Sketch_2.setVertical(SketchLine_10.result())
+SketchLine_11 = Sketch_2.addLine(29, -3, 18, -3)
+SketchConstraintCoincidence_10 = Sketch_2.setCoincident(SketchLine_10.endPoint(), SketchLine_11.startPoint())
+SketchLine_12 = Sketch_2.addLine(18, -3, 18, 11)
+SketchConstraintCoincidence_11 = Sketch_2.setCoincident(SketchLine_11.endPoint(), SketchLine_12.startPoint())
+SketchConstraintVertical_6 = Sketch_2.setVertical(SketchLine_12.result())
+SketchLine_13 = Sketch_2.addLine(18, 11, 3, 11)
+SketchConstraintCoincidence_12 = Sketch_2.setCoincident(SketchLine_12.endPoint(), SketchLine_13.startPoint())
+SketchLine_14 = Sketch_2.addLine(3, 11, 3, 21)
+SketchConstraintCoincidence_13 = Sketch_2.setCoincident(SketchLine_13.endPoint(), SketchLine_14.startPoint())
+SketchConstraintVertical_7 = Sketch_2.setVertical(SketchLine_14.result())
+SketchLine_15 = Sketch_2.addLine(3, 21, 18, 21)
+SketchConstraintCoincidence_14 = Sketch_2.setCoincident(SketchLine_14.endPoint(), SketchLine_15.startPoint())
+SketchConstraintHorizontal_6 = Sketch_2.setHorizontal(SketchLine_15.result())
+SketchLine_16 = Sketch_2.addLine(18, 21, 18, 39)
+SketchConstraintCoincidence_15 = Sketch_2.setCoincident(SketchLine_15.endPoint(), SketchLine_16.startPoint())
+SketchConstraintCoincidence_16 = Sketch_2.setCoincident(SketchLine_5.startPoint(), SketchLine_16.endPoint())
+SketchConstraintVertical_8 = Sketch_2.setVertical(SketchLine_16.result())
+SketchConstraintHorizontal_7 = Sketch_2.setHorizontal(SketchLine_13.result())
+SketchConstraintHorizontal_8 = Sketch_2.setHorizontal(SketchLine_11.result())
+SketchConstraintCoincidence_17 = Sketch_2.setCoincident(SketchLine_16.result(), SketchLine_12.endPoint())
+SketchConstraintCoincidence_18 = Sketch_2.setCoincident(SketchLine_6.result(), SketchLine_10.startPoint())
+SketchConstraintCoincidence_19 = Sketch_2.setCoincident(SketchLine_15.result(), SketchLine_7.startPoint())
+SketchConstraintCoincidence_20 = Sketch_2.setCoincident(SketchLine_13.result(), SketchLine_10.startPoint())
+model.do()
+ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_16r-SketchLine_15r-SketchLine_14r-SketchLine_13r-SketchLine_12r-SketchLine_11r-SketchLine_10r-SketchLine_9r-SketchLine_8r-SketchLine_7r-SketchLine_6r-SketchLine_5r")], model.selection(), 0, 10, [model.selection("SOLID", "Extrusion_1_1")])
+# two smae filters with two planes each
+Filters = model.filters(Part_1_doc, [
+  model.addFilter(name = "OnPlane", args = [model.selection("FACE", "ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16"), model.selection("FACE", "ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6")]),
+  model.addFilter(name = "OnPlane", args = [model.selection("FACE", "ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15"), model.selection("FACE", "ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13")])])
+
+model.end()
+
+# 4 edges are in result: intersection of pairs of planes
+Reference = {
+    model.selection("EDGE", "[ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15]"): True,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7]"): True,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12]"): True,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10]"): True,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2][(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)2(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10)2]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3][(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)2(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10)2]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12]"): False,
+    model.selection("EDGE", "[(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)2(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10)2][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12]"): False,
+    model.selection("EDGE", "([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13])([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15])([ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13])([ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15])"): False,
+    model.selection("EDGE", "[(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)2(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10)2][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13]"): False,
+    model.selection("EDGE", "[(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15)(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13)2][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15]"): False,
+    model.selection("EDGE", "[Extrusion_1_1/From_Face][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16][ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2][(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15)(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13)2]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15)(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13)2]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16]"): False,
+    model.selection("EDGE", "[(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_15)(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_13)2][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16]"): False,
+    model.selection("EDGE", "[Extrusion_1_1/From_Face][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6][(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7)(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9)2]"): False,
+    model.selection("EDGE", "([ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10])([ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12])([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10])([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12])"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10][(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)2(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12)2]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][Extrusion_1_1/From_Face]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10]"): False,
+    model.selection("EDGE", "([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16])([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6])([ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16][ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face])([ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6])"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3][(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)2(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12)2]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4][(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)2(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12)2]"): False,
+    model.selection("EDGE", "([ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7])([ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9])([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7])([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9])"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9][(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)2(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_12)2]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9]"): False,
+    model.selection("EDGE", "[Extrusion_1_1/From_Face][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7]"): False,
+    model.selection("EDGE", "[(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7)(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9)2][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4]"): False,
+    model.selection("EDGE", "[ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1][(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7)(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9)2]"): False,
+    model.selection("EDGE", "[(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_1)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_6)(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4)(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_7)(Extrusion_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_2)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_16)2(ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face)2(ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_3)2(ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9)2][ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchLine_4]"): False,
+}
+
+model.checkFilter(Part_1_doc, model, Filters, Reference)
diff --git a/src/FiltersPlugin/Test/TestFilter_RelativeToSolid_Multi.py b/src/FiltersPlugin/Test/TestFilter_RelativeToSolid_Multi.py
new file mode 100644 (file)
index 0000000..1007428
--- /dev/null
@@ -0,0 +1,61 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(18.85055999990443, 20.40038647424626, 12.41450630559296)
+SketchCircle_2 = Sketch_1.addCircle(25.08744138385902, 26.81117176326632, 2.271058181559462)
+SketchCircle_2.setName("SketchCircle_3")
+SketchCircle_2.result().setName("SketchCircle_3")
+SketchCircle_2.results()[1].setName("SketchCircle_3_2")
+model.do()
+Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_3 = Sketch_2.addCircle(30.95603034069762, 31.40670583726882, 10.95693005417035)
+SketchCircle_3.setName("SketchCircle_4")
+SketchCircle_3.result().setName("SketchCircle_4")
+SketchCircle_3.results()[1].setName("SketchCircle_4_2")
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("WIRE", "Sketch_1/Face-SketchCircle_1_2r-SketchCircle_3_2r_wire")], model.selection(), 50, 0)
+Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("WIRE", "Sketch_2/Face-SketchCircle_4_2r_wire")], model.selection(), 40, -10)
+Extrusion_3 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_3_2f")], model.selection(), 20, -30)
+# Two filters to search all face inside of two solids
+Filters = model.filters(Part_1_doc, [
+  model.addFilter(name = "RelativeToSolid", args = [model.selection("SOLID", "Extrusion_1_1"), "in"]),
+  model.addFilter(name = "RelativeToSolid", args = [model.selection("SOLID", "Extrusion_2_1"), "in"])])
+model.end()
+
+# 3 faces of internal cylinder is inside
+Reference = {
+    model.selection("FACE", "Extrusion_3_1/Generated_Face&Sketch_1/SketchCircle_3_2"): True,
+    model.selection("FACE", "Extrusion_3_1/From_Face"): True,
+    model.selection("FACE", "Extrusion_3_1/To_Face"): True,
+    model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2"): False,
+    model.selection("FACE", "Extrusion_1_1/From_Face"): False,
+    model.selection("FACE", "Extrusion_1_1/To_Face"): False,
+    model.selection("FACE", "Extrusion_2_1/Generated_Face&Sketch_2/SketchCircle_4_2"): False,
+    model.selection("FACE", "Extrusion_2_1/From_Face"): False,
+    model.selection("FACE", "Extrusion_2_1/To_Face"): False
+}
+
+model.checkFilter(Part_1_doc, model, Filters, Reference)
index 8e6d0babdd9daf0881f1a542830dd42626fd0cfc..1e9aade08d1ab575aeadfa7951dfd6dd90df2e4d 100644 (file)
@@ -33,6 +33,8 @@ In this panel:
 .. centered::
   Filters **Horizontal faces** and **On plane** added to the property panel.
   
+If filter is implemented as "Multiple", several instances of this filter may be added in one selection. Otherwise the added filter is removed from the "Add new filter..." list.
+
 Each filter item can be deleted with help of |delete.icon| button. A filter can be reverced with help of toggle button |plus.icon|/|minus.icon|. Also a filter could have input fields in case
 if the filter has arguments.
 
@@ -73,7 +75,7 @@ By default, the result of Selection feature all selectable entities from all Sha
 
 - **Result type:** Any
 - **Argument:** Any Shape, multiple OR selection accepted
-- **Algorithm:** Returns the shapes which have the similar underlying geometry of the given Shape. Like all faces laying of the same geometrical surface or edges laying of the same geometrical surface or edges laying on the line.
+- **Algorithm:** Returns the shapes which have the similar underlying geometry of the given Shape. Like all faces laying of the same geometrical surface or edges laying on the line.
 
 **On plane side**
 
index f84febdfcb599a7d476ef74903c06387a630056a..d02682d68caafbcec06f9ba8fec193fe86cd2df2 100644 (file)
@@ -40,6 +40,21 @@ struct FilterArgs {
   std::string myFilterID;
 };
 
+/// Returns the filter ID without the filter index
+static std::string pureFilterID(const std::string& theID)
+{
+  // remove from aPure "_" + number + "_" starting part
+  if (theID.size() > 3 && theID[0] == '_') {
+    int aNumDigits = 0;
+    while(theID[aNumDigits + 1] < '9' && theID[aNumDigits + 1] > '0')
+      aNumDigits++;
+    if (aNumDigits && theID[aNumDigits + 1] == '_') {
+      return theID.substr(aNumDigits + 2);
+    }
+  }
+  return theID;
+}
+
 bool Model_FiltersFactory::isValid(FeaturePtr theFiltersFeature,
                                    ResultPtr theResult,
                                    GeomShapePtr theShape)
@@ -63,7 +78,8 @@ bool Model_FiltersFactory::isValid(FeaturePtr theFiltersFeature,
   theFiltersFeature->data()->allGroups(aGroups);
   for(std::list<std::string>::iterator aGIter = aGroups.begin(); aGIter != aGroups.end(); aGIter++)
   {
-    if (myFilters.find(*aGIter) == myFilters.end())
+    std::string aPureID = pureFilterID(*aGIter);
+    if (myFilters.find(aPureID) == myFilters.end())
       continue;
     std::list<std::shared_ptr<ModelAPI_Attribute> > anAttrs;
     theFiltersFeature->data()->attributesOfGroup(*aGIter, anAttrs);
@@ -73,7 +89,7 @@ bool Model_FiltersFactory::isValid(FeaturePtr theFiltersFeature,
       if (anArgID.empty()) { // reverse flag
         std::shared_ptr<ModelAPI_AttributeBoolean> aReverse =
           std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(*anAttrIter);
-        FilterArgs aFArgs = { myFilters[*aGIter] , aReverse->value() , *aGIter };
+        FilterArgs aFArgs = { myFilters[aPureID] , aReverse->value() , *aGIter };
         aFilters.push_back(aFArgs);
 
       } else {
@@ -113,7 +129,8 @@ std::list<FilterPtr> Model_FiltersFactory::filters(GeomAPI_Shape::ShapeType theT
 
 FilterPtr Model_FiltersFactory::filter(std::string theID)
 {
-  std::map<std::string, FilterPtr>::iterator aFound = myFilters.find(theID);
+  std::string aPureID = pureFilterID(theID);
+  std::map<std::string, FilterPtr>::iterator aFound = myFilters.find(aPureID);
   return aFound == myFilters.end() ? FilterPtr() : aFound->second;
 }
 
index b4790994376d8aa1c6a996b47a237c5ed6889a8a..9c2064d45af282a99a65cdb06169a3d82d9bbb13 100644 (file)
@@ -33,7 +33,8 @@ class ModelAPI_FiltersFeature: public ModelAPI_Feature
 {
 public:
   /// Adds a filter to the feature. Also initializes arguments of this filter.
-  virtual void addFilter(const std::string theFilterID) = 0;
+  /// Returns the real identifier of the filter.
+  virtual std::string addFilter(const std::string theFilterID) = 0;
 
   /// Removes an existing filter from the feature.
   virtual void removeFilter(const std::string theFilterID) = 0;
index ab7b17e103e4882e54c016ac02de634f101e2834..269d445b52e2927af8a7bb9e14e0eecfa1526dcd 100644 (file)
 #include <climits>
 
 ModuleBase_WidgetFactory::ModuleBase_WidgetFactory(const std::string& theXmlRepresentation,
-                                                   ModuleBase_IWorkshop* theWorkshop)
+                                                   ModuleBase_IWorkshop* theWorkshop,
+                                                   const std::string theAttributePrefix)
     : myWorkshop(theWorkshop)
 {
-  myWidgetApi = new Config_WidgetAPI(theXmlRepresentation);
+  myWidgetApi = new Config_WidgetAPI(theXmlRepresentation, theAttributePrefix);
 }
 
 ModuleBase_WidgetFactory::~ModuleBase_WidgetFactory()
index 90ea7428b54936be913a9ce98774de8ac8479f14..49fc9a05370b3ce00e71db73ef4bad4006012e54 100644 (file)
@@ -43,8 +43,10 @@ class MODULEBASE_EXPORT ModuleBase_WidgetFactory
    /// Constructor
    /// \param theXmlRepresentation content of XML file
    /// \param theWorkshop reference to workshop instance
-  ModuleBase_WidgetFactory(const std::string& theXmlRepresentation,
-                           ModuleBase_IWorkshop* theWorkshop);
+   /// \param theAttributePrefix prefix that must be added to the attribute name
+   ModuleBase_WidgetFactory(const std::string& theXmlRepresentation,
+                           ModuleBase_IWorkshop* theWorkshop,
+                           const std::string theAttributePrefix = "");
   virtual ~ModuleBase_WidgetFactory();
 
   /// Creates content widget for property panel
index f812ac262e3f1de1c8ecc0d2e70be70b59d5a37c..6e916b7ab2c6e870247999018c26a08739a9e6d7 100644 (file)
@@ -143,12 +143,17 @@ ModuleBase_FilterItem::ModuleBase_FilterItem(
   : QWidget(theParent->filtersWidget()), myFilterID(theFilter),
     mySelection(std::dynamic_pointer_cast<ModelAPI_FiltersFeature>(theParent->feature()))
 {
-  std::string aXmlString =
-      ModelAPI_Session::get()->filters()->filter(theFilter)->xmlRepresentation();
+  FilterPtr aFilter = ModelAPI_Session::get()->filters()->filter(theFilter);
+  std::string aXmlString = aFilter->xmlRepresentation();
   if (aXmlString.length() == 0)
     addItemRow(this);
   else {
-    ModuleBase_WidgetFactory aFactory(aXmlString, theParent->workshop());
+    std::string anAttrPrefix; // this must be added to the attributes names for multiple filters
+    std::string aFilterKind = ModelAPI_Session::get()->filters()->id(aFilter);
+    if (theFilter != aFilterKind) {
+      anAttrPrefix = theFilter.substr(0, theFilter.size() - aFilterKind.size());
+    }
+    ModuleBase_WidgetFactory aFactory(aXmlString, theParent->workshop(), anAttrPrefix);
     Config_ValidatorReader aValidatorReader(aXmlString, true);
     aValidatorReader.setFeatureId(mySelection->getKind());
     aValidatorReader.readAll();