Skip to content

Using Lua scripts (Part 04): Drawing rectangles, circles and arcs

Jake Ob edited this page Jan 4, 2022 · 5 revisions

iv: Drawing rectangles, circles and arcs

There are a few things that cairo can draw other than lines, namely rectangles, arcs (and circles), and curves.

Rectangles

We could use lines as in the triangle to draw ourselves a rectangle, but Cairo has the ability to draw rectangles built-in so we might as well use it: cairo_rectangle (cr, x, y, width, height). The x, y coordinates are the top left corner of the rectangle.

Just like our triangle, we can set up our shape like so:

-- Settings.
line_width = 5
top_left_x = 20
top_left_y = 20
rec_width = 100
rec_height = 50
red = 1
green = 0
blue = 0
alpha = 1

-- Draw it.
cairo_set_line_width (cr, line_width)
cairo_rectangle (cr, top_left_x, top_left_y, rec_width, rec_height)
cairo_set_source_rgba (cr, red, green, blue, alpha)

Then we can use cairo_stroke (cr) to draw the outline or cairo_fill (cr) to fill it in.

Or if you want the rectangle filled with one color and line in a different color, you would first fill with cairo_fill_preserve, then set our second color and draw our line with cairo_stroke.

-- Settings.
line_width = 5
top_left_x = 20
top_left_y = 20
rec_width = 100
rec_height = 50
fill_red = 1
fill_green = 1
fill_blue = 1
fill_alpha = 1
line_red = 1
line_green = 0
line_blue = 0
line_alpha = 1
-- Draw it.
cairo_set_line_width (cr, line_width)
cairo_rectangle (cr, top_left_x, top_left_y, rec_width, rec_height)
cairo_set_source_rgba (cr, fill_red, fill_green, fill_blue, fill_alpha)
cairo_fill_preserve (cr)
cairo_set_source_rgba (cr, line_red, line_green, line_blue, line_alpha)
cairo_stroke (cr)

NOTE: with rectangles there is no point in setting line_cap like we did for lines as there are no "loose ends" to the rectangle. There is also no need to use cairo_close_path, as it is closed automatically. However, setting how the lines join will affect the look of the rectangle when we use cairo_stroke.

cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND)
cairo_stroke (cr)

This will give rounded corners, for example.

Line width, line join type and end cap type do not affect the fill command.

Arcs and circles

There are 2 commands for arcs, cairo_arc (cr, center_x, center_y, radius, start_angle, end_angle) and cairo_arc_negative (cr, center_x, center_y, radius, start_angle, end_angle).

cairo_arc draws the arc clockwise while cairo_arc_negative draws the arc anticlockwise.

The first obstacle to drawing arcs is that you need to enter your start and end angles in radians rather than degrees. So we need to know how to convert between the two: radians = degrees * (pi / 180).

Lua has a number of built-in math functions and one of them, math.pi, gives you the value of pi.

The other quirk about using the arc drawing command is that angle 0 isn't the top of the circle as you would expect.

Rather angle 0 is the rightmost point in the circle (which I would call 90 degrees).

These things don't matter if we only want to draw a complete circle. We just need the following:

center_x = 100
center_y = 100
radius = 50
start_angle = 0
end_angle = 2 * math.pi -- Same thing as 360 degrees.
cairo_arc (cr, center_x, center_y, radius, start_angle, end_angle)
cairo_stroke (cr)

NOTE: whenever you use stroke to draw something you need to set a line_width. Also, with stroke, if you are going to see line ends then altering the line cap type will affect those ends. The other setting that can affect stroke is the line join type if creating a path from multiple elements arcs along with curves and lines can be all put together to form a single path. In the example above you will also need to set color and alpha as before.

Change cairo_stroke to cairo_fill to fill the circle in or use the previously described method for fill and line. I wont necessarily put these setup lines or all the available options into code examples from now on.

BUT if we don't want a full circle then we need to worry about radians and setting angles. To get an 1 / 4 circle, from the topmost point of the circle, clockwise to the rightmost point, we can do several things ...

My code would look like this:

center_x = 100
center_y = 100
radius = 50
start_angle = 0
end_angle = 90
cairo_arc (cr, center_x, center_y, radius,
		(start_angle - 90) * (math.pi / 180), (end_angle-90) *
		(math.pi / 180))
cairo_stroke (cr)

Since I only want to enter angles as degrees I can put the conversion calculations into the arc command, eg (start_angle - 90)*(math.pi / 180).

First I want the top of my circle to be 0 degrees (or 360 if you like), so I have to compensate for the arc command quirk by subtracting 90 degrees from the value I set. Then I need to convert to radians by multiplying by math.pi / 180.

cairo_stroke to get the line.

This in the code ...

cairo_close_path (cr)
cairo_stroke (cr)

... will result in a straight line drawn from the end of the arc back to the beginning. cairo_fill (cr) instead of cairo_stroke would result in a filled-in bump with a flat bottom.

Here is cairo_stroke without close_path, cairo_stroke with close path and cairo_fill: .

Using arc or arc_negative.

So if you give cairo_arc a start angle of 270 and an end angle of 90, you get the top half of a circle. You could also set start = 270, end = 450 (360 + 90) and get the same thing. start=-90, end = 90 would also work.

If you give cairo_arc_negative a start angle of 270 and an end angle of 90 you get the bottom half of a circle.

This can be important for drawing paths, as you want your path to be a continuous progression (imagine drawing a line on some paper without lifting the pen). Also remember that using the close_path or fill commands (which close the path automatically) you draw a line from where you ended to where you started. You can get unexpected fill and stroke results if you piece together your path in a non-continuous manner.

The other time you may want to use arc or arc negative is to make a ring meter go one way or another. While you could achieve the effect just using, for example, arc (which naturally draws in a clockwise direction) for both, you have to think a bit harder about the math to get the ring going in the opposite direction.

Curves

I'll leave curves for another time. They can be quite tricky to set up and use.

Clone this wiki locally