Improve Bevy's double-precision story for third-party crates (#19194)
# Objective Certain classes of games, usually those with enormous worlds, require some amount of support for double-precision. Libraries like `big_space` exist to allow for large worlds while integrating cleanly with Bevy's primarily single-precision ecosystem, but even then, games will often still work directly in double-precision throughout the part of the pipeline that feeds into the Bevy interface. Currently, working with double-precision types in Bevy is a pain. `glam` provides types like `DVec3`, but Bevy doesn't provide double-precision analogs for `glam` wrappers like `Dir3`. This is mostly because doing so involves one of: - code duplication - generics - templates (like `glam` uses) - macros Each of these has issues that are enough to be deal-breakers as far as maintainability, usability or readability. To work around this, I'm putting together `bevy_dmath`, a crate that duplicates `bevy_math` types and functionality to allow downstream users to enjoy the ergonomics and power of `bevy_math` in double-precision. For the most part, it's a smooth process, but in order to fully integrate, there are some necessary changes that can only be made in `bevy_math`. ## Solution This PR addresses the first and easiest issue with downstream double-precision math support: `VectorSpace` currently can only represent vector spaces over `f32`. This automatically closes the door to double-precision curves, among other things. This restriction can be easily lifted by allowing vector spaces to specify the underlying scalar field. This PR adds a new trait `ScalarField` that satisfies the properties of a scalar field (the ones that can be upheld statically) and adds a new associated type `type Scalar: ScalarField` to `VectorSpace`. It's mostly an unintrusive change. The biggest annoyances are: - it touches a lot of curve code - `bevy_math::ops` doesn't support `f64`, so there are some annoying workarounds As far as curves code, I wanted to make this change unintrusive and bite-sized, so I'm trying to touch as little code as possible. To prove to myself it can be done, I went ahead and (*not* in this PR) migrated most of the curves API to support different `ScalarField`s and it went really smoothly! The ugliest thing was adding `P::Scalar: From<usize>` in several places. There's an argument to be made here that we should be using `num-traits`, but that's not immediately relevant. The point is that for now, the smallest change I could make was to go into every curve impl and make them generic over `VectorSpace<Scalar = f32>`. Curves work exactly like before and don't change the user API at all. # Follow-up - **Extend `bevy_math::ops` to work with `f64`.** `bevy_math::ops` is used all over, and if curves are ever going to support different `ScalarField` types, we'll need to be able to use the correct `std` or `libm` ops for `f64` types as well. Adding an `ops64` mod turned out to be really ugly, but I'll point out the maintenance burden is low because we're not going to be adding new floating-point ops anytime soon. Another solution is to build a floating-point trait that calls the right op variant and impl it for `f32` and `f64`. This reduces maintenance burden because on the off chance we ever *do* want to go modify it, it's all tied together: you can't change the interface on one without changing the trait, which forces you to update the other. A third option is to use `num-traits`, which is basically option 2 but someone else did the work for us. They already support `no_std` using `libm`, so it would be more or less a drop-in replacement. They're missing a couple floating-point ops like `floor` and `ceil`, but we could make our own floating-point traits for those (there's even the potential for upstreaming them into `num-traits`). - **Tweak curves to accept vector spaces over any `ScalarField`.** Curves are ready to support custom scalar types as soon as the bullet above is addressed. I will admit that the code is not as fun to look at: `P::Scalar` instead of `f32` everywhere. We could consider an alternate design where we use `f32` even to interpolate something like a `DVec3`, but personally I think that's a worse solution than parameterizing curves over the vector space's scalar type. At the end of the day, it's not really bad to deal with in my opinion... `ScalarType` supports enough operations that working with them is almost like working with raw float types, and it unlocks a whole ecosystem for games that want to use double-precision.
This commit is contained in:
parent
76b8310da5
commit
ddee5cca85
@ -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>,
|
||||||
|
@ -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],
|
||||||
|
@ -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,)+
|
||||||
};
|
};
|
||||||
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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> {
|
||||||
|
@ -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> {
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {}
|
||||||
|
@ -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`.
|
Loading…
Reference in New Issue
Block a user