Salome HOME
neutral fiber macro
[modules/shaper.git] / src / PythonAddons / macros / rectangle / feature.py
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2014-2021  CEA/DEN, EDF R&D
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20
21 """
22 Macro-feature to produce rectangle in the sketcher
23 Author: Artem ZHIDKOV
24 """
25
26 from salome.shaper import model
27 import ModelAPI
28 import GeomDataAPI
29
30 class SketchPlugin_Rectangle(model.Feature):
31     """
32     Implementation of rectangle creation.
33
34     It produced 2 horizontal lines and 2 vertical lines connected by coincidence constraints
35     """
36
37 # Initializations
38
39     __sketch = None
40     __isHV = list()
41
42     def __init__(self):
43         """x.__init__(...) initializes x; see x.__class__.__doc__ for signature"""
44         model.Feature.__init__(self)
45
46     @staticmethod
47     def ID():
48         """Rectangle feature kind."""
49         return "SketchRectangle"
50
51     @staticmethod
52     def START_ID():
53         """Returns ID of first corner."""
54         return "RectStartPoint"
55
56     @staticmethod
57     def END_ID():
58         """Returns ID of second corner."""
59         return "RectEndPoint"
60
61     @staticmethod
62     def AUXILIARY_ID():
63         """Returns whether the rectangle is accessory."""
64         return "Auxiliary"
65
66     @staticmethod
67     def LINES_LIST_ID():
68         """Returns ID of list containing lines created."""
69         return "RectangleList"
70
71     @staticmethod
72     def RECTANGLE_TYPE_ID():
73         """Returns ID of type of rectangle creation (by corners or by center and corner)."""
74         return "RectangleType"
75
76     @staticmethod
77     def RECTANGLE_BY_CORNERS_ID():
78         """Returns ID of creation type by opposite corners."""
79         return "RectangleTypeByCorners"
80
81     @staticmethod
82     def RECTANGLE_CENTERED_ID():
83         """Returns ID of creation type by center point and a corner."""
84         return "RectangleTypeCentered"
85
86     @staticmethod
87     def CENTER_ID():
88         """Returns ID of center point."""
89         return "RectCenterPoint"
90
91     @staticmethod
92     def CENTER_REF_ID():
93         """Returns ID of the reference to the center point."""
94         return "RectCenterPointRef"
95
96     @staticmethod
97     def CORNER_ID():
98         """Returns ID of a corner."""
99         return "RectCornerPoint"
100
101
102     def getKind(self):
103         """Override Feature.getKind()"""
104         return SketchPlugin_Rectangle.ID()
105
106
107 # Initialization of the rectangle
108
109     def initAttributes(self):
110         """Override Feature.initAttributes()"""
111         # Flag whether the rectangle is accessory
112         self.data().addAttribute(self.AUXILIARY_ID(), ModelAPI.ModelAPI_AttributeBoolean_typeId())
113         # Creating corners of the rectangle
114         self.data().addAttribute(self.START_ID(), GeomDataAPI.GeomDataAPI_Point2D_typeId())
115         self.data().addAttribute(self.END_ID(), GeomDataAPI.GeomDataAPI_Point2D_typeId())
116         # Creating list to store lines
117         self.data().addAttribute(self.LINES_LIST_ID(), ModelAPI.ModelAPI_AttributeRefList_typeId())
118         ModelAPI.ModelAPI_Session.get().validators().registerNotObligatory(self.getKind(), self.LINES_LIST_ID())
119         # Type of rectangle
120         self.data().addAttribute(self.RECTANGLE_TYPE_ID(), ModelAPI.ModelAPI_AttributeString_typeId())
121         # Center and corner of the rectangle
122         self.data().addAttribute(self.CENTER_ID(), GeomDataAPI.GeomDataAPI_Point2D_typeId())
123         self.data().addAttribute(self.CORNER_ID(), GeomDataAPI.GeomDataAPI_Point2D_typeId())
124
125         self.data().addAttribute(self.CENTER_REF_ID(), ModelAPI.ModelAPI_AttributeRefAttr_typeId())
126         ModelAPI.ModelAPI_Session.get().validators().registerNotObligatory(self.getKind(), self.CENTER_REF_ID())
127
128     def isMacro(self):
129         """
130         Override Feature.isMacro().
131         Rectangle feature is macro: removes itself on the creation transaction finish.
132         """
133         return True
134
135 # Edition of the rectangle
136
137     def execute(self):
138         """F.execute() -- execute the Feature"""
139         # Retrieving list of already created lines
140         aLinesList = self.reflist(self.LINES_LIST_ID())
141         aNbLines = aLinesList.size()
142         if aNbLines == 1:
143             # Create 1-4 lines to compose the rectangle
144             for i in range (0, 3):
145                 aLine = self.__sketch.addFeature("SketchLine")
146                 aLinesList.append(aLine)
147             self.updateLines()
148             aNbLines = aLinesList.size()
149             aStartPoints = []
150             # Create constraints to keep the rectangle
151             for i in range (0, aNbLines):
152                 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
153                 # connect neighbor lines by coincidence
154                 iPrev = i - 1
155                 if iPrev < 0:
156                     iPrev = aNbLines - 1
157                 aPrevLine = ModelAPI.objectToFeature(aLinesList.object(iPrev))
158                 aCoincidence = self.__sketch.addFeature("SketchConstraintCoincidence")
159                 aRefAttrA = aCoincidence.refattr("ConstraintEntityA")
160                 aRefAttrB = aCoincidence.refattr("ConstraintEntityB")
161                 aRefAttrA.setAttr(aPrevLine.attribute("EndPoint"))
162                 aRefAttrB.setAttr(aLine.attribute("StartPoint"))
163                 aStartPoints.append(aLine.attribute("StartPoint"))
164             # Flags which show horizontal or vertical constraint is build for corresponding line
165             self.__isHV = [False, False, False, False]
166             # Update coordinates of created lines
167             self.updateLines()
168             # Create auxiliary diagonals in case of centered rectangle
169             if self.string(self.RECTANGLE_TYPE_ID()).value() == self.RECTANGLE_CENTERED_ID():
170                 aDiag1 = self.__sketch.addFeature("SketchLine")
171                 aLinesList.append(aDiag1)
172                 aDiag2 = self.__sketch.addFeature("SketchLine")
173                 aLinesList.append(aDiag2)
174                 # coincidences in corners
175                 aPoints = [aDiag1.attribute("StartPoint"), aDiag2.attribute("StartPoint"),
176                            aDiag1.attribute("EndPoint"), aDiag2.attribute("EndPoint")]
177                 for i in range (0, len(aPoints)):
178                     aCoincidence = self.__sketch.addFeature("SketchConstraintCoincidence")
179                     aRefAttrA = aCoincidence.refattr("ConstraintEntityA")
180                     aRefAttrB = aCoincidence.refattr("ConstraintEntityB")
181                     aRefAttrA.setAttr(aStartPoints[i])
182                     aRefAttrB.setAttr(aPoints[i])
183                 # Update coordinates of created lines
184                 self.updateLines()
185                 aDiag1.execute()
186                 aDiag2.execute()
187                 # coincidences between center point and diagonals
188                 refPnt = self.getReferencePoint(self.refattr(self.CENTER_REF_ID()))
189                 if refPnt is not None:
190                     for line in [aDiag1.lastResult(), aDiag2.lastResult()]:
191                         aCoincidence = self.__sketch.addFeature("SketchConstraintCoincidence")
192                         aCoincidence.refattr("ConstraintEntityA").setAttr(refPnt)
193                         aCoincidence.refattr("ConstraintEntityB").setObject(line)
194         # Add horizontal and vertical constraint for the lines which already have result
195         for i in range (0, 4):
196             if self.__isHV[i]:
197                 continue
198             aLine = ModelAPI.objectToFeature(aLinesList.object(i))
199             aLineResult = aLine.lastResult()
200             if aLineResult is None:
201                 continue
202             aHVName = "SketchConstraintHorizontal"
203             if i % 2 == 1:
204                 aHVName = "SketchConstraintVertical"
205             aHVConstraint = self.__sketch.addFeature(aHVName)
206             aRefAttrA = aHVConstraint.refattr("ConstraintEntityA")
207             aRefAttrA.setObject(aLine.lastResult())
208             self.__isHV[i] = True
209
210     def attributeChanged(self, theID):
211         """attributeChanged"""
212         if theID == self.START_ID() or theID == self.END_ID() or theID == self.CENTER_ID() or theID == self.CENTER_REF_ID() or theID == self.CORNER_ID():
213             # Search the sketch containing this rectangle
214             aRefs = self.data().refsToMe()
215             for itera in aRefs:
216                 aFeature = ModelAPI.objectToFeature(itera.owner())
217                 if aFeature.getKind() == "Sketch":
218                     self.__sketch = ModelAPI.featureToCompositeFeature(aFeature)
219                     break
220
221             aLinesList = self.reflist(self.LINES_LIST_ID())
222             aNbLines = aLinesList.size()
223             if aNbLines == 0:
224                 # Create first line to be able to create a coincidence with selected point/feature
225                 for i in range (0, 1):
226                     aLine = self.__sketch.addFeature("SketchLine")
227                     aLinesList.append(aLine)
228
229             aStartPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.START_ID()))
230             aEndPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.END_ID()))
231             aCenter = self.getPointByRef(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
232             aCorner = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CORNER_ID()))
233             if (aStartPoint.isInitialized() and aEndPoint.isInitialized()) or (aCenter is not None and aCorner.isInitialized()):
234                 self.updateLines()
235             else:
236                 self.updateStartPoint()
237         if theID == self.AUXILIARY_ID():
238             anAuxiliary = self.data().boolean(self.AUXILIARY_ID()).value()
239             aLinesList = self.reflist(self.LINES_LIST_ID())
240             aNbLines = aLinesList.size()
241             # Update coordinates of rectangle lines
242             for i in range (0, aNbLines):
243                 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
244                 aLine.data().boolean("Auxiliary").setValue(anAuxiliary)
245
246     def getReferencePoint(self, theRef):
247         """getReferencePoint"""
248         if theRef.isObject() and theRef.object() is not None:
249             feature = ModelAPI.ModelAPI_Feature.feature(theRef.object())
250             if feature.getKind() == "SketchPoint":
251                 return feature.attribute("PointCoordinates")
252         else:
253             return theRef.attr()
254         return None
255
256     def getPointByRef(self, thePoint, theRef):
257         """getPointByRef"""
258         attr = thePoint
259         if theRef.isInitialized():
260             refPnt = self.getReferencePoint(theRef)
261             if refPnt is not None:
262                 attr = refPnt
263         if attr is None or not attr.isInitialized():
264             return None
265         return GeomDataAPI.geomDataAPI_Point2D(attr).pnt()
266
267     def updateLines(self):
268         """updateLines"""
269         # Retrieving list of already created lines
270         aLinesList = self.reflist(self.LINES_LIST_ID())
271         aNbLines = min(aLinesList.size(), 4)
272         if self.string(self.RECTANGLE_TYPE_ID()).value() == self.RECTANGLE_CENTERED_ID():
273             aCenter = self.getPointByRef(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
274             aCorner = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CORNER_ID()))
275             aStartX = 2.0 * aCenter.x() - aCorner.x()
276             aStartY = 2.0 * aCenter.y() - aCorner.y()
277             aX = [aStartX, aStartX, aCorner.x(), aCorner.x()]
278             aY = [aStartY, aCorner.y(), aCorner.y(), aStartY]
279         else:
280             aStartPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.START_ID()))
281             aEndPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.END_ID()))
282             aX = [aStartPoint.x(), aStartPoint.x(), aEndPoint.x(), aEndPoint.x()]
283             aY = [aStartPoint.y(), aEndPoint.y(), aEndPoint.y(), aStartPoint.y()]
284         anAuxiliary = self.data().boolean(self.AUXILIARY_ID()).value()
285
286         # do not recalculate the rectrangle after each update
287         wasBlocked = []
288         for i in range (0, aLinesList.size()):
289             wasBlocked.append(aLinesList.object(i).data().blockSendAttributeUpdated(True))
290
291         # Update coordinates of rectangle lines
292         for i in range (0, aNbLines):
293             aLine = ModelAPI.objectToFeature(aLinesList.object(i))
294             aLineStart = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("StartPoint"))
295             aLineEnd = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("EndPoint"))
296             aLineStart.setValue(aX[i-1], aY[i-1])
297             aLineEnd.setValue(aX[i], aY[i])
298             aLine.data().boolean("Auxiliary").setValue(anAuxiliary)
299
300         # Update auxiliary diagonals
301         if self.string(self.RECTANGLE_TYPE_ID()).value() == self.RECTANGLE_CENTERED_ID():
302             for i in range (aNbLines, aLinesList.size()):
303                 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
304                 aLineStart = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("StartPoint"))
305                 aLineEnd = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("EndPoint"))
306                 aLineStart.setValue(aX[i-aNbLines-1], aY[i-aNbLines-1])
307                 aLineEnd.setValue(aX[i-aNbLines+1], aY[i-aNbLines+1])
308                 aLine.data().boolean("Auxiliary").setValue(True)
309
310         # update the rectangle
311         for i in range (0, aLinesList.size()):
312             aLinesList.object(i).data().blockSendAttributeUpdated(wasBlocked[i], True)
313
314     def updateStartPoint(self):
315         """updateStartPoint"""
316         # Retrieving list of already created lines
317         aLinesList = self.reflist(self.LINES_LIST_ID())
318         aNbLines = aLinesList.size()
319
320         aStartPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.START_ID()))
321         if aStartPoint.isInitialized:
322             aX = aStartPoint.x()
323             aY = aStartPoint.y()
324         else:
325             aCenter = self.getPointByRef(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
326             aX = aCenter.x()
327             aY = aCenter.y()
328
329         # Update coordinates of rectangle lines
330         for i in range (0, aNbLines):
331             aLine = ModelAPI.objectToFeature(aLinesList.object(i))
332             aLineStart = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("EndPoint"))
333             aLineStart.setValue(aX, aY)