1 # Copyright (C) 2014-2024 CEA, EDF
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 # Lesser General Public License for more details.
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 Macro-feature to produce rectangle in the sketcher
25 from salome.shaper import model
29 class SketchPlugin_Rectangle(model.Feature):
31 Implementation of rectangle creation.
33 It produced 2 horizontal lines and 2 vertical lines connected by coincidence constraints
39 """x.__init__(...) initializes x; see x.__class__.__doc__ for signature"""
40 model.Feature.__init__(self)
44 """Rectangle feature kind."""
45 return "SketchRectangle"
49 """Returns ID of first corner."""
50 return "RectStartPoint"
54 """Returns ID of second corner."""
59 """Returns whether the rectangle is accessory."""
64 """Returns ID of list containing lines created."""
65 return "RectangleList"
68 def RECTANGLE_TYPE_ID():
69 """Returns ID of type of rectangle creation (by corners or by center and corner)."""
70 return "RectangleType"
73 def RECTANGLE_BY_CORNERS_ID():
74 """Returns ID of creation type by opposite corners."""
75 return "RectangleTypeByCorners"
78 def RECTANGLE_CENTERED_ID():
79 """Returns ID of creation type by center point and a corner."""
80 return "RectangleTypeCentered"
84 """Returns ID of center point."""
85 return "RectCenterPoint"
89 """Returns ID of the reference to the center point."""
90 return "RectCenterPointRef"
94 """Returns ID of a corner."""
95 return "RectCornerPoint"
99 """Override Feature.getKind()"""
100 return SketchPlugin_Rectangle.ID()
103 # Initialization of the rectangle
105 def initAttributes(self):
106 """Override Feature.initAttributes()"""
107 # Flag whether the rectangle is accessory
108 self.data().addAttribute(self.AUXILIARY_ID(), ModelAPI.ModelAPI_AttributeBoolean.typeId())
109 # Corners of the rectangle (being defined by opposite corners)
110 self.data().addAttribute(self.START_ID(), GeomDataAPI.GeomDataAPI_Point2D.typeId())
111 self.data().addAttribute(self.END_ID(), GeomDataAPI.GeomDataAPI_Point2D.typeId())
112 # List with both contour and diagonal lines
113 self.data().addAttribute(self.LINES_LIST_ID(), ModelAPI.ModelAPI_AttributeRefList.typeId())
114 ModelAPI.ModelAPI_Session.get().validators().registerNotObligatory(self.getKind(), self.LINES_LIST_ID())
116 self.data().addAttribute(self.RECTANGLE_TYPE_ID(), ModelAPI.ModelAPI_AttributeString.typeId())
117 # Center and corner of centered rectangle (being defined by center and corner)
118 self.data().addAttribute(self.CENTER_ID(), GeomDataAPI.GeomDataAPI_Point2D.typeId())
119 self.data().addAttribute(self.CORNER_ID(), GeomDataAPI.GeomDataAPI_Point2D.typeId())
121 self.data().addAttribute(self.CENTER_REF_ID(), ModelAPI.ModelAPI_AttributeRefAttr.typeId())
122 ModelAPI.ModelAPI_Session.get().validators().registerNotObligatory(self.getKind(), self.CENTER_REF_ID())
126 Override Feature.isMacro().
127 Rectangle feature is macro: removes itself on the creation transaction finish.
132 # Editing of a rectangle. It is called on select of the first generative point
133 # and on hover of the second generative point. Generative point is either corner or center,
134 # depending on rectangle type. And also it is called on call of Sketch.addRectangleCentered(...) in TUI.
137 # Retrieve list of already created lines
138 aLinesList = self.reflist(self.LINES_LIST_ID())
139 if aLinesList.size() == 1:
140 # Create remaining rectangle contour lines
141 for i in range (1, 4):
142 aLine = self.__sketch.addFeature("SketchLine")
143 aLinesList.append(aLine)
146 # Connect rectangle contour lines
148 for i in range (0, 4):
149 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
150 iPrev = i - 1 if i != 0 else 3
151 aPrevLine = ModelAPI.objectToFeature(aLinesList.object(iPrev))
152 aCoincidence = self.__sketch.addFeature("SketchConstraintCoincidence")
153 aCoincidence.refattr("ConstraintEntityA").setAttr(aPrevLine.attribute("EndPoint"))
154 aCoincidence.refattr("ConstraintEntityB").setAttr(aLine.attribute("StartPoint"))
155 aStartPoints.append(aLine.attribute("StartPoint"))
157 # Update coordinates of created lines
160 # Flags, indicating perpendicular constraint is imposed on contour lines i and i+1.
161 self.__isPERP = [False, False, False]
163 # Create auxiliary diagonals in case of centered rectangle
164 if self.string(self.RECTANGLE_TYPE_ID()).value() == self.RECTANGLE_CENTERED_ID():
165 aDiag1 = self.__sketch.addFeature("SketchLine")
166 aLinesList.append(aDiag1)
167 aDiag2 = self.__sketch.addFeature("SketchLine")
168 aLinesList.append(aDiag2)
170 # Add coincidences between diagonals' endpoints and rectangle vertices
171 aPoints = [aDiag1.attribute("StartPoint"), aDiag2.attribute("StartPoint"),
172 aDiag1.attribute("EndPoint"), aDiag2.attribute("EndPoint")]
174 for i in range (0, len(aPoints)):
175 aCoincidence = self.__sketch.addFeature("SketchConstraintCoincidence")
176 aCoincidence.refattr("ConstraintEntityA").setAttr(aStartPoints[i])
177 aCoincidence.refattr("ConstraintEntityB").setAttr(aPoints[i])
179 # Update coordinates of created lines
184 # coincidences between center point and diagonals
185 attr = self.__getPoint2DAttrOfSketchPoint(self.refattr(self.CENTER_REF_ID()))
187 for line in [aDiag1.lastResult(), aDiag2.lastResult()]:
188 aCoincidence = self.__sketch.addFeature("SketchConstraintCoincidence")
189 aCoincidence.refattr("ConstraintEntityA").setAttr(attr)
190 aCoincidence.refattr("ConstraintEntityB").setObject(line)
192 # Add perpendicular constraints to the contour lines, which already have result
193 for i in range (0, 3):
197 aLine_A = ModelAPI.objectToFeature(aLinesList.object(i))
198 aLineResult_A = aLine_A.lastResult()
199 if aLineResult_A is None:
202 aLine_B = ModelAPI.objectToFeature(aLinesList.object(i+1))
203 aLineResult_B = aLine_B.lastResult()
204 if aLineResult_B is None:
207 aConstraintPerp = self.__sketch.addFeature("SketchConstraintPerpendicular")
208 aConstraintPerp.refattr("ConstraintEntityA").setObject(aLine_A.lastResult())
209 aConstraintPerp.refattr("ConstraintEntityB").setObject(aLine_B.lastResult())
210 self.__isPERP[i] = True
213 def attributeChanged(self, theID):
214 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():
215 # Find the sketch containing this rectangle
217 aRefsToMe = self.data().refsToMe()
218 for aRefToMe in aRefsToMe:
219 aFeature = ModelAPI.objectToFeature(aRefToMe.owner())
220 if aFeature.getKind() == "Sketch":
221 self.__sketch = ModelAPI.featureToCompositeFeature(aFeature)
224 if theID == self.CENTER_ID():
225 aCenter = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CENTER_ID())) # shared_ptr to Point2D
226 aCenterSketchPointAttr = self.refattr(self.CENTER_REF_ID())
227 if (not aCenterSketchPointAttr.isInitialized()):
228 # Create SketchPoint. In .execute() it will be constrained to keep center.
229 aCenterSketchPoint = self.__sketch.addFeature("SketchPoint")
230 aCenterSketchPoint.data().boolean("Auxiliary").setValue(True)
231 aCenterSketchPointCoords = GeomDataAPI.geomDataAPI_Point2D(aCenterSketchPoint.attribute("PointCoordinates")) # shared_ptr to Point2D
232 aCenterSketchPointCoords.setValue(aCenter.x(), aCenter.y())
233 aCenterSketchPointAttr.setObject(aCenterSketchPoint)
235 # Maintain consistency between center SketchPoint and center Point2D.
236 aCenterSketchPointCoordsAttr = self.__getPoint2DAttrOfSketchPoint(aCenterSketchPointAttr)
237 if (aCenterSketchPointCoordsAttr == None):
238 Warning("Faulty logic")
240 aCenterSketchPointCoords = GeomDataAPI.geomDataAPI_Point2D(aCenterSketchPointCoordsAttr) # shared_ptr to Point2D
241 aCenterSketchPointCoords.setValue(aCenter.x(), aCenter.y())
242 elif theID == self.CENTER_REF_ID():
243 aCenterSketchPointAttr = self.refattr(self.CENTER_REF_ID())
244 aCenterSketchPointCoordsAttr = self.__getPoint2DAttrOfSketchPoint(aCenterSketchPointAttr) # shared_ptr to Point2D
245 if (aCenterSketchPointCoordsAttr == None):
246 Warning("Faulty logic. Attempt to set rectangle's attribute " + self.CENTER_REF_ID() + " not with refattr to SketchPoint.")
249 # Maintain consistency between center SketchPoint and center Point2D.
250 aCenterSketchPointCoords = GeomDataAPI.geomDataAPI_Point2D(aCenterSketchPointCoordsAttr) # shared_ptr to Point2D
251 aCenter = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CENTER_ID())) # shared_ptr to Point2D
252 aCenter.setValue(aCenterSketchPointCoords.x(), aCenterSketchPointCoords.y())
254 aLinesList = self.reflist(self.LINES_LIST_ID())
255 aNbLines = aLinesList.size()
257 # If only one generative point is iniialized,
258 # do not create the full set of contour lines to not clutter
259 # UI with icons of constraints near the mouse pointer.
260 aLine = self.__sketch.addFeature("SketchLine")
261 aLinesList.append(aLine)
263 aStartPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.START_ID()))
264 aEndPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.END_ID()))
265 aCenter = self.__getPoint2D(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
266 aCorner = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CORNER_ID()))
268 if (aStartPoint.isInitialized() and aEndPoint.isInitialized()) or (aCenter is not None and aCorner.isInitialized()):
271 self.__updateLinesWithOnlyGenerativePoint()
272 elif theID == self.AUXILIARY_ID():
273 # Change aux attribute of contour lines
274 anAuxiliary = self.data().boolean(self.AUXILIARY_ID()).value()
275 aLinesList = self.reflist(self.LINES_LIST_ID())
276 for i in range (0, min(aLinesList.size(), 4)):
277 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
278 aLine.data().boolean("Auxiliary").setValue(anAuxiliary)
279 elif theID == self.RECTANGLE_TYPE_ID():
280 # TODO Find a way to distinguish "attribute changed" events on hover and on click.
281 # Now, if both generative points are selected, but the rectangle is not applied,
282 # and then rectangle type is changed, the unapplied rectangle is erased.
283 # It should be applied instead.
284 aLinesList = self.reflist(self.LINES_LIST_ID()).clear()
285 aCenterSketchPointAttr = self.refattr(self.CENTER_REF_ID())
286 aCenterSketchPointAttr.reset()
287 self.attribute(self.START_ID()).reset()
288 self.attribute(self.END_ID()).reset()
289 self.attribute(self.CENTER_ID()).reset()
290 self.attribute(self.CORNER_ID()).reset()
293 def __getPoint2DAttrOfSketchPoint(self, theSketchPointRefAttr):
294 if theSketchPointRefAttr.isObject() and theSketchPointRefAttr.object() is not None:
295 feature = ModelAPI.ModelAPI_Feature.feature(theSketchPointRefAttr.object())
296 if feature.getKind() == "SketchPoint":
297 return feature.attribute("PointCoordinates")
299 return theSketchPointRefAttr.attr()
302 def __getPoint2D(self, thePoint2DAttr, theSketchPointRefAttr):
303 attr = thePoint2DAttr
304 if theSketchPointRefAttr.isInitialized():
305 aPoint2DAttr = self.__getPoint2DAttrOfSketchPoint(theSketchPointRefAttr)
306 if aPoint2DAttr is not None:
308 if attr is None or not attr.isInitialized():
310 return GeomDataAPI.geomDataAPI_Point2D(attr).pnt()
313 def __updateLines(self):
314 if self.string(self.RECTANGLE_TYPE_ID()).value() == self.RECTANGLE_CENTERED_ID():
315 aCenter = self.__getPoint2D(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
316 aCorner = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CORNER_ID()))
317 aStartX = 2.0 * aCenter.x() - aCorner.x()
318 aStartY = 2.0 * aCenter.y() - aCorner.y()
319 aX = [aStartX, aStartX, aCorner.x(), aCorner.x()]
320 aY = [aStartY, aCorner.y(), aCorner.y(), aStartY]
322 aStartPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.START_ID()))
323 aEndPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.END_ID()))
324 aX = [aStartPoint.x(), aStartPoint.x(), aEndPoint.x(), aEndPoint.x()]
325 aY = [aStartPoint.y(), aEndPoint.y(), aEndPoint.y(), aStartPoint.y()]
327 # Retrieve list of already created lines
328 aLinesList = self.reflist(self.LINES_LIST_ID())
329 aNumOfContourLines = min(aLinesList.size(), 4)
331 # Do not update lines during update of coordinates
333 for i in range (0, aLinesList.size()):
334 wasBlocked.append(aLinesList.object(i).data().blockSendAttributeUpdated(True))
336 # Update coordinates of rectangle lines
337 anAuxiliary = self.data().boolean(self.AUXILIARY_ID()).value()
338 for i in range (0, aNumOfContourLines):
339 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
340 aLineStart = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("StartPoint"))
341 aLineEnd = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("EndPoint"))
342 aLineStart.setValue(aX[i-1], aY[i-1])
343 aLineEnd.setValue(aX[i], aY[i])
344 aLine.data().boolean("Auxiliary").setValue(anAuxiliary)
346 # Update auxiliary diagonals
347 if self.string(self.RECTANGLE_TYPE_ID()).value() == self.RECTANGLE_CENTERED_ID():
348 for i in range (aNumOfContourLines, aLinesList.size()):
349 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
350 aLineStart = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("StartPoint"))
351 aLineEnd = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("EndPoint"))
352 aLineStart.setValue(aX[i-aNumOfContourLines-1], aY[i-aNumOfContourLines-1])
353 aLineEnd.setValue(aX[i-aNumOfContourLines+1], aY[i-aNumOfContourLines+1])
354 aLine.data().boolean("Auxiliary").setValue(True)
356 # Update the rectangle
357 for i in range (0, aLinesList.size()):
358 aLinesList.object(i).data().blockSendAttributeUpdated(wasBlocked[i], True)
361 def __updateLinesWithOnlyGenerativePoint(self):
362 # Retrieve list of already created lines
363 aLinesList = self.reflist(self.LINES_LIST_ID())
364 aNbLines = aLinesList.size()
366 aStartPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.START_ID()))
367 if aStartPoint.isInitialized:
371 aCenter = self.__getPoint2D(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
375 # Update coordinates of rectangle lines
376 for i in range (0, aNbLines):
377 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
378 aLineStart = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("EndPoint"))
379 aLineStart.setValue(aX, aY)