From: Jérôme Date: Mon, 2 Nov 2020 14:01:08 +0000 (+0300) Subject: CEA : Lot2 - Add new filters X-Git-Tag: V9_8_0a1~16 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=edc11bc233d6a429f6191c993fe815e7d25b066a;p=modules%2Fshaper.git CEA : Lot2 - Add new filters * Edge size * Face size * Volume size * Feature edges * Continuous faces --- diff --git a/src/FiltersAPI/FiltersAPI.i b/src/FiltersAPI/FiltersAPI.i index 76bcad128..90c2a7681 100644 --- a/src/FiltersAPI/FiltersAPI.i +++ b/src/FiltersAPI/FiltersAPI.i @@ -74,6 +74,9 @@ $1 = 0; } } else + if (!PyFloat_Check(item) && PyLong_Check(item)) + $1 = 0; + else if (!PyUnicode_Check(item) && !PyBool_Check(item)) $1 = 0; } @@ -83,13 +86,14 @@ %typemap(in) const std::list & (std::list temp) { ModelHighAPI_Selection* temp_selection; std::string* temp_string; + ModelHighAPI_Double* temp_double; int newmem = 0; if (PySequence_Check($input)) { for (Py_ssize_t i = 0; i < PySequence_Size($input); ++i) { PyObject * item = PySequence_GetItem($input, i); if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_selection, $descriptor(ModelHighAPI_Selection*), SWIG_POINTER_EXCEPTION, &newmem)) == 0) { if (!temp_selection) { - PyErr_SetString(PyExc_TypeError, "argument must be ModelHighAPI_Selection, string or boolean."); + PyErr_SetString(PyExc_TypeError, "argument must be ModelHighAPI_Selection, string, double or boolean.1"); return NULL; } temp.push_back(FiltersAPI_Argument(*temp_selection)); @@ -99,7 +103,7 @@ } else if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_string, $descriptor(std::string*), SWIG_POINTER_EXCEPTION, &newmem)) == 0) { if (!temp_string) { - PyErr_SetString(PyExc_TypeError, "argument must be ModelHighAPI_Selection, string or boolean."); + PyErr_SetString(PyExc_TypeError, "argument must be ModelHighAPI_Selection, string, double or boolean.2"); return NULL; } temp.push_back(FiltersAPI_Argument(*temp_string)); @@ -112,15 +116,18 @@ } else if (PyBool_Check(item)) { temp.push_back(FiltersAPI_Argument(item == Py_True)); + } else + if (PyFloat_Check(item) || PyLong_Check(item)) { + temp.push_back(FiltersAPI_Argument(ModelHighAPI_Double(PyFloat_AsDouble(item)))); } else { - PyErr_SetString(PyExc_TypeError, "argument must be ModelHighAPI_Selection, string or boolean."); + PyErr_SetString(PyExc_TypeError, "argument must be ModelHighAPI_Selection, string, double or boolean.4"); return NULL; } Py_DECREF(item); } $1 = &temp; } else { - PyErr_SetString(PyExc_TypeError, "argument must be ModelHighAPI_Selection, string or boolean."); + PyErr_SetString(PyExc_TypeError, "argument must be ModelHighAPI_Selection, string, double or boolean.5"); return NULL; } } diff --git a/src/FiltersAPI/FiltersAPI_Argument.cpp b/src/FiltersAPI/FiltersAPI_Argument.cpp index 30d5e36cd..90a79bb5d 100644 --- a/src/FiltersAPI/FiltersAPI_Argument.cpp +++ b/src/FiltersAPI/FiltersAPI_Argument.cpp @@ -28,6 +28,16 @@ FiltersAPI_Argument::FiltersAPI_Argument(const bool theValue) { } +FiltersAPI_Argument::FiltersAPI_Argument(const ModelHighAPI_Double theValue) + : myDouble(theValue) +{ +} + +FiltersAPI_Argument::FiltersAPI_Argument(const double& theValue) +{ + myDouble = theValue; +} + FiltersAPI_Argument::FiltersAPI_Argument(const std::string& theValue) : myValue(theValue) { @@ -54,9 +64,14 @@ void FiltersAPI_Argument::dump(ModelHighAPI_Dumper& theDumper) const theDumper << "model.selection()"; // mySelectionAttr; } else if (mySelection.variantType() == ModelHighAPI_Selection::VT_Empty) { - if (myValue.empty()) + if (myDouble.value() > std::numeric_limits::lowest() ) { + theDumper << myDouble.value(); + } + else if (myValue.empty()) { theDumper << myBoolean; - else + } + else{ theDumper << "\"" << myValue << "\""; + } } } diff --git a/src/FiltersAPI/FiltersAPI_Argument.h b/src/FiltersAPI/FiltersAPI_Argument.h index 7b7cd30ff..dd5699894 100644 --- a/src/FiltersAPI/FiltersAPI_Argument.h +++ b/src/FiltersAPI/FiltersAPI_Argument.h @@ -23,10 +23,13 @@ #include "FiltersAPI.h" #include +#include #include #include +#include + /**\class FiltersAPI_Argument * \ingroup CPPHighAPI * \brief Argument of the Filter @@ -39,6 +42,12 @@ public: FILTERSAPI_EXPORT FiltersAPI_Argument(const bool theValue); + FILTERSAPI_EXPORT + FiltersAPI_Argument(const ModelHighAPI_Double theValue); + + FILTERSAPI_EXPORT + FiltersAPI_Argument(const double& theValue); + FILTERSAPI_EXPORT FiltersAPI_Argument(const std::string& theValue); @@ -55,13 +64,14 @@ public: const bool boolean() const { return myBoolean; } const std::string& string() const { return myValue; } const ModelHighAPI_Selection& selection() const { return mySelection; } - + const ModelHighAPI_Double& dble() const { return myDouble; } /// Dump wrapped feature FILTERSAPI_EXPORT void dump(ModelHighAPI_Dumper& theDumper) const; private: bool myBoolean; + ModelHighAPI_Double myDouble = std::numeric_limits::lowest() ; std::string myValue; ModelHighAPI_Selection mySelection; AttributeSelectionPtr mySelectionAttr; diff --git a/src/FiltersAPI/FiltersAPI_Feature.cpp b/src/FiltersAPI/FiltersAPI_Feature.cpp index 215b2ef12..329eee72d 100644 --- a/src/FiltersAPI/FiltersAPI_Feature.cpp +++ b/src/FiltersAPI/FiltersAPI_Feature.cpp @@ -20,11 +20,11 @@ #include "FiltersAPI_Feature.h" #include +#include +#include #include #include -#include -#include FiltersAPI_Feature::FiltersAPI_Feature( const std::shared_ptr & theFeature) @@ -41,14 +41,19 @@ FiltersAPI_Feature::~FiltersAPI_Feature() static void separateArguments(const std::list& theArguments, std::list& theSelections, std::list& theTextArgs, - std::list& theBoolArgs) + std::list& theBoolArgs, + std::list& theDoubleArgs) { std::list::const_iterator anIt = theArguments.begin(); for (; anIt != theArguments.end(); ++anIt) { if (anIt->selection().variantType() != ModelHighAPI_Selection::VT_Empty) theSelections.push_back(anIt->selection()); - else if (anIt->string().empty()) + else if (anIt->dble().value() > std::numeric_limits::lowest()) { + theDoubleArgs.push_back(anIt->dble()); + } + else if (anIt->string().empty()) { theBoolArgs.push_back(anIt->boolean()); + } else theTextArgs.push_back(anIt->string()); } @@ -68,7 +73,8 @@ void FiltersAPI_Feature::setFilters(const std::list& theFilters) std::list aSelections; std::list aTexts; std::list aBools; - separateArguments(anArgs, aSelections, aTexts, aBools); + std::list aDoubles; + separateArguments(anArgs, aSelections, aTexts, aBools, aDoubles); std::list aFilterArgs = aBase->filterArgs(aFilterID); std::list::iterator aFIt = aFilterArgs.begin(); @@ -78,6 +84,7 @@ void FiltersAPI_Feature::setFilters(const std::list& theFilters) if (aReversedFlag) ++aFIt; // fill arguments of the filter + std::list::const_iterator anItDle = aDoubles.begin(); for (; aFIt != aFilterArgs.end(); ++aFIt) { AttributeSelectionListPtr aSelList = std::dynamic_pointer_cast(*aFIt); @@ -98,8 +105,17 @@ void FiltersAPI_Feature::setFilters(const std::list& theFilters) else { AttributeBooleanPtr aBoolean = std::dynamic_pointer_cast(*aFIt); - if (aBoolean && aBools.size() == 1) - fillAttribute(aBools.front(), aBoolean); + if (aBoolean) { + if (aBools.size() == 1) + fillAttribute(aBools.front(), aBoolean); + } else { + AttributeDoublePtr aDouble = + std::dynamic_pointer_cast(*aFIt); + if (aDouble) { + fillAttribute((*anItDle).value(), aDouble); + anItDle++; + } + } } } } diff --git a/src/FiltersAPI/FiltersAPI_Filter.cpp b/src/FiltersAPI/FiltersAPI_Filter.cpp index 5ae1afb02..f1f37393a 100644 --- a/src/FiltersAPI/FiltersAPI_Filter.cpp +++ b/src/FiltersAPI/FiltersAPI_Filter.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -72,6 +73,12 @@ FiltersAPI_Filter::FiltersAPI_Filter(const std::string& theName, continue; } + AttributeDoublePtr aDouble = std::dynamic_pointer_cast(*anArgIt); + if (aDouble) { + myFilterArguments.push_back(FiltersAPI_Argument(aDouble->value())); + continue; + } + AttributeBooleanPtr aBoolean = std::dynamic_pointer_cast(*anArgIt); if (aBoolean) { myFilterArguments.push_back(FiltersAPI_Argument(aBoolean->value())); diff --git a/src/FiltersPlugin/CMakeLists.txt b/src/FiltersPlugin/CMakeLists.txt index 7b77d8466..f32f2047d 100644 --- a/src/FiltersPlugin/CMakeLists.txt +++ b/src/FiltersPlugin/CMakeLists.txt @@ -35,6 +35,11 @@ SET(PROJECT_HEADERS FiltersPlugin_RelativeToSolid.h FiltersPlugin_ExternalFaces.h FiltersPlugin_Validators.h + FiltersPlugin_EdgeSize.h + FiltersPlugin_FaceSize.h + FiltersPlugin_VolumeSize.h + FiltersPlugin_FeatureEdges.h + FiltersPlugin_ContinuousFaces.h ) SET(PROJECT_SOURCES @@ -51,6 +56,11 @@ SET(PROJECT_SOURCES FiltersPlugin_RelativeToSolid.cpp FiltersPlugin_ExternalFaces.cpp FiltersPlugin_Validators.cpp + FiltersPlugin_EdgeSize.cpp + FiltersPlugin_FaceSize.cpp + FiltersPlugin_VolumeSize.cpp + FiltersPlugin_FeatureEdges.cpp + FiltersPlugin_ContinuousFaces.cpp ) SET(PROJECT_LIBRARIES @@ -76,6 +86,11 @@ SET(XML_RESOURCES filter-OppositeToEdge.xml filter-RelativeToSolid.xml filter-TopoConnectedFaces.xml + filter-EdgeSize.xml + filter-FaceSize.xml + filter-VolumeSize.xml + filter-FeatureEdges.xml + filter-ContinuousFaces.xml ) SET(TEXT_RESOURCES @@ -114,17 +129,16 @@ ADD_UNIT_TESTS(${TEST_NAMES}) if(${HAVE_SALOME}) enable_testing() set(TEST_INSTALL_DIRECTORY "${SALOME_SHAPER_INSTALL_TESTS}/FiltersPlugin") - + install(FILES CTestTestfileInstall.cmake DESTINATION ${TEST_INSTALL_DIRECTORY} RENAME CTestTestfile.cmake) install(FILES tests.set DESTINATION ${TEST_INSTALL_DIRECTORY}) - + set(TMP_TESTS_NAMES) foreach(tfile ${TEST_NAMES}) list(APPEND TMP_TESTS_NAMES "Test/${tfile}") endforeach(tfile ${TEST_NAMES}) - + install(FILES ${TMP_TESTS_NAMES} DESTINATION ${TEST_INSTALL_DIRECTORY}) endif(${HAVE_SALOME}) - diff --git a/src/FiltersPlugin/FiltersPlugin_ContinuousFaces.cpp b/src/FiltersPlugin/FiltersPlugin_ContinuousFaces.cpp new file mode 100644 index 000000000..f0e0feea9 --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_ContinuousFaces.cpp @@ -0,0 +1,195 @@ +// Copyright (C) 2014-2021 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_ContinuousFaces.h" + +#include +#include +#include +#include + +#include +#include +#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); + } +} + +//================================================================================================= +// Find all continuous faces for the given. +static void cacheContinuousFace(const GeomShapePtr theFace, + const MapShapeAndAncestors& theEdgeToFaces, + SetOfShapes& theCache, + const double & theAngle) +{ + + MapShapeAndAncestors::const_iterator aFound; + GeomAPI_ShapeExplorer aEExp(theFace, GeomAPI_Shape::EDGE); + for (; aEExp.more(); aEExp.next()){ + aFound = theEdgeToFaces.find(aEExp.current()); + if (aFound == theEdgeToFaces.end()) + continue; + + GeomEdgePtr anEdge; + anEdge = GeomEdgePtr(new GeomAPI_Edge(aEExp.current())); + + for (SetOfShapes::const_iterator aFIt = aFound->second.begin(); + aFIt != aFound->second.end(); ++aFIt) { + std::string anError = ""; + if (theCache.find(*aFIt) == theCache.end()) { + GeomPointPtr aPoint = anEdge->middlePoint(); + if (GeomAlgoAPI_ShapeTools::isContinuousFaces(theFace, + *aFIt, + aPoint, + theAngle, + anError)) { + theCache.insert(*aFIt); + cacheContinuousFace(*aFIt, theEdgeToFaces, theCache, theAngle); + } + } + } + } +} + +//================================================================================================= +static void cacheContinuousFaces(const GeomShapePtr theTopLevelShape, + const SetOfShapes& theFaces, + SetOfShapes& theCache, + const double & theAngle) +{ + if (!theTopLevelShape || theFaces.empty()) + return; + + MapShapeAndAncestors anEdgesToFaces; + mapEdgesAndFaces(theTopLevelShape, anEdgesToFaces); + + for (SetOfShapes::const_iterator aFIt = theFaces.begin(); + aFIt != theFaces.end(); ++aFIt) { + // keep the original face + theCache.insert(*aFIt); + // cache continuous face + cacheContinuousFace(*aFIt, anEdgesToFaces, theCache,theAngle); + } +} + +//================================================================================================= +static bool updateFaces(const AttributeSelectionListPtr& theList, + SetOfShapes& theFaces) +{ + bool aNewCache = false; + if (theFaces.size() != theList->size()) { + aNewCache = true; + } else { + for (int i = 0; i < theList->size(); i++) { + AttributeSelectionPtr aCurAttr = theList->value(i); + GeomShapePtr aFace = aCurAttr->value(); + if (theFaces.empty() || theFaces.find(aFace) == theFaces.end()) { + aNewCache = true; + break; + } + } + } + if (aNewCache) { + theFaces.clear(); + for (int i = 0; i < theList->size(); i++) { + AttributeSelectionPtr aCurAttr = theList->value(i); + GeomShapePtr aFace = aCurAttr->value(); + theFaces.insert(aFace); + } + } + return aNewCache; +} + +//================================================================================================= +bool FiltersPlugin_ContinuousFaces::isSupported(GeomAPI_Shape::ShapeType theType) const +{ + return theType == GeomAPI_Shape::FACE; +} + +//================================================================================================= +bool FiltersPlugin_ContinuousFaces::isOk(const GeomShapePtr& theShape, const ResultPtr&, + const ModelAPI_FiltersArgs& theArgs) const +{ + AttributePtr aAttr = theArgs.argument("faces"); + AttributeSelectionListPtr aList = + std::dynamic_pointer_cast(aAttr); + if (!aList.get()) + return false; + + AttributePtr anAttr = theArgs.argument("value"); + AttributeDoublePtr aValue = std::dynamic_pointer_cast(anAttr); + if (!aValue.get()|| !anAttr->isInitialized()) + return false; + double anAngle= aValue->value(); + + bool aNewCache = updateFaces(aList, + const_cast(this)->myFaces); + + if (aNewCache || fabs(myAngle - anAngle) > 1e-10 ) { + const_cast(this)->myAngle = anAngle; + const_cast(this)->myCachedShapes.clear(); + } + + if (myCachedShapes.empty()) { + for (size_t i = 0; i < aList->size(); i++) + { + ResultBodyPtr aBaseResult = ModelAPI_Tools::bodyOwner(aList->value(i)->context(), true); + if (!aBaseResult.get()) { + aBaseResult = std::dynamic_pointer_cast(aList->value(i)->context()); + if (!aBaseResult.get()) + return false; + } + cacheContinuousFaces(aBaseResult->shape(), + const_cast(this)->myFaces, + const_cast(this)->myCachedShapes,anAngle); + } + } + return myCachedShapes.find(theShape) != myCachedShapes.end(); +} + +//================================================================================================= +std::string FiltersPlugin_ContinuousFaces::xmlRepresentation() const +{ + return xmlFromFile("filter-ContinuousFaces.xml"); +} + +//================================================================================================= +void FiltersPlugin_ContinuousFaces::initAttributes(ModelAPI_FiltersArgs& theArguments) +{ + theArguments.initAttribute("value", ModelAPI_AttributeDouble::typeId()); + theArguments.initAttribute("faces", ModelAPI_AttributeSelectionList::typeId()); +} diff --git a/src/FiltersPlugin/FiltersPlugin_ContinuousFaces.h b/src/FiltersPlugin/FiltersPlugin_ContinuousFaces.h new file mode 100644 index 000000000..386b2de17 --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_ContinuousFaces.h @@ -0,0 +1,73 @@ +// Copyright (C) 2014-2021 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_CONTINUOUSFACES_H_ +#define FILTERSPLUGIN_CONTINUOUSFACES_H_ + +#include "FiltersPlugin.h" + +#include +#include + +#include + +typedef std::set SetOfShapes; + +/**\class FiltersPlugin_ContinuousFaces +* \ingroup DataModel +* \brief Filter for face with specific area +*/ +class FiltersPlugin_ContinuousFaces : public ModelAPI_Filter +{ +public: + FiltersPlugin_ContinuousFaces() : ModelAPI_Filter() { + myAngle = 0.0; + } + + virtual const std::string& name() const { + static const std::string kName("Continuous faces"); + return kName; + } + + /// Returns true for face type + 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 + /// \param theArgs arguments of the filter + virtual bool isOk(const GeomShapePtr& theShape, const ResultPtr&, + 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 faces selected for filtering + SetOfShapes myFaces; + /// Shapes applicable for the filter + SetOfShapes myCachedShapes; + /// Angle tolerance + double myAngle; +}; + +#endif diff --git a/src/FiltersPlugin/FiltersPlugin_EdgeSize.cpp b/src/FiltersPlugin/FiltersPlugin_EdgeSize.cpp new file mode 100644 index 000000000..e274831f2 --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_EdgeSize.cpp @@ -0,0 +1,116 @@ +// Copyright (C) 2014-2021 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_EdgeSize.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +//================================================================================================= +bool FiltersPlugin_EdgeSize::isSupported(GeomAPI_Shape::ShapeType theType) const +{ + return theType == GeomAPI_Shape::EDGE; +} + +//================================================================================================= +bool FiltersPlugin_EdgeSize::isOk(const GeomShapePtr& theShape, const ResultPtr&, + const ModelAPI_FiltersArgs& theArgs) const +{ + AttributePtr anAttr = theArgs.argument("value"); + AttributeDoublePtr aValue = std::dynamic_pointer_cast(anAttr); + if (!aValue.get()|| !anAttr->isInitialized() ) + return false; + double aVal = aValue->value(); + + anAttr = theArgs.argument("valueMax"); + aValue = std::dynamic_pointer_cast(anAttr); + if (!aValue.get()|| !anAttr->isInitialized() ) + return false; + double aValMax = aValue->value(); + + if (aVal < 0.0) + return false; + + GeomEdgePtr anEdge; + switch (theShape->shapeType()) { + case GeomAPI_Shape::EDGE: + anEdge = GeomEdgePtr(new GeomAPI_Edge(theShape)); + break; + case GeomAPI_Shape::WIRE: + anEdge = GeomAlgoAPI_ShapeTools::wireToEdge( + GeomWirePtr(new GeomAPI_Wire(theShape))); + break; + default: + return false; + } + + double aLength = anEdge->length(); + + anAttr = theArgs.argument("comparatorType"); + AttributeStringPtr aCompAttr = std::dynamic_pointer_cast(anAttr); + if (!aCompAttr) + return false; + std::string aCompString = aCompAttr->value(); + + bool isOK = false; + if (aCompString == "inf") + isOK = aLength < aVal - Precision::Confusion(); + else if (aCompString == "infEq") + isOK = aLength < aVal + Precision::Confusion(); + else if (aCompString == "sup") + isOK = aLength > aVal + Precision::Confusion(); + else if (aCompString == "supEq") + isOK = aLength > aVal - Precision::Confusion(); + else if (aCompString == "isBetween") + isOK = aVal <= aValMax + && aLength > aVal - Precision::Confusion() + && aLength < aValMax + Precision::Confusion(); + else if (aCompString == "isStrictlyBetween") + isOK = aVal <= aValMax + && aLength > aVal + Precision::Confusion() + && aLength < aValMax - Precision::Confusion(); + return isOK; +} + +//================================================================================================= +std::string FiltersPlugin_EdgeSize::xmlRepresentation() const +{ + return xmlFromFile("filter-EdgeSize.xml"); +} + +//================================================================================================= +void FiltersPlugin_EdgeSize::initAttributes(ModelAPI_FiltersArgs& theArguments) +{ + theArguments.initAttribute("comparatorType", ModelAPI_AttributeString::typeId()); + theArguments.initAttribute("value", ModelAPI_AttributeDouble::typeId()); + theArguments.initAttribute("valueMax", ModelAPI_AttributeDouble::typeId()); +} diff --git a/src/FiltersPlugin/FiltersPlugin_EdgeSize.h b/src/FiltersPlugin/FiltersPlugin_EdgeSize.h new file mode 100644 index 000000000..fc53fec9a --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_EdgeSize.h @@ -0,0 +1,60 @@ +// Copyright (C) 2014-2021 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_EDGESIZE_H_ +#define FILTERSPLUGIN_EDGESIZE_H_ + +#include "FiltersPlugin.h" + +#include +#include + +/**\class FiltersPlugin_EdgeSize +* \ingroup DataModel +* \brief Filter for edges with specific size +*/ +class FiltersPlugin_EdgeSize : public ModelAPI_Filter +{ +public: + FiltersPlugin_EdgeSize() : ModelAPI_Filter() {} + + virtual const std::string& name() const { + static const std::string kName("Edge size"); + return kName; + } + + /// Returns true for edge type + 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 + /// \param theArgs arguments of the filter + virtual bool isOk(const GeomShapePtr& theShape, const ResultPtr&, + 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; + +}; + +#endif diff --git a/src/FiltersPlugin/FiltersPlugin_FaceSize.cpp b/src/FiltersPlugin/FiltersPlugin_FaceSize.cpp new file mode 100644 index 000000000..1311f5719 --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_FaceSize.cpp @@ -0,0 +1,101 @@ +// Copyright (C) 2014-2021 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_FaceSize.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +//================================================================================================= +bool FiltersPlugin_FaceSize::isSupported(GeomAPI_Shape::ShapeType theType) const +{ + return theType == GeomAPI_Shape::FACE; +} + +//================================================================================================= +bool FiltersPlugin_FaceSize::isOk(const GeomShapePtr& theShape, const ResultPtr&, + const ModelAPI_FiltersArgs& theArgs) const +{ + AttributePtr anAttr = theArgs.argument("value"); + AttributeDoublePtr aValue = std::dynamic_pointer_cast(anAttr); + if (!aValue.get()|| !anAttr->isInitialized() ) + return false; + double aVal = aValue->value(); + + anAttr = theArgs.argument("valueMax"); + aValue = std::dynamic_pointer_cast(anAttr); + if (!aValue.get()|| !anAttr->isInitialized() ) + return false; + double aValMax = aValue->value(); + + if (aVal < 0.0) + return false; + + double aSurfArea = GeomAlgoAPI_ShapeTools::area(theShape); + + anAttr = theArgs.argument("comparatorType"); + AttributeStringPtr aCompAttr = std::dynamic_pointer_cast(anAttr); + if (!aCompAttr) + return false; + std::string aCompString = aCompAttr->value(); + + bool isOK = false; + if (aCompString == "inf") + isOK = aSurfArea < aVal - Precision::Confusion(); + else if (aCompString == "infEq") + isOK = aSurfArea < aVal + Precision::Confusion(); + else if (aCompString == "sup") + isOK = aSurfArea > aVal + Precision::Confusion(); + else if (aCompString == "supEq") + isOK = aSurfArea > aVal - Precision::Confusion(); + else if (aCompString == "isBetween") + isOK = aVal <= aValMax + && aSurfArea > aVal - Precision::Confusion() + && aSurfArea < aValMax + Precision::Confusion(); + else if (aCompString == "isStrictlyBetween") + isOK = aVal <= aValMax + && aSurfArea > aVal + Precision::Confusion() + && aSurfArea < aValMax - Precision::Confusion(); + return isOK; +} + +//================================================================================================= +std::string FiltersPlugin_FaceSize::xmlRepresentation() const +{ + return xmlFromFile("filter-FaceSize.xml"); +} + +//================================================================================================= +void FiltersPlugin_FaceSize::initAttributes(ModelAPI_FiltersArgs& theArguments) +{ + theArguments.initAttribute("comparatorType", ModelAPI_AttributeString::typeId()); + theArguments.initAttribute("value", ModelAPI_AttributeDouble::typeId()); + theArguments.initAttribute("valueMax", ModelAPI_AttributeDouble::typeId()); +} diff --git a/src/FiltersPlugin/FiltersPlugin_FaceSize.h b/src/FiltersPlugin/FiltersPlugin_FaceSize.h new file mode 100644 index 000000000..588e7452e --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_FaceSize.h @@ -0,0 +1,61 @@ +// Copyright (C) 2014-2021 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_FACESIZE_H_ +#define FILTERSPLUGIN_FACESIZE_H_ + +#include "FiltersPlugin.h" + +#include +#include + + +/**\class FiltersPlugin_FaceSize +* \ingroup DataModel +* \brief Filter for face with specific area +*/ +class FiltersPlugin_FaceSize : public ModelAPI_Filter +{ +public: + FiltersPlugin_FaceSize() : ModelAPI_Filter() {} + + virtual const std::string& name() const { + static const std::string kName("Face size"); + return kName; + } + + /// Returns true for face type + 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 + /// \param theArgs arguments of the filter + virtual bool isOk(const GeomShapePtr& theShape, const ResultPtr&, + 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; + +}; + +#endif diff --git a/src/FiltersPlugin/FiltersPlugin_FeatureEdges.cpp b/src/FiltersPlugin/FiltersPlugin_FeatureEdges.cpp new file mode 100644 index 000000000..2849dc590 --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_FeatureEdges.cpp @@ -0,0 +1,144 @@ +// Copyright (C) 2014-2021 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_FeatureEdges.h" + +#include +#include +#include +#include + +#include +#include +#include +#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); + } +} + +//================================================================================================= +static void cacheFeatureEdge(const GeomShapePtr theTopLevelShape, + SetOfShapes& theCache, + const double & theAngle) +{ + if (!theTopLevelShape) + return; + + MapShapeAndAncestors anEdgesToFaces; + mapEdgesAndFaces(theTopLevelShape, anEdgesToFaces); + + MapShapeAndAncestors::const_iterator aIt; + for (aIt = anEdgesToFaces.begin(); aIt != anEdgesToFaces.end(); ++aIt) { + GeomEdgePtr anEdge; + anEdge = GeomEdgePtr(new GeomAPI_Edge(aIt->first)); + + for (SetOfShapes::const_iterator aFIt = aIt->second.begin(); + aFIt != aIt->second.end(); ++aFIt) { + SetOfShapes::const_iterator aFIt2 = aFIt; + ++aFIt2; + for (;aFIt2 != aIt->second.end(); ++aFIt2) { + std::string anError; + if (theCache.find(*aFIt) == theCache.end()) { + if (theAngle < Precision::Confusion() + || !GeomAlgoAPI_ShapeTools::isContinuousFaces(*aFIt, + *aFIt2, + anEdge->middlePoint(), + theAngle, + anError)) { + if (anError.empty()) + theCache.insert(anEdge); + } + } + } + } + } +} + +//================================================================================================= +bool FiltersPlugin_FeatureEdges::isSupported(GeomAPI_Shape::ShapeType theType) const +{ + return theType == GeomAPI_Shape::EDGE; +} + +//================================================================================================= +bool FiltersPlugin_FeatureEdges::isOk(const GeomShapePtr& theShape, const ResultPtr& theResult, + const ModelAPI_FiltersArgs& theArgs) const +{ + AttributePtr anAttr = theArgs.argument("value"); + AttributeDoublePtr aValue = std::dynamic_pointer_cast(anAttr); + + if (!aValue.get() || !anAttr->isInitialized()) + return false; + double anAngle = aValue->value(); + + // check base result + ResultBodyPtr aBaseResult = ModelAPI_Tools::bodyOwner(theResult, true); + if (!aBaseResult) { + aBaseResult = std::dynamic_pointer_cast(theResult); + if (!aBaseResult.get()) { + return false; + } + } + if (fabs(myAngle - anAngle) > 1e-10 + || !myBaseShape + || !myBaseShape->isSame(aBaseResult->shape())) { + const_cast(this)->myAngle = anAngle; + const_cast(this)->myBaseShape = aBaseResult->shape(); + const_cast(this)->myCachedShapes.clear(); + } + + if (myCachedShapes.empty()) { + + cacheFeatureEdge(aBaseResult->shape(), + const_cast(this)->myCachedShapes, anAngle); + } + + return myCachedShapes.find(theShape) != myCachedShapes.end(); +} + +//================================================================================================= +std::string FiltersPlugin_FeatureEdges::xmlRepresentation() const +{ + return xmlFromFile("filter-FeatureEdges.xml"); +} + +//================================================================================================= +void FiltersPlugin_FeatureEdges::initAttributes(ModelAPI_FiltersArgs& theArguments) +{ + theArguments.initAttribute("value", ModelAPI_AttributeDouble::typeId()); +} diff --git a/src/FiltersPlugin/FiltersPlugin_FeatureEdges.h b/src/FiltersPlugin/FiltersPlugin_FeatureEdges.h new file mode 100644 index 000000000..536bfee6f --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_FeatureEdges.h @@ -0,0 +1,73 @@ +// Copyright (C) 2014-2021 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_FEATUREEDGES_H_ +#define FILTERSPLUGIN_FEATUREEDGES_H_ + +#include "FiltersPlugin.h" + +#include +#include + +#include + +typedef std::set SetOfShapes; + +/**\class FiltersPlugin_FeatureEdges +* \ingroup DataModel +* \brief Filter for edges with feature angle +*/ +class FiltersPlugin_FeatureEdges : public ModelAPI_Filter +{ +public: + FiltersPlugin_FeatureEdges() : ModelAPI_Filter() { + myAngle = 0.0; + } + + virtual const std::string& name() const { + static const std::string kName("Feature edges"); + return kName; + } + + /// Returns true for edge type + 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 + /// \param theArgs arguments of the filter + virtual bool isOk(const GeomShapePtr& theShape, const ResultPtr&, + 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: + /// Shapes applicable for the filter + SetOfShapes myCachedShapes; + /// Angle tolerance + double myAngle; + /// the base shape + GeomShapePtr myBaseShape; +}; + +#endif diff --git a/src/FiltersPlugin/FiltersPlugin_Plugin.cpp b/src/FiltersPlugin/FiltersPlugin_Plugin.cpp index db708db90..59d25b9dd 100644 --- a/src/FiltersPlugin/FiltersPlugin_Plugin.cpp +++ b/src/FiltersPlugin/FiltersPlugin_Plugin.cpp @@ -17,19 +17,24 @@ // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -#include "FiltersPlugin_Plugin.h" -#include "FiltersPlugin_Selection.h" -#include "FiltersPlugin_HorizontalFace.h" -#include "FiltersPlugin_VerticalFace.h" #include "FiltersPlugin_BelongsTo.h" -#include "FiltersPlugin_OnPlane.h" -#include "FiltersPlugin_OnLine.h" +#include "FiltersPlugin_ContinuousFaces.h" +#include "FiltersPlugin_EdgeSize.h" +#include "FiltersPlugin_ExternalFaces.h" +#include "FiltersPlugin_FaceSize.h" +#include "FiltersPlugin_FeatureEdges.h" +#include "FiltersPlugin_HorizontalFace.h" #include "FiltersPlugin_OnGeometry.h" +#include "FiltersPlugin_OnLine.h" +#include "FiltersPlugin_OnPlane.h" #include "FiltersPlugin_OnPlaneSide.h" #include "FiltersPlugin_OppositeToEdge.h" +#include "FiltersPlugin_Plugin.h" #include "FiltersPlugin_RelativeToSolid.h" -#include "FiltersPlugin_ExternalFaces.h" +#include "FiltersPlugin_Selection.h" #include "FiltersPlugin_Validators.h" +#include "FiltersPlugin_VerticalFace.h" +#include "FiltersPlugin_VolumeSize.h" #include @@ -54,6 +59,11 @@ FiltersPlugin_Plugin::FiltersPlugin_Plugin() aFactory->registerFilter("OppositeToEdge", new FiltersPlugin_OppositeToEdge); aFactory->registerFilter("RelativeToSolid", new FiltersPlugin_RelativeToSolid); aFactory->registerFilter("ExternalFaces", new FiltersPlugin_ExternalFaces); + aFactory->registerFilter("EdgeSize", new FiltersPlugin_EdgeSize); + aFactory->registerFilter("FaceSize", new FiltersPlugin_FaceSize); + aFactory->registerFilter("VolumeSize", new FiltersPlugin_VolumeSize); + aFactory->registerFilter("FeatureEdges", new FiltersPlugin_FeatureEdges); + aFactory->registerFilter("ContinuousFaces", new FiltersPlugin_ContinuousFaces); Config_ModuleReader::loadScript("FiltersPlugin_TopoConnectedFaces"); diff --git a/src/FiltersPlugin/FiltersPlugin_VolumeSize.cpp b/src/FiltersPlugin/FiltersPlugin_VolumeSize.cpp new file mode 100644 index 000000000..977629646 --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_VolumeSize.cpp @@ -0,0 +1,101 @@ +// Copyright (C) 2014-2021 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_VolumeSize.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +//================================================================================================= +bool FiltersPlugin_VolumeSize::isSupported(GeomAPI_Shape::ShapeType theType) const +{ + return theType == GeomAPI_Shape::SOLID; +} + +//================================================================================================= +bool FiltersPlugin_VolumeSize::isOk(const GeomShapePtr& theShape, const ResultPtr&, + const ModelAPI_FiltersArgs& theArgs) const +{ + AttributePtr anAttr = theArgs.argument("value"); + AttributeDoublePtr aValue = std::dynamic_pointer_cast(anAttr); + if (!aValue.get()|| !anAttr->isInitialized()) + return false; + double aVal = aValue->value(); + + anAttr = theArgs.argument("valueMax"); + aValue = std::dynamic_pointer_cast(anAttr); + if (!aValue.get()|| !anAttr->isInitialized()) + return false; + double aValMax = aValue->value(); + + if (aVal < 0.0) + return false; + + double aVolume = GeomAlgoAPI_ShapeTools::volume(theShape); + + anAttr = theArgs.argument("comparatorType"); + AttributeStringPtr aCompAttr = std::dynamic_pointer_cast(anAttr); + if (!aCompAttr) + return false; + std::string aCompString = aCompAttr->value(); + + bool isOK = false; + if (aCompString == "inf") + isOK = aVolume < aVal - Precision::Confusion(); + else if (aCompString == "infEq") + isOK = aVolume < aVal + Precision::Confusion(); + else if (aCompString == "sup") + isOK = aVolume > aVal + Precision::Confusion(); + else if (aCompString == "supEq") + isOK = aVolume > aVal - Precision::Confusion(); + else if (aCompString == "isBetween") + isOK = aVal <= aValMax + && aVolume > aVal - Precision::Confusion() + && aVolume < aValMax + Precision::Confusion(); + else if (aCompString == "isStrictlyBetween") + isOK = aVal <= aValMax + && aVolume > aVal + Precision::Confusion() + && aVolume < aValMax - Precision::Confusion(); + return isOK; +} + +//================================================================================================= +std::string FiltersPlugin_VolumeSize::xmlRepresentation() const +{ + return xmlFromFile("filter-VolumeSize.xml"); +} + +//================================================================================================= +void FiltersPlugin_VolumeSize::initAttributes(ModelAPI_FiltersArgs& theArguments) +{ + theArguments.initAttribute("comparatorType", ModelAPI_AttributeString::typeId()); + theArguments.initAttribute("value", ModelAPI_AttributeDouble::typeId()); + theArguments.initAttribute("valueMax", ModelAPI_AttributeDouble::typeId()); +} diff --git a/src/FiltersPlugin/FiltersPlugin_VolumeSize.h b/src/FiltersPlugin/FiltersPlugin_VolumeSize.h new file mode 100644 index 000000000..7ff4f5b92 --- /dev/null +++ b/src/FiltersPlugin/FiltersPlugin_VolumeSize.h @@ -0,0 +1,61 @@ +// Copyright (C) 2014-2021 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_VOLUMESIZE_H_ +#define FILTERSPLUGIN_VOLUMESIZE_H_ + +#include "FiltersPlugin.h" + +#include +#include + + +/**\class FiltersPlugin_VolumeSize +* \ingroup DataModel +* \brief Filter for solid with specific volume +*/ +class FiltersPlugin_VolumeSize : public ModelAPI_Filter +{ +public: + FiltersPlugin_VolumeSize() : ModelAPI_Filter() {} + + virtual const std::string& name() const { + static const std::string kName("Volume size"); + return kName; + } + + /// Returns true for solid type + 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 + /// \param theArgs arguments of the filter + virtual bool isOk(const GeomShapePtr& theShape, const ResultPtr&, + 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; + +}; + +#endif diff --git a/src/FiltersPlugin/FiltersPlugin_msg_fr.ts b/src/FiltersPlugin/FiltersPlugin_msg_fr.ts index 16b245820..eb39232e5 100644 --- a/src/FiltersPlugin/FiltersPlugin_msg_fr.ts +++ b/src/FiltersPlugin/FiltersPlugin_msg_fr.ts @@ -63,10 +63,26 @@ Belongs to Appartient à + + Edge size + Taille d'arête + External faces Externe faces + + Continuous faces + Faces continues + + + Feature edges + Arêtes caractéristiques + + + Face size + Surface de face + Horizontal faces Faces horizontales @@ -103,6 +119,10 @@ Vertical faces Faces verticales + + Volume size + volume + Attribute "%1" is not initialized. Sélectionnez un objet. @@ -140,6 +160,81 @@ + + + EdgeSize + + Size + Longueur + + + Min size + Longueur Min + + + Max size + Longueur Max + + + is between + est compris entre + + + is strictly between + est compris strictement entre + + + + + + FaceSize + + Size + Longueur + + + Min size + Longueur Min + + + Max size + Longueur Max + + + is between + est compris entre + + + is strictly between + est compris strictement entre + + + + + + VolumeSize + + Size + Longueur + + + Min size + Longueur min + + + Max size + Longueur max + + + is between + est compris entre + + + is strictly between + est compris strictement entre + + + OnGeometry diff --git a/src/FiltersPlugin/Test/TestFilter_ContinuousFaces.py b/src/FiltersPlugin/Test/TestFilter_ContinuousFaces.py new file mode 100644 index 000000000..7df65fe10 --- /dev/null +++ b/src/FiltersPlugin/Test/TestFilter_ContinuousFaces.py @@ -0,0 +1,87 @@ +# Copyright (C) 2014-2021 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 +from SketchAPI import * +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() + +###===========Test with fillet on solide=========================================================== + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Box +Box_2 = model.addBox(Part_1_doc, 10, 10, 10) + +### Create Fillet +Fillet_1_objects_4 = [model.selection("FACE", "Box_1_1/Left"), + model.selection("FACE", "Box_1_1/Front"), + model.selection("FACE", "Box_1_1/Top"), + model.selection("FACE", "Box_1_1/Right"), + model.selection("FACE", "Box_1_1/Bottom")] +Fillet_1 = model.addFillet(Part_1_doc, Fillet_1_objects_4, 2, keepSubResults = True) + +model.end() + +model.do() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "ContinuousFaces", args = [model.selection("FACE", "Fillet_1_1/MF:Fillet&Box_1_1/Left"), 5.0 ])]) +model.end() + +Reference = {} +ResultFillet_1_1 = Fillet_1.result().resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(ResultFillet_1_1.shape(), GeomAPI_Shape.FACE) +while exp.more(): + Reference[model.selection(ResultFillet_1_1, exp.current())] = True + exp.next() +model.checkFilter(Part_1_doc, model, Filters, Reference) + +###===========Test with Chamfer by an angle of 43° on solid and filters with angle 50 =========== + +### Create Part +Part_2 = model.addPart(partSet) +Part_2_doc = Part_2.document() + +### Create Box +Box_3 = model.addBox(Part_2_doc, 10, 10, 10) + +### Create Chamfer +Chamfer_1_objects = [model.selection("FACE", "Box_1_1/Left"), + model.selection("FACE", "Box_1_1/Front"), + model.selection("FACE", "Box_1_1/Top"), + model.selection("FACE", "Box_1_1/Right"), + model.selection("FACE", "Box_1_1/Bottom")] +Chamfer_2 = model.addChamfer(Part_2_doc, Chamfer_1_objects, False, 2, 43, keepSubResults = True) + +model.end() + +model.do() +Filters = model.filters(Part_2_doc, [model.addFilter(name = "ContinuousFaces", args = [model.selection("FACE", "Chamfer_1_1/MF:Chamfer&Box_1_1/Left"), 50.0 ])]) +model.end() + +Reference = {} +ResultChamfer_2_1 = Chamfer_2.result().resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(ResultChamfer_2_1.shape(), GeomAPI_Shape.FACE) +while exp.more(): + Reference[model.selection(ResultChamfer_2_1, exp.current())] = True + exp.next() +model.checkFilter(Part_2_doc, model, Filters, Reference) diff --git a/src/FiltersPlugin/Test/TestFilter_EdgeSize.py b/src/FiltersPlugin/Test/TestFilter_EdgeSize.py new file mode 100644 index 000000000..d0a13f487 --- /dev/null +++ b/src/FiltersPlugin/Test/TestFilter_EdgeSize.py @@ -0,0 +1,170 @@ +# Copyright (C) 2014-2021 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 +from SketchAPI import * +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Box +Box_1 = model.addBox(Part_1_doc, 100, 50, 100) + +Filters = model.filters(Part_1_doc, [model.addFilter(name = "EdgeSize", args = [ "inf" , 60.0 ])]) + +model.end() +Reference = { + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Left]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"): False, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"): False, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"): False, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"): False, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]"): False} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "EdgeSize", args = [ "sup" , 60.0 ])]) +model.end() + +Reference = { + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Bottom]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Bottom]"): False, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Left]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"): True, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]"): True} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "EdgeSize", args = [ "inf" , 50.0 ])]) +model.end() + +Reference = { + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Bottom]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Bottom]"): False, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Left]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"): False, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"): False, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"): False, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"): False, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]"): False} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "EdgeSize", args = [ "infEq" , 50.0 ])]) +model.end() + +Reference = { + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Left]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"): False, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"): False, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"): False, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"): False, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]"): False} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "EdgeSize", args = [ "supEq" , 50.0 ])]) +model.end() + +Reference = { + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Left]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"): True, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]"): True} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "EdgeSize", args = [ "isBetween" , 50.0 , 200.0])]) +model.end() + +Reference = { + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Left]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"): True, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]"): True} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "EdgeSize", args = [ "isStrictlyBetween" , 50.0 , 200.0])]) +model.end() + +Reference = { + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Bottom]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Bottom]"): False, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"): False, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Left]"): True, + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"): True, + model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"): True, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"): True, + model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Top]"): True, + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]"): True} + +model.checkFilter(Part_1_doc, model, Filters, Reference) diff --git a/src/FiltersPlugin/Test/TestFilter_FaceSize.py b/src/FiltersPlugin/Test/TestFilter_FaceSize.py new file mode 100644 index 000000000..8b12826e5 --- /dev/null +++ b/src/FiltersPlugin/Test/TestFilter_FaceSize.py @@ -0,0 +1,142 @@ +# Copyright (C) 2014-2021 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 +from SketchAPI import * +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Box +Box_1 = model.addBox(Part_1_doc, 100, 50, 100) + +Filters = model.filters(Part_1_doc, [model.addFilter(name = "FaceSize", args = [ "inf" , 5000.0 ])]) + +model.end() +Reference = { + model.selection("FACE", "Box_1_1/Top"): False, + model.selection("FACE", "Box_1_1/Back"): False, + model.selection("FACE", "Box_1_1/Front"): False, + model.selection("FACE", "Box_1_1/Bottom"): False, + model.selection("FACE","Box_1_1/Right"): False, + model.selection("FACE","Box_1_1/Left"): False} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "FaceSize", args = [ "infEq" , 5000.0 ])]) +model.end() + +Reference = { + model.selection("FACE", "Box_1_1/Top"): True, + model.selection("FACE", "Box_1_1/Back"): True, + model.selection("FACE", "Box_1_1/Front"): True, + model.selection("FACE", "Box_1_1/Bottom"): True, + model.selection("FACE", "Box_1_1/Right"): False, + model.selection("FACE", "Box_1_1/Left"): False} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "FaceSize", args = [ "sup" , 5000.0 ])]) +model.end() + +Reference = { + model.selection("FACE", "Box_1_1/Top"): False, + model.selection("FACE", "Box_1_1/Back"): False, + model.selection("FACE", "Box_1_1/Front"): False, + model.selection("FACE", "Box_1_1/Bottom"): False, + model.selection("FACE", "Box_1_1/Right"): True, + model.selection("FACE", "Box_1_1/Left"): True} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "FaceSize", args = [ "inf" , 50.0 ])]) +model.end() + +Reference = { + model.selection("FACE", "Box_1_1/Top"): False, + model.selection("FACE", "Box_1_1/Back"): False, + model.selection("FACE", "Box_1_1/Front"): False, + model.selection("FACE", "Box_1_1/Bottom"): False, + model.selection("FACE", "Box_1_1/Right"): False, + model.selection("FACE", "Box_1_1/Left"): False} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "FaceSize", args = [ "supEq" , 5000.0 ])]) +model.end() + +Reference = { + model.selection("FACE", "Box_1_1/Top"): True, + model.selection("FACE", "Box_1_1/Back"): True, + model.selection("FACE", "Box_1_1/Front"): True, + model.selection("FACE", "Box_1_1/Bottom"): True, + model.selection("FACE", "Box_1_1/Right"): True, + model.selection("FACE", "Box_1_1/Left"): True} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "FaceSize", args = [ "supEq" , 7000.0 ])]) +model.end() + +Reference = { + model.selection("FACE", "Box_1_1/Top"): False, + model.selection("FACE", "Box_1_1/Back"): False, + model.selection("FACE", "Box_1_1/Front"): False, + model.selection("FACE", "Box_1_1/Bottom"): False, + model.selection("FACE", "Box_1_1/Right"): True, + model.selection("FACE", "Box_1_1/Left"): True} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "FaceSize", args = [ "isBetween" , 50.0 , 10000.0])]) +model.end() + +Reference = { + model.selection("FACE", "Box_1_1/Top"): True, + model.selection("FACE", "Box_1_1/Back"): True, + model.selection("FACE", "Box_1_1/Front"): True, + model.selection("FACE", "Box_1_1/Bottom"): True, + model.selection("FACE", "Box_1_1/Right"): True, + model.selection("FACE", "Box_1_1/Left"): True} + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "FaceSize", args = [ "isStrictlyBetween" , 500.0 , 10000.0])]) +model.end() + +Reference = { + model.selection("FACE", "Box_1_1/Top"): True, + model.selection("FACE", "Box_1_1/Back"): True, + model.selection("FACE", "Box_1_1/Front"): True, + model.selection("FACE", "Box_1_1/Bottom"): True, + model.selection("FACE", "Box_1_1/Right"): False, + model.selection("FACE", "Box_1_1/Left"): False} + +model.checkFilter(Part_1_doc, model, Filters, Reference) diff --git a/src/FiltersPlugin/Test/TestFilter_FeatureEdges.py b/src/FiltersPlugin/Test/TestFilter_FeatureEdges.py new file mode 100644 index 000000000..324b98744 --- /dev/null +++ b/src/FiltersPlugin/Test/TestFilter_FeatureEdges.py @@ -0,0 +1,124 @@ +# Copyright (C) 2014-2021 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 +from SketchAPI import * +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() + +###===========Test with chamfer on face========================================================= + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Box +Box_1 = model.addBox(Part_1_doc, 10, 10, 10) + +### Create Chamfer +Chamfer_1 = model.addChamfer(Part_1_doc, [model.selection("FACE", "Box_1_1/Left")], True, 2, 2, keepSubResults = True) + +model.do() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "FeatureEdges", args = [ 5.0 ])]) +model.end() + +Reference = {} +ResultChamfer_1_1 = Chamfer_1.result().resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(ResultChamfer_1_1.shape(), GeomAPI_Shape.EDGE) +while exp.more(): + Reference[model.selection(ResultChamfer_1_1, exp.current())] = True + exp.next() +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.do() + +###===========Test with fillet on solide=========================================================== + +### Create Part +Part_2 = model.addPart(partSet) +Part_2_doc = Part_2.document() + +### Create Box +Box_2 = model.addBox(Part_2_doc, 10, 10, 10) + +### Create Fillet +Fillet_1_objects_4 = [model.selection("FACE", "Box_1_1/Left"), + model.selection("FACE", "Box_1_1/Front"), + model.selection("FACE", "Box_1_1/Top"), + model.selection("FACE", "Box_1_1/Right"), + model.selection("FACE", "Box_1_1/Bottom")] +Fillet_1 = model.addFillet(Part_2_doc, Fillet_1_objects_4, 2, keepSubResults = True) + +model.end() + +model.do() +Filters = model.filters(Part_2_doc, [model.addFilter(name = "FeatureEdges", args = [ 5.0 ])]) +model.end() + + +###===========Test with Chamfer by an angle of 43° and filters with angle 30 and 50 =========== +Reference = {} +ResultFillet_1_1 = Fillet_1.result().resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(ResultFillet_1_1.shape(), GeomAPI_Shape.EDGE) +while exp.more(): + Reference[model.selection(ResultFillet_1_1, exp.current())] = False + exp.next() +model.checkFilter(Part_2_doc, model, Filters, Reference) + +### Create Part +Part_3 = model.addPart(partSet) +Part_3_doc = Part_3.document() + +### Create Box +Box_3 = model.addBox(Part_3_doc, 10, 10, 10) + +### Create Chamfer +Chamfer_1_objects = [model.selection("FACE", "Box_1_1/Left"), + model.selection("FACE", "Box_1_1/Front"), + model.selection("FACE", "Box_1_1/Top"), + model.selection("FACE", "Box_1_1/Right"), + model.selection("FACE", "Box_1_1/Bottom")] +Chamfer_2 = model.addChamfer(Part_3_doc, Chamfer_1_objects, False, 2, 43, keepSubResults = True) + +model.end() + +model.do() +Filters = model.filters(Part_3_doc, [model.addFilter(name = "FeatureEdges", args = [ 30.0 ])]) +model.end() + +Reference = {} +ResultChamfer_2_1 = Chamfer_2.result().resultSubShapePair()[0] +exp = GeomAPI_ShapeExplorer(ResultChamfer_2_1.shape(), GeomAPI_Shape.EDGE) +while exp.more(): + Reference[model.selection(ResultChamfer_2_1, exp.current())] = True + exp.next() +model.checkFilter(Part_3_doc, model, Filters, Reference) + +model.do() +Filters = model.filters(Part_3_doc, [model.addFilter(name = "FeatureEdges", args = [ 50.0 ])]) +model.end() + +Reference = {} +exp = GeomAPI_ShapeExplorer(ResultChamfer_2_1.shape(), GeomAPI_Shape.EDGE) +while exp.more(): + Reference[model.selection(ResultChamfer_2_1, exp.current())] = False + exp.next() +model.checkFilter(Part_3_doc, model, Filters, Reference) diff --git a/src/FiltersPlugin/Test/TestFilter_VolumeSize.py b/src/FiltersPlugin/Test/TestFilter_VolumeSize.py new file mode 100644 index 000000000..22a4c3ca1 --- /dev/null +++ b/src/FiltersPlugin/Test/TestFilter_VolumeSize.py @@ -0,0 +1,159 @@ +# Copyright (C) 2014-2021 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 +from SketchAPI import * +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Box +Box_1 = model.addBox(Part_1_doc, 100, 50, 100) + +### Create Point +Point_1 = model.addPoint(Part_1_doc, 100, 0, 0) + +### Create Point +Point_2 = model.addPoint(Part_1_doc, 250, 50, 100) + +### Create Box +Box_2 = model.addBox(Part_1_doc, model.selection("VERTEX", "all-in-Point_1"), model.selection("VERTEX", "all-in-Point_2")) + +### Create Point +Point_3 = model.addPoint(Part_1_doc, 100, 50, 250) + +### Create Box +Box_3 = model.addBox(Part_1_doc, model.selection("VERTEX", "[Box_1_1/Back][Box_1_1/Left][Box_1_1/Top]"), model.selection("VERTEX", "Point_3")) + +### Create CompSolid +CompSolid_1_objects = [model.selection("SOLID", "Box_3_1"), + model.selection("SOLID", "Box_2_1"), + model.selection("SOLID", "Box_1_1")] +CompSolid_1 = model.addCompSolid(Part_1_doc, CompSolid_1_objects) + +Filters = model.filters(Part_1_doc, [model.addFilter(name = "VolumeSize", args = [ "inf" , 500000.0 ])]) + +model.end() + +Reference = {} +ResultCompSolid_1 = CompSolid_1.result().resultSubShapePair()[0] +ResultBox_1 = Box_1.result().resultSubShapePair()[0] +ResultBox_2 = Box_2.result().resultSubShapePair()[0] +ResultBox_3 = Box_3.result().resultSubShapePair()[0] + +exp = GeomAPI_ShapeExplorer(ResultCompSolid_1.shape(), GeomAPI_Shape.SOLID) +while exp.more(): + Reference[model.selection(ResultCompSolid_1, exp.current())] = False + exp.next() +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "VolumeSize", args = [ "infEq" , 500000.0 ])]) +model.end() + +Reference = {} +exp = GeomAPI_ShapeExplorer(ResultBox_1.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_1, exp.current())] = True +exp = GeomAPI_ShapeExplorer(ResultBox_2.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_2, exp.current())] = False +exp = GeomAPI_ShapeExplorer(ResultBox_3.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_3, exp.current())] = False + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "VolumeSize", args = [ "sup" , 500000.0 ])]) +model.end() + +Reference = {} +exp = GeomAPI_ShapeExplorer(ResultBox_1.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_1, exp.current())] = False +exp = GeomAPI_ShapeExplorer(ResultBox_2.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_2, exp.current())] = True +exp = GeomAPI_ShapeExplorer(ResultBox_3.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_3, exp.current())] = True + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "VolumeSize", args = [ "inf" , 50.0 ])]) +model.end() + +Reference = {} +exp = GeomAPI_ShapeExplorer(ResultBox_1.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_1, exp.current())] = False +exp = GeomAPI_ShapeExplorer(ResultBox_2.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_2, exp.current())] = False +exp = GeomAPI_ShapeExplorer(ResultBox_3.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_3, exp.current())] = False + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "VolumeSize", args = [ "supEq" , 500000.0 ])]) +model.end() + +Reference = {} +exp = GeomAPI_ShapeExplorer(ResultCompSolid_1.shape(), GeomAPI_Shape.SOLID) +while exp.more(): + Reference[model.selection(ResultCompSolid_1, exp.current())] = True + exp.next() +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "VolumeSize", args = [ "supEq" , 800000.0 ])]) +model.end() + +Reference = {} +exp = GeomAPI_ShapeExplorer(ResultCompSolid_1.shape(), GeomAPI_Shape.SOLID) +while exp.more(): + Reference[model.selection(ResultCompSolid_1, exp.current())] = False + exp.next() + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "VolumeSize", args = [ "isBetween" , 50.0 , 600000.0])]) +model.end() +Reference = {} +exp = GeomAPI_ShapeExplorer(ResultBox_1.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_1, exp.current())] = True +exp = GeomAPI_ShapeExplorer(ResultBox_2.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_2, exp.current())] = False +exp = GeomAPI_ShapeExplorer(ResultBox_3.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_3, exp.current())] = False + +model.checkFilter(Part_1_doc, model, Filters, Reference) + +model.begin() +Filters = model.filters(Part_1_doc, [model.addFilter(name = "VolumeSize", args = [ "isStrictlyBetween" , 500.0 , 750000.0])]) +model.end() +Reference = {} +exp = GeomAPI_ShapeExplorer(ResultBox_1.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_1, exp.current())] = True +exp = GeomAPI_ShapeExplorer(ResultBox_2.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_2, exp.current())] = False +exp = GeomAPI_ShapeExplorer(ResultBox_3.shape(), GeomAPI_Shape.SOLID) +Reference[model.selection(ResultBox_3, exp.current())] = False + +model.checkFilter(Part_1_doc, model, Filters, Reference) diff --git a/src/FiltersPlugin/Test/TestFilters_Supported.py b/src/FiltersPlugin/Test/TestFilters_Supported.py index 4d393252f..d40fbd3d5 100644 --- a/src/FiltersPlugin/Test/TestFilters_Supported.py +++ b/src/FiltersPlugin/Test/TestFilters_Supported.py @@ -34,15 +34,20 @@ FILTER_EXTERNAL_FACES = "ExternalFaces" FILTER_HORIZONTAL_FACES = "HorizontalFaces" FILTER_VERTICAL_FACES = "VerticalFaces" FILTER_CONNECTED_FACES = "TopoConnectedFaces" +FILTER_EDGE_SIZE = "EdgeSize" +FILTER_FACE_SIZE = "FaceSize" +FILTER_VOLUME_SIZE = "VolumeSize" +FILTER_FEATURE_EDGES = "FeatureEdges" +FILTER_CONTINUOUS_FACES= "ContinuousFaces" # Reference data (supported filters) for each type of shape Reference = { GeomAPI_Shape.VERTEX : [FILTER_BELONGS_TO, FILTER_ON_PLANE, FILTER_ON_LINE, FILTER_ON_PLANE_SIDE, FILTER_RELATIVE_TO_SOLID], - GeomAPI_Shape.EDGE : [FILTER_BELONGS_TO, FILTER_ON_PLANE, FILTER_ON_LINE, FILTER_ON_GEOMETRY, FILTER_ON_PLANE_SIDE, FILTER_OPPOSITE_TO_EDGE, FILTER_RELATIVE_TO_SOLID], + GeomAPI_Shape.EDGE : [FILTER_BELONGS_TO, FILTER_ON_PLANE, FILTER_ON_LINE, FILTER_ON_GEOMETRY, FILTER_ON_PLANE_SIDE, FILTER_OPPOSITE_TO_EDGE, FILTER_RELATIVE_TO_SOLID, FILTER_EDGE_SIZE, FILTER_FEATURE_EDGES], GeomAPI_Shape.WIRE : [FILTER_BELONGS_TO, FILTER_ON_PLANE, FILTER_ON_PLANE_SIDE, FILTER_RELATIVE_TO_SOLID], - GeomAPI_Shape.FACE : [FILTER_BELONGS_TO, FILTER_ON_PLANE, FILTER_ON_GEOMETRY, FILTER_ON_PLANE_SIDE, FILTER_RELATIVE_TO_SOLID, FILTER_EXTERNAL_FACES, FILTER_HORIZONTAL_FACES, FILTER_VERTICAL_FACES, FILTER_CONNECTED_FACES], + GeomAPI_Shape.FACE : [FILTER_BELONGS_TO, FILTER_ON_PLANE, FILTER_ON_GEOMETRY, FILTER_ON_PLANE_SIDE, FILTER_RELATIVE_TO_SOLID, FILTER_EXTERNAL_FACES, FILTER_HORIZONTAL_FACES, FILTER_VERTICAL_FACES, FILTER_CONNECTED_FACES, FILTER_FACE_SIZE, FILTER_CONTINUOUS_FACES], GeomAPI_Shape.SHELL : [FILTER_BELONGS_TO, FILTER_ON_PLANE, FILTER_ON_PLANE_SIDE, FILTER_RELATIVE_TO_SOLID], - GeomAPI_Shape.SOLID : [FILTER_BELONGS_TO, FILTER_ON_PLANE_SIDE], + GeomAPI_Shape.SOLID : [FILTER_BELONGS_TO, FILTER_ON_PLANE_SIDE, FILTER_VOLUME_SIZE], } model.begin() diff --git a/src/FiltersPlugin/doc/FiltersPlugin.rst b/src/FiltersPlugin/doc/FiltersPlugin.rst index 81b67a1da..3d9bbb492 100644 --- a/src/FiltersPlugin/doc/FiltersPlugin.rst +++ b/src/FiltersPlugin/doc/FiltersPlugin.rst @@ -57,6 +57,39 @@ By default, the result of Selection feature all selectable entities from all Sha - **Argument:** Any result object, multiple OR selection accepted - **Algorithm:** Returns only shapes that belong to selected results. +**Edge size** + +- **Result type:** Edge +- **Argument:** + - **Comparator:** <, <=, >, >=, is between, is strictly between + - **Size** or **Min size** and **Max size** +- **Algorithm:** Returns all edges whose length respect comparator rules. + +**Face size** + +- **Result type:** Face +- **Argument:** + - **Comparator:** <, <=, >, >=, is between, is strictly between + - **Size** or **Min size** and **Max size** +- **Algorithm:** Returns all faces whose area respect comparator rules. + +**Volume size** + +- **Result type:** Solid +- **Argument:** + - **Comparator:** <, <=, >, >=, is between, is strictly between + - **Size** or **Min size** and **Max size** +- **Algorithm:** Returns all solids whose volume respect comparator rules. + +**Feature edges** + +This algorithm identifies edges between two faces discontinuous with an angular tolerance. + +- **Result type:** Edge +- **Argument:** + - **Angle** an angular tolerance used by G1 continuity criterion for comparing the angle between the normals +- **Algorithm:** Returns all edges between two discontinuous faces. + **On a plane** - **Result type:** Vertex, Edge, Face @@ -89,6 +122,16 @@ This algorithm is based on the Propagate geompy function. It works on a model pa - **Argument:** An edge belonging to a quadrangular face - **Algorithm:** Returns all Edges opposite to the given Edge on all quadrangular faces connected to this Edge. The algorithm is recursive: after an edge is found on one face, it adds edges opposite to this new one. +**Continuous Faces** + +This algorithm identifies continuous faces with an angular tolerance given by topological propagation. + +- **Result type:** Face +- **Argument:** + - **Angle:** an angular tolerance used by G1 continuity criterion for comparing the angle between the normals. + - **Faces:** Faces to start the propagation. +- **Algorithm:** Returns continuous faces. + **On/In/Out a Solid** This algorithm reproduces the GetShapesOnShape function of geompy. diff --git a/src/FiltersPlugin/filter-ContinuousFaces.xml b/src/FiltersPlugin/filter-ContinuousFaces.xml new file mode 100644 index 000000000..45036ef51 --- /dev/null +++ b/src/FiltersPlugin/filter-ContinuousFaces.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/src/FiltersPlugin/filter-EdgeSize.xml b/src/FiltersPlugin/filter-EdgeSize.xml new file mode 100644 index 000000000..b6eaed803 --- /dev/null +++ b/src/FiltersPlugin/filter-EdgeSize.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/FiltersPlugin/filter-FaceSize.xml b/src/FiltersPlugin/filter-FaceSize.xml new file mode 100644 index 000000000..41d75e61f --- /dev/null +++ b/src/FiltersPlugin/filter-FaceSize.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/FiltersPlugin/filter-FeatureEdges.xml b/src/FiltersPlugin/filter-FeatureEdges.xml new file mode 100644 index 000000000..a96933f01 --- /dev/null +++ b/src/FiltersPlugin/filter-FeatureEdges.xml @@ -0,0 +1,9 @@ + + + + diff --git a/src/FiltersPlugin/filter-VolumeSize.xml b/src/FiltersPlugin/filter-VolumeSize.xml new file mode 100644 index 000000000..a09a03056 --- /dev/null +++ b/src/FiltersPlugin/filter-VolumeSize.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + to"> + + + + + + + + + + + + + + + + + + + + + diff --git a/src/FiltersPlugin/tests.set b/src/FiltersPlugin/tests.set index 722601d08..98aa0c340 100644 --- a/src/FiltersPlugin/tests.set +++ b/src/FiltersPlugin/tests.set @@ -118,4 +118,9 @@ SET(TEST_NAMES Test17924.py Test17962.py Test19190.py + TestFilter_FaceSize.py + TestFilter_EdgeSize.py + TestFilter_FeatureEdges.py + TestFilter_ContinuousFaces.py + TestFilter_VolumeSize.py ) diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp index 037bacbc4..1d07bf469 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp @@ -100,6 +100,8 @@ #include +#include + //================================================================================================== static GProp_GProps props(const TopoDS_Shape& theShape) { @@ -133,11 +135,11 @@ static GProp_GProps props(const TopoDS_Shape& theShape) double GeomAlgoAPI_ShapeTools::length(const std::shared_ptr theShape) { GProp_GProps aGProps; - if(!theShape.get()) { + if (!theShape.get()) { return 0.0; } const TopoDS_Shape& aShape = theShape->impl(); - if(aShape.IsNull()) { + if (aShape.IsNull()) { return 0.0; } @@ -148,11 +150,11 @@ double GeomAlgoAPI_ShapeTools::length(const std::shared_ptr theSh //================================================================================================== double GeomAlgoAPI_ShapeTools::volume(const std::shared_ptr theShape) { - if(!theShape.get()) { + if (!theShape.get()) { return 0.0; } const TopoDS_Shape& aShape = theShape->impl(); - if(aShape.IsNull()) { + if (aShape.IsNull()) { return 0.0; } const Standard_Real anEps = 1.e-6; @@ -169,11 +171,11 @@ double GeomAlgoAPI_ShapeTools::volume(const std::shared_ptr theSh double GeomAlgoAPI_ShapeTools::area (const std::shared_ptr theShape) { GProp_GProps aGProps; - if(!theShape.get()) { + if (!theShape.get()) { return 0.0; } const TopoDS_Shape& aShape = theShape->impl(); - if(aShape.IsNull()) { + if (aShape.IsNull()) { return 0.0; } const Standard_Real anEps = 1.e-6; @@ -182,20 +184,119 @@ double GeomAlgoAPI_ShapeTools::area (const std::shared_ptr theSha return aGProps.Mass(); } +//================================================================================================== +bool GeomAlgoAPI_ShapeTools::isContinuousFaces(const GeomShapePtr& theFace1, + const GeomShapePtr& theFace2, + const GeomPointPtr& thePoint, + const double & theAngle, + std::string& theError) +{ + + #ifdef _DEBUG + std::cout << "isContinuousFaces " << std::endl; + #endif + + if (!thePoint.get()) { + theError = "isContinuousFaces : An invalid argument"; + return false; + } + const gp_Pnt& aPoint = thePoint->impl(); + + // Getting base shape. + if (!theFace1.get()) { + theError = "isContinuousFaces : An invalid argument"; + return false; + } + + TopoDS_Shape aShape1 = theFace1->impl(); + + if (aShape1.IsNull()) { + theError = "isContinuousFaces : An invalid argument"; + return false; + } + + // Getting base shape. + if (!theFace2.get()) { + theError = "isContinuousFaces : An invalid argument"; + return false; + } + + TopoDS_Shape aShape2 = theFace2->impl(); + + if (aShape2.IsNull()) { + theError = "isContinuousFaces : An invalid argument"; + return false; + } + + TopoDS_Face aFace1 = TopoDS::Face(aShape1); + if (aFace1.IsNull()) { + theError = "isContinuousFaces : An invalid argument"; + return false; + } + + Handle(Geom_Surface) aSurf1 = BRep_Tool::Surface(aFace1); + if (aSurf1.IsNull()) { + theError = "isContinuousFaces : An invalid surface"; + return false; + } + + ShapeAnalysis_Surface aSAS1(aSurf1); + gp_Pnt2d aPointOnFace1 = aSAS1.ValueOfUV(aPoint, Precision::Confusion()); + + TopoDS_Face aFace2 = TopoDS::Face(aShape2); + if (aFace2.IsNull()) { + theError = "isContinuousFaces : An invalid argument"; + return false; + } + + Handle(Geom_Surface) aSurf2 = BRep_Tool::Surface(aFace2); + if (aSurf2.IsNull()) { + theError = "isContinuousFaces : An invalid surface"; + return false; + } + + ShapeAnalysis_Surface aSAS2(aSurf2); + gp_Pnt2d aPointOnFace2= aSAS2.ValueOfUV(aPoint, Precision::Confusion()); + + bool aRes = false; + try { + OCC_CATCH_SIGNALS; + LocalAnalysis_SurfaceContinuity aLocAnal(aSurf1, + aPointOnFace1.X(), + aPointOnFace1.Y(), + aSurf2, + aPointOnFace2.X(), + aPointOnFace2.Y(), + GeomAbs_Shape::GeomAbs_G1, // Order + 0.001, // EpsNul + 0.001, // EpsC0 + 0.001, // EpsC1 + 0.001, // EpsC2 + theAngle * M_PI / 180.0); //EpsG1 + aRes = aLocAnal.IsG1(); + } + catch (Standard_Failure const& anException) { + theError = "LocalAnalysis_SurfaceContinuity error :"; + theError += anException.GetMessageString(); + } + + return aRes; +} + //================================================================================================== std::shared_ptr GeomAlgoAPI_ShapeTools::centreOfMass(const std::shared_ptr theShape) { GProp_GProps aGProps; - if(!theShape) { + if (!theShape) { return std::shared_ptr(); } const TopoDS_Shape& aShape = theShape->impl(); - if(aShape.IsNull()) { + if (aShape.IsNull()) { return std::shared_ptr(); } gp_Pnt aCentre; - if(aShape.ShapeType() == TopAbs_VERTEX) { + if (aShape.ShapeType() == TopAbs_VERTEX) { aCentre = BRep_Tool::Pnt(TopoDS::Vertex(aShape)); } else { aGProps = props(aShape); @@ -266,17 +367,17 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( GeomShapePtr aResult = theCompound; - if(!theCompound.get()) { + if (!theCompound.get()) { return aResult; } - if(theType != GeomAPI_Shape::SHELL && theType != GeomAPI_Shape::COMPSOLID) { + if (theType != GeomAPI_Shape::SHELL && theType != GeomAPI_Shape::COMPSOLID) { return aResult; } TopAbs_ShapeEnum aTS = TopAbs_EDGE; TopAbs_ShapeEnum aTA = TopAbs_FACE; - if(theType == GeomAPI_Shape::COMPSOLID) { + if (theType == GeomAPI_Shape::COMPSOLID) { aTS = TopAbs_FACE; aTA = TopAbs_SOLID; } @@ -289,15 +390,15 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( // Get free shapes. int anOrder = 0; const TopoDS_Shape& aShapesComp = theCompound->impl(); - for(TopoDS_Iterator anIter(aShapesComp); anIter.More(); anIter.Next(), anOrder++) { + for (TopoDS_Iterator anIter(aShapesComp); anIter.More(); anIter.Next(), anOrder++) { const TopoDS_Shape& aShape = anIter.Value(); - if(aShape.ShapeType() > aTA) { + if (aShape.ShapeType() > aTA) { std::shared_ptr aGeomShape(new GeomAPI_Shape); aGeomShape->setImpl(new TopoDS_Shape(aShape)); aResFreeShapes.push_back(aGeomShape); anInputOrder[aGeomShape] = anOrder; } else { - for(TopExp_Explorer anExp(aShape, aTA); anExp.More(); anExp.Next()) { + for (TopExp_Explorer anExp(aShape, aTA); anExp.More(); anExp.Next()) { anAncestorsOrder.Bind(anExp.Current(), anOrder); } } @@ -306,7 +407,7 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( // Map sub-shapes and shapes. TopTools_IndexedDataMapOfShapeListOfShape aMapSA; TopExp::MapShapesAndAncestors(aShapesComp, aTS, aTA, aMapSA); - if(aMapSA.IsEmpty()) { + if (aMapSA.IsEmpty()) { return aResult; } theResuts.clear(); @@ -314,13 +415,13 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( // Get all shapes with common sub-shapes and free shapes. NCollection_Map aFreeShapes; NCollection_Vector> aShapesWithCommonSubshapes; - for(TopTools_IndexedDataMapOfShapeListOfShape::Iterator + for (TopTools_IndexedDataMapOfShapeListOfShape::Iterator anIter(aMapSA); anIter.More(); anIter.Next()) { TopTools_ListOfShape& aListOfShape = anIter.ChangeValue(); - if(aListOfShape.IsEmpty()) { + if (aListOfShape.IsEmpty()) { continue; } - else if(aListOfShape.Size() == 1) { + else if (aListOfShape.Size() == 1) { const TopoDS_Shape& aF = aListOfShape.First(); aFreeShapes.Add(aF); aListOfShape.Clear(); @@ -333,17 +434,17 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( aFreeShapes.Remove(aListIt.Value()); } aListOfShape.Clear(); - for(NCollection_List::Iterator + for (NCollection_List::Iterator aTempIter(aTempList); aTempIter.More(); aTempIter.Next()) { const TopoDS_Shape& aTempShape = aTempIter.Value(); - for(TopTools_IndexedDataMapOfShapeListOfShape::Iterator + for (TopTools_IndexedDataMapOfShapeListOfShape::Iterator anIter2(aMapSA); anIter2.More(); anIter2.Next()) { TopTools_ListOfShape& aTempListOfShape = anIter2.ChangeValue(); - if(aTempListOfShape.IsEmpty()) { + if (aTempListOfShape.IsEmpty()) { continue; - } else if(aTempListOfShape.Size() == 1 && aTempListOfShape.First() == aTempShape) { + } else if (aTempListOfShape.Size() == 1 && aTempListOfShape.First() == aTempShape) { aTempListOfShape.Clear(); - } else if(aTempListOfShape.Size() > 1) { + } else if (aTempListOfShape.Size() > 1) { TopTools_ListOfShape::Iterator anIt1(aTempListOfShape); for (; anIt1.More(); anIt1.Next()) { if (anIt1.Value() == aTempShape) { @@ -369,7 +470,7 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( } // Combine shapes with common sub-shapes. - for(NCollection_Vector>::Iterator + for (NCollection_Vector>::Iterator anIter(aShapesWithCommonSubshapes); anIter.More(); anIter.Next()) { TopoDS_Shell aShell; TopoDS_CompSolid aCSolid; @@ -378,9 +479,9 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( theType == GeomAPI_Shape::COMPSOLID ? aBuilder.MakeCompSolid(aCSolid) : aBuilder.MakeShell(aShell); NCollection_Map& aShapesMap = anIter.ChangeValue(); - for(TopExp_Explorer anExp(aShapesComp, aTA); anExp.More(); anExp.Next()) { + for (TopExp_Explorer anExp(aShapesComp, aTA); anExp.More(); anExp.Next()) { const TopoDS_Shape& aShape = anExp.Current(); - if(aShapesMap.Contains(aShape)) { + if (aShapesMap.Contains(aShape)) { theType == GeomAPI_Shape::COMPSOLID ? aBuilder.Add(aCSolid, aShape) : aBuilder.Add(aShell, aShape); aShapesMap.Remove(aShape); @@ -398,9 +499,9 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( } // Adding free shapes. - for(TopExp_Explorer anExp(aShapesComp, aTA); anExp.More(); anExp.Next()) { + for (TopExp_Explorer anExp(aShapesComp, aTA); anExp.More(); anExp.Next()) { const TopoDS_Shape& aShape = anExp.Current(); - if(aFreeShapes.Contains(aShape)) { + if (aFreeShapes.Contains(aShape)) { std::shared_ptr aGeomShape(new GeomAPI_Shape); aGeomShape->setImpl(new TopoDS_Shape(aShape)); aResFreeShapes.push_back(aGeomShape); @@ -408,10 +509,10 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( } } - if(aResCombinedShapes.size() == 1 && aResFreeShapes.size() == 0) { + if (aResCombinedShapes.size() == 1 && aResFreeShapes.size() == 0) { aResult = aResCombinedShapes.front(); theResuts.push_back(aResult); - } else if(aResCombinedShapes.size() == 0 && aResFreeShapes.size() == 1) { + } else if (aResCombinedShapes.size() == 0 && aResFreeShapes.size() == 1) { aResult = aResFreeShapes.front(); theResuts.push_back(aResult); } else { @@ -421,10 +522,10 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( // put to result compound and result list in accordance to the order numbers std::map::iterator anInputIter = anInputOrder.begin(); std::map aNums; - for(; anInputIter != anInputOrder.end(); anInputIter++) + for (; anInputIter != anInputOrder.end(); anInputIter++) aNums[anInputIter->second] = anInputIter->first; std::map::iterator aNumsIter = aNums.begin(); - for(; aNumsIter != aNums.end(); aNumsIter++) { + for (; aNumsIter != aNums.end(); aNumsIter++) { aBuilder.Add(aResultComp, (aNumsIter->second)->impl()); theResuts.push_back(aNumsIter->second); } @@ -438,12 +539,12 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( static void addSimpleShapeToList(const TopoDS_Shape& theShape, NCollection_List& theList) { - if(theShape.IsNull()) { + if (theShape.IsNull()) { return; } - if(theShape.ShapeType() == TopAbs_COMPOUND) { - for(TopoDS_Iterator anIt(theShape); anIt.More(); anIt.Next()) { + if (theShape.ShapeType() == TopAbs_COMPOUND) { + for (TopoDS_Iterator anIt(theShape); anIt.More(); anIt.Next()) { addSimpleShapeToList(anIt.Value(), theList); } } else { @@ -459,7 +560,7 @@ static TopoDS_Compound makeCompound(const NCollection_List theShap BRep_Builder aBuilder; aBuilder.MakeCompound(aCompound); - for(NCollection_List::Iterator anIt(theShapes); anIt.More(); anIt.Next()) { + for (NCollection_List::Iterator anIt(theShapes); anIt.More(); anIt.Next()) { aBuilder.Add(aCompound, anIt.Value()); } @@ -570,7 +671,7 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::groupSharedTopology( for (NCollection_Vector::Iterator anIt(aGroups); anIt.More(); anIt.Next()) { const TopTools_MapOfShape& aGroup = anIt.ChangeValue(); GeomShapePtr aGeomShape(new GeomAPI_Shape()); - if(aGroup.Size() == 1) { + if (aGroup.Size() == 1) { TopTools_MapOfShape::Iterator aOneShapeIter(aGroup); aGeomShape->setImpl(new TopoDS_Shape(aOneShapeIter.Value())); } else { @@ -589,7 +690,7 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::groupSharedTopology( aBuilder.Add(aCompound, aGeomShape->impl()); } - if(!aCompound.IsNull()) { + if (!aCompound.IsNull()) { aResult->setImpl(new TopoDS_Shape(aCompound)); } @@ -628,7 +729,7 @@ std::list > BRepBndLib::Add(aShape, aBndBox); } - if(theEnlarge != 0.0) { + if (theEnlarge != 0.0) { // We enlarge bounding box just to be sure that plane will be large enough to cut all objects. aBndBox.Enlarge(theEnlarge); } @@ -637,9 +738,9 @@ std::list > Standard_Real aYArr[2] = {aBndBox.CornerMin().Y(), aBndBox.CornerMax().Y()}; Standard_Real aZArr[2] = {aBndBox.CornerMin().Z(), aBndBox.CornerMax().Z()}; std::list > aResultPoints; - for(int i = 0; i < 2; i++) { - for(int j = 0; j < 2; j++) { - for(int k = 0; k < 2; k++) { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { std::shared_ptr aPnt(new GeomAPI_Pnt(aXArr[i], aYArr[j], aZArr[k])); aResultPoints.push_back(aPnt); } @@ -656,27 +757,27 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::fitPlaneToBox( { std::shared_ptr aResultFace; - if(!thePlane.get()) { + if (!thePlane.get()) { return aResultFace; } const TopoDS_Shape& aShape = thePlane->impl(); - if(aShape.ShapeType() != TopAbs_FACE) { + if (aShape.ShapeType() != TopAbs_FACE) { return aResultFace; } TopoDS_Face aFace = TopoDS::Face(aShape); Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace); - if(aSurf.IsNull()) { + if (aSurf.IsNull()) { return aResultFace; } GeomLib_IsPlanarSurface isPlanar(aSurf); - if(!isPlanar.IsPlanar()) { + if (!isPlanar.IsPlanar()) { return aResultFace; } - if(thePoints.size() != 8) { + if (thePoints.size() != 8) { return aResultFace; } @@ -693,10 +794,10 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::fitPlaneToBox( const gp_Pnt& aPntOnFace = anIntAna.Point(1); Standard_Real aPntU(0), aPntV(0); GeomLib_Tool::Parameters(aFacePlane, aPntOnFace, Precision::Confusion(), aPntU, aPntV); - if(aPntU < UMin) UMin = aPntU; - if(aPntU > UMax) UMax = aPntU; - if(aPntV < VMin) VMin = aPntV; - if(aPntV > VMax) VMax = aPntV; + if (aPntU < UMin) UMin = aPntU; + if (aPntU > UMax) UMax = aPntU; + if (aPntV < VMin) VMin = aPntV; + if (aPntV > VMax) VMax = aPntV; } aResultFace.reset(new GeomAPI_Face()); aResultFace->setImpl(new TopoDS_Face(BRepLib_MakeFace(aFacePln, UMin, UMax, VMin, VMax).Face())); @@ -743,7 +844,7 @@ void GeomAlgoAPI_ShapeTools::makeFacesWithHoles(const std::shared_ptrimpl()); @@ -752,11 +853,11 @@ void GeomAlgoAPI_ShapeTools::makeFacesWithHoles(const std::shared_ptrsetImpl(new TopoDS_Shape(aFRestrictor.Current())); theFaces.push_back(aShape); @@ -770,12 +871,12 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::findPlane(const ListOfShape BRep_Builder aBuilder; aBuilder.MakeCompound(aCompound); - for(ListOfShape::const_iterator anIt = theShapes.cbegin(); anIt != theShapes.cend(); ++anIt) { + for (ListOfShape::const_iterator anIt = theShapes.cbegin(); anIt != theShapes.cend(); ++anIt) { aBuilder.Add(aCompound, (*anIt)->impl()); } BRepBuilderAPI_FindPlane aFindPlane(aCompound); - if(aFindPlane.Found() != Standard_True) { + if (aFindPlane.Found() != Standard_True) { return std::shared_ptr(); } @@ -796,28 +897,28 @@ bool GeomAlgoAPI_ShapeTools::isSubShapeInsideShape( const std::shared_ptr theSubShape, const std::shared_ptr theBaseShape) { - if(!theSubShape.get() || !theBaseShape.get()) { + if (!theSubShape.get() || !theBaseShape.get()) { return false; } const TopoDS_Shape& aSubShape = theSubShape->impl(); const TopoDS_Shape& aBaseShape = theBaseShape->impl(); - if(aSubShape.ShapeType() == TopAbs_VERTEX) { + if (aSubShape.ShapeType() == TopAbs_VERTEX) { // If sub-shape is a vertex check distance to shape. If it is <= Precision::Confusion() then OK. BRepExtrema_DistShapeShape aDist(aBaseShape, aSubShape); aDist.Perform(); - if(!aDist.IsDone() || aDist.Value() > Precision::Confusion()) { + if (!aDist.IsDone() || aDist.Value() > Precision::Confusion()) { return false; } } else if (aSubShape.ShapeType() == TopAbs_EDGE) { - if(aBaseShape.ShapeType() == TopAbs_FACE) { + if (aBaseShape.ShapeType() == TopAbs_FACE) { // Check that edge is on face surface. TopoDS_Face aFace = TopoDS::Face(aBaseShape); TopoDS_Edge anEdge = TopoDS::Edge(aSubShape); BRepLib_CheckCurveOnSurface aCheck(anEdge, aFace); aCheck.Perform(); - if(!aCheck.IsDone() || aCheck.MaxDistance() > Precision::Confusion()) { + if (!aCheck.IsDone() || aCheck.MaxDistance() > Precision::Confusion()) { return false; } @@ -826,15 +927,15 @@ bool GeomAlgoAPI_ShapeTools::isSubShapeInsideShape( ShapeAnalysis::FindBounds(anEdge, aV1, aV2); gp_Pnt aPnt1 = BRep_Tool::Pnt(aV1); gp_Pnt aPnt2 = BRep_Tool::Pnt(aV2); - for(TopExp_Explorer anExp(aBaseShape, TopAbs_EDGE); anExp.More(); anExp.Next()) { + for (TopExp_Explorer anExp(aBaseShape, TopAbs_EDGE); anExp.More(); anExp.Next()) { const TopoDS_Shape& anEdgeOnFace = anExp.Current(); BRepExtrema_DistShapeShape aDist(anEdgeOnFace, anEdge); aDist.Perform(); - if(aDist.IsDone() && aDist.Value() <= Precision::Confusion()) { + if (aDist.IsDone() && aDist.Value() <= Precision::Confusion()) { // Edge intersect face bound. Check that it is not on edge begin or end. - for(Standard_Integer anIndex = 1; anIndex <= aDist.NbSolution(); ++anIndex) { + for (Standard_Integer anIndex = 1; anIndex <= aDist.NbSolution(); ++anIndex) { gp_Pnt aPntOnSubShape = aDist.PointOnShape2(anIndex); - if(aPntOnSubShape.Distance(aPnt1) > Precision::Confusion() + if (aPntOnSubShape.Distance(aPnt1) > Precision::Confusion() && aPntOnSubShape.Distance(aPnt2) > Precision::Confusion()) { return false; } @@ -851,7 +952,7 @@ bool GeomAlgoAPI_ShapeTools::isSubShapeInsideShape( ShapeAnalysis_Surface aSAS(aSurface); gp_Pnt2d aPointOnFace = aSAS.ValueOfUV(aPointToCheck, Precision::Confusion()); BRepTopAdaptor_FClass2d aFClass2d(aFace, Precision::Confusion()); - if(aFClass2d.Perform(aPointOnFace) == TopAbs_OUT) { + if (aFClass2d.Perform(aPointOnFace) == TopAbs_OUT) { return false; } @@ -868,7 +969,7 @@ bool GeomAlgoAPI_ShapeTools::isSubShapeInsideShape( //================================================================================================== bool GeomAlgoAPI_ShapeTools::isShapeValid(const std::shared_ptr theShape) { - if(!theShape.get()) { + if (!theShape.get()) { return false; } @@ -882,7 +983,7 @@ std::shared_ptr { GeomShapePtr anOuterWire; - if(!theFace.get() || !theFace->isFace()) { + if (!theFace.get() || !theFace->isFace()) { return anOuterWire; } @@ -937,7 +1038,7 @@ bool GeomAlgoAPI_ShapeTools::isTangent(const std::shared_ptr theEd bool GeomAlgoAPI_ShapeTools::isParallel(const std::shared_ptr theEdge, const std::shared_ptr theFace) { - if(!theEdge.get() || !theFace.get()) { + if (!theEdge.get() || !theFace.get()) { return false; } @@ -953,7 +1054,7 @@ std::list > GeomAlgoAPI_ShapeTools::intersect( const std::shared_ptr theEdge, const std::shared_ptr theFace) { std::list > aResult; - if(!theEdge.get() || !theFace.get()) { + if (!theEdge.get() || !theFace.get()) { return aResult; } @@ -968,7 +1069,7 @@ std::list > GeomAlgoAPI_ShapeTools::intersect( if (!anIntAlgo.IsDone()) return aResult; // searching for points-intersection - for(int anIntNum = 1; anIntNum <= anIntAlgo.NbPoints() + anIntAlgo.NbSegments(); anIntNum++) { + for (int anIntNum = 1; anIntNum <= anIntAlgo.NbPoints() + anIntAlgo.NbSegments(); anIntNum++) { gp_Pnt anInt; if (anIntNum <= anIntAlgo.NbPoints()) { anInt = anIntAlgo.Point(anIntNum); @@ -1331,12 +1432,12 @@ void GeomAlgoAPI_ShapeTools::computeThroughAll(const ListOfShape& theObjects, TopExp_Explorer anExp(aBaseShape, TopAbs_FACE); const TopoDS_Shape& aFace = anExp.Current(); Handle(Geom_Surface) aSurface = BRep_Tool::Surface(TopoDS::Face(aFace)); - if(aSurface->DynamicType() == STANDARD_TYPE(Geom_RectangularTrimmedSurface)) { + if (aSurface->DynamicType() == STANDARD_TYPE(Geom_RectangularTrimmedSurface)) { Handle(Geom_RectangularTrimmedSurface) aTrimSurface = Handle(Geom_RectangularTrimmedSurface)::DownCast(aSurface); aSurface = aTrimSurface->BasisSurface(); } - if(aSurface->DynamicType() != STANDARD_TYPE(Geom_Plane)) { + if (aSurface->DynamicType() != STANDARD_TYPE(Geom_Plane)) { return; } aPlane = Handle(Geom_Plane)::DownCast(aSurface); diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h index b1f154948..5f511d73c 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #ifdef WIN32 @@ -56,6 +57,19 @@ public: /// \return the total area of the faces of the current shape or 0.0 if it can be computed. GEOMALGOAPI_EXPORT static double area(const std::shared_ptr theShape); + /// indicate if two faces are continuous + /// with an angular tolerance used for G1 continuity to compare the angle between the normals + /// \param theFace1 the first face + /// \param theFace2 the second face + /// \param thePoint the point for the normal + /// \param theAngle the angular tolerance + /// \param theError error + GEOMALGOAPI_EXPORT static bool isContinuousFaces(const GeomShapePtr& theFace1, + const GeomShapePtr& theFace2, + const GeomPointPtr& thePoint, + const double & theAngle, + std::string& theError); + /// \return the center of mass of the current face. /// The coordinates returned for the center of mass /// are expressed in the absolute Cartesian coordinate system. diff --git a/src/GeomValidators/GeomValidators_Positive.cpp b/src/GeomValidators/GeomValidators_Positive.cpp index 89cc9a263..4d5527eba 100644 --- a/src/GeomValidators/GeomValidators_Positive.cpp +++ b/src/GeomValidators/GeomValidators_Positive.cpp @@ -44,11 +44,11 @@ bool GeomValidators_Positive::isValid(const AttributePtr& theAttribute, const std::list& theArguments, Events_InfoMessage& theError) const { - double aMinValue = 1.e-5; - if(theArguments.size() == 1) { + double aMinValue = 1.e-12; + if (theArguments.size() == 1) { std::list::const_iterator anIt = theArguments.begin(); double aValue = Config_PropManager::stringToDouble((*anIt).c_str()); - if(aValue != 0) { + if (aValue != 0) { // very probably ok aMinValue = aValue; } @@ -79,6 +79,5 @@ bool GeomValidators_Positive::isValid(const AttributePtr& theAttribute, return false; } } - return true; } diff --git a/src/ModuleBase/ModuleBase_WidgetSelectionFilter.cpp b/src/ModuleBase/ModuleBase_WidgetSelectionFilter.cpp index 2c10b1adb..acfc5c633 100644 --- a/src/ModuleBase/ModuleBase_WidgetSelectionFilter.cpp +++ b/src/ModuleBase/ModuleBase_WidgetSelectionFilter.cpp @@ -180,6 +180,7 @@ ModuleBase_FilterItem::ModuleBase_FilterItem( connect(aWidget, SIGNAL(focusOutWidget(ModuleBase_ModelWidget*)), theParent, SIGNAL(focusOutWidget(ModuleBase_ModelWidget*))); connect(aWidget, SIGNAL(objectUpdated()), theParent, SLOT(onObjectUpdated())); + aWidget->enableFocusProcessing(); } aLayout->addWidget(aParamsWgt); }