1extern crate num;
2
3#[cfg(test)] extern crate hamcrest;
4#[cfg(test)] extern crate quickcheck;
5
6
7use num::{Float, Num, NumCast, Signed, Zero, cast};
8use std::f64::consts::PI;
9use std::fmt::{Display, Formatter, Error};
10use std::ops::{Add, Div, Mul, Neg, Sub};
11
12
13#[derive(Copy, Clone, Debug)]
17pub enum Angle<T=f64> {
18 Radians(T),
19 Degrees(T)
20}
21
22impl<T: NumCast> Angle<T> {
23 pub fn in_radians(self) -> T {
25 match self {
26 Radians(v) => v,
27 Degrees(v) => cast(cast::<T, f64>(v).unwrap() / 180.0 * PI).unwrap()
28 }
29 }
30
31 pub fn in_degrees(self) -> T {
33 match self {
34 Radians(v) => cast(cast::<T, f64>(v).unwrap() / PI * 180.0).unwrap(),
35 Degrees(v) => v
36 }
37 }
38
39 pub fn eighth() -> Angle<T> {
41 Degrees(cast(45).unwrap())
42 }
43
44 pub fn quarter() -> Angle<T> {
46 Degrees(cast(90).unwrap())
47 }
48
49 pub fn half() -> Angle<T> {
51 Degrees(cast(180).unwrap())
52 }
53
54 pub fn full() -> Angle<T> {
56 Degrees(cast(360).unwrap())
57 }
58}
59
60impl<T: Copy + Num + NumCast + PartialOrd> Angle<T> {
61 pub fn normalized(self) -> Self {
64 let (v, upper) = match self {
65 Radians(v) => (v, cast(2.0 * PI).unwrap()),
66 Degrees(v) => (v, cast(360.0).unwrap())
67 };
68
69 let normalized = if v < upper && v >= Zero::zero() {
70 v
71 } else {
72 let v = v % upper;
73
74 if v >= Zero::zero() {
75 v
76 } else {
77 v + upper
78 }
79 };
80
81 match self {
82 Radians(_) => Radians(normalized),
83 Degrees(_) => Degrees(normalized)
84 }
85 }
86}
87
88impl<T: Float> Angle<T> {
89 pub fn min_dist(self, other: Angle<T>) -> Angle<T> {
98 let pi = cast(PI).unwrap();
99 let two_pi = cast(2.0 * PI).unwrap();
100
101 let a = self.in_radians();
102 let b = other.in_radians();
103
104 let d = (a - b).abs();
105
106 Radians(if a >= T::zero() && a < two_pi && b >= T::zero() && b < two_pi {
108 d.min(two_pi - d)
109 } else {
110 pi - ((d % two_pi) - pi).abs()
111 })
112 }
113}
114
115impl<T: Signed> Angle<T> {
116 pub fn abs(self) -> Self {
118 match self {
119 Radians(v) => Radians(v.abs()),
120 Degrees(v) => Degrees(v.abs())
121 }
122 }
123}
124
125impl<T: Float + NumCast> Angle<T> {
126 pub fn sin(self) -> T {
128 self.in_radians().sin()
129 }
130
131 pub fn cos(self) -> T {
133 self.in_radians().cos()
134 }
135
136 pub fn tan(self) -> T {
138 self.in_radians().tan()
139 }
140
141 pub fn sin_cos(self) -> (T, T) {
144 self.in_radians().sin_cos()
145 }
146}
147
148impl<T: Zero + Copy + NumCast> Zero for Angle<T> {
149 fn zero() -> Self {
150 Radians(T::zero())
151 }
152
153 fn is_zero(&self) -> bool {
154 match self {
155 &Radians(ref v) => v.is_zero(),
156 &Degrees(ref v) => v.is_zero()
157 }
158 }
159}
160
161impl<T: PartialEq + Copy + NumCast> PartialEq for Angle<T> {
162 fn eq(&self, other: &Angle<T>) -> bool {
163 if let (Degrees(a), Degrees(b)) = (*self, *other) {
164 a == b
165 } else {
166 self.in_radians() == other.in_radians()
167 }
168 }
169}
170
171macro_rules! math_additive(
172 ($bound:ident, $func:ident) => (
173 impl<T: $bound + Copy + NumCast> $bound for Angle<T> {
174 type Output = Angle<T::Output>;
175 fn $func(self, rhs: Angle<T>) -> Self::Output {
176 if let (Degrees(a), Degrees(b)) = (self, rhs) {
177 Degrees(a.$func(b))
178 } else {
179 Radians(self.in_radians().$func(rhs.in_radians()))
180 }
181 }
182 }
183 );
184);
185
186math_additive!(Add, add);
187math_additive!(Sub, sub);
188
189macro_rules! math_multiplicative(
190 ($bound:ident, $func:ident, $($t:ident),*) => (
191 impl<T: $bound> $bound<T> for Angle<T> {
192 type Output = Angle<T::Output>;
193 fn $func(self, rhs: T) -> Self::Output {
194 match self {
195 Radians(v) => Radians(v.$func(rhs)),
196 Degrees(v) => Degrees(v.$func(rhs))
197 }
198 }
199 }
200
201 $(
202 impl $bound<Angle<$t>> for $t {
203 type Output = Angle<$t>;
204 fn $func(self, rhs: Angle<$t>) -> Self::Output {
205 match rhs {
206 Radians(v) => Radians(self.$func(v)),
207 Degrees(v) => Degrees(self.$func(v))
208 }
209 }
210 }
211 )*
212 );
213);
214
215math_multiplicative!(Mul, mul, u8, u16, u32, u64, i8, i16, i32, i64, usize, isize, f32, f64);
216math_multiplicative!(Div, div, u8, u16, u32, u64, i8, i16, i32, i64, usize, isize, f32, f64);
217
218impl<T: Neg> Neg for Angle<T> {
219 type Output = Angle<T::Output>;
220 fn neg(self) -> Self::Output {
221 match self {
222 Radians(v) => Radians(-v),
223 Degrees(v) => Degrees(-v)
224 }
225 }
226}
227
228impl<T: Display> Display for Angle<T> {
229 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
230 match *self {
231 Radians(ref v) => write!(f, "{}rad", v),
232 Degrees(ref v) => write!(f, "{}°", v)
233 }
234 }
235}
236
237unsafe impl<T: Send> Send for Angle<T> { }
238
239
240pub fn asin<T: Float>(value: T) -> Option<Angle<T>> {
243 let value = value.asin();
244 if value.is_nan() {
245 None
246 } else {
247 Some(Radians(value))
248 }
249}
250
251pub fn acos<T: Float>(value: T) -> Option<Angle<T>> {
254 let value = value.acos();
255 if value.is_nan() {
256 None
257 } else {
258 Some(Radians(value))
259 }
260}
261
262pub fn atan<T: Float>(value: T) -> Angle<T> {
265 Radians(value.atan())
266}
267
268pub fn atan2<T: Float>(y: T, x: T) -> Angle<T> {
270 Radians(y.atan2(x))
271}
272
273pub fn mean_angle<T: Float>(angles: &[Angle<T>]) -> Angle<T>
277{
278 let mut x = T::zero();
279 let mut y = T::zero();
280
281 for angle in angles {
282 let (sin, cos) = angle.sin_cos();
283
284 x = x + cos;
285 y = y + sin;
286 }
287
288 let n = cast(angles.len()).unwrap();
289 let a = (y/n).atan2(x/n);
290
291 Radians(a).normalized()
292}
293
294
295pub use Angle::{Radians, Degrees};
297
298
299#[cfg(test)]
300mod tests {
301 use hamcrest::{assert_that, is, close_to};
302 use num::{Float, cast};
303 use quickcheck::{Arbitrary, Gen, quickcheck};
304 use std::f64::consts::PI;
305
306 use super::*;
307
308 #[test]
309 fn test_angle_conversions() {
310 fn prop(angle: Angle) -> bool {
311 are_close(angle.in_radians(), Degrees(angle.in_degrees()).in_radians())
312 }
313 quickcheck(prop as fn(Angle) -> bool);
314 }
315
316 #[test]
317 fn test_angle_math_multiplicative() {
318 fn prop(a: Angle, x: f64) -> bool {
319 match a {
320 Radians(v) => (a * x).in_radians() == v * x &&
321 (a / x).in_radians() == v / x,
322 Degrees(v) => (a * x).in_degrees() == v * x &&
323 (a / x).in_degrees() == v / x
324 }
325 }
326 quickcheck(prop as fn(Angle, f64) -> bool);
327 }
328
329 #[test]
330 fn test_angle_math_additive() {
331 fn prop(a: Angle, b: Angle) -> bool {
332 if let (Radians(x), Radians(y)) = (a, b) {
333 (a + b).in_radians() == x + y &&
334 (a - b).in_radians() == x - y
335 } else if let (Degrees(x), Degrees(y)) = (a, b) {
336 (a + b).in_degrees() == x + y &&
337 (a - b).in_degrees() == x - y
338 } else {
339 (a + b).in_radians() == a.in_radians() + b.in_radians()
340 }
341 }
342 quickcheck(prop as fn(Angle, Angle) -> bool);
343 }
344
345 #[test]
346 fn test_angle_normalization() {
347 fn prop(angle: Angle) -> bool {
348 let v = angle.normalized();
349 let rad = v.in_radians();
350 let deg = v.in_degrees();
351
352 0.0 <= rad && rad < 2.0 * PI &&
353 0.0 <= deg && deg < 360.0 &&
354 are_close(rad.cos(), angle.cos())
355 }
356 quickcheck(prop as fn(Angle) -> bool);
357 }
358
359 #[test]
360 fn test_angle_minimal_distance() {
361 fn prop(a: Angle, b: Angle) -> bool {
362 let d = a.min_dist(b);
363 0.0 <= d.in_radians() && d.in_radians() <= PI
364 }
365 quickcheck(prop as fn(Angle, Angle) -> bool);
366
367 assert_that(Degrees(180.0).min_dist(Degrees(0.0)).in_degrees(), is(close_to(180.0, 0.000001)));
368 assert_that(Degrees(0.1).min_dist(Degrees(359.9)).in_degrees(), is(close_to(0.2, 0.000001)));
369 assert_that(Degrees(1.0).min_dist(Degrees(2.0)).in_degrees(), is(close_to(1.0, 0.000001)));
370 }
371
372 #[test]
373 pub fn test_mean_angle() {
374 assert_that(mean_angle(&[Degrees(90.0)]).in_degrees(), is(close_to(90.0, 0.000001)));
375 assert_that(mean_angle(&[Degrees(90.0), Degrees(90.0)]).in_degrees(), is(close_to(90.0, 0.000001)));
376 assert_that(mean_angle(&[Degrees(90.0), Degrees(180.0), Degrees(270.0)]).in_degrees(), is(close_to(180.0, 0.000001)));
377 assert_that(mean_angle(&[Degrees(20.0), Degrees(350.0)]).in_degrees(), is(close_to(5.0, 0.000001)));
378 }
379
380 fn are_close<T: Float>(a: T, b: T) -> bool {
381 (a - b).abs() < cast(1.0e-10).unwrap()
382 }
383
384 impl<T: Arbitrary> Arbitrary for Angle<T> {
385 fn arbitrary<G: Gen>(g: &mut G) -> Self {
386 let v = Arbitrary::arbitrary(g);
387 if bool::arbitrary(g) {
388 Radians(v)
389 } else {
390 Degrees(v)
391 }
392 }
393 }
394}