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 $(0, 0)$ to the point $(1, 1)$

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:

c = canvas.canvas()
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
c.writePDFfile("line")

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 $(0, 0)$ to $(1, 1)$, 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 $(1, 0)$ and ending at $(0, 1)$. Note that although both lines intersect at the point $(1/2, 1/2)$, 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.
\includegraphics{rects}
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 $(0, 1)$ back to the origin at $(0, 0)$) 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.