1use crate::{
19 domain::Reversible,
20 error::NoError,
21 graph::{
22 occupancy::{Cell, Grid},
23 Graph,
24 },
25 motion::r2::Point,
26};
27use bitfield::{bitfield, Bit};
28use std::{
29 collections::{HashMap, HashSet},
30 sync::Arc,
31};
32
33pub struct AccessibilityGraph<G: Grid> {
36 accessibility: Arc<Accessibility<G>>,
37}
38
39impl<G: Grid> AccessibilityGraph<G> {
40 pub fn new(accessibility: Arc<Accessibility<G>>) -> Self {
41 Self { accessibility }
42 }
43}
44
45impl<G: Grid> Clone for AccessibilityGraph<G> {
46 fn clone(&self) -> Self {
47 Self {
48 accessibility: self.accessibility.clone(),
49 }
50 }
51}
52
53impl<G: Grid> Graph for AccessibilityGraph<G> {
54 type Key = Cell;
55 type Vertex = Point;
56 type EdgeAttributes = ();
57
58 type VertexRef<'a>
59 = Point
60 where
61 G: 'a;
62 type Edge<'a>
63 = (Cell, Cell)
64 where
65 G: 'a;
66 type EdgeIter<'a>
67 = AccessibilityGraphEdges
68 where
69 Self: 'a;
70
71 fn vertex<'a>(&'a self, key: &Cell) -> Option<Point> {
72 if self.accessibility.is_inaccessible(key) {
73 return None;
74 }
75
76 Some(key.center_point(self.accessibility.grid.cell_size()))
77 }
78
79 fn edges_from_vertex<'a>(&'a self, key: &Self::Key) -> Self::EdgeIter<'a>
80 where
81 Self: 'a,
82 Self::Vertex: 'a,
83 Self::Key: 'a,
84 Self::EdgeAttributes: 'a,
85 {
86 if self.accessibility.grid.is_occupied(key) {
87 return AccessibilityGraphEdges::None;
88 }
89
90 let directions = match self.accessibility.constraints.get(key) {
91 Some(constraints) => match constraints {
92 CellAccessibility::Accessible(constraints) => *constraints,
93 CellAccessibility::Inaccessible => return AccessibilityGraphEdges::None,
94 },
95 None => CellDirections::all(),
96 };
97
98 let from_cell = *key;
99 let directions = directions.into_iter();
100 AccessibilityGraphEdges::Edges {
101 from_cell,
102 directions,
103 }
104 }
105
106 type LazyEdgeIter<'a>
107 = [(Cell, Cell); 0]
108 where
109 G: 'a;
110
111 fn lazy_edges_between<'a>(&'a self, _: &Self::Key, _: &Self::Key) -> Self::LazyEdgeIter<'a>
112 where
113 Self: 'a,
114 Self::Vertex: 'a,
115 Self::Key: 'a,
116 Self::EdgeAttributes: 'a,
117 {
118 []
119 }
120}
121
122pub enum AccessibilityGraphEdges {
123 None,
124 Edges {
125 from_cell: Cell,
126 directions: CellDirectionsIter,
127 },
128}
129
130impl Iterator for AccessibilityGraphEdges {
131 type Item = (Cell, Cell);
132 fn next(&mut self) -> Option<Self::Item> {
133 let Self::Edges {
134 from_cell,
135 directions,
136 } = self
137 else {
138 return None;
139 };
140
141 directions
142 .next()
143 .map(|[i, j]| (*from_cell, from_cell.shifted(i, j)))
144 }
145}
146
147impl<G: Grid> Reversible for AccessibilityGraph<G> {
148 type ReversalError = NoError;
149 fn reversed(&self) -> Result<Self, Self::ReversalError>
150 where
151 Self: Sized,
152 {
153 Ok(self.clone())
156 }
157}
158
159bitfield! {
160 #[derive(Clone, Copy, PartialEq, Eq)]
161 pub struct CellDirections(u8);
162 impl Debug;
163 u8;
164 pub north, set_north: 0;
165 pub northeast, set_northeast: 1;
166 pub east, set_east: 2;
167 pub southeast, set_southeast: 3;
168 pub south, set_south: 4;
169 pub southwest, set_southwest: 5;
170 pub west, set_west: 6;
171 pub northwest, set_northwest: 7;
172}
173
174impl CellDirections {
175 pub fn iter_from(self, cell: Cell) -> impl Iterator<Item = Cell> {
176 self.iter().map(move |[i, j]| cell.shifted(i, j))
177 }
178
179 pub fn iter(self) -> CellDirectionsIter {
181 CellDirectionsIter {
182 next_dir: 0,
183 directions: self,
184 accessibility: true,
185 }
186 }
187
188 pub fn iter_inaccessible(self) -> CellDirectionsIter {
190 CellDirectionsIter {
191 next_dir: 0,
192 directions: self,
193 accessibility: false,
194 }
195 }
196}
197
198impl IntoIterator for CellDirections {
199 type Item = [i64; 2];
200 type IntoIter = CellDirectionsIter;
201 fn into_iter(self) -> Self::IntoIter {
202 self.iter()
203 }
204}
205
206pub struct CellDirectionsIter {
207 next_dir: u16,
210 directions: CellDirections,
211 accessibility: bool,
214}
215
216impl Iterator for CellDirectionsIter {
217 type Item = [i64; 2];
218 fn next(&mut self) -> Option<Self::Item> {
219 if self.next_dir > 7 {
220 return None;
222 }
223
224 while self.directions.bit(self.next_dir as usize) != self.accessibility {
225 self.next_dir += 1;
226 if self.next_dir > 7 {
227 return None;
228 }
229 }
230
231 let shift = match self.next_dir {
232 0 => [0, 1],
233 1 => [1, 1],
234 2 => [1, 0],
235 3 => [1, -1],
236 4 => [0, -1],
237 5 => [-1, -1],
238 6 => [-1, 0],
239 7 => [-1, 1],
240 _ => return None,
241 };
242
243 self.next_dir += 1;
244 Some(shift)
245 }
246}
247
248impl CellDirections {
249 pub fn all() -> Self {
250 Self(u8::MAX)
251 }
252
253 pub fn is_all(&self) -> bool {
254 self.0 == u8::MAX
255 }
256
257 pub fn set_direction(&mut self, i: i8, j: i8, value: bool) -> Result<(), [i8; 2]> {
258 match [i, j] {
259 [0, 1] => self.set_north(value),
260 [1, 1] => self.set_northeast(value),
261 [1, 0] => self.set_east(value),
262 [1, -1] => self.set_southeast(value),
263 [0, -1] => self.set_south(value),
264 [-1, -1] => self.set_southwest(value),
265 [-1, 0] => self.set_west(value),
266 [-1, 1] => self.set_northwest(value),
267 _ => return Err([i, j]),
268 }
269
270 Ok(())
271 }
272}
273
274#[derive(Clone, Debug)]
275pub enum CellAccessibility {
276 Accessible(CellDirections),
279 Inaccessible,
282}
283
284impl CellAccessibility {
285 pub fn is_inaccessible(&self) -> bool {
286 matches!(self, CellAccessibility::Inaccessible)
287 }
288}
289
290#[derive(Clone, Debug)]
291pub struct Accessibility<G: Grid> {
292 grid: G,
293 agent_radius: f64,
294 cell_shift: i64,
295 constraints: HashMap<Cell, CellAccessibility>,
296}
297
298impl<G: Grid> Accessibility<G> {
299 pub fn new(grid: G, agent_radius: f64) -> Self {
300 let cell_size = grid.cell_size();
301 let mut output = Self {
302 grid,
303 agent_radius,
304 cell_shift: Self::calculate_cell_shift(agent_radius, cell_size),
305 constraints: HashMap::new(),
306 };
307
308 Self::update_constraints(
309 output.grid.occupied_cells(),
310 &output.grid,
311 output.agent_radius,
312 output.cell_shift,
313 &mut output.constraints,
314 );
315
316 output
317 }
318
319 pub fn iter_accessibility<'a>(
320 &'a self,
321 ) -> impl Iterator<Item = (&'a Cell, &'a CellAccessibility)> {
322 self.constraints.iter()
323 }
324
325 pub fn grid(&self) -> &G {
326 &self.grid
327 }
328
329 pub fn agent_radius(&self) -> f64 {
330 self.agent_radius
331 }
332
333 pub fn is_inaccessible(&self, cell: &Cell) -> bool {
334 self.grid.is_occupied(cell)
335 || self
336 .constraints
337 .get(cell)
338 .filter(|c| c.is_inaccessible())
339 .is_some()
340 }
341
342 pub fn change_cells(&mut self, mut changes: HashMap<Cell, bool>) -> bool {
343 changes.retain(|cell, value| self.grid.is_occupied(cell) != *value);
344 if changes.is_empty() {
345 return false;
346 }
347
348 self.grid.change_cells(&changes);
349 Self::update_constraints(
350 changes.keys(),
351 &self.grid,
352 self.agent_radius,
353 self.cell_shift,
354 &mut self.constraints,
355 );
356
357 return true;
358 }
359
360 pub fn change_agent_radius(&mut self, value: f64) {
361 self.agent_radius = value;
362 self.cell_shift = Self::calculate_cell_shift(self.agent_radius, self.grid.cell_size());
363 self.constraints.clear();
364
365 Self::update_constraints(
366 self.grid.occupied_cells(),
367 &self.grid,
368 self.agent_radius,
369 self.cell_shift,
370 &mut self.constraints,
371 );
372 }
373
374 fn update_constraints<'a>(
375 changed_cells: impl IntoIterator<Item = &'a Cell>,
376 grid: &G,
377 agent_radius: f64,
378 cell_shift: i64,
379 constraints: &mut HashMap<Cell, CellAccessibility>,
380 ) {
381 let mut inspect_cells: HashSet<Cell> = HashSet::new();
382 for cell in changed_cells {
383 let cell: Cell = *cell;
384 for x in -cell_shift..=cell_shift {
385 for y in -cell_shift..=cell_shift {
386 inspect_cells.insert(cell.shifted(x, y));
387 }
388 }
389 }
390
391 for cell in &inspect_cells {
393 if grid.is_occupied(cell) {
394 constraints.remove(cell);
398 continue;
399 }
400
401 let p = cell.center_point(grid.cell_size());
402 if grid.is_circle_occupied(p, agent_radius).is_some() {
403 constraints.insert(*cell, CellAccessibility::Inaccessible);
404 continue;
405 }
406
407 constraints.remove(cell);
410 }
411
412 for from_cell in &inspect_cells {
414 if grid.is_occupied(from_cell) {
415 continue;
418 }
419
420 if constraints.contains_key(from_cell) {
421 continue;
424 }
425
426 let from_p = from_cell.center_point(grid.cell_size());
427 let mut cell_directions = CellDirections::all();
428 for i in [-1, 0, 1] {
429 for j in [-1, 0, 1] {
430 if i == 0 && j == 0 {
431 continue;
432 }
433
434 let to_cell: Cell = from_cell.shifted(i, j);
435 if grid.is_occupied(&to_cell)
436 || constraints
437 .get(&to_cell)
438 .filter(|c| c.is_inaccessible())
439 .is_some()
440 {
441 cell_directions.set_direction(i as i8, j as i8, false).ok();
442 continue;
443 }
444
445 let to_p = to_cell.center_point(grid.cell_size());
446 if grid
447 .is_sweep_occupied(from_p, to_p, 2.0 * agent_radius)
448 .is_some()
449 {
450 cell_directions.set_direction(i as i8, j as i8, false).ok();
451 }
452 }
453 }
454
455 if !cell_directions.is_all() {
456 constraints.insert(*from_cell, CellAccessibility::Accessible(cell_directions));
457 }
458 }
459 }
460
461 fn calculate_cell_shift(agent_radius: f64, cell_size: f64) -> i64 {
462 (agent_radius / cell_size + 0.5).ceil() as i64
463 }
464}