Blob Blame Raw

#include <math.h>
#include <stdlib.h>
#include <helianthus.h>



#define MAX_BALLS   1000
#define MAX_LINKS   10000
#define BALL_RADIUS 1.0
#define BALL_FADE   0.8
#define LINK_K      0.05
#define GRID_SIZE   5



typedef struct {
  double x, y;
  double vx, vy;
} Ball;


typedef struct {
  double dx, dy;
  Ball *a, *b;
} Link;



Ball balls[MAX_BALLS];
Link links[MAX_LINKS];
int ballsCount = 0;
int linksCount = 0;

double remainDt;

Ball *mball;
double mdx, mdy;
double mx, my;



void ballUpdate(Ball *ball, double dt) {
  double fade = pow(BALL_FADE, dt);
  ball->x += ball->vx*dt;
  ball->y += ball->vy*dt;
  ball->vx *= fade;
  ball->vy *= fade;
}


void linkUpdate(Link *link, double dt) {
  double dx = link->b->x - link->a->x;
  double dy = link->b->y - link->a->y;

  double dvx = (dx - link->dx)*0.5*LINK_K;
  double dvy = (dy - link->dy)*0.5*LINK_K;

  link->a->vx += dvx; link->b->vx -= dvx;
  link->a->vy += dvy; link->b->vy -= dvy;
}


void update(double dt) {
  remainDt += dt;
  while(remainDt > 0.001) {
    remainDt -= 0.001;
    for(int i = 0; i < linksCount; ++i)
      linkUpdate(&links[i], dt);
    for(int i = 0; i < ballsCount; ++i)
      ballUpdate(&balls[i], dt);
  }
}


void init() {
  double dist = 3*BALL_RADIUS;
  for(int i = 0; i < GRID_SIZE; ++i) {
    for(int j = 0; j < GRID_SIZE; ++j) {
      Ball *ball = &balls[ballsCount++];
      ball->x = (i - 0.5*(GRID_SIZE - 1))*dist;
      ball->y = (j - 0.5*(GRID_SIZE - 1))*dist;
      if (i > 0) {
        Link *link = &links[linksCount++];
        link->a = ball - GRID_SIZE;
        link->b = ball;
        link->dx = dist;
      }
      if (j > 0) {
        Link *link = &links[linksCount++];
        link->a = ball - 1;
        link->b = ball;
        link->dy = dist;
      }
    }
  }
}


void draw() {
  double dt = windowGetFrameTime();
  double w = windowGetWidth();
  double h = windowGetHeight();

  saveState();
  translate(w/2, h/2);
  zoom(20);

  mx = mouseTransformedX();
  my = mouseTransformedY();
  if (mouseDown("left")) {
    if (!mball) {
      for(int i = ballsCount-1; i >= 0; --i) {
        Ball *ball = &balls[i];
        double dx = mx - ball->x;
        double dy = my - ball->y;
        if (dx*dx + dy*dy <= BALL_RADIUS*BALL_RADIUS) {
          mball = ball;
          mdx = dx;
          mdy = dy;
          break;
        }
      }
    }
  } else {
    mball = NULL;
  }


  update(dt);

  if (mball) {
    mball->x = mx - mdx;
    mball->y = my - mdy;
    mball->vx = 0;
    mball->vy = 0;
  }

  stroke(COLOR_BLACK);
  strokeWidth(BALL_RADIUS*0.1);
  noFill();
  for(int i = 0; i < linksCount; ++i) {
    Link *link = &links[i];
    line(link->a->x, link->a->y, link->b->x, link->b->y);
  }

  noStroke();
  fill(COLOR_BLUE);
  for(int i = 0; i < ballsCount; ++i) {
    Ball *ball = &balls[i];
    circle(ball->x, ball->y, BALL_RADIUS);
  }

  restoreState();
}


int main() {
  windowSetTitle("Rubber");
  windowSetResizable(TRUE);
  windowSetVariableFrameRate();
  windowSetInit(&init);
  windowSetDraw(&draw);
  windowRun();
  return 0;
}