* 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.
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);
}
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)) {
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
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(),
const ModelHighAPI_RefAttr & theCenter,
const ModelHighAPI_Double & theAngle,
const ModelHighAPI_Integer & theNumberOfObjects,
- bool theFullValue)
+ bool theFullValue,
+ bool theReversed)
{
std::shared_ptr<ModelAPI_Feature> aFeature =
compositeFeature()->addFeature(SketchPlugin_MultiRotation::ID());
return RotationPtr(
new SketchAPI_Rotation(aFeature, theObjects, theCenter,
- theAngle, theNumberOfObjects, theFullValue));
+ theAngle, theNumberOfObjects, theFullValue, theReversed));
}
//--------------------------------------------------------------------------------------
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
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
#include <GeomDataAPI_Point2D.h>
#include <ModelAPI_AttributeRefAttr.h>
+#include <ModelAPI_AttributeBoolean.h>
#include <ModelAPI_AttributeDouble.h>
#include <ModelAPI_AttributeString.h>
#include <ModelAPI_AttributeInteger.h>
#include <cmath>
-#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)
{
}
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()->
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;
+ }
}
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();
// double theCenterX, double theCenterY, double theAngle);
bool updateFullAngleValue();
+
+ bool isUpdatingAngle;
};
#endif
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);
#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"
#include <ModelAPI_Data.h>
#include <ModelAPI_Validator.h>
#include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeInteger.h>
#include <ModelAPI_AttributeRefAttr.h>
-
#include <ModelAPI_AttributeRefAttrList.h>
#include <ModelAPI_AttributeRefList.h>
#include <ModelAPI_AttributeSelectionList.h>
theError = "The object selected is not a sketch feature";
return false;
}
+
+bool SketchPlugin_MultiRotationAngleValidator::isValid(const AttributePtr& theAttribute,
+ const std::list<std::string>& 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<ModelAPI_AttributeDouble>(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;
+}
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<std::string>& theArguments,
+ Events_InfoMessage& theError) const;
+};
+
#endif
--- /dev/null
+## 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<mailto: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()
--- /dev/null
+## 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<mailto: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()
--- /dev/null
+## 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<mailto: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())
--- /dev/null
+## 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<mailto: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())
--- /dev/null
+## 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<mailto: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())
label="Angle"
icon="icons/Sketch/angle.png"
tooltip="Rotation angle"
- default="90" use_reset="false"/>
+ default="90" min="0" max="360"
+ use_reset="false">
+ <validator id="SketchPlugin_MultiRotationAngleValidator" />
+ </doublevalue>
+ <boolvalue id="MultiRotationReversed"
+ label="Reversed"
+ tooltip="Reverse angular copy"
+ default="false"
+ obligatory="0"/>
</box>
<box id="FullAngle" title="Full angle" icon="icons/Sketch/angle_up_full_32x32.png">
<doublevalue id="MultiRotationAngle"
label="Angle"
icon="icons/Sketch/angle.png"
tooltip="Rotation angle"
- default="90" use_reset="false"/>
+ default="90" min="0" max="360"
+ use_reset="false">
+ <validator id="SketchPlugin_MultiRotationAngleValidator" />
+ </doublevalue>
+ <boolvalue id="MultiRotationReversed"
+ label="Reversed"
+ tooltip="Reverse angular copy"
+ default="false"
+ obligatory="0"/>
</box>
</toolbox>
<integervalue id="MultiRotationObjects"
void SketchSolver_ConstraintMultiRotation::getAttributes(
EntityWrapperPtr& theCenter, ScalarWrapperPtr& theAngle,
- bool& theFullValue, std::list<EntityWrapperPtr>& theEntities)
+ bool& theFullValue, bool& theReversed, std::list<EntityWrapperPtr>& theEntities)
{
AttributePtr anAngleAttr = myBaseConstraint->attribute(SketchPlugin_MultiRotation::ANGLE_ID());
PlaneGCSSolver_AttributeBuilder aValueBuilder;
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
EntityWrapperPtr aRotationCenter;
std::list<EntityWrapperPtr> aBaseEntities;
- getAttributes(aRotationCenter, myAngle, myIsFullValue, aBaseEntities);
+ getAttributes(aRotationCenter, myAngle, myIsFullValue, myIsRevered, aBaseEntities);
if (!myErrorMsg.empty())
return;
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();
myStorage->setNeedToResolve(false);
return;
}
+ if (myIsRevered)
+ anAngleValue *= -1.0;
// Obtain coordinates of rotation center
AttributeRefAttrPtr aCenterAttr =
}
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);
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<EntityWrapperPtr>& theEntities);
/// \brief This method is used in derived objects to check consistence of constraint.
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