雪の結晶を作成する(ProcessingAdventCalendar2016#3)

ProcessingAdventCalendar

今年もProcessingAdventCalendar2016が開催されております。(ちなみに今年は管理の関係上Qiitaに移行しました)
今年は常連様の参加が少なかったので、AdventCalendar存亡の危機にさらされていたのですが、そんな中@n_ryotaさんが2日間素敵なツリー作品を投稿してくださいました。ありがとうございました。


私も何か投稿せねばと思い、急遽3日目を担当することにしました。

今回の成果物

2日間ツリーでしたので、何かツリー縛りで考えたのですが、何も思いつかなかったので、困ったときのGenerative頼みで、雪の結晶を作成することにしました。Generative最高ヽ(・-・)ノ

こんなものができます。

snow

概要

点を描画するパーティクルクラスを作成し、進行方向や速度などを管理することにします。そのパーティクルをArrayListに格納し、ひたすら点を打ち込んでいくだけの簡単なお仕事です。

雪の結晶は6方向に進んでいくので、1パーティクルに対し、60度ずつ回転させながら6回描画すると結晶っぽくなります。

途中で確率で枝分かれを行い、ArrayListに子パーティクルを追加していけば、あとはGenerativeでいい味を出してくれます。永遠とさまざまな形の雪の結晶を作成しますので、ずっと眺めていることができますね。

動くものは下記OpenProcessingで公開しております。

https://www.openprocessing.org/sketch/392536

ソースコード

//  パーティクルクラス
class P {
  float ang;    //  進行角度
  PVector pos;  //  位置
  float v;      //  速度
  int depth;    //  深さ
  float size;   // 大きさ

  P() {
    pos = new PVector();
  }

  //  初期化
  void init(float _ang, float x, float y, float _v, int _depth, float _size) {
    ang = _ang;
    pos.set(x, y);
    v = _v;
    depth = _depth;
    size = _size;
  }

  //  子として生まれる
  void born(P p, float _ang) {
    ang = p.ang + _ang;
    pos.set(p.pos.x, p.pos.y);
    depth = p.depth - 1;
    v = p.v * (0.4 * depth) * (random(0.2) + 0.8);
    size = p.size;
  }

  //  描画動作
  void act() {

    //  パラメータ変更
    pos.add(cos(ang) * v, sin(ang) * v);
    v -= 0.02;

    //  描画
    translate(width / 2, height / 2);
    for (int i = 0; i < 6; i++) {
      ellipse(pos.x, pos.y, size, size);
      rotate(radians(60));
    }
    translate(- width / 2, -height / 2);
  }
}

ArrayList<P> perticles = new ArrayList<P>();
float generateRatio; //  子供性確率変数
float waitCount;  //  描画終了後の待機時間

//-------------------------------------------------
//  setup
//--------------------------------------------------
void setup() {
  background(0);
  size(700, 700);
  init();
}

//--------------------------------------------------
//  初期化処理
//--------------------------------------------------
void init() {
  fill(255, 255);
  noStroke();  

  perticles.clear();

  P p = new P();
  p.init(random(60), 0, 0, random(2) + 2, 3, random(3) + 1);
  perticles.add(p);

  generateRatio = random(0.03) + 0.02;
}

//--------------------------------------------------
//  draw
//--------------------------------------------------
void draw() {

  if (perticles.size() == 0) {
    waiting();
  } else {
    drawing();
  }
}

//--------------------------------------------------
//  描画完了後の余韻
//--------------------------------------------------
void waiting() {
  fill(0, 8);
  rect(0, 0, width, height);
  waitCount++;
  if (waitCount >= 50) {
    waitCount = 0;
    init();
  }
}

//--------------------------------------------------
//  描画メイン処理
//--------------------------------------------------
void drawing() {

  ArrayList<P> childs = new ArrayList<P>();  //  今回生まれた子
  ArrayList<P> deads = new ArrayList<P>();   //  今回死ぬパーティクル

  for (P p : perticles) {
    p.act();
    if (p.v <= 0) {
      deads.add(p);
    }

    if (random(1) < (generateRatio * p.depth)) {
      float r = random(30) + 20;
      P child1 = new P();
      child1.born(p, radians(r));
      childs.add(child1);
      P child2 = new P();
      child2.born(p, radians(-r));
      childs.add(child2);
    }
  }

  //  リストから生まれた子を追加
  for (P child : childs) {
    perticles.add(child);
  }

  //  リストから死んだ子を削除
  for (P dead : deads) {
    perticles.remove(dead);
  }
}

void mouseClicked() {
  init();
}

発展

パラメータを調整すれば、自然界に存在しない10角形の結晶や30角形の結晶も作り出すことができます。
雪の結晶というよりもコースターに近いかな。

snow30 snow10

感想

Generative画像はいつまでも観ていられるので本当に楽しいものです。
来年も素敵なProcessingライフをお送りください。

あと、ProcessingAdventCalendar2016にはまだまだ開き枠がありますので、皆様ぜひぜひご参加ください。
http://qiita.com/advent-calendar/2016/processing

  • URLをコピーしました!

コメント

コメントする

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

目次