Skip to content
Advertisement

How can I change the angle of a vector when hitting an obstacle

So, my issue concerns vectors, I don’t know where I’m going with that case. I’m building a pandemic simulation (using Javascript and the library p5.js), and I’m trying to add a lockdown feature.

Here is an image to make everything clearer:

Pandemic simulation

Essentially, at the moment, when two molecules collide, their velocity vector change appropriately by switching their former velocity.

} else {
            // dx & dy derivate  are equal to the difference of our molecules x & y coordinates
            let dx = this.position.x - _molecule.position.x;
            let dy = this.position.y - _molecule.position.y;

            // normalX & normalY are equal to theirs respective derivates divided by the distance
            let normalX = dx / _distance;
            let normalY = dy / _distance;

            // dVector is the vector which determine how the molecules will move appropiately on  x & y axis
            let dVector = (this.velocity.x - _molecule.velocity.x) * normalX;
            dVector += (this.velocity.y - _molecule.velocity.y) * normalY;

            // the molecules velocity is then  determined by the product of dVector by normalX & normalY
            let dvx = dVector * normalX;
            let dvy = dVector * normalY;

            // constrain limits the velocities between -1 & 1
            let constrainX = constrain(dvx, -1, 1);
            let constrainY = constrain(dvy, -1, 1);

            this.velocity.x -= constrainX;
            this.velocity.y -= constrainY;

            _molecule.velocity.x += constrainX;
            _molecule.velocity.y += constrainY;
        }

My issue arises when I want to change a vector’s angle when a molecule hits another fixed molecule. Unlike the code above, the fixed molecule has to stay still. Thus, I assume that I can’t set this.velocity.x (or y) to be simply reversed.

The bounce method gets two parameters: _molecule (or ball B, the one which ball A collides with) and the distance, calculated as such:

let distance = dist(this.position.x, this.position.y, _molecule.position.x, _molecule.position.y)

I assume that I have to use Sine and Cosine, but I’m not really sure about it.

Advertisement

Answer

I’ve created a tutorial on OpenProcessing that I think should help you understand the vector math for dealing with collisions between moving and stationary circular objects. In short, one circular object colliding with another can be generalized as the collision between that circle and the line that is tangent to the other circle and perpendicular to the line from the center of one circle and the other.

Here is the relevant code sample from page 4 of the tutorial:

const radius = 30;
const speed = 100;

let pos;
let vel;
let time;

let boundary = [];
let obstacles = [];

function setup() {
  createCanvas(400, 400);
  angleMode(DEGREES);
  ellipseMode(RADIUS);

  boundary.push(createVector(60, 4));
  boundary.push(createVector(width - 4, 60));
  boundary.push(createVector(width - 60, height - 4));
  boundary.push(createVector(4, height - 60));

  obstacles.push(createVector(width / 2, height / 2));

  pos = createVector(
    random(40, width - 40),
    random(40, height - 40)
  );
  vel = createVector(100, 0).rotate(random(0, 360));
  time = millis();
}

function draw() {
  deltaT = millis() - time;
  time = millis();

  background('dimgray');

  push();
  fill('lightgray');
  stroke('black');
  strokeWeight(2);
  beginShape();
  for (let v of boundary) {
    vertex(v.x, v.y);
  }
  endShape(CLOSE);
  pop();

  // update position
  pos = createVector(
    min(max(0, pos.x + vel.x * (deltaT / 1000)), width),
    min(max(0, pos.y + vel.y * (deltaT / 1000)), height)
  );

  circle(pos.x, pos.y, radius);

  // check for collisions
  for (let i = 0; i < boundary.length; i++) {
    checkCollision(boundary[i], boundary[(i + 1) % boundary.length]);
  }

  push();
  fill('dimgray');
  for (let obstacle of obstacles) {
    circle(obstacle.x, obstacle.y, radius);

    // Find the tangent plane that is perpendicular to a line from the obstacle to
    // the moving circle

    // A vector pointing in the direction of the moving object
    let dirVector = p5.Vector.sub(pos, obstacle).normalize().mult(radius);

    // The point on the perimiter of the obstacle that is in the direction of the
    // moving object
    let p1 = p5.Vector.add(obstacle, dirVector);
    checkCollision(p1, p5.Vector.add(p1, p5.Vector.rotate(dirVector, -90)));
  }
  pop();
}

// Handles collision with a plane given two points on the plane.
// It is assumed that given a vector from p1 to p2, roating that vector
// clockwise 90 degrees will give a vector pointing to the in-bounds side of the
// plane (i.e. a "normal").
function checkCollision(p1, p2) {
  let boundaryVector = p5.Vector.sub(p2, p1);
  let objVector = p5.Vector.sub(pos, p1);
  let angle = boundaryVector.angleBetween(objVector);
  let dist = objVector.mag() * sin(angle);

  if (dist <= radius) {
    // Collision
    let vParallel = project(vel, boundaryVector);
    let vPerpendicular = p5.Vector.sub(vel, vParallel);

    vel = p5.Vector.add(vParallel, p5.Vector.mult(vPerpendicular, -1));

    let bounce = min(radius, radius - dist);
    // If the ball has crossed over beyond the plane we want to offset it to be on
    // the in-bounds side of the plane.
    let bounceOffset = p5.Vector.rotate(boundaryVector, 90).normalize().mult(bounce);
    pos.add(bounceOffset);
  }
}

function project(vect1, vect2) {
  vect2 = p5.Vector.normalize(vect2);
  return p5.Vector.mult(vect2, p5.Vector.dot(vect1, vect2));
}
<!DOCTYPE html>
<html lang="en">

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>

<body>
</body>

</html>
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement