(gravity) added radial, Newtonian (1/r^2) force field

This commit is contained in:
Antonio Carzaniga 2021-12-12 00:36:07 +01:00
parent dc0e5be086
commit 53e71fa27d
4 changed files with 124 additions and 26 deletions

11
balls.c
View File

@ -48,11 +48,14 @@ void balls_init_state () {
} }
void ball_update_state (struct ball * p) { void ball_update_state (struct ball * p) {
p->x += delta*p->v_x + delta*delta*g_x/2.0; struct gravity_vector g;
p->v_x += delta*g_x; gravity_get_vector (&g, p);
p->y += delta*p->v_y + delta*delta*g_y/2.0; p->x += delta*p->v_x + delta*delta*g.x/2.0;
p->v_y += delta*g_y; 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->x + p->radius > width) { /* right wall */
if (p->v_x > 0) { if (p->v_x > 0) {

108
gravity.c
View File

@ -4,36 +4,110 @@
#include "gravity.h" #include "gravity.h"
#include "game.h" #include "game.h"
double g_y = 20; static double g_y = 20;
double g_x = 0; 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_countdown = 0;
static int gravity_vector_init = 300; 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) { void gravity_draw (cairo_t * cr) {
if (gravity_vector_countdown != 0) { if (constant_field) {
cairo_save(cr); if (gravity_vector_countdown != 0) {
cairo_new_path(cr); cairo_save(cr);
cairo_move_to(cr, width/2, height/2); cairo_new_path(cr);
cairo_line_to(cr, width/2 + g_x, height/2 + g_y); 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_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_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); cairo_restore(cr);
} }
} }
void gravity_show () { 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) { void gravity_change (double dx, double dy) {
g_x += dx; if (constant_field) {
g_y += dy; g_x += dx;
gravity_show (); 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;
}
}
}
}

View File

@ -1,11 +1,22 @@
#ifndef GRAVITY_H_INCLUDED #ifndef GRAVITY_H_INCLUDED
#define GRAVITY_H_INCLUDED #define GRAVITY_H_INCLUDED
extern double g_y; #include "balls.h"
extern double g_x;
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_draw (cairo_t * cr);
extern void gravity_change (double dx, double dy); extern void gravity_change (double dx, double dy);
extern void gravity_show (); extern void gravity_show ();
extern void gravity_collisions (struct ball * begin, struct ball * end);
#endif #endif

16
main.c
View File

@ -24,6 +24,8 @@ void check_collisions_simple () {
ball_elastic_collision(balls + i, balls + j); ball_elastic_collision(balls + i, balls + j);
for(int j = 0; j < n_balls; ++j) for(int j = 0; j < n_balls; ++j)
ball_elastic_collision(&spaceship, balls + j); ball_elastic_collision(&spaceship, balls + j);
gravity_collisions (balls, balls + n_balls);
gravity_collisions (&spaceship, &spaceship + 1);
} }
void check_collisions_with_index () { void check_collisions_with_index () {
@ -31,6 +33,8 @@ void check_collisions_with_index () {
c_index_check_collisions(ball_elastic_collision); c_index_check_collisions(ball_elastic_collision);
for(int j = 0; j < n_balls; ++j) for(int j = 0; j < n_balls; ++j)
ball_elastic_collision(&spaceship, balls + j); ball_elastic_collision(&spaceship, balls + j);
gravity_collisions (balls, balls + n_balls);
gravity_collisions (&spaceship, &spaceship + 1);
} }
void (*check_collisions)() = 0; void (*check_collisions)() = 0;
@ -137,7 +141,8 @@ void print_usage (const char * progname) {
"options:\n" "options:\n"
"\t<width>x<height>\n" "\t<width>x<height>\n"
"\tn=<number of balls>\n" "\tn=<number of balls>\n"
"\tfx=<x-force>\n" "\tfconst=<x-force>,<y-force> :: constant force field\n"
"\tfnewt=<radius>,<g> :: radial, Newtonian force field\n"
"\tfy=<y-force>\n" "\tfy=<y-force>\n"
"\tradius=<min-radius>-<max-radius>\n" "\tradius=<min-radius>-<max-radius>\n"
"\tv=<min-velocity>-<max-velocity>\n" "\tv=<min-velocity>-<max-velocity>\n"
@ -193,16 +198,21 @@ gboolean timeout (gpointer user_data) {
int main (int argc, const char *argv[]) { int main (int argc, const char *argv[]) {
int w = DEFAULT_WIDTH; int w = DEFAULT_WIDTH;
int h = DEFAULT_HEIGHT; int h = DEFAULT_HEIGHT;
double fa, fb;
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
if (sscanf(argv[i], "%dx%d", &w, &h) == 2) if (sscanf(argv[i], "%dx%d", &w, &h) == 2)
continue; continue;
if (sscanf(argv[i], "n=%u", &n_balls) == 1) if (sscanf(argv[i], "n=%u", &n_balls) == 1)
continue; 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; 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; continue;
}
if (sscanf(argv[i], "radius=%u-%u", &radius_min, &radius_max) == 2) if (sscanf(argv[i], "radius=%u-%u", &radius_min, &radius_max) == 2)
continue; continue;
if (sscanf(argv[i], "v=%u-%u", &v_min, &v_max) == 2) if (sscanf(argv[i], "v=%u-%u", &v_min, &v_max) == 2)