]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Task 2.2. Sketch Angular copy
authorazv <azv@opencascade.com>
Tue, 22 May 2018 05:42:44 +0000 (08:42 +0300)
committerazv <azv@opencascade.com>
Tue, 22 May 2018 05:42:44 +0000 (08:42 +0300)
* 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.

18 files changed:
src/SketchAPI/SketchAPI_Rotation.cpp
src/SketchAPI/SketchAPI_Rotation.h
src/SketchAPI/SketchAPI_Sketch.cpp
src/SketchAPI/SketchAPI_Sketch.h
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_MultiRotation.cpp
src/SketchPlugin/SketchPlugin_MultiRotation.h
src/SketchPlugin/SketchPlugin_Plugin.cpp
src/SketchPlugin/SketchPlugin_Validators.cpp
src/SketchPlugin/SketchPlugin_Validators.h
src/SketchPlugin/Test/TestMultiRotation01.py [new file with mode: 0644]
src/SketchPlugin/Test/TestMultiRotation02.py [new file with mode: 0644]
src/SketchPlugin/Test/TestMultiRotation03.py [new file with mode: 0644]
src/SketchPlugin/Test/TestMultiRotation04.py [new file with mode: 0644]
src/SketchPlugin/Test/TestMultiRotation05.py [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml
src/SketchSolver/SketchSolver_ConstraintMultiRotation.cpp
src/SketchSolver/SketchSolver_ConstraintMultiRotation.h

index a736df25b8f759e1d19d9de19d71d93a24de1698..67393b56e5642caaed6a200905e5111e0cf547c0 100644 (file)
@@ -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
index 5583e5ddf75db4a4963cccf579a38fd0e37b3b7f..d776efccce7f60668db9b0371cec30f4c4002965 100644 (file)
@@ -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(),
index 63e7a10f451ac8b68b5728ecb1e82a98bb9cbab8..2e38d2d33f3b9c4c083430daf437e576989d1087 100644 (file)
@@ -509,13 +509,14 @@ std::shared_ptr<SketchAPI_Rotation> SketchAPI_Sketch::addRotation(
     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));
 }
 
 //--------------------------------------------------------------------------------------
index 3e531012dcfc64b7721e9d80aaa0c3d20335797e..5e7d902e8f6bc6b03528d2d883dc450c7b36ee06 100644 (file)
@@ -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
index 2e5f42bc7c99c9095a1725affcc1556a558efc0a..4fa4f8c0fa0f068aa86d00609488ba6aabafce61 100644 (file)
@@ -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
index 3fd19dc330fb75afab595294c0da47b59bb78326..6a9a1596eab33ba9ae5e7525cc7c06ed1fd81686 100755 (executable)
@@ -23,6 +23,7 @@
 
 #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)
 {
 }
 
@@ -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;
+  }
 }
index 9b43abe82edc1fc610483ca670915774e93b29e2..8e8f2b89cda187761059554349541baa2779a58c 100644 (file)
@@ -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
index aaf1402ed22edbaf8b10c74c7759cc21d29227fc..a392b6ef74c1536958545b24cd07f46460f5fbce 100644 (file)
@@ -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);
index 31d7069da86ed8d2591e60194089df8e28f11af1..0dce7c4fe454aa8240bca588a467ee402468b67a 100755 (executable)
@@ -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 <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>
@@ -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<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;
+}
index 56ab74b7a236a5e18fecf8e52a0b58448a53b353..bfaffbeda13bbf6c246793c5e4524d5bb0bcefe1 100644 (file)
@@ -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<std::string>& theArguments,
+                       Events_InfoMessage& theError) const;
+};
+
 #endif
diff --git a/src/SketchPlugin/Test/TestMultiRotation01.py b/src/SketchPlugin/Test/TestMultiRotation01.py
new file mode 100644 (file)
index 0000000..4577e1b
--- /dev/null
@@ -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<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()
diff --git a/src/SketchPlugin/Test/TestMultiRotation02.py b/src/SketchPlugin/Test/TestMultiRotation02.py
new file mode 100644 (file)
index 0000000..6a872a4
--- /dev/null
@@ -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<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()
diff --git a/src/SketchPlugin/Test/TestMultiRotation03.py b/src/SketchPlugin/Test/TestMultiRotation03.py
new file mode 100644 (file)
index 0000000..a3d02b6
--- /dev/null
@@ -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<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())
diff --git a/src/SketchPlugin/Test/TestMultiRotation04.py b/src/SketchPlugin/Test/TestMultiRotation04.py
new file mode 100644 (file)
index 0000000..1feca45
--- /dev/null
@@ -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<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())
diff --git a/src/SketchPlugin/Test/TestMultiRotation05.py b/src/SketchPlugin/Test/TestMultiRotation05.py
new file mode 100644 (file)
index 0000000..a811f64
--- /dev/null
@@ -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<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())
index 2ebc0bf3ed2b211c2c901f6cfdaeb91e955e7208..366b8b63b8f8bafc4f8ca50b51e19149e1e3d194 100644 (file)
@@ -607,14 +607,30 @@ email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com
                          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"
index ce40fdcafb786ff78ee5302a7e265fa4b437f144..fba148a7ae0f015dae4258fae73ebb465979af88 100644 (file)
@@ -35,7 +35,7 @@
 
 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;
@@ -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<EntityWrapperPtr> 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);
index d174a560433a796e50bd1699c56200c243ac7d35..f2c74237074c3b443b5291cd3186a1ca3dccb693 100644 (file)
@@ -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<EntityWrapperPtr>& 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