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.
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?
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>