# Objective
Fixes#13189
## Solution
To add the reflect impls I needed to make all the struct fields pub. I
don't think there's any harm for these types, but just a note for
review.
---------
Co-authored-by: Ben Harper <ben@tukom.org>
# Objective
Fixes#13332.
## Solution
The assertion `circumradius >= 0.0` to allow zero.
Are there any other shapes that need to be allowed to be constructed
with zero?
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
As was pointed out in #13183, `bevy_mikktspace` is missing it's msrv
from it `Cargo.toml`. This promted me to check the msrv of every
`bevy_*` crate. Closes#13183.
## Solution
- Call `cargo check` with different rust versions on every bevy crate
until it doesn't complain.
- Write down the rust version `cargo check` started working.
## Testing
- Install `cargo-msrv`.
- Run `cargo msrv verify`.
- Rejoice.
---
## Changelog
Every published bevy crate now specifies a MSRV. If your rust toolchain
isn't at least version `1.77.0` You'll likely not be able to compile
most of bevy.
## Migration Guide
If your rust toolchain is bellow version`1.77.0, update.
# Objective
The `Cone` primitive should support meshing.
## Solution
Implement meshing for the `Cone` primitive. The default cone has a
height of 1 and a base radius of 0.5, and is centered at the origin.
An issue with cone meshes is that the tip does not really have a normal
that works, even with duplicated vertices. This PR uses only a single
vertex for the tip, with a normal of zero; this results in an "invalid"
normal that gets ignored by the fragment shader. This seems to be the
only approach we have for perfectly smooth cones. For discussion on the
topic, see #10298 and #5891.
Another thing to note is that the cone uses polar coordinates for the
UVs:
<img
src="https://github.com/bevyengine/bevy/assets/57632562/e101ded9-110a-4ac4-a98d-f1e4d740a24a"
alt="cone" width="400" />
This way, textures are applied as if looking at the cone from above:
<img
src="https://github.com/bevyengine/bevy/assets/57632562/8dea00f1-a283-4bc4-9676-91e8d4adb07a"
alt="texture" width="200" />
<img
src="https://github.com/bevyengine/bevy/assets/57632562/d9d1b5e6-a8ba-4690-b599-904dd85777a1"
alt="cone" width="200" />
# Objective
Sometimes it's nice to iterate over all the coordinate axes using
something like `Vec3::AXES`. This was not available for the
corresponding `Dir` types and now it is.
## Solution
We already have things like `Dir2::X`, `Dir3::Z` and so on, so I just
threw them in an array like the vector types do it. I also slightly
refactored the sphere gizmo code to use `Dir3::AXES` and operate on
directions instead of using `Dir3::new_unchecked`.
## Testing
I looked at the sphere in the `3d_gizmos` example and it seems to work,
so I assume I didn't break anything.
# Objective
- Adds a basic `Extrusion<T: Primitive2d>` shape, suggestion of #10572
## Solution
- Adds `Measured2d` and `Measured3d` traits for getting the
perimeter/area or area/volume of shapes. This allows implementing
`.volume()` and `.area()` for all extrusions `Extrusion<T: Primitive2d +
Measured2d>` within `bevy_math`
- All existing perimeter, area and volume implementations for primitves
have been moved into implementations of `Measured2d` and `Measured3d`
- Shapes should be extruded along the Z-axis since an extrusion of depth
`0.` should be equivalent in everything but name to the base shape
## Caviats
- I am not sure about the naming. `Extrusion<T>` could also be
`Prism<T>` and the `MeasuredNd` could also be something like
`MeasuredPrimitiveNd`. If you have any other suggestions, please fell
free to share them :)
## Future work
This PR adds a basic `Extrusion` shape and does not implement a lot of
things you might want it to. Some of the future possibilities include:
- [ ] bounding for extrusions
- [ ] making extrusions work with gizmos
- [ ] meshing
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- `README.md` is a common file that usually gives an overview of the
folder it is in.
- When on <https://crates.io>, `README.md` is rendered as the main
description.
- Many crates in this repository are lacking `README.md` files, which
makes it more difficult to understand their purpose.
<img width="1552" alt="image"
src="https://github.com/bevyengine/bevy/assets/59022059/78ebf91d-b0c4-4b18-9874-365d6310640f">
- There are also a few inconsistencies with `README.md` files that this
PR and its follow-ups intend to fix.
## Solution
- Create a `README.md` file for all crates that do not have one.
- This file only contains the title of the crate (underscores removed,
proper capitalization, acronyms expanded) and the <https://shields.io>
badges.
- Remove the `readme` field in `Cargo.toml` for `bevy` and
`bevy_reflect`.
- This field is redundant because [Cargo automatically detects
`README.md`
files](https://doc.rust-lang.org/cargo/reference/manifest.html#the-readme-field).
The field is only there if you name it something else, like `INFO.md`.
- Fix capitalization of `bevy_utils`'s `README.md`.
- It was originally `Readme.md`, which is inconsistent with the rest of
the project.
- I created two commits renaming it to `README.md`, because Git appears
to be case-insensitive.
- Expand acronyms in title of `bevy_ptr` and `bevy_utils`.
- In the commit where I created all the new `README.md` files, I
preferred using expanded acronyms in the titles. (E.g. "Bevy Developer
Tools" instead of "Bevy Dev Tools".)
- This commit changes the title of existing `README.md` files to follow
the same scheme.
- I do not feel strongly about this change, please comment if you
disagree and I can revert it.
- Add <https://shields.io> badges to `bevy_time` and `bevy_transform`,
which are the only crates currently lacking them.
---
## Changelog
- Added `README.md` files to all crates missing it.
# Objective
- Update glam version requirement to latest version.
## Solution
- Updated `glam` version requirement from 0.25 to 0.27.
- Updated `encase` and `encase_derive_impl` version requirement from 0.7
to 0.8.
- Updated `hexasphere` version requirement from 10.0 to 12.0.
- Breaking changes from glam changelog:
- [0.26.0] Minimum Supported Rust Version bumped to 1.68.2 for impl
From<bool> for {f32,f64} support.
- [0.27.0] Changed implementation of vector fract method to match the
Rust implementation instead of the GLSL implementation, that is self -
self.trunc() instead of self - self.floor().
---
## Migration Guide
- When using `glam` exports, keep in mind that `vector` `fract()` method
now matches Rust implementation (that is `self - self.trunc()` instead
of `self - self.floor()`). If you want to use the GLSL implementation
you should now use `fract_gl()`.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- People have reported bounding volumes being slower than their existing
solution because it doesn't use SIMD aligned types.
## Solution
- Use `Vec3A` internally for bounding volumes, accepting `Into<Vec3A>`
wherever possible
- Change some code to make it more likely SIMD operations are used.
---
## Changelog
- Use `Vec3A` for 3D bounding volumes and raycasts
## Migration Guide
- 3D bounding volumes now use `Vec3A` types internally, return values
from methods on them now return `Vec3A` instead of `Vec3`
# Objective
Adds a few extra `#[doc(alias)]` entries to the
`bevy_math::primitives::WindingOrder` enum and its variants to improve
searchability.
## Solution
- Add "Orientation" for `WindingOrder` itself
- Add "AntiClockwise" for `CounterClockwise` variant
- Add "Collinear" for `Invalid` variant
These alternate terms seem to be quite common, especially in the
contexts of rendering and collision-detection.
Signed-off-by: Nullicorn <git@nullicorn.me>
# Objective
- General clenup of the primitives in `bevy_math`
- Add `eccentricity()` to `Ellipse`
## Solution
- Moved `Bounded3d` implementation for `Triangle3d` to the `bounded`
module
- Added `eccentricity()` to `Ellipse`
- `Ellipse::semi_major()` and `::semi_minor()` now accept `&self`
instead of `self`
- `Triangle3d::is_degenerate()` actually uses `f32::EPSILON` as
documented
- Added tests for `Triangle3d`-maths
---------
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
Co-authored-by: Miles Silberling-Cook <nth.tensor@gmail.com>
# Objective
Augment Bevy's random sampling capabilities by providing good tools for
producing random directions and rotations.
## Solution
The `rand` crate has a natural tool for providing `Distribution`s whose
output is a type that doesn't require any additional data to sample
values — namely,
[`Standard`](https://docs.rs/rand/latest/rand/distributions/struct.Standard.html).
Here, our existing `ShapeSample` implementations have been put to good
use in providing these, resulting in patterns like the following:
```rust
// Using thread-local rng
let random_direction1: Dir3 = random();
// Using an explicit rng
let random_direction2: Dir3 = rng.gen();
// Using an explicit rng coupled explicitly with Standard
let random_directions: Vec<Dir3> = rng.sample_iter(Standard).take(5).collect();
```
Furthermore, we have introduced a trait `FromRng` which provides sugar
for `rng.gen()` that is more namespace-friendly (in this author's
opinion):
```rust
let random_direction = Dir3::from_rng(rng);
```
The types this has been implemented for are `Dir2`, `Dir3`, `Dir3A`, and
`Quat`. Notably, `Quat` uses `glam`'s implementation rather than an
in-house one, and as a result, `bevy_math`'s "rand" feature now enables
that of `glam`.
---
## Changelog
- Created `standard` submodule in `sampling` to hold implementations and
other items related to the `Standard` distribution.
- "rand" feature of `bevy_math` now enables that of `glam`.
---
## Discussion
From a quick glance at `Quat`'s distribution implementation in `glam`, I
am a bit suspicious, since it is simple and doesn't match any algorithm
that I came across in my research. I will do a little more digging as a
follow-up to this and see if it's actually uniform (maybe even using
those tools I wrote — what a thrill).
As an aside, I'd also like to say that I think
[`Distribution`](https://docs.rs/rand/latest/rand/distributions/trait.Distribution.html)
is really, really good. It integrates with distributions provided
externally (e.g. in `rand` itself and its extensions) along with doing a
good job of isolating the source of randomness, so that output can be
reliably reproduced if need be. Finally, `Distribution::sample_iter` is
quite good for ergonomically acquiring lots of random values. At one
point I found myself writing traits to describe random sampling and
essentially reinvented this one. I just think it's good, and I think
it's worth centralizing around to a significant extent.
# Objective
`AspectRatio` is a newtype of `f32`, so it can implement basic traits;
`Copy`, `Clone`, `Debug`, `PartialEq` and `PartialOrd`.
## Solution
Derive basic traits for `AspectRatio`.
# Objective
- #10572
There is no 3D primitive available for the common shape of a tetrahedron
(3-simplex).
## Solution
This PR introduces a new type to the existing math primitives:
- `Tetrahedron`: a polyhedron composed of four triangular faces, six
straight edges, and four vertices
---
## Changelog
### Added
- `Tetrahedron` primitive to the `bevy_math` crate
- `Tetrahedron` tests (`area`, `volume` methods)
- `impl_reflect!` declaration for `Tetrahedron` in the `bevy_reflect`
crate
# Objective
- There are several redundant imports in the tests and examples that are
not caught by CI because additional flags need to be passed.
## Solution
- Run `cargo check --workspace --tests` and `cargo check --workspace
--examples`, then fix all warnings.
- Add `test-check` to CI, which will be run in the check-compiles job.
This should catch future warnings for tests. Examples are already
checked, but I'm not yet sure why they weren't caught.
## Discussion
- Should the `--tests` and `--examples` flags be added to CI, so this is
caught in the future?
- If so, #12818 will need to be merged first. It was also a warning
raised by checking the examples, but I chose to split off into a
separate PR.
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
- Fixes #[12762](https://github.com/bevyengine/bevy/issues/12762).
## Migration Guide
- `Quat` no longer implements `VectorSpace` as unit quaternions don't
actually form proper vector spaces. If you're absolutely certain that
what you're doing is correct, convert the `Quat` into a `Vec4` and
perform the operations before converting back.
# Objective
When I wrote #12747 I neglected to translate random samples from
triangles back to the point where they originated, so they would be
sampled near the origin instead of at the actual triangle location.
## Solution
Translate by the first vertex location so that the samples follow the
actual triangle.
# Objective
Previously, the `Point` trait, which abstracts all of the operations of
a real vector space, was sitting in the submodule of `bevy_math` for
cubic splines. However, the trait has broader applications than merely
cubic splines, and we should use it when possible to avoid code
duplication when performing vector operations.
## Solution
`Point` has been moved into a new submodule in `bevy_math` named
`common_traits`. Furthermore, it has been renamed to `VectorSpace`,
which is more descriptive, and an additional trait `NormedVectorSpace`
has been introduced to expand the API to cover situations involving
geometry in addition to algebra. Additionally, `VectorSpace` itself now
requires a `ZERO` constant and `Neg`. It also supports a `lerp` function
as an automatic trait method.
Here is what that looks like:
```rust
/// A type that supports the mathematical operations of a real vector space, irrespective of dimension.
/// In particular, this means that the implementing type supports:
/// - Scalar multiplication and division on the right by elements of `f32`
/// - Negation
/// - Addition and subtraction
/// - Zero
///
/// Within the limitations of floating point arithmetic, all the following are required to hold:
/// - (Associativity of addition) For all `u, v, w: Self`, `(u + v) + w == u + (v + w)`.
/// - (Commutativity of addition) For all `u, v: Self`, `u + v == v + u`.
/// - (Additive identity) For all `v: Self`, `v + Self::ZERO == v`.
/// - (Additive inverse) For all `v: Self`, `v - v == v + (-v) == Self::ZERO`.
/// - (Compatibility of multiplication) For all `a, b: f32`, `v: Self`, `v * (a * b) == (v * a) * b`.
/// - (Multiplicative identity) For all `v: Self`, `v * 1.0 == v`.
/// - (Distributivity for vector addition) For all `a: f32`, `u, v: Self`, `(u + v) * a == u * a + v * a`.
/// - (Distributivity for scalar addition) For all `a, b: f32`, `v: Self`, `v * (a + b) == v * a + v * b`.
///
/// Note that, because implementing types use floating point arithmetic, they are not required to actually
/// implement `PartialEq` or `Eq`.
pub trait VectorSpace:
Mul<f32, Output = Self>
+ Div<f32, Output = Self>
+ Add<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Neg
+ Default
+ Debug
+ Clone
+ Copy
{
/// The zero vector, which is the identity of addition for the vector space type.
const ZERO: Self;
/// Perform vector space linear interpolation between this element and another, based
/// on the parameter `t`. When `t` is `0`, `self` is recovered. When `t` is `1`, `rhs`
/// is recovered.
///
/// Note that the value of `t` is not clamped by this function, so interpolating outside
/// of the interval `[0,1]` is allowed.
#[inline]
fn lerp(&self, rhs: Self, t: f32) -> Self {
*self * (1. - t) + rhs * t
}
}
```
```rust
/// A type that supports the operations of a normed vector space; i.e. a norm operation in addition
/// to those of [`VectorSpace`]. Specifically, the implementor must guarantee that the following
/// relationships hold, within the limitations of floating point arithmetic:
/// - (Nonnegativity) For all `v: Self`, `v.norm() >= 0.0`.
/// - (Positive definiteness) For all `v: Self`, `v.norm() == 0.0` implies `v == Self::ZERO`.
/// - (Absolute homogeneity) For all `c: f32`, `v: Self`, `(v * c).norm() == v.norm() * c.abs()`.
/// - (Triangle inequality) For all `v, w: Self`, `(v + w).norm() <= v.norm() + w.norm()`.
///
/// Note that, because implementing types use floating point arithmetic, they are not required to actually
/// implement `PartialEq` or `Eq`.
pub trait NormedVectorSpace: VectorSpace {
/// The size of this element. The return value should always be nonnegative.
fn norm(self) -> f32;
/// The squared norm of this element. Computing this is often faster than computing
/// [`NormedVectorSpace::norm`].
#[inline]
fn norm_squared(self) -> f32 {
self.norm() * self.norm()
}
/// The distance between this element and another, as determined by the norm.
#[inline]
fn distance(self, rhs: Self) -> f32 {
(rhs - self).norm()
}
/// The squared distance between this element and another, as determined by the norm. Note that
/// this is often faster to compute in practice than [`NormedVectorSpace::distance`].
#[inline]
fn distance_squared(self, rhs: Self) -> f32 {
(rhs - self).norm_squared()
}
}
```
Furthermore, this PR also demonstrates the use of the
`NormedVectorSpace` combined API to implement `ShapeSample` for
`Triangle2d` and `Triangle3d` simultaneously. Such deduplication is one
of the drivers for developing these APIs.
---
## Changelog
- `Point` from `cubic_splines` becomes `VectorSpace`, exported as
`bevy::math::VectorSpace`.
- `VectorSpace` requires `Neg` and `VectorSpace::ZERO` in addition to
its existing prerequisites.
- Introduced public traits `bevy::math::NormedVectorSpace` for generic
geometry tasks involving vectors.
- Implemented `ShapeSample` for `Triangle2d` and `Triangle3d`.
## Migration Guide
Since `Point` no longer exists, any projects using it must switch to
`bevy::math::VectorSpace`. Additionally, third-party implementations of
this trait now require the `Neg` trait; the constant `VectorSpace::ZERO`
must be provided as well.
---
## Discussion
### Design considerations
Originally, the `NormedVectorSpace::norm` method was part of a separate
trait `Normed`. However, I think that was probably too broad and, more
importantly, the semantics of having it in `NormedVectorSpace` are much
clearer.
As it currently stands, the API exposed here is pretty minimal, and
there is definitely a lot more that we could do, but there are more
questions to answer along the way. As a silly example, we could
implement `NormedVectorSpace::length` as an alias for
`NormedVectorSpace::norm`, but this overlaps with methods in all of the
glam types, so we would want to make sure that the implementations are
effectively identical (for what it's worth, I think they are already).
### Future directions
One example of something that could belong in the `NormedVectorSpace`
API is normalization. Actually, such a thing previously existed on this
branch before I decided to shelve it because of concerns with namespace
collision. It looked like this:
```rust
/// This element, but normalized to norm 1 if possible. Returns an error when the reciprocal of
/// the element's norm is not finite.
#[inline]
#[must_use]
fn normalize(&self) -> Result<Self, NonNormalizableError> {
let reciprocal = 1.0 / self.norm();
if reciprocal.is_finite() {
Ok(*self * reciprocal)
} else {
Err(NonNormalizableError { reciprocal })
}
}
/// An error indicating that an element of a [`NormedVectorSpace`] was non-normalizable due to having
/// non-finite norm-reciprocal.
#[derive(Debug, Error)]
#[error("Element with norm reciprocal {reciprocal} cannot be normalized")]
pub struct NonNormalizableError {
reciprocal: f32
}
```
With this kind of thing in hand, it might be worth considering
eventually making the passage from vectors to directions fully generic
by employing a wrapper type. (Of course, for our concrete types, we
would leave the existing names in place as aliases.) That is, something
like:
```rust
pub struct NormOne<T>
where T: NormedVectorSpace { //... }
```
Utterly separately, the reason that I implemented `ShapeSample` for
`Triangle2d`/`Triangle3d` was to prototype uniform sampling of abstract
meshes, so that's also a future direction.
---------
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
# Objective
Since it is common to store a pair of width and height as `Vec2`, it
would be useful to have an easy way to instantiate `AspectRatio` from
`Vec2`.
## Solution
Add `impl From<Vec2> for AspectRatio`.
---
## Changelog
- Added `impl From<Vec2> for AspectRatio`
# Objective
Fixes `cargo test -p bevy_math` as in #12729.
## Solution
As described in
[message](https://github.com/bevyengine/bevy/issues/12729#issuecomment-2022197944)
Added workaround `bevy_math = { path = ".", version = "0.14.0-dev",
features = ["approx"] }` to `bevy_math`'s `dev-dependencies`
---------
Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
# Objective
- Fixes#12712
## Solution
- Move the `float_ord.rs` file to `bevy_math`
- Change any `bevy_utils::FloatOrd` statements to `bevy_math::FloatOrd`
---
## Changelog
- Moved `FloatOrd` from `bevy_utils` to `bevy_math`
## Migration Guide
- References to `bevy_utils::FloatOrd` should be changed to
`bevy_math::FloatOrd`
# Objective
Resolves#3824. `unsafe` code should be the exception, not the norm in
Rust. It's obviously needed for various use cases as it's interfacing
with platforms and essentially running the borrow checker at runtime in
the ECS, but the touted benefits of Bevy is that we are able to heavily
leverage Rust's safety, and we should be holding ourselves accountable
to that by minimizing our unsafe footprint.
## Solution
Deny `unsafe_code` workspace wide. Add explicit exceptions for the
following crates, and forbid it in almost all of the others.
* bevy_ecs - Obvious given how much unsafe is needed to achieve
performant results
* bevy_ptr - Works with raw pointers, even more low level than bevy_ecs.
* bevy_render - due to needing to integrate with wgpu
* bevy_window - due to needing to integrate with raw_window_handle
* bevy_utils - Several unsafe utilities used by bevy_ecs. Ideally moved
into bevy_ecs instead of made publicly usable.
* bevy_reflect - Required for the unsafe type casting it's doing.
* bevy_transform - for the parallel transform propagation
* bevy_gizmos - For the SystemParam impls it has.
* bevy_assets - To support reflection. Might not be required, not 100%
sure yet.
* bevy_mikktspace - due to being a conversion from a C library. Pending
safe rewrite.
* bevy_dynamic_plugin - Inherently unsafe due to the dynamic loading
nature.
Several uses of unsafe were rewritten, as they did not need to be using
them:
* bevy_text - a case of `Option::unchecked` could be rewritten as a
normal for loop and match instead of an iterator.
* bevy_color - the Pod/Zeroable implementations were replaceable with
bytemuck's derive macros.
# Objective
- #10572
There is no 2D primitive available for the common shape of an annulus
(ring).
## Solution
This PR introduces a new type to the existing math primitives:
- `Annulus`: the region between two concentric circles
---
## Changelog
### Added
- `Annulus` primitive to the `bevy_math` crate
- `Annulus` tests (`diameter`, `thickness`, `area`, `perimeter` and
`closest_point` methods)
---------
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
# Objective
Currently the built docs only shows the logo and favicon for the top
level `bevy` crate. This makes views like
https://docs.rs/bevy_ecs/latest/bevy_ecs/ look potentially unrelated to
the project at first glance.
## Solution
Reproduce the docs attributes for every crate that Bevy publishes.
Ideally this would be done with some workspace level Cargo.toml control,
but AFAICT, such support does not exist.
# Context
[GitHub Discussion
Link](https://github.com/bevyengine/bevy/discussions/12506)
# Objective
- **Clarity:** More explicit representation of a common geometric
primitive.
- **Convenience:** Provide methods tailored to 3D triangles (area,
perimeters, etc.).
## Solution
- Adding the `Triangle3d` primitive into the `bevy_math` crate.
---
## Changelog
### Added
- `Triangle3d` primitive to the `bevy_math` crate
### Changed
- `Triangle2d::reverse`: the first and last vertices are swapped instead
of the second and third.
---------
Co-authored-by: Miles Silberling-Cook <NthTensor@users.noreply.github.com>
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
# 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.
# Objective
Currently in order to retrieve the inner values from direction types is
that you need to use the `Deref` trait or `From`/`Into`. `Deref` that is
currently implemented is an anti-pattern that I believe should be less
relied upon.
This pull-request add getters for retrieving the inner values for
direction types.
Advantages of getters:
- Let rust-analyzer to list out available methods for users to
understand better to on how to get the inner value. (This happens to me.
I really don't know how to get the value until I look through the source
code.)
- They are simple.
- Generally won't be ambiguous in most context. Traits such as
`From`/`Into` will require fully qualified syntax from time to time.
- Unsurprising result.
Disadvantages of getters:
- More verbose
Advantages of deref polymorphism:
- You reduce boilerplate for getting the value and call inner methods
by:
```rust
let dir = Dir3::new(Vec3::UP).unwrap();
// getting value
let value = *dir;
// instead of using getters
let value = dir.vec3();
// calling methods for the inner vector
dir.xy();
// instead of using getters
dir.vec3().xy();
```
Disadvantages of deref polymorphism:
- When under more level of indirection, it will requires more
dereferencing which will get ugly in some part:
```rust
// getting value
let value = **dir;
// instead of using getters
let value = dir.vec3();
// calling methods for the inner vector
dir.xy();
// instead of using getters
dir.vec3().xy();
```
[More detail
here](https://rust-unofficial.github.io/patterns/anti_patterns/deref.html).
Edit: Update information for From/Into trait.
Edit: Advantages and disadvantages.
## Solution
Add `vec2` method for Dir2.
Add `vec3` method for Dir3.
Add `vec3a` method for Dir3A.
# Objective
Give easy methods for uniform point sampling in a variety of primitive
shapes (particularly useful for circles and spheres) because in a lot of
cases its quite easy to get wrong (non-uniform).
## Solution
Added the `ShapeSample` trait to `bevy_math` and implemented it for
`Circle`, `Sphere`, `Rectangle`, `Cuboid`, `Cylinder`, `Capsule2d` and
`Capsule3d`. There are a few other shapes it would be reasonable to
implement for like `Triangle`, `Ellipse` and `Torus` but I'm not
immediately sure how these would be implemented (other than rejection
which could be the best method, and could be more performant than some
of the solutions in this pr I'm not sure). This exposes the
`sample_volume` and `sample_surface` methods to get both a random point
from its interior or its surface. EDIT: Renamed `sample_volume` to
`sample_interior` and `sample_surface` to `sample_boundary`
This brings in `rand` as a default optional dependency (without default
features), and the methods take `&mut impl Rng` which allows them to use
any random source implementing `RngCore`.
---
## Changelog
### Added
Added the methods `sample_interior` and `sample_boundary` to a variety
of primitive shapes providing easy uniform point sampling.
# Objective
Add a `scale_around_center` method to the `BoundingVolume` trait, as per
#12130.
## Solution
Added `scale_around_center` to the `BoundingVolume` trait, implemented
in `Aabb2d`, `Aabb3d`, `BoundingCircle`, and `BoundingSphere` (with
tests).
# Objective
Rotating vectors is a very common task. It is required for a variety of
things both within Bevy itself and in many third party plugins, for
example all over physics and collision detection, and for things like
Bevy's bounding volumes and several gizmo implementations.
For 3D, we can do this using a `Quat`, but for 2D, we do not have a
clear and efficient option. `Mat2` can be used for rotating vectors if
created using `Mat2::from_angle`, but this is not obvious to many users,
it doesn't have many rotation helpers, and the type does not give any
guarantees that it represents a valid rotation.
We should have a proper type for 2D rotations. In addition to allowing
for potential optimization, it would allow us to have a consistent and
explicitly documented representation used throughout the engine, i.e.
counterclockwise and in radians.
## Representation
The mathematical formula for rotating a 2D vector is the following:
```
new_x = x * cos - y * sin
new_y = x * sin + y * cos
```
Here, `sin` and `cos` are the sine and cosine of the rotation angle.
Computing these every time when a vector needs to be rotated can be
expensive, so the rotation shouldn't be just an `f32` angle. Instead, it
is often more efficient to represent the rotation using the sine and
cosine of the angle instead of storing the angle itself. This can be
freely passed around and reused without unnecessary computations.
The two options are either a 2x2 rotation matrix or a unit complex
number where the cosine is the real part and the sine is the imaginary
part. These are equivalent for the most part, but the unit complex
representation is a bit more memory efficient (two `f32`s instead of
four), so I chose that. This is like Nalgebra's
[`UnitComplex`](https://docs.rs/nalgebra/latest/nalgebra/geometry/type.UnitComplex.html)
type, which can be used for the
[`Rotation2`](https://docs.rs/nalgebra/latest/nalgebra/geometry/type.Rotation2.html)
type.
## Implementation
Add a `Rotation2d` type represented as a unit complex number:
```rust
/// A counterclockwise 2D rotation in radians.
///
/// The rotation angle is wrapped to be within the `]-pi, pi]` range.
pub struct Rotation2d {
/// The cosine of the rotation angle in radians.
///
/// This is the real part of the unit complex number representing the rotation.
pub cos: f32,
/// The sine of the rotation angle in radians.
///
/// This is the imaginary part of the unit complex number representing the rotation.
pub sin: f32,
}
```
Using it is similar to using `Quat`, but in 2D:
```rust
let rotation = Rotation2d::radians(PI / 2.0);
// Rotate vector (also works on Direction2d!)
assert_eq!(rotation * Vec2::X, Vec2::Y);
// Get angle as degrees
assert_eq!(rotation.as_degrees(), 90.0);
// Getting sin and cos is free
let (sin, cos) = rotation.sin_cos();
// "Subtract" rotations
let rotation2 = Rotation2d::FRAC_PI_4; // there are constants!
let diff = rotation * rotation2.inverse();
assert_eq!(diff.as_radians(), PI / 4.0);
// This is equivalent to the above
assert_eq!(rotation2.angle_between(rotation), PI / 4.0);
// Lerp
let rotation1 = Rotation2d::IDENTITY;
let rotation2 = Rotation2d::FRAC_PI_2;
let result = rotation1.lerp(rotation2, 0.5);
assert_eq!(result.as_radians(), std::f32::consts::FRAC_PI_4);
// Slerp
let rotation1 = Rotation2d::FRAC_PI_4);
let rotation2 = Rotation2d::degrees(-180.0); // we can use degrees too!
let result = rotation1.slerp(rotation2, 1.0 / 3.0);
assert_eq!(result.as_radians(), std::f32::consts::FRAC_PI_2);
```
There's also a `From<f32>` implementation for `Rotation2d`, which means
that methods can still accept radians as floats if the argument uses
`impl Into<Rotation2d>`. This means that adding `Rotation2d` shouldn't
even be a breaking change.
---
## Changelog
- Added `Rotation2d`
- Bounding volume methods now take an `impl Into<Rotation2d>`
- Gizmo methods with rotation now take an `impl Into<Rotation2d>`
## Future use cases
- Collision detection (a type like this is quite essential considering
how common vector rotations are)
- `Transform` helpers (e.g. return a 2D rotation about the Z axis from a
`Transform`)
- The rotation used for `Transform2d` (#8268)
- More gizmos, maybe meshes... everything in 2D that uses rotation
---------
Co-authored-by: Tristan Guichaoua <33934311+tguichaoua@users.noreply.github.com>
Co-authored-by: Robert Walter <robwalter96@gmail.com>
Co-authored-by: IQuick 143 <IQuick143cz@gmail.com>
# Objective
Fix missing `TextBundle` (and many others) which are present in the main
crate as default features but optional in the sub-crate. See:
- https://docs.rs/bevy/0.13.0/bevy/ui/node_bundles/index.html
- https://docs.rs/bevy_ui/0.13.0/bevy_ui/node_bundles/index.html
~~There are probably other instances in other crates that I could track
down, but maybe "all-features = true" should be used by default in all
sub-crates? Not sure.~~ (There were many.) I only noticed this because
rust-analyzer's "open docs" features takes me to the sub-crate, not the
main one.
## Solution
Add "all-features = true" to docs.rs metadata for crates that use
features.
## Changelog
### Changed
- Unified features documented on docs.rs between main crate and
sub-crates
# Objective
Fixes#12310.
#11681 added transformations for bounding volumes, but I accidentally
only added a note in the docs about repeated rotations for `Aabb2d` and
not `Aabb3d`.
## Solution
Copy the docs over to `Aabb3d`.
# Objective
Make it straightforward to translate and rotate bounding volumes.
## Solution
Add `translate_by`/`translated_by`, `rotate_by`/`rotated_by`,
`transform_by`/`transformed_by` methods to the `BoundingVolume` trait.
This follows the naming used for mesh transformations (see #11454 and
#11675).
---
## Changelog
- Added `translate_by`/`translated_by`, `rotate_by`/`rotated_by`,
`transform_by`/`transformed_by` methods to the `BoundingVolume` trait
and implemented them for the bounding volumes
- Renamed `Position` associated type to `Translation`
---------
Co-authored-by: Mateusz Wachowiak <mateusz_wachowiak@outlook.com>
# Objective
`Dir3` and `Dir3A` can be rotated using `Quat`s. However, if enough
floating point error accumulates or (more commonly) the rotation itself
is degenerate (like not normalized), the resulting direction can also
become denormalized.
Currently, with debug assertions enabled, it panics in these cases with
the message `rotated.is_normalized()`. This error message is unclear,
doesn't give information about *how* it is denormalized (like is the
length too large, NaN, or something else), and is overall not very
helpful. Panicking for small-ish error might also be a bit too strict,
and has lead to unwanted crashes in crates like `bevy_xpbd` (although it
has also helped in finding actual bugs).
The error message should be clearer and give more context, and it
shouldn't cause unwanted crashes.
## Solution
Change the `debug_assert!` to a warning for small error with a (squared
length) threshold of 2e-4 and a panic for clear error with a threshold
of 2e-2. The warnings mention the direction type and the length of the
denormalized vector.
Here's what the error and warning look like:
```
Error: `Dir3` is denormalized after rotation. The length is 1.014242.
```
```
Warning: `Dir3A` is denormalized after rotation. The length is 1.0001414.
```
I gave the same treatment to `new_unchecked`:
```
Error: The vector given to `Dir3::new_unchecked` is not normalized. The length is 1.014242.
```
```
Warning: The vector given to `Dir3A::new_unchecked` is not normalized. The length is 1.0001414.
```
---
## Discussion
### Threshold values
The thresholds are somewhat arbitrary. 2e-4 is what Glam uses for the
squared length in `is_normalized` (after I corrected it in
bitshifter/glam-rs#480), and 2e-2 is just what I thought could be a
clear sign of something being critically wrong. I can definitely tune
them if there are better thresholds though.
### Logging
`bevy_math` doesn't have `bevy_log`, so we can't use `warn!` or
`error!`. This is why I made it use just `eprintln!` and `panic!` for
now. Let me know if there's a better way of logging errors in
`bevy_math`.
# Objective
Bevy's `Dir3` and `Dir3A` only implement `Mul<f32>` and not vice versa,
and `Dir2` can not be multiplied by `f32` at all. They all should
implement multiplication both ways, just like Glam's vector types.
## Solution
Implement `Mul<Dir2>`, `Mul<Dir3>`, and `Mul<Dir3A>` for `f32`, and
`Mul<f32>` for `Dir2`.
# Objective
Split up from #12017, rename Bevy's direction types.
Currently, Bevy has the `Direction2d`, `Direction3d`, and `Direction3dA`
types, which provide a type-level guarantee that their contained vectors
remain normalized. They can be very useful for a lot of APIs for safety,
explicitness, and in some cases performance, as they can sometimes avoid
unnecessary normalizations.
However, many consider them to be inconvenient to use, and opt for
standard vector types like `Vec3` because of this. One reason is that
the direction type names are a bit long and can be annoying to write (of
course you can use autocomplete, but just typing `Vec3` is still nicer),
and in some intances, the extra characters can make formatting worse.
The naming is also inconsistent with Glam's shorter type names, and
results in names like `Direction3dA`, which (in my opinion) are
difficult to read and even a bit ugly.
This PR proposes renaming the types to `Dir2`, `Dir3`, and `Dir3A`.
These names are nice and easy to write, consistent with Glam, and work
well for variants like the SIMD aligned `Dir3A`. As a bonus, it can also
result in nicer formatting in a lot of cases, which can be seen from the
diff of this PR.
Some examples of what it looks like: (copied from #12017)
```rust
// Before
let ray_cast = RayCast2d::new(Vec2::ZERO, Direction2d::X, 5.0);
// After
let ray_cast = RayCast2d::new(Vec2::ZERO, Dir2::X, 5.0);
```
```rust
// Before (an example using Bevy XPBD)
let hit = spatial_query.cast_ray(
Vec3::ZERO,
Direction3d::X,
f32::MAX,
true,
SpatialQueryFilter::default(),
);
// After
let hit = spatial_query.cast_ray(
Vec3::ZERO,
Dir3::X,
f32::MAX,
true,
SpatialQueryFilter::default(),
);
```
```rust
// Before
self.circle(
Vec3::new(0.0, -2.0, 0.0),
Direction3d::Y,
5.0,
Color::TURQUOISE,
);
// After (formatting is collapsed in this case)
self.circle(Vec3::new(0.0, -2.0, 0.0), Dir3::Y, 5.0, Color::TURQUOISE);
```
## Solution
Rename `Direction2d`, `Direction3d`, and `Direction3dA` to `Dir2`,
`Dir3`, and `Dir3A`.
---
## Migration Guide
The `Direction2d` and `Direction3d` types have been renamed to `Dir2`
and `Dir3`.
## Additional Context
This has been brought up on the Discord a few times, and we had a small
[poll](https://discord.com/channels/691052431525675048/1203087353850364004/1212465038711984158)
on this. `Dir2`/`Dir3`/`Dir3A` was quite unanimously chosen as the best
option, but of course it was a very small poll and inconclusive, so
other opinions are certainly welcome too.
---------
Co-authored-by: IceSentry <c.giguere42@gmail.com>
# Objective
Improve the `bevy::math::cubic_splines` module by making it more
flexible and adding new curve types.
Closes#10220
## Solution
Added new spline types and improved existing
---
## Changelog
### Added
- `CubicNurbs` rational cubic curve generator, allows setting the knot
vector and weights associated with every point
- `LinearSpline` curve generator, allows generating a linearly
interpolated curve segment
- Ability to push additional cubic segments to `CubicCurve`
- `IntoIterator` and `Extend` implementations for `CubicCurve`
### Changed
- `Point` trait has been implemented for more types: `Quat` and `Vec4`.
- `CubicCurve::coefficients` was moved to `CubicSegment::coefficients`
because the function returns `CubicSegment`, so it seems logical to be
associated with `CubicSegment` instead. The method is still not public.
### Fixed
- `CubicBSpline::new` was referencing Cardinal spline instead of
B-Spline
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: IQuick 143 <IQuick143cz@gmail.com>
Co-authored-by: Miles Silberling-Cook <nth.tensor@gmail.com>
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
# Objective
Split up from #12017, add an aligned version of `Direction3d` for SIMD,
and move direction types out of `primitives`.
## Solution
Add `Direction3dA` and move direction types into a new `direction`
module.
---
## Migration Guide
The `Direction2d`, `Direction3d`, and `InvalidDirectionError` types have
been moved out of `bevy::math::primitives`.
Before:
```rust
use bevy::math::primitives::Direction3d;
```
After:
```rust
use bevy::math::Direction3d;
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Fixes#12016.
Bump version after release
This PR has been auto-generated
Co-authored-by: Bevy Auto Releaser <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: François <mockersf@gmail.com>
# Objective
- I hated having to do `Cuboid::new(1.0, 1.0, 1.0)` or
`Cuboid::from_size(Vec3::splat(1.0))` when there should be a much easier
way to do this.
## Solution
- Implemented a `from_length()` method that only takes in a single
float, and constructs a primitive of equal size in all directions.
- Ex:
```rs
// These:
Cuboid::new(1.0, 1.0, 1.0);
Cuboid::from_size(Vec3::splat(1.0));
// Are equivalent to this:
Cuboid::from_length(1.0);
```
- For the rest of the changed primitives:
```rs
Rectangle::from_length(1.0);
Plane3d::default().mesh().from_length(1.0);
```
# Objective
Split up from #11007, fixing most of the remaining work for #10569.
Implement `Meshable` for `Cuboid`, `Sphere`, `Cylinder`, `Capsule`,
`Torus`, and `Plane3d`. This covers all shapes that Bevy has mesh
structs for in `bevy_render::mesh::shapes`.
`Cone` and `ConicalFrustum` are new shapes, so I can add them in a
follow-up, or I could just add them here directly if that's preferrable.
## Solution
Implement `Meshable` for `Cuboid`, `Sphere`, `Cylinder`, `Capsule`,
`Torus`, and `Plane3d`.
The logic is mostly just a copy of the the existing `bevy_render`
shapes, but `Plane3d` has a configurable surface normal that affects the
orientation. Some property names have also been changed to be more
consistent.
The default values differ from the old shapes to make them a bit more
logical:
- Spheres now have a radius of 0.5 instead of 1.0. The default capsule
is equivalent to the default cylinder with the sphere's halves glued on.
- The inner and outer radius of the torus are now 0.5 and 1.0 instead of
0.5 and 1.5 (i.e. the new minor and major radii are 0.25 and 0.75). It's
double the width of the default cuboid, half of its height, and the
default sphere matches the size of the hole.
- `Cuboid` is 1x1x1 by default unlike the dreaded `Box` which is 2x1x1.
Before, with "old" shapes:
![old](https://github.com/bevyengine/bevy/assets/57632562/733f3dda-258c-4491-8152-9829e056a1a3)
Now, with primitive meshing:
![new](https://github.com/bevyengine/bevy/assets/57632562/5a1af14f-bb98-401d-82cf-de8072fea4ec)
I only changed the `3d_shapes` example to use primitives for now. I can
change them all in this PR or a follow-up though, whichever way is
preferrable.
### Sphere API
Spheres have had separate `Icosphere` and `UVSphere` structs, but with
primitives we only have one `Sphere`.
We need to handle this with builders:
```rust
// Existing structs
let ico = Mesh::try_from(Icophere::default()).unwrap();
let uv = Mesh::from(UVSphere::default());
// Primitives
let ico = Sphere::default().mesh().ico(5).unwrap();
let uv = Sphere::default().mesh().uv(32, 18);
```
We could add methods on `Sphere` directly to skip calling `.mesh()`.
I also added a `SphereKind` enum that can be used with the `kind`
method:
```rust
let ico = Sphere::default()
.mesh()
.kind(SphereKind::Ico { subdivisions: 8 })
.build();
```
The default mesh for a `Sphere` is an icosphere with 5 subdivisions
(like the default `Icosphere`).
---
## Changelog
- Implement `Meshable` and `Default` for `Cuboid`, `Sphere`, `Cylinder`,
`Capsule`, `Torus`, and `Plane3d`
- Use primitives in `3d_shapes` example
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>