日常の進捗

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

Mod:Coding Challenge #34: Diffusion-Limited Aggregation

f:id:takawo:20171014195430p:plain

Diffusion-Limited Aggregation,日本語訳すると「拡散律速凝集(かくさんりっそくぎょうしゅう)」.なんのこっちゃという感じであるが,フラクタルやシミュレーション系のアルゴリズムとしてよく使われている.

空間に自由な粒子(パーティクル)が舞っていたりして,それが付着・堆積して形を作っていくような感じで,その粒子の運動をランダムウォークブラウン運動として記述して考える.付着や体積は距離の計算で接してる状態のものが残るとするとイメージしやすいかもしれない.

動画は45分で見てすんなり入ってこない感じではあったものの,他の参考リンクや関連情報を見ていくうちに動かせるようになった.

例によってp5.jsをProcessingに書き換えた.完了後画像保存するようにしている.

ArrayList<Mover> tree;
ArrayList<Mover> movers;

int maxMovers;
int iterations;

float radius;
float hue;
float shrink;
float bg_hue;

boolean nextFrame = false;

void setup() {
  fullScreen();
  colorMode(HSB, 360, 100, 100, 100);
  init();
}

void init() {

  maxMovers = (int)random(100, 300);
  iterations = (int)random(100, 300);
  float len = sqrt(sq(width/2) + sq(height/2));
  radius = random(len/40,len/30);
  hue = random(360);
  bg_hue = (hue+180)%360;
  shrink = 1 - random(0.0005, 0.001);
  nextFrame = false;

  tree = new ArrayList<Mover>();
  movers = new ArrayList<Mover>();

  tree.add(new Mover(width/2, height/2, radius, hue));
  radius *= shrink;

  for (int i = 0; i < maxMovers; i++) {
    movers.add(new Mover(radius));
    radius *= shrink;
  }
}


void draw() {
  background(bg_hue, 80, 20);
  for (Mover t : tree) {
    t.draw();
  }
  for (Mover w : movers) {
    w.draw();
  }
  for (int n = 0; n < iterations; n++) {
    for (int i = movers.size()-1; i >= 0; i--) {
      Mover m = movers.get(i);
      m.update();
      if (m.checkStuck(tree)) {
        m.setHue(hue%360);
        hue += 1;
        tree.add(m);
        movers.remove(i);
      }
    }
  }
  if (nextFrame) {
    saveFrame("#####.png");
    init();
  }
  if (movers.size() == 0) {
    nextFrame = true;
  }
}

void mousePressed() {
  init();
}

void keyPressed() {
  init();
}

class Mover {
  PVector pos;
  boolean stuck;
  float r;
  float hue;
  Mover(float _x, float _y, float _r, float _hue) {
    pos = new PVector(_x, _y);
    r = _r;
    stuck = true;
    hue = _hue;
  }

  Mover(float _r) {    
    pos = randomPosition();
    r = _r;
    stuck = false;
  }
  void draw() {
    noStroke();
    if (stuck) {
      fill(hue, 80, 100, 100);
    } else {
      fill(0, 0, 100, 30);
    }
    ellipse(pos.x, pos.y, r*2, r*2);
  }
  void update() {
    PVector vel = PVector.random2D();
    vel.x *= 3;
    vel.y *= 3;
    pos.add(vel);
    pos.x = constrain(pos.x, 0, width);
    pos.y = constrain(pos.y, 0, height);
  }
  boolean checkStuck(ArrayList<Mover> _other) {
    for (Mover m : _other) {
      float d = sq(dist(pos.x, pos.y, m.pos.x, m.pos.y));
      if (d < (r * r + m.r * m.r +  2 * m.r * r)) {
        stuck = true;
        return true;
      }
    }
    return false;
  }
  void setHue(float _hue) {
    hue = _hue;
  }

  PVector randomPosition() {
    PVector newPos = new PVector();
    int n = int(random(4));
    float x = 0;
    float y = 0;
    switch(n) {
    case 0:
      x = random(width);
      newPos.x = x;
      break;
    case 1:
      x = random(width);
      newPos.x = x;
      newPos.y = height;
      break;
    case 2:
      y = random(height);
      newPos.y = y;
      break;
    case 3:
      y = random(height);
      newPos.x = width;
      newPos.y = y;
      break;
    }
    return newPos;
  }
}

リファレンス