sketch_210605a

NonCircularPacking.pde


class NonCircularPacking {
  
  PGraphics pg;
  PGraphics tmpPg;
  
  ArrayList<AbstractItem> items;
  
  AbstractItem maskItem;
  int margin;
  
  NonCircularPacking() {
    this(null);
  }
  
  NonCircularPacking(AbstractItem maskItem) {
    this(maskItem, 0);
  }
  
  NonCircularPacking(AbstractItem maskItem, int margin) {
    this.margin = margin;
    this.maskItem = maskItem;
    
    pg = createGraphics(width, height);
    pg.beginDraw();
    pg.background(0, 0);
    if (maskItem != null) {
      maskItem.draw(pg, 0);
    }
    pg.endDraw();
    
    // マスクが設定されているときは透明度をひっくり返しておく
    if (maskItem != null) {
      pg.loadPixels();
      for (int k = 0; k < pg.pixels.length; k++) {
        color pixel = pg.pixels[k];
        pg.pixels[k] = color(red(pixel), green(pixel), blue(pixel), 255 - alpha(pixel));
      }
      pg.updatePixels();
    }
    
    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) {
    tmpPg.beginDraw();
    tmpPg.background(0, 0);
    item.draw(tmpPg, 0);
    for (int k = 0; k < margin; k++) {
      tmpPg.filter(DILATE);
    }
    tmpPg.endDraw();
    
    pg.beginDraw();
    pg.image(tmpPg, 0, 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;
  float w;
  color c;
  
  RectItem(PVector pos, float w, color c) {
    this.pos = pos;
    this.w = w;
    this.c = c;
  }
  
  void draw(PGraphics pg, int t) {
    pg.rectMode(CENTER);
    pg.fill(c);
    pg.noStroke();
    pg.rect(pos.x, pos.y, w, w);
  }
}

class CircleItem extends AbstractItem {
  
  PVector pos;
  float r;
  color c;
  
  CircleItem(PVector pos, float r, color c) {
    this.pos = pos;
    this.r = r;
    this.c = c;
  }
  
  void draw(PGraphics pg, int t) {
    pg.fill(c);
    pg.noStroke();
    pg.circle(pos.x, pos.y, r);
  }
}

class HeartItem extends AbstractItem {
  
  PVector pos;
  float r;
  float rot;
  color heartColor;
  
  HeartItem(PVector pos, float r) {
    this(pos, r, 0.0, color(255, 0, 0));
  }
  
  HeartItem(PVector pos, float r, float rot, color heartColor) {
    this.pos = pos;
    this.r = r;
    this.rot = rot;
    this.heartColor = heartColor;
  }
  
  void draw(PGraphics pg, int t) {
    pg.fill(0);
    pg.noStroke();
    
    PShape heart = createHeart();
    heart.beginShape();
    heart.noStroke();
    heart.fill(heartColor);
    heart.endShape();
    
    pg.pushMatrix();
    pg.translate(pos.x, pos.y);
    pg.scale(r / heart.width / 2.0 * 0.98, r / heart.height / 2.0 * 0.98);
    pg.rotate(rot);
    pg.shape(heart, 0, 0);
    pg.popMatrix();
  }
  
  PShape createHeart() {
    int N = 100;
    
    PShape s = createShape();
    s.beginShape();
    for (int k = 0; k < N; k++) {
        float t = TWO_PI * k / (float)N;
        float x = 16 * pow(sin(t), 3);
        float y = -(13 * cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t));
        s.vertex(x, y);
    }
    s.endShape(CLOSE);
    
    return s;
  }
}

class InvertedItem extends AbstractItem {
  
  AbstractItem item;
  PGraphics tmpPg;
  
  int margin;
  
  InvertedItem(AbstractItem item) {
    this(item, 0);
  }
  
  InvertedItem(AbstractItem item, int margin) {
    this.item = item;
    this.margin = margin;
    
    tmpPg = createGraphics(width, height);
    tmpPg.beginDraw();
    tmpPg.background(0, 0);
    item.draw(tmpPg, 0);
    for (int k = 0; k < margin; k++) {
      tmpPg.filter(DILATE);
    }
    tmpPg.endDraw();
    
    // ピクセルを反転
    tmpPg.loadPixels();
    for (int k = 0; k < tmpPg.pixels.length; k++) {
      color pixel = tmpPg.pixels[k];
      tmpPg.pixels[k] = color(red(pixel), green(pixel), blue(pixel), 255 - alpha(pixel));
    }
    tmpPg.updatePixels();
  }
  
  void draw(PGraphics pg, int t) {
    pg.image(tmpPg, 0, 0);
  }
}

sketch_210605a.pde

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

NonCircularPacking ncp1;
NonCircularPacking ncp2;

void setup() {
  size(800, 800);
  
  AbstractItem maskItem1 = new HeartItem(new PVector(width / 2.0, height / 2.0), 600);
  InvertedItem maskItem2 = new InvertedItem(maskItem1, 3);
  
  ncp1 = new NonCircularPacking(maskItem1, 3);
  ncp2 = new NonCircularPacking(maskItem2, 3);
  generateItems(ncp1, true);
  generateItems(ncp2, false);
  
  println("done");
}

void draw() {
  background(220);
  
  ncp1.draw(g, frameCount);
  ncp2.draw(g, frameCount);
}

void generateItems(NonCircularPacking ncp, boolean isRed) {
  for (int k = 0; k < 300; k++) {
    PVector pos = new PVector(random(0, width), random(0, height));
    
    float r = 255;
    float g = random(0, 128);
    float b = random(0, 128);
    color c = isRed ? color(r, g, b) : color(b, g, r);
    
    float rot = random(0, TWO_PI);
    for (int l = 0; l < 20; l++) {
      float w = 128 * (1.0 - (float)l / 20);
      HeartItem item = new HeartItem(pos, w, rot, c);
      if (!ncp.isOverlapped(item)) {
        ncp.addItem(item);
        break;
      }
    }
  }
  
  for (int k = 0; k < 3000; k++) {
    PVector pos = new PVector(random(0, width), random(0, height));
    
    float r = 255;
    float g = random(0, 128);
    float b = random(0, 128);
    color c = isRed ? color(r, g, b) : color(b, g, r);
    
    float rot = random(0, TWO_PI);
    for (int l = 0; l < 10; l++) {
      float w = 32 * (1.0 - (float)l / 10);
      HeartItem item = new HeartItem(pos, w, rot, c);
      if (!ncp.isOverlapped(item)) {
        ncp.addItem(item);
        break;
      }
    }
  }
}

void mousePressed() {
  if (mouseButton == RIGHT) {
    save("example.png");
  }
}