## Monday, February 22, 2010

When to use "for i = do" and a look at drawing with cairo.

I was fiddling with my updated flowers conky, drawing a bumpy circle with cairo to use as the flowers center. Drawing with cairo via a text file isn't the most user friendly thing you can do, but with some experiementation I had worked out a good way to draw a repeating apttern in a cicle using the cairo_curve_to command.

The approach involved defining a set of circles, and then using the mathematics available in Lua to generate plot points.

Like so:
but first I need to set up some variables!
inner=110
outer1=120

outer2=118
outer3=120

The 4 lines above set out the radius of the circles from which I will be calculating my plot points.
varth1=0.53
varth2=0.42
The above 2 lines will be seen in action later.
hori=150
vert=200

These are positional setups.
Now I can do the drawing.

cairo_translate (cr, hori, vert)
cairo_move_to (cr, 0, 0)
I like to use the translate command for positioning figures. The reason for this is that it allows you to assign the figure "local" coordinates of 0,0 so that you can apply rotations to the figures without cauing problems. When using trandlate in a drawing function you always need to translate back at the bottom of the function. In this case I would translate back with:
"cairo_translate (cr, -hori, -vert)

--inner arc from cairo_set_source_rgba (cr, 1, 1, 1, 1);
text_arc=((2*math.pi/24)*(3))

txs1=0+inner*(math.sin(text_arc))
tys1=0-inner*(math.cos(text_arc))

Above I am setting a starting point for our drawing. The first line is calculating radians for points around the circle. In this case I have divided the circle up into 24 pieces and we are getting the radian value for point number 3. Theres no reason I started at 3, thats just how it happened.
I am also using comments to remind me whats what.

--outer arc through bump1
text_arc=((2*math.pi/24)*(3+varth1))
txs2=0+outer1*(math.sin(text_arc))

tys2=0-outer1*(math.cos(text_arc))
This is the "control" point that will determine the extent of the curve.
Take a look here to see how cairo works out its curves (scroll down a little for curves.)

--outer arc to
text_arc=((2*math.pi/24)*(4))

txs3=0+outer2*(math.sin(text_arc))

tys3=0-outer2*(math.cos(text_arc))
cairo_move_to (cr, txs1, tys1)

This is the coodinates of where the curve will finish.

cairo_curve_to (cr, txs1, tys1, txs2, tys2, txs3, tys3)
And above the curve is being drawn. Because I am drawing very simple curves (arcs really but I find the use of curve_to easier and more understandable than arc_to) I am basically getting this effect: So the above drawing made a curve out, now we will curve back in, from point txs3,tys3 to coordinates txs5,tys5 and that curve will be controlled by point txs4,tys4 as below.

--outer arc through
text_arc=((2*math.pi/24)*(4+varth2))
txs4=0+outer3*(math.sin(text_arc))
tys4=0-outer3*(math.cos(text_arc))
--outer arc to
text_arc=((2*math.pi/24)*(5))
txs5=0+inner*(math.sin(text_arc))
tys5=0-inner*(math.cos(text_arc))
cairo_curve_to (cr, txs3, tys3, txs4, tys4, txs5, tys5)

This gives me one bump like this: Then originally I did this:
--outer arc through bump2
text_arc=((2*math.pi/24)*(5+varth1))
txs6=0+outer1*(math.sin(text_arc))
tys6=0-outer1*(math.cos(text_arc))
--outer arc to
text_arc=((2*math.pi/24)*(6))
txs7=0+outer2*(math.sin(text_arc))
tys7=0-outer2*(math.cos(text_arc))
cairo_curve_to (cr, txs5, tys5, txs6, tys6, txs7, tys7)
--outer arc through
text_arc=((2*math.pi/24)*(6+varth2))
txs8=0+outer3*(math.sin(text_arc))
tys8=0-outer3*(math.cos(text_arc))
--outer arc to
text_arc=((2*math.pi/24)*(7))
txs9=0+inner*(math.sin(text_arc))
tys9=0-inner*(math.cos(text_arc))
cairo_curve_to (cr, txs7, tys7, txs8, tys8, txs9, tys9)
--outer arc through bump3
text_arc=((2*math.pi/24)*(7+varth1))
txs10=0+outer1*(math.sin(text_arc))
tys10=0-outer1*(math.cos(text_arc))
--outer arc to
text_arc=((2*math.pi/24)*(8))
txs11=0+outer2*(math.sin(text_arc))
tys11=0-outer2*(math.cos(text_arc))
cairo_curve_to (cr, txs9, tys9, txs10, tys10, txs11, tys11)
--outer arc through
text_arc=((2*math.pi/24)*(8+varth2))
txs12=0+outer3*(math.sin(text_arc))
tys12=0-outer3*(math.cos(text_arc))
--outer arc to
text_arc=((2*math.pi/24)*(9))
txs13=0+inner*(math.sin(text_arc))
tys13=0-inner*(math.cos(text_arc))
cairo_curve_to (cr, txs11, tys11, txs12, tys12, txs13, tys13)

etc etc...

Drawing the lines for the curves one by one, out then in, out then in... until I got all the way around, closed the path and filled it in. This was was pretty easy, just copy and paste and edit the numbers. I also pretty much knew that I didn't have to number the strings in order but I did it anyway. For example:
string=1
print (string) --> 1
string=2
print (string) -->2

But I could see that there was an obvious pattern and was a candidate for using the:

"for i = x,y do" command.

So I looked at the first bump I had drawn, assigned i a value of 1 and then edited the values so that the points I was plotting on the circles were expressed via i, point 3 being i+2 and point 4 being i+3. And setting up a string to supply the number of points, substituting the string into the code. What I ended up with is this:

number=12
for i = 1,number do
cairo_set_source_rgba (cr, 1, 1, 1, 1);
--inner arc from
text_arc=((2*math.pi/number)*(i+2))
txs1=0+inner*(math.sin(text_arc))
tys1=0-inner*(math.cos(text_arc))
--outer arc through bump1
text_arc=((2*math.pi/number)*((i+2)+varth1))
txs2=0+outer1*(math.sin(text_arc))
tys2=0-outer1*(math.cos(text_arc))
--outer arc to
text_arc=((2*math.pi/number)*(i+3))
txs3=0+outer2*(math.sin(text_arc))
tys3=0-outer2*(math.cos(text_arc))
cairo_move_to (cr, 0, 0)
cairo_line_to (cr, txs1, tys1)
cairo_curve_to (cr, txs1, tys1, txs2, tys2, txs3, tys3)
--outer arc through
text_arc=((2*math.pi/number)*((i+3)+varth2))
txs4=0+outer3*(math.sin(text_arc))
tys4=0-outer3*(math.cos(text_arc))
--outer arc to
text_arc=((2*math.pi/number)*(i+4))
txs5=0+inner*(math.sin(text_arc))
tys5=0-inner*(math.cos(text_arc))
cairo_curve_to (cr, txs3, tys3, txs4, tys4, txs5, tys5)
end
cairo_close_path (cr)
cairo_fill (cr)

But it wasn't quite so simple. For some reason the shapes of the bumps changed (they were alot more pointy instead of round) and I had to add these lines or the middle of my drawing did not get filled in:
cairo_move_to (cr, 0, 0)
cairo_line_to (cr, txs1, tys1)

But it was quite easy to fiddle with the settings to make the bumps rounded and voila!.

So when you have a repeating pattern, of you want multiple instances of a figure, then it's a good bet that you can cut down the line count by using this technique. You can also affect other things with the "i" term. Achieving a position change from figure to figure you could do something like:

horizontal = 10*i
vertical = 10

in this case you would get a horizontal line of figures, spaced 10 apart.

But you could also do something like:
cairo_set_source_rgba (cr, 1, 1, 1, 0+(i/10)

So if "i" has a range of 1 to 10, the first figure would have an alpha of 0.1 while the tenth figure would have an alpha of 1.

Or you can fit "i" into any number of equations and have it affect any number of variables.

And if you want more figures, just increase the range of "i" without having to add any additional lines of code.