Merge pull request #293 from bitshifter/scalarops

Support for scalar on the lhs of arithmetic operators
This commit is contained in:
Brendan Zabarauskas 2016-01-02 14:38:42 +11:00
commit 58cde94eb7
7 changed files with 310 additions and 1 deletions

View file

@ -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 {

View file

@ -172,6 +172,19 @@ macro_rules! impl_point {
fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ }
});
impl_scalar_ops!($PointN<usize> { $($field),+ });
impl_scalar_ops!($PointN<u8> { $($field),+ });
impl_scalar_ops!($PointN<u16> { $($field),+ });
impl_scalar_ops!($PointN<u32> { $($field),+ });
impl_scalar_ops!($PointN<u64> { $($field),+ });
impl_scalar_ops!($PointN<isize> { $($field),+ });
impl_scalar_ops!($PointN<i8> { $($field),+ });
impl_scalar_ops!($PointN<i16> { $($field),+ });
impl_scalar_ops!($PointN<i32> { $($field),+ });
impl_scalar_ops!($PointN<i64> { $($field),+ });
impl_scalar_ops!($PointN<f32> { $($field),+ });
impl_scalar_ops!($PointN<f64> { $($field),+ });
impl_index_operators!($PointN<S>, $n, S, usize);
impl_index_operators!($PointN<S>, $n, [S], Range<usize>);
impl_index_operators!($PointN<S>, $n, [S], RangeTo<usize>);
@ -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);

View file

@ -167,6 +167,31 @@ impl_operator!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
}
});
macro_rules! impl_scalar_mul {
($S:ident) => {
impl_operator!(Mul<Quaternion<$S>> 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<Quaternion<$S>> 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<S: BaseFloat> ApproxEq for Quaternion<S> {
type Epsilon = S;

View file

@ -244,6 +244,7 @@ macro_rules! impl_vector {
impl_operator!(<S: BaseNum> Mul<$VectorN<S> > for $VectorN<S> {
fn mul(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field * rhs.$field),+) }
});
impl_assignment_operator!(<S: BaseNum> MulAssign<S> for $VectorN<S> {
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<usize> { $($field),+ });
impl_scalar_ops!($VectorN<u8> { $($field),+ });
impl_scalar_ops!($VectorN<u16> { $($field),+ });
impl_scalar_ops!($VectorN<u32> { $($field),+ });
impl_scalar_ops!($VectorN<u64> { $($field),+ });
impl_scalar_ops!($VectorN<isize> { $($field),+ });
impl_scalar_ops!($VectorN<i8> { $($field),+ });
impl_scalar_ops!($VectorN<i16> { $($field),+ });
impl_scalar_ops!($VectorN<i32> { $($field),+ });
impl_scalar_ops!($VectorN<i64> { $($field),+ });
impl_scalar_ops!($VectorN<f32> { $($field),+ });
impl_scalar_ops!($VectorN<f64> { $($field),+ });
impl_index_operators!($VectorN<S>, $n, S, usize);
impl_index_operators!($VectorN<S>, $n, [S], Range<usize>);
impl_index_operators!($VectorN<S>, $n, [S], RangeTo<usize>);
@ -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<S> { x, y }, 2, vec2);
impl_vector!(Vector3<S> { x, y, z }, 3, vec3);
impl_vector!(Vector4<S> { x, y, z, w }, 4, vec4);

View file

@ -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));
}

View file

@ -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;

View file

@ -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);