Sniff is a "Scratch-like" programming language that's designed to help Scratchers move gently from Scratch to more conventional languages. They can start writing programs, without having to learn a new language because Sniff is based on Scratch. They learn a little more about variables, compiling, syntax errors (!), and they can have fun controlling real hardware while they're doing it.

Thursday, 3 September 2015


We're always looking for fun little mathematical toys we can turn into Sniff programs, and if they look pretty then even better. I was going to do something on Lissajous figures, which are an easy way of drawing nice abstract curved shapes. If you've still got an oscilloscope and a signal generator (once staples of any high school electronics/physics lab) you can make them really easily. However when I went to check for some mathematical background on the Wikipedia page I found something interesting: The first line of the page says "Not to be confused with spirographs"

That's odd because it never occurred to me before to confuse them with Spirographs, but now that I have "confused" them, Spirographs seem far more interesting!

Just in case you missed the '70s we have a ring around the outside, and a disk which spins inside it. We put a pen through a hole in the disk, and rotate it around to get a funky pattern. You can still buy new Spirographs very cheaply, and they're lots of fun!

Clicking over to the Spirograph page we find the mathematical derivation of the path of a Spirograph. We'll skip over most of that, and cut to the final result which is:

This is something we can easily work with! 

These are equations for a "parametric curve". We have a parameter t  which represents how far along the line we are. If we were drawing this, then as we move the pen, each position would represent an increase in t. We start with t=0 and increase t until we've traced out the whole path. We have two equations, so for a value of t we get an x and y value.

The R, l and k represent constants which select the different spirograph shapes. R is the simplest and just represents size of the outer ring.

k represents the size of the disk that spins round inside the outer ring. However they've done a little bit of a trick - the pattern doesn't really depend on the size of the inner disk, but rather on the relative sizes of the disk and the ring. So rather than using R and r to represent the sizes they use k=r/R, so k is always between 0 and 1, and we can make the whole pattern bigger or smaller just by changing R.

If you look at the equations closely we can see that if k=0 then the first part of the equation is multiplied by 1, and the second by 0, which gives the much simpler equations x=R cos(t) and y=R sin(t). This is just the equation of a circle, which is exactly what we'd expect if instead of using a disk we just moved a point around the inside of the ring.

The final parameter is l which represents the distance the pen is from the centre of the disk. We can again see that if l=0  we're back to a circle, which is pretty obvious if you think about it. Big values of l (up to 1) make the pattern more loopy, while smaller values make it less eccentric.

So lets code it:

make display window device
make displayX number
make displayY number
make displayColor number

when start
..tell display to "get event"

make R number
make l number
make k number
make t number

We start with some basic setup - make a window device we can draw in. As we're running on a desktop computer we also need to call "get event" to keep the windowing system happy. Then we make some variables to represent the constants.

Now we're ready to actually do the work:

make t number
when start
.set R to 300
.set l to 0.3
.set k to 0.7
.set displayColor to 700
.set t to 0
.set displayX to (1+(l-1)*k)*R+320
.set displayY to 240
.tell display to "move"
.repeat 360
..change t by 7
..set displayX to (1-k)*(cos of t) + l*k*cos of ((1-k)*t/k)
..set displayY to (1-k)*(sin of t) - l*k*sin of ((1-k)*t/k)
..set displayX to displayX*R+320
..set displayY to displayY*R+240
..tell display to "draw"

I picked values of  l and k which produce a generic spirograph pattern, and set R so that pattern would  fill most of the screen. 

We start with a value of 0 for t. While we could just dump that into the full equation infact if t=0 then we can precalculate the sin/cos terms and end up with the much simpler value for the starting point for the curve. We move there and we're ready to go.

The main loop is pretty simple - just increment t and use the values in the equations. We add 320 and 240 to centre the image.

The only tricky part is working out how many times we need to go around to get back to our starting point. The final value of t has to be an exact multiple of 360 so that the disk itself is back to its starting point, but it may not be oriented correctly. For that to happen (1-k)/k has to be a whole number to that that the orientation of the disk is also an exact multiple of 360.

In our case k=0.7, or 7/10 so we have (3/10)/(7/10) or 3/7. From that we can see that if t=7*360 then those terms become 3*360 and we're back where we started.

We could go around 360*7 times in steps of 1, but instead I've made it go around 360 times in steps of 7. This captures some of the strucuture - the outer loop will always bring us back to the same place on the ring and we can just adjust the step length to make sure we complete the curve.
And here it is! There's lots more we could do with this code - Try changing all the parameters to produce different curves. We should really move the drawing code into a separate script, so we can draw multiple curves in different colours. Could you change the loop to actually check if we've closed the curve?

I've got special plans of how we can use this code for something even more fun, but for now just try drawing some fun pictures!

[update: extra hint/idea... circumference is proportional to radius, so k=r/R could equally be written k=c/C. In the case of real spirographs, circumference is replaced with number of teeth in the wheel which is always an integer. This changes things slightly, so the number of times you need to go around is now the lowest common multiple of the two numbers]

No comments:

Post a Comment