Update force functions to use Option<Force>
This commit is contained in:
@@ -51,7 +51,7 @@ struct PlayerBoid;
|
|||||||
#[derive(Component, Deref, DerefMut)]
|
#[derive(Component, Deref, DerefMut)]
|
||||||
pub(crate) struct Velocity(Vec3);
|
pub(crate) struct Velocity(Vec3);
|
||||||
|
|
||||||
#[derive(Component, Deref, DerefMut, PartialEq, Debug)]
|
#[derive(Component, Default, Deref, DerefMut, PartialEq, Debug)]
|
||||||
pub(crate) struct Force(Vec3);
|
pub(crate) struct Force(Vec3);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@@ -197,7 +197,10 @@ fn cohesion(
|
|||||||
for (transform, mut acceleration) in &mut boids {
|
for (transform, mut acceleration) in &mut boids {
|
||||||
let neighbors = spatial_tree.within_distance(transform.translation.xy(), BOID_VIEW_RANGE);
|
let neighbors = spatial_tree.within_distance(transform.translation.xy(), BOID_VIEW_RANGE);
|
||||||
if let Some(center_mass) = center_of_boids(neighbors.iter().map(|boid| boid.0)) {
|
if let Some(center_mass) = center_of_boids(neighbors.iter().map(|boid| boid.0)) {
|
||||||
let force = cohesive_force(center_mass, transform.translation.xy());
|
let force = cohesive_force(
|
||||||
|
center_mass,
|
||||||
|
transform.translation.xy()
|
||||||
|
).unwrap_or_default();
|
||||||
acceleration.0 += *force * COHESION_FACTOR;
|
acceleration.0 += *force * COHESION_FACTOR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,7 +220,10 @@ fn separation(
|
|||||||
let accel = neighbors.iter().map(|(pos, _)| pos.extend(0.0)).fold(
|
let accel = neighbors.iter().map(|(pos, _)| pos.extend(0.0)).fold(
|
||||||
Vec3::ZERO,
|
Vec3::ZERO,
|
||||||
|accumulator, neighbor| {
|
|accumulator, neighbor| {
|
||||||
let force = separation_force(boid_transform.translation.xy(), neighbor.xy());
|
let force = separation_force(
|
||||||
|
boid_transform.translation.xy(),
|
||||||
|
neighbor.xy()
|
||||||
|
).unwrap_or_default();
|
||||||
accumulator + *force * SEPARATION_FACTOR
|
accumulator + *force * SEPARATION_FACTOR
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -292,7 +298,7 @@ fn average_of_vec2s(points: impl Iterator<Item = Vec2>) -> Option<Vec2> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// f(x) = 4((x-0.5)^3 + 0.125)
|
// f(x) = 4((x-0.5)^3 + 0.125)
|
||||||
fn cohesive_force(boid: Vec2, target: Vec2) -> Force {
|
fn cohesive_force(boid: Vec2, target: Vec2) -> Option<Force> {
|
||||||
let deviation = target - boid;
|
let deviation = target - boid;
|
||||||
/*
|
/*
|
||||||
Scale deviation vector by the boid's view range. The curve is made to
|
Scale deviation vector by the boid's view range. The curve is made to
|
||||||
@@ -300,6 +306,7 @@ fn cohesive_force(boid: Vec2, target: Vec2) -> Force {
|
|||||||
*/
|
*/
|
||||||
let scaled = deviation / BOID_VIEW_RANGE;
|
let scaled = deviation / BOID_VIEW_RANGE;
|
||||||
let mag = scaled.length();
|
let mag = scaled.length();
|
||||||
|
if mag > 0.0 {
|
||||||
let cube: f32 = (mag - 0.5).powf(3.0);
|
let cube: f32 = (mag - 0.5).powf(3.0);
|
||||||
let offset = cube + 0.125;
|
let offset = cube + 0.125;
|
||||||
let mul = offset * 4.0;
|
let mul = offset * 4.0;
|
||||||
@@ -307,17 +314,24 @@ fn cohesive_force(boid: Vec2, target: Vec2) -> Force {
|
|||||||
// This is because it needs to be a unit vector before getting a new
|
// This is because it needs to be a unit vector before getting a new
|
||||||
// magnitude assigned.
|
// magnitude assigned.
|
||||||
let force_vec = mul * scaled.normalize();
|
let force_vec = mul * scaled.normalize();
|
||||||
Force(force_vec.extend(0.0))
|
Some(Force(force_vec.extend(0.0)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// f(x) = x^2 - 1
|
// f(x) = x^2 - 1
|
||||||
fn separation_force(boid: Vec2, target: Vec2) -> Force {
|
fn separation_force(boid: Vec2, target: Vec2) -> Option<Force> {
|
||||||
// Scale from BOID_VIEW_RANGE to unit space
|
// Scale from BOID_VIEW_RANGE to unit space
|
||||||
let distance_unit = (target - boid) / BOID_VIEW_RANGE;
|
let distance_unit = (target - boid) / BOID_VIEW_RANGE;
|
||||||
let mag = distance_unit.length();
|
let mag = distance_unit.length();
|
||||||
|
if mag > 0.0 {
|
||||||
let force_mag = mag.powf(2.0) - 1.0;
|
let force_mag = mag.powf(2.0) - 1.0;
|
||||||
let force = force_mag * distance_unit.normalize();
|
let force = force_mag * distance_unit.normalize();
|
||||||
Force(force.extend(0.0))
|
Some(Force(force.extend(0.0)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -339,8 +353,7 @@ mod tests{
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_cohesion_zero_zero() {
|
fn check_cohesion_zero_zero() {
|
||||||
let force = cohesive_force(Vec2::ZERO, Vec2::ZERO);
|
let force = cohesive_force(Vec2::ZERO, Vec2::ZERO);
|
||||||
eprintln!("Cohesive force of overlapping points: {}", *force);
|
assert!(force.is_none());
|
||||||
panic!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// *********************
|
// *********************
|
||||||
@@ -351,7 +364,7 @@ mod tests{
|
|||||||
fn check_cohesion_midpoint_x_positive(){
|
fn check_cohesion_midpoint_x_positive(){
|
||||||
// Pull right 0.5 units
|
// Pull right 0.5 units
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(0.5, 0.0, 0.0)),
|
Some(Force(Vec3::new(0.5, 0.0, 0.0))),
|
||||||
cohesive_force(
|
cohesive_force(
|
||||||
Vec2::new(0.0, 0.0),
|
Vec2::new(0.0, 0.0),
|
||||||
Vec2::new(0.5 * BOID_VIEW_RANGE, 0.0),
|
Vec2::new(0.5 * BOID_VIEW_RANGE, 0.0),
|
||||||
@@ -363,7 +376,7 @@ mod tests{
|
|||||||
fn check_cohesion_midpoint_x_negative(){
|
fn check_cohesion_midpoint_x_negative(){
|
||||||
// Pull left 0.5 units
|
// Pull left 0.5 units
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(-0.5, 0.0, 0.0)),
|
Some(Force(Vec3::new(-0.5, 0.0, 0.0))),
|
||||||
cohesive_force(
|
cohesive_force(
|
||||||
Vec2::new(0.0, 0.0),
|
Vec2::new(0.0, 0.0),
|
||||||
Vec2::new(-0.5 * BOID_VIEW_RANGE, 0.0),
|
Vec2::new(-0.5 * BOID_VIEW_RANGE, 0.0),
|
||||||
@@ -375,7 +388,7 @@ mod tests{
|
|||||||
fn check_cohesion_edge_x_positive() {
|
fn check_cohesion_edge_x_positive() {
|
||||||
// pull left 1.0 units
|
// pull left 1.0 units
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(1.0, 0.0, 0.0)),
|
Some(Force(Vec3::new(1.0, 0.0, 0.0))),
|
||||||
cohesive_force(
|
cohesive_force(
|
||||||
Vec2::new(0.0, 0.0),
|
Vec2::new(0.0, 0.0),
|
||||||
Vec2::new(1.0 * BOID_VIEW_RANGE, 0.0),
|
Vec2::new(1.0 * BOID_VIEW_RANGE, 0.0),
|
||||||
@@ -387,7 +400,7 @@ mod tests{
|
|||||||
fn check_cohesion_edge_x_negative() {
|
fn check_cohesion_edge_x_negative() {
|
||||||
// pull left 1.0 units
|
// pull left 1.0 units
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(-1.0, 0.0, 0.0)),
|
Some(Force(Vec3::new(-1.0, 0.0, 0.0))),
|
||||||
cohesive_force(
|
cohesive_force(
|
||||||
Vec2::new(0.0, 0.0),
|
Vec2::new(0.0, 0.0),
|
||||||
Vec2::new(-1.0 * BOID_VIEW_RANGE, 0.0),
|
Vec2::new(-1.0 * BOID_VIEW_RANGE, 0.0),
|
||||||
@@ -403,7 +416,7 @@ mod tests{
|
|||||||
fn check_cohesion_midpoint_y_positive(){
|
fn check_cohesion_midpoint_y_positive(){
|
||||||
// Pull up 0.5 units
|
// Pull up 0.5 units
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(0.0, 0.5, 0.0)),
|
Some(Force(Vec3::new(0.0, 0.5, 0.0))),
|
||||||
cohesive_force(
|
cohesive_force(
|
||||||
Vec2::new(0.0, 0.0),
|
Vec2::new(0.0, 0.0),
|
||||||
Vec2::new(0.0, 0.5 * BOID_VIEW_RANGE),
|
Vec2::new(0.0, 0.5 * BOID_VIEW_RANGE),
|
||||||
@@ -415,7 +428,7 @@ mod tests{
|
|||||||
fn check_cohesion_midpoint_y_negative(){
|
fn check_cohesion_midpoint_y_negative(){
|
||||||
// Pull down 0.5 units
|
// Pull down 0.5 units
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(0.0, -0.5, 0.0)),
|
Some(Force(Vec3::new(0.0, -0.5, 0.0))),
|
||||||
cohesive_force(
|
cohesive_force(
|
||||||
Vec2::new(0.0, 0.0),
|
Vec2::new(0.0, 0.0),
|
||||||
Vec2::new(0.0, -0.5 * BOID_VIEW_RANGE),
|
Vec2::new(0.0, -0.5 * BOID_VIEW_RANGE),
|
||||||
@@ -427,7 +440,7 @@ mod tests{
|
|||||||
fn check_cohesion_edge_y_positive() {
|
fn check_cohesion_edge_y_positive() {
|
||||||
// Pull up 1.0 units
|
// Pull up 1.0 units
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(0.0, 1.0, 0.0)),
|
Some(Force(Vec3::new(0.0, 1.0, 0.0))),
|
||||||
cohesive_force(
|
cohesive_force(
|
||||||
Vec2::new(0.0, 0.0),
|
Vec2::new(0.0, 0.0),
|
||||||
Vec2::new(0.0, 1.0 * BOID_VIEW_RANGE)
|
Vec2::new(0.0, 1.0 * BOID_VIEW_RANGE)
|
||||||
@@ -439,7 +452,7 @@ mod tests{
|
|||||||
fn check_cohesion_edge_y_negative() {
|
fn check_cohesion_edge_y_negative() {
|
||||||
// pull down 0.2 units
|
// pull down 0.2 units
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(0.0, -1.0, 0.0)),
|
Some(Force(Vec3::new(0.0, -1.0, 0.0))),
|
||||||
cohesive_force(
|
cohesive_force(
|
||||||
Vec2::new(0.0, 0.0),
|
Vec2::new(0.0, 0.0),
|
||||||
Vec2::new(0.0, -1.0 * BOID_VIEW_RANGE),
|
Vec2::new(0.0, -1.0 * BOID_VIEW_RANGE),
|
||||||
@@ -454,8 +467,7 @@ mod tests{
|
|||||||
Vec2::ZERO,
|
Vec2::ZERO,
|
||||||
Vec2::ZERO
|
Vec2::ZERO
|
||||||
);
|
);
|
||||||
eprintln!("Separation force of overlapping points: {}", *force);
|
assert!(force.is_none());
|
||||||
panic!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// *********************
|
// *********************
|
||||||
@@ -464,7 +476,7 @@ mod tests{
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_separation_midpoint_x_positive(){
|
fn check_separation_midpoint_x_positive(){
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(0.75, 0.0, 0.0)), // expected force
|
Some(Force(Vec3::new(0.75, 0.0, 0.0))), // expected force
|
||||||
separation_force(
|
separation_force(
|
||||||
Vec2::new(0.5 * BOID_VIEW_RANGE, 0.0), // boid position
|
Vec2::new(0.5 * BOID_VIEW_RANGE, 0.0), // boid position
|
||||||
Vec2::ZERO, // obstacle position
|
Vec2::ZERO, // obstacle position
|
||||||
@@ -475,7 +487,7 @@ mod tests{
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_separation_midpoint_x_negative(){
|
fn check_separation_midpoint_x_negative(){
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(-0.75, 0.0, 0.0)), // expected force
|
Some(Force(Vec3::new(-0.75, 0.0, 0.0))), // expected force
|
||||||
separation_force(
|
separation_force(
|
||||||
Vec2::new(-0.5 * BOID_VIEW_RANGE, 0.0), // boid position
|
Vec2::new(-0.5 * BOID_VIEW_RANGE, 0.0), // boid position
|
||||||
Vec2::ZERO, // obstacle position
|
Vec2::ZERO, // obstacle position
|
||||||
@@ -486,7 +498,7 @@ mod tests{
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_separation_edge_x_positive() {
|
fn check_separation_edge_x_positive() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::ZERO),
|
Some(Force(Vec3::ZERO)),
|
||||||
separation_force(
|
separation_force(
|
||||||
Vec2::new(1.0 * BOID_VIEW_RANGE, 0.0),
|
Vec2::new(1.0 * BOID_VIEW_RANGE, 0.0),
|
||||||
Vec2::ZERO,
|
Vec2::ZERO,
|
||||||
@@ -497,7 +509,7 @@ mod tests{
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_separation_edge_x_negative() {
|
fn check_separation_edge_x_negative() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::ZERO),
|
Some(Force(Vec3::ZERO)),
|
||||||
separation_force(
|
separation_force(
|
||||||
Vec2::new(-1.0 * BOID_VIEW_RANGE, 0.0),
|
Vec2::new(-1.0 * BOID_VIEW_RANGE, 0.0),
|
||||||
Vec2::ZERO,
|
Vec2::ZERO,
|
||||||
@@ -511,7 +523,7 @@ mod tests{
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_separation_midpoint_y_positive(){
|
fn check_separation_midpoint_y_positive(){
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(0.0, 0.75, 0.0)),
|
Some(Force(Vec3::new(0.0, 0.75, 0.0))),
|
||||||
separation_force(
|
separation_force(
|
||||||
Vec2::new(0.0, 0.5 * BOID_VIEW_RANGE),
|
Vec2::new(0.0, 0.5 * BOID_VIEW_RANGE),
|
||||||
Vec2::ZERO,
|
Vec2::ZERO,
|
||||||
@@ -522,7 +534,7 @@ mod tests{
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_separation_midpoint_y_negative(){
|
fn check_separation_midpoint_y_negative(){
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::new(0.0, -0.75, 0.0)),
|
Some(Force(Vec3::new(0.0, -0.75, 0.0))),
|
||||||
separation_force(
|
separation_force(
|
||||||
Vec2::new(0.0, -0.5 * BOID_VIEW_RANGE),
|
Vec2::new(0.0, -0.5 * BOID_VIEW_RANGE),
|
||||||
Vec2::ZERO,
|
Vec2::ZERO,
|
||||||
@@ -533,7 +545,7 @@ mod tests{
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_separation_edge_y_positive() {
|
fn check_separation_edge_y_positive() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::ZERO),
|
Some(Force(Vec3::ZERO)),
|
||||||
separation_force(
|
separation_force(
|
||||||
Vec2::new(0.0, 1.0 * BOID_VIEW_RANGE),
|
Vec2::new(0.0, 1.0 * BOID_VIEW_RANGE),
|
||||||
Vec2::ZERO,
|
Vec2::ZERO,
|
||||||
@@ -544,7 +556,7 @@ mod tests{
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_separation_edge_y_negative() {
|
fn check_separation_edge_y_negative() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Force(Vec3::ZERO),
|
Some(Force(Vec3::ZERO)),
|
||||||
separation_force(
|
separation_force(
|
||||||
Vec2::new(0.0, -1.0 * BOID_VIEW_RANGE),
|
Vec2::new(0.0, -1.0 * BOID_VIEW_RANGE),
|
||||||
Vec2::ZERO,
|
Vec2::ZERO,
|
||||||
|
|||||||
Reference in New Issue
Block a user