Stipple Cat


  // Stipple Cat  Jim Bumgardner
  //
  //   @pjs preload="/assets/cats2_0.png";
  //   @pjs preload="/assets/cats2_1.png";
  //   @pjs preload="/assets/cats2_2.png";
  //   @pjs preload="/assets/cats2_3.png";
  //   @pjs preload="/assets/cats2_4.png";
  //   @pjs preload="/assets/cats2_5.png";
  //   @pjs preload="/assets/cats2_6.png";
  //   @pjs preload="/assets/cats2_7.png";
  
  
  // Play with these...
  float imageScale;
  float dotDensity = .24; // higher ratios = more dots
  
  
  // Probably don't need to play with these as much...
  float  damping = 0.7;
  float  kRadiusFactor = 0.5;
  float  kSpeed = 3.0;
  float  minDistFactor = 2.5; // area of influence - smaller numbers make rendering go faster
  int  nbrParticles = 2200;
  
  int catSpeed = 2;
  
  
  PImage reference; // for dithering initialilzation...
  int nbrCatFrames = 8;
  
  PImage[] cats = new PImage[nbrCatFrames];
  
  class Particle
  {
    float  x, y, vx, vy, rad;
    float  fx, fy, wt;
  
    Particle(float _x, float _y)
    {
      vx = 0;
      vy = 0;
      x = _x;
      y = _y;
      rad = 1;
    }
  }
  
  Particle[] particles;
  
  float minRadius, maxRadius, medRadius;
  
  void setup()
  {
    size(500, 214);
  
    for (int i = 0; i < nbrCatFrames; ++i) {
      cats[i] = loadImage("/assets/cats2_" + i + ".png");
    }
    reference = cats[0];
  
    int dwidth = 136;
    int dheight = 68;
    imageScale = width/(float) dwidth;
  
    particles = new Particle[nbrParticles];
  
    for (int i = 0; i < nbrParticles; ++i) {
      particles[i] = new Particle(random(width), random(height));
    }
  
    frameRate(24);
    // noLoop();
    smooth();
    noStroke();
  
    float medArea = (width*height)/nbrParticles;
    medRadius = sqrt(medArea/PI);
    minRadius = medRadius;
    maxRadius = medRadius*medRadius*1;
    // println("nbrParticles = " + nbrParticles);
    // println("medrad = " + medRadius);
    // println("min-max = " + minRadius + " --> " + maxRadius);
    background(255);
  }
  
  void doPhysics()
  {
    if (frameCount % catSpeed == 0) {
      int frameCtr = (frameCount/catSpeed % nbrCatFrames);
      reference = cats[frameCtr];
    }
  
    for (int i = 0; i < nbrParticles; ++i) {
      int px = (int) (particles[i].x / imageScale);
      int py = (int) (particles[i].y / imageScale);
      if (px >= 0 && px < reference.width && py >= 0 && py < reference.height) {
        int v = (int) red(reference.pixels[ py*reference.width + px ]);
        particles[i].rad = map(v/255.0, 0, 1, minRadius, maxRadius);
      }
    }
  
  
    for (int i = 0; i < nbrParticles; ++i) {
      Particle p = particles[i];
      p.fx = p.fy = p.wt = 0;
  
      p.vx *= damping;
      p.vy *= damping;
    }
  
    // Particle -> particle interactions
    for (int i = 0; i < nbrParticles-1; ++i) {
      Particle p = particles[i];
      for (int j = i+1; j < nbrParticles; ++j) {
        Particle pj = particles[j];
        if (i== j || Math.abs(pj.x - p.x) > p.rad*minDistFactor ||
          Math.abs(pj.y - p.y) > p.rad*minDistFactor)
          continue;
  
        double  dx = p.x - pj.x;
        double  dy = p.y - pj.y;
        double  distance = Math.sqrt(dx*dx+dy*dy);
  
        double  maxDist = (p.rad + pj.rad);
        double  diff = maxDist - distance;
        if (diff > 0) {
          double scle = diff/maxDist;
          scle = scle*scle;
          p.wt += scle;
          pj.wt += scle;
          scle = scle*kSpeed/distance;
          p.fx += dx*scle;
          p.fy += dy*scle;
          pj.fx -= dx*scle;
          pj.fy -= dy*scle;
        }
      }
    }
  
    for (int i = 0; i < nbrParticles; ++i) {
      Particle p = particles[i];
  
      // keep within edges
      double dx, dy, distance, scle, diff;
      double maxDist = p.rad;
      // left edge  
      distance = dx = p.x - 0;    
      dy = 0;
      diff = maxDist - distance;
      if (diff > 0) {
        scle = diff/maxDist;
        scle = scle*scle;
        p.wt += scle;
        scle = scle*kSpeed/distance;
        p.fx += dx*scle;
        p.fy += dy*scle;
      }
      // right edge  
      dx = p.x - width;    
      dy = 0;
      distance = -dx;
      diff = maxDist - distance;
      if (diff > 0) {
        scle = diff/maxDist;
        scle = scle*scle;
        p.wt += scle;
        scle = scle*kSpeed/distance;
        p.fx += dx*scle;
        p.fy += dy*scle;
      }
      // top edge
      distance = dy = p.y - 0;    
      dx = 0;
      diff = maxDist - distance;
      if (diff > 0) {
        scle = diff/maxDist;
        scle = scle*scle;
        p.wt += scle;
        scle = scle*kSpeed/distance;
        p.fx += dx*scle;
        p.fy += dy*scle;
      }
      // bot edge  
      dy = p.y - height;    
      dx = 0;
      distance = -dy;
      diff = maxDist - distance;
      if (diff > 0) {
        scle = diff/maxDist;
        scle = scle*scle;
        p.wt += scle;
        scle = scle*kSpeed/distance;
        p.fx += dx*scle;
        p.fy += dy*scle;
      }
      if (p.wt > 0) {
        p.vx += p.fx/p.wt;
        p.vy += p.fy/p.wt;
      }
      p.x += p.vx;
      p.y += p.vy;
    }
  }
  
  void draw()
  {
    doPhysics();
    fill(255, 72);
    rect(0, 0, width, height);
    stroke(0);
    strokeWeight(medRadius*1.1);
    for (int i = 0; i < nbrParticles; ++i) {
      point(particles[i].x, particles[i].y);
    }
    // if (frameCount % 60 == 0)
    //  println("fps = " + frameRate);
  }