From cc5472416b99bde132439b36e6123fcde6a451a7 Mon Sep 17 00:00:00 2001 From: Matthew Weatherley Date: Wed, 27 Mar 2024 08:32:34 -0400 Subject: [PATCH 1/6] Moved and expanded Point -> VectorSpace --- crates/bevy_color/src/lib.rs | 2 +- crates/bevy_math/src/common_traits.rs | 143 +++++++++++++++++++++++++ crates/bevy_math/src/cubic_splines.rs | 98 +++++++---------- crates/bevy_math/src/lib.rs | 2 + crates/bevy_math/src/shape_sampling.rs | 75 ++++++++++++- examples/animation/color_animation.rs | 6 +- 6 files changed, 260 insertions(+), 66 deletions(-) create mode 100644 crates/bevy_math/src/common_traits.rs diff --git a/crates/bevy_color/src/lib.rs b/crates/bevy_color/src/lib.rs index 648dbd0ace6bf..c373db9b1eb57 100644 --- a/crates/bevy_color/src/lib.rs +++ b/crates/bevy_color/src/lib.rs @@ -239,7 +239,7 @@ macro_rules! impl_componentwise_point { } } - impl bevy_math::cubic_splines::Point for $ty {} + impl bevy_math::VectorSpace for $ty {} }; } diff --git a/crates/bevy_math/src/common_traits.rs b/crates/bevy_math/src/common_traits.rs new file mode 100644 index 0000000000000..ee1c7a5fcbe6b --- /dev/null +++ b/crates/bevy_math/src/common_traits.rs @@ -0,0 +1,143 @@ +use glam::{Quat, Vec2, Vec3, Vec3A, Vec4}; +use std::fmt::Debug; +use std::ops::{Add, Div, Mul, Sub}; + +/// A type that supports the mathematical operations of a vector space, irrespective of dimension. +pub trait VectorSpace: + Mul + + Div + + Add + + Sub + + Default + + Debug + + Clone + + Copy +{ + /// 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 + } +} + +impl VectorSpace for Quat {} +impl VectorSpace for Vec4 {} +impl VectorSpace for Vec3 {} +impl VectorSpace for Vec3A {} +impl VectorSpace for Vec2 {} +impl VectorSpace for f32 {} + +/// A normed type, where extracting a nonnegative size for each element makes sense. +pub trait Normed { + /// 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 + /// [`Normed::norm`]. + #[inline] + fn norm_squared(&self) -> f32 { + self.norm() * self.norm() + } +} + +impl Normed for Quat { + #[inline] + fn norm(&self) -> f32 { + self.length() + } + + #[inline] + fn norm_squared(&self) -> f32 { + self.length_squared() + } +} + +impl Normed for Vec4 { + #[inline] + fn norm(&self) -> f32 { + self.length() + } + + #[inline] + fn norm_squared(&self) -> f32 { + self.length_squared() + } +} + +impl Normed for Vec3 { + #[inline] + fn norm(&self) -> f32 { + self.length() + } + + #[inline] + fn norm_squared(&self) -> f32 { + self.length_squared() + } +} + +impl Normed for Vec3A { + #[inline] + fn norm(&self) -> f32 { + self.length() + } + + #[inline] + fn norm_squared(&self) -> f32 { + self.length_squared() + } +} + +impl Normed for Vec2 { + #[inline] + fn norm(&self) -> f32 { + self.length() + } + + #[inline] + fn norm_squared(&self) -> f32 { + self.length_squared() + } +} + +impl Normed for f32 { + #[inline] + fn norm(&self) -> f32 { + self.abs() + } + + #[inline] + fn norm_squared(&self) -> f32 { + self * self + } +} + +/// A type that supports the operations of a normed vector space; i.e. both those of [`Normed`] +/// and those of [`VectorSpace`]. The implementor must guarantee that the axioms of a normed vector +/// space are satisfied. +pub trait NormedVectorSpace: VectorSpace + Normed { + /// 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() + } +} + +impl NormedVectorSpace for Quat {} +impl NormedVectorSpace for Vec4 {} +impl NormedVectorSpace for Vec3 {} +impl NormedVectorSpace for Vec3A {} +impl NormedVectorSpace for Vec2 {} +impl NormedVectorSpace for f32 {} diff --git a/crates/bevy_math/src/cubic_splines.rs b/crates/bevy_math/src/cubic_splines.rs index b7796b68f036c..c1da3184f7be8 100644 --- a/crates/bevy_math/src/cubic_splines.rs +++ b/crates/bevy_math/src/cubic_splines.rs @@ -1,34 +1,10 @@ //! Provides types for building cubic splines for rendering curves and use with animation easing. -use std::{ - fmt::Debug, - iter::once, - ops::{Add, Div, Mul, Sub}, -}; +use std::{fmt::Debug, iter::once}; -use glam::{Quat, Vec2, Vec3, Vec3A, Vec4}; -use thiserror::Error; - -/// A point in space of any dimension that supports the math ops needed for cubic spline -/// interpolation. -pub trait Point: - Mul - + Div - + Add - + Sub - + Default - + Debug - + Clone - + Copy -{ -} +use crate::{Vec2, VectorSpace}; -impl Point for Quat {} -impl Point for Vec4 {} -impl Point for Vec3 {} -impl Point for Vec3A {} -impl Point for Vec2 {} -impl Point for f32 {} +use thiserror::Error; /// A spline composed of a single cubic Bezier curve. /// @@ -64,11 +40,11 @@ impl Point for f32 {} /// let bezier = CubicBezier::new(points).to_curve(); /// let positions: Vec<_> = bezier.iter_positions(100).collect(); /// ``` -pub struct CubicBezier { +pub struct CubicBezier { control_points: Vec<[P; 4]>, } -impl CubicBezier

{ +impl CubicBezier

{ /// Create a new cubic Bezier curve from sets of control points. pub fn new(control_points: impl Into>) -> Self { Self { @@ -76,7 +52,7 @@ impl CubicBezier

{ } } } -impl CubicGenerator

for CubicBezier

{ +impl CubicGenerator

for CubicBezier

{ #[inline] fn to_curve(&self) -> CubicCurve

{ // A derivation for this matrix can be found in "General Matrix Representations for B-splines" by Kaihuai Qin. @@ -135,10 +111,10 @@ impl CubicGenerator

for CubicBezier

{ /// let hermite = CubicHermite::new(points, tangents).to_curve(); /// let positions: Vec<_> = hermite.iter_positions(100).collect(); /// ``` -pub struct CubicHermite { +pub struct CubicHermite { control_points: Vec<(P, P)>, } -impl CubicHermite

{ +impl CubicHermite

{ /// Create a new Hermite curve from sets of control points. pub fn new( control_points: impl IntoIterator, @@ -149,7 +125,7 @@ impl CubicHermite

{ } } } -impl CubicGenerator

for CubicHermite

{ +impl CubicGenerator

for CubicHermite

{ #[inline] fn to_curve(&self) -> CubicCurve

{ let char_matrix = [ @@ -201,12 +177,12 @@ impl CubicGenerator

for CubicHermite

{ /// let cardinal = CubicCardinalSpline::new(0.3, points).to_curve(); /// let positions: Vec<_> = cardinal.iter_positions(100).collect(); /// ``` -pub struct CubicCardinalSpline { +pub struct CubicCardinalSpline { tension: f32, control_points: Vec

, } -impl CubicCardinalSpline

{ +impl CubicCardinalSpline

{ /// Build a new Cardinal spline. pub fn new(tension: f32, control_points: impl Into>) -> Self { Self { @@ -223,7 +199,7 @@ impl CubicCardinalSpline

{ } } } -impl CubicGenerator

for CubicCardinalSpline

{ +impl CubicGenerator

for CubicCardinalSpline

{ #[inline] fn to_curve(&self) -> CubicCurve

{ let s = self.tension; @@ -288,10 +264,10 @@ impl CubicGenerator

for CubicCardinalSpline

{ /// let b_spline = CubicBSpline::new(points).to_curve(); /// let positions: Vec<_> = b_spline.iter_positions(100).collect(); /// ``` -pub struct CubicBSpline { +pub struct CubicBSpline { control_points: Vec

, } -impl CubicBSpline

{ +impl CubicBSpline

{ /// Build a new B-Spline. pub fn new(control_points: impl Into>) -> Self { Self { @@ -299,7 +275,7 @@ impl CubicBSpline

{ } } } -impl CubicGenerator

for CubicBSpline

{ +impl CubicGenerator

for CubicBSpline

{ #[inline] fn to_curve(&self) -> CubicCurve

{ // A derivation for this matrix can be found in "General Matrix Representations for B-splines" by Kaihuai Qin. @@ -405,12 +381,12 @@ pub enum CubicNurbsError { /// .to_curve(); /// let positions: Vec<_> = nurbs.iter_positions(100).collect(); /// ``` -pub struct CubicNurbs { +pub struct CubicNurbs { control_points: Vec

, weights: Vec, knots: Vec, } -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. @@ -570,7 +546,7 @@ impl CubicNurbs

{ ] } } -impl RationalGenerator

for CubicNurbs

{ +impl RationalGenerator

for CubicNurbs

{ #[inline] fn to_curve(&self) -> RationalCurve

{ let segments = self @@ -609,10 +585,10 @@ impl RationalGenerator

for CubicNurbs

{ /// /// ### Continuity /// The curve is C0 continuous, meaning it has no holes or jumps. -pub struct LinearSpline { +pub struct LinearSpline { points: Vec

, } -impl LinearSpline

{ +impl LinearSpline

{ /// Create a new linear spline pub fn new(points: impl Into>) -> Self { Self { @@ -620,7 +596,7 @@ impl LinearSpline

{ } } } -impl CubicGenerator

for LinearSpline

{ +impl CubicGenerator

for LinearSpline

{ #[inline] fn to_curve(&self) -> CubicCurve

{ let segments = self @@ -639,7 +615,7 @@ impl CubicGenerator

for LinearSpline

{ } /// Implement this on cubic splines that can generate a cubic curve from their spline parameters. -pub trait CubicGenerator { +pub trait CubicGenerator { /// Build a [`CubicCurve`] by computing the interpolation coefficients for each curve segment. fn to_curve(&self) -> CubicCurve

; } @@ -649,11 +625,11 @@ pub trait CubicGenerator { /// /// Segments can be chained together to form a longer compound curve. #[derive(Clone, Debug, Default, PartialEq)] -pub struct CubicSegment { +pub struct CubicSegment { coeff: [P; 4], } -impl CubicSegment

{ +impl CubicSegment

{ /// Instantaneous position of a point at parametric value `t`. #[inline] pub fn position(&self, t: f32) -> P { @@ -807,11 +783,11 @@ impl CubicSegment { /// Use any struct that implements the [`CubicGenerator`] trait to create a new curve, such as /// [`CubicBezier`]. #[derive(Clone, Debug, PartialEq)] -pub struct CubicCurve { +pub struct CubicCurve { segments: Vec>, } -impl CubicCurve

{ +impl CubicCurve

{ /// Compute the position of a point on the cubic curve at the parametric value `t`. /// /// Note that `t` varies from `0..=(n_points - 3)`. @@ -912,13 +888,13 @@ impl CubicCurve

{ } } -impl Extend> for CubicCurve

{ +impl Extend> for CubicCurve

{ fn extend>>(&mut self, iter: T) { self.segments.extend(iter); } } -impl IntoIterator for CubicCurve

{ +impl IntoIterator for CubicCurve

{ type IntoIter = > as IntoIterator>::IntoIter; type Item = CubicSegment

; @@ -929,7 +905,7 @@ impl IntoIterator for CubicCurve

{ } /// Implement this on cubic splines that can generate a rational cubic curve from their spline parameters. -pub trait RationalGenerator { +pub trait RationalGenerator { /// Build a [`RationalCurve`] by computing the interpolation coefficients for each curve segment. fn to_curve(&self) -> RationalCurve

; } @@ -939,7 +915,7 @@ pub trait RationalGenerator { /// /// Segments can be chained together to form a longer compound curve. #[derive(Clone, Debug, Default, PartialEq)] -pub struct RationalSegment { +pub struct RationalSegment { /// The coefficients matrix of the cubic curve. coeff: [P; 4], /// The homogeneous weight coefficients. @@ -948,7 +924,7 @@ pub struct RationalSegment { knot_span: f32, } -impl RationalSegment

{ +impl RationalSegment

{ /// Instantaneous position of a point at parametric value `t` in `[0, knot_span)`. #[inline] pub fn position(&self, t: f32) -> P { @@ -1066,11 +1042,11 @@ impl RationalSegment

{ /// Use any struct that implements the [`RationalGenerator`] trait to create a new curve, such as /// [`CubicNurbs`], or convert [`CubicCurve`] using `into/from`. #[derive(Clone, Debug, PartialEq)] -pub struct RationalCurve { +pub struct RationalCurve { segments: Vec>, } -impl RationalCurve

{ +impl RationalCurve

{ /// Compute the position of a point on the curve at the parametric value `t`. /// /// Note that `t` varies from `0..=(n_points - 3)`. @@ -1190,13 +1166,13 @@ impl RationalCurve

{ } } -impl Extend> for RationalCurve

{ +impl Extend> for RationalCurve

{ fn extend>>(&mut self, iter: T) { self.segments.extend(iter); } } -impl IntoIterator for RationalCurve

{ +impl IntoIterator for RationalCurve

{ type IntoIter = > as IntoIterator>::IntoIter; type Item = RationalSegment

; @@ -1206,7 +1182,7 @@ impl IntoIterator for RationalCurve

{ } } -impl From> for RationalSegment

{ +impl From> for RationalSegment

{ fn from(value: CubicSegment

) -> Self { Self { coeff: value.coeff, @@ -1216,7 +1192,7 @@ impl From> for RationalSegment

{ } } -impl From> for RationalCurve

{ +impl From> for RationalCurve

{ fn from(value: CubicCurve

) -> Self { Self { segments: value.segments.into_iter().map(Into::into).collect(), diff --git a/crates/bevy_math/src/lib.rs b/crates/bevy_math/src/lib.rs index 9d7962e7fe993..f9d8dd4bdd7e8 100644 --- a/crates/bevy_math/src/lib.rs +++ b/crates/bevy_math/src/lib.rs @@ -13,6 +13,7 @@ mod affine3; mod aspect_ratio; pub mod bounding; +mod common_traits; pub mod cubic_splines; mod direction; pub mod primitives; @@ -24,6 +25,7 @@ mod shape_sampling; pub use affine3::*; pub use aspect_ratio::AspectRatio; +pub use common_traits::*; pub use direction::*; pub use ray::{Ray2d, Ray3d}; pub use rects::*; diff --git a/crates/bevy_math/src/shape_sampling.rs b/crates/bevy_math/src/shape_sampling.rs index eff4a0898f37b..6f801493359ad 100644 --- a/crates/bevy_math/src/shape_sampling.rs +++ b/crates/bevy_math/src/shape_sampling.rs @@ -1,6 +1,6 @@ use std::f32::consts::{PI, TAU}; -use crate::{primitives::*, Vec2, Vec3}; +use crate::{primitives::*, NormedVectorSpace, Vec2, Vec3}; use rand::{ distributions::{Distribution, WeightedIndex}, Rng, @@ -142,6 +142,79 @@ 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 { + let [a, b, c] = vertices; + let ab = b - a; + let ac = c - a; + + // Generate random points on a parallelipiped 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); + + if u + v > 1. { + let u1 = 1. - v; + let v1 = 1. - u; + ab * u1 + ac * v1 + } else { + ab * u + ac * v + } +} + +// Boundary sampling for triangles which doesn't depend on the ambient dimension. +fn sample_triangle_boundary( + vertices: [P; 3], + rng: &mut R, +) -> P { + 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); + + if let Ok(dist) = WeightedIndex::new([ab.norm(), ac.norm(), bc.norm()]) { + match dist.sample(rng) { + 0 => a.lerp(b, t), + 1 => a.lerp(c, t), + 2 => b.lerp(c, t), + _ => unreachable!(), + } + } else { + // This should only occur when the triangle is 0-dimensional degenerate + // so this is actually the correct result. + a + } +} + +impl ShapeSample for Triangle2d { + type Output = Vec2; + + fn sample_interior(&self, rng: &mut R) -> Self::Output { + sample_triangle_interior(self.vertices, rng) + } + + fn sample_boundary(&self, rng: &mut R) -> Self::Output { + sample_triangle_boundary(self.vertices, rng) + } +} + +impl ShapeSample for Triangle3d { + type Output = Vec3; + + fn sample_interior(&self, rng: &mut R) -> Self::Output { + sample_triangle_interior(self.vertices, rng) + } + + fn sample_boundary(&self, rng: &mut R) -> Self::Output { + sample_triangle_boundary(self.vertices, rng) + } +} + impl ShapeSample for Cylinder { type Output = Vec3; diff --git a/examples/animation/color_animation.rs b/examples/animation/color_animation.rs index 605e8f395e2a3..48c5ecc25f089 100644 --- a/examples/animation/color_animation.rs +++ b/examples/animation/color_animation.rs @@ -1,10 +1,10 @@ //! Demonstrates how to animate colors in different color spaces using mixing and splines. -use bevy::{math::cubic_splines::Point, 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. -trait CurveColor: Point + Into + Send + Sync + 'static {} -impl + Send + Sync + 'static> CurveColor for T {} +trait CurveColor: VectorSpace + Into + Send + Sync + 'static {} +impl + 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 {} From c20d49645f9da5701dbce5972e74f64112bb0da0 Mon Sep 17 00:00:00 2001 From: Matthew Weatherley Date: Wed, 27 Mar 2024 09:17:27 -0400 Subject: [PATCH 2/6] Rolled norm into NormedVectorSpace --- crates/bevy_math/src/common_traits.rs | 82 ++++++++++++--------------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/crates/bevy_math/src/common_traits.rs b/crates/bevy_math/src/common_traits.rs index ee1c7a5fcbe6b..22c4b7099e483 100644 --- a/crates/bevy_math/src/common_traits.rs +++ b/crates/bevy_math/src/common_traits.rs @@ -32,112 +32,102 @@ impl VectorSpace for Vec3A {} impl VectorSpace for Vec2 {} impl VectorSpace for f32 {} -/// A normed type, where extracting a nonnegative size for each element makes sense. -pub trait Normed { +/// A type that supports the operations of a normed vector space; i.e. a norm operation in addition +/// to those of [`VectorSpace`]. The implementor must guarantee that the axioms of a normed vector +/// space are satisfied. +pub trait NormedVectorSpace: VectorSpace { /// The size of this element. The return value should always be nonnegative. - fn norm(&self) -> f32; + fn norm(self) -> f32; /// The squared norm of this element. Computing this is often faster than computing /// [`Normed::norm`]. #[inline] - fn norm_squared(&self) -> f32 { + fn norm_squared(self) -> f32 { self.norm() * self.norm() } -} -impl Normed for Quat { + /// The distance between this element and another, as determined by the norm. #[inline] - fn norm(&self) -> f32 { - self.length() + 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 norm_squared(&self) -> f32 { - self.length_squared() + fn distance_squared(self, rhs: Self) -> f32 { + (rhs - self).norm_squared() } } -impl Normed for Vec4 { +impl NormedVectorSpace for Quat { #[inline] - fn norm(&self) -> f32 { + fn norm(self) -> f32 { self.length() } #[inline] - fn norm_squared(&self) -> f32 { + fn norm_squared(self) -> f32 { self.length_squared() } } -impl Normed for Vec3 { +impl NormedVectorSpace for Vec4 { #[inline] - fn norm(&self) -> f32 { + fn norm(self) -> f32 { self.length() } #[inline] - fn norm_squared(&self) -> f32 { + fn norm_squared(self) -> f32 { self.length_squared() } } -impl Normed for Vec3A { +impl NormedVectorSpace for Vec3 { #[inline] - fn norm(&self) -> f32 { + fn norm(self) -> f32 { self.length() } #[inline] - fn norm_squared(&self) -> f32 { + fn norm_squared(self) -> f32 { self.length_squared() } } -impl Normed for Vec2 { +impl NormedVectorSpace for Vec3A { #[inline] - fn norm(&self) -> f32 { + fn norm(self) -> f32 { self.length() } #[inline] - fn norm_squared(&self) -> f32 { + fn norm_squared(self) -> f32 { self.length_squared() } } -impl Normed for f32 { +impl NormedVectorSpace for Vec2 { #[inline] - fn norm(&self) -> f32 { - self.abs() + fn norm(self) -> f32 { + self.length() } #[inline] - fn norm_squared(&self) -> f32 { - self * self + fn norm_squared(self) -> f32 { + self.length_squared() } } -/// A type that supports the operations of a normed vector space; i.e. both those of [`Normed`] -/// and those of [`VectorSpace`]. The implementor must guarantee that the axioms of a normed vector -/// space are satisfied. -pub trait NormedVectorSpace: VectorSpace + Normed { - /// The distance between this element and another, as determined by the norm. +impl NormedVectorSpace for f32 { #[inline] - fn distance(&self, rhs: Self) -> f32 { - (rhs - *self).norm() + fn norm(self) -> f32 { + self.abs() } - /// 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() + fn norm_squared(self) -> f32 { + self * self } -} - -impl NormedVectorSpace for Quat {} -impl NormedVectorSpace for Vec4 {} -impl NormedVectorSpace for Vec3 {} -impl NormedVectorSpace for Vec3A {} -impl NormedVectorSpace for Vec2 {} -impl NormedVectorSpace for f32 {} +} \ No newline at end of file From cea6e471f4d831198e5ba16aa01e7e7031f4e271 Mon Sep 17 00:00:00 2001 From: Matthew Weatherley Date: Wed, 27 Mar 2024 09:53:44 -0400 Subject: [PATCH 3/6] Whitespace --- crates/bevy_math/src/common_traits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_math/src/common_traits.rs b/crates/bevy_math/src/common_traits.rs index 22c4b7099e483..48cf921e25809 100644 --- a/crates/bevy_math/src/common_traits.rs +++ b/crates/bevy_math/src/common_traits.rs @@ -130,4 +130,4 @@ impl NormedVectorSpace for f32 { fn norm_squared(self) -> f32 { self * self } -} \ No newline at end of file +} From e0ff266c7c810f0206afe9dea40587def75c3b6b Mon Sep 17 00:00:00 2001 From: Matthew Weatherley Date: Wed, 27 Mar 2024 16:44:48 -0400 Subject: [PATCH 4/6] Added VectorSpace::ZERO and rewrote docs. --- crates/bevy_color/src/laba.rs | 6 +-- crates/bevy_color/src/lib.rs | 10 +++-- crates/bevy_color/src/linear_rgba.rs | 6 +-- crates/bevy_color/src/oklaba.rs | 6 +-- crates/bevy_color/src/srgba.rs | 5 ++- crates/bevy_color/src/xyza.rs | 4 +- crates/bevy_math/src/common_traits.rs | 62 +++++++++++++++++++++++---- 7 files changed, 74 insertions(+), 25 deletions(-) diff --git a/crates/bevy_color/src/laba.rs b/crates/bevy_color/src/laba.rs index cec3019a319c3..5c95472932420 100644 --- a/crates/bevy_color/src/laba.rs +++ b/crates/bevy_color/src/laba.rs @@ -1,6 +1,6 @@ use crate::{ - impl_componentwise_point, Alpha, ClampColor, Hsla, Hsva, Hwba, LinearRgba, Luminance, Mix, - Oklaba, Srgba, StandardColor, Xyza, + impl_componentwise_vector_space, Alpha, ClampColor, Hsla, Hsva, Hwba, LinearRgba, Luminance, + Mix, Oklaba, Srgba, StandardColor, Xyza, }; use bevy_reflect::prelude::*; @@ -29,7 +29,7 @@ pub struct Laba { impl StandardColor for Laba {} -impl_componentwise_point!(Laba, [lightness, a, b, alpha]); +impl_componentwise_vector_space!(Laba, [lightness, a, b, alpha]); impl Laba { /// Construct a new [`Laba`] color from components. diff --git a/crates/bevy_color/src/lib.rs b/crates/bevy_color/src/lib.rs index c373db9b1eb57..6e8de8865e158 100644 --- a/crates/bevy_color/src/lib.rs +++ b/crates/bevy_color/src/lib.rs @@ -163,7 +163,7 @@ where { } -macro_rules! impl_componentwise_point { +macro_rules! impl_componentwise_vector_space { ($ty: ident, [$($element: ident),+]) => { impl std::ops::Add for $ty { type Output = Self; @@ -239,8 +239,12 @@ macro_rules! impl_componentwise_point { } } - impl bevy_math::VectorSpace for $ty {} + impl bevy_math::VectorSpace for $ty { + const ZERO: Self = Self { + $($element: 0.0,)+ + }; + } }; } -pub(crate) use impl_componentwise_point; +pub(crate) use impl_componentwise_vector_space; diff --git a/crates/bevy_color/src/linear_rgba.rs b/crates/bevy_color/src/linear_rgba.rs index 8f0cd071f1b33..9f2065ddce4d2 100644 --- a/crates/bevy_color/src/linear_rgba.rs +++ b/crates/bevy_color/src/linear_rgba.rs @@ -1,6 +1,6 @@ use crate::{ - color_difference::EuclideanDistance, impl_componentwise_point, Alpha, ClampColor, Luminance, - Mix, StandardColor, + color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ClampColor, + Luminance, Mix, StandardColor, }; use bevy_math::Vec4; use bevy_reflect::prelude::*; @@ -32,7 +32,7 @@ pub struct LinearRgba { impl StandardColor for LinearRgba {} -impl_componentwise_point!(LinearRgba, [red, green, blue, alpha]); +impl_componentwise_vector_space!(LinearRgba, [red, green, blue, alpha]); impl LinearRgba { /// A fully black color with full alpha. diff --git a/crates/bevy_color/src/oklaba.rs b/crates/bevy_color/src/oklaba.rs index 06a2d6775349f..65cd88f7758b7 100644 --- a/crates/bevy_color/src/oklaba.rs +++ b/crates/bevy_color/src/oklaba.rs @@ -1,6 +1,6 @@ use crate::{ - color_difference::EuclideanDistance, impl_componentwise_point, Alpha, ClampColor, Hsla, Hsva, - Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza, + color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ClampColor, Hsla, + Hsva, Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza, }; use bevy_reflect::prelude::*; @@ -29,7 +29,7 @@ pub struct Oklaba { impl StandardColor for Oklaba {} -impl_componentwise_point!(Oklaba, [lightness, a, b, alpha]); +impl_componentwise_vector_space!(Oklaba, [lightness, a, b, alpha]); impl Oklaba { /// Construct a new [`Oklaba`] color from components. diff --git a/crates/bevy_color/src/srgba.rs b/crates/bevy_color/src/srgba.rs index 31fafd0802dc7..43c82fda25d68 100644 --- a/crates/bevy_color/src/srgba.rs +++ b/crates/bevy_color/src/srgba.rs @@ -1,6 +1,7 @@ use crate::color_difference::EuclideanDistance; use crate::{ - impl_componentwise_point, Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor, Xyza, + impl_componentwise_vector_space, Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor, + Xyza, }; use bevy_math::Vec4; use bevy_reflect::prelude::*; @@ -31,7 +32,7 @@ pub struct Srgba { impl StandardColor for Srgba {} -impl_componentwise_point!(Srgba, [red, green, blue, alpha]); +impl_componentwise_vector_space!(Srgba, [red, green, blue, alpha]); impl Srgba { // The standard VGA colors, with alpha set to 1.0. diff --git a/crates/bevy_color/src/xyza.rs b/crates/bevy_color/src/xyza.rs index bc0c50e29b8bb..d3baf464f472e 100644 --- a/crates/bevy_color/src/xyza.rs +++ b/crates/bevy_color/src/xyza.rs @@ -1,5 +1,5 @@ use crate::{ - impl_componentwise_point, Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor, + impl_componentwise_vector_space, Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor, }; use bevy_reflect::prelude::*; @@ -28,7 +28,7 @@ pub struct Xyza { impl StandardColor for Xyza {} -impl_componentwise_point!(Xyza, [x, y, z, alpha]); +impl_componentwise_vector_space!(Xyza, [x, y, z, alpha]); impl Xyza { /// Construct a new [`Xyza`] color from components. diff --git a/crates/bevy_math/src/common_traits.rs b/crates/bevy_math/src/common_traits.rs index 48cf921e25809..a66c51bb45ed0 100644 --- a/crates/bevy_math/src/common_traits.rs +++ b/crates/bevy_math/src/common_traits.rs @@ -3,6 +3,22 @@ use std::fmt::Debug; use std::ops::{Add, Div, Mul, Sub}; /// A type that supports the mathematical operations of a 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` +/// - Addition and subtraction +/// +/// 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 * (-1) == 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 + Div @@ -13,6 +29,9 @@ pub trait VectorSpace: + 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. @@ -25,22 +44,47 @@ pub trait VectorSpace: } } -impl VectorSpace for Quat {} -impl VectorSpace for Vec4 {} -impl VectorSpace for Vec3 {} -impl VectorSpace for Vec3A {} -impl VectorSpace for Vec2 {} -impl VectorSpace for f32 {} +// This is cursed and we should probably remove Quat from these. +impl VectorSpace for Quat { + const ZERO: Self = Quat::from_xyzw(0., 0., 0., 0.); +} + +impl VectorSpace for Vec4 { + const ZERO: Self = Vec4::ZERO; +} + +impl VectorSpace for Vec3 { + const ZERO: Self = Vec3::ZERO; +} + +impl VectorSpace for Vec3A { + const ZERO: Self = Vec3A::ZERO; +} + +impl VectorSpace for Vec2 { + const ZERO: Self = Vec2::ZERO; +} + +impl VectorSpace for f32 { + const ZERO: Self = 0.0; +} /// A type that supports the operations of a normed vector space; i.e. a norm operation in addition -/// to those of [`VectorSpace`]. The implementor must guarantee that the axioms of a normed vector -/// space are satisfied. +/// 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 - /// [`Normed::norm`]. + /// [`NormedVectorSpace::norm`]. #[inline] fn norm_squared(self) -> f32 { self.norm() * self.norm() From 977dd077e9343b49b98cdcc3f8e49e79c905eb6b Mon Sep 17 00:00:00 2001 From: Matty Date: Thu, 28 Mar 2024 06:23:44 -0400 Subject: [PATCH 5/6] Doc tweaks from code review Co-authored-by: Zachary Harrold --- crates/bevy_math/src/common_traits.rs | 2 +- crates/bevy_math/src/shape_sampling.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_math/src/common_traits.rs b/crates/bevy_math/src/common_traits.rs index a66c51bb45ed0..693e6e4deb368 100644 --- a/crates/bevy_math/src/common_traits.rs +++ b/crates/bevy_math/src/common_traits.rs @@ -2,7 +2,7 @@ use glam::{Quat, Vec2, Vec3, Vec3A, Vec4}; use std::fmt::Debug; use std::ops::{Add, Div, Mul, Sub}; -/// A type that supports the mathematical operations of a 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: /// - Scalar multiplication and division on the right by elements of `f32` /// - Addition and subtraction diff --git a/crates/bevy_math/src/shape_sampling.rs b/crates/bevy_math/src/shape_sampling.rs index 6f801493359ad..1479aa7209df0 100644 --- a/crates/bevy_math/src/shape_sampling.rs +++ b/crates/bevy_math/src/shape_sampling.rs @@ -142,7 +142,7 @@ 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( vertices: [P; 3], rng: &mut R, @@ -165,7 +165,7 @@ fn sample_triangle_interior( } } -// 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( vertices: [P; 3], rng: &mut R, From 221f1ec29427b14f54ca93dcc950812929069130 Mon Sep 17 00:00:00 2001 From: Matthew Weatherley Date: Thu, 28 Mar 2024 08:08:55 -0400 Subject: [PATCH 6/6] Added Neg to VectorSpace requirements and impl macro for colors --- crates/bevy_color/src/lib.rs | 10 ++++++++++ crates/bevy_math/src/common_traits.rs | 7 +++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/bevy_color/src/lib.rs b/crates/bevy_color/src/lib.rs index 6e8de8865e158..b81fcf7955452 100644 --- a/crates/bevy_color/src/lib.rs +++ b/crates/bevy_color/src/lib.rs @@ -181,6 +181,16 @@ macro_rules! impl_componentwise_vector_space { } } + impl std::ops::Neg for $ty { + type Output = Self; + + fn neg(self) -> Self::Output { + Self::Output { + $($element: -self.$element,)+ + } + } + } + impl std::ops::Sub for $ty { type Output = Self; diff --git a/crates/bevy_math/src/common_traits.rs b/crates/bevy_math/src/common_traits.rs index a66c51bb45ed0..77c99e4af638c 100644 --- a/crates/bevy_math/src/common_traits.rs +++ b/crates/bevy_math/src/common_traits.rs @@ -1,17 +1,19 @@ use glam::{Quat, Vec2, Vec3, Vec3A, Vec4}; use std::fmt::Debug; -use std::ops::{Add, Div, Mul, Sub}; +use std::ops::{Add, Div, Mul, Neg, Sub}; /// A type that supports the mathematical operations of a 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 * (-1) == 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`. /// - (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`. @@ -24,6 +26,7 @@ pub trait VectorSpace: + Div + Add + Sub + + Neg + Default + Debug + Clone