use crate::avoid::{resolve, AvoidanceConfig, Neighbor2};
use crate::vec::NavVec;
#[derive(Clone, Copy, Debug)]
pub struct AgentNeighbor<V> {
pub position: V,
pub velocity: V,
pub radius: f32,
}
#[derive(Clone, Debug)]
pub struct NavigationAgent<V: NavVec> {
pub path: Vec<V>,
pub waypoint: usize,
pub max_speed: f32,
pub arrival_radius: f32,
pub waypoint_advance: f32,
pub desired_velocity: V,
pub avoidance: AvoidanceConfig,
pub avoidance_enabled: bool,
}
impl<V: NavVec> NavigationAgent<V> {
pub fn new(max_speed: f32) -> Self {
Self {
path: Vec::new(),
waypoint: 0,
max_speed,
arrival_radius: 0.5,
waypoint_advance: 0.3,
desired_velocity: V::ZERO,
avoidance: AvoidanceConfig::default(),
avoidance_enabled: false,
}
}
pub fn set_path(&mut self, path: Vec<V>) {
self.waypoint = 0;
self.path = path;
self.desired_velocity = V::ZERO;
}
pub fn clear_path(&mut self) {
self.path.clear();
self.waypoint = 0;
self.desired_velocity = V::ZERO;
}
pub fn target(&self) -> Option<V> {
self.path.last().copied()
}
pub fn is_finished(&self, position: V) -> bool {
match self.target() {
Some(target) => position.distance(target) <= self.arrival_radius,
None => true,
}
}
pub fn advance(&mut self, position: V) {
while self.waypoint + 1 < self.path.len()
&& position.distance(self.path[self.waypoint]) <= self.waypoint_advance
{
self.waypoint += 1;
}
}
pub fn desired_velocity(&mut self, position: V) -> V {
self.advance(position);
if self.path.is_empty() {
self.desired_velocity = V::ZERO;
return V::ZERO;
}
let waypoint = self.path[self.waypoint];
let distance = position.distance(waypoint);
let is_final = self.waypoint + 1 >= self.path.len();
if is_final && distance <= self.arrival_radius {
self.desired_velocity = V::ZERO;
return V::ZERO;
}
let direction = waypoint.sub(position).normalized();
let speed = if is_final {
let span = (distance - self.arrival_radius).max(0.0) / self.arrival_radius.max(1e-4);
self.max_speed * span.min(1.0)
} else {
self.max_speed
};
self.desired_velocity = direction.mul(speed);
self.desired_velocity
}
pub fn avoidance_velocity(&self, position: V, neighbors: &[AgentNeighbor<V>]) -> V {
if !self.avoidance_enabled || neighbors.is_empty() {
return self.desired_velocity;
}
let plane: Vec<Neighbor2> = neighbors
.iter()
.map(|n| {
let p = n.position.nav2();
let v = n.velocity.nav2();
Neighbor2 {
position: glam::Vec2::new(p[0], p[1]),
velocity: glam::Vec2::new(v[0], v[1]),
radius: n.radius,
}
})
.collect();
let desired = self.desired_velocity.nav2();
let resolved = resolve(
glam::Vec2::new(desired[0], desired[1]),
glam::Vec2::new(position.nav2()[0], position.nav2()[1]),
&plane,
&self.avoidance,
);
V::from_nav2([resolved.x, resolved.y], self.desired_velocity.height())
}
}