Trig. Yes I know. It's a four-letter word. Nevertheless, I'll do my
level best to show you that:
Trig is simpler than you think
Trig can do beautiful things to your multimedia screen.
This is a two-part article. In this first part, you'll be exploring
the anatomy of triangles and circles, tinkering with Pythagoras' Theorem
and discovering the secrets of the Sine and Cosine. Then you'll write
a simple handler and adapt it to produce some gorgeous curves: circles
and waves, spirograph patterns and string art.
There are a couple of movies to help you. One is a shocked tutorial
movie, with interactive diagrams.
The other is an unprotected Practice movie which you can use as a starting
point for investigations of your own. It contains well-behaved versions of
all the scripts in the article. (Scripts in this article use hardcoded tight
repeat loops, for concision).
In the second article, we'll look at how vector geometry can provide certain
solutions faster than trig can. You'll be developing a simple game of
pool which uses vectors to bounce balls off each other according to the
laws of physics. Who said geometry was no fun?
Trig is simpler than you think
Trigonometry (to give it its Sunday name) means "the measurement of trigons".
You won't find "trigon" in your dictionary, though you might find "polygon".
A polygon is a geometrical figure with many angles. A trigon is a figure
with three angles... more commonly known as a triangle. Trig is all about
triangles.
Trig's favorite triangle is the right-angled triangle. The godfather
of the right-angled triangle is Pythagoras, so we'll start with him.
If you don't remember what Pythagoras' Theorem was all about, go and
check out the Tutorial. It demonstrates in non-mathematical terms that: the area
of the square drawn on the hypotenuse of a right-angled triangle is equal
to the sum of the areas drawn on the other two sides.
Circles and triangles
Believe it or not, triangles have a lot in common with circles. Circles
+ math = π. Pi is a weird number. Nobody knows exactly what its value
is. If you ask Director, you'll get a close enough approximation. Type
this in the Message window:
set the floatPrecision to 14
put pi()
-- 3.14159265358979
Where does this number come from? It's simply the ratio between the circumference
of a circle and its diameter. There's a demonstration of this in the Tutorial
movie.
So what does pi have to do with triangles?
Pi and angles
Imagine that each angle of a triangle is at the center of a circle. You
can define the angle as being a certain proportion of the whole circle.
But how do you measure that proportion? You can't use a straight ruler,
because the edge of a circle is round. So you have to divide the edge
of the circle up into equal parts.
The Babylonians divided the circle into 360 degrees. This was for two
reasons: - They reckoned the year to have 360 days, plus five extra days,
one for each of the known planets. 360 was thus a number that described
the full circle of the year. - They did their arithmetic in base 60. This
is an extraordinarily handy base, when it comes to calculating fractions.
360 is conveniently 6 times 60.
Modern math divides the circle into radians. There are 2π radians in
one complete rotation. While you may be more familiar with measuring angles
in degrees, you'll find that the radian is a very powerful unit. Director
works in radians, so let's try to understand why.
Radians
When you draw a circle with a pair of compasses, you only need to know
one thing: its radius. A curved section of the circumference of a circle
is called an arc. To imagine a radian, simply imagine an arc exactly one
radius long. If you draw two lines, one from each end of the arc to the
center of the circle, the angle between the two lines is a radian. There's
a demonstration of this in the Tutorial
movie.
The circumference of a circle is, by definition, π times its diameter.
The diameter is twice the radius, so you could express the circumference
as 2π times the radius, or 2πr.
You need 2π radians to rotate once in a complete circle, and you need
2π radius lengths to draw the complete circumference.
In other words, each time you turn a line through an angle of one radian,
each point on the line will move along an arc whose curved length is equal
to its distance from the center of rotation. Obvious isn't it?
Sines and angles
There's another way of determining angles, a way that deals only with
straight lines.
Imagine that you have a ladder that is exactly one ladder-length long.
(That's a mathematician's way of saying that we will say that its length
is 1 unit, so that we can conveniently simplify things).
If you lean the ladder against a wall, it will make a certain angle with
the ground. You could measure that angle in degrees or radians, or any
other unit of circular measurement. Or you could simply measure how high
the top of the ladder is from the ground.
If you change the angle, you change the height of the ladder. If you
change the height, you change the angle. So why can't we use the height
of the ladder as a measure of the angle?
The answer is... you can. If you use sines. What is a sine? A sine is
a proportion. A sine is the height of a right-angled triangle compared
to the length of the hypotenuse. In our example, the ladder makes a certain
angle with the ground. The sine of that angle is equal to the height of
the ladder divided by its length.
Converting from angles to sines
You can measure straight lines in feet or meters. Maybe you still have
a school ruler with inches on one side and centimeters on the other. Or
you may have a table that allows you to convert from one unit of measurement
to the other.
You could create a similar sort of conversion table with angles on one
side and sine values on the other. The only difference will be that the
values will not change in a linear fashion, since we're dealing with circles
and not with straight lines.
Director has access to just such a table. It is stored in special logic
circuits in the heart of the microprocessor. Unfortunately, Director takes
a little time to read the table, since it is not linear. We'll see shortly
how to use Lingo to build a simpler table which is faster than the built-in
trig functions.
Cosines
Is there any other straight line we could measure in order to determine
the angle?
What about the horizontal distance between the foot of the ladder and
the foot of the wall? That would give us a result which is complementary
to the sine. It's called the cosine.
An angle of zero corresponds to a ladder lying flat on the ground. The
height of the ladder (the sine) is zero and the distance between the foot
of the ladder and the wall (the cosine) is one. When the ladder is standing
vertically against the wall, it makes an angle of π/2 radians with the
ground. In Lingo terms, sin (pi/2) = 1, and the cos (pi /2) = 0. When
the ladder is at 45Á (or π/4 radians as you should now say), the sine
and the cosine are equal. From Pythagoras' Theorem, you can calculate
that they will both be Ì2 (or sqrt (2.0), for those who prefer their math
in Lingo).
Screen coordinates
On your computer screen there are no triangles and no circles. All you
have are beautifully square pixels, arranged in serried rows and columns.
So to draw triangles and circles we have to work with coordinates (and
not with compasses).
Any point on the screen has a loc or location. This is expressed by two
figures, the locH and the locV, or horizontal and vertical location. By
convention, pixels are counted from the top left corner of the screen.
A pixel with a high locH value is near the right of the screen. A pixel
with a high locV value is near the bottom.
When you did geometry in class, you probably used a different coordinate
system. Instead of locH and locV, you'd have used "x" and "y". In that
system, coordinates are measured from a central point on the page, known
as the Origin: x is counted to the right (just as locH is), but y was
counted upward, whereas locV is counted downward.
To emphasize this difference, my examples use the variables h and v,
instead of the more usual x and y.
And in the "Sine and cosine" section of my Tutorial
movie, I have respected this difference again. You will not see a
ladder leaning up against a wall. Instead you will find a line leaning down and to the right.
Trig can do beautiful things to your multimedia
screen
Hey, didn't I promise you that you'd be creating luscious curves? I haven't
forgotten. Let's start by drawing a circle. Create a new movie, then place
a small bitmap in sprite channel one. Type this into a Movie Script (OK,
so it's hardcoded, but no-one's looking. By the way, I use 400 iterations
just because that's how many it takes to fill in all the points on the
circle Try it with other values if you like).
on circle1
set the trails of sprite 1 to TRUE
set center to point (160, 160)
set radius to 100
repeat with angle = 1 to 400
set h to radius * sin (angle)
set v to radius * cos (angle)
set the loc of sprite 1 to center + point (h, v)
updateStage
end repeat
end circle1
Now type this into the Message Window:
circle1
To erase your circle, type this into the Message Window:
set the stageColor to the stageColor
The circle1 handler makes a circle appear on the stage, almost as if
it were being screwed in. What is happening?
To count through a repeat loop, you need integers. I use the variable
"angle". Each time angle is incremented, the bitmap is moved one radian
around the circle. (Try using only six iterations: repeat with angle =
1 to 6. That will make the effect easier to see).
If you want the circle to be drawn in only continuous movement, you're
going to have to use floating point numbers. Clear the trails from the
stage, then try this:
on circle2
set the trails of sprite 1 to TRUE
set center to point (160, 160)
set radius to 100
set iterations to 400
set adjust to iterations / (2 * pi())
repeat with angle = 1 to iterations
set h to radius * sin (angle / adjust)
set v to radius * cos (angle / adjust)
set the loc of sprite 1 to center + point (h, v)
updateStage
end repeat
end circle2
How is the circle actually being drawn? Each time you pass through the
repeat loop, Director imagines the hypotenuse of a different triangle.
Director measures how many pixels right and how many pixels down it should
place the far end of the hypotenuse, and stamps an image of your bitmap
at that point.
You draw a circle by tracing the tip of a series of triangles. As I said
before: circles and triangles have lots in common. All the other handlers
you'll meet in this article are simple variations on this theme.
Optimizing
Calculating sine and cosine values on each iteration wastes CPU cycles.
Floating point math is slow. Let's treat both these problems now. Let's
create a list to store all the trig values that we'll ever need for our
handler:
on sineList numberOfPoints, scaleFactor
set angleBetweenPoints to (2 * pi()) / numberOfPoints
set sineList to []
repeat with i = 1 to numberOfPoints
set sine to integer (sin (angleBetweenPoints * i) * scaleFactor)
append (sineList, sine)
end repeat
return sineList
end
This returns a list of integers. All sine values are between - 1 and
1. The parameter scaleFactor is used to scale these values up so that
they lie in a useful range. Suppose we want to create a sine table using
degrees instead of radians: a good value for scaleFactor would be 10000,
since it would give us a unique value for each degree. Try this in the
Message Window:
I have edited the output to show only the values for 1Á - 10Á and for
81Á - 90Á. You may like to compare the results with what you get on your
pocket calculator. Each value in the list is 10000 times the real sine
value.
Actually, powers of two are better than not powers of ten, because the
microprocessor works slightly faster with powers of two. Using 8192 (
2 to the power 13) gives us excellent results here. A value as low as
64 (2 to the power 6) still draws an acceptable circle.
When you come to plot the values on the screen, you divide by scaleFactor
again, so as to revert to the normal trig values.
A similar handler can be written to return a list of cosine values.
Benchmark tests
Test the difference in calculation time between the circle2 handler and
the following one (you'll find benchmark code in the Practice
movie). Drawing trails on to the screen takes a heavy toll on the
processor's time, so you may see little difference in the actual execution
of the circle on the screen. Indeed, creating the lists then drawing the
circle only once may even take a few extra ticks. The second and subsequent
times the circle is drawn, however, calculation time is reduced by around
70%.
on circle3
set the trails of sprite 1 to TRUE
set center to point (160, 160)
set radius to 100
set iterations to 400
set scaleFactor to 8192
set sineList to sineList (iterations, scaleFactor)
set cosList to cosList (iterations, scaleFactor)
repeat with angle = 1 to iterations
set h to radius * getAt (sineList, angle) / scaleFactor
set v to radius * getAt (cosList, angle) / scaleFactor
set the loc of sprite 1 to center + point (h, v)
updateStage
end repeat
end circle3
Epicycloids
What happens if you move the center of your circle while you're drawing
it?
Here's a Movie Script that works like a spirograph. It moves the point
tempCenter around a circle of a given radius. Sprite 1 is moved in a circle
around the shifting tempCenter. The result is a four-leafed clover. I've
used globals so that you can type changes into the Message Window and
see what happens. You can test with floating point numbers as well as
integers.
global hRadius, vRadius, center, adjust, counter
global lengthRatio, angleRatio
on startMovie
set the trails of sprite 1 to TRUE
set hRadius to 60
set vRadius to 60
set center to point (the width of the rect of the stage / 2, ¬
the height of the rect of the stage / 2)
set adjust to 1000 / (2 * pi())
set counter to 0
set lengthRatio to 2
set angleRatio to 5
end
on idle
set counter to counter + 1
-- MOVE tempCenter
set h to hRadius * sin (counter / adjust)
set v to vRadius * cos (counter / adjust)
set tempCenter to center + point (h, v)
-- MOVE SPRITE 1 AROUND tempCenter
set angle to angleRatio * counter / adjust
set h to (hRadius * sin (angle)) / lengthRatio
set v to (vRadius * cos (angle)) / lengthRatio
set the loc of sprite 1 to tempCenter + point (h, v)
updateStage
end idle
A more elaborate version of this, incorporating the scaled integer technique,
appears in the Practice
movie.
Ellipses
An ellipse is simply a circle with two different diameters. Or if you
prefer, a circle is a particular type of ellipse where both horizontal
and vertical diameters are the same. Actually, that's the way the above
handler works. Both hRadius and vRadius are originally set to the same
value. Try changing one or the other through the Message Window to see
the effect.
Waves
To draw circles and ellipses, we've been plotting sin () against cos().
You can make a perfect wave form, by simply plotting sin() directly against
your iterative counter. Here's a handler that owes a lot to our circle:
on sinewave
set the trails of sprite 1 to TRUE
set radius to 100
set centerV to 160
set iterations to 320
set adjust to iterations / (2 * pi())
repeat with angle = 1 to iterations
set h to angle set v to radius * sin (angle / adjust) -- ¬
-- + (radius * sin ((8 * angle) / adjust) / 4)
set the loc of sprite 1 to point (h, centerV - v)
updateStage
end repeat
end sinewave
Harmonics
I've commented out part of one of the lines. Uncomment this (in two places),
to see what happens. This gives a similar effect to changing lengthRatio
and angleRatio in the spirograph: you add a small wave to a larger one.
Try altering the hardcoded values to see what strange waves you can create.
Or add even more harmonics: continue the line, adding more elements of
the type I had commented out, each with different values. Be esoteric:
call your creations Fourier curves.
Rotating lines
So far, we've simply been placing a bitmap image at the point of a series
of imaginary triangles. Now we'll try twirling lines. For this we need
a drawLine handler. You'll find this in a separate cast member in the Practice movie .
Since it doesn't use trig, I won't quote it here. I'll just tip my hat
in passing to Tom Collins who wrote the original version of it.
A line needs to have a start and an end point. If we keep one end point
fixed, and move the other round in a circle, with trails off, we get a
clock hand. Here's our familiar circle handler revisited:
on circle4
set the member of sprite 1 to member "line\"
set the lineSize of sprite 1 to 2
set centerH to 160
set centerV to 120
set radius to 100
set iterations to 400
set adjust to iterations / (2 * pi())
repeat with angle = 1 to iterations
set h to radius * sin (angle / adjust)
set v to radius * cos (angle / adjust)
drawLine (sprite 1, centerH, centerV, centerH + h, centerV - v)
updateStage
end repeat
end circle4
String art
But if you're an artist and not a clock watcher, why keep one end fixed?
In the Practice movie,
you'll find a behavior entitled "String Art". There I make the v coordinate
move more rapidly than the h coordinate. To do this, I multiply the angle
used to calculate v by a factor I call hvRatio. I also calculate two points,
at different places on the same curve. The variable pointAdvance determines
how far round the curve the second point is. Then I draw a line between
the two points. Here are the key lines of code:
on drawNext me
set counter to counter + 1
set the forecolor of sprite the spriteNum of me to counter mod 256
set h1 to centerH + hRadius * sin (counter / adjust)
set v1 to centerV + vRadius * cos (hvRatio * counter / adjust)
set angle to (counter + pointAdvance) / adjust
set h2 to centerH + hRadius * sin (angle)
set temp to cos (hvRatio * angle)
set v2 to centerV + vRadius * temp
drawLine the spriteNum of me, h1, v1, h2, v2
end drawNext
I change the color of the line at each iteration for a better visual
effect.
Summary
With two simple trigonometric functions, sin() and cos(), you can create
a whole series of curves. By combining these curves with each other and
with straight lines, you can produce dazzling patterns. To save CPU cycles,
you can create lists of sine and cosine values, and refer to them rather
than the native Lingo functions. In the next article, we'll look at cases where
vector geometry leads to handlers that run faster even than optimized
trig.