cargo fmt again
This commit is contained in:
@@ -2,7 +2,7 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use bevy::{prelude::*, sprite::MaterialMesh2dBundle};
|
use bevy::{prelude::*, sprite::MaterialMesh2dBundle};
|
||||||
use bevy_spatial::{
|
use bevy_spatial::{
|
||||||
kdtree::KDTree2, AutomaticUpdate, SpatialAccess, SpatialStructure, TransformMode
|
kdtree::KDTree2, AutomaticUpdate, SpatialAccess, SpatialStructure, TransformMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BACKGROUND_COLOR: Color = Color::srgb(0.4, 0.4, 0.4);
|
const BACKGROUND_COLOR: Color = Color::srgb(0.4, 0.4, 0.4);
|
||||||
@@ -17,14 +17,17 @@ pub struct BoidsPlugin;
|
|||||||
|
|
||||||
impl Plugin for BoidsPlugin {
|
impl Plugin for BoidsPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app
|
app.add_plugins(
|
||||||
.add_plugins(AutomaticUpdate::<TrackedByKdTree>::new()
|
AutomaticUpdate::<TrackedByKdTree>::new()
|
||||||
// .with_frequency(Duration::from_secs_f32(0.3))
|
// .with_frequency(Duration::from_secs_f32(0.3))
|
||||||
.with_transform(TransformMode::GlobalTransform)
|
.with_transform(TransformMode::GlobalTransform)
|
||||||
.with_spatial_ds(SpatialStructure::KDTree2))
|
.with_spatial_ds(SpatialStructure::KDTree2),
|
||||||
.insert_resource(ClearColor(BACKGROUND_COLOR))
|
)
|
||||||
.add_systems(Startup, (spawn_camera, spawn_boids))
|
.insert_resource(ClearColor(BACKGROUND_COLOR))
|
||||||
.add_systems(FixedUpdate, (
|
.add_systems(Startup, (spawn_camera, spawn_boids))
|
||||||
|
.add_systems(
|
||||||
|
FixedUpdate,
|
||||||
|
(
|
||||||
apply_velocity,
|
apply_velocity,
|
||||||
turn_if_edge,
|
turn_if_edge,
|
||||||
check_keyboard,
|
check_keyboard,
|
||||||
@@ -32,7 +35,8 @@ impl Plugin for BoidsPlugin {
|
|||||||
separation,
|
separation,
|
||||||
alignment,
|
alignment,
|
||||||
// space_brakes,
|
// space_brakes,
|
||||||
));
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +148,7 @@ fn turn_if_edge(
|
|||||||
|
|
||||||
fn apply_velocity(
|
fn apply_velocity(
|
||||||
mut query: Query<(&mut Transform, &Velocity, &mut Acceleration)>,
|
mut query: Query<(&mut Transform, &Velocity, &mut Acceleration)>,
|
||||||
time: Res<Time>
|
time: Res<Time>,
|
||||||
) {
|
) {
|
||||||
for (mut transform, velocity, mut acceleration) in &mut query {
|
for (mut transform, velocity, mut acceleration) in &mut query {
|
||||||
let delta_v = **acceleration * time.delta_seconds();
|
let delta_v = **acceleration * time.delta_seconds();
|
||||||
@@ -191,13 +195,8 @@ fn cohesion(
|
|||||||
// find vector from boid to flock CoM
|
// find vector from boid to flock CoM
|
||||||
// apply force
|
// apply force
|
||||||
for (transform, mut acceleration) in &mut boids {
|
for (transform, mut acceleration) in &mut boids {
|
||||||
let neighbors = spatial_tree.within_distance(
|
let neighbors = spatial_tree.within_distance(transform.translation.xy(), BOID_VIEW_RANGE);
|
||||||
transform.translation.xy(),
|
if let Some(center_mass) = center_of_boids(neighbors.iter().map(|boid| boid.0)) {
|
||||||
BOID_VIEW_RANGE
|
|
||||||
);
|
|
||||||
if let Some(center_mass) = center_of_boids(
|
|
||||||
neighbors.iter().map(|boid| boid.0 )
|
|
||||||
) {
|
|
||||||
let towards = (center_mass.extend(0.0) - transform.translation).normalize();
|
let towards = (center_mass.extend(0.0) - transform.translation).normalize();
|
||||||
acceleration.0 += towards * COHESION_FACTOR;
|
acceleration.0 += towards * COHESION_FACTOR;
|
||||||
}
|
}
|
||||||
@@ -213,16 +212,15 @@ fn separation(
|
|||||||
// sum force from neighbors
|
// sum force from neighbors
|
||||||
// apply force
|
// apply force
|
||||||
for (boid_transform, mut boid_acceleration) in &mut boids {
|
for (boid_transform, mut boid_acceleration) in &mut boids {
|
||||||
let neighbors = spatial_tree.within_distance(
|
let neighbors =
|
||||||
boid_transform.translation.xy(),
|
spatial_tree.within_distance(boid_transform.translation.xy(), BOID_VIEW_RANGE / 4.0);
|
||||||
BOID_VIEW_RANGE / 4.0,
|
let accel = neighbors.iter().map(|(pos, _)| pos.extend(0.0)).fold(
|
||||||
);
|
Vec3::ZERO,
|
||||||
let accel = neighbors.iter()
|
|accumulator, neighbor| {
|
||||||
.map(|(pos, _)| pos.extend(0.0))
|
|
||||||
.fold(Vec3::ZERO, |accumulator, neighbor |{
|
|
||||||
let force = separation_force(boid_transform.translation.xy(), neighbor.xy());
|
let force = separation_force(boid_transform.translation.xy(), neighbor.xy());
|
||||||
accumulator + *force
|
accumulator + *force
|
||||||
});
|
},
|
||||||
|
);
|
||||||
boid_acceleration.0 += accel;
|
boid_acceleration.0 += accel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,23 +244,22 @@ fn alignment(
|
|||||||
// apply steering force
|
// apply steering force
|
||||||
|
|
||||||
for (transform, velocity, mut acceleration) in &mut boids {
|
for (transform, velocity, mut acceleration) in &mut boids {
|
||||||
let neighbors = spatial_tree.within_distance(
|
let neighbors = spatial_tree.within_distance(transform.translation.xy(), BOID_VIEW_RANGE);
|
||||||
transform.translation.xy(),
|
|
||||||
BOID_VIEW_RANGE,
|
|
||||||
);
|
|
||||||
// averaging divides by length. Guard against an empty set of neighbors
|
// averaging divides by length. Guard against an empty set of neighbors
|
||||||
// so that we don't divide by zero.
|
// so that we don't divide by zero.
|
||||||
if neighbors.len() > 0 {
|
if neighbors.len() > 0 {
|
||||||
if let Some(avg_velocity) = velocity_of_boids(
|
if let Some(avg_velocity) =
|
||||||
neighbors.iter().map(|(vel, opt_entity)| {
|
velocity_of_boids(neighbors.iter().map(|(vel, opt_entity)| {
|
||||||
// I've observed no panics in the old version, nor the debug_plugins version
|
// I've observed no panics in the old version, nor the debug_plugins version
|
||||||
// I'm not clear on the conditions that cause a None option, but I want to
|
// I'm not clear on the conditions that cause a None option, but I want to
|
||||||
// crash when I find one.
|
// crash when I find one.
|
||||||
let entity_id = opt_entity.unwrap_or_else(|| panic!("Boid has no Entity ID!"));
|
let entity_id = opt_entity.unwrap_or_else(|| panic!("Boid has no Entity ID!"));
|
||||||
let vel = boid_velocities.get(entity_id).unwrap_or_else(|_| panic!("Boid has no velocity!"));
|
let vel = boid_velocities
|
||||||
|
.get(entity_id)
|
||||||
|
.unwrap_or_else(|_| panic!("Boid has no velocity!"));
|
||||||
(*vel).xy()
|
(*vel).xy()
|
||||||
})
|
}))
|
||||||
) {
|
{
|
||||||
let deviation = -velocity.0 + avg_velocity.extend(0.0);
|
let deviation = -velocity.0 + avg_velocity.extend(0.0);
|
||||||
acceleration.0 += deviation * ALIGNMENT_FACTOR;
|
acceleration.0 += deviation * ALIGNMENT_FACTOR;
|
||||||
}
|
}
|
||||||
@@ -270,7 +267,6 @@ fn alignment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn center_of_boids(points: impl Iterator<Item = Vec2>) -> Option<Vec2> {
|
pub(crate) fn center_of_boids(points: impl Iterator<Item = Vec2>) -> Option<Vec2> {
|
||||||
average_of_vec2s(points)
|
average_of_vec2s(points)
|
||||||
}
|
}
|
||||||
@@ -285,20 +281,18 @@ fn average_of_vec2s(points: impl Iterator<Item = Vec2>) -> Option<Vec2> {
|
|||||||
// Passing the points as an iterator means we lose the length of the
|
// Passing the points as an iterator means we lose the length of the
|
||||||
// list. The `.enumerate()` iterator reintroduces that count.
|
// list. The `.enumerate()` iterator reintroduces that count.
|
||||||
let mut points = points.enumerate();
|
let mut points = points.enumerate();
|
||||||
|
|
||||||
// Empty iterators have no points and so no center of mass.
|
// Empty iterators have no points and so no center of mass.
|
||||||
// Try to get the first one, but exit with None if it doesn't yield.
|
// Try to get the first one, but exit with None if it doesn't yield.
|
||||||
let init = points.next()?;
|
let init = points.next()?;
|
||||||
|
|
||||||
// if we get one, fold all the remaining values into it.
|
// if we get one, fold all the remaining values into it.
|
||||||
let (len, sum) = points.fold(
|
let (len, sum) = points.fold(init, |(len, sum), (idx, point)| {
|
||||||
init,
|
// replace length with most recent index
|
||||||
|(len, sum), (idx, point)| {
|
// add running sum & new point for new running sum
|
||||||
// replace length with most recent index
|
(idx, sum + point)
|
||||||
// add running sum & new point for new running sum
|
});
|
||||||
(idx, sum + point)
|
|
||||||
});
|
|
||||||
let avg = sum / ((len + 1) as f32);
|
let avg = sum / ((len + 1) as f32);
|
||||||
|
|
||||||
Some(avg)
|
Some(avg)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
use bevy::{prelude::*, sprite::MaterialMesh2dBundle, window::PrimaryWindow};
|
use bevy::{prelude::*, sprite::MaterialMesh2dBundle, window::PrimaryWindow};
|
||||||
use bevy_spatial::{
|
use bevy_spatial::{kdtree::KDTree2, SpatialAccess};
|
||||||
kdtree::KDTree2,
|
|
||||||
SpatialAccess,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::birdoids_plugin::{
|
use crate::birdoids_plugin::{
|
||||||
center_of_boids, velocity_of_boids, Acceleration, Boid, TrackedByKdTree, Velocity
|
center_of_boids, velocity_of_boids, Acceleration, Boid, TrackedByKdTree, Velocity,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SCANRADIUS: f32 = 50.0;
|
const SCANRADIUS: f32 = 50.0;
|
||||||
@@ -14,11 +11,7 @@ pub struct BoidsDebugPlugin;
|
|||||||
impl Plugin for BoidsDebugPlugin {
|
impl Plugin for BoidsDebugPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, setup)
|
app.add_systems(Startup, setup)
|
||||||
.add_systems(FixedUpdate, (
|
.add_systems(FixedUpdate, (update_cursor, update_scanner_mode, do_scan));
|
||||||
update_cursor,
|
|
||||||
update_scanner_mode,
|
|
||||||
do_scan,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +23,9 @@ fn setup(
|
|||||||
commands.spawn((
|
commands.spawn((
|
||||||
ScannerWidget::default(),
|
ScannerWidget::default(),
|
||||||
MaterialMesh2dBundle {
|
MaterialMesh2dBundle {
|
||||||
mesh: meshes.add(Annulus::new(SCANRADIUS - 1.0, SCANRADIUS)).into(),
|
mesh: meshes
|
||||||
|
.add(Annulus::new(SCANRADIUS - 1.0, SCANRADIUS))
|
||||||
|
.into(),
|
||||||
material: materials.add(Color::srgb(0.0, 0.0, 0.0)),
|
material: materials.add(Color::srgb(0.0, 0.0, 0.0)),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
@@ -96,11 +91,11 @@ fn update_cursor(
|
|||||||
fn update_scanner_mode(
|
fn update_scanner_mode(
|
||||||
keycodes: Res<ButtonInput<KeyCode>>,
|
keycodes: Res<ButtonInput<KeyCode>>,
|
||||||
mousebuttons: Res<ButtonInput<MouseButton>>,
|
mousebuttons: Res<ButtonInput<MouseButton>>,
|
||||||
mut scanner_query: Query<(&mut SelectionMode, &mut ScannerMode), With<Cursor>>,
|
mut scanner_query: Query<(&mut SelectionMode, &mut ScannerMode), With<Cursor>>,
|
||||||
) {
|
) {
|
||||||
// I'm making another assertion that there is exactly one scanner.
|
// I'm making another assertion that there is exactly one scanner.
|
||||||
let (mut select_mode, mut scan_mode) = scanner_query.get_single_mut().unwrap();
|
let (mut select_mode, mut scan_mode) = scanner_query.get_single_mut().unwrap();
|
||||||
|
|
||||||
// Assign selection mode
|
// Assign selection mode
|
||||||
if keycodes.just_pressed(KeyCode::Digit1) {
|
if keycodes.just_pressed(KeyCode::Digit1) {
|
||||||
*select_mode = SelectionMode::NearestSingle;
|
*select_mode = SelectionMode::NearestSingle;
|
||||||
@@ -116,9 +111,7 @@ fn update_scanner_mode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_gizmo_config(
|
fn print_gizmo_config(query: Query<(&SelectionMode, &ScannerMode), With<Cursor>>) {
|
||||||
query: Query<(&SelectionMode, &ScannerMode), With<Cursor>>,
|
|
||||||
) {
|
|
||||||
let (select, scan) = query.get_single().unwrap();
|
let (select, scan) = query.get_single().unwrap();
|
||||||
println!("Selection: {select:?}, Scanning: {scan:?}");
|
println!("Selection: {select:?}, Scanning: {scan:?}");
|
||||||
}
|
}
|
||||||
@@ -134,43 +127,34 @@ fn do_scan(
|
|||||||
match select_mode {
|
match select_mode {
|
||||||
SelectionMode::NearestSingle => todo!(),
|
SelectionMode::NearestSingle => todo!(),
|
||||||
SelectionMode::CircularArea => {
|
SelectionMode::CircularArea => {
|
||||||
let boids = spatial_tree.within_distance(
|
let boids = spatial_tree.within_distance(cursor_pos.translation.xy(), SCANRADIUS);
|
||||||
cursor_pos.translation.xy(),
|
|
||||||
SCANRADIUS,
|
|
||||||
);
|
|
||||||
match scan_mode {
|
match scan_mode {
|
||||||
ScannerMode::CenterOfMass => {
|
ScannerMode::CenterOfMass => {
|
||||||
if let Some(center_mass) = center_of_boids(
|
if let Some(center_mass) = center_of_boids(
|
||||||
// boids returns too many things.
|
// boids returns too many things.
|
||||||
// Map over it and extract only the Vec3's
|
// Map over it and extract only the Vec3's
|
||||||
boids.iter().map(|item| {
|
boids.iter().map(|item| item.0),
|
||||||
item.0
|
|
||||||
})
|
|
||||||
) {
|
) {
|
||||||
gizmos.circle_2d(
|
gizmos.circle_2d(center_mass, 1.0, bevy::color::palettes::css::RED);
|
||||||
center_mass,
|
|
||||||
1.0,
|
|
||||||
bevy::color::palettes::css::RED
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
ScannerMode::Velocity => {
|
ScannerMode::Velocity => {
|
||||||
if let Some(avg_velocity) = velocity_of_boids(
|
if let Some(avg_velocity) = velocity_of_boids(boids.iter().map(|item| {
|
||||||
boids.iter().map(|item| {
|
let entity_id = item.1.unwrap_or_else(|| panic!("Entity has no ID!"));
|
||||||
let entity_id = item.1.unwrap_or_else(|| panic!("Entity has no ID!"));
|
let (_, vel, _) = boids_query
|
||||||
let (_, vel, _) = boids_query.get(entity_id).unwrap_or_else(|_| panic!("Boid has no Velocity component!"));
|
.get(entity_id)
|
||||||
(*vel).xy() * 1.0
|
.unwrap_or_else(|_| panic!("Boid has no Velocity component!"));
|
||||||
})
|
(*vel).xy() * 1.0
|
||||||
) {
|
})) {
|
||||||
// cursor_pos.translation is already in world space, so I can skip the window -> world transform like in update_cursor()
|
// cursor_pos.translation is already in world space, so I can skip the window -> world transform like in update_cursor()
|
||||||
gizmos.line_2d(
|
gizmos.line_2d(
|
||||||
cursor_pos.translation.xy(),
|
cursor_pos.translation.xy(),
|
||||||
cursor_pos.translation.xy() + avg_velocity,
|
cursor_pos.translation.xy() + avg_velocity,
|
||||||
bevy::color::palettes::css::RED
|
bevy::color::palettes::css::RED,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user