Project P1a for CS4496, instructor Jarek Rossignac.


Purpose of P1a:
Project P1a was meant as an exercise on how to visualize real world motion concepts such as acceleration, movement, stopping, etc.. and then how one would go about shaping the curves to define that motion. The basics of any animation starts with motion, for without motion you don't have an animation at all --just a static image. It also will help us in creating any motion functions need for further portions of the accident re-creation project.


Code and implementation.


All Functions
All of our graphs, how pretty!


Function f2
Jarek's hint tells us that the acceleration/deceleration to create this graph is constant. Acceleration is the 2nd derivative of the function we are seeking. Integrating twice gets us the function we need.
1) Constant acceleration. 2) Varying resultant velocity.
f''(t) = 1               f''(t) = -1
 f'(t) = x                f'(t) = -x
  f(t) = x^2/2             f(t) = -x^2/2
The [0,0.5] f(t) must be scaled to fit the desired range; this is a simple multiply. The [0.5,1] f(t) has it's input t manipulated to vary the t parameter from 0.5 down to 0. The resultant curve must be scaled (same as before) and translated (add 1).
float f2(float t) {
 if( t <= 0.5 ) {
  f2Pos = t*t*f2expand;  // varies 0.0 --> 0.5	
 }
 else
 {
  t = 1 - t;             // varies 0.5 --> 0.0
  f2Pos = -t*t*f2expand; // p(t) * expand ... varies -0.5 --> 0.0
  f2Pos = f2Pos + 1.0;   // shift the curve over
 }
 return f2Pos;
}
Function f3
From Jarek's hint, we know the graph of the *acceleration* (2nd derivative) looks like the [0,pi] range of the cosine graph. So take integrals to get us back to f(t).
f''(t) =  cos(t)
 f'(t) =  sin(t)
  f(t) = -cos(t)
So, the [0,pi] domain of f(t) above is the one we need, but it needs to be squished into range [0,1]. It's current range is [-1,1]. To do that we add 1 (making the range [0,2]), and then divide the range by two.

So, f3 in it's simple, yet strangely painful(!), glory becomes...
float f3(float t) {
 f3Pos = -cos(pi*t);  // f(t) ... range [-1,1]
 f3Pos += 1.0;        // shift range up [0,2]
 f3Pos /= 2.0;        // squish range [0,1]
	
 return f3Pos;
}
Function f4
To get this one we looked at the cosine^2 function. The section of the curve that had similar properties to what we wanted was the range [pi/2,pi] (a long time getting away from zero and a capping out at 1). After comparing this to Jarek's picture, it was obvious that the car was not getting a slow enough start. To create this, we simply squared the function again (cos^4).
We just want to extrapolate the highlighted portion above.
float f4(float t) {
 f4Pos = cos(pi/2 + pi/2*t);  // varies 0.0 --> -1.0
 f4Pos = f4Pos * f4Pos;       // squared
 f4Pos *= f4Pos;              // to the 4th

 return f4Pos;
}
Function f5
To create this curve we defined a cubic bezier curve as discussed in class. We created the "hull" ABDE (actually linear here so hull may be a misnomer). A and E were fixed at 0 & 1, respectively. That left just B and D to play with to get the correct curve shape. B needed to be negative to get the "wind-up" curve shape and D fairly close to, but greater than 1, in order to get the desired steepness at the end of the curve. The values that we settled on were -0.3 and 1.1, respectively.

To find points on the curve we used the De Casteljau construction technique. In the code, the "slide" function and slide_vals array (calculation workspace) implement this.
float ptA = 0.0;
float ptB = -0.3;  // value picked to match Jarek's curve.
float ptD = 1.1;   // value picked to match Jarek's curve.
float ptE = 1.0;
float slide_vals[] = new float[3];  // workspace during computation

float f5(float t) {
 slide_vals[0] = slide(ptA,ptB,t);
 slide_vals[1] = slide(ptB,ptD,t);
 slide_vals[2] = slide(ptD,ptE,t);
 slide_vals[0] = slide(slide_vals[0], slide_vals[1],t);
 slide_vals[1] = slide(slide_vals[1], slide_vals[2],t);
 
 f5Pos = slide(slide_vals[0], slide_vals[1],t);
 
 return(f5Pos);
} 

/**
* finds the position along the vector AB indicated by param
**/
float slide(float a, float b, float param) {
 return (b-a)*param + a; 
}
Function f6
The hint suggested a cubic so we started by looking at t^3. It is easy to see that this is the right curve shape, but we needed it in a different orientation. To achieve this we made the t parameter run from 1 down to 0 instead of 0 to 1 (via t=1-t). This creates a curve that goes from 1 down to 0, but which has the correct shape still. Subtracting this curve from 1 gave us the desired f(t).
float f6(float t) {
 t = 1-t;
 f6Pos = 1 - t*t*t;
 return f6Pos;
}
1) Using just t^3. 2) Using (1 - t)^3 inverts the curve.
Function f7
For this we just set the central 4 constraints to all have position of 0.5. There are also 2 constraints at the beginning, each set to position 0.0, and 2 constraints at the end set to position 1.0.
1) Before smoothing. 2) After smoothing.

Basically we added the stop_and_go function to pre-load up all of our constraints when the application loads, thus creating the un-smoothed graph at the start. These constraints are hard coded to always be there.
void stop_and_go() {
 V[0]=0;    C[0]=true;  S[0]=0;     // original constraint
 V[2]=0;    C[2]=true;  S[2]=0;

 V[20]=0.5; C[20]=true; S[20]=0.5;
 V[21]=0.5; C[21]=true; S[21]=0.5;
 V[29]=0.5; C[29]=true; S[29]=0.5;
 V[30]=0.5; C[30]=true; S[30]=0.5;

 V[N-3]=1; C[N-3]=true; S[N-3]=1;
 V[N-1]=1; C[N-1]=true; S[N-1]=1;  // original constraint
}