Tree.pde
// https://stackoverflow.com/questions/3522454/how-to-implement-a-tree-data-structure-in-java
// https://github.com/gt4dev/yet-another-tree-structure
class TreeNode<T extends AbstractShape> {
final int MAX_N = 8;
int N = 1;
T data;
TreeNode<T> parent;
ArrayList<TreeNode<T>> children;
TreeNode(T data) {
this.data = data;
this.children = new ArrayList<TreeNode<T>>();
}
private int findMinimumSquare(int n) {
for (int k = 1; k <= MAX_N; k++) {
if (n <= k * k) {
return k;
}
}
return MAX_N;
}
TreeNode<T> addChild(T child) {
TreeNode<T> childNode = new TreeNode<T>(child);
childNode.parent = this;
this.children.add(childNode);
this.N = findMinimumSquare(this.children.size());
return childNode;
}
boolean isRoot() {
return parent == null;
}
boolean isLeaf() {
return children.size() == 0;
}
int getLevel() {
if (this.isRoot()) {
return 0;
} else {
return parent.getLevel() + 1;
}
}
void draw() {
if (isLeaf()) {
data.draw();
} else {
for (int k = 0; k < children.size(); k++) {
int i = k % N;
int j = k / N;
pushMatrix();
translate(width / (float)N * j , height / (float)N * i);
scale(1.0 / N);
children.get(k).draw();
popMatrix();
}
}
}
}
TreeShape.pde
abstract class AbstractShape {
abstract void draw();
}
class Circle extends AbstractShape {
void draw() {
rect(0, 0, width, height);
translate(width / 2.0, height / 2.0);
ellipse(0, 0, width, height);
}
}
class DicePalette {
private color backgroundColor;
private color pipColor;
private color onePipColor;
DicePalette(color backgroundColor, color pipColor, color onePipColor) {
this.backgroundColor = backgroundColor;
this.pipColor = pipColor;
this.onePipColor = onePipColor;
}
color getBackgroundColor() { return backgroundColor; }
color getPipColor() {return pipColor; }
color getOnePipColor() { return onePipColor; }
}
DicePalette getDefaultPalette() {
return new DicePalette(color(255), color(0), color(255, 0, 0));
}
void drawHeart() {
int N_POINTS = 200;
beginShape();
for (int k = 0; k < N_POINTS; k++) {
float t = TWO_PI * k / (float)N_POINTS;
float x = 16 * pow(sin(t), 3);
float y = -(13 * cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t));
vertex(x, y);
}
endShape(CLOSE);
}
class Dice extends AbstractShape {
int N_POINTS = 100;
float ROUND = 100.0;
DicePalette palette;
int number;
int interval = 30;
boolean heartFlag = false;
Dice() {
this((int)(random(6) + 1), getDefaultPalette());
}
Dice(int number) {
this(number, getDefaultPalette());
}
Dice(DicePalette palette) {
this((int)(random(6) + 1), palette);
}
Dice(int number, DicePalette palette) {
this.number = number;
this.palette = palette;
if (random(20) < 2) {
heartFlag = true;
}
}
void draw() {
translate(width / 2.0, height / 2.0);
scale(0.975);
if (frameCount % interval == 0) {
number = number + 1;
number = number == 7 ? 1 : number;
}
int nextNumber = number + 1;
nextNumber = nextNumber == 7 ? 1 : nextNumber;
float t = (frameCount % interval) / (float)interval;
drawBody();
drawPips(linearstep(0.2, 0.6, 1.0 - t), number);
drawPips(linearstep(0.2, 0.6, pow(t, 4)), nextNumber);
}
private float linearstep(float edge0, float edge1, float t) {
return min(max((t - edge0) / (edge1 - edge0), 0.0), 1.0);
}
void drawBody() {
noStroke();
fill(palette.getBackgroundColor());
beginShape();
for (int k = 0; k < N_POINTS; k++) {
float t = TWO_PI * k / (float)N_POINTS;
float x = ROUND * cos(t);
float y = ROUND * sin(t);
if (t < PI / 2.0) {
x = x + (width / 2.0 - ROUND);
y = y + (height / 2.0 - ROUND);
} else if (t < PI) {
x = x - (width / 2.0 - ROUND);
y = y + (height / 2.0 - ROUND);
} else if (t < PI * 3 / 2) {
x = x - (width / 2.0 - ROUND);
y = y - (height / 2.0 - ROUND);
} else {
x = x + (width / 2.0 - ROUND);
y = y - (height / 2.0 - ROUND);
}
vertex(x, y);
}
endShape(CLOSE);
}
void drawPips(float pipRadScale, int number) {
float onePipRad = width / 4.0 * pipRadScale;
float pipRad = width / 5.0 * pipRadScale;
if (number == 1) {
fill(palette.getOnePipColor());
noStroke();
if (heartFlag) {
pushMatrix();
scale(onePipRad / 17.0);
drawHeart();
popMatrix();
} else {
circle(0, 0, onePipRad);
}
} else if (number == 2) {
fill(palette.getPipColor());
circle(width / 3.0, height / 3.0, pipRad);
circle(-width / 3.0, -height / 3.0, pipRad);
} else if (number == 3) {
fill(palette.getPipColor());
circle(width / 3.0, height / 3.0, pipRad);
circle(0.0, 0.0, pipRad);
circle(-width / 3.0, -height / 3.0, pipRad);
} else if (number == 4) {
fill(palette.getPipColor());
circle(width / 3.0, height / 3.0, pipRad);
circle(-width / 3.0, height / 3.0, pipRad);
circle(width / 3.0, -height / 3.0, pipRad);
circle(-width / 3.0, -height / 3.0, pipRad);
} else if (number == 5) {
fill(palette.getPipColor());
circle(0.0, 0.0, pipRad);
circle(width / 3.0, height / 3.0, pipRad);
circle(-width / 3.0, height / 3.0, pipRad);
circle(width / 3.0, -height / 3.0, pipRad);
circle(-width / 3.0, -height / 3.0, pipRad);
} else if (number == 6) {
fill(palette.getPipColor());
circle(width / 3.0, 0.0, pipRad);
circle(-width / 3.0, 0.0, pipRad);
circle(width / 3.0, height / 3.0, pipRad);
circle(-width / 3.0, height / 3.0, pipRad);
circle(width / 3.0, -height / 3.0, pipRad);
circle(-width / 3.0, -height / 3.0, pipRad);
}
}
}
sketch_210206b.pde
TreeNode tree;
boolean recording = false;
void setup() {
size(800, 800);
frameRate(30);
tree = new TreeNode(new Dice());
buildTree(tree, 0);
}
void draw() {
background(0);
tree.draw();
if (recording) {
saveFrame("frames/#####.png");
}
}
Dice generateRandomDice() {
color[][] dicePalettes = new color[][] {
{color(255), color(0), color(255, 0, 0)},
{color(0x19, 0x00, 0xB3), color(0xEA), color(0xEA)},
{color(0xAD, 0x15, 0x00), color(0xEA), color(0xEA)},
{color(0xFF, 0x9D, 0x05), color(0xFA), color(0xFA)},
};
int k = (int)random(dicePalettes.length);
DicePalette palette = new DicePalette(dicePalettes[k][0], dicePalettes[k][1], dicePalettes[k][2]);
return new Dice(palette);
}
void buildTree(TreeNode t, int depth) {
if (depth != 0 && (random(10) < 3 || t.getLevel() >= 3)) {
t.addChild(generateRandomDice());
return;
}
int[] counts = {4, 9};
int n = counts[(int)random(counts.length)];
for (int k = 0; k < n; k++) {
TreeNode child = t.addChild(generateRandomDice());
buildTree(child, depth + 1);
}
}
void mousePressed() {
tree = new TreeNode(new Dice());
buildTree(tree, 0);
redraw();
}
static final String timestamp(final String name, final String ext) {
return name + "-" + year() + nf(month(), 2) + nf(day(), 2) +
"-" + nf(hour(), 2) + nf(minute(), 2) + nf(second(), 2) + ext;
}
void keyReleased() {
// saveFrame(String.format("frames/%s", timestamp("Project", ".png")));
recording = !recording;
}