Skip to content

Using Lua scripts (Part 03): Drawing lines

Chahak Mehta edited this page Dec 4, 2020 · 5 revisions

iii: Drawing lines

How to draw a straight line!

One of the basic things to be able to do is to draw a line. There are several commands we need to consider for lines.

line thickness cairo_set_line_width (cr, 1)

NOTE, you can see here and in other examples the general form that the Cairo commands take is cairo_something (cr, settings). We set the value of cr in our main function's setup lines. Every time you use a Cairo command, the first thing within the curved brackets will be cr.

Line end cap:

cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT)
or
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND)
or
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE)

See here for more info: Cairo Manual. The default is BUTT, so that is what you get if you don't set up the cap type.

And then a command we have seen before, cairo_set_source_rgba (cr, 1, 1, 1, 1).

And we need to specify where the line is going to start: cairo_move_to (cr, 100, 100).

We could do our setup like we did for our text.

line_width = 1
line_cap = CAIRO_LINE_CAP_BUTT
red, green, blue, alpha = 1, 1, 1, 1
startx = 100
starty = 100
---------------------------
cairo_set_line_width (cr, line_width)
cairo_set_line_cap  (cr, line_cap)
cairo_set_source_rgba (cr, red, green, blue, alpha)
cairo_move_to (cr, startx, starty)

Now we need to draw the line and there are two ways to do it: you can specify the ending coordinates directly -- cairo_line_to (cr, 200, 100) -- or you can specify where the line should go relative to the start -- cairo_rel_line_to (cr, 100, 0).

In this case we get the same result ... In the first example we start at 100, 100 and tell Cairo to draw a line to 200, 100.

In the second example we start at 100, 100 and tell Cairo to draw a line to a point 100 pixels to the right and 0 pixels down from where we started.

So both examples will produce a horizontal line 100 pixels long.

Once we have finished specifying the coordinates we then tell Cairo to actually draw the line (cairo_stroke (cr)).

I tend to use the absolute method -- "line_to" -- rather than the relative method.

So altogether, we have:

line_width = 1
line_cap = CAIRO_LINE_CAP_BUTT
red, green, blue, alpha = 1, 1, 1, 1
startx = 100
starty = 100
endx = 200
endy = 100
----------------------------
cairo_set_line_width (cr, line_width)
cairo_set_line_cap  (cr, line_cap)
cairo_set_source_rgba (cr, red, green, blue, alpha)
cairo_move_to (cr, startx, starty)
cairo_line_to (cr, endx, endy)
cairo_stroke (cr)

About line thickness. We set a line from 100, 100 to 200, 100 but setting a line thickness greater than 1 will affect not only what the line looks like but where it appears to have been drawn. If we set, for example, a line width of 10, then the line will be 5 pixels wide to one side of the base line, and 5 pixels wide to the other side.

That is, the top left corner of the line will actually be at 100, 95 and the bottom left corner at 100, 105 (remember that the larger the x and y numbers, the more right and down they are) -- the line will be more of a rectangle.

Making the line more interesting

But with some math and tweaking we can easily make this line change in length relative to a Conky object.

Once you get the idea of using variables and strings to affect the coordinates and other values of drawn objects then you are a good way there and everything else is just the complexity of the interactions and what is being drawn.

For example, we want to make a cpu usage indicator line cpu_perc = tonumber (conky_parse ("${cpu}")). I will use tonumber just to make sure ... it can be easy to get lost in curved brackets when using compound commands like this :).

This will output a number between 0 and 100. The line we have already drawn is 100 pixels long so its easy enough to do the following:

cpu_perc = tonumber (conky_parse ("${cpu}"))
line_width = 1
line_cap = CAIRO_LINE_CAP_BUTT
red, green, blue, alpha = 1, 1, 1, 1
startx = 100
starty = 100
endx = startx + cpu_perc
endy = starty
----------------------------
cairo_set_line_width (cr, line_width)
cairo_set_line_cap  (cr, line_cap)
cairo_set_source_rgba (cr, red, green, blue, alpha)
cairo_move_to (cr, startx, starty)
cairo_line_to (cr, endx, endy)
cairo_stroke (cr)

This is what is going to make the line move: endx = startx + cpu_perc. The horizontal end point of my line will change in relation to the value of cpu_perc as measured through the Conky variable ${cpu}.

I have made a couple of other changes ... instead of saying endx = 100 + cpu_perc I have put endx = startx + cpu_perc and instead of endy = 100 I have endy = starty.

Using this method, all I have to do is edit startx and starty and those changes will follow on to the other strings.

When I am writing scripts I find it timesaving to set as few "absolute" vales as possible. this gives you less things to find later on when you want to make changes.

You can do more with lines than just draw lines.

You can use lines to draw shapes. You can then have these shapes as outlines or fill them in.

A triangle, for example. There are some other things to consider when using multiple lines. One is how to join the lines together, which is set by the following:

cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER)
or
cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL)
or
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND)

See here for more details Cairo manual (line join).

MITER is the default, so that is what you get when you don't set line join type

We can also use this command: cairo_close_path (cr). Close path tells Cairo that the lines should be joined together at the ends to make one continuous line.

This is what you get without close_path on the left and with close_path on the right, and using the default MITER line join.

NOTE, as soon as you set cairo_close_path, the setting for line cap type becomes redundant, as there are no longer any line ends.

Here is the code for the figure on the right above.

line_width = 20
line_cap = CAIRO_LINE_CAP_BUTT 	-- We don't need this any more after closing
								-- 		the path.
line_join = CAIRO_LINE_JOIN_MITER -- but this will still affect how the lines
								  --	look
red, green, blue, alpha = 1, 1, 1, 1
startx = 100
starty = 100
pointx = startx + 100
pointy = starty + 100
endx = pointx - 100
endy = pointy
----------------------------
cairo_set_line_width (cr, line_width)
cairo_set_line_cap  (cr, line_cap)
cairo_set_source_rgba (cr, red, green, blue, alpha)
cairo_move_to (cr, startx, starty)
cairo_line_to (cr, pointx, pointy)
cairo_line_to (cr, endx, endy)
cairo_line_to (cr, startx, starty)
cairo_set_line_join (cr, line_join)
cairo_close_path (cr)
cairo_stroke (cr)

NOTE, as you can see, you don't have to draw each individual line, as you are instead drawing a "path". Imagine a pen being drawn on paper in one continuous line from point to point.

You only need to use cairo_stroke at the end to draw along the path you set to see the lines.

It is also good to know that by using the close_path command we don't actually need to draw the third side of the triangle. When you use close_path Cairo will draw a line from wherever you stop back to the start.

If we want to fill in the triangle we use cairo_fill (cr) instead of cairo_stroke (cr).

But with cairo_fill the setting for line width no longer applies. Also with fill the line join type no longer applies.

The fill fills in only the triangle bound by the coordinates we set.

ALSO, with cairo_fill, we need not use the close_path command, as the fill command will automatically close the path in order to generate a boundary area to fill in ... but it never hurts to use the close_path command and nothing bad will happen if you set redundant commands :D.

For example I could specify all 3 sides of the triangle, close_path and then cairo_fill and I will still get a filled-in triangle! Lua code can be quite forgiving.

Here is stroke vs fill:

And if you want the shape outlined and filled (for example in different colors) you would first use cairo_fill_preserve (instead of just cairo_fill), then set our second color and use cairo_stroke to draw the outline.

cairo_set_line_width (cr, 20)
cairo_move_to (cr, 100, 100) -- Start point.
cairo_line_to (cr, 200, 200) -- Diagonal line down.
cairo_line_to (cr, 100, 200) -- Horizontal line.
cairo_close_path (cr) -- Draws vertical line back to start.
cairo_set_source_rgba (cr, 1, 1, 1, 1) -- White.
cairo_fill_preserve (cr) -- Fills in the triangle in white.
cairo_set_source_rgba (cr, 1, 0, 0, 1) -- Red.
cairo_stroke (cr) -- Draws the triangle outline in red.
Clone this wiki locally