pathfinding/
grid.rs

1//! Rectangular grid in which vertices can be added or removed, with or
2//! without diagonal links.
3
4use super::matrix::Matrix;
5use crate::directed::bfs::bfs_reach;
6use crate::directed::dfs::dfs_reach;
7use crate::utils::constrain;
8use crate::FxIndexSet;
9use num_traits::ToPrimitive;
10use std::collections::BTreeSet;
11use std::fmt;
12use std::iter::FusedIterator;
13use std::ops::Sub;
14
15#[derive(Clone)]
16/// A rectangular grid.
17///
18/// Representation of a rectangular grid in which vertices can be added
19/// or removed. Edges are automatically created between adjacent vertices.
20/// By default, only vertical and horizontal edges are created, unless
21/// diagonal mode is enabled.
22///
23/// The coordinate system is of the form `(x, y)`, where `x` is the column
24/// and `y` is the row. `(0, 0)` corresponds to the top-left corner.
25///
26/// Internally, a Grid is represented either as a collection of vertices
27/// or as a collection of absent vertices, depending on the density of
28/// the grid. The switch between both representations is done automatically
29/// when vertices are added or removed, or when the grid is resized.
30///
31/// `Grid` implements `Debug` and represents the content using `#` and `.`
32/// characters. Alternate block characters `▓` and `░` can be selected by
33/// using the alternate debug format (`{:#?}`):
34///
35/// ```
36/// use pathfinding::prelude::Grid;
37///
38/// let mut g = Grid::new(3, 4);
39/// g.add_borders();
40///
41/// assert_eq!(&format!("{g:?}"), "\
42/// ####
43/// #.#
44/// #.#
45/// ####");
46///
47/// assert_eq!(&format!("{g:#?}"), "\
48/// ▓▓▓
49/// ▓░▓
50/// ▓░▓
51/// ▓▓▓");
52/// ```
53///
54/// One of the ways to build a `Grid` is to start from an iterator of
55/// `(usize, usize)` representing the `(x, y)` coordinates:
56///
57/// ```
58/// use pathfinding::prelude::Grid;
59///
60/// let g = vec![(0, 0), (2, 2), (3, 2)].into_iter().collect::<Grid>();
61/// assert_eq!(format!("{g:?}"), "\
62/// #...
63/// ....
64/// ..##");
65/// ```
66///
67/// To be able to easily use the grid as a visualization tool for
68/// arbitrary types of coordinates, a [`from_coordinates`](Grid::from_coordinates)
69/// method will build a grid and remap the top-left most coordinate as `(0, 0)`:
70///
71/// ```
72/// use pathfinding::prelude::Grid;
73///
74/// let g = Grid::from_coordinates(&[(-16, -15), (-16, -16), (-15, -16)]).unwrap();
75/// assert_eq!(format!("{g:#?}"), "\
76/// ▓▓
77/// ▓░");
78/// ```
79/// Also, the order of lines can be inverted by using the `-` sign modifier while
80/// displaying:
81///
82/// ```
83/// # use pathfinding::prelude::Grid;
84/// #
85/// # let g = Grid::from_coordinates(&[(-16, -15), (-16, -16), (-15, -16)]).unwrap();
86/// assert_eq!(format!("{g:-#?}"), "\
87/// ▓░
88/// ▓▓");
89/// ```
90pub struct Grid {
91    /// The grid width.
92    pub width: usize,
93    /// The grid height.
94    pub height: usize,
95    diagonal_mode: bool,
96    // `dense` is true if the grid is full by default and `exclusions`
97    // contains absent vertices. It is false if the grid is empty by default
98    // and `exclusions` contains the vertices.
99    dense: bool,
100    exclusions: FxIndexSet<(usize, usize)>,
101}
102
103impl Grid {
104    /// Create a new empty grid object of the given dimensions, with
105    /// diagonal mode disabled.
106    #[must_use]
107    pub fn new(width: usize, height: usize) -> Self {
108        Self {
109            width,
110            height,
111            diagonal_mode: false,
112            dense: false,
113            exclusions: FxIndexSet::default(),
114        }
115    }
116
117    /// Check if a (possibly removed) vertex belongs to the grid or if it
118    /// is located outside the grid.
119    #[inline]
120    #[must_use]
121    pub const fn is_inside(&self, vertex: (usize, usize)) -> bool {
122        vertex.0 < self.width && vertex.1 < self.height
123    }
124
125    /// Enable diagonal mode. Diagonal edges will be created between
126    /// adjacent vertices.
127    pub fn enable_diagonal_mode(&mut self) {
128        self.diagonal_mode = true;
129    }
130
131    /// Disable diagonal mode. Only horizontal and vertical edges will
132    /// be created between adjacent vertices.
133    pub fn disable_diagonal_mode(&mut self) {
134        self.diagonal_mode = false;
135    }
136
137    /// Resize the grid to the given dimensions. Return `true` if this
138    /// caused any existing vertex to be discarded.
139    pub fn resize(&mut self, width: usize, height: usize) -> bool {
140        let mut truncated = false;
141        if width < self.width {
142            truncated |=
143                (width..self.width).any(|c| (0..self.height).any(|r| self.has_vertex((c, r))));
144        }
145        if height < self.height {
146            truncated |=
147                (0..self.width).any(|c| (height..self.height).any(|r| self.has_vertex((c, r))));
148        }
149        self.exclusions.retain(|&(x, y)| x < width && y < height);
150        if self.dense {
151            for c in self.width..width {
152                for r in 0..height {
153                    self.exclusions.insert((c, r));
154                }
155            }
156            for c in 0..self.width.min(width) {
157                for r in self.height..height {
158                    self.exclusions.insert((c, r));
159                }
160            }
161        }
162        self.width = width;
163        self.height = height;
164        self.rebalance();
165        truncated
166    }
167
168    /// Return the number of positions in this grid.
169    #[must_use]
170    pub const fn size(&self) -> usize {
171        self.width * self.height
172    }
173
174    /// Return the number of vertices.
175    #[must_use]
176    pub fn vertices_len(&self) -> usize {
177        if self.dense {
178            self.size() - self.exclusions.len()
179        } else {
180            self.exclusions.len()
181        }
182    }
183
184    /// Add a new vertex. Return `true` if the vertex did not previously
185    /// exist and has been added. Return `false` if the vertex exists
186    /// already or would be outside the grid.
187    pub fn add_vertex(&mut self, vertex: (usize, usize)) -> bool {
188        if !self.is_inside(vertex) {
189            return false;
190        }
191        let r = if self.dense {
192            self.exclusions.swap_remove(&vertex)
193        } else {
194            self.exclusions.insert(vertex)
195        };
196        self.rebalance();
197        r
198    }
199
200    /// Remove a vertex. Return `true` if the vertex did previously exist
201    /// and has been removed.
202    pub fn remove_vertex(&mut self, vertex: (usize, usize)) -> bool {
203        if !self.is_inside(vertex) {
204            return false;
205        }
206        let r = if self.dense {
207            self.exclusions.insert(vertex)
208        } else {
209            self.exclusions.swap_remove(&vertex)
210        };
211        self.rebalance();
212        r
213    }
214
215    /// Return an iterator over the border vertices. The grid must not have
216    /// a zero width or height.
217    fn borders(&self) -> impl Iterator<Item = (usize, usize)> {
218        let width = self.width;
219        let height = self.height;
220        (0..width)
221            .flat_map(move |x| vec![(x, 0), (x, height - 1)].into_iter())
222            .chain((1..height - 1).flat_map(move |y| vec![(0, y), (width - 1, y)].into_iter()))
223    }
224
225    /// Add the borders of the grid. Return the number of added vertices.
226    pub fn add_borders(&mut self) -> usize {
227        if self.width == 0 || self.height == 0 {
228            return 0;
229        }
230        let count = if self.dense {
231            self.borders()
232                .filter(|v| self.exclusions.swap_remove(v))
233                .count()
234        } else {
235            self.borders()
236                .filter(|v| self.exclusions.insert(*v))
237                .count()
238        };
239        self.rebalance();
240        count
241    }
242
243    /// Remove the borders of the grid. Return the number of removed vertices.
244    pub fn remove_borders(&mut self) -> usize {
245        if self.width == 0 || self.height == 0 {
246            return 0;
247        }
248        let count = if self.dense {
249            self.borders()
250                .filter(|v| self.exclusions.insert(*v))
251                .count()
252        } else {
253            self.borders()
254                .filter(|v| self.exclusions.swap_remove(v))
255                .count()
256        };
257        self.rebalance();
258        count
259    }
260
261    fn rebalance(&mut self) {
262        if self.exclusions.len() > self.width * self.height / 2 {
263            self.exclusions = (0..self.width)
264                .flat_map(|c| (0..self.height).map(move |r| (c, r)))
265                .filter(|v| !self.exclusions.contains(v))
266                .collect();
267            self.dense = !self.dense;
268        }
269    }
270
271    /// Remove all vertices from the grid. Return `true` if the grid
272    /// previously contained at least one vertex.
273    pub fn clear(&mut self) -> bool {
274        let r = !self.is_empty();
275        self.dense = false;
276        self.exclusions.clear();
277        r
278    }
279
280    /// Fill the grid with all possible vertices. Return `true` if
281    /// this caused the addition of at least one vertex.
282    pub fn fill(&mut self) -> bool {
283        let r = !self.is_full();
284        self.clear();
285        self.invert();
286        r
287    }
288
289    /// Return `true` if the grid contains no vertices.
290    #[must_use]
291    pub fn is_empty(&self) -> bool {
292        if self.dense {
293            self.exclusions.len() == self.size()
294        } else {
295            self.exclusions.is_empty()
296        }
297    }
298
299    /// Return `true` if no additional vertices can be set
300    /// (because they are all already set).
301    #[must_use]
302    pub fn is_full(&self) -> bool {
303        if self.dense {
304            self.exclusions.is_empty()
305        } else {
306            self.exclusions.len() == self.size()
307        }
308    }
309
310    /// Remove every existing vertex, and add all absent vertices.
311    /// If you see the grid as a black and white array, imagine that
312    /// the color are exchanged.
313    pub fn invert(&mut self) {
314        self.dense = !self.dense;
315    }
316
317    /// Check if a vertex is present.
318    #[must_use]
319    pub fn has_vertex(&self, vertex: (usize, usize)) -> bool {
320        self.is_inside(vertex) && (self.exclusions.contains(&vertex) ^ self.dense)
321    }
322
323    /// Check if an edge is present.
324    #[must_use]
325    pub fn has_edge(&self, v1: (usize, usize), v2: (usize, usize)) -> bool {
326        if !self.has_vertex(v1) || !self.has_vertex(v2) {
327            return false;
328        }
329        let x = v1.0.abs_diff(v2.0);
330        let y = v1.1.abs_diff(v2.1);
331        x + y == 1 || (x == 1 && y == 1 && self.diagonal_mode)
332    }
333
334    /// Iterate over edges.
335    #[must_use]
336    pub const fn edges(&self) -> EdgesIterator {
337        EdgesIterator {
338            grid: self,
339            x: 0,
340            y: 0,
341            i: 0,
342        }
343    }
344
345    /// Return the list of neighbours of a given vertex. If `vertex` is absent
346    /// from the grid, an empty list is returned. Only existing vertices will
347    /// be returned.
348    #[must_use]
349    pub fn neighbours(&self, vertex: (usize, usize)) -> Vec<(usize, usize)> {
350        if !self.has_vertex(vertex) {
351            return vec![];
352        }
353        let (x, y) = vertex;
354        let mut candidates = Vec::with_capacity(8);
355        if x > 0 {
356            candidates.push((x - 1, y));
357            if self.diagonal_mode {
358                if y > 0 {
359                    candidates.push((x - 1, y - 1));
360                }
361                if y + 1 < self.height {
362                    candidates.push((x - 1, y + 1));
363                }
364            }
365        }
366        if x + 1 < self.width {
367            candidates.push((x + 1, y));
368            if self.diagonal_mode {
369                if y > 0 {
370                    candidates.push((x + 1, y - 1));
371                }
372                if y + 1 < self.height {
373                    candidates.push((x + 1, y + 1));
374                }
375            }
376        }
377        if y > 0 {
378            candidates.push((x, y - 1));
379        }
380        if y + 1 < self.height {
381            candidates.push((x, y + 1));
382        }
383        candidates.retain(|&v| self.has_vertex(v));
384        candidates
385    }
386
387    /// Return a set of the indices reachable from a candidate starting point
388    /// and for which the given predicate is valid using BFS. This can be used for example
389    /// to implement a flood-filling algorithm. Since the indices are collected
390    /// into a collection, they can later be used without keeping a reference on the
391    /// matrix itself, e.g., to modify the grid.
392    ///
393    /// The search is done using a breadth first search (BFS) algorithm.
394    ///
395    /// # See also
396    ///
397    /// The [`dfs_reachable()`](`Self::dfs_reachable`) method performs a DFS search instead.
398    pub fn bfs_reachable<P>(
399        &self,
400        start: (usize, usize),
401        mut predicate: P,
402    ) -> BTreeSet<(usize, usize)>
403    where
404        P: FnMut((usize, usize)) -> bool,
405    {
406        bfs_reach(start, |&n| {
407            self.neighbours(n)
408                .into_iter()
409                .filter(|&n| predicate(n))
410                .collect::<Vec<_>>()
411        })
412        .collect()
413    }
414
415    /// Return a set of the indices reachable from a candidate starting point
416    /// and for which the given predicate is valid using BFS. This can be used for example
417    /// to implement a flood-filling algorithm. Since the indices are collected
418    /// into a collection, they can later be used without keeping a reference on the
419    /// matrix itself, e.g., to modify the grid.
420    ///
421    /// The search is done using a depth first search (DFS) algorithm.
422    ///
423    /// # See also
424    ///
425    /// The [`bfs_reachable()`](`Self::bfs_reachable`) method performs a BFS search instead.
426    pub fn dfs_reachable<P>(
427        &self,
428        start: (usize, usize),
429        mut predicate: P,
430    ) -> BTreeSet<(usize, usize)>
431    where
432        P: FnMut((usize, usize)) -> bool,
433    {
434        dfs_reach(start, |&n| {
435            self.neighbours(n)
436                .into_iter()
437                .filter(|&n| predicate(n))
438                .collect::<Vec<_>>()
439        })
440        .collect()
441    }
442
443    /// Iterate over vertices.
444    #[must_use]
445    pub fn iter(&self) -> GridIterator {
446        self.into_iter()
447    }
448
449    /// Distance between two potential vertices. If diagonal mode is
450    /// enabled, this is the maximum of both coordinates difference.
451    /// If diagonal mode is disabled, this is the Manhattan distance.
452    #[must_use]
453    pub fn distance(&self, a: (usize, usize), b: (usize, usize)) -> usize {
454        let (dx, dy) = (a.0.abs_diff(b.0), a.1.abs_diff(b.1));
455        if self.diagonal_mode {
456            dx.max(dy)
457        } else {
458            dx + dy
459        }
460    }
461
462    /// Build a grid from an arbitrary set of `(x, y)` coordinates. Coordinates will
463    /// be adjusted so that the returned grid is the smallest one containing
464    /// all the points while conserving horizontal and vertical distances
465    /// between them.
466    ///
467    /// This can be used for example to visualize data whose coordinates are
468    /// expressed using a non-usize integer type, such as `(isize, isize)`.
469    ///
470    /// This method returns `None` if any axis of any coordinate cannot be
471    /// represented as an `usize` once the minimum for this axis has been
472    /// subtracted.
473    ///
474    /// # Example
475    ///
476    /// ```
477    /// use pathfinding::prelude::Grid;
478    ///
479    /// let grid = Grid::from_coordinates(&[(2, 2), (3, 4)]).unwrap();
480    /// assert_eq!(vec![(0, 0), (1, 2)], grid.iter().collect::<Vec<_>>());
481    /// ```
482    pub fn from_coordinates<T>(points: &[(T, T)]) -> Option<Self>
483    where
484        T: Ord + Sub<Output = T> + Copy + Default + ToPrimitive,
485    {
486        let (min_x, min_y) = (
487            points
488                .iter()
489                .map(|(x, _)| x)
490                .min()
491                .copied()
492                .unwrap_or_default(),
493            points
494                .iter()
495                .map(|(_, y)| y)
496                .min()
497                .copied()
498                .unwrap_or_default(),
499        );
500        points
501            .iter()
502            .map(|(x, y)| Some(((*x - min_x).to_usize()?, (*y - min_y).to_usize()?)))
503            .collect()
504    }
505
506    /// Constrain a wrapped-around index so that it falls inside the
507    /// grid.
508    ///
509    /// # Examples
510    ///
511    /// ```rust
512    /// use pathfinding::grid::Grid;
513    ///
514    /// let grid = Grid::new(3, 5);
515    /// assert_eq!(grid.constrain((1, 2)), (1, 2));
516    /// assert_eq!(grid.constrain((10, -53)), (1, 2));
517    /// ```
518    #[must_use]
519    pub const fn constrain(&self, vertex: (isize, isize)) -> (usize, usize) {
520        (
521            constrain(vertex.0, self.width),
522            constrain(vertex.1, self.height),
523        )
524    }
525}
526
527impl FromIterator<(usize, usize)> for Grid {
528    fn from_iter<T>(iter: T) -> Self
529    where
530        T: IntoIterator<Item = (usize, usize)>,
531    {
532        let vertices = iter.into_iter().collect();
533        let mut width = 0;
534        let mut height = 0;
535        for &(x, y) in &vertices {
536            if x + 1 > width {
537                width = x + 1;
538            }
539            if y + 1 > height {
540                height = y + 1;
541            }
542        }
543        let mut grid = Self {
544            width,
545            height,
546            diagonal_mode: false,
547            dense: false,
548            exclusions: vertices,
549        };
550        grid.rebalance();
551        grid
552    }
553}
554
555/// Iterator returned by calling `.into_iter()` on a grid.
556pub struct GridIntoIterator {
557    grid: Grid,
558    x: usize,
559    y: usize,
560}
561
562impl Iterator for GridIntoIterator {
563    type Item = (usize, usize);
564
565    fn next(&mut self) -> Option<Self::Item> {
566        if self.grid.dense {
567            loop {
568                if self.y == self.grid.height {
569                    return None;
570                }
571                let r = (self.grid.has_vertex((self.x, self.y))).then_some((self.x, self.y));
572                self.x += 1;
573                if self.x == self.grid.width {
574                    self.x = 0;
575                    self.y += 1;
576                }
577                if r.is_some() {
578                    return r;
579                }
580            }
581        } else {
582            self.grid.exclusions.pop()
583        }
584    }
585}
586
587impl FusedIterator for GridIntoIterator {}
588
589impl IntoIterator for Grid {
590    type Item = (usize, usize);
591    type IntoIter = GridIntoIterator;
592
593    #[must_use]
594    fn into_iter(self) -> Self::IntoIter {
595        GridIntoIterator {
596            grid: self,
597            x: 0,
598            y: 0,
599        }
600    }
601}
602
603/// Iterator returned by calling `.iter()` on a grid.
604pub struct GridIterator<'a> {
605    grid: &'a Grid,
606    x: usize,
607    y: usize,
608}
609
610impl Iterator for GridIterator<'_> {
611    type Item = (usize, usize);
612
613    fn next(&mut self) -> Option<Self::Item> {
614        if self.grid.dense {
615            loop {
616                if self.y == self.grid.height {
617                    return None;
618                }
619                let r = (self.grid.has_vertex((self.x, self.y))).then_some((self.x, self.y));
620                self.x += 1;
621                if self.x == self.grid.width {
622                    self.x = 0;
623                    self.y += 1;
624                }
625                if r.is_some() {
626                    return r;
627                }
628            }
629        } else {
630            self.grid
631                .exclusions
632                .get_index(self.x)
633                .inspect(|_| {
634                    self.x += 1;
635                })
636                .copied()
637        }
638    }
639}
640
641impl FusedIterator for GridIterator<'_> {}
642
643impl<'a> IntoIterator for &'a Grid {
644    type Item = (usize, usize);
645    type IntoIter = GridIterator<'a>;
646
647    #[must_use]
648    fn into_iter(self) -> Self::IntoIter {
649        GridIterator {
650            grid: self,
651            x: 0,
652            y: 0,
653        }
654    }
655}
656
657/// Iterator returned by calling `.edges()` on a grid.
658pub struct EdgesIterator<'a> {
659    grid: &'a Grid,
660    x: usize,
661    y: usize,
662    i: usize,
663}
664
665impl Iterator for EdgesIterator<'_> {
666    type Item = ((usize, usize), (usize, usize));
667
668    fn next(&mut self) -> Option<Self::Item> {
669        loop {
670            if self.y == self.grid.height {
671                return None;
672            }
673            let x = self.x;
674            let y = self.y;
675            let other = match self.i {
676                0 => (x + 1, y),
677                1 => (x, y + 1),
678                2 => (x + 1, y + 1),
679                _ => (x - 1, y + 1),
680            };
681            self.i += 1;
682            if (x == 0 && self.i == 3) || self.i == 4 {
683                self.i = 0;
684                self.x += 1;
685                if self.x == self.grid.width {
686                    self.x = 0;
687                    self.y += 1;
688                }
689            }
690            if self.grid.has_edge((x, y), other) {
691                return Some(((x, y), other));
692            }
693        }
694    }
695}
696
697impl FusedIterator for EdgesIterator<'_> {}
698
699impl fmt::Debug for Grid {
700    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
701        let (present, absent) = if f.alternate() {
702            ('▓', '░')
703        } else {
704            ('#', '.')
705        };
706        let lines: Vec<_> = if f.sign_minus() {
707            (0..self.height).rev().collect()
708        } else {
709            (0..self.height).collect()
710        };
711        let last = *lines.last().unwrap();
712        for y in lines {
713            for x in 0..self.width {
714                write!(
715                    f,
716                    "{}",
717                    if self.has_vertex((x, y)) {
718                        present
719                    } else {
720                        absent
721                    }
722                )?;
723            }
724            if y != last {
725                writeln!(f)?;
726            }
727        }
728        Ok(())
729    }
730}
731
732impl From<&Matrix<bool>> for Grid {
733    fn from(matrix: &Matrix<bool>) -> Self {
734        let mut grid = Self::new(matrix.columns, matrix.rows);
735        for ((r, c), &v) in matrix.items() {
736            if v {
737                grid.add_vertex((c, r));
738            }
739        }
740        grid
741    }
742}
743
744impl From<Matrix<bool>> for Grid {
745    fn from(matrix: Matrix<bool>) -> Self {
746        Self::from(&matrix)
747    }
748}
749
750impl PartialEq for Grid {
751    fn eq(&self, other: &Self) -> bool {
752        self.vertices_len() == other.vertices_len()
753            && self.iter().zip(other.iter()).all(|(a, b)| a == b)
754    }
755}
756
757impl Eq for Grid {}