2.1 Introduction
The path module allows one to construct PostScript-like
paths, which are one of the main building blocks for the
generation of drawings. A PostScript path is an arbitrary shape
consisting of straight lines, arc segments and cubic Bézier curves.
Such a path does not have to be connected but may also comprise
several disconnected segments, which will be called subpaths
in the following.
XXX example for paths and subpaths (figure)
Usually, a path is constructed by passing a list of the path
primitives moveto, lineto, curveto, etc., to the
constructor of the path class. The following code snippet, for
instance, defines a path p that consists of a straight line
from the point
to the point
from pyx import *
p = path.path(path.moveto(0, 0), path.lineto(1, 1))
Equivalently, one can also use the predefined path subclass
line and write
p = path.line(0, 0, 1, 1)
While already some geometrical operations can be performed with this
path (see next section), another PyX object is needed in order to
actually being able to draw the path, namely an instance of the
canvas class. By convention, we use the name c for this
instance:
In order to draw the path on the canvas, we use the stroke() method
of the canvas class, i.e.,
c.stroke(p)
c.writeEPSfile("line")
To complete the example, we have added a writeEPSfile() call,
which writes the contents of the canvas to the file line.eps.
Note that an extension .eps is added automatically, if not
already present in the given filename. Similarly, if you want to
generate a PDF file instead, use
As a second example, let us define a path which consists of more than
one subpath:
cross = path.path(path.moveto(0, 0), path.rlineto(1, 1),
path.moveto(1, 0), path.rlineto(-1, 1))
The first subpath is again a straight line from
to
,
with the only difference that we now have used the rlineto
class, whose arguments count relative from the last point in the path.
The second moveto instance opens a new subpath starting at the
point
and ending at
. Note that although both lines
intersect at the point
, they count as disconnected
subpaths. The general rule is that each occurrence of a moveto
instance opens a new subpath. This means that if one wants to draw a
rectangle, one should not use
rect1 = path.path(path.moveto(0, 0), path.lineto(0, 1),
path.moveto(0, 1), path.lineto(1, 1),
path.moveto(1, 1), path.lineto(1, 0),
path.moveto(1, 0), path.lineto(0, 0))
which would construct a rectangle out of four disconnected
subpaths (see Fig. 2.1a). In a better solution (see
Fig. 2.1b), the pen is not lifted between the first and
the last point:
Figure 2.1:
Rectangle consisting of (a) four separate lines, (b) one open
path, and (c) one closed path. (d) Filling a
path always closes it automatically.
|
|
rect2 = path.path(path.moveto(0, 0), path.lineto(0, 1),
path.lineto(1, 1), path.lineto(1, 0),
path.lineto(0, 0))
However, as one can see in the lower left corner of
Fig. 2.1b, the rectangle is still incomplete. It needs to
be closed, which can be done explicitly by using for the last straight
line of the rectangle (from the point
back to the origin at
)
the closepath directive:
rect3 = path.path(path.moveto(0, 0), path.lineto(0, 1),
path.lineto(1, 1), path.lineto(1, 0),
path.closepath())
The closepath directive adds a straight line from the current
point to the first point of the current subpath and furthermore
closes the sub path, i.e., it joins the beginning and the end
of the line segment. This results in the intended rectangle shown in
Fig. 2.1c. Note that filling the path implicitly closes
every open subpath, as is shown for a single subpath in
Fig. 2.1d), which results from
c.stroke(rect2, [deco.filled([color.grey(0.95)])])
Here, we supply as second argument of the stroke() method a
list which in the present case only consists of a single element,
namely the so called decorator deco.filled. As it name says,
this decorator specifies that the path is not only being stroked but
also filled with the given color. More information about decorators,
styles and other attributes which can be passed as elements of the
list can be found in Sect. 2.3. More details on
the available path elements can be found in Sect. 2.4.2.
To conclude this section, we should not forget to mention that
rectangles are, of course, predefined in PyX, so above we could
have as well written
rect2 = path.rect(0, 0, 1, 1)
Here, the first two arguments specify the origin of the rectangle
while the second two arguments define its width and height,
respectively. For more details on the predefined paths, we
refer the reader to Sect. 2.4.5.