Forces and Vectors

Published February 05, 2018

This week, we looked at how vectors and vector math can provide a valuable framework for building a physics engine. Because the underlying math for manipulating vectors remains the same whether we are dealing with position, velocity, acceleration or multiple independent force vectors, our code can rely on a single set of functions to perform vector manipulations. Luckily for us, most of these functions are already baked into the p5.js library.

For my homework, I recreated my sketch from last week using vectors to steer the movers towards their respective home-bases and away from the mouse. I followed along with this tutorial and adapted much of the code from the corresponding github repo. Most helpful for me in coding with vectors is to always remember which time domain each vector is in: are we working with position (independent of time), in velocity (change in position over time), or acceleration (change in velocity over time / change in position over time squared)? Because time is never explicitly called out in my sketch (but is instead reliant on the p5.js draw loop), and because the vector objects are independent of their time domain, this is not always obvious.

The above sketch implements two forces which act on acceleration: an attraction toward their position in the map, and a repulsion from the mouse. Code below:

class Walker {
	constructor(x,y){
		this.dest = createVector(x,y)
		this.pos = createVector(floor(random(width)),floor(random(height)));
		this.vel = createVector();
		this.acc = createVector();

    this.maxSpeed = 10;
    this.maxForce = 1;
	}

  work(){
    let homewardForce = this.getHomewardForce();
    let repulsiveForce = this.getRepulsiveForce();

    this.applyForce(repulsiveForce);
    this.applyForce(homewardForce);
  }

	render(){
		fill(0);
		ellipse(this.pos.x,this.pos.y, 8,8);
	}

	update(){
    this.pos.add(this.vel);
    this.vel.add(this.acc);
    this.acc.mult(0); // reset acceleration each frame
	}

	getHomewardForce(){
    let desired = p5.Vector.sub(this.dest,this.pos);
    let d = desired.mag();
    let speed = this.maxSpeed;
    if (d < 100){
      speed = map(d,0,100,0,this.maxSpeed);
    } // slowing
    desired.setMag(speed);
    let steer = p5.Vector.sub(desired,this.vel);
    steer.limit(this.maxForce);
    return steer;
	}

  getRepulsiveForce(){
    let mouse = createVector(mouseX,mouseY);
    let desired = p5.Vector.sub(mouse,this.pos);
    let d = desired.mag();
    if (d<50){
      desired.setMag(this.maxSpeed);
      desired.mult(-1);
      let steer = p5.Vector.sub(desired,this.vel);
      steer.limit(this.maxSpeed);
      return steer;
    } else{
      return createVector(0,0);
    }
  }

	applyForce(f){
		this.acc.add(f);
	}
}