From 46d76cd07aec0e520d4179139520c36bd7fb5c95 Mon Sep 17 00:00:00 2001 From: azv Date: Tue, 29 May 2018 13:32:34 +0300 Subject: [PATCH] Task 2.8. Measurement functions Implement feature to compute length, distance, radius or angle. --- src/FeaturesAPI/CMakeLists.txt | 2 + src/FeaturesAPI/FeaturesAPI.i | 1 + src/FeaturesAPI/FeaturesAPI_Measurement.cpp | 117 ++++++++ src/FeaturesAPI/FeaturesAPI_Measurement.h | 58 ++++ src/FeaturesAPI/FeaturesAPI_swig.h | 1 + src/FeaturesPlugin/CMakeLists.txt | 7 + .../FeaturesPlugin_Measurement.cpp | 253 ++++++++++++++++++ .../FeaturesPlugin_Measurement.h | 171 ++++++++++++ src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp | 5 + .../FeaturesPlugin_Validators.cpp | 18 ++ .../FeaturesPlugin_Validators.h | 15 ++ .../Test/TestMeasurementAngle.py | 95 +++++++ .../Test/TestMeasurementDistance.py | 131 +++++++++ .../Test/TestMeasurementLength.py | 84 ++++++ .../Test/TestMeasurementRadius.py | 79 ++++++ src/FeaturesPlugin/icons/edge.png | Bin 0 -> 312 bytes src/FeaturesPlugin/icons/meas_angle_32x32.png | Bin 0 -> 629 bytes .../icons/meas_distance_32x32.png | Bin 0 -> 525 bytes .../icons/meas_length_32x32.png | Bin 0 -> 489 bytes .../icons/meas_radius_32x32.png | Bin 0 -> 1097 bytes src/FeaturesPlugin/icons/measurement.png | Bin 0 -> 392 bytes src/FeaturesPlugin/measurement_widget.xml | 80 ++++++ src/FeaturesPlugin/plugin-Features.xml | 5 + src/GeomAPI/CMakeLists.txt | 2 + src/GeomAPI/GeomAPI_Angle.cpp | 90 +++++++ src/GeomAPI/GeomAPI_Angle.h | 47 ++++ src/GeomAPI/GeomAPI_Angle2d.h | 4 +- src/GeomAPI/GeomAPI_Edge.h | 3 +- src/GeomAPI/GeomAPI_Face.h | 3 + src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp | 27 ++ src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h | 8 + src/PythonAPI/model/features/__init__.py | 1 + 32 files changed, 1304 insertions(+), 3 deletions(-) create mode 100644 src/FeaturesAPI/FeaturesAPI_Measurement.cpp create mode 100644 src/FeaturesAPI/FeaturesAPI_Measurement.h create mode 100644 src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp create mode 100644 src/FeaturesPlugin/FeaturesPlugin_Measurement.h create mode 100644 src/FeaturesPlugin/Test/TestMeasurementAngle.py create mode 100644 src/FeaturesPlugin/Test/TestMeasurementDistance.py create mode 100644 src/FeaturesPlugin/Test/TestMeasurementLength.py create mode 100644 src/FeaturesPlugin/Test/TestMeasurementRadius.py create mode 100644 src/FeaturesPlugin/icons/edge.png create mode 100644 src/FeaturesPlugin/icons/meas_angle_32x32.png create mode 100644 src/FeaturesPlugin/icons/meas_distance_32x32.png create mode 100644 src/FeaturesPlugin/icons/meas_length_32x32.png create mode 100644 src/FeaturesPlugin/icons/meas_radius_32x32.png create mode 100644 src/FeaturesPlugin/icons/measurement.png create mode 100644 src/FeaturesPlugin/measurement_widget.xml create mode 100644 src/GeomAPI/GeomAPI_Angle.cpp create mode 100644 src/GeomAPI/GeomAPI_Angle.h diff --git a/src/FeaturesAPI/CMakeLists.txt b/src/FeaturesAPI/CMakeLists.txt index 1c5492242..83e853688 100644 --- a/src/FeaturesAPI/CMakeLists.txt +++ b/src/FeaturesAPI/CMakeLists.txt @@ -27,6 +27,7 @@ SET(PROJECT_HEADERS FeaturesAPI_ExtrusionBoolean.h FeaturesAPI_Fillet.h FeaturesAPI_Intersection.h + FeaturesAPI_Measurement.h FeaturesAPI_MultiRotation.h FeaturesAPI_MultiTranslation.h FeaturesAPI_Partition.h @@ -49,6 +50,7 @@ SET(PROJECT_SOURCES FeaturesAPI_ExtrusionBoolean.cpp FeaturesAPI_Fillet.cpp FeaturesAPI_Intersection.cpp + FeaturesAPI_Measurement.cpp FeaturesAPI_MultiRotation.cpp FeaturesAPI_MultiTranslation.cpp FeaturesAPI_Partition.cpp diff --git a/src/FeaturesAPI/FeaturesAPI.i b/src/FeaturesAPI/FeaturesAPI.i index 62afc3e48..01d1c9197 100644 --- a/src/FeaturesAPI/FeaturesAPI.i +++ b/src/FeaturesAPI/FeaturesAPI.i @@ -69,6 +69,7 @@ %include "FeaturesAPI_ExtrusionBoolean.h" %include "FeaturesAPI_Fillet.h" %include "FeaturesAPI_Intersection.h" +%include "FeaturesAPI_Measurement.h" %include "FeaturesAPI_MultiRotation.h" %include "FeaturesAPI_MultiTranslation.h" %include "FeaturesAPI_Partition.h" diff --git a/src/FeaturesAPI/FeaturesAPI_Measurement.cpp b/src/FeaturesAPI/FeaturesAPI_Measurement.cpp new file mode 100644 index 000000000..466aa0178 --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_Measurement.cpp @@ -0,0 +1,117 @@ +// Copyright (C) 2018-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 "FeaturesAPI_Measurement.h" + +#include +#include +#include +#include + +double measureLength(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theEdge) +{ + FeaturePtr aMeasure = thePart->addFeature(FeaturesPlugin_Measurement::ID()); + fillAttribute(FeaturesPlugin_Measurement::MEASURE_LENGTH(), + aMeasure->string(FeaturesPlugin_Measurement::MEASURE_KIND())); + fillAttribute(theEdge, aMeasure->selection(FeaturesPlugin_Measurement::EDGE_FOR_LENGTH_ID())); + aMeasure->execute(); + + // obtain result + AttributeDoubleArrayPtr aResult = std::dynamic_pointer_cast( + aMeasure->attribute(FeaturesPlugin_Measurement::RESULT_VALUES_ID())); + double aValue = aResult->size() ? aResult->value(0) : -1.0; + + // perform removing macro feature Measurement + thePart->removeFeature(aMeasure); + apply(); + + return aValue; +} + +double measureDistance(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theFrom, + const ModelHighAPI_Selection& theTo) +{ + FeaturePtr aMeasure = thePart->addFeature(FeaturesPlugin_Measurement::ID()); + fillAttribute(FeaturesPlugin_Measurement::MEASURE_DISTANCE(), + aMeasure->string(FeaturesPlugin_Measurement::MEASURE_KIND())); + fillAttribute(theFrom, + aMeasure->selection(FeaturesPlugin_Measurement::DISTANCE_FROM_OBJECT_ID())); + fillAttribute(theTo, aMeasure->selection(FeaturesPlugin_Measurement::DISTANCE_TO_OBJECT_ID())); + aMeasure->execute(); + + // obtain result + AttributeDoubleArrayPtr aResult = std::dynamic_pointer_cast( + aMeasure->attribute(FeaturesPlugin_Measurement::RESULT_VALUES_ID())); + double aValue = aResult->size() ? aResult->value(0) : -1.0; + + // perform removing macro feature Measurement + thePart->removeFeature(aMeasure); + apply(); + + return aValue; +} + +double measureRadius(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theObject) +{ + FeaturePtr aMeasure = thePart->addFeature(FeaturesPlugin_Measurement::ID()); + fillAttribute(FeaturesPlugin_Measurement::MEASURE_RADIUS(), + aMeasure->string(FeaturesPlugin_Measurement::MEASURE_KIND())); + fillAttribute(theObject, aMeasure->selection(FeaturesPlugin_Measurement::CIRCULAR_OBJECT_ID())); + aMeasure->execute(); + + // obtain result + AttributeDoubleArrayPtr aResult = std::dynamic_pointer_cast( + aMeasure->attribute(FeaturesPlugin_Measurement::RESULT_VALUES_ID())); + double aValue = aResult->size() ? aResult->value(0) : -1.0; + + // perform removing macro feature Measurement + thePart->removeFeature(aMeasure); + apply(); + + return aValue; +} + +std::list measureAngle(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theFrom, + const ModelHighAPI_Selection& theTo) +{ + FeaturePtr aMeasure = thePart->addFeature(FeaturesPlugin_Measurement::ID()); + fillAttribute(FeaturesPlugin_Measurement::MEASURE_ANGLE(), + aMeasure->string(FeaturesPlugin_Measurement::MEASURE_KIND())); + fillAttribute(theFrom, aMeasure->selection(FeaturesPlugin_Measurement::ANGLE_FROM_EDGE_ID())); + fillAttribute(theTo, aMeasure->selection(FeaturesPlugin_Measurement::ANGLE_TO_EDGE_ID())); + aMeasure->execute(); + + // obtain result + AttributeDoubleArrayPtr aResult = std::dynamic_pointer_cast( + aMeasure->attribute(FeaturesPlugin_Measurement::RESULT_VALUES_ID())); + std::list aValues; + for (int i = 0, n = aResult->size(); i < n; ++i) + aValues.push_back(aResult->value(i)); + + // perform removing macro feature Measurement + thePart->removeFeature(aMeasure); + apply(); + + return aValues; +} diff --git a/src/FeaturesAPI/FeaturesAPI_Measurement.h b/src/FeaturesAPI/FeaturesAPI_Measurement.h new file mode 100644 index 000000000..271201821 --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_Measurement.h @@ -0,0 +1,58 @@ +// Copyright (C) 2018-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 FeaturesAPI_Measurement_H_ +#define FeaturesAPI_Measurement_H_ + +#include "FeaturesAPI.h" + +#include +#include + +class ModelAPI_Document; +class ModelHighAPI_Selection; + +/// \ingroup CPPHighAPI +/// \brief Calculate length of the edge. +FEATURESAPI_EXPORT +double measureLength(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theEdge); + +/// \ingroup CPPHighAPI +/// \brief Calculate distance between objects. +FEATURESAPI_EXPORT +double measureDistance(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theFrom, + const ModelHighAPI_Selection& theTo); + +/// \ingroup CPPHighAPI +/// \brief Calculate radius of circular. +FEATURESAPI_EXPORT +double measureRadius(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theObject); + +/// \ingroup CPPHighAPI +/// \brief Calculate angle(s) between edges. +FEATURESAPI_EXPORT +std::list measureAngle(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theFrom, + const ModelHighAPI_Selection& theTo); + +#endif // FeaturesAPI_Measurement_H_ diff --git a/src/FeaturesAPI/FeaturesAPI_swig.h b/src/FeaturesAPI/FeaturesAPI_swig.h index 8b6ce0005..a45b780ca 100644 --- a/src/FeaturesAPI/FeaturesAPI_swig.h +++ b/src/FeaturesAPI/FeaturesAPI_swig.h @@ -29,6 +29,7 @@ #include "FeaturesAPI_ExtrusionBoolean.h" #include "FeaturesAPI_Fillet.h" #include "FeaturesAPI_Intersection.h" + #include "FeaturesAPI_Measurement.h" #include "FeaturesAPI_MultiRotation.h" #include "FeaturesAPI_MultiTranslation.h" #include "FeaturesAPI_Partition.h" diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index b0be8b412..ed5523231 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -52,6 +52,7 @@ SET(PROJECT_HEADERS FeaturesPlugin_MultiTranslation.h FeaturesPlugin_MultiRotation.h FeaturesPlugin_Fillet.h + FeaturesPlugin_Measurement.h ) SET(PROJECT_SOURCES @@ -84,6 +85,7 @@ SET(PROJECT_SOURCES FeaturesPlugin_MultiTranslation.cpp FeaturesPlugin_MultiRotation.cpp FeaturesPlugin_Fillet.cpp + FeaturesPlugin_Measurement.cpp ) SET(XML_RESOURCES @@ -109,6 +111,7 @@ SET(XML_RESOURCES multitranslation_widget.xml multirotation_widget.xml fillet_widget.xml + measurement_widget.xml ) SET(TEXT_RESOURCES @@ -219,6 +222,10 @@ ADD_UNIT_TESTS(TestExtrusion.py TestUnion4CurvedFaces.py TestUnion4Faces.py TestUnionOfUnion.py + TestMeasurementLength.py + TestMeasurementDistance.py + TestMeasurementRadius.py + TestMeasurementAngle.py Test1922.py Test1942.py Test1915.py diff --git a/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp b/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp new file mode 100644 index 000000000..0082c2ffd --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp @@ -0,0 +1,253 @@ +// Copyright (C) 2018-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 "FeaturesPlugin_Measurement.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +FeaturesPlugin_Measurement::FeaturesPlugin_Measurement() +{ +} + +void FeaturesPlugin_Measurement::initAttributes() +{ + data()->addAttribute(FeaturesPlugin_Measurement::MEASURE_KIND(), + ModelAPI_AttributeString::typeId()); + + // attribute for length + data()->addAttribute(EDGE_FOR_LENGTH_ID(), ModelAPI_AttributeSelection::typeId()); + // attributes for distance + data()->addAttribute(DISTANCE_FROM_OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); + data()->addAttribute(DISTANCE_TO_OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); + // attribute for radius + data()->addAttribute(CIRCULAR_OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); + // attribute for angle + data()->addAttribute(ANGLE_FROM_EDGE_ID(), ModelAPI_AttributeSelection::typeId()); + data()->addAttribute(ANGLE_TO_EDGE_ID(), ModelAPI_AttributeSelection::typeId()); + // attribute for result message and values + data()->addAttribute(RESULT_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(RESULT_VALUES_ID(), ModelAPI_AttributeDoubleArray::typeId()); +} + +void FeaturesPlugin_Measurement::execute() +{ +} + +void FeaturesPlugin_Measurement::attributeChanged(const std::string& theID) +{ + if (theID == MEASURE_KIND()) { + // clear selected objects + selection(EDGE_FOR_LENGTH_ID())->reset(); + selection(DISTANCE_FROM_OBJECT_ID())->reset(); + selection(DISTANCE_TO_OBJECT_ID())->reset(); + selection(CIRCULAR_OBJECT_ID())->reset(); + selection(ANGLE_FROM_EDGE_ID())->reset(); + selection(ANGLE_TO_EDGE_ID())->reset(); + string(RESULT_ID())->setValue(""); + std::dynamic_pointer_cast( + attribute(RESULT_VALUES_ID()))->setSize(0); + } + else if (theID != RESULT_ID()) { + std::string aKind = string(MEASURE_KIND())->value(); + if (aKind == MEASURE_LENGTH()) + computeLength(); + else if (aKind == MEASURE_DISTANCE()) + computeDistance(); + else if (aKind == MEASURE_RADIUS()) + computeRadius(); + else if (aKind == MEASURE_ANGLE()) + computeAngle(); + } +} + +void FeaturesPlugin_Measurement::computeLength() +{ + AttributeSelectionPtr aSelectedFeature = selection(EDGE_FOR_LENGTH_ID()); + + GeomShapePtr aShape; + GeomEdgePtr anEdge; + if (aSelectedFeature) + aShape = aSelectedFeature->value(); + if (!aShape && aSelectedFeature->context()) + aShape = aSelectedFeature->context()->shape(); + if (aShape && aShape->isEdge()) + anEdge = GeomEdgePtr(new GeomAPI_Edge(aShape)); + if (!anEdge) + return; + + std::ostringstream anOutput; + anOutput << "Length = " << std::setprecision(10) << anEdge->length(); + string(RESULT_ID())->setValue(anOutput.str()); + + AttributeDoubleArrayPtr aValues = + std::dynamic_pointer_cast(attribute(RESULT_VALUES_ID())); + aValues->setSize(1); + aValues->setValue(0, anEdge->length()); +} + +void FeaturesPlugin_Measurement::computeDistance() +{ + AttributeSelectionPtr aFirstFeature = selection(DISTANCE_FROM_OBJECT_ID()); + GeomShapePtr aShape1; + if (aFirstFeature) + aShape1 = aFirstFeature->value(); + if (!aShape1 && aFirstFeature->context()) + aShape1 = aFirstFeature->context()->shape(); + + AttributeSelectionPtr aSecondFeature = selection(DISTANCE_TO_OBJECT_ID()); + GeomShapePtr aShape2; + if (aSecondFeature) + aShape2 = aSecondFeature->value(); + if (!aShape2 && aSecondFeature->context()) + aShape2 = aSecondFeature->context()->shape(); + + if (!aShape1 || !aShape2) + return; + + double aDistance = GeomAlgoAPI_ShapeTools::minimalDistance(aShape1, aShape2); + + std::ostringstream anOutput; + anOutput << "Distance = " << std::setprecision(10) << aDistance; + string(RESULT_ID())->setValue(anOutput.str()); + + AttributeDoubleArrayPtr aValues = + std::dynamic_pointer_cast(attribute(RESULT_VALUES_ID())); + aValues->setSize(1); + aValues->setValue(0, aDistance); +} + +void FeaturesPlugin_Measurement::computeRadius() +{ + AttributeSelectionPtr aSelectedFeature = selection(CIRCULAR_OBJECT_ID()); + + GeomShapePtr aShape; + if (aSelectedFeature) + aShape = aSelectedFeature->value(); + if (!aShape && aSelectedFeature->context()) + aShape = aSelectedFeature->context()->shape(); + + double aRadius = -1.0; + if (aShape) { + if (aShape->isEdge()) { + GeomEdgePtr anEdge(new GeomAPI_Edge(aShape)); + if (anEdge->isCircle()) { + aRadius = anEdge->circle()->radius(); + } + } else if (aShape->isFace()) { + GeomFacePtr aFace(new GeomAPI_Face(aShape)); + aRadius = GeomAlgoAPI_ShapeTools::radius(aFace); + } + } + + if (aRadius < 0.0) + return; + + std::ostringstream anOutput; + anOutput << "Radius = " << std::setprecision(10) << aRadius; + string(RESULT_ID())->setValue(anOutput.str()); + + AttributeDoubleArrayPtr aValues = + std::dynamic_pointer_cast(attribute(RESULT_VALUES_ID())); + aValues->setSize(1); + aValues->setValue(0, aRadius); +} + +void FeaturesPlugin_Measurement::computeAngle() +{ + AttributeSelectionPtr aFirstFeature = selection(ANGLE_FROM_EDGE_ID()); + GeomShapePtr aShape1; + GeomEdgePtr anEdge1; + if (aFirstFeature) + aShape1 = aFirstFeature->value(); + if (!aShape1 && aFirstFeature->context()) + aShape1 = aFirstFeature->context()->shape(); + if (aShape1 && aShape1->isEdge()) + anEdge1 = GeomEdgePtr(new GeomAPI_Edge(aShape1)); + + + AttributeSelectionPtr aSecondFeature = selection(ANGLE_TO_EDGE_ID()); + GeomShapePtr aShape2; + GeomEdgePtr anEdge2; + if (aSecondFeature) + aShape2 = aSecondFeature->value(); + if (!aShape2 && aSecondFeature->context()) + aShape2 = aSecondFeature->context()->shape(); + if (aShape2 && aShape2->isEdge()) + anEdge2 = GeomEdgePtr(new GeomAPI_Edge(aShape2)); + + if (!anEdge1 || !anEdge2) + return; + + GeomShapePtr anInter = anEdge1->intersect(anEdge2); + + std::ostringstream anOutput; + anOutput << std::setprecision(10); + std::list aValuesList; + if (anInter) { + if (anInter->isVertex()) { + std::shared_ptr aVertex(new GeomAPI_Vertex(anInter)); + std::shared_ptr anAngle( + new GeomAPI_Angle(anEdge1, anEdge2, aVertex->point())); + double anAngleValue = anAngle->angleDegree(); + anOutput << "Angle = " << anAngleValue << std::endl; + aValuesList.push_back(anAngleValue); + } + else { + GeomAPI_ShapeIterator anIt(anInter); + for (int anIndex = 1; anIt.more(); anIt.next(), ++anIndex) { + GeomShapePtr aCurrent = anIt.current(); + if (!aCurrent->isVertex()) + continue; + std::shared_ptr aVertex(new GeomAPI_Vertex(aCurrent)); + std::shared_ptr anAngle( + new GeomAPI_Angle(anEdge1, anEdge2, aVertex->point())); + double anAngleValue = anAngle->angleDegree(); + anOutput << "Angle" << anIndex << " = " << anAngleValue << std::endl; + aValuesList.push_back(anAngleValue); + } + } + } + + string(RESULT_ID())->setValue(anOutput.str()); + + AttributeDoubleArrayPtr aValues = + std::dynamic_pointer_cast(attribute(RESULT_VALUES_ID())); + aValues->setSize((int)aValuesList.size()); + int anIndex = 0; + for (std::list::iterator anIt = aValuesList.begin(); anIt != aValuesList.end(); ++anIt) + aValues->setValue(anIndex++, *anIt); +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_Measurement.h b/src/FeaturesPlugin/FeaturesPlugin_Measurement.h new file mode 100644 index 000000000..a358155cf --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Measurement.h @@ -0,0 +1,171 @@ +// Copyright (C) 2018-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 FeaturesPlugin_Measurement_H_ +#define FeaturesPlugin_Measurement_H_ + +#include "FeaturesPlugin.h" +#include + +/// \class FeaturesPlugin_Measurement +/// \ingroup Plugins +/// \brief Feature for calculation metrics. +/// +/// Supported following metrics: +/// * length of edge, +/// * distance between shapes, +/// * radius of arc or cylindrical faces, +/// * angle between edges. +class FeaturesPlugin_Measurement : public ModelAPI_Feature +{ +public: + /// Feature kind. + inline static const std::string& ID() + { + static const std::string MY_ID("Measurement"); + return MY_ID; + } + + /// \return the kind of a feature. + virtual const std::string& getKind() + { + return ID(); + } + + /// Attribute name for measurement method. + inline static const std::string& MEASURE_KIND() + { + static const std::string MY_MEASURE_KIND_ID("MeasureKind"); + return MY_MEASURE_KIND_ID; + } + + /// Attribute name for length measure. + inline static const std::string& MEASURE_LENGTH() + { + static const std::string MY_MEASURE_ID("Length"); + return MY_MEASURE_ID; + } + + /// Attribute name for distance measure. + inline static const std::string& MEASURE_DISTANCE() + { + static const std::string MY_MEASURE_ID("Distance"); + return MY_MEASURE_ID; + } + + /// Attribute name for radius measure. + inline static const std::string& MEASURE_RADIUS() + { + static const std::string MY_MEASURE_ID("Radius"); + return MY_MEASURE_ID; + } + + /// Attribute name for angle measure. + inline static const std::string& MEASURE_ANGLE() + { + static const std::string MY_MEASURE_ID("Angle"); + return MY_MEASURE_ID; + } + + + /// Attribute name of edge selected for length calculation. + inline static const std::string& EDGE_FOR_LENGTH_ID() + { + static const std::string MY_EDGE_FOR_LENGTH_ID("edge_for_length"); + return MY_EDGE_FOR_LENGTH_ID; + } + + /// Attribute name of first shape selected for distance calculation. + inline static const std::string& DISTANCE_FROM_OBJECT_ID() + { + static const std::string MY_DISTANCE_FROM_OBJECT_ID("distance_from"); + return MY_DISTANCE_FROM_OBJECT_ID; + } + + /// Attribute name of second shape selected for distance calculation. + inline static const std::string& DISTANCE_TO_OBJECT_ID() + { + static const std::string MY_DISTANCE_TO_OBJECT_ID("distance_to"); + return MY_DISTANCE_TO_OBJECT_ID; + } + + // Attribute name of edge or face selected to calculate radius. + inline static const std::string& CIRCULAR_OBJECT_ID() + { + static const std::string MY_CIRCULAR_OBJECT_ID("circular"); + return MY_CIRCULAR_OBJECT_ID; + } + + /// Attribute name of first edge selected for angle calculation. + inline static const std::string& ANGLE_FROM_EDGE_ID() + { + static const std::string MY_ANGLE_FROM_EDGE_ID("angle_from"); + return MY_ANGLE_FROM_EDGE_ID; + } + + /// Attribute name of second shape selected for distance calculation. + inline static const std::string& ANGLE_TO_EDGE_ID() + { + static const std::string MY_ANGLE_TO_EDGE_ID("angle_to"); + return MY_ANGLE_TO_EDGE_ID; + } + + /// Attribute name for result. + inline static const std::string& RESULT_ID() + { + static const std::string MY_RESULT_ID("result"); + return MY_RESULT_ID; + } + + /// Attribute name for values of result. + inline static const std::string& RESULT_VALUES_ID() + { + static const std::string MY_RESULT_VALUES_ID("result_values"); + return MY_RESULT_VALUES_ID; + } + + /// Creates a new part document if needed + FEATURESPLUGIN_EXPORT virtual void execute(); + + /// Request for initialization of data model of the feature: adding all attributes + FEATURESPLUGIN_EXPORT virtual void initAttributes(); + + /// Called on change of any argument-attribute of this object + /// \param theID identifier of changed attribute + FEATURESPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID); + + /// Reimplemented from ModelAPI_Feature::isMacro(). Returns true. + virtual bool isMacro() const { return true; } + + /// Use plugin manager for features creation + FeaturesPlugin_Measurement(); + +private: + /// Compute length of the edge + void computeLength(); + /// Compute minimal distance between pair of shapes + void computeDistance(); + /// Compute radius of circular edge or cylindrical face + void computeRadius(); + /// Compute angle(s) between pair of edges if they are intersected + void computeAngle(); +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp index b9760dc07..2a8023433 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,8 @@ FeaturesPlugin_Plugin::FeaturesPlugin_Plugin() new FeaturesPlugin_ValidatorConcealedResult); aFactory->registerValidator("FeaturesPlugin_ValidatorFilletSelection", new FeaturesPlugin_ValidatorFilletSelection); + aFactory->registerValidator("FeaturesPlugin_ValidatorCircular", + new FeaturesPlugin_ValidatorCircular); // register this plugin ModelAPI_Session::get()->registerPlugin(this); @@ -136,6 +139,8 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new FeaturesPlugin_MultiRotation); } else if (theFeatureID == FeaturesPlugin_Fillet::ID()) { return FeaturePtr(new FeaturesPlugin_Fillet); + } else if (theFeatureID == FeaturesPlugin_Measurement::ID()) { + return FeaturePtr(new FeaturesPlugin_Measurement); } // feature of such kind is not found diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp index b4ca4332f..5a06ceaac 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -998,3 +999,20 @@ bool FeaturesPlugin_ValidatorConcealedResult::isValid(const AttributePtr& theAtt return theError.empty(); } + +bool FeaturesPlugin_ValidatorCircular::isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + static std::list aEdgeArg(1, "circle"); + static std::list aFaceArg(1, "cylinder"); + + Events_InfoMessage aError; + bool isValid = GeomValidators_ShapeType().isValid(theAttribute, aEdgeArg, aError); + if (!isValid) { + isValid = GeomValidators_Face().isValid(theAttribute, aFaceArg, aError); + if (!isValid) + theError = "The shape neither circle nor cylinder"; + } + return isValid; +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.h b/src/FeaturesPlugin/FeaturesPlugin_Validators.h index b54b3f0be..5824ce9dc 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.h @@ -252,4 +252,19 @@ class FeaturesPlugin_ValidatorConcealedResult: public ModelAPI_AttributeValidato Events_InfoMessage& theError) const; }; +/// \class FeaturesPlugin_ValidatorCircular +/// \ingroup Validators +/// \brief Verifies the selected object is circular edge or cylindrical face +class FeaturesPlugin_ValidatorCircular : public ModelAPI_AttributeValidator +{ +public: + //! \return True if the attribute is valid. + //! \param[in] theAttribute the checked attribute. + //! \param[in] theArguments arguments of the attribute. + //! \param[out] theError error message. + virtual bool isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const; +}; + #endif diff --git a/src/FeaturesPlugin/Test/TestMeasurementAngle.py b/src/FeaturesPlugin/Test/TestMeasurementAngle.py new file mode 100644 index 000000000..d00f1c842 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestMeasurementAngle.py @@ -0,0 +1,95 @@ +## Copyright (C) 2018-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 +## + +from SketchAPI import * + +from salome.shaper import model + +import math + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +ParamR = model.addParameter(Part_1_doc, "R", "50") +ParamL = model.addParameter(Part_1_doc, "L", "40") +ParamH = model.addParameter(Part_1_doc, "H", "20") +Point_2 = model.addPoint(Part_1_doc, 0, 100, 100) +Plane_4 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OX"), model.selection("VERTEX", "Point_1"), False) +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchCircle_1 = Sketch_1.addCircle(50, 50, 50) +SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], "R") +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_1 = SketchProjection_1.createdFeature() +SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_1.result(), SketchCircle_1.results()[1]) +SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_2 = SketchProjection_2.createdFeature() +SketchConstraintTangent_2 = Sketch_1.setTangent(SketchLine_2.result(), SketchCircle_1.results()[1]) +SketchLine_3 = Sketch_1.addLine(70, 40, 30, 40) +SketchLine_4 = Sketch_1.addLine(30, 40, 30, 60) +SketchLine_5 = Sketch_1.addLine(30, 60, 70, 60) +SketchLine_6 = Sketch_1.addLine(70, 60, 70, 40) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint()) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result()) +SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result()) +SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result()) +SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result()) +SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_3.result(), "L") +SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_4.result(), "H") +SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "L/2") +SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "H/2") +model.do() +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f")], model.selection(), model.selection("FACE", "Plane_1"), 0, model.selection(), 10) +Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face_1")) +SketchLine_7 = Sketch_2.addLine(119.1454520140253, 130.0744845530344, 10, 0) +SketchProjection_3 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_8 = SketchProjection_3.createdFeature() +SketchConstraintAngle_1 = Sketch_2.setAngle(SketchLine_8.result(), SketchLine_7.result(), 50) +SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchLine_7.endPoint(), SketchLine_8.result()) +SketchConstraintDistanceHorizontal_2 = Sketch_2.setHorizontalDistance(SketchAPI_Line(SketchLine_8).startPoint(), SketchLine_7.endPoint(), 10) +model.do() + +TOLERANCE = 1.e-6 + +# reference data +REF_DATA = [("Sketch_1/Edge-SketchLine_3", "Sketch_1/Edge-SketchLine_6", [90]), + ("Extrusion_1_1/Generated_Face_5", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", [45]), + ("Sketch_1/Edge-SketchCircle_1_2", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", [0]), + ("Sketch_1/Edge-SketchLine_3", "Sketch_1/Edge-SketchCircle_1_2", []), + ("Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", []), + ("Sketch_2/Edge-SketchLine_7", "Extrusion_1_1/To_Face_1&Extrusion_1_1/Generated_Face_4", [130]), + ("Sketch_2/Edge-SketchLine_7", "Extrusion_1_1/To_Face_1&Extrusion_1_1/Generated_Face_1", [140]), + ("Sketch_2/Edge-SketchLine_7", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", [85.95645561, 95.19768874]) + ] + +for ref in REF_DATA: + angle = model.measureAngle(Part_1_doc, model.selection("EDGE", ref[0]), model.selection("EDGE", ref[1])) + assert(angle.size() == len(ref[2])) + for a, r in zip(angle, ref[2]): + assert(math.fabs(a - r) < TOLERANCE), "Angle {} differs from expected value {}".format(a, r) + +# select incorrect data +angle = model.measureAngle(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face_1"), model.selection("EDGE", "Sketch_2/Edge-SketchLine_7")) +assert(angle.size() == 0) + +model.end() diff --git a/src/FeaturesPlugin/Test/TestMeasurementDistance.py b/src/FeaturesPlugin/Test/TestMeasurementDistance.py new file mode 100644 index 000000000..c2b7d0a6a --- /dev/null +++ b/src/FeaturesPlugin/Test/TestMeasurementDistance.py @@ -0,0 +1,131 @@ +## Copyright (C) 2018-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 +## + +from salome.shaper import model + +import math + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +ParamR = model.addParameter(Part_1_doc, "R", "50") +ParamL = model.addParameter(Part_1_doc, "L", "40") +ParamH = model.addParameter(Part_1_doc, "H", "20") +Point_2 = model.addPoint(Part_1_doc, 0, 100, 100) +Plane_4 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OX"), model.selection("VERTEX", "Point_1"), False) +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchCircle_1 = Sketch_1.addCircle(50, 50, 50) +SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], "R") +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_1 = SketchProjection_1.createdFeature() +SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_1.result(), SketchCircle_1.results()[1]) +SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_2 = SketchProjection_2.createdFeature() +SketchConstraintTangent_2 = Sketch_1.setTangent(SketchLine_2.result(), SketchCircle_1.results()[1]) +SketchLine_3 = Sketch_1.addLine(70, 40, 30, 40) +SketchLine_4 = Sketch_1.addLine(30, 40, 30, 60) +SketchLine_5 = Sketch_1.addLine(30, 60, 70, 60) +SketchLine_6 = Sketch_1.addLine(70, 60, 70, 40) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint()) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result()) +SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result()) +SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result()) +SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result()) +SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_3.result(), "L") +SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_4.result(), "H") +SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "L/2") +SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "H/2") +model.do() +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f")], model.selection(), model.selection("FACE", "Plane_1"), 0, model.selection(), 10) +Sketch_2 = model.addSketch(Part_1_doc, model.standardPlane("YOZ")) +SketchArc_1 = Sketch_2.addArc(100, 48.41229182762603, 87.5, 0, 87.5, 96.82458365525073, False) +SketchArc_2 = Sketch_2.addArc(0, 48.41229182762603, 87.5, 0, 87.5, 96.82458365525073, False) +SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchArc_1.startPoint(), SketchArc_2.startPoint()) +SketchConstraintCoincidence_6 = Sketch_2.setCoincident(SketchArc_2.endPoint(), SketchArc_1.endPoint()) +SketchConstraintRadius_2 = Sketch_2.setRadius(SketchArc_1.results()[1], "R") +SketchConstraintRadius_3 = Sketch_2.setRadius(SketchArc_2.results()[1], "2*R") +SketchLine_7 = Sketch_2.addLine(0, 48.41229182762603, 100, 48.41229182762603) +SketchLine_7.setAuxiliary(True) +SketchConstraintCoincidence_7 = Sketch_2.setCoincident(SketchArc_2.center(), SketchLine_7.startPoint()) +SketchConstraintCoincidence_8 = Sketch_2.setCoincident(SketchArc_1.center(), SketchLine_7.endPoint()) +SketchConstraintHorizontal_3 = Sketch_2.setHorizontal(SketchLine_7.result()) +SketchProjection_3 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_8 = SketchProjection_3.createdFeature() +SketchConstraintCoincidence_9 = Sketch_2.setCoincident(SketchArc_1.startPoint(), SketchLine_8.result()) +SketchProjection_4 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OZ"), False) +SketchLine_9 = SketchProjection_4.createdFeature() +SketchConstraintCoincidence_10 = Sketch_2.setCoincident(SketchLine_7.startPoint(), SketchLine_9.result()) +SketchConstraintCoincidence_11 = Sketch_2.setCoincident(SketchLine_7.endPoint(), SketchArc_2.results()[1]) +SketchConstraintMirror_1 = Sketch_2.addMirror(SketchLine_9.result(), [SketchArc_2.results()[1], SketchArc_1.results()[1]]) +[SketchArc_3, SketchArc_4] = SketchConstraintMirror_1.mirrored() +model.do() +Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_2")], model.selection(), 100, 0) +model.do() + +TOLERANCE = 1.e-6 + +# reference data +REF_DATA = [(model.selection("VERTEX", "PartSet/Origin"), + model.selection("VERTEX", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1"), + 122.4744871), + (model.selection("EDGE", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1"), + model.selection("VERTEX", "Sketch_2/Vertex-SketchArc_1-SketchLine_7e"), + 36.94403089), + (model.selection("EDGE", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1"), + model.selection("EDGE", "Extrusion_1_1/Generated_Face_5"), + 0), + (model.selection("EDGE", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1"), + model.selection("EDGE", "Extrusion_2_1/Generated_Face_2&Extrusion_2_1/To_Face_1"), + 16.00781059), + (model.selection("EDGE", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1"), + model.selection("FACE", "Extrusion_2_1/Generated_Face_2"), + 8.412291828), + (model.selection("VERTEX", "Sketch_1/Vertex-SketchCircle_1"), + model.selection("FACE", "Plane_1"), + 35.35533906), + (model.selection("FACE", "Extrusion_2_2/From_Face_1"), + model.selection("FACE", "Extrusion_2_2/To_Face_1"), + 100), + (model.selection("FACE", "Extrusion_1_1/Generated_Face_5"), + model.selection("FACE", "Extrusion_2_1/Generated_Face_2"), + 0), + (model.selection("FACE", "Extrusion_1_1/Generated_Face_5"), + model.selection("FACE", "Extrusion_1_1/Generated_Face_2"), + 27.63932023), + (model.selection("SOLID", "Extrusion_1_1"), + model.selection("FACE", "Extrusion_2_1/To_Face_1"), + 12.5), + (model.selection("SOLID", "Extrusion_1_1"), + model.selection("SOLID", "Extrusion_2_1"), + 0), + (model.selection("SOLID", "Extrusion_1_1"), + model.selection("SOLID", "Extrusion_2_2"), + 87.5) + ] + +for ref in REF_DATA: + dist = model.measureDistance(Part_1_doc, ref[0], ref[1]) + assert(math.fabs(dist - ref[2]) < TOLERANCE), "Distance {} differs from expected value {}".format(dist, ref[2]) + +model.end() diff --git a/src/FeaturesPlugin/Test/TestMeasurementLength.py b/src/FeaturesPlugin/Test/TestMeasurementLength.py new file mode 100644 index 000000000..f557f8fde --- /dev/null +++ b/src/FeaturesPlugin/Test/TestMeasurementLength.py @@ -0,0 +1,84 @@ +## Copyright (C) 2018-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 +## + +from salome.shaper import model + +import math + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +ParamR = model.addParameter(Part_1_doc, "R", "50") +ParamL = model.addParameter(Part_1_doc, "L", "40") +ParamH = model.addParameter(Part_1_doc, "H", "20") +Point_2 = model.addPoint(Part_1_doc, 0, 100, 100) +Plane_4 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OX"), model.selection("VERTEX", "Point_1"), False) +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchCircle_1 = Sketch_1.addCircle(50, 50, 50) +SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], "R") +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_1 = SketchProjection_1.createdFeature() +SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_1.result(), SketchCircle_1.results()[1]) +SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_2 = SketchProjection_2.createdFeature() +SketchConstraintTangent_2 = Sketch_1.setTangent(SketchLine_2.result(), SketchCircle_1.results()[1]) +SketchLine_3 = Sketch_1.addLine(70, 40, 30, 40) +SketchLine_4 = Sketch_1.addLine(30, 40, 30, 60) +SketchLine_5 = Sketch_1.addLine(30, 60, 70, 60) +SketchLine_6 = Sketch_1.addLine(70, 60, 70, 40) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint()) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result()) +SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result()) +SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result()) +SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result()) +SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_3.result(), "L") +SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_4.result(), "H") +SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "L/2") +SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "H/2") +model.do() +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f")], model.selection(), model.selection("FACE", "Plane_1"), 0, model.selection(), 10) +model.do() + +TOLERANCE = 1.e-6 + +# reference data +REF_DATA = [("Extrusion_1_1/From_Face_1&Extrusion_1_1/Generated_Face_1", ParamH.value()), + ("Extrusion_1_1/From_Face_1&Extrusion_1_1/Generated_Face_4", ParamL.value()), + ("Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", ParamR.value() * 2.0 * math.pi), + ("Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", 381.981436664), + ("Extrusion_1_1/Generated_Face_5", 60), + ("Sketch_1/Edge-SketchLine_3", ParamL.value()), + ("Sketch_1/Edge-SketchLine_4", ParamH.value()), + ("Sketch_1/Edge-SketchCircle_1_2", ParamR.value() * 2.0 * math.pi) + ] + +for ref in REF_DATA: + length = model.measureLength(Part_1_doc, model.selection("EDGE", ref[0])) + assert(math.fabs(length - ref[1]) < TOLERANCE), "Length {} differs from expected value {}".format(length, ref[1]) + +# select incorrect data +length = model.measureLength(Part_1_doc, model.selection("FACE", "Extrusion_1_1/Generated_Face_5")) +assert(length == -1) + +model.end() diff --git a/src/FeaturesPlugin/Test/TestMeasurementRadius.py b/src/FeaturesPlugin/Test/TestMeasurementRadius.py new file mode 100644 index 000000000..a39927d46 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestMeasurementRadius.py @@ -0,0 +1,79 @@ +## Copyright (C) 2018-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 +## + +from salome.shaper import model + +import math + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +ParamR = model.addParameter(Part_1_doc, "R", "50") +ParamL = model.addParameter(Part_1_doc, "L", "40") +ParamH = model.addParameter(Part_1_doc, "H", "20") +Point_2 = model.addPoint(Part_1_doc, 0, 100, 100) +Plane_4 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OX"), model.selection("VERTEX", "Point_1"), False) +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchCircle_1 = Sketch_1.addCircle(50, 50, 50) +SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], "R") +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_1 = SketchProjection_1.createdFeature() +SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_1.result(), SketchCircle_1.results()[1]) +SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_2 = SketchProjection_2.createdFeature() +SketchConstraintTangent_2 = Sketch_1.setTangent(SketchLine_2.result(), SketchCircle_1.results()[1]) +SketchLine_3 = Sketch_1.addLine(70, 40, 30, 40) +SketchLine_4 = Sketch_1.addLine(30, 40, 30, 60) +SketchLine_5 = Sketch_1.addLine(30, 60, 70, 60) +SketchLine_6 = Sketch_1.addLine(70, 60, 70, 40) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint()) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result()) +SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result()) +SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result()) +SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result()) +SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_3.result(), "L") +SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_4.result(), "H") +SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "L/2") +SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "H/2") +model.do() +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f")], model.selection(), model.selection("FACE", "Plane_1"), 0, model.selection(), 10) +model.do() + +TOLERANCE = 1.e-6 + +# reference data +REF_DATA = [(model.selection("EDGE", "Sketch_1/Edge-SketchCircle_1_2"), ParamR.value()), + (model.selection("EDGE", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1"), ParamR.value()), + (model.selection("FACE", "Extrusion_1_1/Generated_Face_5"), ParamR.value()) + ] + +for ref in REF_DATA: + radius = model.measureRadius(Part_1_doc, ref[0]) + assert(math.fabs(radius - ref[1]) < TOLERANCE), "Radius {} differs from expected value {}".format(radius, ref[1]) + +# select incorrect data +radius = model.measureRadius(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face_1")) +assert(radius == -1) + +model.end() diff --git a/src/FeaturesPlugin/icons/edge.png b/src/FeaturesPlugin/icons/edge.png new file mode 100644 index 0000000000000000000000000000000000000000..53dbb08209ac7146945737f458421bba72069360 GIT binary patch literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4f%C91l5uNKibeP{t#q*Ko9uk%z7GxX%ZHx0BQk zTst1T-glqzopr0JVg2 A<}^PL!4&s0}x3dGT9vfkpyrI%)}26m@h|V-WVV-PXmQ`0V>V?D9!5w z)S7!h3Xx?*rV$x%36Lo7Bz->#ECBz@hCf=Ml3LvtfE2tJw-NKv+fcAg-FOqbHF_&kd4L0*#!=&;#VAhWHU?S^Y=|wjHszpRHgjZ4?a0X;-gaurlf$%Kq z+3^ZWxQ6YxX`rZp zms30m;VpwOZO5$ub>KpSbb-!j(DM`Gd%$E3fQioa5UHWma6Z7$;!WUGdlRV7i3e+l z#}*_YQj;)=I^PGr0IV=PI`Q9cLc}om^{W=(8j-7v!eyYs?9;sgo~4u(h)f&Wi9OH- zK7bNX3Jl}*>EQ6vK%E0GMC4<@2ihogoXMfBbH=*HH&9D<9|LrN7VxOO1$6X*sp-V* z3n*~}U&3=E8Eo-U3d z5v^~hJ7yhn5OKIKZxrc~xm)uK(_7IyWx3{Z3%jeB`JbJ#o^o%IG^?g=luP!k`>n}7 z9m%I%jHK;(QWE~v_dhw`-myx^*eAp)L7>v@iRi@T*H)-5)7ZnPY2f~KpK4=M<{jn( zOP_DZW%%?|?95|Zg-fh#2bl~CO!qt#zoFLG=*Qg1XJDsR7?CjBAd5rayQfB|w9(dv zb(1hh%DQWgovR!~_gOO5h)d6J(eP=M_Fu#hf51rK4x=8!{T*{%L4w|^R~rOYl>cr$ z6cEZ8Wl&ka9RdyC{)<5K{indo2ySicKgVUm#GwS0H2mG)vUVOfB^MOc(T@80^ zZPp2e{%%g0f30o{>FVdQ&MBb@02#Buwg3PC literal 0 HcmV?d00001 diff --git a/src/FeaturesPlugin/icons/meas_radius_32x32.png b/src/FeaturesPlugin/icons/meas_radius_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..a3c70fca4045a53c97c2cf553c1f1c505a241460 GIT binary patch literal 1097 zcmV-P1h)H$P)3gp$&GxQ8KzxN0%5Haht|jq$6XQS-6W@vce+6*6 z({l*%b)aK=*B>w%1Vah3#;R*0_op=qZKB}KIBd`=I0@rH|zVGEC;^t8O@FH$~-^(F# z(j6*>FwGp}NTv58q!+;{+!Z6}X1qm==AV~=B)6cOMvPgie@X_$aZ=A*+;LKf4AvVm zOQxsGS_Mv)T_u;niad9ZXIgU~Xxl+?lS9i0DJ=X2*qbAfgXD)76o)&WJDW;WUZobv*wUDEbSk=w_#Q P00000NkvXXu0mjfr;+>r literal 0 HcmV?d00001 diff --git a/src/FeaturesPlugin/icons/measurement.png b/src/FeaturesPlugin/icons/measurement.png new file mode 100644 index 0000000000000000000000000000000000000000..c93fa62b2b98672db4c8386f5925ac32202d799d GIT binary patch literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4Ake!1`2qpOrKN{tEn3VwjvdPNJ7`$cBE!Pza@^l_;$)VKR)<*MEMagz z7OAwRBYfY??{Ck%(<(d6l(2b*%>@U4{YXXuQ_j{BX@~XStLl5jSxRE3`uxu)YiCuwOP}%3!Sd6}3yfwCRR|1ybKc4;`|9)G{yVrFe)Z3(- g@3}e%|CRp08`+SgP-||@2n;m_Pgg&ebxsLQ00i-yHUIzs literal 0 HcmV?d00001 diff --git a/src/FeaturesPlugin/measurement_widget.xml b/src/FeaturesPlugin/measurement_widget.xml new file mode 100644 index 000000000..22c557b1d --- /dev/null +++ b/src/FeaturesPlugin/measurement_widget.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +