日常の進捗

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

Mod:Coding Challenge #24: Perlin Noise Flow Field

f:id:takawo:20170924152756g:plain

p5.jsで書かれたものをProcessingに移植するときに、ArrayIndexOutOfBoundsExceptionが出てしまって解決する方法を考えて、随分書き換えた。グリッドに力場を作って、動くポイントに近い力場の影響を受ける。力場は2次元のパーリンノイズで動きの方向が変わっていく。パーティクルは一定時間でリセットされながら動く。

コード

float inc = 0.01;
float scale = 10;
int rows, cols;
float zoff = 0;
Particle[] particles;
Field[] flowfield;
int num = 500;
float gridX, gridY;

void setup() {
  size(960, 540,FX2D);
  colorMode(HSB, 360, 100, 100, 100);
  cols = floor(width / scale);
  rows = floor(height / scale);
  gridX = width/cols;
  gridY = height/rows;
  println(cols, rows);
  flowfield = new Field[cols * rows];
  particles = new Particle[num];
  for (int i =0; i < num; i++) {
    particles[i] = new Particle();
  }
  background(0, 0, 100);
}

void draw() {
  fill(0, 0, 100, 10);
  noStroke();
  rect(0, 0, width, height);
  float yoff = 0;
  for (int y = 0; y < rows; y++) {
    float xoff = 0;
    for (int x = 0; x < cols; x++) {
      int index = x + y * cols;
      float theta = noise(xoff, yoff, zoff) * TWO_PI * 4;
      PVector v = PVector.fromAngle(theta);
      float px = x * scale;
      float py = y * scale;
      v.setMag(1);
      flowfield[index] = new Field(new PVector(px, py), v);
      xoff += inc;
    }
    yoff += inc;
    zoff += inc / 300f;
  }

  for (int i = 0; i < particles.length; i++) {
    particles[i].follow(flowfield);
    particles[i].update();
    particles[i].edges();
    particles[i].draw();
  }
}

class Field {
  PVector pos;
  PVector v;
  Field(PVector _pos, PVector _v) {
    pos = _pos;
    v = _v;
  }
}
class Particle {
  float life;
  float lifeSpan;
  PVector pos;
  PVector vel;
  PVector acc;
  float maxSpeed;
  float brightness;
  PVector prevPos;
  Particle() {
    init();
  }

  void init() {
    pos = new PVector(random(width), random(height));
    vel = new PVector(0, 0);
    acc = new PVector(0, 0);
    maxSpeed = 2;
    brightness = random(100);
    prevPos = pos.copy();
    life = random(1.5, 3);
    lifeSpan = random(life/300, life/100);
  }

  void follow(Field[] flowfield) {
    for (Field p : flowfield) {
      if (pos.dist(p.pos) < sqrt(sq(gridX/2)+sq(gridY/2))) {
        applyForce(p.v);
      }
    }
  }

  void applyForce(PVector force) {
    acc.add(force);
  }

  void update() {
    vel.add(acc);
    vel.limit(maxSpeed);
    pos.add(vel);
    acc.mult(0);

    life -= lifeSpan;
    if (life < 0) {
      init();
    }
  }

  void edges() {
    if (pos.x > width) {
      pos.x = 0;
      updatePrev();
    }
    if (pos.x < 0) {
      pos.x = width;
      updatePrev();
    }
    if (pos.y > height) {
      pos.y = 0;
      updatePrev();
    }
    if (pos.y < 0) {
      pos.y = height;
      updatePrev();
    }
  }

  void updatePrev() {
    prevPos = pos.copy();
  }

  void draw() {
    stroke(0, 0, brightness);
    brightness = (brightness + 0.1) % 100;
    line(pos.x, pos.y, prevPos.x, prevPos.y);
    updatePrev();
  }
}

リファレンス