bevy/crates
Greeble 861e778c4c
Add unit structs for each ease function (#18739)
## Objective

Allow users to directly call ease functions rather than going through
the `EaseFunction` struct. This is less verbose and more efficient when
the user doesn't need the data-driven aspects of `EaseFunction`.

## Background

`EaseFunction` is a flexible and data-driven way to apply easing. But
that has a price when a user just wants to call a specific ease
function:

```rust
EaseFunction::SmoothStep.sample(t);
```

This is a bit verbose, but also surprisingly inefficient. It calls the
general `EaseFunction::eval`, which won't be inlined and adds an
unnecessary branch. It can also increase code size since it pulls in all
ease functions even though the user might only require one. As far as I
can tell this is true even with `opt-level = 3` and `lto = "fat"`.

```asm
; EaseFunction::SmoothStep.sample_unchecked(t)
lea rcx, [rip + __unnamed_2] ; Load the disciminant for EaseFunction::SmoothStep.
movaps xmm1, xmm0
jmp bevy_math::curve::easing::EaseFunction::eval    
```

## Solution

This PR adds a struct for each ease function. Most are unit structs, but
a couple have parameters:

```rust
SmoothStep.sample(t);

Elastic(50.0).sample(t);

Steps(4, JumpAt::Start).sample(t)
```

The structs implement the `Curve<f32>` trait. This means they fit into
the broader `Curve` system, and the user can choose between `sample`,
`sample_unchecked`, and `sample_clamped`. The internals are a simple
function call so the compiler can easily estimate the cost of inlining:

```asm
; SmoothStep.sample_unchecked(t)
movaps xmm1, xmm0
addss xmm1, xmm0
movss xmm2, dword ptr [rip + __real@40400000] ; 3.0
subss xmm2, xmm1
mulss xmm2, xmm0
mulss xmm0, xmm2
```

In a microbenchmark this is around 4x faster. If inlining permits
auto-vectorization then it's 20-50x faster, but that's a niche case.

Adding unit structs is also a small boost to discoverability - the unit
struct can be found in VS Code via "Go To Symbol" -> "smoothstep", which
doesn't find `EaseFunction::SmoothStep`.

### Concerns

- While the unit structs have advantages, they add a lot of API surface
area.
- Another option would have been to expose the underlying functions.
  - But functions can't implement the `Curve` trait.
- And the underlying functions are unclamped, which could be a footgun.
- Or there have to be three functions to cover unchecked/checked/clamped
variants.
- The unit structs can't be used with `EasingCurve`, which requires
`EaseFunction`.
  - This might confuse users and limit optimisation.
    -  Wrong: `EasingCurve::new(a, b, SmoothStep)`.
    -  Right: `EasingCurve::new(a, b, EaseFunction::SmoothStep)`.
- In theory `EasingCurve` could be changed to support any `Curve<f32>`
or a more limited trait.
- But that's likely to be a breaking change and raises questions around
reflection and reliability.
- The unit structs don't have serialization.
- I don't know much about the motivations/requirements for
serialization.
- Each unit struct duplicates the documentation of `EaseFunction`.
- This is convenient for the user, but awkward for anyone updating the
code.
- Maybe better if each unit struct points to the matching
`EaseFunction`.
  - Might also make the module page less intimidating (see screenshot).


![image](https://github.com/user-attachments/assets/59d1cf1f-d136-437f-8ad6-fdae8ca7d57a)

## Testing

```
cargo test -p bevy_math
```
2025-07-02 19:18:20 +00:00
..
bevy_a11y Bump Version after Release (#19774) 2025-06-22 23:06:43 +00:00
bevy_animation Prevent AnimationGraph from serializing AssetIds. (#19615) 2025-06-30 22:26:05 +00:00
bevy_anti_aliasing Make render and compute pipeline descriptors defaultable. (#19903) 2025-07-02 18:47:27 +00:00
bevy_app Upgrade to Rust 1.88 (#19825) 2025-06-26 19:38:19 +00:00
bevy_asset Replace Handle::Weak with Handle::Uuid. (#19896) 2025-07-02 14:40:35 +00:00
bevy_audio Upgrade to Rust 1.88 (#19825) 2025-06-26 19:38:19 +00:00
bevy_color Ugrade to wgpu version 25.0 (#19563) 2025-06-26 19:41:47 +00:00
bevy_core_pipeline Make render and compute pipeline descriptors defaultable. (#19903) 2025-07-02 18:47:27 +00:00
bevy_core_widgets Newtyped ScrollPosition (#19881) 2025-07-01 17:41:48 +00:00
bevy_derive Bump Version after Release (#19774) 2025-06-22 23:06:43 +00:00
bevy_dev_tools Upgrade to Rust 1.88 (#19825) 2025-06-26 19:38:19 +00:00
bevy_diagnostic updating: very very minorly (#19827) 2025-06-26 21:48:20 +00:00
bevy_dylib Bump Version after Release (#19774) 2025-06-22 23:06:43 +00:00
bevy_ecs Implement MapEntities for arrays, HashMap, BTreeMap, IndexMap (#19908) 2025-07-02 14:50:55 +00:00
bevy_encase_derive Bump Version after Release (#19774) 2025-06-22 23:06:43 +00:00
bevy_feathers Feathers checkbox (#19900) 2025-07-01 06:59:14 +00:00
bevy_gilrs Add newlines before impl blocks (#19746) 2025-06-22 23:07:02 +00:00
bevy_gizmos Make render and compute pipeline descriptors defaultable. (#19903) 2025-07-02 18:47:27 +00:00
bevy_gltf Nudge users into migrating to new default glTF coordinate conversion (#19816) 2025-06-28 18:35:41 +00:00
bevy_image optimize ktx2 level data concatenation (#19845) 2025-06-29 21:59:56 +00:00
bevy_input bevy_input: build warnings without bevy_reflect (#19862) 2025-06-29 17:13:43 +00:00
bevy_input_focus Upgrade to Rust 1.88 (#19825) 2025-06-26 19:38:19 +00:00
bevy_internal Light Textures (#18031) 2025-06-30 21:56:17 +00:00
bevy_log Upgrade to Rust 1.88 (#19825) 2025-06-26 19:38:19 +00:00
bevy_macro_utils Bump Version after Release (#19774) 2025-06-22 23:06:43 +00:00
bevy_math Add unit structs for each ease function (#18739) 2025-07-02 19:18:20 +00:00
bevy_mesh Ugrade to wgpu version 25.0 (#19563) 2025-06-26 19:41:47 +00:00
bevy_mikktspace Bump Version after Release (#19774) 2025-06-22 23:06:43 +00:00
bevy_pbr Make render and compute pipeline descriptors defaultable. (#19903) 2025-07-02 18:47:27 +00:00
bevy_picking Upstream raycasting UVs (#19791) 2025-06-24 18:10:59 +00:00
bevy_platform bevy_platform: clippy without default features (#19860) 2025-06-29 17:12:57 +00:00
bevy_ptr Add newlines before impl blocks (#19746) 2025-06-22 23:07:02 +00:00
bevy_reflect bevy_reflect: streamline generated FromReflect::from_reflect (#19906) 2025-07-02 14:59:20 +00:00
bevy_remote Upgrade to Rust 1.88 (#19825) 2025-06-26 19:38:19 +00:00
bevy_render Make render and compute pipeline descriptors defaultable. (#19903) 2025-07-02 18:47:27 +00:00
bevy_scene Upgrade to Rust 1.88 (#19825) 2025-06-26 19:38:19 +00:00
bevy_solari Make render and compute pipeline descriptors defaultable. (#19903) 2025-07-02 18:47:27 +00:00
bevy_sprite Make render and compute pipeline descriptors defaultable. (#19903) 2025-07-02 18:47:27 +00:00
bevy_state Upgrade to Rust 1.88 (#19825) 2025-06-26 19:38:19 +00:00
bevy_tasks Update derive_more requirement from 1 to 2 (#19671) 2025-06-24 11:13:04 +00:00
bevy_text Replace Handle::Weak with Handle::Uuid. (#19896) 2025-07-02 14:40:35 +00:00
bevy_time Bump Version after Release (#19774) 2025-06-22 23:06:43 +00:00
bevy_transform Update derive_more requirement from 1 to 2 (#19671) 2025-06-24 11:13:04 +00:00
bevy_ui Make render and compute pipeline descriptors defaultable. (#19903) 2025-07-02 18:47:27 +00:00
bevy_utils bevy_utils: clippy without debug feature enabled (#19861) 2025-06-29 17:13:27 +00:00
bevy_window bevy_window: fix compilation without default features (#19870) 2025-06-30 20:51:52 +00:00
bevy_winit bevy_winit: fix compile and clippy on wasm (#19869) 2025-06-29 21:30:28 +00:00