Salome HOME
Implementation of macro-feature Rectangle
authorazv <azv@opencascade.com>
Fri, 5 Feb 2016 08:29:47 +0000 (11:29 +0300)
committerdbv <dbv@opencascade.com>
Tue, 16 Feb 2016 14:04:36 +0000 (17:04 +0300)
13 files changed:
src/ModelAPI/ModelAPI.i
src/PartSet/PartSet_icons.qrc
src/PartSet/icons/rectangle.png [new file with mode: 0644]
src/PythonAddons/addons_Features.py
src/PythonAddons/addons_Features.xml
src/PythonAddons/macros/rectangle/__init__.py [new file with mode: 0644]
src/PythonAddons/macros/rectangle/feature.py [new file with mode: 0644]
src/PythonAddons/macros/rectangle/widget.xml [new file with mode: 0644]
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_Plugin.cpp
src/SketchPlugin/SketchPlugin_Sketch.cpp
src/SketchPlugin/Test/TestRectangle.py [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml

index 441cfc22d77f8399146f6b8bff0024a2c43a2a88..aaf61bdc477cd4f22bbd3fa891e1637cea9270ab 100644 (file)
@@ -23,6 +23,7 @@
 %include "std_string.i"
 %include "std_list.i"
 %include "std_shared_ptr.i"
+%include "std_set.i"
 
 // directors
 %feature("director") ModelAPI_Plugin;
 %template(ObjectList) std::list<std::shared_ptr<ModelAPI_Object> >;
 %template(ResultList) std::list<std::shared_ptr<ModelAPI_Result> >;
 %template(DocumentList) std::list<std::shared_ptr<ModelAPI_Document> >;
+// std::set -> []
+%template(AttributeSet) std::set<std::shared_ptr<ModelAPI_Attribute> >;
 
 // std::dynamic_pointer_cast
 template<class T1, class T2> std::shared_ptr<T1> shared_ptr_cast(std::shared_ptr<T2> theObject);
index 368893c2bdcac0ee83ef316aaab0b97ea90e4ded..30e6b1b354dee44d9b0012aed510a2a72d6e7735 100644 (file)
@@ -89,5 +89,6 @@
      <file>icons/plane_view.png</file>
      <file>icons/collinear.png</file>
      <file>icons/middlepoint.png</file>
+     <file>icons/rectangle.png</file>
  </qresource>
  </RCC>
diff --git a/src/PartSet/icons/rectangle.png b/src/PartSet/icons/rectangle.png
new file mode 100644 (file)
index 0000000..b98a991
Binary files /dev/null and b/src/PartSet/icons/rectangle.png differ
index 3fef980a8e2638086adb4f5c53d4310b8a042bd6..9499f21134c5aefef059bad27012e07f1a15ea45 100644 (file)
@@ -2,7 +2,8 @@
 """
 
 import ModelAPI
-from macros.box.feature      import BoxFeature
+from macros.box.feature       import BoxFeature
+from macros.rectangle.feature import SketchPlugin_Rectangle
 
 
 class PythonFeaturesPlugin(ModelAPI.ModelAPI_Plugin):
@@ -25,6 +26,9 @@ class PythonFeaturesPlugin(ModelAPI.ModelAPI_Plugin):
         if theFeatureID == BoxFeature.ID():
             aFeature = BoxFeature().__disown__()
 
+        elif theFeatureID == SketchPlugin_Rectangle.ID():
+            aFeature = SketchPlugin_Rectangle().__disown__()
+
         else:
             raise StandardError("No such feature %s" % theFeatureID)
 
index cc18bfb85b8e5246acac3a92c187639335a8d031..97ffbd109208bf5eb933ed0c2b79ea3dee9a9f18 100644 (file)
@@ -1,3 +1,4 @@
 <plugin>
   <source path="../addons/macros/box/widget.xml"/>
+  <source path="../addons/macros/rectangle/widget.xml"/>
 </plugin>
diff --git a/src/PythonAddons/macros/rectangle/__init__.py b/src/PythonAddons/macros/rectangle/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/PythonAddons/macros/rectangle/feature.py b/src/PythonAddons/macros/rectangle/feature.py
new file mode 100644 (file)
index 0000000..aaa6551
--- /dev/null
@@ -0,0 +1,150 @@
+"""
+Macro-feature to produce rectangle in the sketcher
+Author: Artem ZHIDKOV
+Copyright (C) 2016-20xx CEA/DEN, EDF R&D
+"""
+
+import model
+import ModelAPI
+import GeomDataAPI
+
+class SketchPlugin_Rectangle(model.Feature):
+    """
+    Implementation of rectangle creation.
+
+    It produced 2 horizontal lines and 2 vertical lines connected by coincidence constraints
+    """
+
+# Initializations
+
+    def __init__(self):
+        """x.__init__(...) initializes x; see x.__class__.__doc__ for signature"""
+        model.Feature.__init__(self)
+
+    @staticmethod
+    def ID():
+        """Rectangle feature kind."""
+        return "SketchRectangle"
+
+    @staticmethod
+    def START_ID():
+        """Returns ID of first corner."""
+        return "RectStartPoint"
+
+    @staticmethod
+    def END_ID():
+        """Returns ID of second corner."""
+        return "RectEndPoint"
+
+    @staticmethod
+    def AUXILIARY_ID():
+        """Returns whether the rectangle is accessory."""
+        return "Auxiliary"
+
+    @staticmethod
+    def LINES_LIST_ID():
+        """Returns ID of list containing lines created."""
+        return "RectangleList"
+
+    def getKind(self):
+        """Override Feature.getKind()"""
+        return SketchPlugin_Rectangle.ID()
+
+
+# Initialization of the rectangle
+
+    def initAttributes(self):
+        """Override Feature.initAttributes()"""
+        # Flag whether the rectangle is accessory
+        self.data().addAttribute(self.AUXILIARY_ID(), ModelAPI.ModelAPI_AttributeBoolean_typeId())
+        # Creating corners of the rectangle
+        self.data().addAttribute(self.START_ID(), GeomDataAPI.GeomDataAPI_Point2D_typeId())
+        self.data().addAttribute(self.END_ID(), GeomDataAPI.GeomDataAPI_Point2D_typeId())
+        # Creating list to store lines
+        self.data().addAttribute(self.LINES_LIST_ID(), ModelAPI.ModelAPI_AttributeRefList_typeId())
+        ModelAPI.ModelAPI_Session.get().validators().registerNotObligatory(self.getKind(), self.LINES_LIST_ID())
+
+    def isMacro(self):
+        """
+        Override Feature.isMacro().
+        Rectangle feature is macro: removes itself on the creation transaction finish.
+        """
+        return True
+
+# Edition of the rectangle
+
+    def execute(self):
+        # Retrieving list of already created lines
+        aLinesList = self.reflist(self.LINES_LIST_ID())
+        aNbLines = aLinesList.size()
+        if aNbLines == 0:
+            # Search the sketch containing this rectangle
+            self.__sketch = None
+            aRefs = self.data().refsToMe();
+            for iter in aRefs:
+                aFeature = ModelAPI.objectToFeature(iter.owner())
+                if aFeature.getKind() == "Sketch":
+                    self.__sketch = ModelAPI.featureToCompositeFeature(aFeature)
+                    break
+            # Create lines to compose the rectangle
+            for i in range (0, 4):
+                aLine = self.__sketch.addFeature("SketchLine")
+                aLinesList.append(aLine)
+            aNbLines = aLinesList.size()
+            # Create constraints to keep the rectangle
+            for i in range (0, aNbLines):
+                aLine = ModelAPI.objectToFeature(aLinesList.object(i))
+                # connect neighbor lines by coincidence
+                iPrev = i - 1
+                if iPrev < 0:
+                    iPrev = aNbLines - 1
+                aPrevLine = ModelAPI.objectToFeature(aLinesList.object(iPrev))
+                aCoincidence = self.__sketch.addFeature("SketchConstraintCoincidence")
+                aRefAttrA = aCoincidence.refattr("ConstraintEntityA")
+                aRefAttrB = aCoincidence.refattr("ConstraintEntityB")
+                aRefAttrA.setAttr(aPrevLine.attribute("EndPoint"))
+                aRefAttrB.setAttr(aLine.attribute("StartPoint"))
+            # Flags which show horizontal or vertical constraint is build for correponding line
+            self.__isHV = [False, False, False, False]
+        # Update coordinates of created lines
+        self.updateLines()
+        # Add horizontal and vertical constraint for the lines which already have result
+        for i in range (0, aNbLines):
+            if self.__isHV[i]:
+                continue
+            aLine = ModelAPI.objectToFeature(aLinesList.object(i))
+            aLineResult = aLine.lastResult()
+            if aLineResult is None:
+                continue
+            aHVName = "SketchConstraintHorizontal"
+            if i % 2 == 1:
+                aHVName = "SketchConstraintVertical"
+            aHVConstraint = self.__sketch.addFeature(aHVName)
+            aRefAttrA = aHVConstraint.refattr("ConstraintEntityA")
+            aRefAttrA.setObject(aLine.lastResult())
+            self.__isHV[i] = True
+
+    def attributeChanged(self, theID):
+        if theID == self.START_ID() or theID == self.END_ID():
+            aStartPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.START_ID()))
+            aEndPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.END_ID()))
+            if aStartPoint.isInitialized() and aEndPoint.isInitialized:
+                self.updateLines()
+
+    def updateLines(self):
+        # Retrieving list of already created lines
+        aLinesList = self.reflist(self.LINES_LIST_ID())
+        aNbLines = aLinesList.size()
+
+        aStartPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.START_ID()))
+        aEndPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.END_ID()))
+        aX = [aStartPoint.x(), aStartPoint.x(), aEndPoint.x(), aEndPoint.x()]
+        aY = [aStartPoint.y(), aEndPoint.y(), aEndPoint.y(), aStartPoint.y()]
+
+        # Update coordinates of rectangle lines
+        for i in range (0, aNbLines):
+            aLine = ModelAPI.objectToFeature(aLinesList.object(i))
+            aLineStart = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("StartPoint"))
+            aLineEnd = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("EndPoint"))
+            aLineStart.setValue(aX[i-1], aY[i-1])
+            aLineEnd.setValue(aX[i], aY[i])
diff --git a/src/PythonAddons/macros/rectangle/widget.xml b/src/PythonAddons/macros/rectangle/widget.xml
new file mode 100644 (file)
index 0000000..120dd62
--- /dev/null
@@ -0,0 +1,19 @@
+<source>
+  <workbench id="Sketch">
+    <group id="Macro">
+
+      <!-- SketchRectangle (python feature) -->
+      <feature
+        id="SketchRectangle"
+        title="Rectangle"
+        tooltip="Create rectangle"
+        icon=":icons/rectangle.png">
+        <sketch-2dpoint_selector id="RectStartPoint" accept_expressions="0" title="Start point" tooltip="Start point coordinates"/>
+        <sketch-2dpoint_selector id="RectEndPoint" accept_expressions="0" title="End point" tooltip="End point coordinates"/>
+        <boolvalue id="Auxiliary" label="Auxiliary" default="false" tooltip="Construction element" obligatory="0"/>
+        <validator id="GeomValidators_Different" parameters="RectStartPoint,RectEndPoint"/>
+      </feature>
+
+    </group>
+  </workbench>  
+</source>
index bf05209c40618ab691d1ca182b2eeafe4281f040..d92bfba14cea94711e03aa16272bc78223bbba4a 100644 (file)
@@ -122,5 +122,6 @@ ADD_UNIT_TESTS(TestSketchPointLine.py
                TestMultiRotation.py
                TestMultiTranslation.py
                TestFillet.py
+               TestRectangle.py
                TestHighload.py
                TestSnowflake.py)
index 6cd164a88613c4a260c1648ccf6998ef06a89bda..03a5d894c981c7855f370eb37d37df3be44e5b86 100644 (file)
@@ -211,6 +211,8 @@ std::shared_ptr<ModelAPI_FeatureStateMessage> SketchPlugin_Plugin
       aMsg->setState(SketchPlugin_ConstraintAngle::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_MultiRotation::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_MultiTranslation::ID(), aHasSketchPlane);
+      // SketchRectangle is a python feature, so its ID is passed just as a string
+      aMsg->setState("SketchRectangle", aHasSketchPlane);
     }
   }
   return aMsg;
index 739f347bfd3b0077ec7b43fb087591c8c70632ba..12ec6c4f9711c03d16e1a47dc66342d22c6a12ae 100644 (file)
@@ -133,7 +133,12 @@ std::shared_ptr<ModelAPI_Feature> SketchPlugin_Sketch::addFeature(std::string th
 {
   std::shared_ptr<ModelAPI_Feature> aNew = document()->addFeature(theID, false);
   if (aNew) {
-    std::dynamic_pointer_cast<SketchPlugin_Feature>(aNew)->setSketch(this);
+    // the sketch cannot be specified for the macro-features defined in python
+    // like SketchRectangle, so we need to check the type of new feature
+    std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
+        std::dynamic_pointer_cast<SketchPlugin_Feature>(aNew);
+    if (aSketchFeature)
+      aSketchFeature->setSketch(this);
     data()->reflist(SketchPlugin_Sketch::FEATURES_ID())->append(aNew);
   }
    // set as current also after it becomes sub to set correctly enabled for other sketch subs
diff --git a/src/SketchPlugin/Test/TestRectangle.py b/src/SketchPlugin/Test/TestRectangle.py
new file mode 100644 (file)
index 0000000..d4c7061
--- /dev/null
@@ -0,0 +1,90 @@
+"""
+    TestRectangle.py
+    Unit test of SketchPlugin_Ractangle class
+
+"""
+from GeomDataAPI import *
+from ModelAPI import *
+import math
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+
+__updated__ = "2016-02-05"
+
+
+#=========================================================================
+# Auxiliary functions
+#=========================================================================
+def isHorizontal(line):
+    aStart = geomDataAPI_Point2D(line.attribute("StartPoint"))
+    aEnd   = geomDataAPI_Point2D(line.attribute("EndPoint"))
+    return aStart.y() == aEnd.y()
+
+def isVertical(line):
+    aStart = geomDataAPI_Point2D(line.attribute("StartPoint"))
+    aEnd   = geomDataAPI_Point2D(line.attribute("EndPoint"))
+    return aStart.x() == aEnd.x()
+
+
+#=========================================================================
+# Start of test
+#=========================================================================
+aSession = ModelAPI_Session.get()
+aDocument = aSession.moduleDocument()
+#=========================================================================
+# Creation of a sketch
+#=========================================================================
+aSession.startOperation()
+aSketchFeature = featureToCompositeFeature(aDocument.addFeature("Sketch"))
+origin = geomDataAPI_Point(aSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSession.finishOperation()
+#=========================================================================
+# Create a rectangle
+#=========================================================================
+aSession.startOperation()
+aRectangle = aSketchFeature.addFeature("SketchRectangle")
+aStartCorner = geomDataAPI_Point2D(aRectangle.attribute("RectStartPoint"))
+aEndCorner = geomDataAPI_Point2D(aRectangle.attribute("RectEndPoint"))
+aStartCorner.setValue(10., 10.)
+aEndCorner.setValue(40., 30.)
+aSession.finishOperation()
+#=========================================================================
+# Check the lines of rectangle are parallel to the axes
+#=========================================================================
+aNbSubs = aSketchFeature.numberOfSubs()
+aNbLines = 0
+for i in range (0, aNbSubs):
+    aFeature = objectToFeature(aSketchFeature.subFeature(i))
+    if aFeature.getKind() == "SketchLine":
+        aLastLine = aFeature
+        assert (isHorizontal(aLastLine) or isVertical(aLastLine))
+        aNbLines = aNbLines + 1
+assert (aNbLines == 4)
+#=========================================================================
+# Move one of lines
+#=========================================================================
+aSession.startOperation()
+aLineEnd = geomDataAPI_Point2D(aLastLine.attribute("EndPoint"))
+aLineEnd.setValue(50., 50.)
+aSession.finishOperation()
+#=========================================================================
+# Check the lines of rectangle are parallel to the axes
+#=========================================================================
+aNbSubs = aSketchFeature.numberOfSubs()
+aNbLines = 0
+for i in range (0, aNbSubs):
+    aFeature = objectToFeature(aSketchFeature.subFeature(i))
+    if aFeature.getKind() == "SketchLine":
+        aLastLine = aFeature
+        assert (isHorizontal(aLastLine) or isVertical(aLastLine))
+        aNbLines = aNbLines + 1
+assert (aNbLines == 4)
+#=========================================================================
+# End of test
+#=========================================================================
index deaee27b974d03400e8c5cb98f568660b595f563..6b3ba5c8b37d80e8b9ea051b0355a4ff3d678770 100644 (file)
@@ -5,7 +5,7 @@
     <group id="Basic">
       <feature
         id="Sketch"
-        nested="SketchPoint SketchLine SketchCircle SketchArc SketchConstraintLength SketchConstraintRadius SketchConstraintDistance SketchConstraintParallel SketchConstraintPerpendicular SketchConstraintRigid SketchConstraintHorizontal SketchConstraintVertical SketchConstraintEqual SketchConstraintTangent SketchConstraintFillet SketchConstraintCoincidence SketchConstraintMirror SketchConstraintAngle SketchMultiRotation SketchMultiTranslation SketchConstraintCollinear SketchConstraintMiddle"
+        nested="SketchPoint SketchLine SketchCircle SketchArc SketchRectangle SketchConstraintLength SketchConstraintRadius SketchConstraintDistance SketchConstraintParallel SketchConstraintPerpendicular SketchConstraintRigid SketchConstraintHorizontal SketchConstraintVertical SketchConstraintEqual SketchConstraintTangent SketchConstraintFillet SketchConstraintCoincidence SketchConstraintMirror SketchConstraintAngle SketchMultiRotation SketchMultiTranslation SketchConstraintCollinear SketchConstraintMiddle"
         when_nested="accept abort"
         title="Sketch"
         tooltip="Create sketch"
@@ -89,7 +89,9 @@
           </box>
         </toolbox>
       </feature>
+    </group>
 
+    <group id="Macro">
       <!--  SketchConstraintFillet  -->
       <feature id="SketchConstraintFillet" title="Fillet" tooltip="Create constraint defining fillet between two objects" icon=":icons/fillet.png">
         <!--<sketch_shape_selector id="ConstraintEntityA"