|
|
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 |
}
|