Often we want to move an object (such as a flare) along a defined path. One way to achieve this is by using mathematical functions. In this section, we will explore the paths created by various functions that can later be used to define the motion of objects.
Excercise 3-13. In this exercise we will write code to animate flares along paths.
Create a function called PathFunc that takes a single real argument X. Inside this function simply assign 0.5 to Result (since we're assigning 0.5 to Result what does the return type of the function have to be?). We also want to create two global variables X and XStep which are both real types. Initialize X to -14 and XStep to 0.01. During OnEnterFrame, call Flare(X, PathFunc(X), GetHue(clOrange), 255) and then increment X by XStep. Remember to call DoConvolutionFilter or nothing will happen. (Don't look at the solution for this. Each step is spelled out in order to help you practice translating words into code.)
You'll notice that the flare in the previous program moves very slowly. One way to speed it up is to increase the step size. Make the step size 0.1 to make it 10 times faster.
Although this speeds the flare up, there is the apperance of a small gap as the flare moves. This becomes worse if you make the flare move even faster (try 1 instead of 0.1). If the desired effect is continuous motion, you should take multiple steps per frame and change XStep back to 0.01. Implement this using a for loop that takes 10 steps per frame.
If the flare reaches the right hand side of the screen, make it start back at the beginning. (Hint: The right side of the screen is X = 14. The problem started with an if so that means you probably need an if statement in the code.)
We have to use greater than or equal to instead of just equals because there's a chance (especially with real numbers due to rounding errors) that X will not ever equal exactly 14. We just want to make it return to -14 if it ever becomes greater than or equal to 14.
First, let's change the color to lime just for practice in OnEnterFrame. Turn on the grid using GridVisible in Initialize. Now change the path the flare takes by modifying the code in PathFunc. Nothing needs to be changed in OnEnterFrame. We want the flare to take the path of the following function:
When running this program the flare should take a sinusoidal path. Notice that every peak of the sinusoid is exactly on a grid line. This means that the wavelength (
) of the sinusoid is 1 grid unit. When a sinusoid is in the form
, we say that
is its frequency. In the function we used,
,
is equal to 1.
Increase the frequency of the sinusoid to 2 and see what happens (i.e., multiply the argument to the cos function by 2). Also decrease the frequency by half (i.e., multiply the argument to the cos function by 0.5).
With
, the sinusoid makes twice as many cycles per grid unit as it did before. This means the wavelength is now 0.5. With
, the sinusoid peaks every 2 grid units. In other words, it has half as many cycles per grid unit so the wavelength is 2. If you haven't already noticed the pattern. There is an inverse relationship between frequency and wavelength (
). You can use this to determine how quickly you want your moving object to oscillate.
We can vary the amplitude of a sinusoid by multiplying a constant outside of the function. By default the amplitude of a sinusoid is 1 (because it ranges between -1 and 1). Change the code to make the sinusoid have an amplitude of 5.
You'll notice that the amplitude is higher, but the path is not very smooth. To fix this, you could decrease the step size to 0.001 and increase the number of iterations of the for loop to 100.
What happens when you sum two sinusoids of different frequencies? Change the path code so that it is the sum of the following:
What you're observing is known as a beat frequency. It occurs as the peaks of the two sinusoids coincide and sum together to give a larger peak. This occurs of course every 2 grid units because this is the location of the peaks of the slowest oscillating sinusoid.
What happens when you multiply two sinusoids of different frequencies? To really observe this effect you need to have one sinusoid that is several times slower than the other. Change the frequency of the 0.5 sinusoid to 0.125 and multiply the two sinusoids together.
Here you see that the motion of the fast oscillating sinusoid is faded in and out by the motion of the slow oscillating sinusoid. The slow oscillating sinusoid is known as an envelope function because it appears to contain the other sinusoid.
Finally, in a last example of an envelope function we will use exponential decay.
We use an amplitude of 8 because the idea behind exponential decay is that the amplitude is decaying while the frequency remains constant (thus, we have to have some large number to decay from). The -(X+14) determines the start of the decay (X = -14) and the 5 determines the rate of decay. You can experiment with different rates to see how they effect decay times. The best way to compare decay constants is to run three different functions at once (one at 2.5, one at 5, and one at 10). Do this and see what happens. Make one lime, one red, and one blue. You could also not use PathFunc and instead create a new function called DecayFunc that has arguments X and DecayConst.
Here you can see that the frequency of the sinusoids does not change but the rate of decay of the amplitude does. The blue sinusoid with a rate of 10 decays the slowest and the lime sinusoid with a rate of 2.5 decays the fastest.
So why would we ever need all these mathematical functions for animation? To begin with, wave paths can be pretty cool. But also, the motion of quite a few objects is described by sinusoids, especially decaying sinusoids.
Excercise 3-14. Using the same code template, create something that looks like a pendulum. The top (the axis of rotation) should be a small circle and the bottom should be a larger circle. Make the top at (0,6) and the bottom at (0,-4). Also, the CircleAt and PolarToXY routines are reproduced below to make drawing easier:
procedure CircleAt(X,Y,R : Double; Color : TColor);
begin
FillEllipse(X-R,Y-R,X+R,Y+R,Color);
end;
function PolarToXY(X,Y,Theta,Radius : Double) : TVector2D;
begin
Result.X := X+cos(Theta*PI/180)*Radius;
Result.Y := Y+sin(Theta*PI/180)*Radius;
end;
Clear everything in OnEnterFrame so that you're starting from scratch. For now, just make the pendulum hang straight down with no motion.
Now modify your code so that the pendulum can be positioned at any Theta where Theta is the angle between the pendulum cable and the y-axis (not the x-axis so you'll need to subtract 90 degrees from any calculations made with PolarToXY). The easiest way to do this is to declare a variable V of type TVector2D and assign to it the output of PolarToXY where the starting point is (0,6) because this is the location of the axis around which the pendulum rotates, the angle is Theta-90 and the radius is 10 (because we want it to rest to (0,-4) when the angle is 0).
First we declare Theta and V. Theta can be initialized to anything we want just to test and see if the code works. We use 30 degrees here. You use the coordinates V.X and V.Y to determine the LineTo and CircleAt positions for the large circle.
Now make a flare sinusoid appear behind the pendulum just like the sinusoids we made earlier. Give the sinusoid a frequency of 0.5 and an amplitude of 1.
The code for the flare has to go before the code for the pendulum or the pendulum will be erased from the screen.
A pendulum exhibits what is known as simple harmonic motion. This means that the angle it makes with the y-axis evolves over time as a sinusoid. Thus, we can animate the pendulum using PathFunc. Make the pendulum oscillate between -20 and 20 degrees using path func. Hint: You can solve this problem by thinking in two steps: (1) What variable should be assigned to in order for the pendulum to move and (2) PathFunc as it is written oscillates between -1 and 1 so how do we change its amplitude (you don't have to modify the code in PathFunc to do this)?
We have to modify only 1 line: Theta := 20*PathFunc(X). Because PathFunc is a sinusoid, all we have to do is scale the sinusoid by 20 in order to get it to oscillate between -20 and 20. When you run this program, note that the frequency of the sinusoid matches the frequency of the pendulum (i.e., when the sinusoid reaches a peak the pendulum has swung to the far right).
Pendulum's obviously don't swing forever. Their oscillations decay due to friction. Make the motion decay using functions we have already defined.
Just use DecayFunc(X,DecayConst) where DecayConst is any value you choose. You of course have to modify DecayFunc to have an initial amplitude of 1 since we specified an amplitude of 8 originally.