diff --git a/src/macros.rs b/src/macros.rs index ed2223a..c4d60a2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -95,6 +95,26 @@ macro_rules! impl_operator { } } }; + // When the left operand is a scalar + ($Op:ident<$Rhs:ident<$S:ident>> for $Lhs:ty { + fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } + }) => { + impl $Op<$Rhs<$S>> for $Lhs { + type Output = $Output; + #[inline] + fn $op(self, other: $Rhs<$S>) -> $Output { + let ($lhs, $rhs) = (self, other); $body + } + } + + impl<'a> $Op<&'a $Rhs<$S>> for $Lhs { + type Output = $Output; + #[inline] + fn $op(self, other: &'a $Rhs<$S>) -> $Output { + let ($lhs, $rhs) = (self, other); $body + } + } + }; } macro_rules! impl_assignment_operator { diff --git a/src/point.rs b/src/point.rs index 362f1b8..33dfc14 100644 --- a/src/point.rs +++ b/src/point.rs @@ -172,6 +172,19 @@ macro_rules! impl_point { fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ } }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_scalar_ops!($PointN { $($field),+ }); + impl_index_operators!($PointN, $n, S, usize); impl_index_operators!($PointN, $n, [S], Range); impl_index_operators!($PointN, $n, [S], RangeTo); @@ -180,6 +193,20 @@ macro_rules! impl_point { } } +macro_rules! impl_scalar_ops { + ($PointN:ident<$S:ident> { $($field:ident),+ }) => { + impl_operator!(Mul<$PointN<$S>> for $S { + fn mul(scalar, vector) -> $PointN<$S> { $PointN::new($(scalar * vector.$field),+) } + }); + impl_operator!(Div<$PointN<$S>> for $S { + fn div(scalar, vector) -> $PointN<$S> { $PointN::new($(scalar / vector.$field),+) } + }); + impl_operator!(Rem<$PointN<$S>> for $S { + fn rem(scalar, vector) -> $PointN<$S> { $PointN::new($(scalar % vector.$field),+) } + }); + }; +} + impl_point!(Point2 { x, y }, Vector2, 2); impl_point!(Point3 { x, y, z }, Vector3, 3); diff --git a/src/quaternion.rs b/src/quaternion.rs index a20690d..b249960 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -167,6 +167,31 @@ impl_operator!( Mul > for Quaternion { } }); +macro_rules! impl_scalar_mul { + ($S:ident) => { + impl_operator!(Mul> for $S { + fn mul(scalar, quat) -> Quaternion<$S> { + Quaternion::from_sv(scalar * quat.s, scalar * quat.v) + } + }); + }; +} + +macro_rules! impl_scalar_div { + ($S:ident) => { + impl_operator!(Div> for $S { + fn div(scalar, quat) -> Quaternion<$S> { + Quaternion::from_sv(scalar / quat.s, scalar / quat.v) + } + }); + }; +} + +impl_scalar_mul!(f32); +impl_scalar_mul!(f64); +impl_scalar_div!(f32); +impl_scalar_div!(f64); + impl ApproxEq for Quaternion { type Epsilon = S; diff --git a/src/vector.rs b/src/vector.rs index 91abfcb..75e4e1e 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -244,6 +244,7 @@ macro_rules! impl_vector { impl_operator!( Mul<$VectorN > for $VectorN { fn mul(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field * rhs.$field),+) } }); + impl_assignment_operator!( MulAssign for $VectorN { fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ } }); @@ -277,6 +278,19 @@ macro_rules! impl_vector { fn rem_assign(&mut self, other) { $(self.$field %= other.$field);+ } }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_scalar_ops!($VectorN { $($field),+ }); + impl_index_operators!($VectorN, $n, S, usize); impl_index_operators!($VectorN, $n, [S], Range); impl_index_operators!($VectorN, $n, [S], RangeTo); @@ -285,6 +299,26 @@ macro_rules! impl_vector { } } +macro_rules! impl_scalar_ops { + ($VectorN:ident<$S:ident> { $($field:ident),+ }) => { + impl_operator!(Add<$VectorN<$S>> for $S { + fn add(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar + vector.$field),+) } + }); + impl_operator!(Sub<$VectorN<$S>> for $S { + fn sub(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar - vector.$field),+) } + }); + impl_operator!(Mul<$VectorN<$S>> for $S { + fn mul(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar * vector.$field),+) } + }); + impl_operator!(Div<$VectorN<$S>> for $S { + fn div(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar / vector.$field),+) } + }); + impl_operator!(Rem<$VectorN<$S>> for $S { + fn rem(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar % vector.$field),+) } + }); + }; +} + impl_vector!(Vector2 { x, y }, 2, vec2); impl_vector!(Vector3 { x, y, z }, 3, vec3); impl_vector!(Vector4 { x, y, z, w }, 4, vec4); diff --git a/tests/point.rs b/tests/point.rs index 3dbfef6..fcd2714 100644 --- a/tests/point.rs +++ b/tests/point.rs @@ -16,11 +16,62 @@ extern crate cgmath; -use cgmath::Point3; +use cgmath::{Point2, Point3}; use cgmath::ApproxEq; +macro_rules! impl_test_mul { + ($PointN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( + // point * scalar ops + assert_eq!($v * $s, $PointN::new($($v.$field * $s),+)); + assert_eq!($s * $v, $PointN::new($($s * $v.$field),+)); + assert_eq!(&$v * $s, $v * $s); + assert_eq!($s * &$v, $s * $v); + // commutativity + assert_eq!($v * $s, $s * $v); + ) +} + +macro_rules! impl_test_div { + ($PointN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( + // point / scalar ops + assert_eq!($v / $s, $PointN::new($($v.$field / $s),+)); + assert_eq!($s / $v, $PointN::new($($s / $v.$field),+)); + assert_eq!(&$v / $s, $v / $s); + assert_eq!($s / &$v, $s / $v); + ) +} + +macro_rules! impl_test_rem { + ($PointN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( + // point % scalar ops + assert_eq!($v % $s, $PointN::new($($v.$field % $s),+)); + assert_eq!($s % $v, $PointN::new($($s % $v.$field),+)); + assert_eq!(&$v % $s, $v % $s); + assert_eq!($s % &$v, $s % $v); + ) +} + #[test] fn test_homogeneous() { let p = Point3::new(1.0f64, 2.0f64, 3.0f64); assert!(p.approx_eq(&Point3::from_homogeneous(p.to_homogeneous()))); } + +#[test] +fn test_mul() { + impl_test_mul!(Point3 { x, y, z }, 2.0f32, Point3::new(2.0f32, 4.0, 6.0)); + impl_test_mul!(Point2 { x, y }, 2.0f32, Point2::new(2.0f32, 4.0)); +} + +#[test] +fn test_div() { + impl_test_div!(Point3 { x, y, z }, 2.0f32, Point3::new(2.0f32, 4.0, 6.0)); + impl_test_div!(Point2 { x, y }, 2.0f32, Point2::new(2.0f32, 4.0)); +} + +#[test] +fn test_rem() { + impl_test_rem!(Point3 { x, y, z }, 2.0f32, Point3::new(2.0f32, 4.0, 6.0)); + impl_test_rem!(Point2 { x, y }, 2.0f32, Point2::new(2.0f32, 4.0)); +} + diff --git a/tests/quaternion.rs b/tests/quaternion.rs index e97abab..effa31e 100644 --- a/tests/quaternion.rs +++ b/tests/quaternion.rs @@ -16,6 +16,42 @@ #[macro_use] extern crate cgmath; +macro_rules! impl_test_mul { + ($s:expr, $v:expr) => ( + // point * scalar ops + assert_eq!($v * $s, Quaternion::from_sv($v.s * $s, $v.v * $s)); + assert_eq!($s * $v, Quaternion::from_sv($s * $v.s, $s * $v.v)); + assert_eq!(&$v * $s, $v * $s); + assert_eq!($s * &$v, $s * $v); + // commutativity + assert_eq!($v * $s, $s * $v); + ) +} + +macro_rules! impl_test_div { + ($s:expr, $v:expr) => ( + // point / scalar ops + assert_eq!($v / $s, Quaternion::from_sv($v.s / $s, $v.v / $s)); + assert_eq!($s / $v, Quaternion::from_sv($s / $v.s, $s / $v.v)); + assert_eq!(&$v / $s, $v / $s); + assert_eq!($s / &$v, $s / $v); + ) +} + +mod operators { + use cgmath::*; + + #[test] + fn test_mul() { + impl_test_mul!(2.0f32, Quaternion::from_euler(rad(1f32), rad(1f32), rad(1f32))); + } + + #[test] + fn test_div() { + impl_test_div!(2.0f32, Quaternion::from_euler(rad(1f32), rad(1f32), rad(1f32))); + } +} + mod to_from_euler { use std::f32; diff --git a/tests/vector.rs b/tests/vector.rs index cd76a1b..b076f8b 100644 --- a/tests/vector.rs +++ b/tests/vector.rs @@ -33,6 +33,122 @@ fn test_from_value() { assert_eq!(Vector4::from_value(76.5f64), Vector4::new(76.5f64, 76.5f64, 76.5f64, 76.5f64)); } +macro_rules! impl_test_add { + ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( + // vector + vector ops + assert_eq!($v + $v, $VectorN::new($($v.$field + $v.$field),+)); + assert_eq!(&$v + &$v, $v + $v); + assert_eq!(&$v + $v, $v + $v); + assert_eq!($v + &$v, $v + $v); + // vector + scalar ops + assert_eq!($v + $s, $VectorN::new($($v.$field + $s),+)); + assert_eq!($s + $v, $VectorN::new($($s + $v.$field),+)); + assert_eq!(&$v + $s, $v + $s); + assert_eq!($s + &$v, $s + $v); + // commutativity + assert_eq!($v + $s, $s + $v); + ) +} + +macro_rules! impl_test_sub { + ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( + // vector - vector ops + assert_eq!($v - $v, $VectorN::new($($v.$field - $v.$field),+)); + assert_eq!(&$v - &$v, $v - $v); + assert_eq!(&$v - $v, $v - $v); + assert_eq!($v - &$v, $v - $v); + // vector - scalar ops + assert_eq!($v - $s, $VectorN::new($($v.$field - $s),+)); + assert_eq!($s - $v, $VectorN::new($($s - $v.$field),+)); + assert_eq!(&$v - $s, $v - $s); + assert_eq!($s - &$v, $s - $v); + // commutativity + assert_eq!($v - $s, -($s - $v)); + ) +} + +macro_rules! impl_test_mul { + ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( + // vector * vector ops + assert_eq!($v * $v, $VectorN::new($($v.$field * $v.$field),+)); + assert_eq!(&$v * &$v, $v * $v); + assert_eq!(&$v * $v, $v * $v); + assert_eq!($v * &$v, $v * $v); + // vector * scalar ops + assert_eq!($v * $s, $VectorN::new($($v.$field * $s),+)); + assert_eq!($s * $v, $VectorN::new($($s * $v.$field),+)); + assert_eq!(&$v * $s, $v * $s); + assert_eq!($s * &$v, $s * $v); + // commutativity + assert_eq!($v * $s, $s * $v); + ) +} + +macro_rules! impl_test_div { + ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( + // vector / vector ops + assert_eq!($v / $v, $VectorN::new($($v.$field / $v.$field),+)); + assert_eq!(&$v / &$v, $v / $v); + assert_eq!(&$v / $v, $v / $v); + assert_eq!($v / &$v, $v / $v); + // vector / scalar ops + assert_eq!($v / $s, $VectorN::new($($v.$field / $s),+)); + assert_eq!($s / $v, $VectorN::new($($s / $v.$field),+)); + assert_eq!(&$v / $s, $v / $s); + assert_eq!($s / &$v, $s / $v); + ) +} + +macro_rules! impl_test_rem { + ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( + // vector % vector ops + assert_eq!($v % $v, $VectorN::new($($v.$field % $v.$field),+)); + assert_eq!(&$v % &$v, $v % $v); + assert_eq!(&$v % $v, $v % $v); + assert_eq!($v % &$v, $v % $v); + // vector % scalar ops + assert_eq!($v % $s, $VectorN::new($($v.$field % $s),+)); + assert_eq!($s % $v, $VectorN::new($($s % $v.$field),+)); + assert_eq!(&$v % $s, $v % $s); + assert_eq!($s % &$v, $s % $v); + ) +} + +#[test] +fn test_add() { + impl_test_add!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); + impl_test_add!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); + impl_test_add!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); +} + +#[test] +fn test_sub() { + impl_test_sub!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); + impl_test_sub!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); + impl_test_sub!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); +} + +#[test] +fn test_mul() { + impl_test_mul!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); + impl_test_mul!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); + impl_test_mul!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); +} + +#[test] +fn test_div() { + impl_test_div!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); + impl_test_div!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); + impl_test_div!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); +} + +#[test] +fn test_rem() { + impl_test_rem!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); + impl_test_rem!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); + impl_test_rem!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); +} + #[test] fn test_dot() { assert_eq!(Vector2::new(1isize, 2isize).dot(Vector2::new(3isize, 4isize)), 11isize);