Salome HOME
[PythonAPI / doc] possible doc structuration example on extrusion feature
[modules/shaper.git] / src / PythonAPI / model / sketcher / sketch.py
1 # Author: Daniel Brunier-Coulin with contribution by Mikhail Ponikarov
2 #         finalized by Renaud Nedelec and Sergey Pokhodenko
3 # Copyright (C) 2014-20xx CEA/DEN, EDF R&D
4
5 """Sketcher interface.
6 This interface allows to add a sketch
7 in a part or partset.
8 The created sketch object provides all the needed methods 
9 for sketch modification and constraint edition.
10
11 Example of code:
12
13 .. doctest:: 
14
15    >>> import model
16    >>> model.begin()
17    >>> partset = model.moduleDocument()
18    >>> part = model.addPart(partset).document()
19    >>> plane = model.defaultPlane("XOY")
20    >>> sketch = model.addSketch(part, plane)
21    >>> line = sketch.addLine(0, 0, 0, 1)
22    >>> line.endPointData().x()
23    0.0
24    >>> line.endPointData().y()
25    1.0
26 """
27
28 from ModelAPI import modelAPI_ResultConstruction, featureToCompositeFeature
29 from GeomDataAPI import geomDataAPI_Point, geomDataAPI_Dir
30 from GeomAlgoAPI import GeomAlgoAPI_SketchBuilder, ShapeList
31
32 from model.sketcher.point import Point
33 from model.sketcher.line import Line
34 from model.sketcher.circle import Circle
35 from model.sketcher.arc import Arc
36 from model.sketcher.mirror import Mirror
37 from model.roots import Interface
38 from model.tools import Selection
39
40
41 def addSketch(document, plane):
42     """Add a sketch to a Part or PartSet.
43
44     Arguments:
45        document(ModelAPI_Document): part or partset document
46        plane(geom.Ax3): plane on wich the sketch is built
47     
48     Returns:
49        Sketch: sketch object
50     """
51     feature = featureToCompositeFeature(document.addFeature("Sketch"))
52     return Sketch(feature, plane)
53
54 class Sketch(Interface):
55     """Interface class for Sketch feature."""
56     def __init__(self, feature, *args):
57         """Initialize a 2D Sketch on the given plane.
58
59         The plane can be defined either by:
60         - a 3D axis system (geom.Ax3),
61         - an existing face identified by its topological name.
62         """
63         Interface.__init__(self, feature)
64         assert(self._feature.getKind() == "Sketch")
65
66         self._origin = geomDataAPI_Point(
67             self._feature.data().attribute("Origin")
68             )
69         self._dir_x = geomDataAPI_Dir(
70             self._feature.data().attribute("DirX")
71             )
72         self._norm = geomDataAPI_Dir(
73             self._feature.data().attribute("Norm")
74             )
75         self._external = self._feature.data().selection("External")
76
77         # If no arguments are given the attributes of the feature 
78         # are not Initialized
79         if args is not None:
80             plane = args[0]
81             if isinstance(plane, str):
82                 self.__sketchOnFace(plane)
83             else:
84                 self.__sketchOnPlane(plane)
85
86     def __sketchOnPlane(self, plane):
87         """Create the sketch on a plane."""
88         origin = plane.location()
89         normal = plane.direction()
90         x_direction = plane.xDirection()
91         self._origin.setValue(origin.x(), origin.y(), origin.z())
92         self._norm.setValue(normal.x(), normal.y(), normal.z())
93         self._dir_x.setValue(x_direction.x(), x_direction.y(), x_direction.z())
94
95     def __sketchOnFace(self, name):
96         """Initialize the sketch on a face given by its name."""
97         self._external.selectSubShape("FACE", name)
98
99     #-------------------------------------------------------------
100     #
101     # Creation of Geometries
102     #
103     #-------------------------------------------------------------
104
105     def addPoint(self, *args):
106         """Add a point to the sketch."""
107         if not args:
108             raise TypeError("No arguments given")
109         point_feature = self._feature.addFeature("SketchPoint")
110         return Point(point_feature, *args)
111
112     def addLine(self, *args):
113         """Add a line to the sketch.
114         
115         .. function:: addLine(name)
116         Select an existing line. The line is added to the sketch with a rigid 
117         constraint (it cannot be modified by the sketch)
118         
119         Arguments:
120             name(str): name of an existing line
121         
122         .. function:: addLine(start, end)
123         Create a line by points
124         
125         Arguments:
126            start(point): start point of the line
127            end(point): end point of the line
128         
129         .. function:: addLine(start_x, start_y, end_x, end_y)
130         Create a line by coordinates
131         
132         Arguments:
133            start_x(double): start point x coordinate
134         """
135         if not args:
136             raise TypeError("No arguments given")
137         line_feature = self._feature.addFeature("SketchLine")
138         line_interface = Line(line_feature, *args)
139         # if the line is created by name add a rigid constraint
140         # to the created line
141         if len(args) == 1 and isinstance(args[0], str):
142             constraint = self._feature.addFeature("SketchConstraintRigid")
143             constraint.refattr("ConstraintEntityA").setObject(
144                 line_feature.firstResult()
145                 )
146         return line_interface
147
148     def addCircle(self, *args):
149         """Add a circle to this Sketch."""
150         if not args:
151             raise TypeError("No arguments given")
152         circle_feature = self._feature.addFeature("SketchCircle")
153         return Circle(circle_feature, *args)
154
155     def addArc(self, *args):
156         """Add an arc of circle to the sketch and return an arc object.
157         
158         Two different syntaxes are allowed:
159         
160         .. function:: addArc(center, start, end)
161         
162         Arguments:
163             center (point): center of the arc  
164             start (point): start point of the arc
165             end (point): end point of the arc
166         
167         .. function:: addArc(center_x, center_y, start_x, start_y, end_x, end_y)
168         
169         Same as above but with coordinates
170         
171         Returns:
172             Arc: arc object
173         Raises:
174             TypeError: if no argument is provided
175         """
176         if not args:
177             raise TypeError("No arguments given")
178         arc_feature = self._feature.addFeature("SketchArc")
179         return Arc(arc_feature, *args)
180
181     #-------------------------------------------------------------
182     #
183     # Creation of Geometrical and Dimensional Constraints
184     #
185     #-------------------------------------------------------------
186
187     def setCoincident(self, p1, p2):
188         """Set coincident the two given points and add the corresponding
189         constraint to this Sketch."""
190         # assert(p1 and p2) NOTE : if an argument is missing python
191         # will raise TypeError by itself.
192         # It seems better to check only that provided arguments are not 
193         # None
194         if p1 is None or p2 is None:
195             raise TypeError("NoneType argument given")
196         constraint = self._feature.addFeature("SketchConstraintCoincidence")
197         constraint.data().refattr("ConstraintEntityA").setAttr(p1)
198         constraint.data().refattr("ConstraintEntityB").setAttr(p2)
199         self.execute()
200         return constraint
201
202     def setParallel(self, l1, l2):
203         """Set parallel the two given lines and add the corresponding
204         constraint to this Sketch."""
205         if l1 is None or l2 is None:
206             raise TypeError("NoneType argument given")
207         constraint = self._feature.addFeature("SketchConstraintParallel")
208         constraint.data().refattr("ConstraintEntityA").setObject(l1)
209         constraint.data().refattr("ConstraintEntityB").setObject(l2)
210         self.execute()
211         return constraint
212
213     def setPerpendicular(self, l1, l2):
214         """Set perpendicular the two given lines and add the corresponding
215         constraint to this Sketch."""
216         if l1 is None or l2 is None:
217             raise TypeError("NoneType argument given")
218         constraint = self._feature.addFeature("SketchConstraintPerpendicular")
219         constraint.data().refattr("ConstraintEntityA").setObject(l1)
220         constraint.data().refattr("ConstraintEntityB").setObject(l2)
221         self.execute()
222         return constraint
223
224     def setHorizontal(self, line):
225         """Set horizontal the given line and add the corresponding
226         constraint to this Sketch."""
227         if line is None:
228             raise TypeError("NoneType argument given")
229         constraint = self._feature.addFeature("SketchConstraintHorizontal")
230         constraint.data().refattr("ConstraintEntityA").setObject(line)
231         self.execute()
232         return constraint
233
234     def setVertical(self, line):
235         """Set vertical the given line and add the corresponding
236         constraint to this Sketch."""
237         if line is None:
238             raise TypeError("NoneType argument given")
239         constraint = self._feature.addFeature("SketchConstraintVertical")
240         constraint.data().refattr("ConstraintEntityA").setObject(line)
241         self.execute()
242         return constraint
243
244     def setDistance(self, point, line, length):
245         """Set the distance between the given point and line, and add
246         the corresponding constraint to this Sketch."""
247         if point is None or line is None:
248             raise TypeError("NoneType argument given")
249         constraint = self._feature.addFeature("SketchConstraintDistance")
250         if isinstance(line, basestring):
251             # Add the edge identified by the given topological name
252             # to this Sketch
253             line = self.addLine(line).result()
254         constraint.data().refattr("ConstraintEntityA").setAttr(point)
255         constraint.data().refattr("ConstraintEntityB").setObject(line)
256         constraint.data().real("ConstraintValue").setValue(length)
257         self.execute()
258         return constraint
259
260     def setLength(self, line, length):
261         """Set the length of the given line and add the corresponding
262         constraint to this Sketch."""
263         if line is None:
264             raise TypeError("NoneType argument given")
265         constraint = self._feature.addFeature("SketchConstraintLength")
266         constraint.data().refattr("ConstraintEntityA").setObject(line)
267         constraint.data().real("ConstraintValue").setValue(length)
268         self.execute()
269         return constraint
270
271     def setRadius(self, circle, radius):
272         """Set the radius of the given circle and add the corresponding
273         constraint to this Sketch."""
274         constraint = self._feature.addFeature("SketchConstraintRadius")
275         constraint.data().refattr("ConstraintEntityA").setObject(circle)
276         constraint.data().real("ConstraintValue").setValue(radius)
277         self.execute()
278         return constraint
279
280     def setEqual(self, object_1, object_2):
281         """Set the radii of two circles or the length of two lines equal.
282
283         The corresponding constraint is added to the sketch"""
284         constraint = self._feature.addFeature("SketchConstraintEqual")
285         constraint.data().refattr("ConstraintEntityA").setObject(object_1)
286         constraint.data().refattr("ConstraintEntityB").setObject(object_2)
287         self.execute()
288         return constraint
289
290     def setAngle(self, line_1, line_2, angle):
291         """Set the angle between the given 2 lines and add the corresponding
292         constraint to the sketch."""
293         constraint = self._feature.addFeature("SketchConstraintAngle")
294         constraint.data().refattr("ConstraintEntityA").setObject(line_1)
295         constraint.data().refattr("ConstraintEntityB").setObject(line_2)
296         constraint.data().real("ConstraintValue").setValue(angle)
297         self.execute()
298         return constraint
299
300     def setTangent(self, object_1, object_2):
301         """Set a tangential continuity between two objects
302         at their coincidence point."""
303         if object_1 is None or object_2 is None:
304             raise TypeError("NoneType argument given")
305         constraint = self._feature.addFeature("SketchConstraintTangent")
306         constraint.data().refattr("ConstraintEntityA").setObject(object_1)
307         constraint.data().refattr("ConstraintEntityB").setObject(object_2)
308         self.execute()
309         return constraint
310
311     def setFillet(self, line_1, line_2, radius):
312         """Set a fillet constraint between the 2 given lines with the given
313         filleting radius."""
314         constraint = self._feature.addFeature("SketchConstraintFillet")
315         constraint.data().refattr("ConstraintEntityA").setObject(line_1)
316         constraint.data().reflist("ConstraintEntityB").clear()
317         constraint.data().reflist("ConstraintEntityB").append(line_2)
318         self.execute()
319         return constraint
320     
321     def setRigid(self, object_):
322         """Set a rigid constraint on a given object."""
323         constraint = self._feature.addFeature("SketchConstraintRigid")
324         constraint.data().refattr("ConstraintEntityA").setObject(object_)
325         self.execute()
326         return constraint
327     
328     #-------------------------------------------------------------
329     #
330     # Transformation constraints
331     #
332     #-------------------------------------------------------------
333     
334     def addMirror(self, mirror_line, sketch_objects):
335         """Add a mirror transformation of the given objects to the sketch.
336         
337         This transformation is a constraint.
338         
339         :return: interface to the constraint
340         :rtype: Mirror object
341         """
342         mirror_constraint = self._feature.addFeature("SketchConstraintMirror")
343         mirror_interface = Mirror(mirror_constraint, mirror_line, sketch_objects)
344         self.execute()
345         return mirror_interface
346         
347
348     #-------------------------------------------------------------
349     #
350     # Edition of Dimensional Constraints
351     #
352     #-------------------------------------------------------------
353
354     def setValue(self, constraint, value):
355         """Modify the value of the given dimensional constraint."""
356         constraint.data().real("ConstraintValue").setValue(value)
357
358     #-------------------------------------------------------------
359     #
360     # Macro functions combining geometry creation and constraints
361     #
362     #-------------------------------------------------------------
363
364     def addPolyline(self, *coords):
365         """Add a poly-line to this Sketch.
366
367         The end of consecutive segments are defined as coincident.
368         """
369         c0 = coords[0]
370         c1 = coords[1]
371         polyline = []
372         line_1 = self.addLine(c0, c1)
373         polyline.append(line_1)
374         # Adding and connecting next lines
375         for c2 in coords[2:]:
376             line_2 = self.addLine(c1, c2)
377             self.setCoincident(line_1.endPointData(), line_2.startPointData())
378             polyline.append(line_2)
379             c1 = c2
380             line_1 = line_2
381         return polyline
382
383     def addPolygon(self, *coords):
384         """Add a polygon to this Sketch.
385
386         The end of consecutive segments are defined as coincident.
387         """
388         pg = self.addPolyline(*coords)
389         # Closing the poly-line supposed being defined by at least 3 points
390         c0 = coords[0]
391         cn = coords[len(coords) - 1]
392         ln = self.addLine(cn, c0)
393         self.setCoincident(
394             pg[len(coords) - 2].endPointData(), ln.startPointData()
395             )
396         self.setCoincident(
397             ln.endPointData(), pg[0].startPointData()
398             )
399         pg.append(ln)
400         return pg
401
402     #-------------------------------------------------------------
403     #
404     # Getters
405     #
406     #-------------------------------------------------------------
407
408     def selectFace(self, *args):
409         """Select the geometrical entities of this Sketch on which
410         the result Face must be built.
411
412         When no entity is given, the face is based on all existing
413         geometry of this Sketch.
414         """
415         if len(args) == 0:
416             wire = modelAPI_ResultConstruction(
417                 self._feature.firstResult()
418                 ).shape()
419         elif len(args) == 1:
420             wire = args[0].shape()
421         else:
422             raise Exception("not yet implemented")
423         # TODO: simple version now, should be a list of selected faces
424         return [Selection(self.result(), self.buildShape(wire))]
425
426     def buildShape(self, wire):
427         """Build the result Shape of this Sketch according to the
428         selected geometrical entities."""
429         o = self._origin.pnt()
430         dx = self._dir_x.dir()
431         n = self._norm.dir()
432
433         # The faces are kept otherwise they are destroyed at exit
434         faces = ShapeList()
435         GeomAlgoAPI_SketchBuilder.createFaces(o, dx, n, wire, faces)
436         # TODO: Deal with several faces
437         return faces[0]
438
439     def result(self):
440         """Returns the result data of this Feature."""
441         return self._feature.firstResult()