Merge branch 'inelastic-collisions' into c++-port

This commit is contained in:
Antonio Carzaniga 2022-12-28 11:47:05 +01:00
commit 6078f57647
4 changed files with 113 additions and 32 deletions

111
balls.cc
View File

@ -18,6 +18,55 @@ unsigned int v_angle_max = 100;
ball * balls = nullptr;
unsigned int n_balls = 50;
// Coefficient of restitution:
// C_r == 1.0 ==> perfectly elastic collisions
// C_r == 0.0 ==> perfectly inelastic collisions
//
static double C_r = 1.0;
static int c_r_display_countdown = 0;
static int c_r_display_init = 300;
void restitution_coefficient_set (double c) {
C_r = c;
if (C_r > 1.0)
C_r = 1.0;
else if (C_r < 0.0)
C_r = 0.0;
}
double restitution_coefficient_get () {
return C_r;
}
void restitution_coefficient_change (double d) {
C_r += d;
if (C_r > 1.0)
C_r = 1.0;
else if (C_r < 0.0)
C_r = 0.0;
restitution_coefficient_show ();
}
void restitution_coefficient_show () {
c_r_display_countdown = c_r_display_init;
};
void restitution_coefficient_draw (cairo_t * cr) {
static const double margin = 20;
if (c_r_display_countdown != 0) {
cairo_save(cr);
cairo_new_path(cr);
cairo_move_to(cr, margin, margin);
cairo_line_to(cr, margin + (width - 2*margin)*C_r, margin);
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
cairo_set_line_width(cr, margin/2);
cairo_stroke(cr);
if (c_r_display_countdown > 0)
--c_r_display_countdown;
cairo_restore(cr);
}
}
static vec2d random_velocity() {
double r2;
vec2d v;
@ -49,43 +98,45 @@ void balls_init_state () {
}
}
void ball_walls_collision (ball * p) {
if (p->position.x + p->radius > width) { /* right wall */
if (p->velocity.x > 0) {
p->position.x -= p->position.x + p->radius - width;
p->velocity.x = -C_r*p->velocity.x;
}
} else if (p->position.x < p->radius) { /* left wall */
if (p->velocity.x < 0) {
p->position.x += p->radius - p->position.x;
p->velocity.x = -C_r*p->velocity.x;
}
}
if (p->position.y + p->radius > height) { /* bottom wall */
if (p->velocity.y > 0) {
p->position.y -= p->position.y + p->radius - height;
p->velocity.y = -C_r*p->velocity.y;
}
} else if (p->position.y < p->radius) { /* top wall */
if (p->velocity.y < 0) {
p->position.y += p->radius - p->position.y;
p->velocity.y = -C_r*p->velocity.y;
}
}
}
void ball_update_state (ball * p) {
vec2d g = gravity_vector (p);
p->position += delta*p->velocity + delta*delta*g/2.0;
p->velocity += delta*g;
if (p->position.x + p->radius > width) { /* right wall */
if (p->velocity.x > 0) {
p->position.x -= p->position.x + p->radius - width;
p->velocity.x = -p->velocity.x;
}
} else if (p->position.x < p->radius) { /* left wall */
if (p->velocity.x < 0) {
p->position.x += p->radius - p->position.x;
p->velocity.x = -p->velocity.x;
}
}
if (p->position.y + p->radius > height) { /* bottom wall */
if (p->velocity.y > 0) {
p->position.y -= p->position.y + p->radius - height;
p->velocity.y = -p->velocity.y;
}
} else if (p->position.y < p->radius) { /* top wall */
if (p->velocity.y < 0) {
p->position.y += p->radius - p->position.y;
p->velocity.y = -p->velocity.y;
}
}
p->angle += delta*p->v_angle;
while (p->angle >= 2*M_PI)
p->angle -= 2*M_PI;
while (p->angle < 0)
p->angle += 2*M_PI;
ball_walls_collision (p);
}
void ball_elastic_collision (ball * p, ball * q) {
void ball_ball_collision (ball * p, ball * q) {
vec2d pq = q->position - p->position;
double d2 = vec2d::dot(pq,pq);
double r = p->radius + q->radius;
@ -94,13 +145,19 @@ void ball_elastic_collision (ball * p, ball * q) {
double mp = p->radius * p->radius;
double mq = q->radius * q->radius;
double m_total = mp + mp;
double d = sqrt(d2);
vec2d pq_overlap = (r - d)/d*pq;
p->position -= pq_overlap*mq/m_total;
q->position += pq_overlap*mp/m_total;
double f = vec2d::dot(pq_v, pq);
if (f < 0) {
f /= d2*(mp + mq);
p->velocity += 2*mq*f*pq;
q->velocity -= 2*mp*f*pq;
p->velocity += 2*C_r*mq*f*pq;
q->velocity -= 2*C_r*mp*f*pq;
}
}
}

View File

@ -39,8 +39,14 @@ extern int face_rotation;
extern void balls_init ();
extern void balls_destroy ();
extern void ball_update_state (ball * p);
extern void ball_elastic_collision (ball * p, ball * q);
extern void ball_ball_collision (ball * p, ball * q);
extern void ball_reposition (ball * b);
extern void balls_draw (cairo_t * cr);
extern void restitution_coefficient_show ();
extern void restitution_coefficient_draw (cairo_t * cr);
extern void restitution_coefficient_set (double c);
extern double restitution_coefficient_get ();
extern void restitution_coefficient_change (double d);
#endif

View File

@ -91,6 +91,11 @@ void gravity_collisions (ball * begin, ball * end) {
double d2 = vec2d::dot(b_c, b_c);
double r = b->radius + g_r;
if (d2 <= r*r) {
double d = sqrt(d2);
if (d <= b->radius)
b->position = vec2d{width/2.0,height/2.0+r};
else
b->position += (r - d)/d*b_c;
double f = vec2d::dot(b->velocity, b_c);
if (f < 0) {
f /= d2;

21
main.cc
View File

@ -21,18 +21,18 @@
void check_collisions_simple () {
for(unsigned int i = 0; i < n_balls; ++i)
for(unsigned int j = i + 1; j < n_balls; ++j)
ball_elastic_collision(balls + i, balls + j);
ball_ball_collision(balls + i, balls + j);
for(unsigned int j = 0; j < n_balls; ++j)
ball_elastic_collision(&spaceship, balls + j);
ball_ball_collision(&spaceship, balls + j);
gravity_collisions (balls, balls + n_balls);
gravity_collisions (&spaceship, &spaceship + 1);
}
void check_collisions_with_index () {
c_index_build();
c_index_check_collisions(ball_elastic_collision);
c_index_check_collisions(ball_ball_collision);
for(unsigned int j = 0; j < n_balls; ++j)
ball_elastic_collision(&spaceship, balls + j);
ball_ball_collision(&spaceship, balls + j);
gravity_collisions (balls, balls + n_balls);
gravity_collisions (&spaceship, &spaceship + 1);
}
@ -66,6 +66,7 @@ gboolean draw_frame (GtkWidget * widget, cairo_t *cr, gpointer data) {
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
cairo_paint(cr);
gravity_draw (cr);
restitution_coefficient_draw (cr);
balls_draw (cr);
spaceship_draw (cr);
return FALSE;
@ -100,6 +101,12 @@ gint keyboard_input (GtkWidget *widget, GdkEventKey *event) {
case GDK_KEY_Right:
gravity_change (10, 0);
break;
case GDK_KEY_R:
restitution_coefficient_change (0.01);
break;
case GDK_KEY_r:
restitution_coefficient_change (-0.01);
break;
case GDK_KEY_G:
case GDK_KEY_g:
gravity_show ();
@ -150,6 +157,7 @@ void print_usage (const char * progname) {
"\tface=<filename>\n"
"\tstats=<sample-count> :: rendering timing statitstics (0=disabled, default)\n"
"\tcollisions=<C> :: n=no collisions, s=simple, i=index\n"
"\trestitution=<C_r> :: restitution coefficient C_r\n"
"\t-r :: activate face rotation\n",
progname);
}
@ -247,6 +255,11 @@ int main (int argc, const char *argv[]) {
face_rotation = 1;
continue;
}
double c_r;
if (sscanf(argv[i], "restitution=%lf", &c_r) == 1) {
restitution_coefficient_set (c_r);
continue;
}
print_usage(argv[0]);
return 1;
}