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, 10 September 2015

SVG Files in Sniff

You might not have come across SVG files very much but they're pretty handy - it stands for "Scalable Vector Graphics", which means they consist of a bunch of coordinates and lines, rather than a set of pixels. This makes them resolution independent, so we can draw them any size and they stay looking sharp. However the downside is that we actually have to draw them, which can be quite tricky.

In Sniff R21 we've added a simple "device" to read SVG files. The files consist of a series of "paths", where each path is a series of line segments. Internally its actually a bit more complex, but we handle that for you, and you just get a series of points you need to connect up. Draw each path in turn, and you get your image.

We start by making a window device to draw on, and handling the events:
make display window device
make displayX number
make displayY number
make displayColor number

when start
.forever
..tell display to "get event"
..wait 0.1 secs

Now we're ready to make an svgFile device:

make svgFile device
make fileData string
make fileOK boolean
make svgPointsX list of numbers
make svgPointsY list of numbers

And then we're ready to go:

when start
.set fileData to "pirate.svg"
.tell svgFile to "start read"
.if not fileOK
..say "Can't Read File"
..stop script

We start reading the file just like we would if it was a text file device, so here I've got a file called "pirate.svg". Then we're going to read paths from the file as long as there are some left:

.repeat until not fileOK
..tell svgFile to "read path"

Read path loads the points from a path into the two lists svgPointsX and svgPoints.

Now we just need to draw the path:

..set displayX to item 1 of svgPointsX
..set displayY to item 1 of svgPointsY
..tell display to "move"
..set count to 2
..repeat until count>length of svgPointsX
...set displayX to item count of svgPointsX
...set displayY to item count of svgPointsY
...tell display to "draw"
...change count by 1



Not all SVG files work so well - we've only got the outline info. The SVG Format also supports thick lines, filled areas, including colours and gradients, and a lot of other complex stuff that's not currently supported in Sniff. We may add that in the future, but for now we've got the all important coordinates.

It's appropriate that our test image is a pirate symbol as I often tell students to "think like a pirate" when getting to grips with transformations and coordinates. Start at the dead tree, and walk 10 paces north...

We can write a simple "transform" script to apply any kind of transformation on the points before we draw them:
when transform
.repeat length of svgPointsX using count
..set displayX to item count of svgPointsX*scale
..set displayY to item count of svgPointsY*scale
..
..change displayX by 100
..change displayY by 0
..
..replace item count of svgPointsX with displayX
..replace item count of svgPointsY with displayY


This moves the whole image to the right. To incorporate it into our main script we just add the line:

.repeat until not fileOK
..tell svgFile to "read path"
..if fileOK
...broadcast transform and wait

[Technical Aside: There's actually a minor quirk in the behaviour of "read path" at the moment in that that last time round when it reaches the end of the file it correctly sets fileOK to "no", but doesn't delete the old path, so the svgPoints list still contain the previous path. This means its actually getting drawn twice, which would be OK but if we transform it twice, it gets drawn in the wrong place - this will be fixed but for now we check fileOK, which is sort of what we should be doing anyway]

With that in place we can learn all about transformations by just editing the transform script. For example we could add a sheer:

..change displayX by 0.4*displayY
..change displayY by 0

I'm sure you can think of all sorts of educational fun and exciting things you could do with this... how would you animate the prirate so it looks like he's rippling around like on a flag?

Of course the real reason for implementing SVG files was to get them working with the laser engraver! Once we've got our pirate drawn on the screen, all we need to do is replace the window device with the gcode device, and our pirate will be send to the CNC machine! 



SVG files are ideally suited to this as CNC machines are designed to move the head around, along paths. The result of sending SVG files is much better than we get using the supplied software which sent regular image files, and because we only draw the stuff we have to its much faster too!

1 comment: