Cairo is a 2D graphics library with support for multiple output devices. Currently supported output targets include the X Window System, Quartz, Win32, image buffers, PostScript, PDF, and SVG file output. Experimental backends include OpenGL, XCB, BeOS, OS/2, and DirectFB.

This little article will take the fast train of "quick and dirty" to show you how to draw something with the Cairo lib. As an example we will use a six players chess board with hexagonal tiles.

In the beginning we have to spend some thoughts on the geometry of hexagons and how to put them to their places in the easiest way.

The above figure shows the geometric properties of a hexagon. The shown values are obtained using simple trigonometry. The figure even shows two import facts about the Cairo lib:

- The orientation of the cartesian system.
- By default the origin of the system is in the upper left corner of an image, but for trigonometry the origin was set in the middle of the hexagon.

The cartesian coordinate system of the cairo module can be treated as known from mathematics. The *Context* class provides methods for coordinate transformations as indicated in the figure below.

In the figure we can see the two most important coordinate transformations: translation (1 and 2) and rotation (3). The *Context*'s coordinate system can be altered in any arbitrary way. Additionally some sort of stack is provided which remembers all transformations.

The *save* method of the *Context* remembers the current coordinate system and the *restore* method changes the coordinate system back to the last stored system. Thus one can easily undo a transformation.

In this example we will use this knowledge to write our script in an elegant way.

The idea is similar to offset printing. In the end we want an image containing colored pattern, but to get there we have to do some steps:

- We have to define an image plane the so-called
*ImageSurface*. - We define the pattern in a so-called
*Context*. - Afterwards -- or prior to that -- we choose a color.
- Then we put the pattern to the surface. Therefore we can choose between printing the lines (stroke) or filling the pattern (fill).
- Finally we have to save the image to a file.

First of all we need to define the surface for the image and a context. the ImageSurface class expects three arguments specifying color format, width and height (integer, in pixels).

```
surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, width, height)
ctx = cairo.Context (surface)
```

If you would like to have a solid background color instead of a transparent background, you can fill the surface with a rectangle. Therefore you need a color, of course.

```
ctx.set_source_rgb(1,1,1)
ctx.rectangle(0,0,width,height)
ctx.fill()
```

As you can see above, we have chosen a RGB color space. In cairo one does not use the hexadecimal notation but the value range [0,1]. Once a *source color* is specified it is used until it is changed. So if we want to draw something else to the surface, we have to change the color therebefore. Otherwise we will still see only a white surface.

The rectangle has upper left corner coordinates, here (0,0), and an extent on the surface, here (width, height). Using the fill method the entire surface is painted white.

Now that we have painted our background white we want to actually draw something more complicated. A hexagon is a slightly less basic structure and not provided by a method of the *Context*. Therefore we have to draw it ourselves using lines (aka path).

```
field_colors = ((1,1,1), (0,0,0), (1,0,0))
p = (
(math.sqrt(3)*D/4., D/4.),
(0, D/2.),
(-math.sqrt(3)*D/4., D/4.),
(-math.sqrt(3)*D/4., -D/4.),
(0, -D/2.),
(math.sqrt(3)*D/4.,-D/4.)
)
def hexagon(ctx,color):
for pair in p:
ctx.line_to(pair[0],pair[1])
ctx.close_path()
ctx.set_source_rgb(0,0,0)
ctx.stroke_preserve()
ctx.set_source_rgb(*field_colors[color%3])
ctx.fill()
```

In *p* the coordinates of the edges of the hexagon are stored. The tuple *field_colors* holds three different RGB color values (white, black, red). Our function *hexagon* expects the *Context* instance and an integer in the variable *color*.

The function *hexagon* adds the coordinates of the six corners to the path by using the method *line_to*. So far we have a hexagon with one side open. To close it we simly call *close_path* or we add the coordinates of the first corner again. As you can see each hexagon will get a black contour. The method *stroke_preserve* paints the line and keeps the previous structure in the *Context* so that we can reuse it -- which we do by filling it with another color.

Now that we have a function that draws nice hexagons, we can start to fill the entire surface with them. For our hexagonal chess board we want to have a defined number of fields on each side and no unneccessary white space, so we do some math to fit the chess board perfectly onto the surface:

```
D = 33
# diameter of hexagon in pixels
shift_x = (math.sqrt(3)*D/2., 0)
# vectorial distance to next hexagon in row
shift_y = (math.sqrt(3)*D/4., 3*D/4.)
# vectorial distance to hexagon in next row
side_fields = 7
# number of fields along each side
width = int((2*side_fields-1)*math.sqrt(3)*D/2.+3*D/4.)+1
# with of surface plus some border in pixels
height = int((2*side_fields-1)*3*D/4.+2*D)+1
# with of surface plus some border in pixels
```

Having all geometric information together we can now start to iterate through the chess board and draw hexagons:

```
fields_in_line = side_fields
increment, decreasing = 1, 0
ctx.translate((side_fields-1)*math.sqrt(3)*D/4.,D)
for j in range(2*side_fields-1):
if fields_in_line > 2*side_fields-2:
increment = -1
for i in range(fields_in_line):
ctx.translate(shift_x[0],shift_x[1])
hexagon(ctx,i+j+decreasing)
ctx.translate(-fields_in_line*shift_x[0],-fields_in_line*shift_x[1])
ctx.translate(-increment*shift_y[0],shift_y[1])
if increment == -1:
decreasing += 1
fields_in_line += increment
surface.write_to_png('hexach.png')
```

First we translate to the center of the hexagon in the upper left corner of the chess board. Then we iterate through hexagons of a row by translating the coordinate system by shift_x. At the end of the row we have to get back to the first hexagon in the next row. Therefore we go back to the first hexagon's position in the current row and depending on whether the number of hexagons in the next row is in- or decresed we go the closest position left or right in the next row (see image on the left).

In each row iteration we increase the number of hexagons by one until we reach the maximum number of hexagons per row. Then we decrease the number of hexagons per row by one.

Finally we save the image to the file hexach.png.

This is the final image.