73 lines
3.7 KiB
JavaScript
73 lines
3.7 KiB
JavaScript
import { cond, sub, divide, multiply, sqrt, add, block, set, exp, sin, cos, eq, or, neq, and, lessThan, greaterThan, proc, min, abs } from '../base';
|
|
import AnimatedValue from '../core/InternalAnimatedValue';
|
|
const MAX_STEPS_MS = 64;
|
|
|
|
function spring(clock, state, config) {
|
|
const lastTime = cond(state.time, state.time, clock);
|
|
const deltaTime = min(sub(clock, lastTime), MAX_STEPS_MS);
|
|
const c = config.damping;
|
|
const m = config.mass;
|
|
const k = config.stiffness;
|
|
const v0 = multiply(-1, state.velocity);
|
|
const x0 = sub(config.toValue, state.position);
|
|
const zeta = divide(c, multiply(2, sqrt(multiply(k, m)))); // damping ratio
|
|
|
|
const omega0 = sqrt(divide(k, m)); // undamped angular frequency of the oscillator (rad/ms)
|
|
|
|
const omega1 = multiply(omega0, sqrt(sub(1, multiply(zeta, zeta)))); // exponential decay
|
|
|
|
const t = divide(deltaTime, 1000); // in seconds
|
|
|
|
const sin1 = sin(multiply(omega1, t));
|
|
const cos1 = cos(multiply(omega1, t)); // under damped
|
|
|
|
const underDampedEnvelope = exp(multiply(-1, zeta, omega0, t));
|
|
const underDampedFrag1 = multiply(underDampedEnvelope, add(multiply(sin1, divide(add(v0, multiply(zeta, omega0, x0)), omega1)), multiply(x0, cos1)));
|
|
const underDampedPosition = sub(config.toValue, underDampedFrag1); // This looks crazy -- it's actually just the derivative of the oscillation function
|
|
|
|
const underDampedVelocity = sub(multiply(zeta, omega0, underDampedFrag1), multiply(underDampedEnvelope, sub(multiply(cos1, add(v0, multiply(zeta, omega0, x0))), multiply(omega1, x0, sin1)))); // critically damped
|
|
|
|
const criticallyDampedEnvelope = exp(multiply(-1, omega0, t));
|
|
const criticallyDampedPosition = sub(config.toValue, multiply(criticallyDampedEnvelope, add(x0, multiply(add(v0, multiply(omega0, x0)), t))));
|
|
const criticallyDampedVelocity = multiply(criticallyDampedEnvelope, add(multiply(v0, sub(multiply(t, omega0), 1)), multiply(t, x0, omega0, omega0))); // conditions for stopping the spring animations
|
|
|
|
const prevPosition = state.prevPosition ? state.prevPosition : new AnimatedValue(0);
|
|
const isOvershooting = cond(and(config.overshootClamping, neq(config.stiffness, 0)), cond(lessThan(prevPosition, config.toValue), greaterThan(state.position, config.toValue), lessThan(state.position, config.toValue)));
|
|
const isVelocity = lessThan(abs(state.velocity), config.restSpeedThreshold);
|
|
const isDisplacement = or(eq(config.stiffness, 0), lessThan(abs(sub(config.toValue, state.position)), config.restDisplacementThreshold));
|
|
return block([set(prevPosition, state.position), cond(lessThan(zeta, 1), [set(state.position, underDampedPosition), set(state.velocity, underDampedVelocity)], [set(state.position, criticallyDampedPosition), set(state.velocity, criticallyDampedVelocity)]), set(state.time, clock), cond(or(isOvershooting, and(isVelocity, isDisplacement)), [cond(neq(config.stiffness, 0), [set(state.velocity, 0), set(state.position, config.toValue)]), set(state.finished, 1)])]);
|
|
}
|
|
|
|
const procSpring = proc((finished, velocity, position, time, prevPosition, toValue, damping, mass, stiffness, overshootClamping, restSpeedThreshold, restDisplacementThreshold, clock) => spring(clock, {
|
|
finished,
|
|
velocity,
|
|
position,
|
|
time,
|
|
// @ts-ignore
|
|
prevPosition
|
|
}, {
|
|
toValue,
|
|
damping,
|
|
mass,
|
|
stiffness,
|
|
overshootClamping,
|
|
restDisplacementThreshold,
|
|
restSpeedThreshold
|
|
}));
|
|
export default ((clock, {
|
|
finished,
|
|
velocity,
|
|
position,
|
|
time,
|
|
// @ts-ignore
|
|
prevPosition
|
|
}, {
|
|
toValue,
|
|
damping,
|
|
mass,
|
|
stiffness,
|
|
overshootClamping,
|
|
restDisplacementThreshold,
|
|
restSpeedThreshold
|
|
}) => procSpring(finished, velocity, position, time, prevPosition, toValue, damping, mass, stiffness, overshootClamping, restSpeedThreshold, restDisplacementThreshold, clock));
|
|
//# sourceMappingURL=spring.js.map
|