diff options
| author | pancelor <pancelor@gmail.com> | 2024-03-12 22:10:38 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-03-13 18:35:07 -0700 |
| commit | 7a858257f2c05f0ab8df0debda9f1d58b5ecf251 (patch) | |
| tree | fd97c46a20905ce449c5ef6f99931a9974ba995f /lib/std/math.zig | |
| parent | ea8e9e668b8b566a94e8d69476d392458918382a (diff) | |
| download | zig-7a858257f2c05f0ab8df0debda9f1d58b5ecf251.tar.gz zig-7a858257f2c05f0ab8df0debda9f1d58b5ecf251.zip | |
remove `math.lerp` bounds for t
I think of lerp() as a way to change coordinate systems, essentially
remapping the input numberline onto a shifted+rescaled numberline. In
my mind the full numberline is remapped, not just the 0-1 segment.
An example of how this is useful: in a game, you can write:
`myPos = lerp(pos0, pos1, easeOutBack(u))`
for some `u` that changes from 0 to 1 over time.
(see https://easings.net/#easeOutBack)
This will animate `myPos` between `pos0` and `pos1`, overshooting the
goal position `pos1` in a nicely-animated way.
`easeOutBack(float)->float` is a pure function that overshoots 1,
and by combining it with `lerp()` we can remap coordinates in other
coordinate systems, making them overshoot in the same way.
However, this overshooting is only possible because `easeOutBack(t)`
sometimes exceeds the range 0-1 (e.g. `easeOutBack(0.5)` is 1.0877),
which is not allowed by the current `math.lerp` implementation.
This commit removes the asserts that prevented this use-case. Now, any
value can be inputted for t. For example, `lerp(10,20, 2.0)` will now
return 30, instead of throwing an assert error.
Diffstat (limited to 'lib/std/math.zig')
| -rw-r--r-- | lib/std/math.zig | 30 |
1 files changed, 16 insertions, 14 deletions
diff --git a/lib/std/math.zig b/lib/std/math.zig index 11ab9189fa..f0403edd01 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -1363,23 +1363,13 @@ test "lossyCast" { } /// Performs linear interpolation between *a* and *b* based on *t*. -/// *t* must be in range 0.0 to 1.0. Supports floats and vectors of floats. +/// *t* ranges from 0.0 to 1.0, but may exceed these bounds. +/// Supports floats and vectors of floats. /// /// This does not guarantee returning *b* if *t* is 1 due to floating-point errors. /// This is monotonic. pub fn lerp(a: anytype, b: anytype, t: anytype) @TypeOf(a, b, t) { const Type = @TypeOf(a, b, t); - - switch (@typeInfo(Type)) { - .Float, .ComptimeFloat => assert(t >= 0 and t <= 1), - .Vector => { - const lower_bound = @reduce(.And, t >= @as(Type, @splat(0))); - const upper_bound = @reduce(.And, t <= @as(Type, @splat(1))); - assert(lower_bound and upper_bound); - }, - else => comptime unreachable, - } - return @mulAdd(Type, b - a, t, a); } @@ -1392,6 +1382,9 @@ test "lerp" { try testing.expectEqual(@as(f32, 43.75), lerp(50, 25, 0.25)); try testing.expectEqual(@as(f64, -31.25), lerp(-50, 25, 0.25)); + try testing.expectEqual(@as(f64, 30), lerp(10, 20, 2.0)); + try testing.expectEqual(@as(f64, 5), lerp(10, 20, -0.5)); + try testing.expectApproxEqRel(@as(f32, -7.16067345e+03), lerp(-10000.12345, -5000.12345, 0.56789), 1e-19); try testing.expectApproxEqRel(@as(f64, 7.010987590521e+62), lerp(0.123456789e-64, 0.123456789e64, 0.56789), 1e-33); @@ -1405,8 +1398,8 @@ test "lerp" { const b: @Vector(3, f32) = @splat(50); const t: @Vector(3, f32) = @splat(0.5); try testing.expectEqual( - lerp(a, b, t), @Vector(3, f32){ 25, 25, 25 }, + lerp(a, b, t), ); } { @@ -1414,8 +1407,17 @@ test "lerp" { const b: @Vector(3, f64) = @splat(100); const t: @Vector(3, f64) = @splat(0.5); try testing.expectEqual( - lerp(a, b, t), @Vector(3, f64){ 75, 75, 75 }, + lerp(a, b, t), + ); + } + { + const a: @Vector(2, f32) = @splat(40); + const b: @Vector(2, f32) = @splat(80); + const t: @Vector(2, f32) = @Vector(2, f32){ 0.25, 0.75 }; + try testing.expectEqual( + @Vector(2, f32){ 50, 70 }, + lerp(a, b, t), ); } } |
