Blame c/gtk3/gravity/gravity.c

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