Keeping a Car on a Racetrack - Adapted from a Visual Linear Algebra (VLA) Module by Eugene A. Herman and Michael D. Pepe 

> with(LinearAlgebra): with(plots):
 

If we want to create a realistic animation of a car moving along a curved road, we face two problems: 

 

 

 

We address these problems one at a time in what follows. 

 

Staying on the Curve 

Here is the parametrized curve we will use:  

> x := t -> cos(t);
y := t -> sin(2*t);
 

 

proc (t) options operator, arrow; cos(t) end proc
proc (t) options operator, arrow; sin(`+`(`*`(2, `*`(t)))) end proc
 

And here is the definition of the car in homogeneous coordinates: 

> car := Matrix([[.15,-.15,-.15,.15],[0,.075,-.075,0],[1,1,1,1]]);
 

Matrix(%id = 18446744078252918174)
 

We draw the curve and the car in a single plot. Note that the car is pointing in the direction of the positive x axis.  

> carp:=polygonplot([[0.15,0],[-.15,.075],[-.15,-.075],[.15,0]],color=red):
pathpic := plot([x(t),y(t),t=0..2*Pi],color=black):
display([carp,pathpic]);
 

Plot_2d
 

 

 

 

To create the illusion of motion along the curve, we need to place the car at numerous points along the curve.    

To keep the car on the curve, we translate the car from the origin to the curve - ie we perform Tcurve.car 

> Tcurve := Matrix([[1, 0, cos(t)], [0, 1, sin(t)], [0, 0, 1]]);
 

Matrix(%id = 18446744078379193998) (1)
 

> Tcurve.car;
 

Matrix(%id = 18446744078379194118) (2)
 

 

For example, let's plug in t = 0: 

> carpict:=polygonplot([[0.15+cos(0),sin(0)],[-.15+cos(0),.075+sin(0)],[-.15+cos(0),-.075+sin(0)],[.15+cos(0),sin(0)]],color=red):
display([carpict,pathpic]);
 

Plot_2d
 

Now t=1, which looks like we are moving the car on the curve  from t = 1 

> T := Matrix([[1, 0, cos(1)], [0, 1, sin(2)], [0, 0, 1]]);
 

Matrix(%id = 18446744078379194478)
 

> T.car;
 

Matrix(%id = 18446744078379194598)
 

> carpict:=polygonplot([[0.15+cos(1),sin(2)],[-.15+cos(1),.075+sin(2)],[-.15+cos(1),-.075+sin(2)],[.15+cos(1),sin(2)]],color=red):
display([carpict,pathpic]);
 

Plot_2d
 

 

Next, we can create a loop to create and save a similar plot for 33 values of t, ranging from t = 0 to t = `+`(`*`(2, `*`(pi))) in increments of pi/16.  

 

Plot_2d
 

 

Here's what I actually plotted, but it looks like (relatively) smooth motion to your eyes: 

Plot_2d 

 

Pointing in the Direction of Motion 

We are halfway there!  Now to keep the car pointing in the direction of motion, we should rotate the car at each step of the animation. If we can find the appropriate rotation matrix R for each value of t, we will have solved our last remaining problem. 

 

Recall that the velocity (or tangent) vector to the parametrized curve always points in the direction of motion. We calculate the velocity vector by taking the derivative of each of the component functions of the curve with respect to t:  

> vel := Vector([diff(x(t),t),diff(y(t),t)]);
 

Vector[column](%id = 18446744078379194958)
 

Next we define the unit vector T in the direction of vel, namely  `/`(`*`(vel), `*`(abs(vel))) by using the Pythagorean Theorem, or equivalently the square root of the dot product, to divide by the length of the vector by its norm: 

> T := Vector[column]([[-sin(t)/(sin(t)^2+4*cos(2*t)^2)^(1/2)], [2*cos(2*t)/(sin(t)^2+4*cos(2*t)^2)^(1/2)]]);
 

Vector[column](%id = 18446744078379195318)
 

Notice that the columns of a 2 by 2 matrix transformation are the images of the standard unit vectors (1,0)  and 0, 1. Since the car points in the direction of 1, 0and we want it to point in the direction of the unit vector T, the image of 1, 0 must be T. Furthermore, if we were to rotate 1, 0by 90 degrees counter-clockwise, we would get 0, 1; so rotating T by 90 degrees counter-clockwise, we get the image of 0, 1. Let's call this image U. (It is the unit normal vector.) 

> U := Vector[column]([[-2*cos(2*t)/(sin(t)^2+4*cos(2*t)^2)^(1/2)], [-sin(t)/(sin(t)^2+4*cos(2*t)^2)^(1/2)]]);
 

Vector[column](%id = 18446744078379195438)
 

The rotation matrix we seek has columns T and U which are orthonormal vectors - ie unit length and orthogonal/perpendicular. 

> R := Matrix([T,U]);
 

Matrix(%id = 18446744078379196158)
 

We must also construct the corresponding homogeneous version of R: 

> Rh := Matrix([[-sin(t)/(sin(t)^2+4*cos(2*t)^2)^(1/2), -2*cos(2*t)/(sin(t)^2+4*cos(2*t)^2)^(1/2), 0], [2*cos(2*t)/(sin(t)^2+4*cos(2*t)^2)^(1/2), -sin(t)/(sin(t)^2+4*cos(2*t)^2)^(1/2), 0], [0, 0, 1]]);
 

Matrix(%id = 18446744078379196278)
 

>
 

We are ready to test our results - we rotate the car at the origin and then translate it to the curve for various t values:  Tcurve.Rh.car (as is typical, order is important here!) 

Now try out the animation: 

Plot_2d
 

 

 

Here is what happens if I mess up the order and use Rh.Tcurve.car rather than the correct Tcurve.Rh.car  

Plot_2d 

Tcurve.Rh.car is correct (the car is originally at the origin - so we first rotate it there - ie Rh should be first on the right side, before we translate): 

Plot_2d 

Why did we need to use the unit vectors in the rotation matrix?  Othewise that matix won't preserve distance, ie the size of the car.  

Let's see that in action by using the matrix obtained by the velocity vector (tangent) and a vector orthogonal to it (negative reciprocal slope) placed into homogeneous coordinates without diving by the norm: 

Matrix(%id = 18446744078379185566) 

> Plot_2d