Search code examples
javascriptgeometryp5.jslerp

Rotating a line to look at the mouse with linear interpolation causes jumping


Earlier, I was trying to find out how to rotate a line around a pivot using p5.js, and I played with the code a little bit to make the line point towards the mouse using the atan2 function. It worked fine, and I decided to see what it would look like if I used linear interpolation (lerp) to make the animation look smoother. What was weird is that it seemed once the line passed a certain point, it jumped to the other side instead of just moving to that point. example image of issue

Here's the code I'm having the issue with:

let angle = 0;

let rot = 0;

function setup() {
  createCanvas(600, 300);
}

function draw() {
  let v1 = createVector(width / 2 - 50, height / 2);
  let v2 = createVector(width / 2 + 50, height / 2);

  background(255);
  stroke(0);
  strokeWeight(4);

  rot = lerp(rot, atan2(mouseY - v1.y, mouseX - v1.x), 0.1);

  push();
  translate(v1.x, v1.y);
  rotate(rot);
  translate(-v1.x, -v1.y);
  let r0 = line(v1.x, v1.y, v2.x, v2.y);
  strokeWeight(10);
  let p1 = point(v1.x, v1.y);
  let p2 = point(v2.x, v2.y);
  pop();
}

How can I make this animation look smooth, without the weird jumping?


Solution

  • The issue is because atan2 returns value between -Math.PI to +Math.PI so there is a jump where the linear interpolation fails for the expression target_angle = atan2(mouseY - v1.y, mouseX - v1.x).

    That's why, in order to make the shortest path between current rot to target_angle we should normalize the difference between so that the distance to cover its abs value should be less than Math.PI (half a circle)

    let angle = 0;
    
    let rot = 0;
    
    function setup() {
      createCanvas(600, 300);
    }
    
    function draw() {
      let v1 = createVector(width / 2 - 50, height / 2);
      let v2 = createVector(width / 2 + 50, height / 2);
    
      background(255);
      stroke(0);
      strokeWeight(4);
    
    
      let target = atan2(mouseY - v1.y, mouseX - v1.x);
    
      while (target - rot > Math.PI) {
        target -= 2 * Math.PI;
      }
    
      while (target - rot < -Math.PI) {
        target += 2 * Math.PI;
      }
    
      rot = lerp(rot, target, 0.1);
    
      push();
      translate(v1.x, v1.y);
      rotate(rot);
      translate(-v1.x, -v1.y);
      let r0 = line(v1.x, v1.y, v2.x, v2.y);
      strokeWeight(10);
      let p1 = point(v1.x, v1.y);
      let p2 = point(v2.x, v2.y);
      pop();
    }
    <script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/p5.js"></script>