Blame onefile/car.c

261920
261920
#include <math.h></math.h>
261920
#include <string.h></string.h>
261920
#include <helianthus.h></helianthus.h>
261920
261920
261920
#define ROAD_SEGMENTS  128
261920
#define SEGMENTS_WIDTH 25
261920
261920
261920
typedef struct {
261920
  double x, y;
261920
} Point;
261920
261920
typedef struct {
261920
  double m00, m01, m10, m11;
261920
} Matrix;
261920
261920
typedef struct {
261920
  Point p0;
261920
  union {
261920
    Matrix mat;
261920
    struct { Point dir, norm; };
261920
  };
261920
  Matrix matInv;
261920
} Segment;
261920
261920
typedef struct {
261920
  double radius;
261920
  Point pos;
261920
  double angle;
261920
261920
  Point velocity;
261920
  double angVel;
261920
  double force;
261920
261920
  int collision;
261920
  Point norm;
261920
} Wheel;
261920
261920
typedef struct {
261920
  Wheel w0, w1;
261920
  double len;
261920
  Point center;
261920
  double angle;
261920
} Car;
261920
261920
261920
Car car;
261920
int roadSegments;
261920
double roadk0, roadk1;
261920
Segment road[ROAD_SEGMENTS];
261920
261920
261920
261920
void matrixInvert(const Matrix *src, Matrix *dst) {
261920
  double kd = src->m00*src->m11 - src->m01*src->m10;
261920
  kd = fabs(kd) > 1e-10 ? 1/kd : 0;
261920
  dst->m00 =  kd*src->m11;
261920
  dst->m01 = -kd*src->m01;
261920
  dst->m10 = -kd*src->m10;
261920
  dst->m11 =  kd*src->m00;
261920
}
261920
261920
void matrixMultPoint(const Matrix *m, const Point *src, Point *dst) {
261920
  dst->x = src->x*m->m00 + src->y*m->m10;
261920
  dst->y = src->x*m->m01 + src->y*m->m11;
261920
}
261920
261920
261920
void segmentInit(Segment *s, const Point *p0, const Point *p1) {
261920
  s->p0.x = p0->x;
261920
  s->p0.y = p0->y;
261920
  s->dir.x = p1->x - p0->x;
261920
  s->dir.y = p1->y - p0->y;
261920
  double kl = sqrt(s->dir.x*s->dir.x + s->dir.y*s->dir.y);
261920
  kl = kl > 1e-10 ? 1/kl : 0;
261920
  s->norm.x =  kl*s->dir.y;
261920
  s->norm.y = -kl*s->dir.x;
261920
  matrixInvert(&s->mat, &s->matInv);
261920
}
261920
261920
261920
void wheelCollideSegment(Wheel *wheel, Segment *s) {
261920
  Point pc, p = { wheel->pos.x - s->p0.x, wheel->pos.y - s->p0.y };
261920
  matrixMultPoint(&s->matInv, &p, &pc);
261920
  if (pc.x >= 0 && pc.x <= 1 && pc.y < wheel->radius && pc.y > -wheel->radius) {
261920
    pc.y = wheel->radius;
261920
    matrixMultPoint(&s->mat, &pc, &p);
261920
    ++wheel->collision;
261920
    wheel->pos.x = p.x + s->p0.x;
261920
    wheel->pos.y = p.y + s->p0.y;
261920
    wheel->norm.x = s->norm.x;
261920
    wheel->norm.y = s->norm.y;
261920
    return;
261920
  }
261920
261920
  double kd = p.x*p.x + p.y*p.y;
261920
  if (kd > 1e-10 && kd < wheel->radius*wheel->radius) {
261920
    kd = 1/sqrt(kd);
261920
    ++wheel->collision;
261920
    wheel->pos.x = p.x*kd*wheel->radius + s->p0.x;
261920
    wheel->pos.y = p.y*kd*wheel->radius + s->p0.y;
261920
    wheel->norm.x = p.x*kd;
261920
    wheel->norm.y = p.y*kd;
261920
    return;
261920
  }
261920
}
261920
261920
void wheelUpdate(Wheel *wheel, double dt) {
261920
  wheel->pos.x += wheel->velocity.x * dt;
261920
  wheel->pos.y += wheel->velocity.y * dt;
261920
  wheel->angle += wheel->angVel * dt;
261920
261920
  wheel->collision = 0;
261920
  for(int i = 0; i < roadSegments; ++i)
261920
    wheelCollideSegment(wheel, &road[i]);
261920
261920
  wheel->velocity.y += 200*dt;
261920
  if (wheel->collision) {
261920
    double dx = -wheel->norm.y;
261920
    double dy =  wheel->norm.x;
261920
    double v = wheel->velocity.x*dx  + wheel->velocity.y*dy;
261920
    v += wheel->force*dt;
261920
    wheel->velocity.x = v*dx;
261920
    wheel->velocity.y = v*dy;
261920
    wheel->angVel = v/wheel->radius;
261920
  } else {
261920
    wheel->angVel += wheel->force/wheel->radius*dt;
261920
    double maxAngVel = 100;
261920
    if (wheel->angVel >  maxAngVel) wheel->angVel =  maxAngVel; else
261920
    if (wheel->angVel < -maxAngVel) wheel->angVel = -maxAngVel;
261920
  }
261920
}
261920
261920
void wheelDraw(Wheel *wheel) {
261920
  saveState();
261920
  strokeWidth(0.1*wheel->radius);
261920
  stroke(COLOR_BLACK);
261920
  fill(COLOR_GREEN);
261920
  circle(wheel->pos.x, wheel->pos.y, wheel->radius);
261920
  circle(wheel->pos.x + 0.5*wheel->radius*cos(wheel->angle),
261920
         wheel->pos.y + 0.5*wheel->radius*sin(wheel->angle), wheel->radius*0.25);
261920
  restoreState();
261920
}
261920
261920
void carUpdate(Car *car, double dt) {
261920
  Point c = { (car->w0.pos.x + car->w1.pos.x)/2,
261920
              (car->w0.pos.y + car->w1.pos.y)/2 };
261920
  car->center.x = c.x;
261920
  car->center.y = c.y;
261920
261920
  Point d = { car->w1.pos.x - car->w0.pos.x,
261920
              car->w1.pos.y - car->w0.pos.y };
261920
  double kl = d.x*d.x + d.y*d.y;
261920
  kl = kl > 1e-10 ? 1/sqrt(kl) : 0;
261920
  Point n = { d.x*kl, d.y*kl };
261920
  car->angle = atan2(n.y, n.x);
261920
261920
  car->w0.pos.x = c.x - n.x*car->len/2;
261920
  car->w0.pos.y = c.y - n.y*car->len/2;
261920
  car->w1.pos.x = c.x + n.x*car->len/2;
261920
  car->w1.pos.y = c.y + n.y*car->len/2;
261920
261920
  Point np = { -n.y, n.x };
261920
  Point cv = { (car->w0.velocity.x + car->w1.velocity.x)/2,
261920
               (car->w0.velocity.y + car->w1.velocity.y)/2 };
261920
  double v0 = (car->w0.velocity.x - cv.x)*np.x
261920
            + (car->w0.velocity.y - cv.y)*np.y;
261920
  car->w0.velocity.x = cv.x + v0*np.x;
261920
  car->w0.velocity.y = cv.y + v0*np.y;
261920
  double v1 = (car->w1.velocity.x - cv.x)*np.x
261920
            + (car->w1.velocity.y - cv.y)*np.y;
261920
  car->w1.velocity.x = cv.x + v1*np.x;
261920
  car->w1.velocity.y = cv.y + v1*np.y;
261920
261920
261920
261920
  wheelUpdate(&car->w0, dt);
261920
  wheelUpdate(&car->w1, dt);
261920
}
261920
261920
void carDraw(Car *car) {
261920
  saveState();
261920
  translate(car->center.x, car->center.y);
261920
  rotate(car->angle/PI*180);
261920
261920
  strokeWidth(0.02*car->len);
261920
  stroke(COLOR_BLACK);
261920
  fill(COLOR_BLUE);
261920
  double l = car->len;
261920
261920
  moveTo(0, 0);
261920
  lineTo( l*0.75, 0);
261920
  lineTo( l*0.75, -l*0.25);
261920
  lineTo( l*0.50, -l*0.25);
261920
  lineTo( l*0.25, -l*0.50);
261920
  lineTo(-l*0.25, -l*0.50);
261920
  lineTo(-l*0.50, -l*0.25);
261920
  lineTo(-l*0.75, -l*0.25);
261920
  lineTo(-l*0.75, 0);
261920
  closePath();
261920
  restoreState();
261920
261920
  wheelDraw(&car->w0);
261920
  wheelDraw(&car->w1);
261920
}
261920
261920
void roadAddSegemnt(double segmentWidth) {
261920
  if (!roadSegments) {
261920
    Point p0 = { 0, 0 };
261920
    Point p1 = { 0, segmentWidth };
261920
    segmentInit(&road[0], &p0, &p1);
261920
    ++roadSegments;
261920
    return;
261920
  }
261920
261920
  if (roadSegments >= ROAD_SEGMENTS) {
261920
    memmove(road, road + 1, sizeof(*road)*(ROAD_SEGMENTS - 1));
261920
    --roadSegments;
261920
  }
261920
261920
  double ky = 0;
261920
  for(int i = 0; i < 5; ++i) {
261920
    roadk0 += (randomFloat()*2 - 1 - roadk0)*0.05;
261920
    roadk1 += (roadk0 - roadk1)*0.25;
261920
    ky += roadk1;
261920
  }
261920
261920
  for(int i = 0; i < roadSegments; ++i)
261920
    road[i].p0.x -= segmentWidth;
261920
  car.w0.pos.x -= segmentWidth;
261920
  car.w1.pos.x -= segmentWidth;
261920
261920
  Segment *prev = &road[roadSegments-1];
261920
  Point p0 = { prev->p0.x + prev->dir.x,
261920
               prev->p0.y + prev->dir.y };
261920
  Point p1 = { p0.x + segmentWidth,
261920
               p0.y + segmentWidth*0.5*ky };
261920
  segmentInit(&road[roadSegments], &p0, &p1);
261920
  ++roadSegments;
261920
}
261920
261920
261920
261920
void init() {
261920
  for(int i = 0; i < ROAD_SEGMENTS; ++i)
261920
    roadAddSegemnt(SEGMENTS_WIDTH);
261920
  car.w0.pos.x  = car.w1.pos.x  = road[ROAD_SEGMENTS/2].p0.x;
261920
  car.w0.pos.y  = car.w1.pos.y  = road[ROAD_SEGMENTS/2].p0.y - 100;
261920
  car.w0.radius = car.w1.radius = 20;
261920
  car.w1.pos.x += 1;
261920
  car.len = 100;
261920
}
261920
261920
261920
void draw() {
261920
  double dt = windowGetFrameTime();
261920
  double w = windowGetWidth();
261920
  double h = windowGetHeight();
261920
261920
  double fk = 100;
261920
  double force = 0;
261920
  if (keyDown("left"))  force -= 1000;
261920
  if (keyDown("right")) force += 1000;
261920
  car.w0.force = car.w1.force = force;
261920
  if (car.w0.angVel > 1e-10 && car.w0.force > 0) car.w0.force *= 1/(fabs(car.w0.angVel)/fk + 1);
261920
  if (car.w0.angVel < 1e-10 && car.w0.force < 0) car.w0.force *= 1/(fabs(car.w0.angVel)/fk + 1);
261920
  if (car.w1.angVel > 1e-10 && car.w1.force > 0) car.w1.force *= 1/(fabs(car.w1.angVel)/fk + 1);
261920
  if (car.w1.angVel < 1e-10 && car.w1.force < 0) car.w1.force *= 1/(fabs(car.w1.angVel)/fk + 1);
261920
261920
  while(car.w0.pos.x - road[0].p0.x > 512 && road[roadSegments-1].p0.x - car.w0.pos.x < w)
261920
    roadAddSegemnt(SEGMENTS_WIDTH);
261920
261920
  carUpdate(&car, dt);
261920
261920
  saveState();
261920
  translate(w/2, h/2);
261920
  translate(-car.w0.pos.x, -car.w0.pos.y);
261920
261920
  saveState();
261920
  moveTo(road[0].p0.x, road[0].p0.y);
261920
  for(int i = 1; i < roadSegments; ++i)
261920
    lineTo(road[i].p0.x, road[i].p0.y);
261920
  strokePath();
261920
  restoreState();
261920
261920
  carDraw(&car);
261920
261920
  restoreState();
261920
}
261920
261920
261920
int main() {
261920
  windowSetResizable(TRUE);
261920
  windowSetVariableFrameRate();
261920
  windowSetInit(&init);
261920
  windowSetDraw(&draw);
261920
  windowRun();
261920
  return 0;
261920
}