sketch_210221a

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


// http://blawat2015.no-ip.com/~mieki256/diary/201603202.html
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;
  }
};

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 UFO extends AbstractShape {
  
  color[] palette;
  float angularVelocity = TWO_PI;

  UFO(color[] palette) {
    this(palette, TWO_PI*random(1.0, 2.0)*(random(1) > 0.5 ? 1.0 : -1.0));
  }
  
  UFO(color[] palette, float angularVelocity) {
    this.palette = palette;
    this.angularVelocity = angularVelocity;
  }
  
  void draw() {
    float s = 0.6;
    int nCircles = 6;
    
    float w = width;
    float h = height;
    
    // noStroke();
    rectMode(CENTER);
    
    translate(width / 2.0, height / 2.0);
    strokeWeight(0.8);
    
    fill(palette[0]);
    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 t = frameCount / 60.0 / TWO_PI;
      float cx = (w / 2.0 - circleWidth / 2.0) * cos(t * angularVelocity + phi);
      float cz = sin(t * angularVelocity + phi);
      points[k] = new PVector(cx, 0, cz);
    }
    
    fill(palette[2]);
    Arrays.sort(points, comp);
    for (int k = 0; k < nCircles; k++) {
      float cx = points[k].x;
      arc(cx, h / 4.0, circleWidth, circleWidth, 0, PI);
    }
    
    fill(palette[1]);
    beginShape();
    vertex(w/2.0*s, 0);
    vertex(w/2.0, h/4.0);
    vertex(-w/2.0, h/4.0);
    vertex(-w/2.0*s, 0);
    endShape(CLOSE);
  }
}

sketch_210221a.pde

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

TreeNode tree;
boolean recording = false;

void setup() {
  size(800, 800);
  
  frameRate(30);
  
  tree = new TreeNode(new UFO(randomPalette()));
  buildTree(tree, 0);
}

void draw() {
  background(10, 10, 64);
  tree.draw();
  
  if (recording) {
    saveFrame("frames/#####.png");
  }
}

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];
}

void buildTree(TreeNode t, int depth) {
  
  if (depth != 0 && (random(10) < 3 || t.getLevel() >= 3)) {
    t.addChild(new UFO(randomPalette()));
    return;
  }
  
  int[] counts = {4, 9};
  int n = counts[(int)random(counts.length)];
  for (int k = 0; k < n; k++) {
    TreeNode child = t.addChild(new UFO(randomPalette()));
    buildTree(child, depth + 1);
  }
}

void mousePressed() {
  tree = new TreeNode(new UFO(randomPalette()));
  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;
}