Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 4 additions & 1 deletion benches/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ bevy_platform = { path = "../crates/bevy_platform", default-features = false, fe
] }

# Other crates
glam = { version = "0.30.7", default-features = false, features = ["std"] }
glam = { version = "0.30.7", default-features = false, features = [
"std",
"rand",
] }
rand = "0.9"
rand_chacha = "0.9"
nonmax = { version = "0.5", default-features = false }
Expand Down
67 changes: 67 additions & 0 deletions benches/benches/bevy_math/bounding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
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)
}
}

fn bounding(c: &mut Criterion) {
// Create point clouds of various sizes, then benchmark two different ways
// of finding the bounds of each cloud and merging them together.

let mut rng1 = StdRng::seed_from_u64(123);
let mut rng2 = StdRng::seed_from_u64(456);

let point_clouds = Uniform::<usize>::new(black_box(3), black_box(30))
.unwrap()
.sample_iter(&mut rng1)
.take(black_box(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(|| {
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);
});
});
}
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