sketch_210604c

NonCircularPacking.pde


class NonCircularPacking {
  
  PGraphics pg;
  PGraphics tmpPg;
  
  ArrayList<AbstractItem> items;
  
  NonCircularPacking() {
    pg = createGraphics(width, height);
    pg.beginDraw();
    pg.background(0, 0);
    pg.endDraw();
    
    tmpPg = createGraphics(pg.width, pg.height);
    tmpPg.beginDraw();
    tmpPg.background(0, 0);
    tmpPg.endDraw();
    
    items = new ArrayList();
  }
  
  boolean isOverlapped(AbstractItem item) {
    
    tmpPg.beginDraw();
    tmpPg.background(0, 0);
    item.draw(tmpPg, 0);
    tmpPg.endDraw();
    
    boolean overlapped = false;
    
    pg.loadPixels();
    tmpPg.loadPixels();
    for (int k = 0; k < pg.pixels.length; k++) {
      float a1 = alpha(pg.pixels[k]);
      float a2 = alpha(tmpPg.pixels[k]);
      
      if (a2 > 0 && a1 > 0) {
        overlapped = true;
        break;
      }
    }
    
    return overlapped;
  }
  
  void addItem(AbstractItem item) {
    pg.beginDraw();
    item.draw(pg, 0);
    pg.endDraw();
    
    items.add(item);
  }
  
  void draw(PGraphics g, int t) {
    for (AbstractItem item : items) {
      item.draw(g, t);
    }
  }
}

PackingItems.pde

abstract class AbstractItem {
  abstract void draw(PGraphics pg, int t);
}

class RectItem extends AbstractItem {
  
  PVector pos;
  
  RectItem(PVector pos) {
    this.pos = pos;
  }
  
  void draw(PGraphics pg, int t) {
    pg.rectMode(CENTER);
    pg.fill(0, 255, 0);
    pg.noStroke();
    pg.rect(pos.x, pos.y, 100, 100);
  }
}

class ImageItem extends AbstractItem {
  
  PVector pos;
  float rot;
  float sc;
  
  PImage img;
  
  ImageItem(PImage img, PVector pos) {
    this(img, pos, 0.0, 1.0);
  }
  
  ImageItem(PImage img, PVector pos, float rot, float sc) {
    this.img = img;
    this.pos = pos;
    this.rot = rot;
    this.sc = sc;
  }
  
  void draw(PGraphics pg, int t) {
    pg.imageMode(CENTER);
    pg.pushMatrix();
    pg.translate(pos.x, pos.y);
    pg.rotate(rot);
    pg.scale(sc);
    pg.image(img, pos.x, pos.y);
    pg.popMatrix();
  }
}

final Comparator<PVector> comp = new Comparator<PVector>() {
  int compare(PVector v1, PVector v2) {
    if (v1.z > v2.z) return 1;
    else if (v1.z == v2.z) return 0;
    return -1;
  }
};

class UFOItem extends AbstractItem {

  PVector pos;
  float rot;
  float sc;
  
  color[] palette;
  float angularVelocity = TWO_PI;

  UFOItem(PVector pos, float rot, float sc, color[] palette) {
    this(pos, rot, sc, palette, TWO_PI*random(1.0, 2.0)*(random(1) > 0.5 ? 1.0 : -1.0));
  }

  UFOItem(PVector pos, float rot, float sc, color[] palette, float angularVelocity) {
    this.pos = pos;
    this.rot = rot;
    this.sc = sc;
    this.palette = palette;
    this.angularVelocity = angularVelocity;
  }

  void draw(PGraphics pg, int t) {
    float s = 0.6;
    int nCircles = 6;

    float w = pg.width;
    float h = pg.height;
    
    pg.pushMatrix();

    pg.rectMode(CENTER);

    pg.translate(pos.x, pos.y);
    pg.rotate(rot);
    pg.scale(sc);
    
    pg.strokeWeight(0.8);

    pg.fill(palette[0]);
    pg.arc(0, 0, w*s, h*s, PI, TWO_PI);

    PVector[] points = new PVector[nCircles];
    float circleWidth = w / 3.0;
    float angleInterval = TWO_PI / nCircles;
    for (int k = 0; k < nCircles; k++) {
      float phi = angleInterval * k;
      float theta = t / 60.0 / TWO_PI;
      float cx = (w / 2.0 - circleWidth / 2.0) * cos(theta * angularVelocity + phi);
      float cz = sin(theta * angularVelocity + phi);
      points[k] = new PVector(cx, 0, cz);
    }

    pg.fill(palette[2]);
    Arrays.sort(points, comp);
    for (int k = 0; k < nCircles; k++) {
      float cx = points[k].x;
      pg.arc(cx, h / 4.0, circleWidth, circleWidth, 0, PI);
    }

    pg.fill(palette[1]);
    pg.beginShape();
    pg.vertex(w/2.0*s, 0);
    pg.vertex(w/2.0, h/4.0);
    pg.vertex(-w/2.0, h/4.0);
    pg.vertex(-w/2.0*s, 0);
    pg.endShape(CLOSE);
    
    pg.popMatrix();
  }
}

color[] randomPalette() {
  color[][] palettes = new color[][] {
    {color(#16C5F5), color(#F52F6E), color(#F5E52F)},
    {color(#18F569), color(#9A32F5), color(#F5B249)},
    {color(#B7F52F), color(#2F73F5), color(#F53816)},
  };

  int k = (int)random(palettes.length);
  return palettes[k];
}

Recorder.pde

class Recorder {
  
  int frameIdx = 0;
  boolean recording = false;
  
  void start() {
    frameIdx = 0;
    recording = true;
  }
  
  void stop() {
    recording = false;
  }
  
  void update() {
    if (recording) {
      saveFrame(String.format("frames/%05d.png", frameIdx));
      frameIdx = frameIdx + 1;
    }
  }
  
  boolean isRunning() {
    return recording;
  }
}

sketch_210604c.pde

import java.util.Arrays;
import java.util.Comparator;

NonCircularPacking ncp;
Recorder recorder;

void setup() {
  size(800, 800);
  
  ncp = new NonCircularPacking();
  
  PImage img = loadImage("cherry-blossom.png");
  
  for (int k = 0; k < 800; k++) {
    PVector pos = new PVector(random(0, width), random(0, height));
    float rot = random(0, TWO_PI);
    for (int l = 0; l < 10; l++) {
      float sc = 1.0 * (1.0 - (float)l / 10);
      ImageItem item = new ImageItem(img, pos, rot, sc);
      if (!ncp.isOverlapped(item)) {
        ncp.addItem(item);
      }
    }
  }
  
  /*
  for (int k = 0; k < 1000; k++) {
    PVector pos = new PVector(random(0, width), random(0, height));
    float rot = random(0, TWO_PI);
    color[] palette = randomPalette();
    float scDefault = 0.25;
    for (int l = 0; l < 10; l++) {
      float sc = scDefault * (1.0 - (float)l / 10);
      UFOItem item = new UFOItem(pos, rot, sc, palette);
      if (!ncp.isOverlapped(item)) {
        ncp.addItem(item);
        break;
      }
    }
  }
  */
  
  recorder = new Recorder();
  println("done");
}

void draw() {
  // background(220);
  background(81, 148, 68);
  ncp.draw(g, frameCount);
  recorder.update();
}

void mousePressed() {
  if (mouseButton == LEFT) {
    RectItem item = new RectItem(new PVector(mouseX, mouseY));
    boolean b = ncp.isOverlapped(item);
    println(b, item.pos);
    if (!b) {
      ncp.addItem(item);
    }
  } else if (mouseButton == RIGHT) {
    save("example.png");
  }
}

void keyPressed() {
  if (recorder.isRunning()) {
    recorder.stop();
    println("finished");
  } else {
    println("started");
    recorder.start();
  }
}