diff --git a/balls.c b/balls.c index 240e8bf..5c908b7 100644 --- a/balls.c +++ b/balls.c @@ -48,11 +48,14 @@ void balls_init_state () { } void ball_update_state (struct ball * p) { - p->x += delta*p->v_x + delta*delta*g_x/2.0; - p->v_x += delta*g_x; + struct gravity_vector g; + gravity_get_vector (&g, p); - p->y += delta*p->v_y + delta*delta*g_y/2.0; - p->v_y += delta*g_y; + p->x += delta*p->v_x + delta*delta*g.x/2.0; + p->v_x += delta*g.x; + + p->y += delta*p->v_y + delta*delta*g.y/2.0; + p->v_y += delta*g.y; if (p->x + p->radius > width) { /* right wall */ if (p->v_x > 0) { diff --git a/gravity.c b/gravity.c index 0b5eb64..1590cdf 100644 --- a/gravity.c +++ b/gravity.c @@ -4,36 +4,110 @@ #include "gravity.h" #include "game.h" -double g_y = 20; -double g_x = 0; +static double g_y = 20; +static double g_x = 0; + +static double g_r = 50; +static double g_g = 10000000; + +static int constant_field = 1; static int gravity_vector_countdown = 0; static int gravity_vector_init = 300; +void gravity_constant_field (double x, double y) { + constant_field = 1; + g_x = x; + g_y = y; +} + +void gravity_newton_field (double r, double g) { + constant_field = 0; + g_r = r; + g_g = g; +} + void gravity_draw (cairo_t * cr) { - if (gravity_vector_countdown != 0) { - cairo_save(cr); - cairo_new_path(cr); - cairo_move_to(cr, width/2, height/2); - cairo_line_to(cr, width/2 + g_x, height/2 + g_y); + if (constant_field) { + if (gravity_vector_countdown != 0) { + cairo_save(cr); + cairo_new_path(cr); + cairo_move_to(cr, width/2, height/2); + cairo_line_to(cr, width/2 + g_x, height/2 + g_y); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + cairo_arc(cr, width/2 + g_x, height/2 + g_y, 3, 0, 2*M_PI); + cairo_fill(cr); + if (gravity_vector_countdown > 0) + --gravity_vector_countdown; + cairo_restore(cr); + } + } else { cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_set_line_width(cr, 1.0); + cairo_save (cr); + cairo_new_path (cr); + cairo_arc (cr, width/2, height/2, g_r, 0, 2*M_PI); + cairo_set_line_width(cr, 3.0); cairo_stroke(cr); - cairo_arc(cr, width/2 + g_x, height/2 + g_y, 3, 0, 2*M_PI); - cairo_fill(cr); - if (gravity_vector_countdown > 0) - --gravity_vector_countdown; cairo_restore(cr); } } void gravity_show () { - gravity_vector_countdown = gravity_vector_init; + if (constant_field) + gravity_vector_countdown = gravity_vector_init; }; void gravity_change (double dx, double dy) { - g_x += dx; - g_y += dy; - gravity_show (); -}; + if (constant_field) { + g_x += dx; + g_y += dy; + gravity_show (); + } else { + g_r += dx; + g_g += dy; + } +} +void gravity_get_vector (struct gravity_vector * v, const struct ball * b) { + if (constant_field) { + v->x = g_x; + v->y = g_y; + } else { + double dx = width/2 - b->x; + double dy = height/2 - b->y; + double r2 = dx*dx+dy*dy; + if (r2 < g_r*g_r) { + v->x = 0; + v->y = 0; + } else { + double r = sqrt(r2); + v->x = g_g/r2/r*dx; + v->y = g_g/r2/r*dy; + } + } +} + +void gravity_collisions (struct ball * begin, struct ball * end) { + if (constant_field) + return; + for (struct ball * b = begin; b != end; ++b) { + double dx = b->x - width/2; + double dy = b->y - height/2; + double d2 = dx*dx + dy*dy; + double r = b->radius + g_r; + if (d2 <= r*r) { + double dv_x = b->v_x; + double dv_y = b->v_y; + + double f = dv_x*dx + dv_y*dy; + + if (f < 0) { + f /= d2; + b->v_x -= 2*f*dx; + b->v_y -= 2*f*dy; + } + } + } +} diff --git a/gravity.h b/gravity.h index 08bf4f1..4620a9e 100644 --- a/gravity.h +++ b/gravity.h @@ -1,11 +1,22 @@ #ifndef GRAVITY_H_INCLUDED #define GRAVITY_H_INCLUDED -extern double g_y; -extern double g_x; +#include "balls.h" + +struct gravity_vector { + double x; + double y; +}; + +extern void gravity_constant_field (double x, double y); +extern void gravity_newton_field (double r, double g); + +extern void gravity_get_vector (struct gravity_vector * v, const struct ball * b); extern void gravity_draw (cairo_t * cr); extern void gravity_change (double dx, double dy); extern void gravity_show (); +extern void gravity_collisions (struct ball * begin, struct ball * end); + #endif diff --git a/main.c b/main.c index bff379b..37a1c12 100644 --- a/main.c +++ b/main.c @@ -24,6 +24,8 @@ void check_collisions_simple () { ball_elastic_collision(balls + i, balls + j); for(int j = 0; j < n_balls; ++j) ball_elastic_collision(&spaceship, balls + j); + gravity_collisions (balls, balls + n_balls); + gravity_collisions (&spaceship, &spaceship + 1); } void check_collisions_with_index () { @@ -31,6 +33,8 @@ void check_collisions_with_index () { c_index_check_collisions(ball_elastic_collision); for(int j = 0; j < n_balls; ++j) ball_elastic_collision(&spaceship, balls + j); + gravity_collisions (balls, balls + n_balls); + gravity_collisions (&spaceship, &spaceship + 1); } void (*check_collisions)() = 0; @@ -137,7 +141,8 @@ void print_usage (const char * progname) { "options:\n" "\tx\n" "\tn=\n" - "\tfx=\n" + "\tfconst=, :: constant force field\n" + "\tfnewt=, :: radial, Newtonian force field\n" "\tfy=\n" "\tradius=-\n" "\tv=-\n" @@ -193,16 +198,21 @@ gboolean timeout (gpointer user_data) { int main (int argc, const char *argv[]) { int w = DEFAULT_WIDTH; int h = DEFAULT_HEIGHT; + double fa, fb; for (int i = 1; i < argc; ++i) { if (sscanf(argv[i], "%dx%d", &w, &h) == 2) continue; if (sscanf(argv[i], "n=%u", &n_balls) == 1) continue; - if (sscanf(argv[i], "fx=%lf", &g_x) == 1) + if (sscanf(argv[i], "fconst=%lf,%lf", &fa, &fb) == 2) { + gravity_constant_field (fa,fb); continue; - if (sscanf(argv[i], "fy=%lf", &g_y) == 1) + } + if (sscanf(argv[i], "fnewt=%lf,%lf", &fa, &fb) == 2) { + gravity_newton_field (fa,fb); continue; + } if (sscanf(argv[i], "radius=%u-%u", &radius_min, &radius_max) == 2) continue; if (sscanf(argv[i], "v=%u-%u", &v_min, &v_max) == 2)