Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benches/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ bevy_platform = { path = "../crates/bevy_platform", default-features = false, fe
] }

# Other crates
glam = "0.30.1"
glam = { version = "0.30.1", features = ["rand"] }
rand = "0.9"
rand_chacha = "0.9"
nonmax = { version = "0.5", default-features = false }
Expand Down
73 changes: 73 additions & 0 deletions benches/benches/bevy_math/bounding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use benches::bench;
use bevy_math::{
bounding::{Aabb3d, BoundingSphere, BoundingVolume},
prelude::*,
};
use core::hint::black_box;
use criterion::{criterion_group, Criterion};
use rand::{
distr::{Distribution, StandardUniform, Uniform},
rngs::StdRng,
Rng, SeedableRng,
};

criterion_group!(benches, bounding);

struct PointCloud {
points: Vec<Vec3A>,
isometry: Isometry3d,
}

impl PointCloud {
fn aabb(&self) -> Aabb3d {
Aabb3d::from_point_cloud(self.isometry, self.points.iter().copied())
}

fn sphere(&self) -> BoundingSphere {
BoundingSphere::from_point_cloud(self.isometry, &self.points)
}
}

#[inline(never)]
fn bounding_function(point_clouds: &[PointCloud]) {
// For various types of bounds, calculate the bounds of each point cloud
// then merge them together.

let aabb = point_clouds
.iter()
.map(PointCloud::aabb)
.reduce(|l, r| l.merge(&r));

let sphere = point_clouds
.iter()
.map(PointCloud::sphere)
.reduce(|l, r| l.merge(&r));

black_box(aabb);
black_box(sphere);
}

fn bounding(c: &mut Criterion) {
let mut rng1 = StdRng::seed_from_u64(123);
let mut rng2 = StdRng::seed_from_u64(456);

// Create an array of point clouds of various sizes.
let point_clouds = Uniform::<usize>::new(3, 30)
.unwrap()
.sample_iter(&mut rng1)
.take(1000)
.map(|num_points| PointCloud {
points: StandardUniform
.sample_iter(&mut rng2)
.take(num_points)
.collect::<Vec<Vec3A>>(),
isometry: Isometry3d::new(rng2.random::<Vec3>(), rng2.random::<Quat>()),
})
.collect::<Vec<_>>();

c.bench_function(bench!("bounding"), |b| {
b.iter(|| {
bounding_function(&point_clouds);
});
});
}
3 changes: 2 additions & 1 deletion benches/benches/bevy_math/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use criterion::criterion_main;

mod bezier;
mod bounding;

criterion_main!(bezier::benches);
criterion_main!(bezier::benches, bounding::benches);
74 changes: 37 additions & 37 deletions crates/bevy_math/src/bounding/bounded2d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
use serde::{Deserialize, Serialize};

/// Computes the geometric center of the given set of points.
#[inline(always)]
#[inline]
fn point_cloud_2d_center(points: &[Vec2]) -> Vec2 {
assert!(
!points.is_empty(),
Expand Down Expand Up @@ -56,7 +56,7 @@ pub struct Aabb2d {

impl Aabb2d {
/// Constructs an AABB from its center and half-size.
#[inline(always)]
#[inline]
pub fn new(center: Vec2, half_size: Vec2) -> Self {
debug_assert!(half_size.x >= 0.0 && half_size.y >= 0.0);
Self {
Expand All @@ -71,7 +71,7 @@ impl Aabb2d {
/// # Panics
///
/// Panics if the given set of points is empty.
#[inline(always)]
#[inline]
pub fn from_point_cloud(isometry: impl Into<Isometry2d>, points: &[Vec2]) -> Aabb2d {
let isometry = isometry.into();

Expand All @@ -93,7 +93,7 @@ impl Aabb2d {
}

/// Computes the smallest [`BoundingCircle`] containing this [`Aabb2d`].
#[inline(always)]
#[inline]
pub fn bounding_circle(&self) -> BoundingCircle {
let radius = self.min.distance(self.max) / 2.0;
BoundingCircle::new(self.center(), radius)
Expand All @@ -103,7 +103,7 @@ impl Aabb2d {
///
/// If the point is outside the AABB, the returned point will be on the perimeter of the AABB.
/// Otherwise, it will be inside the AABB and returned as is.
#[inline(always)]
#[inline]
pub fn closest_point(&self, point: Vec2) -> Vec2 {
// Clamp point coordinates to the AABB
point.clamp(self.min, self.max)
Expand All @@ -115,39 +115,39 @@ impl BoundingVolume for Aabb2d {
type Rotation = Rot2;
type HalfSize = Vec2;

#[inline(always)]
#[inline]
fn center(&self) -> Self::Translation {
(self.min + self.max) / 2.
}

#[inline(always)]
#[inline]
fn half_size(&self) -> Self::HalfSize {
(self.max - self.min) / 2.
}

#[inline(always)]
#[inline]
fn visible_area(&self) -> f32 {
let b = (self.max - self.min).max(Vec2::ZERO);
b.x * b.y
}

#[inline(always)]
#[inline]
fn contains(&self, other: &Self) -> bool {
other.min.x >= self.min.x
&& other.min.y >= self.min.y
&& other.max.x <= self.max.x
&& other.max.y <= self.max.y
}

#[inline(always)]
#[inline]
fn merge(&self, other: &Self) -> Self {
Self {
min: self.min.min(other.min),
max: self.max.max(other.max),
}
}

#[inline(always)]
#[inline]
fn grow(&self, amount: impl Into<Self::HalfSize>) -> Self {
let amount = amount.into();
let b = Self {
Expand All @@ -158,7 +158,7 @@ impl BoundingVolume for Aabb2d {
b
}

#[inline(always)]
#[inline]
fn shrink(&self, amount: impl Into<Self::HalfSize>) -> Self {
let amount = amount.into();
let b = Self {
Expand All @@ -169,7 +169,7 @@ impl BoundingVolume for Aabb2d {
b
}

#[inline(always)]
#[inline]
fn scale_around_center(&self, scale: impl Into<Self::HalfSize>) -> Self {
let scale = scale.into();
let b = Self {
Expand All @@ -187,7 +187,7 @@ impl BoundingVolume for Aabb2d {
/// Note that the result may not be as tightly fitting as the original, and repeated rotations
/// can cause the AABB to grow indefinitely. Avoid applying multiple rotations to the same AABB,
/// and consider storing the original AABB and rotating that every time instead.
#[inline(always)]
#[inline]
fn transformed_by(
mut self,
translation: impl Into<Self::Translation>,
Expand All @@ -204,7 +204,7 @@ impl BoundingVolume for Aabb2d {
/// Note that the result may not be as tightly fitting as the original, and repeated rotations
/// can cause the AABB to grow indefinitely. Avoid applying multiple rotations to the same AABB,
/// and consider storing the original AABB and rotating that every time instead.
#[inline(always)]
#[inline]
fn transform_by(
&mut self,
translation: impl Into<Self::Translation>,
Expand All @@ -214,7 +214,7 @@ impl BoundingVolume for Aabb2d {
self.translate_by(translation);
}

#[inline(always)]
#[inline]
fn translate_by(&mut self, translation: impl Into<Self::Translation>) {
let translation = translation.into();
self.min += translation;
Expand All @@ -228,7 +228,7 @@ impl BoundingVolume for Aabb2d {
/// Note that the result may not be as tightly fitting as the original, and repeated rotations
/// can cause the AABB to grow indefinitely. Avoid applying multiple rotations to the same AABB,
/// and consider storing the original AABB and rotating that every time instead.
#[inline(always)]
#[inline]
fn rotated_by(mut self, rotation: impl Into<Self::Rotation>) -> Self {
self.rotate_by(rotation);
self
Expand All @@ -241,7 +241,7 @@ impl BoundingVolume for Aabb2d {
/// Note that the result may not be as tightly fitting as the original, and repeated rotations
/// can cause the AABB to grow indefinitely. Avoid applying multiple rotations to the same AABB,
/// and consider storing the original AABB and rotating that every time instead.
#[inline(always)]
#[inline]
fn rotate_by(&mut self, rotation: impl Into<Self::Rotation>) {
let rot_mat = Mat2::from(rotation.into());
let half_size = rot_mat.abs() * self.half_size();
Expand All @@ -250,7 +250,7 @@ impl BoundingVolume for Aabb2d {
}

impl IntersectsVolume<Self> for Aabb2d {
#[inline(always)]
#[inline]
fn intersects(&self, other: &Self) -> bool {
let x_overlaps = self.min.x <= other.max.x && self.max.x >= other.min.x;
let y_overlaps = self.min.y <= other.max.y && self.max.y >= other.min.y;
Expand All @@ -259,7 +259,7 @@ impl IntersectsVolume<Self> for Aabb2d {
}

impl IntersectsVolume<BoundingCircle> for Aabb2d {
#[inline(always)]
#[inline]
fn intersects(&self, circle: &BoundingCircle) -> bool {
let closest_point = self.closest_point(circle.center);
let distance_squared = circle.center.distance_squared(closest_point);
Expand Down Expand Up @@ -492,7 +492,7 @@ pub struct BoundingCircle {

impl BoundingCircle {
/// Constructs a bounding circle from its center and radius.
#[inline(always)]
#[inline]
pub fn new(center: Vec2, radius: f32) -> Self {
debug_assert!(radius >= 0.);
Self {
Expand All @@ -505,7 +505,7 @@ impl BoundingCircle {
/// transformed by the rotation and translation of the given isometry.
///
/// The bounding circle is not guaranteed to be the smallest possible.
#[inline(always)]
#[inline]
pub fn from_point_cloud(isometry: impl Into<Isometry2d>, points: &[Vec2]) -> BoundingCircle {
let isometry = isometry.into();

Expand All @@ -524,13 +524,13 @@ impl BoundingCircle {
}

/// Get the radius of the bounding circle
#[inline(always)]
#[inline]
pub fn radius(&self) -> f32 {
self.circle.radius
}

/// Computes the smallest [`Aabb2d`] containing this [`BoundingCircle`].
#[inline(always)]
#[inline]
pub fn aabb_2d(&self) -> Aabb2d {
Aabb2d {
min: self.center - Vec2::splat(self.radius()),
Expand All @@ -542,7 +542,7 @@ impl BoundingCircle {
///
/// If the point is outside the circle, the returned point will be on the perimeter of the circle.
/// Otherwise, it will be inside the circle and returned as is.
#[inline(always)]
#[inline]
pub fn closest_point(&self, point: Vec2) -> Vec2 {
self.circle.closest_point(point - self.center) + self.center
}
Expand All @@ -553,28 +553,28 @@ impl BoundingVolume for BoundingCircle {
type Rotation = Rot2;
type HalfSize = f32;

#[inline(always)]
#[inline]
fn center(&self) -> Self::Translation {
self.center
}

#[inline(always)]
#[inline]
fn half_size(&self) -> Self::HalfSize {
self.radius()
}

#[inline(always)]
#[inline]
fn visible_area(&self) -> f32 {
core::f32::consts::PI * self.radius() * self.radius()
}

#[inline(always)]
#[inline]
fn contains(&self, other: &Self) -> bool {
let diff = self.radius() - other.radius();
self.center.distance_squared(other.center) <= ops::copysign(diff.squared(), diff)
}

#[inline(always)]
#[inline]
fn merge(&self, other: &Self) -> Self {
let diff = other.center - self.center;
let length = diff.length();
Expand All @@ -591,42 +591,42 @@ impl BoundingVolume for BoundingCircle {
)
}

#[inline(always)]
#[inline]
fn grow(&self, amount: impl Into<Self::HalfSize>) -> Self {
let amount = amount.into();
debug_assert!(amount >= 0.);
Self::new(self.center, self.radius() + amount)
}

#[inline(always)]
#[inline]
fn shrink(&self, amount: impl Into<Self::HalfSize>) -> Self {
let amount = amount.into();
debug_assert!(amount >= 0.);
debug_assert!(self.radius() >= amount);
Self::new(self.center, self.radius() - amount)
}

#[inline(always)]
#[inline]
fn scale_around_center(&self, scale: impl Into<Self::HalfSize>) -> Self {
let scale = scale.into();
debug_assert!(scale >= 0.);
Self::new(self.center, self.radius() * scale)
}

#[inline(always)]
#[inline]
fn translate_by(&mut self, translation: impl Into<Self::Translation>) {
self.center += translation.into();
}

#[inline(always)]
#[inline]
fn rotate_by(&mut self, rotation: impl Into<Self::Rotation>) {
let rotation: Rot2 = rotation.into();
self.center = rotation * self.center;
}
}

impl IntersectsVolume<Self> for BoundingCircle {
#[inline(always)]
#[inline]
fn intersects(&self, other: &Self) -> bool {
let center_distance_squared = self.center.distance_squared(other.center);
let radius_sum_squared = (self.radius() + other.radius()).squared();
Expand All @@ -635,7 +635,7 @@ impl IntersectsVolume<Self> for BoundingCircle {
}

impl IntersectsVolume<Aabb2d> for BoundingCircle {
#[inline(always)]
#[inline]
fn intersects(&self, aabb: &Aabb2d) -> bool {
aabb.intersects(self)
}
Expand Down
Loading
Loading