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