From cff305b5e67d360123748369da91be12b93a2d11 Mon Sep 17 00:00:00 2001 From: azv Date: Tue, 22 May 2018 08:42:44 +0300 Subject: [PATCH] Task 2.2. Sketch Angular copy * Limit angle of rotation by range [0, 360]. * Switch between "SingleAngle" and "FullAngle" does not affect on shapes. * Implement "Reversed" flag to cover negative angles. --- src/SketchAPI/SketchAPI_Rotation.cpp | 13 +- src/SketchAPI/SketchAPI_Rotation.h | 7 +- src/SketchAPI/SketchAPI_Sketch.cpp | 5 +- src/SketchAPI/SketchAPI_Sketch.h | 3 +- src/SketchPlugin/CMakeLists.txt | 7 +- .../SketchPlugin_MultiRotation.cpp | 33 +++++- src/SketchPlugin/SketchPlugin_MultiRotation.h | 9 ++ src/SketchPlugin/SketchPlugin_Plugin.cpp | 2 + src/SketchPlugin/SketchPlugin_Validators.cpp | 44 ++++++- src/SketchPlugin/SketchPlugin_Validators.h | 15 +++ src/SketchPlugin/Test/TestMultiRotation01.py | 40 +++++++ src/SketchPlugin/Test/TestMultiRotation02.py | 45 +++++++ src/SketchPlugin/Test/TestMultiRotation03.py | 77 ++++++++++++ src/SketchPlugin/Test/TestMultiRotation04.py | 87 ++++++++++++++ src/SketchPlugin/Test/TestMultiRotation05.py | 112 ++++++++++++++++++ src/SketchPlugin/plugin-Sketch.xml | 20 +++- .../SketchSolver_ConstraintMultiRotation.cpp | 21 +++- .../SketchSolver_ConstraintMultiRotation.h | 11 +- 18 files changed, 530 insertions(+), 21 deletions(-) create mode 100644 src/SketchPlugin/Test/TestMultiRotation01.py create mode 100644 src/SketchPlugin/Test/TestMultiRotation02.py create mode 100644 src/SketchPlugin/Test/TestMultiRotation03.py create mode 100644 src/SketchPlugin/Test/TestMultiRotation04.py create mode 100644 src/SketchPlugin/Test/TestMultiRotation05.py diff --git a/src/SketchAPI/SketchAPI_Rotation.cpp b/src/SketchAPI/SketchAPI_Rotation.cpp index a736df25b..67393b56e 100644 --- a/src/SketchAPI/SketchAPI_Rotation.cpp +++ b/src/SketchAPI/SketchAPI_Rotation.cpp @@ -39,15 +39,17 @@ SketchAPI_Rotation::SketchAPI_Rotation( const ModelHighAPI_RefAttr & theCenter, const ModelHighAPI_Double & theAngle, const ModelHighAPI_Integer & theNumberOfObjects, - bool theFullValue) + bool theFullValue, + bool theReversed) : ModelHighAPI_Interface(theFeature) { if (initialize()) { fillAttribute(theObjects, rotationList()); fillAttribute(theCenter, center()); + fillAttribute(theFullValue ? "FullAngle" : "SingleAngle", valueType()); fillAttribute(theAngle, angle()); + fillAttribute(theReversed, reversed()); fillAttribute(theNumberOfObjects, numberOfObjects()); - fillAttribute(theFullValue ? "FullAngle" : "SingleAngle", valueType()); execute(true); } @@ -85,6 +87,7 @@ void SketchAPI_Rotation::dump(ModelHighAPI_Dumper& theDumper) const AttributeDoublePtr anAngle = angle(); AttributeIntegerPtr aNbCopies = numberOfObjects(); bool isFullValue = valueType()->value() != "SingleAngle"; + bool isReversed = reversed()->value(); // Check all attributes are already dumped. If not, store the constraint as postponed. if (!theDumper.isDumped(aCenter) || !theDumper.isDumped(aRotObjects)) { @@ -94,8 +97,12 @@ void SketchAPI_Rotation::dump(ModelHighAPI_Dumper& theDumper) const theDumper << aBase << " = " << aSketchName << ".addRotation(" << aRotObjects << ", " << aCenter << ", " << anAngle << ", " << aNbCopies; - if (isFullValue) + if (isFullValue || isReversed) + { theDumper << ", " << isFullValue; + if (isReversed) + theDumper << ", " << isReversed; + } theDumper << ")" << std::endl; // Dump variables for a list of rotated features diff --git a/src/SketchAPI/SketchAPI_Rotation.h b/src/SketchAPI/SketchAPI_Rotation.h index 5583e5ddf..d776efccc 100644 --- a/src/SketchAPI/SketchAPI_Rotation.h +++ b/src/SketchAPI/SketchAPI_Rotation.h @@ -54,18 +54,21 @@ public: const ModelHighAPI_RefAttr & theCenter, const ModelHighAPI_Double & theAngle, const ModelHighAPI_Integer & theNumberOfObjects, - bool theFullValue = false); + bool theFullValue = false, + bool theReversed = false); /// Destructor SKETCHAPI_EXPORT virtual ~SketchAPI_Rotation(); - INTERFACE_7(SketchPlugin_MultiRotation::ID(), + INTERFACE_8(SketchPlugin_MultiRotation::ID(), rotationList, SketchPlugin_MultiRotation::ROTATION_LIST_ID(), ModelAPI_AttributeRefList, /** Rotation list */, center, SketchPlugin_MultiRotation::CENTER_ID(), ModelAPI_AttributeRefAttr, /** Center */, angle, SketchPlugin_MultiRotation::ANGLE_ID(), ModelAPI_AttributeDouble, /** Angle */, + reversed, SketchPlugin_MultiRotation::REVERSED_ID(), + ModelAPI_AttributeBoolean, /** Negative angle */, numberOfObjects, SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID(), ModelAPI_AttributeInteger, /** Number of objects */, valueType, SketchPlugin_MultiRotation::ANGLE_TYPE(), diff --git a/src/SketchAPI/SketchAPI_Sketch.cpp b/src/SketchAPI/SketchAPI_Sketch.cpp index 63e7a10f4..2e38d2d33 100644 --- a/src/SketchAPI/SketchAPI_Sketch.cpp +++ b/src/SketchAPI/SketchAPI_Sketch.cpp @@ -509,13 +509,14 @@ std::shared_ptr SketchAPI_Sketch::addRotation( const ModelHighAPI_RefAttr & theCenter, const ModelHighAPI_Double & theAngle, const ModelHighAPI_Integer & theNumberOfObjects, - bool theFullValue) + bool theFullValue, + bool theReversed) { std::shared_ptr aFeature = compositeFeature()->addFeature(SketchPlugin_MultiRotation::ID()); return RotationPtr( new SketchAPI_Rotation(aFeature, theObjects, theCenter, - theAngle, theNumberOfObjects, theFullValue)); + theAngle, theNumberOfObjects, theFullValue, theReversed)); } //-------------------------------------------------------------------------------------- diff --git a/src/SketchAPI/SketchAPI_Sketch.h b/src/SketchAPI/SketchAPI_Sketch.h index 3e531012d..5e7d902e8 100644 --- a/src/SketchAPI/SketchAPI_Sketch.h +++ b/src/SketchAPI/SketchAPI_Sketch.h @@ -281,7 +281,8 @@ public: const ModelHighAPI_RefAttr & theCenter, const ModelHighAPI_Double & theAngle, const ModelHighAPI_Integer & theNumberOfObjects, - bool theFullValue = false); + bool theFullValue = false, + bool theReversed = false); /// Add split SKETCHAPI_EXPORT diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 2e5f42bc7..4fa4f8c0f 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -181,9 +181,14 @@ ADD_UNIT_TESTS(TestSketchPointLine.py TestConstraintMiddlePoint.py TestEdgesOrder.py TestMirror.py - TestMultiRotation.py TestMultiTranslation.py + TestMultiRotation.py TestMultiRotationWithParameter.py + TestMultiRotation01.py + TestMultiRotation02.py + TestMultiRotation03.py + TestMultiRotation04.py + TestMultiRotation05.py TestFillet.py TestFilletInteracting.py TestRectangle.py diff --git a/src/SketchPlugin/SketchPlugin_MultiRotation.cpp b/src/SketchPlugin/SketchPlugin_MultiRotation.cpp index 3fd19dc33..6a9a1596e 100755 --- a/src/SketchPlugin/SketchPlugin_MultiRotation.cpp +++ b/src/SketchPlugin/SketchPlugin_MultiRotation.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -43,9 +44,12 @@ #include -#define PI 3.1415926535897932 +static const double PI = 3.1415926535897932; +static const double PERIOD = 360.0; +static const double ANGLETOL = 1.e-7; SketchPlugin_MultiRotation::SketchPlugin_MultiRotation() + : isUpdatingAngle(false) { } @@ -59,6 +63,8 @@ void SketchPlugin_MultiRotation::initAttributes() data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefList::typeId()); data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId()); data()->addAttribute(ROTATION_LIST_ID(), ModelAPI_AttributeRefList::typeId()); + data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId()); + ModelAPI_Session::get()->validators()-> registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_A()); ModelAPI_Session::get()->validators()-> @@ -372,4 +378,29 @@ void SketchPlugin_MultiRotation::attributeChanged(const std::string& theID) reflist(SketchPlugin_Constraint::ENTITY_A())->clear(); } } + else if (!isUpdatingAngle && real(ANGLE_ID())->isInitialized()) + { + isUpdatingAngle = true; + AttributeDoublePtr anAngle = real(ANGLE_ID()); + if (theID == ANGLE_TYPE() && integer(NUMBER_OF_OBJECTS_ID())->isInitialized()) { + if (string(ANGLE_TYPE())->value() != "SingleAngle") + anAngle->setValue(anAngle->value() * (integer(NUMBER_OF_OBJECTS_ID())->value() - 1)); + else + { + int aNbSplits = integer(NUMBER_OF_OBJECTS_ID())->value(); + if (anAngle->value() < PERIOD - ANGLETOL) + aNbSplits -= 1; + anAngle->setValue(anAngle->value() / aNbSplits); + } + } + else if (theID == ANGLE_ID()) { + if (anAngle->value() > PERIOD + ANGLETOL || anAngle->value() < -ANGLETOL) + anAngle->setValue(anAngle->value() + PERIOD * ceil(-anAngle->value() / PERIOD)); + if (fabs(anAngle->value() - PERIOD) < ANGLETOL) + anAngle->setValue(PERIOD); + else if (fabs(anAngle->value()) < ANGLETOL) + anAngle->setValue(0.); + } + isUpdatingAngle = false; + } } diff --git a/src/SketchPlugin/SketchPlugin_MultiRotation.h b/src/SketchPlugin/SketchPlugin_MultiRotation.h index 9b43abe82..8e8f2b89c 100644 --- a/src/SketchPlugin/SketchPlugin_MultiRotation.h +++ b/src/SketchPlugin/SketchPlugin_MultiRotation.h @@ -87,6 +87,13 @@ class SketchPlugin_MultiRotation : public SketchPlugin_ConstraintBase return MY_NUMBER_OF_OBJECTS_ID; } + /// Name of the flag to reverse rotation + inline static const std::string& REVERSED_ID() + { + static const std::string MY_REVERSED_ID("MultiRotationReversed"); + return MY_REVERSED_ID; + } + /// \brief Creates a new part document if needed SKETCHPLUGIN_EXPORT virtual void execute(); @@ -112,6 +119,8 @@ private: // double theCenterX, double theCenterY, double theAngle); bool updateFullAngleValue(); + + bool isUpdatingAngle; }; #endif diff --git a/src/SketchPlugin/SketchPlugin_Plugin.cpp b/src/SketchPlugin/SketchPlugin_Plugin.cpp index aaf1402ed..a392b6ef7 100644 --- a/src/SketchPlugin/SketchPlugin_Plugin.cpp +++ b/src/SketchPlugin/SketchPlugin_Plugin.cpp @@ -138,6 +138,8 @@ SketchPlugin_Plugin::SketchPlugin_Plugin() new SketchPlugin_ReplicationReferenceValidator); aFactory->registerValidator("SketchPlugin_SketchFeatureValidator", new SketchPlugin_SketchFeatureValidator); + aFactory->registerValidator("SketchPlugin_MultiRotationAngleValidator", + new SketchPlugin_MultiRotationAngleValidator); // register this plugin ModelAPI_Session::get()->registerPlugin(this); diff --git a/src/SketchPlugin/SketchPlugin_Validators.cpp b/src/SketchPlugin/SketchPlugin_Validators.cpp index 31d7069da..0dce7c4fe 100755 --- a/src/SketchPlugin/SketchPlugin_Validators.cpp +++ b/src/SketchPlugin/SketchPlugin_Validators.cpp @@ -30,6 +30,7 @@ #include "SketchPlugin_Line.h" #include "SketchPlugin_MacroArc.h" #include "SketchPlugin_MacroCircle.h" +#include "SketchPlugin_MultiRotation.h" #include "SketchPlugin_Point.h" #include "SketchPlugin_Sketch.h" #include "SketchPlugin_Trim.h" @@ -42,8 +43,8 @@ #include #include #include +#include #include - #include #include #include @@ -1636,3 +1637,44 @@ bool SketchPlugin_SketchFeatureValidator::isValid(const AttributePtr& theAttribu theError = "The object selected is not a sketch feature"; return false; } + +bool SketchPlugin_MultiRotationAngleValidator::isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + if (theAttribute->attributeType() != ModelAPI_AttributeDouble::typeId()) { + theError = "The attribute with the %1 type is not processed"; + theError.arg(theAttribute->attributeType()); + return false; + } + + AttributeDoublePtr anAngleAttr = + std::dynamic_pointer_cast(theAttribute); + + FeaturePtr aMultiRotation = ModelAPI_Feature::feature(theAttribute->owner()); + AttributeStringPtr anAngleType = + aMultiRotation->string(SketchPlugin_MultiRotation::ANGLE_TYPE()); + AttributeIntegerPtr aNbCopies = + aMultiRotation->integer(SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID()); + + if (anAngleType->value() != "FullAngle") + { + double aFullAngleValue = anAngleAttr->value() * (aNbCopies->value() - 1); + if (aFullAngleValue < -1.e-7 || aFullAngleValue > 359.9999999) + { + theError = "Rotation single angle should produce full angle less than 360 degree"; + return false; + } + } + else + { + double aFullAngleValue = anAngleAttr->value(); + if (aFullAngleValue < -1.e-7 || aFullAngleValue > 360.0000001) + { + theError = "Rotation full angle should be in range [0, 360]"; + return false; + } + } + + return true; +} diff --git a/src/SketchPlugin/SketchPlugin_Validators.h b/src/SketchPlugin/SketchPlugin_Validators.h index 56ab74b7a..bfaffbeda 100644 --- a/src/SketchPlugin/SketchPlugin_Validators.h +++ b/src/SketchPlugin/SketchPlugin_Validators.h @@ -480,4 +480,19 @@ class SketchPlugin_SketchFeatureValidator: public ModelAPI_AttributeValidator Events_InfoMessage& theError) const; }; +/**\class SketchPlugin_MultiRotationAngleValidator + * \ingroup Validators + * \brief Validator for checking whether the angle of MultiRotation is in range [0, 360]. + */ +class SketchPlugin_MultiRotationAngleValidator : public ModelAPI_AttributeValidator +{ + //! returns true if attribute is valid + //! \param theAttribute the checked attribute + //! \param theArguments arguments of the attribute + //! \param theError error message + virtual bool isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const; +}; + #endif diff --git a/src/SketchPlugin/Test/TestMultiRotation01.py b/src/SketchPlugin/Test/TestMultiRotation01.py new file mode 100644 index 000000000..4577e1bb9 --- /dev/null +++ b/src/SketchPlugin/Test/TestMultiRotation01.py @@ -0,0 +1,40 @@ +## 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 + +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(30, 20, 10, 20) +SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False) +SketchPoint_1 = SketchProjection_1.createdFeature() +model.do() +SketchMultiRotation_1 = Sketch_1.addRotation([SketchLine_1.result()], SketchAPI_Point(SketchPoint_1).coordinates(), 90, 5) +model.do() + +# check MultiRotation is invalid +assert(SketchMultiRotation_1.feature().error() != "") + +model.end() diff --git a/src/SketchPlugin/Test/TestMultiRotation02.py b/src/SketchPlugin/Test/TestMultiRotation02.py new file mode 100644 index 000000000..6a872a478 --- /dev/null +++ b/src/SketchPlugin/Test/TestMultiRotation02.py @@ -0,0 +1,45 @@ +## 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 + +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(30, 20, 10, 20) +SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False) +SketchPoint_1 = SketchProjection_1.createdFeature() +model.do() +SketchMultiRotation_1 = Sketch_1.addRotation([SketchLine_1.result()], SketchAPI_Point(SketchPoint_1).coordinates(), 200, 2) +model.do() + +# check MultiRotation is valid +assert(SketchMultiRotation_1.feature().error() == "") + +# update number of copies to make MultiRotation invalid +SketchMultiRotation_1.feature().integer("MultiRotationObjects").setValue(3) +model.do() +assert(SketchMultiRotation_1.feature().error() != "") + +model.end() diff --git a/src/SketchPlugin/Test/TestMultiRotation03.py b/src/SketchPlugin/Test/TestMultiRotation03.py new file mode 100644 index 000000000..a3d02b630 --- /dev/null +++ b/src/SketchPlugin/Test/TestMultiRotation03.py @@ -0,0 +1,77 @@ +## 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 GeomAlgoAPI import GeomAlgoAPI_ShapeTools +from SketchAPI import * +from math import * + +from salome.shaper import model + +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(30, 20, 10, 20) +SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False) +SketchPoint_1 = SketchProjection_1.createdFeature() +model.do() + +ROT_ANGLE = 90 +ROT_COPIES = 3 +SketchMultiRotation_1 = Sketch_1.addRotation([SketchLine_1.result()], SketchAPI_Point(SketchPoint_1).coordinates(), ROT_ANGLE, ROT_COPIES) +model.do() + +assert(SketchMultiRotation_1.angle().value() == ROT_ANGLE) + +# set angle value from single to full +SketchMultiRotation_1.feature().string("AngleType").setValue("FullAngle") +model.do() +assert(SketchMultiRotation_1.angle().value() == ROT_ANGLE*(ROT_COPIES - 1)) + +# check coordinates of center of mass of each line +REF_DATA = [[-20, 20], + [-20, -20]] +TOLERANCE = 1.e-7 +aLines = SketchMultiRotation_1.rotated() +ind = 0 +for lin in aLines: + curShape = lin.feature().lastResult().shape() + massCenter = GeomAlgoAPI_ShapeTools.centreOfMass(curShape) + assert(fabs(massCenter.x() - REF_DATA[ind][0]) < TOLERANCE and fabs(massCenter.y() - REF_DATA[ind][1]) < TOLERANCE and massCenter.z() == 0), "({}, {}, {}) != ({}, {}, {})".format(massCenter.x(), massCenter.y(), massCenter.z(), REF_DATA[ind][0], REF_DATA[ind][1], 0.0) + ind += 1 + +# set angle value from full to single +SketchMultiRotation_1.feature().string("AngleType").setValue("SingleAngle") +model.do() +assert(SketchMultiRotation_1.angle().value() == ROT_ANGLE) + +# check coordinates of center of mass of each line +aLines = SketchMultiRotation_1.rotated() +ind = 0 +for lin in aLines: + curShape = lin.feature().lastResult().shape() + massCenter = GeomAlgoAPI_ShapeTools.centreOfMass(curShape) + assert(fabs(massCenter.x() - REF_DATA[ind][0]) < TOLERANCE and fabs(massCenter.y() - REF_DATA[ind][1]) < TOLERANCE and massCenter.z() == 0), "({}, {}, {}) != ({}, {}, {})".format(massCenter.x(), massCenter.y(), massCenter.z(), REF_DATA[ind][0], REF_DATA[ind][1], 0.0) + ind += 1 + +model.end() + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/Test/TestMultiRotation04.py b/src/SketchPlugin/Test/TestMultiRotation04.py new file mode 100644 index 000000000..1feca4578 --- /dev/null +++ b/src/SketchPlugin/Test/TestMultiRotation04.py @@ -0,0 +1,87 @@ +## 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 GeomAlgoAPI import GeomAlgoAPI_ShapeTools +from SketchAPI import * +from math import * + +from salome.shaper import model + +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(30, 20, 10, 20) +SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False) +SketchPoint_1 = SketchProjection_1.createdFeature() +model.do() + +ROT_ANGLE = 360 +ROT_COPIES = 4 +SketchMultiRotation_1 = Sketch_1.addRotation([SketchLine_1.result()], SketchAPI_Point(SketchPoint_1).coordinates(), ROT_ANGLE, ROT_COPIES, True) +model.do() + +assert(SketchMultiRotation_1.angle().value() == ROT_ANGLE) + +# check coordinates of center of mass of each line +REF_DATA = [[-20, 20], + [-20, -20], + [20, -20]] +TOLERANCE = 1.e-7 +aLines = SketchMultiRotation_1.rotated() +ind = 0 +for lin in aLines: + curShape = lin.feature().lastResult().shape() + massCenter = GeomAlgoAPI_ShapeTools.centreOfMass(curShape) + assert(fabs(massCenter.x() - REF_DATA[ind][0]) < TOLERANCE and fabs(massCenter.y() - REF_DATA[ind][1]) < TOLERANCE and massCenter.z() == 0), "({}, {}, {}) != ({}, {}, {})".format(massCenter.x(), massCenter.y(), massCenter.z(), REF_DATA[ind][0], REF_DATA[ind][1], 0.0) + ind += 1 + +# set angle value from full to single +SketchMultiRotation_1.feature().string("AngleType").setValue("SingleAngle") +model.do() +assert(SketchMultiRotation_1.angle().value() == ROT_ANGLE/ROT_COPIES) + +# check coordinates of center of mass of each line +aLines = SketchMultiRotation_1.rotated() +ind = 0 +for lin in aLines: + curShape = lin.feature().lastResult().shape() + massCenter = GeomAlgoAPI_ShapeTools.centreOfMass(curShape) + assert(fabs(massCenter.x() - REF_DATA[ind][0]) < TOLERANCE and fabs(massCenter.y() - REF_DATA[ind][1]) < TOLERANCE and massCenter.z() == 0), "({}, {}, {}) != ({}, {}, {})".format(massCenter.x(), massCenter.y(), massCenter.z(), REF_DATA[ind][0], REF_DATA[ind][1], 0.0) + ind += 1 + +# set angle value from single to full +SketchMultiRotation_1.feature().string("AngleType").setValue("FullAngle") +model.do() +assert(SketchMultiRotation_1.angle().value() == ROT_ANGLE/ROT_COPIES*(ROT_COPIES - 1)) + +# check coordinates of center of mass of each line +aLines = SketchMultiRotation_1.rotated() +ind = 0 +for lin in aLines: + curShape = lin.feature().lastResult().shape() + massCenter = GeomAlgoAPI_ShapeTools.centreOfMass(curShape) + assert(fabs(massCenter.x() - REF_DATA[ind][0]) < TOLERANCE and fabs(massCenter.y() - REF_DATA[ind][1]) < TOLERANCE and massCenter.z() == 0), "({}, {}, {}) != ({}, {}, {})".format(massCenter.x(), massCenter.y(), massCenter.z(), REF_DATA[ind][0], REF_DATA[ind][1], 0.0) + ind += 1 + +model.end() + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/Test/TestMultiRotation05.py b/src/SketchPlugin/Test/TestMultiRotation05.py new file mode 100644 index 000000000..a811f646f --- /dev/null +++ b/src/SketchPlugin/Test/TestMultiRotation05.py @@ -0,0 +1,112 @@ +## 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 GeomAlgoAPI import GeomAlgoAPI_ShapeTools +from SketchAPI import * +from math import * + +from salome.shaper import model + +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(10, 0, 30, 0) +SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False) +SketchPoint_1 = SketchProjection_1.createdFeature() +model.do() + +ROT_ANGLE = 20 +ROT_COPIES = 15 +SketchMultiRotation_1 = Sketch_1.addRotation([SketchLine_1.result()], SketchAPI_Point(SketchPoint_1).coordinates(), ROT_ANGLE, ROT_COPIES) +model.do() + +# collect coordinates of centers as reference data +TOLERANCE = 1.e-7 +REF_DATA = [[]] +aLines = SketchMultiRotation_1.rotated() +for lin in aLines: + curShape = lin.feature().lastResult().shape() + massCenter = GeomAlgoAPI_ShapeTools.centreOfMass(curShape) + REF_DATA.append([massCenter.x(), massCenter.y()]) +del REF_DATA[0] + +FeatureMultiRotation = SketchMultiRotation_1.feature() + +# assign "reversed" flag +FeatureMultiRotation.boolean("MultiRotationReversed").setValue(True) +model.do() +assert(SketchMultiRotation_1.angle().value() == ROT_ANGLE) + +# check coordinates of center of mass of each line +aLines = SketchMultiRotation_1.rotated() +ind = 0 +for lin in aLines: + curShape = lin.feature().lastResult().shape() + massCenter = GeomAlgoAPI_ShapeTools.centreOfMass(curShape) + assert(fabs(massCenter.x() - REF_DATA[ind][0]) < TOLERANCE and fabs(massCenter.y() + REF_DATA[ind][1]) < TOLERANCE and massCenter.z() == 0), "({}, {}, {}) != ({}, {}, {})".format(massCenter.x(), massCenter.y(), massCenter.z(), REF_DATA[ind][0], -REF_DATA[ind][1], 0.0) + ind += 1 + +# drop "reversed" flag +FeatureMultiRotation.boolean("MultiRotationReversed").setValue(False) +model.do() +assert(SketchMultiRotation_1.angle().value() == ROT_ANGLE) + +# check coordinates of center of mass of each line +aLines = SketchMultiRotation_1.rotated() +ind = 0 +for lin in aLines: + curShape = lin.feature().lastResult().shape() + massCenter = GeomAlgoAPI_ShapeTools.centreOfMass(curShape) + assert(fabs(massCenter.x() - REF_DATA[ind][0]) < TOLERANCE and fabs(massCenter.y() - REF_DATA[ind][1]) < TOLERANCE and massCenter.z() == 0), "({}, {}, {}) != ({}, {}, {})".format(massCenter.x(), massCenter.y(), massCenter.z(), REF_DATA[ind][0], REF_DATA[ind][1], 0.0) + ind += 1 + +# set angle value from single to full +SketchMultiRotation_1.feature().string("AngleType").setValue("FullAngle") +model.do() +assert(SketchMultiRotation_1.angle().value() == ROT_ANGLE*(ROT_COPIES - 1)) + +# check coordinates of center of mass of each line +aLines = SketchMultiRotation_1.rotated() +ind = 0 +for lin in aLines: + curShape = lin.feature().lastResult().shape() + massCenter = GeomAlgoAPI_ShapeTools.centreOfMass(curShape) + assert(fabs(massCenter.x() - REF_DATA[ind][0]) < TOLERANCE and fabs(massCenter.y() - REF_DATA[ind][1]) < TOLERANCE and massCenter.z() == 0), "({}, {}, {}) != ({}, {}, {})".format(massCenter.x(), massCenter.y(), massCenter.z(), REF_DATA[ind][0], REF_DATA[ind][1], 0.0) + ind += 1 + +# assign "reversed" flag +FeatureMultiRotation.boolean("MultiRotationReversed").setValue(True) +model.do() +assert(SketchMultiRotation_1.angle().value() == ROT_ANGLE*(ROT_COPIES - 1)) + +# check coordinates of center of mass of each line +aLines = SketchMultiRotation_1.rotated() +ind = 0 +for lin in aLines: + curShape = lin.feature().lastResult().shape() + massCenter = GeomAlgoAPI_ShapeTools.centreOfMass(curShape) + assert(fabs(massCenter.x() - REF_DATA[ind][0]) < TOLERANCE and fabs(massCenter.y() + REF_DATA[ind][1]) < TOLERANCE and massCenter.z() == 0), "({}, {}, {}) != ({}, {}, {})".format(massCenter.x(), massCenter.y(), massCenter.z(), REF_DATA[ind][0], -REF_DATA[ind][1], 0.0) + ind += 1 + +model.end() + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index 2ebc0bf3e..366b8b63b 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -607,14 +607,30 @@ email : webmaster.salome@opencascade.com + default="90" min="0" max="360" + use_reset="false"> + + + + default="90" min="0" max="360" + use_reset="false"> + + + & theEntities) + bool& theFullValue, bool& theReversed, std::list& theEntities) { AttributePtr anAngleAttr = myBaseConstraint->attribute(SketchPlugin_MultiRotation::ANGLE_ID()); PlaneGCSSolver_AttributeBuilder aValueBuilder; @@ -59,6 +59,8 @@ void SketchSolver_ConstraintMultiRotation::getAttributes( myBaseConstraint->string(SketchPlugin_MultiRotation::ANGLE_TYPE()); theFullValue = aMethodTypeAttr->value() != "SingleAngle"; + theReversed = myBaseConstraint->boolean(SketchPlugin_MultiRotation::REVERSED_ID())->value(); + getEntities(theEntities); // add owner of central point of Multi-Rotation to the list of monitored features @@ -77,7 +79,7 @@ void SketchSolver_ConstraintMultiRotation::process() EntityWrapperPtr aRotationCenter; std::list aBaseEntities; - getAttributes(aRotationCenter, myAngle, myIsFullValue, aBaseEntities); + getAttributes(aRotationCenter, myAngle, myIsFullValue, myIsRevered, aBaseEntities); if (!myErrorMsg.empty()) return; @@ -90,10 +92,12 @@ void SketchSolver_ConstraintMultiRotation::process() void SketchSolver_ConstraintMultiRotation::updateLocal() { double aValue = myBaseConstraint->real(SketchPlugin_MultiRotation::ANGLE_ID())->value(); - if (fabs(myAngle->value() - aValue) > tolerance) + bool isReversed = myBaseConstraint->boolean(SketchPlugin_MultiRotation::REVERSED_ID())->value(); + if (fabs(myAngle->value() - aValue) > tolerance || isReversed != myIsRevered) myAdjusted = false; // update angle value myAngle->setValue(aValue); + myIsRevered = isReversed; // update center DataPtr aData = myBaseConstraint->data(); @@ -123,6 +127,8 @@ void SketchSolver_ConstraintMultiRotation::adjustConstraint() myStorage->setNeedToResolve(false); return; } + if (myIsRevered) + anAngleValue *= -1.0; // Obtain coordinates of rotation center AttributeRefAttrPtr aCenterAttr = @@ -145,7 +151,14 @@ void SketchSolver_ConstraintMultiRotation::adjustConstraint() } if (myIsFullValue && myNumberOfCopies > 0) - anAngleValue /= myNumberOfCopies; + { + // if the full angle value is equal to 360, then distribute rotated items + // to avoid superposition of original feature and last copy + if (fabs(anAngleValue - 360.0) < 1.e-7) + anAngleValue /= (myNumberOfCopies + 1); + else + anAngleValue /= myNumberOfCopies; + } myRotationVal[0] = sin(anAngleValue * PI / 180.0); myRotationVal[1] = cos(anAngleValue * PI / 180.0); diff --git a/src/SketchSolver/SketchSolver_ConstraintMultiRotation.h b/src/SketchSolver/SketchSolver_ConstraintMultiRotation.h index d174a5604..f2c742370 100644 --- a/src/SketchSolver/SketchSolver_ConstraintMultiRotation.h +++ b/src/SketchSolver/SketchSolver_ConstraintMultiRotation.h @@ -42,13 +42,15 @@ protected: virtual void process(); /// \brief Generate list of rotated entities - /// \param[out] theCenter central point of rotation - /// \param[out] theAngle rotation angle - /// \param[out] theFullValue applying translation using the disstance as a full or single value - /// \param[out] theEntities list of base entities + /// \param[out] theCenter central point of rotation + /// \param[out] theAngle rotation angle + /// \param[out] theFullValue applying translation using the distance as a full or single value + /// \param[out] theReversed rotation angle is negative + /// \param[out] theEntities list of base entities void getAttributes(EntityWrapperPtr& theCenter, ScalarWrapperPtr& theAngle, bool& theFullValue, + bool& theReversed, std::list& theEntities); /// \brief This method is used in derived objects to check consistence of constraint. @@ -73,6 +75,7 @@ private: private: AttributePoint2DPtr myCenterPointAttribute; ///< a center of rotation ScalarWrapperPtr myAngle; ///< angle of rotation + bool myIsRevered; ///< angle of rotation is negative double myCenterCoord[2]; ///< coordinates of rotation center double myRotationVal[2]; ///< sinus and cosine of rotation angle -- 2.39.2