bevy_math/sampling/mesh_sampling.rs
1//! Functionality related to random sampling from triangle meshes.
2
3use crate::{
4 primitives::{Measured2d, Triangle3d},
5 ShapeSample, Vec3,
6};
7use alloc::vec::Vec;
8use rand::Rng;
9use rand_distr::{
10 weighted::{Error as WeightedError, WeightedAliasIndex},
11 Distribution,
12};
13
14/// A [distribution] that caches data to allow fast sampling from a collection of triangles.
15/// Generally used through [`sample`] or [`sample_iter`].
16///
17/// [distribution]: Distribution
18/// [`sample`]: Distribution::sample
19/// [`sample_iter`]: Distribution::sample_iter
20///
21/// Example
22/// ```
23/// # use bevy_math::{Vec3, primitives::*};
24/// # use bevy_math::sampling::mesh_sampling::UniformMeshSampler;
25/// # use rand::{SeedableRng, rngs::StdRng, distr::Distribution};
26/// let faces = Tetrahedron::default().faces();
27/// let sampler = UniformMeshSampler::try_new(faces).unwrap();
28/// let rng = StdRng::seed_from_u64(8765309);
29/// // 50 random points on the tetrahedron:
30/// let samples: Vec<Vec3> = sampler.sample_iter(rng).take(50).collect();
31/// ```
32pub struct UniformMeshSampler {
33 triangles: Vec<Triangle3d>,
34 face_distribution: WeightedAliasIndex<f32>,
35}
36
37impl Distribution<Vec3> for UniformMeshSampler {
38 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec3 {
39 let face_index = self.face_distribution.sample(rng);
40 self.triangles[face_index].sample_interior(rng)
41 }
42}
43
44impl UniformMeshSampler {
45 /// Construct a new [`UniformMeshSampler`] from a list of [triangles].
46 ///
47 /// Returns an error if the distribution of areas for the collection of triangles could not be formed
48 /// (most notably if the collection has zero surface area).
49 ///
50 /// [triangles]: Triangle3d
51 pub fn try_new<T: IntoIterator<Item = Triangle3d>>(
52 triangles: T,
53 ) -> Result<Self, WeightedError> {
54 let triangles: Vec<Triangle3d> = triangles.into_iter().collect();
55 let areas = triangles.iter().map(Measured2d::area).collect();
56
57 WeightedAliasIndex::new(areas).map(|face_distribution| Self {
58 triangles,
59 face_distribution,
60 })
61 }
62}