fix various deprecation warnings
[PyX/mjg.git] / manual / graphics.tex
blob571ec8a6531223c6f85e28e182fea9407b9543f1
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 path.lineto(0, 0))
91 \end{verbatim}
92 However, as one can see in the lower left corner of
93 Fig.~\ref{fig:rects}b, the rectangle is still incomplete. It needs to
94 be closed, which can be done explicitly by using for the last straight
95 line of the rectangle (from the point $(0, 1)$ back to the origin at $(0, 0)$)
96 the \class{closepath} directive:
97 \begin{verbatim}
98 rect3 = path.path(path.moveto(0, 0), path.lineto(0, 1),
99 path.lineto(1, 1), path.lineto(1, 0),
100 path.closepath())
101 \end{verbatim}
102 The \class{closepath} directive adds a straight line from the current
103 point to the first point of the current subpath and furthermore
104 \textit{closes} the sub path, i.e., it joins the beginning and the end
105 of the line segment. This results in the intended rectangle shown in
106 Fig.~\ref{fig:rects}c. Note that filling the path implicitly closes
107 every open subpath, as is shown for a single subpath in
108 Fig.~\ref{fig:rects}d), which results from
109 \begin{verbatim}
110 c.stroke(rect2, [deco.filled([color.grey(0.95)])])
111 \end{verbatim}
112 More details on the available path elements can be found in
113 Sect.~\ref{path:pathitem}.
115 XXX more on styles and attributes and reference to corresponding section
117 Of course, rectangles are also predefined in \PyX{}, so above we could
118 have as well written
119 \begin{verbatim}
120 rect2 = path.rect(0, 0, 1, 1)
121 \end{verbatim}
122 Here, the first two arguments specify the origin of the rectangle
123 while the second two arguments define its width and height,
124 respectively. For more details on the predefined paths, we
125 refer the reader to Sect.~\ref{path:predefined}.
127 \section{Path operations}
129 Often, one wants to perform geometrical operations with a path before
130 placing it on a canvas by stroking or filling it. For instance, one
131 might want to intersect one path with another one, split the paths at
132 the intersection points, and then join the segments together in a new
133 way. \PyX{} supports such tasks by means of a number of path methods,
134 which we will introduce in the following.
136 Suppose you want to draw the radii to the intersection points of a
137 circle with a straight line. This task can be done using the following
138 code which results in Fig.~\ref{fig:radii}
139 \verbatiminput{radii.py}
140 \begin{figure}
141 \centerline{\includegraphics{radii}}
142 \caption{Example: Intersection of circle with line yielding two radii.}
143 \label{fig:radii}
144 \end{figure}
145 Here, the basic elements, a circle around the point $(0, 0)$ with
146 radius $2$ and a straight line, are defined. Then, passing the \var{line}, to
147 the \method{intersect()} method of \var{circle}, we obtain a tuple of
148 parameter values of the intersection points. The first element of the
149 tuple is a list of parameter values for the path whose
150 \method{intersect()} method has been called, the second element is the
151 corresponding list for the path passed as argument to this method. In
152 the present example, we only need one list of parameter values, namely
153 \var{isects_circle}. Using the \method{at()} path method to obtain
154 the point corresponding to the parameter value, we draw the radii for
155 the different intersection points.
157 Another powerful feature of \PyX{} is its ability to split paths at a
158 given set of parameters. For instance, in order to fill in the
159 previous example the segment of the circle delimited by the straight
160 line (cf.\ Fig.~\ref{fig:radii2}), one first has to construct a path
161 corresponding to the outline of this segment. The following code
162 snippet yields this \var{segment}
163 \begin{verbatim}
164 arc1, arc2 = circle.split(isects_circle)
165 if arc1.arclen() < arc2.arclen():
166 arc = arc1
167 else:
168 arc = arc2
170 isects_line.sort()
171 line1, line2, line3 = line.split(isects_line)
173 segment = line2 << arc
174 \end{verbatim}
175 \begin{figure}
176 \centerline{\includegraphics{radii2}}
177 \caption{Example: Intersection of circle with line yielding radii and
178 circle segment.}
179 \label{fig:radii2}
180 \end{figure}
181 Here, we first split the circle using the \method{split()} method passing
182 the list of parameters obtained above. Since the circle is closed,
183 this yields two arc segments. We then use the \method{arclen()}, which
184 returns the arc length of the path, to find the shorter of the two
185 arcs. Before splitting the line, we have to take into account that
186 the \method{split()} method only accepts a sorted list of parameters.
187 Finally, we join the straight line and the arc segment. For
188 this, we make use of the \verb|<<| operator, which not only adds
189 the paths (which could be done using \samp{line2 + arc}), but also
190 joins the last subpath of \var{line2} and the first one of
191 \var{arc}. Thus, \var{segment} consists of only a single subpath
192 and filling works as expected.
194 An important issue when operating on paths is the parametrisation
195 used. Internally, \PyX{} uses a parametrisation which uses an interval
196 of length $1$ for each path element of a path. For instance, for a
197 simple straight line, the possible parameter values range from $0$ to
198 $1$, corresponding to the first and last point, respectively, of the
199 line. Appending another straight line, would extend this range to a
200 maximal value of $2$.
202 However, the situation becomes more complicated if more complex
203 objects like a circle are involved. Then, one could be tempted to
204 assume that again the parameter value ranges from $0$ to $1$, because
205 the predefined circle consists just of one \class{arc} together with a
206 \class{closepath} element. However, this is not the case: the actual
207 range is much larger. The reason for this behaviour lies in the
208 internal path handling of \PyX: Before performing any non-trivial
209 geometrical operation with a path, it will automatically be converted
210 into an instance of the \class{normpath} class (see also
211 Sect.~\ref{path:normpath}). These so generated paths are already
212 separated in their subpaths and only contain straight lines and
213 B\'ezier curve segments. Thus, as is easily imaginable, they are much
214 simpler to deal with.
216 A more geometrical way of accessing a point on the path is to use the
217 arc length of the path segment from the first point of the path to the
218 given point. Thus, all \PyX{} path methods that accept a parameter
219 value also allow the user to pass an arc length. For instance,
220 \begin{verbatim}
221 from math import pi
223 pt1 = path.circle(0, 0, 1).at(pi)
224 pt2 = path.circle(0, 0, 1).at(3*pi/2)
226 c.stroke(path.path(path.moveto(*pt1), path.lineto(*pt2)))
227 \end{verbatim}
228 will draw a straight line from a point at angle $180$ degrees (in
229 radians $\pi$) to another point at angle $270$ degrees (in radians
230 $3\pi/2$) on the unit circle. Note however, that the mapping arc
231 length $\to$ point is in general discontinuous at the begin and the
232 end of a subpath, and thus \PyX{} does not guarantee any particular
233 result for this boundary case.
235 More information on the available path methods can be found
236 in Sect.~\ref{path:path}.
238 \section{Attributes: Styles and Decorations}
240 XXX to be done