Skip to main content

bevy_math/sampling/
standard.rs

1//! This module holds local implementations of the [`Distribution`] trait for [`StandardUniform`], which
2//! allow certain Bevy math types (those whose values can be randomly generated without additional
3//! input other than an [`RngExt`]) to be produced using [`rand`]'s APIs. It also holds [`FromRng`],
4//! an ergonomic extension to that functionality which permits the omission of type annotations.
5//!
6//! For instance:
7//! ```
8//! # use rand::{random,  RngExt, SeedableRng, rngs::StdRng, distr::StandardUniform};
9//! # use bevy_math::{Dir3, sampling::FromRng};
10//! let mut rng = StdRng::seed_from_u64(7313429298);
11//! // Random direction using thread-local rng
12//! let random_direction1: Dir3 = random();
13//!
14//! // Random direction using the rng constructed above
15//! let random_direction2: Dir3 = rng.random();
16//!
17//! // The same as the previous but with different syntax
18//! let random_direction3 = Dir3::from_rng(&mut rng);
19//!
20//! // Five random directions, using StandardUniform explicitly
21//! let many_random_directions: Vec<Dir3> = rng.sample_iter(StandardUniform).take(5).collect();
22//! ```
23
24use core::f32::consts::{PI, TAU};
25
26use crate::{ops, Dir2, Dir3, Dir3A, Quat, Rot2, Vec2, Vec3};
27use rand::{
28    distr::{Distribution, StandardUniform},
29    RngExt,
30};
31
32/// Ergonomics trait for a type with a [`StandardUniform`] distribution, allowing values to be generated
33/// uniformly from an [`RngExt`] by a method in its own namespace.
34///
35/// Example
36/// ```
37/// # use rand::{RngExt, SeedableRng, rngs::StdRng};
38/// # use bevy_math::{Dir3, sampling::FromRng};
39/// let mut rng = StdRng::seed_from_u64(451);
40/// let random_dir = Dir3::from_rng(&mut rng);
41/// ```
42pub trait FromRng
43where
44    Self: Sized,
45    StandardUniform: Distribution<Self>,
46{
47    /// Construct a value of this type uniformly at random using `rng` as the source of randomness.
48    fn from_rng<R: RngExt + ?Sized>(rng: &mut R) -> Self {
49        rng.random()
50    }
51}
52
53impl Distribution<Dir2> for StandardUniform {
54    #[inline]
55    fn sample<R: RngExt + ?Sized>(&self, rng: &mut R) -> Dir2 {
56        let theta = rng.random_range(0.0..TAU);
57        let (sin, cos) = ops::sin_cos(theta);
58        let vector = Vec2::new(cos, sin);
59        Dir2::new_unchecked(vector)
60    }
61}
62
63/// Boundary sampling for unit-spheres
64#[inline]
65fn sample_unit_sphere_boundary<R: RngExt + ?Sized>(rng: &mut R) -> Vec3 {
66    let z = rng.random_range(-1f32..=1f32);
67    let (a_sin, a_cos) = ops::sin_cos(rng.random_range(-PI..=PI));
68    let c = ops::sqrt(1f32 - z * z);
69    let x = a_sin * c;
70    let y = a_cos * c;
71
72    Vec3::new(x, y, z)
73}
74
75impl FromRng for Dir2 {}
76
77impl Distribution<Dir3> for StandardUniform {
78    #[inline]
79    fn sample<R: RngExt + ?Sized>(&self, rng: &mut R) -> Dir3 {
80        let vector = sample_unit_sphere_boundary(rng);
81        Dir3::new_unchecked(vector)
82    }
83}
84
85impl FromRng for Dir3 {}
86
87impl Distribution<Dir3A> for StandardUniform {
88    #[inline]
89    fn sample<R: RngExt + ?Sized>(&self, rng: &mut R) -> Dir3A {
90        let vector = sample_unit_sphere_boundary(rng);
91        Dir3A::new_unchecked(vector.to_vec3a())
92    }
93}
94
95impl FromRng for Dir3A {}
96
97impl Distribution<Rot2> for StandardUniform {
98    #[inline]
99    fn sample<R: RngExt + ?Sized>(&self, rng: &mut R) -> Rot2 {
100        let angle = rng.random_range(0.0..TAU);
101        Rot2::radians(angle)
102    }
103}
104
105impl FromRng for Rot2 {}
106
107impl FromRng for Quat {}