~ubuntu-branches/debian/sid/pyx/sid

« back to all changes in this revision

Viewing changes to examples/drawing2/ellipse.txt

  • Committer: Bazaar Package Importer
  • Author(s): Stuart Prescott
  • Date: 2011-05-20 00:13:52 UTC
  • mto: (9.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 8.
  • Revision ID: james.westby@ubuntu.com-20110520001352-odcuqpdezuusbbw1
Tags: upstream-0.11.1
ImportĀ upstreamĀ versionĀ 0.11.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Applying transformations on a path or canvas: Drawing an ellipse
 
2
 
 
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. ...
 
5
 
 
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.
 
17
 
 
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.
 
22
 
 
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
 
28
 
 
29
    c.stroke(circ, [trafo.translate(1,0) * trafo.rotate(45) * trafo.scale(sx=2, sy=1.5)])
 
30
 
 
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
 
34
 
 
35
    c.stroke(circ, [trafo.scale(sx=2, sy=1.5, x=1, y=0), trafo.rotate(45, x=1, y=0)])
 
36
 
 
37
where we have started already from a circle centered around the desired point 1,0.
 
38
 
 
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.
 
52
 
 
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.