]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Merge remote-tracking branch 'remotes/origin/2018_Lot1_Tasks'
authordbv <dbv@opencascade.com>
Mon, 18 Jun 2018 06:22:32 +0000 (09:22 +0300)
committerdbv <dbv@opencascade.com>
Mon, 18 Jun 2018 06:22:32 +0000 (09:22 +0300)
86 files changed:
src/FeaturesAPI/CMakeLists.txt
src/FeaturesAPI/FeaturesAPI.i
src/FeaturesAPI/FeaturesAPI_Measurement.cpp [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_Measurement.h [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_Symmetry.cpp
src/FeaturesAPI/FeaturesAPI_Symmetry.h
src/FeaturesAPI/FeaturesAPI_swig.h
src/FeaturesPlugin/CMakeLists.txt
src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_Measurement.h [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp
src/FeaturesPlugin/FeaturesPlugin_Symmetry.cpp
src/FeaturesPlugin/FeaturesPlugin_Symmetry.h
src/FeaturesPlugin/FeaturesPlugin_Validators.cpp
src/FeaturesPlugin/FeaturesPlugin_Validators.h
src/FeaturesPlugin/Test/TestMeasurementAngle.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestMeasurementDistance.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestMeasurementLength.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestMeasurementRadius.py [new file with mode: 0644]
src/FeaturesPlugin/icons/edge.png [new file with mode: 0644]
src/FeaturesPlugin/icons/meas_angle_32x32.png [new file with mode: 0644]
src/FeaturesPlugin/icons/meas_distance_32x32.png [new file with mode: 0644]
src/FeaturesPlugin/icons/meas_length_32x32.png [new file with mode: 0644]
src/FeaturesPlugin/icons/meas_radius_32x32.png [new file with mode: 0644]
src/FeaturesPlugin/icons/measurement.png [new file with mode: 0644]
src/FeaturesPlugin/measurement_widget.xml [new file with mode: 0644]
src/FeaturesPlugin/plugin-Features.xml
src/GeomAPI/CMakeLists.txt
src/GeomAPI/GeomAPI_Angle.cpp [new file with mode: 0644]
src/GeomAPI/GeomAPI_Angle.h [new file with mode: 0644]
src/GeomAPI/GeomAPI_Angle2d.h
src/GeomAPI/GeomAPI_Edge.cpp
src/GeomAPI/GeomAPI_Edge.h
src/GeomAPI/GeomAPI_Face.h
src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp
src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h
src/InitializationPlugin/InitializationPlugin_PyInterp.cpp
src/Model/Model_AttributeSelection.cpp
src/Model/Model_ResultConstruction.cpp
src/Model/Model_Update.cpp
src/ModelAPI/Test/TestCustomName_Recover.py
src/ModuleBase/ModuleBase_ParamSpinBox.cpp
src/ModuleBase/ModuleBase_ParamSpinBox.h
src/ModuleBase/ModuleBase_Tools.cpp
src/ModuleBase/ModuleBase_Tools.h
src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp
src/ModuleBase/ModuleBase_WidgetDoubleValue.h
src/ModuleBase/ModuleBase_WidgetExprEditor.cpp
src/ModuleBase/ModuleBase_WidgetIntValue.cpp
src/ModuleBase/ModuleBase_WidgetIntValue.h
src/PartSet/PartSet_Module.cpp
src/PartSet/PartSet_Validators.cpp
src/PartSet/PartSet_Validators.h
src/PythonAPI/model/features/__init__.py
src/SketchAPI/SketchAPI_IntersectionPoint.cpp
src/SketchAPI/SketchAPI_IntersectionPoint.h
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_IntersectionPoint.cpp
src/SketchPlugin/SketchPlugin_IntersectionPoint.h
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/TestIntersectionChangeEdge.py [new file with mode: 0644]
src/SketchPlugin/Test/TestIntersectionWithCircle.py [new file with mode: 0644]
src/SketchPlugin/Test/TestIntersectionWithLine.py [new file with mode: 0644]
src/SketchPlugin/Test/TestIntersectionWithSpline.py [new file with mode: 0644]
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/icons/intersection.png [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml
src/SketchSolver/SketchSolver_ConstraintMultiRotation.cpp
src/SketchSolver/SketchSolver_ConstraintMultiRotation.h
src/XGUI/XGUI_DataModel.cpp
src/XGUI/XGUI_ObjectsBrowser.cpp
src/XGUI/XGUI_ObjectsBrowser.h
src/XGUI/XGUI_Workshop.cpp
test.API/SHAPER/Transformations/TestSymmetry.py

index 1c549224202d4f881ecb6d847c8bad37a59ac8b4..83e853688b8a54f2efe19def56dfe5c474a0c7a4 100644 (file)
@@ -27,6 +27,7 @@ SET(PROJECT_HEADERS
   FeaturesAPI_ExtrusionBoolean.h
   FeaturesAPI_Fillet.h
   FeaturesAPI_Intersection.h
+  FeaturesAPI_Measurement.h
   FeaturesAPI_MultiRotation.h
   FeaturesAPI_MultiTranslation.h
   FeaturesAPI_Partition.h
@@ -49,6 +50,7 @@ SET(PROJECT_SOURCES
   FeaturesAPI_ExtrusionBoolean.cpp
   FeaturesAPI_Fillet.cpp
   FeaturesAPI_Intersection.cpp
+  FeaturesAPI_Measurement.cpp
   FeaturesAPI_MultiRotation.cpp
   FeaturesAPI_MultiTranslation.cpp
   FeaturesAPI_Partition.cpp
index 62afc3e4889829499a60f6e0870b7319d3ffee73..01d1c9197b4239835df6ef0ef6ea6b9a706dfc53 100644 (file)
@@ -69,6 +69,7 @@
 %include "FeaturesAPI_ExtrusionBoolean.h"
 %include "FeaturesAPI_Fillet.h"
 %include "FeaturesAPI_Intersection.h"
+%include "FeaturesAPI_Measurement.h"
 %include "FeaturesAPI_MultiRotation.h"
 %include "FeaturesAPI_MultiTranslation.h"
 %include "FeaturesAPI_Partition.h"
diff --git a/src/FeaturesAPI/FeaturesAPI_Measurement.cpp b/src/FeaturesAPI/FeaturesAPI_Measurement.cpp
new file mode 100644 (file)
index 0000000..466aa01
--- /dev/null
@@ -0,0 +1,117 @@
+// Copyright (C) 2018-20xx  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or
+// email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
+//
+
+#include "FeaturesAPI_Measurement.h"
+
+#include <FeaturesPlugin_Measurement.h>
+#include <ModelAPI_AttributeDoubleArray.h>
+#include <ModelHighAPI_Services.h>
+#include <ModelHighAPI_Tools.h>
+
+double measureLength(const std::shared_ptr<ModelAPI_Document>& thePart,
+                     const ModelHighAPI_Selection& theEdge)
+{
+  FeaturePtr aMeasure = thePart->addFeature(FeaturesPlugin_Measurement::ID());
+  fillAttribute(FeaturesPlugin_Measurement::MEASURE_LENGTH(),
+                aMeasure->string(FeaturesPlugin_Measurement::MEASURE_KIND()));
+  fillAttribute(theEdge, aMeasure->selection(FeaturesPlugin_Measurement::EDGE_FOR_LENGTH_ID()));
+  aMeasure->execute();
+
+  // obtain result
+  AttributeDoubleArrayPtr aResult = std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(
+      aMeasure->attribute(FeaturesPlugin_Measurement::RESULT_VALUES_ID()));
+  double aValue = aResult->size() ? aResult->value(0) : -1.0;
+
+  // perform removing macro feature Measurement
+  thePart->removeFeature(aMeasure);
+  apply();
+
+  return aValue;
+}
+
+double measureDistance(const std::shared_ptr<ModelAPI_Document>& thePart,
+                       const ModelHighAPI_Selection& theFrom,
+                       const ModelHighAPI_Selection& theTo)
+{
+  FeaturePtr aMeasure = thePart->addFeature(FeaturesPlugin_Measurement::ID());
+  fillAttribute(FeaturesPlugin_Measurement::MEASURE_DISTANCE(),
+                aMeasure->string(FeaturesPlugin_Measurement::MEASURE_KIND()));
+  fillAttribute(theFrom,
+                aMeasure->selection(FeaturesPlugin_Measurement::DISTANCE_FROM_OBJECT_ID()));
+  fillAttribute(theTo, aMeasure->selection(FeaturesPlugin_Measurement::DISTANCE_TO_OBJECT_ID()));
+  aMeasure->execute();
+
+  // obtain result
+  AttributeDoubleArrayPtr aResult = std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(
+      aMeasure->attribute(FeaturesPlugin_Measurement::RESULT_VALUES_ID()));
+  double aValue = aResult->size() ? aResult->value(0) : -1.0;
+
+  // perform removing macro feature Measurement
+  thePart->removeFeature(aMeasure);
+  apply();
+
+  return aValue;
+}
+
+double measureRadius(const std::shared_ptr<ModelAPI_Document>& thePart,
+                     const ModelHighAPI_Selection& theObject)
+{
+  FeaturePtr aMeasure = thePart->addFeature(FeaturesPlugin_Measurement::ID());
+  fillAttribute(FeaturesPlugin_Measurement::MEASURE_RADIUS(),
+                aMeasure->string(FeaturesPlugin_Measurement::MEASURE_KIND()));
+  fillAttribute(theObject, aMeasure->selection(FeaturesPlugin_Measurement::CIRCULAR_OBJECT_ID()));
+  aMeasure->execute();
+
+  // obtain result
+  AttributeDoubleArrayPtr aResult = std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(
+      aMeasure->attribute(FeaturesPlugin_Measurement::RESULT_VALUES_ID()));
+  double aValue = aResult->size() ? aResult->value(0) : -1.0;
+
+  // perform removing macro feature Measurement
+  thePart->removeFeature(aMeasure);
+  apply();
+
+  return aValue;
+}
+
+std::list<double> measureAngle(const std::shared_ptr<ModelAPI_Document>& thePart,
+                               const ModelHighAPI_Selection& theFrom,
+                               const ModelHighAPI_Selection& theTo)
+{
+  FeaturePtr aMeasure = thePart->addFeature(FeaturesPlugin_Measurement::ID());
+  fillAttribute(FeaturesPlugin_Measurement::MEASURE_ANGLE(),
+                aMeasure->string(FeaturesPlugin_Measurement::MEASURE_KIND()));
+  fillAttribute(theFrom, aMeasure->selection(FeaturesPlugin_Measurement::ANGLE_FROM_EDGE_ID()));
+  fillAttribute(theTo, aMeasure->selection(FeaturesPlugin_Measurement::ANGLE_TO_EDGE_ID()));
+  aMeasure->execute();
+
+  // obtain result
+  AttributeDoubleArrayPtr aResult = std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(
+      aMeasure->attribute(FeaturesPlugin_Measurement::RESULT_VALUES_ID()));
+  std::list<double> aValues;
+  for (int i = 0, n = aResult->size(); i < n; ++i)
+    aValues.push_back(aResult->value(i));
+
+  // perform removing macro feature Measurement
+  thePart->removeFeature(aMeasure);
+  apply();
+
+  return aValues;
+}
diff --git a/src/FeaturesAPI/FeaturesAPI_Measurement.h b/src/FeaturesAPI/FeaturesAPI_Measurement.h
new file mode 100644 (file)
index 0000000..2712018
--- /dev/null
@@ -0,0 +1,58 @@
+// Copyright (C) 2018-20xx  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or
+// email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
+//
+
+#ifndef FeaturesAPI_Measurement_H_
+#define FeaturesAPI_Measurement_H_
+
+#include "FeaturesAPI.h"
+
+#include <list>
+#include <memory>
+
+class ModelAPI_Document;
+class ModelHighAPI_Selection;
+
+/// \ingroup CPPHighAPI
+/// \brief Calculate length of the edge.
+FEATURESAPI_EXPORT
+double measureLength(const std::shared_ptr<ModelAPI_Document>& thePart,
+                     const ModelHighAPI_Selection& theEdge);
+
+/// \ingroup CPPHighAPI
+/// \brief Calculate distance between objects.
+FEATURESAPI_EXPORT
+double measureDistance(const std::shared_ptr<ModelAPI_Document>& thePart,
+                       const ModelHighAPI_Selection& theFrom,
+                       const ModelHighAPI_Selection& theTo);
+
+/// \ingroup CPPHighAPI
+/// \brief Calculate radius of circular.
+FEATURESAPI_EXPORT
+double measureRadius(const std::shared_ptr<ModelAPI_Document>& thePart,
+                     const ModelHighAPI_Selection& theObject);
+
+/// \ingroup CPPHighAPI
+/// \brief Calculate angle(s) between edges.
+FEATURESAPI_EXPORT
+std::list<double> measureAngle(const std::shared_ptr<ModelAPI_Document>& thePart,
+                               const ModelHighAPI_Selection& theFrom,
+                               const ModelHighAPI_Selection& theTo);
+
+#endif // FeaturesAPI_Measurement_H_
index 8f48cc96599a72169e5a8c359351bdab651814da..6e8b8b6220b5557d4e7cbd53bd241c750415bfba 100644 (file)
@@ -32,8 +32,8 @@ FeaturesAPI_Symmetry::FeaturesAPI_Symmetry(const std::shared_ptr<ModelAPI_Featur
 
 //==================================================================================================
 FeaturesAPI_Symmetry::FeaturesAPI_Symmetry(const std::shared_ptr<ModelAPI_Feature>& theFeature,
-                                       const std::list<ModelHighAPI_Selection>& theMainObjects,
-                                       const ModelHighAPI_Selection& theObject)
+                                           const std::list<ModelHighAPI_Selection>& theMainObjects,
+                                           const ModelHighAPI_Selection& theObject)
 : ModelHighAPI_Interface(theFeature)
 {
   if(initialize()) {
@@ -98,7 +98,7 @@ void FeaturesAPI_Symmetry::dump(ModelHighAPI_Dumper& theDumper) const
 
   AttributeSelectionListPtr anAttrObjects =
     aBase->selectionList(FeaturesPlugin_Symmetry::OBJECTS_LIST_ID());
-  theDumper << aBase << " = model.addSymmetry(" << aDocName << ", " << anAttrObjects;
+  theDumper << aBase << " = model.addMirror(" << aDocName << ", " << anAttrObjects;
 
   std::string aCreationMethod =
     aBase->string(FeaturesPlugin_Symmetry::CREATION_METHOD())->value();
@@ -121,9 +121,9 @@ void FeaturesAPI_Symmetry::dump(ModelHighAPI_Dumper& theDumper) const
 }
 
 //==================================================================================================
-SymmetryPtr addSymmetry(const std::shared_ptr<ModelAPI_Document>& thePart,
-                              const std::list<ModelHighAPI_Selection>& theMainObjects,
-                              const ModelHighAPI_Selection& theObject)
+SymmetryPtr addMirror(const std::shared_ptr<ModelAPI_Document>& thePart,
+                      const std::list<ModelHighAPI_Selection>& theMainObjects,
+                      const ModelHighAPI_Selection& theObject)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature = thePart->addFeature(FeaturesAPI_Symmetry::ID());
   return SymmetryPtr(new FeaturesAPI_Symmetry(aFeature, theMainObjects, theObject));
index 77ef644980ce7d3f3ccbf55da13f0d97fc290131..1b4ab9dacdc172394ea5a61c59474484976e3c20 100644 (file)
@@ -34,7 +34,7 @@ class ModelHighAPI_Selection;
 
 /// \class FeaturesAPI_Symmetry
 /// \ingroup CPPHighAPI
-/// \brief Interface for Symmetry feature.
+/// \brief Interface for the Mirror Copy feature.
 class FeaturesAPI_Symmetry: public ModelHighAPI_Interface
 {
 public:
@@ -45,8 +45,8 @@ public:
   /// Constructor with values.
   FEATURESAPI_EXPORT
   explicit FeaturesAPI_Symmetry(const std::shared_ptr<ModelAPI_Feature>& theFeature,
-                                   const std::list<ModelHighAPI_Selection>& theMainObjects,
-                                   const ModelHighAPI_Selection& theObject);
+                                const std::list<ModelHighAPI_Selection>& theMainObjects,
+                                const ModelHighAPI_Selection& theObject);
 
   /// Destructor.
   FEATURESAPI_EXPORT
@@ -89,10 +89,10 @@ public:
 typedef std::shared_ptr<FeaturesAPI_Symmetry> SymmetryPtr;
 
 /// \ingroup CPPHighAPI
-/// \brief Create Symmetry feature.
+/// \brief Create the Mirror Copy feature.
 FEATURESAPI_EXPORT
-SymmetryPtr addSymmetry(const std::shared_ptr<ModelAPI_Document>& thePart,
-                    const std::list<ModelHighAPI_Selection>& theMainObjects,
-                    const ModelHighAPI_Selection& theObject);
+SymmetryPtr addMirror(const std::shared_ptr<ModelAPI_Document>& thePart,
+                      const std::list<ModelHighAPI_Selection>& theMainObjects,
+                      const ModelHighAPI_Selection& theObject);
 
 #endif // FEATURESAPI_SYMMETRY_H_
index 8b6ce0005d468d1366028a17764a6ba9a7fea3cd..a45b780ca99321045a6d45a2881e6da57641a75c 100644 (file)
@@ -29,6 +29,7 @@
   #include "FeaturesAPI_ExtrusionBoolean.h"
   #include "FeaturesAPI_Fillet.h"
   #include "FeaturesAPI_Intersection.h"
+  #include "FeaturesAPI_Measurement.h"
   #include "FeaturesAPI_MultiRotation.h"
   #include "FeaturesAPI_MultiTranslation.h"
   #include "FeaturesAPI_Partition.h"
index b0be8b41278ecdeba343131e49998b7b0d07c41d..ed5523231db41dedb98db313a44fbdac26acb69c 100644 (file)
@@ -52,6 +52,7 @@ SET(PROJECT_HEADERS
     FeaturesPlugin_MultiTranslation.h
     FeaturesPlugin_MultiRotation.h
     FeaturesPlugin_Fillet.h
+    FeaturesPlugin_Measurement.h
 )
 
 SET(PROJECT_SOURCES
@@ -84,6 +85,7 @@ SET(PROJECT_SOURCES
     FeaturesPlugin_MultiTranslation.cpp
     FeaturesPlugin_MultiRotation.cpp
     FeaturesPlugin_Fillet.cpp
+    FeaturesPlugin_Measurement.cpp
 )
 
 SET(XML_RESOURCES
@@ -109,6 +111,7 @@ SET(XML_RESOURCES
   multitranslation_widget.xml
   multirotation_widget.xml
   fillet_widget.xml
+  measurement_widget.xml
 )
 
 SET(TEXT_RESOURCES
@@ -219,6 +222,10 @@ ADD_UNIT_TESTS(TestExtrusion.py
                TestUnion4CurvedFaces.py
                TestUnion4Faces.py
                TestUnionOfUnion.py
+               TestMeasurementLength.py
+               TestMeasurementDistance.py
+               TestMeasurementRadius.py
+               TestMeasurementAngle.py
                Test1922.py
                Test1942.py
                Test1915.py
diff --git a/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp b/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp
new file mode 100644 (file)
index 0000000..0082c2f
--- /dev/null
@@ -0,0 +1,253 @@
+// Copyright (C) 2018-20xx  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or
+// email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
+//
+
+#include "FeaturesPlugin_Measurement.h"
+
+#include <ModelAPI_AttributeDoubleArray.h>
+#include <ModelAPI_AttributeSelection.h>
+#include <ModelAPI_AttributeString.h>
+#include <ModelAPI_Data.h>
+#include <ModelAPI_Session.h>
+
+#include <GeomAPI_Angle.h>
+#include <GeomAPI_Circ.h>
+#include <GeomAPI_Edge.h>
+#include <GeomAPI_Face.h>
+#include <GeomAPI_Pnt.h>
+#include <GeomAPI_Shape.h>
+#include <GeomAPI_ShapeIterator.h>
+
+#include <GeomAlgoAPI_ShapeTools.h>
+
+#include <iomanip>
+#include <sstream>
+
+FeaturesPlugin_Measurement::FeaturesPlugin_Measurement()
+{
+}
+
+void FeaturesPlugin_Measurement::initAttributes()
+{
+  data()->addAttribute(FeaturesPlugin_Measurement::MEASURE_KIND(),
+                       ModelAPI_AttributeString::typeId());
+
+  // attribute for length
+  data()->addAttribute(EDGE_FOR_LENGTH_ID(), ModelAPI_AttributeSelection::typeId());
+  // attributes for distance
+  data()->addAttribute(DISTANCE_FROM_OBJECT_ID(), ModelAPI_AttributeSelection::typeId());
+  data()->addAttribute(DISTANCE_TO_OBJECT_ID(), ModelAPI_AttributeSelection::typeId());
+  // attribute for radius
+  data()->addAttribute(CIRCULAR_OBJECT_ID(), ModelAPI_AttributeSelection::typeId());
+  // attribute for angle
+  data()->addAttribute(ANGLE_FROM_EDGE_ID(), ModelAPI_AttributeSelection::typeId());
+  data()->addAttribute(ANGLE_TO_EDGE_ID(), ModelAPI_AttributeSelection::typeId());
+  // attribute for result message and values
+  data()->addAttribute(RESULT_ID(), ModelAPI_AttributeString::typeId());
+  data()->addAttribute(RESULT_VALUES_ID(), ModelAPI_AttributeDoubleArray::typeId());
+}
+
+void FeaturesPlugin_Measurement::execute()
+{
+}
+
+void FeaturesPlugin_Measurement::attributeChanged(const std::string& theID)
+{
+  if (theID == MEASURE_KIND()) {
+    // clear selected objects
+    selection(EDGE_FOR_LENGTH_ID())->reset();
+    selection(DISTANCE_FROM_OBJECT_ID())->reset();
+    selection(DISTANCE_TO_OBJECT_ID())->reset();
+    selection(CIRCULAR_OBJECT_ID())->reset();
+    selection(ANGLE_FROM_EDGE_ID())->reset();
+    selection(ANGLE_TO_EDGE_ID())->reset();
+    string(RESULT_ID())->setValue("");
+    std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(
+        attribute(RESULT_VALUES_ID()))->setSize(0);
+  }
+  else if (theID != RESULT_ID()) {
+    std::string aKind = string(MEASURE_KIND())->value();
+    if (aKind == MEASURE_LENGTH())
+      computeLength();
+    else if (aKind == MEASURE_DISTANCE())
+      computeDistance();
+    else if (aKind == MEASURE_RADIUS())
+      computeRadius();
+    else if (aKind == MEASURE_ANGLE())
+      computeAngle();
+  }
+}
+
+void FeaturesPlugin_Measurement::computeLength()
+{
+  AttributeSelectionPtr aSelectedFeature = selection(EDGE_FOR_LENGTH_ID());
+
+  GeomShapePtr aShape;
+  GeomEdgePtr anEdge;
+  if (aSelectedFeature)
+    aShape = aSelectedFeature->value();
+  if (!aShape && aSelectedFeature->context())
+    aShape = aSelectedFeature->context()->shape();
+  if (aShape && aShape->isEdge())
+    anEdge = GeomEdgePtr(new GeomAPI_Edge(aShape));
+  if (!anEdge)
+    return;
+
+  std::ostringstream anOutput;
+  anOutput << "Length = " << std::setprecision(10) << anEdge->length();
+  string(RESULT_ID())->setValue(anOutput.str());
+
+  AttributeDoubleArrayPtr aValues =
+      std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(attribute(RESULT_VALUES_ID()));
+  aValues->setSize(1);
+  aValues->setValue(0, anEdge->length());
+}
+
+void FeaturesPlugin_Measurement::computeDistance()
+{
+  AttributeSelectionPtr aFirstFeature = selection(DISTANCE_FROM_OBJECT_ID());
+  GeomShapePtr aShape1;
+  if (aFirstFeature)
+    aShape1 = aFirstFeature->value();
+  if (!aShape1 && aFirstFeature->context())
+    aShape1 = aFirstFeature->context()->shape();
+
+  AttributeSelectionPtr aSecondFeature = selection(DISTANCE_TO_OBJECT_ID());
+  GeomShapePtr aShape2;
+  if (aSecondFeature)
+    aShape2 = aSecondFeature->value();
+  if (!aShape2 && aSecondFeature->context())
+    aShape2 = aSecondFeature->context()->shape();
+
+  if (!aShape1 || !aShape2)
+    return;
+
+  double aDistance = GeomAlgoAPI_ShapeTools::minimalDistance(aShape1, aShape2);
+
+  std::ostringstream anOutput;
+  anOutput << "Distance = " << std::setprecision(10) << aDistance;
+  string(RESULT_ID())->setValue(anOutput.str());
+
+  AttributeDoubleArrayPtr aValues =
+      std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(attribute(RESULT_VALUES_ID()));
+  aValues->setSize(1);
+  aValues->setValue(0, aDistance);
+}
+
+void FeaturesPlugin_Measurement::computeRadius()
+{
+  AttributeSelectionPtr aSelectedFeature = selection(CIRCULAR_OBJECT_ID());
+
+  GeomShapePtr aShape;
+  if (aSelectedFeature)
+    aShape = aSelectedFeature->value();
+  if (!aShape && aSelectedFeature->context())
+    aShape = aSelectedFeature->context()->shape();
+
+  double aRadius = -1.0;
+  if (aShape) {
+    if (aShape->isEdge()) {
+      GeomEdgePtr anEdge(new GeomAPI_Edge(aShape));
+      if (anEdge->isCircle()) {
+        aRadius = anEdge->circle()->radius();
+      }
+    } else if (aShape->isFace()) {
+      GeomFacePtr aFace(new GeomAPI_Face(aShape));
+      aRadius = GeomAlgoAPI_ShapeTools::radius(aFace);
+    }
+  }
+
+  if (aRadius < 0.0)
+    return;
+
+  std::ostringstream anOutput;
+  anOutput << "Radius = " << std::setprecision(10) << aRadius;
+  string(RESULT_ID())->setValue(anOutput.str());
+
+  AttributeDoubleArrayPtr aValues =
+      std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(attribute(RESULT_VALUES_ID()));
+  aValues->setSize(1);
+  aValues->setValue(0, aRadius);
+}
+
+void FeaturesPlugin_Measurement::computeAngle()
+{
+  AttributeSelectionPtr aFirstFeature = selection(ANGLE_FROM_EDGE_ID());
+  GeomShapePtr aShape1;
+  GeomEdgePtr anEdge1;
+  if (aFirstFeature)
+    aShape1 = aFirstFeature->value();
+  if (!aShape1 && aFirstFeature->context())
+    aShape1 = aFirstFeature->context()->shape();
+  if (aShape1 && aShape1->isEdge())
+    anEdge1 = GeomEdgePtr(new GeomAPI_Edge(aShape1));
+
+
+  AttributeSelectionPtr aSecondFeature = selection(ANGLE_TO_EDGE_ID());
+  GeomShapePtr aShape2;
+  GeomEdgePtr anEdge2;
+  if (aSecondFeature)
+    aShape2 = aSecondFeature->value();
+  if (!aShape2 && aSecondFeature->context())
+    aShape2 = aSecondFeature->context()->shape();
+  if (aShape2 && aShape2->isEdge())
+    anEdge2 = GeomEdgePtr(new GeomAPI_Edge(aShape2));
+
+  if (!anEdge1 || !anEdge2)
+    return;
+
+  GeomShapePtr anInter = anEdge1->intersect(anEdge2);
+
+  std::ostringstream anOutput;
+  anOutput << std::setprecision(10);
+  std::list<double> aValuesList;
+  if (anInter) {
+    if (anInter->isVertex()) {
+      std::shared_ptr<GeomAPI_Vertex> aVertex(new GeomAPI_Vertex(anInter));
+      std::shared_ptr<GeomAPI_Angle> anAngle(
+          new GeomAPI_Angle(anEdge1, anEdge2, aVertex->point()));
+      double anAngleValue = anAngle->angleDegree();
+      anOutput << "Angle = " << anAngleValue << std::endl;
+      aValuesList.push_back(anAngleValue);
+    }
+    else {
+      GeomAPI_ShapeIterator anIt(anInter);
+      for (int anIndex = 1; anIt.more(); anIt.next(), ++anIndex) {
+        GeomShapePtr aCurrent = anIt.current();
+        if (!aCurrent->isVertex())
+          continue;
+        std::shared_ptr<GeomAPI_Vertex> aVertex(new GeomAPI_Vertex(aCurrent));
+        std::shared_ptr<GeomAPI_Angle> anAngle(
+            new GeomAPI_Angle(anEdge1, anEdge2, aVertex->point()));
+        double anAngleValue = anAngle->angleDegree();
+        anOutput << "Angle" << anIndex << " = " << anAngleValue << std::endl;
+        aValuesList.push_back(anAngleValue);
+      }
+    }
+  }
+
+  string(RESULT_ID())->setValue(anOutput.str());
+
+  AttributeDoubleArrayPtr aValues =
+      std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(attribute(RESULT_VALUES_ID()));
+  aValues->setSize((int)aValuesList.size());
+  int anIndex = 0;
+  for (std::list<double>::iterator anIt = aValuesList.begin(); anIt != aValuesList.end(); ++anIt)
+    aValues->setValue(anIndex++, *anIt);
+}
diff --git a/src/FeaturesPlugin/FeaturesPlugin_Measurement.h b/src/FeaturesPlugin/FeaturesPlugin_Measurement.h
new file mode 100644 (file)
index 0000000..a358155
--- /dev/null
@@ -0,0 +1,171 @@
+// Copyright (C) 2018-20xx  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or
+// email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
+//
+
+#ifndef FeaturesPlugin_Measurement_H_
+#define FeaturesPlugin_Measurement_H_
+
+#include "FeaturesPlugin.h"
+#include <ModelAPI_Feature.h>
+
+/// \class FeaturesPlugin_Measurement
+/// \ingroup Plugins
+/// \brief Feature for calculation metrics.
+///
+/// Supported following metrics:
+/// * length of edge,
+/// * distance between shapes,
+/// * radius of arc or cylindrical faces,
+/// * angle between edges.
+class FeaturesPlugin_Measurement : public ModelAPI_Feature
+{
+public:
+  /// Feature kind.
+  inline static const std::string& ID()
+  {
+    static const std::string MY_ID("Measurement");
+    return MY_ID;
+  }
+
+  /// \return the kind of a feature.
+  virtual const std::string& getKind()
+  {
+    return ID();
+  }
+
+  /// Attribute name for measurement method.
+  inline static const std::string& MEASURE_KIND()
+  {
+    static const std::string MY_MEASURE_KIND_ID("MeasureKind");
+    return MY_MEASURE_KIND_ID;
+  }
+
+  /// Attribute name for length measure.
+  inline static const std::string& MEASURE_LENGTH()
+  {
+    static const std::string MY_MEASURE_ID("Length");
+    return MY_MEASURE_ID;
+  }
+
+  /// Attribute name for distance measure.
+  inline static const std::string& MEASURE_DISTANCE()
+  {
+    static const std::string MY_MEASURE_ID("Distance");
+    return MY_MEASURE_ID;
+  }
+
+  /// Attribute name for radius measure.
+  inline static const std::string& MEASURE_RADIUS()
+  {
+    static const std::string MY_MEASURE_ID("Radius");
+    return MY_MEASURE_ID;
+  }
+
+  /// Attribute name for angle measure.
+  inline static const std::string& MEASURE_ANGLE()
+  {
+    static const std::string MY_MEASURE_ID("Angle");
+    return MY_MEASURE_ID;
+  }
+
+
+  /// Attribute name of edge selected for length calculation.
+  inline static const std::string& EDGE_FOR_LENGTH_ID()
+  {
+    static const std::string MY_EDGE_FOR_LENGTH_ID("edge_for_length");
+    return MY_EDGE_FOR_LENGTH_ID;
+  }
+
+  /// Attribute name of first shape selected for distance calculation.
+  inline static const std::string& DISTANCE_FROM_OBJECT_ID()
+  {
+    static const std::string MY_DISTANCE_FROM_OBJECT_ID("distance_from");
+    return MY_DISTANCE_FROM_OBJECT_ID;
+  }
+
+  /// Attribute name of second shape selected for distance calculation.
+  inline static const std::string& DISTANCE_TO_OBJECT_ID()
+  {
+    static const std::string MY_DISTANCE_TO_OBJECT_ID("distance_to");
+    return MY_DISTANCE_TO_OBJECT_ID;
+  }
+
+  // Attribute name of edge or face selected to calculate radius.
+  inline static const std::string& CIRCULAR_OBJECT_ID()
+  {
+    static const std::string MY_CIRCULAR_OBJECT_ID("circular");
+    return MY_CIRCULAR_OBJECT_ID;
+  }
+
+  /// Attribute name of first edge selected for angle calculation.
+  inline static const std::string& ANGLE_FROM_EDGE_ID()
+  {
+    static const std::string MY_ANGLE_FROM_EDGE_ID("angle_from");
+    return MY_ANGLE_FROM_EDGE_ID;
+  }
+
+  /// Attribute name of second shape selected for distance calculation.
+  inline static const std::string& ANGLE_TO_EDGE_ID()
+  {
+    static const std::string MY_ANGLE_TO_EDGE_ID("angle_to");
+    return MY_ANGLE_TO_EDGE_ID;
+  }
+
+  /// Attribute name for result.
+  inline static const std::string& RESULT_ID()
+  {
+    static const std::string MY_RESULT_ID("result");
+    return MY_RESULT_ID;
+  }
+
+  /// Attribute name for values of result.
+  inline static const std::string& RESULT_VALUES_ID()
+  {
+    static const std::string MY_RESULT_VALUES_ID("result_values");
+    return MY_RESULT_VALUES_ID;
+  }
+
+  /// Creates a new part document if needed
+  FEATURESPLUGIN_EXPORT virtual void execute();
+
+  /// Request for initialization of data model of the feature: adding all attributes
+  FEATURESPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Called on change of any argument-attribute of this object
+  /// \param theID identifier of changed attribute
+  FEATURESPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID);
+
+  /// Reimplemented from ModelAPI_Feature::isMacro(). Returns true.
+  virtual bool isMacro() const { return true; }
+
+  /// Use plugin manager for features creation
+  FeaturesPlugin_Measurement();
+
+private:
+  /// Compute length of the edge
+  void computeLength();
+  /// Compute minimal distance between pair of shapes
+  void computeDistance();
+  /// Compute radius of circular edge or cylindrical face
+  void computeRadius();
+  /// Compute angle(s) between pair of edges if they are intersected
+  void computeAngle();
+};
+
+#endif
index b9760dc07027ad7c55a7d4a85171ae343ad62659..2a8023433c763f1f06c3752987da571887980439 100644 (file)
@@ -26,6 +26,7 @@
 #include <FeaturesPlugin_ExtrusionFuse.h>
 #include <FeaturesPlugin_Fillet.h>
 #include <FeaturesPlugin_Intersection.h>
+#include <FeaturesPlugin_Measurement.h>
 #include <FeaturesPlugin_MultiRotation.h>
 #include <FeaturesPlugin_MultiTranslation.h>
 #include <FeaturesPlugin_Partition.h>
@@ -87,6 +88,8 @@ FeaturesPlugin_Plugin::FeaturesPlugin_Plugin()
                               new FeaturesPlugin_ValidatorConcealedResult);
   aFactory->registerValidator("FeaturesPlugin_ValidatorFilletSelection",
                               new FeaturesPlugin_ValidatorFilletSelection);
+  aFactory->registerValidator("FeaturesPlugin_ValidatorCircular",
+                              new FeaturesPlugin_ValidatorCircular);
 
   // register this plugin
   ModelAPI_Session::get()->registerPlugin(this);
@@ -136,6 +139,8 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID)
     return FeaturePtr(new FeaturesPlugin_MultiRotation);
   } else if (theFeatureID == FeaturesPlugin_Fillet::ID()) {
     return FeaturePtr(new FeaturesPlugin_Fillet);
+  } else if (theFeatureID == FeaturesPlugin_Measurement::ID()) {
+    return FeaturePtr(new FeaturesPlugin_Measurement);
   }
 
   // feature of such kind is not found
index 7121e968d710362ec4be831088e383c6ba14f249..418913246cdd883fae29ab0f9c868cdcd0f7e8ef 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <FeaturesPlugin_Symmetry.h>
 
+#include <GeomAlgoAPI_CompoundBuilder.h>
 #include <GeomAlgoAPI_PointBuilder.h>
 #include <GeomAlgoAPI_FaceBuilder.h>
 
@@ -81,27 +82,36 @@ void FeaturesPlugin_Symmetry::execute()
 }
 
 //=================================================================================================
-void FeaturesPlugin_Symmetry::performSymmetryByPoint()
+bool FeaturesPlugin_Symmetry::collectSourceObjects(ListOfShape& theSourceShapes,
+                                                   std::list<ResultPtr>& theSourceResults)
 {
-  // Getting objects.
-  ListOfShape anObjects;
-  std::list<ResultPtr> aContextes;
   AttributeSelectionListPtr anObjectsSelList =
     selectionList(FeaturesPlugin_Symmetry::OBJECTS_LIST_ID());
   if (anObjectsSelList->size() == 0) {
-    return;
+    return false;
   }
-  for(int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
+  for (int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
     std::shared_ptr<ModelAPI_AttributeSelection> anObjectAttr =
       anObjectsSelList->value(anObjectsIndex);
     std::shared_ptr<GeomAPI_Shape> anObject = anObjectAttr->value();
-    if(!anObject.get()) { // may be for not-activated parts
+    if (!anObject.get()) { // may be for not-activated parts
       eraseResults();
-      return;
+      return false;
     }
-    anObjects.push_back(anObject);
-    aContextes.push_back(anObjectAttr->context());
+    theSourceShapes.push_back(anObject);
+    theSourceResults.push_back(anObjectAttr->context());
   }
+  return true;
+}
+
+//=================================================================================================
+void FeaturesPlugin_Symmetry::performSymmetryByPoint()
+{
+  // Getting objects.
+  ListOfShape anObjects;
+  std::list<ResultPtr> aContextes;
+  if (!collectSourceObjects(anObjects, aContextes))
+    return;
 
   //Getting point.
   std::shared_ptr<GeomAPI_Pnt> aPoint;
@@ -130,10 +140,9 @@ void FeaturesPlugin_Symmetry::performSymmetryByPoint()
       std::shared_ptr<GeomAPI_Trsf> aTrsf(new GeomAPI_Trsf());
       aTrsf->setSymmetry(aPoint);
       ResultPartPtr anOrigin = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aContext);
-      ResultPartPtr aResultPart = document()->copyPart(anOrigin, data(), aResultIndex);
-      aResultPart->setTrsf(*aContext, aTrsf);
-      setResult(aResultPart, aResultIndex);
-    } else {
+      buildResult(anOrigin, aTrsf, aResultIndex);
+    }
+    else {
       GeomAlgoAPI_Symmetry aSymmetryAlgo(aBaseShape, aPoint);
 
       if (!aSymmetryAlgo.check()) {
@@ -160,9 +169,7 @@ void FeaturesPlugin_Symmetry::performSymmetryByPoint()
         break;
       }
 
-      ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
-      loadNamingDS(aSymmetryAlgo, aResultBody, aBaseShape);
-      setResult(aResultBody, aResultIndex);
+      buildResult(aSymmetryAlgo, aBaseShape, aResultIndex);
     }
     aResultIndex++;
   }
@@ -177,22 +184,8 @@ void FeaturesPlugin_Symmetry::performSymmetryByAxis()
   // Getting objects.
   ListOfShape anObjects;
   std::list<ResultPtr> aContextes;
-  AttributeSelectionListPtr anObjectsSelList =
-    selectionList(FeaturesPlugin_Symmetry::OBJECTS_LIST_ID());
-  if (anObjectsSelList->size() == 0) {
+  if (!collectSourceObjects(anObjects, aContextes))
     return;
-  }
-  for(int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
-    std::shared_ptr<ModelAPI_AttributeSelection> anObjectAttr =
-      anObjectsSelList->value(anObjectsIndex);
-    std::shared_ptr<GeomAPI_Shape> anObject = anObjectAttr->value();
-    if(!anObject.get()) { // may be for not-activated parts
-      eraseResults();
-      return;
-    }
-    anObjects.push_back(anObject);
-    aContextes.push_back(anObjectAttr->context());
-  }
 
   //Getting axis.
   std::shared_ptr<GeomAPI_Ax1> anAxis;
@@ -223,10 +216,9 @@ void FeaturesPlugin_Symmetry::performSymmetryByAxis()
       std::shared_ptr<GeomAPI_Trsf> aTrsf(new GeomAPI_Trsf());
       aTrsf->setSymmetry(anAxis);
       ResultPartPtr anOrigin = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aContext);
-      ResultPartPtr aResultPart = document()->copyPart(anOrigin, data(), aResultIndex);
-      aResultPart->setTrsf(*aContext, aTrsf);
-      setResult(aResultPart, aResultIndex);
-    } else {
+      buildResult(anOrigin, aTrsf, aResultIndex);
+    }
+    else {
       GeomAlgoAPI_Symmetry aSymmetryAlgo(aBaseShape, anAxis);
 
       if (!aSymmetryAlgo.check()) {
@@ -253,9 +245,7 @@ void FeaturesPlugin_Symmetry::performSymmetryByAxis()
         break;
       }
 
-      ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
-      loadNamingDS(aSymmetryAlgo, aResultBody, aBaseShape);
-      setResult(aResultBody, aResultIndex);
+      buildResult(aSymmetryAlgo, aBaseShape, aResultIndex);
     }
     aResultIndex++;
   }
@@ -270,22 +260,8 @@ void FeaturesPlugin_Symmetry::performSymmetryByPlane()
   // Getting objects.
   ListOfShape anObjects;
   std::list<ResultPtr> aContextes;
-  AttributeSelectionListPtr anObjectsSelList =
-    selectionList(FeaturesPlugin_Symmetry::OBJECTS_LIST_ID());
-  if (anObjectsSelList->size() == 0) {
+  if (!collectSourceObjects(anObjects, aContextes))
     return;
-  }
-  for(int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
-    std::shared_ptr<ModelAPI_AttributeSelection> anObjectAttr =
-      anObjectsSelList->value(anObjectsIndex);
-    std::shared_ptr<GeomAPI_Shape> anObject = anObjectAttr->value();
-    if(!anObject.get()) { // may be for not-activated parts
-      eraseResults();
-      return;
-    }
-    anObjects.push_back(anObject);
-    aContextes.push_back(anObjectAttr->context());
-  }
 
   //Getting axis.
   std::shared_ptr<GeomAPI_Ax2> aPlane;
@@ -318,9 +294,7 @@ void FeaturesPlugin_Symmetry::performSymmetryByPlane()
       std::shared_ptr<GeomAPI_Trsf> aTrsf(new GeomAPI_Trsf());
       aTrsf->setSymmetry(aPlane);
       ResultPartPtr anOrigin = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aContext);
-      ResultPartPtr aResultPart = document()->copyPart(anOrigin, data(), aResultIndex);
-      aResultPart->setTrsf(*aContext, aTrsf);
-      setResult(aResultPart, aResultIndex);
+      buildResult(anOrigin, aTrsf, aResultIndex);
     } else {
       GeomAlgoAPI_Symmetry aSymmetryAlgo(aBaseShape, aPlane);
 
@@ -348,9 +322,7 @@ void FeaturesPlugin_Symmetry::performSymmetryByPlane()
         break;
       }
 
-      ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
-      loadNamingDS(aSymmetryAlgo, aResultBody, aBaseShape);
-      setResult(aResultBody, aResultIndex);
+      buildResult(aSymmetryAlgo, aBaseShape, aResultIndex);
     }
     aResultIndex++;
   }
@@ -359,14 +331,46 @@ void FeaturesPlugin_Symmetry::performSymmetryByPlane()
   removeResults(aResultIndex);
 }
 
+//=================================================================================================
+void FeaturesPlugin_Symmetry::buildResult(GeomAlgoAPI_Symmetry& theSymmetryAlgo,
+                                          std::shared_ptr<GeomAPI_Shape> theBaseShape,
+                                          int theResultIndex)
+{
+  // Compose source shape and the result of symmetry.
+  ListOfShape aShapes;
+  aShapes.push_back(theBaseShape);
+  aShapes.push_back(theSymmetryAlgo.shape());
+  std::shared_ptr<GeomAPI_Shape> aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
+
+  // Store and name the result.
+  ResultBodyPtr aResultBody = document()->createBody(data(), theResultIndex);
+  aResultBody->storeModified(theBaseShape, aCompound);
+  loadNamingDS(theSymmetryAlgo, aResultBody, theBaseShape);
+  setResult(aResultBody, theResultIndex);
+}
+
+//=================================================================================================
+void FeaturesPlugin_Symmetry::buildResult(ResultPartPtr theOriginal,
+                                          std::shared_ptr<GeomAPI_Trsf> theTrsf,
+                                          int& theResultIndex)
+{
+  std::shared_ptr<GeomAPI_Trsf> anIdentity(new GeomAPI_Trsf());
+  ResultPartPtr aCopy = document()->copyPart(theOriginal, data(), theResultIndex);
+  aCopy->setTrsf(theOriginal, anIdentity);
+  setResult(aCopy, theResultIndex);
+
+  ++theResultIndex;
+
+  ResultPartPtr aResultPart = document()->copyPart(theOriginal, data(), theResultIndex);
+  aResultPart->setTrsf(theOriginal, theTrsf);
+  setResult(aResultPart, theResultIndex);
+}
+
 //=================================================================================================
 void FeaturesPlugin_Symmetry::loadNamingDS(GeomAlgoAPI_Symmetry& theSymmetryAlgo,
                                            std::shared_ptr<ModelAPI_ResultBody> theResultBody,
                                            std::shared_ptr<GeomAPI_Shape> theBaseShape)
 {
-  // Store and name the result.
-  theResultBody->storeModified(theBaseShape, theSymmetryAlgo.shape());
-
   // Name the faces
   std::shared_ptr<GeomAPI_DataMapOfShapeShape> aSubShapes = theSymmetryAlgo.mapOfSubShapes();
   std::string aReflectedName = "Symmetried";
index aa67bd79b72580d9c245a18f0bb0bfbf8e38c668..edb152dd117499dc8af9c0bf6746c5efb2379327 100644 (file)
@@ -27,6 +27,8 @@
 
 #include <GeomAlgoAPI_Symmetry.h>
 
+class GeomAPI_Trsf;
+
 /** \class FeaturesPlugin_Symmetry
  *  \ingroup Plugins
  *  \brief Feature that performs reflection with respect to a point, axis, or plane.
@@ -37,7 +39,7 @@ class FeaturesPlugin_Symmetry : public ModelAPI_Feature
   /// Symmetry kind.
   inline static const std::string& ID()
   {
-    static const std::string MY_SYMMETRY_ID("Symmetry");
+    static const std::string MY_SYMMETRY_ID("MirrorCopy");
     return MY_SYMMETRY_ID;
   }
 
@@ -114,6 +116,10 @@ class FeaturesPlugin_Symmetry : public ModelAPI_Feature
   FeaturesPlugin_Symmetry();
 
 private:
+  /// Obtain list of source objects of the mirror
+  bool collectSourceObjects(ListOfShape& theSourceShapes,
+                            std::list<std::shared_ptr<ModelAPI_Result>>& theSourceResults);
+
   /// Perform symmetry with respect to a point.
   void performSymmetryByPoint();
 
@@ -127,6 +133,16 @@ private:
   void loadNamingDS(GeomAlgoAPI_Symmetry& theSymmetryAlgo,
                     std::shared_ptr<ModelAPI_ResultBody> theResultBody,
                     std::shared_ptr<GeomAPI_Shape> theBaseShape);
+
+  /// Create new result on given shapes and the index of result
+  void buildResult(GeomAlgoAPI_Symmetry& theSymmetryAlgo,
+                   std::shared_ptr<GeomAPI_Shape> theBaseShape,
+                   int theResultIndex);
+
+  /// Create new result for the given part and transformation
+  void buildResult(std::shared_ptr<ModelAPI_ResultPart> theOriginal,
+                   std::shared_ptr<GeomAPI_Trsf> theTrsf,
+                   int& theResultIndex);
 };
 
 #endif // FEATURESPLUGIN_SYMMETRY_H_
index b4ca4332fd22bd0347be0ffe5f115e1ed28a2f90..5a06ceaaccb7c195ec89a83c589203d4162ca55a 100644 (file)
@@ -37,6 +37,7 @@
 #include <ModelAPI_Tools.h>
 
 #include <GeomValidators_BodyShapes.h>
+#include <GeomValidators_Face.h>
 #include <GeomValidators_FeatureKind.h>
 #include <GeomValidators_ShapeType.h>
 
@@ -998,3 +999,20 @@ bool FeaturesPlugin_ValidatorConcealedResult::isValid(const AttributePtr& theAtt
 
   return theError.empty();
 }
+
+bool FeaturesPlugin_ValidatorCircular::isValid(const AttributePtr& theAttribute,
+                                               const std::list<std::string>& theArguments,
+                                               Events_InfoMessage& theError) const
+{
+  static std::list<std::string> aEdgeArg(1, "circle");
+  static std::list<std::string> aFaceArg(1, "cylinder");
+
+  Events_InfoMessage aError;
+  bool isValid = GeomValidators_ShapeType().isValid(theAttribute, aEdgeArg, aError);
+  if (!isValid) {
+    isValid = GeomValidators_Face().isValid(theAttribute, aFaceArg, aError);
+    if (!isValid)
+      theError = "The shape neither circle nor cylinder";
+  }
+  return isValid;
+}
index b54b3f0be54992f8d699d2c322e82c6a0cd72905..5824ce9dc08753ffb46219711eae7e46bee418ff 100644 (file)
@@ -252,4 +252,19 @@ class FeaturesPlugin_ValidatorConcealedResult: public ModelAPI_AttributeValidato
                         Events_InfoMessage& theError) const;
 };
 
+/// \class FeaturesPlugin_ValidatorCircular
+/// \ingroup Validators
+/// \brief Verifies the selected object is circular edge or cylindrical face
+class FeaturesPlugin_ValidatorCircular : public ModelAPI_AttributeValidator
+{
+public:
+  //! \return True if the attribute is valid.
+  //! \param[in] theAttribute the checked attribute.
+  //! \param[in] theArguments arguments of the attribute.
+  //! \param[out] theError error message.
+  virtual bool isValid(const AttributePtr& theAttribute,
+                       const std::list<std::string>& theArguments,
+                       Events_InfoMessage& theError) const;
+};
+
 #endif
diff --git a/src/FeaturesPlugin/Test/TestMeasurementAngle.py b/src/FeaturesPlugin/Test/TestMeasurementAngle.py
new file mode 100644 (file)
index 0000000..d00f1c8
--- /dev/null
@@ -0,0 +1,95 @@
+## Copyright (C) 2018-20xx  CEA/DEN, EDF R&D
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public
+## License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+## See http:##www.salome-platform.org/ or
+## email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
+##
+
+from SketchAPI import *
+
+from salome.shaper import model
+
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamR = model.addParameter(Part_1_doc, "R", "50")
+ParamL = model.addParameter(Part_1_doc, "L", "40")
+ParamH = model.addParameter(Part_1_doc, "H", "20")
+Point_2 = model.addPoint(Part_1_doc, 0, 100, 100)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OX"), model.selection("VERTEX", "Point_1"), False)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(50, 50, 50)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], "R")
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_1 = SketchProjection_1.createdFeature()
+SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_1.result(), SketchCircle_1.results()[1])
+SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_2 = SketchProjection_2.createdFeature()
+SketchConstraintTangent_2 = Sketch_1.setTangent(SketchLine_2.result(), SketchCircle_1.results()[1])
+SketchLine_3 = Sketch_1.addLine(70, 40, 30, 40)
+SketchLine_4 = Sketch_1.addLine(30, 40, 30, 60)
+SketchLine_5 = Sketch_1.addLine(30, 60, 70, 60)
+SketchLine_6 = Sketch_1.addLine(70, 60, 70, 40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_3.result(), "L")
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_4.result(), "H")
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "L/2")
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "H/2")
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f")], model.selection(), model.selection("FACE", "Plane_1"), 0, model.selection(), 10)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face_1"))
+SketchLine_7 = Sketch_2.addLine(119.1454520140253, 130.0744845530344, 10, 0)
+SketchProjection_3 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_8 = SketchProjection_3.createdFeature()
+SketchConstraintAngle_1 = Sketch_2.setAngle(SketchLine_8.result(), SketchLine_7.result(), 50)
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchLine_7.endPoint(), SketchLine_8.result())
+SketchConstraintDistanceHorizontal_2 = Sketch_2.setHorizontalDistance(SketchAPI_Line(SketchLine_8).startPoint(), SketchLine_7.endPoint(), 10)
+model.do()
+
+TOLERANCE = 1.e-6
+
+# reference data
+REF_DATA = [("Sketch_1/Edge-SketchLine_3", "Sketch_1/Edge-SketchLine_6", [90]),
+            ("Extrusion_1_1/Generated_Face_5", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", [45]),
+            ("Sketch_1/Edge-SketchCircle_1_2", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", [0]),
+            ("Sketch_1/Edge-SketchLine_3", "Sketch_1/Edge-SketchCircle_1_2", []),
+            ("Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", []),
+            ("Sketch_2/Edge-SketchLine_7", "Extrusion_1_1/To_Face_1&Extrusion_1_1/Generated_Face_4", [130]),
+            ("Sketch_2/Edge-SketchLine_7", "Extrusion_1_1/To_Face_1&Extrusion_1_1/Generated_Face_1", [140]),
+            ("Sketch_2/Edge-SketchLine_7", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", [85.95645561, 95.19768874])
+           ]
+
+for ref in REF_DATA:
+    angle = model.measureAngle(Part_1_doc, model.selection("EDGE", ref[0]), model.selection("EDGE", ref[1]))
+    assert(angle.size() == len(ref[2]))
+    for a, r in zip(angle, ref[2]):
+        assert(math.fabs(a - r) < TOLERANCE), "Angle {} differs from expected value {}".format(a, r)
+
+# select incorrect data
+angle = model.measureAngle(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face_1"), model.selection("EDGE", "Sketch_2/Edge-SketchLine_7"))
+assert(angle.size() == 0)
+
+model.end()
diff --git a/src/FeaturesPlugin/Test/TestMeasurementDistance.py b/src/FeaturesPlugin/Test/TestMeasurementDistance.py
new file mode 100644 (file)
index 0000000..c2b7d0a
--- /dev/null
@@ -0,0 +1,131 @@
+## Copyright (C) 2018-20xx  CEA/DEN, EDF R&D
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public
+## License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+## See http:##www.salome-platform.org/ or
+## email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
+##
+
+from salome.shaper import model
+
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamR = model.addParameter(Part_1_doc, "R", "50")
+ParamL = model.addParameter(Part_1_doc, "L", "40")
+ParamH = model.addParameter(Part_1_doc, "H", "20")
+Point_2 = model.addPoint(Part_1_doc, 0, 100, 100)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OX"), model.selection("VERTEX", "Point_1"), False)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(50, 50, 50)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], "R")
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_1 = SketchProjection_1.createdFeature()
+SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_1.result(), SketchCircle_1.results()[1])
+SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_2 = SketchProjection_2.createdFeature()
+SketchConstraintTangent_2 = Sketch_1.setTangent(SketchLine_2.result(), SketchCircle_1.results()[1])
+SketchLine_3 = Sketch_1.addLine(70, 40, 30, 40)
+SketchLine_4 = Sketch_1.addLine(30, 40, 30, 60)
+SketchLine_5 = Sketch_1.addLine(30, 60, 70, 60)
+SketchLine_6 = Sketch_1.addLine(70, 60, 70, 40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_3.result(), "L")
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_4.result(), "H")
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "L/2")
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "H/2")
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f")], model.selection(), model.selection("FACE", "Plane_1"), 0, model.selection(), 10)
+Sketch_2 = model.addSketch(Part_1_doc, model.standardPlane("YOZ"))
+SketchArc_1 = Sketch_2.addArc(100, 48.41229182762603, 87.5, 0, 87.5, 96.82458365525073, False)
+SketchArc_2 = Sketch_2.addArc(0, 48.41229182762603, 87.5, 0, 87.5, 96.82458365525073, False)
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchArc_1.startPoint(), SketchArc_2.startPoint())
+SketchConstraintCoincidence_6 = Sketch_2.setCoincident(SketchArc_2.endPoint(), SketchArc_1.endPoint())
+SketchConstraintRadius_2 = Sketch_2.setRadius(SketchArc_1.results()[1], "R")
+SketchConstraintRadius_3 = Sketch_2.setRadius(SketchArc_2.results()[1], "2*R")
+SketchLine_7 = Sketch_2.addLine(0, 48.41229182762603, 100, 48.41229182762603)
+SketchLine_7.setAuxiliary(True)
+SketchConstraintCoincidence_7 = Sketch_2.setCoincident(SketchArc_2.center(), SketchLine_7.startPoint())
+SketchConstraintCoincidence_8 = Sketch_2.setCoincident(SketchArc_1.center(), SketchLine_7.endPoint())
+SketchConstraintHorizontal_3 = Sketch_2.setHorizontal(SketchLine_7.result())
+SketchProjection_3 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_8 = SketchProjection_3.createdFeature()
+SketchConstraintCoincidence_9 = Sketch_2.setCoincident(SketchArc_1.startPoint(), SketchLine_8.result())
+SketchProjection_4 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OZ"), False)
+SketchLine_9 = SketchProjection_4.createdFeature()
+SketchConstraintCoincidence_10 = Sketch_2.setCoincident(SketchLine_7.startPoint(), SketchLine_9.result())
+SketchConstraintCoincidence_11 = Sketch_2.setCoincident(SketchLine_7.endPoint(), SketchArc_2.results()[1])
+SketchConstraintMirror_1 = Sketch_2.addMirror(SketchLine_9.result(), [SketchArc_2.results()[1], SketchArc_1.results()[1]])
+[SketchArc_3, SketchArc_4] = SketchConstraintMirror_1.mirrored()
+model.do()
+Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_2")], model.selection(), 100, 0)
+model.do()
+
+TOLERANCE = 1.e-6
+
+# reference data
+REF_DATA = [(model.selection("VERTEX", "PartSet/Origin"),
+             model.selection("VERTEX", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1"),
+             122.4744871),
+            (model.selection("EDGE", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1"),
+             model.selection("VERTEX", "Sketch_2/Vertex-SketchArc_1-SketchLine_7e"),
+             36.94403089),
+            (model.selection("EDGE", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1"),
+             model.selection("EDGE", "Extrusion_1_1/Generated_Face_5"),
+             0),
+            (model.selection("EDGE", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1"),
+             model.selection("EDGE", "Extrusion_2_1/Generated_Face_2&Extrusion_2_1/To_Face_1"),
+             16.00781059),
+            (model.selection("EDGE", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1"),
+             model.selection("FACE", "Extrusion_2_1/Generated_Face_2"),
+             8.412291828),
+            (model.selection("VERTEX", "Sketch_1/Vertex-SketchCircle_1"),
+             model.selection("FACE", "Plane_1"),
+             35.35533906),
+            (model.selection("FACE", "Extrusion_2_2/From_Face_1"),
+             model.selection("FACE", "Extrusion_2_2/To_Face_1"),
+             100),
+            (model.selection("FACE", "Extrusion_1_1/Generated_Face_5"),
+             model.selection("FACE", "Extrusion_2_1/Generated_Face_2"),
+             0),
+            (model.selection("FACE", "Extrusion_1_1/Generated_Face_5"),
+             model.selection("FACE", "Extrusion_1_1/Generated_Face_2"),
+             27.63932023),
+            (model.selection("SOLID", "Extrusion_1_1"),
+             model.selection("FACE", "Extrusion_2_1/To_Face_1"),
+             12.5),
+            (model.selection("SOLID", "Extrusion_1_1"),
+             model.selection("SOLID", "Extrusion_2_1"),
+             0),
+            (model.selection("SOLID", "Extrusion_1_1"),
+             model.selection("SOLID", "Extrusion_2_2"),
+             87.5)
+           ]
+
+for ref in REF_DATA:
+    dist = model.measureDistance(Part_1_doc, ref[0], ref[1])
+    assert(math.fabs(dist - ref[2]) < TOLERANCE), "Distance {} differs from expected value {}".format(dist, ref[2])
+
+model.end()
diff --git a/src/FeaturesPlugin/Test/TestMeasurementLength.py b/src/FeaturesPlugin/Test/TestMeasurementLength.py
new file mode 100644 (file)
index 0000000..f557f8f
--- /dev/null
@@ -0,0 +1,84 @@
+## Copyright (C) 2018-20xx  CEA/DEN, EDF R&D
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public
+## License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+## See http:##www.salome-platform.org/ or
+## email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
+##
+
+from salome.shaper import model
+
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamR = model.addParameter(Part_1_doc, "R", "50")
+ParamL = model.addParameter(Part_1_doc, "L", "40")
+ParamH = model.addParameter(Part_1_doc, "H", "20")
+Point_2 = model.addPoint(Part_1_doc, 0, 100, 100)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OX"), model.selection("VERTEX", "Point_1"), False)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(50, 50, 50)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], "R")
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_1 = SketchProjection_1.createdFeature()
+SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_1.result(), SketchCircle_1.results()[1])
+SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_2 = SketchProjection_2.createdFeature()
+SketchConstraintTangent_2 = Sketch_1.setTangent(SketchLine_2.result(), SketchCircle_1.results()[1])
+SketchLine_3 = Sketch_1.addLine(70, 40, 30, 40)
+SketchLine_4 = Sketch_1.addLine(30, 40, 30, 60)
+SketchLine_5 = Sketch_1.addLine(30, 60, 70, 60)
+SketchLine_6 = Sketch_1.addLine(70, 60, 70, 40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_3.result(), "L")
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_4.result(), "H")
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "L/2")
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "H/2")
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f")], model.selection(), model.selection("FACE", "Plane_1"), 0, model.selection(), 10)
+model.do()
+
+TOLERANCE = 1.e-6
+
+# reference data
+REF_DATA = [("Extrusion_1_1/From_Face_1&Extrusion_1_1/Generated_Face_1", ParamH.value()),
+            ("Extrusion_1_1/From_Face_1&Extrusion_1_1/Generated_Face_4", ParamL.value()),
+            ("Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1", ParamR.value() * 2.0 * math.pi),
+            ("Extrusion_1_1/Generated_Face_5&Extrusion_1_1/To_Face_1", 381.981436664),
+            ("Extrusion_1_1/Generated_Face_5", 60),
+            ("Sketch_1/Edge-SketchLine_3", ParamL.value()),
+            ("Sketch_1/Edge-SketchLine_4", ParamH.value()),
+            ("Sketch_1/Edge-SketchCircle_1_2", ParamR.value() * 2.0 * math.pi)
+           ]
+
+for ref in REF_DATA:
+    length = model.measureLength(Part_1_doc, model.selection("EDGE", ref[0]))
+    assert(math.fabs(length - ref[1]) < TOLERANCE), "Length {} differs from expected value {}".format(length, ref[1])
+
+# select incorrect data
+length = model.measureLength(Part_1_doc, model.selection("FACE", "Extrusion_1_1/Generated_Face_5"))
+assert(length == -1)
+
+model.end()
diff --git a/src/FeaturesPlugin/Test/TestMeasurementRadius.py b/src/FeaturesPlugin/Test/TestMeasurementRadius.py
new file mode 100644 (file)
index 0000000..a39927d
--- /dev/null
@@ -0,0 +1,79 @@
+## Copyright (C) 2018-20xx  CEA/DEN, EDF R&D
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public
+## License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+## See http:##www.salome-platform.org/ or
+## email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
+##
+
+from salome.shaper import model
+
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamR = model.addParameter(Part_1_doc, "R", "50")
+ParamL = model.addParameter(Part_1_doc, "L", "40")
+ParamH = model.addParameter(Part_1_doc, "H", "20")
+Point_2 = model.addPoint(Part_1_doc, 0, 100, 100)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OX"), model.selection("VERTEX", "Point_1"), False)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(50, 50, 50)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], "R")
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_1 = SketchProjection_1.createdFeature()
+SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_1.result(), SketchCircle_1.results()[1])
+SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_2 = SketchProjection_2.createdFeature()
+SketchConstraintTangent_2 = Sketch_1.setTangent(SketchLine_2.result(), SketchCircle_1.results()[1])
+SketchLine_3 = Sketch_1.addLine(70, 40, 30, 40)
+SketchLine_4 = Sketch_1.addLine(30, 40, 30, 60)
+SketchLine_5 = Sketch_1.addLine(30, 60, 70, 60)
+SketchLine_6 = Sketch_1.addLine(70, 60, 70, 40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_3.result(), "L")
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_4.result(), "H")
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "L/2")
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_4.startPoint(), SketchCircle_1.center(), "H/2")
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f")], model.selection(), model.selection("FACE", "Plane_1"), 0, model.selection(), 10)
+model.do()
+
+TOLERANCE = 1.e-6
+
+# reference data
+REF_DATA = [(model.selection("EDGE", "Sketch_1/Edge-SketchCircle_1_2"), ParamR.value()),
+            (model.selection("EDGE", "Extrusion_1_1/Generated_Face_5&Extrusion_1_1/From_Face_1"), ParamR.value()),
+            (model.selection("FACE", "Extrusion_1_1/Generated_Face_5"), ParamR.value())
+           ]
+
+for ref in REF_DATA:
+    radius = model.measureRadius(Part_1_doc, ref[0])
+    assert(math.fabs(radius - ref[1]) < TOLERANCE), "Radius {} differs from expected value {}".format(radius, ref[1])
+
+# select incorrect data
+radius = model.measureRadius(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face_1"))
+assert(radius == -1)
+
+model.end()
diff --git a/src/FeaturesPlugin/icons/edge.png b/src/FeaturesPlugin/icons/edge.png
new file mode 100644 (file)
index 0000000..53dbb08
Binary files /dev/null and b/src/FeaturesPlugin/icons/edge.png differ
diff --git a/src/FeaturesPlugin/icons/meas_angle_32x32.png b/src/FeaturesPlugin/icons/meas_angle_32x32.png
new file mode 100644 (file)
index 0000000..800536f
Binary files /dev/null and b/src/FeaturesPlugin/icons/meas_angle_32x32.png differ
diff --git a/src/FeaturesPlugin/icons/meas_distance_32x32.png b/src/FeaturesPlugin/icons/meas_distance_32x32.png
new file mode 100644 (file)
index 0000000..d9a6d5b
Binary files /dev/null and b/src/FeaturesPlugin/icons/meas_distance_32x32.png differ
diff --git a/src/FeaturesPlugin/icons/meas_length_32x32.png b/src/FeaturesPlugin/icons/meas_length_32x32.png
new file mode 100644 (file)
index 0000000..37ebd09
Binary files /dev/null and b/src/FeaturesPlugin/icons/meas_length_32x32.png differ
diff --git a/src/FeaturesPlugin/icons/meas_radius_32x32.png b/src/FeaturesPlugin/icons/meas_radius_32x32.png
new file mode 100644 (file)
index 0000000..a3c70fc
Binary files /dev/null and b/src/FeaturesPlugin/icons/meas_radius_32x32.png differ
diff --git a/src/FeaturesPlugin/icons/measurement.png b/src/FeaturesPlugin/icons/measurement.png
new file mode 100644 (file)
index 0000000..c93fa62
Binary files /dev/null and b/src/FeaturesPlugin/icons/measurement.png differ
diff --git a/src/FeaturesPlugin/measurement_widget.xml b/src/FeaturesPlugin/measurement_widget.xml
new file mode 100644 (file)
index 0000000..22c557b
--- /dev/null
@@ -0,0 +1,80 @@
+<!--
+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>
+-->
+
+<source>
+  <toolbox id="MeasureKind">
+    <box id="Length" title="Edge length" icon="icons/Features/meas_length_32x32.png">
+      <shape_selector id="edge_for_length"
+                      icon="icons/Features/edge.png"
+                      label="Edge"
+                      tooltip="Select an edge"
+                      shape_types="edge"
+                      default="">
+        <validator id="GeomValidators_ShapeType" parameters="edge"/>
+      </shape_selector>
+    </box>
+    <box id="Distance" title="Distance between objects" icon="icons/Features/meas_distance_32x32.png">
+      <shape_selector id="distance_from"
+                      icon=""
+                      label="From"
+                      tooltip="Select a shape"
+                      shape_types="vertex edge wire face shell solid objects"
+                      default="">
+      </shape_selector>
+      <shape_selector id="distance_to"
+                      icon=""
+                      label="To"
+                      tooltip="Select a shape"
+                      shape_types="vertex edge wire face shell solid objects"
+                      default="">
+      </shape_selector>
+    </box>
+    <box id="Radius" title="Radius of circular" icon="icons/Features/meas_radius_32x32.png">
+      <shape_selector id="circular"
+                      icon=""
+                      label="Object"
+                      tooltip="Select an edge or face"
+                      shape_types="edge face"
+                      default="">
+        <validator id="FeaturesPlugin_ValidatorCircular"/>
+      </shape_selector>
+    </box>
+    <box id="Angle" title="Angle between edges" icon="icons/Features/meas_angle_32x32.png">
+      <shape_selector id="angle_from"
+                      icon="icons/Features/edge.png"
+                      label="First edge"
+                      tooltip="Select an edge"
+                      shape_types="edge"
+                      default="">
+        <validator id="GeomValidators_ShapeType" parameters="edge"/>
+      </shape_selector>
+      <shape_selector id="angle_to"
+                      icon="icons/Features/edge.png"
+                      label="Second edge"
+                      tooltip="Select an edge"
+                      shape_types="edge"
+                      default="">
+        <validator id="GeomValidators_ShapeType" parameters="edge"/>
+      </shape_selector>
+    </box>
+  </toolbox>
+  <label id="result"/>
+</source>
index 88c1c02908bff6ea939f6287c325168571af6078..5a4ac2f526988d246b6aabdc8b5d399e16203bfa 100644 (file)
@@ -97,7 +97,7 @@ email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com
       <feature id="Rotation" title="Rotation" tooltip="Perform rotation of an objects around the axis to specified angle" icon="icons/Features/rotation.png">
         <source path="rotation_widget.xml"/>
       </feature>
-      <feature id="Symmetry" title="Symmetry" tooltip="Perform symmetry with respect to a point, an axis or a plane" icon="icons/Features/symmetry.png">
+      <feature id="MirrorCopy" title="Mirror copy" tooltip="Perform symmetry with respect to a point, an axis or a plane" icon="icons/Features/symmetry.png">
         <source path="symmetry_widget.xml"/>
       </feature>
       <feature id="LinearCopy" title="Linear copy" tooltip="Perform copy and translate" icon="icons/Features/multitranslation.png">
@@ -107,5 +107,10 @@ email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com
         <source path="multirotation_widget.xml"/>
       </feature>
     </group>
+    <group id="Measurement">
+      <feature id="Measurement" title="Measurement" tooltip="Calculate properties of objects" icon="icons/Features/measurement.png">
+        <source path="measurement_widget.xml"/>
+      </feature>
+    </group>
   </workbench>
 </plugin>
index 325e5055d15213afea642371172db3df3879a03a..b75212b547336fce43aa271dd167dd3c54a3f2ab 100644 (file)
@@ -55,6 +55,7 @@ SET(PROJECT_HEADERS
     GeomAPI_Ax3.h
     GeomAPI_Trsf.h
     GeomAPI_Angle2d.h
+    GeomAPI_Angle.h
     GeomAPI_Wire.h
     GeomAPI_Ellipse.h
     GeomAPI_Ellipse2d.h
@@ -91,6 +92,7 @@ SET(PROJECT_SOURCES
     GeomAPI_IPresentable.cpp
     GeomAPI_Trsf.cpp
     GeomAPI_Angle2d.cpp
+    GeomAPI_Angle.cpp
     GeomAPI_Wire.cpp
     GeomAPI_Ellipse.cpp
     GeomAPI_Ellipse2d.cpp
diff --git a/src/GeomAPI/GeomAPI_Angle.cpp b/src/GeomAPI/GeomAPI_Angle.cpp
new file mode 100644 (file)
index 0000000..97d45ee
--- /dev/null
@@ -0,0 +1,90 @@
+// 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>
+//
+
+#include <GeomAPI_Angle.h>
+
+#include <GeomAPI_Edge.h>
+#include <GeomAPI_Pnt.h>
+
+#include <BRep_Tool.hxx>
+#include <ElCLib.hxx>
+#include <Geom_Curve.hxx>
+#include <GeomAPI_ProjectPointOnCurve.hxx>
+#include <Precision.hxx>
+#include <TopoDS_Edge.hxx>
+
+/// \struct AngleDirections
+/// \brief Used to store info about angle.
+struct AngleDirections {
+  gp_Vec myDir1;
+  gp_Vec myDir2;
+};
+
+#define MY_ANGLE implPtr<AngleDirections>()
+#define PI 3.1415926535897932
+
+
+GeomAPI_Angle::GeomAPI_Angle(const std::shared_ptr<GeomAPI_Edge>& theEdge1,
+                             const std::shared_ptr<GeomAPI_Edge>& theEdge2,
+                             const std::shared_ptr<GeomAPI_Pnt>&  thePoint)
+{
+  gp_Pnt aPoint = thePoint->impl<gp_Pnt>();
+  const TopoDS_Edge& anEdge1 = theEdge1->impl<TopoDS_Edge>();
+  const TopoDS_Edge& anEdge2 = theEdge2->impl<TopoDS_Edge>();
+
+  double aF1, aL1;
+  Handle(Geom_Curve) aCurve1 = BRep_Tool::Curve(anEdge1, aF1, aL1);
+  double aF2, aL2;
+  Handle(Geom_Curve) aCurve2 = BRep_Tool::Curve(anEdge2, aF2, aL2);
+
+  AngleDirections* anAngle = new AngleDirections;
+  gp_Pnt aP;
+
+  GeomAPI_ProjectPointOnCurve aProj1(aPoint, aCurve1);
+  if (aProj1.NbPoints() > 0)
+    aCurve1->D1(aProj1.Parameter(1), aP, anAngle->myDir1);
+
+  GeomAPI_ProjectPointOnCurve aProj2(aPoint, aCurve2);
+  if (aProj2.NbPoints() > 0)
+    aCurve2->D1(aProj2.Parameter(1), aP, anAngle->myDir2);
+
+  setImpl(anAngle);
+}
+
+double GeomAPI_Angle::angleDegree()
+{
+  return angleRadian() * 180.0 / PI;
+}
+
+double GeomAPI_Angle::angleRadian()
+{
+  AngleDirections* anAngle = MY_ANGLE;
+  if (anAngle->myDir1.SquareMagnitude() < Precision::SquareConfusion() ||
+      anAngle->myDir2.SquareMagnitude() < Precision::SquareConfusion())
+    return 0.0;
+
+  gp_Dir aDir1(anAngle->myDir1);
+  gp_Dir aDir2(anAngle->myDir2);
+  double aRes = aDir1.Angle(aDir2);
+  aRes = ElCLib::InPeriod(aRes, 0.0, 2.0 * PI);
+  if (Abs(aRes) < 1.e-12)
+    aRes = 0.0;
+  return aRes;
+}
diff --git a/src/GeomAPI/GeomAPI_Angle.h b/src/GeomAPI/GeomAPI_Angle.h
new file mode 100644 (file)
index 0000000..e0c2eec
--- /dev/null
@@ -0,0 +1,47 @@
+// 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>
+//
+
+#ifndef GeomAPI_Angle_H_
+#define GeomAPI_Angle_H_
+
+#include <GeomAPI_Interface.h>
+
+class GeomAPI_Edge;
+class GeomAPI_Pnt;
+
+/// \class GeomAPI_Angle
+/// \ingroup DataModel
+/// \brief Build an angle
+class GeomAPI_Angle : public GeomAPI_Interface
+{
+public:
+  /// Creation of an angle defined by two edges in the given intersection point.
+  GEOMAPI_EXPORT
+  GeomAPI_Angle(const std::shared_ptr<GeomAPI_Edge>& theEdge1,
+                const std::shared_ptr<GeomAPI_Edge>& theEdge2,
+                const std::shared_ptr<GeomAPI_Pnt>&  thePoint);
+
+  /// Returns value of the angle in degrees
+  GEOMAPI_EXPORT double angleDegree();
+  /// Returns value of the angle in radians
+  GEOMAPI_EXPORT double angleRadian();
+};
+
+#endif
index ca84924c08a79d4735c1cef3cd0ed9892e6721d3..78846df72ad53277d4962d1bb5f01e982201cd17 100644 (file)
@@ -18,8 +18,8 @@
 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
 //
 
-#ifndef GeomAPI_Angle_H_
-#define GeomAPI_Angle_H_
+#ifndef GeomAPI_Angle2d_H_
+#define GeomAPI_Angle2d_H_
 
 #include <GeomAPI_Interface.h>
 
index cf6197337c8ee80f4af7f39fea46cc2cf8dd55d2..b5d0de4b673c35ad38c0d532e3b8cbf8df7d87b3 100644 (file)
 #include <TopoDS.hxx>
 #include <BRep_Builder.hxx>
 #include <BRep_Tool.hxx>
+#include <ElCLib.hxx>
 #include <Geom_Curve.hxx>
 #include <Geom_Line.hxx>
 #include <Geom_Circle.hxx>
 #include <Geom_Ellipse.hxx>
+#include <Geom_Plane.hxx>
+#include <GeomAPI_IntCS.hxx>
 #include <GeomAdaptor_Curve.hxx>
 #include <gp_Ax1.hxx>
 #include <gp_Pln.hxx>
@@ -280,6 +283,39 @@ bool GeomAPI_Edge::isInPlane(std::shared_ptr<GeomAPI_Pln> thePlane) const
   return inPlane;
 }
 
+void GeomAPI_Edge::intersectWithPlane(const std::shared_ptr<GeomAPI_Pln> thePlane,
+                                      std::list<std::shared_ptr<GeomAPI_Pnt>>& theResult) const
+{
+  double aFirst, aLast;
+  const TopoDS_Shape& aShape = const_cast<GeomAPI_Edge*>(this)->impl<TopoDS_Shape>();
+  Handle(Geom_Curve) aCurve = BRep_Tool::Curve((const TopoDS_Edge&)aShape, aFirst, aLast);
+  if (!aCurve.IsNull()) {
+    double A, B, C, D;
+    thePlane->coefficients(A, B, C, D);
+    gp_Pln aPln(A, B, C, D);
+
+    Handle(Geom_Plane) aPlane = new Geom_Plane(aPln);
+    GeomAPI_IntCS aIntersect;
+    aIntersect.Perform(aCurve, aPlane);
+    if (aIntersect.IsDone() && (aIntersect.NbPoints() > 0)) {
+      gp_Pnt aPnt;
+      for (int i = 1; i <= aIntersect.NbPoints(); i++) {
+        // check the parameter of intersection in the edge range
+        aIntersect.Parameters(i, A, B, C);
+        if (aCurve->IsPeriodic())
+          C = ElCLib::InPeriod(C, aFirst, aFirst + aCurve->Period());
+        if (C < aFirst - Precision::PConfusion() || C > aLast + Precision::PConfusion())
+          continue;
+
+        // obtain intersection point
+        aPnt = aIntersect.Point(i);
+        std::shared_ptr<GeomAPI_Pnt> aPntPtr(new GeomAPI_Pnt(aPnt.X(), aPnt.Y(), aPnt.Z()));
+        theResult.push_back(aPntPtr);
+      }
+    }
+  }
+}
+
 double GeomAPI_Edge::length() const
 {
   const TopoDS_Edge& anEdge = TopoDS::Edge(impl<TopoDS_Shape>());
index e7d4910f1deb0eda3ea05d810e485b2f4396d4ed..ac5973d9bca882b6e7e8c63ca22981321b907460 100644 (file)
@@ -90,9 +90,16 @@ public:
   void getRange(double& theFirst, double& theLast) const;
 
   /// Returns true, if the edge is fully placed in the specified plane
+  /// \param thePlane a plane for intersection
   GEOMAPI_EXPORT
   bool isInPlane(const std::shared_ptr<GeomAPI_Pln> thePlane) const;
 
+  /// Returns list of intersection points if the edge has intersections with the given plane
+  /// \param thePlane a plane for intersection
+  GEOMAPI_EXPORT
+  void intersectWithPlane(const std::shared_ptr<GeomAPI_Pln> thePlane,
+                          std::list<std::shared_ptr<GeomAPI_Pnt> >& theResult) const;
+
   /// Returns edge length.
   GEOMAPI_EXPORT
   double length() const;
index 4d1250d9cb4ac2b7698d41e6d333bfc69e6ca0c0..477b91b9007c52a6674ddfab2bab306afae58ff3 100644 (file)
@@ -53,5 +53,8 @@ public:
   std::shared_ptr<GeomAPI_Pln> getPlane() const;
 };
 
+//! Pointer on attribute object
+typedef std::shared_ptr<GeomAPI_Face> GeomFacePtr;
+
 #endif
 
index a8df94d7dbd49d0fcbae3da17db9a159078a9fb2..32384bf25021c13c6191deb79e34a5ae931a021c 100644 (file)
@@ -51,6 +51,7 @@
 #include <Geom2d_Curve.hxx>
 #include <BRepLib_CheckCurveOnSurface.hxx>
 #include <BRep_Tool.hxx>
+#include  <Geom_CylindricalSurface.hxx>
 #include <Geom_Line.hxx>
 #include <Geom_Plane.hxx>
 #include <GeomAPI_ProjectPointOnCurve.hxx>
@@ -142,6 +143,32 @@ std::shared_ptr<GeomAPI_Pnt>
   return std::shared_ptr<GeomAPI_Pnt>(new GeomAPI_Pnt(aCentre.X(), aCentre.Y(), aCentre.Z()));
 }
 
+//==================================================================================================
+double GeomAlgoAPI_ShapeTools::radius(const std::shared_ptr<GeomAPI_Face>& theCylinder)
+{
+  double aRadius = -1.0;
+  if (theCylinder->isCylindrical()) {
+    const TopoDS_Shape& aShape = theCylinder->impl<TopoDS_Shape>();
+    Handle(Geom_Surface) aSurf = BRep_Tool::Surface(TopoDS::Face(aShape));
+    Handle(Geom_CylindricalSurface) aCyl = Handle(Geom_CylindricalSurface)::DownCast(aSurf);
+    if (!aCyl.IsNull())
+      aRadius = aCyl->Radius();
+  }
+  return aRadius;
+}
+
+//==================================================================================================
+double GeomAlgoAPI_ShapeTools::minimalDistance(const GeomShapePtr& theShape1,
+                                               const GeomShapePtr& theShape2)
+{
+  const TopoDS_Shape& aShape1 = theShape1->impl<TopoDS_Shape>();
+  const TopoDS_Shape& aShape2 = theShape2->impl<TopoDS_Shape>();
+
+  BRepExtrema_DistShapeShape aDist(aShape1, aShape2);
+  aDist.Perform();
+  return aDist.IsDone() ? aDist.Value() : Precision::Infinite();
+}
+
 //==================================================================================================
 std::shared_ptr<GeomAPI_Shape> GeomAlgoAPI_ShapeTools::combineShapes(
   const std::shared_ptr<GeomAPI_Shape> theCompound,
index 42f87c8626ce40d8725329917da8a7305cc5d41c..a9211e397b16dc03423c39544ba9ca5dfc32c4cf 100644 (file)
@@ -59,6 +59,14 @@ public:
   GEOMALGOAPI_EXPORT static std::shared_ptr<GeomAPI_Pnt>
     centreOfMass(const std::shared_ptr<GeomAPI_Shape> theShape);
 
+  /// \brief Obtain radius of cylindrical face.
+  ///        Return negative value if the face is not a cylinder
+  GEOMALGOAPI_EXPORT static double radius(const std::shared_ptr<GeomAPI_Face>& theCylinder);
+
+  /// \brief Calculate minimal distance between shapes
+  GEOMALGOAPI_EXPORT static double minimalDistance(const GeomShapePtr& theShape1,
+                                                   const GeomShapePtr& theShape2);
+
   /// \brief Combines faces with common edges to shells, or solids to compsolids.
   /// \param[in] theCompound compound of shapes.
   /// \param[in] theType type of combine.
index e3cce6120e3186dee0f8d45d0dbcdd559efc0ba6..de159cffca884e606a7d54a34789b737efb8d4d6 100644 (file)
@@ -43,6 +43,16 @@ const char* aSearchCode =
   "            positions.append((node.lineno, node.col_offset))\n"
   "FindName(name).visit(ast.parse(expression))";
 
+// make the expression be correct for the python interpreter even for the
+// beta=alfa*2 expressions
+static std::string adjustExpression(const std::string& theExpression) {
+  std::string anExpression = theExpression;
+  if (!anExpression.empty() && anExpression.back() == '=') {
+    anExpression = anExpression.substr(0, anExpression.length() - 1);
+  }
+  return anExpression;
+}
+
 std::list<std::pair<int, int> >
 InitializationPlugin_PyInterp::positions(const std::string& theExpression,
                                      const std::string& theName)
@@ -56,8 +66,9 @@ InitializationPlugin_PyInterp::positions(const std::string& theExpression,
   PyObject* aBuiltinModule = PyImport_AddModule("__builtin__");
   PyDict_SetItemString(aContext, "__builtins__", aBuiltinModule);
 
+  std::string anExpression = adjustExpression(theExpression);
   // extend aContext with variables
-  PyDict_SetItemString(aContext, "expression", PyString_FromString(theExpression.c_str()));
+  PyDict_SetItemString(aContext, "expression", PyString_FromString(anExpression.c_str()));
   PyDict_SetItemString(aContext, "name", PyString_FromString(theName.c_str()));
   PyDict_SetItemString(aContext, "positions", Py_BuildValue("[]"));
 
@@ -93,10 +104,12 @@ std::list<std::string> InitializationPlugin_PyInterp::compile(const std::string&
     PyErr_Print();
     return aResult;
   }
+  // support "variable_name=" expression as "variable_name"
+  std::string anExpression = adjustExpression(theExpression);
 
   PyObject *aCodePyObj =
     PyObject_CallMethod(aCodeopModule, (char*)"compile_command", (char*)"(s)",
-                        theExpression.c_str());
+                        anExpression.c_str());
 
   if(!aCodePyObj || aCodePyObj == Py_None || !PyCode_Check(aCodePyObj)) {
     Py_XDECREF(aCodePyObj);
@@ -144,10 +157,13 @@ void InitializationPlugin_PyInterp::clearLocalContext()
 double InitializationPlugin_PyInterp::evaluate(const std::string& theExpression,
                                                std::string& theError)
 {
+  // support "variable_name=" expression as "variable_name"
+  std::string anExpression = adjustExpression(theExpression);
+
   PyLockWrapper lck; // Acquire GIL until the end of the method
   PyCompilerFlags aFlags = {CO_FUTURE_DIVISION};
   aFlags.cf_flags = CO_FUTURE_DIVISION;
-  PyCodeObject* anExprCode = (PyCodeObject *) Py_CompileStringFlags(theExpression.c_str(),
+  PyCodeObject* anExprCode = (PyCodeObject *) Py_CompileStringFlags(anExpression.c_str(),
                                 "<string>", Py_eval_input, &aFlags);
   if(!anExprCode) {
     theError = errorMessage();
index 93a9b24bbe28e64cb0cf8a0588e01b4b9d0562e7..af3b9cb9d854fe384b84ee2bbf6c0e41a80e0277 100644 (file)
@@ -111,9 +111,11 @@ bool Model_AttributeSelection::setValue(const ResultPtr& theContext,
   bool isOldShape = isOldContext &&
     (theSubShape == anOldShape || (theSubShape && anOldShape && theSubShape->isEqual(anOldShape)));
   if (isOldShape) return false; // shape is the same, so context is also unchanged
+  bool aToUnblock = false;
   // update the referenced object if needed
   if (!isOldContext) {
-      myRef.setValue(theContext);
+    aToUnblock = !owner()->data()->blockSendAttributeUpdated(true);
+    myRef.setValue(theContext);
   }
 
   // do noth use naming if selected shape is result shape itself, but not sub-shape
@@ -136,6 +138,8 @@ bool Model_AttributeSelection::setValue(const ResultPtr& theContext,
     TDF_Label aRefLab = myRef.myRef->Label();
     aSelLab.ForgetAllAttributes(true);
     myRef.myRef = TDF_Reference::Set(aSelLab.Father(), aSelLab.Father());
+    if (aToUnblock)
+      owner()->data()->blockSendAttributeUpdated(false);
     return false;
   }
   if (theContext->groupName() == ModelAPI_ResultBody::group()) {
@@ -170,6 +174,10 @@ bool Model_AttributeSelection::setValue(const ResultPtr& theContext,
   }
 
   owner()->data()->sendAttributeUpdated(this);
+
+  if (aToUnblock)
+    owner()->data()->blockSendAttributeUpdated(false);
+
   return true;
 }
 
index 25116990809bb0440cca76236c6da66721d37312..737a63d25df6cfeab3d39769e870f5ad5be33e4f 100644 (file)
@@ -611,10 +611,30 @@ bool Model_ResultConstruction::update(const int theIndex,
       }
       return aRes;
     } else {
+      // check is this modified or not
+      std::shared_ptr<GeomAPI_Shape> aNewShape = shape();
+      TopoDS_Shape anOldSh;
+      Handle(TNaming_NamedShape) aNS;
+      if (aLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
+        anOldSh = aNS->Get();
+      }
+      if (aNewShape.get()) {
+        if (anOldSh.IsNull())
+          theModified = true;
+        else {
+          std::shared_ptr<GeomAPI_Shape> anOldShape(new GeomAPI_Shape);
+          anOldShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(anOldSh));
+          theModified = !anOldShape->isEqual(aNewShape);
+        }
+      }
+      else if (!anOldSh.IsNull()) {
+        theModified = true;
+      }
+
       // For correct naming selection, put the shape into the naming structure.
       // It seems sub-shapes are not needed: only this shape is (and can be ) selected.
       TNaming_Builder aBuilder(aLab);
-      aBuilder.Generated(shape()->impl<TopoDS_Shape>());
+      aBuilder.Generated(aNewShape->impl<TopoDS_Shape>());
     }
     return shape() && !shape()->isNull();
   }
index 44f7a47e94c7abb217565f1732083eee156a2d3a..c0a791e26322adb672972c235278ab6e399db981 100755 (executable)
@@ -578,7 +578,10 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
     if (aReason != theFeature && (aReason)->data()->isValid()) {
       if (processFeature(aReason))
         aIsModified = true;
-      if (aReason->data()->execState() == ModelAPI_StateInvalidArgument)
+      // check validity of aReason once again because it may be removed by dependent feature
+      // (e.g. by SketchPlugin_IntersectionPoint)
+      if (!aReason->data()->isValid() ||
+          aReason->data()->execState() == ModelAPI_StateInvalidArgument)
         isReferencedInvalid = true;
     }
     // searching for the next not used reason
index e069c44fb65b3a7eab72b4bfa3c8bd4198ff7341..cae44462df16b2c985e665bc34e7153537801d19 100644 (file)
@@ -27,7 +27,7 @@ Part_1_doc = Part_1.document()
 Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
 Cylinder_1.result().setName("cylinder")
 Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "cylinder/Face_2"), 10, False)
-Symmetry_1 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "cylinder")], model.selection("FACE", "Plane_1"))
+Symmetry_1 = model.addMirror(Part_1_doc, [model.selection("SOLID", "cylinder")], model.selection("FACE", "Plane_1"))
 model.do()
 
 # check the name of the Symmetry
index 37d888213e2b456dd8bc191f0c19aad0eb7508d3..8a556c240b0aa543587738673f2daaaa839452dd 100644 (file)
 
 #include "ModuleBase_ParamSpinBox.h"
 
-#include <ModelAPI_Session.h>
-#include <ModelAPI_Document.h>
-#include <ModelAPI_Feature.h>
-#include <ModelAPI_ResultParameter.h>
-#include <ModelAPI_AttributeDouble.h>
-#include <ModelAPI_Tools.h>
-
 #include <QKeyEvent>
-#include <QLineEdit>
 #include <QLocale>
 #include <QRegExp>
 #include <QToolTip>
 
 #include <QStringListModel>
 #include <QCompleter>
+#include <QAbstractItemView>
 #include <QShortcut>
 
 #include <string>
 #include <iostream>
 
-//#define DEBUG_COMPLETE_WITH_PARAMETERS
-
 ModuleBase_ParamSpinBox::ModuleBase_ParamSpinBox(QWidget* theParent, int thePrecision)
-    : ModuleBase_DoubleSpinBox(theParent, thePrecision),
-      myAcceptVariables(true)
+  : QAbstractSpinBox(theParent),
+  myPrecision(thePrecision),
+  myIsEquation(false),
+  myAcceptVariables(true),
+  mySingleStep(1),
+  myMinimum(DBL_MIN),
+  myMaximum(DBL_MAX)
 {
-#ifdef DEBUG_COMPLETE_WITH_PARAMETERS
   myCompleter = new QCompleter(this);
-  myCompleter->setWidget(this);
+  myCompleter->setWidget(lineEdit());
   myCompleter->setCompletionMode(QCompleter::PopupCompletion);
 
   myCompleterModel = new QStringListModel(this);
@@ -57,20 +52,27 @@ ModuleBase_ParamSpinBox::ModuleBase_ParamSpinBox(QWidget* theParent, int thePrec
   // Use sorted model to accelerate completion (QCompleter will use binary search)
   myCompleter->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
   myCompleter->setCaseSensitivity(Qt::CaseInsensitive);
+  connect(myCompleter, SIGNAL(highlighted(const QString&)),
+    this, SLOT(insertCompletion(const QString&)));
 
-  lineEdit()->setCompleter(myCompleter);
-#endif
+  //  connectSignalsAndSlots();
+  myEnabledBaseColor = palette().color(QPalette::Active, QPalette::Base);
+  connect(lineEdit(), SIGNAL(textChanged(const QString&)),
+    this, SLOT(onTextChanged(const QString&)));
 
-  connectSignalsAndSlots();
+  setLocale(QLocale::c());
+
+  myValidator = new QDoubleValidator(this);
+  myValidator->setLocale(locale());
+  myValidator->setRange(myMinimum, myMaximum);
+  myValidator->setDecimals(3);
 }
 
 void ModuleBase_ParamSpinBox::setCompletionList(QStringList& theList)
 {
-#ifdef DEBUG_COMPLETE_WITH_PARAMETERS
   theList.sort();
   theList.removeDuplicates();
   myCompleterModel->setStringList(theList);
-#endif
 }
 
 /*!
@@ -80,6 +82,7 @@ ModuleBase_ParamSpinBox::~ModuleBase_ParamSpinBox()
 {
 }
 
+
 /*!
  \brief Perform \a steps increment/decrement steps.
 
@@ -91,43 +94,21 @@ ModuleBase_ParamSpinBox::~ModuleBase_ParamSpinBox()
  */
 void ModuleBase_ParamSpinBox::stepBy(int steps)
 {
-  if ((!myTextValue.isEmpty()) && hasVariable())
+  if (hasVariable())
     return;
 
-  ModuleBase_DoubleSpinBox::stepBy(steps);
+  double aVal = lineEdit()->text().toDouble();
+  aVal += steps * mySingleStep;
+  setValue(aVal);
+  QAbstractSpinBox::stepBy(steps);
 }
 
-/*!
- \brief Connect signals and slots.
- */
-void ModuleBase_ParamSpinBox::connectSignalsAndSlots()
+void ModuleBase_ParamSpinBox::onTextChanged(const QString& theText)
 {
-  connect(this, SIGNAL(valueChanged(const QString&)),
-          this, SLOT(onTextChanged(const QString&)));
+  myIsEquation = hasVariable(theText);
+  emit textChanged(theText);
 }
 
-void ModuleBase_ParamSpinBox::onTextChanged(const QString& text)
-{
-  myTextValue = text;
-  emit textChanged(text);
-}
-
-double ModuleBase_ParamSpinBox::valueFromText(const QString& theText) const
-{
-  if (!hasVariable(theText))
-    return ModuleBase_DoubleSpinBox::valueFromText(theText);
-
-  // small hack: return hash of the string to initiate valuesChanged signal
-  return qHash(theText);
-}
-
-QString ModuleBase_ParamSpinBox::textFromValue (double theValue) const
-{
-  if ((!myTextValue.isEmpty()) && hasVariable(myTextValue)){
-    return myTextValue;
-  }
-  return ModuleBase_DoubleSpinBox::textFromValue(theValue);
-}
 
 /*!
  \brief This function is used to determine whether input is valid.
@@ -138,14 +119,14 @@ QString ModuleBase_ParamSpinBox::textFromValue (double theValue) const
 QValidator::State ModuleBase_ParamSpinBox::validate(QString& str, int& pos) const
 {
   // Trying to interpret the current input text as a numeric value
-  if (!hasVariable(str))
-    return ModuleBase_DoubleSpinBox::validate(str, pos);
-
-  QValidator::State res = QValidator::Invalid;
-  if (isAcceptVariables()) {
-    res = QValidator::Acceptable;
+  if (!hasVariable(str)) {
+    /// If decimals = 0 do not accept '.' (interpret as int)
+    if ((myValidator->decimals() == 0) && str.endsWith('.'))
+      return QValidator::Invalid;
+    return myValidator->validate(str, pos);
   }
-  return res;
+
+  return isAcceptVariables() ? QValidator::Acceptable : QValidator::Invalid;
 }
 
 /*!
@@ -154,13 +135,22 @@ QValidator::State ModuleBase_ParamSpinBox::validate(QString& str, int& pos) cons
 
  The new value is ignored if the spinbox has a variable.
  */
-void ModuleBase_ParamSpinBox::setValue(const double value)
+void ModuleBase_ParamSpinBox::setValue(double value)
 {
-  if (hasVariable())
-    return;
+  myIsEquation = false;
+  double aVal = value;
+  if (aVal < myMinimum)
+    aVal = myMinimum;
+  else if (aVal > myMaximum)
+    aVal = myMaximum;
+  QString aText = QString::number(aVal, 'g', decimals());
+  lineEdit()->setText(aText);
+  emit textChanged(aText);
+}
 
-  myTextValue = ModuleBase_DoubleSpinBox::textFromValue(value);
-  ModuleBase_DoubleSpinBox::setValue(value);
+double ModuleBase_ParamSpinBox::value() const
+{
+  return lineEdit()->text().toDouble();
 }
 
 /*!
@@ -169,8 +159,11 @@ void ModuleBase_ParamSpinBox::setValue(const double value)
  */
 void ModuleBase_ParamSpinBox::setText(const QString& value)
 {
-  myTextValue = value;
-  lineEdit()->setText(value);
+  myIsEquation = hasVariable(value);
+  if (myAcceptVariables && myIsEquation) {
+    lineEdit()->setText(value);
+    emit textChanged(value);
+  }
 }
 
 /*!
@@ -181,6 +174,9 @@ void ModuleBase_ParamSpinBox::setText(const QString& value)
 void ModuleBase_ParamSpinBox::setAcceptVariables(const bool flag)
 {
   myAcceptVariables = flag;
+  if ((!myAcceptVariables) && myIsEquation) {
+    setValue(0);
+  }
 }
 
 /*!
@@ -193,100 +189,104 @@ bool ModuleBase_ParamSpinBox::isAcceptVariables() const
 
 bool ModuleBase_ParamSpinBox::hasVariable() const
 {
-  if (myTextValue.isEmpty())
-    return false;
-  return hasVariable(myTextValue);
+  return myIsEquation;
 }
 
 bool ModuleBase_ParamSpinBox::hasVariable(const QString& theText) const
 {
-  //const QString aDigitPattern = QString("[-+]?[0-9]*[%1]?[0-9]*([eE][-+]?[0-9]+)?");
-
-  //bool aHasDigit = false;
-  //{
-  //  QRegExp varNameMask(aDigitPattern.arg("."));
-  //  aHasDigit = varNameMask.exactMatch(theText);
-  //}
-  //if (!aHasDigit)
-  //{
-  //  QRegExp varNameMask(aDigitPattern.arg(","));
-  //  aHasDigit = varNameMask.exactMatch(theText);
-  //}
   bool isDouble = false;
   QLocale::c().toDouble(theText, &isDouble);
-
-//  theText.toDouble(&isDouble);
-//  if (isDouble) {
-//    QLocale aLoc; // create default locale
-//    QChar aDecPnt = aLoc.decimalPoint();
-//    if (aDecPnt == '.')
-//      isDouble = theText.contains(aDecPnt) || (!theText.contains(','));
-//    else if (aDecPnt == ',')
-//      isDouble = theText.contains(aDecPnt) || (!theText.contains('.'));
-//  }
   return !isDouble;
 }
 
-/*!
- \brief This function is used to determine whether input is valid.
- \return validating operation result
- */
-ModuleBase_ParamSpinBox::State ModuleBase_ParamSpinBox::isValid(const QString& theText,
-                                                                double& theValue) const
+void ModuleBase_ParamSpinBox::keyReleaseEvent(QKeyEvent* e)
 {
-  if (hasVariable() && !findVariable(theText, theValue)) {
-    bool ok = false;
-    theValue = locale().toDouble(theText, &ok);
-    if (!ok) {
-      return NoVariable;
+  switch (e->key()) {
+  case Qt::Key_Return:
+  case Qt::Key_Enter:
+  {
+    if (myCompleter->popup()->isVisible()) {
+      myCompleter->popup()->hide();
+      myIsEquation = true;
     }
+    emit textChanged(lineEdit()->text());
+    return;
   }
-  if (!checkRange(theValue)) {
-    return Invalid;
+  case Qt::Key_Space:
+    if (e->modifiers() & Qt::ControlModifier) {
+      myCompletePos = lineEdit()->cursorPosition();
+      int aStart, aEnd;
+      QString aPrefix = getPrefix(aStart, aEnd);
+      myCompleter->setCompletionPrefix(aPrefix);
+      myCompleter->complete();
+    }
+    break;
+  default:
+    QAbstractSpinBox::keyReleaseEvent(e);
   }
-
-  return Acceptable;
 }
 
-/*!
- \brief This function is used to check that string value lies within predefined range.
- \return check status
- */
-bool ModuleBase_ParamSpinBox::checkRange(const double theValue) const
+
+QString ModuleBase_ParamSpinBox::getPrefix(int& theStart, int& theEnd) const
 {
-  return theValue >= minimum() && theValue <= maximum();
+  QString aPrefix;
+  QString aText = lineEdit()->text();
+  theStart = theEnd = myCompletePos;
+  const int aLen = aText.length();
+  if (aLen > 0) {
+    if (myCompletePos > 0) {
+      int aLastChar = myCompletePos - 1;
+      QChar aChar = aText.at(aLastChar);
+      while (aChar.isLetter() || aChar.isDigit()) {
+        aPrefix.prepend(aText.at(aLastChar));
+        aLastChar--;
+        if (aLastChar < 0)
+          break;
+        aChar = aText.at(aLastChar);
+      }
+      theStart = aLastChar + 1;
+    }
+    if (myCompletePos < aLen) {
+      int aLastChar = myCompletePos;
+      QChar aChar = aText.at(aLastChar);
+      while (aChar.isLetter() || aChar.isDigit()) {
+        aPrefix.append(aText.at(aLastChar));
+        aLastChar++;
+        if (aLastChar >= aLen)
+          break;
+        aChar = aText.at(aLastChar);
+      }
+      theEnd = aLastChar;
+    }
+  }
+  return aPrefix;
 }
 
-/*!
- \brief This function is used to determine whether input is a variable name and to get its value.
- \return status of search operation
- */
-bool ModuleBase_ParamSpinBox::findVariable(const QString& theName,
-                                           double& outValue) const
+
+void ModuleBase_ParamSpinBox::insertCompletion(const QString& theText)
 {
-  ResultParameterPtr aParam;
-  return ModelAPI_Tools::findVariable(FeaturePtr(), theName.toStdString(), outValue, aParam);
+  QString aText = lineEdit()->text();
+  int aStart, aEnd;
+  QString aPrefix = getPrefix(aStart, aEnd);
+
+  QString aResult;
+  int aPrefLen = aPrefix.length();
+  if (aPrefLen == 0)
+    aResult = aText.insert(myCompletePos, theText);
+  else {
+    aResult = aText.left(aStart) + theText + aText.right(aText.length() - aEnd);
+  }
+  lineEdit()->setText(aResult);
+  myIsEquation = true;
 }
 
-/*!
- \brief This function is called when the spinbox receives key press event.
- */
-//void ModuleBase_ParamSpinBox::keyPressEvent(QKeyEvent* e)
-//{
-//  if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
-//    QWidget::keyPressEvent(e);
-//  } else {
-//    ModuleBase_DoubleSpinBox::keyPressEvent(e);
-//  }
-//}
 
-/*!
- \brief This function is called when the spinbox receives show event.
- */
-void ModuleBase_ParamSpinBox::showEvent(QShowEvent* theEvent)
+void ModuleBase_ParamSpinBox::setValueEnabled(bool theEnable)
 {
-  ModuleBase_DoubleSpinBox::showEvent(theEvent);
-  if ((!myTextValue.isEmpty()) && hasVariable(myTextValue)) {
-    setText(myTextValue);
-  }
+  setReadOnly(!theEnable);
+
+  QPalette aPal = palette();
+  aPal.setColor(QPalette::All, QPalette::Base,
+    theEnable ? myEnabledBaseColor : aPal.color(QPalette::Disabled, QPalette::Base));
+  setPalette(aPal);
 }
index a8cfe4407c597376d92eb0b4a7edae8b978ddea5..696bf0cd79bf4ad1e3e812587502566017bf46e7 100644 (file)
@@ -23,9 +23,9 @@
 
 #include "ModuleBase.h"
 
-#include <ModuleBase_DoubleSpinBox.h>
-
+#include <QAbstractSpinBox>
 #include <QValidator>
+#include <QLineEdit>
 
 class QStringListModel;
 class QCompleter;
@@ -34,7 +34,7 @@ class QCompleter;
 * \ingroup GUI
 * An extension of a double spin box which let to use parameters and expressions for value definition
 */
-class MODULEBASE_EXPORT ModuleBase_ParamSpinBox : public ModuleBase_DoubleSpinBox
+class MODULEBASE_EXPORT ModuleBase_ParamSpinBox : public QAbstractSpinBox
 {
   Q_OBJECT
 
@@ -51,7 +51,7 @@ public:
    \param theParent a parent object
    \param thePrecision a precision of values display
    */
-  explicit ModuleBase_ParamSpinBox( QWidget* theParent = 0, int thePrecision = -12 );
+  ModuleBase_ParamSpinBox( QWidget* theParent = 0, int thePrecision = -12 );
 
   /// Set list of completion strings
   void setCompletionList(QStringList&);
@@ -60,15 +60,19 @@ public:
 
   virtual void stepBy(int);
 
-  virtual double valueFromText(const QString&) const;
-  virtual QString textFromValue (double value) const;
+//  virtual double valueFromText(const QString&) const;
+//  virtual QString textFromValue (double value) const;
 
   virtual QValidator::State validate(QString&, int&) const;
 
   virtual void setValue(double);
 
+  double value() const;
+
   virtual void setText(const QString&);
 
+  QString text() const { return lineEdit()->text(); }
+
   /// Set a flag about accepted variable
   void setAcceptVariables(const bool);
 
@@ -78,42 +82,82 @@ public:
   /// Returns True if the input value contains variable
   bool hasVariable() const;
 
+  double minimum() const { return myMinimum; }
+  double maximum() const { return myMaximum; }
+
+  void setMinimum(double theMin) { myMinimum = theMin; myValidator->setBottom(theMin); }
+  void setMaximum(double theMax) { myMaximum = theMax; myValidator->setTop(theMax); }
+
+  int decimals() const { return myValidator->decimals(); }
+  void setDecimals(int thePrecision) { myValidator->setDecimals(thePrecision); }
+
+  double singleStep() const { return mySingleStep; }
+  void setSingleStep(double theStep) { mySingleStep = theStep; }
+
+  void setValueEnabled(bool theEnable);
+
 protected:
+  /*!
+  \brief This function is called when the spinbox receives key release event.
+  */
+  virtual void keyReleaseEvent(QKeyEvent *event);
+
+  virtual StepEnabled stepEnabled() const { return StepUpEnabled | StepDownEnabled; }
+
    /// Returns True if the given text contains variable
    /// \param theText a text string
   bool hasVariable(const QString& theText) const;
 
-  /// Returns state of the control
-  State isValid(const QString&, double&) const;
-
-  /// Returns True if the given value is within min and max of the control
-  bool checkRange(const double) const;
-
-  /// Finds a variable by its name. Returns true in success
-  /// \param theName a name of variable
-  /// \param outValue an output value of the variable
-  bool findVariable(const QString& theName, double& outValue) const;
+//  /// Returns state of the control
+//  State isValid(const QString&, double&) const;
+//
+//  /// Returns True if the given value is within min and max of the control
+//  bool checkRange(const double) const;
+//
+//  /// Finds a variable by its name. Returns true in success
+//  /// \param theName a name of variable
+//  /// \param outValue an output value of the variable
+//  bool findVariable(const QString& theName, double& outValue) const;
 
 signals:
-  void textChanged(const QString& theText);
+  void textChanged(const QString&);
 
- protected:
-  virtual void showEvent(QShowEvent*);
+// protected:
+//  virtual void showEvent(QShowEvent*);
+//
+// protected slots:
+//   /// A slot called on text change
+//  void onTextChanged(const QString&);
+//
+// private:
+//  void connectSignalsAndSlots();
 
- protected slots:
-   /// A slot called on text change
-  void onTextChanged(const QString&);
+private slots:
+  void insertCompletion(const QString&);
 
- private:
-  void connectSignalsAndSlots();
+  void onTextChanged(const QString&);
 
- private:
-  QString myTextValue;
+private:
+  QString getPrefix(int& theStart, int& theEnd) const;
 
+  bool myIsEquation;
   bool myAcceptVariables;
 
   QStringListModel* myCompleterModel;
   QCompleter* myCompleter;
+  int myPrecision;
+
+  double myMinimum;
+  double myMaximum;
+
+  int myCompletePos;
+
+  double mySingleStep;
+
+  /// Cashed color of active base palette
+  QColor myEnabledBaseColor;
+
+  QDoubleValidator* myValidator;
 };
 
 #endif
index 10f191b27df6c444f18b8ee04dbcc876e2da5ed6..8b09ef91aed1d2169bd03f8a8a0cd4568bd20485 100755 (executable)
@@ -39,6 +39,8 @@
 #include <ModelAPI_AttributeRefAttrList.h>
 #include <ModelAPI_ResultPart.h>
 #include <ModelAPI_ResultConstruction.h>
+#include <ModelAPI_AttributeString.h>
+#include <ModelAPI_Expression.h>
 #include <Events_Loop.h>
 
 #include <ModelAPI_Data.h>
@@ -1169,6 +1171,104 @@ void setPointBallHighlighting(AIS_Shape* theAIS)
   }
 }
 
+FeaturePtr createParameter(const QString& theText)
+{
+  FeaturePtr aParameter;
+  QStringList aList = theText.split("=");
+  if (aList.count() != 2) {
+    return aParameter;
+  }
+  QString aParamName = aList.at(0).trimmed();
+
+  if (isNameExist(aParamName, FeaturePtr())) {
+    return aParameter;
+  }
+
+  if (!ModelAPI_Expression::isVariable(aParamName.toStdString())) {
+    return aParameter;
+  }
+
+  QString aExpression = aList.at(1).trimmed();
+  if (aExpression.isEmpty()) {
+    return aParameter;
+  }
+
+  SessionPtr aMgr = ModelAPI_Session::get();
+  std::shared_ptr<ModelAPI_Document> aDoc = aMgr->activeDocument();
+
+  aParameter = aDoc->addFeature("Parameter");
+  if (aParameter.get()) {
+    AttributeStringPtr aNameAttr = aParameter->string("variable");
+    aNameAttr->setValue(aParamName.toStdString());
+
+    AttributeStringPtr aExprAttr = aParameter->string("expression");
+    aExprAttr->setValue(aExpression.toStdString());
+    aParameter->execute();
+
+    Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
+    Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
+  }
+  return aParameter;
+}
+
+void editParameter(FeaturePtr theParam, const QString& theText)
+{
+  QStringList aList = theText.split("=");
+  QString aParamName = aList.at(0).trimmed();
+
+  QString aExpression = aList.at(1).trimmed();
+  if (aExpression.isEmpty()) {
+    return;
+  }
+
+  if (isNameExist(aParamName, theParam)) {
+    return;
+  }
+  AttributeStringPtr aNameAttr = theParam->string("variable");
+  aNameAttr->setValue(aParamName.toStdString());
+
+  AttributeStringPtr aExprAttr = theParam->string("expression");
+  aExprAttr->setValue(aExpression.toStdString());
+  theParam->execute();
+
+  Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
+}
+
+bool isNameExist(const QString& theName, FeaturePtr theIgnoreParameter)
+{
+  SessionPtr aMgr = ModelAPI_Session::get();
+  std::shared_ptr<ModelAPI_Document> aDoc = aMgr->activeDocument();
+  FeaturePtr aParamFeature;
+  int aNbFeatures = aDoc->numInternalFeatures();
+  std::string aName = theName.toStdString();
+  for (int i = 0; i < aNbFeatures; i++) {
+    aParamFeature = aDoc->internalFeature(i);
+    if (aParamFeature && aParamFeature->getKind() == "Parameter") {
+      if ((theIgnoreParameter != aParamFeature) && (aParamFeature->name() == aName))
+        return true;
+    }
+  }
+  return false;
+}
+
+FeaturePtr findParameter(const QString& theName)
+{
+  SessionPtr aMgr = ModelAPI_Session::get();
+  std::shared_ptr<ModelAPI_Document> aDoc = aMgr->activeDocument();
+  FeaturePtr aParamFeature;
+  int aNbFeatures = aDoc->numInternalFeatures();
+  std::string aName = theName.toStdString();
+  for (int i = 0; i < aNbFeatures; i++) {
+    aParamFeature = aDoc->internalFeature(i);
+    if (aParamFeature && aParamFeature->getKind() == "Parameter") {
+      if (aParamFeature->name() == aName)
+        return aParamFeature;
+    }
+  }
+  return FeaturePtr();
+}
+
+
 } // namespace ModuleBase_Tools
 
 
index 0cd2ed2dc815842b1185cb53b6e01cb8b30c24ac..4570f3fe64f4aa12ee07d85d70a537919c854295 100755 (executable)
@@ -364,6 +364,22 @@ QString MODULEBASE_EXPORT translate(const std::string& theContext, const std::st
 /// \param theAIS - the presentation
 void MODULEBASE_EXPORT setPointBallHighlighting(AIS_Shape* theAIS);
 
+/// Creates a parameter from a given string
+/// \theText a text wit equation
+FeaturePtr MODULEBASE_EXPORT createParameter(const QString& theText);
+
+/// Edits parameter replacing its name and expression according to the given string
+/// \theParam a editing parameter
+/// \theText a text wit equation
+void MODULEBASE_EXPORT editParameter(FeaturePtr theParam, const QString& theText);
+
+/// Returns True if a parameter with the given name already exists
+/// \theName a name of parameter
+bool MODULEBASE_EXPORT isNameExist(const QString& theName, FeaturePtr theIgnoreParameter);
+
+/// Find parameter by its name
+/// \theName a name of parameter
+FeaturePtr MODULEBASE_EXPORT findParameter(const QString& theName);
 }
 
 #endif
index 3b5b8b455cf0633464499acd892207f194b2d523..117cc8c0858ee5dbb58cba4024478febeca5da99 100644 (file)
 
 #include <Config_Keywords.h>
 #include <Config_WidgetAPI.h>
+#include <Events_Loop.h>
 
 #include <ModelAPI_AttributeDouble.h>
 #include <ModelAPI_Data.h>
 #include <ModelAPI_Object.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_Document.h>
+#include <ModelAPI_ResultParameter.h>
+#include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_Tools.h>
+#include <ModelAPI_Events.h>
+#include <ModelAPI_AttributeString.h>
 
 #include <ModuleBase_ParamSpinBox.h>
 #include <ModuleBase_Tools.h>
@@ -46,8 +54,6 @@
 #include <iostream>
 #endif
 
-//#define DEBUG_COMPLETE_WITH_PARAMETERS
-
 ModuleBase_WidgetDoubleValue::ModuleBase_WidgetDoubleValue(QWidget* theParent,
                                                            const Config_WidgetAPI* theData)
     : ModuleBase_ModelWidget(theParent, theData)
@@ -117,11 +123,9 @@ ModuleBase_WidgetDoubleValue::~ModuleBase_WidgetDoubleValue()
 void ModuleBase_WidgetDoubleValue::activateCustom()
 {
   ModuleBase_ModelWidget::activateCustom();
-#ifdef DEBUG_COMPLETE_WITH_PARAMETERS
   QStringList aParameters;
   ModuleBase_Tools::getParameters(aParameters);
   mySpinBox->setCompletionList(aParameters);
-#endif
 }
 
 bool ModuleBase_WidgetDoubleValue::resetCustom()
@@ -149,8 +153,28 @@ bool ModuleBase_WidgetDoubleValue::storeValueCustom()
   AttributeDoublePtr aReal = aData->real(attributeID());
   if (mySpinBox->hasVariable()) {
     // Here is a text of a real value or an expression.
-    std::string aText = mySpinBox->text().toStdString();
-    aReal->setText(aText);
+    QString aText = mySpinBox->text();
+    if (aText.contains('=')) {
+      if (!myParameter.get()) {
+        myParameter = ModuleBase_Tools::createParameter(aText);
+        if (!myParameter.get()) {
+          aReal->setExpressionError("Parameter cannot be created");
+          aReal->setExpressionInvalid(true);
+          updateObject(myFeature);
+          return false;
+        } else if (aReal->expressionInvalid()) {
+          aReal->setExpressionError("");
+          aReal->setExpressionInvalid(false);
+        }
+      } else {
+        ModuleBase_Tools::editParameter(myParameter, aText);
+      }
+      aText = aText.split('=').at(0) + "=";
+    } else if (myParameter.get()){
+      // Nullyfy the parameter reference without deletion of the created
+      myParameter = FeaturePtr();
+    }
+    aReal->setText(aText.toStdString());
   } else {
     // it is important to set the empty text value to the attribute before set the value
     // because setValue tries to calculate the attribute value according to the
@@ -168,7 +192,20 @@ bool ModuleBase_WidgetDoubleValue::restoreValueCustom()
   AttributeDoublePtr aRef = aData->real(attributeID());
   std::string aTextRepr = aRef->text();
   if (!aTextRepr.empty()) {
-    ModuleBase_Tools::setSpinText(mySpinBox, QString::fromStdString(aTextRepr));
+    QString aText = QString::fromStdString(aTextRepr);
+    if (aText.endsWith('=')) {
+      if (!myParameter.get()) {
+        QString aName = aText.left(aText.indexOf('=')).trimmed();
+        myParameter = ModuleBase_Tools::findParameter(aName);
+      }
+      /// If myParameter is empty then it was not created because of an error
+      if (!myParameter.get())
+        return false;
+
+      AttributeStringPtr aExprAttr = myParameter->string("expression");
+      aText += aExprAttr->value().c_str();
+    }
+    ModuleBase_Tools::setSpinText(mySpinBox, aText);
   } else {
     ModuleBase_Tools::setSpinValue(mySpinBox, aRef->isInitialized() ? aRef->value() : 0);
   }
index 347f3ca99700be165d63dccdbc6ddda2602ae558..4cdae391d5224ab061675144d4bc8a2801177497 100644 (file)
@@ -86,6 +86,8 @@ protected:
 
   /// Input value control
   ModuleBase_ParamSpinBox* mySpinBox;
+
+  FeaturePtr myParameter;
 };
 
 #endif
index 5da885e1333d502f66d661a4eda3275dc316a790..b9099882564f192633f3a7e244af3347587748a7 100644 (file)
@@ -103,9 +103,7 @@ void ExpressionEditor::performCompletion()
   QTextCursor aCursor = textCursor();
   aCursor.select(QTextCursor::WordUnderCursor);
   const QString aPrefix = aCursor.selectedText();
-  if (!aPrefix.isEmpty() && aPrefix.at(aPrefix.length() - 1).isLetter()) {
-    performCompletion(aPrefix);
-  }
+  performCompletion(aPrefix);
 }
 
 void ExpressionEditor::performCompletion(const QString& theCompletionPrefix)
index abfead215283f6fd7099af41bed0ca0c86b9be7d..e694d35f58b0953cc71ef626fcc081cc5d056917 100644 (file)
 #include <ModuleBase_WidgetIntValue.h>
 #include <ModuleBase_ParamSpinBox.h>
 #include <ModuleBase_Tools.h>
-#include <ModuleBase_ParamIntSpinBox.h>
+#include <ModuleBase_ParamSpinBox.h>
 #include <ModuleBase_IconFactory.h>
 
 #include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_Data.h>
+#include <ModelAPI_AttributeString.h>
 
 #include <Config_Keywords.h>
 #include <Config_WidgetAPI.h>
@@ -62,7 +63,8 @@ ModuleBase_WidgetIntValue::ModuleBase_WidgetIntValue(QWidget* theParent,
   if (!aLabelIcon.isEmpty())
     myLabel->setPixmap(ModuleBase_IconFactory::loadPixmap(aLabelIcon));
 
-  mySpinBox = new ModuleBase_ParamIntSpinBox(this);
+  mySpinBox = new ModuleBase_ParamSpinBox(this);
+  mySpinBox->setDecimals(0);
   QString anObjName = QString::fromStdString(attributeID());
   mySpinBox->setObjectName(anObjName);
 
@@ -106,6 +108,14 @@ ModuleBase_WidgetIntValue::~ModuleBase_WidgetIntValue()
 {
 }
 
+void ModuleBase_WidgetIntValue::activateCustom()
+{
+  ModuleBase_ModelWidget::activateCustom();
+  QStringList aParameters;
+  ModuleBase_Tools::getParameters(aParameters);
+  mySpinBox->setCompletionList(aParameters);
+}
+
 bool ModuleBase_WidgetIntValue::resetCustom()
 {
   bool aDone = false;
@@ -131,8 +141,28 @@ bool ModuleBase_WidgetIntValue::storeValueCustom()
   AttributeIntegerPtr anAttribute = aData->integer(attributeID());
   if (mySpinBox->hasVariable()) {
     // Here is a text of a real value or an expression.
-    std::string aText = mySpinBox->text().toStdString();
-    anAttribute->setText(aText);
+    QString aText = mySpinBox->text();
+    if (aText.contains('=')) {
+      if (!myParameter.get()) {
+        myParameter = ModuleBase_Tools::createParameter(aText);
+        if (!myParameter.get()) {
+          anAttribute->setExpressionError("Parameter cannot be created");
+          anAttribute->setExpressionInvalid(true);
+          updateObject(myFeature);
+          return false;
+        } else if (anAttribute->expressionInvalid()) {
+          anAttribute->setExpressionError("");
+          anAttribute->setExpressionInvalid(false);
+        }
+      } else {
+        ModuleBase_Tools::editParameter(myParameter, aText);
+      }
+      aText = aText.split('=').at(0) + "=";
+    } else if (myParameter.get()) {
+      // Nullyfy the parameter reference without deletion of the created
+      myParameter = FeaturePtr();
+    }
+    anAttribute->setText(aText.toStdString());
   } else {
     // it is important to set the empty text value to the attribute before set the value
     // because setValue tries to calculate the attribute value according to the
@@ -150,7 +180,20 @@ bool ModuleBase_WidgetIntValue::restoreValueCustom()
   AttributeIntegerPtr anAttribute = aData->integer(attributeID());
   std::string aTextRepr = anAttribute->text();
   if (!aTextRepr.empty()) {
-    ModuleBase_Tools::setSpinText(mySpinBox, QString::fromStdString(aTextRepr));
+    QString aText = QString::fromStdString(aTextRepr);
+    if (aText.endsWith('=')) {
+      if (!myParameter.get()) {
+        QString aName = aText.left(aText.indexOf('=')).trimmed();
+        myParameter = ModuleBase_Tools::findParameter(aName);
+      }
+      /// If myParameter is empty then it was not created because of an error
+      if (!myParameter.get())
+        return false;
+
+      AttributeStringPtr aExprAttr = myParameter->string("expression");
+      aText += aExprAttr->value().c_str();
+    }
+    ModuleBase_Tools::setSpinText(mySpinBox, aText);
   } else {
     ModuleBase_Tools::setSpinValue(mySpinBox, anAttribute->value());
   }
index 0394458c98641ae2b6f823607523651330216679..bcd45d4b199719d9eaf373260fe8aa2a11e3e5b2 100644 (file)
@@ -24,7 +24,7 @@
 #include "ModuleBase.h"
 #include "ModuleBase_ModelWidget.h"
 
-class ModuleBase_ParamIntSpinBox;
+class ModuleBase_ParamSpinBox;
 class Config_WidgetAPI;
 class QWidget;
 class QLabel;
@@ -49,6 +49,9 @@ Q_OBJECT
 
   virtual ~ModuleBase_WidgetIntValue();
 
+  /// The methiod called when widget is activated
+  virtual void activateCustom();
+
   /// Select the internal content if it can be selected. It is empty in the default realization
   virtual void selectContent();
 
@@ -77,7 +80,9 @@ protected:
   QLabel* myLabel;
 
   /// Input value control
-  ModuleBase_ParamIntSpinBox* mySpinBox;
+  ModuleBase_ParamSpinBox* mySpinBox;
+
+  FeaturePtr myParameter;
 };
 
 #endif
index dd8931d19c17728504cfc332f18e75454920c11d..329eb0bf50e2618f9793996332d2e63a457e2281 100755 (executable)
@@ -265,6 +265,7 @@ void PartSet_Module::registerValidators()
     new PartSet_MultyTranslationSelection);
   aFactory->registerValidator("PartSet_SplitSelection", new PartSet_SplitSelection);
   aFactory->registerValidator("PartSet_ProjectionSelection", new PartSet_ProjectionSelection);
+  aFactory->registerValidator("PartSet_IntersectionSelection", new PartSet_IntersectionSelection);
 }
 
 //******************************************************
@@ -1047,22 +1048,20 @@ void PartSet_Module::onObjectDisplayed(ObjectPtr theObject, AISObjectPtr theAIS)
 {
   Handle(AIS_InteractiveObject) anAIS = theAIS->impl<Handle(AIS_InteractiveObject)>();
   if (!anAIS.IsNull()) {
-    bool aToUseZLayer = false;
     FeaturePtr aFeature = ModelAPI_Feature::feature(theObject);
-    if (aFeature.get() && PartSet_Tools::findRefsToMeFeature(aFeature,
-                                                        SketchPlugin_Projection::ID()))
-      aToUseZLayer = true;
-    Handle(AIS_InteractiveContext) aCtx = anAIS->GetContext();
-    Handle(AIS_Dimension) aDim = Handle(AIS_Dimension)::DownCast(anAIS);
-    if (!aDim.IsNull()) {
-      aToUseZLayer = true;
-    } else {
-      Handle(SketcherPrs_SymbolPrs) aCons = Handle(SketcherPrs_SymbolPrs)::DownCast(anAIS);
-      if (!aCons.IsNull())
-      aToUseZLayer = true;
+    if (aFeature.get()) {
+      bool aToUseZLayer = false;
+      if (PartSet_Tools::findRefsToMeFeature(aFeature,SketchPlugin_Projection::ID()))
+        aToUseZLayer = true;
+      else {
+        CompositeFeaturePtr aParent = ModelAPI_Tools::compositeOwner(aFeature);
+        aToUseZLayer = (aParent.get() && (aParent->getKind() == SketchPlugin_Sketch::ID()));
+      }
+      if (aToUseZLayer) {
+        Handle(AIS_InteractiveContext) aCtx = anAIS->GetContext();
+        aCtx->SetZLayer(anAIS, myVisualLayerId);
+      }
     }
-    if (aToUseZLayer)
-      aCtx->SetZLayer(anAIS, myVisualLayerId);
   }
 }
 
index b2f95ddd3f665a4bc4871e814c7ba1e63996cd8b..49f89d9c033ac2a308aade1b1a558a10846ac122 100755 (executable)
@@ -407,6 +407,17 @@ bool PartSet_ProjectionSelection::isValid(const ModuleBase_ISelection* theSelect
   }
 }
 
+bool PartSet_IntersectionSelection::isValid(const ModuleBase_ISelection* theSelection,
+                                     ModuleBase_Operation* theOperation) const
+{
+  if (theSelection->getSelected(ModuleBase_ISelection::Viewer).size() == 0) {
+    return isEmptySelectionValid(theOperation);
+  } else {
+    int aCount = shapesNbLines(theSelection);
+    return aCount == 0;
+  }
+}
+
 
 std::string PartSet_DifferentObjectsValidator::errorMessage(
                          const PartSet_DifferentObjectsValidator::ErrorType& theType,
index 039d259ca9536dec339feab3d2f9cfb4b592225d..c1fd7eaee43b6cad787e7fd699fe72f23077f871 100644 (file)
@@ -188,6 +188,15 @@ public:
                                       ModuleBase_Operation* theOperation) const;
 };
 
+//! \ingroup Validators
+//! A class to validate a selection for intersection operation
+class PartSet_IntersectionSelection : public ModuleBase_SelectionValidator
+{
+public:
+  PARTSET_EXPORT virtual bool isValid(const ModuleBase_ISelection* theSelection,
+                                      ModuleBase_Operation* theOperation) const;
+};
+
 ////////////// Attribute validators ////////////////
 
 
index 0347b26b273484f14c39702e969245bb5e132788..266e7ee8855d3fe3d3624570d07cd4b981b80791 100644 (file)
@@ -1,7 +1,7 @@
 """Package for Features plugin for the Parametric Geometry API of the Modeler.
 """
 
-from FeaturesAPI import addPlacement, addRotation, addScale, addSymmetry, addTranslation
+from FeaturesAPI import addPlacement, addRotation, addScale, addMirror, addTranslation
 from FeaturesAPI import addMultiTranslation, addMultiRotation
 from FeaturesAPI import addExtrusion, addExtrusionCut, addExtrusionFuse
 from FeaturesAPI import addRevolution, addRevolutionCut, addRevolutionFuse
@@ -10,3 +10,4 @@ from FeaturesAPI import addCut, addFuse, addCommon, addSmash, addFill
 from FeaturesAPI import addIntersection, addPartition, addUnion, addRemoveSubShapes
 from FeaturesAPI import addRecover
 from FeaturesAPI import addFillet
+from FeaturesAPI import measureLength, measureDistance, measureRadius, measureAngle
index 39f5b27266535699f200f1b6eb65c4e2fad51d22..ff689965c69a9c76e49d76cc311f43505f41009d 100644 (file)
@@ -19,6 +19,9 @@
 //
 
 #include "SketchAPI_IntersectionPoint.h"
+#include "SketchAPI_Point.h"
+//--------------------------------------------------------------------------------------
+#include <SketchPlugin_Point.h>
 //--------------------------------------------------------------------------------------
 #include <GeomAPI_Pnt2d.h>
 //--------------------------------------------------------------------------------------
@@ -39,7 +42,7 @@ SketchAPI_IntersectionPoint::SketchAPI_IntersectionPoint(
 : SketchAPI_SketchEntity(theFeature)
 {
   if (initialize()) {
-    setByExternalLine(theExternal);
+    setByExternalEdge(theExternal);
   }
 }
 
@@ -49,7 +52,7 @@ SketchAPI_IntersectionPoint::SketchAPI_IntersectionPoint(
 : SketchAPI_SketchEntity(theFeature)
 {
   if (initialize()) {
-    setByExternalLineName(theExternalName);
+    setByExternalEdgeName(theExternalName);
   }
 }
 
@@ -59,20 +62,48 @@ SketchAPI_IntersectionPoint::~SketchAPI_IntersectionPoint()
 }
 
 //--------------------------------------------------------------------------------------
-void SketchAPI_IntersectionPoint::setByExternalLine(const ModelHighAPI_Selection & theExternalLine)
+void SketchAPI_IntersectionPoint::setByExternalEdge(const ModelHighAPI_Selection & theExternalLine)
 {
-  fillAttribute(theExternalLine, externalLine());
+  fillAttribute(theExternalLine, externalFeature());
 
   execute();
 }
 
-void SketchAPI_IntersectionPoint::setByExternalLineName(const std::string & theExternalLineName)
+void SketchAPI_IntersectionPoint::setByExternalEdgeName(const std::string & theExternalLineName)
 {
-  fillAttribute(ModelHighAPI_Selection("EDGE", theExternalLineName), externalLine());
+  fillAttribute(ModelHighAPI_Selection("EDGE", theExternalLineName), externalFeature());
 
   execute();
 }
 
+void SketchAPI_IntersectionPoint::setIncludeToResult(bool theKeepResult)
+{
+  fillAttribute(theKeepResult, includeToResult());
+  execute(true);
+}
+
+//--------------------------------------------------------------------------------------
+
+std::list<std::shared_ptr<SketchAPI_SketchEntity> >
+SketchAPI_IntersectionPoint::intersectionPoints() const
+{
+  std::list<std::shared_ptr<SketchAPI_SketchEntity> > anEntities;
+
+  std::list<ObjectPtr> anIntersections =
+      feature()->reflist(SketchPlugin_IntersectionPoint::INTERSECTION_POINTS_ID())->list();
+  for (std::list<ObjectPtr>::iterator anIt = anIntersections.begin();
+       anIt != anIntersections.end(); ++anIt) {
+    FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
+    if (aFeature && aFeature->getKind() == SketchPlugin_Point::ID())
+    {
+      std::shared_ptr<SketchAPI_SketchEntity> anEnt(new SketchAPI_Point(aFeature));
+      anEntities.push_back(anEnt);
+    }
+  }
+
+  return anEntities;
+}
+
 //--------------------------------------------------------------------------------------
 
 void SketchAPI_IntersectionPoint::dump(ModelHighAPI_Dumper& theDumper) const
@@ -80,9 +111,21 @@ void SketchAPI_IntersectionPoint::dump(ModelHighAPI_Dumper& theDumper) const
   FeaturePtr aBase = feature();
   const std::string& aSketchName = theDumper.parentName(aBase);
 
-  AttributeSelectionPtr aLine = externalLine();
-  theDumper << aBase << " = " <<
-    aSketchName << ".addIntersectionPoint(" << aLine << ")" << std::endl;
+  AttributeSelectionPtr anExternal = externalFeature();
+  AttributeBooleanPtr isIncludeToRes = includeToResult();
+  theDumper << aBase << " = " << aSketchName << ".addIntersectionPoint("
+            << anExternal << ", " << isIncludeToRes << ")" << std::endl;
   // dump "auxiliary" flag if necessary
   SketchAPI_SketchEntity::dump(theDumper);
+
+  // Dump variables for a list of intersected points
+  theDumper << "[";
+  std::list<std::shared_ptr<SketchAPI_SketchEntity> > aList = intersectionPoints();
+  std::list<std::shared_ptr<SketchAPI_SketchEntity> >::const_iterator anIt = aList.begin();
+  for (; anIt != aList.end(); ++anIt) {
+    if (anIt != aList.begin())
+      theDumper << ", ";
+    theDumper << (*anIt)->feature();
+  }
+  theDumper << "] = " << theDumper.name(aBase) << ".intersectionPoints()" << std::endl;
 }
index 325d522bf42c0892aa05023024b1eeedf2fb3b7b..cc1b4e947a107f780ff058eec33d5acc77ee5f7d 100644 (file)
@@ -52,20 +52,30 @@ public:
   SKETCHAPI_EXPORT
   virtual ~SketchAPI_IntersectionPoint();
 
-  INTERFACE_2(SketchPlugin_IntersectionPoint::ID(),
-              coordinates, SketchPlugin_IntersectionPoint::COORD_ID(),
-              GeomDataAPI_Point2D, /** IntersectionPoint coordinates */,
-              externalLine, SketchPlugin_IntersectionPoint::EXTERNAL_LINE_ID(),
-              ModelAPI_AttributeSelection, /** External line */
+  INTERFACE_3(SketchPlugin_IntersectionPoint::ID(),
+              externalFeature, SketchPlugin_IntersectionPoint::EXTERNAL_FEATURE_ID(),
+              ModelAPI_AttributeSelection, /** External edge */,
+              external, SketchPlugin_IntersectionPoint::EXTERNAL_ID(),
+              ModelAPI_AttributeSelection, /** External */,
+              includeToResult, SketchPlugin_IntersectionPoint::INCLUDE_INTO_RESULT(),
+              ModelAPI_AttributeBoolean, /** Include into result */
   )
 
   /// Set by external
   SKETCHAPI_EXPORT
-  void setByExternalLine(const ModelHighAPI_Selection & theExternalLine);
+  void setByExternalEdge(const ModelHighAPI_Selection & theExternaEdge);
 
   /// Set by external name
   SKETCHAPI_EXPORT
-  void setByExternalLineName(const std::string & theExternalLineName);
+  void setByExternalEdgeName(const std::string & theExternalEdgeName);
+
+  /// Set flag to include projection to result or not
+  SKETCHAPI_EXPORT
+  void setIncludeToResult(bool theKeepResult);
+
+  /// Returns created intersection points
+  SKETCHAPI_EXPORT
+  std::list<std::shared_ptr<SketchAPI_SketchEntity> > intersectionPoints() const;
 
   /// Dump wrapped feature
   SKETCHAPI_EXPORT
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..24be396bf39a2a109d11bd5f4d90d51b82b03e9a 100644 (file)
@@ -235,18 +235,24 @@ std::shared_ptr<SketchAPI_Point> SketchAPI_Sketch::addPoint(const std::string &
 
 //--------------------------------------------------------------------------------------
 std::shared_ptr<SketchAPI_IntersectionPoint> SketchAPI_Sketch::addIntersectionPoint(
-    const ModelHighAPI_Selection & theExternal)
+    const ModelHighAPI_Selection & theExternal,
+    bool theKeepResult)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature =
     compositeFeature()->addFeature(SketchPlugin_IntersectionPoint::ID());
-  return IntersectionPointPtr(new SketchAPI_IntersectionPoint(aFeature, theExternal));
+  IntersectionPointPtr anIntersection(new SketchAPI_IntersectionPoint(aFeature, theExternal));
+  anIntersection->setIncludeToResult(theKeepResult);
+  return anIntersection;
 }
 std::shared_ptr<SketchAPI_IntersectionPoint> SketchAPI_Sketch::addIntersectionPoint(
-    const std::string & theExternalName)
+    const std::string & theExternalName,
+    bool theKeepResult)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature =
     compositeFeature()->addFeature(SketchPlugin_IntersectionPoint::ID());
-  return IntersectionPointPtr(new SketchAPI_IntersectionPoint(aFeature, theExternalName));
+  IntersectionPointPtr anIntersection(new SketchAPI_IntersectionPoint(aFeature, theExternalName));
+  anIntersection->setIncludeToResult(theKeepResult);
+  return anIntersection;
 }
 
 //--------------------------------------------------------------------------------------
@@ -509,13 +515,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..aa58dc89edab2ea9fe68955450764eaa9dacf1e6 100644 (file)
@@ -125,11 +125,13 @@ public:
   /// Add intersection point
   SKETCHAPI_EXPORT
   std::shared_ptr<SketchAPI_IntersectionPoint>
-    addIntersectionPoint(const ModelHighAPI_Selection & theExternal);
+    addIntersectionPoint(const ModelHighAPI_Selection & theExternal,
+                         bool theKeepResult = false);
   /// Add point
   SKETCHAPI_EXPORT
   std::shared_ptr<SketchAPI_IntersectionPoint>
-    addIntersectionPoint(const std::string & theExternalName);
+    addIntersectionPoint(const std::string & theExternalName,
+                         bool theKeepResult = false);
 
   /// Add line
   SKETCHAPI_EXPORT
@@ -281,7 +283,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..47550e2d55bd8d24b1f500f0b1fc2b6c4686f883 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
@@ -227,6 +232,10 @@ ADD_UNIT_TESTS(TestSketchPointLine.py
                TestDistanceSignedVsUnsigned05.py
                TestSignedDistancePointPoint.py
                TestSignedDistancePointLine.py
+               TestIntersectionWithLine.py
+               TestIntersectionWithCircle.py
+               TestIntersectionWithSpline.py
+               TestIntersectionChangeType.py
                Test2273.py
                Test2280.py
                Test2287.py
index a391b22e6a31f49d5c9a9a373575db2833dd75bd..be389af74185cdfc8f8d62f926cb8697f3a82a0c 100644 (file)
 //
 
 #include "SketchPlugin_IntersectionPoint.h"
+#include "SketchPlugin_Point.h"
 
 #include <ModelAPI_AttributeSelection.h>
+#include <ModelAPI_AttributeRefList.h>
+#include <ModelAPI_ResultConstruction.h>
 #include <ModelAPI_Session.h>
+#include <ModelAPI_Tools.h>
 #include <ModelAPI_Validator.h>
 
 #include <GeomAPI_Edge.h>
 #include <GeomAPI_Lin.h>
+#include <GeomAPI_Pnt2d.h>
 #include <GeomDataAPI_Point2D.h>
 
 SketchPlugin_IntersectionPoint::SketchPlugin_IntersectionPoint()
-    : SketchPlugin_Point()
+  : SketchPlugin_SketchEntity(),
+    myIsComputing(false)
 {
 }
 
 void SketchPlugin_IntersectionPoint::initDerivedClassAttributes()
 {
-  data()->addAttribute(EXTERNAL_LINE_ID(), ModelAPI_AttributeSelection::typeId());
+  data()->addAttribute(EXTERNAL_FEATURE_ID(), ModelAPI_AttributeSelection::typeId());
+  data()->addAttribute(INTERSECTION_POINTS_ID(), ModelAPI_AttributeRefList::typeId());
+  data()->attribute(INTERSECTION_POINTS_ID())->setIsArgument(false);
 
-  SketchPlugin_Point::initDerivedClassAttributes();
+  data()->addAttribute(INCLUDE_INTO_RESULT(), ModelAPI_AttributeBoolean::typeId());
+
+  data()->addAttribute(EXTERNAL_ID(), ModelAPI_AttributeSelection::typeId());
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EXTERNAL_ID());
+
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), AUXILIARY_ID());
 }
 
 void SketchPlugin_IntersectionPoint::execute()
 {
-  SketchPlugin_Sketch* aSketch = sketch();
-  if (aSketch) {
-    computePoint();
-    SketchPlugin_Point::execute();
+  AttributeRefListPtr anIntersectionsList = reflist(INTERSECTION_POINTS_ID());
+  if (!anIntersectionsList || !anIntersectionsList->isInitialized())
+    return; // no intersections
 
-    // set this feature as external
-    data()->selection(EXTERNAL_ID())->setValue(lastResult(), lastResult()->shape());
-  }
+  computePoint(EXTERNAL_FEATURE_ID());
 }
 
 void SketchPlugin_IntersectionPoint::attributeChanged(const std::string& theID)
 {
-  if (theID == EXTERNAL_LINE_ID()) {
-    // compute intersection between line and sketch plane
-    computePoint();
-  }
+  // compute intersection between line and sketch plane
+  computePoint(theID);
 }
 
-void SketchPlugin_IntersectionPoint::computePoint()
+void SketchPlugin_IntersectionPoint::computePoint(const std::string& theID)
 {
-  AttributeSelectionPtr aLineAttr =
-      std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(attribute(EXTERNAL_LINE_ID()));
-
-  std::shared_ptr<GeomAPI_Edge> anEdge;
-  if(aLineAttr && aLineAttr->value() && aLineAttr->value()->isEdge()) {
-    anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aLineAttr->value()));
-  } else if(aLineAttr->context() && aLineAttr->context()->shape() &&
-            aLineAttr->context()->shape()->isEdge()) {
-    anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aLineAttr->context()->shape()));
-  }
-  if(!anEdge.get())
+  if (theID != EXTERNAL_FEATURE_ID() && theID != EXTERNAL_ID())
     return;
 
-  std::shared_ptr<GeomAPI_Lin> aLine = anEdge->line();
-  std::shared_ptr<GeomAPI_Pln> aSketchPlane = sketch()->plane();
-
-  std::shared_ptr<GeomAPI_Pnt> anIntersection = aSketchPlane->intersect(aLine);
-  if (!anIntersection)
+  if (myIsComputing)
     return;
+  myIsComputing = true;
+
+  AttributeSelectionPtr anExternalFeature = selection(EXTERNAL_FEATURE_ID());
+
+  GeomShapePtr aShape;
+  GeomEdgePtr anEdge;
+  if (anExternalFeature)
+    aShape = anExternalFeature->value();
+  if (!aShape && anExternalFeature->context())
+    aShape = anExternalFeature->context()->shape();
+  if (aShape && aShape->isEdge())
+    anEdge = GeomEdgePtr(new GeomAPI_Edge(aShape));
+
+  if (anEdge) {
+    std::shared_ptr<GeomAPI_Pln> aSketchPlane = sketch()->plane();
+
+    std::list<GeomPointPtr> anIntersectionsPoints;
+    anEdge->intersectWithPlane(aSketchPlane, anIntersectionsPoints);
 
-  std::shared_ptr<GeomDataAPI_Point2D> aCoordAttr =
-      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(COORD_ID()));
-  aCoordAttr->setValue(sketch()->to2D(anIntersection));
+    AttributeRefListPtr anIntersectionsList = reflist(INTERSECTION_POINTS_ID());
+    std::list<ObjectPtr> anExistentIntersections = anIntersectionsList->list();
+    std::list<ObjectPtr>::const_iterator aExistInterIt = anExistentIntersections.begin();
+
+    const std::list<ResultPtr>& aResults = results();
+    std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
+
+    int aResultIndex = 0;
+    for (std::list<GeomPointPtr>::iterator aPntIt = anIntersectionsPoints.begin();
+         aPntIt != anIntersectionsPoints.end(); ++aPntIt, ++aResultIndex) {
+      std::shared_ptr<SketchPlugin_Point> aCurSketchPoint;
+      if (aExistInterIt == anExistentIntersections.end()) {
+        // create new point
+        aCurSketchPoint = std::dynamic_pointer_cast<SketchPlugin_Point>(
+          sketch()->addFeature(SketchPlugin_Point::ID()));
+        aCurSketchPoint->boolean(COPY_ID())->setValue(true);
+        anIntersectionsList->append(aCurSketchPoint);
+      } else {
+        // update existent point
+        aCurSketchPoint = std::dynamic_pointer_cast<SketchPlugin_Point>(*aExistInterIt);
+        ++aExistInterIt;
+      }
+
+      ResultConstructionPtr aCurResult;
+      if (aResIt == aResults.end()) {
+        // create new result
+        aCurResult = document()->createConstruction(data(), aResultIndex);
+        aCurResult->setIsInHistory(false);
+        aCurResult->setDisplayed(false);
+      } else {
+        // update existent result
+        aCurResult = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aResIt);
+        aCurResult->setShape(std::shared_ptr<GeomAPI_Edge>());
+        ++aResIt;
+      }
+
+      // update coordinates of intersection
+      GeomPnt2dPtr aPointInSketch = sketch()->to2D(*aPntIt);
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        aCurSketchPoint->attribute(SketchPlugin_Point::COORD_ID()))->setValue(aPointInSketch);
+      aCurSketchPoint->execute();
+
+      // update result
+      aCurResult->setShape(aCurSketchPoint->lastResult()->shape());
+      setResult(aCurResult, aResultIndex);
+
+      // make intersection point external
+      GeomShapePtr anEmptyVal;
+      aCurSketchPoint->selection(EXTERNAL_ID())->setValue(aCurResult, anEmptyVal);
+    }
+
+    // remove rest results from previous pass
+    removeResults(aResultIndex);
+    std::set<FeaturePtr> aFeaturesToBeRemoved;
+    for (; aExistInterIt != anExistentIntersections.end(); ++aExistInterIt) {
+      aFeaturesToBeRemoved.insert(ModelAPI_Feature::feature(*aExistInterIt));
+      anIntersectionsList->removeLast();
+    }
+    ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved);
+
+    if (theID != EXTERNAL_ID())
+      selection(EXTERNAL_ID())->selectValue(anExternalFeature);
+  }
+  myIsComputing = false;
 }
index a25f1be363dbcfe3c63bcac0c01cefa31b66e2b0..2ddebd6b1fa044eeccae4a100f35727b5b5eacac 100644 (file)
 #ifndef SketchPlugin_IntersectionPoint_H_
 #define SketchPlugin_IntersectionPoint_H_
 
-#include "SketchPlugin_Point.h"
+#include "SketchPlugin_SketchEntity.h"
 
 /**\class SketchPlugin_IntersectionPoint
  * \ingroup Plugins
  * \brief Feature for creation of external point as an intersection
  *        between external edge and a plane of the sketch.
  */
-class SketchPlugin_IntersectionPoint : public SketchPlugin_Point
+class SketchPlugin_IntersectionPoint : public SketchPlugin_SketchEntity
 {
 public:
   /// Point feature kind
@@ -44,16 +44,33 @@ public:
     return MY_KIND;
   }
 
-  static const std::string& EXTERNAL_LINE_ID()
+  static const std::string& EXTERNAL_FEATURE_ID()
   {
-    static std::string MY_LINE_ID("ExternalLine");
-    return MY_LINE_ID;
+    static std::string MY_FEATURE_ID("ExternalFeature");
+    return MY_FEATURE_ID;
+  }
+
+  static const std::string& INTERSECTION_POINTS_ID()
+  {
+    static std::string MY_INTERSECTIONS_ID("IntersectionPoints");
+    return MY_INTERSECTIONS_ID;
+  }
+
+  static const std::string& INCLUDE_INTO_RESULT()
+  {
+    static std::string MY_INCLUDE("IncludeToResult");
+    return MY_INCLUDE;
   }
 
   /// Returns true because intersection point is always external
   virtual bool isFixed()
   { return true; }
 
+  /// Returns true if the feature and the feature results can be displayed.
+  /// \return false
+  virtual bool canBeDisplayed() const
+  { return false; }
+
   /// Creates a new part document if needed
   SKETCHPLUGIN_EXPORT virtual void execute();
 
@@ -69,7 +86,9 @@ protected:
 
 private:
   /// \brief Find intersection between a line and a sketch plane
-  void computePoint();
+  void computePoint(const std::string& theID);
+
+  bool myIsComputing;
 };
 
 #endif
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..c003dd7eda119e37eee5dc8e23d82926115a41e9 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>
@@ -800,27 +801,25 @@ bool SketchPlugin_IntersectionValidator::isValid(const AttributePtr& theAttribut
     theError.arg(theAttribute->attributeType());
     return false;
   }
-  AttributeSelectionPtr aLineAttr =
-                       std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
+  AttributeSelectionPtr anExternalAttr =
+      std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
   std::shared_ptr<GeomAPI_Edge> anEdge;
-  if(aLineAttr && aLineAttr->value() && aLineAttr->value()->isEdge()) {
-    anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aLineAttr->value()));
-  } else if(aLineAttr->context() &&
-            aLineAttr->context()->shape() && aLineAttr->context()->shape()->isEdge()) {
-    anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aLineAttr->context()->shape()));
+  if (anExternalAttr && anExternalAttr->value() && anExternalAttr->value()->isEdge()) {
+    anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(anExternalAttr->value()));
+  } else if(anExternalAttr->context() && anExternalAttr->context()->shape() &&
+            anExternalAttr->context()->shape()->isEdge()) {
+    anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(anExternalAttr->context()->shape()));
   }
 
-  if (!anEdge || !anEdge->isLine()) {
-    theError = "The attribute %1 should be a line";
+  if (!anEdge) {
+    theError = "The attribute %1 should be an edge";
     theError.arg(theAttribute->id());
     return false;
   }
 
-  std::shared_ptr<GeomAPI_Dir> aLineDir = anEdge->line()->direction();
-
   // find a sketch
   std::shared_ptr<SketchPlugin_Sketch> aSketch;
-  std::set<AttributePtr> aRefs = aLineAttr->owner()->data()->refsToMe();
+  std::set<AttributePtr> aRefs = anExternalAttr->owner()->data()->refsToMe();
   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
   for (; anIt != aRefs.end(); ++anIt) {
     CompositeFeaturePtr aComp =
@@ -835,9 +834,16 @@ bool SketchPlugin_IntersectionValidator::isValid(const AttributePtr& theAttribut
     return false;
   }
 
+  // check the edge is intersected with sketch plane
   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
-  std::shared_ptr<GeomAPI_Dir> aNormal = aPlane->direction();
-  return fabs(aNormal->dot(aLineDir)) > tolerance * tolerance;
+
+  std::list<GeomPointPtr> anIntersectionsPoints;
+  anEdge->intersectWithPlane(aPlane, anIntersectionsPoints);
+  if (anIntersectionsPoints.empty()) {
+    theError = "The edge is not intersected with sketch plane";
+    return false;
+  }
+  return true;
 }
 
 bool SketchPlugin_SplitValidator::isValid(const AttributePtr& theAttribute,
@@ -1636,3 +1642,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/TestIntersectionChangeEdge.py b/src/SketchPlugin/Test/TestIntersectionChangeEdge.py
new file mode 100644 (file)
index 0000000..f328df9
--- /dev/null
@@ -0,0 +1,111 @@
+## 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>
+##
+
+"""
+    TestIntersectionChangeEdge.py
+    Unit test of SketchPlugin_IntersectionPoint class
+"""
+
+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"))
+SketchArc_1 = Sketch_1.addArc(0, 0, 13.75, -26.66341125962693, -24.98309575119448, 16.60857991180005, False)
+SketchArc_2 = Sketch_1.addArc(0, 0, 53.75, -26.66341125962693, -53.75, -26.66341125962693, False)
+SketchLine_1 = Sketch_1.addLine(13.75, -26.66341125962693, 53.75, -26.66341125962693)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchArc_1.startPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchArc_2.startPoint(), SketchLine_1.endPoint())
+SketchLine_2 = Sketch_1.addLine(-24.98309575119448, 16.60857991180005, -53.75, -26.66341125962693)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchArc_2.endPoint(), SketchLine_2.endPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchArc_2.center(), SketchArc_1.center())
+SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_2.result(), SketchArc_1.results()[1])
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_1.result())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchArc_1.results()[1], 30)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchArc_2.center(), SketchAPI_Point(SketchPoint_1).coordinates())
+SketchConstraintRadius_2 = Sketch_1.setRadius(SketchArc_2.results()[1], 60)
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 40)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchArc_1_2r-SketchArc_2_2f-SketchLine_1f-SketchLine_2r")], model.selection(), 50, 50)
+Filling_1 = model.addFilling(Part_1_doc, [model.selection("EDGE", "Extrusion_1_1/Generated_Face_4&Extrusion_1_1/From_Face_1"), model.selection("EDGE", "Extrusion_1_1/Generated_Face_3&Extrusion_1_1/To_Face_1")])
+Plane_4 = model.addPlane(Part_1_doc, model.selection("EDGE", "Extrusion_1_1/Generated_Face_4&Extrusion_1_1/To_Face_1"), model.selection("VERTEX", "Extrusion_1_1/Generated_Face_2&Extrusion_1_1/Generated_Face_1&Extrusion_1_1/From_Face_1"), False)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Plane_1"))
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "Sketch_1/Vertex-SketchArc_1-SketchArc_2-SketchProjection_1-SketchPoint_1"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchLine_3 = Sketch_2.addLine(-70, 60, 70, 60)
+SketchLine_4 = Sketch_2.addLine(70, 60, 70, -60)
+SketchLine_5 = Sketch_2.addLine(70, -60, -70, -60)
+SketchLine_6 = Sketch_2.addLine(-70, -60, -70, 60)
+SketchConstraintCoincidence_8 = Sketch_2.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_9 = Sketch_2.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintCoincidence_10 = Sketch_2.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_11 = Sketch_2.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_2 = Sketch_2.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_4.result())
+SketchConstraintHorizontal_3 = Sketch_2.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_2 = Sketch_2.setVertical(SketchLine_6.result())
+SketchConstraintDistance_1 = Sketch_2.setDistance(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_5.result(), 60, True)
+SketchConstraintDistance_2 = Sketch_2.setDistance(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_3.result(), 60, True)
+SketchConstraintDistance_3 = Sketch_2.setDistance(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_4.result(), 70, True)
+SketchConstraintDistance_4 = Sketch_2.setDistance(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_6.result(), 70, True)
+model.do()
+Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_3r-SketchLine_4r-SketchLine_5r-SketchLine_6r")])
+Intersection_1 = model.addIntersection(Part_1_doc, [model.selection("FACE", "Filling_1_1")], [model.selection("FACE", "Face_1_1")])
+
+# set different edges used for intersection and check reference data: number of intersection points and their coordinates
+REF_DATA = [("Sketch_1/Edge-SketchArc_1_2", 1, [[30, 0]]),
+            ("Sketch_1/Edge-SketchLine_2", 1, [[-36.024358588836, 0]]),
+            ("Sketch_1/Edge-SketchArc_2_2", 2, [[60, 0], [-60, 0]]),
+            ("Sketch_1/Edge-SketchLine_1", 0, []),
+            ("Extrusion_1_1/Generated_Face_1&Extrusion_1_1/To_Face_1", 1, [[30, 50]]),
+            ("Extrusion_1_1/Generated_Face_2&Extrusion_1_1/To_Face_1", 1, [[-36.024358588836, 50]]),
+            ("Extrusion_1_1/Generated_Face_3&Extrusion_1_1/To_Face_1", 2, [[60, 50], [-60, 50]]),
+            ("Extrusion_1_1/Generated_Face_4&Extrusion_1_1/To_Face_1", 0, []),
+            ("Intersection_1_1", 2, [[-0.0515933488223, -11.6181750315], [41.6438615258, -11.6181750315]])]
+TOLERANCE = 1.e-7
+
+Sketch_3 = model.addSketch(Part_1_doc, model.defaultPlane("XOZ"))
+SketchIntersectionPoint_1 = Sketch_3.addIntersectionPoint(model.selection("EDGE", "Intersection_1_1"), True)
+model.do()
+
+for ref in REF_DATA:
+    SketchIntersectionPoint_1.setByExternalEdgeName(ref[0])
+    aPoints = SketchIntersectionPoint_1.intersectionPoints()
+    assert(len(aPoints) == ref[1]), "Intersection with edge {} produce {} points, expected {}".format(ref[0], aPoints.size(), ref[1])
+    # check coordinates
+    ind = 0
+    for p in aPoints:
+        pnt = SketchAPI_Point(p)
+        delta = (pnt.coordinates().x() - ref[2][ind][0])**2 + (pnt.coordinates().y() - ref[2][ind][1])**2
+        assert(delta < TOLERANCE * TOLERANCE), "Wrong coordinates of intersection with edge {}: ({}, {}) != expected ({}, {})".format(ref[0], pnt.coordinates().x(), pnt.coordinates().y(), ref[2][ind][0], ref[2][ind][1])
+        ind += 1
+model.do()
+
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestIntersectionWithCircle.py b/src/SketchPlugin/Test/TestIntersectionWithCircle.py
new file mode 100644 (file)
index 0000000..47237e8
--- /dev/null
@@ -0,0 +1,158 @@
+## 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>
+##
+
+"""
+    TestIntersectionWithCircle.py
+    Unit test of SketchPlugin_IntersectionPoint class
+"""
+
+from GeomAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamD = model.addParameter(Part_1_doc, "D", "12")
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(21.25834261322606, 21.25834261322606, 0, 0)
+SketchLine_2 = Sketch_1.addLine(0, 0, 50, -4.088404617988313e-023)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(50, -4.088404617988313e-023, 50, 50)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchLine_4 = Sketch_1.addLine(50, 50, 28.74165738677394, 28.74165738677394)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_2.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_3.result())
+SketchConstraintCollinear_1 = Sketch_1.setCollinear(SketchLine_4.result(), SketchLine_1.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_3.result())
+SketchConstraintEqual_2 = Sketch_1.setEqual(SketchLine_1.result(), SketchLine_4.result())
+SketchArc_1 = Sketch_1.addArc(31, 19, 21.25834261322606, 21.25834261322606, 28.74165738677394, 28.74165738677394, False)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchArc_1.startPoint())
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchArc_1.endPoint())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_2.result(), 50)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchArc_1.results()[1], 10)
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchArc_1.center(), SketchLine_2.result(), 19, True)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1f-SketchLine_2f-SketchLine_3f-SketchLine_4f-SketchArc_1_2r")], model.selection(), 10, 0)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "Extrusion_1_1/Generated_Face_4"), "D", True)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Plane_1"))
+SketchIntersectionPoint_1 = Sketch_2.addIntersectionPoint(model.selection("EDGE", "Extrusion_1_1/Generated_Face_1&Extrusion_1_1/To_Face_1"), True)
+SketchIntersectionPoint_1.result().setName("SketchIntersectionPoint_1")
+[SketchPoint_2, SketchPoint_3] = SketchIntersectionPoint_1.intersectionPoints()
+SketchCircle_1 = Sketch_2.addCircle(38.14142842854285, 10, 1)
+SketchConstraintCoincidence_7 = Sketch_2.setCoincident(SketchCircle_1.center(), SketchAPI_Point(SketchPoint_2).coordinates())
+SketchConstraintRadius_2 = Sketch_2.setRadius(SketchCircle_1.results()[1], 1)
+SketchLine_5 = Sketch_2.addLine(23.85857157145715, 10.5, 23.85857157145715, 5)
+SketchLine_6 = Sketch_2.addLine(22.85857157145715, 6, 22.85857157145715, 10.5)
+SketchLine_7 = Sketch_2.addLine(22.85857157145715, 10.5, 23.85857157145715, 10.5)
+SketchConstraintCoincidence_8 = Sketch_2.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint())
+SketchConstraintCoincidence_9 = Sketch_2.setCoincident(SketchLine_5.startPoint(), SketchLine_7.endPoint())
+SketchConstraintCoincidence_10 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchLine_5.result())
+SketchConstraintVertical_2 = Sketch_2.setVertical(SketchLine_5.result())
+SketchConstraintVertical_3 = Sketch_2.setVertical(SketchLine_6.result())
+SketchConstraintHorizontal_2 = Sketch_2.setHorizontal(SketchLine_7.result())
+SketchConstraintDistanceHorizontal_1 = Sketch_2.setHorizontalDistance(SketchLine_6.endPoint(), SketchLine_5.startPoint(), 1)
+SketchConstraintDistanceVertical_1 = Sketch_2.setVerticalDistance(SketchLine_5.startPoint(), SketchAPI_Point(SketchPoint_3).coordinates(), 0.5)
+SketchArc_2 = Sketch_2.addArc(23.85857157145715, 6, 23.85857157145715, 5, 22.85857157145715, 6, True)
+SketchConstraintCoincidence_11 = Sketch_2.setCoincident(SketchLine_5.result(), SketchArc_2.center())
+SketchConstraintCoincidence_12 = Sketch_2.setCoincident(SketchLine_5.endPoint(), SketchArc_2.startPoint())
+SketchConstraintCoincidence_13 = Sketch_2.setCoincident(SketchArc_2.endPoint(), SketchLine_6.startPoint())
+SketchConstraintDistanceVertical_2 = Sketch_2.setVerticalDistance(SketchAPI_Point(SketchPoint_3).coordinates(), SketchArc_2.startPoint(), 5)
+SketchConstraintTangent_1 = Sketch_2.setTangent(SketchArc_2.results()[1], SketchLine_6.result())
+model.do()
+RevolutionCut_1 = model.addRevolutionCut(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_5r-SketchLine_6r-SketchLine_7r-SketchArc_2_2f")], model.selection("EDGE", "Sketch_2/Edge-SketchLine_5"), 360, 0, [model.selection("SOLID", "Extrusion_1_1")])
+ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchCircle_1_2f")], model.selection(), 5, 5, [model.selection("SOLID", "RevolutionCut_1_1")])
+model.do()
+
+assert(SketchIntersectionPoint_1.feature().results().size() == 2)
+
+model.testNbResults(ExtrusionCut_1, 1)
+model.testNbSubResults(ExtrusionCut_1, [0])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.FACE, [13])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.EDGE, [66])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.VERTEX, [132])
+model.testResultsVolumes(ExtrusionCut_1, [9451.611727849665840039961040020])
+
+# change parameter and check intersection validity
+ParamD.setValue(15)
+model.do();
+
+assert(SketchIntersectionPoint_1.feature().results().size() == 2)
+
+model.testNbResults(ExtrusionCut_1, 1)
+model.testNbSubResults(ExtrusionCut_1, [0])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.FACE, [14])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.EDGE, [74])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.VERTEX, [148])
+model.testResultsVolumes(ExtrusionCut_1, [9451.152473626798382611013948917])
+
+# change parameter and check intersection validity
+ParamD.setValue(19)
+model.do();
+
+assert(SketchIntersectionPoint_1.feature().results().size() == 2)
+
+model.testNbResults(ExtrusionCut_1, 1)
+model.testNbSubResults(ExtrusionCut_1, [0])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.FACE, [14])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.EDGE, [74])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.VERTEX, [148])
+model.testResultsVolumes(ExtrusionCut_1, [9448.046243665688962209969758987])
+
+# change parameter and check intersection validity
+ParamD.setValue(23)
+model.do();
+
+assert(SketchIntersectionPoint_1.feature().results().size() == 1)
+
+model.testNbResults(ExtrusionCut_1, 1)
+model.testNbSubResults(ExtrusionCut_1, [0])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.FACE, [10])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.EDGE, [50])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.VERTEX, [100])
+model.testResultsVolumes(ExtrusionCut_1, [9458.635858820198336616158485413])
+
+# change parameter and check intersection validity
+ParamD.setValue(29)
+model.do();
+
+assert(SketchIntersectionPoint_1.feature().results().size() == 1)
+
+model.testNbResults(ExtrusionCut_1, 1)
+model.testNbSubResults(ExtrusionCut_1, [0])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.FACE, [9])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.EDGE, [42])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.VERTEX, [84])
+model.testResultsVolumes(ExtrusionCut_1, [9463.846034357124153757467865944])
+
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestIntersectionWithLine.py b/src/SketchPlugin/Test/TestIntersectionWithLine.py
new file mode 100644 (file)
index 0000000..a6b163c
--- /dev/null
@@ -0,0 +1,130 @@
+## 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>
+##
+
+"""
+    TestIntersectionWithLine.py
+    Unit test of SketchPlugin_IntersectionPoint class
+"""
+
+from GeomAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamD = model.addParameter(Part_1_doc, "D", "40")
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(0, 0, 50, 0)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchPoint_1.result())
+SketchLine_2 = Sketch_1.addLine(50, 0, 50, 10)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(50, 10, 40, 10)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchLine_4 = Sketch_1.addLine(40, 10, 40, 30)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchLine_5 = Sketch_1.addLine(40, 30, 50, 30)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchLine_6 = Sketch_1.addLine(50, 30, 50, 40)
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchLine_7 = Sketch_1.addLine(50, 40, 0, 50)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint())
+SketchLine_8 = Sketch_1.addLine(0, 50, 0, 0)
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint())
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_8.endPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_8.result())
+SketchConstraintCollinear_1 = Sketch_1.setCollinear(SketchLine_2.result(), SketchLine_6.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_6.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 50)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_8.result(), 50)
+SketchConstraintLength_3 = Sketch_1.setLength(SketchLine_2.result(), 10)
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_1.startPoint(), SketchLine_4.startPoint(), "D")
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchLine_1.endPoint(), SketchLine_7.startPoint(), 40, True)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1f-SketchLine_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f-SketchLine_7f-SketchLine_8f")], model.selection(), 10, 0)
+ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [], model.selection(), 5, 5, [model.selection("SOLID", "Extrusion_1_1")])
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/Generated_Face_5"))
+SketchIntersectionPoint_1 = Sketch_2.addIntersectionPoint(model.selection("EDGE", "Extrusion_1_1/Generated_Face_2&Extrusion_1_1/To_Face_1"))
+[SketchPoint_2] = SketchIntersectionPoint_1.intersectionPoints()
+SketchCircle_1 = Sketch_2.addCircle(40, 10, 5)
+SketchConstraintCoincidence_10 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_2.setRadius(SketchCircle_1.results()[1], 5)
+ExtrusionCut_1.setNestedSketch(Sketch_2)
+model.do()
+
+assert(SketchIntersectionPoint_1.feature().results().empty() == False)
+
+model.testNbResults(ExtrusionCut_1, 1)
+model.testNbSubResults(ExtrusionCut_1, [0])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.FACE, [13])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.EDGE, [66])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.VERTEX, [132])
+model.testResultsVolumes(ExtrusionCut_1, [20303.650459150881943060085177422])
+
+# change parameter and check intersection validity
+ParamD.setValue(35)
+model.do();
+
+assert(SketchIntersectionPoint_1.feature().results().empty() == False)
+
+model.testNbResults(ExtrusionCut_1, 1)
+model.testNbSubResults(ExtrusionCut_1, [0])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.FACE, [13])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.EDGE, [66])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.VERTEX, [132])
+model.testResultsVolumes(ExtrusionCut_1, [19303.650459150881943060085177422])
+
+# change parameter and check the intersection is not valid now
+ParamD.setValue(60)
+model.do();
+assert(SketchIntersectionPoint_1.feature().results().empty() == True)
+
+# change parameter to initial value and check intersection validity
+ParamD.setValue(40)
+model.do();
+# coincidence of circle center and intersection point is lost on previous step, restore it
+[SketchPoint_2] = SketchIntersectionPoint_1.intersectionPoints()
+Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchCircle_1.center())
+model.do()
+
+assert(SketchIntersectionPoint_1.feature().results().empty() == False)
+
+model.testNbResults(ExtrusionCut_1, 1)
+model.testNbSubResults(ExtrusionCut_1, [0])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.FACE, [13])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.EDGE, [66])
+model.testNbSubShapes(ExtrusionCut_1, GeomAPI_Shape.VERTEX, [132])
+model.testResultsVolumes(ExtrusionCut_1, [20303.650459150881943060085177422])
+
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestIntersectionWithSpline.py b/src/SketchPlugin/Test/TestIntersectionWithSpline.py
new file mode 100644 (file)
index 0000000..9fffb54
--- /dev/null
@@ -0,0 +1,151 @@
+## 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>
+##
+
+"""
+    TestIntersectionWithSpline.py
+    Unit test of SketchPlugin_IntersectionPoint class
+"""
+
+from GeomAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamD = model.addParameter(Part_1_doc, "D", "5")
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchArc_1 = Sketch_1.addArc(-20, 0, -12.51615726667241, -49.43674845641531, -67.51615726667241, 15.56325154358469, False)
+SketchLine_1 = Sketch_1.addLine(-67.51615726667241, 15.56325154358469, -12.51615726667241, 15.56325154358469)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_1.startPoint())
+SketchLine_2 = Sketch_1.addLine(-12.51615726667241, 15.56325154358469, -12.51615726667241, -49.43674845641531)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchArc_1.startPoint(), SketchLine_2.endPoint())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_3 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_3.result())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchArc_1.results()[1], 50)
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_2.result(), 65)
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchArc_1.center(), SketchAPI_Line(SketchLine_3).startPoint(), 20)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_1.result(), 55)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchArc_1_2f-SketchLine_1f-SketchLine_2f")], model.selection(), 25, 25)
+Filling_1 = model.addFilling(Part_1_doc, [model.selection("EDGE", "Extrusion_1_1/Generated_Face_1&Extrusion_1_1/From_Face_1"), model.selection("EDGE", "Extrusion_1_1/Generated_Face_3&Extrusion_1_1/To_Face_1")])
+Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchArc_1_2f-SketchLine_1f-SketchLine_2f")])
+Intersection_1 = model.addIntersection(Part_1_doc, [model.selection("FACE", "Filling_1_1")], [model.selection("FACE", "Face_1_1")])
+Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_4 = Sketch_2.addLine(-34.11217447219568, 40, 20, -37.28019411294778)
+SketchLine_5 = Sketch_2.addLine(20, -37.28019411294778, 20, 40)
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchLine_6 = Sketch_2.addLine(20, 40, -34.11217447219568, 40)
+SketchConstraintCoincidence_6 = Sketch_2.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintCoincidence_7 = Sketch_2.setCoincident(SketchLine_4.startPoint(), SketchLine_6.endPoint())
+SketchConstraintVertical_2 = Sketch_2.setVertical(SketchLine_5.result())
+SketchConstraintHorizontal_2 = Sketch_2.setHorizontal(SketchLine_6.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_2.createdFeature()
+SketchConstraintDistance_1 = Sketch_2.setDistance(SketchAPI_Point(SketchPoint_1).coordinates(), SketchLine_5.result(), 20, True)
+SketchConstraintDistance_2 = Sketch_2.setDistance(SketchAPI_Point(SketchPoint_1).coordinates(), SketchLine_6.result(), 40, True)
+SketchConstraintDistance_3 = Sketch_2.setDistance(SketchAPI_Point(SketchPoint_1).coordinates(), SketchLine_4.result(), "D", True)
+SketchConstraintAngle_1 = Sketch_2.setAngle(SketchLine_5.result(), SketchLine_4.result(), 35)
+model.do()
+Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_4f-SketchLine_5f-SketchLine_6f")], model.selection(), 10, 10)
+ExtrusionFuse_1 = model.addExtrusionFuse(Part_1_doc, [], model.selection(), 10, 0, [model.selection("SOLID", "Extrusion_2_1")])
+Sketch_3 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_2_1/Generated_Face_3"))
+SketchIntersectionPoint_1 = Sketch_3.addIntersectionPoint(model.selection("EDGE", "Intersection_1_1"), False)
+[SketchPoint_2, SketchPoint_3] = SketchIntersectionPoint_1.intersectionPoints()
+SketchCircle_1 = Sketch_3.addCircle(-33.22640570408476, 0, 5)
+SketchConstraintCoincidence_8 = Sketch_3.setCoincident(SketchCircle_1.center(), SketchAPI_Point(SketchPoint_2).coordinates())
+SketchCircle_2 = Sketch_3.addCircle(21.47998214403209, 0, 5)
+SketchConstraintCoincidence_9 = Sketch_3.setCoincident(SketchCircle_2.center(), SketchAPI_Point(SketchPoint_3).coordinates())
+SketchConstraintEqual_1 = Sketch_3.setEqual(SketchCircle_1.results()[1], SketchCircle_2.results()[1])
+SketchConstraintRadius_2 = Sketch_3.setRadius(SketchCircle_1.results()[1], 5)
+ExtrusionFuse_1.setNestedSketch(Sketch_3)
+model.do()
+
+assert(SketchIntersectionPoint_1.feature().results().size() == 2)
+
+model.testNbResults(ExtrusionFuse_1, 1)
+model.testNbSubResults(ExtrusionFuse_1, [0])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.FACE, [9])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.EDGE, [30])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.VERTEX, [60])
+model.testResultsVolumes(ExtrusionFuse_1, [43388.789797644698410294950008392])
+
+# change parameter and check intersection validity
+ParamD.setValue(15)
+model.do();
+
+assert(SketchIntersectionPoint_1.feature().results().size() == 2)
+
+model.testNbResults(ExtrusionFuse_1, 1)
+model.testNbSubResults(ExtrusionFuse_1, [0])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.FACE, [9])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.EDGE, [30])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.VERTEX, [60])
+model.testResultsVolumes(ExtrusionFuse_1, [64385.484780074148147832602262497])
+
+# change parameter and check single intersection
+ParamD.setValue(25)
+model.do();
+
+assert(SketchIntersectionPoint_1.feature().results().size() == 1)
+
+model.testNbResults(ExtrusionFuse_1, 1)
+model.testNbSubResults(ExtrusionFuse_1, [1])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.FACE, [11])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.EDGE, [42])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.VERTEX, [84])
+model.testResultsVolumes(ExtrusionFuse_1, [89224.362837261898675933480262756])
+
+# change parameter and check intersection is NOT valid now
+ParamD.setValue(50)
+model.do();
+
+assert(SketchIntersectionPoint_1.feature().results().empty())
+
+# revert parameter to original value and reset coincidences
+ParamD.setValue(5)
+model.do()
+[SketchPoint_2, SketchPoint_3] = SketchIntersectionPoint_1.intersectionPoints()
+SketchConstraintCoincidence_8 = Sketch_3.setCoincident(SketchCircle_1.center(), SketchAPI_Point(SketchPoint_2).coordinates())
+SketchConstraintCoincidence_9 = Sketch_3.setCoincident(SketchCircle_2.center(), SketchAPI_Point(SketchPoint_3).coordinates())
+model.do()
+
+assert(SketchIntersectionPoint_1.feature().results().size() == 2)
+
+model.testNbResults(ExtrusionFuse_1, 1)
+model.testNbSubResults(ExtrusionFuse_1, [0])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.FACE, [9])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.EDGE, [30])
+model.testNbSubShapes(ExtrusionFuse_1, GeomAPI_Shape.VERTEX, [60])
+model.testResultsVolumes(ExtrusionFuse_1, [43388.789797644698410294950008392])
+
+model.end()
+
+assert(model.checkPythonDump())
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())
diff --git a/src/SketchPlugin/icons/intersection.png b/src/SketchPlugin/icons/intersection.png
new file mode 100644 (file)
index 0000000..5dc3448
Binary files /dev/null and b/src/SketchPlugin/icons/intersection.png differ
index 2ebc0bf3ed2b211c2c901f6cfdaeb91e955e7208..9f28795bcd5bec0ed718a2effb20e081cbd30895 100644 (file)
@@ -451,24 +451,6 @@ email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com
 </excluded>
 
     <group id="Projection">
-      <!-- Intersection Point -->
-      <!-- feature
-        id="SketchIntersectionPoint"
-        title="Intersection Point"
-        tooltip="Create intersection point"
-        icon="icons/Sketch/intersection_point.png">
-        <sketch_shape_selector
-              id="ExternalLine"
-              label="Edge"
-              tooltip="Select external line."
-              shape_types="edge"
-              use_external="false"
-              use_sketch_plane="false">
-          <validator id="GeomValidators_ShapeType" parameters="line"/>
-          <validator id="SketchPlugin_IntersectionValidator"/>
-        </sketch_shape_selector>
-      </feature -->
-
       <!-- Projected feature -->
       <feature
         id="SketchProjection"
@@ -488,6 +470,26 @@ email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com
         <boolvalue id="IncludeToResult" label="Include into the sketch result" default="true" tooltip="Include projected feature into the sketch result"/>
         <validator id="PartSet_ProjectionSelection"/>
       </feature>
+
+      <!-- Intersection Point -->
+      <feature
+        id="SketchIntersectionPoint"
+        title="Intersection"
+        tooltip="Intersect edge with sketch plane"
+        icon="icons/Sketch/intersection.png">
+        <sketch_shape_selector
+              id="ExternalFeature"
+              label="Object"
+              tooltip="Select external edge."
+              shape_types="edge"
+              use_external="true"
+              can_create_external="false"
+              use_sketch_plane="false">
+          <validator id="SketchPlugin_IntersectionValidator"/>
+        </sketch_shape_selector>
+        <boolvalue id="IncludeToResult" label="Include into the sketch result" default="true" tooltip="Include projected feature into the sketch result"/>
+        <validator id="PartSet_IntersectionSelection"/>
+      </feature>
     </group>
 
     <group id="Replication">
@@ -607,14 +609,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
index 2f92a30f22ced17f0157ee800181c593615f0383..051402c908ebb3bff6077ce63741c944129af17c 100644 (file)
@@ -53,7 +53,7 @@
 /// Returns ResultPart object if the given object is a Part feature
 /// Otherwise returns NULL
 
-#define SELECTABLE_COLOR QColor(80, 80, 80)
+#define SELECTABLE_COLOR QColor(110, 110, 110)
 #define DISABLED_COLOR QColor(200, 200, 200)
 
 
index 883d3f2f3798f8e69e35340b578604e0a6d32b26..d256e56dba733e6704d73550683ba4d21846cb25 100644 (file)
@@ -545,6 +545,20 @@ void XGUI_ObjectsBrowser::setObjectsSelected(const QObjectPtrList& theObjects)
   }
 }
 
+//***************************************************
+void XGUI_ObjectsBrowser::ensureVisible(const ObjectPtr theObject)
+{
+  QModelIndex aIndex = myDocModel->objectIndex(theObject);
+  if (aIndex.isValid())  {
+    QModelIndex aParent = aIndex.parent();
+    while (aParent.isValid()) {
+      myTreeView->expand(aParent);
+      aParent = aParent.parent();
+    }
+    myTreeView->scrollTo(aIndex);
+  }
+}
+
 //***************************************************
 void XGUI_ObjectsBrowser::clearContent()
 {
index c5eb366de8d07e9ea75208a2fd7b07f8742c5cfe..f83ddfcadfc13583265d06c6036755b9ab053ab9 100644 (file)
@@ -168,6 +168,10 @@ Q_OBJECT
   /// \param theObjects list of objects to select
   void setObjectsSelected(const QObjectPtrList& theObjects);
 
+  //! Scroll TreeView to make given object visible
+  //! \param theObject object to make it visible
+  void ensureVisible(const ObjectPtr theObject);
+
   //! Returns currently selected indexes
   QModelIndexList selectedIndexes() const
   {
index 34cbc8b5fc5d17ea676a6141d6f542ecb433bc27..9ca33837f0da0fc847c1073470a3d51066038bd9 100755 (executable)
@@ -2601,6 +2601,7 @@ void XGUI_Workshop::highlightResults(const QObjectPtrList& theObjects)
 {
   FeaturePtr aFeature;
   QObjectPtrList aSelList = theObjects;
+  QObjectPtrList aNewSel;
   bool aHasHidden = false;
   foreach(ObjectPtr aObj, theObjects) {
     aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
@@ -2611,6 +2612,7 @@ void XGUI_Workshop::highlightResults(const QObjectPtrList& theObjects)
       for(aIt = aResults.cbegin(); aIt != aResults.cend(); aIt++) {
         aHasHidden |= (*aIt)->isConcealed();
         aSelList.append(*aIt);
+        aNewSel.append(*aIt);
       }
     }
   }
@@ -2619,6 +2621,7 @@ void XGUI_Workshop::highlightResults(const QObjectPtrList& theObjects)
     bool aBlocked = objectBrowser()->blockSignals(true);
     objectBrowser()->setObjectsSelected(aSelList);
     objectBrowser()->blockSignals(aBlocked);
+    objectBrowser()->ensureVisible(aNewSel.first());
   }
   if (aHasHidden)
     QMessageBox::information(desktop(), tr("Find results"),
@@ -2630,6 +2633,7 @@ void XGUI_Workshop::highlightFeature(const QObjectPtrList& theObjects)
 {
   ResultPtr aResult;
   QObjectPtrList aSelList = theObjects;
+  QObjectPtrList aNewSel;
   FeaturePtr aFeature;
   foreach(ObjectPtr aObj, theObjects) {
     aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aObj);
@@ -2637,6 +2641,7 @@ void XGUI_Workshop::highlightFeature(const QObjectPtrList& theObjects)
       aFeature = ModelAPI_Feature::feature(aResult);
       if (aFeature.get()) {
         aSelList.append(aFeature);
+        aNewSel.append(aFeature);
       }
     }
   }
@@ -2645,6 +2650,7 @@ void XGUI_Workshop::highlightFeature(const QObjectPtrList& theObjects)
     bool aBlocked = objectBrowser()->blockSignals(true);
     objectBrowser()->setObjectsSelected(aSelList);
     objectBrowser()->blockSignals(aBlocked);
+    objectBrowser()->ensureVisible(aNewSel.first());
   }
 }
 
index 161a53449815671c6ba03969bc2e4813b419bd28..d7e0195fac79fdf93d704544ffdd094f9df200f4 100644 (file)
@@ -104,26 +104,26 @@ Face_1 = model.addFace(Part_1_doc, [model.selection("WIRE", "Sketch_2/Wire-Sketc
 
 
 # Symmetries
-Symmetry_1 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("VERTEX", "PartSet/Origin"))
-Symmetry_2 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_2_1")], model.selection("VERTEX", "Point_1"))
-Symmetry_3 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_3_1")], model.selection("VERTEX", "Box_3_1/Front&Box_3_1/Right&Box_3_1/Top"))
-Symmetry_4 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_4_1")], model.selection("VERTEX", "Sketch_1/Vertex-SketchLine_1e"))
-Symmetry_5 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_5_1")], model.selection("VERTEX", "Vertex_1_1"))
-Symmetry_6 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_6_1")], model.selection("VERTEX", "InvalidName"))
-
-Symmetry_7 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_7_1")], model.selection("EDGE", "PartSet/OZ"))
-Symmetry_8 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_8_1")], model.selection("EDGE", "Axis_1"))
-Symmetry_9 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_9_1")], model.selection("EDGE", "Box_9_1/Front&Box_9_1/Top"))
-Symmetry_10 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_10_1")], model.selection("EDGE", "Sketch_1/Edge-SketchLine_1"))
-Symmetry_11 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_11_1")], model.selection("EDGE", "Edge_1_1"))
-Symmetry_12 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_12_1")], model.selection("EDGE", "InvalidName"))
-
-Symmetry_13 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_13_1")], model.selection("FACE", "PartSet/XOY"))
-Symmetry_14 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_14_1")], model.selection("FACE", "Plane_1"))
-Symmetry_15 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_15_1")], model.selection("FACE", "Box_15_1/Front"))
-Symmetry_16 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_16_1")], model.selection("FACE", "Face_1_1"))
-Symmetry_17 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Box_17_1")], model.selection("FACE", "InvalidName"))
-Symmetry_18 = model.addSymmetry(Part_1_doc, [model.selection("SHELL", "Extrusion_1_1")], model.selection("FACE", "Plane_2"))
+Symmetry_1 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("VERTEX", "PartSet/Origin"))
+Symmetry_2 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_2_1")], model.selection("VERTEX", "Point_1"))
+Symmetry_3 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_3_1")], model.selection("VERTEX", "Box_3_1/Front&Box_3_1/Right&Box_3_1/Top"))
+Symmetry_4 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_4_1")], model.selection("VERTEX", "Sketch_1/Vertex-SketchLine_1e"))
+Symmetry_5 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_5_1")], model.selection("VERTEX", "Vertex_1_1"))
+Symmetry_6 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_6_1")], model.selection("VERTEX", "InvalidName"))
+
+Symmetry_7 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_7_1")], model.selection("EDGE", "PartSet/OZ"))
+Symmetry_8 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_8_1")], model.selection("EDGE", "Axis_1"))
+Symmetry_9 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_9_1")], model.selection("EDGE", "Box_9_1/Front&Box_9_1/Top"))
+Symmetry_10 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_10_1")], model.selection("EDGE", "Sketch_1/Edge-SketchLine_1"))
+Symmetry_11 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_11_1")], model.selection("EDGE", "Edge_1_1"))
+Symmetry_12 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_12_1")], model.selection("EDGE", "InvalidName"))
+
+Symmetry_13 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_13_1")], model.selection("FACE", "PartSet/XOY"))
+Symmetry_14 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_14_1")], model.selection("FACE", "Plane_1"))
+Symmetry_15 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_15_1")], model.selection("FACE", "Box_15_1/Front"))
+Symmetry_16 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_16_1")], model.selection("FACE", "Face_1_1"))
+Symmetry_17 = model.addMirror(Part_1_doc, [model.selection("SOLID", "Box_17_1")], model.selection("FACE", "InvalidName"))
+Symmetry_18 = model.addMirror(Part_1_doc, [model.selection("SHELL", "Extrusion_1_1")], model.selection("FACE", "Plane_2"))
 
 model.do()
 model.end()
@@ -132,87 +132,87 @@ model.end()
 from GeomAPI import GeomAPI_Shape
 
 model.testNbResults(Symmetry_1, 1)
-model.testNbSubResults(Symmetry_1, [0])
-model.testNbSubShapes(Symmetry_1, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_1, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_1, [2])
+model.testNbSubShapes(Symmetry_1, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_1, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_1, model, Part_1_doc)
 
 model.testNbResults(Symmetry_2, 1)
-model.testNbSubResults(Symmetry_2, [0])
-model.testNbSubShapes(Symmetry_2, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_2, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_2, [2])
+model.testNbSubShapes(Symmetry_2, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_2, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_2, model, Part_1_doc)
 
 model.testNbResults(Symmetry_3, 1)
-model.testNbSubResults(Symmetry_3, [0])
-model.testNbSubShapes(Symmetry_3, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_3, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_3, [2])
+model.testNbSubShapes(Symmetry_3, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_3, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_3, model, Part_1_doc)
 
 model.testNbResults(Symmetry_4, 1)
-model.testNbSubResults(Symmetry_4, [0])
-model.testNbSubShapes(Symmetry_4, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_4, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_4, [2])
+model.testNbSubShapes(Symmetry_4, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_4, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_4, model, Part_1_doc)
 
 model.testNbResults(Symmetry_5, 1)
-model.testNbSubResults(Symmetry_5, [0])
-model.testNbSubShapes(Symmetry_5, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_5, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_5, [2])
+model.testNbSubShapes(Symmetry_5, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_5, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_5, model, Part_1_doc)
 
 model.testNbResults(Symmetry_7, 1)
-model.testNbSubResults(Symmetry_7, [0])
-model.testNbSubShapes(Symmetry_7, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_7, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_7, [2])
+model.testNbSubShapes(Symmetry_7, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_7, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_7, model, Part_1_doc)
 
 model.testNbResults(Symmetry_8, 1)
-model.testNbSubResults(Symmetry_8, [0])
-model.testNbSubShapes(Symmetry_8, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_8, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_8, [2])
+model.testNbSubShapes(Symmetry_8, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_8, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_8, model, Part_1_doc)
 
 model.testNbResults(Symmetry_9, 1)
-model.testNbSubResults(Symmetry_9, [0])
-model.testNbSubShapes(Symmetry_9, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_9, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_9, [2])
+model.testNbSubShapes(Symmetry_9, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_9, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_9, model, Part_1_doc)
 
 model.testNbResults(Symmetry_10, 1)
-model.testNbSubResults(Symmetry_10, [0])
-model.testNbSubShapes(Symmetry_10, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_10, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_10, [2])
+model.testNbSubShapes(Symmetry_10, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_10, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_10, model, Part_1_doc)
 
 model.testNbResults(Symmetry_11, 1)
-model.testNbSubResults(Symmetry_11, [0])
-model.testNbSubShapes(Symmetry_11, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_11, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_11, [2])
+model.testNbSubShapes(Symmetry_11, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_11, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_11, model, Part_1_doc)
 
 model.testNbResults(Symmetry_13, 1)
-model.testNbSubResults(Symmetry_13, [0])
-model.testNbSubShapes(Symmetry_13, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_13, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_13, [2])
+model.testNbSubShapes(Symmetry_13, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_13, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_13, model, Part_1_doc)
 
 model.testNbResults(Symmetry_14, 1)
-model.testNbSubResults(Symmetry_14, [0])
-model.testNbSubShapes(Symmetry_14, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_14, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_14, [2])
+model.testNbSubShapes(Symmetry_14, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_14, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_14, model, Part_1_doc)
 
 model.testNbResults(Symmetry_15, 1)
-model.testNbSubResults(Symmetry_15, [0])
-model.testNbSubShapes(Symmetry_15, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_15, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_15, [2])
+model.testNbSubShapes(Symmetry_15, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_15, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_15, model, Part_1_doc)
 
 model.testNbResults(Symmetry_16, 1)
-model.testNbSubResults(Symmetry_16, [0])
-model.testNbSubShapes(Symmetry_16, GeomAPI_Shape.SOLID, [1])
-model.testNbSubShapes(Symmetry_16, GeomAPI_Shape.FACE, [6])
+model.testNbSubResults(Symmetry_16, [2])
+model.testNbSubShapes(Symmetry_16, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Symmetry_16, GeomAPI_Shape.FACE, [12])
 model.testHaveNamingFaces(Symmetry_16, model, Part_1_doc)
 
 model.testNbResults(Symmetry_6, 0)
@@ -224,10 +224,9 @@ assert(Symmetry_12.feature().error() == 'Attribute "axis_object" is not initiali
 model.testNbResults(Symmetry_17, 0)
 assert(Symmetry_17.feature().error() == 'Attribute "plane_object" is not initialized.')
 
-# To uncomment when #2046 will be performed
-#model.testNbResults(Symmetry_18, 1)
-#model.testNbSubResults(Symmetry_18, [0])
-#model.testNbSubShapes(Symmetry_18, GeomAPI_Shape.SOLID, [0])
-#model.testNbSubShapes(Symmetry_18, GeomAPI_Shape.SHELL, [1])
-#model.testNbSubShapes(Symmetry_18, GeomAPI_Shape.FACE, [5])
-#model.testHaveNamingFaces(Symmetry_18, model, Part_1_doc)
\ No newline at end of file
+model.testNbResults(Symmetry_18, 1)
+model.testNbSubResults(Symmetry_18, [2])
+model.testNbSubShapes(Symmetry_18, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Symmetry_18, GeomAPI_Shape.SHELL, [2])
+model.testNbSubShapes(Symmetry_18, GeomAPI_Shape.FACE, [10])
+model.testHaveNamingFaces(Symmetry_18, model, Part_1_doc)