1#![allow(dead_code, clippy::legacy_numeric_constants)]
2
3use std::collections::HashSet;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15enum Tag {
16 New,
17 Open,
18 Closed,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23enum CellKind {
24 Free,
25 Obstacle,
26}
27
28#[derive(Debug, Clone)]
30struct Cell {
31 tag: Tag,
32 kind: CellKind,
33 h: f64,
35 k: f64,
37 parent: usize,
39}
40
41#[derive(Debug)]
43struct Map {
44 rows: usize,
45 cols: usize,
46 cells: Vec<Cell>,
47}
48
49impl Map {
50 fn new(rows: usize, cols: usize) -> Self {
51 let cells = vec![
52 Cell {
53 tag: Tag::New,
54 kind: CellKind::Free,
55 h: 0.0,
56 k: 0.0,
57 parent: usize::MAX,
58 };
59 rows * cols
60 ];
61 Map { rows, cols, cells }
62 }
63
64 #[inline]
65 fn idx(&self, r: usize, c: usize) -> usize {
66 r * self.cols + c
67 }
68
69 #[inline]
70 fn pos(&self, idx: usize) -> (usize, usize) {
71 (idx / self.cols, idx % self.cols)
72 }
73
74 fn neighbors(&self, idx: usize) -> Vec<usize> {
75 let (r, c) = self.pos(idx);
76 let mut result = Vec::with_capacity(8);
77 for dr in [-1i32, 0, 1] {
78 for dc in [-1i32, 0, 1] {
79 if dr == 0 && dc == 0 {
80 continue;
81 }
82 let nr = r as i32 + dr;
83 let nc = c as i32 + dc;
84 if nr >= 0 && (nr as usize) < self.rows && nc >= 0 && (nc as usize) < self.cols {
85 result.push(self.idx(nr as usize, nc as usize));
86 }
87 }
88 }
89 result
90 }
91
92 fn cost(&self, a: usize, b: usize) -> f64 {
94 if self.cells[a].kind == CellKind::Obstacle || self.cells[b].kind == CellKind::Obstacle {
95 return f64::MAX;
96 }
97 let (ar, ac) = self.pos(a);
98 let (br, bc) = self.pos(b);
99 let dr = (ar as f64) - (br as f64);
100 let dc = (ac as f64) - (bc as f64);
101 (dr * dr + dc * dc).sqrt()
102 }
103
104 fn set_obstacle(&mut self, r: usize, c: usize) {
105 if r < self.rows && c < self.cols {
106 let idx = self.idx(r, c);
107 self.cells[idx].kind = CellKind::Obstacle;
108 }
109 }
110}
111
112#[derive(Debug)]
118pub struct DStar {
119 map: Map,
120 open_list: HashSet<usize>,
121 x_min_world: i32,
122 y_min_world: i32,
123}
124
125impl DStar {
126 pub fn new(ox: &[i32], oy: &[i32]) -> Self {
130 let x_min = *ox.iter().min().unwrap_or(&0);
131 let y_min = *oy.iter().min().unwrap_or(&0);
132 let x_max = *ox.iter().max().unwrap_or(&0);
133 let y_max = *oy.iter().max().unwrap_or(&0);
134
135 let rows = (x_max - x_min + 1) as usize;
136 let cols = (y_max - y_min + 1) as usize;
137
138 let mut map = Map::new(rows, cols);
139
140 for (&x, &y) in ox.iter().zip(oy.iter()) {
141 let r = (x - x_min) as usize;
142 let c = (y - y_min) as usize;
143 map.set_obstacle(r, c);
144 }
145
146 DStar {
147 map,
148 open_list: HashSet::new(),
149 x_min_world: x_min,
150 y_min_world: y_min,
151 }
152 }
153
154 fn min_state(&self) -> Option<usize> {
156 self.open_list.iter().copied().min_by(|&a, &b| {
157 self.map.cells[a]
158 .k
159 .partial_cmp(&self.map.cells[b].k)
160 .unwrap_or(std::cmp::Ordering::Equal)
161 })
162 }
163
164 fn get_kmin(&self) -> f64 {
166 self.open_list
167 .iter()
168 .map(|&i| self.map.cells[i].k)
169 .fold(f64::INFINITY, f64::min)
170 .min(f64::INFINITY)
171 * if self.open_list.is_empty() { 0.0 } else { 1.0 }
172 + if self.open_list.is_empty() { -1.0 } else { 0.0 }
173 }
174
175 fn insert(&mut self, idx: usize, h_new: f64) {
177 let cell = &mut self.map.cells[idx];
178 match cell.tag {
179 Tag::New => cell.k = h_new,
180 Tag::Open => cell.k = cell.k.min(h_new),
181 Tag::Closed => cell.k = cell.h.min(h_new),
182 }
183 cell.h = h_new;
184 cell.tag = Tag::Open;
185 self.open_list.insert(idx);
186 }
187
188 fn remove(&mut self, idx: usize) {
190 if self.map.cells[idx].tag == Tag::Open {
191 self.map.cells[idx].tag = Tag::Closed;
192 }
193 self.open_list.remove(&idx);
194 }
195
196 fn process_state(&mut self) -> f64 {
199 let x = match self.min_state() {
200 Some(idx) => idx,
201 None => return -1.0,
202 };
203
204 let k_old = self.get_kmin();
205 self.remove(x);
206
207 let neighbors = self.map.neighbors(x);
208 let x_h = self.map.cells[x].h;
209
210 if k_old < x_h {
211 for &y in &neighbors {
213 let y_h = self.map.cells[y].h;
214 let cost_xy = self.map.cost(x, y);
215 if y_h <= k_old && x_h > y_h + cost_xy {
216 self.map.cells[x].parent = y;
217 self.map.cells[x].h = y_h + cost_xy;
218 }
219 }
220 }
221
222 let x_h = self.map.cells[x].h;
224
225 if (k_old - x_h).abs() < 1e-10 {
226 for &y in &neighbors {
228 let y_tag = self.map.cells[y].tag;
229 let y_h = self.map.cells[y].h;
230 let y_parent = self.map.cells[y].parent;
231 let cost_xy = self.map.cost(x, y);
232 if y_tag == Tag::New
233 || (y_parent == x && (y_h - (x_h + cost_xy)).abs() > 1e-10)
234 || (y_parent != x && y_h > x_h + cost_xy)
235 {
236 self.map.cells[y].parent = x;
237 self.insert(y, x_h + cost_xy);
238 }
239 }
240 } else {
241 for &y in &neighbors {
243 let y_tag = self.map.cells[y].tag;
244 let y_h = self.map.cells[y].h;
245 let y_parent = self.map.cells[y].parent;
246 let cost_xy = self.map.cost(x, y);
247 if y_tag == Tag::New || (y_parent == x && (y_h - (x_h + cost_xy)).abs() > 1e-10) {
248 self.map.cells[y].parent = x;
249 self.insert(y, x_h + cost_xy);
250 } else if y_parent != x && y_h > x_h + cost_xy {
251 self.insert(x, x_h);
252 } else if y_parent != x
253 && x_h > y_h + cost_xy
254 && y_tag == Tag::Closed
255 && y_h > k_old
256 {
257 self.insert(y, y_h);
258 }
259 }
260 }
261
262 self.get_kmin()
263 }
264
265 fn modify_cost(&mut self, idx: usize) {
270 if self.map.cells[idx].tag == Tag::Closed {
271 let parent = self.map.cells[idx].parent;
272 let cost = self.map.cost(idx, parent);
273 if cost < f64::MAX {
274 let parent_h = self.map.cells[parent].h;
276 self.insert(idx, parent_h + cost);
277 } else {
278 let neighbors = self.map.neighbors(idx);
280 let mut best_h = f64::MAX;
281 let mut best_neighbor = usize::MAX;
282 for &n in &neighbors {
283 if self.map.cells[n].kind == CellKind::Obstacle {
284 continue;
285 }
286 let c = self.map.cost(idx, n);
287 let candidate = self.map.cells[n].h + c;
288 if candidate < best_h {
289 best_h = candidate;
290 best_neighbor = n;
291 }
292 }
293 if best_neighbor != usize::MAX {
294 self.map.cells[idx].parent = best_neighbor;
295 self.insert(idx, best_h);
296 } else {
297 self.insert(idx, f64::MAX);
299 }
300 }
301 }
302 }
303
304 fn modify(&mut self, idx: usize) {
305 self.modify_cost(idx);
306 loop {
307 let k_min = self.process_state();
308 if k_min >= self.map.cells[idx].h || k_min < 0.0 {
309 break;
310 }
311 }
312 }
313
314 pub fn plan_xy(&mut self, sx: i32, sy: i32, gx: i32, gy: i32) -> Option<Vec<(i32, i32)>> {
318 let start_r = (sx - self.x_min_world) as usize;
319 let start_c = (sy - self.y_min_world) as usize;
320 let goal_r = (gx - self.x_min_world) as usize;
321 let goal_c = (gy - self.y_min_world) as usize;
322
323 if start_r >= self.map.rows
324 || start_c >= self.map.cols
325 || goal_r >= self.map.rows
326 || goal_c >= self.map.cols
327 {
328 return None;
329 }
330
331 let start = self.map.idx(start_r, start_c);
332 let goal = self.map.idx(goal_r, goal_c);
333
334 self.insert(goal, 0.0);
336
337 loop {
338 let k = self.process_state();
339 if self.map.cells[start].tag == Tag::Closed || k < 0.0 {
340 break;
341 }
342 }
343
344 if self.map.cells[start].tag != Tag::Closed || !self.map.cells[start].h.is_finite() {
345 return None; }
347
348 self.extract_path(start, goal)
350 }
351
352 pub fn plan_with_new_obstacles(
357 &mut self,
358 sx: i32,
359 sy: i32,
360 gx: i32,
361 gy: i32,
362 new_obstacles: &[(i32, i32)],
363 ) -> Option<Vec<(i32, i32)>> {
364 let start_r = (sx - self.x_min_world) as usize;
365 let start_c = (sy - self.y_min_world) as usize;
366 let goal_r = (gx - self.x_min_world) as usize;
367 let goal_c = (gy - self.y_min_world) as usize;
368
369 if start_r >= self.map.rows
370 || start_c >= self.map.cols
371 || goal_r >= self.map.rows
372 || goal_c >= self.map.cols
373 {
374 return None;
375 }
376
377 let start = self.map.idx(start_r, start_c);
378 let goal = self.map.idx(goal_r, goal_c);
379
380 self.insert(goal, 0.0);
382 loop {
383 let k = self.process_state();
384 if self.map.cells[start].tag == Tag::Closed || k < 0.0 {
385 break;
386 }
387 }
388
389 if self.map.cells[start].tag != Tag::Closed || !self.map.cells[start].h.is_finite() {
390 return None;
391 }
392
393 for &(ox, oy) in new_obstacles {
395 let r = (ox - self.x_min_world) as usize;
396 let c = (oy - self.y_min_world) as usize;
397 if r < self.map.rows && c < self.map.cols {
398 self.map.set_obstacle(r, c);
399 }
400 }
401
402 let mut path = Vec::new();
404 let mut current = start;
405
406 let max_steps = self.map.rows * self.map.cols;
407 let mut steps = 0;
408
409 while current != goal {
410 if steps > max_steps {
411 return None; }
413 steps += 1;
414
415 let (r, c) = self.map.pos(current);
416 path.push((r as i32 + self.x_min_world, c as i32 + self.y_min_world));
417
418 let parent = self.map.cells[current].parent;
419 if parent == usize::MAX {
420 return None;
421 }
422
423 if self.map.cells[parent].kind == CellKind::Obstacle {
424 self.modify(current);
426 continue;
428 }
429
430 current = parent;
431 }
432
433 let (r, c) = self.map.pos(goal);
434 path.push((r as i32 + self.x_min_world, c as i32 + self.y_min_world));
435
436 Some(path)
437 }
438
439 pub fn add_obstacle(&mut self, x: i32, y: i32) {
441 let r = (x - self.x_min_world) as usize;
442 let c = (y - self.y_min_world) as usize;
443 if r < self.map.rows && c < self.map.cols {
444 let idx = self.map.idx(r, c);
445 self.map.set_obstacle(r, c);
446 let neighbors = self.map.neighbors(idx);
448 for n in neighbors {
449 if self.map.cells[n].parent == idx && self.map.cells[n].tag == Tag::Closed {
450 self.modify(n);
451 }
452 }
453 }
454 }
455
456 fn extract_path(&self, start: usize, goal: usize) -> Option<Vec<(i32, i32)>> {
458 let mut path = Vec::new();
459 let mut current = start;
460 let max_steps = self.map.rows * self.map.cols;
461
462 for _ in 0..max_steps {
463 let (r, c) = self.map.pos(current);
464 path.push((r as i32 + self.x_min_world, c as i32 + self.y_min_world));
465
466 if current == goal {
467 return Some(path);
468 }
469
470 let parent = self.map.cells[current].parent;
471 if parent == usize::MAX || parent == current {
472 return None;
473 }
474 current = parent;
475 }
476
477 None
478 }
479}
480
481#[cfg(test)]
482mod tests {
483 use super::*;
484
485 fn make_box_obstacles() -> (Vec<i32>, Vec<i32>) {
487 let mut ox = Vec::new();
488 let mut oy = Vec::new();
489 for i in -1..=11 {
490 ox.push(i);
491 oy.push(-1); ox.push(i);
493 oy.push(11); ox.push(-1);
495 oy.push(i); ox.push(11);
497 oy.push(i); }
499 (ox, oy)
500 }
501
502 #[test]
503 fn test_dstar_creation() {
504 let (ox, oy) = make_box_obstacles();
505 let dstar = DStar::new(&ox, &oy);
506 assert!(dstar.map.rows > 0);
507 assert!(dstar.map.cols > 0);
508 }
509
510 #[test]
511 fn test_dstar_simple_path() {
512 let (ox, oy) = make_box_obstacles();
513 let mut dstar = DStar::new(&ox, &oy);
514 let path = dstar.plan_xy(0, 0, 5, 5);
515 assert!(path.is_some());
516 let path = path.unwrap();
517 assert_eq!(path.first(), Some(&(0, 0)));
518 assert_eq!(path.last(), Some(&(5, 5)));
519 assert!(path.len() >= 2);
520 }
521
522 #[test]
523 fn test_dstar_start_equals_goal() {
524 let (ox, oy) = make_box_obstacles();
525 let mut dstar = DStar::new(&ox, &oy);
526 let path = dstar.plan_xy(3, 3, 3, 3);
527 assert!(path.is_some());
528 let path = path.unwrap();
529 assert_eq!(path.len(), 1);
530 assert_eq!(path[0], (3, 3));
531 }
532
533 #[test]
534 fn test_dstar_path_avoids_obstacle() {
535 let (mut ox, mut oy) = make_box_obstacles();
536 for y in 0..=8 {
538 ox.push(5);
539 oy.push(y);
540 }
541 let mut dstar = DStar::new(&ox, &oy);
542 let path = dstar.plan_xy(2, 2, 8, 2);
543 assert!(path.is_some());
544 let path = path.unwrap();
545 for &(px, py) in &path {
547 if px == 5 {
548 assert!(py > 8, "path should go around the wall");
549 }
550 }
551 assert_eq!(path.first(), Some(&(2, 2)));
552 assert_eq!(path.last(), Some(&(8, 2)));
553 }
554
555 #[test]
556 fn test_dstar_no_path_blocked() {
557 let mut ox = Vec::new();
561 let mut oy = Vec::new();
562 for i in -1..=10 {
564 ox.push(i);
565 oy.push(-1);
566 ox.push(i);
567 oy.push(10);
568 ox.push(-1);
569 oy.push(i);
570 ox.push(10);
571 oy.push(i);
572 }
573 for j in -1..=10 {
575 ox.push(5);
576 oy.push(j);
577 }
578 let mut dstar = DStar::new(&ox, &oy);
579 let path = dstar.plan_xy(2, 5, 8, 5);
580 assert!(
582 path.is_none(),
583 "goal should be unreachable through complete wall"
584 );
585 }
586
587 #[test]
588 fn test_dstar_with_new_obstacles() {
589 let (ox, oy) = make_box_obstacles();
590 let mut dstar = DStar::new(&ox, &oy);
591
592 let new_obs: Vec<(i32, i32)> = (0..=6).map(|i| (5, i)).collect();
594
595 let path = dstar.plan_with_new_obstacles(2, 5, 8, 5, &new_obs);
596 assert!(path.is_some(), "should find path through gap above wall");
597 let path = path.unwrap();
598 assert_eq!(path.first(), Some(&(2, 5)));
599 assert_eq!(path.last(), Some(&(8, 5)));
600 }
601
602 #[test]
603 fn test_dstar_out_of_bounds() {
604 let (ox, oy) = make_box_obstacles();
605 let mut dstar = DStar::new(&ox, &oy);
606 let path = dstar.plan_xy(-100, -100, 200, 200);
607 assert!(path.is_none());
608 }
609
610 #[test]
611 fn test_dstar_adjacent_cells() {
612 let (ox, oy) = make_box_obstacles();
613 let mut dstar = DStar::new(&ox, &oy);
614 let path = dstar.plan_xy(3, 3, 4, 4);
615 assert!(path.is_some());
616 let path = path.unwrap();
617 assert_eq!(path.len(), 2); }
619
620 #[test]
621 fn test_dstar_straight_line() {
622 let (ox, oy) = make_box_obstacles();
623 let mut dstar = DStar::new(&ox, &oy);
624 let path = dstar.plan_xy(1, 1, 1, 9);
625 assert!(path.is_some());
626 let path = path.unwrap();
627 assert_eq!(path.first(), Some(&(1, 1)));
628 assert_eq!(path.last(), Some(&(1, 9)));
629 for &(px, _) in &path {
631 assert_eq!(px, 1);
632 }
633 }
634
635 #[test]
636 fn test_map_neighbors() {
637 let map = Map::new(5, 5);
638 let n = map.neighbors(map.idx(0, 0));
640 assert_eq!(n.len(), 3);
641 let n = map.neighbors(map.idx(0, 2));
643 assert_eq!(n.len(), 5);
644 let n = map.neighbors(map.idx(2, 2));
646 assert_eq!(n.len(), 8);
647 }
648
649 #[test]
650 fn test_map_cost_with_obstacle() {
651 let mut map = Map::new(5, 5);
652 let a = map.idx(1, 1);
653 let b = map.idx(1, 2);
654 let cost = map.cost(a, b);
656 assert!((cost - 1.0).abs() < 1e-10);
657 map.set_obstacle(1, 2);
659 let cost = map.cost(a, b);
660 assert_eq!(cost, f64::MAX);
661 }
662}