Skip to content

Commit 5dbd827

Browse files
authored
Annulus sampling (#13471)
# Objective Add random sampling for the `Annulus` primitive. This is part of ongoing work to bring the various `bevy_math` primitives to feature parity. ## Solution `Annulus` implements `ShapeSample`. Boundary sampling is implemented in the obvious way, and interior sampling works exactly as in the implementation for `Circle`, using the fact that the square of the radius should be taken uniformly from between r^2 and R^2, where r and R are the inner and outer radii respectively. ## Testing I generated a bunch of random points and rendered them. Here's 1000 points on the interior of the default annulus: <img width="1440" alt="Screenshot 2024-05-22 at 8 01 34 AM" src="https://github.com/bevyengine/bevy/assets/2975848/19c31bb0-edba-477f-b247-2b12d854afae"> This looks kind of weird around the edges, but I verified that they're all actually inside the annulus, so I assume it has to do with the fact that the rendered circles have some radius.
1 parent d2ef88f commit 5dbd827

File tree

1 file changed

+29
-0
lines changed

1 file changed

+29
-0
lines changed

crates/bevy_math/src/sampling/shape_sampling.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,35 @@ impl ShapeSample for Sphere {
191191
}
192192
}
193193

194+
impl ShapeSample for Annulus {
195+
type Output = Vec2;
196+
197+
fn sample_interior<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::Output {
198+
let inner_radius = self.inner_circle.radius;
199+
let outer_radius = self.outer_circle.radius;
200+
201+
// Like random sampling for a circle, radius is weighted by the square.
202+
let r_squared = rng.gen_range((inner_radius * inner_radius)..(outer_radius * outer_radius));
203+
let r = r_squared.sqrt();
204+
let theta = rng.gen_range(0.0..TAU);
205+
206+
Vec2::new(r * theta.cos(), r * theta.sin())
207+
}
208+
209+
fn sample_boundary<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::Output {
210+
let total_perimeter = self.inner_circle.perimeter() + self.outer_circle.perimeter();
211+
let inner_prob = (self.inner_circle.perimeter() / total_perimeter) as f64;
212+
213+
// Sample from boundary circles, choosing which one by weighting by perimeter:
214+
let inner = rng.gen_bool(inner_prob);
215+
if inner {
216+
self.inner_circle.sample_boundary(rng)
217+
} else {
218+
self.outer_circle.sample_boundary(rng)
219+
}
220+
}
221+
}
222+
194223
impl ShapeSample for Rectangle {
195224
type Output = Vec2;
196225

0 commit comments

Comments
 (0)