take into account kerning and inter-character spacing in bounding box
[PyX.git] / manual / path.rst
blob1b7378a9aa208b0dc52fd35aa0fd9c4d475fe86f
2 .. module:: path
4 ==================
5 Module :mod:`path`
6 ==================
8 .. sectionauthor:: Jörg Lehmann <joergl@users.sourceforge.net>
11 The :mod:`path` module defines several important classes which are documented in
12 the present section.
15 .. _postscript_like_paths:
17 Class :class:`path` --- PostScript-like paths
18 ---------------------------------------------
20 .. class:: path(*pathitems)
22    This class represents a PostScript like path consisting of the path elements
23    *pathitems*.
25    All possible path items are described in Sect. :ref:`path_pathitem`. Note that
26    there are restrictions on the first path element and likewise on each path
27    element after a :class:`closepath` directive. In both cases, no current point is
28    defined and the path element has to be an instance of one of the following
29    classes: :class:`moveto`, :class:`arc`, and :class:`arcn`.
31 Instances of the class :class:`path` provide the following methods (in
32 alphabetic order):
35 .. method:: path.append(pathitem)
37    Appends a *pathitem* to the end of the path.
40 .. method:: path.arclen()
42    Returns the total arc length of the path. [#normpathconvert]_
45 .. method:: path.arclentoparam(lengths)
47    Returns the parameter value(s) corresponding to the arc length(s) *lengths*.
48    [#normpathconvert]_
51 .. method:: path.at(params)
53    Returns the coordinates (as 2-tuple) of the path point(s) corresponding to the
54    parameter value(s) *params*. [#normpathconvert]_ [#value_or_list]_
57 .. method:: path.atbegin()
59    Returns the coordinates (as 2-tuple) of the first point of the path. [#normpathconvert]_
62 .. method:: path.atend()
64    Returns the coordinates (as 2-tuple) of the end point of the path. [#normpathconvert]_
67 .. method:: path.bbox()
69    Returns the bounding box of the path.
72 .. method:: path.begin()
74    Returns the parameter value (a :class:`normpathparam` instance) of the first
75    point in the path.
78 .. method:: path.curveradius(params)
80    Returns the curvature radius/radii (or None if infinite) at parameter value(s)
81    *params*. [#value_or_list]_ This is the inverse of the curvature at this
82    parameter. Note that this radius can be negative or positive, depending on the
83    sign of the curvature. [#normpathconvert]_
86 .. method:: path.end()
88    Returns the parameter value (a :class:`normpathparam` instance) of the last
89    point in the path.
92 .. method:: path.extend(pathitems)
94    Appends the list *pathitems* to the end of the path.
97 .. method:: path.intersect(opath)
99    Returns a tuple consisting of two lists of parameter values corresponding to the
100    intersection points of the path with the other path *opath*, respectively.
101    [#normpathconvert]_ For intersection points which are not farther apart then
102    *epsilon* (defaulting to :math:`10^{-5}` PostScript points), only one is returned.
105 .. method:: path.joined(opath)
107    Appends *opath* to the end of the path, thereby merging the last subpath (which
108    must not be closed) of the path with the first sub path of *opath* and returns
109    the resulting new path. [#normpathconvert]_ Instead of using the
110    :meth:`joined` method, you can also join two paths together with help of the
111    ``<<`` operator, for instance ``p = p1 << p2``.
114 .. method:: path.normpath(epsilon=None)
116    Returns the equivalent :class:`normpath`. For the conversion and for later
117    calculations with this :class:`normpath` an accuracy of *epsilon* is used.
118    If *epsilon* is *None*, the global *epsilon* of the :mod:`path` module is
119    used.
122 .. method:: path.paramtoarclen(params)
124    Returns the arc length(s) corresponding to the parameter value(s) *params*.
125    [#value_or_list]_ [#normpathconvert]_
128 .. method:: path.range()
130    Returns the maximal parameter value *param* that is allowed in the path methods.
133 .. method:: path.reversed()
135    Returns the reversed path. [#normpathconvert]_
138 .. method:: path.rotation(params)
140    Returns a transformation or a list of transformations, which rotate the
141    x-direction to the tangent vector and the y-direction to the normal vector
142    at the parameter value(s) *params*. [#value_or_list]_ [#normpathconvert]_
145 .. method:: path.split(params)
147    Splits the path at the parameter values *params*, which have to be sorted in
148    ascending order, and returns a corresponding list of :class:`normpath`
149    instances. [#normpathconvert]_
152 .. method:: path.tangent(params, length=1)
154    Return a :class:`line` instance or a list of :class:`line` instances,
155    corresponding to the tangent vectors at the parameter value(s) *params*.
156    [#value_or_list]_ The tangent vector will be scaled to the length *length*.
157    [#normpathconvert]_
160 .. method:: path.trafo(params)
162    Returns a transformation or a list of tranformations, which translate the
163    origin to a point on the path corresponding to parameter value(s) *params*
164    and rotate the x-direction to the tangent vector and the y-direction to the
165    normal vector. [#normpathconvert]_
168 .. method:: path.transformed(trafo)
170    Returns the path transformed according to the linear transformation *trafo*.
171    Here, ``trafo`` must be an instance of the :class:`trafo.trafo` class.
172    [#normpathconvert]_
175 .. [#normpathconvert]
176    This method requires a prior conversion of the path into a :class:`normpath`
177    instance. This is done automatically (using the precision *epsilon* set
178    globally using :meth:`path.set`). If you need a different *epsilon* for a
179    normpath, you also can perform the conversion manually.
181 .. [#value_or_list]
182    In these methods, *params* may either be a single value or a
183    list. In the latter case, the result of the method will be a list consisting of
184    the results for each parameter.  The parameter itself may either be a length
185    (or a number which is then interpreted as a user length) or an instance of the
186    class :class:`normpathparam`. In the former case, the length refers to the arc
187    length along the path.
190 .. _path_pathitem:
192 Path elements
193 -------------
195 The class :class:`pathitem` is the superclass of all PostScript path
196 construction primitives. It is never used directly, but only by instantiating
197 its subclasses, which correspond one by one to the PostScript primitives.
199 Except for the path elements ending in ``_pt``, all coordinates passed to the
200 path elements can be given as number (in which case they are interpreted as user
201 units with the currently set default type) or in PyX lengths.
203 The following operation move the current point and open a new subpath:
206 .. class:: moveto(x, y)
208    Path element which sets the current point to the absolute coordinates (*x*,
209    *y*). This operation opens a new subpath.
212 .. class:: rmoveto(dx, dy)
214    Path element which moves the current point by (*dx*, *dy*).  This operation
215    opens a new subpath.
217 Drawing a straight line can be accomplished using:
220 .. class:: lineto(x, y)
222    Path element which appends a straight line from the current point to the point
223    with absolute coordinates (*x*, *y*), which becomes the new current point.
226 .. class:: rlineto(dx, dy)
228    Path element which appends a straight line from the current point to the point
229    with relative coordinates (*dx*, *dy*), which becomes the new current point.
231 For the construction of arc segments, the following three operations are
232 available:
235 .. class:: arc(x, y, r, angle1, angle2)
237    Path element which appends an arc segment in counterclockwise direction with
238    absolute coordinates (*x*, *y*) of the center and  radius *r* from *angle1* to
239    *angle2* (in degrees).  If before the operation, the current point is defined, a
240    straight line from the current point to the beginning of the arc segment is
241    prepended. Otherwise, a subpath, which thus is the first one in the path, is
242    opened. After the operation, the current point is at the end of the arc segment.
245 .. class:: arcn(x, y, r, angle1, angle2)
247    Same as :class:`arc` but in clockwise direction.
250 .. class:: arct(x1, y1, x2, y2, r)
252    Path element consisting of a line followed by an arc of radius *r*. The arc
253    is part of the circle inscribed to the angle at *x1*, *y1* given by lines in
254    the directions to the current point and to *x2*, *y2*. The initial line
255    connects the current point to the point where the circle touches the line
256    through the current point and *x1*, *y1*. The arc then continues to the
257    point where the circle touches the line through *x1*, *y1* and *x2*, *y2*.
259 Bézier curves can be constructed using:
261 .. class:: curveto(x1, y1, x2, y2, x3, y3)
263    Path element which appends a Bézier curve with the current point as first
264    control point and the other control points (*x1*, *y1*), (*x2*, *y2*), and
265    (*x3*, *y3*).
268 .. class:: rcurveto(dx1, dy1, dx2, dy2, dx3, dy3)
270    Path element which appends a Bézier curve with the current point as first
271    control point and the other control points defined relative to the current point
272    by the coordinates (*dx1*, *dy1*), (*dx2*, *dy2*), and (*dx3*, *dy3*).
274 Note that when calculating the bounding box (see Sect. :mod:`bbox`) of Bézier
275 curves, PyX uses for performance reasons the so-called control box, i.e., the
276 smallest rectangle enclosing the four control points of the Bézier curve. In
277 general, this is not the smallest rectangle enclosing the Bézier curve.
279 Finally, an open subpath can be closed using:
282 .. class:: closepath()
284    Path element which closes the current subpath.
286 For performance reasons, two non-PostScript path elements are defined,  which
287 perform multiple identical operations:
290 .. class:: multilineto_pt(points_pt)
292    Path element which appends straight line segments starting from the current
293    point and going through the list of points given in the *points_pt*
294    argument. All coordinates have to be given in PostScript points.
297 .. class:: multicurveto_pt(points_pt)
299    Path element which appends Bézier curve segments starting from the current
300    point. *points_pt* is a sequence of 6-tuples containing the coordinates of
301    the two control points and the end point of a multicurveto segment.
304 .. _path_normpath:
306 Class :class:`normpath`
307 -----------------------
309 The :class:`normpath` class is used internally for all non-trivial path
310 operations, cf. footnote [#normpathconvert]_ in Sect. :ref:`postscript_like_paths`.
311 It represents a path as a list of subpaths, which are
312 instances of the class :class:`normsubpath`. These :class:`normsubpath`\ s
313 themselves consist of a list of :class:`normsubpathitems` which are either
314 straight lines (:class:`normline`) or Bézier curves (:class:`normcurve`).
316 A given path ``p`` can easily be converted to the corresponding
317 :class:`normpath` ``np`` by::
319    np = p.normpath()
321 Additionally, the accuracy that is used in all :class:`normpath` calculations can be
322 specified by means of the argument *epsilon*, which defaults to
323 :math:`10^{-5}`, where units of PostScript points are understood. This default
324 value can also be changed using the module function :func:`path.set`.
326 To construct a :class:`normpath` from a list of :class:`normsubpath` instances,
327 they are passed to the :class:`normpath` constructor:
329 .. class:: normpath(normsubpaths=[])
331    Construct a :class:`normpath` consisting of *subnormpaths*, which is a list of
332    :class:`subnormpath` instances.
334 Instances of :class:`normpath` offer all methods of regular :class:`path` instances,
335 which also have the same semantics. An exception are the methods :meth:`append`
336 and :meth:`extend`. While they allow for adding of instances of
337 :class:`subnormpath` to the :class:`normpath` instance, they also keep the
338 functionality of a regular path and allow for regular path elements to be
339 appended. The latter are converted to the proper normpath representation during
340 addition.
342 In addition to the :class:`path` methods, a :class:`normpath` instance also
343 offers the following methods, which operate on the instance itself, i.e., modify
344 it in place.
347 .. method:: normpath.join(other)
349    Join *other*, which has to be a :class:`path` instance, to the :class:`normpath`
350    instance.
353 .. method:: normpath.reverse()
355    Reverses the :class:`normpath` instance.
358 .. method:: normpath.transform(trafo)
360    Transforms the :class:`normpath` instance according to the linear transformation
361    *trafo*.
363 Finally, we remark that the sum of a :class:`normpath` and a :class:`path`
364 always yields a :class:`normpath`.
367 Class :class:`normsubpath`
368 --------------------------
371 .. class:: normsubpath(normsubpathitems=[], closed=0, epsilon=1e-5)
373    Construct a :class:`normsubpath` consisting of *normsubpathitems*, which is a
374    list of :class:`normsubpathitem` instances. If *closed* is set, the
375    :class:`normsubpath` will be closed, thereby appending a straight line segment
376    from the first to the last point, if it is not already present. All calculations
377    with the :class:`normsubpath` are performed with an accuracy of *epsilon*
378    (in units of PostScript points).
380 Most :class:`normsubpath` methods behave like the ones of a :class:`path`.
382 Exceptions are:
385 .. method:: normsubpath.append(anormsubpathitem)
387    Append the *normsubpathitem* to the end of the :class:`normsubpath` instance.
388    This is only possible if the :class:`normsubpath` is not closed, otherwise an
389    :exc:`NormpathException` is raised.
392 .. method:: normsubpath.extend(normsubpathitems)
394    Extend the :class:`normsubpath` instances by *normsubpathitems*, which has to be
395    a list of :class:`normsubpathitem` instances. This is only possible if the
396    :class:`normsubpath` is not closed, otherwise an :exc:`NormpathException` is
397    raised.
400 .. method:: normsubpath.close()
402    Close the :class:`normsubpath` instance by appending a straight line
403    segment from the first to the last point, if not already present.
406 .. _path_predefined:
408 Predefined paths
409 ----------------
412 For convenience, some often used paths are already predefined. All of them are
413 subclasses of the :class:`path` class.
416 .. class:: line(x0, y0, x1, y1)
418    A straight line from the point (*x0*, *y0*) to the point (*x1*, *y1*).
421 .. class:: curve(x0, y0, x1, y1, x2, y2, x3, y3)
423    A Bézier curve with  control points  (*x0*, *y0*), :math:`\dots`, (*x3*, *y3*).\
426 .. class:: rect(x, y, w, h)
428    A closed rectangle with lower left point (*x*, *y*), width *w*, and height *h*.
431 .. class:: circle(x, y, r)
433    A closed circle with center (*x*, *y*) and radius *r*.