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