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