1use crate::{
2 resource::{NavVec3, ZERO_TRESHOLD},
3 Scalar,
4};
5use core::id::ID;
6use petgraph::{algo::astar, graph::NodeIndex, visit::EdgeRef, Graph, Undirected};
7#[cfg(feature = "parallel")]
8use rayon::prelude::*;
9use serde::{Deserialize, Serialize};
10use spade::{rtree::RTree, BoundingRect, SpatialObject};
11use std::{
12 collections::HashMap,
13 hash::{Hash, Hasher},
14 result::Result as StdResult,
15};
16
17#[cfg(feature = "parallel")]
18macro_rules! iter {
19 ($v:expr) => {
20 $v.par_iter()
21 };
22}
23#[cfg(not(feature = "parallel"))]
24macro_rules! iter {
25 ($v:expr) => {
26 $v.iter()
27 };
28}
29#[cfg(feature = "parallel")]
30macro_rules! into_iter {
31 ($v:expr) => {
32 $v.into_par_iter()
33 };
34}
35#[cfg(not(feature = "parallel"))]
36macro_rules! into_iter {
37 ($v:expr) => {
38 $v.into_iter()
39 };
40}
41
42pub type NavMeshID = ID<NavMesh>;
44
45#[derive(Debug, Clone)]
47pub enum Error {
48 TriangleVerticeIndexOutOfBounds(u32, u8, u32),
51}
52
53pub type NavResult<T> = StdResult<T, Error>;
55
56#[repr(C)]
58#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
59pub struct NavTriangle {
60 pub first: u32,
61 pub second: u32,
62 pub third: u32,
63}
64
65impl From<(u32, u32, u32)> for NavTriangle {
66 fn from(value: (u32, u32, u32)) -> Self {
67 Self {
68 first: value.0,
69 second: value.1,
70 third: value.2,
71 }
72 }
73}
74
75#[repr(C)]
77#[derive(Debug, Default, Clone, Serialize, Deserialize)]
78pub struct NavArea {
79 pub triangle: u32,
81 pub size: Scalar,
83 pub cost: Scalar,
86 pub center: NavVec3,
88 pub radius: Scalar,
90 pub radius_sqr: Scalar,
92}
93
94impl NavArea {
95 #[inline]
102 pub fn calculate_area(a: NavVec3, b: NavVec3, c: NavVec3) -> Scalar {
103 let ab = b - a;
104 let ac = c - a;
105 ab.cross(ac).magnitude() * 0.5
106 }
107
108 #[inline]
115 pub fn calculate_center(a: NavVec3, b: NavVec3, c: NavVec3) -> NavVec3 {
116 let v = a + b + c;
117 NavVec3::new(v.x / 3.0, v.y / 3.0, v.z / 3.0)
118 }
119}
120
121#[derive(Debug, Default, Copy, Clone, Eq, Serialize, Deserialize)]
122struct NavConnection(pub u32, pub u32);
123
124impl Hash for NavConnection {
125 fn hash<H: Hasher>(&self, state: &mut H) {
126 let first = self.0.min(self.1);
127 let second = self.0.max(self.1);
128 first.hash(state);
129 second.hash(state);
130 }
131}
132
133impl PartialEq for NavConnection {
134 fn eq(&self, other: &Self) -> bool {
135 let first = self.0.min(self.1);
136 let second = self.0.max(self.1);
137 let ofirst = other.0.min(other.1);
138 let osecond = other.0.max(other.1);
139 first == ofirst && second == osecond
140 }
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub(crate) struct NavSpatialObject {
145 pub index: usize,
146 pub a: NavVec3,
147 pub b: NavVec3,
148 pub c: NavVec3,
149 ab: NavVec3,
150 bc: NavVec3,
151 ca: NavVec3,
152 normal: NavVec3,
153 dab: NavVec3,
154 dbc: NavVec3,
155 dca: NavVec3,
156}
157
158impl NavSpatialObject {
159 pub fn new(index: usize, a: NavVec3, b: NavVec3, c: NavVec3) -> Self {
160 let ab = b - a;
161 let bc = c - b;
162 let ca = a - c;
163 let normal = (a - b).cross(a - c).normalize();
164 let dab = normal.cross(ab);
165 let dbc = normal.cross(bc);
166 let dca = normal.cross(ca);
167 Self {
168 index,
169 a,
170 b,
171 c,
172 ab,
173 bc,
174 ca,
175 normal,
176 dab,
177 dbc,
178 dca,
179 }
180 }
181
182 #[inline]
183 pub fn normal(&self) -> NavVec3 {
184 self.normal
185 }
186
187 pub fn closest_point(&self, point: NavVec3) -> NavVec3 {
188 let pab = point.project(self.a, self.b);
189 let pbc = point.project(self.b, self.c);
190 let pca = point.project(self.c, self.a);
191 if pca > 1.0 && pab < 0.0 {
192 return self.a;
193 } else if pab > 1.0 && pbc < 0.0 {
194 return self.b;
195 } else if pbc > 1.0 && pca < 0.0 {
196 return self.c;
197 } else if pab >= 0.0 && pab <= 1.0 && !point.is_above_plane(self.a, self.dab) {
198 return NavVec3::unproject(self.a, self.b, pab);
199 } else if pbc >= 0.0 && pbc <= 1.0 && !point.is_above_plane(self.b, self.dbc) {
200 return NavVec3::unproject(self.b, self.c, pbc);
201 } else if pca >= 0.0 && pca <= 1.0 && !point.is_above_plane(self.c, self.dca) {
202 return NavVec3::unproject(self.c, self.a, pca);
203 }
204 point.project_on_plane(self.a, self.normal)
205 }
206}
207
208impl SpatialObject for NavSpatialObject {
209 type Point = NavVec3;
210
211 fn mbr(&self) -> BoundingRect<Self::Point> {
212 let min = NavVec3::new(
213 self.a.x.min(self.b.x).min(self.c.x),
214 self.a.y.min(self.b.y).min(self.c.y),
215 self.a.z.min(self.b.z).min(self.c.z),
216 );
217 let max = NavVec3::new(
218 self.a.x.max(self.b.x).max(self.c.x),
219 self.a.y.max(self.b.y).max(self.c.y),
220 self.a.z.max(self.b.z).max(self.c.z),
221 );
222 BoundingRect::from_corners(&min, &max)
223 }
224
225 fn distance2(&self, point: &Self::Point) -> Scalar {
226 (*point - self.closest_point(*point)).sqr_magnitude()
227 }
228}
229
230#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
232pub enum NavQuery {
233 Accuracy,
235 Closest,
237 ClosestFirst,
239}
240
241#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
243pub enum NavPathMode {
244 Accuracy,
246 MidPoints,
248}
249
250#[derive(Debug, Default)]
252pub struct NavMeshesRes(pub(crate) HashMap<NavMeshID, NavMesh>);
253
254impl NavMeshesRes {
255 #[inline]
263 pub fn register(&mut self, mesh: NavMesh) -> NavMeshID {
264 let id = mesh.id();
265 self.0.insert(id, mesh);
266 id
267 }
268
269 #[inline]
277 pub fn unregister(&mut self, id: NavMeshID) -> Option<NavMesh> {
278 self.0.remove(&id)
279 }
280
281 #[inline]
283 pub fn unregister_all(&mut self) {
284 self.0.clear()
285 }
286
287 #[inline]
289 pub fn meshes_iter(&self) -> impl Iterator<Item = &NavMesh> {
290 self.0.values()
291 }
292
293 #[inline]
301 pub fn find_mesh(&self, id: NavMeshID) -> Option<&NavMesh> {
302 self.0.get(&id)
303 }
304
305 #[inline]
313 pub fn find_mesh_mut(&mut self, id: NavMeshID) -> Option<&mut NavMesh> {
314 self.0.get_mut(&id)
315 }
316
317 pub fn closest_point(&self, point: NavVec3, query: NavQuery) -> Option<(NavMeshID, NavVec3)> {
326 iter!(self.0)
327 .filter_map(|(id, mesh)| {
328 mesh.closest_point(point, query)
329 .map(|p| (p, (p - point).sqr_magnitude(), *id))
330 })
331 .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
332 .map(|(p, _, id)| (id, p))
333 }
334}
335
336#[derive(Debug, Default, Clone)]
338pub struct NavMesh {
339 id: NavMeshID,
340 vertices: Vec<NavVec3>,
341 triangles: Vec<NavTriangle>,
342 areas: Vec<NavArea>,
343 connections: HashMap<NavConnection, (Scalar, NavConnection)>,
345 graph: Graph<(), Scalar, Undirected>,
346 nodes: Vec<NodeIndex>,
347 nodes_map: HashMap<NodeIndex, usize>,
348 rtree: RTree<NavSpatialObject>,
349 spatials: Vec<NavSpatialObject>,
350 hard_edges: HashMap<usize, Vec<(NavVec3, NavVec3)>>,
352}
353
354impl NavMesh {
355 pub fn new(vertices: Vec<NavVec3>, triangles: Vec<NavTriangle>) -> NavResult<Self> {
387 let areas = iter!(triangles)
388 .enumerate()
389 .map(|(i, triangle)| {
390 if triangle.first >= vertices.len() as u32 {
391 return Err(Error::TriangleVerticeIndexOutOfBounds(
392 i as u32,
393 0,
394 triangle.first,
395 ));
396 }
397 if triangle.second >= vertices.len() as u32 {
398 return Err(Error::TriangleVerticeIndexOutOfBounds(
399 i as u32,
400 1,
401 triangle.second,
402 ));
403 }
404 if triangle.third >= vertices.len() as u32 {
405 return Err(Error::TriangleVerticeIndexOutOfBounds(
406 i as u32,
407 2,
408 triangle.third,
409 ));
410 }
411 let first = vertices[triangle.first as usize];
412 let second = vertices[triangle.second as usize];
413 let third = vertices[triangle.third as usize];
414 let center = NavArea::calculate_center(first, second, third);
415 let radius = (first - center)
416 .magnitude()
417 .max((second - center).magnitude())
418 .max((third - center).magnitude());
419 Ok(NavArea {
420 triangle: i as u32,
421 size: NavArea::calculate_area(first, second, third),
422 cost: 1.0,
423 center,
424 radius,
425 radius_sqr: radius * radius,
426 })
427 })
428 .collect::<NavResult<Vec<_>>>()?;
429
430 let mut edges = HashMap::<NavConnection, Vec<usize>>::with_capacity(triangles.len() * 3);
432 for (index, triangle) in triangles.iter().enumerate() {
433 let edge_a = NavConnection(triangle.first, triangle.second);
434 let edge_b = NavConnection(triangle.second, triangle.third);
435 let edge_c = NavConnection(triangle.third, triangle.first);
436 if let Some(tris) = edges.get_mut(&edge_a) {
437 tris.push(index);
438 } else {
439 edges.insert(edge_a, vec![index]);
440 }
441 if let Some(tris) = edges.get_mut(&edge_b) {
442 tris.push(index);
443 } else {
444 edges.insert(edge_b, vec![index]);
445 }
446 if let Some(tris) = edges.get_mut(&edge_c) {
447 tris.push(index);
448 } else {
449 edges.insert(edge_c, vec![index]);
450 }
451 }
452
453 let connections = into_iter!(iter!(edges)
454 .flat_map(|(verts, tris)| {
455 let mut result = HashMap::with_capacity(tris.len() * tris.len());
456 for a in tris {
457 for b in tris {
458 if a != b {
459 result.insert(NavConnection(*a as u32, *b as u32), *verts);
460 }
461 }
462 }
463 result
464 })
465 .collect::<HashMap<_, _>>())
466 .map(|(tri_conn, vert_conn)| {
467 let a = areas[tri_conn.0 as usize].center;
468 let b = areas[tri_conn.1 as usize].center;
469 let weight = (b - a).sqr_magnitude();
470 (tri_conn, (weight, vert_conn))
471 })
472 .collect::<HashMap<_, _>>();
473
474 let mut graph = Graph::<(), Scalar, Undirected>::new_undirected();
475 let nodes = (0..triangles.len())
476 .map(|_| graph.add_node(()))
477 .collect::<Vec<_>>();
478 graph.extend_with_edges(
479 connections
480 .iter()
481 .map(|(conn, (w, _))| (nodes[conn.0 as usize], nodes[conn.1 as usize], w)),
482 );
483 let nodes_map = iter!(nodes).enumerate().map(|(i, n)| (*n, i)).collect();
484
485 let spatials = iter!(triangles)
486 .enumerate()
487 .map(|(index, triangle)| {
488 NavSpatialObject::new(
489 index,
490 vertices[triangle.first as usize],
491 vertices[triangle.second as usize],
492 vertices[triangle.third as usize],
493 )
494 })
495 .collect::<Vec<_>>();
496
497 let mut rtree = RTree::new();
498 for spatial in &spatials {
499 rtree.insert(spatial.clone());
500 }
501
502 let hard_edges = iter!(triangles)
503 .enumerate()
504 .filter_map(|(index, triangle)| {
505 let edge_a = NavConnection(triangle.first, triangle.second);
506 let edge_b = NavConnection(triangle.second, triangle.third);
507 let edge_c = NavConnection(triangle.third, triangle.first);
508 let mut planes = vec![];
509 if edges[&edge_a].len() < 2 {
510 planes.push((
511 vertices[triangle.first as usize],
512 vertices[triangle.second as usize],
513 ));
514 }
515 if edges[&edge_b].len() < 2 {
516 planes.push((
517 vertices[triangle.second as usize],
518 vertices[triangle.third as usize],
519 ));
520 }
521 if edges[&edge_c].len() < 2 {
522 planes.push((
523 vertices[triangle.third as usize],
524 vertices[triangle.first as usize],
525 ));
526 }
527 if planes.is_empty() {
528 None
529 } else {
530 Some((index, planes))
531 }
532 })
533 .collect::<HashMap<_, _>>();
534
535 Ok(Self {
536 id: ID::new(),
537 vertices,
538 triangles,
539 areas,
540 connections,
541 graph,
542 nodes,
543 nodes_map,
544 rtree,
545 spatials,
546 hard_edges,
547 })
548 }
549
550 #[inline]
552 pub fn id(&self) -> NavMeshID {
553 self.id
554 }
555
556 #[inline]
558 pub fn vertices(&self) -> &[NavVec3] {
559 &self.vertices
560 }
561
562 #[inline]
564 pub fn triangles(&self) -> &[NavTriangle] {
565 &self.triangles
566 }
567
568 #[inline]
570 pub fn areas(&self) -> &[NavArea] {
571 &self.areas
572 }
573
574 #[inline]
583 pub fn set_area_cost(&mut self, index: usize, cost: Scalar) -> Scalar {
584 let area = &mut self.areas[index];
585 let old = area.cost;
586 let cost = cost.max(0.0);
587 area.cost = cost;
588 old
589 }
590
591 pub fn closest_point(&self, point: NavVec3, query: NavQuery) -> Option<NavVec3> {
600 self.find_closest_triangle(point, query)
601 .map(|triangle| self.spatials[triangle].closest_point(point))
602 }
603
604 pub fn find_path(
655 &self,
656 from: NavVec3,
657 to: NavVec3,
658 query: NavQuery,
659 mode: NavPathMode,
660 ) -> Option<Vec<NavVec3>> {
661 if (to - from).sqr_magnitude() < ZERO_TRESHOLD {
662 return None;
663 }
664 let start = if let Some(start) = self.find_closest_triangle(from, query) {
665 start
666 } else {
667 return None;
668 };
669 let end = if let Some(end) = self.find_closest_triangle(to, query) {
670 end
671 } else {
672 return None;
673 };
674 let from = self.spatials[start].closest_point(from);
675 let to = self.spatials[end].closest_point(to);
676 if let Some((triangles, _)) = self.find_path_triangles(start, end) {
677 if triangles.is_empty() {
678 return None;
679 } else if triangles.len() == 1 {
680 return Some(vec![from, to]);
681 }
682 match mode {
683 NavPathMode::Accuracy => {
684 return Some(self.find_path_accuracy(from, to, &triangles));
685 }
686 NavPathMode::MidPoints => {
687 return Some(self.find_path_midpoints(from, to, &triangles));
688 }
689 }
690 }
691 None
692 }
693
694 fn find_path_accuracy(&self, from: NavVec3, to: NavVec3, triangles: &[usize]) -> Vec<NavVec3> {
695 #[derive(Debug)]
696 enum Node {
697 Point(NavVec3),
698 LevelChange(NavVec3, NavVec3, NavVec3),
700 }
701
702 if triangles.len() == 2 {
704 let NavConnection(a, b) =
705 self.connections[&NavConnection(triangles[0] as u32, triangles[1] as u32)].1;
706 let a = self.vertices[a as usize];
707 let b = self.vertices[b as usize];
708 let n = self.spatials[triangles[0]].normal();
709 let m = self.spatials[triangles[1]].normal();
710 if !NavVec3::is_line_between_points(from, to, a, b, n) {
711 let da = (from - a).sqr_magnitude();
712 let db = (from - b).sqr_magnitude();
713 let point = if da < db { a } else { b };
714 return vec![from, point, to];
715 } else if n.dot(m) < 1.0 - ZERO_TRESHOLD {
716 let n = (b - a).normalize().cross(n);
717 if let Some(point) = NavVec3::raycast_line(from, to, a, b, n) {
718 return vec![from, point, to];
719 }
720 }
721 return vec![from, to];
722 }
723 let mut start = from;
724 let mut last_normal = self.spatials[triangles[0]].normal();
725 let mut nodes = Vec::with_capacity(triangles.len() - 1);
726 for triplets in triangles.windows(3) {
727 let NavConnection(a, b) =
728 self.connections[&NavConnection(triplets[0] as u32, triplets[1] as u32)].1;
729 let a = self.vertices[a as usize];
730 let b = self.vertices[b as usize];
731 let NavConnection(c, d) =
732 self.connections[&NavConnection(triplets[1] as u32, triplets[2] as u32)].1;
733 let c = self.vertices[c as usize];
734 let d = self.vertices[d as usize];
735 let n = self.spatials[triplets[1]].normal();
736 let old_last_normal = last_normal;
737 last_normal = n;
738 if !NavVec3::is_line_between_points(start, c, a, b, n)
739 || !NavVec3::is_line_between_points(start, d, a, b, n)
740 {
741 let da = (start - a).sqr_magnitude();
742 let db = (start - b).sqr_magnitude();
743 start = if da < db { a } else { b };
744 nodes.push(Node::Point(start));
745 } else if old_last_normal.dot(n) < 1.0 - ZERO_TRESHOLD {
746 let n = self.spatials[triplets[0]].normal();
747 let n = (b - a).normalize().cross(n);
748 nodes.push(Node::LevelChange(a, b, n));
749 }
750 }
751 {
752 let NavConnection(a, b) = self.connections[&NavConnection(
753 triangles[triangles.len() - 2] as u32,
754 triangles[triangles.len() - 1] as u32,
755 )]
756 .1;
757 let a = self.vertices[a as usize];
758 let b = self.vertices[b as usize];
759 let n = self.spatials[triangles[triangles.len() - 2]].normal();
760 let m = self.spatials[triangles[triangles.len() - 1]].normal();
761 if !NavVec3::is_line_between_points(start, to, a, b, n) {
762 let da = (start - a).sqr_magnitude();
763 let db = (start - b).sqr_magnitude();
764 let point = if da < db { a } else { b };
765 nodes.push(Node::Point(point));
766 } else if n.dot(m) < 1.0 - ZERO_TRESHOLD {
767 let n = (b - a).normalize().cross(n);
768 nodes.push(Node::LevelChange(a, b, n));
769 }
770 }
771
772 let mut points = Vec::with_capacity(nodes.len() + 2);
773 points.push(from);
774 let mut point = from;
775 for i in 0..nodes.len() {
776 match nodes[i] {
777 Node::Point(p) => {
778 point = p;
779 points.push(p);
780 }
781 Node::LevelChange(a, b, n) => {
782 let next = nodes
783 .iter()
784 .skip(i + 1)
785 .find_map(|n| match n {
786 Node::Point(p) => Some(*p),
787 _ => None,
788 })
789 .unwrap_or(to);
790 if let Some(p) = NavVec3::raycast_line(point, next, a, b, n) {
791 points.push(p);
792 }
793 }
794 }
795 }
796 points.push(to);
797 points.dedup();
798 points
799 }
800
801 fn find_path_midpoints(&self, from: NavVec3, to: NavVec3, triangles: &[usize]) -> Vec<NavVec3> {
802 if triangles.len() == 2 {
803 let NavConnection(a, b) =
804 self.connections[&NavConnection(triangles[0] as u32, triangles[1] as u32)].1;
805 let a = self.vertices[a as usize];
806 let b = self.vertices[b as usize];
807 let n = self.spatials[triangles[0]].normal();
808 let m = self.spatials[triangles[1]].normal();
809 if n.dot(m) < 1.0 - ZERO_TRESHOLD || !NavVec3::is_line_between_points(from, to, a, b, n)
810 {
811 return vec![from, (a + b) * 0.5, to];
812 } else {
813 return vec![from, to];
814 }
815 }
816 let mut start = from;
817 let mut last_normal = self.spatials[triangles[0]].normal();
818 let mut points = Vec::with_capacity(triangles.len() + 1);
819 points.push(from);
820 for triplets in triangles.windows(3) {
821 let NavConnection(a, b) =
822 self.connections[&NavConnection(triplets[0] as u32, triplets[1] as u32)].1;
823 let a = self.vertices[a as usize];
824 let b = self.vertices[b as usize];
825 let point = (a + b) * 0.5;
826 let n = self.spatials[triplets[1]].normal();
827 let old_last_normal = last_normal;
828 last_normal = n;
829 if old_last_normal.dot(n) < 1.0 - ZERO_TRESHOLD {
830 start = point;
831 points.push(start);
832 } else {
833 let NavConnection(c, d) =
834 self.connections[&NavConnection(triplets[1] as u32, triplets[2] as u32)].1;
835 let c = self.vertices[c as usize];
836 let d = self.vertices[d as usize];
837 let end = (c + d) * 0.5;
838 if !NavVec3::is_line_between_points(start, end, a, b, n) {
839 start = point;
840 points.push(start);
841 }
842 }
843 }
844 {
845 let NavConnection(a, b) = self.connections[&NavConnection(
846 triangles[triangles.len() - 2] as u32,
847 triangles[triangles.len() - 1] as u32,
848 )]
849 .1;
850 let a = self.vertices[a as usize];
851 let b = self.vertices[b as usize];
852 let n = self.spatials[triangles[triangles.len() - 2]].normal();
853 let m = self.spatials[triangles[triangles.len() - 1]].normal();
854 if n.dot(m) < 1.0 - ZERO_TRESHOLD
855 || !NavVec3::is_line_between_points(start, to, a, b, n)
856 {
857 points.push((a + b) * 0.5);
858 }
859 }
860 points.push(to);
861 points.dedup();
862 points
863 }
864
865 #[inline]
900 pub fn find_path_triangles(&self, from: usize, to: usize) -> Option<(Vec<usize>, Scalar)> {
901 let to = self.nodes[to];
902 astar(
903 &self.graph,
904 self.nodes[from],
905 |n| n == to,
906 |e| {
907 let a = self.areas[self.nodes_map[&e.source()]].cost;
908 let b = self.areas[self.nodes_map[&e.target()]].cost;
909 *e.weight() * a * b
910 },
911 |_| 0.0,
912 )
913 .map(|(c, v)| (iter!(v).map(|v| self.nodes_map[&v]).collect(), c))
914 }
915
916 pub fn find_closest_triangle(&self, point: NavVec3, query: NavQuery) -> Option<usize> {
925 match query {
926 NavQuery::Accuracy => self.rtree.nearest_neighbor(&point).map(|t| t.index),
927 NavQuery::ClosestFirst => self.rtree.close_neighbor(&point).map(|t| t.index),
928 NavQuery::Closest => self
929 .rtree
930 .nearest_neighbors(&point)
931 .into_iter()
932 .map(|o| (o.distance2(&point), o))
933 .fold(None, |a: Option<(Scalar, &NavSpatialObject)>, i| {
934 if let Some(a) = a {
935 if i.0 < a.0 {
936 Some(i)
937 } else {
938 Some(a)
939 }
940 } else {
941 Some(i)
942 }
943 })
944 .map(|(_, t)| t.index),
945 }
946 }
947
948 pub fn path_target_point(
958 path: &[NavVec3],
959 point: NavVec3,
960 offset: Scalar,
961 ) -> Option<(NavVec3, Scalar)> {
962 let s = Self::project_on_path(path, point, offset);
963 if let Some(p) = Self::point_on_path(path, s) {
964 Some((p, s))
965 } else {
966 None
967 }
968 }
969
970 pub fn project_on_path(path: &[NavVec3], point: NavVec3, offset: Scalar) -> Scalar {
980 let p = match path.len() {
981 0 | 1 => 0.0,
982 2 => Self::project_on_line(path[0], path[1], point),
983 _ => {
984 path.windows(2)
985 .scan(0.0, |state, pair| {
986 let dist = *state;
987 *state += (pair[1] - pair[0]).magnitude();
988 Some((dist, pair))
989 })
990 .map(|(dist, pair)| {
991 let (p, s) = Self::point_on_line(pair[0], pair[1], point);
992 (dist + s, (p - point).sqr_magnitude())
993 })
994 .min_by(|(_, a), (_, b)| a.partial_cmp(&b).unwrap())
995 .unwrap()
996 .0
997 }
998 };
999 (p + offset).max(0.0).min(Self::path_length(path))
1000 }
1001
1002 pub fn point_on_path(path: &[NavVec3], mut s: Scalar) -> Option<NavVec3> {
1011 match path.len() {
1012 0 | 1 => None,
1013 2 => Some(NavVec3::unproject(
1014 path[0],
1015 path[1],
1016 s / Self::path_length(path),
1017 )),
1018 _ => {
1019 for pair in path.windows(2) {
1020 let d = (pair[1] - pair[0]).magnitude();
1021 if s <= d {
1022 return Some(NavVec3::unproject(pair[0], pair[1], s / d));
1023 }
1024 s -= d;
1025 }
1026 None
1027 }
1028 }
1029 }
1030
1031 pub fn path_length(path: &[NavVec3]) -> Scalar {
1039 match path.len() {
1040 0 | 1 => 0.0,
1041 2 => (path[1] - path[0]).magnitude(),
1042 _ => path
1043 .windows(2)
1044 .fold(0.0, |a, pair| a + (pair[1] - pair[0]).magnitude()),
1045 }
1046 }
1047
1048 fn project_on_line(from: NavVec3, to: NavVec3, point: NavVec3) -> Scalar {
1049 let d = (to - from).magnitude();
1050 let p = point.project(from, to);
1051 d * p
1052 }
1053
1054 fn point_on_line(from: NavVec3, to: NavVec3, point: NavVec3) -> (NavVec3, Scalar) {
1055 let d = (to - from).magnitude();
1056 let p = point.project(from, to);
1057 if p <= 0.0 {
1058 (from, 0.0)
1059 } else if p >= 1.0 {
1060 (to, d)
1061 } else {
1062 (NavVec3::unproject(from, to, p), p * d)
1063 }
1064 }
1065}