From 577eafa654b00eb6350bb53094ee01a7477b9f57 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sat, 12 Dec 2015 22:17:03 +1100 Subject: [PATCH] Standardise macro use for code generation This moves lots of the common code generation patterns into a macros module. In doing so, the code can be greatly reduced in size. --- src/lib.rs | 2 + src/macros.rs | 203 +++++++++++++++++ src/point.rs | 541 +++++++--------------------------------------- src/quaternion.rs | 68 +----- src/vector.rs | 282 +++++------------------- 5 files changed, 348 insertions(+), 748 deletions(-) create mode 100644 src/macros.rs diff --git a/src/lib.rs b/src/lib.rs index 2a3f758..dc065bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,8 @@ pub use rust_num::{One, Zero, one, zero}; // Modules +mod macros; + mod array; mod matrix; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..938f437 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,203 @@ +// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, +// refer to the Cargo.toml file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Utility macros for code generation + +#![macro_use] + +/// Generates a binary operator implementation for the permutations of by-ref and by-val +macro_rules! impl_binary_operator { + // When the right operand is a scalar + (<$S:ident: $Constraint:ident> $Binop:ident<$Rhs:ident> for $Lhs:ty { + fn $binop:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } + }) => { + impl<$S: $Constraint> $Binop<$Rhs> for $Lhs { + type Output = $Output; + #[inline] + fn $binop(self, other: $Rhs) -> $Output { + let ($lhs, $rhs) = (self, other); $body + } + } + + impl<'a, $S: $Constraint> $Binop<$Rhs> for &'a $Lhs { + type Output = $Output; + #[inline] + fn $binop(self, other: $Rhs) -> $Output { + let ($lhs, $rhs) = (self, other); $body + } + } + }; + // When the right operand is a compound type + (<$S:ident: $Constraint:ident> $Binop:ident<$Rhs:ty> for $Lhs:ty { + fn $binop:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } + }) => { + impl<$S: $Constraint> $Binop<$Rhs> for $Lhs { + type Output = $Output; + #[inline] + fn $binop(self, other: $Rhs) -> $Output { + let ($lhs, $rhs) = (self, other); $body + } + } + + impl<'a, $S: $Constraint> $Binop<&'a $Rhs> for $Lhs { + type Output = $Output; + #[inline] + fn $binop(self, other: &'a $Rhs) -> $Output { + let ($lhs, $rhs) = (self, other); $body + } + } + + impl<'a, $S: $Constraint> $Binop<$Rhs> for &'a $Lhs { + type Output = $Output; + #[inline] + fn $binop(self, other: $Rhs) -> $Output { + let ($lhs, $rhs) = (self, other); $body + } + } + + impl<'a, 'b, $S: $Constraint> $Binop<&'a $Rhs> for &'b $Lhs { + type Output = $Output; + #[inline] + fn $binop(self, other: &'a $Rhs) -> $Output { + let ($lhs, $rhs) = (self, other); $body + } + } + }; +} + +macro_rules! fold_array { + (&$method:ident, { $x:expr, $y:expr }) => { $x.$method(&$y) }; + (&$method:ident, { $x:expr, $y:expr, $z:expr }) => { $x.$method(&$y).$method(&$z) }; + (&$method:ident, { $x:expr, $y:expr, $z:expr, $w:expr }) => { $x.$method(&$y).$method(&$z).$method(&$w) }; + ($method:ident, { $x:expr, $y:expr }) => { $x.$method($y) }; + ($method:ident, { $x:expr, $y:expr, $z:expr }) => { $x.$method($y).$method($z) }; + ($method:ident, { $x:expr, $y:expr, $z:expr, $w:expr }) => { $x.$method($y).$method($z).$method($w) }; +} + +/// Generate array conversion implementations for a compound array type +macro_rules! impl_fixed_array_conversions { + ($ArrayN:ident <$S:ident> { $($field:ident : $index:expr),+ }, $n:expr) => { + impl<$S> Into<[$S; $n]> for $ArrayN<$S> { + #[inline] + fn into(self) -> [$S; $n] { + match self { $ArrayN { $($field),+ } => [$($field),+] } + } + } + + impl<$S> AsRef<[$S; $n]> for $ArrayN<$S> { + #[inline] + fn as_ref(&self) -> &[$S; $n] { + unsafe { mem::transmute(self) } + } + } + + impl<$S> AsMut<[$S; $n]> for $ArrayN<$S> { + #[inline] + fn as_mut(&mut self) -> &mut [$S; $n] { + unsafe { mem::transmute(self) } + } + } + + impl<$S: Clone> From<[$S; $n]> for $ArrayN<$S> { + #[inline] + fn from(v: [$S; $n]) -> $ArrayN<$S> { + // We need to use a clone here because we can't pattern match on arrays yet + $ArrayN { $($field: v[$index].clone()),+ } + } + } + + impl<'a, $S> From<&'a [$S; $n]> for &'a $ArrayN<$S> { + #[inline] + fn from(v: &'a [$S; $n]) -> &'a $ArrayN<$S> { + unsafe { mem::transmute(v) } + } + } + + impl<'a, $S> From<&'a mut [$S; $n]> for &'a mut $ArrayN<$S> { + #[inline] + fn from(v: &'a mut [$S; $n]) -> &'a mut $ArrayN<$S> { + unsafe { mem::transmute(v) } + } + } + } +} + +/// Generate homogeneous tuple conversion implementations for a compound array type +macro_rules! impl_tuple_conversions { + ($ArrayN:ident <$S:ident> { $($field:ident),+ }, $Tuple:ty) => { + impl<$S> Into<$Tuple> for $ArrayN<$S> { + #[inline] + fn into(self) -> $Tuple { + match self { $ArrayN { $($field),+ } => ($($field),+) } + } + } + + impl<$S> AsRef<$Tuple> for $ArrayN<$S> { + #[inline] + fn as_ref(&self) -> &$Tuple { + unsafe { mem::transmute(self) } + } + } + + impl<$S> AsMut<$Tuple> for $ArrayN<$S> { + #[inline] + fn as_mut(&mut self) -> &mut $Tuple { + unsafe { mem::transmute(self) } + } + } + + impl<$S> From<$Tuple> for $ArrayN<$S> { + #[inline] + fn from(v: $Tuple) -> $ArrayN<$S> { + match v { ($($field),+) => $ArrayN { $($field: $field),+ } } + } + } + + impl<'a, $S> From<&'a $Tuple> for &'a $ArrayN<$S> { + #[inline] + fn from(v: &'a $Tuple) -> &'a $ArrayN<$S> { + unsafe { mem::transmute(v) } + } + } + + impl<'a, $S> From<&'a mut $Tuple> for &'a mut $ArrayN<$S> { + #[inline] + fn from(v: &'a mut $Tuple) -> &'a mut $ArrayN<$S> { + unsafe { mem::transmute(v) } + } + } + } +} + +/// Generates index operators for a compound type +macro_rules! impl_index_operators { + ($VectorN:ident<$S:ident>, $n:expr, $Output:ty, $I:ty) => { + impl<$S> Index<$I> for $VectorN<$S> { + type Output = $Output; + + #[inline] + fn index<'a>(&'a self, i: $I) -> &'a $Output { + let v: &[$S; $n] = self.as_ref(); &v[i] + } + } + + impl<$S> IndexMut<$I> for $VectorN<$S> { + #[inline] + fn index_mut<'a>(&'a mut self, i: $I) -> &'a mut $Output { + let v: &mut [$S; $n] = self.as_mut(); &mut v[i] + } + } + } +} diff --git a/src/point.rs b/src/point.rs index e5256c9..90f9b88 100644 --- a/src/point.rs +++ b/src/point.rs @@ -122,476 +122,99 @@ pub trait Point: Copy + Clone where /// This is a weird one, but its useful for plane calculations. fn dot(self, v: Self::Vector) -> Self::Scalar; - - #[must_use] - fn min(self, p: Self) -> Self; - - #[must_use] - fn max(self, p: Self) -> Self; } -impl Array for Point2 { - type Element = S; +macro_rules! impl_point { + ($PointN:ident { $($field:ident),+ }, $VectorN:ident, $n:expr) => { + impl Array for $PointN { + type Element = S; - fn sum(self) -> S { - self.x + self.y - } + #[inline] fn sum(self) -> S { fold_array!(add, { $(self.$field),+ }) } + #[inline] fn product(self) -> S { fold_array!(mul, { $(self.$field),+ }) } + #[inline] fn min(self) -> S { fold_array!(partial_min, { $(self.$field),+ }) } + #[inline] fn max(self) -> S { fold_array!(partial_max, { $(self.$field),+ }) } + } - fn product(self) -> S { - self.x * self.y - } + impl Point for $PointN { + type Scalar = S; + type Vector = $VectorN; - fn min(self) -> S { - self.x.partial_min(self.y) - } + #[inline] + fn origin() -> $PointN { + $PointN { $($field: S::zero()),+ } + } - fn max(self) -> S { - self.x.partial_max(self.y) + #[inline] + fn from_vec(v: $VectorN) -> $PointN { + $PointN::new($(v.$field),+) + } + + #[inline] + fn to_vec(self) -> $VectorN { + $VectorN::new($(self.$field),+) + } + + #[inline] fn mul_s(self, scalar: S) -> $PointN { self * scalar } + #[inline] fn div_s(self, scalar: S) -> $PointN { self / scalar } + #[inline] fn rem_s(self, scalar: S) -> $PointN { self % scalar } + #[inline] fn add_v(self, v: $VectorN) -> $PointN { self + v } + #[inline] fn sub_p(self, p: $PointN) -> $VectorN { self - p } + #[inline] fn mul_self_s(&mut self, scalar: S) { *self = *self * scalar; } + #[inline] fn div_self_s(&mut self, scalar: S) { *self = *self / scalar; } + #[inline] fn rem_self_s(&mut self, scalar: S) { *self = *self % scalar; } + #[inline] fn add_self_v(&mut self, vector: $VectorN) { *self = *self + vector; } + + #[inline] + fn dot(self, v: $VectorN) -> S { + $VectorN::new($(self.$field * v.$field),+).sum() + } + } + + impl ApproxEq for $PointN { + type Epsilon = S; + + #[inline] + fn approx_eq_eps(&self, other: &$PointN, epsilon: &S) -> bool { + $(self.$field.approx_eq_eps(&other.$field, epsilon))&&+ + } + } + + impl_binary_operator!( Add<$VectorN > for $PointN { + fn add(lhs, rhs) -> $PointN { $PointN::new($(lhs.$field + rhs.$field),+) } + }); + + impl_binary_operator!( Sub<$PointN > for $PointN { + fn sub(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field - rhs.$field),+) } + }); + + impl_binary_operator!( Mul for $PointN { + fn mul(point, scalar) -> $PointN { $PointN::new($(point.$field * scalar),+) } + }); + + impl_binary_operator!( Div for $PointN { + fn div(point, scalar) -> $PointN { $PointN::new($(point.$field / scalar),+) } + }); + + impl_binary_operator!( Rem for $PointN { + fn rem(point, scalar) -> $PointN { $PointN::new($(point.$field % scalar),+) } + }); + + impl_index_operators!($PointN, $n, S, usize); + impl_index_operators!($PointN, $n, [S], Range); + impl_index_operators!($PointN, $n, [S], RangeTo); + impl_index_operators!($PointN, $n, [S], RangeFrom); + impl_index_operators!($PointN, $n, [S], RangeFull); } } -impl Point for Point2 { - type Scalar = S; - type Vector = Vector2; +impl_point!(Point2 { x, y }, Vector2, 2); +impl_point!(Point3 { x, y, z }, Vector3, 3); - #[inline] - fn origin() -> Point2 { - Point2::new(S::zero(), S::zero()) - } +impl_fixed_array_conversions!(Point2 { x: 0, y: 1 }, 2); +impl_fixed_array_conversions!(Point3 { x: 0, y: 1, z: 2 }, 3); - #[inline] - fn from_vec(v: Vector2) -> Point2 { - Point2::new(v.x, v.y) - } - - #[inline] - fn to_vec(self) -> Vector2 { - Vector2::new(self.x, self.y) - } - - #[inline] fn mul_s(self, scalar: S) -> Point2 { self * scalar } - #[inline] fn div_s(self, scalar: S) -> Point2 { self / scalar } - #[inline] fn rem_s(self, scalar: S) -> Point2 { self % scalar } - #[inline] fn add_v(self, v: Vector2) -> Point2 { self + v } - #[inline] fn sub_p(self, p: Point2) -> Vector2 { self - p } - - #[inline] - fn mul_self_s(&mut self, scalar: S) { - self.x = self.x * scalar; - self.y = self.y * scalar; - } - - #[inline] - fn div_self_s(&mut self, scalar: S) { - self.x = self.x / scalar; - self.y = self.y / scalar; - } - - #[inline] - fn rem_self_s(&mut self, scalar: S) { - self.x = self.x % scalar; - self.y = self.y % scalar; - } - - #[inline] - fn add_self_v(&mut self, v: Vector2) { - self.x = self.x + v.x; - self.y = self.y + v.y; - } - - #[inline] - fn dot(self, v: Vector2) -> S { - self.x * v.x + - self.y * v.y - } - - #[inline] - fn min(self, p: Point2) -> Point2 { - Point2::new(self.x.partial_min(p.x), self.y.partial_min(p.y)) - } - - #[inline] - fn max(self, p: Point2) -> Point2 { - Point2::new(self.x.partial_max(p.x), self.y.partial_max(p.y)) - } -} - -impl ApproxEq for Point2 { - type Epsilon = S; - - #[inline] - fn approx_eq_eps(&self, other: &Point2, epsilon: &S) -> bool { - self.x.approx_eq_eps(&other.x, epsilon) && - self.y.approx_eq_eps(&other.y, epsilon) - } -} - -impl Array for Point3 { - type Element = S; - - fn sum(self) -> S { - self.x + self.y + self.z - } - - fn product(self) -> S { - self.x * self.y * self.z - } - - fn min(self) -> S { - self.x.partial_min(self.y).partial_min(self.z) - } - - fn max(self) -> S { - self.x.partial_max(self.y).partial_max(self.z) - } -} - -impl Point for Point3 { - type Scalar = S; - type Vector = Vector3; - - #[inline] - fn origin() -> Point3 { - Point3::new(S::zero(), S::zero(), S::zero()) - } - - #[inline] - fn from_vec(v: Vector3) -> Point3 { - Point3::new(v.x, v.y, v.z) - } - - #[inline] - fn to_vec(self) -> Vector3 { - Vector3::new(self.x, self.y, self.z) - } - - #[inline] fn mul_s(self, scalar: S) -> Point3 { self * scalar } - #[inline] fn div_s(self, scalar: S) -> Point3 { self / scalar } - #[inline] fn rem_s(self, scalar: S) -> Point3 { self % scalar } - #[inline] fn add_v(self, v: Vector3) -> Point3 { self + v } - #[inline] fn sub_p(self, p: Point3) -> Vector3 { self - p } - - #[inline] - fn mul_self_s(&mut self, scalar: S) { - self.x = self.x * scalar; - self.y = self.y * scalar; - self.z = self.z * scalar; - } - - #[inline] - fn div_self_s(&mut self, scalar: S) { - self.x = self.x / scalar; - self.y = self.y / scalar; - self.z = self.z / scalar; - } - - #[inline] - fn rem_self_s(&mut self, scalar: S) { - self.x = self.x % scalar; - self.y = self.y % scalar; - self.z = self.z % scalar; - } - - #[inline] - fn add_self_v(&mut self, v: Vector3) { - self.x = self.x + v.x; - self.y = self.y + v.y; - self.z = self.z + v.z; - } - - #[inline] - fn dot(self, v: Vector3) -> S { - self.x * v.x + - self.y * v.y + - self.z * v.z - } - - #[inline] - fn min(self, p: Point3) -> Point3 { - Point3::new(self.x.partial_min(p.x), self.y.partial_min(p.y), self.z.partial_min(p.z)) - } - - #[inline] - fn max(self, p: Point3) -> Point3 { - Point3::new(self.x.partial_max(p.x), self.y.partial_max(p.y), self.z.partial_max(p.z)) - } -} - -impl ApproxEq for Point3 { - type Epsilon = S; - - #[inline] - fn approx_eq_eps(&self, other: &Point3, epsilon: &S) -> bool { - self.x.approx_eq_eps(&other.x, epsilon) && - self.y.approx_eq_eps(&other.y, epsilon) && - self.z.approx_eq_eps(&other.z, epsilon) - } -} - - -macro_rules! impl_operators { - ($PointN:ident { $($field:ident),+ }, $VectorN:ident) => { - impl Mul for $PointN { - type Output = $PointN; - - #[inline] - fn mul(self, scalar: S) -> $PointN { - $PointN::new($(self.$field * scalar),+) - } - } - - impl Div for $PointN { - type Output = $PointN; - - #[inline] - fn div(self, scalar: S) -> $PointN { - $PointN::new($(self.$field / scalar),+) - } - } - - impl Rem for $PointN { - type Output = $PointN; - - #[inline] - fn rem(self, scalar: S) -> $PointN { - $PointN::new($(self.$field % scalar),+) - } - } - - impl<'a, S: BaseNum> Mul for &'a $PointN { - type Output = $PointN; - - #[inline] - fn mul(self, scalar: S) -> $PointN { - $PointN::new($(self.$field * scalar),+) - } - } - - impl<'a, S: BaseNum> Div for &'a $PointN { - type Output = $PointN; - - #[inline] - fn div(self, scalar: S) -> $PointN { - $PointN::new($(self.$field / scalar),+) - } - } - - impl<'a, S: BaseNum> Rem for &'a $PointN { - type Output = $PointN; - - #[inline] - fn rem(self, scalar: S) -> $PointN { - $PointN::new($(self.$field % scalar),+) - } - } - - impl Add<$VectorN> for $PointN { - type Output = $PointN; - - #[inline] - fn add(self, v: $VectorN) -> $PointN { - $PointN::new($(self.$field + v.$field),+) - } - } - - impl Sub<$PointN> for $PointN { - type Output = $VectorN; - - #[inline] - fn sub(self, p: $PointN) -> $VectorN { - $VectorN::new($(self.$field - p.$field),+) - } - } - - impl<'a, S: BaseNum> Add<&'a $VectorN> for $PointN { - type Output = $PointN; - - #[inline] - fn add(self, v: &'a $VectorN) -> $PointN { - $PointN::new($(self.$field + v.$field),+) - } - } - - impl<'a, S: BaseNum> Sub<&'a $PointN> for $PointN { - type Output = $VectorN; - - #[inline] - fn sub(self, p: &'a $PointN) -> $VectorN { - $VectorN::new($(self.$field - p.$field),+) - } - } - - impl<'a, S: BaseNum> Add<$VectorN> for &'a $PointN { - type Output = $PointN; - - #[inline] - fn add(self, v: $VectorN) -> $PointN { - $PointN::new($(self.$field + v.$field),+) - } - } - - impl<'a, S: BaseNum> Sub<$PointN> for &'a $PointN { - type Output = $VectorN; - - #[inline] - fn sub(self, p: $PointN) -> $VectorN { - $VectorN::new($(self.$field - p.$field),+) - } - } - - impl<'a, 'b, S: BaseNum> Add<&'a $VectorN> for &'b $PointN { - type Output = $PointN; - - #[inline] - fn add(self, v: &'a $VectorN) -> $PointN { - $PointN::new($(self.$field + v.$field),+) - } - } - - impl<'a, 'b, S: BaseNum> Sub<&'a $PointN> for &'b $PointN { - type Output = $VectorN; - - #[inline] - fn sub(self, p: &'a $PointN) -> $VectorN { - $VectorN::new($(self.$field - p.$field),+) - } - } - } -} - -impl_operators!(Point2 { x, y }, Vector2); -impl_operators!(Point3 { x, y, z }, Vector3); - -macro_rules! fixed_array_conversions { - ($PointN:ident <$S:ident> { $($field:ident : $index:expr),+ }, $n:expr) => { - impl<$S> Into<[$S; $n]> for $PointN<$S> { - #[inline] - fn into(self) -> [$S; $n] { - match self { $PointN { $($field),+ } => [$($field),+] } - } - } - - impl<$S> AsRef<[$S; $n]> for $PointN<$S> { - #[inline] - fn as_ref(&self) -> &[$S; $n] { - unsafe { mem::transmute(self) } - } - } - - impl<$S> AsMut<[$S; $n]> for $PointN<$S> { - #[inline] - fn as_mut(&mut self) -> &mut [$S; $n] { - unsafe { mem::transmute(self) } - } - } - - impl<$S: Clone> From<[$S; $n]> for $PointN<$S> { - #[inline] - fn from(v: [$S; $n]) -> $PointN<$S> { - // We need to use a clone here because we can't pattern match on arrays yet - $PointN { $($field: v[$index].clone()),+ } - } - } - - impl<'a, $S> From<&'a [$S; $n]> for &'a $PointN<$S> { - #[inline] - fn from(v: &'a [$S; $n]) -> &'a $PointN<$S> { - unsafe { mem::transmute(v) } - } - } - - impl<'a, $S> From<&'a mut [$S; $n]> for &'a mut $PointN<$S> { - #[inline] - fn from(v: &'a mut [$S; $n]) -> &'a mut $PointN<$S> { - unsafe { mem::transmute(v) } - } - } - } -} - -fixed_array_conversions!(Point2 { x:0, y:1 }, 2); -fixed_array_conversions!(Point3 { x:0, y:1, z:2 }, 3); - -macro_rules! tuple_conversions { - ($PointN:ident <$S:ident> { $($field:ident),+ }, $Tuple:ty) => { - impl<$S> Into<$Tuple> for $PointN<$S> { - #[inline] - fn into(self) -> $Tuple { - match self { $PointN { $($field),+ } => ($($field),+) } - } - } - - impl<$S> AsRef<$Tuple> for $PointN<$S> { - #[inline] - fn as_ref(&self) -> &$Tuple { - unsafe { mem::transmute(self) } - } - } - - impl<$S> AsMut<$Tuple> for $PointN<$S> { - #[inline] - fn as_mut(&mut self) -> &mut $Tuple { - unsafe { mem::transmute(self) } - } - } - - impl<$S> From<$Tuple> for $PointN<$S> { - #[inline] - fn from(v: $Tuple) -> $PointN<$S> { - // We need to use a clone here because we can't pattern match on arrays yet - match v { ($($field),+) => $PointN { $($field: $field),+ } } - } - } - - impl<'a, $S> From<&'a $Tuple> for &'a $PointN<$S> { - #[inline] - fn from(v: &'a $Tuple) -> &'a $PointN<$S> { - unsafe { mem::transmute(v) } - } - } - - impl<'a, $S> From<&'a mut $Tuple> for &'a mut $PointN<$S> { - #[inline] - fn from(v: &'a mut $Tuple) -> &'a mut $PointN<$S> { - unsafe { mem::transmute(v) } - } - } - } -} - -tuple_conversions!(Point2 { x, y }, (S, S)); -tuple_conversions!(Point3 { x, y, z }, (S, S, S)); - -macro_rules! index_operators { - ($PointN:ident<$S:ident>, $n:expr, $Output:ty, $I:ty) => { - impl<$S> Index<$I> for $PointN<$S> { - type Output = $Output; - - #[inline] - fn index<'a>(&'a self, i: $I) -> &'a $Output { - let v: &[$S; $n] = self.as_ref(); &v[i] - } - } - - impl<$S> IndexMut<$I> for $PointN<$S> { - #[inline] - fn index_mut<'a>(&'a mut self, i: $I) -> &'a mut $Output { - let v: &mut [$S; $n] = self.as_mut(); &mut v[i] - } - } - } -} - -index_operators!(Point2, 2, S, usize); -index_operators!(Point3, 3, S, usize); -index_operators!(Point2, 2, [S], Range); -index_operators!(Point3, 3, [S], Range); -index_operators!(Point2, 2, [S], RangeTo); -index_operators!(Point3, 3, [S], RangeTo); -index_operators!(Point2, 2, [S], RangeFrom); -index_operators!(Point3, 3, [S], RangeFrom); -index_operators!(Point2, 2, [S], RangeFull); -index_operators!(Point3, 3, [S], RangeFull); +impl_tuple_conversions!(Point2 { x, y }, (S, S)); +impl_tuple_conversions!(Point3 { x, y, z }, (S, S, S)); impl fmt::Debug for Point2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/quaternion.rs b/src/quaternion.rs index 96bfbb6..1f9abdc 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -123,75 +123,19 @@ impl<'a, S: BaseFloat> Neg for &'a Quaternion { fn neg(self) -> Quaternion { Quaternion::from_sv(-self.s, -self.v) } } -/// Generates a binary operator implementation for the permutations of by-ref and by-val -macro_rules! impl_binary_operator { - // When the right operand is a scalar - (<$S:ident> $Binop:ident<$Rhs:ident> for $Lhs:ty { fn $binop:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } }) => { - impl<$S: BaseFloat> $Binop<$Rhs> for $Lhs { - type Output = $Output; - #[inline] - fn $binop(self, other: $Rhs) -> $Output { - let ($lhs, $rhs) = (self, other); $body - } - } - - impl<'a, $S: BaseFloat> $Binop<$Rhs> for &'a $Lhs { - type Output = $Output; - #[inline] - fn $binop(self, other: $Rhs) -> $Output { - let ($lhs, $rhs) = (self, other); $body - } - } - }; - // When the right operand is a compound type - (<$S:ident> $Binop:ident<$Rhs:ty> for $Lhs:ty { fn $binop:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } }) => { - impl<$S: BaseFloat> $Binop<$Rhs> for $Lhs { - type Output = $Output; - #[inline] - fn $binop(self, other: $Rhs) -> $Output { - let ($lhs, $rhs) = (self, other); $body - } - } - - impl<'a, $S: BaseFloat> $Binop<&'a $Rhs> for $Lhs { - type Output = $Output; - #[inline] - fn $binop(self, other: &'a $Rhs) -> $Output { - let ($lhs, $rhs) = (self, other); $body - } - } - - impl<'a, $S: BaseFloat> $Binop<$Rhs> for &'a $Lhs { - type Output = $Output; - #[inline] - fn $binop(self, other: $Rhs) -> $Output { - let ($lhs, $rhs) = (self, other); $body - } - } - - impl<'a, 'b, $S: BaseFloat> $Binop<&'a $Rhs> for &'b $Lhs { - type Output = $Output; - #[inline] - fn $binop(self, other: &'a $Rhs) -> $Output { - let ($lhs, $rhs) = (self, other); $body - } - } - }; -} - -impl_binary_operator!( Mul for Quaternion { +impl_binary_operator!( Mul for Quaternion { fn mul(lhs, rhs) -> Quaternion { Quaternion::from_sv(lhs.s * rhs, lhs.v * rhs) } }); -impl_binary_operator!( Div for Quaternion { +impl_binary_operator!( Div for Quaternion { fn div(lhs, rhs) -> Quaternion { Quaternion::from_sv(lhs.s / rhs, lhs.v / rhs) } }); -impl_binary_operator!( Mul > for Quaternion { +impl_binary_operator!( Mul > for Quaternion { fn mul(lhs, rhs) -> Vector3 {{ let rhs = rhs.clone(); let two: S = cast(2i8).unwrap(); @@ -200,19 +144,19 @@ impl_binary_operator!( Mul > for Quaternion { }} }); -impl_binary_operator!( Add > for Quaternion { +impl_binary_operator!( Add > for Quaternion { fn add(lhs, rhs) -> Quaternion { Quaternion::from_sv(lhs.s + rhs.s, lhs.v + rhs.v) } }); -impl_binary_operator!( Sub > for Quaternion { +impl_binary_operator!( Sub > for Quaternion { fn sub(lhs, rhs) -> Quaternion { Quaternion::from_sv(lhs.s - rhs.s, lhs.v - rhs.v) } }); -impl_binary_operator!( Mul > for Quaternion { +impl_binary_operator!( Mul > for Quaternion { fn mul(lhs, rhs) -> Quaternion { Quaternion::new(lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z, lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y, diff --git a/src/vector.rs b/src/vector.rs index 5861a7f..063f2ec 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -204,7 +204,7 @@ pub trait Vector: Copy + Clone where #[inline] pub fn dot(a: V, b: V) -> V::Scalar { a.dot(b) } // Utility macro for generating associated functions for the vectors -macro_rules! vec { +macro_rules! impl_vector { ($VectorN:ident <$S:ident> { $($field:ident),+ }, $n:expr, $constructor:ident) => { #[derive(PartialEq, Eq, Copy, Clone, Hash, RustcEncodable, RustcDecodable)] pub struct $VectorN { $(pub $field: S),+ } @@ -242,10 +242,10 @@ macro_rules! vec { impl Array for $VectorN { type Element = S; - #[inline] fn sum(self) -> S where S: Add { fold!(add, { $(self.$field),+ }) } - #[inline] fn product(self) -> S where S: Mul { fold!(mul, { $(self.$field),+ }) } - #[inline] fn min(self) -> S where S: PartialOrd { fold!(partial_min, { $(self.$field),+ }) } - #[inline] fn max(self) -> S where S: PartialOrd { fold!(partial_max, { $(self.$field),+ }) } + #[inline] fn sum(self) -> S where S: Add { fold_array!(add, { $(self.$field),+ }) } + #[inline] fn product(self) -> S where S: Mul { fold_array!(mul, { $(self.$field),+ }) } + #[inline] fn min(self) -> S where S: PartialOrd { fold_array!(partial_min, { $(self.$field),+ }) } + #[inline] fn max(self) -> S where S: PartialOrd { fold_array!(partial_max, { $(self.$field),+ }) } } impl Vector for $VectorN { @@ -302,233 +302,61 @@ macro_rules! vec { $VectorN { $($field: rng.gen()),+ } } } + + impl_binary_operator!( Add for $VectorN { + fn add(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field + scalar),+) } + }); + impl_binary_operator!( Add<$VectorN > for $VectorN { + fn add(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field + rhs.$field),+) } + }); + + impl_binary_operator!( Sub for $VectorN { + fn sub(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field - scalar),+) } + }); + impl_binary_operator!( Sub<$VectorN > for $VectorN { + fn sub(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field - rhs.$field),+) } + }); + + impl_binary_operator!( Mul for $VectorN { + fn mul(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field * scalar),+) } + }); + impl_binary_operator!( Mul<$VectorN > for $VectorN { + fn mul(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field * rhs.$field),+) } + }); + + impl_binary_operator!( Div for $VectorN { + fn div(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field / scalar),+) } + }); + impl_binary_operator!( Div<$VectorN > for $VectorN { + fn div(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field / rhs.$field),+) } + }); + + impl_binary_operator!( Rem for $VectorN { + fn rem(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field % scalar),+) } + }); + impl_binary_operator!( Rem<$VectorN > for $VectorN { + fn rem(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field % rhs.$field),+) } + }); + + impl_index_operators!($VectorN, $n, S, usize); + impl_index_operators!($VectorN, $n, [S], Range); + impl_index_operators!($VectorN, $n, [S], RangeTo); + impl_index_operators!($VectorN, $n, [S], RangeFrom); + impl_index_operators!($VectorN, $n, [S], RangeFull); } } -macro_rules! impl_binary_operator { - ($Binop:ident :: $binop:ident, $VectorN:ident { $($field:ident),+ }) => { - impl $Binop for $VectorN { - type Output = $VectorN; +impl_vector!(Vector2 { x, y }, 2, vec2); +impl_vector!(Vector3 { x, y, z }, 3, vec3); +impl_vector!(Vector4 { x, y, z, w }, 4, vec4); - #[inline] - fn $binop(self, scalar: S) -> $VectorN { - $VectorN::new($(self.$field.$binop(scalar)),+) - } - } +impl_fixed_array_conversions!(Vector2 { x: 0, y: 1 }, 2); +impl_fixed_array_conversions!(Vector3 { x: 0, y: 1, z: 2 }, 3); +impl_fixed_array_conversions!(Vector4 { x: 0, y: 1, z: 2, w: 3 }, 4); - impl<'a, S: BaseNum> $Binop for &'a $VectorN { - type Output = $VectorN; - - #[inline] - fn $binop(self, scalar: S) -> $VectorN { - $VectorN::new($(self.$field.$binop(scalar)),+) - } - } - - impl $Binop<$VectorN> for $VectorN { - type Output = $VectorN; - - #[inline] - fn $binop(self, other: $VectorN) -> $VectorN { - $VectorN::new($(self.$field.$binop(other.$field)),+) - } - } - - impl<'a, S: BaseNum> $Binop<&'a $VectorN> for $VectorN { - type Output = $VectorN; - - #[inline] - fn $binop(self, other: &'a $VectorN) -> $VectorN { - $VectorN::new($(self.$field.$binop(other.$field)),+) - } - } - - impl<'a, S: BaseNum> $Binop<$VectorN> for &'a $VectorN { - type Output = $VectorN; - - #[inline] - fn $binop(self, other: $VectorN) -> $VectorN { - $VectorN::new($(self.$field.$binop(other.$field)),+) - } - } - - impl<'a, 'b, S: BaseNum> $Binop<&'a $VectorN> for &'b $VectorN { - type Output = $VectorN; - - #[inline] - fn $binop(self, other: &'a $VectorN) -> $VectorN { - $VectorN::new($(self.$field.$binop(other.$field)),+) - } - } - } -} - -impl_binary_operator!(Add::add, Vector2 { x, y }); -impl_binary_operator!(Add::add, Vector3 { x, y, z }); -impl_binary_operator!(Add::add, Vector4 { x, y, z, w }); -impl_binary_operator!(Sub::sub, Vector2 { x, y }); -impl_binary_operator!(Sub::sub, Vector3 { x, y, z }); -impl_binary_operator!(Sub::sub, Vector4 { x, y, z, w }); -impl_binary_operator!(Mul::mul, Vector2 { x, y }); -impl_binary_operator!(Mul::mul, Vector3 { x, y, z }); -impl_binary_operator!(Mul::mul, Vector4 { x, y, z, w }); -impl_binary_operator!(Div::div, Vector2 { x, y }); -impl_binary_operator!(Div::div, Vector3 { x, y, z }); -impl_binary_operator!(Div::div, Vector4 { x, y, z, w }); -impl_binary_operator!(Rem::rem, Vector2 { x, y }); -impl_binary_operator!(Rem::rem, Vector3 { x, y, z }); -impl_binary_operator!(Rem::rem, Vector4 { x, y, z, w }); - -macro_rules! fold { - (&$method:ident, { $x:expr, $y:expr }) => { $x.$method(&$y) }; - (&$method:ident, { $x:expr, $y:expr, $z:expr }) => { $x.$method(&$y).$method(&$z) }; - (&$method:ident, { $x:expr, $y:expr, $z:expr, $w:expr }) => { $x.$method(&$y).$method(&$z).$method(&$w) }; - ($method:ident, { $x:expr, $y:expr }) => { $x.$method($y) }; - ($method:ident, { $x:expr, $y:expr, $z:expr }) => { $x.$method($y).$method($z) }; - ($method:ident, { $x:expr, $y:expr, $z:expr, $w:expr }) => { $x.$method($y).$method($z).$method($w) }; -} - -vec!(Vector2 { x, y }, 2, vec2); -vec!(Vector3 { x, y, z }, 3, vec3); -vec!(Vector4 { x, y, z, w }, 4, vec4); - -macro_rules! fixed_array_conversions { - ($VectorN:ident <$S:ident> { $($field:ident : $index:expr),+ }, $n:expr) => { - - impl<$S> Into<[$S; $n]> for $VectorN<$S> { - #[inline] - fn into(self) -> [$S; $n] { - match self { $VectorN { $($field),+ } => [$($field),+] } - } - } - - impl<$S> AsRef<[$S; $n]> for $VectorN<$S> { - #[inline] - fn as_ref(&self) -> &[$S; $n] { - unsafe { mem::transmute(self) } - } - } - - impl<$S> AsMut<[$S; $n]> for $VectorN<$S> { - #[inline] - fn as_mut(&mut self) -> &mut [$S; $n] { - unsafe { mem::transmute(self) } - } - } - - impl<$S: Clone> From<[$S; $n]> for $VectorN<$S> { - #[inline] - fn from(v: [$S; $n]) -> $VectorN<$S> { - // We need to use a clone here because we can't pattern match on arrays yet - $VectorN { $($field: v[$index].clone()),+ } - } - } - - impl<'a, $S> From<&'a [$S; $n]> for &'a $VectorN<$S> { - #[inline] - fn from(v: &'a [$S; $n]) -> &'a $VectorN<$S> { - unsafe { mem::transmute(v) } - } - } - - impl<'a, $S> From<&'a mut [$S; $n]> for &'a mut $VectorN<$S> { - #[inline] - fn from(v: &'a mut [$S; $n]) -> &'a mut $VectorN<$S> { - unsafe { mem::transmute(v) } - } - } - } -} - -fixed_array_conversions!(Vector2 { x:0, y:1 }, 2); -fixed_array_conversions!(Vector3 { x:0, y:1, z:2 }, 3); -fixed_array_conversions!(Vector4 { x:0, y:1, z:2, w:3 }, 4); - -macro_rules! tuple_conversions { - ($VectorN:ident <$S:ident> { $($field:ident),+ }, $Tuple:ty) => { - impl<$S> Into<$Tuple> for $VectorN<$S> { - #[inline] - fn into(self) -> $Tuple { - match self { $VectorN { $($field),+ } => ($($field),+) } - } - } - - impl<$S> AsRef<$Tuple> for $VectorN<$S> { - #[inline] - fn as_ref(&self) -> &$Tuple { - unsafe { mem::transmute(self) } - } - } - - impl<$S> AsMut<$Tuple> for $VectorN<$S> { - #[inline] - fn as_mut(&mut self) -> &mut $Tuple { - unsafe { mem::transmute(self) } - } - } - - impl<$S> From<$Tuple> for $VectorN<$S> { - #[inline] - fn from(v: $Tuple) -> $VectorN<$S> { - match v { ($($field),+) => $VectorN { $($field: $field),+ } } - } - } - - impl<'a, $S> From<&'a $Tuple> for &'a $VectorN<$S> { - #[inline] - fn from(v: &'a $Tuple) -> &'a $VectorN<$S> { - unsafe { mem::transmute(v) } - } - } - - impl<'a, $S> From<&'a mut $Tuple> for &'a mut $VectorN<$S> { - #[inline] - fn from(v: &'a mut $Tuple) -> &'a mut $VectorN<$S> { - unsafe { mem::transmute(v) } - } - } - } -} - -tuple_conversions!(Vector2 { x, y }, (S, S)); -tuple_conversions!(Vector3 { x, y, z }, (S, S, S)); -tuple_conversions!(Vector4 { x, y, z, w }, (S, S, S, S)); - -macro_rules! index_operators { - ($VectorN:ident<$S:ident>, $n:expr, $Output:ty, $I:ty) => { - impl<$S> Index<$I> for $VectorN<$S> { - type Output = $Output; - - #[inline] - fn index<'a>(&'a self, i: $I) -> &'a $Output { - let v: &[$S; $n] = self.as_ref(); &v[i] - } - } - - impl<$S> IndexMut<$I> for $VectorN<$S> { - #[inline] - fn index_mut<'a>(&'a mut self, i: $I) -> &'a mut $Output { - let v: &mut [$S; $n] = self.as_mut(); &mut v[i] - } - } - } -} - -index_operators!(Vector2, 2, S, usize); -index_operators!(Vector3, 3, S, usize); -index_operators!(Vector4, 4, S, usize); -index_operators!(Vector2, 2, [S], Range); -index_operators!(Vector3, 3, [S], Range); -index_operators!(Vector4, 4, [S], Range); -index_operators!(Vector2, 2, [S], RangeTo); -index_operators!(Vector3, 3, [S], RangeTo); -index_operators!(Vector4, 4, [S], RangeTo); -index_operators!(Vector2, 2, [S], RangeFrom); -index_operators!(Vector3, 3, [S], RangeFrom); -index_operators!(Vector4, 4, [S], RangeFrom); -index_operators!(Vector2, 2, [S], RangeFull); -index_operators!(Vector3, 3, [S], RangeFull); -index_operators!(Vector4, 4, [S], RangeFull); +impl_tuple_conversions!(Vector2 { x, y }, (S, S)); +impl_tuple_conversions!(Vector3 { x, y, z }, (S, S, S)); +impl_tuple_conversions!(Vector4 { x, y, z, w }, (S, S, S, S)); /// Operations specific to numeric two-dimensional vectors. impl Vector2 {