From e9c7126660098bac6a46efb0f55a76fd91e290e2 Mon Sep 17 00:00:00 2001 From: azv Date: Fri, 5 Feb 2016 11:29:47 +0300 Subject: [PATCH] Implementation of macro-feature Rectangle --- src/ModelAPI/ModelAPI.i | 3 + src/PartSet/PartSet_icons.qrc | 1 + src/PartSet/icons/rectangle.png | Bin 0 -> 304 bytes src/PythonAddons/addons_Features.py | 6 +- src/PythonAddons/addons_Features.xml | 1 + src/PythonAddons/macros/rectangle/__init__.py | 0 src/PythonAddons/macros/rectangle/feature.py | 150 ++++++++++++++++++ src/PythonAddons/macros/rectangle/widget.xml | 19 +++ src/SketchPlugin/CMakeLists.txt | 1 + src/SketchPlugin/SketchPlugin_Plugin.cpp | 2 + src/SketchPlugin/SketchPlugin_Sketch.cpp | 7 +- src/SketchPlugin/Test/TestRectangle.py | 90 +++++++++++ src/SketchPlugin/plugin-Sketch.xml | 4 +- 13 files changed, 281 insertions(+), 3 deletions(-) create mode 100644 src/PartSet/icons/rectangle.png create mode 100644 src/PythonAddons/macros/rectangle/__init__.py create mode 100644 src/PythonAddons/macros/rectangle/feature.py create mode 100644 src/PythonAddons/macros/rectangle/widget.xml create mode 100644 src/SketchPlugin/Test/TestRectangle.py diff --git a/src/ModelAPI/ModelAPI.i b/src/ModelAPI/ModelAPI.i index 441cfc22d..aaf61bdc4 100644 --- a/src/ModelAPI/ModelAPI.i +++ b/src/ModelAPI/ModelAPI.i @@ -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; @@ -103,6 +104,8 @@ %template(ObjectList) std::list >; %template(ResultList) std::list >; %template(DocumentList) std::list >; +// std::set -> [] +%template(AttributeSet) std::set >; // std::dynamic_pointer_cast template std::shared_ptr shared_ptr_cast(std::shared_ptr theObject); diff --git a/src/PartSet/PartSet_icons.qrc b/src/PartSet/PartSet_icons.qrc index 368893c2b..30e6b1b35 100644 --- a/src/PartSet/PartSet_icons.qrc +++ b/src/PartSet/PartSet_icons.qrc @@ -89,5 +89,6 @@ icons/plane_view.png icons/collinear.png icons/middlepoint.png + icons/rectangle.png diff --git a/src/PartSet/icons/rectangle.png b/src/PartSet/icons/rectangle.png new file mode 100644 index 0000000000000000000000000000000000000000..b98a99162ce70129bafcc89b78cd5db47b8b64c7 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zKpodXn9)gNb_Gz7y~NYkmHhz|3#X8-?1>~BpwM1V7sn8b(?=(t + diff --git a/src/PythonAddons/macros/rectangle/__init__.py b/src/PythonAddons/macros/rectangle/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/PythonAddons/macros/rectangle/feature.py b/src/PythonAddons/macros/rectangle/feature.py new file mode 100644 index 000000000..aaa65512a --- /dev/null +++ b/src/PythonAddons/macros/rectangle/feature.py @@ -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 index 000000000..120dd6202 --- /dev/null +++ b/src/PythonAddons/macros/rectangle/widget.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index bf05209c4..d92bfba14 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -122,5 +122,6 @@ ADD_UNIT_TESTS(TestSketchPointLine.py TestMultiRotation.py TestMultiTranslation.py TestFillet.py + TestRectangle.py TestHighload.py TestSnowflake.py) diff --git a/src/SketchPlugin/SketchPlugin_Plugin.cpp b/src/SketchPlugin/SketchPlugin_Plugin.cpp index 6cd164a88..03a5d894c 100644 --- a/src/SketchPlugin/SketchPlugin_Plugin.cpp +++ b/src/SketchPlugin/SketchPlugin_Plugin.cpp @@ -211,6 +211,8 @@ std::shared_ptr 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; diff --git a/src/SketchPlugin/SketchPlugin_Sketch.cpp b/src/SketchPlugin/SketchPlugin_Sketch.cpp index 739f347bf..12ec6c4f9 100644 --- a/src/SketchPlugin/SketchPlugin_Sketch.cpp +++ b/src/SketchPlugin/SketchPlugin_Sketch.cpp @@ -133,7 +133,12 @@ std::shared_ptr SketchPlugin_Sketch::addFeature(std::string th { std::shared_ptr aNew = document()->addFeature(theID, false); if (aNew) { - std::dynamic_pointer_cast(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 aSketchFeature = + std::dynamic_pointer_cast(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 index 000000000..d4c7061a3 --- /dev/null +++ b/src/SketchPlugin/Test/TestRectangle.py @@ -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 +#========================================================================= diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index deaee27b9..6b3ba5c8b 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -5,7 +5,7 @@ + +