Replace prolog module by new, more generic resource module and replace prolog
[PyX/mjg.git] / manual / graphics.tex
blobbe7f1135eece2628906d84d177ccb69b8be5538c
1 \chapter{Basic graphics}
3 \sectionauthor{J\"org Lehmann}{joergl@users.sourceforge.net}
5 \label{graphics}
7 \section{Introduction}
9 The path module allows one to construct PostScript-like
10 \textit{paths}, which are one of the main building blocks for the
11 generation of drawings. A PostScript path is an arbitrary shape
12 consisting of straight lines, arc segments and cubic B\'ezier curves.
13 Such a path does not have to be connected but may also comprise
14 several disconnected segments, which will be called \textit{subpaths}
15 in the following.
17 XXX example for paths and subpaths
19 Usually, a path is constructed by passing a list of the path
20 primitives \class{moveto}, \class{lineto}, \class{curveto}, etc., to the
21 constructor of the \class{path} class. The following code snippet, for
22 instance, defines a path \var{p} that consists of a straight line
23 from the point $(0, 0)$ to the point $(1, 1)$
24 \begin{verbatim}
25 from pyx import *
26 p = path.path(path.moveto(0, 0), path.lineto(1, 1))
27 \end{verbatim}
28 Equivalently, one can also use the predefined \class{path} subclass
29 \class{line} and write
30 \begin{verbatim}
31 p = path.line(0, 0, 1, 1)
32 \end{verbatim}
34 While already some geometrical operations can be performed with this
35 path (see next section), another \PyX{} object is needed in order to
36 actually being able to draw the path, namely an instance of the
37 \class{canvas} class. By convention, we use the name \var{c} for this
38 instance:
39 \begin{verbatim}
40 c = canvas.canvas()
41 \end{verbatim}
42 In order to draw the path on the canvas, we use the \method{stroke()} method
43 of the \class{canvas} class, i.e.,
44 \begin{verbatim}
45 c.stroke(p)
46 c.writeEPSfile("line")
47 \end{verbatim}
48 To complete the example, we have added a \method{writeEPSfile()} call,
49 which writes the contents of the canvas to the file \file{line.eps}.
50 Note that an extension \file{.eps} is added automatically, if not
51 already present in the given filename.
53 As a second example, let us define a path which consists of more than
54 one subpath:
55 \begin{verbatim}
56 cross = path.path(path.moveto(0, 0), path.rlineto(1, 1),
57 path.moveto(1, 0), path.rlineto(-1, 1))
58 \end{verbatim}
59 The first subpath is again a straight line from $(0, 0)$ to $(1, 1)$,
60 with the only difference that we now have used the \class{rlineto}
61 class, whose arguments count relative from the last point in the path.
62 The second \class{moveto} instance opens a new subpath starting at the
63 point $(1, 0)$ and ending at $(0, 1)$. Note that although both lines
64 intersect at the point $(1/2, 1/2)$, they count as disconnected
65 subpaths. The general rule is that each occurrence of a \class{moveto}
66 instance opens a new subpath. This means that if one wants to draw a
67 rectangle, one should not use
68 \begin{verbatim}
69 rect1 = path.path(path.moveto(0, 0), path.lineto(0, 1),
70 path.moveto(0, 1), path.lineto(1, 1),
71 path.moveto(1, 1), path.lineto(1, 0),
72 path.moveto(1, 0), path.lineto(0, 0))
73 \end{verbatim}
74 which would construct a rectangle out of four disconnected
75 subpaths (see Fig.~\ref{fig:rects}a). In a better solution (see
76 Fig.~\ref{fig:rects}b), the pen is not lifted between the first and
77 the last point:
79 \begin{figure}
80 \centerline{\includegraphics{rects}}
81 \caption{Rectangle consisting of (a) four separate lines, (b) one open
82 path, and (c) one closed path. (d) Filling a
83 path always closes it automatically.}
84 \label{fig:rects}
85 \end{figure}
87 \begin{verbatim}
88 rect2 = path.path(path.moveto(0, 0), path.lineto(0, 1),
89 path.lineto(1, 1), path.lineto(1, 0))
90 \end{verbatim}
91 However, as one can see in the lower left corner of
92 Fig.~\ref{fig:rects}b, the rectangle is still incomplete. It needs to
93 be closed, which can be done explicitly by using for the last straight
94 line of the rectangle (from the point $(0, 1)$ back to the origin at $(0, 0)$)
95 the \class{closepath} directive:
96 \begin{verbatim}
97 rect3 = path.path(path.moveto(0, 0), path.lineto(0, 1),
98 path.lineto(1, 1), path.lineto(1, 0),
99 path.closepath())
100 \end{verbatim}
101 The \class{closepath} directive adds a straight line from the current
102 point to the first point of the current subpath and furthermore
103 \textit{closes} the sub path, i.e., it joins the beginning and the end
104 of the line segment. This results in the intended rectangle shown in
105 Fig.~\ref{fig:rects}c. Note that filling the path implicitly closes
106 every open subpath, as is shown for a single subpath in
107 Fig.~\ref{fig:rects}d), which results from
108 \begin{verbatim}
109 c.stroke(rect2, [deco.filled([color.grey(0.95)])])
110 \end{verbatim}
111 More details on the available path elements can be found in
112 Sect.~\ref{path:pathitem}.
114 XXX more on styles and attributes and reference to corresponding section
116 Of course, rectangles are also predefined in \PyX{}, so above we could
117 have as well written
118 \begin{verbatim}
119 rect2 = path.rect(0, 0, 1, 1)
120 \end{verbatim}
121 Here, the first two arguments specify the origin of the rectangle
122 while the second two arguments define its width and height,
123 respectively. For more details on the predefined paths, we
124 refer the reader to Sect.~\ref{path:predefined}.
126 \section{Path operations}
128 Often, one wants to perform geometrical operations with a path before
129 placing it on a canvas by stroking or filling it. For instance, one
130 might want to intersect one path with another one, split the paths at
131 the intersection points, and then join the segments together in a new
132 way. \PyX{} supports such tasks by means of a number of path methods,
133 which we will introduce in the following.
135 Suppose you want to draw the radii to the intersection points of a
136 circle with a straight line. This task can be done using the following
137 code which results in Fig.~\ref{fig:radii}
138 \verbatiminput{radii.py}
139 \begin{figure}
140 \centerline{\includegraphics{radii}}
141 \caption{Example: Intersection of circle with line yielding two radii.}
142 \label{fig:radii}
143 \end{figure}
144 Here, the basic elements, a circle around the point $(0, 0)$ with
145 radius $2$ and a straight line, are defined. Then, passing the \var{line}, to
146 the \method{intersect()} method of \var{circle}, we obtain a tuple of
147 parameter values of the intersection points. The first element of the
148 tuple is a list of parameter values for the path whose
149 \method{intersect()} method has been called, the second element is the
150 corresponding list for the path passed as argument to this method. In
151 the present example, we only need one list of parameter values, namely
152 \var{isects_circle}. Using the \method{at()} path method to obtain
153 the point corresponding to the parameter value, we draw the radii for
154 the different intersection points.
156 Another powerful feature of \PyX{} is its ability to split paths at a
157 given set of parameters. For instance, in order to fill in the
158 previous example the segment of the circle delimited by the straight
159 line (cf.\ Fig.~\ref{fig:radii2}), one first has to construct a path
160 corresponding to the outline of this segment. The following code
161 snippet yields this \var{segment}
162 \begin{verbatim}
163 arc1, arc2 = circle.split(isects_circle)
164 if arc1.arclen() < arc2.arclen():
165 arc = arc1
166 else:
167 arc = arc2
169 isects_line.sort()
170 line1, line2, line3 = line.split(isects_line)
172 segment = line2 << arc
173 \end{verbatim}
174 \begin{figure}
175 \centerline{\includegraphics{radii2}}
176 \caption{Example: Intersection of circle with line yielding radii and
177 circle segment.}
178 \label{fig:radii2}
179 \end{figure}
180 Here, we first split the circle using the \method{split()} method passing
181 the list of parameters obtained above. Since the circle is closed,
182 this yields two arc segments. We then use the \method{arclen()}, which
183 returns the arc length of the path, to find the shorter of the two
184 arcs. Before splitting the line, we have to take into account that
185 the \method{split()} method only accepts a sorted list of parameters.
186 Finally, we join the straight line and the arc segment. For
187 this, we make use of the \verb|<<| operator, which not only adds
188 the paths (which could be done using \samp{line2 + arc}), but also
189 joins the last subpath of \var{line2} and the first one of
190 \var{arc}. Thus, \var{segment} consists of only a single subpath
191 and filling works as expected.
193 An important issue when operating on paths is the parametrisation
194 used. Internally, \PyX{} uses a parametrisation which uses an interval
195 of length $1$ for each path element of a path. For instance, for a
196 simple straight line, the possible parameter values range from $0$ to
197 $1$, corresponding to the first and last point, respectively, of the
198 line. Appending another straight line, would extend this range to a
199 maximal value of $2$.
201 However, the situation becomes more complicated if more complex
202 objects like a circle are involved. Then, one could be tempted to
203 assume that again the parameter value ranges from $0$ to $1$, because
204 the predefined circle consists just of one \class{arc} together with a
205 \class{closepath} element. However, this is not the case: the actual
206 range is much larger. The reason for this behaviour lies in the
207 internal path handling of \PyX: Before performing any non-trivial
208 geometrical operation with a path, it will automatically be converted
209 into an instance of the \class{normpath} class (see also
210 Sect.~\ref{path:normpath}). These so generated paths are already
211 separated in their subpaths and only contain straight lines and
212 B\'ezier curve segments. Thus, as is easily imaginable, they are much
213 simpler to deal with.
215 A more geometrical way of accessing a point on the path is to use the
216 arc length of the path segment from the first point of the path to the
217 given point. Thus, all \PyX{} path methods that accept a parameter
218 value also allow the user to pass an arc length. For instance,
219 \begin{verbatim}
220 from math import pi
222 pt1 = path.circle(0, 0, 1).at(pi)
223 pt2 = path.circle(0, 0, 1).at(3*pi/2)
225 c.stroke(path.path(path.moveto(*pt1), path.lineto(*pt2)))
226 \end{verbatim}
227 will draw a straight line from a point at angle $180$ degrees (in
228 radians $\pi$) to another point at angle $270$ degrees (in radians
229 $3\pi/2$) on the unit circle. Note however, that the mapping arc
230 length $\to$ point is in general discontinuous at the begin and the
231 end of a subpath, and thus \PyX{} does not guarantee any particular
232 result for this boundary case.
234 More information on the available path methods can be found
235 in Sect.~\ref{path:path}.
237 \section{Attributes: Styles and Decorations}
239 XXX to be done