1use crate::vec::NavVec;
10use crate::NavError;
11use std::collections::HashMap;
12
13#[derive(Clone, Debug, PartialEq)]
15pub struct Polygon<V> {
16 pub indices: Vec<u32>,
18 pub center: V,
21 pub radius: f32,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct Neighbor {
28 pub polygon: u32,
30 pub edge: u32,
32}
33
34#[derive(Clone, Debug)]
39pub struct NavMesh<V: NavVec> {
40 pub vertices: Vec<V>,
42 pub polygons: Vec<Polygon<V>>,
44 neighbors: Vec<Vec<Option<Neighbor>>>,
45}
46
47impl<V: NavVec> NavMesh<V> {
48 pub fn from_polygons(vertices: Vec<V>, polygons: Vec<Vec<u32>>) -> Result<Self, NavError> {
53 if polygons.is_empty() {
54 return Err(NavError::EmptyRegion);
55 }
56 let mut regions = Vec::with_capacity(polygons.len());
57 for indices in polygons {
58 if indices.len() < 3 {
59 return Err(NavError::DegeneratePolygon);
60 }
61 let mut center = V::ZERO;
62 for &index in &indices {
63 let vertex = *vertices.get(index as usize).ok_or(NavError::BadIndex)?;
64 center = center.add(vertex);
65 }
66 center = center.mul(1.0 / indices.len() as f32);
67 let mut radius = 0.0f32;
68 for &index in &indices {
69 radius = radius.max(center.distance(vertices[index as usize]));
70 }
71 regions.push(Polygon { indices, center, radius });
72 }
73
74 let mut neighbors = vec![Vec::new(); regions.len()];
75 let mut directed: HashMap<(u32, u32), (u32, u32)> = HashMap::new();
76 for (polygon, region) in regions.iter().enumerate() {
77 let count = region.indices.len();
78 neighbors[polygon] = vec![None; count];
79 for edge in 0..count {
80 let a = region.indices[edge];
81 let b = region.indices[(edge + 1) % count];
82 directed.insert((a, b), (polygon as u32, edge as u32));
83 }
84 }
85 for (polygon, region) in regions.iter().enumerate() {
86 let count = region.indices.len();
87 for edge in 0..count {
88 let a = region.indices[edge];
89 let b = region.indices[(edge + 1) % count];
90 if let Some(&(other, other_edge)) = directed.get(&(b, a)) {
91 if other as usize != polygon {
92 neighbors[polygon][edge] = Some(Neighbor { polygon: other, edge: other_edge });
93 }
94 }
95 }
96 }
97
98 Ok(Self { vertices, polygons: regions, neighbors })
99 }
100
101 pub fn polygon_count(&self) -> usize {
103 self.polygons.len()
104 }
105
106 pub fn polygon(&self, index: u32) -> Option<&Polygon<V>> {
108 self.polygons.get(index as usize)
109 }
110
111 pub fn polygon_indices(&self, index: u32) -> Option<&[u32]> {
113 self.polygons.get(index as usize).map(|p| p.indices.as_slice())
114 }
115
116 pub fn neighbor(&self, polygon: u32, edge: u32) -> Option<Neighbor> {
118 self.neighbors.get(polygon as usize)?.get(edge as usize).copied().flatten()
119 }
120
121 pub fn portal(&self, polygon: u32, edge: u32) -> Option<[V; 2]> {
123 let region = self.polygons.get(polygon as usize)?;
124 let count = region.indices.len();
125 let a = *region.indices.get(edge as usize)?;
126 let b = region.indices[(edge as usize + 1) % count];
127 Some([self.vertices[a as usize], self.vertices[b as usize]])
128 }
129
130 pub fn locate(&self, point: V) -> Option<u32> {
133 let mut best: Option<(u32, f32)> = None;
134 for (index, region) in self.polygons.iter().enumerate() {
135 let region_vertices = self.region_vertices(region);
136 if point_in_polygon(®ion_vertices, point).is_none() {
137 continue;
138 }
139 let distance = region.center.distance(point);
140 match best {
141 Some((_, d)) if d <= distance => {}
142 _ => best = Some((index as u32, distance)),
143 }
144 }
145 best.map(|(index, _)| index)
146 }
147
148 pub fn closest_polygon(&self, point: V) -> Option<u32> {
150 let mut best: Option<(u32, f32)> = None;
151 for (index, region) in self.polygons.iter().enumerate() {
152 let candidate = closest_point_on_polygon(&self.vertices, region, point);
153 let distance = point.distance(candidate);
154 match best {
155 Some((_, d)) if d <= distance => {}
156 _ => best = Some((index as u32, distance)),
157 }
158 }
159 best.map(|(index, _)| index)
160 }
161
162 pub fn closest_point_on_polygon(&self, polygon: u32, point: V) -> V {
168 match self.polygons.get(polygon as usize) {
169 Some(region) => closest_point_on_polygon(&self.vertices, region, point),
170 None => point,
171 }
172 }
173
174 pub(crate) fn region_vertices(&self, region: &Polygon<V>) -> Vec<V> {
175 region
176 .indices
177 .iter()
178 .map(|&index| self.vertices[index as usize])
179 .collect()
180 }
181}
182
183pub(crate) fn barycentric(plane: [f32; 2], a: [f32; 2], b: [f32; 2], c: [f32; 2]) -> Option<[f32; 3]> {
186 let v0 = [b[0] - a[0], b[1] - a[1]];
187 let v1 = [c[0] - a[0], c[1] - a[1]];
188 let v2 = [plane[0] - a[0], plane[1] - a[1]];
189 let denom = v0[0] * v1[1] - v1[0] * v0[1];
190 if denom.abs() < 1e-12 {
191 return None;
192 }
193 let w1 = (v2[0] * v1[1] - v1[0] * v2[1]) / denom;
194 let w2 = (v0[0] * v2[1] - v2[0] * v0[1]) / denom;
195 let w0 = 1.0 - w1 - w2;
196 const EPS: f32 = 1e-5;
197 if w0 >= -EPS && w1 >= -EPS && w2 >= -EPS {
198 Some([w0, w1, w2])
199 } else {
200 None
201 }
202}
203
204pub(crate) fn point_in_polygon<V: NavVec>(vertices: &[V], point: V) -> Option<(usize, [f32; 3])> {
207 if vertices.len() < 3 {
208 return None;
209 }
210 let plane = point.nav2();
211 let a = vertices[0].nav2();
212 for i in 1..vertices.len() - 1 {
213 let b = vertices[i].nav2();
214 let c = vertices[i + 1].nav2();
215 if let Some(weights) = barycentric(plane, a, b, c) {
216 return Some((i, weights));
217 }
218 }
219 None
220}
221
222pub(crate) fn closest_on_segment<V: NavVec>(point: V, a: V, b: V) -> V {
224 let ab = b.sub(a);
225 let denom = ab.dot(ab);
226 if denom <= 1e-20 {
227 return a;
228 }
229 let t = (point.sub(a).dot(ab) / denom).clamp(0.0, 1.0);
230 a.add(ab.mul(t))
231}
232
233pub(crate) fn closest_point_on_polygon<V: NavVec>(
234 vertices: &[V],
235 region: &Polygon<V>,
236 point: V,
237) -> V {
238 let region_vertices: Vec<V> =
239 region.indices.iter().map(|&i| vertices[i as usize]).collect();
240 if let Some((triangle, weights)) = point_in_polygon(®ion_vertices, point) {
241 let a = region_vertices[0].mul(weights[0]);
242 let b = region_vertices[triangle].mul(weights[1]);
243 let c = region_vertices[triangle + 1].mul(weights[2]);
244 return a.add(b).add(c);
245 }
246 let mut best = region_vertices[0];
247 let mut best_distance = point.distance(best);
248 for i in 0..region_vertices.len() {
249 let a = region_vertices[i];
250 let b = region_vertices[(i + 1) % region_vertices.len()];
251 let candidate = closest_on_segment(point, a, b);
252 let distance = point.distance(candidate);
253 if distance < best_distance {
254 best_distance = distance;
255 best = candidate;
256 }
257 }
258 best
259}