From 74b81005e210f0269c129547d17cd3c13807a645 Mon Sep 17 00:00:00 2001 From: azv Date: Thu, 21 Dec 2017 08:55:42 +0300 Subject: [PATCH] Filling operation: create a face from a set of edges/wires 1. Filling feature implementation 2. Python API for the Filling feature 3. Python dump 4. Unit tests --- src/BuildAPI/BuildAPI.i | 2 + src/BuildAPI/BuildAPI_Filling.cpp | 180 ++++++++++++++ src/BuildAPI/BuildAPI_Filling.h | 141 +++++++++++ src/BuildAPI/BuildAPI_swig.h | 1 + src/BuildAPI/CMakeLists.txt | 2 + src/BuildPlugin/BuildPlugin_Filling.cpp | 232 ++++++++++++++++++ src/BuildPlugin/BuildPlugin_Filling.h | 182 ++++++++++++++ src/BuildPlugin/BuildPlugin_Plugin.cpp | 5 +- src/BuildPlugin/CMakeLists.txt | 6 + src/BuildPlugin/Test/TestFilling_ByEdges.py | 154 ++++++++++++ src/BuildPlugin/Test/TestFilling_ByWires.py | 152 ++++++++++++ src/BuildPlugin/Test/TestFilling_Mixed.py | 168 +++++++++++++ src/BuildPlugin/filling_widget.xml | 47 ++++ src/BuildPlugin/icons/feature_filling.png | Bin 0 -> 592 bytes src/BuildPlugin/plugin-Build.xml | 7 +- src/GeomAPI/GeomAPI_Shape.cpp | 11 + src/GeomAPI/GeomAPI_Shape.h | 7 + src/GeomAPI/GeomAPI_Wire.cpp | 8 + src/GeomAPI/GeomAPI_Wire.h | 6 +- src/GeomAlgoAPI/CMakeLists.txt | 2 + src/GeomAlgoAPI/GeomAlgoAPI_Filling.cpp | 191 ++++++++++++++ src/GeomAlgoAPI/GeomAlgoAPI_Filling.h | 65 +++++ src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp | 16 ++ src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h | 5 + src/GeomValidators/CMakeLists.txt | 2 + .../GeomValidators_ValueOrder.cpp | 132 ++++++++++ .../GeomValidators_ValueOrder.h | 63 +++++ src/ModelHighAPI/ModelHighAPI_Dumper.cpp | 66 +++-- src/PythonAPI/model/build/__init__.py | 1 + 29 files changed, 1828 insertions(+), 26 deletions(-) create mode 100644 src/BuildAPI/BuildAPI_Filling.cpp create mode 100644 src/BuildAPI/BuildAPI_Filling.h create mode 100644 src/BuildPlugin/BuildPlugin_Filling.cpp create mode 100644 src/BuildPlugin/BuildPlugin_Filling.h create mode 100644 src/BuildPlugin/Test/TestFilling_ByEdges.py create mode 100644 src/BuildPlugin/Test/TestFilling_ByWires.py create mode 100644 src/BuildPlugin/Test/TestFilling_Mixed.py create mode 100644 src/BuildPlugin/filling_widget.xml create mode 100644 src/BuildPlugin/icons/feature_filling.png create mode 100644 src/GeomAlgoAPI/GeomAlgoAPI_Filling.cpp create mode 100644 src/GeomAlgoAPI/GeomAlgoAPI_Filling.h create mode 100644 src/GeomValidators/GeomValidators_ValueOrder.cpp create mode 100644 src/GeomValidators/GeomValidators_ValueOrder.h diff --git a/src/BuildAPI/BuildAPI.i b/src/BuildAPI/BuildAPI.i index 5156e2742..8977df7ce 100644 --- a/src/BuildAPI/BuildAPI.i +++ b/src/BuildAPI/BuildAPI.i @@ -41,6 +41,7 @@ // shared pointers %shared_ptr(BuildAPI_Edge) %shared_ptr(BuildAPI_Face) +%shared_ptr(BuildAPI_Filling) %shared_ptr(BuildAPI_Shell) %shared_ptr(BuildAPI_SubShapes) %shared_ptr(BuildAPI_Vertex) @@ -49,6 +50,7 @@ // all supported interfaces %include "BuildAPI_Edge.h" %include "BuildAPI_Face.h" +%include "BuildAPI_Filling.h" %include "BuildAPI_Shell.h" %include "BuildAPI_SubShapes.h" %include "BuildAPI_Vertex.h" diff --git a/src/BuildAPI/BuildAPI_Filling.cpp b/src/BuildAPI/BuildAPI_Filling.cpp new file mode 100644 index 000000000..8951bd8d4 --- /dev/null +++ b/src/BuildAPI/BuildAPI_Filling.cpp @@ -0,0 +1,180 @@ +// Copyright (C) 2017-20xx 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 "BuildAPI_Filling.h" + +#include +#include + +BuildAPI_Filling::BuildAPI_Filling(const std::shared_ptr& theFeature) + : ModelHighAPI_Interface(theFeature) +{ + initialize(); +} + +BuildAPI_Filling::BuildAPI_Filling(const std::shared_ptr& theFeature, + const std::list& theBaseObjects) + : ModelHighAPI_Interface(theFeature) +{ + if(initialize()) + setBase(theBaseObjects); +} + +BuildAPI_Filling::BuildAPI_Filling(const std::shared_ptr& theFeature, + const std::list& theBaseObjects, + const std::string& theOrientCorrection, + const int theMinDegree, + const int theMaxDegree, + const int theNbIter, + const double theTolerance2D, + const double theTolerance3D, + const bool theApproximate) + : ModelHighAPI_Interface(theFeature) +{ + if(initialize()) { + setOrientationMethod(theOrientCorrection); + setMinDegree(theMinDegree); + setMaxDegree(theMaxDegree); + setNbIterations(theNbIter); + setTolerance2d(theTolerance2D); + setTolerance3d(theTolerance3D); + setApproximation(theApproximate); + setAdvancedOptions(); + setBase(theBaseObjects); + } +} + +BuildAPI_Filling::~BuildAPI_Filling() +{ +} + +void BuildAPI_Filling::execIfBaseNotEmpty() +{ + if (baseObjects()->size() > 0) + execute(); +} + +void BuildAPI_Filling::setBase(const std::list& theBaseObjects) +{ + fillAttribute(theBaseObjects, mybaseObjects); + execIfBaseNotEmpty(); +} + +void BuildAPI_Filling::setOrientationMethod(const std::string& theMethod) +{ + fillAttribute(theMethod, myorientationMethod); + if (theMethod != BuildPlugin_Filling::METHOD_DEFAULT()) + setAdvancedOptions(); + execIfBaseNotEmpty(); +} + +void BuildAPI_Filling::setMinDegree(const int theMinDegree) +{ + fillAttribute(theMinDegree, myminDegree); + if (theMinDegree != BuildPlugin_Filling::MINIMAL_DEGREE_DEFAULT()) + setAdvancedOptions(); + execIfBaseNotEmpty(); +} + +void BuildAPI_Filling::setMaxDegree(const int theMaxDegree) +{ + fillAttribute(theMaxDegree, mymaxDegree); + if (theMaxDegree != BuildPlugin_Filling::MAXIMAL_DEGREE_DEFAULT()) + setAdvancedOptions(); + execIfBaseNotEmpty(); +} + +void BuildAPI_Filling::setNbIterations(const int theNbIter) +{ + fillAttribute(theNbIter, mynbIterations); + if (theNbIter != BuildPlugin_Filling::NUMBER_OF_ITERATIONS_DEFAULT()) + setAdvancedOptions(); + execIfBaseNotEmpty(); +} + +void BuildAPI_Filling::setTolerance2d(const double theTol2d) +{ + fillAttribute(theTol2d, mytolerance2d); + if (theTol2d != BuildPlugin_Filling::TOLERANCE_2D_DEFAULT()) + setAdvancedOptions(); + execIfBaseNotEmpty(); +} + +void BuildAPI_Filling::setTolerance3d(const double theTol3d) +{ + fillAttribute(theTol3d, mytolerance3d); + if (theTol3d != BuildPlugin_Filling::TOLERANCE_3D_DEFAULT()) + setAdvancedOptions(); + execIfBaseNotEmpty(); +} + +void BuildAPI_Filling::setApproximation(const bool theApproximate) +{ + fillAttribute(theApproximate, myapproximate); + if (theApproximate != BuildPlugin_Filling::APPROXIMATION_DEFAULT()) + setAdvancedOptions(); + execIfBaseNotEmpty(); +} + +void BuildAPI_Filling::setAdvancedOptions() +{ + feature()->string(BuildPlugin_Filling::ADVANCED_OPTIONS_ID())->setValue("true"); +} + +void BuildAPI_Filling::dump(ModelHighAPI_Dumper& theDumper) const +{ + FeaturePtr aBase = feature(); + std::string aPartName = theDumper.name(aBase->document()); + + theDumper << aBase << " = model.addFilling(" << aPartName << ", " << baseObjects(); + + if (!aBase->string(BuildPlugin_Filling::ADVANCED_OPTIONS_ID())->value().empty()) { + // dump options too, + theDumper << ", " << orientationMethod() + << ", " << minDegree() << ", " << maxDegree() << ", " << nbIterations() + << ", " << tolerance2d() << ", " << tolerance3d() + << ", " << approximate(); + } + theDumper << ")" << std::endl; +} + +//================================================================================================== + +FillingPtr addFilling(const std::shared_ptr& thePart, + const std::list& theBaseObjects) +{ + std::shared_ptr aFeature = thePart->addFeature(BuildAPI_Filling::ID()); + return FillingPtr(new BuildAPI_Filling(aFeature, theBaseObjects)); +} + +FillingPtr addFilling(const std::shared_ptr& thePart, + const std::list& theBaseObjects, + const std::string& theOrientCorrection, + const int theMinDegree, + const int theMaxDegree, + const int theNbIter, + const double theTolerance2D, + const double theTolerance3D, + const bool theApproximate) +{ + std::shared_ptr aFeature = thePart->addFeature(BuildAPI_Filling::ID()); + return FillingPtr(new BuildAPI_Filling(aFeature, theBaseObjects, theOrientCorrection, + theMinDegree, theMaxDegree, theNbIter, theTolerance2D, theTolerance3D, theApproximate)); +} diff --git a/src/BuildAPI/BuildAPI_Filling.h b/src/BuildAPI/BuildAPI_Filling.h new file mode 100644 index 000000000..8ccab1fee --- /dev/null +++ b/src/BuildAPI/BuildAPI_Filling.h @@ -0,0 +1,141 @@ +// Copyright (C) 2017-20xx 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 BuildAPI_Filling_H_ +#define BuildAPI_Filling_H_ + +#include "BuildAPI.h" + +#include + +#include +#include + +class ModelHighAPI_Selection; + +/// \class BuildAPI_Filling +/// \ingroup CPPHighAPI +/// \brief Interface for Filling feature. +class BuildAPI_Filling : public ModelHighAPI_Interface +{ +public: + /// Constructor without values. + BUILDAPI_EXPORT explicit BuildAPI_Filling(const std::shared_ptr& theFeature); + + /// Constructor with base objects. + BUILDAPI_EXPORT + explicit BuildAPI_Filling(const std::shared_ptr& theFeature, + const std::list& theBaseObjects); + + /// Constructor with values. + BUILDAPI_EXPORT + explicit BuildAPI_Filling(const std::shared_ptr& theFeature, + const std::list& theBaseObjects, + const std::string& theOrientCorrection, + const int theMinDegree, + const int theMaxDegree, + const int theNbIter, + const double theTolerance2D, + const double theTolerance3D, + const bool theApproximate); + + /// Destructor. + BUILDAPI_EXPORT virtual ~BuildAPI_Filling(); + + INTERFACE_8(BuildPlugin_Filling::ID(), + baseObjects, BuildPlugin_Filling::BASE_OBJECTS_ID(), + ModelAPI_AttributeSelectionList, + /** Base objects */, + orientationMethod, BuildPlugin_Filling::METHOD_ID(), + ModelAPI_AttributeString, + /** Method to keep edge orientaion */, + minDegree, BuildPlugin_Filling::MINIMAL_DEGREE_ID(), + ModelAPI_AttributeInteger, + /** Minimal degree */, + maxDegree, BuildPlugin_Filling::MAXIMAL_DEGREE_ID(), + ModelAPI_AttributeInteger, + /** Maximal degree */, + nbIterations, BuildPlugin_Filling::NUMBER_OF_ITERATIONS_ID(), + ModelAPI_AttributeInteger, + /** Number of iterations */, + tolerance2d, BuildPlugin_Filling::TOLERANCE_2D_ID(), + ModelAPI_AttributeDouble, + /** 2D tolerance */, + tolerance3d, BuildPlugin_Filling::TOLERANCE_3D_ID(), + ModelAPI_AttributeDouble, + /** 3D tolerance */, + approximate, BuildPlugin_Filling::APPROXIMATION_ID(), + ModelAPI_AttributeBoolean, + /** Approximate original edges */) + + /// Modify base attribute of the feature. + BUILDAPI_EXPORT void setBase(const std::list& theBaseObjects); + + /// Modify orientation method + BUILDAPI_EXPORT void setOrientationMethod(const std::string& theMethod); + + /// Modify minimal degree of result B-spline + BUILDAPI_EXPORT void setMinDegree(const int theMinDegree); + + /// Modify maximal degree of result B-spline + BUILDAPI_EXPORT void setMaxDegree(const int theMaxDegree); + + /// Modify number of iterations + BUILDAPI_EXPORT void setNbIterations(const int theNbIter); + + /// Set 2D tolerance + BUILDAPI_EXPORT void setTolerance2d(const double theTol2d); + + /// Set 3D tolerance + BUILDAPI_EXPORT void setTolerance3d(const double theTol3d); + + /// Set approximation flag + BUILDAPI_EXPORT void setApproximation(const bool theApproximate); + + /// Dump wrapped feature + BUILDAPI_EXPORT virtual void dump(ModelHighAPI_Dumper& theDumper) const; + +private: + void execIfBaseNotEmpty(); + void setAdvancedOptions(); +}; + +/// Pointer on Face object. +typedef std::shared_ptr FillingPtr; + +/// \ingroup CPPHighAPI +/// \brief Create Filling feature. +BUILDAPI_EXPORT FillingPtr addFilling(const std::shared_ptr& thePart, + const std::list& theBaseObjects); + +/// \ingroup CPPHighAPI +/// \brief Create Filling feature. +BUILDAPI_EXPORT FillingPtr addFilling( + const std::shared_ptr& thePart, + const std::list& theBaseObjects, + const std::string& theOrientCorrection, + const int theMinDegree = BuildPlugin_Filling::MINIMAL_DEGREE_DEFAULT(), + const int theMaxDegree = BuildPlugin_Filling::MAXIMAL_DEGREE_DEFAULT(), + const int theNbIter = BuildPlugin_Filling::NUMBER_OF_ITERATIONS_DEFAULT(), + const double theTolerance2D = BuildPlugin_Filling::TOLERANCE_2D_DEFAULT(), + const double theTolerance3D = BuildPlugin_Filling::TOLERANCE_3D_DEFAULT(), + const bool theApproximate = BuildPlugin_Filling::APPROXIMATION_DEFAULT()); + +#endif // BuildAPI_Filling_H_ diff --git a/src/BuildAPI/BuildAPI_swig.h b/src/BuildAPI/BuildAPI_swig.h index b0bddae3b..96ee58c0e 100644 --- a/src/BuildAPI/BuildAPI_swig.h +++ b/src/BuildAPI/BuildAPI_swig.h @@ -25,6 +25,7 @@ #include "BuildAPI_Edge.h" #include "BuildAPI_Face.h" + #include "BuildAPI_Filling.h" #include "BuildAPI_Shell.h" #include "BuildAPI_SubShapes.h" #include "BuildAPI_Vertex.h" diff --git a/src/BuildAPI/CMakeLists.txt b/src/BuildAPI/CMakeLists.txt index 3ffcf78cc..bf4951210 100644 --- a/src/BuildAPI/CMakeLists.txt +++ b/src/BuildAPI/CMakeLists.txt @@ -24,6 +24,7 @@ SET(PROJECT_HEADERS BuildAPI.h BuildAPI_Edge.h BuildAPI_Face.h + BuildAPI_Filling.h BuildAPI_Shell.h BuildAPI_SubShapes.h BuildAPI_Vertex.h @@ -33,6 +34,7 @@ SET(PROJECT_HEADERS SET(PROJECT_SOURCES BuildAPI_Edge.cpp BuildAPI_Face.cpp + BuildAPI_Filling.cpp BuildAPI_Shell.cpp BuildAPI_SubShapes.cpp BuildAPI_Vertex.cpp diff --git a/src/BuildPlugin/BuildPlugin_Filling.cpp b/src/BuildPlugin/BuildPlugin_Filling.cpp new file mode 100644 index 000000000..a5597f6e6 --- /dev/null +++ b/src/BuildPlugin/BuildPlugin_Filling.cpp @@ -0,0 +1,232 @@ +// Copyright (C) 2017-20xx 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 "BuildPlugin_Filling.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +struct FillingParameters +{ + std::string method; + int minDegree; + int maxDegree; + int nbIter; + double tol2D; + double tol3D; + bool isApprox; +}; + + +//================================================================================================= +BuildPlugin_Filling::BuildPlugin_Filling() +{ +} + +//================================================================================================= +void BuildPlugin_Filling::initAttributes() +{ + data()->addAttribute(BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId()); + data()->addAttribute(ADVANCED_OPTIONS_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(METHOD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(MINIMAL_DEGREE_ID(), ModelAPI_AttributeInteger::typeId()); + data()->addAttribute(MAXIMAL_DEGREE_ID(), ModelAPI_AttributeInteger::typeId()); + data()->addAttribute(NUMBER_OF_ITERATIONS_ID(), ModelAPI_AttributeInteger::typeId()); + data()->addAttribute(TOLERANCE_2D_ID(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(TOLERANCE_3D_ID(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(APPROXIMATION_ID(), ModelAPI_AttributeBoolean::typeId()); + + restoreDefaultParameters(); + string(ADVANCED_OPTIONS_ID())->setValue(""); +} + +//================================================================================================= +void BuildPlugin_Filling::execute() +{ + // get parameters of algorithm + FillingParameters aParameters; + aParameters.method = string(METHOD_ID())->value(); + aParameters.minDegree = integer(MINIMAL_DEGREE_ID())->value(); + aParameters.maxDegree = integer(MAXIMAL_DEGREE_ID())->value(); + aParameters.nbIter = integer(NUMBER_OF_ITERATIONS_ID())->value(); + aParameters.tol2D = real(TOLERANCE_2D_ID())->value(); + aParameters.tol3D = real(TOLERANCE_3D_ID())->value(); + aParameters.isApprox = boolean(APPROXIMATION_ID())->value(); + + if (aParameters.minDegree > aParameters.maxDegree) { + setError("Error: " + getKind() + " algorithm failed (max deg < min deg)."); + return; + } + + std::shared_ptr aFilling( + new GeomAlgoAPI_Filling(aParameters.minDegree, aParameters.maxDegree, aParameters.nbIter, + aParameters.tol2D, aParameters.tol3D)); + + // get base objects list + AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID()); + if (aSelectionList->size() <= 1) + return; + + // collect base shapes + for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) { + AttributeSelectionPtr aSelection = aSelectionList->value(anIndex); + GeomEdgePtr anEdge = toEdge(aSelection->value(), aParameters.method); + if (!anEdge) { + myLastEdgeStartPoint = GeomPointPtr(); + myLastEdgeEndPoint = GeomPointPtr(); + return; + } + aFilling->add(anEdge); + } + myLastEdgeStartPoint = GeomPointPtr(); + myLastEdgeEndPoint = GeomPointPtr(); + + // build result + aFilling->build(aParameters.isApprox); + if (isFailed(aFilling)) { + removeResults(0); + return; + } + + /// store result + GeomShapePtr aCreatedFace = aFilling->shape(); + ResultBodyPtr aResultBody = document()->createBody(data()); + aResultBody->store(aCreatedFace); + // store edges + int anEdgeInd = 0; + for(GeomAPI_ShapeExplorer anExp(aCreatedFace, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) { + GeomShapePtr anEdge = anExp.current(); + aResultBody->generated(anEdge, "Edge_" + std::to_string((long long)anEdgeInd), ++anEdgeInd); + } + setResult(aResultBody, 0); +} + +bool BuildPlugin_Filling::isFailed( + const std::shared_ptr& theAlgorithm) +{ + if (!theAlgorithm->isDone()) { + static const std::string aFeatureError = "Error: filling algorithm failed."; + setError(aFeatureError); + return true; + } + if (theAlgorithm->shape()->isNull()) { + static const std::string aShapeError = "Error: Resulting shape of filling is Null."; + setError(aShapeError); + return true; + } + if (!theAlgorithm->isValid()) { + std::string aFeatureError = "Error: Resulting shape of filling is not valid."; + setError(aFeatureError); + return true; + } + return false; +} + +//================================================================================================= +void BuildPlugin_Filling::attributeChanged(const std::string& theID) +{ + if (theID == ADVANCED_OPTIONS_ID() && string(ADVANCED_OPTIONS_ID())->value().empty()) { + // Advanced options flag just unchecked => restore default state of all parameters + restoreDefaultParameters(); + } +} + +//================================================================================================= +GeomEdgePtr BuildPlugin_Filling::toEdge(const GeomShapePtr& theShape, const std::string& theMethod) +{ + 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: + break; + } + + if (!anEdge || anEdge->empty()) { + static const std::string aFeatureError = + "Error: incorrect type of input feature (edges/wire are supported only)."; + setError(aFeatureError); + return anEdge; + } + + // correct edge orientation according to filling method + if (theMethod == Method::AUTO_CORRECT_ORIENTATION()) { + // check the distance to previous edge boundaries, reverse edge if necessary + GeomPointPtr aStartPnt = anEdge->firstPoint(); + GeomPointPtr aEndPnt = anEdge->lastPoint(); + bool isReverse = false; + if (myLastEdgeStartPoint) { + double d1 = myLastEdgeStartPoint->distance(aStartPnt) + + myLastEdgeEndPoint->distance(aEndPnt); + double d2 = myLastEdgeStartPoint->distance(aEndPnt) + + myLastEdgeEndPoint->distance(aStartPnt); + if (fabs(d1 - d2) < 1.e-7) { + // undefined case => check distance to start point only + d1 = myLastEdgeStartPoint->distance(aStartPnt); + d2 = myLastEdgeStartPoint->distance(aEndPnt); + } + isReverse = d2 < d1; + } + + if (isReverse) { + anEdge->reverse(); + myLastEdgeStartPoint = aEndPnt; + myLastEdgeEndPoint = aStartPnt; + } else { + myLastEdgeStartPoint = aStartPnt; + myLastEdgeEndPoint = aEndPnt; + } + } + else if (theMethod == Method::USE_CURVE_INFORMATION()) { + // make all edges FORWARD to avoid reversing the curves by GeomAlgoAPI_Filling algorithm + anEdge->setOrientation(GeomAPI_Shape::FORWARD); + } + return anEdge; +} + +//================================================================================================= +void BuildPlugin_Filling::restoreDefaultParameters() +{ + string(METHOD_ID())->setValue(METHOD_DEFAULT()); + integer(MINIMAL_DEGREE_ID())->setValue(MINIMAL_DEGREE_DEFAULT()); + integer(MAXIMAL_DEGREE_ID())->setValue(MAXIMAL_DEGREE_DEFAULT()); + integer(NUMBER_OF_ITERATIONS_ID())->setValue(NUMBER_OF_ITERATIONS_DEFAULT()); + real(TOLERANCE_2D_ID())->setValue(TOLERANCE_2D_DEFAULT()); + real(TOLERANCE_3D_ID())->setValue(TOLERANCE_3D_DEFAULT()); + boolean(APPROXIMATION_ID())->setValue(APPROXIMATION_DEFAULT()); +} diff --git a/src/BuildPlugin/BuildPlugin_Filling.h b/src/BuildPlugin/BuildPlugin_Filling.h new file mode 100644 index 000000000..e4a23b609 --- /dev/null +++ b/src/BuildPlugin/BuildPlugin_Filling.h @@ -0,0 +1,182 @@ +// Copyright (C) 2014-2017 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 BuildPlugin_Filling_H_ +#define BuildPlugin_Filling_H_ + +#include "BuildPlugin.h" + +#include + +#include +#include + +class GeomAlgoAPI_MakeShape; +class GeomAPI_Edge; +class GeomAPI_Pnt; +class GeomAPI_Shape; + +/// \class BuildPlugin_Filling +/// \ingroup Plugins +/// \brief Feature for creation of face from list of edges (1D objects). +class BuildPlugin_Filling: public ModelAPI_Feature +{ +public: + /// Use plugin manager for features creation + BuildPlugin_Filling(); + + /// Feature kind. + inline static const std::string& ID() + { + static const std::string MY_ID("Filling"); + return MY_ID; + } + + /// \return the kind of a feature. + BUILDPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = BuildPlugin_Filling::ID(); + return MY_KIND; + } + + /// Attribute name of base objects. + inline static const std::string& BASE_OBJECTS_ID() + { + static const std::string MY_BASE_OBJECTS_ID("base_objects"); + return MY_BASE_OBJECTS_ID; + } + + /// Attribute name of advanced options. + inline static const std::string& ADVANCED_OPTIONS_ID() + { + static const std::string MY_ADVANCED_OPTIONS_ID("advanced_options"); + return MY_ADVANCED_OPTIONS_ID; + } + + /// Attribute name of method of edge orientation. + inline static const std::string& METHOD_ID() + { + static const std::string MY_METHOD_ID("orientation"); + return MY_METHOD_ID; + } + + /// Supported methods for edge orientation correction + struct Method { + inline static const std::string& AUTO_CORRECT_ORIENTATION() + { + static const std::string MY_AUTO_CORRECT_ORIENTATION("auto_correct"); + return MY_AUTO_CORRECT_ORIENTATION; + } + inline static const std::string& USE_CURVE_INFORMATION() + { + static const std::string MY_USE_CURVE_INFORMATION("curve_info"); + return MY_USE_CURVE_INFORMATION; + } + inline static const std::string& USE_EDGES_ORIENTATION() + { + static const std::string MY_USE_EDGES_ORIENTATION("edge_orient"); + return MY_USE_EDGES_ORIENTATION; + } + }; + + /// Attribute name of minimal degree. + inline static const std::string& MINIMAL_DEGREE_ID() + { + static const std::string MY_MINIMAL_DEGREE_ID("min_degree"); + return MY_MINIMAL_DEGREE_ID; + } + + /// Attribute name of maximal degree. + inline static const std::string& MAXIMAL_DEGREE_ID() + { + static const std::string MY_MAXIMAL_DEGREE_ID("max_degree"); + return MY_MAXIMAL_DEGREE_ID; + } + + /// Attribute name of number of iterations. + inline static const std::string& NUMBER_OF_ITERATIONS_ID() + { + static const std::string MY_NUMBER_OF_ITERATIONS_ID("nb_iter"); + return MY_NUMBER_OF_ITERATIONS_ID; + } + + /// Attribute name of 2D tolerance. + inline static const std::string& TOLERANCE_2D_ID() + { + static const std::string MY_TOLERANCE_2D_ID("tol_2d"); + return MY_TOLERANCE_2D_ID; + } + + /// Attribute name of 3D tolerance. + inline static const std::string& TOLERANCE_3D_ID() + { + static const std::string MY_TOLERANCE_3D_ID("tol_3d"); + return MY_TOLERANCE_3D_ID; + } + + /// Attribute name of approximation. + inline static const std::string& APPROXIMATION_ID() + { + static const std::string MY_APPROXIMATION_ID("approximation"); + return MY_APPROXIMATION_ID; + } + + /// Default value of the orientation + inline static const std::string& METHOD_DEFAULT() { return Method::AUTO_CORRECT_ORIENTATION(); } + /// Default value of minimal degree + inline static int MINIMAL_DEGREE_DEFAULT() { return 2; } + /// Default value of maximal degree + inline static int MAXIMAL_DEGREE_DEFAULT() { return 5; } + /// Default value of number of iterations + inline static int NUMBER_OF_ITERATIONS_DEFAULT() { return 0; } + /// Default value of 2D tolerance + inline static double TOLERANCE_2D_DEFAULT() { return 1.e-4; } + /// Default value of 3D tolerance + inline static double TOLERANCE_3D_DEFAULT() { return 1.e-4; } + /// Default value of the approximation attribute + inline static bool APPROXIMATION_DEFAULT() { return false; } + + /// Request for initialization of data model of the feature: adding all attributes. + BUILDPLUGIN_EXPORT virtual void initAttributes(); + + /// Creates a new part document if needed. + BUILDPLUGIN_EXPORT virtual void execute(); + + /// Called on change of any argument-attribute of this object. + /// \param[in] theID identifier of changed attribute. + BUILDPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID); + +private: + /// Check the filling algorithm is failed + bool isFailed(const std::shared_ptr& theAlgorithm); + + /// Convert shape to edge according to construction method + std::shared_ptr toEdge(const std::shared_ptr& theShape, + const std::string& theMethod); + + /// Update values of attributes by their default values + void restoreDefaultParameters(); + +private: + std::shared_ptr myLastEdgeStartPoint; + std::shared_ptr myLastEdgeEndPoint; +}; + +#endif diff --git a/src/BuildPlugin/BuildPlugin_Plugin.cpp b/src/BuildPlugin/BuildPlugin_Plugin.cpp index 8849b519a..fed1877f9 100644 --- a/src/BuildPlugin/BuildPlugin_Plugin.cpp +++ b/src/BuildPlugin/BuildPlugin_Plugin.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2017 CEA/DEN, EDF R&D +// Copyright (C) 2017-20xx 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 @@ -29,6 +29,7 @@ #include #include #include +#include #include // the only created instance of this plugin @@ -68,6 +69,8 @@ FeaturePtr BuildPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new BuildPlugin_Shell()); } else if(theFeatureID == BuildPlugin_SubShapes::ID()) { return FeaturePtr(new BuildPlugin_SubShapes()); + } else if(theFeatureID == BuildPlugin_Filling::ID()) { + return FeaturePtr(new BuildPlugin_Filling()); } // Feature of such kind is not found. diff --git a/src/BuildPlugin/CMakeLists.txt b/src/BuildPlugin/CMakeLists.txt index 9331922f7..aeba2d0bc 100644 --- a/src/BuildPlugin/CMakeLists.txt +++ b/src/BuildPlugin/CMakeLists.txt @@ -38,6 +38,7 @@ SET(PROJECT_HEADERS BuildPlugin_Face.h BuildPlugin_Shell.h BuildPlugin_SubShapes.h + BuildPlugin_Filling.h BuildPlugin_Validators.h ) @@ -49,6 +50,7 @@ SET(PROJECT_SOURCES BuildPlugin_Face.cpp BuildPlugin_Shell.cpp BuildPlugin_SubShapes.cpp + BuildPlugin_Filling.cpp BuildPlugin_Validators.cpp ) @@ -60,6 +62,7 @@ SET(XML_RESOURCES face_widget.xml shell_widget.xml subshapes_widget.xml + filling_widget.xml ) SET(TEXT_RESOURCES @@ -92,4 +95,7 @@ ADD_UNIT_TESTS(TestVertex.py TestFace.py TestShell.py TestSubShapes.py + TestFilling_ByEdges.py + TestFilling_ByWires.py + TestFilling_Mixed.py Test1920.py) diff --git a/src/BuildPlugin/Test/TestFilling_ByEdges.py b/src/BuildPlugin/Test/TestFilling_ByEdges.py new file mode 100644 index 000000000..ac2468ab3 --- /dev/null +++ b/src/BuildPlugin/Test/TestFilling_ByEdges.py @@ -0,0 +1,154 @@ +## Copyright (C) 2017-20xx 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 +## + +# Initialization of the test +from ModelAPI import * +from GeomDataAPI import * +from GeomAlgoAPI import * +from GeomAPI import * + +# Get document +aSession = ModelAPI_Session.get() +aDocument = aSession.moduleDocument() + +# Create a part +aSession.startOperation() +aPartFeature = aDocument.addFeature("Part") +aSession.finishOperation() +aPartResult = modelAPI_ResultPart(aPartFeature.firstResult()) +aPart = aPartResult.partDoc() + +# Create first edge in a sketch +aSession.startOperation() +aSketch1 = featureToCompositeFeature(aPart.addFeature("Sketch")) +anOrigin = geomDataAPI_Point(aSketch1.attribute("Origin")) +anOrigin.setValue(0, 0, 0) +aDirX = geomDataAPI_Dir(aSketch1.attribute("DirX")) +aDirX.setValue(1, 0, 0) +aNorm = geomDataAPI_Dir(aSketch1.attribute("Norm")) +aNorm.setValue(0, 0, 1) +# an arc +anArc = aSketch1.addFeature("SketchArc") +anArcCenter = geomDataAPI_Point2D(anArc.attribute("center_point")) +anArcCenter.setValue(1, 1) +anArcStartPoint = geomDataAPI_Point2D(anArc.attribute("start_point")) +anArcStartPoint.setValue(20, 5) +anArcEndPoint = geomDataAPI_Point2D(anArc.attribute("end_point")) +anArcEndPoint.setValue(5, 20) +aSession.finishOperation() +aSketch1Result = aSketch1.firstResult() + +# Create second edge in another sketch +aSession.startOperation() +aSketch2 = featureToCompositeFeature(aPart.addFeature("Sketch")) +anOrigin = geomDataAPI_Point(aSketch2.attribute("Origin")) +anOrigin.setValue(0, 0, 0) +aDirX = geomDataAPI_Dir(aSketch2.attribute("DirX")) +aDirX.setValue(1, 0, 0) +aNorm = geomDataAPI_Dir(aSketch2.attribute("Norm")) +aNorm.setValue(0, 0.7071067811865475, 0.7071067811865475) +# a line +aLine = aSketch2.addFeature("SketchLine") +aLineStartPoint = geomDataAPI_Point2D(aLine.attribute("StartPoint")) +aLineStartPoint.setValue(0, 0) +aLineEndPoint = geomDataAPI_Point2D(aLine.attribute("EndPoint")) +aLineEndPoint.setValue(20, 20) +aSession.finishOperation() +aSketch2Result = aSketch2.firstResult() +# an edge +aSession.startOperation() +anEdge = aPart.addFeature("Edge") +aBaseObjectsList = anEdge.selectionList("base_objects") +aBaseObjectsList.append(aSketch2Result, aLine.lastResult().shape()) +aSession.finishOperation() + +# Create filling +aSession.startOperation() +aFillingFeature = aPart.addFeature("Filling") +aBaseObjectsList = aFillingFeature.selectionList("base_objects") +aBaseObjectsList.append(aSketch1Result, anArc.lastResult().shape()) +aSession.finishOperation() + +# ============================================================================= +# Test 1. Filling on one edge is failed (error is reported) +# ============================================================================= +assert(len(aFillingFeature.results()) == 0) +assert(aFillingFeature.error() != "") + +# ============================================================================= +# Test 2. Add another edge, filling should be completed +# ============================================================================= +aSession.startOperation() +aBaseObjectsList.append(anEdge.lastResult(), None) +aSession.finishOperation() +assert(len(aFillingFeature.results()) > 0) + +# ============================================================================= +# Test 3. Change parameters one-by-one and check validity of result +# ============================================================================= +aSession.startOperation() +aFillingFeature.string("advanced_options").setValue("true") +aSession.finishOperation() +orientations = ["auto_correct", "curve_info", "edge_orient"] +tolerances = [0.0001, 0.001] +for ori in orientations: + for minDeg in range(2, 4): + for maxDeg in range(5, 7): + for nbIter in range(0, 3, 2): + for tol2d in tolerances: + for tol3d in tolerances: + for approx in [False, True]: + aSession.startOperation() + aFillingFeature.string("orientation").setValue(ori) + aFillingFeature.integer("min_degree").setValue(minDeg) + aFillingFeature.integer("max_degree").setValue(maxDeg) + aFillingFeature.integer("nb_iter").setValue(nbIter) + aFillingFeature.real("tol_2d").setValue(tol2d) + aFillingFeature.real("tol_3d").setValue(tol3d) + aFillingFeature.boolean("approximation").setValue(approx) + aSession.finishOperation() + assert(len(aFillingFeature.results()) > 0), "Filling feature failed with parameters:\n orientation={}\n min deg={}\n max deg={}\n nb iter={}\n tol 2d={}\n tol 3d={}\n approximation={}".format(ori, minDeg, maxDeg, nbIter, tol2d, tol3d, approx) + +# ============================================================================= +# Test 4. Discard parameters to default and add one more edge +# ============================================================================= +aSession.startOperation() +aFillingFeature.string("advanced_options").setValue("") +aSession.finishOperation() +# new arc +aSession.startOperation() +anArc2 = aSketch1.addFeature("SketchArc") +anArc2Center = geomDataAPI_Point2D(anArc2.attribute("center_point")) +anArc2Center.setValue(0, -5) +anArc2StartPoint = geomDataAPI_Point2D(anArc2.attribute("start_point")) +anArc2StartPoint.setValue(-20, -5) +anArc2EndPoint = geomDataAPI_Point2D(anArc2.attribute("end_point")) +anArc2EndPoint.setValue(20, -5) +aSession.finishOperation() +aSketch1Result = aSketch1.firstResult() +# update filling +aSession.startOperation() +aPart.setCurrentFeature(aFillingFeature, True) +aBaseObjectsList.append(aSketch1Result, anArc2.lastResult().shape()) +aSession.finishOperation() +assert(len(aFillingFeature.results()) > 0) + +from salome.shaper import model +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestFilling_ByWires.py b/src/BuildPlugin/Test/TestFilling_ByWires.py new file mode 100644 index 000000000..238e7764a --- /dev/null +++ b/src/BuildPlugin/Test/TestFilling_ByWires.py @@ -0,0 +1,152 @@ +## Copyright (C) 2017-20xx 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 +## + +# Initialization of the test +from ModelAPI import * +from GeomDataAPI import * +from GeomAlgoAPI import * +from GeomAPI import * + +# Get document +aSession = ModelAPI_Session.get() +aDocument = aSession.moduleDocument() + +# Create a part +aSession.startOperation() +aPartFeature = aDocument.addFeature("Part") +aSession.finishOperation() +aPartResult = modelAPI_ResultPart(aPartFeature.firstResult()) +aPart = aPartResult.partDoc() + +# Create first wire in a sketch +aSession.startOperation() +aSketch1 = featureToCompositeFeature(aPart.addFeature("Sketch")) +anOrigin = geomDataAPI_Point(aSketch1.attribute("Origin")) +anOrigin.setValue(0, 0, 0) +aDirX = geomDataAPI_Dir(aSketch1.attribute("DirX")) +aDirX.setValue(1, 0, 0) +aNorm = geomDataAPI_Dir(aSketch1.attribute("Norm")) +aNorm.setValue(0, 0, 1) +# arc 1 +anArc1 = aSketch1.addFeature("SketchArc") +anArcCenter = geomDataAPI_Point2D(anArc1.attribute("center_point")) +anArcCenter.setValue(1, 1) +anArcStartPoint = geomDataAPI_Point2D(anArc1.attribute("start_point")) +anArcStartPoint.setValue(20, 5) +anArcEndPoint = geomDataAPI_Point2D(anArc1.attribute("end_point")) +anArcEndPoint.setValue(5, 20) +# arc 2 +anArc2 = aSketch1.addFeature("SketchArc") +anArcCenter = geomDataAPI_Point2D(anArc2.attribute("center_point")) +anArcCenter.setValue(12.5, 12.5) +anArcStartPoint = geomDataAPI_Point2D(anArc2.attribute("start_point")) +anArcStartPoint.setValue(5, 20) +anArcEndPoint = geomDataAPI_Point2D(anArc2.attribute("end_point")) +anArcEndPoint.setValue(20, 5) +aSession.finishOperation() +aSketch1Result = aSketch1.firstResult() +# a wire +aSession.startOperation() +aWire1 = aPart.addFeature("Wire") +aBaseObjectsList = aWire1.selectionList("base_objects") +aBaseObjectsList.append(aSketch1Result, anArc1.lastResult().shape()) +aBaseObjectsList.append(aSketch1Result, anArc2.lastResult().shape()) +aSession.finishOperation() + +# Create second wire in another sketch +aSession.startOperation() +aSketch2 = featureToCompositeFeature(aPart.addFeature("Sketch")) +anOrigin = geomDataAPI_Point(aSketch2.attribute("Origin")) +anOrigin.setValue(0, 0, 0) +aDirX = geomDataAPI_Dir(aSketch2.attribute("DirX")) +aDirX.setValue(1, 0, 0) +aNorm = geomDataAPI_Dir(aSketch2.attribute("Norm")) +aNorm.setValue(0, 0.7071067811865475, 0.7071067811865475) +# line 1 +aLine1 = aSketch2.addFeature("SketchLine") +aLineStartPoint = geomDataAPI_Point2D(aLine1.attribute("StartPoint")) +aLineStartPoint.setValue(30, 0) +aLineEndPoint = geomDataAPI_Point2D(aLine1.attribute("EndPoint")) +aLineEndPoint.setValue(20, 20) +# line 2 +aLine2 = aSketch2.addFeature("SketchLine") +aLineStartPoint = geomDataAPI_Point2D(aLine2.attribute("StartPoint")) +aLineStartPoint.setValue(20, 20) +aLineEndPoint = geomDataAPI_Point2D(aLine2.attribute("EndPoint")) +aLineEndPoint.setValue(20, 0) +aSession.finishOperation() +aSketch2Result = aSketch2.firstResult() +# a wire +aSession.startOperation() +aWire2 = aPart.addFeature("Wire") +aBaseObjectsList = aWire2.selectionList("base_objects") +aBaseObjectsList.append(aSketch2Result, aLine1.lastResult().shape()) +aBaseObjectsList.append(aSketch2Result, aLine2.lastResult().shape()) +aSession.finishOperation() + +# Create filling +aSession.startOperation() +aFillingFeature = aPart.addFeature("Filling") +aBaseObjectsList = aFillingFeature.selectionList("base_objects") +aBaseObjectsList.append(aWire1.lastResult(), None) +aSession.finishOperation() + +# ============================================================================= +# Test 1. Filling on one wire is failed (error is reported) +# ============================================================================= +assert(len(aFillingFeature.results()) == 0) +assert(aFillingFeature.error() != "") + +# ============================================================================= +# Test 2. Add another wire, filling should be completed +# ============================================================================= +aSession.startOperation() +aBaseObjectsList.append(aWire2.lastResult(), None) +aSession.finishOperation() +assert(len(aFillingFeature.results()) > 0) + +# ============================================================================= +# Test 3. Change parameters one-by-one and check validity of result +# ============================================================================= +aSession.startOperation() +aFillingFeature.string("advanced_options").setValue("true") +aSession.finishOperation() +orientations = ["auto_correct", "curve_info", "edge_orient"] +tolerances = [0.0001, 0.001] +for ori in orientations: + for minDeg in range(2, 4): + for maxDeg in range(5, 7): + for nbIter in range(0, 3, 2): + for tol2d in tolerances: + for tol3d in tolerances: + for approx in [False, True]: + aSession.startOperation() + aFillingFeature.string("orientation").setValue(ori) + aFillingFeature.integer("min_degree").setValue(minDeg) + aFillingFeature.integer("max_degree").setValue(maxDeg) + aFillingFeature.integer("nb_iter").setValue(nbIter) + aFillingFeature.real("tol_2d").setValue(tol2d) + aFillingFeature.real("tol_3d").setValue(tol3d) + aFillingFeature.boolean("approximation").setValue(approx) + aSession.finishOperation() + assert(len(aFillingFeature.results()) > 0), "Filling feature failed with parameters:\n orientation={}\n min deg={}\n max deg={}\n nb iter={}\n tol 2d={}\n tol 3d={}\n approximation={}".format(ori, minDeg, maxDeg, nbIter, tol2d, tol3d, approx) + +from salome.shaper import model +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestFilling_Mixed.py b/src/BuildPlugin/Test/TestFilling_Mixed.py new file mode 100644 index 000000000..c6d436535 --- /dev/null +++ b/src/BuildPlugin/Test/TestFilling_Mixed.py @@ -0,0 +1,168 @@ +## Copyright (C) 2017-20xx 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 +## + +# Initialization of the test +from ModelAPI import * +from GeomDataAPI import * +from GeomAlgoAPI import * +from GeomAPI import * + +# Get document +aSession = ModelAPI_Session.get() +aDocument = aSession.moduleDocument() + +# Create a part +aSession.startOperation() +aPartFeature = aDocument.addFeature("Part") +aSession.finishOperation() +aPartResult = modelAPI_ResultPart(aPartFeature.firstResult()) +aPart = aPartResult.partDoc() + +# Create first edge in a sketch +aSession.startOperation() +aSketch1 = featureToCompositeFeature(aPart.addFeature("Sketch")) +anOrigin = geomDataAPI_Point(aSketch1.attribute("Origin")) +anOrigin.setValue(0, 0, 0) +aDirX = geomDataAPI_Dir(aSketch1.attribute("DirX")) +aDirX.setValue(1, 0, 0) +aNorm = geomDataAPI_Dir(aSketch1.attribute("Norm")) +aNorm.setValue(0, 0, 1) +# an arc +anArc = aSketch1.addFeature("SketchArc") +anArcCenter = geomDataAPI_Point2D(anArc.attribute("center_point")) +anArcCenter.setValue(1, 1) +anArcStartPoint = geomDataAPI_Point2D(anArc.attribute("start_point")) +anArcStartPoint.setValue(20, 5) +anArcEndPoint = geomDataAPI_Point2D(anArc.attribute("end_point")) +anArcEndPoint.setValue(5, 20) +aSession.finishOperation() +aSketch1Result = aSketch1.firstResult() + +# Create a wire in another sketch +aSession.startOperation() +aSketch2 = featureToCompositeFeature(aPart.addFeature("Sketch")) +anOrigin = geomDataAPI_Point(aSketch2.attribute("Origin")) +anOrigin.setValue(0, 0, 0) +aDirX = geomDataAPI_Dir(aSketch2.attribute("DirX")) +aDirX.setValue(1, 0, 0) +aNorm = geomDataAPI_Dir(aSketch2.attribute("Norm")) +aNorm.setValue(0, 0.7071067811865475, 0.7071067811865475) +# line 1 +aLine1 = aSketch2.addFeature("SketchLine") +aLineStartPoint = geomDataAPI_Point2D(aLine1.attribute("StartPoint")) +aLineStartPoint.setValue(0, 0) +aLineEndPoint = geomDataAPI_Point2D(aLine1.attribute("EndPoint")) +aLineEndPoint.setValue(10, 10) +# line 2 +aLine2 = aSketch2.addFeature("SketchLine") +aLineStartPoint = geomDataAPI_Point2D(aLine2.attribute("StartPoint")) +aLineStartPoint.setValue(10, 10) +aLineEndPoint = geomDataAPI_Point2D(aLine2.attribute("EndPoint")) +aLineEndPoint.setValue(30, 0) +aSession.finishOperation() +aSketch2Result = aSketch2.firstResult() +# a wire +aSession.startOperation() +aWire = aPart.addFeature("Wire") +aBaseObjectsList = aWire.selectionList("base_objects") +aBaseObjectsList.append(aSketch2Result, aLine1.lastResult().shape()) +aBaseObjectsList.append(aSketch2Result, aLine2.lastResult().shape()) +aSession.finishOperation() + +# Create filling +aSession.startOperation() +aFillingFeature = aPart.addFeature("Filling") +aBaseObjectsList = aFillingFeature.selectionList("base_objects") +aBaseObjectsList.append(aSketch1Result, anArc.lastResult().shape()) +aSession.finishOperation() + +# ============================================================================= +# Test 1. Filling on one edge is failed (error is reported) +# ============================================================================= +assert(len(aFillingFeature.results()) == 0) +assert(aFillingFeature.error() != "") + +# ============================================================================= +# Test 2. Add a wire, filling should be completed +# ============================================================================= +aSession.startOperation() +aBaseObjectsList.append(aWire.lastResult(), None) +aSession.finishOperation() +assert(len(aFillingFeature.results()) > 0) + +# ============================================================================= +# Test 3. Change parameters one-by-one and check validity of result +# ============================================================================= +aSession.startOperation() +aFillingFeature.string("advanced_options").setValue("true") +aSession.finishOperation() +orientations = ["auto_correct", "curve_info", "edge_orient"] +tolerances = [0.0001, 0.001] +for ori in orientations: + for minDeg in range(2, 4): + for maxDeg in range(5, 7): + for nbIter in range(0, 3, 2): + for tol2d in tolerances: + for tol3d in tolerances: + for approx in [False, True]: + aSession.startOperation() + aFillingFeature.string("orientation").setValue(ori) + aFillingFeature.integer("min_degree").setValue(minDeg) + aFillingFeature.integer("max_degree").setValue(maxDeg) + aFillingFeature.integer("nb_iter").setValue(nbIter) + aFillingFeature.real("tol_2d").setValue(tol2d) + aFillingFeature.real("tol_3d").setValue(tol3d) + aFillingFeature.boolean("approximation").setValue(approx) + aSession.finishOperation() + assert(len(aFillingFeature.results()) > 0), "Filling feature failed with parameters:\n orientation={}\n min deg={}\n max deg={}\n nb iter={}\n tol 2d={}\n tol 3d={}\n approximation={}".format(ori, minDeg, maxDeg, nbIter, tol2d, tol3d, approx) + +# ============================================================================= +# Test 4. Discard parameters to default and add one more edge +# ============================================================================= +aSession.startOperation() +aFillingFeature.string("advanced_options").setValue("") +aSession.finishOperation() +# new arc +aSession.startOperation() +anArc2 = aSketch1.addFeature("SketchArc") +anArc2Center = geomDataAPI_Point2D(anArc2.attribute("center_point")) +anArc2Center.setValue(0, -5) +anArc2StartPoint = geomDataAPI_Point2D(anArc2.attribute("start_point")) +anArc2StartPoint.setValue(-20, -5) +anArc2EndPoint = geomDataAPI_Point2D(anArc2.attribute("end_point")) +anArc2EndPoint.setValue(20, -5) +aSession.finishOperation() +aSketch1Result = aSketch1.firstResult() +# an edge +aSession.startOperation() +aPart.setCurrentFeature(aWire, True) +anEdge = aPart.addFeature("Edge") +anEdgeObjectsList = anEdge.selectionList("base_objects") +anEdgeObjectsList.append(aSketch1Result, anArc2.lastResult().shape()) +aSession.finishOperation() +# update filling +aSession.startOperation() +aPart.setCurrentFeature(aFillingFeature, True) +aBaseObjectsList.append(anEdge.lastResult(), None) +aSession.finishOperation() +assert(len(aFillingFeature.results()) > 0) + +from salome.shaper import model +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/filling_widget.xml b/src/BuildPlugin/filling_widget.xml new file mode 100644 index 000000000..a9e393004 --- /dev/null +++ b/src/BuildPlugin/filling_widget.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BuildPlugin/icons/feature_filling.png b/src/BuildPlugin/icons/feature_filling.png new file mode 100644 index 0000000000000000000000000000000000000000..ef16696bb6240ce14236e3d8b2479d4cc2c3f028 GIT binary patch literal 592 zcmV-W0^hwl^;nJx7Zs&nfG5}rfQC;a2!jLjaqQ+E#m*o0@xzr z0Ary@By;~oZZkp(m6V0qt%5Wg&)vGp+|2@soznea3~bwwsd?;jWBndrP=Jk}b#LZ2 z#S&zmX#(I+6T7O5rtj;Ww*5FbI`PsN3Y99;00KBe{hYXa#Ts{qIIt2v$#U#6DuGsf z_Io9IXmUH1df@LAX2mQ8t!Wj`l|y^HPj1@OY-c@g$!Kz0B-0rBXN*ga>`ApG?OEZ# zXLrV4-L$_hm7Whr2QGrY&a=73t;cG!zSiQ#TgkimaSfMl)TK%@+|mGG_^7_2T-~eU zJsvDf;e0%c@mE8wa@*`Zl6z>K&!&yPW2DOu26zoPjXD4Ic@D?rI=`1gfioQ&0FG@~ eunh~%75oBmg-1BdW(S7=0000 - + + + + + + diff --git a/src/GeomAPI/GeomAPI_Shape.cpp b/src/GeomAPI/GeomAPI_Shape.cpp index f66a6a1e6..b634f3dcc 100644 --- a/src/GeomAPI/GeomAPI_Shape.cpp +++ b/src/GeomAPI/GeomAPI_Shape.cpp @@ -103,6 +103,12 @@ bool GeomAPI_Shape::isEdge() const return !aShape.IsNull() && aShape.ShapeType() == TopAbs_EDGE; } +bool GeomAPI_Shape::isWire() const +{ + const TopoDS_Shape& aShape = const_cast(this)->impl(); + return !aShape.IsNull() && aShape.ShapeType() == TopAbs_WIRE; +} + bool GeomAPI_Shape::isFace() const { const TopoDS_Shape& aShape = const_cast(this)->impl(); @@ -408,6 +414,11 @@ void GeomAPI_Shape::setOrientation(const GeomAPI_Shape::Orientation theOrientati } } +void GeomAPI_Shape::reverse() +{ + MY_SHAPE->Reverse(); +} + bool GeomAPI_Shape::isSubShape(const std::shared_ptr theShape, const bool theCheckOrientation) const { diff --git a/src/GeomAPI/GeomAPI_Shape.h b/src/GeomAPI/GeomAPI_Shape.h index 242a85bc7..7138a776d 100644 --- a/src/GeomAPI/GeomAPI_Shape.h +++ b/src/GeomAPI/GeomAPI_Shape.h @@ -78,6 +78,10 @@ public: GEOMAPI_EXPORT virtual bool isEdge() const; + /// Returns whether the shape is a wire + GEOMAPI_EXPORT + virtual bool isWire() const; + /// Returns whether the shape is a face GEOMAPI_EXPORT virtual bool isFace() const; @@ -123,6 +127,9 @@ public: /// Sets the shape orientation. GEOMAPI_EXPORT virtual void setOrientation(const Orientation theOrientation); + /// Reverse shape + GEOMAPI_EXPORT virtual void reverse(); + /// \return true if passed shape is a sub-shape of this shape. /// \param theShape shape to search. /// \param theCheckOrientation if false, returns true even if orientation of shape differs diff --git a/src/GeomAPI/GeomAPI_Wire.cpp b/src/GeomAPI/GeomAPI_Wire.cpp index 039be40a8..e750f3ac5 100644 --- a/src/GeomAPI/GeomAPI_Wire.cpp +++ b/src/GeomAPI/GeomAPI_Wire.cpp @@ -33,3 +33,11 @@ GeomAPI_Wire::GeomAPI_Wire() this->setImpl(aWire); } + +//================================================================================================== +GeomAPI_Wire::GeomAPI_Wire(const std::shared_ptr& theShape) +{ + if (!theShape->isNull() && theShape->isWire()) { + setImpl(new TopoDS_Shape(theShape->impl())); + } +} diff --git a/src/GeomAPI/GeomAPI_Wire.h b/src/GeomAPI/GeomAPI_Wire.h index 837039d4c..888e9b19c 100644 --- a/src/GeomAPI/GeomAPI_Wire.h +++ b/src/GeomAPI/GeomAPI_Wire.h @@ -31,7 +31,11 @@ class GeomAPI_Wire: public GeomAPI_Shape public: /// Makes an undefined Wire. GEOMAPI_EXPORT GeomAPI_Wire(); + + /// Creation of wire by the wire-shape + GEOMAPI_EXPORT GeomAPI_Wire(const std::shared_ptr& theShape); }; -#endif +typedef std::shared_ptr GeomWirePtr; +#endif diff --git a/src/GeomAlgoAPI/CMakeLists.txt b/src/GeomAlgoAPI/CMakeLists.txt index c9f493d71..81c0d1f0f 100644 --- a/src/GeomAlgoAPI/CMakeLists.txt +++ b/src/GeomAlgoAPI/CMakeLists.txt @@ -76,6 +76,7 @@ SET(PROJECT_HEADERS GeomAlgoAPI_UnifySameDomain.h GeomAlgoAPI_Fillet.h GeomAlgoAPI_SortListOfShapes.h + GeomAlgoAPI_Filling.h ) SET(PROJECT_SOURCES @@ -130,6 +131,7 @@ SET(PROJECT_SOURCES GeomAlgoAPI_UnifySameDomain.cpp GeomAlgoAPI_Fillet.cpp GeomAlgoAPI_SortListOfShapes.cpp + GeomAlgoAPI_Filling.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Filling.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_Filling.cpp new file mode 100644 index 000000000..338409aa5 --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Filling.cpp @@ -0,0 +1,191 @@ +// Copyright (C) 2017-20xx 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 "GeomAlgoAPI_Filling.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void edgesToCurves(const std::list& theEdges, + std::list& theCurves) +{ + for (std::list::const_iterator anIt = theEdges.begin(); + anIt != theEdges.end(); ++anIt) { + const TopoDS_Edge& anEdge = (*anIt)->impl(); + if (BRep_Tool::Degenerated(anEdge)) + continue; + + double aFirst, aLast; + Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast); + + aCurve = new Geom_TrimmedCurve(aCurve, aFirst, aLast); + if (anEdge.Orientation() == TopAbs_REVERSED) + aCurve->Reverse(); + + theCurves.push_back(aCurve); + } +} + + +GeomAlgoAPI_Filling::GeomAlgoAPI_Filling(const int theMinDegree, + const int theMaxDegree, + const int theNbIter, + const double theTol2D, + const double theTol3D) + : myMinDegree(theMinDegree), + myMaxDegree(theMaxDegree), + myNbIter(theNbIter), + myTol2D(theTol2D), + myTol3D(theTol3D) +{ +} + +void GeomAlgoAPI_Filling::add(const GeomEdgePtr theEdge) +{ + myConstraints.push_back(theEdge); +} + +void GeomAlgoAPI_Filling::build(bool isApproximate) +{ + if (myConstraints.size() <= 1) // not enough edges + return; + + if (isApproximate) + buildByControlPoints(); + else + buildByEdges(); +} + +void GeomAlgoAPI_Filling::buildByEdges() +{ + GeomFill_SectionGenerator aSection; + + // obtain section curves + std::list aCurves; + edgesToCurves(myConstraints, aCurves); + for (std::list::iterator anIt = aCurves.begin(); + anIt != aCurves.end(); ++anIt) + aSection.AddCurve(*anIt); + + // a 'tolerance' is used to compare 2 knots + aSection.Perform(Precision::PConfusion()); + Handle(GeomFill_Line) aLine = new GeomFill_Line((int)aCurves.size()); + + // perform filling by sections + GeomFill_AppSurf anAppSurf(myMinDegree, myMaxDegree, myTol3D, myTol2D, myNbIter); + anAppSurf.Perform(aLine, aSection); + if (!anAppSurf.IsDone()) + return; + + // build calculated surface + Standard_Integer UDegree, VDegree, NbUPoles, NbVPoles, NbUKnots, NbVKnots; + anAppSurf.SurfShape(UDegree, VDegree, NbUPoles, NbVPoles, NbUKnots, NbVKnots); + Handle(Geom_BSplineSurface) GBS = new Geom_BSplineSurface( + anAppSurf.SurfPoles(), anAppSurf.SurfWeights(), anAppSurf.SurfUKnots(), anAppSurf.SurfVKnots(), + anAppSurf.SurfUMults(), anAppSurf.SurfVMults(), anAppSurf.UDegree(), anAppSurf.VDegree()); + + if (GBS.IsNull()) + return; + + // store result + TopoDS_Face aFace = BRepBuilderAPI_MakeFace(GBS, Precision::Confusion()); + std::shared_ptr aShape(new GeomAPI_Shape()); + aShape->setImpl(new TopoDS_Shape(aFace)); + setShape(aShape); + setDone(true); +} + +static Handle(Geom_Curve) removeTrim(const Handle(Geom_Curve)& theCurve) +{ + Handle(Geom_Curve) aCurve = theCurve; + Handle(Geom_TrimmedCurve) aTC = Handle(Geom_TrimmedCurve)::DownCast(aCurve); + while (!aTC.IsNull()) { + aCurve = aTC->BasisCurve(); + aTC = Handle(Geom_TrimmedCurve)::DownCast(aCurve); + } + return aCurve; +} + +void GeomAlgoAPI_Filling::buildByControlPoints() +{ + // obtain section curves + std::list aCurves; + edgesToCurves(myConstraints, aCurves); + + // compute maximal number of poles in B-spline curves + int aMaxPoles = 0; + std::list::iterator anIt = aCurves.begin(); + for (; anIt != aCurves.end(); ++anIt) { + Handle(Geom_BSplineCurve) aBC = Handle(Geom_BSplineCurve)::DownCast(removeTrim(*anIt)); + if (!aBC.IsNull()) + aMaxPoles = Max(aMaxPoles, aBC->NbPoles()); + } + + // prepare array of points for creation bspline surface + // size of this array: by U parameter - number of curves, + // by V parameter - determ using MaxNbPoles but it's + // value must be between 21(min) and 101(max) + int aNbSections = (int) aCurves.size(); + int aNbPntInSection = Max(21, 2 * aMaxPoles - 1); + TColgp_Array2OfPnt aPoints(1, aNbSections, 1, aNbPntInSection); + anIt = aCurves.begin(); + for (int i = 1; anIt != aCurves.end(); ++i, ++anIt) { + Handle(Geom_Curve) aC = *anIt; + double fp = aC->FirstParameter(); + double lp = aC->LastParameter(); + double dp = (lp - fp) / (aNbPntInSection - 1); + + gp_Pnt aPnt; + for (int j = 0; j < aNbPntInSection; j++) { + aC->D0(fp + dp * j, aPnt); + aPoints.SetValue(i, j+1, aPnt); + } + } + + // convert a grid of points to B-spline surface + GeomAPI_PointsToBSplineSurface aPTB(aPoints, myMinDegree, myMaxDegree, GeomAbs_C2, myTol3D); + Handle(Geom_BSplineSurface) aBS = aPTB.Surface(); + if (aBS.IsNull()) + return; + + // fix face orientation + TopoDS_Face aFace = BRepBuilderAPI_MakeFace(aBS, Precision::Confusion()); + Handle(ShapeFix_Face) aFix = new ShapeFix_Face(aFace); + aFix->Perform(); + aFix->FixOrientation(); + aFace = aFix->Face(); + + // store result + std::shared_ptr aShape(new GeomAPI_Shape()); + aShape->setImpl(new TopoDS_Shape(aFace)); + setShape(aShape); + setDone(true); +} diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Filling.h b/src/GeomAlgoAPI/GeomAlgoAPI_Filling.h new file mode 100644 index 000000000..99275bf67 --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Filling.h @@ -0,0 +1,65 @@ +// Copyright (C) 2017-20xx 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 GeomAlgoAPI_Filling_H_ +#define GeomAlgoAPI_Filling_H_ + +#include +#include + +#include + +/// \class GeomAlgoAPI_Filling +/// \ingroup DataAlgo +/// \brief Perform building face by the set of edges/wires +class GeomAlgoAPI_Filling : public GeomAlgoAPI_MakeShape +{ +public: + /// \brief Construct filling operation by the parameters. + GEOMALGOAPI_EXPORT GeomAlgoAPI_Filling(const int theMinDegree = 2, + const int theMaxDegree = 5, + const int theNbIter = 0, + const double theTol2D = 1.e-4, + const double theTol3D = 1.e-4); + + /// \brief Add an edge to constrain filling (the result face should pass through the edge) + GEOMALGOAPI_EXPORT void add(const GeomEdgePtr theEdge); + + /// \brief Perform filling operation + /// \param isApproximate approximate curves before building a face + GEOMALGOAPI_EXPORT void build(bool isApproximate = false); + +private: + /// \brief Perform filling using the given edges + void buildByEdges(); + + /// \brief Perform filling by a set of points calculated on each edge + void buildByControlPoints(); + +private: + int myMinDegree; + int myMaxDegree; + int myNbIter; + double myTol2D; + double myTol3D; + std::list myConstraints; +}; + +#endif diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp index fd854afb7..60b9fe453 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp @@ -28,11 +28,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -953,3 +955,17 @@ std::shared_ptr GeomAlgoAPI_ShapeTools::buildDirFromAxisAndShape( aCentreOfMassPoint.Z()-aPoint.Z())); return aDir; } + +//================================================================================================== +std::shared_ptr GeomAlgoAPI_ShapeTools::wireToEdge( + const std::shared_ptr& theWire) +{ + GeomEdgePtr anEdge; + if (theWire) { + const TopoDS_Wire& aWire = theWire->impl(); + TopoDS_Edge aNewEdge = BRepAlgo::ConcatenateWireC0(aWire); + anEdge = GeomEdgePtr(new GeomAPI_Edge); + anEdge->setImpl(new TopoDS_Edge(aNewEdge)); + } + return anEdge; +} diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h index 7745c4011..42f87c862 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h @@ -36,6 +36,7 @@ class GeomAPI_Face; class GeomAPI_PlanarEdges; class GeomAPI_Pln; class GeomAPI_Pnt; +class GeomAPI_Wire; class GeomDataAPI_Point2D; class ModelAPI_Object; @@ -174,6 +175,10 @@ public: GEOMALGOAPI_EXPORT static std::shared_ptr buildDirFromAxisAndShape( const std::shared_ptr theBaseShape, const std::shared_ptr theAxis); + + /// \brief Reapproximate a wire to build a single edge + GEOMALGOAPI_EXPORT static std::shared_ptr wireToEdge( + const std::shared_ptr& theWire); }; #endif diff --git a/src/GeomValidators/CMakeLists.txt b/src/GeomValidators/CMakeLists.txt index 92bed77b3..3b343b292 100644 --- a/src/GeomValidators/CMakeLists.txt +++ b/src/GeomValidators/CMakeLists.txt @@ -38,6 +38,7 @@ SET(PROJECT_HEADERS GeomValidators_Different.h GeomValidators_IntersectionSelection.h GeomValidators_MinObjectsSelected.h + GeomValidators_ValueOrder.h ) SET(PROJECT_SOURCES @@ -57,6 +58,7 @@ SET(PROJECT_SOURCES GeomValidators_Different.cpp GeomValidators_IntersectionSelection.cpp GeomValidators_MinObjectsSelected.cpp + GeomValidators_ValueOrder.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/GeomValidators/GeomValidators_ValueOrder.cpp b/src/GeomValidators/GeomValidators_ValueOrder.cpp new file mode 100644 index 000000000..03654381f --- /dev/null +++ b/src/GeomValidators/GeomValidators_ValueOrder.cpp @@ -0,0 +1,132 @@ +// Copyright (C) 2017-20xx 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 "GeomValidators_ValueOrder.h" + +#include +#include + +#include +#include +#include +#include + +static double attributeValue(AttributePtr theAttr) +{ + AttributeIntegerPtr anIntAttr = std::dynamic_pointer_cast(theAttr); + if (anIntAttr) + return (double)anIntAttr->value(); + AttributeDoublePtr aDoubleAttr = std::dynamic_pointer_cast(theAttr); + if (aDoubleAttr) + return aDoubleAttr->value(); + return 0.0; +} + +static bool isGreaterOrEqual(AttributePtr theFirstArg, AttributePtr theSecondArg) +{ + if (theFirstArg && theFirstArg->isInitialized() && + theSecondArg && theSecondArg->isInitialized()) + return attributeValue(theFirstArg) >= attributeValue(theSecondArg); + return false; +} + +static bool isLessOrEqual(AttributePtr theFirstArg, AttributePtr theSecondArg) +{ + if (theFirstArg && theFirstArg->isInitialized() && + theSecondArg && theSecondArg->isInitialized()) + return attributeValue(theFirstArg) <= attributeValue(theSecondArg); + return false; +} + +// Check the attributes are satisfy theCompare function +static bool isValidOrder(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError, + bool (*theCompare)(AttributePtr, AttributePtr)) +{ + std::string anAttrType = theAttribute->attributeType(); + if (anAttrType != ModelAPI_AttributeDouble::typeId() && + anAttrType != ModelAPI_AttributeInteger::typeId()) { + theError = "Unsupported attribute type (integer or double applicable)"; + return false; + } + + FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner()); + if (!anOwner) { + theError = "Attribute without owner"; + return false; + } + + for (std::list::const_iterator anIt = theArguments.begin(); + anIt != theArguments.end(); ++anIt) { + // check the argument links to the attribute of the current feature + AttributePtr aCurAttr = anOwner->attribute(*anIt); + if (!aCurAttr) { + theError = "Arguments should be names of attributes of current feature"; + return false; + } + + // compare values + if (!(*theCompare)(theAttribute, aCurAttr)) { + theError = "Attributes have incorrect order"; + return false; + } + } + + return true; +} + + + +/// Global instance for validators factory +GeomValidators_GreaterOrEqual MY_GEQ_INSTANCE; +GeomValidators_LessOrEqual MY_LEQ_INSTANCE; + +GeomValidators_GreaterOrEqual::GeomValidators_GreaterOrEqual() +{ + // this validator is registered in the factory on this library loading + SessionPtr aMgr = ModelAPI_Session::get(); + ModelAPI_ValidatorsFactory* aFactory = aMgr->validators(); + aFactory->registerValidator("GeomValidators_GreaterOrEqual", this); +} + +bool GeomValidators_GreaterOrEqual::isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + return isValidOrder(theAttribute, theArguments, theError, &isGreaterOrEqual); +} + + + +GeomValidators_LessOrEqual::GeomValidators_LessOrEqual() +{ + // this validator is registered in the factory on this library loading + SessionPtr aMgr = ModelAPI_Session::get(); + ModelAPI_ValidatorsFactory* aFactory = aMgr->validators(); + aFactory->registerValidator("GeomValidators_LessOrEqual", this); +} + +bool GeomValidators_LessOrEqual::isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + return isValidOrder(theAttribute, theArguments, theError, &isLessOrEqual); +} diff --git a/src/GeomValidators/GeomValidators_ValueOrder.h b/src/GeomValidators/GeomValidators_ValueOrder.h new file mode 100644 index 000000000..f4dd4fb26 --- /dev/null +++ b/src/GeomValidators/GeomValidators_ValueOrder.h @@ -0,0 +1,63 @@ +// Copyright (C) 2017-20xx 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 GeomValidators_ValueOrder_H +#define GeomValidators_ValueOrder_H + +#include +#include + +/** + * Validates that the integer/double attribute is greater or equal to another attribute values + */ +class GeomValidators_GreaterOrEqual : public ModelAPI_AttributeValidator +{ +public: + //! Constructor for only one instance per application: will register the validator + GeomValidators_GreaterOrEqual(); + //! returns true if attribute is valid + //! \param[in] theAttribute the checked attribute + //! \param[in] theArguments arguments of the attribute + //! \param[out] theError error message. + GEOMVALIDATORS_EXPORT virtual bool isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const; + +}; + +/** + * Validates that the integer/double attribute is less or equal to another attribute values + */ +class GeomValidators_LessOrEqual : public ModelAPI_AttributeValidator +{ +public: + //! Constructor for only one instance per application: will register the validator + GeomValidators_LessOrEqual(); + //! returns true if attribute is valid + //! \param[in] theAttribute the checked attribute + //! \param[in] theArguments arguments of the attribute + //! \param[out] theError error message. + GEOMVALIDATORS_EXPORT virtual bool isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const; + +}; + +#endif diff --git a/src/ModelHighAPI/ModelHighAPI_Dumper.cpp b/src/ModelHighAPI/ModelHighAPI_Dumper.cpp index 20e8ff20f..89546d145 100644 --- a/src/ModelHighAPI/ModelHighAPI_Dumper.cpp +++ b/src/ModelHighAPI/ModelHighAPI_Dumper.cpp @@ -975,36 +975,56 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<( ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<( const std::shared_ptr& theAttrSelList) { - myDumpBuffer << "["; + static const int aThreshold = 2; + // if number of elements in the list if greater than a threshold, + // dump it in a separate line with specific name + std::string aDumped = myDumpBuffer.str(); - GeomShapePtr aShape; - std::string aShapeTypeStr; + if (aDumped.empty() || theAttrSelList->size() <= aThreshold) { + myDumpBuffer << "["; - bool isAdded = false; + GeomShapePtr aShape; + std::string aShapeTypeStr; - for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) { - AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex); - aShape = anAttribute->value(); - if(!aShape.get()) { - ResultPtr aContext = anAttribute->context(); - if (aContext.get()) - aShape = aContext->shape(); - } + bool isAdded = false; - if(!aShape.get()) { - continue; - } + for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) { + AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex); + aShape = anAttribute->value(); + if(!aShape.get()) { + ResultPtr aContext = anAttribute->context(); + if (aContext.get()) + aShape = aContext->shape(); + } - if(isAdded) { - myDumpBuffer << ", "; - } else { - isAdded = true; + if(!aShape.get()) { + continue; + } + + if(isAdded) { + myDumpBuffer << ", "; + } else { + isAdded = true; + } + myDumpBuffer << "model.selection(\"" << + aShape->shapeTypeStr() << "\", \"" << anAttribute->namingName() << "\")"; } - myDumpBuffer << "model.selection(\"" << - aShape->shapeTypeStr() << "\", \"" << anAttribute->namingName() << "\")"; - } - myDumpBuffer << "]"; + myDumpBuffer << "]"; + } else { + // clear buffer and store list "as is" + myDumpBuffer.str(""); + *this << theAttrSelList; + // save buffer and clear it again + std::string aDumpedList = myDumpBuffer.str(); + myDumpBuffer.str(""); + // obtain name of list + FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelList->owner()); + std::string aListName = name(anOwner) + "_objects"; + // store all previous data + myDumpBuffer << aListName << " = " << aDumpedList << std::endl + << aDumped << aListName; + } return *this; } diff --git a/src/PythonAPI/model/build/__init__.py b/src/PythonAPI/model/build/__init__.py index 1556669bf..ed30370e4 100644 --- a/src/PythonAPI/model/build/__init__.py +++ b/src/PythonAPI/model/build/__init__.py @@ -3,3 +3,4 @@ from BuildAPI import addVertex, addEdge, addWire, addFace, addShell from BuildAPI import addSubShapes +from BuildAPI import addFilling -- 2.30.2