From 40f5c0f4621a4f3281af94838fb5a1f4736fe27f Mon Sep 17 00:00:00 2001 From: Jason Travis Smith Date: Thu, 1 Dec 2016 01:15:24 -0500 Subject: [PATCH] Added Radians and Degrees to the trig module. These types allow the correct tracking of the type of angle measurement being used. --- src/constants.rs | 26 ++ src/lib.rs | 6 +- src/macros.rs | 143 ++++++++++ src/real.rs | 55 +++- src/trig.rs | 722 +++++++++++++++++++++++++++++++++++++++++++++-- src/vector.rs | 251 ++++------------ 6 files changed, 982 insertions(+), 221 deletions(-) create mode 100644 src/macros.rs diff --git a/src/constants.rs b/src/constants.rs index 72eec71..e8e40b1 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -236,6 +236,30 @@ pub trait Constants /// # assert_eq!(val64, 2.0f64 * (std::f64::consts::PI).sqrt().recip()); ///``` const TWO_INVERSE_SQRT_PI: Self; + + /// PI divided by 180. This is used for radian and degree conversions. + /// + ///``` + /// use sigils::Constants; + /// + /// let val32: f32 = Constants::PI_DIVIDED_BY_180; + /// let val64: f64 = Constants::PI_DIVIDED_BY_180; + /// # assert_eq!(val32, (std::f32::consts::PI) / 180.0f32); + /// # assert_eq!(val64, (std::f64::consts::PI) / 180.0f64); + ///``` + const PI_DIVIDED_BY_180: Self; + + /// 180 divided by PI. This is used for radian and degree conversions. + /// + ///``` + /// use sigils::Constants; + /// + /// let val32: f32 = Constants::INVERSE_PI_DIVIDED_BY_180; + /// let val64: f64 = Constants::INVERSE_PI_DIVIDED_BY_180; + /// # assert_eq!(val32, 180.0f32 / (std::f32::consts::PI)); + /// # assert_eq!(val64, 180.0f64 / (std::f64::consts::PI)); + ///``` + const INVERSE_PI_DIVIDED_BY_180: Self; } @@ -271,6 +295,8 @@ macro_rules! constants_trait_impl const INVERSE_PI: $T = 1.0 / $pi; const TWO_INVERSE_PI: $T = 2.0 / $pi; const TWO_INVERSE_SQRT_PI: $T = 2.0 / $sqrtPI; + const PI_DIVIDED_BY_180: $T = $pi / 180.0; + const INVERSE_PI_DIVIDED_BY_180: $T = 180.0 / $pi; } } } diff --git a/src/lib.rs b/src/lib.rs index eb5ab79..2ab299d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,9 @@ #![feature(associated_consts)] +#[macro_use] +mod macros; + mod zero; mod one; mod bounded; @@ -15,7 +18,7 @@ mod integer; mod real; mod constants; -pub mod trig; +mod trig; pub mod vector; pub mod matrix; pub mod quaternion; @@ -28,3 +31,4 @@ pub use self::whole::Whole; pub use self::integer::Integer; pub use self::real::Real; pub use self::constants::Constants; +pub use self::trig::{Degree, Radian}; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..de6da5a --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,143 @@ +/// A macro that will define a binary operation for +/// a structure and its components. +macro_rules! binary_operator_impl +{ + ($traitName: ident :: $funcName: ident, + $structName: ident {$($field: ident),+}, + $generic_type: ident) => + { + impl $traitName for $structName + where T: $generic_type + { + type Output = $structName; + + fn $funcName(self, val: T) -> $structName + { + $structName::new($(self.$field.$funcName(val)),+) + } + } + + impl<'a, T> $traitName for &'a $structName + where T: $generic_type + { + type Output = $structName; + + fn $funcName(self, val: T) -> $structName + { + $structName::new($(self.$field.$funcName(val)),+) + } + } + + impl<'a, T> $traitName<&'a T> for $structName + where T: $generic_type + { + type Output = $structName; + + fn $funcName(self, val: &'a T) -> $structName + { + $structName::new($(self.$field.$funcName(*val)),+) + } + } + + impl<'a, 'b, T> $traitName<&'b T> for &'a $structName + where T: $generic_type + { + type Output = $structName; + + fn $funcName(self, val: &'b T) -> $structName + { + $structName::new($(self.$field.$funcName(*val)),+) + } + } + + impl $traitName<$structName> for $structName + where T: $generic_type + { + type Output = $structName; + + fn $funcName(self, other: $structName) -> $structName + { + $structName::new($(self.$field.$funcName(other.$field)),+) + } + } + + impl<'a, T> $traitName<$structName> for &'a $structName + where T: $generic_type + { + type Output = $structName; + + fn $funcName(self, other: $structName) -> $structName + { + $structName::new($(self.$field.$funcName(other.$field)),+) + } + } + + impl<'a, T> $traitName<&'a $structName> for $structName + where T: $generic_type + { + type Output = $structName; + + fn $funcName(self, other: &'a $structName) -> $structName + { + $structName::new($(self.$field.$funcName(other.$field)),+) + } + } + + impl<'a, 'b, T> $traitName<&'a $structName> for &'b $structName + where T: $generic_type + { + type Output = $structName; + + fn $funcName(self, other: &'a $structName) -> $structName + { + $structName::new($(self.$field.$funcName(other.$field)),+) + } + } + } +} + +/// A macro that will define an assignment binary operation for +/// a structure and its components. +macro_rules! binary_operator_assign_impl +{ + ($traitName: ident :: $funcName: ident, + $structName: ident {$($field: ident),+}, + $generic_type: ident) => + { + impl $traitName<$structName> for $structName + where T: $generic_type + { + fn $funcName(&mut self, rhs: $structName) + { + $(self.$field.$funcName(rhs.$field);)+ + } + } + + impl $traitName for $structName + where T: $generic_type + { + fn $funcName(&mut self, rhs: T) + { + $(self.$field.$funcName(rhs);)+ + } + } + + impl<'a, T> $traitName<&'a $structName> for $structName + where T: $generic_type + { + fn $funcName(&mut self, rhs: &'a $structName) + { + $(self.$field.$funcName(rhs.$field);)+ + } + } + + impl<'a, T> $traitName<&'a T> for $structName + where T: $generic_type + { + fn $funcName(&mut self, rhs: &'a T) + { + $(self.$field.$funcName(*rhs);)+ + } + } + } +} diff --git a/src/real.rs b/src/real.rs index c36e338..a07e1c4 100644 --- a/src/real.rs +++ b/src/real.rs @@ -1,7 +1,8 @@ use std::num::FpCategory; use std::ops::Neg; -use super::number::Number; +use ::number::Number; +use ::trig::Radian; // TODO: Double check these examples. Most are OK, @@ -730,7 +731,7 @@ pub trait Real : Number + Neg /// /// assert!(abs_difference < 1e-10); /// ``` - fn asin(self) -> Self; + fn asin(self) -> Radian; /// Computes the arccosine of a number. Return value is in radians in /// the range [0, pi] or NaN if the number is outside the range @@ -747,7 +748,7 @@ pub trait Real : Number + Neg /// /// assert!(abs_difference < 1e-10); /// ``` - fn acos(self) -> Self; + fn acos(self) -> Radian; /// Computes the arctangent of a number. Return value is in radians in the /// range [-pi/2, pi/2]; @@ -762,7 +763,7 @@ pub trait Real : Number + Neg /// /// assert!(abs_difference < 1e-10); /// ``` - fn atan(self) -> Self; + fn atan(self) -> Radian; /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`). /// @@ -791,7 +792,7 @@ pub trait Real : Number + Neg /// assert!(abs_difference_1 < 1e-10); /// assert!(abs_difference_2 < 1e-10); /// ``` - fn atan2(self, other: Self) -> Self; + fn atan2(self, other: Self) -> Radian; /// Simultaneously computes the sine and cosine of the number, `x`. Returns /// `(sin(x), cos(x))`. @@ -935,7 +936,7 @@ pub trait Real : Number + Neg /// /// assert!(abs_difference < 1.0e-10); /// ``` - fn asinh(self) -> Self; + fn asinh(self) -> Radian; /// Inverse hyperbolic cosine function. /// @@ -949,7 +950,7 @@ pub trait Real : Number + Neg /// /// assert!(abs_difference < 1.0e-10); /// ``` - fn acosh(self) -> Self; + fn acosh(self) -> Radian; /// Inverse hyperbolic tangent function. /// @@ -968,7 +969,7 @@ pub trait Real : Number + Neg /// //assert!(abs_difference32 < 1.0e-10); /// assert!(abs_difference64 < 1.0e-10); /// ``` - fn atanh(self) -> Self; + fn atanh(self) -> Radian; } @@ -997,6 +998,18 @@ macro_rules! define_self_bool_func } } +/// +macro_rules! define_self_rad_func +{ + ($varType: ident, $funcName: ident) => + { + fn $funcName(self) -> Radian<$varType> + { + Radian::new(<$varType>::$funcName(self)) + } + } +} + /// macro_rules! define_self_other_func { @@ -1009,6 +1022,18 @@ macro_rules! define_self_other_func } } +/// +macro_rules! define_self_other_rad_func +{ + ($varType: ident, $funcName: ident) => + { + fn $funcName(self, other: $varType) -> Radian<$varType> + { + Radian::new(<$varType>::$funcName(self, other)) + } + } +} + /// A macro to make implementing the trait easier for all the /// base float types in rust. macro_rules! real_trait_impl @@ -1097,16 +1122,16 @@ macro_rules! real_trait_impl define_self_func!($varType, cos); define_self_func!($varType, sin); define_self_func!($varType, tan); - define_self_func!($varType, acos); - define_self_func!($varType, asin); - define_self_func!($varType, atan); + define_self_rad_func!($varType, acos); + define_self_rad_func!($varType, asin); + define_self_rad_func!($varType, atan); define_self_func!($varType, cosh); define_self_func!($varType, sinh); define_self_func!($varType, tanh); - define_self_func!($varType, acosh); - define_self_func!($varType, asinh); - define_self_func!($varType, atanh); - define_self_other_func!($varType, atan2); + define_self_rad_func!($varType, acosh); + define_self_rad_func!($varType, asinh); + define_self_rad_func!($varType, atanh); + define_self_other_rad_func!($varType, atan2); } )*) } diff --git a/src/trig.rs b/src/trig.rs index 247d822..a5a3296 100644 --- a/src/trig.rs +++ b/src/trig.rs @@ -1,26 +1,716 @@ -//use super::constants::Constants; -use super::real::Real; +use std::ops::{Add, Sub, Mul, Div, Rem, Neg}; +use std::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign}; + +use ::constants::Constants; +use ::real::Real; -/* -pub struct Radians where T: Real + +/// A degree usually denoted by ° (the degree symbol), +/// is a measurement of a plane angle, defined so that a +/// full rotation is 360 degrees. +#[derive(Clone, Copy, Eq, PartialEq, Hash)] +pub struct Degree { - pub value: T + value: T } -pub struct Degrees where T: Real +/// A unit of angle, equal to an angle at the center of +/// a circle whose arc is equal in length to the radius. +#[derive(Clone, Copy, Eq, PartialEq, Hash)] +pub struct Radian { - pub value: T -} -*/ - - -pub fn acos(x: T) -> T where T: Real -{ - x.acos() + value: T } -pub fn atan2(x: T, y: T) -> T where T: Real + + +impl Degree where T: Real { - x.atan2(y) + /// Create a new Degree structure with the given value. + pub fn new(val: T) -> Degree + { + Degree + { + value: val + } + } + + /// Computes the arccosine of a number. Return value is in Degrees in + /// the range [0, 180] or NaN if the number is outside the range + /// [-1, 1]. + /// + ///``` + /// use sigils::{Degree, Real}; + /// + /// let f: Degree; + /// + /// f = Degree::from(45.0f64); + /// assert!((45.0f64 - *Degree::acos(f.cos())) < 1e-10); + ///``` + pub fn acos(val: T) -> Degree + { + let radians: Radian; + + radians = val.acos(); + Degree::from(radians) + } + + /// Computes the arcsine of a number. Return value is in Degrees in + /// the range [-57.2957795/2, 57.2957795/2] or NaN if the number is + /// outside the range [-1, 1]. + /// + ///``` + /// use sigils::{Degree, Real}; + /// + /// let f: Degree; + /// + /// f = Degree::from(45.0f64); + /// assert!((45.0f64 - *Degree::asin(f.sin())) < 1e-10); + ///``` + pub fn asin(val: T) -> Degree + { + let radians: Radian; + + radians = val.asin(); + Degree::from(radians) + } + + /// Computes the arctangent of a number. Return value is in degrees in the + /// range [-57.2957795/2, 57.2957795/2]; + /// + ///``` + /// use sigils::{Degree, Real}; + /// + /// let f: Degree; + /// + /// f = Degree::from(45.0f64); + /// assert!((45.0f64 - *Degree::atan(f.tan())) < 1e-10); + ///``` + pub fn atan(val: T) -> Degree + { + let radians: Radian; + + radians = val.atan(); + Degree::from(radians) + } + + /// Inverse hyperbolic cosine function. + pub fn acosh(val: T) -> Degree + { + let radians: Radian; + + radians = val.acosh(); + Degree::from(radians) + } + + /// Inverse hyperbolic sine function. + pub fn asinh(val: T) -> Degree + { + let radians: Radian; + + radians = val.asinh(); + Degree::from(radians) + } + + /// Inverse hyperbolic tangent function. + pub fn atanh(val: T) -> Degree + { + let radians: Radian; + + radians = val.atanh(); + Degree::from(radians) + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`). + pub fn atan2(y: T, x: T) -> Degree + { + let radians: Radian; + + radians = y.atan2(x); + Degree::from(radians) + } + + /// Computes the cosine of this angle. + /// + /// ``` + /// use sigils::Degree; + /// use std::f64; + /// + /// let x: Degree = Degree::from(360.0f64); + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + pub fn cos(&self) -> T + { + let radians: Radian; + + radians = Radian::from(*self); + radians.cos() + } + + /// Computes the sine of this angle. + /// + /// ``` + /// use sigils::Degree; + /// use std::f64; + /// + /// let x: Degree = Degree::from(90.0f64); + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + pub fn sin(&self) -> T + { + let radians: Radian; + + radians = Radian::from(*self); + radians.sin() + } + + /// Computes the tangent of this angle. + /// + /// ``` + /// use sigils::Degree; + /// use std::f64; + /// + /// let x: Degree = Degree::from(45.0f64); + /// + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-14); + /// ``` + pub fn tan(&self) -> T + { + let radians: Radian; + + radians = Radian::from(*self); + radians.tan() + } + + /// Hyperbolic cosine function. + /// + /// ``` + /// use sigils::Degree; + /// use sigils::Constants; + /// + /// let e32: f32 = Constants::E; + /// let x32: Degree = Degree::from(f32::INVERSE_PI_DIVIDED_BY_180); + /// let f_val32 = x32.cosh(); + /// let g_val32 = (e32*e32 + 1.0f32)/(2.0f32*e32); + /// let abs_difference32 = (f_val32 - g_val32).abs(); + /// + /// let e64: f64 = Constants::E; + /// let x64: Degree = Degree::from(f64::INVERSE_PI_DIVIDED_BY_180); + /// let f_val64 = x64.cosh(); + /// let g_val64 = (e64*e64 + 1.0f64)/(2.0f64*e64); + /// let abs_difference64 = (f_val64 - g_val64).abs(); + /// + /// // Solving cosh() at 1 gives this result + /// //assert!(abs_difference32 < 1.0e-10); + /// assert!(abs_difference64 < 1.0e-10); + /// ``` + pub fn cosh(&self) -> T + { + let radians: Radian; + + radians = Radian::from(*self); + radians.cosh() + } + + /// Hyperbolic sine function. + /// + /// ``` + /// use sigils::Degree; + /// use sigils::Constants; + /// + /// let e32: f32 = Constants::E; + /// let x32: Degree = Degree::from(f32::INVERSE_PI_DIVIDED_BY_180); + /// + /// let f_val32 = x32.sinh(); + /// let g_val32 = (e32*e32 - 1.0f32)/(2.0f32*e32); + /// let abs_difference32 = (f_val32 - g_val32).abs(); + /// + /// let e64: f64 = Constants::E; + /// let x64: Degree = Degree::from(f64::INVERSE_PI_DIVIDED_BY_180); + /// + /// let f_val64 = x64.sinh(); + /// let g_val64 = (e64*e64 - 1.0f64)/(2.0f64*e64); + /// let abs_difference64 = (f_val64 - g_val64).abs(); + /// + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// //assert!(abs_difference32 < 1e-10); + /// assert!(abs_difference64 < 1e-10); + /// ``` + pub fn sinh(&self) -> T + { + let radians: Radian; + + radians = Radian::from(*self); + radians.sinh() + } + + /// Hyperbolic tangent function. + /// + /// ``` + /// use sigils::Degree; + /// use sigils::Constants; + /// + /// let e32: f32 = Constants::E; + /// let x32: Degree = Degree::from(f32::INVERSE_PI_DIVIDED_BY_180); + /// + /// let f_val32 = x32.tanh(); + /// let g_val32 = (1.0f32 - e32.powi(-2i32))/(1.0f32 + e32.powi(-2i32)); + /// let abs_difference32 = (f_val32 - g_val32).abs(); + /// + /// let e64: f64 = Constants::E; + /// let x64: Degree = Degree::from(f64::INVERSE_PI_DIVIDED_BY_180); + /// + /// let f_val64 = x64.tanh(); + /// let g_val64 = (1.0f64 - e64.powi(-2i32))/(1.0f64 + e64.powi(-2i32)); + /// let abs_difference64 = (f_val64 - g_val64).abs(); + /// + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// //assert!(abs_difference32 < 1.0e-10); + /// assert!(abs_difference64 < 1.0e-10); + /// ``` + pub fn tanh(&self) -> T + { + let radians: Radian; + + radians = Radian::from(*self); + radians.tanh() + } +} + +impl ::std::ops::Deref for Degree where T: Real +{ + type Target = T; + + fn deref(&self) -> &T + { + &self.value + } +} + +impl ::std::ops::DerefMut for Degree where T: Real +{ + fn deref_mut<'a>(&'a mut self) -> &'a mut T + { + &mut self.value + } +} + +binary_operator_impl!(Add::add, Degree {value}, Real); +binary_operator_impl!(Sub::sub, Degree {value}, Real); +binary_operator_impl!(Mul::mul, Degree {value}, Real); +binary_operator_impl!(Div::div, Degree {value}, Real); +binary_operator_impl!(Rem::rem, Degree {value}, Real); + +impl ::std::ops::Neg for Degree + where T: Neg +{ + type Output = Degree; + + fn neg(self) -> Degree + { + let mut degrees: Degree; + + degrees = self; + degrees.value = degrees.value.neg(); + degrees + } +} + +binary_operator_assign_impl!(AddAssign::add_assign, + Degree {value}, Real); +binary_operator_assign_impl!(SubAssign::sub_assign, + Degree {value}, Real); +binary_operator_assign_impl!(MulAssign::mul_assign, + Degree {value}, Real); +binary_operator_assign_impl!(DivAssign::div_assign, + Degree {value}, Real); +binary_operator_assign_impl!(RemAssign::rem_assign, + Degree {value}, Real); + +impl ::std::convert::From> for f32 +{ + fn from(degrees: Degree) -> f32 + { + degrees.value + } +} + +impl ::std::convert::From> for f64 +{ + fn from(degrees: Degree) -> f64 + { + degrees.value + } +} + +impl ::std::convert::From for Degree where T: Real +{ + fn from(val: T) -> Degree + { + Degree::new(val) + } +} + +impl ::std::convert::From> for Degree where T: Real +{ + fn from(radians: Radian) -> Degree + { + let degs: T; + + degs = radians.value * Constants::INVERSE_PI_DIVIDED_BY_180; + Degree::new(degs) + } +} + +impl ::std::fmt::Debug for Degree + where T: Real +{ + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result + { + ::std::fmt::Display::fmt(self, f) + } +} + +impl ::std::fmt::Display for Degree + where T: Real +{ + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result + { + write!(f, "{}", self.value) + } +} + +impl Radian where T: Real +{ + /// Create a new Radian structure with the given value. + pub fn new(val: T) -> Radian + { + Radian + { + value: val + } + } + + /// Computes the arccosine of a number. Return value is in Degrees in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + ///``` + /// use sigils::{Constants, Radian, Real}; + /// + /// let f: Radian; + /// + /// f = Radian::from(f64::PI / 4.0f64); + /// assert!(((f64::PI / 4.0f64) - *Radian::acos(f.cos())) < 1e-10); + ///``` + pub fn acos(val: T) -> Radian + { + val.acos() + } + + /// Computes the arcsine of a number. Return value is in Degrees in + /// the range [-pi/2, pi/2] or NaN if the number is + /// outside the range [-1, 1]. + /// + ///``` + /// use sigils::{Constants, Radian, Real}; + /// + /// let f: Radian; + /// + /// f = Radian::from(f64::PI / 4.0f64); + /// assert!(((f64::PI / 4.0f64) - *Radian::asin(f.sin())) < 1e-10); + ///``` + pub fn asin(val: T) -> Radian + { + val.asin() + } + + /// Computes the arctangent of a number. Return value is in degrees in the + /// range [-pi/2, pi/2]; + /// + ///``` + /// use sigils::{Constants, Radian, Real}; + /// + /// let f: Radian; + /// + /// f = Radian::from(f64::PI / 4.0f64); + /// assert!(((f64::PI / 4.0f64) - *Radian::atan(f.tan())) < 1e-10); + ///``` + pub fn atan(val: T) -> Radian + { + val.atan() + } + + /// Inverse hyperbolic cosine function. + pub fn acosh(val: T) -> Radian + { + val.acosh() + } + + /// Inverse hyperbolic sine function. + pub fn asinh(val: T) -> Radian + { + val.asinh() + } + + /// Inverse hyperbolic tangent function. + pub fn atanh(val: T) -> Radian + { + val.atanh() + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`). + pub fn atan2(y: T, x: T) -> Radian + { + y.atan2(x) + } + + /// Computes the cosine of this angle. + /// + /// ``` + /// use sigils::Radian; + /// use std::f64; + /// + /// let x: Radian = Radian::from(2.0*f64::consts::PI); + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + pub fn cos(&self) -> T + { + self.value.cos() + } + + /// Computes the sine of this angle. + /// + /// ``` + /// use sigils::Radian; + /// use std::f64; + /// + /// let x: Radian = Radian::from(f64::consts::PI/2.0); + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + pub fn sin(&self) -> T + { + self.value.sin() + } + + /// Computes the tangent of this angle. + /// + /// ``` + /// use sigils::Radian; + /// use std::f64; + /// + /// let x: Radian = Radian::from(f64::consts::PI/4.0); + /// + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-14); + /// ``` + pub fn tan(&self) -> T + { + self.value.tan() + } + + /// Hyperbolic cosine function. + /// + /// ``` + /// use sigils::Radian; + /// use sigils::Constants; + /// + /// let e32: f32 = Constants::E; + /// let x32: Radian = Radian::new(1.0f32); + /// let f_val32 = x32.cosh(); + /// let g_val32 = (e32*e32 + 1.0f32)/(2.0f32*e32); + /// let abs_difference32 = (f_val32 - g_val32).abs(); + /// + /// let e64: f64 = Constants::E; + /// let x64: Radian = Radian::new(1.0f64); + /// let f_val64 = x64.cosh(); + /// let g_val64 = (e64*e64 + 1.0f64)/(2.0f64*e64); + /// let abs_difference64 = (f_val64 - g_val64).abs(); + /// + /// // Solving cosh() at 1 gives this result + /// //assert!(abs_difference32 < 1.0e-10); + /// assert!(abs_difference64 < 1.0e-10); + /// ``` + pub fn cosh(&self) -> T + { + self.value.cosh() + } + + /// Hyperbolic sine function. + /// + /// ``` + /// use sigils::Radian; + /// use sigils::Constants; + /// + /// let e32: f32 = Constants::E; + /// let x32: Radian = Radian::from(1.0f32); + /// + /// let f_val32 = x32.sinh(); + /// let g_val32 = (e32*e32 - 1.0f32)/(2.0f32*e32); + /// let abs_difference32 = (f_val32 - g_val32).abs(); + /// + /// let e64: f64 = Constants::E; + /// let x64: Radian = Radian::from(1.0f64); + /// + /// let f_val64 = x64.sinh(); + /// let g_val64 = (e64*e64 - 1.0f64)/(2.0f64*e64); + /// let abs_difference64 = (f_val64 - g_val64).abs(); + /// + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// //assert!(abs_difference32 < 1e-10); + /// assert!(abs_difference64 < 1e-10); + /// ``` + pub fn sinh(&self) -> T + { + self.value.sinh() + } + + /// Hyperbolic tangent function. + /// + /// ``` + /// use sigils::Radian; + /// use sigils::Constants; + /// + /// let e32: f32 = Constants::E; + /// let x32: Radian = Radian::from(1.0f32); + /// + /// let f_val32 = x32.tanh(); + /// let g_val32 = (1.0f32 - e32.powi(-2i32))/(1.0f32 + e32.powi(-2i32)); + /// let abs_difference32 = (f_val32 - g_val32).abs(); + /// + /// let e64: f64 = Constants::E; + /// let x64: Radian = Radian::from(1.0f64); + /// + /// let f_val64 = x64.tanh(); + /// let g_val64 = (1.0f64 - e64.powi(-2i32))/(1.0f64 + e64.powi(-2i32)); + /// let abs_difference64 = (f_val64 - g_val64).abs(); + /// + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// //assert!(abs_difference32 < 1.0e-10); + /// assert!(abs_difference64 < 1.0e-10); + /// ``` + pub fn tanh(&self) -> T + { + self.value.tanh() + } +} + +impl ::std::ops::Deref for Radian where T: Real +{ + type Target = T; + + fn deref(&self) -> &T + { + &self.value + } +} + +impl ::std::ops::DerefMut for Radian where T: Real +{ + fn deref_mut<'a>(&'a mut self) -> &'a mut T + { + &mut self.value + } +} + +binary_operator_impl!(Add::add, Radian {value}, Real); +binary_operator_impl!(Sub::sub, Radian {value}, Real); +binary_operator_impl!(Mul::mul, Radian {value}, Real); +binary_operator_impl!(Div::div, Radian {value}, Real); +binary_operator_impl!(Rem::rem, Radian {value}, Real); + +impl ::std::ops::Neg for Radian + where T: Neg +{ + type Output = Radian; + + fn neg(self) -> Radian + { + let mut radians: Radian; + + radians = self; + radians.value = radians.value.neg(); + radians + } +} + +binary_operator_assign_impl!(AddAssign::add_assign, + Radian {value}, Real); +binary_operator_assign_impl!(SubAssign::sub_assign, + Radian {value}, Real); +binary_operator_assign_impl!(MulAssign::mul_assign, + Radian {value}, Real); +binary_operator_assign_impl!(DivAssign::div_assign, + Radian {value}, Real); +binary_operator_assign_impl!(RemAssign::rem_assign, + Radian {value}, Real); + +impl ::std::convert::From> for f32 +{ + fn from(radians: Radian) -> f32 + { + radians.value + } +} + +impl ::std::convert::From> for f64 +{ + fn from(radians: Radian) -> f64 + { + radians.value + } +} + +impl ::std::convert::From for Radian where T: Real +{ + fn from(val: T) -> Radian + { + Radian::new(val) + } +} + +impl ::std::convert::From> for Radian + where T: Real +{ + fn from(degrees: Degree) -> Radian + { + let rads: T; + + rads = degrees.value * Constants::PI_DIVIDED_BY_180; + Radian::new(rads) + } +} + +impl ::std::fmt::Debug for Radian + where T: Real +{ + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result + { + ::std::fmt::Display::fmt(self, f) + } +} + +impl ::std::fmt::Display for Radian + where T: Real +{ + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result + { + write!(f, "{}", self.value) + } } diff --git a/src/vector.rs b/src/vector.rs index c2dd0e4..0cd97b9 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -9,7 +9,7 @@ use ::zero::Zero; use ::one::One; use ::number::Number; use ::real::Real; -use ::trig::{acos, atan2}; +use ::trig::Radian; @@ -264,13 +264,15 @@ pub trait Vector: Debug + Display + Clone + Default + Zero + One /// // the components are 3. Then get the /// // dot product of the two Vectors. /// use sigils::vector::*; + /// use sigils::Radian; /// - /// let vector: Vector3 = Vector3::::from_value(3i64); - /// let vector_two: Vector3 = Vector3::::from_value(3i64); - /// let dotProduct: i64 = vector.dot(&vector_two); - /// # assert_eq!(dotProduct, 27i64); + /// let vector: Vector3 = Vector3::::from_value(3i32); + /// let vector_two: Vector3 = Vector3::::from_value(3i32); + /// let dotProduct: Radian = vector.dot(&vector_two); + /// # assert_eq!(*dotProduct, 27f64); ///``` - fn dot(&self, vector: &Self) -> T; + fn dot(&self, vector: &Self) -> Radian + where R: Real + ::std::convert::From; } /// Defines the [EuclideanVector][1] trait. @@ -290,12 +292,14 @@ pub trait EuclideanVector : Vector where T: Real ///``` fn get_length(&self) -> T { - let length: T; + let sq_rt: T; + let length: Radian; - length = self.dot(self).sqrt(); - assert!(length.is_finite()); + length = self.dot(self); + sq_rt = (*length).sqrt(); + assert!(sq_rt.is_finite()); - length + sq_rt } /// Get the squared length of the Vector. @@ -310,7 +314,10 @@ pub trait EuclideanVector : Vector where T: Real ///``` fn get_length_squared(&self) -> T { - self.dot(self) + let radians: Radian; + + radians = self.dot(self); + *radians } /// Normalizes the Vector by multplying all the @@ -362,15 +369,18 @@ pub trait EuclideanVector : Vector where T: Real // TODO: Add an example here. fn is_perpendicular_to(&self, vector: &Self) -> bool { + let dot: T; + // TODO: Make this work with a fudge factor since floats // are tricky. - self.dot(vector) == T::zero() + dot = *self.dot(vector); + dot == T::zero() } /// Calculates the angle between this vector and /// another Vector, in radians. // TODO: Add an example here. - fn angle(&self, vector: &Self) -> T; + fn angle(&self, vector: &Self) -> Radian; /// Linearly interpolate the length of this Vector /// towards the length of another Vector by a given amount. @@ -425,141 +435,6 @@ macro_rules! perform_method_on_components }; } -/// A macro that will define a binary operation for -/// a Vector and its components. -macro_rules! binary_operator_impl -{ - ($traitName: ident :: $funcName: ident, - $structName: ident {$($field: ident),+}) => - { - impl $traitName for $structName where T: Number - { - type Output = $structName; - - fn $funcName(self, scalar: T) -> $structName - { - $structName::new($(self.$field.$funcName(scalar)),+) - } - } - - impl<'a, T> $traitName for &'a $structName where T: Number - { - type Output = $structName; - - fn $funcName(self, scalar: T) -> $structName - { - $structName::new($(self.$field.$funcName(scalar)),+) - } - } - - impl<'a, T> $traitName<&'a T> for $structName where T: Number - { - type Output = $structName; - - fn $funcName(self, scalar: &'a T) -> $structName - { - $structName::new($(self.$field.$funcName(*scalar)),+) - } - } - - impl<'a, 'b, T> $traitName<&'b T> for &'a $structName where T: Number - { - type Output = $structName; - - fn $funcName(self, scalar: &'b T) -> $structName - { - $structName::new($(self.$field.$funcName(*scalar)),+) - } - } - - impl $traitName<$structName> for $structName - where T: Number - { - type Output = $structName; - - fn $funcName(self, vector: $structName) -> $structName - { - $structName::new($(self.$field.$funcName(vector.$field)),+) - } - } - - impl<'a, T> $traitName<$structName> for &'a $structName - where T: Number - { - type Output = $structName; - - fn $funcName(self, vector: $structName) -> $structName - { - $structName::new($(self.$field.$funcName(vector.$field)),+) - } - } - - impl<'a, T> $traitName<&'a $structName> for $structName - where T: Number - { - type Output = $structName; - - fn $funcName(self, vector: &'a $structName) -> $structName - { - $structName::new($(self.$field.$funcName(vector.$field)),+) - } - } - - impl<'a, 'b, T> $traitName<&'a $structName> for &'b $structName - where T: Number - { - type Output = $structName; - - fn $funcName(self, vector: &'a $structName) -> $structName - { - $structName::new($(self.$field.$funcName(vector.$field)),+) - } - } - } -} - -/// A macro that will define a binary operation for -/// a Vector and its components. -macro_rules! binary_operator_assign_impl -{ - ($traitName: ident :: $funcName: ident, - $structName: ident {$($field: ident),+}) => - { - impl $traitName<$structName> for $structName where T: Number - { - fn $funcName(&mut self, rhs: $structName) - { - $(self.$field.$funcName(rhs.$field);)+ - } - } - - impl $traitName for $structName where T: Number - { - fn $funcName(&mut self, rhs: T) - { - $(self.$field.$funcName(rhs);)+ - } - } - - impl<'a, T> $traitName<&'a $structName> for $structName - where T: Number - { - fn $funcName(&mut self, rhs: &'a $structName) - { - $(self.$field.$funcName(rhs.$field);)+ - } - } - - impl<'a, T> $traitName<&'a T> for $structName where T: Number - { - fn $funcName(&mut self, rhs: &'a T) - { - $(self.$field.$funcName(*rhs);)+ - } - } - } -} - /// A macro that defines a Vector structure /// that implements the Vector trait. macro_rules! define_vector @@ -589,21 +464,6 @@ macro_rules! define_vector } } - // Give this vector a negation function when the - // type stored in it is negatable. - impl<$T> $structName<$T> where T: Copy + Neg - { - /// Negate this vector in-place (multiply by -1). - /// - ///``` - /// - ///``` - pub fn negate(&mut self) - { - $(self.$field = -self.$field);+ - } - } - impl Vector for $structName where T: Number { fn from_value(val: T) -> $structName @@ -678,15 +538,19 @@ macro_rules! define_vector perform_method_on_components!(mul, {$(self.$field),+}) } - fn dot(&self, vector: &$structName) -> T + fn dot(&self, vector: &$structName) -> Radian + where R: Real + ::std::convert::From { - self.mul(vector).get_sum() + let dot: R; + dot = R::from(self.mul(vector).get_sum()); + Radian::new(dot) } } // Implement the Debug trait for the Vector. impl Debug for $structName where T: Number { + #[allow(unused_assignments)] fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { let mut count: u8; @@ -695,12 +559,12 @@ macro_rules! define_vector // pretty up the printing. count = self.get_size(); - write!(formatter, "<"); + try!(write!(formatter, "<")); $( - write!(formatter, "{:?}", self.$field); + try!(write!(formatter, "{:?}", self.$field)); if count > 0 { - write!(formatter, ", "); + try!(write!(formatter, ", ")); } count -= 1; )* @@ -711,6 +575,7 @@ macro_rules! define_vector // Implement the Display trait for the Vector. impl Display for $structName where T: Number { + #[allow(unused_assignments)] fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { let mut count: u8; @@ -719,12 +584,12 @@ macro_rules! define_vector // pretty up the printing. count = self.get_size(); - write!(formatter, "<"); + try!(write!(formatter, "<")); $( - write!(formatter, "{}", self.$field); + try!(write!(formatter, "{}", self.$field)); if count > 1 { - write!(formatter, ", "); + try!(write!(formatter, ", ")); } count -= 1; )* @@ -770,23 +635,23 @@ macro_rules! define_vector } // Implement the binary operations for this Vector structure. - binary_operator_impl!(Add::add, $structName {$($field),+}); - binary_operator_impl!(Sub::sub, $structName {$($field),+}); - binary_operator_impl!(Mul::mul, $structName {$($field),+}); - binary_operator_impl!(Div::div, $structName {$($field),+}); - binary_operator_impl!(Rem::rem, $structName {$($field),+}); + binary_operator_impl!(Add::add, $structName {$($field),+}, Number); + binary_operator_impl!(Sub::sub, $structName {$($field),+}, Number); + binary_operator_impl!(Mul::mul, $structName {$($field),+}, Number); + binary_operator_impl!(Div::div, $structName {$($field),+}, Number); + binary_operator_impl!(Rem::rem, $structName {$($field),+}, Number); // Handle the assignment operators. binary_operator_assign_impl!(AddAssign::add_assign, - $structName {$($field),+}); + $structName {$($field),+}, Number); binary_operator_assign_impl!(SubAssign::sub_assign, - $structName {$($field),+}); + $structName {$($field),+}, Number); binary_operator_assign_impl!(MulAssign::mul_assign, - $structName {$($field),+}); + $structName {$($field),+}, Number); binary_operator_assign_impl!(DivAssign::div_assign, - $structName {$($field),+}); + $structName {$($field),+}, Number); binary_operator_assign_impl!(RemAssign::rem_assign, - $structName {$($field),+}); + $structName {$($field),+}, Number); } } @@ -797,6 +662,7 @@ define_vector!(Vector3 {x, y, z}, 3u8); define_vector!(Vector4 {x, y, z, w}, 4u8); + // Implements operations specific to the different // Vector structure sizes. impl Vector2 where T: Number @@ -833,9 +699,13 @@ impl Vector2 where T: Number /// Calculate the perpendicular dot product. // TODO: Add an example. - pub fn perpendicular_dot(&self, vector: &Vector2) -> T + pub fn perpendicular_dot(&self, vector: &Vector2) -> Radian + where R: Real + ::std::convert::From { - (self.x * vector.y) - (self.y * vector.x) + let val: R; + + val = R::from((self.x * vector.y) - (self.y * vector.x)); + Radian::new(val) } /// Adds a z component with the given value to the Vector. @@ -1056,24 +926,27 @@ impl Vector4 where T: Number // Implement the angle specific portion of the EuclideanVector. impl EuclideanVector for Vector2 where T: Real { - fn angle(&self, vector: &Vector2) -> T + fn angle(&self, vector: &Vector2) -> Radian { - atan2(self.perpendicular_dot(vector), self.dot(vector)) + Radian::atan2((*self.perpendicular_dot(vector)), (*self.dot(vector))) } } impl EuclideanVector for Vector3 where T: Real { - fn angle(&self, vector: &Vector3) -> T + fn angle(&self, vector: &Vector3) -> Radian { - atan2(self.cross(vector).get_length(), self.dot(vector)) + Radian::atan2(self.cross(vector).get_length(), (*self.dot(vector))) } } impl EuclideanVector for Vector4 where T: Real { - fn angle(&self, vector: &Vector4) -> T + fn angle(&self, vector: &Vector4) -> Radian { - acos(self.dot(vector) / (self.get_length() * vector.get_length())) + let dot: T; + + dot = *self.dot(vector); + Radian::acos(dot / self.get_length() * vector.get_length()) } }