Worley ノイズで算出する距離を角度としたウォーカーの軌跡を描画

2021年9月12日日曜日

Processing アニメーション 作例

t f B! P L

👉 Read this article in English.

Worley ノイズで算出する距離を角度としたウォーカーの軌跡


'Processing' で書いたクリエイティブ・コーディングのアニメーションです。


Worley ノイズの変わった使い方ができないか?

Worley ノイズを使った描画は、距離を元に明度や色相を決定して描画するのが定石です。

Worley ノイズを使った一般的な描画例
Worley ノイズについて詳しくはこちらをどうぞ。

定石とは違う、ちょっと変わった使い方はないかな?と思い、今回は距離を角度に変換して移動するウォーカーを作り、その軌跡を描画してみました。

「こういう軌跡」という視点ではなく、「こういうウォーカー」を作ったらどういう軌跡になるかな?という視点で作成したものです。 その結果、割と面白い動きになったので、アニメーションにしてみました。

'Processing' のサンプルコード

GPL で公開します。GPL の条項に基づいて、どうぞご自由にお使いください。このコードを利用して何か作品を作ってもらえるととても嬉しいです。

本コードはスクリーン上には何も描画しません。描画の結果は画像ファイルとして保存されます。その画像ファイルを元にアニメーションの動画を作成できます。


/**
 * Walk On By.
 * I tried to use the distance value as the direction of the walker.
 *
 * Processing 3.5.3
 * @author @deconbatch
 * @version 0.1
 * created 0.1 2021.09.12
 */

void setup() {
  size(720, 720);
  colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  smooth();
  noLoop();
}

void draw() {
  int   frmMax   = 30 * 6;
  int   cycleMax = 3;
  float baseHue  = random(360.0);

  translate(width * 0.25, height * 0.25);
  for (int cycleCnt = 0; cycleCnt < cycleMax; cycleCnt++) {

    int   nodeMax = 30;
    float nodeGap = 0.05 * min(width, height);
    float zigzag  = random(12.0, 18.0); // walker path roughness
    int   plotMax = floor(20.0 * zigzag);
    baseHue += 90.0;

    // make randomly located nodes
    ArrayList<PVector> nodes = plotNodes(randomAdds(nodeMax, width * 0.5, height * 0.5), nodeGap);

    for (int frmCnt = 0; frmCnt < frmMax; frmCnt++) {
      float easeRatio = InFourthPow(map(frmCnt, 0, frmMax, 0.0, 1.0));
      background(baseHue % 360.0, 30.0, 80.0, 100.0);
      drawWalker(nodes, plotMax, zigzag, easeRatio);

      saveFrame("frames/" + String.format("%02d", cycleCnt) + String.format("%03d", frmCnt + 1) + ".png");
    }
  }
  
  exit();
}


/**
 * randomAdds : make random PVectors
 */
public ArrayList<PVector> randomAdds(float _rndsMax, float _width, float _height) {

  ArrayList<PVector> rnds = new ArrayList<PVector>();
  for (int rndsCnt = 0; rndsCnt < _rndsMax; rndsCnt++) {
    rnds.add(new PVector(random(_width), random(_height)));
  }
  return rnds;

}


/**
 * plotNodes : locate Nodes with some distance each other.
 */
public ArrayList<PVector> plotNodes(ArrayList<PVector> _adds, float _gap) {

  ArrayList<PVector> nodes = new ArrayList<PVector>();

  for (int i = 0; i < _adds.size(); i++) {

    float fX = _adds.get(i).x;
    float fY = _adds.get(i).y;

    // collision?
    boolean collision = false;
    for (PVector f : nodes) {
      if (dist(fX, fY, f.x, f.y) < _gap) {
        collision = true;
        break;
      }
    }
    // add new node
    if (!collision) {
      int pixIndex = floor(fY * width + fX);
      nodes.add(new PVector(fX, fY));
    }
  }

  return nodes;

}


/**
 * drawWalker : draw walker with Worley's noise method.
 */
public void drawWalker(ArrayList<PVector> _nodes, int _plotMax, float _zigzag, float _drawRatio) {

  float range   = max(width, height);
  float step    = floor(range * map(_drawRatio, 0.0, 1.0, 0.020, 0.015));
  int   plotMax = floor(map(_drawRatio * _drawRatio, 0.0, 1.0, 0.1, 1.0) * _plotMax);

  PVector initNode = _nodes.get(0);
  float x = initNode.x;
  float y = initNode.y;
  
  ArrayList<PVector> walker = new ArrayList<PVector>();
  walker.add(new PVector(x, y));

  // walk along the nearest node
  int   minIndx = 0;
  for (int plotCnt = 0; plotCnt < plotMax; plotCnt++) {
    float minDist = range;
    for (int i = 0; i < _nodes.size(); i++) {
      float d = dist(x, y, _nodes.get(i).x, _nodes.get(i).y);
      if (minDist > d) {
        minDist = d;
        minIndx = i;
      }
    }
    float r = _zigzag * PI * sin(PI * minDist / range);
    x += step * cos(r);
    y += step * sin(r);
    walker.add(new PVector(x, y));
  }

  // draw walker path
  noFill();
  stroke(0.0, 0.0, 0.0, 100.0);
  strokeWeight(1.0);
  beginShape();
  for (PVector w : walker) {
    ellipse(w.x, w.y, 4.0, 4.0);
    vertex(w.x, w.y);
  }
  endShape();

  // draw nodes
  noFill();
  stroke(0.0, 0.0, 0.0, 100.0);
  strokeWeight(1.0);
  for (PVector n : _nodes) {
    ellipse(n.x, n.y, 14.0, 14.0);
  }
  ellipse(_nodes.get(minIndx).x, _nodes.get(minIndx).y, 4.0, 4.0);

}


/**
 * InFourthPow : easing function.
 */
private float InFourthPow(float _t) {
  return 1.0 - pow(1.0 - _t, 4);
}


/*
Copyright (C) 2021- deconbatch

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

QooQ