1use crate::mesh::NavMesh;
14use crate::path::{astar_with, ResolvedLink};
15use crate::vec::NavVec;
16use crate::funnel;
17
18#[derive(Clone, Debug, PartialEq)]
20pub struct NavObstacle {
21 pub outline: Vec<[f32; 2]>,
23 pub enabled: bool,
25}
26
27impl NavObstacle {
28 pub fn new(outline: Vec<[f32; 2]>) -> Self {
30 Self { outline, enabled: true }
31 }
32
33 fn contains_point(&self, point: [f32; 2]) -> bool {
34 if !self.enabled || self.outline.len() < 3 {
35 return false;
36 }
37 let mut inside = false;
38 let count = self.outline.len();
39 let mut j = count - 1;
40 for i in 0..count {
41 let a = self.outline[i];
42 let b = self.outline[j];
43 if (a[1] > point[1]) != (b[1] > point[1]) {
44 let t = (point[1] - a[1]) / (b[1] - a[1]);
45 if point[0] < a[0] + t * (b[0] - a[0]) {
46 inside = !inside;
47 }
48 }
49 j = i;
50 }
51 inside
52 }
53}
54
55#[derive(Clone, Copy, Debug, PartialEq)]
57pub struct NavLink<V> {
58 pub start: V,
60 pub end: V,
62 pub bidirectional: bool,
64 pub cost: Option<f32>,
66 pub enabled: bool,
68}
69
70impl<V> NavLink<V> {
71 pub fn new(start: V, end: V) -> Self {
73 Self { start, end, bidirectional: false, cost: None, enabled: true }
74 }
75}
76
77#[derive(Clone, Debug)]
79pub struct NavMap<V: NavVec> {
80 pub mesh: NavMesh<V>,
82 pub obstacles: Vec<NavObstacle>,
84 pub links: Vec<NavLink<V>>,
86}
87
88impl<V: NavVec> NavMap<V> {
89 pub fn new(mesh: NavMesh<V>) -> Self {
91 Self { mesh, obstacles: Vec::new(), links: Vec::new() }
92 }
93
94 pub fn add_obstacle(&mut self, obstacle: NavObstacle) -> usize {
96 self.obstacles.push(obstacle);
97 self.obstacles.len() - 1
98 }
99
100 pub fn add_link(&mut self, link: NavLink<V>) -> usize {
102 self.links.push(link);
103 self.links.len() - 1
104 }
105
106 fn blocked_point(&self, point: V) -> bool {
107 let plane = point.nav2();
108 self.obstacles.iter().any(|o| o.contains_point(plane))
109 }
110
111 fn blocked_polygon(&self, polygon: u32) -> bool {
112 match self.mesh.polygon(polygon) {
113 Some(region) => self.blocked_point(region.center),
114 None => false,
115 }
116 }
117
118 fn blocked_portal(&self, portal: [V; 2]) -> bool {
119 let a = portal[0].nav2();
120 let b = portal[1].nav2();
121 let midpoint = V::from_nav2([(a[0] + b[0]) * 0.5, (a[1] + b[1]) * 0.5], 0.0);
122 self.blocked_point(midpoint)
123 }
124
125 fn resolve_links(&self) -> Vec<ResolvedLink<V>> {
126 let mut resolved = Vec::new();
127 for link in &self.links {
128 if !link.enabled || self.blocked_point(link.start) || self.blocked_point(link.end) {
129 continue;
130 }
131 let from = self.mesh.locate(link.start).or_else(|| self.mesh.closest_polygon(link.start));
132 let to = self.mesh.locate(link.end).or_else(|| self.mesh.closest_polygon(link.end));
133 let (Some(from), Some(to)) = (from, to) else {
134 continue;
135 };
136 if from == to {
137 continue;
138 }
139 let cost = link.cost.unwrap_or(link.start.distance(link.end));
140 resolved.push(ResolvedLink {
141 from,
142 to,
143 portal: [link.start, link.end],
144 cost,
145 bidirectional: link.bidirectional,
146 });
147 }
148 resolved
149 }
150
151 pub fn find_path(&self, from: V, to: V) -> Option<Vec<V>> {
155 let start_polygon = self.mesh.locate(from).or_else(|| self.mesh.closest_polygon(from))?;
156 let end_polygon = self.mesh.locate(to).or_else(|| self.mesh.closest_polygon(to))?;
157 let start = self.mesh.closest_point_on_polygon(start_polygon, from);
158 let end = self.mesh.closest_point_on_polygon(end_polygon, to);
159 if start_polygon == end_polygon {
160 return Some(vec![start, end]);
161 }
162 let links = self.resolve_links();
163 let (_, portals) = astar_with(
164 &self.mesh,
165 start_polygon,
166 end_polygon,
167 start,
168 end,
169 |polygon| self.blocked_polygon(polygon),
170 |portal| self.blocked_portal(portal),
171 &links,
172 )?;
173 let mut path = funnel::funnel(start, end, &portals);
174 funnel::dedup(&mut path);
175 Some(path)
176 }
177}