From fff33463f5f2572da78cf315c468369defbc60c6 Mon Sep 17 00:00:00 2001 From: azv Date: Fri, 28 Jun 2019 12:48:40 +0300 Subject: [PATCH] Implement filter "F7: Opposite to an edge" --- src/FiltersPlugin/CMakeLists.txt | 3 + .../FiltersPlugin_OppositeToEdge.cpp | 165 ++++++++++++++++++ .../FiltersPlugin_OppositeToEdge.h | 68 ++++++++ src/FiltersPlugin/FiltersPlugin_Plugin.cpp | 2 + .../Test/TestFilter_OppositeToEdge.py | 146 ++++++++++++++++ 5 files changed, 384 insertions(+) create mode 100644 src/FiltersPlugin/FiltersPlugin_OppositeToEdge.cpp create mode 100644 src/FiltersPlugin/FiltersPlugin_OppositeToEdge.h create mode 100644 src/FiltersPlugin/Test/TestFilter_OppositeToEdge.py diff --git a/src/FiltersPlugin/CMakeLists.txt b/src/FiltersPlugin/CMakeLists.txt index 8f6941ee7..121dc0ff3 100644 --- a/src/FiltersPlugin/CMakeLists.txt +++ b/src/FiltersPlugin/CMakeLists.txt @@ -31,6 +31,7 @@ SET(PROJECT_HEADERS FiltersPlugin_OnLine.h FiltersPlugin_OnGeometry.h FiltersPlugin_OnPlaneSide.h + FiltersPlugin_OppositeToEdge.h ) SET(PROJECT_SOURCES @@ -43,6 +44,7 @@ SET(PROJECT_SOURCES FiltersPlugin_OnLine.cpp FiltersPlugin_OnGeometry.cpp FiltersPlugin_OnPlaneSide.cpp + FiltersPlugin_OppositeToEdge.cpp ) SET(PROJECT_LIBRARIES @@ -81,6 +83,7 @@ ADD_UNIT_TESTS( TestFilter_OnGeometry_Face.py TestFilter_OnPlaneSide_Face.py TestFilter_OnPlaneSide_Plane.py + TestFilter_OppositeToEdge.py TestFilter_HorizontalFaces.py TestFilter_VerticalFaces.py ) diff --git a/src/FiltersPlugin/FiltersPlugin_OppositeToEdge.cpp b/src/FiltersPlugin/FiltersPlugin_OppositeToEdge.cpp new file mode 100644 index 000000000..35d1a42c6 --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_OppositeToEdge.cpp @@ -0,0 +1,165 @@ +// 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 +// + +#include "FiltersPlugin_OppositeToEdge.h" + +#include +#include +#include + +#include +#include + +#include + +typedef std::map MapShapeAndAncestors; + +static void mapEdgesAndFaces(const GeomShapePtr theShape, MapShapeAndAncestors& theMap) +{ + GeomAPI_ShapeExplorer aFExp(theShape, GeomAPI_Shape::FACE); + for (; aFExp.more(); aFExp.next()) { + GeomShapePtr aFace = aFExp.current(); + GeomAPI_ShapeExplorer aEExp(aFace, GeomAPI_Shape::EDGE); + for (; aEExp.more(); aEExp.next()) + theMap[aEExp.current()].insert(aFace); + } +} + +// Return edge in the quadratic face opposite to the given one. +// If the face is not quadratic, returns empty shape. +static GeomShapePtr oppositeEdgeInQuadFace(const GeomShapePtr theEdge, + const GeomShapePtr theFace) +{ + static int THE_QUAD = 4; + + int aNbEdges = 0; + int anOriginalEdgeIndex = -THE_QUAD; + GeomShapePtr anOppositeEdge; + GeomAPI_ShapeExplorer anExp(theFace, GeomAPI_Shape::EDGE); + while (anExp.more()) { + if (anExp.current()->isSame(theEdge)) + anOriginalEdgeIndex = aNbEdges; + else if (aNbEdges == anOriginalEdgeIndex + THE_QUAD / 2) { + anOppositeEdge = anExp.current(); + if (aNbEdges >= THE_QUAD) + break; + } + + ++aNbEdges; + anExp.next(); + if (!anExp.more()) { + if (aNbEdges != THE_QUAD) { + // not quad face + anOppositeEdge = GeomShapePtr(); + break; + } + if (!anOppositeEdge) + anExp.reinit(); + } + } + return anOppositeEdge; +} + +// Find all opposite edges for the given. +static void cacheOppositeEdge(const GeomShapePtr theEdge, + const MapShapeAndAncestors& theEdgeToFaces, + SetOfShapes& theCache) +{ + MapShapeAndAncestors::const_iterator aFound = theEdgeToFaces.find(theEdge); + if (aFound == theEdgeToFaces.end()) + return; + + for (SetOfShapes::const_iterator aFIt = aFound->second.begin(); + aFIt != aFound->second.end(); ++aFIt) { + GeomShapePtr anOpposite = oppositeEdgeInQuadFace(theEdge, *aFIt); + if (anOpposite && theCache.find(anOpposite) == theCache.end()) { + theCache.insert(anOpposite); + cacheOppositeEdge(anOpposite, theEdgeToFaces, theCache); + } + } +} + +static void cacheOppositeEdges(const GeomShapePtr theTopLevelShape, + const GeomShapePtr theEdge, + SetOfShapes& theCache) +{ + if (!theTopLevelShape || !theEdge) + return; + + MapShapeAndAncestors anEdgesToFaces; + mapEdgesAndFaces(theTopLevelShape, anEdgesToFaces); + + // keep the original edge + theCache.insert(theEdge); + // cache opposite edges + cacheOppositeEdge(theEdge, anEdgesToFaces, theCache); +} + + +bool FiltersPlugin_OppositeToEdge::isSupported(GeomAPI_Shape::ShapeType theType) const +{ + return theType == GeomAPI_Shape::EDGE; +} + +bool FiltersPlugin_OppositeToEdge::isOk(const GeomShapePtr& theShape, + const ModelAPI_FiltersArgs& theArgs) const +{ + AttributePtr aAttr = theArgs.argument("OppositeToEdge"); + AttributeSelectionPtr aList = std::dynamic_pointer_cast(aAttr); + if (!aList.get()) + return false; + GeomShapePtr anEdge = aList->value(); + if (!myOriginalEdge || !myOriginalEdge->isSame(anEdge)) { + // new edge is selected, need to update the cache + const_cast(this)->myOriginalEdge = anEdge; + const_cast(this)->myCachedShapes.clear(); + } + + if (myCachedShapes.empty()) { + ResultBodyPtr aBaseResult = ModelAPI_Tools::bodyOwner(aList->context(), true); + if (!aBaseResult.get()) + return false; + + cacheOppositeEdges(aBaseResult->shape(), anEdge, + const_cast(this)->myCachedShapes); + } + + return myCachedShapes.find(theShape) != myCachedShapes.end(); +} + +static std::string XMLRepresentation = +"" +" " +" " +" " +""; + + +std::string FiltersPlugin_OppositeToEdge::xmlRepresentation() const +{ + return XMLRepresentation; +} + +void FiltersPlugin_OppositeToEdge::initAttributes(ModelAPI_FiltersArgs& theArguments) +{ + theArguments.initAttribute("OppositeToEdge", ModelAPI_AttributeSelection::typeId()); +} diff --git a/src/FiltersPlugin/FiltersPlugin_OppositeToEdge.h b/src/FiltersPlugin/FiltersPlugin_OppositeToEdge.h new file mode 100644 index 000000000..9db4458f9 --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_OppositeToEdge.h @@ -0,0 +1,68 @@ +// 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 +// + +#ifndef FILTERSPLUGIN_OPPOSITETOEDGE_H_ +#define FILTERSPLUGIN_OPPOSITETOEDGE_H_ + +#include "FiltersPlugin.h" + +#include +#include + +#include + +typedef std::set SetOfShapes; + +/**\class FiltersPlugin_OppositeToEdge +* \ingroup DataModel +* \brief Filter for edges of quadrangular faces opposite to the selected edge +*/ +class FiltersPlugin_OppositeToEdge : public ModelAPI_Filter +{ +public: + FiltersPlugin_OppositeToEdge() : ModelAPI_Filter() {} + + virtual const std::string& name() const { + static const std::string kName("Opposite to an edge"); + return kName; + } + + /// Returns true for any type because it supports all selection types + virtual bool isSupported(GeomAPI_Shape::ShapeType theType) const override; + + /// This method should contain the filter logic. It returns true if the given shape + /// is accepted by the filter. + /// \param theShape the given shape + virtual bool isOk(const GeomShapePtr& theShape, + const ModelAPI_FiltersArgs& theArgs) const override; + + /// Returns XML string which represents GUI of the filter + virtual std::string xmlRepresentation() const override; + + /// Initializes arguments of a filter. + virtual void initAttributes(ModelAPI_FiltersArgs& theArguments) override; + +private: + /// Original edge selected for filtering + GeomShapePtr myOriginalEdge; + /// Shapes applicable for the filter + SetOfShapes myCachedShapes; +}; + +#endif \ No newline at end of file diff --git a/src/FiltersPlugin/FiltersPlugin_Plugin.cpp b/src/FiltersPlugin/FiltersPlugin_Plugin.cpp index 67a8c8fce..956e8477d 100644 --- a/src/FiltersPlugin/FiltersPlugin_Plugin.cpp +++ b/src/FiltersPlugin/FiltersPlugin_Plugin.cpp @@ -26,6 +26,7 @@ #include "FiltersPlugin_OnLine.h" #include "FiltersPlugin_OnGeometry.h" #include "FiltersPlugin_OnPlaneSide.h" +#include "FiltersPlugin_OppositeToEdge.h" #include #include @@ -45,6 +46,7 @@ FiltersPlugin_Plugin::FiltersPlugin_Plugin() aFactory->registerFilter("OnLine", new FiltersPlugin_OnLine); aFactory->registerFilter("OnGeometry", new FiltersPlugin_OnGeometry); aFactory->registerFilter("OnPlaneSide", new FiltersPlugin_OnPlaneSide); + aFactory->registerFilter("OppositeToEdge", new FiltersPlugin_OppositeToEdge); ModelAPI_Session::get()->registerPlugin(this); } diff --git a/src/FiltersPlugin/Test/TestFilter_OppositeToEdge.py b/src/FiltersPlugin/Test/TestFilter_OppositeToEdge.py new file mode 100644 index 000000000..8233491c3 --- /dev/null +++ b/src/FiltersPlugin/Test/TestFilter_OppositeToEdge.py @@ -0,0 +1,146 @@ +# 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, 100, 100, 100) +Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_1_1")], -50, -50, -50) +Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/XOY"), model.selection("EDGE", "PartSet/OX"), 30) +Plane_5 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/YOZ"), model.selection("EDGE", "PartSet/OY"), 30) +Plane_6 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/XOZ"), model.selection("EDGE", "PartSet/OZ"), 30) +Partition_1_objects = [model.selection("SOLID", "Translation_1_1"), model.selection("FACE", "Plane_1"), model.selection("FACE", "Plane_2"), model.selection("FACE", "Plane_3")] +Partition_1 = model.addPartition(Part_1_doc, Partition_1_objects, 20190506) +Filters = model.filters(Part_1_doc, [model.addFilter(name = "OppositeToEdge", args = [model.selection("EDGE", "Partition_1_1_7/Generated_Edge&Plane_2/Plane_2&Plane_1/Plane_1")])]) +model.end() + +from GeomAPI import * + +Reference = {} +# Faces are not applicable +ResultBox_1 = Partition_1.result().resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(ResultBox_1.shape(), GeomAPI_Shape.FACE) +while exp.more(): + Reference[model.selection(ResultBox_1, exp.current())] = False + exp.next() +# Vertices are not applicable too +exp = GeomAPI_ShapeExplorer(ResultBox_1.shape(), GeomAPI_Shape.VERTEX) +while exp.more(): + Reference[model.selection(ResultBox_1, exp.current())] = False + exp.next() + +# Edges of the partitioned box. +# Note: the expected values have to be updated if ShapeExplorer will return another order of sub-shapes. + +# sub-result 0 +SubResult = Partition_1.result().subResult(0).resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(SubResult.shape(), GeomAPI_Shape.EDGE) +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() + +# sub-result 1 +SubResult = Partition_1.result().subResult(1).resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(SubResult.shape(), GeomAPI_Shape.EDGE) +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() + +# sub-result 2 +SubResult = Partition_1.result().subResult(2).resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(SubResult.shape(), GeomAPI_Shape.EDGE) +while exp.more(): + Reference[model.selection(SubResult, exp.current())] = False + exp.next() + +# sub-result 3 +SubResult = Partition_1.result().subResult(3).resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(SubResult.shape(), GeomAPI_Shape.EDGE) +while exp.more(): + Reference[model.selection(SubResult, exp.current())] = False + exp.next() + +# sub-result 4 +SubResult = Partition_1.result().subResult(4).resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(SubResult.shape(), GeomAPI_Shape.EDGE) +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() + +# sub-result 5 +SubResult = Partition_1.result().subResult(5).resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(SubResult.shape(), GeomAPI_Shape.EDGE) +while exp.more(): + Reference[model.selection(SubResult, exp.current())] = False + exp.next() + +# sub-result 6 +SubResult = Partition_1.result().subResult(6).resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(SubResult.shape(), GeomAPI_Shape.EDGE) +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() +Reference[model.selection(SubResult, exp.current())] = False; exp.next() +Reference[model.selection(SubResult, exp.current())] = True; exp.next() + +# sub-result 7 +SubResult = Partition_1.result().subResult(7).resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(SubResult.shape(), GeomAPI_Shape.EDGE) +while exp.more(): + Reference[model.selection(SubResult, exp.current())] = False + exp.next() + +model.checkFilter(Part_1_doc, model, Filters, Reference) -- 2.39.2