1#![allow(dead_code)]
2
3use itertools::iproduct;
8use ordered_float::NotNan;
9use std::cmp::Reverse;
10use std::collections::{BTreeMap, BinaryHeap};
11
12use crate::grid_nalgebra;
13
14fn calculate_neighbors(nrows: usize, ncols: usize) -> impl Fn(usize, usize) -> Vec<(usize, usize)> {
17 move |i, j| {
18 let delta = [-1, 0, 1];
19 iproduct!(delta, delta)
20 .filter_map(|(di, dj)| {
21 let ni = i as i32 + di;
22 let nj = j as i32 + dj;
23 if ni >= 0
24 && ni < nrows as i32
25 && nj >= 0
26 && nj < ncols as i32
27 && (ni, nj) != (i as i32, j as i32)
28 {
29 Some((ni as usize, nj as usize))
30 } else {
31 None
32 }
33 })
34 .collect()
35 }
36}
37
38pub fn has_collision(obstacle_map: &grid_nalgebra::Map, ni: usize, nj: usize) -> bool {
40 let (height, width) = obstacle_map.shape();
41 let get_neighbors = calculate_neighbors(height, width);
42
43 let neighbors = get_neighbors(ni, nj);
44
45 if neighbors.iter().all(|&(i, j)| obstacle_map[(i, j)] != 1) && obstacle_map[(ni, nj)] != 1 {
46 return false;
47 }
48
49 true
50}
51
52pub fn dijkstra_plan(
57 obstacle_map: &grid_nalgebra::Map,
58 start: (usize, usize),
59 goal: (usize, usize),
60) -> Option<Vec<(usize, usize)>> {
61 let (height, width) = obstacle_map.shape();
62 let get_neighbors = calculate_neighbors(height, width);
63
64 let mut dist = BTreeMap::new();
65 let mut prev: BTreeMap<(usize, usize), Option<(usize, usize)>> = BTreeMap::new();
66 let mut pq = BinaryHeap::new();
67
68 pq.push((Reverse(NotNan::new(0.0).unwrap()), start));
69 dist.insert(start, 0.0);
70
71 while let Some((distance, current)) = pq.pop() {
72 if current == goal {
73 let mut path = vec![current];
75 while let Some(previous) = prev.get(path.last().unwrap()).unwrap_or(&None) {
76 path.push(*previous);
77 if *path.last().unwrap() == start {
78 path.reverse();
79 return Some(path);
80 }
81 }
82 return None;
83 }
84
85 let (ci, cj) = current;
86 let neighbors = get_neighbors(ci, cj);
87
88 for (ni, nj) in neighbors {
89 if !has_collision(obstacle_map, ni, nj) {
90 let delta_x = ci as f64 - ni as f64;
91 let delta_y = cj as f64 - nj as f64;
92 let edge_distance = (delta_x * delta_x + delta_y * delta_y).sqrt();
93 let alt = distance.0.into_inner() + edge_distance;
94
95 if *dist.get(&(ni, nj)).unwrap_or(&f64::MAX) > alt {
96 dist.insert((ni, nj), alt);
97 prev.insert((ni, nj), Some((ci, cj)));
98 pq.push((Reverse(NotNan::new(alt).unwrap()), (ni, nj)));
99 }
100 }
101 }
102 }
103
104 None
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_dijkstra_simple() {
113 let matrix = nalgebra::DMatrix::from_row_slice(
114 5,
115 5,
116 &[
117 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 ],
119 );
120 let map = grid_nalgebra::Map::new(matrix, 1).unwrap();
121 let result = dijkstra_plan(&map, (0, 0), (4, 4));
122 assert!(result.is_some());
123 let path = result.unwrap();
124 assert_eq!(*path.first().unwrap(), (0, 0));
125 assert_eq!(*path.last().unwrap(), (4, 4));
126 }
127
128 #[test]
129 fn test_has_collision() {
130 let matrix = nalgebra::DMatrix::from_row_slice(
131 5,
132 5,
133 &[
134 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
135 ],
136 );
137 let map = grid_nalgebra::Map::new(matrix, 1).unwrap();
138 assert!(has_collision(&map, 2, 2));
140 assert!(!has_collision(&map, 0, 0));
142 assert!(has_collision(&map, 1, 1));
144 }
145}