Skip to main content

bevy_shape/sampling/
mesh_sampling.rs

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