1#[cfg(feature = "std")]
4use super::ratio::Ratio;
5
6quantity! {
7 quantity: Angle; "angle";
9 dimension: ISQ<
11 Z0, Z0, Z0, Z0, Z0, Z0, Z0>; kind: dyn (crate::si::marker::AngleKind);
19 units {
20 @radian: 1.0_E0; "rad", "radian", "radians";
23 @revolution: 6.283_185_307_179_586_E0; "r", "revolution", "revolutions";
24 @degree: 1.745_329_251_994_329_5_E-2; "°", "degree", "degrees";
25 @gon: 1.570_796_326_794_896_7_E-2; "gon", "gon", "gons";
26 @mil: 9.817_477_E-4; "mil", "mil", "mils";
27 @minute: 2.908_882_086_657_216_E-4; "′", "minute", "minutes";
28 @second: 4.848_136_811_095_36_E-6; "″", "second", "seconds";
29 }
30}
31
32#[cfg(feature = "f32")]
33impl Angle<crate::si::SI<f32>, f32> {
34 pub const HALF_TURN: Self = Self {
36 dimension: crate::lib::marker::PhantomData,
37 units: crate::lib::marker::PhantomData,
38 value: crate::lib::f32::consts::PI,
39 };
40
41 pub const FULL_TURN: Self = Self {
43 dimension: crate::lib::marker::PhantomData,
44 units: crate::lib::marker::PhantomData,
45 value: 2. * crate::lib::f32::consts::PI,
46 };
47}
48
49#[cfg(feature = "f64")]
50impl Angle<crate::si::SI<f64>, f64> {
51 pub const HALF_TURN: Self = Self {
53 dimension: crate::lib::marker::PhantomData,
54 units: crate::lib::marker::PhantomData,
55 value: crate::lib::f64::consts::PI,
56 };
57
58 pub const FULL_TURN: Self = Self {
60 dimension: crate::lib::marker::PhantomData,
61 units: crate::lib::marker::PhantomData,
62 value: 2. * crate::lib::f64::consts::PI,
63 };
64}
65
66#[cfg(feature = "std")]
68impl<U, V> Angle<U, V>
69where
70 U: crate::si::Units<V> + ?Sized,
71 V: crate::num::Float + crate::Conversion<V>,
72{
73 #[must_use = "method returns a new number and does not mutate the original value"]
75 #[inline(always)]
76 pub fn cos(self) -> Ratio<U, V> {
77 self.value.cos().into()
78 }
79
80 #[must_use = "method returns a new number and does not mutate the original value"]
82 #[inline(always)]
83 pub fn cosh(self) -> Ratio<U, V> {
84 self.value.cosh().into()
85 }
86
87 #[must_use = "method returns a new number and does not mutate the original value"]
89 #[inline(always)]
90 pub fn sin(self) -> Ratio<U, V> {
91 self.value.sin().into()
92 }
93
94 #[must_use = "method returns a new number and does not mutate the original value"]
96 #[inline(always)]
97 pub fn sinh(self) -> Ratio<U, V> {
98 self.value.sinh().into()
99 }
100
101 #[must_use = "method returns a new number and does not mutate the original value"]
103 #[inline(always)]
104 pub fn sin_cos(self) -> (Ratio<U, V>, Ratio<U, V>) {
105 let (sin, cos) = self.value.sin_cos();
106 (sin.into(), cos.into())
107 }
108
109 #[must_use = "method returns a new number and does not mutate the original value"]
111 #[inline(always)]
112 pub fn tan(self) -> Ratio<U, V> {
113 self.value.tan().into()
114 }
115
116 #[must_use = "method returns a new number and does not mutate the original value"]
118 #[inline(always)]
119 pub fn tanh(self) -> Ratio<U, V> {
120 self.value.tanh().into()
121 }
122}
123
124#[cfg(feature = "std")]
125impl<D, U, V> crate::si::Quantity<D, U, V>
126where
127 D: crate::si::Dimension + ?Sized,
128 U: crate::si::Units<V> + ?Sized,
129 V: crate::num::Float + crate::Conversion<V>,
130 radian: crate::Conversion<V, T = V::T>,
131{
132 #[must_use = "method returns a new number and does not mutate the original value"]
134 #[inline(always)]
135 pub fn atan2(self, other: Self) -> Angle<U, V> {
136 Angle::new::<radian>(self.value.atan2(other.value))
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 storage_types! {
143 use crate::lib::f64::consts::PI;
144 use crate::num::{FromPrimitive, One};
145 use crate::si::angle as a;
146 use crate::si::quantities::*;
147 use crate::tests::Test;
148
149 #[test]
150 fn check_units() {
151 Test::assert_eq(&Angle::new::<a::radian>(V::from_f64(2.0 * PI).unwrap()),
152 &Angle::new::<a::revolution>(V::one()));
153 Test::assert_eq(&Angle::new::<a::degree>(V::from_f64(360.0).unwrap()),
154 &Angle::new::<a::revolution>(V::one()));
155 Test::assert_approx_eq(&Angle::new::<a::gon>(V::from_f64(400.0).unwrap()),
156 &Angle::new::<a::revolution>(V::one()));
157 Test::assert_eq(&Angle::new::<a::minute>(V::from_f64(60.0).unwrap()),
158 &Angle::new::<a::degree>(V::one()));
159 Test::assert_eq(&Angle::new::<a::second>(V::from_f64(60.0 * 60.0).unwrap()),
160 &Angle::new::<a::degree>(V::one()));
161 }
162 }
163
164 #[cfg(feature = "std")]
165 mod trig {
166 storage_types! {
167 types: Float;
168
169 use crate::lib::f64::consts::PI;
170 use crate::num::{FromPrimitive, Zero};
171 use crate::si::angle as a;
172 use crate::si::length as l;
173 use crate::si::quantities::*;
174 use crate::tests::Test;
175
176 #[test]
177 fn sanity() {
178 let zero: Angle<V> = Angle::zero();
179 let nzero: Angle<V> = -Angle::zero();
180 let pi: Angle<V> = Angle::new::<a::radian>(V::from_f64(PI).unwrap());
181 let half: Angle<V> = Angle::new::<a::radian>(V::from_f64(PI / 2.0).unwrap());
182
183 Test::assert_approx_eq(&zero.cos().into(), &1.0);
184 Test::assert_approx_eq(&nzero.cos().into(), &1.0);
185
186 Test::assert_approx_eq(&pi.cos().into(), &-1.0);
187 Test::assert_approx_eq(&half.cos().into(), &0.0);
188
189 Test::assert_approx_eq(&zero.sin().into(), &0.0);
190 Test::assert_approx_eq(&nzero.sin().into(), &0.0);
191
192 Test::assert_approx_eq(&zero.tan().into(), &0.0);
198 Test::assert_approx_eq(&nzero.tan().into(), &0.0);
199
200 Test::assert_approx_eq(&zero.cosh().into(), &1.0);
207 Test::assert_approx_eq(&nzero.cosh().into(), &1.0);
208
209 Test::assert_approx_eq(&zero.sinh().into(), &0.0);
210 Test::assert_approx_eq(&nzero.sinh().into(), &0.0);
211
212 Test::assert_approx_eq(&zero.tanh().into(), &0.0);
213 Test::assert_approx_eq(&nzero.tanh().into(), &0.0);
214 }
215
216 quickcheck! {
217 #[allow(trivial_casts)]
218 fn atan2(y: V, x: V) -> bool {
219 Test::eq(&y.atan2(x),
220 &Length::new::<l::meter>(y).atan2(Length::new::<l::meter>(x)).get::<a::radian>())
221 }
222 }
223
224 #[test]
225 fn turns() {
226 Test::assert_approx_eq(&Angle::<V>::HALF_TURN.cos().into(), &-1.0);
227 Test::assert_approx_eq(&Angle::<V>::FULL_TURN.cos().into(), &1.0);
228 }
229 }
230 }
231}