skip to Main Content

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?

2

Answers


  1. 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/[email protected]/lib/p5.js"></script>
    Login or Signup to reply.
  2. This is a variation on IT goldman’s nice answer(+1) with the same principle @ggorlen metioned. (I was familiar with the idea from actionscript 3 days from GreenSock’s ShortRotationPlugin). Credits go to them.

    This is the mixture of the above: the key difference is avoiding the blocking while loops (which I don’t generally recommend).

    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);
      // check remainder wrt full 360 degrees (2 * PI)
      let diff = (target - rot) % TWO_PI;
      // if the difference is not on this half of the circle (needs short rotation) wrap around the circle (add or remove 2*PI) depending on the sign of the difference
      if (diff != diff % (PI)) {
        diff = (diff < 0) ? diff + TWO_PI : diff - TWO_PI;
      }
      // ease towards the rotation using short rotation angle difference
      rot = lerp(rot, rot + diff, 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://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.min.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search