diff --git a/src/funs/triganomic.rs b/src/funs/triganomic.rs index 08822d6..052ae64 100644 --- a/src/funs/triganomic.rs +++ b/src/funs/triganomic.rs @@ -113,28 +113,33 @@ pub trait InvTrig { pure fn asin(&self) -> Radians; pure fn acos(&self) -> Radians; pure fn atan(&self) -> Radians; + pure fn atan2(&self, other: &self) -> Radians; } #[inline(always)] pub pure fn asin(x: &T) -> Radians { x.asin() } #[inline(always)] pub pure fn acos(x: &T) -> Radians { x.acos() } #[inline(always)] pub pure fn atan(x: &T) -> Radians { x.atan() } +#[inline(always)] pub pure fn atan2(a: &T, b: &T) -> Radians { a.atan2(b) } pub impl f32: InvTrig { #[inline(always)] pure fn asin(&self) -> Radians { Radians(f32::asin(*self)) } #[inline(always)] pure fn acos(&self) -> Radians { Radians(f32::acos(*self)) } #[inline(always)] pure fn atan(&self) -> Radians { Radians(f32::atan(*self)) } + #[inline(always)] pure fn atan2(&self, other: &f32) -> Radians { Radians(f32::atan2(*self, *other)) } } pub impl f64: InvTrig { #[inline(always)] pure fn asin(&self) -> Radians { Radians(f64::asin(*self)) } #[inline(always)] pure fn acos(&self) -> Radians { Radians(f64::acos(*self)) } #[inline(always)] pure fn atan(&self) -> Radians { Radians(f64::atan(*self)) } + #[inline(always)] pure fn atan2(&self, other: &f64) -> Radians { Radians(f64::atan2(*self, *other)) } } pub impl float: InvTrig { #[inline(always)] pure fn asin(&self) -> Radians { Radians(f64::asin(*self as f64) as float) } #[inline(always)] pure fn acos(&self) -> Radians { Radians(f64::acos(*self as f64) as float) } #[inline(always)] pure fn atan(&self) -> Radians { Radians(f64::atan(*self as f64) as float) } + #[inline(always)] pure fn atan2(&self, other: &float) -> Radians { Radians(f64::atan2(*self as f64, *other as f64) as float) } } // TODO: figure out how to merge with InvTrig diff --git a/src/test/test_vec.rs b/src/test/test_vec.rs index da4e314..5cbc2a0 100644 --- a/src/test/test_vec.rs +++ b/src/test/test_vec.rs @@ -1,4 +1,5 @@ use std::cmp::FuzzyEq; +use angle::*; use vec::*; // TODO @@ -81,8 +82,6 @@ fn test_Vec2_euclidean() { let b0 = Vec2::new(3f, 4f); // (3, 4, 5) Pythagorean triple let b = a.add_v(&b0); - // TODO: test normalize and normalize_self - assert a.length() == 13f; assert a.length2() == 13f * 13f; @@ -92,6 +91,13 @@ fn test_Vec2_euclidean() { assert a.distance(&b) == 5f; assert a.distance2(&b) == 5f * 5f; + assert Vec2::new(1f, 0f).angle(&Vec2::new(0f, 1f)).fuzzy_eq(&Angle::quadrant()); + assert Vec2::new(10f, 0f).angle(&Vec2::new(0f, 5f)).fuzzy_eq(&Angle::quadrant()); + assert Vec2::new(-1f, 0f).angle(&Vec2::new(0f, 1f)).fuzzy_eq(&-Angle::quadrant()); + + assert Vec2::new(3f, 4f).normalize().fuzzy_eq(&Vec2::new(3f/5f, 4f/5f)); + // TODO: test normalize_to, normalize_self, and normalize_self_to + let c = Vec2::new(-2.0f, -1.0f); let d = Vec2::new( 1.0f, 0.0f); @@ -195,8 +201,6 @@ fn test_Vec3_euclidean() { let b0 = Vec3::new(1f, 4f, 8f); // (1, 4, 8, 9) Pythagorean quadruple let b = a.add_v(&b0); - // TODO: test normalize and normalize_self - assert a.length() == 7f; assert a.length2() == 7f * 7f; @@ -206,6 +210,13 @@ fn test_Vec3_euclidean() { assert a.distance(&b) == 9f; assert a.distance2(&b) == 9f * 9f; + assert Vec3::new(1f, 0f, 1f).angle(&Vec3::new(1f, 1f, 0f)).fuzzy_eq(&Angle::sextant()); + assert Vec3::new(10f, 0f, 10f).angle(&Vec3::new(5f, 5f, 0f)).fuzzy_eq(&Angle::sextant()); + assert Vec3::new(-1f, 0f, -1f).angle(&Vec3::new(1f, -1f, 0f)).fuzzy_eq(&Radians(2f * Float::frac_pi_3())); + + assert Vec3::new(2f, 3f, 6f).normalize().fuzzy_eq(&Vec3::new(2f/7f, 3f/7f, 6f/7f)); + // TODO: test normalize_to, normalize_self, and normalize_self_to + let c = Vec3::new(-2.0f, -1.0f, 1.0f); let d = Vec3::new( 1.0f, 0.0f, 0.5f); @@ -309,8 +320,6 @@ fn test_Vec4_euclidean() { let b0 = Vec4::new(1f, 2f, 8f, 10f); // (1, 2, 8, 10, 13) Pythagorean quintuple let b = a.add_v(&b0); - // TODO: test normalize and normalize_self - assert a.length() == 11f; assert a.length2() == 11f * 11f; @@ -319,6 +328,13 @@ fn test_Vec4_euclidean() { assert a.distance(&b) == 13f; assert a.distance2(&b) == 13f * 13f; + + assert Vec4::new(1f, 0f, 1f, 0f).angle(&Vec4::new(0f, 1f, 0f, 1f)).fuzzy_eq(&Angle::quadrant()); + assert Vec4::new(10f, 0f, 10f, 0f).angle(&Vec4::new(0f, 5f, 0f, 5f)).fuzzy_eq(&Angle::quadrant()); + assert Vec4::new(-1f, 0f, -1f, 0f).angle(&Vec4::new(0f, 1f, 0f, 1f)).fuzzy_eq(&Angle::quadrant()); + + assert Vec4::new(1f, 2f, 4f, 10f).normalize().fuzzy_eq(&Vec4::new(1f/11f, 2f/11f, 4f/11f, 10f/11f)); + // TODO: test normalize_to, normalize_self, and normalize_self_to let c = Vec4::new(-2.0f, -1.0f, 1.0f, 2.0f); let d = Vec4::new( 1.0f, 0.0f, 0.5f, 1.0f); diff --git a/src/vec.rs b/src/vec.rs index e929ef0..37f8ba6 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -6,7 +6,9 @@ use core::vec::raw::buf_as_slice; use std::cmp::FuzzyEq; +use angle::Radians; use funs::exponential::Exp; +use funs::triganomic::{InvTrig, acos, atan2}; use num::types::Number; /** @@ -158,6 +160,13 @@ pub trait MutableNumericVector: MutableVector<&self/T> NumericVector { pub trait NumericVector2: NumericVector { // static pure fn unit_x() -> self; // static pure fn unit_y() -> self; + + /** + * # Return value + * + * The perp dot product of the vector and `other` + */ + pure fn perp_dot(&self, other: &self) -> T; } /** @@ -239,6 +248,13 @@ pub trait EuclideanVector: NumericVector { */ pure fn distance(&self, other: &self) -> T; + /** + * # Return value + * + * The angle between the vector and `other` + */ + pure fn angle(&self, other: &self) -> Radians; + /** * # Return value * @@ -435,7 +451,14 @@ pub impl Vec2: MutableNumericVector<&self/T> { } } -pub impl Vec2: EuclideanVector { +pub impl Vec2: NumericVector2 { + #[inline(always)] + pure fn perp_dot(&self, other: &Vec2) ->T { + (self[0] * other[1]) - (self[1] * other[0]) + } +} + +pub impl Vec2: EuclideanVector { #[inline(always)] pure fn length2(&self) -> T { self.dot(self) @@ -456,6 +479,11 @@ pub impl Vec2: EuclideanVector { other.distance2(self).sqrt() } + #[inline(always)] + pure fn angle(&self, other: &Vec2) -> Radians { + atan2(&self.perp_dot(other), &self.dot(other)) + } + #[inline(always)] pure fn normalize(&self) -> Vec2 { let mut n: T = Number::from(1); @@ -475,7 +503,7 @@ pub impl Vec2: EuclideanVector { } } -pub impl Vec2: MutableEuclideanVector<&self/T> { +pub impl Vec2: MutableEuclideanVector<&self/T> { #[inline(always)] fn normalize_self(&mut self) { let mut n: T = Number::from(1); @@ -695,7 +723,7 @@ pub impl Vec3: MutableNumericVector3<&self/T> { } } -pub impl Vec3: EuclideanVector { +pub impl Vec3: EuclideanVector { #[inline(always)] pure fn length2(&self) -> T { self.dot(self) @@ -716,6 +744,11 @@ pub impl Vec3: EuclideanVector { other.distance2(self).sqrt() } + #[inline(always)] + pure fn angle(&self, other: &Vec3) -> Radians { + atan2(&self.cross(other).length(), &self.dot(other)) + } + #[inline(always)] pure fn normalize(&self) -> Vec3 { let mut n: T = Number::from(1); @@ -735,7 +768,7 @@ pub impl Vec3: EuclideanVector { } } -pub impl Vec3: MutableEuclideanVector<&self/T> { +pub impl Vec3: MutableEuclideanVector<&self/T> { #[inline(always)] fn normalize_self(&mut self) { let mut n: T = Number::from(1); @@ -955,7 +988,7 @@ pub impl Vec4: MutableNumericVector<&self/T> { } } -pub impl Vec4: EuclideanVector { +pub impl Vec4: EuclideanVector { #[inline(always)] pure fn length2(&self) -> T { self.dot(self) @@ -976,6 +1009,11 @@ pub impl Vec4: EuclideanVector { other.distance2(self).sqrt() } + #[inline(always)] + pure fn angle(&self, other: &Vec4) -> Radians { + acos(&(self.dot(other) / (self.length() * other.length()))) + } + #[inline(always)] pure fn normalize(&self) -> Vec4 { let mut n: T = Number::from(1); @@ -995,7 +1033,7 @@ pub impl Vec4: EuclideanVector { } } -pub impl Vec4: MutableEuclideanVector<&self/T> { +pub impl Vec4: MutableEuclideanVector<&self/T> { #[inline(always)] fn normalize_self(&mut self) { let mut n: T = Number::from(1);