fix race condition by Michael J Gruber
[PyX/mjg.git] / examples / drawing2 / ellipse.txt
blob36493252b3a6525fd02e310a435da7a40def4e2b
1 Applying transformations on a path or canvas: Drawing an ellipse
3 PyX does not directly provide a path corresponding to an ellipse. This example
4 shows two ways how to draw an ellipse using affine transformations. ...
6 In order to create an ellipse, we best start from a unit circle centered around
7 the point of origin of the coordinate system (here: `circ`). In variant 1, we
8 tell PyX to apply a couple of affine transformations before stroking this
9 circle on the canvas `c`. These affine transformations are contained in the
10 `trafo` module. We first use `trafo.scale` to apply a non-uniform scaling,
11 namely by a factor of 2 in x-direction and a factor of 0.9 in y-direction.
12 Doing so, we define the two principle axes of the ellipse. In a next step, we
13 rotate with `trafo.rotate` the ellipse by an angle of 45 degrees in the
14 mathematical positive direction, i.e. counter-clockwise. Last, we shift the
15 origin of the ellipse to the desired point by applying a `trafo.translate`
16 operation.
18 ! Note that the order of the transformations matters. If you, for instance, would
19 first translate the ellipse, the later scaling would also affect the distance
20 by which you have shifted the ellipse. PyX applies the transformations one after
21 the other, from left to right, so the example shown above does the correct thing.
23 ! You can also treat transformations as mathematical objects (they are
24 represented by two-dimensional matrices together with an offset vector) and
25 multiply them using the `*` operator. Note, however, that mathematically,
26 transformations are applied from right to left, such that variant 1
27 would need to be written as
29     c.stroke(circ, [trafo.translate(1,0) * trafo.rotate(45) * trafo.scale(sx=2, sy=1.5)])
31 ! PyX also provides some convenience methods for applying certain
32 transformations with a given point as the origin. These allow one to write variant 1
33 in yet another form
35     c.stroke(circ, [trafo.scale(sx=2, sy=1.5, x=1, y=0), trafo.rotate(45, x=1, y=0)])
37 where we have started already from a circle centered around the desired point 1,0.
39 When telling the stroke method to apply a number of transformations, we use
40 that a transformation is a so-called deformer. Deformers take an original path,
41 do some operation on it and return the modified version. PyX thus internally
42 converts the circle into a path representing the ellipse. Alternatively, we can
43 also let the PostScript or PDF interpreter do the same transformation. This is what
44 is shown in variant 2. There, we first stroke the circle on a new canvas `sc`.
45 When inserting this canvas in the original canvas `c`, we again pass a set of
46 transformations as arguments. Since PyX cannot deform an entire canvas, it just
47 writes these transformations into the output file. If you compare the resulting
48 output (the right ellipse) with the one of variant 1, you will notice a
49 difference, though: when transforming a whole canvas, the lineshape is
50 transformed as well. Often, this is not the intended result, so you better
51 transform individual objects when stroking or filling them.
53 !! When you look at the EPS or PDF output generated by the example, you will
54 notice that the bounding box is too large. The reason for this artefact lies in
55 the way PyX calculates the bounding box for a transformed canvas: It simply
56 applies the transformation to the bounding box of the canvas and takes the
57 bounding box of this new object. While this certainly yields a bounding box of
58 the canvas, it does not necessarily yield a minimal one. To see this, you just
59 have to consider the two extreme cases of a circle, which is rotationally
60 invariant, and a square, which only posseses a discrete rotational symmetry.
61 Whereas the minimal bounding box of the circle does not change under rotations
62 around its center, the same is not true for the square. When you rotate a
63 circle by applying a deformer (as in variant 1), PyX will thus calculate the
64 correct bounding box. On the other hand, when you insert the circle into a
65 canvas and afterwards transform this canvas (as in variant 2), PyX cannot
66 distinguish between a circle and a square anymore and calculates a too large
67 bounding box.