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");
}
}