This is the last of the ‘boring’ posts on the laser projector. After this, I’ll get the laser out and we can actually do some exciting stuff. Before we scale up everything we’ve learned so far to an actual laser system, we need to learn how to actually trace out shapes to make an image.
So, bear with me. Get through this post and we’re almost there. To help make things a bit more exciting here is a video of the full sequence I showed in my wedding speech. It’s shown on the oscilloscope screen because it’s impossible to film it when projected by laser (the framerate is too low but I’m working on making it filmable).
Not just ‘dot to dot’
As I have touched on in previous posts, the laser projector works by tracing out images to draw ‘vector graphics’. In the previous post, we learned how to use digital to analog conversion to make a dot appear anywhere on a screen of an oscilloscope. Move this dot around in the appropriate manner and we can make it trace out lines.
But it’s not quite that simple. Until now, I’ve made it sound like drawing a line on the screen is achieved by ‘connecting two points’. As in, programming the Arduino to send the dot directly from one point to another. At first glance, this would appear as a line to us as the motion of the dot from one point to the other appears as a line to us.
This, does not work in reality. The oscilloscope (and to a lesser extent the laser deflection method) moves the dot between the points so quickly that we just see 2 points on the screen:
Moving a dot directly between 2 points on an oscilloscope screen (in super slow motion). The dot spends most of its time on the actual points and moves quickly along the ‘line’ we are trying to draw. To our eyes, this just appears as 2 dots.
Quick side note – oscilloscope beam deflection speed
When I say the beam moves quickly between the two points, I am not joking around. The deflection time on oscilloscopes is ridiculously fast – on the order of billionths of a second. This poses a potential problem later, since no system i can think of can possibly deflect a laser beam as quickly as the electron beam in an oscilloscope. When I wrote the programme for the animation, I initially did it on the oscilloscope and ensured the beam was never required to move at too high a speed by introducing time delays.
What we actually want is a trace that looks like this:
Desired slow motion animation of the oscilloscope drawing a line. Note the beam moves continually at the same speed
To draw a single line, that would be easy to programme. You’d just tell the arduino to deflect the beam one pixel across and one pixel down; wait a few milliseconds and repeat.
To draw any line, however, this would be impractical. Imagine having to re-write the above every time we want to draw a single line. What we need instead is a function which can draw any line between points x1,y1 and x2,y2. In other words, we want to be able to type something like ‘line (x1,y1,x2,y2)’ and get a line between these coordinates. Needless to say, an algorithm to do this has existed for decades…
Bresenham’s Line Algorithm
I won’t go into the details of how the algorithm works but it’s very simple if you’re into that kind of thing. It essentially tells the computer how many ‘steps across’ and how many ‘steps down’ to draw the line. Interestingly, this works even better for a laser or oscilloscope than a computer monitor. Think of a diagonal line rendered on a screen – if you zoom into it, it looks all jagged like a staircase. A laser beam or electron beam jumps from one pixel to the next, leaving a continuous trace so we don’t get the same jagged effect.
Anyway, I was going to write a function for the line algorithm but then I discovered someone over at NYCResistor had already done exactly that. They’d used a resistor ladder in just the same way as me to draw images on an oscilloscope with an Arduino. So, I just copied their function for the line and modified it a little. I added a delay after each ‘pixel’ to slow down the function. This prevents the arduino from drawing the line quicker than the max speed of the laser deflection mechanism.
To implement the function, we just type ‘line(x1,y1,x2,y2) to draw a line from point x1,y1 to x2,y2.
The delay is 40 microseconds per pixel. Any guesses to what can deflect a laser that quickly? We’ll find that out in the next post but for now…
Once it is possible to draw a line, polygons are trivial. We can just program multiple lines to form a shape. Remember, the ‘screen’ is divided into a 256X256 grid.
So, to plot a triangle, the code would simply be:
line(50,50,50,150); //horizontal line for base
line(100,150,50,50); //’return line’ to start point
Simple. My final program included hundreds of these.
Circles, parabolas, sin waves etc
There are a number of ways to draw more complex shapes; here are the ones I considered.:
It is difficult to start discussing Lissajous curves without getting carried away and writing a 2000 word essay. I’ll keep it simple though. If we feed a sin wave into the x input of the scope, and another sin wave into the y input, we can get a series of interesting curves by varying the relative frequency, amplitude and phase of the waves. These are called Lissajous curves.
A circle is a special case of a Lissajous curve where the frequency and amplitude of the sin waves are identical but they are 90 degrees out of phase (there is a link to a great youtube video describing this at the bottom of the post).
Lissajous curves are more of a point of interest and are less practical to code. That said, I did draw Lissajous circles in the last couple of sequences to make a circle (which represented a space probe) appear to rotate. Unfortunately, this isn’t really visible in the video above.
Any curve can be plotted by programming its formula into the Arduino. For example, to plot a circle, we would just program in the equations of a circle (x=r*cos(t), y=r*sin(t)).
I did use this method at first to check my setup was working. However, it quickly becomes difficult to program in numerous formulae into Arduino.
Also, programming in formulae means the processor in the Arduino has to solve them. This takes processor time and potentially slows down the program. As the animation is time critical, a delay of just a few milliseconds would throw the whole image out of sync. In reality, this does not matter these days, as even the processor in an Arduino is easily able to calculate the points quick enough. For earlier computers, this was a major consideration.
This option feels like a complete copout but is the easiest to program and requires no computational time from the processor. The points for a curve are saved as a table and simply retrieved and plotted whenever that curve is required. For example to plot a circle of diameter 128 with centrepoint 100:
- A lookup table for points on a circle is saved in the programme. This one table is used to draw every circle in the image sequence. The table lists the points of a circle of diameter 256 and centre 128; ie the largest possible circle that can be drawn on the screen. The table was created in Excel.
- A function, called ‘circle()’ is created which plots these points.
- To draw the desired circle we run the function ‘circle()’ and multiply by 0.5. This reduced the diameter of the plotted circle by half.
- We then subtract 28, to move the centrepoint of the plotted circle from 128 to 100.
That’s it. I also created lookup tables for parabolas and sin waves. I didn’t need anything else – everything I needed to draw could be formed with circle sections, straight lines, parabolas and waves.
No Free Lunch
It is worth noting that lookup tables are a kind of trade off with parametric plotting. As mentioned, plotting points parametrically required CPU processing power and time. But, only the formulae must be saved in the computer memory. Lookup tables on the other hand require very little computational power and time but take up more memory.
A famous example of this is the (incredible) 1980’s computer game ‘Elite’ which used lookup tables to render polygons on machines with slower processors. Other machines had superior processors but less memory available so used the parametric approach instead.
More advanced drawing
So that’s it for producing actual still images on the oscilloscope screen. In the video above, some of the images move as well. The programming for moving images is a little more complicated so I will cover that in a later post. In the meantime, stay tuned…we’ll be dealing with lasers in the next post.
Where we are now
- We can plot any line, polygon or curve on the oscilloscope screen.
- Using these building blocks we can draw any image on the screen (although if too many lines are present in a frame, the image will flicker).
- This is all well and good on the screen of an oscilloscope, but we need to find a way to control the position of a laser beam in order to plot these images on a wall.
https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm Wikipedia post on Besenham’s line algorithm.
http://www.nycresistor.com/2012/09/03/vector-display/ NYCResistor’s post on drawing images on an oscilloscope with an Arduino. This is where I copied the code for the line algorithm from.
Great video explaining how Lissajous curves arise.