Skip to content

Commit cda48f6

Browse files
committed
- fix doc mistakes
- fix bug about shapes `ID`
1 parent 21dbdbe commit cda48f6

File tree

10 files changed

+171
-40
lines changed

10 files changed

+171
-40
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com
99
-->
1010

1111
## [Unreleased]
12+
## [0.15.1-alpha9] - 2025-01-22
13+
14+
- fix doc mistakes
15+
- fix bug about shapes `ID`
16+
1217
## [0.15.1-alpha8] - 2025-01-22
1318

1419
- refator `QuadTree` (replace recursion with loop), a huge performance improvement in theory

bevy_quadtree/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ cmds.spawn((
147147
4. Query the quadtree like bevy's `Or, Not`:
148148

149149
```rust ignore
150-
type MyQuadTree = QuadTree<40, 100, 100, 20>;
150+
type MyQuadTree = QuadTree<8, 40, 100, 100, 20>;
151151

152152
fn pick(
153153
mut gizmos: Gizmos,

bevy_quadtree/src/collision.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,33 @@ impl<T> DynCollision for T where
4444
{
4545
}
4646

47+
/// Erase the `ID` of the shape and store it as a trait object in the `QuadTree`.
48+
#[allow(missing_docs)]
49+
pub trait AsDynCollision {
50+
fn as_dyn_collision(&self) -> Box<dyn DynCollision>;
51+
}
52+
53+
impl<const D: usize> AsDynCollision for CollisionRect<D> {
54+
fn as_dyn_collision(&self) -> Box<dyn DynCollision> {
55+
let this = CollisionRect::from(self);
56+
Box::new(this)
57+
}
58+
}
59+
60+
impl<const D: usize> AsDynCollision for CollisionRotatedRect<D> {
61+
fn as_dyn_collision(&self) -> Box<dyn DynCollision> {
62+
let this = CollisionRotatedRect::from(self);
63+
Box::new(this)
64+
}
65+
}
66+
67+
impl<const D: usize> AsDynCollision for CollisionCircle<D> {
68+
fn as_dyn_collision(&self) -> Box<dyn DynCollision> {
69+
let this = CollisionCircle::from(self);
70+
Box::new(this)
71+
}
72+
}
73+
4774
/// Update the attributes of the shape during PreUpdate stage and before Collision Detection.
4875
pub trait UpdateCollision<C>
4976
where

bevy_quadtree/src/plugin.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! QuadTree Plugin
22
3-
use crate::collision::DynCollision;
3+
use crate::collision::AsDynCollision;
44
use crate::system::{update_collision, update_quadtree};
55
use crate::tree::QuadTree;
66
use crate::UpdateCollision;
@@ -15,14 +15,15 @@ use bevy_transform::components::GlobalTransform;
1515
/// # Type Parameters
1616
/// `P`: `(S, C)` pair (or tuple of `(S, C)`s) where `S` (collision shape) can updated by `C` (component).
1717
///
18-
/// `S: Component + DynCollision + UpdateCollision<C> + Clone`,
18+
/// `S: Component + AsDynCollision + UpdateCollision<C> + Clone`,
1919
/// such as [`CollisionCircle, CollisionRect, CollisionRotatedRect`](crate::shape).
2020
/// Being used to perform Collision Detection,
2121
/// storing the shape and position info, also serving as a marker component in ECS queries.
2222
/// (Do not need to include those only used in the [`QuadTree::query`](crate::QuadTree::query),
23-
/// only those need to be updated.)
23+
/// please include those need to be updated.)
2424
///
25-
/// `C: Component`, only `GlobalTransform`, `Sprite`(need feature `sprite`) or tuples of them for now.
25+
/// `C: Component`, the components the shapes will be updated according to.
26+
/// Only `GlobalTransform`, `Sprite`(need feature `sprite`) or tuples of them for now.
2627
///
2728
/// `N`: The max number of objects each node.
2829
/// `D`: The max depth of the tree.
@@ -43,7 +44,7 @@ use bevy_transform::components::GlobalTransform;
4344
///
4445
/// 2. invalid const parameters.
4546
///
46-
/// N, W, H should > 0. K should >= 10.
47+
/// N, D, W, H should > 0. K should >= 10.
4748
///
4849
/// # Example
4950
/// ```no_run
@@ -118,9 +119,7 @@ where
118119
.add_systems(PreUpdate, P::update_collision())
119120
.add_systems(Update, P::update_quadtree::<N, D, W, H, K, ID>());
120121
#[cfg(feature = "gizmos")]
121-
{
122-
app.add_systems(PostUpdate, P::show_boundary::<N, D, W, H, K, ID>());
123-
}
122+
app.add_systems(PostUpdate, P::show_boundary::<N, D, W, H, K, ID>());
124123
}
125124
}
126125

@@ -157,7 +156,7 @@ macro_rules! impl_tracking_pair {
157156
($c: ty) => {
158157
impl<S> TrackingPair for (S, $c)
159158
where
160-
S: Component + DynCollision + UpdateCollision<$c> + Clone,
159+
S: Component + AsDynCollision + UpdateCollision<$c> + Clone,
161160
{
162161
fn update_collision() -> SystemConfigs {
163162
update_collision::<S, $c>.ambiguous_with_all()
@@ -200,7 +199,7 @@ macro_rules! impl_tracking_pair_tuple {
200199
($($c: ty),+) => {
201200
impl<S> TrackingPair for (S, ($($c),+,))
202201
where
203-
S: Component + DynCollision + $(UpdateCollision<$c>+)+ Clone,
202+
S: Component + AsDynCollision + $(UpdateCollision<$c>+)+ Clone,
204203
$($c: Component),+,
205204
$((S, $c): TrackingPair),+,
206205
{
@@ -250,7 +249,7 @@ macro_rules! impl_tracking_pairs {
250249
let mut set = std::collections::HashMap::new();
251250
$(
252251
if let Some(dup) = set.insert([<P $i>]::shape_id(), std::any::type_name::<[<P $i>]>()) {
253-
panic!("Duplicate quadtree updating system added:\n<{}>\n<{}>\nThey have the same collision shape, merge them into one or use `ID` type parameter of shape.", std::any::type_name::<[<P $i>]>(), dup);
252+
panic!("Duplicate quadtree updating system added:\n<{}>\n<{}>\nThey have the same collision shape, merge them into one or use `ID` type parameter of shape.", dup, std::any::type_name::<[<P $i>]>());
254253
}
255254
);+
256255
}
@@ -264,7 +263,7 @@ macro_rules! impl_tracking_pairs {
264263
let mut set = std::collections::HashMap::new();
265264
$(
266265
if let Some(dup) = set.insert([<P $i>]::shape_id(), std::any::type_name::<[<P $i>]>()) {
267-
panic!("Duplicate gizmos box updating system added:\n<{}>\n<{}>\nThey have the same collision shape, merge them into one or use `ID` type parameter of shape.", std::any::type_name::<[<P $i>]>(), dup);
266+
panic!("Duplicate gizmos box updating system added:\n<{}>\n<{}>\nThey have the same collision shape, merge them into one or use `ID` type parameter of shape.", dup, std::any::type_name::<[<P $i>]>());
268267
}
269268
);+
270269
}
@@ -360,4 +359,19 @@ mod tests {
360359
20,
361360
>::default());
362361
}
362+
363+
#[test]
364+
fn plugin_test_with_id() {
365+
App::new().add_plugins(QuadTreePlugin::<
366+
(
367+
(CollisionCircle<0>, GlobalTransform),
368+
(CollisionCircle<1>, GlobalTransform),
369+
),
370+
40,
371+
4,
372+
100,
373+
100,
374+
20,
375+
>::default());
376+
}
363377
}

bevy_quadtree/src/shape/circle.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use core::fmt;
2-
31
use bevy_ecs::prelude::*;
42
use bevy_math::prelude::*;
53
use bevy_transform::components::GlobalTransform;
4+
use core::fmt;
5+
use std::any::type_name;
66

77
use crate::{
88
collision::{DynCollision, Relation},
@@ -24,11 +24,12 @@ pub struct CollisionCircle<const ID: usize = 0> {
2424
init_radius: f32,
2525
}
2626

27-
impl fmt::Debug for CollisionCircle {
27+
impl<const ID: usize> fmt::Debug for CollisionCircle<ID> {
2828
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2929
write!(
3030
f,
31-
"CollisionCircle: center = ({}, {}); r = {} x {} = {}",
31+
"{}: center = ({}, {}); r = {} x {} = {}",
32+
type_name::<Self>(),
3233
self.center.x,
3334
self.center.y,
3435
self.init_radius,
@@ -38,7 +39,21 @@ impl fmt::Debug for CollisionCircle {
3839
}
3940
}
4041

42+
impl<const ID: usize> From<&CollisionCircle<ID>> for CollisionCircle<0> {
43+
/// Convert the shape with `ID` to the shape with `ID = 0`.
44+
/// Used to eliminate the `ID` in the collision detection.
45+
fn from(value: &CollisionCircle<ID>) -> Self {
46+
Self {
47+
center: value.center,
48+
scale: value.scale,
49+
init_radius: value.init_radius,
50+
}
51+
}
52+
}
53+
4154
impl CollisionCircle {
55+
/// Create a new circle with `ID = 0`. See [`Self::new_id`] for the version with `ID`.
56+
///
4257
/// The initial radius is used to compute the size with the GlobalTransform's scale.
4358
///
4459
/// The initial center is covered by the GlobalTransform's translation during the update.
@@ -49,6 +64,17 @@ impl CollisionCircle {
4964
init_radius: radius,
5065
}
5166
}
67+
}
68+
69+
impl<const ID: usize> CollisionCircle<ID> {
70+
/// Create a new circle with the given `ID`.
71+
pub fn new_id(center: Vec2, radius: f32) -> Self {
72+
Self {
73+
center,
74+
scale: 1.,
75+
init_radius: radius,
76+
}
77+
}
5278

5379
fn radius(&self) -> f32 {
5480
self.init_radius * self.scale
@@ -130,7 +156,7 @@ impl Collision<CollisionCircle> for CollisionCircle {
130156
}
131157
}
132158

133-
impl UpdateCollision<GlobalTransform> for CollisionCircle {
159+
impl<const ID: usize> UpdateCollision<GlobalTransform> for CollisionCircle<ID> {
134160
fn update() -> impl FnOnce(Mut<Self>, &GlobalTransform) {
135161
|mut circle, global_transform| {
136162
circle.center = global_transform.translation().truncate();

bevy_quadtree/src/shape/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
//! Shapes able to be used in Collision Detection
1+
//! Shapes able to be used in Collision Detection.
2+
//!
3+
//! The `ID` of shapes is used to distinguish the same shape following different transforms, having nothing to do with the `ID` of the [`QuadTree`](crate::QuadTree).
4+
//!
5+
//! The `ID` of the `QuadTree` is used to distinguish different `QuadTree`s in the `World`, which is determined by the `ID` of [`QuadTreePlugin`](crate::QuadTreePlugin).
26
37
mod circle;
48
mod rect;

bevy_quadtree/src/shape/rect.rs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use core::fmt;
2-
31
use bevy_ecs::prelude::*;
42
#[cfg(feature = "sprite")]
53
use bevy_log::warn;
64
use bevy_math::prelude::*;
75
#[cfg(feature = "sprite")]
86
use bevy_sprite::Sprite;
97
use bevy_transform::components::GlobalTransform;
8+
use core::fmt;
9+
use std::any::type_name;
1010

1111
use crate::{
1212
collision::{DynCollision, Relation},
@@ -27,11 +27,12 @@ pub struct CollisionRect<const ID: usize = 0> {
2727
init_size: Vec2,
2828
}
2929

30-
impl fmt::Debug for CollisionRect {
30+
impl<const ID: usize> fmt::Debug for CollisionRect<ID> {
3131
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3232
write!(
3333
f,
34-
"CollisionRect: center = ({}, {}); size = ({} x {}) x ({} x {}) = {} x {}",
34+
"{}: center = ({}, {}); size = ({} x {}) x ({} x {}) = {} x {}",
35+
type_name::<Self>(),
3536
self.center.x,
3637
self.center.y,
3738
self.init_size.x,
@@ -44,7 +45,22 @@ impl fmt::Debug for CollisionRect {
4445
}
4546
}
4647

48+
impl<const ID: usize> From<&CollisionRect<ID>> for CollisionRect<0> {
49+
/// Convert the shape with `ID` to the shape with `ID = 0`.
50+
/// Used to eliminate the `ID` in the collision detection.
51+
fn from(value: &CollisionRect<ID>) -> Self {
52+
Self {
53+
center: value.center,
54+
scale: value.scale,
55+
init_size: value.init_size,
56+
}
57+
}
58+
}
59+
4760
impl From<Rect> for CollisionRect {
61+
/// Create a new CollisionRect from a Rect with `ID = 0`.
62+
///
63+
/// See [`Self::new`] for details.
4864
fn from(rect: Rect) -> Self {
4965
Self {
5066
center: rect.center(),
@@ -55,7 +71,8 @@ impl From<Rect> for CollisionRect {
5571
}
5672

5773
impl CollisionRect {
58-
/// Create a new CollisionRect from a Rect.
74+
/// Create a new CollisionRect from a Rect with `ID = 0`.
75+
///
5976
/// The initial size is set to the size of the rect.
6077
/// It is used to compute the size with the GlobalTransform's scale.
6178
///
@@ -64,6 +81,17 @@ impl CollisionRect {
6481
pub fn new(rect: Rect) -> Self {
6582
rect.into()
6683
}
84+
}
85+
86+
impl<const ID: usize> CollisionRect<ID> {
87+
/// Create a new CollisionRect from a Rect with given `ID`.
88+
pub fn new_id(rect: Rect) -> Self {
89+
Self {
90+
center: rect.center(),
91+
scale: Vec2::ONE,
92+
init_size: rect.size(),
93+
}
94+
}
6795

6896
pub(crate) fn size(&self) -> Vec2 {
6997
self.init_size * self.scale
@@ -193,7 +221,7 @@ impl Collision<CollisionCircle> for CollisionRect {
193221
}
194222
}
195223

196-
impl UpdateCollision<GlobalTransform> for CollisionRect {
224+
impl<const ID: usize> UpdateCollision<GlobalTransform> for CollisionRect<ID> {
197225
fn update() -> impl FnOnce(Mut<Self>, &GlobalTransform) {
198226
|mut rect, global_transform| {
199227
debug_assert_eq!(
@@ -209,7 +237,7 @@ impl UpdateCollision<GlobalTransform> for CollisionRect {
209237
}
210238

211239
#[cfg(feature = "sprite")]
212-
impl UpdateCollision<Sprite> for CollisionRect {
240+
impl<const ID: usize> UpdateCollision<Sprite> for CollisionRect<ID> {
213241
fn update() -> impl FnOnce(Mut<Self>, &Sprite) {
214242
|mut rect, sprite| {
215243
if let Some(size) = sprite.custom_size {

0 commit comments

Comments
 (0)