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