corrections by Gert Ingold
[PyX/mjg.git] / manual / graphics.tex
blob42f7d3b44d8ebee636f7a938184ec8e548c35fbc
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 separate subpaths.
65 The general rule is that each occurence of \class{moveto} opens a new
66 subpath. This means that if one wants to draw a rectangle, one should
67 not use
68 \begin{verbatim}
69 # wrong: do not use moveto when you want a single subpath
70 rect1 = path.path(path.moveto(0, 0), path.lineto(0, 1),
71 path.moveto(1, 0), path.lineto(1, 1),
72 path.moveto(1, 1), path.lineto(1, 1),
73 path.moveto(0, 1), path.lineto(0, 0))
74 \end{verbatim}
75 which would construct a rectangle consisting of four disconnected
76 subpaths. Instead the correct way of defining a rectangle is
77 \begin{verbatim}
78 # correct: a rectangle consisting of a single closed subpath
79 rect2 = path.path(path.moveto(0, 0), path.lineto(0, 1),
80 path.lineto(1, 1), path.lineto(1, 0),
81 path.closepath())
82 \end{verbatim}
84 \begin{figure}
85 \centerline{\includegraphics{rects}}
86 \caption{Not closed (left) and closed (midlle) rectangle. Filling a
87 path (right) always closes it automatically.}
88 \label{fig:rects}
89 \end{figure}
90 Note that for the last straight line of the rectangle (from $(0, 1)$
91 back to the origin at $(0, 0)$)) we have used \class{closepath}. This
92 directive adds a straight line from the current point to the first
93 point of the current subpath and furthermore \textit{closes} the sub
94 path, i.e., it joins the beginning and the end of the line segment.
95 The difference can be appreciated in Fig.~\ref{fig:rects}, where
96 also a filled (and at the same time stroked) rectangle is shown.
97 The corresponding code looks like
98 \begin{verbatim}
99 c.stroke(rect1, [deco.filled([color.grey(0.95)])])
100 \end{verbatim}
101 The important point to remember here is that when filling a path,
102 PostScript automatically closes it. More details on the available
103 path elements can be found in Sect.~\ref{path:pathel}.
105 XXX more on styles and attributes and reference to corresponding section
107 Of course, rectangles are also predefined in \PyX{}, so above we could
108 have as well written
109 \begin{verbatim}
110 rect2 = path.rect(0, 0, 1, 1)
111 \end{verbatim}
112 Here, the first two arguments specify the origin of the rectangle
113 while the second two arguments define its width and height,
114 respectively. For more details on the predefined paths, we
115 refer the reader to Sect.~\ref{path:predefined}.
117 \section{Path operations}
119 Often, one not only wants to stroke or fill a path on the canvas
120 but before do some geometrical operations with it. For instance, one
121 might want to intersect one path with another one and the split the
122 paths at the intersection points and then join the segments together
123 in a new way. \PyX{} supports such tasks by means of a number
124 of path methods, which we will introduce in the following.
126 Suppose you want to draw the radii to the intersection points of a
127 circle with a straight line. This task can be done using the following
128 code which gives the result shown in Fig.~\ref{fig:radii}
129 \verbatiminput{radii.py}
130 \begin{figure}
131 \centerline{\includegraphics{radii}}
132 %\caption{}
133 \label{fig:radii}
134 \end{figure}
135 Passing another path, here \var{line}, to the \method{intersect()} method
136 of \var{circle}, we obtain a tuple of parameter values of the
137 intersection points. The first element of the tuple is a list of
138 parameter values for the path whose \method{intersect()} method we have
139 called, the second element is the corresponding list for the path
140 passed as argument to this method. In the present example, we only
141 need one list of parameter values, namely \var{isects_circle}.
142 Iterating over the elements of this list, we draw the radii, using the
143 \method{at()} path method to obtain the point corresponding to the
144 parameter value.
146 Another powerful feature of \PyX{} is its ability to split paths at a
147 given set of parameters. For instance, in order to fill in the
148 previous example the segment of the circle delimited by the straight
149 line (cf.\ Fig.~\ref{fig:radii2}), you first have to construct a path
150 corresponding to the outline of this segment. The following code
151 snippet does yield this \var{segment}
152 \begin{verbatim}
153 arc1, arc2 = circle.split(isects_circle)
154 arc = arc1.arclen()<arc2.arclen() and arc1 or arc2
156 isects_line.sort()
157 line1, line2, line3 = line.split(isects_line)
159 segment = line2 << arc
160 \end{verbatim}
161 \begin{figure}
162 \centerline{\includegraphics{radii2}}
163 %\caption{}
164 \label{fig:radii2}
165 \end{figure}
166 Here, we first split the circle using the \method{split()} method passing
167 the list of parameters obtained above. Since the circle is closed,
168 this yields two arc segments. We then use the \method{arclen()}, which
169 returns the arc length of the path, to find the shorter of the two
170 arcs. Before splitting the line, we have to take into account that
171 the \method{split()} method only accepts a sorted list of parameters.
172 Finally, we join the straight line and the arc segment. For
173 this, we make use of the \verb|<<| operator, which not only adds
174 the paths (which could be done using \samp{line2 + arc}), but also
175 joins the last subpath of \var{line2} and the first one of
176 \var{arc}. Thus, \var{segment} consists of only a single subpath
177 and filling works as expected.
179 An important issue when operating on paths is the parametrisation
180 used. Internally, \PyX{} uses a parametrisation which uses an interval
181 of length $1$ for each path element of a path. For instance, for a
182 simple straight line, the possible parameter values range from $0$ to
183 $1$, corresponding to the first and last point, respectively, of the
184 line. Appending another straight line, would extend this range to a
185 maximal value of $2$. You can always query this maximal value using
186 the \method{range()} method of the \class{path} class.
188 However, the situation becomes more complicated if more complex
189 objects like a circle are involved. Then, one could be tempted to
190 assume that again the parameter value range from $0$ to $1$, because
191 the predefined circle consists just of one \class{arc} together with a
192 \class{closepath} element. However, as a simple \samp{path.circle(0,
193 0, 1).range()} will tell, this is not the case: the actual range is
194 much larger. The reason for this behaviour lies in the internal path
195 handling of \PyX: Before performing any non-trivial geometrical
196 operation with a path, it will automatically be converted into an
197 instance of the \class{normpath} class (see also
198 Sect.~\ref{path:normpath}). These so generated paths are already
199 separated in their subpaths and only contain straight lines and
200 B\'ezier curve segments. Thus, as is easily imaginable, they are much
201 simpler to deal with.
203 A unique way of accessing a point on the path is to use the arc length
204 of the path segment from the first point of the path to the given
205 point. Thus, all \PyX{} path methods that accept a parameter value
206 also allow the user to pass an arc length. For instance,
207 \begin{verbatim}
208 from math import pi
210 pt1 = path.circle(0, 0, 1).at(arclen=pi)
211 pt2 = path.circle(0, 0, 1).at(arclen=3*pi/2)
213 c.stroke(path.path(path.moveto(*pt1), path.lineto(*pt2)))
214 \end{verbatim}
215 will draw a straight line from a point at angle $180$ degrees (in
216 radians $\pi$) to another point at angle $270$ degrees (in radians
217 $3\pi/2$) on the unit circle.
219 More information on the available path methods can be found
220 in Sect.~\ref{path:path}.
222 \section{Attributes: Styles and Decorations}
224 XXX to be done