日常の進捗

主に自分のための,行為とその習慣化の記録

Mod:Coding Challenge #18: 3D Fractal Trees

f:id:takawo:20170917230815g:plain

フラクタルツリーの3D版。長かったけど、2Dのアルゴリズムが作れるとあとは次元を1つ増やして3Dにするだけとも言えるのでそれほど難度を感じなかった。ツリーっぽい形にしたかったのでLeafの生成のところの初期値を山形になるように変更した。

今頃台風来てるので驚く。気圧が低くなると頭が痛くなる気がする。コーヒーで血管広げてる。

import peasy.*;

PeasyCam cam;

Tree tree;

float min_dist = 5;
float max_dist = 500;

void setup() {
  size(600, 900, P3D);
  cam = new PeasyCam(this, 500);
  colorMode(HSB, 360, 100, 100, 100);
  tree = new Tree();
}

void draw() {
  background(0, 0, 33);
  tree.draw();
  tree.grow();
}

class Tree {
  ArrayList<Branch> branches = new ArrayList<Branch>();
  ArrayList<Leaf> leaves = new ArrayList<Leaf>(); 

  Tree() {
    for (int i = 0; i < 1000; i++) {
      leaves.add(new Leaf());
    }
    Branch root = new Branch(new PVector(0, height/2, 0), new PVector(0, -1));
    branches.add(root);
    Branch current = new Branch(root);

    while (!closeEnough(current)) {
      Branch trunk = new Branch(current);
      branches.add(trunk);
      current = trunk;
    }
  }
  boolean closeEnough(Branch b) {
    for (Leaf l : leaves) {
      float d = PVector.dist(b.pos, l.pos);
      if (d < max_dist) {
        return true;
      }
    }
    return false;
  }

  void grow() {
    for (Leaf l : leaves) {
      Branch closest = null;
      PVector closestDir = null;
      float record = -1;

      for (Branch b : branches) {
        PVector dir = PVector.sub(l.pos, b.pos);
        float d = dir.mag();
        if (d < min_dist) {
          l.reached();
          closest = null;
          break;
        } else if (d > max_dist) {
        } else if (closest == null || d < record) {
          closest = b;
          closestDir = dir;
          record = d;
        }
      }
      if (closest != null) {
        closestDir.normalize();
        closest.dir.add(closestDir);
        closest.count++;
      }
    }
    for (int i = leaves.size()-1; i >= 0; i--) {
      if (leaves.get(i).reached) {
        leaves.remove(i);
      }
    }

    for (int i = branches.size()-1; i >= 0; i--) {
      Branch b = branches.get(i);
      if (b.count > 0) {
        b.dir.div(b.count);
        b.dir.normalize();
        PVector rand = PVector.random3D();
        rand.setMag(0.3);
        b.dir.add(rand);
        Branch newB = new Branch(b);
        branches.add(newB);
        b.reset();
      }
    }
  }

  void draw() {
    for (Leaf l : leaves) {
      l.draw();
    }
    for (int i = branches.size()-1; i >= 0; i--) {
      Branch b = branches.get(i);
      if (b.parent != null) {
        stroke(0, 0, 100);
        strokeJoin(PROJECT);
        float sw = map(i, 0, branches.size(), 5, 1);
        strokeWeight(sw);
        line(b.pos.x, b.pos.y, b.pos.z, b.parent.pos.x, b.parent.pos.y, b.parent.pos.z);
      }
    }
  }
}

class Branch {
  Branch parent;
  PVector pos;
  PVector dir;
  int count = 0;
  PVector saveDir;
  float len = 5;

  Branch(PVector _pos, PVector _dir) {
    parent = null;
    pos = _pos;
    dir = _dir;
    saveDir = dir;
  }
  Branch(Branch _parent) {
    parent = _parent;
    pos = parent.next();
    dir = parent.dir;
    saveDir = dir;
  }

  void reset() {
    count = 0;
    dir = new PVector(saveDir.x, saveDir.y);
  }
  PVector next() {
    PVector v = PVector.mult(dir, len);
    PVector next = PVector.add(pos, v);
    return next;
  }
}

class Leaf {
  PVector pos;
  boolean reached = false;
  float d = 4;

  Leaf() {
    float y = (random(1) + random(1) + random(1)+ random(1)+random(1)) / 5;
    float x = random(map(y, 0, 1, 0, 0.35));
    float z = random(map(y, 0, 1, 0, 0.35));
    if (random(1) < 0.5) {
      x *= -1;
    }
    if (random(1) < 0.5) {
      z *= -1;
    }
    pos = new PVector(x, y, z);
    pos.mult(random(width/2));
    pos.y -= height/4;
  }

  void reached() {
    reached = true;
  }

  void draw() {
    fill(0, 0, 100);
    noStroke();
    pushMatrix();
    translate(pos.x, pos.y, pos.z);
    ellipse(0, 0, d, d);
    popMatrix();
  }
}

リファレンス