From 265f27b96b5dd9fd0b2f16251448e926e5c52b11 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 13 Jul 2026 18:42:49 +1000 Subject: [PATCH 01/10] updated for bevy 0.19 --- Cargo.toml | 4 ++-- examples/distance3d.rs | 8 +++++--- examples/modify_timestep.rs | 2 +- src/automatic_systems.rs | 9 ++++++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 48ee500..52731ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ readme = "README.md" authors = ["laund "] [dependencies] -bevy = { version = "0.16", default-features = false } +bevy = { version = "0.19", default-features = false } # KD-Tree dependencies kd-tree = { version = "0.6.0", optional = true } typenum = { version = "1.18.0" } @@ -23,7 +23,7 @@ kdtree_rayon = ["kdtree", "kd-tree/rayon"] kdtree = ["dep:kd-tree"] [dev-dependencies] -bevy = { version = "0.16" } +bevy = { version = "0.19" } rand = "0.8" [profile.dev] diff --git a/examples/distance3d.rs b/examples/distance3d.rs index 9affa47..88a74b6 100644 --- a/examples/distance3d.rs +++ b/examples/distance3d.rs @@ -41,7 +41,6 @@ fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, - mut ambient_light: ResMut, ) { let handles = MaterialHandles { orange_red: materials.add(Color::from(csscolors::ORANGE_RED)), @@ -50,8 +49,11 @@ fn setup( }; commands.insert_resource(handles.clone()); - ambient_light.color = Color::WHITE; - ambient_light.brightness = 500.; + commands.insert_resource(GlobalAmbientLight { + color: Color::WHITE, + brightness: 500., + ..default() + }); commands.spawn(( Camera3d::default(), diff --git a/examples/modify_timestep.rs b/examples/modify_timestep.rs index 37be19f..663a3eb 100644 --- a/examples/modify_timestep.rs +++ b/examples/modify_timestep.rs @@ -34,7 +34,7 @@ fn setup(mut commands: Commands) { commands.spawn(( Text("Click mouse to change rate".to_string()), TextFont { - font_size: 30.0, + font_size: FontSize::Px(30.0), ..default() }, TextColor(Color::BLACK), diff --git a/src/automatic_systems.rs b/src/automatic_systems.rs index af44b82..2426780 100644 --- a/src/automatic_systems.rs +++ b/src/automatic_systems.rs @@ -7,7 +7,10 @@ use crate::{ }; use bevy::{ - ecs::schedule::{ScheduleLabel, SystemSet}, + ecs::{ + component::Mutable, + schedule::{ScheduleLabel, SystemSet}, + }, prelude::*, }; @@ -29,7 +32,7 @@ pub(crate) struct AutoT(PhantomData); impl AutoT where GlamVec: VecFromTransform, - SpatialDS: UpdateSpatialAccess + Resource, + SpatialDS: UpdateSpatialAccess + Resource, ::Point: From<(Entity, GlamVec)>, SpatialDS::Comp: Component, { @@ -61,7 +64,7 @@ pub(crate) struct AutoGT(PhantomData); impl AutoGT where GlamVec: VecFromGlobalTransform, - SpatialDS: UpdateSpatialAccess + Resource, + SpatialDS: UpdateSpatialAccess + Resource, ::Point: From<(Entity, GlamVec)>, SpatialDS::Comp: Component, { From e181527173df923cd0cfd1b7dd913bcf6ec15f44 Mon Sep 17 00:00:00 2001 From: Vitaly Artemiev Date: Sat, 7 Oct 2023 00:50:48 +0300 Subject: [PATCH 02/10] Add within support (rectangular axis-alligned region). Example in within2d.rs Known issues: Panic when loc1 is farther along the [1,1] axis than loc2. Could not find a way to check this and swap them inside of the macro. --- examples/within2d.rs | 172 ++++++++++++++++++++++++++++++++++++++++++ src/kdtree.rs | 23 ++++++ src/spatial_access.rs | 8 ++ 3 files changed, 203 insertions(+) create mode 100644 examples/within2d.rs diff --git a/examples/within2d.rs b/examples/within2d.rs new file mode 100644 index 0000000..0e0f0dc --- /dev/null +++ b/examples/within2d.rs @@ -0,0 +1,172 @@ +use std::time::Duration; + +use bevy::{ + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + math::Vec3Swizzles, + prelude::*, + window::PrimaryWindow, +}; +use bevy_spatial::{kdtree::KDTree2, SpatialAccess}; +use bevy_spatial::{AutomaticUpdate, SpatialStructure}; +// marker for entities tracked by the KDTree +#[derive(Component, Default)] +struct NearestNeighbourComponent; + +// marker for the "cursor" entity +#[derive(Component)] +struct Cursor; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + // Add the plugin, which takes the tracked component as a generic. + .add_plugins( + AutomaticUpdate::::new() + .with_spatial_ds(SpatialStructure::KDTree2) + .with_frequency(Duration::from_millis(1)), + ) + .add_plugins(LogDiagnosticsPlugin::default()) + .add_plugins(FrameTimeDiagnosticsPlugin) + .insert_resource(Mouse2D { pos: Vec2::ZERO }) + .add_systems(Startup, setup) + .add_systems( + Update, + ( + update_mouse_pos, + ( + mouse, + color, + reset_color.before(color), + collide_wall, + movement, + ), + ) + .chain(), + ) + .run(); +} + +// type alias for easier usage later +type NNTree = KDTree2; + +fn setup(mut commands: Commands) { + commands.spawn(Camera2dBundle::default()); + commands.spawn(( + Cursor, + SpriteBundle { + sprite: Sprite { + color: Color::rgb(0.0, 0.0, 1.0), + custom_size: Some(Vec2::new(10.0, 10.0)), + ..default() + }, + transform: Transform { + translation: Vec3::ZERO, + ..default() + }, + ..default() + }, + )); + let sprite = Sprite { + color: Color::ORANGE_RED, + custom_size: Some(Vec2::new(6.0, 6.0)), + ..default() + }; + for x in -100..100 { + for y in -100..100 { + commands.spawn(( + NearestNeighbourComponent, + SpriteBundle { + sprite: sprite.clone(), + transform: Transform { + translation: Vec3::new((x * 4) as f32, (y * 4) as f32, 0.0), + ..default() + }, + ..default() + }, + )); + } + } +} +#[derive(Copy, Clone, Resource)] +struct Mouse2D { + pos: Vec2, +} + +fn update_mouse_pos( + window: Query<&Window, With>, + cam: Query<(&Camera, &GlobalTransform)>, + mut mouse: ResMut, +) { + let win = window.single(); + let (cam, cam_t) = cam.single(); + if let Some(w_pos) = win.cursor_position() { + if let Some(pos) = cam.viewport_to_world_2d(cam_t, w_pos) { + mouse.pos = pos; + } + } +} + +fn mouse( + mut commands: Commands, + mouse: Res, + treeaccess: Res, + mut query: Query<&mut Transform, With>, + ms_buttons: Res>, +) { + let use_mouse = ms_buttons.pressed(MouseButton::Left); + + let mut transform = query.single_mut(); + + if let Some((_pos, entity)) = treeaccess.nearest_neighbour(mouse.pos) { + transform.translation = mouse.pos.extend(0.0); // I don't really know what this is here for + + if use_mouse { + commands.entity(entity.unwrap()).despawn(); + } + } +} + +fn color( + treeaccess: Res, + mouse: Res, + mut query: Query<&mut Sprite, With>, +) { + let p1 = mouse.pos; + let p2 = mouse.pos + Vec2::from([100.0, 100.0]); + + for (_, entity) in treeaccess.within(p1, p2) { + if let Ok(mut sprite) = query.get_mut(entity.unwrap()) { + sprite.color = Color::BLACK; + } + } +} + +fn reset_color(mut query: Query<&mut Sprite, With>) { + for mut sprite in &mut query { + sprite.color = Color::ORANGE_RED; + } +} + +fn movement(mut query: Query<&mut Transform, With>) { + for mut pos in &mut query { + let goal = pos.translation - Vec3::ZERO; + pos.translation += goal.normalize_or_zero(); + } +} + +fn collide_wall( + window: Query<&Window, With>, + mut query: Query<&mut Transform, With>, +) { + let win = window.get_single().unwrap(); + + let w = win.width() / 2.0; + let h = win.height() / 2.0; + + for mut pos in &mut query { + let [x, y] = pos.translation.xy().to_array(); + if y < -h || x < -w || y > h || x > w { + pos.translation = pos.translation.normalize_or_zero(); + } + } +} diff --git a/src/kdtree.rs b/src/kdtree.rs index bfdadf9..1a6b2fa 100644 --- a/src/kdtree.rs +++ b/src/kdtree.rs @@ -100,6 +100,29 @@ macro_rules! kdtree_impl { .collect() } } + + fn within( + &self, + loc1: ::Vec, + loc2: ::Vec, + ) -> Vec { + let _span = info_span!("within").entered(); + + let p1: $pt = loc1.into(); + let p2: $pt = loc2.into(); + + let rect = [p1, p2]; + + if self.tree.len() == 0 { + vec![] + } else { + self.tree + .within(&rect) + .iter() + .map(|e| (e.vec(), e.entity())) + .collect() + } + } } impl UpdateSpatialAccess for $treename { fn update( diff --git a/src/spatial_access.rs b/src/spatial_access.rs index 435a8f1..34d57d1 100644 --- a/src/spatial_access.rs +++ b/src/spatial_access.rs @@ -61,6 +61,14 @@ pub trait SpatialAccess: Send + Sync + 'static { loc: ::Vec, distance: ::Scalar, ) -> Vec; + + /// Return all points which are within the specified rectangular axis-aligned region. + /// Points are expected to be sorted along +X (+Y) +Z diagonal + fn within( + &self, + loc1: ::Vec, + loc2: ::Vec, + ) -> Vec; } // TODO: SpatialAABBAccess trait definition - should it be separate from SpatialAccess or depend on it? From 2153864efd52cde0edc6ef078aa0e3eae397bbec Mon Sep 17 00:00:00 2001 From: Vitaly Artemiev Date: Sat, 7 Oct 2023 01:22:15 +0300 Subject: [PATCH 03/10] Rework example. Move example function back into distance2d.rs; remove within2d.rs. --- examples/distance2d.rs | 24 ++++++ examples/within2d.rs | 172 ----------------------------------------- src/kdtree.rs | 2 + src/spatial_access.rs | 2 +- 4 files changed, 27 insertions(+), 173 deletions(-) delete mode 100644 examples/within2d.rs diff --git a/examples/distance2d.rs b/examples/distance2d.rs index 34923a9..5cd12a1 100644 --- a/examples/distance2d.rs +++ b/examples/distance2d.rs @@ -44,6 +44,7 @@ fn main() { ( mouse, color, + color_rect, reset_color.before(color), collide_wall, movement, @@ -137,6 +138,29 @@ fn color( } } +fn color_rect( + treeaccess: Res, + mouse: Res, + mut query: Query<&mut Sprite, With>, +) { + let mut p1 = mouse.pos; + let mut p2 = Vec2::from([100.0, -100.0]); + + if p1.x > p2.x { + (p1.x, p2.x) = (p2.x, p1.x); + } + + if p1.y > p2.y { + (p1.y, p2.y) = (p2.y, p1.y); + } + + for (_, entity) in treeaccess.within(p1, p2) { + if let Ok(mut sprite) = query.get_mut(entity.unwrap()) { + sprite.color = Color::GREEN; + } + } +} + fn reset_color(mut query: Query<&mut Sprite, With>) { for mut sprite in &mut query { sprite.color = csscolors::ORANGE_RED.into(); diff --git a/examples/within2d.rs b/examples/within2d.rs deleted file mode 100644 index 0e0f0dc..0000000 --- a/examples/within2d.rs +++ /dev/null @@ -1,172 +0,0 @@ -use std::time::Duration; - -use bevy::{ - diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, - math::Vec3Swizzles, - prelude::*, - window::PrimaryWindow, -}; -use bevy_spatial::{kdtree::KDTree2, SpatialAccess}; -use bevy_spatial::{AutomaticUpdate, SpatialStructure}; -// marker for entities tracked by the KDTree -#[derive(Component, Default)] -struct NearestNeighbourComponent; - -// marker for the "cursor" entity -#[derive(Component)] -struct Cursor; - -fn main() { - App::new() - .add_plugins(DefaultPlugins) - // Add the plugin, which takes the tracked component as a generic. - .add_plugins( - AutomaticUpdate::::new() - .with_spatial_ds(SpatialStructure::KDTree2) - .with_frequency(Duration::from_millis(1)), - ) - .add_plugins(LogDiagnosticsPlugin::default()) - .add_plugins(FrameTimeDiagnosticsPlugin) - .insert_resource(Mouse2D { pos: Vec2::ZERO }) - .add_systems(Startup, setup) - .add_systems( - Update, - ( - update_mouse_pos, - ( - mouse, - color, - reset_color.before(color), - collide_wall, - movement, - ), - ) - .chain(), - ) - .run(); -} - -// type alias for easier usage later -type NNTree = KDTree2; - -fn setup(mut commands: Commands) { - commands.spawn(Camera2dBundle::default()); - commands.spawn(( - Cursor, - SpriteBundle { - sprite: Sprite { - color: Color::rgb(0.0, 0.0, 1.0), - custom_size: Some(Vec2::new(10.0, 10.0)), - ..default() - }, - transform: Transform { - translation: Vec3::ZERO, - ..default() - }, - ..default() - }, - )); - let sprite = Sprite { - color: Color::ORANGE_RED, - custom_size: Some(Vec2::new(6.0, 6.0)), - ..default() - }; - for x in -100..100 { - for y in -100..100 { - commands.spawn(( - NearestNeighbourComponent, - SpriteBundle { - sprite: sprite.clone(), - transform: Transform { - translation: Vec3::new((x * 4) as f32, (y * 4) as f32, 0.0), - ..default() - }, - ..default() - }, - )); - } - } -} -#[derive(Copy, Clone, Resource)] -struct Mouse2D { - pos: Vec2, -} - -fn update_mouse_pos( - window: Query<&Window, With>, - cam: Query<(&Camera, &GlobalTransform)>, - mut mouse: ResMut, -) { - let win = window.single(); - let (cam, cam_t) = cam.single(); - if let Some(w_pos) = win.cursor_position() { - if let Some(pos) = cam.viewport_to_world_2d(cam_t, w_pos) { - mouse.pos = pos; - } - } -} - -fn mouse( - mut commands: Commands, - mouse: Res, - treeaccess: Res, - mut query: Query<&mut Transform, With>, - ms_buttons: Res>, -) { - let use_mouse = ms_buttons.pressed(MouseButton::Left); - - let mut transform = query.single_mut(); - - if let Some((_pos, entity)) = treeaccess.nearest_neighbour(mouse.pos) { - transform.translation = mouse.pos.extend(0.0); // I don't really know what this is here for - - if use_mouse { - commands.entity(entity.unwrap()).despawn(); - } - } -} - -fn color( - treeaccess: Res, - mouse: Res, - mut query: Query<&mut Sprite, With>, -) { - let p1 = mouse.pos; - let p2 = mouse.pos + Vec2::from([100.0, 100.0]); - - for (_, entity) in treeaccess.within(p1, p2) { - if let Ok(mut sprite) = query.get_mut(entity.unwrap()) { - sprite.color = Color::BLACK; - } - } -} - -fn reset_color(mut query: Query<&mut Sprite, With>) { - for mut sprite in &mut query { - sprite.color = Color::ORANGE_RED; - } -} - -fn movement(mut query: Query<&mut Transform, With>) { - for mut pos in &mut query { - let goal = pos.translation - Vec3::ZERO; - pos.translation += goal.normalize_or_zero(); - } -} - -fn collide_wall( - window: Query<&Window, With>, - mut query: Query<&mut Transform, With>, -) { - let win = window.get_single().unwrap(); - - let w = win.width() / 2.0; - let h = win.height() / 2.0; - - for mut pos in &mut query { - let [x, y] = pos.translation.xy().to_array(); - if y < -h || x < -w || y > h || x > w { - pos.translation = pos.translation.normalize_or_zero(); - } - } -} diff --git a/src/kdtree.rs b/src/kdtree.rs index 1a6b2fa..38c61a6 100644 --- a/src/kdtree.rs +++ b/src/kdtree.rs @@ -101,6 +101,8 @@ macro_rules! kdtree_impl { } } + /// Return all points which are within the specified rectangular axis-aligned region. + /// loc1, loc2 are expected to be sorted along +X (+Y) +Z diagonal. fn within( &self, loc1: ::Vec, diff --git a/src/spatial_access.rs b/src/spatial_access.rs index 34d57d1..e09a514 100644 --- a/src/spatial_access.rs +++ b/src/spatial_access.rs @@ -63,7 +63,7 @@ pub trait SpatialAccess: Send + Sync + 'static { ) -> Vec; /// Return all points which are within the specified rectangular axis-aligned region. - /// Points are expected to be sorted along +X (+Y) +Z diagonal + /// loc1, loc2 are expected to be sorted along +X (+Y) +Z diagonal. fn within( &self, loc1: ::Vec, From f77d73a2a37f50c9057004419df1a9f125595b23 Mon Sep 17 00:00:00 2001 From: Vitaly Artemiev Date: Sat, 7 Oct 2023 01:54:00 +0300 Subject: [PATCH 04/10] Fix box corners issue. --- examples/distance2d.rs | 12 ++---------- src/kdtree.rs | 4 ++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/examples/distance2d.rs b/examples/distance2d.rs index 5cd12a1..77ce3bf 100644 --- a/examples/distance2d.rs +++ b/examples/distance2d.rs @@ -143,16 +143,8 @@ fn color_rect( mouse: Res, mut query: Query<&mut Sprite, With>, ) { - let mut p1 = mouse.pos; - let mut p2 = Vec2::from([100.0, -100.0]); - - if p1.x > p2.x { - (p1.x, p2.x) = (p2.x, p1.x); - } - - if p1.y > p2.y { - (p1.y, p2.y) = (p2.y, p1.y); - } + let p1 = mouse.pos; + let p2 = Vec2::from([100.0, -100.0]); for (_, entity) in treeaccess.within(p1, p2) { if let Ok(mut sprite) = query.get_mut(entity.unwrap()) { diff --git a/src/kdtree.rs b/src/kdtree.rs index 38c61a6..1354031 100644 --- a/src/kdtree.rs +++ b/src/kdtree.rs @@ -110,8 +110,8 @@ macro_rules! kdtree_impl { ) -> Vec { let _span = info_span!("within").entered(); - let p1: $pt = loc1.into(); - let p2: $pt = loc2.into(); + let p1: $pt = loc1.min(loc2).into(); + let p2: $pt = loc1.max(loc2).into(); let rect = [p1, p2]; From a578e96bd4c88f7aa67182728e5b9118994c32d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D1=82=D0=B0=D0=BB=D0=B8=D0=B9=20=D0=90=D1=80?= =?UTF-8?q?=D1=82=D0=B5=D0=BC=D1=8C=D0=B5=D0=B2?= Date: Sat, 7 Oct 2023 02:11:11 +0300 Subject: [PATCH 05/10] Adjust docstrings --- src/kdtree.rs | 1 - src/spatial_access.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/kdtree.rs b/src/kdtree.rs index 1354031..2a01900 100644 --- a/src/kdtree.rs +++ b/src/kdtree.rs @@ -102,7 +102,6 @@ macro_rules! kdtree_impl { } /// Return all points which are within the specified rectangular axis-aligned region. - /// loc1, loc2 are expected to be sorted along +X (+Y) +Z diagonal. fn within( &self, loc1: ::Vec, diff --git a/src/spatial_access.rs b/src/spatial_access.rs index e09a514..8793b29 100644 --- a/src/spatial_access.rs +++ b/src/spatial_access.rs @@ -63,7 +63,6 @@ pub trait SpatialAccess: Send + Sync + 'static { ) -> Vec; /// Return all points which are within the specified rectangular axis-aligned region. - /// loc1, loc2 are expected to be sorted along +X (+Y) +Z diagonal. fn within( &self, loc1: ::Vec, From 0910979633b1f99ff7e97e25472422928d0f1a6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D1=82=D0=B0=D0=BB=D0=B8=D0=B9=20=D0=90=D1=80?= =?UTF-8?q?=D1=82=D0=B5=D0=BC=D1=8C=D0=B5=D0=B2?= Date: Tue, 2 Apr 2024 04:12:30 +0300 Subject: [PATCH 06/10] Redo as an extension trait. Add separate demo. --- examples/distance2d.rs | 16 ---- examples/within2d.rs | 173 +++++++++++++++++++++++++++++++++++++++++ src/kdtree.rs | 55 +++++++------ src/lib.rs | 2 +- src/spatial_access.rs | 4 +- 5 files changed, 206 insertions(+), 44 deletions(-) create mode 100644 examples/within2d.rs diff --git a/examples/distance2d.rs b/examples/distance2d.rs index 77ce3bf..34923a9 100644 --- a/examples/distance2d.rs +++ b/examples/distance2d.rs @@ -44,7 +44,6 @@ fn main() { ( mouse, color, - color_rect, reset_color.before(color), collide_wall, movement, @@ -138,21 +137,6 @@ fn color( } } -fn color_rect( - treeaccess: Res, - mouse: Res, - mut query: Query<&mut Sprite, With>, -) { - let p1 = mouse.pos; - let p2 = Vec2::from([100.0, -100.0]); - - for (_, entity) in treeaccess.within(p1, p2) { - if let Ok(mut sprite) = query.get_mut(entity.unwrap()) { - sprite.color = Color::GREEN; - } - } -} - fn reset_color(mut query: Query<&mut Sprite, With>) { for mut sprite in &mut query { sprite.color = csscolors::ORANGE_RED.into(); diff --git a/examples/within2d.rs b/examples/within2d.rs new file mode 100644 index 0000000..3ba3394 --- /dev/null +++ b/examples/within2d.rs @@ -0,0 +1,173 @@ +use std::time::Duration; + +use bevy::{ + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + math::Vec3Swizzles, + prelude::*, + window::PrimaryWindow, +}; +use bevy_spatial::{kdtree::KDTree2, SpatialAccess, SpatialAABBAccess}; +use bevy_spatial::{AutomaticUpdate, SpatialStructure}; +// marker for entities tracked by the KDTree +#[derive(Component, Default)] +struct NearestNeighbourComponent; + +// marker for the "cursor" entity +#[derive(Component)] +struct Cursor; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + // Add the plugin, which takes the tracked component as a generic. + .add_plugins( + AutomaticUpdate::::new() + .with_spatial_ds(SpatialStructure::KDTree2) + .with_frequency(Duration::from_millis(1)), + ) + .add_plugins(LogDiagnosticsPlugin::default()) + .add_plugins(FrameTimeDiagnosticsPlugin) + .insert_resource(Mouse2D { pos: Vec2::ZERO }) + .add_systems(Startup, setup) + .add_systems( + Update, + ( + update_mouse_pos, + ( + mouse, + color_rect, + reset_color.before(color_rect), + collide_wall, + movement, + ), + ) + .chain(), + ) + .run(); +} + +// type alias for easier usage later +type NNTree = KDTree2; + +fn setup(mut commands: Commands) { + commands.spawn(Camera2dBundle::default()); + commands.spawn(( + Cursor, + SpriteBundle { + sprite: Sprite { + color: Color::rgb(0.0, 0.0, 1.0), + custom_size: Some(Vec2::new(10.0, 10.0)), + ..default() + }, + transform: Transform { + translation: Vec3::ZERO, + ..default() + }, + ..default() + }, + )); + let sprite = Sprite { + color: Color::ORANGE_RED, + custom_size: Some(Vec2::new(6.0, 6.0)), + ..default() + }; + for x in -100..100 { + for y in -100..100 { + commands.spawn(( + NearestNeighbourComponent, + SpriteBundle { + sprite: sprite.clone(), + transform: Transform { + translation: Vec3::new((x * 4) as f32, (y * 4) as f32, 0.0), + ..default() + }, + ..default() + }, + )); + } + } +} +#[derive(Copy, Clone, Resource)] +struct Mouse2D { + pos: Vec2, +} + +fn update_mouse_pos( + window: Query<&Window, With>, + cam: Query<(&Camera, &GlobalTransform)>, + mut mouse: ResMut, +) { + let win = window.single(); + let (cam, cam_t) = cam.single(); + if let Some(w_pos) = win.cursor_position() { + if let Some(pos) = cam.viewport_to_world_2d(cam_t, w_pos) { + mouse.pos = pos; + } + } +} + +fn mouse( + mut commands: Commands, + mouse: Res, + treeaccess: Res, + mut query: Query<&mut Transform, With>, + ms_buttons: Res>, +) { + let use_mouse = ms_buttons.pressed(MouseButton::Left); + + let p1 = mouse.pos; + let p2 = Vec2::from([100.0, -100.0]); + + let mut transform = query.single_mut(); + + for (_, entity) in treeaccess.within(p1, p2) { + if use_mouse { + commands.entity(entity.unwrap()).despawn(); + } + } +} + +fn color_rect( + treeaccess: Res, + mouse: Res, + mut query: Query<&mut Sprite, With>, +) { + let p1 = mouse.pos; + let p2 = Vec2::from([100.0, -100.0]); + + for (_, entity) in treeaccess.within(p1, p2) { + if let Ok(mut sprite) = query.get_mut(entity.unwrap()) { + sprite.color = Color::GREEN; + } + } +} + +fn reset_color(mut query: Query<&mut Sprite, With>) { + for mut sprite in &mut query { + sprite.color = Color::ORANGE_RED; + } +} + +fn movement(mut query: Query<&mut Transform, With>) { + for mut pos in &mut query { + let goal = pos.translation - Vec3::ZERO; + pos.translation += goal.normalize_or_zero(); + } +} + +fn collide_wall( + window: Query<&Window, With>, + mut query: Query<&mut Transform, With>, +) { + let win = window.get_single().unwrap(); + + let w = win.width() / 2.0; + let h = win.height() / 2.0; + + for mut pos in &mut query { + let [x, y] = pos.translation.xy().to_array(); + if y < -h || x < -w || y > h || x > w { + pos.translation = pos.translation.normalize_or_zero(); + } + } +} diff --git a/src/kdtree.rs b/src/kdtree.rs index 2a01900..00be227 100644 --- a/src/kdtree.rs +++ b/src/kdtree.rs @@ -6,7 +6,7 @@ use kd_tree::{KdPoint, KdTree as BaseKdTree, KdTreeN}; use crate::{ TComp, point::SpatialPoint, - spatial_access::{SpatialAccess, UpdateSpatialAccess}, + spatial_access::{SpatialAABBAccess, SpatialAccess, UpdateSpatialAccess}, }; use std::marker::PhantomData; @@ -47,6 +47,35 @@ macro_rules! kdtree_impl { } } + impl SpatialAABBAccess for $treename + where + Comp: TComp, + { + /// Return all points which are within the specified rectangular axis-aligned region. + fn within( + &self, + loc1: ::Vec, + loc2: ::Vec, + ) -> Vec { + let _span = info_span!("within").entered(); + + let p1: $pt = loc1.min(loc2).into(); + let p2: $pt = loc1.max(loc2).into(); + + let rect = [p1, p2]; + + if self.tree.len() == 0 { + vec![] + } else { + self.tree + .within(&rect) + .iter() + .map(|e| (e.vec(), e.entity())) + .collect() + } + } + } + impl SpatialAccess for $treename where Comp: TComp, @@ -100,30 +129,6 @@ macro_rules! kdtree_impl { .collect() } } - - /// Return all points which are within the specified rectangular axis-aligned region. - fn within( - &self, - loc1: ::Vec, - loc2: ::Vec, - ) -> Vec { - let _span = info_span!("within").entered(); - - let p1: $pt = loc1.min(loc2).into(); - let p2: $pt = loc1.max(loc2).into(); - - let rect = [p1, p2]; - - if self.tree.len() == 0 { - vec![] - } else { - self.tree - .within(&rect) - .iter() - .map(|e| (e.vec(), e.entity())) - .collect() - } - } } impl UpdateSpatialAccess for $treename { fn update( diff --git a/src/lib.rs b/src/lib.rs index 76bf591..5346884 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ pub mod point; mod spatial_access; -pub use self::spatial_access::SpatialAccess; +pub use self::spatial_access::{SpatialAccess, SpatialAABBAccess}; use bevy::prelude::Component; mod timestep; diff --git a/src/spatial_access.rs b/src/spatial_access.rs index 8793b29..2ca52e1 100644 --- a/src/spatial_access.rs +++ b/src/spatial_access.rs @@ -61,7 +61,9 @@ pub trait SpatialAccess: Send + Sync + 'static { loc: ::Vec, distance: ::Scalar, ) -> Vec; +} +pub trait SpatialAABBAccess: SpatialAccess { /// Return all points which are within the specified rectangular axis-aligned region. fn within( &self, @@ -69,5 +71,3 @@ pub trait SpatialAccess: Send + Sync + 'static { loc2: ::Vec, ) -> Vec; } - -// TODO: SpatialAABBAccess trait definition - should it be separate from SpatialAccess or depend on it? From be0f779480b506b0edf0b8c8798dec1262f2bed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D1=82=D0=B0=D0=BB=D0=B8=D0=B9=20=D0=90=D1=80?= =?UTF-8?q?=D1=82=D0=B5=D0=BC=D1=8C=D0=B5=D0=B2?= Date: Tue, 2 Apr 2024 04:17:51 +0300 Subject: [PATCH 07/10] Remove unused import --- examples/within2d.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/within2d.rs b/examples/within2d.rs index 3ba3394..3d9a448 100644 --- a/examples/within2d.rs +++ b/examples/within2d.rs @@ -6,7 +6,7 @@ use bevy::{ prelude::*, window::PrimaryWindow, }; -use bevy_spatial::{kdtree::KDTree2, SpatialAccess, SpatialAABBAccess}; +use bevy_spatial::{kdtree::KDTree2, SpatialAABBAccess}; use bevy_spatial::{AutomaticUpdate, SpatialStructure}; // marker for entities tracked by the KDTree #[derive(Component, Default)] From 29b777b807097bbc1deb5f5b829a278caeec8121 Mon Sep 17 00:00:00 2001 From: Vitaly Artemiev Date: Wed, 11 Sep 2024 01:27:00 +0300 Subject: [PATCH 08/10] Fix example for 0.14 --- examples/within2d.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/within2d.rs b/examples/within2d.rs index 3d9a448..79f67d3 100644 --- a/examples/within2d.rs +++ b/examples/within2d.rs @@ -1,6 +1,7 @@ use std::time::Duration; use bevy::{ + color::palettes::css as csscolors, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, math::Vec3Swizzles, prelude::*, @@ -55,7 +56,7 @@ fn setup(mut commands: Commands) { Cursor, SpriteBundle { sprite: Sprite { - color: Color::rgb(0.0, 0.0, 1.0), + color: Color::srgb(0.0, 0.0, 1.0), custom_size: Some(Vec2::new(10.0, 10.0)), ..default() }, @@ -67,7 +68,7 @@ fn setup(mut commands: Commands) { }, )); let sprite = Sprite { - color: Color::ORANGE_RED, + color: csscolors::ORANGE_RED.into(), custom_size: Some(Vec2::new(6.0, 6.0)), ..default() }; @@ -110,7 +111,6 @@ fn mouse( mut commands: Commands, mouse: Res, treeaccess: Res, - mut query: Query<&mut Transform, With>, ms_buttons: Res>, ) { let use_mouse = ms_buttons.pressed(MouseButton::Left); @@ -118,8 +118,6 @@ fn mouse( let p1 = mouse.pos; let p2 = Vec2::from([100.0, -100.0]); - let mut transform = query.single_mut(); - for (_, entity) in treeaccess.within(p1, p2) { if use_mouse { commands.entity(entity.unwrap()).despawn(); @@ -137,14 +135,14 @@ fn color_rect( for (_, entity) in treeaccess.within(p1, p2) { if let Ok(mut sprite) = query.get_mut(entity.unwrap()) { - sprite.color = Color::GREEN; + sprite.color = csscolors::GREEN.into(); } } } fn reset_color(mut query: Query<&mut Sprite, With>) { for mut sprite in &mut query { - sprite.color = Color::ORANGE_RED; + sprite.color = csscolors::ORANGE_RED.into(); } } From 3b610cc66d6dfd641cb6915c060739182fbb762d Mon Sep 17 00:00:00 2001 From: VitalyArtemiev Date: Sat, 31 May 2025 01:17:23 +0300 Subject: [PATCH 09/10] Fix example for 0.16. Maybe needs rework. --- examples/within2d.rs | 80 +++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/examples/within2d.rs b/examples/within2d.rs index 79f67d3..43dfcf7 100644 --- a/examples/within2d.rs +++ b/examples/within2d.rs @@ -1,3 +1,4 @@ +use std::ops::Deref; use std::time::Duration; use bevy::{ @@ -7,8 +8,8 @@ use bevy::{ prelude::*, window::PrimaryWindow, }; -use bevy_spatial::{kdtree::KDTree2, SpatialAABBAccess}; -use bevy_spatial::{AutomaticUpdate, SpatialStructure}; +use bevy_spatial::{AutomaticUpdate, SpatialAABBAccess, SpatialStructure}; +use bevy_spatial::{SpatialAccess, kdtree::KDTree2}; // marker for entities tracked by the KDTree #[derive(Component, Default)] struct NearestNeighbourComponent; @@ -19,7 +20,13 @@ struct Cursor; fn main() { App::new() - .add_plugins(DefaultPlugins) + .add_plugins(DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + present_mode: bevy::window::PresentMode::AutoNoVsync, + ..default() + }), + ..default() + })) // Add the plugin, which takes the tracked component as a generic. .add_plugins( AutomaticUpdate::::new() @@ -27,7 +34,7 @@ fn main() { .with_frequency(Duration::from_millis(1)), ) .add_plugins(LogDiagnosticsPlugin::default()) - .add_plugins(FrameTimeDiagnosticsPlugin) + .add_plugins(FrameTimeDiagnosticsPlugin::default()) .insert_resource(Mouse2D { pos: Vec2::ZERO }) .add_systems(Startup, setup) .add_systems( @@ -51,19 +58,16 @@ fn main() { type NNTree = KDTree2; fn setup(mut commands: Commands) { - commands.spawn(Camera2dBundle::default()); + commands.spawn(Camera2d); commands.spawn(( Cursor, - SpriteBundle { - sprite: Sprite { - color: Color::srgb(0.0, 0.0, 1.0), - custom_size: Some(Vec2::new(10.0, 10.0)), - ..default() - }, - transform: Transform { - translation: Vec3::ZERO, - ..default() - }, + Sprite { + color: Color::srgb(0.0, 0.0, 1.0), + custom_size: Some(Vec2::new(10.0, 10.0)), + ..default() + }, + Transform { + translation: Vec3::ZERO, ..default() }, )); @@ -76,12 +80,9 @@ fn setup(mut commands: Commands) { for y in -100..100 { commands.spawn(( NearestNeighbourComponent, - SpriteBundle { - sprite: sprite.clone(), - transform: Transform { - translation: Vec3::new((x * 4) as f32, (y * 4) as f32, 0.0), - ..default() - }, + sprite.clone(), + Transform { + translation: Vec3::new((x * 4) as f32, (y * 4) as f32, 0.0), ..default() }, )); @@ -94,14 +95,13 @@ struct Mouse2D { } fn update_mouse_pos( - window: Query<&Window, With>, - cam: Query<(&Camera, &GlobalTransform)>, + window: Single<&Window, With>, + camera: Single<(&Camera, &GlobalTransform)>, mut mouse: ResMut, ) { - let win = window.single(); - let (cam, cam_t) = cam.single(); - if let Some(w_pos) = win.cursor_position() { - if let Some(pos) = cam.viewport_to_world_2d(cam_t, w_pos) { + let (cam, cam_t) = camera.deref(); + if let Some(w_pos) = window.cursor_position() { + if let Ok(pos) = cam.viewport_to_world_2d(cam_t, w_pos) { mouse.pos = pos; } } @@ -111,20 +111,32 @@ fn mouse( mut commands: Commands, mouse: Res, treeaccess: Res, + mut transform: Single<&mut Transform, With>, ms_buttons: Res>, ) { let use_mouse = ms_buttons.pressed(MouseButton::Left); - let p1 = mouse.pos; - let p2 = Vec2::from([100.0, -100.0]); + if let Some((_pos, entity)) = treeaccess.nearest_neighbour(mouse.pos) { + transform.translation = mouse.pos.extend(0.0); // I don't really know what this is here for - for (_, entity) in treeaccess.within(p1, p2) { if use_mouse { commands.entity(entity.unwrap()).despawn(); } } } +fn color( + treeaccess: Res, + mouse: Res, + mut query: Query<&mut Sprite, With>, +) { + for (_, entity) in treeaccess.within_distance(mouse.pos, 50.0) { + if let Ok(mut sprite) = query.get_mut(entity.unwrap()) { + sprite.color = Color::BLACK; + } + } +} + fn color_rect( treeaccess: Res, mouse: Res, @@ -154,13 +166,11 @@ fn movement(mut query: Query<&mut Transform, With>) { } fn collide_wall( - window: Query<&Window, With>, + window: Single<&Window, With>, mut query: Query<&mut Transform, With>, ) { - let win = window.get_single().unwrap(); - - let w = win.width() / 2.0; - let h = win.height() / 2.0; + let w = window.width() / 2.0; + let h = window.height() / 2.0; for mut pos in &mut query { let [x, y] = pos.translation.xy().to_array(); From a652535487ab171041ec92e9fe86fe04091de56d Mon Sep 17 00:00:00 2001 From: VitalyArtemiev Date: Sat, 31 May 2025 01:23:48 +0300 Subject: [PATCH 10/10] within2d.rs: now highlighted entities are deleted. --- examples/within2d.rs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/examples/within2d.rs b/examples/within2d.rs index 43dfcf7..80ce8a2 100644 --- a/examples/within2d.rs +++ b/examples/within2d.rs @@ -111,29 +111,19 @@ fn mouse( mut commands: Commands, mouse: Res, treeaccess: Res, - mut transform: Single<&mut Transform, With>, ms_buttons: Res>, + mut query: Query<&mut Sprite, With>, ) { let use_mouse = ms_buttons.pressed(MouseButton::Left); - if let Some((_pos, entity)) = treeaccess.nearest_neighbour(mouse.pos) { - transform.translation = mouse.pos.extend(0.0); // I don't really know what this is here for + let p1 = mouse.pos; + let p2 = Vec2::from([100.0, -100.0]); + for (_, entity) in treeaccess.within(p1, p2) { if use_mouse { commands.entity(entity.unwrap()).despawn(); } - } -} -fn color( - treeaccess: Res, - mouse: Res, - mut query: Query<&mut Sprite, With>, -) { - for (_, entity) in treeaccess.within_distance(mouse.pos, 50.0) { - if let Ok(mut sprite) = query.get_mut(entity.unwrap()) { - sprite.color = Color::BLACK; - } } }