From 272a545bd83c589503516764430f8d64818cd915 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Jan 20 2019 18:28:26 +0000 Subject: c gtk3: gravity --- diff --git a/c/gtk3/gravity/gravity.c b/c/gtk3/gravity/gravity.c new file mode 100644 index 0000000..2198897 --- /dev/null +++ b/c/gtk3/gravity/gravity.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include + + +#define BALLS_COUNT 5 + +const double pi = 3.141592653589793; +const double g = 100.0; +const guint interval_ms = 10; + +typedef struct { + double x, y, vx, vy, ax, ay, r, m; +} Ball; + +Ball balls[BALLS_COUNT]; +double ring_x = 500.0; +double ring_y = 500.0; + +GtkWidget *window; + + +double ring_normalize(double a, double ring_size) { + if (!ring_size) return a; + return a - ring_size*floor(a/ring_size); +} + +double ring_delta(double a, double b, double ring_size) { + if (!ring_size) return a - b; + double delta = ring_normalize(a - b, ring_size); + if (delta > ring_size/2) delta -= ring_size; + return delta; +} + +gboolean timeout(gpointer data) { + // draw do 100 frames per seconds (see interval_ms) + // and 1000 calculation cycles per each frame + double dt = (double)interval_ms/1000.0/1000.0; + for(int t = 0; t < 1000; ++t) { + // reset accelerations + for(int i = 0; i < BALLS_COUNT; ++i) { + balls[i].ax = 0; + balls[i].ay = 0; + } + + // accumulate accelerations and detect collisions + for(int i = 0; i < BALLS_COUNT; ++i) { + for(int j = i+1; j < BALLS_COUNT; ++j) { + double dx = ring_delta(balls[j].x, balls[i].x, ring_x); + double dy = ring_delta(balls[j].y, balls[i].y, ring_y); + double d = sqrt(dx*dx + dy*dy); + double nx = dx/d; + double ny = dy/d; + if (d > balls[i].r + balls[j].r) { + // balls don't touches, just apply gravity acceleration + double ax = g*balls[i].m*balls[j].m*nx/(d*d); + double ay = g*balls[i].m*balls[j].m*ny/(d*d); + balls[i].ax = balls[i].ax + ax; + balls[i].ay = balls[i].ay + ay; + balls[j].ax = balls[j].ax - ax; + balls[j].ay = balls[j].ay - ay; + } else { + // collision detected + double dvx = balls[j].vx - balls[i].vx; + double dvy = balls[j].vy - balls[i].vy; + double dv = dvx*nx + dvy*ny; + if (dv < 0) { + // calc velocities after ricochet, if balls still flies towards each other + double v0 = 2.0*dv*balls[j].m/(balls[i].m + balls[j].m); + double v1 = -2.0*dv*balls[i].m/(balls[i].m + balls[j].m); + balls[i].vx = balls[i].vx + v0*nx; + balls[i].vy = balls[i].vy + v0*ny; + balls[j].vx = balls[j].vx + v1*nx; + balls[j].vy = balls[j].vy + v1*ny; + } + } + } + } + + // apply accelerations + for(int i = 0; i < BALLS_COUNT; ++i) { + balls[i].vx = balls[i].vx + balls[i].ax*dt/balls[i].m; + balls[i].vy = balls[i].vy + balls[i].ay*dt/balls[i].m; + balls[i].x = ring_normalize(balls[i].x + balls[i].vx*dt, ring_x); + balls[i].y = ring_normalize(balls[i].y + balls[i].vy*dt, ring_y); + } + } + + gtk_widget_queue_draw(window); + return TRUE; +} + +gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data) { + cairo_set_source_rgba(cr, 0.9, 0.9, 0.9, 1); + cairo_paint(cr); + + cairo_set_source_rgba(cr, 0, 0, 0, 1); + cairo_set_line_width(cr, 1.0); + for(int i = 0; i < BALLS_COUNT; ++i) { + double r = balls[i].r; + for(int j = -1; j <= 1; ++j) { + for(int k = -1; k <= 1; ++k) { + double x = balls[i].x + ring_x*j; + double y = balls[i].y + ring_y*k; + cairo_arc(cr, x, y, r, 0, 2*pi); + cairo_stroke(cr); + } + } + } + return TRUE; +} + +void activate(GtkApplication* app, gpointer data) { + srand(time(NULL)); + for(int i = 0; i < BALLS_COUNT; ++i) { + double r = 10.0 + 30.0*(rand()/(double)(RAND_MAX)); + balls[i].x = ring_x*rand()/(double)(RAND_MAX); + balls[i].y = ring_y*rand()/(double)(RAND_MAX); + balls[i].vx = 0.0; + balls[i].vy = 0.0; + balls[i].r = r; + balls[i].m = r*r*r; + } + + window = gtk_application_window_new (app); + g_signal_connect(window, "draw", G_CALLBACK(draw), NULL); + gtk_window_set_default_size(GTK_WINDOW(window), (int)ring_x, (int)ring_y); + gtk_widget_show_all(window); + + g_timeout_add(interval_ms, timeout, NULL); +} + +int main(int argc, char **argv) { + GtkApplication *application = gtk_application_new(NULL, 0); + g_signal_connect(application, "activate", G_CALLBACK(activate), NULL); + int status = g_application_run(G_APPLICATION(application), argc, argv); + g_object_unref(application); + return status; +} +