Week 9 _ Glass

Concept: 

For this project, I decided to make a visualization inspired by tension and release with an interactive element as the mouse hovers around the canvas. Initially, the flock is strictly constructed around an attractor in a circular shape, but over time, it changes and moves around the canvas. The mouse interaction enables the Boids to transform shape and size, adding layers to the sketch. The shape of the Boide is inspired by kite-like crystal shapes in the shade of light transparent blue symbolizing shattered glass. Resembling the sketch this way makes sense to me because it shows fragility and the Boids collective movement while remaining individual. This mirrors the sensitivity of how one Boids movement influences others.

Highlight of Code:

For this project, I reviewed the presentation and watched the videos to understand the concept of flocking. I wanted to bring into the sketch things we learned prior to the class, and I decided that having an attractor would be interesting. I initially started playing around with the code we did in class. I played a lot with different classes and ways to make it work. I added three main classes: an attractor, a Boid, and a flock. Even though I had so much debugging to do, I think I am close enough to the visualization I had in my head.

The attractor class pushed the Boids towards it through a force similar to orbiting. It was hard to figure out how to make it work, but as I played with the numbers, it started making sense. The orbiting force is simply rotating the force by 2. The Boid class is affected by flocking and attraction. This helps the body move in groups and respond to the attraction force. The methods used for the Boid class are froce, flock, attraction, update, borders, and the show functions. 

for (let i = 0; i < 400; i++) {
   //changing this part effects how the overall visulaization is 
   let angle = map(i, 0, 400, 0, TWO_PI);
   let boid = new Boid(
     width/1.7  + radious * cos(angle),
     height/1.7 + radious * sin(angle)
   );
   flock.addBoid(boid);
 }

The above code is the main effector of the visualization. It took me some time to create a visualization I though was interesting, and it was inspired by the midterm for this semester.

class Boid {
  constructor(x, y) {
    this.acceleration = createVector(0, 0);
    this.velocity = createVector(random(-0.01, 0.001), random(-0.01, 0.01));
    this.position = createVector(x, y);
    this.r = 3;
    //mass
    this.mass = 2;
    // Maximum speed
    this.maxSpeed = 0.7;
    // Maximum steering force
    this.maxForce = 5;
  }
  applyForce(force) {
    let f = p5.Vector.div(force, this.mass);
    //changed how it moves looks nice
    this.acceleration.sub(f);
    this.acceleration.mult(10);
    this.acceleration.mult(f);

  }

  run(boids, attractor) {
    this.flock(boids);
    this.applyAttraction(attractor);
    this.update();
    this.borders();
    this.show();
  }

  applyAttraction(attractor) {
    //pull boids
    attractor.attract(this);
  }

  // accumulate  acceleration each time based on three rules
  flock(boids) {
    let sep = this.separate(boids); // Separation
    let ali = this.align(boids); // Alignment
    let coh = this.cohere(boids); // Cohesion
    // Arbitrarily weight these forces
    sep.mult(1);
    ali.mult(1);
    coh.mult(1);
    // Add the force vectors to acceleration
    this.applyForce(sep);
    this.applyForce(ali);
    this.applyForce(coh);
  }

  // update location
  update() {
    //  velocity
    this.velocity.add(this.acceleration);
    // limit speed
    this.velocity.limit(this.maxSpeed);
    this.position.add(this.velocity);
    // reset accelertion to 0 each cycle
    this.acceleration.mult(0);
    
  }

  // calculate and apply a steering force towards a target
  // STEER = DESIRED MINUS VELOCITY
  seek(target) {
    // A vector pointing from the location to the target
    let desired = p5.Vector.sub(target, this.position);
    // Normalize desired and scale to maximum speed
    desired.normalize();
    desired.mult(this.maxSpeed);
    // Steering = Desired minus Velocity
    let steer = p5.Vector.sub(desired, this.velocity);
    steer.limit(this.maxForce); // Limit to maximum steering force
    return steer;
  }

  show() {
    // draw a kite shape
    let angle = this.velocity.heading();
    fill(random(90, 127), random(150, 200), random(180, 255), 150);
    stroke(random(100, 127), random(100, 200), random(200, 255));
    push();
    translate(this.position.x, this.position.y);
    rotate(angle);

    let freqX = map(mouseX, 0, width, 1, 15);
    let freqY = map(mouseY, 0, height, 1, 15);

    beginShape();

    let x = this.r * cos(10 * this.r) * freqX;
    let y = this.r * cos(10 * this.r) * freqY;
    //crystal like shape
    //right
    vertex(x * 2, 0);
    //top right
    vertex(x * 0.7, -y);
    //top left
    vertex(-x * 0.7, -y);
    //left
    vertex(-x * 2, 0);
    //bottom
    vertex(0, y);
    endShape(CLOSE);
    pop();
  }

  // wraparound
  borders() {
    if (this.position.x < -this.r) this.position.x = width + this.r;
    if (this.position.y < -this.r) this.position.y = height + this.r;
    if (this.position.x > width + this.r) this.position.x = -this.r;
    if (this.position.y > height + this.r) this.position.y = -this.r;
  }

  // separation itchecks for nearby boids and steers away
  separate(boids) {
    let desiredSeparation = 15;
    let steer = createVector(0, 0);
    let count = 0;
    // for every boid in the system, check if it's too close
    for (let i = 0; i < boids.length; i++) {
      let d = p5.Vector.dist(this.position, boids[i].position);
      // ff the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
      if (d > 0 && d < desiredSeparation) {
        // calculate vector pointing away from neighbor
        let diff = p5.Vector.sub(this.position, boids[i].position);
        diff.normalize();
        // weight by distance
        diff.div(d);
        steer.add(diff);

        // keep track of how many
        count++;
      }
    }
    // average -- divide by how many
    if (count > 0) {
      steer.div(count);
    }

    // As long as the vector is greater than 0
    if (steer.mag() > 0) {
      // Implement Reynolds: Steering = Desired - Velocity
      steer.normalize();
      steer.mult(this.maxSpeed);
      steer.sub(this.velocity);
      steer.limit(this.maxForce);
    }
    return steer;
  }

  // Alignment
  // For every nearby boid in the system, calculate the average velocity

  align(boids) {
    let neighborDistance = 40;
    let sum = createVector(0, 0);
    let count = 0;
    for (let i = 0; i < boids.length; i++) {
      let d = p5.Vector.dist(this.position, boids[i].position);
      if (d > 0 && d < neighborDistance) {
        sum.add(boids[i].velocity);
        count++;
      }
    }
    if (count > 0) {
      sum.div(count);
      sum.normalize();
      sum.mult(this.maxSpeed);
      let steer = p5.Vector.sub(sum, this.velocity);
      steer.limit(this.maxForce);
      return steer;
    } else {
      return createVector(0, 0);
    }
  }

  // Cohesion
  // For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
  cohere(boids) {
    let neighborDistance = 40;
    let sum = createVector(0, 0); // Start with empty vector to accumulate all locations
    let count = 0;
    for (let i = 0; i < boids.length; i++) {
      let d = p5.Vector.dist(this.position, boids[i].position);
      if (d > 0 && d < neighborDistance) {
        sum.add(boids.position); // Add location
        count++;
      }
    }
    if (count > 0) {
      sum.div(count);
      return this.seek(sum); // Steer towards the location
    } else {
      return createVector(0, 0);
    }
  }
}

The Boid class was the most complicated. I had to do a lot of debugging to add the attractor into action so that the Boids are affected by it.

Embedded Sketch:


Future work:

I am satisfied with how the sketch turned out to be. Things I want to experiment further with is how the flock would move around the canvas by creating a path for them to create different shapes and interactions that are strictly influenced by tension and release. Further, I think having an audio sync into the work is also interesting to see. The audio could perhaps be in relation to the acceleration and speed of the flock’s movement. Further, if this could translate into an interactive project, perhaps I can add a motion sensor so that the audience can influence the behavior of the flock through that data.

Resources:

The Coding Train. “Coding Challenge 124: Flocking Simulation.” YouTube, 11 Dec. 2018, www.youtube.com/watch?v=mhjuuHl6qHM.

Arthur Facredyn. “Path Finding in p5.js # 1.” YouTube, 11 June 2018, www.youtube.com/watch?v=Ohcrstxcci4.

Murmuration – Robert Hodgin. roberthodgin.com/project/murmuration.

5. Autonomous Agents. nature-of-code-2nd-edition.netlify.app/autonomous-agents/#vehicles-and-steering.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *