use std::convert::From;
use std::ops::{
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign,
};
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Default)]
pub struct Vec2d<T> {
x: T,
y: T,
}
macro_rules! impl_vec_ops_for {
($($t:ty)*) => {$(
impl Vec2d<$t>{
#[inline]
pub fn unit_vector(direction: $t) -> Self {
let (y, x) = direction.sin_cos();
Vec2d { x, y }
}
#[inline]
pub fn normalise(self) -> Self {
let magnitude = self.magnitude();
if magnitude > 0.0 {
return self * (1.0 / magnitude)
} self
}
#[inline]
pub fn magnitude(&self) -> $t {
(self.x.powf(2.0) + self.y.powf(2.0)).sqrt()
}
#[inline]
pub fn square_magnitude(&self) -> $t {
self.x.powf(2.0) + self.y.powf(2.0)
}
#[inline]
pub fn direction(&self) -> $t {
self.y.atan2(self.x)
}
#[inline]
pub fn clear(&mut self) {
self.x = 0.0;
self.y = 0.0;
}
}
)*};
}
impl_vec_ops_for!(f32 f64);
impl<T> Vec2d<T> {
#[inline]
pub fn new(x: T, y: T) -> Vec2d<T>
where
T: Add + Mul + Div + Sub + Copy, {
Vec2d { x, y }
}
#[inline]
pub fn x(&self) -> T
where
T: Add + Mul + Div + Sub + Copy, {
self.x
}
#[inline]
pub fn set_x(&mut self, x: T)
where
T: Add + Mul + Div + Sub + Copy, {
self.x = x
}
#[inline]
pub fn y(&self) -> T
where
T: Add + Mul + Div + Sub + Copy, {
self.y
}
#[inline]
pub fn set_y(&mut self, y: T)
where
T: Add + Mul + Div + Sub + Copy, {
self.y = y
}
}
impl<T: Add + Copy> Add<T> for Vec2d<T> {
type Output = Vec2d<T::Output>;
#[inline]
fn add(self, rhs: T) -> Self::Output {
Vec2d {
x: self.x + rhs,
y: self.y + rhs,
}
}
}
impl<T: Add> Add for Vec2d<T> {
type Output = Vec2d<T::Output>;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Vec2d {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl<T: AddAssign + Copy> AddAssign<T> for Vec2d<T> {
#[inline]
fn add_assign(&mut self, rhs: T) {
self.x += rhs;
self.y += rhs;
}
}
impl<T: AddAssign> AddAssign<Vec2d<T>> for Vec2d<T> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
}
}
impl<T: Sub + Copy> Sub<T> for Vec2d<T> {
type Output = Vec2d<T::Output>;
#[inline]
fn sub(self, rhs: T) -> Self::Output {
Vec2d {
x: self.x - rhs,
y: self.y - rhs,
}
}
}
impl<T: Sub> Sub for Vec2d<T> {
type Output = Vec2d<T::Output>;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Vec2d {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl<T: SubAssign + Copy> SubAssign<T> for Vec2d<T> {
#[inline]
fn sub_assign(&mut self, rhs: T) {
self.x -= rhs;
self.y -= rhs;
}
}
impl<T: SubAssign> SubAssign<Vec2d<T>> for Vec2d<T> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
}
}
impl<T: Mul + Copy> Mul<T> for Vec2d<T> {
type Output = Vec2d<T::Output>;
#[inline]
fn mul(self, rhs: T) -> Self::Output {
Vec2d {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl<T: Mul + Add> Mul for Vec2d<T>
where
<T as Mul>::Output: Add,
{
type Output = <<T as Mul>::Output as Add>::Output;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
self.x * rhs.x + self.y * rhs.y
}
}
impl<T: MulAssign + Copy> MulAssign<T> for Vec2d<T> {
#[inline]
fn mul_assign(&mut self, rhs: T) {
self.x *= rhs;
self.y *= rhs;
}
}
impl<T: MulAssign> MulAssign<Vec2d<T>> for Vec2d<T> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
self.x *= rhs.x;
self.y *= rhs.y;
}
}
impl<T: Div + Copy> Div<T> for Vec2d<T> {
type Output = Vec2d<T::Output>;
#[inline]
fn div(self, rhs: T) -> Self::Output {
Vec2d {
x: self.x / rhs,
y: self.y / rhs,
}
}
}
impl<T: Div> Div for Vec2d<T> {
type Output = Vec2d<T::Output>;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
Vec2d {
x: self.x / rhs.x,
y: self.y / rhs.y,
}
}
}
impl<T: DivAssign + Copy> DivAssign<T> for Vec2d<T> {
#[inline]
fn div_assign(&mut self, rhs: T) {
self.x /= rhs;
self.y /= rhs;
}
}
impl<T: DivAssign> DivAssign<Vec2d<T>> for Vec2d<T> {
#[inline]
fn div_assign(&mut self, rhs: Self) {
self.x /= rhs.x;
self.y /= rhs.y;
}
}
macro_rules! forward_ref_binop {
(impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
forward_ref_binop!(impl $imp, $method for $t, $u,
#[stable(feature = "rust1", since = "1.0.0")]);
};
(impl $imp:ident, $method:ident for $t:ty, $u:ty, #[$attr:meta]) => {
impl<'a> $imp<$u> for &'a $t {
type Output = <$t as $imp<$u>>::Output;
#[inline]
fn $method(self, other: $u) -> <$t as $imp<$u>>::Output {
$imp::$method(*self, other)
}
}
impl $imp<&$u> for $t {
type Output = <$t as $imp<$u>>::Output;
#[inline]
fn $method(self, other: &$u) -> <$t as $imp<$u>>::Output {
$imp::$method(self, *other)
}
}
impl $imp<&$u> for &$t {
type Output = <$t as $imp<$u>>::Output;
#[inline]
fn $method(self, other: &$u) -> <$t as $imp<$u>>::Output {
$imp::$method(*self, *other)
}
}
}
}
macro_rules! forward_ref_op_assign {
(impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
forward_ref_op_assign!(impl $imp, $method for $t, $u,
#[stable(feature = "op_assign_builtins_by_ref", since = "1.22.0")]);
};
(impl $imp:ident, $method:ident for $t:ty, $u:ty, #[$attr:meta]) => {
impl $imp<&$u> for $t {
#[inline]
fn $method(&mut self, other: &$u) {
$imp::$method(self, *other);
}
}
}
}
macro_rules! vec2d_impl {
($($t:ty)*) => ($(
forward_ref_binop! { impl Add, add for Vec2d<$t>, Vec2d<$t> }
forward_ref_binop! { impl Add, add for Vec2d<$t>, $t }
forward_ref_binop! { impl Sub, sub for Vec2d<$t>, Vec2d<$t> }
forward_ref_binop! { impl Sub, sub for Vec2d<$t>, $t }
forward_ref_binop! { impl Mul, mul for Vec2d<$t>, Vec2d<$t> }
forward_ref_binop! { impl Mul, mul for Vec2d<$t>, $t }
forward_ref_binop! { impl Div, div for Vec2d<$t>, Vec2d<$t> }
forward_ref_binop! { impl Div, div for Vec2d<$t>, $t }
forward_ref_op_assign! { impl AddAssign, add_assign for Vec2d<$t>, Vec2d<$t> }
forward_ref_op_assign! { impl AddAssign, add_assign for Vec2d<$t>, $t }
forward_ref_op_assign! { impl SubAssign, sub_assign for Vec2d<$t>, Vec2d<$t> }
forward_ref_op_assign! { impl SubAssign, sub_assign for Vec2d<$t>, $t }
forward_ref_op_assign! { impl MulAssign, mul_assign for Vec2d<$t>, Vec2d<$t> }
forward_ref_op_assign! { impl MulAssign, mul_assign for Vec2d<$t>, $t }
forward_ref_op_assign! { impl DivAssign, div_assign for Vec2d<$t>, Vec2d<$t> }
forward_ref_op_assign! { impl DivAssign, div_assign for Vec2d<$t>, $t }
)*)
}
vec2d_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
impl<T: Neg> Neg for Vec2d<T> {
type Output = Vec2d<T::Output>;
#[inline]
fn neg(self) -> Self::Output {
Vec2d {
x: -self.x,
y: -self.y,
}
}
}
impl<T> Into<[T; 2]> for Vec2d<T> {
#[inline]
fn into(self) -> [T; 2] {
[self.x, self.y]
}
}
impl<T: Copy> From<[T; 2]> for Vec2d<T> {
#[inline]
fn from(array: [T; 2]) -> Self {
Vec2d {
x: array[0],
y: array[1],
}
}
}
impl<T> Into<(T, T)> for Vec2d<T> {
#[inline]
fn into(self) -> (T, T) {
(self.x, self.y)
}
}
impl<T> From<(T, T)> for Vec2d<T> {
#[inline]
fn from(tuple: (T, T)) -> Self {
Vec2d {
x: tuple.0,
y: tuple.1,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::f32::consts::PI;
use std::f64::consts::PI as F64_PI;
#[allow(clippy::op_ref)]
#[test]
fn multiply_two_vec2d_for_scalar_f32() {
let epsilon = 0.000001;
let point1: Vec2d<f32> = Vec2d::new(1.0, 2.0);
let point2: Vec2d<f32> = Vec2d::new(2.0, 3.0);
assert!(point1 * point2 >= 8.0 && point1 * point2 < 8.0 + epsilon);
assert!(&point1 * point2 >= 8.0 && &point1 * point2 < 8.0 + epsilon);
assert!(point1 * &point2 >= 8.0 && point1 * &point2 < 8.0 + epsilon);
assert!(&point1 * &point2 >= 8.0 && &point1 * &point2 < 8.0 + epsilon);
}
#[allow(clippy::op_ref)]
#[test]
fn multiply_vec2d_by_scalar_f32() {
let point1: Vec2d<f32> = Vec2d::new(1.0, 2.0);
let scalar: f32 = 3.0;
let res = Vec2d::new(3.0, 6.0);
assert_eq!(point1 * scalar, res);
assert_eq!(&point1 * scalar, res);
assert_eq!(point1 * &scalar, res);
assert_eq!(&point1 * &scalar, res);
}
#[allow(clippy::op_ref)]
#[test]
fn multiply_two_vec2d_for_scalar_f64() {
let epsilon = 0.000000000000001;
let point1: Vec2d<f64> = Vec2d::new(1.0, 2.0);
let point2: Vec2d<f64> = Vec2d::new(2.0, 3.0);
assert!(point1 * point2 >= 8.0 && point1 * point2 < 8.0 + epsilon);
assert!(&point1 * point2 >= 8.0 && &point1 * point2 < 8.0 + epsilon);
assert!(point1 * &point2 >= 8.0 && point1 * &point2 < 8.0 + epsilon);
assert!(&point1 * &point2 >= 8.0 && &point1 * &point2 < 8.0 + epsilon);
}
#[allow(clippy::op_ref)]
#[test]
fn multiply_vec2d_by_scalar_f64() {
let point1: Vec2d<f64> = Vec2d::new(1.0, 2.0);
let scalar: f64 = 3.0;
let res = Vec2d::new(3.0, 6.0);
assert_eq!(point1 * scalar, res);
assert_eq!(&point1 * scalar, res);
assert_eq!(point1 * &scalar, res);
assert_eq!(&point1 * &scalar, res);
}
#[test]
fn unit_vector_32() {
let unit: Vec2d<f32> = Vec2d::<f32>::unit_vector(PI);
assert_eq!(unit, Vec2d::new(-1.0, -0.00000008742278));
}
#[test]
fn unit_vector_64() {
let unit: Vec2d<f64> = Vec2d::<f64>::unit_vector(F64_PI);
assert_eq!(unit, Vec2d::new(-1.0, 0.00000000000000012246467991473532));
}
#[test]
fn normalise_32() {
let point: Vec2d<f32> = Vec2d::new(4.0, 3.0);
assert_eq!(point.normalise(), Vec2d::new(0.8, 0.6));
}
#[test]
fn normalise_64() {
let point: Vec2d<f64> = Vec2d::new(4.0, 3.0);
assert_eq!(point.normalise(), Vec2d::new(0.8, 0.6000000000000001));
}
#[test]
fn magnitude_32() {
let point: Vec2d<f32> = Vec2d::new(4.0, 3.0);
assert!(point.magnitude() >= 5.0 && point.magnitude() < 5.000001);
}
#[test]
fn magnitude_64() {
let point: Vec2d<f64> = Vec2d::new(4.0, 3.0);
assert!(
point.magnitude() >= 5.0 && point.magnitude() < 5.000000000000001
);
}
#[test]
fn direction_pi_32() {
let point: Vec2d<f32> = Vec2d::new(-1.0, 0.0);
assert!(
point.direction() >= PI && point.direction() < PI + f32::EPSILON
);
}
#[test]
fn direction_pi_64() {
let point: Vec2d<f64> = Vec2d::new(-1.0, 0.0);
assert!(
point.direction() >= F64_PI
&& point.direction() < F64_PI + f32::EPSILON as f64
);
}
#[test]
fn direction_pi2_32() {
let point: Vec2d<f32> = Vec2d::new(0.0, 1.0);
assert!(
point.direction() >= PI / 2.0
&& point.direction() < PI / 2.0 + f32::EPSILON
);
}
#[test]
fn direction_pi2_64() {
let point: Vec2d<f64> = Vec2d::new(0.0, 1.0);
assert!(
point.direction() >= F64_PI / 2.0
&& point.direction() < F64_PI / 2.0 + f64::EPSILON
);
}
}