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, 20 August 2015

Round and Round

So a few hours ago Jens Monig (of Snap fame - sorry I don't know how to type the accent!) posted a link to a video which someone had made. This version is done in CSS! Jens suggested doing it in GP and/or Snap, and shortly afterwards posted a link to his version.

Not to miss out on the fun, I thought we could do it in Sniff, and to make it even neater, make it run on Arduino!

make i2c device 8

make display oled device
make displayX number
make displayY number
make displayColor number

make displayFlush boolean

The first thing to do is create a display device. I actually tried this on a few devices, but the big tft display was too slow to refresh. I also found a could to bugs that have slipped into Release 20, concerning Arduino displays. We're currently deprecating the drawImage function, and moving to a more powerfull bitmap architecture, and a couple of devices got left behind... sorry! Anyway we're pretty much limited to a small display to be able to get the necessary performance from both the Arduino and the bus transfer, so we went with the classic 128x64 i2c OLED device. As its the only device we need, we can speed the bus up a little - I chose to run the bus at 8x normal speed, which words great with my setup - if it fails for you, drop the speed down until all is OK.

make theta number
make radius number
make centreX number
make centreY number

when drawSemiCircle
.make tmpTheta number
.set displayX to centreX+radius*cos of theta
.set displayY to centreY+radius*sin of theta
.tell display to "move"
.set tmpTheta to theta
.repeat 36
..change tmpTheta by 5
..set displayX to centreX+radius*cos of tmpTheta
..set displayY to centreY+radius*sin of tmpTheta
..tell display to "draw"

Next we need to draw a semi-circle. Normally I'd avoid using sin/cos when drawing circles. You can also get some good speedup by exploiting symetry, but for something thrown together, this code is simple and obvious.

With that out of the way, the rest is easy:
make circles list of number

when start
.make index number
.set centreX  to displayX/2
.set centreY to displayY/2
.set displayFlush to no
.repeat 10
..add 0 to circles
..set displayColor to 000
..tell display to "clear"
..set displayColor to 777
..set radius to 3
..repeat length of circles using index
...set theta to item index of circles
...broadcast drawSemiCircle and wait
...change radius by 3
...change theta by index

Display devices have this nice feature that on initialisation they set displayX and displayY to their resolution (did I not tell you that before - sorry! its handy!), so we can easily centre the circles on the display. We want everything to refresh in one go, so we turn flushing off.

We're going to have to circles, so we store the current angle for each of them in a list. We don't really need this as we could calculate the values as we need them, but it does make things easier. Initially they're all lined up. so theta is 0.

Now we loop forever: clear the screen to black then draw each of the semi circles, increasing the radius by 3 each time (10 circles-> max radius of 30, max diameter of 60, just fits nicely!). The optical trick is that as we go out each circle spins faster, so we change its angle by index each time - the inner circle moves by 1 degree, the outer by 10 degrees each frame. The result:

And here it is! If you don't have the hardware to run this on Arduino, then it runs just as well on the desktop - just change the device to a "window" device (remember to "getEvent" every now and then to keep the window server happy), and then tweak the speed/size to suit your taste!

No comments:

Post a Comment