1 \chapter{Basic graphics
}
7 The path module allows one to construct PostScript-like
8 \textit{paths
}, which are one of the main building blocks for the
9 generation of drawings. A PostScript path is an arbitrary shape built
10 up of straight lines, arc segments and cubic Bezier curves. Such a
11 path does not have to be connected but may also consist of multiple
12 connected segments, which will be called
\textit{sub paths
} in the
15 Usually, a path is constructed by passing a list of the path
16 primitives
\verb|moveto|,
\verb|lineto|,
\verb|curveto|, etc., to the
17 constructor of the
\verb|path| class. The following code snippet, for
18 instance, defines a path
\verb|p| that consists of a straight line
19 from the point $(
0,
0)$ to the point $(
1,
1)$
22 p = path.path(path.moveto(
0,
0), path.lineto(
1,
1))
24 Equivalently, one can also use the predefined
\verb|path| subclass
27 p = path.line(
0,
0,
1,
1)
30 While you can already do some geometrical operations with the
31 just create path (see next section), we need another
\PyX{} object
32 in order to produce the output corresponding to the path, namely
33 an instance of the
\verb|canvas| class. By convetion, we use
34 the name
\verb|c| for this instance:
38 In order to draw the path on the canvas, we use the
\verb|stroke| method
39 of the
\verb|canvas| class, i.e.,
42 c.writeEPSfile("line")
44 To complete the example, we have added a
\verb|writeEPSfile| call,
45 which writes the contents of the canvas into the given file.
47 Let us as second example define a path which consists of more than
50 cross = path.path(path.moveto(
0,
0), path.rlineto(
1,
1),
51 path.moveto(
1,
0), path.rlineto(-
1,
1))
53 The first sub path is again a straight line from $(
0,
0)$ to $(
1,
1)$,
54 with the only difference that we now have used the
\verb|rlineto|
55 class, whose arguments count relative from the last point in the path.
56 The second
\verb|moveto| instance opens a new sub path starting at the
57 point $(
1,
0)$ and ending at $(
0,
1)$. Note that although both lines
58 intersect at the point $(
1/
2,
1/
2)$, they count as separate sub paths.
59 The general rule is that each occurence of
\verb|moveto| opens a new
60 sub path. This means that if one wants to draw a rectangle, one should
63 # wrong: do not use moveto when you want a single sub path
64 rect1 = path.path(path.moveto(
0,
0), path.lineto(
0,
1),
65 path.moveto(
1,
0), path.lineto(
1,
1),
66 path.moveto(
1,
1), path.lineto(
1,
1),
67 path.moveto(
0,
1), path.lineto(
0,
0))
69 which would construct a rectangle consisting of four disconnected
70 sub paths. Instead the correct way of defining a rectangle is
72 # correct: a rectangle consisting of a single closed sub path
73 rect2 = path.path(path.moveto(
0,
0), path.lineto(
0,
1),
74 path.lineto(
1,
1), path.lineto(
1,
0),
79 \centerline{\includegraphics{rects
}}
80 \caption{Not closed (left) and closed (midlle) rectangle. Filling a
81 path (right) always closes it automatically.
}
84 Note that for the last straight line of the rectangle (from $(
0,
1)$
85 back to the origin at $(
0,
0)$)) we have used
\verb|closepath|. This
86 directive adds a straight line from the current point to the first
87 point of the current sub path and furthermore
\textit{closes
} the sub
88 path, i.e., it joins the beginning and the end of the line segment.
89 The difference can be appreciated in Fig.~
\ref{fig:rects
}, where
90 also a filled (and at the same time stroked) rectangle is shown.
91 The corresponding code looks like
93 c.stroke(rect1,
[deco.filled(
[color.grey(
0.95)
])
])
95 The important point to remember here is that when filling a path, PostScript
96 automatically closes it.
98 Of course, rectangles are also predefined in
\PyX{}, so above we could
101 rect2 = path.rect(
0,
0,
1,
1)
103 Here, the first two arguments specify the origin of the rectangle
104 while the second two arguments define its width and height,
107 XXX arc, bezier example
109 \section{Path operations
}
111 Often, one not only wants to stroke or fill a path on the canvas
112 but before do some geometrical operations with it. For instance, one
113 might want to intersect one path with another one and the split the
114 paths at the intersection points and then join the segments together
115 in a new way.
\PyX{} supports such tasks by means of a number
116 of path methods, which we will introduce in the following.
118 Suppose you want to draw the radii to the intersection points of a
119 circle with a straight line. This task can be done using the following
120 code which gives the result shown in Fig.~
\ref{fig:radii
}
121 \verbatiminput{radii.py
}
123 \centerline{\includegraphics{radii
}}
127 Passing another path, here
\verb|line|, to the
\verb|intersect| method
128 of
\verb|circle|, we obtain a tuple of parameter values of the
129 intersection points. The first element of the tuple is a list of
130 parameter values for the path whose
\verb|intersect| method we have
131 called, the second element is the corresponding list for the path
132 passed as argument to this method. In the present example, we only
133 need one list of parameter values, namely
\verb|isects_circle|.
134 Iterating over the elements of this list, we draw the radii, using the
135 \verb|at| path method to obtain the point corresponding to the
138 Another powerful feature of
\PyX{} is its ability to split paths at a
139 given set of parameters. For instance, in order to fill in the
140 previous example the segment of the circle delimited by the straight
141 line (cf.\ Fig.~
\ref{fig:radii2
}), you first have to construct a path
142 corresponding to the outline of this segment. The following code
143 snippet does yield this
\verb|segment|
145 arc1, arc2 = circle.split(isects_circle)
146 arc = arc1.arclen()<arc2.arclen() and arc1 or arc2
149 line1, line2, line3 = line.split(isects_line)
151 segment = line2 << arc
154 \centerline{\includegraphics{radii2
}}
158 Here, we first split the circle using the
\verb|split| method passing
159 the list of parameters obtained above. Since the circle is closed,
160 this yields two arc segments. We then use the
\verb|arclen|, which
161 returns the arc length of the path, to find the shorter of the two
162 arcs. Before splitting the line, we have to take into account that
163 the
\verb|split| method only accepts a sorted list of parameters.
164 Finally, we join the straight line and the arc segment. For
165 this, we make use of the
\verb|<<| operator, which not only adds
166 the paths (which could be done using
\verb|line2 + arc|), but also
167 joins the last sub path of
\verb|line2| and the first one of
168 \verb|arc|. Thus,
\verb|segment| consists of only a single sub path
169 and filling works as expected.
171 XXX reverse, reversed, parametrisation, arclen parameters
174 \section{Module path
}
176 \subsection{Class pathel
}
178 The class
\verb|pathel| is the superclass of all PostScript path
179 construction primitives. It is never used directly, but only by
180 instantiating its subclasses, which correspond one by one to the
181 PostScript primitives.
184 \begin{tabularx
}{\linewidth}{>
{\hsize=
.7\hsize}X>
{\raggedright\arraybackslash\hsize=
1.3\hsize}X
}
185 Subclass of
\texttt{pathel
} & function \\
187 \texttt{closepath()
} & closes current subpath \\
188 \texttt{moveto(x, y)
} & sets current point to (
\texttt{x
},
190 \texttt{rmoveto(dx, dy)
} & moves current point by (
\texttt{dx
},
192 \texttt{lineto(x, y)
} & moves current point to (
\texttt{x
},
\texttt{y
})
193 while drawing a straight line\\
194 \texttt{rlineto(dx, dy)
} & moves current point by by (
\texttt{dx
},
\texttt{dy
})
195 while drawing a straight line\\
196 \texttt{arc(x, y, r,
\newline\phantom{arc(
}angle1, angle2)
} & appends arc segment in
197 counterclockwise direction with center (
\texttt{x
},
\texttt{y
}) and
198 radius~
\texttt{r
} from
\texttt{angle1
} to
\texttt{angle2
} (in degrees).\\
199 \texttt{arcn(x, y, r,
\newline\phantom{arcn(
}angle1, angle2)
} & appends arc segment in
200 clockwise direction with center (
\texttt{x
},
\texttt{y
}) and
201 radius~
\texttt{r
} from
\texttt{angle1
} to
\texttt{angle2
} (in degrees). \\
202 \texttt{arct(x1, y1, x2, y2, r)
} & appends arc segment of radius
\texttt{r
}
203 connecting between (
\texttt{x1
},
\texttt{y1
}) and (
\texttt{x2
},
\texttt{y2
}).\\
204 \texttt{rcurveto(dx1, dy1,
\newline\phantom{rcurveto(
}dx2, dy2,
\newline\phantom{rcurveto(
}dx3, dy3)
} & appends a B\'ezier curve with
205 the following four control points: current point and the points defined
206 relative to the current point by (
\texttt{dx1
},
\texttt{dy1
}),
207 (
\texttt{dx2
},
\texttt{dy2
}), and (
\texttt{dx3
},
\texttt{dy3
})
211 Some notes on the above:
213 \item All coordinates are in
\PyX\ lengths
214 \item If the current point is defined before an
\verb|arc| or
215 \verb|arcn| command, a straight line from current point to the
216 beginning of the arc is prepended.
217 \item The bounding box (see below) of B\'ezier curves is actually
218 the box enclosing the control points,
\textit{i.e.
}\ not neccesarily the
219 smallest rectangle enclosing the B\'ezier curve.
223 \subsection{Class path
}
225 The methods provided by instance of the class
\verb|path| are
226 summarized in the following table:
229 \begin{tabularx
}{1.04\linewidth}{>
{\hsize=
.7\hsize}X>
{\raggedright\arraybackslash\hsize=
1.3\hsize}X
}
230 \texttt{path
} method & function \\
231 \hline \texttt{\_\_init\_\_(*pathels)} & construct new \texttt{path}
232 consisting of \texttt{pathels}\\
233 \texttt{append(pathel)} & appends \texttt{pathel} to the end of
235 \texttt{arclen(epsilon=1e-5)} & returns the total arc length of
236 all \texttt{path} segments in PostScript points with accuracy
237 \texttt{epsilon}.$^\dagger$\\
238 \texttt{arclentoparam(lengths, \newline\phantom{arclentoparam(}epsilon=1e-5)} & returns the
239 parameter value corresponding to the lengths \texttt{lengths} (one or a list of
240 lengths). This uses arclen-calculations with accuracy
241 \texttt{epsilon}.$^\dagger$\\
242 \texttt{at(param=None,
243 \newline\phantom{at(}arclen=None)} & returns the coordinates of the point of
244 \texttt{path} corresponding to the parameter value
245 \texttt{param} or the arc length \verb|arclen|.$^\dagger$\\
246 \texttt{bbox()} & returns the bounding box of the \texttt{path}\\
247 \texttt{begin()} & return first point of first subpath of
248 \texttt{path}.$^\dagger$\\
249 \texttt{curvradius(self,
250 \newline\phantom{curvradius(}param=None,
251 \newline\phantom{curvradius(}arclen=None)} &
252 Returns the curvature radius (or None if infinite) at parameter param.
253 This is the inverse of the curvature at this parameter
254 Please note that this radius can be negative or positive,
255 depending on the sign of the curvature.
258 \texttt{end()} & return last point of last subpath of
259 \texttt{path}.$^\dagger$\\
260 \texttt{glue(opath)} & returns the \texttt{path} glued together with
261 \texttt{opath}, \textit{i.e.}\ the last subpath of \texttt{path}
262 and the first one of \texttt{opath} are joined.$^\dagger$\\
263 \texttt{intersect(opath, \newline\phantom{intersect(}epsilon=1e-5)}
264 & returns tuple consisting of two lists of parameter values
266 intersection points of \texttt{path} and \texttt{opath},
267 respectively.$^\dagger$\\
268 \texttt{range()} & returns the maximal value of the parameter value
269 \texttt{param} in the path methods\\
270 \texttt{reversed()} & returns the normalized reversed
271 \texttt{path}.$^\dagger$\\
272 \texttt{split(parameters)} & splits the path at the given list of
273 parameters (which have to be sorted in ascending order) and returns
274 a corresponding list of
275 \texttt{normpath}s.$^\dagger$\\
276 \texttt{tangent(param=None,
277 \newline\phantom{tangent(}arclen=None,
278 \newline\phantom{tangent(}length=None)} & return the tuple corresponding to
279 the tangent vector to the path at the parameter value
280 \texttt{param} or the arc length \texttt{arclen}.
281 \texttt{param} (\texttt{arclen}) has to be
282 smaller or equal to \texttt{self.range()} (\texttt{self.arclen()}),
283 otherwise an exception is raised. At discontinuities in the path, the
284 limit from below is returned. If \texttt{length} is not
285 \texttt{None}, the tangent vector will be scaled correspondingly.
287 \texttt{trafo(param=None,
288 \newline\phantom{trafo(}arclen=None} & return a trafo which maps
289 a point $(0, 1)$ to the tangent vector to the path at the parameter value
290 \texttt{param} or the arc length \texttt{arclen}.
291 \texttt{param} (\texttt{arclen}) has to be
292 smaller or equal to \texttt{self.range()} (\texttt{self.arclen()}),
293 otherwise an exception is raised. At discontinuities in the path, the
294 limit from below is returned.
296 \texttt{transformed(trafo)} & returns the normalized and accordingly
297 to the linear transformation \texttt{trafo} transformed path. Here,
298 \texttt{trafo} must be an instance of the \texttt{trafo.trafo}
303 Some notes on the above:
305 \item The bounding box may be too large, if the path contains any
306 \texttt{curveto} elements, since for these the control box,
307 \textit{i.e.}, the bounding box enclosing the control points of
308 the B\'ezier curve is returned.
309 \item The $\dagger$ denotes methods which require a prior
310 conversion of the path into a \verb|normpath| instance. This is
311 done automatically, but if you need to call such methods often,
312 it is a good idea to do the conversion once for performance reasons.
313 \item Instead of using the \verb|glue| method, you can also glue two
314 paths together with help of the \verb|<<| operator, for instance
316 \item In the methods accepting both a parameter value \verb|param| and
317 an arc length \verb|arclen|, exactly one of these arguments has to
321 \subsection{Class normpath}
323 The \texttt{normpath} class represents a specialized form of a
324 \texttt{path} containing only the elements \verb|moveto|,
325 \verb|lineto|, \verb|curveto| and \verb|closepath|. Such normalized
326 paths are used during all of the more sophisticated path operations
327 which are denoted by a $\dagger$ in the above table.
330 Any path can easily be converted to its normalized form by passing it
331 as parameter to the \texttt{normpath} constructor,
335 Additionally, you can specify the accuracy (in points) which is used
336 in all \verb|normpath| calculations by means of the keyword argument
337 \verb|epsilon|, which defaults to $10^{-5}$. Note that the sum of a
338 \verb|normpath| and a \verb|path| always yields a \verb|normpath|.
340 \subsection{Subclasses of path}
342 For your convenience, some special PostScript paths are already defined, which
343 are given in the following table.
346 \begin{tabularx}{\linewidth}{l>{\raggedright\arraybackslash}X}
347 Subclass of \texttt{path} & function \\
349 \texttt{line(x1, y1, x2, y2)} & a line from the point
350 (\texttt{x1}, \texttt{y1}) to the point (\texttt{x2}, \texttt{y2})\\
351 \texttt{curve(x0, y0, x1, y1, x2, y2, x3, y3)} & a B\'ezier curve with
352 control points (\texttt{x0}, \texttt{y0}), $\dots$, (\texttt{x3}, \texttt{y3}).\\
353 \texttt{rect(x, y, w, h)} & a rectangle with the
354 lower left point (\texttt{x}, \texttt{y}), width~\texttt{w}, and
355 height~\texttt{h}. \\
356 \texttt{circle(x, y, r)} & a circle with
357 center (\texttt{x}, \texttt{y}) and radius~\texttt{r}.
368 %%% TeX-master: "manual.tex"