bevy
bevy copied to clipboard
Add `Distribution` access methods for `ShapeSample` trait
Stolen from #12835.
Objective
Sometimes you want to sample a whole bunch of points from a shape instead of just one. You can write your own loop to do this, but it's really more idiomatic to use a rand
Distribution
with the sample_iter
method. Distributions also support other useful things like mapping, and they are suitable as generic items for consumption by other APIs.
Solution
ShapeSample
has been given two new automatic trait methods, interior_dist
and boundary_dist
. They both have similar signatures (recall that Output
is the output type for ShapeSample
):
fn interior_dist(self) -> impl Distribution<Self::Output>
where Self: Sized { //... }
These have default implementations which are powered by wrapper structs InteriorOf
and BoundaryOf
that actually implement Distribution
— the implementations effectively just call ShapeSample::sample_interior
and ShapeSample::sample_boundary
on the contained type.
The upshot is that this allows iteration as follows:
// Get an iterator over boundary points of a rectangle:
let rectangle = Rectangle::new(1.0, 2.0);
let boundary_iter = rectangle.boundary_dist().sample_iter(rng);
// Collect a bunch of boundary points at once:
let boundary_pts: Vec<Vec2> = boundary_iter.take(1000).collect();
Alternatively, you can use InteriorOf
/BoundaryOf
explicitly to similar effect:
let boundary_pts: Vec<Vec2> = BoundaryOf(rectangle).sample_iter(rng).take(1000).collect();
Changelog
- Added
InteriorOf
andBoundaryOf
distribution wrapper structs inbevy_math::sampling::shape_sampling
. - Added
interior_dist
andboundary_dist
automatic trait methods toShapeSample
. - Made
shape_sampling
module public with explanatory documentation.
Discussion
Design choices
The main point of interest here is just the choice of impl Distribution
instead of explicitly using InteriorOf
/BoundaryOf
return types for interior_dist
and boundary_dist
. The reason for this choice is that it allows future optimizations for repeated sampling — for example, instead of just wrapping the base type, interior_dist
/boundary_dist
could construct auxiliary data that is held over between sampling operations.