From 1050654afdb823cd8e3a6e03f129dfe65fea9d3f Mon Sep 17 00:00:00 2001 From: azv Date: Fri, 6 Jul 2018 10:04:12 +0300 Subject: [PATCH] Task 2.8. Measurement functions Add new measurement type: angle by three points. --- src/FeaturesAPI/FeaturesAPI_Measurement.cpp | 25 ++++ src/FeaturesAPI/FeaturesAPI_Measurement.h | 8 ++ .../FeaturesPlugin_Measurement.cpp | 58 +++++++++- .../FeaturesPlugin_Measurement.h | 32 ++++- .../Test/TestMeasurementAngle3Points.py | 109 ++++++++++++++++++ .../icons/meas_angle3p_32x32.png | Bin 0 -> 974 bytes src/FeaturesPlugin/measurement_widget.xml | 26 +++++ src/GeomAPI/GeomAPI_Angle.cpp | 14 +++ src/GeomAPI/GeomAPI_Angle.h | 6 + 9 files changed, 273 insertions(+), 5 deletions(-) create mode 100644 src/FeaturesPlugin/Test/TestMeasurementAngle3Points.py create mode 100644 src/FeaturesPlugin/icons/meas_angle3p_32x32.png diff --git a/src/FeaturesAPI/FeaturesAPI_Measurement.cpp b/src/FeaturesAPI/FeaturesAPI_Measurement.cpp index 466aa0178..400296314 100644 --- a/src/FeaturesAPI/FeaturesAPI_Measurement.cpp +++ b/src/FeaturesAPI/FeaturesAPI_Measurement.cpp @@ -115,3 +115,28 @@ std::list measureAngle(const std::shared_ptr& thePart return aValues; } + +double measureAngle(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& thePoint1, + const ModelHighAPI_Selection& thePoint2, + const ModelHighAPI_Selection& thePoint3) +{ + FeaturePtr aMeasure = thePart->addFeature(FeaturesPlugin_Measurement::ID()); + fillAttribute(FeaturesPlugin_Measurement::MEASURE_ANGLE_POINTS(), + aMeasure->string(FeaturesPlugin_Measurement::MEASURE_KIND())); + fillAttribute(thePoint1, aMeasure->selection(FeaturesPlugin_Measurement::ANGLE_POINT1_ID())); + fillAttribute(thePoint2, aMeasure->selection(FeaturesPlugin_Measurement::ANGLE_POINT2_ID())); + fillAttribute(thePoint3, aMeasure->selection(FeaturesPlugin_Measurement::ANGLE_POINT3_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; +} diff --git a/src/FeaturesAPI/FeaturesAPI_Measurement.h b/src/FeaturesAPI/FeaturesAPI_Measurement.h index 271201821..774f7f98e 100644 --- a/src/FeaturesAPI/FeaturesAPI_Measurement.h +++ b/src/FeaturesAPI/FeaturesAPI_Measurement.h @@ -55,4 +55,12 @@ std::list measureAngle(const std::shared_ptr& thePart const ModelHighAPI_Selection& theFrom, const ModelHighAPI_Selection& theTo); +/// \ingroup CPPHighAPI +/// \brief Calculate angle by 3 points. +FEATURESAPI_EXPORT +double measureAngle(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& thePoint1, + const ModelHighAPI_Selection& thePoint2, + const ModelHighAPI_Selection& thePoint3); + #endif // FeaturesAPI_Measurement_H_ diff --git a/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp b/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp index 4865abfd9..33599bdd3 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -55,10 +56,14 @@ void FeaturesPlugin_Measurement::initAttributes() data()->addAttribute(DISTANCE_TO_OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); // attribute for radius data()->addAttribute(CIRCULAR_OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); - // attribute for angle + // attributes 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 + // attributes for angle by 3 points + data()->addAttribute(ANGLE_POINT1_ID(), ModelAPI_AttributeSelection::typeId()); + data()->addAttribute(ANGLE_POINT2_ID(), ModelAPI_AttributeSelection::typeId()); + data()->addAttribute(ANGLE_POINT3_ID(), ModelAPI_AttributeSelection::typeId()); + // attributes for result message and values data()->addAttribute(RESULT_ID(), ModelAPI_AttributeString::typeId()); data()->addAttribute(RESULT_VALUES_ID(), ModelAPI_AttributeDoubleArray::typeId()); } @@ -77,6 +82,9 @@ void FeaturesPlugin_Measurement::attributeChanged(const std::string& theID) selection(CIRCULAR_OBJECT_ID())->reset(); selection(ANGLE_FROM_EDGE_ID())->reset(); selection(ANGLE_TO_EDGE_ID())->reset(); + selection(ANGLE_POINT1_ID())->reset(); + selection(ANGLE_POINT2_ID())->reset(); + selection(ANGLE_POINT3_ID())->reset(); string(RESULT_ID())->setValue(""); std::dynamic_pointer_cast( attribute(RESULT_VALUES_ID()))->setSize(0); @@ -91,6 +99,8 @@ void FeaturesPlugin_Measurement::attributeChanged(const std::string& theID) computeRadius(); else if (aKind == MEASURE_ANGLE()) computeAngle(); + else if (aKind == MEASURE_ANGLE_POINTS()) + computeAngleByPoints(); } } @@ -236,7 +246,7 @@ void FeaturesPlugin_Measurement::computeAngle() std::shared_ptr anAngle( new GeomAPI_Angle(anEdge1, anEdge2, aVertex->point())); double anAngleValue = anAngle->angleDegree(); - anOutput << "Angle = " << anAngleValue << std::endl; + anOutput << "Angle = " << std::setprecision(10) << anAngleValue << std::endl; aValuesList.push_back(anAngleValue); } else { @@ -249,7 +259,8 @@ void FeaturesPlugin_Measurement::computeAngle() std::shared_ptr anAngle( new GeomAPI_Angle(anEdge1, anEdge2, aVertex->point())); double anAngleValue = anAngle->angleDegree(); - anOutput << "Angle" << anIndex << " = " << anAngleValue << std::endl; + anOutput << "Angle" << anIndex << " = " + << std::setprecision(10) << anAngleValue << std::endl; aValuesList.push_back(anAngleValue); } } @@ -264,3 +275,42 @@ void FeaturesPlugin_Measurement::computeAngle() for (std::list::iterator anIt = aValuesList.begin(); anIt != aValuesList.end(); ++anIt) aValues->setValue(anIndex++, *anIt); } + +static GeomVertexPtr selectionToVertex(AttributeSelectionPtr& aSelection) +{ + GeomShapePtr aShape; + GeomVertexPtr aVertex; + if (aSelection && aSelection->isInitialized()) { + aShape = aSelection->value(); + if (!aShape && aSelection->context()) + aShape = aSelection->context()->shape(); + } + if (aShape && aShape->isVertex()) + aVertex = GeomVertexPtr(new GeomAPI_Vertex(aShape)); + return aVertex; +} + +void FeaturesPlugin_Measurement::computeAngleByPoints() +{ + GeomVertexPtr aVertex1 = selectionToVertex(selection(ANGLE_POINT1_ID())); + GeomVertexPtr aVertex2 = selectionToVertex(selection(ANGLE_POINT2_ID())); + GeomVertexPtr aVertex3 = selectionToVertex(selection(ANGLE_POINT3_ID())); + + if (!aVertex1 || !aVertex2 || ! aVertex3) { + string(RESULT_ID())->setValue(""); + return; + } + + std::shared_ptr anAngle( + new GeomAPI_Angle(aVertex1->point(), aVertex2->point(), aVertex3->point())); + double anAngleValue = anAngle->angleDegree(); + + std::ostringstream anOutput; + anOutput << "Angle = " << std::setprecision(10) << anAngleValue; + string(RESULT_ID())->setValue(anOutput.str()); + + AttributeDoubleArrayPtr aValues = + std::dynamic_pointer_cast(attribute(RESULT_VALUES_ID())); + aValues->setSize(1); + aValues->setValue(0, anAngleValue); +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_Measurement.h b/src/FeaturesPlugin/FeaturesPlugin_Measurement.h index a358155cf..27eefc4d5 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Measurement.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Measurement.h @@ -84,6 +84,13 @@ public: return MY_MEASURE_ID; } + /// Attribute name for angle measurement by 3 points. + inline static const std::string& MEASURE_ANGLE_POINTS() + { + static const std::string MY_MEASURE_ID("AngleBy3Points"); + return MY_MEASURE_ID; + } + /// Attribute name of edge selected for length calculation. inline static const std::string& EDGE_FOR_LENGTH_ID() @@ -120,13 +127,34 @@ public: return MY_ANGLE_FROM_EDGE_ID; } - /// Attribute name of second shape selected for distance calculation. + /// Attribute name of second shape selected for angle 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 of first point selected for angle calculation. + inline static const std::string& ANGLE_POINT1_ID() + { + static const std::string MY_ANGLE_POINT1_ID("angle_point_1"); + return MY_ANGLE_POINT1_ID; + } + + /// Attribute name of second point (apex) selected for angle calculation. + inline static const std::string& ANGLE_POINT2_ID() + { + static const std::string MY_ANGLE_POINT2_ID("angle_point_2"); + return MY_ANGLE_POINT2_ID; + } + + /// Attribute name of third point selected for angle calculation. + inline static const std::string& ANGLE_POINT3_ID() + { + static const std::string MY_ANGLE_POINT3_ID("angle_point_3"); + return MY_ANGLE_POINT3_ID; + } + /// Attribute name for result. inline static const std::string& RESULT_ID() { @@ -166,6 +194,8 @@ private: void computeRadius(); /// Compute angle(s) between pair of edges if they are intersected void computeAngle(); + /// Compute angle by three points + void computeAngleByPoints(); }; #endif diff --git a/src/FeaturesPlugin/Test/TestMeasurementAngle3Points.py b/src/FeaturesPlugin/Test/TestMeasurementAngle3Points.py new file mode 100644 index 000000000..c3f7fcc8e --- /dev/null +++ b/src/FeaturesPlugin/Test/TestMeasurementAngle3Points.py @@ -0,0 +1,109 @@ +## 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() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(0, 0, 30, -51.96152422706629) +SketchLine_2 = Sketch_1.addLine(30, -51.96152422706629, 90, -51.96152422706629) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchLine_3 = Sketch_1.addLine(90, -51.96152422706629, 120, 0) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +SketchLine_4 = Sketch_1.addLine(120, 0, 90, 51.96152422706636) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +SketchLine_5 = Sketch_1.addLine(90, 51.96152422706636, 30, 51.96152422706631) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchLine_6 = Sketch_1.addLine(30, 51.96152422706631, 0, 0) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_6.endPoint()) +SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_1.result()) +SketchConstraintEqual_2 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_1.result()) +SketchConstraintEqual_3 = Sketch_1.setEqual(SketchLine_4.result(), SketchLine_1.result()) +SketchConstraintEqual_4 = Sketch_1.setEqual(SketchLine_5.result(), SketchLine_1.result()) +SketchConstraintEqual_5 = Sketch_1.setEqual(SketchLine_6.result(), SketchLine_1.result()) +SketchConstraintAngle_1 = Sketch_1.setAngleBackward(SketchLine_1.result(), SketchLine_2.result(), 120) +SketchConstraintAngle_2 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_2.result(), 120) +SketchConstraintAngle_3 = Sketch_1.setAngleBackward(SketchLine_3.result(), SketchLine_4.result(), 120) +SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_2.result()) +SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_5.result(), 60) +SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False) +SketchPoint_1 = SketchProjection_1.createdFeature() +SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchAPI_Point(SketchPoint_1).coordinates()) +model.do() +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1f-SketchLine_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f")], model.selection(), 100, 0) +model.do() + +TOLERANCE = 1.e-6 + +# reference data +REF_DATA = [("Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_1&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/Generated_Face_4&Extrusion_1_1/From_Face_1", + 120), + ("Extrusion_1_1/Generated_Face_5&Extrusion_1_1/Generated_Face_4&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_1&Extrusion_1_1/From_Face_1", + 120), + ("Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_4&Extrusion_1_1/Generated_Face_3&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_2&Extrusion_1_1/Generated_Face_1&Extrusion_1_1/From_Face_1", + 60), + ("Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", + "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/Generated_Face_4&Extrusion_1_1/To_Face_1", + "Extrusion_1_1/Generated_Face_4&Extrusion_1_1/Generated_Face_3&Extrusion_1_1/To_Face_1", + 120), + ("Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/Generated_Face_4&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_4&Extrusion_1_1/Generated_Face_3&Extrusion_1_1/To_Face_1", + 104.9068234), + ("Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_4&Extrusion_1_1/Generated_Face_3&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_4&Extrusion_1_1/Generated_Face_3&Extrusion_1_1/To_Face_1", + 90), + ("Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/Generated_Face_4&Extrusion_1_1/To_Face_1", + "Extrusion_1_1/Generated_Face_4&Extrusion_1_1/Generated_Face_3&Extrusion_1_1/From_Face_1", + 52.91916441), + ("Extrusion_1_1/Generated_Face_5&Extrusion_1_1/Generated_Face_4&Extrusion_1_1/To_Face_1", + "Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", + "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/Generated_Face_4&Extrusion_1_1/To_Face_1", + 0) + ] + +for ref in REF_DATA: + angle = model.measureAngle(Part_1_doc, model.selection("VERTEX", ref[0]), model.selection("VERTEX", ref[1]), model.selection("VERTEX", ref[2])) + assert(math.fabs(angle - ref[3]) < TOLERANCE), "Angle {} differs from expected value {}".format(angle, ref[3]) + +# select incorrect data +angle = model.measureAngle(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face_1"), model.selection("VERTEX", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/Generated_Face_4&Extrusion_1_1/To_Face_1"), model.selection("VERTEX", "Extrusion_1_1/Generated_Face_6&Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1")) +assert(angle == -1.0) + +angle = model.measureAngle(Part_1_doc, model.selection("VERTEX", ""), model.selection("VERTEX", ""), model.selection("VERTEX", "")) +assert(angle == -1.0) + +model.end() diff --git a/src/FeaturesPlugin/icons/meas_angle3p_32x32.png b/src/FeaturesPlugin/icons/meas_angle3p_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..8fb6eb88f8561eb5e63dcc37cfb9862afb26ca49 GIT binary patch literal 974 zcmV;<12O!GP)sp`bSVf1~pV*1VL$#sYAhxI9mSyH}ACgUG@3q z%{Vj8c;Il~J@=h+zUSQgJNMkkOsy1+M!jdz(skL)4G>+?H#{=fpF4M+H49X07Zm54 z&(E`&n|l^5g{`vzpsQVQKJv@SW(~mArZE7bEBYoQn>7H#BZK`bP9A%0<tZkEdc%}eEdm*Z%u@iTxqCH(kNZccH&rAUrF9Y*c2XtQHW&tsPeKX~p zPIVw+0dByLIF8X4FbxcdST%b9SXRalZ~}*L7<;k1tbZ!vc@bA!$R+|XP#DhP72Jon zi{8uFF5=cJLeQZGY%DdPJ6T(P|C-cW{&R1U4JEshRnx5l<7MEW%wzZx|DaFi*%s8_ z;Q{QxO?Xel{F%zY$I0g{^JkKU<5bLg@>R*iyVJ=)C!@b4?@ZbVcP4uZh{vyN3nJ!W zG4@X(g@36IB)lP_x6S5i5smTG+s{BRLiNc_o-()L6LjNE5lcjD6fuC$a9{%6OVasp zN#*J(?`NRRFR)iU0k)o?hYL*ScY z@B0GQYXa?%u+GmT~`y zdi_Sceu>Gh=&QBv&F|d{VAEs&9&C!J7vtx-wbmzwR0Zqn66M}9?vr_>J<^{Vje5^@ z*B=>k2s^!c4$YbSoT^CuU5x&G!FY*C=BakQiKt|BtF|Xz$ChkY_k!J6EA!k`oJ@zh wI=*`5#Fne!9BRWOgZ({=mTKRdtKl5}2j4@jdJx%{ + + + + + + + + + + +