日常の進捗

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

フロッキング3D

f:id:takawo:20170811114810g:plain

今日は前に描いた2Dのフロッキングのコードを3Dにしようと思った。が、途中で3Dの角度計算が必要になるのに気づいて爆死した。それっぽく動いてるけど、Z軸方向でのフロッキングは機能してないと思う。今度調べる。

Processingの機能のうちで限定的な部分をWebで実装しているProcessing.jsだと動かないコードになってきた。予めProcessing.jsとかP5.jsで書くようにすれば良いのかもしれない。今だったらp5.jsをやると思う。

Flock flock;
int num =500;
// setup関数 : 初回1度だけ実行される
void setup() {
  fullScreen(P3D); // ウィンドウサイズを960px,540pxに
  colorMode(HSB, 360, 100, 100, 100); // HSBでの色指定にする
  flock = new Flock();
  for (int i = 0; i < num; i++) {
    float x = random(-width/2, width/2);
    float y = random(-height/2, height/2);
    float z = random(-500/2, 500/2);
    flock.addBoid(new Boid(x, y, z));
  }
  background(0, 0, 0);
}

// draw関数 : setup関数実行後繰り返し実行される
void draw() {
  hint(DISABLE_DEPTH_TEST);
  fill(220, 20, 10, 15);
  rect(0, 0, width, height);
  translate(width/2, height/2, -500);
  rotateY(frameCount*0.001);
  rotateZ(PI/6);
  flock.run();
}

void mousePressed() {
  for (int i = 0; i < 5; i = i + 1) {
    flock.addBoid(new Boid(mouseX-width/2, mouseY-height/2, random(-250, 250)));
  }
}

void keyPressed() {
  flock.randomize();
  flock.addBoid(new Boid(mouseX-width/2, mouseY-height/2, random(-250, 250)));
}

class Flock {
  ArrayList<Boid> boids = new ArrayList<Boid>();

  void addBoid(Boid b) {
    boids.add(b);
  }
  void run() {
    for (Boid b : boids) {
      b.run(boids);
    }
  }

  void randomize() {
    for (Boid b : boids) {
      b.randomize();
    }
  }
}

class Boid {
  ArrayList<Integer>colors = new ArrayList<Integer>();
  color c;
  PVector pos;
  PVector vel;
  PVector acc;
  float r;
  float maxForce;
  float maxSpeed;
  Boid(float x, float y, float z) {
    pos = new PVector(x, y, z);
    //vel = PVector.random2D();
    float angleA = random(360);
    float angleB = random(360);
    float thetaA = radians(angleA);
    float thetaB = radians(angleB);
    float vx = cos(thetaA)*cos(thetaB);
    float vy = sin(thetaA)*cos(thetaB);
    float vz = sin(thetaB);
    vel = new PVector(vx, vy, vz);
    acc = new PVector();
    int n = (int)random(0, 3);
    r = map(n, 0, 3, 0.5, 3);
    maxForce = map(n, 0, 3, 0.03, 0.01);
    maxSpeed = map(n, 0, 3, 2, 0.5);
    colors.add(color(12, 78, 100));
    colors.add(color(60, 38, 100));
    colors.add(color(90, 32, 92));
    colors.add(color(139, 36, 74));
    colors.add(color(170, 100, 64));
    int m = (int) random(0, 5);
    c = colors.get(m);
  }

  void run(ArrayList<Boid> boids) {
    flock(boids);
    update();
    borders();
    render();
  }  

  void flock(ArrayList<Boid> boids) {
    PVector sep = separate(boids);
    PVector ali = align(boids);
    PVector coh = cohesion(boids);
    sep.mult(1.5);
    ali.mult(1.0);
    coh.mult(1.0);
    applyForce(sep);
    applyForce(ali);
    applyForce(coh);
  }
  void applyForce(PVector f) {
    acc.add(f);
  }
  void update() {
    vel.add(acc);
    vel.limit(maxSpeed);
    pos.add(vel);
    acc.mult(0);
  }

  void borders() {
    if (pos.x < -width/2-r) pos.x = width + r;
    if (pos.x > width/2 + r) pos.x = -r;
    if (pos.y < -height/2-r) pos.y = height + r;
    if (pos.y > height/2 + r) pos.y = -r;
    if (pos.z < -250 - r) pos.z = width + r;
    if (pos.z > 250 + r) pos.z = -r;
  }

  void render() {
    float angle = vel.heading2D();
    float theta = angle + radians(90);
    //float hue = abs(degrees(angle)%360);
    fill(c);
    noStroke();
    pushMatrix();
    translate(pos.x, pos.y, pos.z);
    rotate(theta);
    beginShape(TRIANGLES);
    vertex(0, -r*2);
    vertex(-r, r*2);
    vertex(r, r*2);
    endShape();
    popMatrix();
  }
  PVector separate(ArrayList<Boid> boids) {
    float desiredSeparation = 25f;
    PVector steer = new PVector(0, 0);
    float count = 0;
    for (Boid other : boids) {
      float d = PVector.dist(pos, other.pos);
      if ((d > 0) && (d < desiredSeparation)) {
        PVector diff = PVector.sub(pos, other.pos);
        diff.normalize();
        diff.div(d);
        steer.add(diff);
        count++;
      }
    }
    if (count > 0) {
      steer.div(count);
    }
    if (steer.mag() > 0) {
      //steer.setMag(maxSpeed);
      steer.normalize();
      steer.mult(maxSpeed);
      steer.sub(vel);
      steer.limit(maxForce);
    }
    return steer;
  }
  PVector align(ArrayList<Boid> boids) {
    float neighbordist = 50;
    PVector sum = new PVector(0, 0);
    float count = 0;
    for (Boid other : boids) {
      float d = PVector.dist(pos, other.pos);
      if ((d > 0) && (d < neighbordist)) {
        sum.add(other.vel);
        count++;
      }
    }
    if (count > 0) {
      sum.div(count);
      sum.normalize();
      sum.mult(maxSpeed);
      PVector steer = PVector.sub(sum, vel);
      steer.limit(maxForce);
      return steer;
    } else {
      return new PVector();
    }
  }
  PVector cohesion(ArrayList<Boid> boids) {
    float neighbordist = 50;
    PVector sum = new PVector(0, 0);
    float count = 0;
    for (Boid other : boids) {
      float d = PVector.dist(pos, other.pos);
      if ((d > 0) && (d < neighbordist)) {
        sum.add(other.pos);
        count++;
      }
    }
    if (count > 0) {
      sum.div(count);
      return seek(sum);
    } else {
      return new PVector(0, 0);
    }
  }
  PVector seek(PVector target) {
    PVector desired = PVector.sub(target, pos);
    desired.normalize();
    desired.mult(maxSpeed);
    PVector steer = PVector.sub(desired, vel);
    steer.limit(maxForce);
    return steer;
  }
  void randomize() {
    float angle = random(360);
    float theta = radians(angle);
    vel = new PVector(cos(theta), sin(theta));
  }
}