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