angulus/
rand.rs

1//! Generate random angles with the [rand crate](https://docs.rs/rand/latest/rand/).
2//!
3//! # Provided implementations
4//!
5//! [`Angle`] and [`AngleUnbounded`] (and [their unit-wrapped equivalents][crate::units]) can be generated
6//! with the [`Standard`] distribution, and so with [`rand::random`](https://docs.rs/rand/latest/rand/fn.random.html)
7//! with the following ranges and distributions:
8//!
9//! - [`Angle`]: uniformly distributed on the circle.
10//! - [`AngleUnbounded`]: uniformly distributed in [the main range](crate#the-main-range).
11//!
12//! **Note**: The unit wrappers have no influence on the generated value.
13//!
14//! # Uniform ranges
15//!
16//! [`Angle`] and [`AngleUnbounded`] can also be generated from a range using [`rand::Rng::gen_range`]
17//! with some specific behaviour.
18//!
19//! ## [`Angle`]
20//!
21//! Because [`Angle`] [did not implement `PartialOrd`](Angle#why-doesnt-it-implement-partialord-),
22//! the generated angle will belong to the part of the circle between the bounds in counterclockwise.
23//! I.e., the order of the bounds will determine which part of the circle the generated angle belongs to.
24//!
25//! ```
26//! # use angulus::*;
27//! # use ::rand::*;
28//! let top = Angle32::DEG_90;
29//! let bottom = -Angle32::DEG_90;
30//!
31//! let mut rng = thread_rng();
32//!
33//! // Generate an angle on the "left" part of the circle.
34//! let a = rng.gen_range(top..=bottom);
35//! assert!(a.cos() <= 0.0);
36//!
37//! // Generate an angle on the "right" part of the circle.
38//! let a = rng.gen_range(bottom..=top);
39//! assert!(a.cos() >= 0.0);
40//! ```
41//!
42//! ## [`AngleUnbounded`]
43//!
44//! Since [`AngleUnbounded`] implements [`PartialOrd`], the order matters and the range may be empty,
45//! resulting in panic.
46//!
47//! ```no_run
48//! # use angulus::*;
49//! # use ::rand::*;
50//! let low = AngleUnbounded32::ZERO;
51//! let high = AngleUnbounded32::DEG_90;
52//! let x = thread_rng().gen_range(low..=high);
53//! ```
54//!
55//! ```should_panic
56//! # use angulus::*;
57//! # use ::rand::*;
58//! # let low = AngleUnbounded32::ZERO;
59//! # let high = AngleUnbounded32::DEG_90;
60//! let x = thread_rng().gen_range(high..=low);
61//! // panic: "cannot sample empty range"
62//! ```
63
64use core::ops::{Range, RangeInclusive};
65
66use rand::distributions::uniform::{
67    SampleBorrow, SampleRange, SampleUniform, UniformFloat, UniformSampler,
68};
69use rand::distributions::{Distribution, Standard};
70use rand::Rng;
71
72use crate::float::Float;
73use crate::units::{Degrees, Gradians, Radians, Turns};
74use crate::{Angle, AngleUnbounded};
75
76//-------------------------------------------------------------------
77// Standard Distribution
78//-------------------------------------------------------------------
79
80impl<F: Float> Distribution<Angle<F>> for Standard
81where
82    Self: Distribution<F>,
83{
84    #[inline]
85    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Angle<F> {
86        // Standard distribution generates float in the range [0, 1).
87        let x = rng.gen::<F>();
88
89        let x = x * F::TAU; // [0, 1) --> [0, TAU)
90        let x = x - F::PI; // [0, TAU) --> [-PI, PI)
91        let x = -x; // [-PI, PI) --> (-PI, PI]
92
93        debug_assert!(-F::PI < x && x <= F::PI);
94
95        Angle::from_radians_unchecked(x)
96    }
97}
98
99impl<F: Float> Distribution<AngleUnbounded<F>> for Standard
100where
101    Self: Distribution<Angle<F>>,
102{
103    #[inline]
104    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> AngleUnbounded<F> {
105        rng.gen::<Angle<F>>().to_unbounded()
106    }
107}
108
109macro_rules! impl_distribution_for_unit {
110    (
111        $($Unit:ident),+
112    ) => {
113        $(
114            impl<T> Distribution<$Unit<T>> for Standard
115            where
116                Self: Distribution<T>,
117            {
118                #[inline]
119                fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $Unit<T> {
120                    $Unit(rng.gen())
121                }
122            }
123        )+
124    };
125}
126
127impl_distribution_for_unit!(Radians, Degrees, Turns, Gradians);
128
129//-------------------------------------------------------------------
130// Range
131//-------------------------------------------------------------------
132
133/// The back-end implementing [`UniformSampler`] for [`Angle`].
134///
135/// Unless you are implementing [`UniformSampler`] for your own type, this type
136/// should not be used directly, use [`Uniform`][rand::distributions::uniform::Uniform] instead.
137#[derive(Clone, Copy, Debug, PartialEq)]
138pub struct UniformAngle<F>(UniformFloat<F>);
139
140impl<F: Float> SampleUniform for Angle<F>
141where
142    UniformFloat<F>: UniformSampler<X = F>,
143    F: SampleBorrow<F>,
144{
145    type Sampler = UniformAngle<F>;
146}
147
148impl<F: Float> UniformSampler for UniformAngle<F>
149where
150    UniformFloat<F>: UniformSampler<X = F>,
151    F: SampleBorrow<F>,
152{
153    type X = Angle<F>;
154
155    #[inline]
156    fn new<B1, B2>(low: B1, high: B2) -> Self
157    where
158        B1: SampleBorrow<Self::X> + Sized,
159        B2: SampleBorrow<Self::X> + Sized,
160    {
161        let low = low.borrow().to_radians();
162        let mut high = high.borrow().to_radians();
163        if low > high {
164            high += F::TAU;
165        }
166        Self(UniformFloat::new(low, high))
167    }
168
169    #[inline]
170    fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
171    where
172        B1: SampleBorrow<Self::X> + Sized,
173        B2: SampleBorrow<Self::X> + Sized,
174    {
175        let low = low.borrow().to_radians();
176        let mut high = high.borrow().to_radians();
177        if low > high {
178            high += F::TAU;
179        }
180        Self(UniformFloat::new_inclusive(low, high))
181    }
182
183    #[inline]
184    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
185        let x = self.0.sample(rng);
186        Angle::from_radians(x)
187    }
188}
189
190impl<F: Float> SampleRange<Angle<F>> for Range<Angle<F>>
191where
192    UniformFloat<F>: UniformSampler<X = F>,
193    F: SampleBorrow<F>,
194{
195    #[inline]
196    fn sample_single<R: rand::RngCore + ?Sized>(self, rng: &mut R) -> Angle<F> {
197        UniformAngle::sample_single(self.start, self.end, rng)
198    }
199
200    #[inline]
201    fn is_empty(&self) -> bool {
202        self.start == self.end
203    }
204}
205
206impl<F: Float> SampleRange<Angle<F>> for RangeInclusive<Angle<F>>
207where
208    UniformFloat<F>: UniformSampler<X = F>,
209    F: SampleBorrow<F>,
210{
211    #[inline]
212    fn sample_single<R: rand::RngCore + ?Sized>(self, rng: &mut R) -> Angle<F> {
213        UniformAngle::sample_single_inclusive(self.start(), self.end(), rng)
214    }
215
216    #[inline]
217    fn is_empty(&self) -> bool {
218        false
219    }
220}
221
222//-------------------------------------------------------------------
223
224/// The back-end implementing [`UniformSampler`] for [`AngleUnbounded`].
225///
226/// Unless you are implementing [`UniformSampler`] for your own type, this type
227/// should not be used directly, use [`Uniform`][rand::distributions::uniform::Uniform] instead.
228#[derive(Clone, Copy, Debug, PartialEq)]
229pub struct UniformAngleUnbounded<F>(UniformFloat<F>);
230
231impl<F: Copy> SampleUniform for AngleUnbounded<F>
232where
233    UniformFloat<F>: UniformSampler<X = F>,
234    F: SampleBorrow<F>,
235{
236    type Sampler = UniformAngleUnbounded<F>;
237}
238
239impl<F: Copy> UniformSampler for UniformAngleUnbounded<F>
240where
241    UniformFloat<F>: UniformSampler<X = F>,
242    F: SampleBorrow<F>,
243{
244    type X = AngleUnbounded<F>;
245
246    fn new<B1, B2>(low: B1, high: B2) -> Self
247    where
248        B1: SampleBorrow<Self::X> + Sized,
249        B2: SampleBorrow<Self::X> + Sized,
250    {
251        let low = low.borrow().to_radians();
252        let high = high.borrow().to_radians();
253        Self(UniformFloat::new(low, high))
254    }
255
256    fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
257    where
258        B1: SampleBorrow<Self::X> + Sized,
259        B2: SampleBorrow<Self::X> + Sized,
260    {
261        let low = low.borrow().to_radians();
262        let high = high.borrow().to_radians();
263        Self(UniformFloat::new_inclusive(low, high))
264    }
265
266    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
267        let x = self.0.sample(rng);
268        AngleUnbounded::from_radians(x)
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use rand::Rng;
275
276    use crate::{units::*, Angle32, Angle64, AngleUnbounded32, AngleUnbounded64};
277
278    #[test]
279    fn check_rand_impl() {
280        let mut rng = rand::thread_rng();
281
282        // Check if the code compile
283        macro_rules! check {
284            (
285                $($angle:ident),+
286            ) => {
287                $(
288                    let _: $angle = rand::random();
289                    let _: Radians<$angle> = rand::random();
290                    let _: Degrees<$angle> = rand::random();
291                    let _: Turns<$angle> = rand::random();
292                    let _: Gradians<$angle> = rand::random();
293
294                    let _: $angle = rng.gen_range($angle::ZERO..$angle::RAD_PI);
295                    let _: $angle = rng.gen_range($angle::ZERO..=$angle::RAD_PI);
296                )+
297            };
298        }
299
300        check!(Angle32, Angle64, AngleUnbounded32, AngleUnbounded64);
301    }
302}