Imagine that there is a pendulum swinging from -1 to 1:
This pendulum is equivalent to SINE. Imagine also that this pendulum is swinging quite fast in an update loop of 60fps.
This might be my code:
let angle = 0
function update() {
angle += .1
pendulum.angle = Math.sin(angle)
requestAnimationFrame(update)
}
update()
That's all fine. What I want is to detect the point at which this pendulum is closest to -1 or 1. One may think you could just write:
if (pendulum.angle == -1 || pendulum.angle == 1) {
console.log('pendulum is at it\'s maximum')
}
However, it doesn't quite work like that: If the pendulum's speed was moving quite fast, it may miss -1 or 1 completely! Another seemingly possible solution is to detect if the pendulum is within a certain range and if so, raise the message:
if (pendulum.angle < -0.9 || pendulum.angle > 0.9) {
console.log('pendulum is at it\'s maximum')
}
...But what if the speed was going really, really fast? What if it missed those ranges?
The angle's getting bigger is being restricted to the programs framerate - so the faster it spins, the more space it skips.
This leads to the question: What formula could I use to find out how large the range should be - based on it's speed?
Any help would be appreciated.
Compute the current value's sin as well as that of the preceding and subsequent values. If your sin value is closer to -1 or 1 than that of the preceding and subsequent values, you're the closest, do your logging.
function update() {
lastanglemag = Math.abs(Math.sin(angle)); // Get the preadjustment magnitude
angle += .1
pendulum.angle = Math.sin(angle)
currentanglemag = Math.abs(pendulum.angle) // Get current magnitude
// If current magnitude is larger than both neighbors, we're at a maximum
if (currentanglemag > lastanglemag && currentanglemag > Math.abs(Math.sin(angle + 0.1))) {
console.log("pendulum is at its maximum")
}
requestAnimationFrame(update)
}
update()
Optimizations to only compute one new sin per update (the one for the next angle), caching the other values, to reduce 2-3 sin calls per update to just one are left as an exercise for the reader (I didn't want to introduce any additional persistent state and the associated complexity, and in practice, for anything based on a GUI, the cost of 1-2 extra sin computations is meaningless; the user can't see a delay measured in nanoseconds anyway).
Using that d/dx sin(x) = cos(x), one could simplify the other answer to
function update() {
lastangle_d = Math.cos(angle); // Get the preadjustment derivative
angle += .1
pendulum.angle = Math.sin(angle);
currentangle_d = Math.cos(angle); // Get current derivative
// If signum of current and previous derivatives differ, we've passed a maximum
if (Math.sign(lastangle_d) !== Math.sign(currentangle_d)) {
console.log("pendulum is at its maximum")
}
requestAnimationFrame(update);
}
update();
If someone really wants to hack, if (lastangle_d*currentangle_d < 0) could do that too.