bevy/crates
Matty 93c17d105a
Make cardinal splines include endpoints (#12574)
# Objective

- Fixes #12570 

## Solution

Previously, cardinal splines constructed by `CubicCardinalSpline` would
leave out their endpoints when constructing the cubic curve segments
connecting their points. (See the linked issue for details.)

Now, cardinal splines include the endpoints. For instance, the provided
usage example
```rust
let points = [
    vec2(-1.0, -20.0),
    vec2(3.0, 2.0),
    vec2(5.0, 3.0),
    vec2(9.0, 8.0),
];
let cardinal = CubicCardinalSpline::new(0.3, points).to_curve();
let positions: Vec<_> = cardinal.iter_positions(100).collect();
```
will actually produce a spline that connects all four of these points
instead of just the middle two "interior" points.

Internally, this is achieved by duplicating the endpoints of the vector
of control points before performing the construction of the associated
`CubicCurve`. This amounts to specifying that the tangents at the
endpoints `P_0` and `P_n` (say) should be parallel to `P_1 - P_0` and
`P_n - P_{n-1}`.

---

## Migration Guide

Any users relying on the old behavior of `CubicCardinalSpline` will have
to truncate any parametrizations they used in order to access a curve
identical to the one they had previously. This would be done by chopping
off a unit-distance segment from each end of the parametrizing interval.
For instance, if a user's existing code looks as follows
```rust
fn interpolate(t: f32) -> Vec2 {
    let points = [
        vec2(-1.0, -20.0),
        vec2(3.0, 2.0),
        vec2(5.0, 3.0),
        vec2(9.0, 8.0),
    ];
    let my_curve = CubicCardinalSpline::new(0.3, points).to_curve();
    my_curve.position(t)
}
```

then in order to obtain similar behavior, `t` will need to be shifted up
by 1, since the output of `CubicCardinalSpline::to_curve` has introduced
a new segment in the interval [0,1], displacing the old segment from
[0,1] to [1,2]:

```rust
fn interpolate(t: f32) -> Vec2 {
    let points = [
        vec2(-1.0, -20.0),
        vec2(3.0, 2.0),
        vec2(5.0, 3.0),
        vec2(9.0, 8.0),
    ];
    let my_curve = CubicCardinalSpline::new(0.3, points).to_curve();
    my_curve.position(t+1)
}
```

(Note that this does not provide identical output for values of `t`
outside of the interval [0,1].)

On the other hand, any user who was specifying additional endpoint
tangents simply to get the curve to pass through the right points (i.e.
not requiring exactly the same output) can simply omit the endpoints
that were being supplied only for control purposes.

---

## Discussion

### Design considerations

This is one of the two approaches outlined in #12570 — in this PR, we
are basically declaring that the docs are right and the implementation
was flawed.

One semi-interesting question is how the endpoint tangents actually
ought to be defined when we include them, and another option considered
was mirroring the control points adjacent to the endpoints instead of
duplicating them, which would have had the advantage that the expected
length of the corresponding difference should be more similar to that of
the other difference-tangents, provided that the points are equally
spaced.

In this PR, the duplication method (which produces smaller tangents) was
chosen for a couple reasons:
- It seems to be more standard
- It is exceptionally simple to implement
- I was a little concerned that the aforementioned alternative would
result in some over-extrapolation

### An annoyance

If you look at the code, you'll see I was unable to find a satisfactory
way of doing this without allocating a new vector. This doesn't seem
like a big problem given the context, but it does bother me. In
particular, if there is some easy parallel to `slice::windows` for
iterators that doesn't pull in an external dependency, I would love to
know about it.
2024-03-21 18:58:51 +00:00
..
bevy_a11y Bump Version after Release (#12020) 2024-02-21 20:58:59 +00:00
bevy_animation Use async-fn in traits rather than BoxedFuture (#12550) 2024-03-18 17:56:57 +00:00
bevy_app Add "all-features = true" to docs.rs metadata for most crates (#12366) 2024-03-08 20:03:09 +00:00
bevy_asset Make AssetAction::Ignore not copy assets to imported_assets (#12605) 2024-03-21 18:13:18 +00:00
bevy_audio Use async-fn in traits rather than BoxedFuture (#12550) 2024-03-18 17:56:57 +00:00
bevy_color Fix Oklab and Oklch color space inconsistency (#12583) 2024-03-19 22:50:42 +00:00
bevy_core Add "all-features = true" to docs.rs metadata for most crates (#12366) 2024-03-08 20:03:09 +00:00
bevy_core_pipeline Explain Camera2dBundle.projection needs to be set carefully (#11115) 2024-03-18 17:35:33 +00:00
bevy_derive Bump Version after Release (#12020) 2024-02-21 20:58:59 +00:00
bevy_dev_tools Always spawn fps_overlay on top of everything (#12586) 2024-03-20 13:11:48 +00:00
bevy_diagnostic Add "all-features = true" to docs.rs metadata for most crates (#12366) 2024-03-08 20:03:09 +00:00
bevy_dylib Bump Version after Release (#12020) 2024-02-21 20:58:59 +00:00
bevy_dynamic_plugin Document all members of bevy_dynamic_plugin (#12029) 2024-02-22 13:28:52 +00:00
bevy_ecs Fix Ci failing over dead code in tests (#12623) 2024-03-21 18:08:47 +00:00
bevy_ecs_compile_fail_tests Fix Ci failing over dead code in tests (#12623) 2024-03-21 18:08:47 +00:00
bevy_encase_derive Bump Version after Release (#12020) 2024-02-21 20:58:59 +00:00
bevy_gilrs Send GamepadEvent for gamepads connected at startup (#12424) 2024-03-11 20:05:10 +00:00
bevy_gizmos Gizmo 3d grids (#12430) 2024-03-13 18:51:53 +00:00
bevy_gltf Use async-fn in traits rather than BoxedFuture (#12550) 2024-03-18 17:56:57 +00:00
bevy_hierarchy Fix Ci failing over dead code in tests (#12623) 2024-03-21 18:08:47 +00:00
bevy_input Add table showing complexity of methods for Input (#10126) 2024-03-10 23:00:20 +00:00
bevy_internal refactor: separate out PanicHandlerPlugin (#12557) 2024-03-19 00:56:49 +00:00
bevy_log refactor: separate out PanicHandlerPlugin (#12557) 2024-03-19 00:56:49 +00:00
bevy_macro_utils fix deprecations from toml_edit (#12421) 2024-03-11 17:53:38 +00:00
bevy_macros_compile_fail_tests Fix Ci failing over dead code in tests (#12623) 2024-03-21 18:08:47 +00:00
bevy_math Make cardinal splines include endpoints (#12574) 2024-03-21 18:58:51 +00:00
bevy_mikktspace fix some typos (#12038) 2024-02-22 18:55:22 +00:00
bevy_panic_handler refactor: separate out PanicHandlerPlugin (#12557) 2024-03-19 00:56:49 +00:00
bevy_pbr Add setting to enable/disable shadows to MaterialPlugin (#12538) 2024-03-18 17:54:41 +00:00
bevy_ptr Add more comprehensive crate level docs for bevy_ptr (#12391) 2024-03-12 14:04:16 +00:00
bevy_reflect add reflect for BinaryHeap (#12503) 2024-03-17 22:24:04 +00:00
bevy_reflect_compile_fail_tests Fix Ci failing over dead code in tests (#12623) 2024-03-21 18:08:47 +00:00
bevy_render Reflect default in some types on bevy_render (#12580) 2024-03-19 22:50:17 +00:00
bevy_scene Use async-fn in traits rather than BoxedFuture (#12550) 2024-03-18 17:56:57 +00:00
bevy_sprite Update to fixedbitset 0.5 (#12512) 2024-03-17 18:43:05 +00:00
bevy_tasks Add "all-features = true" to docs.rs metadata for most crates (#12366) 2024-03-08 20:03:09 +00:00
bevy_text Use async-fn in traits rather than BoxedFuture (#12550) 2024-03-18 17:56:57 +00:00
bevy_time Add "all-features = true" to docs.rs metadata for most crates (#12366) 2024-03-08 20:03:09 +00:00
bevy_transform remove link to inexistent example (#12531) 2024-03-17 19:35:00 +00:00
bevy_ui UI: rounded border should use camera instead of windows (#12601) 2024-03-20 23:50:08 +00:00
bevy_utils Use async-fn in traits rather than BoxedFuture (#12550) 2024-03-18 17:56:57 +00:00
bevy_window Expose Winit's with_skip_taskbar on window creation (#12450) 2024-03-18 17:41:42 +00:00
bevy_winit typo: 'plateform' -> 'platform' (#12626) 2024-03-21 18:37:35 +00:00