pub const trait LerpUtil { fn lerp(self, from: Self, to: Self) -> Self; } const impl LerpUtil for f32 { /// linear interpolation /// from * (1.0 - self) + to * self fn lerp(self, from: Self, to: Self) -> Self { from + (to - from) * self } } macro_rules! impl_op { ($T:ident $op:ident $fn:ident $opa:ident $fna:ident; $($field:ident)*) => { #[allow(non_snake_case)] mod ${concat($T, _op_, $fn, _impl)} { use super::*; #[allow(unused_imports)] use std::ops::*; const impl $op for $T { type Output = Self; fn $fn(self, rhs: Self) -> Self::Output { Self { $($field: self.$field.$fn(rhs.$field),)* } } } const impl $opa for $T { fn $fna(&mut self, rhs: Self) { *self = self.$fn(rhs); } } const impl $op for $T { type Output = Self; fn $fn(self, rhs: f32) -> Self::Output { Self { $($field: self.$field.$fn(rhs),)* } } } const impl $op<$T> for f32 { type Output = $T; fn $fn(self, rhs: $T) -> Self::Output { $T { $($field: self.$fn(rhs.$field),)* } } } const impl $opa for $T { fn $fna(&mut self, rhs: f32) { *self = self.$fn(rhs); } } } }; // Without the `f32` operations, for a type whose fields are not all the // same kind of number: there is nothing a bare float means to a fraction // and an offset at once. (same $T:ident $op:ident $fn:ident $opa:ident $fna:ident; $($field:ident)*) => { #[allow(non_snake_case)] mod ${concat($T, _op_, $fn, _same_impl)} { use super::*; #[allow(unused_imports)] use std::ops::*; const impl $op for $T { type Output = Self; fn $fn(self, rhs: Self) -> Self::Output { Self { $($field: self.$field.$fn(rhs.$field),)* } } } const impl $opa for $T { fn $fna(&mut self, rhs: Self) { *self = self.$fn(rhs); } } } }; (same $T:ident $op:ident $fn:ident; $($field:ident)*) => { impl_op!(same $T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*); }; ($T:ident $op:ident $fn:ident; $($field:ident)*) => { impl_op!($T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*); }; (impl $op:ident for $T:ident: $fn:ident $($field:ident)*) => { impl_op!($T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*); }; } pub(crate) use impl_op; /// `Index` for a pair, which is how every pair here is read by axis. /// The generics clause is given in braces where the type has one. macro_rules! impl_axis_index { ($({$($gen:tt)*})? $T:ty => $Out:ty) => { const impl $(<$($gen)*>)? std::ops::Index for $T { type Output = $Out; fn index(&self, axis: crate::Axis) -> &$Out { match axis { crate::Axis::X => &self.x, crate::Axis::Y => &self.y, } } } const impl $(<$($gen)*>)? std::ops::IndexMut for $T { fn index_mut(&mut self, axis: crate::Axis) -> &mut $Out { match axis { crate::Axis::X => &mut self.x, crate::Axis::Y => &mut self.y, } } } }; } pub(crate) use impl_axis_index;