7ed1f6a9b6
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d2ef88f5e8
|
Add Distribution access methods for ShapeSample trait (#13315)
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`](https://docs.rs/rand/latest/rand/distributions/trait.Distribution.html) 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`): ```rust 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: ```rust // 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: ```rust let boundary_pts: Vec<Vec2> = BoundaryOf(rectangle).sample_iter(rng).take(1000).collect(); ``` --- ## Changelog - Added `InteriorOf` and `BoundaryOf` distribution wrapper structs in `bevy_math::sampling::shape_sampling`. - Added `interior_dist` and `boundary_dist` automatic trait methods to `ShapeSample`. - 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. |
||
|
|
3a7923ea92
|
Random sampling of directions and quaternions (#12857)
# Objective Augment Bevy's random sampling capabilities by providing good tools for producing random directions and rotations. ## Solution The `rand` crate has a natural tool for providing `Distribution`s whose output is a type that doesn't require any additional data to sample values — namely, [`Standard`](https://docs.rs/rand/latest/rand/distributions/struct.Standard.html). Here, our existing `ShapeSample` implementations have been put to good use in providing these, resulting in patterns like the following: ```rust // Using thread-local rng let random_direction1: Dir3 = random(); // Using an explicit rng let random_direction2: Dir3 = rng.gen(); // Using an explicit rng coupled explicitly with Standard let random_directions: Vec<Dir3> = rng.sample_iter(Standard).take(5).collect(); ``` Furthermore, we have introduced a trait `FromRng` which provides sugar for `rng.gen()` that is more namespace-friendly (in this author's opinion): ```rust let random_direction = Dir3::from_rng(rng); ``` The types this has been implemented for are `Dir2`, `Dir3`, `Dir3A`, and `Quat`. Notably, `Quat` uses `glam`'s implementation rather than an in-house one, and as a result, `bevy_math`'s "rand" feature now enables that of `glam`. --- ## Changelog - Created `standard` submodule in `sampling` to hold implementations and other items related to the `Standard` distribution. - "rand" feature of `bevy_math` now enables that of `glam`. --- ## Discussion From a quick glance at `Quat`'s distribution implementation in `glam`, I am a bit suspicious, since it is simple and doesn't match any algorithm that I came across in my research. I will do a little more digging as a follow-up to this and see if it's actually uniform (maybe even using those tools I wrote — what a thrill). As an aside, I'd also like to say that I think [`Distribution`](https://docs.rs/rand/latest/rand/distributions/trait.Distribution.html) is really, really good. It integrates with distributions provided externally (e.g. in `rand` itself and its extensions) along with doing a good job of isolating the source of randomness, so that output can be reliably reproduced if need be. Finally, `Distribution::sample_iter` is quite good for ergonomically acquiring lots of random values. At one point I found myself writing traits to describe random sampling and essentially reinvented this one. I just think it's good, and I think it's worth centralizing around to a significant extent. |