Merge branch 'main' into schema-types-metadata

This commit is contained in:
MevLyshkin 2025-06-08 10:39:10 +02:00 committed by GitHub
commit 27f36dc425
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 399 additions and 142 deletions

View File

@ -32,7 +32,7 @@ fn segment_ease(c: &mut Criterion) {
fn curve_position(c: &mut Criterion) { fn curve_position(c: &mut Criterion) {
/// A helper function that benchmarks calling [`CubicCurve::position()`] over a generic [`VectorSpace`]. /// A helper function that benchmarks calling [`CubicCurve::position()`] over a generic [`VectorSpace`].
fn bench_curve<M: Measurement, P: VectorSpace>( fn bench_curve<M: Measurement, P: VectorSpace<Scalar = f32>>(
group: &mut BenchmarkGroup<M>, group: &mut BenchmarkGroup<M>,
name: &str, name: &str,
curve: CubicCurve<P>, curve: CubicCurve<P>,

View File

@ -55,7 +55,7 @@ pub struct CubicKeyframeCurve<T> {
impl<V> Curve<V> for CubicKeyframeCurve<V> impl<V> Curve<V> for CubicKeyframeCurve<V>
where where
V: VectorSpace, V: VectorSpace<Scalar = f32>,
{ {
#[inline] #[inline]
fn domain(&self) -> Interval { fn domain(&self) -> Interval {
@ -179,7 +179,7 @@ pub struct WideLinearKeyframeCurve<T> {
impl<T> IterableCurve<T> for WideLinearKeyframeCurve<T> impl<T> IterableCurve<T> for WideLinearKeyframeCurve<T>
where where
T: VectorSpace, T: VectorSpace<Scalar = f32>,
{ {
#[inline] #[inline]
fn domain(&self) -> Interval { fn domain(&self) -> Interval {
@ -289,7 +289,7 @@ pub struct WideCubicKeyframeCurve<T> {
impl<T> IterableCurve<T> for WideCubicKeyframeCurve<T> impl<T> IterableCurve<T> for WideCubicKeyframeCurve<T>
where where
T: VectorSpace, T: VectorSpace<Scalar = f32>,
{ {
#[inline] #[inline]
fn domain(&self) -> Interval { fn domain(&self) -> Interval {
@ -406,7 +406,7 @@ fn cubic_spline_interpolation<T>(
step_duration: f32, step_duration: f32,
) -> T ) -> T
where where
T: VectorSpace, T: VectorSpace<Scalar = f32>,
{ {
let coeffs = (vec4(2.0, 1.0, -2.0, 1.0) * lerp + vec4(-3.0, -2.0, 3.0, -1.0)) * lerp; 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) value_start * (coeffs.x * lerp + 1.0)
@ -415,7 +415,7 @@ where
+ tangent_in_end * step_duration * lerp * coeffs.w + tangent_in_end * step_duration * lerp * coeffs.w
} }
fn cubic_spline_interpolate_slices<'a, T: VectorSpace>( fn cubic_spline_interpolate_slices<'a, T: VectorSpace<Scalar = f32>>(
width: usize, width: usize,
first: &'a [T], first: &'a [T],
second: &'a [T], second: &'a [T],

View File

@ -262,6 +262,7 @@ macro_rules! impl_componentwise_vector_space {
} }
impl bevy_math::VectorSpace for $ty { impl bevy_math::VectorSpace for $ty {
type Scalar = f32;
const ZERO: Self = Self { const ZERO: Self = Self {
$($element: 0.0,)+ $($element: 0.0,)+
}; };

View File

@ -358,7 +358,10 @@ mod tests {
// Next allocated entity should be a further generation on the same index // Next allocated entity should be a further generation on the same index
let entity = world.spawn_empty().id(); let entity = world.spawn_empty().id();
assert_eq!(entity.index(), dead_ref.index()); assert_eq!(entity.index(), dead_ref.index());
assert!(entity.generation() > dead_ref.generation()); assert!(entity
.generation()
.cmp_approx(&dead_ref.generation())
.is_gt());
} }
#[test] #[test]
@ -373,7 +376,10 @@ mod tests {
// Next allocated entity should be a further generation on the same index // Next allocated entity should be a further generation on the same index
let entity = world.spawn_empty().id(); let entity = world.spawn_empty().id();
assert_eq!(entity.index(), dead_ref.index()); assert_eq!(entity.index(), dead_ref.index());
assert!(entity.generation() > dead_ref.generation()); assert!(entity
.generation()
.cmp_approx(&dead_ref.generation())
.is_gt());
} }
#[test] #[test]

View File

@ -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. /// 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 /// # Aliasing
/// ///
/// Internally [`EntityGeneration`] wraps a `u32`, so it can't represent *every* possible generation. /// 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`]. /// Represents the first generation of an [`EntityRow`].
pub const FIRST: Self = Self(0); 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. /// Gets some bits that represent this value.
/// The bits are opaque and should not be regarded as meaningful. /// The bits are opaque and should not be regarded as meaningful.
#[inline(always)] #[inline(always)]
@ -266,18 +246,48 @@ impl EntityGeneration {
let raw = self.0.overflowing_add(versions); let raw = self.0.overflowing_add(versions);
(Self(raw.0), raw.1) (Self(raw.0), raw.1)
} }
}
impl PartialOrd for EntityGeneration { /// Compares two generations.
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { ///
Some(self.cmp(other)) /// Generations that are later will be [`Greater`](core::cmp::Ordering::Greater) than earlier ones.
} ///
} /// ```
/// # use bevy_ecs::entity::EntityGeneration;
impl Ord for EntityGeneration { /// # use core::cmp::Ordering;
fn cmp(&self, other: &Self) -> core::cmp::Ordering { /// let later_generation = EntityGeneration::FIRST.after_versions(400);
let diff = self.0.wrapping_sub(other.0); /// assert_eq!(EntityGeneration::FIRST.cmp_approx(&later_generation), Ordering::Less);
(1u32 << 31).cmp(&diff) ///
/// 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 // The very next entity allocated should be a further generation on the same index
let next_entity = entities.alloc(); let next_entity = entities.alloc();
assert_eq!(next_entity.index(), entity.index()); 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] #[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] #[test]
fn entity_debug() { fn entity_debug() {
let entity = Entity::from_raw(EntityRow::new(NonMaxU32::new(42).unwrap())); let entity = Entity::from_raw(EntityRow::new(NonMaxU32::new(42).unwrap()));

View File

@ -1,6 +1,6 @@
//! This module contains abstract mathematical traits shared by types used in `bevy_math`. //! 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::{ use core::{
fmt::Debug, fmt::Debug,
ops::{Add, Div, Mul, Neg, Sub}, 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. /// A type that supports the mathematical operations of a real vector space, irrespective of dimension.
/// In particular, this means that the implementing type supports: /// 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 /// - Negation
/// - Addition and subtraction /// - Addition and subtraction
/// - Zero /// - Zero
@ -19,16 +19,16 @@ use variadics_please::all_tuples_enumerated;
/// - (Commutativity of addition) For all `u, v: Self`, `u + v == v + u`. /// - (Commutativity of addition) For all `u, v: Self`, `u + v == v + u`.
/// - (Additive identity) For all `v: Self`, `v + Self::ZERO == v`. /// - (Additive identity) For all `v: Self`, `v + Self::ZERO == v`.
/// - (Additive inverse) For all `v: Self`, `v - v == v + (-v) == Self::ZERO`. /// - (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`. /// - (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 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: f32`, `v: Self`, `v * (a + b) == v * a + v * b`. /// - (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 /// Note that, because implementing types use floating point arithmetic, they are not required to actually
/// implement `PartialEq` or `Eq`. /// implement `PartialEq` or `Eq`.
pub trait VectorSpace: pub trait VectorSpace:
Mul<f32, Output = Self> Mul<Self::Scalar, Output = Self>
+ Div<f32, Output = Self> + Div<Self::Scalar, Output = Self>
+ Add<Self, Output = Self> + Add<Self, Output = Self>
+ Sub<Self, Output = Self> + Sub<Self, Output = Self>
+ Neg<Output = Self> + Neg<Output = Self>
@ -37,6 +37,9 @@ pub trait VectorSpace:
+ Clone + Clone
+ Copy + 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. /// The zero vector, which is the identity of addition for the vector space type.
const ZERO: Self; 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 /// Note that the value of `t` is not clamped by this function, so extrapolating outside
/// of the interval `[0,1]` is allowed. /// of the interval `[0,1]` is allowed.
#[inline] #[inline]
fn lerp(self, rhs: Self, t: f32) -> Self { fn lerp(self, rhs: Self, t: Self::Scalar) -> Self {
self * (1. - t) + rhs * t self * (Self::Scalar::ONE - t) + rhs * t
} }
} }
impl VectorSpace for Vec4 { impl VectorSpace for Vec4 {
type Scalar = f32;
const ZERO: Self = Vec4::ZERO; const ZERO: Self = Vec4::ZERO;
} }
impl VectorSpace for Vec3 { impl VectorSpace for Vec3 {
type Scalar = f32;
const ZERO: Self = Vec3::ZERO; const ZERO: Self = Vec3::ZERO;
} }
impl VectorSpace for Vec3A { impl VectorSpace for Vec3A {
type Scalar = f32;
const ZERO: Self = Vec3A::ZERO; const ZERO: Self = Vec3A::ZERO;
} }
impl VectorSpace for Vec2 { impl VectorSpace for Vec2 {
type Scalar = f32;
const ZERO: Self = Vec2::ZERO; 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<T: ScalarField> 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<Self, Output = Self>
+ Div<Self, Output = Self>
+ Add<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Neg<Output = Self>
+ 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 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, /// 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))] #[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub struct Sum<V, W>(pub V, pub W); pub struct Sum<V, W>(pub V, pub W);
impl<V, W> Mul<f32> for Sum<V, W> impl<F: ScalarField, V, W> Mul<F> for Sum<V, W>
where where
V: VectorSpace, V: VectorSpace<Scalar = F>,
W: VectorSpace, W: VectorSpace<Scalar = F>,
{ {
type Output = Self; type Output = Self;
fn mul(self, rhs: f32) -> Self::Output { fn mul(self, rhs: F) -> Self::Output {
Sum(self.0 * rhs, self.1 * rhs) Sum(self.0 * rhs, self.1 * rhs)
} }
} }
impl<V, W> Div<f32> for Sum<V, W> impl<F: ScalarField, V, W> Div<F> for Sum<V, W>
where where
V: VectorSpace, V: VectorSpace<Scalar = F>,
W: VectorSpace, W: VectorSpace<Scalar = F>,
{ {
type Output = Self; type Output = Self;
fn div(self, rhs: f32) -> Self::Output { fn div(self, rhs: F) -> Self::Output {
Sum(self.0 / rhs, self.1 / rhs) Sum(self.0 / rhs, self.1 / rhs)
} }
} }
@ -149,11 +222,12 @@ where
} }
} }
impl<V, W> VectorSpace for Sum<V, W> impl<F: ScalarField, V, W> VectorSpace for Sum<V, W>
where where
V: VectorSpace, V: VectorSpace<Scalar = F>,
W: VectorSpace, W: VectorSpace<Scalar = F>,
{ {
type Scalar = F;
const ZERO: Self = Sum(V::ZERO, W::ZERO); const ZERO: Self = Sum(V::ZERO, W::ZERO);
} }
@ -162,32 +236,32 @@ where
/// relationships hold, within the limitations of floating point arithmetic: /// relationships hold, within the limitations of floating point arithmetic:
/// - (Nonnegativity) For all `v: Self`, `v.norm() >= 0.0`. /// - (Nonnegativity) For all `v: Self`, `v.norm() >= 0.0`.
/// - (Positive definiteness) For all `v: Self`, `v.norm() == 0.0` implies `v == Self::ZERO`. /// - (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()`. /// - (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 /// Note that, because implementing types use floating point arithmetic, they are not required to actually
/// implement `PartialEq` or `Eq`. /// implement `PartialEq` or `Eq`.
pub trait NormedVectorSpace: VectorSpace { pub trait NormedVectorSpace: VectorSpace {
/// The size of this element. The return value should always be nonnegative. /// 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 /// The squared norm of this element. Computing this is often faster than computing
/// [`NormedVectorSpace::norm`]. /// [`NormedVectorSpace::norm`].
#[inline] #[inline]
fn norm_squared(self) -> f32 { fn norm_squared(self) -> Self::Scalar {
self.norm() * self.norm() self.norm() * self.norm()
} }
/// The distance between this element and another, as determined by the norm. /// The distance between this element and another, as determined by the norm.
#[inline] #[inline]
fn distance(self, rhs: Self) -> f32 { fn distance(self, rhs: Self) -> Self::Scalar {
(rhs - self).norm() (rhs - self).norm()
} }
/// The squared distance between this element and another, as determined by the norm. Note that /// 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`]. /// this is often faster to compute in practice than [`NormedVectorSpace::distance`].
#[inline] #[inline]
fn distance_squared(self, rhs: Self) -> f32 { fn distance_squared(self, rhs: Self) -> Self::Scalar {
(rhs - self).norm_squared() (rhs - self).norm_squared()
} }
} }
@ -245,10 +319,55 @@ impl NormedVectorSpace for f32 {
fn norm(self) -> f32 { fn norm(self) -> f32 {
ops::abs(self) ops::abs(self)
} }
}
impl NormedVectorSpace for DVec4 {
#[inline]
fn norm(self) -> f64 {
self.length()
}
#[inline] #[inline]
fn norm_squared(self) -> f32 { fn norm_squared(self) -> f64 {
self * self 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. // VectorSpace type, but the "natural from the semantics" part is less clear in general.
impl<V> StableInterpolate for V impl<V> StableInterpolate for V
where where
V: NormedVectorSpace, V: NormedVectorSpace<Scalar = f32>,
{ {
#[inline] #[inline]
fn interpolate_stable(&self, other: &Self, t: f32) -> Self { fn interpolate_stable(&self, other: &Self, t: f32) -> Self {
@ -462,10 +581,13 @@ impl<V: VectorSpace> HasTangent for V {
type Tangent = V; type Tangent = V;
} }
impl<M, N> HasTangent for (M, N) impl<F, U, V, M, N> HasTangent for (M, N)
where where
M: HasTangent, F: ScalarField,
N: HasTangent, U: VectorSpace<Scalar = F>,
V: VectorSpace<Scalar = F>,
M: HasTangent<Tangent = U>,
N: HasTangent<Tangent = V>,
{ {
type Tangent = Sum<M::Tangent, N::Tangent>; type Tangent = Sum<M::Tangent, N::Tangent>;
} }

View File

@ -10,7 +10,7 @@ use super::{CubicCurve, RationalCurve};
// -- CubicSegment // -- CubicSegment
impl<P: VectorSpace> Curve<P> for CubicSegment<P> { impl<P: VectorSpace<Scalar = f32>> Curve<P> for CubicSegment<P> {
#[inline] #[inline]
fn domain(&self) -> Interval { fn domain(&self) -> Interval {
Interval::UNIT Interval::UNIT
@ -22,7 +22,7 @@ impl<P: VectorSpace> Curve<P> for CubicSegment<P> {
} }
} }
impl<P: VectorSpace> SampleDerivative<P> for CubicSegment<P> { impl<P: VectorSpace<Scalar = f32>> SampleDerivative<P> for CubicSegment<P> {
#[inline] #[inline]
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<P> { fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<P> {
WithDerivative { WithDerivative {
@ -32,7 +32,7 @@ impl<P: VectorSpace> SampleDerivative<P> for CubicSegment<P> {
} }
} }
impl<P: VectorSpace> SampleTwoDerivatives<P> for CubicSegment<P> { impl<P: VectorSpace<Scalar = f32>> SampleTwoDerivatives<P> for CubicSegment<P> {
#[inline] #[inline]
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<P> { fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<P> {
WithTwoDerivatives { WithTwoDerivatives {
@ -46,7 +46,7 @@ impl<P: VectorSpace> SampleTwoDerivatives<P> for CubicSegment<P> {
// -- CubicCurve // -- CubicCurve
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> Curve<P> for CubicCurve<P> { impl<P: VectorSpace<Scalar = f32>> Curve<P> for CubicCurve<P> {
#[inline] #[inline]
fn domain(&self) -> Interval { fn domain(&self) -> Interval {
// The non-emptiness invariant guarantees that this succeeds. // The non-emptiness invariant guarantees that this succeeds.
@ -61,7 +61,7 @@ impl<P: VectorSpace> Curve<P> for CubicCurve<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> SampleDerivative<P> for CubicCurve<P> { impl<P: VectorSpace<Scalar = f32>> SampleDerivative<P> for CubicCurve<P> {
#[inline] #[inline]
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<P> { fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<P> {
WithDerivative { WithDerivative {
@ -72,7 +72,7 @@ impl<P: VectorSpace> SampleDerivative<P> for CubicCurve<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> SampleTwoDerivatives<P> for CubicCurve<P> { impl<P: VectorSpace<Scalar = f32>> SampleTwoDerivatives<P> for CubicCurve<P> {
#[inline] #[inline]
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<P> { fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<P> {
WithTwoDerivatives { WithTwoDerivatives {
@ -85,7 +85,7 @@ impl<P: VectorSpace> SampleTwoDerivatives<P> for CubicCurve<P> {
// -- RationalSegment // -- RationalSegment
impl<P: VectorSpace> Curve<P> for RationalSegment<P> { impl<P: VectorSpace<Scalar = f32>> Curve<P> for RationalSegment<P> {
#[inline] #[inline]
fn domain(&self) -> Interval { fn domain(&self) -> Interval {
Interval::UNIT Interval::UNIT
@ -97,7 +97,7 @@ impl<P: VectorSpace> Curve<P> for RationalSegment<P> {
} }
} }
impl<P: VectorSpace> SampleDerivative<P> for RationalSegment<P> { impl<P: VectorSpace<Scalar = f32>> SampleDerivative<P> for RationalSegment<P> {
#[inline] #[inline]
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<P> { fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<P> {
WithDerivative { WithDerivative {
@ -107,7 +107,7 @@ impl<P: VectorSpace> SampleDerivative<P> for RationalSegment<P> {
} }
} }
impl<P: VectorSpace> SampleTwoDerivatives<P> for RationalSegment<P> { impl<P: VectorSpace<Scalar = f32>> SampleTwoDerivatives<P> for RationalSegment<P> {
#[inline] #[inline]
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<P> { fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<P> {
WithTwoDerivatives { WithTwoDerivatives {
@ -121,7 +121,7 @@ impl<P: VectorSpace> SampleTwoDerivatives<P> for RationalSegment<P> {
// -- RationalCurve // -- RationalCurve
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> Curve<P> for RationalCurve<P> { impl<P: VectorSpace<Scalar = f32>> Curve<P> for RationalCurve<P> {
#[inline] #[inline]
fn domain(&self) -> Interval { fn domain(&self) -> Interval {
// The non-emptiness invariant guarantees the success of this. // The non-emptiness invariant guarantees the success of this.
@ -136,7 +136,7 @@ impl<P: VectorSpace> Curve<P> for RationalCurve<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> SampleDerivative<P> for RationalCurve<P> { impl<P: VectorSpace<Scalar = f32>> SampleDerivative<P> for RationalCurve<P> {
#[inline] #[inline]
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<P> { fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<P> {
WithDerivative { WithDerivative {
@ -147,7 +147,7 @@ impl<P: VectorSpace> SampleDerivative<P> for RationalCurve<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> SampleTwoDerivatives<P> for RationalCurve<P> { impl<P: VectorSpace<Scalar = f32>> SampleTwoDerivatives<P> for RationalCurve<P> {
#[inline] #[inline]
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<P> { fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<P> {
WithTwoDerivatives { WithTwoDerivatives {

View File

@ -68,7 +68,7 @@ impl<P: VectorSpace> CubicBezier<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> CubicGenerator<P> for CubicBezier<P> { impl<P: VectorSpace<Scalar = f32>> CubicGenerator<P> for CubicBezier<P> {
type Error = CubicBezierError; type Error = CubicBezierError;
#[inline] #[inline]
@ -176,7 +176,7 @@ impl<P: VectorSpace> CubicHermite<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> CubicGenerator<P> for CubicHermite<P> { impl<P: VectorSpace<Scalar = f32>> CubicGenerator<P> for CubicHermite<P> {
type Error = InsufficientDataError; type Error = InsufficientDataError;
#[inline] #[inline]
@ -202,7 +202,7 @@ impl<P: VectorSpace> CubicGenerator<P> for CubicHermite<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> CyclicCubicGenerator<P> for CubicHermite<P> { impl<P: VectorSpace<Scalar = f32>> CyclicCubicGenerator<P> for CubicHermite<P> {
type Error = InsufficientDataError; type Error = InsufficientDataError;
#[inline] #[inline]
@ -313,7 +313,7 @@ impl<P: VectorSpace> CubicCardinalSpline<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> CubicGenerator<P> for CubicCardinalSpline<P> { impl<P: VectorSpace<Scalar = f32>> CubicGenerator<P> for CubicCardinalSpline<P> {
type Error = InsufficientDataError; type Error = InsufficientDataError;
#[inline] #[inline]
@ -351,7 +351,7 @@ impl<P: VectorSpace> CubicGenerator<P> for CubicCardinalSpline<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> CyclicCubicGenerator<P> for CubicCardinalSpline<P> { impl<P: VectorSpace<Scalar = f32>> CyclicCubicGenerator<P> for CubicCardinalSpline<P> {
type Error = InsufficientDataError; type Error = InsufficientDataError;
#[inline] #[inline]
@ -471,7 +471,7 @@ impl<P: VectorSpace> CubicBSpline<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> CubicGenerator<P> for CubicBSpline<P> { impl<P: VectorSpace<Scalar = f32>> CubicGenerator<P> for CubicBSpline<P> {
type Error = InsufficientDataError; type Error = InsufficientDataError;
#[inline] #[inline]
@ -494,7 +494,7 @@ impl<P: VectorSpace> CubicGenerator<P> for CubicBSpline<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> CyclicCubicGenerator<P> for CubicBSpline<P> { impl<P: VectorSpace<Scalar = f32>> CyclicCubicGenerator<P> for CubicBSpline<P> {
type Error = InsufficientDataError; type Error = InsufficientDataError;
#[inline] #[inline]
@ -620,7 +620,7 @@ pub struct CubicNurbs<P: VectorSpace> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> CubicNurbs<P> { impl<P: VectorSpace<Scalar = f32>> CubicNurbs<P> {
/// Build a Non-Uniform Rational B-Spline. /// Build a Non-Uniform Rational B-Spline.
/// ///
/// If provided, weights must be the same length as the control points. Defaults to equal weights. /// If provided, weights must be the same length as the control points. Defaults to equal weights.
@ -781,7 +781,7 @@ impl<P: VectorSpace> CubicNurbs<P> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> RationalGenerator<P> for CubicNurbs<P> { impl<P: VectorSpace<Scalar = f32>> RationalGenerator<P> for CubicNurbs<P> {
type Error = InsufficientDataError; type Error = InsufficientDataError;
#[inline] #[inline]
@ -962,7 +962,7 @@ pub struct CubicSegment<P: VectorSpace> {
pub coeff: [P; 4], pub coeff: [P; 4],
} }
impl<P: VectorSpace> CubicSegment<P> { impl<P: VectorSpace<Scalar = f32>> CubicSegment<P> {
/// Instantaneous position of a point at parametric value `t`. /// Instantaneous position of a point at parametric value `t`.
#[inline] #[inline]
pub fn position(&self, t: f32) -> P { pub fn position(&self, t: f32) -> P {
@ -1184,7 +1184,7 @@ pub struct CubicCurve<P: VectorSpace> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> CubicCurve<P> { impl<P: VectorSpace<Scalar = f32>> CubicCurve<P> {
/// Create a new curve from a collection of segments. If the collection of segments is empty, /// 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. /// a curve cannot be built and `None` will be returned instead.
pub fn from_segments(segments: impl IntoIterator<Item = CubicSegment<P>>) -> Option<Self> { pub fn from_segments(segments: impl IntoIterator<Item = CubicSegment<P>>) -> Option<Self> {
@ -1347,7 +1347,7 @@ pub struct RationalSegment<P: VectorSpace> {
/// The width of the domain of this segment. /// The width of the domain of this segment.
pub knot_span: f32, pub knot_span: f32,
} }
impl<P: VectorSpace> RationalSegment<P> { impl<P: VectorSpace<Scalar = f32>> RationalSegment<P> {
/// Instantaneous position of a point at parametric value `t` in `[0, 1]`. /// Instantaneous position of a point at parametric value `t` in `[0, 1]`.
#[inline] #[inline]
pub fn position(&self, t: f32) -> P { pub fn position(&self, t: f32) -> P {
@ -1484,7 +1484,7 @@ pub struct RationalCurve<P: VectorSpace> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<P: VectorSpace> RationalCurve<P> { impl<P: VectorSpace<Scalar = f32>> RationalCurve<P> {
/// Create a new curve from a collection of segments. If the collection of segments is empty, /// 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. /// a curve cannot be built and `None` will be returned instead.
pub fn from_segments(segments: impl IntoIterator<Item = RationalSegment<P>>) -> Option<Self> { pub fn from_segments(segments: impl IntoIterator<Item = RationalSegment<P>>) -> Option<Self> {

View File

@ -208,10 +208,12 @@ where
// -- ZipCurve // -- ZipCurve
impl<S, T, C, D> SampleDerivative<(S, T)> for ZipCurve<S, T, C, D> impl<U, V, S, T, C, D> SampleDerivative<(S, T)> for ZipCurve<S, T, C, D>
where where
S: HasTangent, U: VectorSpace<Scalar = f32>,
T: HasTangent, V: VectorSpace<Scalar = f32>,
S: HasTangent<Tangent = U>,
T: HasTangent<Tangent = V>,
C: SampleDerivative<S>, C: SampleDerivative<S>,
D: SampleDerivative<T>, D: SampleDerivative<T>,
{ {
@ -225,10 +227,12 @@ where
} }
} }
impl<S, T, C, D> SampleTwoDerivatives<(S, T)> for ZipCurve<S, T, C, D> impl<U, V, S, T, C, D> SampleTwoDerivatives<(S, T)> for ZipCurve<S, T, C, D>
where where
S: HasTangent, U: VectorSpace<Scalar = f32>,
T: HasTangent, V: VectorSpace<Scalar = f32>,
S: HasTangent<Tangent = U>,
T: HasTangent<Tangent = V>,
C: SampleTwoDerivatives<S>, C: SampleTwoDerivatives<S>,
D: SampleTwoDerivatives<T>, D: SampleTwoDerivatives<T>,
{ {
@ -248,9 +252,10 @@ where
// -- GraphCurve // -- GraphCurve
impl<T, C> SampleDerivative<(f32, T)> for GraphCurve<T, C> impl<V, T, C> SampleDerivative<(f32, T)> for GraphCurve<T, C>
where where
T: HasTangent, V: VectorSpace<Scalar = f32>,
T: HasTangent<Tangent = V>,
C: SampleDerivative<T>, C: SampleDerivative<T>,
{ {
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<(f32, T)> { fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<(f32, T)> {
@ -262,9 +267,10 @@ where
} }
} }
impl<T, C> SampleTwoDerivatives<(f32, T)> for GraphCurve<T, C> impl<V, T, C> SampleTwoDerivatives<(f32, T)> for GraphCurve<T, C>
where where
T: HasTangent, V: VectorSpace<Scalar = f32>,
T: HasTangent<Tangent = V>,
C: SampleTwoDerivatives<T>, C: SampleTwoDerivatives<T>,
{ {
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<(f32, T)> { fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<(f32, T)> {
@ -321,9 +327,10 @@ where
// -- CurveReparamCurve // -- CurveReparamCurve
impl<T, C, D> SampleDerivative<T> for CurveReparamCurve<T, C, D> impl<V, T, C, D> SampleDerivative<T> for CurveReparamCurve<T, C, D>
where where
T: HasTangent, V: VectorSpace<Scalar = f32>,
T: HasTangent<Tangent = V>,
C: SampleDerivative<T>, C: SampleDerivative<T>,
D: SampleDerivative<f32>, D: SampleDerivative<f32>,
{ {
@ -349,9 +356,10 @@ where
} }
} }
impl<T, C, D> SampleTwoDerivatives<T> for CurveReparamCurve<T, C, D> impl<V, T, C, D> SampleTwoDerivatives<T> for CurveReparamCurve<T, C, D>
where where
T: HasTangent, V: VectorSpace<Scalar = f32>,
T: HasTangent<Tangent = V>,
C: SampleTwoDerivatives<T>, C: SampleTwoDerivatives<T>,
D: SampleTwoDerivatives<f32>, D: SampleTwoDerivatives<f32>,
{ {
@ -386,9 +394,10 @@ where
// -- LinearReparamCurve // -- LinearReparamCurve
impl<T, C> SampleDerivative<T> for LinearReparamCurve<T, C> impl<V, T, C> SampleDerivative<T> for LinearReparamCurve<T, C>
where where
T: HasTangent, V: VectorSpace<Scalar = f32>,
T: HasTangent<Tangent = V>,
C: SampleDerivative<T>, C: SampleDerivative<T>,
{ {
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> { fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
@ -413,9 +422,10 @@ where
} }
} }
impl<T, C> SampleTwoDerivatives<T> for LinearReparamCurve<T, C> impl<V, T, C> SampleTwoDerivatives<T> for LinearReparamCurve<T, C>
where where
T: HasTangent, V: VectorSpace<Scalar = f32>,
T: HasTangent<Tangent = V>,
C: SampleTwoDerivatives<T>, C: SampleTwoDerivatives<T>,
{ {
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> { fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {

View File

@ -32,7 +32,7 @@ pub trait Ease: Sized {
fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self>; fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self>;
} }
impl<V: VectorSpace> Ease for V { impl<V: VectorSpace<Scalar = f32>> Ease for V {
fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> { fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
FunctionCurve::new(Interval::EVERYWHERE, move |t| V::lerp(start, end, t)) FunctionCurve::new(Interval::EVERYWHERE, move |t| V::lerp(start, end, t))
} }

View File

@ -40,11 +40,12 @@
use core::f32::consts::{PI, TAU}; use core::f32::consts::{PI, TAU};
use crate::{ops, primitives::*, NormedVectorSpace, Vec2, Vec3}; use crate::{ops, primitives::*, NormedVectorSpace, ScalarField, Vec2, Vec3};
use rand::{ use rand::{
distributions::{Distribution, WeightedIndex}, distributions::{Distribution, WeightedIndex},
Rng, Rng,
}; };
use rand_distr::uniform::SampleUniform;
/// Exposes methods to uniformly sample a variety of primitive shapes. /// Exposes methods to uniformly sample a variety of primitive shapes.
pub trait ShapeSample { pub trait ShapeSample {
@ -281,22 +282,24 @@ impl ShapeSample for Cuboid {
} }
/// Interior sampling for triangles which doesn't depend on the ambient dimension. /// Interior sampling for triangles which doesn't depend on the ambient dimension.
fn sample_triangle_interior<P: NormedVectorSpace, R: Rng + ?Sized>( fn sample_triangle_interior<P, R>(vertices: [P; 3], rng: &mut R) -> P
vertices: [P; 3], where
rng: &mut R, P: NormedVectorSpace,
) -> P { P::Scalar: SampleUniform + PartialOrd,
R: Rng + ?Sized,
{
let [a, b, c] = vertices; let [a, b, c] = vertices;
let ab = b - a; let ab = b - a;
let ac = c - a; let ac = c - a;
// Generate random points on a parallelepiped and reflect so that // Generate random points on a parallelepiped and reflect so that
// we can use the points that lie outside the triangle // we can use the points that lie outside the triangle
let u = rng.gen_range(0.0..=1.0); let u = rng.gen_range(P::Scalar::ZERO..=P::Scalar::ONE);
let v = rng.gen_range(0.0..=1.0); let v = rng.gen_range(P::Scalar::ZERO..=P::Scalar::ONE);
if u + v > 1. { if u + v > P::Scalar::ONE {
let u1 = 1. - v; let u1 = P::Scalar::ONE - v;
let v1 = 1. - u; let v1 = P::Scalar::ONE - u;
a + (ab * u1 + ac * v1) a + (ab * u1 + ac * v1)
} else { } else {
a + (ab * u + ac * v) a + (ab * u + ac * v)
@ -304,16 +307,18 @@ fn sample_triangle_interior<P: NormedVectorSpace, R: Rng + ?Sized>(
} }
/// Boundary sampling for triangles which doesn't depend on the ambient dimension. /// Boundary sampling for triangles which doesn't depend on the ambient dimension.
fn sample_triangle_boundary<P: NormedVectorSpace, R: Rng + ?Sized>( fn sample_triangle_boundary<P, R>(vertices: [P; 3], rng: &mut R) -> P
vertices: [P; 3], where
rng: &mut R, P: NormedVectorSpace,
) -> P { P::Scalar: SampleUniform + PartialOrd + for<'a> ::core::ops::AddAssign<&'a P::Scalar>,
R: Rng + ?Sized,
{
let [a, b, c] = vertices; let [a, b, c] = vertices;
let ab = b - a; let ab = b - a;
let ac = c - a; let ac = c - a;
let bc = c - b; 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()]) { if let Ok(dist) = WeightedIndex::new([ab.norm(), ac.norm(), bc.norm()]) {
match dist.sample(rng) { match dist.sample(rng) {

View File

@ -91,7 +91,7 @@ Set the `PKG_CONFIG_PATH` env var to `/usr/lib/<target>/pkgconfig/`. For example
export PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig/" export PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig/"
``` ```
## Arch / Manjaro ## [Arch](https://archlinux.org/) / [Manjaro](https://manjaro.org/)
```bash ```bash
sudo pacman -S libx11 pkgconf alsa-lib libxcursor libxrandr libxi 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: Depending on your graphics card, you may have to install one of the following:
`vulkan-radeon`, `vulkan-intel`, or `mesa-vulkan-drivers` `vulkan-radeon`, `vulkan-intel`, or `mesa-vulkan-drivers`
## Void ## [Void](https://voidlinux.org/)
```bash ```bash
sudo xbps-install -S pkgconf alsa-lib-devel libX11-devel eudev-libudev-devel 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) ## [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: Add a `shell.nix` file to the root of the project containing:
```nix ```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: 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)). 1. Install a system specific nixGL wrapper ([docs](https://github.com/nix-community/nixGL)).
* If you're running a nvidia GPU choose `nixVulkanNvidia`. - If you're running a nvidia GPU choose `nixVulkanNvidia`.
* Otherwise, choose another wrapper appropriate for your system. - 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. 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). 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. 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`. 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. is an example of packaging a Bevy program in nix.
## [OpenSUSE](https://www.opensuse.org/) ## [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 sudo zypper install libudev-devel gcc-c++ alsa-lib-devel
``` ```
## Gentoo ## [Gentoo](https://www.gentoo.org/)
```bash ```bash
sudo emerge --ask libX11 pkgconf alsa-lib sudo emerge --ask libX11 pkgconf alsa-lib

View File

@ -3,8 +3,8 @@
use bevy::{math::VectorSpace, prelude::*}; 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. // 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<Color> + Send + Sync + 'static {} trait CurveColor: VectorSpace<Scalar = f32> + Into<Color> + Send + Sync + 'static {}
impl<T: VectorSpace + Into<Color> + Send + Sync + 'static> CurveColor for T {} impl<T: VectorSpace<Scalar = f32> + Into<Color> + 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. // 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<Color> + Send + Sync + 'static {} trait MixedColor: Mix + Into<Color> + Send + Sync + 'static {}

View File

@ -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`.