summaryrefslogtreecommitdiff
path: root/src/animation.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/animation.zig')
-rw-r--r--src/animation.zig26
1 files changed, 26 insertions, 0 deletions
diff --git a/src/animation.zig b/src/animation.zig
index 9002a2c6..12d352d1 100644
--- a/src/animation.zig
+++ b/src/animation.zig
@@ -744,6 +744,14 @@ pub const Spring = struct {
return spring.at(now_ns).value;
}
+ /// Moves what it shows by `by` at `now_ns`, its speed kept, toward the
+ /// same target (a scroll's picture falling behind the text it follows),
+ /// at `motion`'s pace, never past its mark.
+ pub fn carry(spring: *Spring, by: f32, now_ns: u64, motion: Motion) void {
+ const state = spring.at(now_ns);
+ spring.* = .{ .omega = motion.omega, .zeta = 1, .target = spring.target, .from = state.value + by, .velocity = state.velocity, .from_ns = now_ns, .settled = false };
+ }
+
/// Heads for `target` from wherever it is at `now_ns`, moving as it was,
/// at the pace and damping `motion` gives: at once when it is `off`.
pub fn retarget(spring: *Spring, target: f32, now_ns: u64, motion: Motion) void {
@@ -769,6 +777,24 @@ pub const Spring = struct {
}
};
+test "a carried spring keeps its speed, heads back to its mark and never passes it" {
+ const ms = std.time.ns_per_ms;
+ var spring: Spring = .{};
+ const smooth = Motion.of(.smooth);
+ spring.carry(-3, 0, smooth);
+ try std.testing.expectEqual(@as(f32, -3), spring.value(0));
+ const mid = spring.at(40 * ms);
+ try std.testing.expect(mid.value > -3 and mid.value < 0 and mid.velocity > 0);
+ // Another notch mid-way: one more row behind, still moving back.
+ spring.carry(-1, 40 * ms, smooth);
+ const after = spring.at(40 * ms);
+ try std.testing.expectApproxEqAbs(mid.value - 1, after.value, 1e-5);
+ try std.testing.expectApproxEqAbs(mid.velocity, after.velocity, 1e-4);
+ var t: u64 = 40 * ms;
+ while (spring.step(t)) : (t += frame_ns) try std.testing.expect(spring.value(t) <= 1e-4);
+ try std.testing.expect(t < 400 * ms);
+}
+
test "a spring settles without overshoot and keeps its velocity when retargeted" {
var spring: Spring = .{};
const ms = std.time.ns_per_ms;