/*
* acc specifies how much to accelerate, or break if it is negative.
* It may not have absolute value larger than the acc parameter given to init
*
* da specifies how much to turn right or left.
* It may not have absolute value larger than the turn parameter given to init
*
* x, y, dx, dy, alpha, dalpha, wp, and skidding are all global variables
* x and y give the location of the car, while dx and dy give the velocity
* alpha gives the angle the car is facing, while dalpha gives the rotational momentum when skidding
* skidding indicates if the car was skidding in the previous time step
* wp gives the current waypoint index
*
* The most complicated part of the code is dealing with friction and air resistance
* Most importantly, when accelerating forward, the air resistance is removed from the forward
* force before friction is checked (more as if the car were propelled by rockets than wheels).
* In the cross direction, the forces of friction and air resistance are added to see if the car
* can make the turn.
*/
void step(double acc, double da){
if (!skidding) {
//not skidding, so reset
dalpha = 0;
}
if(acc < -friction){
acc = -friction;
}
dalpha += da;
alpha += dalpha; //adjust the direction we're facing
if (alpha > Math.PI) alpha -= 2 * Math.PI;
else if (alpha < -Math.PI) alpha += 2 * Math.PI;
dalpha /= 2; //dampen the rotational momentum
double cross = (dx * Math.sin(alpha) - dy * Math.cos(alpha)); //signed magnitude of velocity which is across direction we're facing
double forward = (dx * Math.cos(alpha) + dy * Math.sin(alpha)); //signed magnitude of velocity which is in direction we're facing
//now compute the forward and cross components of air resistance. Resistance is proportional to velocity squared
double crossair = cross / Math.hypot(cross, forward) * air * Math.hypot(dx, dy) * Math.hypot(dx, dy); //signed
double forwardair = Math.abs(forward / Math.hypot(cross, forward) * air * Math.hypot(dx, dy) * Math.hypot(dx, dy)); //unsigned
if (cross == 0 && forward == 0) crossair = forwardair = 0; //avoid NaN
acc -= forwardair; //remove some forward power due to air resistance
if (acc < 0 && Math.abs(acc) > Math.abs(forward)) acc = -forward;//can only stop as much as we're moving
else if (acc < 0 && forward < 0) acc = -acc;//breaking while going backwards results in positive force
double mcross = 0, macc = 0;
//next we have to compute how much of the force of friction goes in the forward direction, and how much goes in the cross direction
mcross = friction * cross / Math.hypot(cross, acc);
macc = friction * acc / Math.hypot(cross, acc);
if (cross == 0 && acc == 0) { //avoid NaN when stopped
mcross = 0;
macc = 0;
}
mcross += crossair; //the air resistance slows us down in the cross direction
//detect skidding. The cross velocity is the current cross velocity, minus the cross air resistance
//the forward velocity used is either the acceleration, or the acceleration minus the air resistance
//we do the second to avoid thinking that we are skidding when we are really just slowing rapidly due to air resistance
skidding = Math.hypot(cross - crossair, acc) >= friction && Math.hypot(cross - crossair, acc + forwardair) > friction;
//finally we check to see if the cross force or forward force on the car exceed the frictional allowance, and adjust accordingly
if (cross > mcross && cross >= 0) {
cross = mcross;
} else if (cross < mcross && cross <= 0) {
cross = mcross;
}
if (acc > macc && acc >= 0) {
acc = macc;
} //else case intentionally omitted
//calculations all done, adjust velocity and position
dx += cross * Math.cos(alpha + Math.PI / 2);
dy += cross * Math.sin(alpha + Math.PI / 2);
dx += acc * Math.cos(alpha);
dy += acc * Math.sin(alpha);
x += dx;
y += dy;
finally, check the waypoints. wx and wy give the locations
while(wp < wx.length && Math.hypot(x-wx[wp],y-wy[wp]) < 1000){
wp++;
}
}