Monday, February 15, 2010


Here is another Lua script. For the code and to see it in action go here to my post on the crunchbang forum.


require 'cairo'

I have describe the below function in my full screen lua script. This function is here because at first I was just trying to generate numbers arranged around a circle, and I wanted all the numbers to be 3 digits constantly. When I first wrote the function It was to output numbers around a circle. I suppose I need to re-write this script for the sake of clarity :)
function addzero100(num)
if tonumber(num) <>
return "00" .. num

return "0" .. num

else
return num
end

end


The below function is the figure drawing function. This was taken from the circle-writing function I wrote for my valentines flower conky.

function circlewriting(inum, text, ival, font, fsize, radi, horiz, verti, tred, tgreen, tblue, var1, var2)
Above is the function name followed by a long list of strings that the function needs to work.

inum - "length" ie the number of instances that the data will move to. In the above image this number was 10.

text - the actual data that is being represented, in this case cpu usage.

ival - in the below function an array is used to generate the data and in that array we will be using the code line:
for i = 1, tonumber(len_t) do
len_t is the same number as "inum" above. So "i" is all the numbers between 1 and 10.

font - to set the font for the text output
fsize - sets the font size
radi - sets the radius of the circle around which the bars or text will be displayed.
horiz - horizontal, x, position.
verti - vertical, y, position
tred - output red color value
tgreen - output green color value
tblue - oputput blue color value
var1 - variable 1
var2 - variable 2

variables 1 and 2 are used to fine tune the position and orientation of the bars or text around the circle.

deg=360/inum
Above is a quick calculation to work out how many degrees there are between each bar. This will be used for the rotation of each bar.

Now comes the hard math!
text_arc=((2*math.pi/inum)*ival+var1)
txs=0+radi*(math.sin(text_arc))
tys=0-radi*(math.cos(text_arc))
The above 3 lines are equations to work out the positions of each bar around the circle.
As we all know: circumference = 2 x pi x radius
and to calculate coordinates for each point around a circle
x=SIN(degrees x n)*radius
and
y=COS(degrees x n)*radius

Where "n" is the number order of the thing that is being displayed. For example, on a clock face there are the numbers 1 to 12. So to get the x,y position of those numbers, n would be 0 for 12, 1 for 1, 2 for 2 etc.

But Lua only works with radians so the first line is converting degrees to radians.
The next 2 calculate the x and y plot points.

cairo_select_font_face (cr, font, CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);

cairo_set_font_size (cr, 22);
cairo_set_source_rgba (cr, tred, tgreen, tblue, ival/10);
The above lines are setting the font, font size and color.

Below are the lines that generate the bars. I worked out how to apply the rotation to the bars "locally" and position them with the translate command.

First the position is translated into position.
cairo_translate (cr, txs+horiz, tys+verti)
Then the bar is rotated below. You might wonder why I am taking the "deg" string, set earlier, and performing calculations on it to convert degrees into radians, when I have already done that 8 lines earlier when I was calculating the x,y plot points of my bars. The answer is simple... when I first wrote the function for the flowers.lua my math skills re circles were extremely rusty, so instead of trying to work out the x,y equations for myself, I borrowed then from dissecting londonali1010's air clock lua script (found here) without really understanding the math but knowing it worked. So when I implemented the rotation feature I wrote the calculations seperately. A case of historical artifacts. Just another thing to clean up in this script :)
cairo_rotate (cr, (deg*(ival+var2)*(math.pi/180)))
Then the rectangle is drawn at "local" position 0,0.
cairo_rectangle (cr, 0, 0, 20, (text*-1))
cairo_fill (cr)

Then the rotation is reset.
cairo_rotate (cr, ((deg*(ival+var2)*(math.pi/180)*-1)))
And finally the translation point is reset. Remember that all cairo transformations are cumulative so if these reset steps were not done then the next bar would be translated and rotated relative to the first bars position and rotation.
cairo_translate (cr, -1*(txs+horiz), -1*(tys+verti))

Here are the lines that generate the text output. I decided to comment them out as I liked the lookof the bars better. But they can be reinstated easily
--you can reinstate these lines to get a text output around the circle
--cairo_move_to (cr, txs+horiz, tys+verti);
--cairo_rotate (cr, (deg*(ival+var2)*(math.pi/180)))
--cairo_show_text (cr, (addzero100(text)))
--cairo_rotate (cr, ((deg*(ival+var2)*(math.pi/180)*-1)))
end
end
Thats the end of the figure drawing function.
Next comes the data generating function.

function conky_draw_graph()
Above is the name of the function, this is the function we will call in conky.
if conky_window == nil then return end
local cs = cairo_xlib_surface_create(conky_window.display, conky_window.drawable, conky_window.visual, conky_window.width, conky_window.height)
cr = cairo_create(cs)
Above is the "canvas" setup just set out a little differently from how I have done it before.

local updates=tonumber(conky_parse('${updates}'))
if updates==1 then
len_t=10
t1={}
end
The above lines are particularly important. Here we are initializing the variable. Basically we are telling Lua that the string called "t1" is a table. We are also setting the "length" of the figure with len_t. It took me a little figuring out to understand why these lines had to be here and why they were set only when conky update number = 1.
The reason is that if the line "t1={}" were moved below with the rest of the function (which has to start when conky updates >3 to prevent a segmentation fault when using the conky object ${cpu}) then with each cycle, the line:
"if updates> 3 then"
would be true and everything below that line would be performed and the data stored table "t" would be reset each time. We are relying on the data in the table to "remember" the past cpu usage numbers so t1={} can only be set once.
if updates> 3 then
Below is the array that will generate the data and store it in the table.
And here is the all important "i"
for i = 1, tonumber(len_t) do
Here we are saying that "i" is equal to every number from 1 to len_t (set above). So to think about this array more simply we will say that "i"=1
if t1[i+1]==nil then t1[i+1]=0 end
The above line is a error checking line to stop the "tried to call a nil value" error that can cause the script to stop functioning.
t1[i]=t1[i+1]
So when "i"=1 then
t1[1]=t1[2].
As we have seen before, numbers, or strings in square brackets relate to an entry in a table, in this case table "t1". This doesnt make much sense until we get to the next couple of lines:
if i==len_t then
t1[len_t]=tonumber(conky_parse('${cpu}'))
end
So when "i" = the upper limit of its range, len_t (which in this case is 10) then t1[10]=current cpu usage. Now everything else is set because
t1[9]=t1[10]
t1[8]=t1[9]
t1[7]=t1[8]
to
t1[1]=t1[2]

Now you might think that if everything equals everything else then why isn't t1[1] to t1[10] all the same number? What you need to think about here is how the Lua script is working with conky. Say you have your conkyrc set to update every second, then every second the Lua script is run from the top, so actually the example I gave above about how the table is set up is the wrong way round.

Because the script lines are performed in order from top to bottom here is what is happening: (or this is how it makes sense to me)

on the first pass at time 1:
t1[10]=cpu usage at time 1

then on the next pass at time 2
t1[9]=t1[10] *but* this is the t1[10] from the previous pass where t1[10]=cpu at time 1
because t1[9] is set every so slightly before t1[10] is reset to equal cpu usage at time 2

and so on it goes so that
t1[10] = current cpu usage at time t
t1[9] = cpu usage at t-1
t1[8] = cpu usage at t-2
t1[7] = cpu usage at t-3
etc...

--circlewriting (inum, text, ival, font, fsize, radi, horiz, verti, tred, tgreen, tblue, var1, var2)
Above I am reminding myself of all the variable i need to send the circlewriting function and below I have called the function with all of the relevant info.
circlewriting(len_t, t1[i], i, "Mono", 22, 40, 200, 200, 0, 0, 0, -0.25, 0)
Below we are ending the part of the function that began "if updates> 3 then" It is important to put the call to the figure writing function above this end.
end
Below we are freeing some memory, althoug in action I havnt noticed any difference from when I use these lines to when I do not use these lines.
cairo_destroy(cr)
cairo_surface_destroy(cs)
end
And then we close the function.
end
I will probably revisit this explanation again soon to go over some parts more thoroughly.

No comments:

Post a Comment