diff --git a/benches/benches/bevy_math/bezier.rs b/benches/benches/bevy_math/bezier.rs index a95cb4a821..70f3cb6703 100644 --- a/benches/benches/bevy_math/bezier.rs +++ b/benches/benches/bevy_math/bezier.rs @@ -32,7 +32,7 @@ fn segment_ease(c: &mut Criterion) { fn curve_position(c: &mut Criterion) { /// A helper function that benchmarks calling [`CubicCurve::position()`] over a generic [`VectorSpace`]. - fn bench_curve( + fn bench_curve>( group: &mut BenchmarkGroup, name: &str, curve: CubicCurve

, diff --git a/crates/bevy_animation/src/gltf_curves.rs b/crates/bevy_animation/src/gltf_curves.rs index 688011a32c..593ca04d2e 100644 --- a/crates/bevy_animation/src/gltf_curves.rs +++ b/crates/bevy_animation/src/gltf_curves.rs @@ -55,7 +55,7 @@ pub struct CubicKeyframeCurve { impl Curve for CubicKeyframeCurve where - V: VectorSpace, + V: VectorSpace, { #[inline] fn domain(&self) -> Interval { @@ -179,7 +179,7 @@ pub struct WideLinearKeyframeCurve { impl IterableCurve for WideLinearKeyframeCurve where - T: VectorSpace, + T: VectorSpace, { #[inline] fn domain(&self) -> Interval { @@ -289,7 +289,7 @@ pub struct WideCubicKeyframeCurve { impl IterableCurve for WideCubicKeyframeCurve where - T: VectorSpace, + T: VectorSpace, { #[inline] fn domain(&self) -> Interval { @@ -406,7 +406,7 @@ fn cubic_spline_interpolation( step_duration: f32, ) -> T where - T: VectorSpace, + T: VectorSpace, { let coeffs = (vec4(2.0, 1.0, -2.0, 1.0) * lerp + vec4(-3.0, -2.0, 3.0, -1.0)) * lerp; value_start * (coeffs.x * lerp + 1.0) @@ -415,7 +415,7 @@ where + tangent_in_end * step_duration * lerp * coeffs.w } -fn cubic_spline_interpolate_slices<'a, T: VectorSpace>( +fn cubic_spline_interpolate_slices<'a, T: VectorSpace>( width: usize, first: &'a [T], second: &'a [T], diff --git a/crates/bevy_color/src/lib.rs b/crates/bevy_color/src/lib.rs index 96770c96e8..d5d72d1544 100644 --- a/crates/bevy_color/src/lib.rs +++ b/crates/bevy_color/src/lib.rs @@ -262,6 +262,7 @@ macro_rules! impl_componentwise_vector_space { } impl bevy_math::VectorSpace for $ty { + type Scalar = f32; const ZERO: Self = Self { $($element: 0.0,)+ }; diff --git a/crates/bevy_ecs/src/entity/map_entities.rs b/crates/bevy_ecs/src/entity/map_entities.rs index 3dac2fa749..647bde983a 100644 --- a/crates/bevy_ecs/src/entity/map_entities.rs +++ b/crates/bevy_ecs/src/entity/map_entities.rs @@ -358,7 +358,10 @@ mod tests { // Next allocated entity should be a further generation on the same index let entity = world.spawn_empty().id(); assert_eq!(entity.index(), dead_ref.index()); - assert!(entity.generation() > dead_ref.generation()); + assert!(entity + .generation() + .cmp_approx(&dead_ref.generation()) + .is_gt()); } #[test] @@ -373,7 +376,10 @@ mod tests { // Next allocated entity should be a further generation on the same index let entity = world.spawn_empty().id(); assert_eq!(entity.index(), dead_ref.index()); - assert!(entity.generation() > dead_ref.generation()); + assert!(entity + .generation() + .cmp_approx(&dead_ref.generation()) + .is_gt()); } #[test] diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 617f945bb7..0e22fddbc8 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -185,29 +185,6 @@ impl SparseSetIndex for EntityRow { /// /// This should be treated as a opaque identifier, and its internal representation may be subject to change. /// -/// # Ordering -/// -/// [`EntityGeneration`] implements [`Ord`]. -/// Generations that are later will be [`Greater`](core::cmp::Ordering::Greater) than earlier ones. -/// -/// ``` -/// # use bevy_ecs::entity::EntityGeneration; -/// assert!(EntityGeneration::FIRST < EntityGeneration::FIRST.after_versions(400)); -/// let (aliased, did_alias) = EntityGeneration::FIRST.after_versions(400).after_versions_and_could_alias(u32::MAX); -/// assert!(did_alias); -/// assert!(EntityGeneration::FIRST < aliased); -/// ``` -/// -/// Ordering will be incorrect for distant generations: -/// -/// ``` -/// # use bevy_ecs::entity::EntityGeneration; -/// // This ordering is wrong! -/// assert!(EntityGeneration::FIRST > EntityGeneration::FIRST.after_versions(400 + (1u32 << 31))); -/// ``` -/// -/// This strange behavior needed to account for aliasing. -/// /// # Aliasing /// /// Internally [`EntityGeneration`] wraps a `u32`, so it can't represent *every* possible generation. @@ -235,6 +212,9 @@ impl EntityGeneration { /// Represents the first generation of an [`EntityRow`]. pub const FIRST: Self = Self(0); + /// Non-wrapping difference between two generations after which a signed interpretation becomes negative. + const DIFF_MAX: u32 = 1u32 << 31; + /// Gets some bits that represent this value. /// The bits are opaque and should not be regarded as meaningful. #[inline(always)] @@ -266,18 +246,48 @@ impl EntityGeneration { let raw = self.0.overflowing_add(versions); (Self(raw.0), raw.1) } -} -impl PartialOrd for EntityGeneration { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for EntityGeneration { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - let diff = self.0.wrapping_sub(other.0); - (1u32 << 31).cmp(&diff) + /// Compares two generations. + /// + /// Generations that are later will be [`Greater`](core::cmp::Ordering::Greater) than earlier ones. + /// + /// ``` + /// # use bevy_ecs::entity::EntityGeneration; + /// # use core::cmp::Ordering; + /// let later_generation = EntityGeneration::FIRST.after_versions(400); + /// assert_eq!(EntityGeneration::FIRST.cmp_approx(&later_generation), Ordering::Less); + /// + /// let (aliased, did_alias) = EntityGeneration::FIRST.after_versions(400).after_versions_and_could_alias(u32::MAX); + /// assert!(did_alias); + /// assert_eq!(EntityGeneration::FIRST.cmp_approx(&aliased), Ordering::Less); + /// ``` + /// + /// Ordering will be incorrect and [non-transitive](https://en.wikipedia.org/wiki/Transitive_relation) + /// for distant generations: + /// + /// ```should_panic + /// # use bevy_ecs::entity::EntityGeneration; + /// # use core::cmp::Ordering; + /// let later_generation = EntityGeneration::FIRST.after_versions(3u32 << 31); + /// let much_later_generation = later_generation.after_versions(3u32 << 31); + /// + /// // while these orderings are correct and pass assertions... + /// assert_eq!(EntityGeneration::FIRST.cmp_approx(&later_generation), Ordering::Less); + /// assert_eq!(later_generation.cmp_approx(&much_later_generation), Ordering::Less); + /// + /// // ... this ordering is not and the assertion fails! + /// assert_eq!(EntityGeneration::FIRST.cmp_approx(&much_later_generation), Ordering::Less); + /// ``` + /// + /// Because of this, `EntityGeneration` does not implement `Ord`/`PartialOrd`. + #[inline] + pub const fn cmp_approx(&self, other: &Self) -> core::cmp::Ordering { + use core::cmp::Ordering; + match self.0.wrapping_sub(other.0) { + 0 => Ordering::Equal, + 1..Self::DIFF_MAX => Ordering::Greater, + _ => Ordering::Less, + } } } @@ -1422,7 +1432,10 @@ mod tests { // The very next entity allocated should be a further generation on the same index let next_entity = entities.alloc(); assert_eq!(next_entity.index(), entity.index()); - assert!(next_entity.generation() > entity.generation().after_versions(GENERATIONS)); + assert!(next_entity + .generation() + .cmp_approx(&entity.generation().after_versions(GENERATIONS)) + .is_gt()); } #[test] @@ -1609,6 +1622,24 @@ mod tests { } } + #[test] + fn entity_generation_is_approximately_ordered() { + use core::cmp::Ordering; + + let old = EntityGeneration::FIRST; + let middle = old.after_versions(1); + let younger_before_ord_wrap = middle.after_versions(EntityGeneration::DIFF_MAX); + let younger_after_ord_wrap = younger_before_ord_wrap.after_versions(1); + + assert_eq!(middle.cmp_approx(&old), Ordering::Greater); + assert_eq!(middle.cmp_approx(&middle), Ordering::Equal); + assert_eq!(middle.cmp_approx(&younger_before_ord_wrap), Ordering::Less); + assert_eq!( + middle.cmp_approx(&younger_after_ord_wrap), + Ordering::Greater + ); + } + #[test] fn entity_debug() { let entity = Entity::from_raw(EntityRow::new(NonMaxU32::new(42).unwrap())); diff --git a/crates/bevy_math/src/common_traits.rs b/crates/bevy_math/src/common_traits.rs index 4e127f4026..b249b34618 100644 --- a/crates/bevy_math/src/common_traits.rs +++ b/crates/bevy_math/src/common_traits.rs @@ -1,6 +1,6 @@ //! This module contains abstract mathematical traits shared by types used in `bevy_math`. -use crate::{ops, Dir2, Dir3, Dir3A, Quat, Rot2, Vec2, Vec3, Vec3A, Vec4}; +use crate::{ops, DVec2, DVec3, DVec4, Dir2, Dir3, Dir3A, Quat, Rot2, Vec2, Vec3, Vec3A, Vec4}; use core::{ fmt::Debug, ops::{Add, Div, Mul, Neg, Sub}, @@ -9,7 +9,7 @@ use variadics_please::all_tuples_enumerated; /// 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` +/// - Scalar multiplication and division on the right by elements of `Self::Scalar` /// - Negation /// - Addition and subtraction /// - Zero @@ -19,16 +19,16 @@ use variadics_please::all_tuples_enumerated; /// - (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`. +/// - (Compatibility of multiplication) For all `a, b: Self::Scalar`, `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`. +/// - (Distributivity for vector addition) For all `a: Self::Scalar`, `u, v: Self`, `(u + v) * a == u * a + v * a`. +/// - (Distributivity for scalar addition) For all `a, b: Self::Scalar`, `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 - + Div + Mul + + Div + Add + Sub + Neg @@ -37,6 +37,9 @@ pub trait VectorSpace: + Clone + Copy { + /// The scalar type of this vector space. + type Scalar: ScalarField; + /// The zero vector, which is the identity of addition for the vector space type. const ZERO: Self; @@ -47,29 +50,99 @@ pub trait VectorSpace: /// Note that the value of `t` is not clamped by this function, so extrapolating outside /// of the interval `[0,1]` is allowed. #[inline] - fn lerp(self, rhs: Self, t: f32) -> Self { - self * (1. - t) + rhs * t + fn lerp(self, rhs: Self, t: Self::Scalar) -> Self { + self * (Self::Scalar::ONE - t) + rhs * t } } impl VectorSpace for Vec4 { + type Scalar = f32; const ZERO: Self = Vec4::ZERO; } impl VectorSpace for Vec3 { + type Scalar = f32; const ZERO: Self = Vec3::ZERO; } impl VectorSpace for Vec3A { + type Scalar = f32; const ZERO: Self = Vec3A::ZERO; } impl VectorSpace for Vec2 { + type Scalar = f32; const ZERO: Self = Vec2::ZERO; } -impl VectorSpace for f32 { +impl VectorSpace for DVec4 { + type Scalar = f64; + const ZERO: Self = DVec4::ZERO; +} + +impl VectorSpace for DVec3 { + type Scalar = f64; + const ZERO: Self = DVec3::ZERO; +} + +impl VectorSpace for DVec2 { + type Scalar = f64; + const ZERO: Self = DVec2::ZERO; +} + +// Every scalar field is a 1-dimensional vector space over itself. +impl VectorSpace for T { + type Scalar = Self; + const ZERO: Self = Self::ZERO; +} + +/// A type that supports the operations of a scalar field. An implementation should support: +/// - Addition and subtraction +/// - Multiplication and division +/// - Negation +/// - Zero (additive identity) +/// - One (multiplicative identity) +/// +/// 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`. +/// - (Associativity of multiplication) For all `u, v, w: Self`, `(u * v) * w == u * (v * w)`. +/// - (Commutativity of multiplication) For all `u, v: Self`, `u * v == v * u`. +/// - (Multiplicative identity) For all `v: Self`, `v * Self::ONE == v`. +/// - (Multiplicative inverse) For all `v: Self`, `v / v == v * v.inverse() == Self::ONE`. +/// - (Distributivity over addition) For all `a, b: Self`, `u, v: Self`, `(u + v) * a == u * a + v * a`. +pub trait ScalarField: + Mul + + Div + + Add + + Sub + + Neg + + Default + + Debug + + Clone + + Copy +{ + /// The additive identity. + const ZERO: Self; + /// The multiplicative identity. + const ONE: Self; + + /// The multiplicative inverse of this element. This is equivalent to `1.0 / self`. + fn recip(self) -> Self { + Self::ONE / self + } +} + +impl ScalarField for f32 { const ZERO: Self = 0.0; + const ONE: Self = 1.0; +} + +impl ScalarField for f64 { + const ZERO: Self = 0.0; + const ONE: Self = 1.0; } /// A type consisting of formal sums of elements from `V` and `W`. That is, @@ -84,24 +157,24 @@ impl VectorSpace for f32 { #[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))] pub struct Sum(pub V, pub W); -impl Mul for Sum +impl Mul for Sum where - V: VectorSpace, - W: VectorSpace, + V: VectorSpace, + W: VectorSpace, { type Output = Self; - fn mul(self, rhs: f32) -> Self::Output { + fn mul(self, rhs: F) -> Self::Output { Sum(self.0 * rhs, self.1 * rhs) } } -impl Div for Sum +impl Div for Sum where - V: VectorSpace, - W: VectorSpace, + V: VectorSpace, + W: VectorSpace, { type Output = Self; - fn div(self, rhs: f32) -> Self::Output { + fn div(self, rhs: F) -> Self::Output { Sum(self.0 / rhs, self.1 / rhs) } } @@ -149,11 +222,12 @@ where } } -impl VectorSpace for Sum +impl VectorSpace for Sum where - V: VectorSpace, - W: VectorSpace, + V: VectorSpace, + W: VectorSpace, { + type Scalar = F; const ZERO: Self = Sum(V::ZERO, W::ZERO); } @@ -162,32 +236,32 @@ where /// 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()`. +/// - (Absolute homogeneity) For all `c: Self::Scalar`, `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; + fn norm(self) -> Self::Scalar; /// The squared norm of this element. Computing this is often faster than computing /// [`NormedVectorSpace::norm`]. #[inline] - fn norm_squared(self) -> f32 { + fn norm_squared(self) -> Self::Scalar { self.norm() * self.norm() } /// The distance between this element and another, as determined by the norm. #[inline] - fn distance(self, rhs: Self) -> f32 { + fn distance(self, rhs: Self) -> Self::Scalar { (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 { + fn distance_squared(self, rhs: Self) -> Self::Scalar { (rhs - self).norm_squared() } } @@ -245,10 +319,55 @@ impl NormedVectorSpace for f32 { fn norm(self) -> f32 { ops::abs(self) } +} + +impl NormedVectorSpace for DVec4 { + #[inline] + fn norm(self) -> f64 { + self.length() + } #[inline] - fn norm_squared(self) -> f32 { - self * self + fn norm_squared(self) -> f64 { + self.length_squared() + } +} + +impl NormedVectorSpace for DVec3 { + #[inline] + fn norm(self) -> f64 { + self.length() + } + + #[inline] + fn norm_squared(self) -> f64 { + self.length_squared() + } +} + +impl NormedVectorSpace for DVec2 { + #[inline] + fn norm(self) -> f64 { + self.length() + } + + #[inline] + fn norm_squared(self) -> f64 { + self.length_squared() + } +} + +impl NormedVectorSpace for f64 { + #[inline] + #[cfg(feature = "std")] + fn norm(self) -> f64 { + f64::abs(self) + } + + #[inline] + #[cfg(all(any(feature = "libm", feature = "nostd-libm"), not(feature = "std")))] + fn norm(self) -> f64 { + libm::fabs(self) } } @@ -353,7 +472,7 @@ pub trait StableInterpolate: Clone { // VectorSpace type, but the "natural from the semantics" part is less clear in general. impl StableInterpolate for V where - V: NormedVectorSpace, + V: NormedVectorSpace, { #[inline] fn interpolate_stable(&self, other: &Self, t: f32) -> Self { @@ -462,10 +581,13 @@ impl HasTangent for V { type Tangent = V; } -impl HasTangent for (M, N) +impl HasTangent for (M, N) where - M: HasTangent, - N: HasTangent, + F: ScalarField, + U: VectorSpace, + V: VectorSpace, + M: HasTangent, + N: HasTangent, { type Tangent = Sum; } diff --git a/crates/bevy_math/src/cubic_splines/curve_impls.rs b/crates/bevy_math/src/cubic_splines/curve_impls.rs index 85fd9fb6ad..c21763db4e 100644 --- a/crates/bevy_math/src/cubic_splines/curve_impls.rs +++ b/crates/bevy_math/src/cubic_splines/curve_impls.rs @@ -10,7 +10,7 @@ use super::{CubicCurve, RationalCurve}; // -- CubicSegment -impl Curve

for CubicSegment

{ +impl> Curve

for CubicSegment

{ #[inline] fn domain(&self) -> Interval { Interval::UNIT @@ -22,7 +22,7 @@ impl Curve

for CubicSegment

{ } } -impl SampleDerivative

for CubicSegment

{ +impl> SampleDerivative

for CubicSegment

{ #[inline] fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative

{ WithDerivative { @@ -32,7 +32,7 @@ impl SampleDerivative

for CubicSegment

{ } } -impl SampleTwoDerivatives

for CubicSegment

{ +impl> SampleTwoDerivatives

for CubicSegment

{ #[inline] fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives

{ WithTwoDerivatives { @@ -46,7 +46,7 @@ impl SampleTwoDerivatives

for CubicSegment

{ // -- CubicCurve #[cfg(feature = "alloc")] -impl Curve

for CubicCurve

{ +impl> Curve

for CubicCurve

{ #[inline] fn domain(&self) -> Interval { // The non-emptiness invariant guarantees that this succeeds. @@ -61,7 +61,7 @@ impl Curve

for CubicCurve

{ } #[cfg(feature = "alloc")] -impl SampleDerivative

for CubicCurve

{ +impl> SampleDerivative

for CubicCurve

{ #[inline] fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative

{ WithDerivative { @@ -72,7 +72,7 @@ impl SampleDerivative

for CubicCurve

{ } #[cfg(feature = "alloc")] -impl SampleTwoDerivatives

for CubicCurve

{ +impl> SampleTwoDerivatives

for CubicCurve

{ #[inline] fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives

{ WithTwoDerivatives { @@ -85,7 +85,7 @@ impl SampleTwoDerivatives

for CubicCurve

{ // -- RationalSegment -impl Curve

for RationalSegment

{ +impl> Curve

for RationalSegment

{ #[inline] fn domain(&self) -> Interval { Interval::UNIT @@ -97,7 +97,7 @@ impl Curve

for RationalSegment

{ } } -impl SampleDerivative

for RationalSegment

{ +impl> SampleDerivative

for RationalSegment

{ #[inline] fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative

{ WithDerivative { @@ -107,7 +107,7 @@ impl SampleDerivative

for RationalSegment

{ } } -impl SampleTwoDerivatives

for RationalSegment

{ +impl> SampleTwoDerivatives

for RationalSegment

{ #[inline] fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives

{ WithTwoDerivatives { @@ -121,7 +121,7 @@ impl SampleTwoDerivatives

for RationalSegment

{ // -- RationalCurve #[cfg(feature = "alloc")] -impl Curve

for RationalCurve

{ +impl> Curve

for RationalCurve

{ #[inline] fn domain(&self) -> Interval { // The non-emptiness invariant guarantees the success of this. @@ -136,7 +136,7 @@ impl Curve

for RationalCurve

{ } #[cfg(feature = "alloc")] -impl SampleDerivative

for RationalCurve

{ +impl> SampleDerivative

for RationalCurve

{ #[inline] fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative

{ WithDerivative { @@ -147,7 +147,7 @@ impl SampleDerivative

for RationalCurve

{ } #[cfg(feature = "alloc")] -impl SampleTwoDerivatives

for RationalCurve

{ +impl> SampleTwoDerivatives

for RationalCurve

{ #[inline] fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives

{ WithTwoDerivatives { diff --git a/crates/bevy_math/src/cubic_splines/mod.rs b/crates/bevy_math/src/cubic_splines/mod.rs index 0f4082bd09..1b04603a73 100644 --- a/crates/bevy_math/src/cubic_splines/mod.rs +++ b/crates/bevy_math/src/cubic_splines/mod.rs @@ -68,7 +68,7 @@ impl CubicBezier

{ } #[cfg(feature = "alloc")] -impl CubicGenerator

for CubicBezier

{ +impl> CubicGenerator

for CubicBezier

{ type Error = CubicBezierError; #[inline] @@ -176,7 +176,7 @@ impl CubicHermite

{ } #[cfg(feature = "alloc")] -impl CubicGenerator

for CubicHermite

{ +impl> CubicGenerator

for CubicHermite

{ type Error = InsufficientDataError; #[inline] @@ -202,7 +202,7 @@ impl CubicGenerator

for CubicHermite

{ } #[cfg(feature = "alloc")] -impl CyclicCubicGenerator

for CubicHermite

{ +impl> CyclicCubicGenerator

for CubicHermite

{ type Error = InsufficientDataError; #[inline] @@ -313,7 +313,7 @@ impl CubicCardinalSpline

{ } #[cfg(feature = "alloc")] -impl CubicGenerator

for CubicCardinalSpline

{ +impl> CubicGenerator

for CubicCardinalSpline

{ type Error = InsufficientDataError; #[inline] @@ -351,7 +351,7 @@ impl CubicGenerator

for CubicCardinalSpline

{ } #[cfg(feature = "alloc")] -impl CyclicCubicGenerator

for CubicCardinalSpline

{ +impl> CyclicCubicGenerator

for CubicCardinalSpline

{ type Error = InsufficientDataError; #[inline] @@ -471,7 +471,7 @@ impl CubicBSpline

{ } #[cfg(feature = "alloc")] -impl CubicGenerator

for CubicBSpline

{ +impl> CubicGenerator

for CubicBSpline

{ type Error = InsufficientDataError; #[inline] @@ -494,7 +494,7 @@ impl CubicGenerator

for CubicBSpline

{ } #[cfg(feature = "alloc")] -impl CyclicCubicGenerator

for CubicBSpline

{ +impl> CyclicCubicGenerator

for CubicBSpline

{ type Error = InsufficientDataError; #[inline] @@ -620,7 +620,7 @@ pub struct CubicNurbs { } #[cfg(feature = "alloc")] -impl CubicNurbs

{ +impl> CubicNurbs

{ /// Build a Non-Uniform Rational B-Spline. /// /// If provided, weights must be the same length as the control points. Defaults to equal weights. @@ -781,7 +781,7 @@ impl CubicNurbs

{ } #[cfg(feature = "alloc")] -impl RationalGenerator

for CubicNurbs

{ +impl> RationalGenerator

for CubicNurbs

{ type Error = InsufficientDataError; #[inline] @@ -962,7 +962,7 @@ pub struct CubicSegment { pub coeff: [P; 4], } -impl CubicSegment

{ +impl> CubicSegment

{ /// Instantaneous position of a point at parametric value `t`. #[inline] pub fn position(&self, t: f32) -> P { @@ -1184,7 +1184,7 @@ pub struct CubicCurve { } #[cfg(feature = "alloc")] -impl CubicCurve

{ +impl> CubicCurve

{ /// Create a new curve from a collection of segments. If the collection of segments is empty, /// a curve cannot be built and `None` will be returned instead. pub fn from_segments(segments: impl IntoIterator>) -> Option { @@ -1347,7 +1347,7 @@ pub struct RationalSegment { /// The width of the domain of this segment. pub knot_span: f32, } -impl RationalSegment

{ +impl> RationalSegment

{ /// Instantaneous position of a point at parametric value `t` in `[0, 1]`. #[inline] pub fn position(&self, t: f32) -> P { @@ -1484,7 +1484,7 @@ pub struct RationalCurve { } #[cfg(feature = "alloc")] -impl RationalCurve

{ +impl> RationalCurve

{ /// Create a new curve from a collection of segments. If the collection of segments is empty, /// a curve cannot be built and `None` will be returned instead. pub fn from_segments(segments: impl IntoIterator>) -> Option { diff --git a/crates/bevy_math/src/curve/derivatives/adaptor_impls.rs b/crates/bevy_math/src/curve/derivatives/adaptor_impls.rs index a499526b78..9e3686b5aa 100644 --- a/crates/bevy_math/src/curve/derivatives/adaptor_impls.rs +++ b/crates/bevy_math/src/curve/derivatives/adaptor_impls.rs @@ -208,10 +208,12 @@ where // -- ZipCurve -impl SampleDerivative<(S, T)> for ZipCurve +impl SampleDerivative<(S, T)> for ZipCurve where - S: HasTangent, - T: HasTangent, + U: VectorSpace, + V: VectorSpace, + S: HasTangent, + T: HasTangent, C: SampleDerivative, D: SampleDerivative, { @@ -225,10 +227,12 @@ where } } -impl SampleTwoDerivatives<(S, T)> for ZipCurve +impl SampleTwoDerivatives<(S, T)> for ZipCurve where - S: HasTangent, - T: HasTangent, + U: VectorSpace, + V: VectorSpace, + S: HasTangent, + T: HasTangent, C: SampleTwoDerivatives, D: SampleTwoDerivatives, { @@ -248,9 +252,10 @@ where // -- GraphCurve -impl SampleDerivative<(f32, T)> for GraphCurve +impl SampleDerivative<(f32, T)> for GraphCurve where - T: HasTangent, + V: VectorSpace, + T: HasTangent, C: SampleDerivative, { fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<(f32, T)> { @@ -262,9 +267,10 @@ where } } -impl SampleTwoDerivatives<(f32, T)> for GraphCurve +impl SampleTwoDerivatives<(f32, T)> for GraphCurve where - T: HasTangent, + V: VectorSpace, + T: HasTangent, C: SampleTwoDerivatives, { fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<(f32, T)> { @@ -321,9 +327,10 @@ where // -- CurveReparamCurve -impl SampleDerivative for CurveReparamCurve +impl SampleDerivative for CurveReparamCurve where - T: HasTangent, + V: VectorSpace, + T: HasTangent, C: SampleDerivative, D: SampleDerivative, { @@ -349,9 +356,10 @@ where } } -impl SampleTwoDerivatives for CurveReparamCurve +impl SampleTwoDerivatives for CurveReparamCurve where - T: HasTangent, + V: VectorSpace, + T: HasTangent, C: SampleTwoDerivatives, D: SampleTwoDerivatives, { @@ -386,9 +394,10 @@ where // -- LinearReparamCurve -impl SampleDerivative for LinearReparamCurve +impl SampleDerivative for LinearReparamCurve where - T: HasTangent, + V: VectorSpace, + T: HasTangent, C: SampleDerivative, { fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative { @@ -413,9 +422,10 @@ where } } -impl SampleTwoDerivatives for LinearReparamCurve +impl SampleTwoDerivatives for LinearReparamCurve where - T: HasTangent, + V: VectorSpace, + T: HasTangent, C: SampleTwoDerivatives, { fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives { diff --git a/crates/bevy_math/src/curve/easing.rs b/crates/bevy_math/src/curve/easing.rs index c0b452e001..91908ee80b 100644 --- a/crates/bevy_math/src/curve/easing.rs +++ b/crates/bevy_math/src/curve/easing.rs @@ -32,7 +32,7 @@ pub trait Ease: Sized { fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve; } -impl Ease for V { +impl> Ease for V { fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve { FunctionCurve::new(Interval::EVERYWHERE, move |t| V::lerp(start, end, t)) } diff --git a/crates/bevy_math/src/sampling/shape_sampling.rs b/crates/bevy_math/src/sampling/shape_sampling.rs index 3be0ead1da..c17bc6fa76 100644 --- a/crates/bevy_math/src/sampling/shape_sampling.rs +++ b/crates/bevy_math/src/sampling/shape_sampling.rs @@ -40,11 +40,12 @@ use core::f32::consts::{PI, TAU}; -use crate::{ops, primitives::*, NormedVectorSpace, Vec2, Vec3}; +use crate::{ops, primitives::*, NormedVectorSpace, ScalarField, Vec2, Vec3}; use rand::{ distributions::{Distribution, WeightedIndex}, Rng, }; +use rand_distr::uniform::SampleUniform; /// Exposes methods to uniformly sample a variety of primitive shapes. pub trait ShapeSample { @@ -281,22 +282,24 @@ impl ShapeSample for Cuboid { } /// Interior sampling for triangles which doesn't depend on the ambient dimension. -fn sample_triangle_interior( - vertices: [P; 3], - rng: &mut R, -) -> P { +fn sample_triangle_interior(vertices: [P; 3], rng: &mut R) -> P +where + P: NormedVectorSpace, + P::Scalar: SampleUniform + PartialOrd, + R: Rng + ?Sized, +{ let [a, b, c] = vertices; let ab = b - a; let ac = c - a; // Generate random points on a parallelepiped and reflect so that // we can use the points that lie outside the triangle - let u = rng.gen_range(0.0..=1.0); - let v = rng.gen_range(0.0..=1.0); + let u = rng.gen_range(P::Scalar::ZERO..=P::Scalar::ONE); + let v = rng.gen_range(P::Scalar::ZERO..=P::Scalar::ONE); - if u + v > 1. { - let u1 = 1. - v; - let v1 = 1. - u; + if u + v > P::Scalar::ONE { + let u1 = P::Scalar::ONE - v; + let v1 = P::Scalar::ONE - u; a + (ab * u1 + ac * v1) } else { a + (ab * u + ac * v) @@ -304,16 +307,18 @@ fn sample_triangle_interior( } /// Boundary sampling for triangles which doesn't depend on the ambient dimension. -fn sample_triangle_boundary( - vertices: [P; 3], - rng: &mut R, -) -> P { +fn sample_triangle_boundary(vertices: [P; 3], rng: &mut R) -> P +where + P: NormedVectorSpace, + P::Scalar: SampleUniform + PartialOrd + for<'a> ::core::ops::AddAssign<&'a P::Scalar>, + R: Rng + ?Sized, +{ let [a, b, c] = vertices; let ab = b - a; let ac = c - a; let bc = c - b; - let t = rng.gen_range(0.0..=1.0); + let t = rng.gen_range(P::Scalar::ZERO..=P::Scalar::ONE); if let Ok(dist) = WeightedIndex::new([ab.norm(), ac.norm(), bc.norm()]) { match dist.sample(rng) { diff --git a/docs/linux_dependencies.md b/docs/linux_dependencies.md index a09bd629ff..a5a43c3d5f 100644 --- a/docs/linux_dependencies.md +++ b/docs/linux_dependencies.md @@ -91,7 +91,7 @@ Set the `PKG_CONFIG_PATH` env var to `/usr/lib//pkgconfig/`. For example export PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig/" ``` -## Arch / Manjaro +## [Arch](https://archlinux.org/) / [Manjaro](https://manjaro.org/) ```bash sudo pacman -S libx11 pkgconf alsa-lib libxcursor libxrandr libxi @@ -102,7 +102,7 @@ Install `pipewire-alsa` or `pulseaudio-alsa` depending on the sound server you a Depending on your graphics card, you may have to install one of the following: `vulkan-radeon`, `vulkan-intel`, or `mesa-vulkan-drivers` -## Void +## [Void](https://voidlinux.org/) ```bash sudo xbps-install -S pkgconf alsa-lib-devel libX11-devel eudev-libudev-devel @@ -110,6 +110,80 @@ sudo xbps-install -S pkgconf alsa-lib-devel libX11-devel eudev-libudev-devel ## [Nix](https://nixos.org) +### flake.nix + +Add a `flake.nix` file to the root of your GitHub repository containing: + +```nix +{ + description = "bevy flake"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { + nixpkgs, + rust-overlay, + flake-utils, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + in + { + devShells.default = + with pkgs; + mkShell { + buildInputs = + [ + # Rust dependencies + (rust-bin.stable.latest.default.override { extensions = [ "rust-src" ]; }) + pkg-config + ] + ++ lib.optionals (lib.strings.hasInfix "linux" system) [ + # for Linux + # Audio (Linux only) + alsa-lib + # Cross Platform 3D Graphics API + vulkan-loader + # For debugging around vulkan + vulkan-tools + # Other dependencies + libudev-zero + xorg.libX11 + xorg.libXcursor + xorg.libXi + xorg.libXrandr + libxkbcommon + ]; + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; + LD_LIBRARY_PATH = lib.makeLibraryPath [ + vulkan-loader + xorg.libX11 + xorg.libXi + xorg.libXcursor + libxkbcommon + ]; + }; + } + ); +} +``` + +> [!TIP] +> We have confirmed that this flake.nix can be used successfully on NixOS and MacOS with Rust's edition set to 2021. + +### shell.nix + Add a `shell.nix` file to the root of the project containing: ```nix @@ -138,8 +212,8 @@ If running nix on a non NixOS system (such as ubuntu, arch etc.), [NixGL](https: to link graphics drivers into the context of software installed by nix: 1. Install a system specific nixGL wrapper ([docs](https://github.com/nix-community/nixGL)). - * If you're running a nvidia GPU choose `nixVulkanNvidia`. - * Otherwise, choose another wrapper appropriate for your system. + - If you're running a nvidia GPU choose `nixVulkanNvidia`. + - Otherwise, choose another wrapper appropriate for your system. 2. Run `nixVulkanNvidia-xxx.xxx.xx cargo run` to compile a bevy program, where `xxx-xxx-xx` denotes the graphics driver version `nixVulkanNvidia` was compiled with. This is also possible with [Nix flakes](https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html). @@ -152,7 +226,7 @@ for more information about `devShells`. Note that this template does not add Rust to the environment because there are many ways to do it. For example, to use stable Rust from nixpkgs, you can add `cargo` and `rustc` to `nativeBuildInputs`. -[Here]([https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/ju/jumpy/package.nix](https://github.com/NixOS/nixpkgs/blob/0da3c44a9460a26d2025ec3ed2ec60a895eb1114/pkgs/games/jumpy/default.nix)) +[Here](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/ju/jumpy/package.nix) is an example of packaging a Bevy program in nix. ## [OpenSUSE](https://www.opensuse.org/) @@ -161,7 +235,7 @@ is an example of packaging a Bevy program in nix. sudo zypper install libudev-devel gcc-c++ alsa-lib-devel ``` -## Gentoo +## [Gentoo](https://www.gentoo.org/) ```bash sudo emerge --ask libX11 pkgconf alsa-lib diff --git a/examples/animation/color_animation.rs b/examples/animation/color_animation.rs index 2b72c44bff..df7a764bbf 100644 --- a/examples/animation/color_animation.rs +++ b/examples/animation/color_animation.rs @@ -3,8 +3,8 @@ use bevy::{math::VectorSpace, prelude::*}; // We define this trait so we can reuse the same code for multiple color types that may be implemented using curves. -trait CurveColor: VectorSpace + Into + Send + Sync + 'static {} -impl + Send + Sync + 'static> CurveColor for T {} +trait CurveColor: VectorSpace + Into + Send + Sync + 'static {} +impl + Into + Send + Sync + 'static> CurveColor for T {} // We define this trait so we can reuse the same code for multiple color types that may be implemented using mixing. trait MixedColor: Mix + Into + Send + Sync + 'static {} diff --git a/release-content/migration-guides/scalar-field-on-vector-space.md b/release-content/migration-guides/scalar-field-on-vector-space.md new file mode 100644 index 0000000000..d3b102bd93 --- /dev/null +++ b/release-content/migration-guides/scalar-field-on-vector-space.md @@ -0,0 +1,8 @@ +--- +title: `VectorSpace` implementations +pull_requests: [19194] +--- + +Previously, implementing `VectorSpace` for a type required your type to use or at least interface with `f32`. This made implementing `VectorSpace` for double-precision types (like `DVec3`) less meaningful and useful, requiring lots of casting. `VectorSpace` has a new required associated type `Scalar` that's bounded by a new trait `ScalarField`. `bevy_math` implements this trait for `f64` and `f32` out of the box, and `VectorSpace` is now implemented for `DVec[N]` types. + +If you manually implemented `VectorSpace` for any type, you'll need to implement `Scalar` for it. If you were working with single-precision floating-point types and you want the exact behavior from before, set it to `f32`.