cell_map/
cell_map.rs

1//! # `CellMap` implementation
2
3// ------------------------------------------------------------------------------------------------
4// IMPORTS
5// ------------------------------------------------------------------------------------------------
6
7use std::{
8    marker::PhantomData,
9    ops::{Index, IndexMut},
10    usize,
11};
12
13use nalgebra::{Affine2, Point2, Vector2};
14use ndarray::{s, Array2};
15use serde::{de::DeserializeOwned, Deserialize, Serialize};
16
17use crate::{
18    cell_map_file::CellMapFile,
19    extensions::Point2Ext,
20    iterators::{
21        layerers::Many,
22        slicers::{Cells, Line, Windows},
23        CellMapIter, CellMapIterMut,
24    },
25    map_metadata::CellMapMetadata,
26    Error, Layer,
27};
28
29// ------------------------------------------------------------------------------------------------
30// STRUCTS
31// ------------------------------------------------------------------------------------------------
32
33/// Provides a many-layer 2D map of cellular data.
34#[derive(Debug, Clone, Serialize, Deserialize)]
35#[serde(
36    try_from = "CellMapFile<L, T>",
37    into = "CellMapFile<L, T>",
38    bound = "T: Clone + Serialize + DeserializeOwned, L: Serialize + DeserializeOwned"
39)]
40pub struct CellMap<L, T>
41where
42    L: Layer,
43{
44    /// Stores each layer in the map as an [`ndarray::Array2<T>`].
45    ///
46    /// TODO:
47    /// When constgenerics is stabilised would be good to make this an array of `L::NUM_LAYERS`, to
48    /// avoid the vec allocation.
49    pub(crate) data: Vec<Array2<T>>,
50
51    /// Metadata associated with this map.
52    pub(crate) metadata: CellMapMetadata,
53
54    /// The original parameters supplied to `CellMap::new()`.
55    pub(crate) params: CellMapParams,
56
57    layer_type: PhantomData<L>,
58}
59
60/// Contains parameters required to construct a [`CellMap`]
61#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
62pub struct CellMapParams {
63    /// The size (resolution) of each cell in the map, in parent frame coordinates.
64    ///
65    /// # Default
66    ///
67    /// The default value is `[1.0, 1.0]`.
68    pub cell_size: Vector2<f64>,
69
70    /// The number of cells in the `x` and `y` directions.
71    ///
72    /// # Default
73    ///
74    /// The default value is [`Bounds::empty()`].
75    pub cell_bounds: Bounds,
76
77    /// The rotation of the map's Z axis about the parent Z axis in radians.
78    ///
79    /// # Default
80    ///
81    /// The default value is `0.0`.
82    pub rotation_in_parent_rad: f64,
83
84    /// The position of the origin of the map in the parent frame, in parent frame units.
85    ///
86    /// # Default
87    ///
88    /// The default value is `[0.0, 0.0]`.
89    pub position_in_parent: Vector2<f64>,
90
91    /// The precision to use when determining cell boundaries.
92    ///
93    /// This precision factor allows us to account for times when a cell position should fit into a
94    /// particular cell index, but due to floating point rounding does not. For example take a map
95    /// with a `cell_size = [0.1, 0.1]`, the cell index of the position `[0.7, 0.1]` should be `[7,
96    /// 1`], however the positions floating point index would be calculated as `[6.999999999999998,
97    /// 0.9999999999999999]`, which if `floor()`ed to fit into a `usize` would give the incorrect
98    /// index `[6, 0]`.
99    ///
100    /// When calculating cell index we therefore `floor` the floating point index unless it is
101    /// within `cell_size * cell_boundary_precision`, in which case we round up to the next cell.
102    /// Mutliplying by `cell_size` allows this value to be independent of the scale of the map.
103    ///
104    /// # Default
105    ///
106    /// The default value is `1e-10`.
107    pub cell_boundary_precision: f64,
108}
109
110/// Rectangular bounds describing the number of cells in each direction of the map.
111///
112/// These bounds are a half-open range, i.e. satisfied in the ranges:
113///  - $x_0 <= x < x_1$
114///  - $y_0 <= y < y_1$
115// NOTE: Range isn't uses since it's not Copy
116#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
117pub struct Bounds {
118    /// The bounds on the x axis, in the format (min, max),
119    pub x: (isize, isize),
120
121    /// The bounds on the y axis, in the format (min, max),
122    pub y: (isize, isize),
123}
124
125// ------------------------------------------------------------------------------------------------
126// IMPLS
127// ------------------------------------------------------------------------------------------------
128
129impl<L, T> CellMap<L, T>
130where
131    L: Layer,
132{
133    /// Creates a new map from the given data.
134    ///
135    /// If data is the wrong shape or has the wrong number of layers this function will return an
136    /// error.
137    pub fn new_from_data(params: CellMapParams, data: Vec<Array2<T>>) -> Result<Self, Error> {
138        if data.len() != L::NUM_LAYERS {
139            return Err(Error::WrongNumberOfLayers(L::NUM_LAYERS, data.len()));
140        }
141
142        if !data.is_empty() {
143            let layer_cells = (data[0].shape()[0], data[0].shape()[1]);
144
145            if layer_cells != params.cell_bounds.get_shape() {
146                return Err(Error::LayerWrongShape(
147                    layer_cells,
148                    params.cell_bounds.get_shape(),
149                ));
150            }
151        }
152
153        Ok(Self {
154            data,
155            metadata: params.into(),
156            params,
157            layer_type: PhantomData,
158        })
159    }
160
161    /// Returns the size of the cells in the map.
162    pub fn cell_size(&self) -> Vector2<f64> {
163        self.metadata.cell_size
164    }
165
166    /// Returns the number of cells in each direction of the map.
167    pub fn num_cells(&self) -> Vector2<usize> {
168        self.metadata.num_cells
169    }
170
171    /// Returns the bounds of this map
172    pub fn cell_bounds(&self) -> Bounds {
173        self.metadata.cell_bounds
174    }
175
176    /// Returns the parameters used to build this map.
177    pub fn params(&self) -> CellMapParams {
178        self.params
179    }
180
181    /// Gets the [`nalgebra::Affine2<f64>`] transformation between the map frame and the parent
182    /// frame.
183    pub fn to_parent(&self) -> Affine2<f64> {
184        self.metadata.to_parent
185    }
186
187    /// Moves this map relative to a new position and rotation relative to the parent frame.
188    ///
189    /// **Note:** This doesn't move the data relative to the map origin, the indexes into the map
190    /// remain the same, but the position of each cell in the map will change.
191    pub fn move_map(&mut self, position_in_parent: Vector2<f64>, rotation_in_parent_rad: f64) {
192        // Recalculate the map's to_parent affine
193        self.metadata.to_parent = CellMapMetadata::calc_to_parent(
194            position_in_parent,
195            rotation_in_parent_rad,
196            self.metadata.cell_size,
197        );
198
199        // Update the parameter values
200        self.params.position_in_parent = position_in_parent;
201        self.params.rotation_in_parent_rad = rotation_in_parent_rad;
202    }
203
204    /// Returns whether or not the given index is inside the map.
205    pub fn index_in_map(&self, index: Point2<usize>) -> bool {
206        self.metadata.is_in_map(index)
207    }
208
209    /// Returns whether or not the given parent-relative position is inside the map.
210    pub fn position_in_map(&self, position: Point2<f64>) -> bool {
211        self.index(position).is_some()
212    }
213
214    /// Get a reference to the value at the given layer and index. Returns `None` if the index is
215    /// outside the bounds of the map.
216    pub fn get(&self, layer: L, index: Point2<usize>) -> Option<&T> {
217        if self.index_in_map(index) {
218            Some(&self[(layer, index)])
219        } else {
220            None
221        }
222    }
223
224    /// Get a reference to the value at the given layer and index, without checking the bounds of
225    /// the map.
226    ///
227    /// # Safety
228    ///
229    /// This function will panic if `index` is outside the map.
230    pub unsafe fn get_unchecked(&self, layer: L, index: Point2<usize>) -> &T {
231        &self[(layer, index)]
232    }
233
234    /// Get a mutable reference to the value at the given layer and index. Returns `None` if the
235    /// index is outside the bounds of the map.
236    pub fn get_mut(&mut self, layer: L, index: Point2<usize>) -> Option<&mut T> {
237        if self.index_in_map(index) {
238            Some(&mut self[(layer, index)])
239        } else {
240            None
241        }
242    }
243
244    /// Get a mutable reference to the value at the given layer and index, without checking the
245    /// bounds of the map.
246    ///
247    /// # Safety
248    ///
249    /// This function will panic if `index` is outside the map.
250    pub unsafe fn get_mut_unchecked(&mut self, layer: L, index: Point2<usize>) -> &mut T {
251        &mut self[(layer, index)]
252    }
253
254    /// Set the given layer and index in the map to the given value. Returns an [`Error`] if the
255    /// index was outside the map.
256    pub fn set(&mut self, layer: L, index: Point2<usize>, value: T) -> Result<(), Error> {
257        if self.index_in_map(index) {
258            self[(layer, index)] = value;
259            Ok(())
260        } else {
261            Err(Error::IndexOutsideMap(index))
262        }
263    }
264
265    /// Set the given layer and index in the map to the given value, without checking if index is
266    /// the map.
267    ///
268    /// # Safety
269    ///
270    /// This function will panic if `index` is outside the map
271    pub unsafe fn set_unchecked(&mut self, layer: L, index: Point2<usize>, value: T) {
272        self[(layer, index)] = value;
273    }
274
275    /// Returns the position in the parent frame of the centre of the given cell index.
276    ///
277    /// Returns `None` if the given `index` is not inside the map.
278    pub fn position(&self, index: Point2<usize>) -> Option<Point2<f64>> {
279        self.metadata.position(index)
280    }
281
282    /// Returns the position in the parent frame of the centre of the given cell index, without
283    /// checking that the `index` is inside the map.
284    ///
285    /// # Safety
286    ///
287    /// This method won't panic if `index` is outside the map, but it's result can't be guaranteed
288    /// to be a position in the map.
289    pub fn position_unchecked(&self, index: Point2<usize>) -> Point2<f64> {
290        self.metadata.position_unchecked(index)
291    }
292
293    /// Get the cell index of the given poisition.
294    ///
295    /// Returns `None` if the given `position` is not inside the map.
296    pub fn index(&self, position: Point2<f64>) -> Option<Point2<usize>> {
297        self.metadata.index(position)
298    }
299
300    /// Get the cell index of the given poisition, without checking that the position is inside the
301    /// map.
302    ///
303    /// # Safety
304    ///
305    /// This function will not panic if `position` is outside the map, but use of the result to
306    /// index into the map is not guaranteed to be safe. It is possible for this function to return
307    /// a negative index value, which would indicate that the cell is outside the map.
308    pub unsafe fn index_unchecked(&self, position: Point2<f64>) -> Point2<isize> {
309        self.metadata.index_unchecked(position)
310    }
311
312    /// Returns an iterator over each cell in all layers of the map.
313    pub fn iter(&self) -> CellMapIter<'_, L, T, Many<L>, Cells> {
314        CellMapIter::<'_, L, T, Many<L>, Cells>::new_cells(self)
315    }
316
317    /// Returns a mutable iterator over each cell in all layers of the map.
318    pub fn iter_mut(&mut self) -> CellMapIterMut<'_, L, T, Many<L>, Cells> {
319        CellMapIterMut::<'_, L, T, Many<L>, Cells>::new_cells(self)
320    }
321
322    /// Returns an iterator over windows of cells in the map.
323    ///
324    /// The `semi_width` is half the size of the window in the x and y axes, not including
325    /// the central cell. E.g. to have a window which is in total 5x5, the `semi_window_size` needs
326    /// to be `Vector2::new(2, 2)`.
327    pub fn window_iter(
328        &self,
329        semi_width: Vector2<usize>,
330    ) -> Result<CellMapIter<'_, L, T, Many<L>, Windows>, Error> {
331        CellMapIter::<'_, L, T, Many<L>, Windows>::new_windows(self, semi_width)
332    }
333
334    /// Returns a mutable iterator over windows of cells in the map.
335    ///
336    /// The `semi_width` is half the size of the window in the x and y axes, not including
337    /// the central cell. E.g. to have a window which is in total 5x5, the `semi_window_size` needs
338    /// to be `Vector2::new(2, 2)`.
339    pub fn window_iter_mut(
340        &mut self,
341        semi_width: Vector2<usize>,
342    ) -> Result<CellMapIterMut<'_, L, T, Many<L>, Windows>, Error> {
343        CellMapIterMut::<'_, L, T, Many<L>, Windows>::new_windows(self, semi_width)
344    }
345
346    /// Returns an iterator over cells along the line joining `start_position` and
347    /// `end_position`, which are expressed as positions in the map's parent frame.
348    pub fn line_iter(
349        &self,
350        start_position: Point2<f64>,
351        end_position: Point2<f64>,
352    ) -> Result<CellMapIter<'_, L, T, Many<L>, Line>, Error> {
353        CellMapIter::<'_, L, T, Many<L>, Line>::new_line(self, start_position, end_position)
354    }
355
356    /// Returns a mutable iterator over cells along the line joining `start_position` and
357    /// `end_position`, which are expressed as positions in the map's parent frame.
358    pub fn line_iter_mut(
359        &mut self,
360        start_position: Point2<f64>,
361        end_position: Point2<f64>,
362    ) -> Result<CellMapIterMut<'_, L, T, Many<L>, Line>, Error> {
363        CellMapIterMut::<'_, L, T, Many<L>, Line>::new_line(self, start_position, end_position)
364    }
365}
366
367impl<L, T> CellMap<L, T>
368where
369    L: Layer + Serialize,
370    T: Clone + Serialize,
371{
372    /// Builds a new [`CellMapFile`] from the given map, which can be serialised or deserialised
373    /// using serde.
374    pub fn to_cell_map_file(&self) -> CellMapFile<L, T> {
375        CellMapFile::new(self)
376    }
377
378    /// Writes the map to the given path as a JSON file.
379    #[cfg(feature = "json")]
380    pub fn write_json<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), Error> {
381        let map_file = CellMapFile::new(&self);
382        map_file.write_json(path)
383    }
384}
385
386impl<L, T> CellMap<L, T>
387where
388    L: Layer + DeserializeOwned,
389    T: DeserializeOwned,
390{
391    /// Loads a map stored in JSON format at the given path.
392    #[cfg(feature = "json")]
393    pub fn from_json<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
394        let map_file = CellMapFile::from_json(path)?;
395        map_file.into_cell_map()
396    }
397}
398
399impl<L, T> CellMap<L, T>
400where
401    L: Layer,
402    T: Clone,
403{
404    /// Creates a new [`CellMap`] from the given params, filling each cell with `elem`.
405    pub fn new_from_elem(params: CellMapParams, elem: T) -> Self {
406        let data = vec![Array2::from_elem(params.cell_bounds.get_shape(), elem); L::NUM_LAYERS];
407
408        Self {
409            data,
410            metadata: params.into(),
411            params,
412            layer_type: PhantomData,
413        }
414    }
415}
416
417impl<L, T> CellMap<L, T>
418where
419    L: Layer,
420    T: Default + Clone,
421{
422    /// Creates a new [`CellMap`] from the given params, filling each cell with `T::default()`.
423    pub fn new(params: CellMapParams) -> Self {
424        let data =
425            vec![Array2::from_elem(params.cell_bounds.get_shape(), T::default()); L::NUM_LAYERS];
426
427        Self {
428            data,
429            metadata: params.into(),
430            params,
431            layer_type: PhantomData,
432        }
433    }
434
435    /// Resizes the map into the new bounds, filling any newly added cells with `T::default()`.
436    ///
437    /// Any cells that are in the map currently, which would be outside the new map, are removed.
438    // NOTE: It doesn't seem possible to resize an ndarray in place, so we have to allocate a new
439    // one.
440    pub fn resize(&mut self, new_bounds: Bounds) {
441        // Allocate new data
442        let mut data = vec![Array2::from_elem(new_bounds.get_shape(), T::default()); L::NUM_LAYERS];
443
444        // Get the slice describing the position of the old map inside the new map, based on the
445        // bounds. If there's no intersection then we can skip this step
446        if let Some(old_in_new) = new_bounds.get_slice_of_other(&self.metadata.cell_bounds) {
447            // Get the slice of new relative to old. Unwrap is ok sice we already know there's an
448            // intersection.
449            let new_in_old = self
450                .metadata
451                .cell_bounds
452                .get_slice_of_other(&new_bounds)
453                .unwrap();
454            for (new, old) in data.iter_mut().zip(self.data.iter()) {
455                new.slice_mut(s![
456                    old_in_new.y.0..old_in_new.y.1,
457                    old_in_new.x.0..old_in_new.x.1
458                ])
459                .assign(&old.slice(s![
460                    new_in_old.y.0..new_in_old.y.1,
461                    new_in_old.x.0..new_in_old.x.1
462                ]));
463            }
464        }
465
466        self.data = data;
467        self.metadata.cell_bounds = new_bounds;
468        self.params.cell_bounds = new_bounds;
469        self.metadata.num_cells = new_bounds.get_num_cells();
470    }
471
472    /// Merge `other` into self, resizing `self` so that `other` will be fully included in the map.
473    ///
474    /// Both maps should belong to the same parent frame, and `other.cell_size <= self.cell_size`.
475    /// For `other`s that have larger cells than `self` you should implement your own merge functon
476    /// based on this one that could for example use 2D linear interpolation.
477    ///
478    /// `func` is responsible for actually merging data in both `self` and `other` into a single
479    /// new value in `self`. The first argument is the value of the cell in `self`, while the
480    /// second argument will be the values from cells in `other` whose centres lie within the cell
481    /// in `self`.
482    pub fn merge<F: Fn(&T, &[T]) -> T>(&mut self, other: &CellMap<L, T>, func: F) {
483        // First get the bounds of `other` wrt `self`, which we have to do by accounting for the
484        // potential different alignment of `other` wrt `parent`. We do this by getting the corner
485        // points, transforming from `other` to `parent`, then from `parent` to `self`. We have to
486        // transform all corner points because rotation may lead to the corners being in different
487        // positions than when aligned to `other`.
488        let other_bounds = other.cell_bounds();
489        let corners_in_other = vec![
490            Point2::new(other_bounds.x.0, other_bounds.y.0).cast(),
491            Point2::new(other_bounds.x.1, other_bounds.y.0).cast() + Vector2::new(1.0, 0.0),
492            Point2::new(other_bounds.x.0, other_bounds.y.1).cast() + Vector2::new(0.0, 1.0),
493            Point2::new(other_bounds.x.1, other_bounds.y.1).cast() + Vector2::new(1.0, 1.0),
494        ];
495        let corners_in_parent: Vec<Point2<f64>> = corners_in_other
496            .iter()
497            .map(|c| other.to_parent().transform_point(c))
498            .collect();
499        let other_bl_parent = Point2::new(
500            corners_in_parent
501                .iter()
502                .min_by_key(|c| c.x.floor() as isize)
503                .unwrap()
504                .x
505                .floor(),
506            corners_in_parent
507                .iter()
508                .min_by_key(|c| c.y.floor() as isize)
509                .unwrap()
510                .y
511                .floor(),
512        );
513        let other_ur_parent = Point2::new(
514            corners_in_parent
515                .iter()
516                .max_by_key(|c| c.x.ceil() as isize)
517                .unwrap()
518                .x
519                .ceil(),
520            corners_in_parent
521                .iter()
522                .max_by_key(|c| c.y.ceil() as isize)
523                .unwrap()
524                .y
525                .ceil(),
526        );
527        let other_in_self =
528            Bounds::from_corner_positions(&self.metadata, other_bl_parent, other_ur_parent);
529        let store_offset = Vector2::new(
530            other_in_self.x.0.clamp(0, self.num_cells().x as isize) as usize,
531            other_in_self.y.0.clamp(0, self.num_cells().y as isize) as usize,
532        );
533
534        // Calculate the union of both bounds
535        let new_bounds = self.cell_bounds().union(&other_in_self);
536
537        // Resize self
538        self.resize(new_bounds);
539
540        // Get the index offset to go from an index into self to the 2D storage array (see store)
541        let store_slice_in_new = if let Some(slice) = new_bounds.get_slice_of_other(&other_in_self)
542        {
543            slice
544        } else {
545            unreachable!("Other was not inside self's new bounds");
546        };
547
548        // For each layer in the map
549        for layer in L::all() {
550            // Create a new array of size other_in_self, which will hold a copy of all items in
551            // other which fall into each cell in self.
552            let mut store: Array2<Vec<T>> = Array2::default(other_in_self.get_shape());
553
554            // For each cell in other get its position in parent, convert that to a cell index in
555            // self, and add that cell's value to the store
556            for ((_, pos), val) in other.iter().layer(layer.clone()).positioned() {
557                // The index of pos in self
558                if let Some(idx) = self.index(pos) {
559                    // Get the index into the store array by subtracting the store offset
560                    let store_idx = (idx.cast() - store_offset).map(|e| e as usize);
561
562                    // Mutate the store vector by pushing val into it
563                    if let Some(vec) = store.get_mut(store_idx.as_array2_index()) {
564                        vec.push(val.clone());
565                    } else {
566                        unreachable!("Store index {} was invalid", store_idx);
567                    }
568                } else {
569                    // Point was outside the map, this shouldn't happen
570                    unreachable!("Point in other ({}) was outside self during merge", pos);
571                }
572            }
573
574            // Iterate over the store and self, calling the merge function with the value in self
575            // and the values in the store
576            for (self_val, store_vec) in self.data[layer.to_index()]
577                .slice_mut(s![
578                    store_slice_in_new.y.0..store_slice_in_new.y.1,
579                    store_slice_in_new.x.0..store_slice_in_new.x.1,
580                ])
581                .iter_mut()
582                .zip(store.iter())
583            {
584                *self_val = func(self_val, store_vec.as_slice());
585            }
586        }
587    }
588}
589
590impl<L, T> Index<L> for CellMap<L, T>
591where
592    L: Layer,
593{
594    type Output = Array2<T>;
595
596    fn index(&self, index: L) -> &Self::Output {
597        &self.data[index.to_index()]
598    }
599}
600
601impl<L, T> IndexMut<L> for CellMap<L, T>
602where
603    L: Layer,
604{
605    fn index_mut(&mut self, index: L) -> &mut Self::Output {
606        &mut self.data[index.to_index()]
607    }
608}
609
610impl<L, T> Index<(L, Point2<usize>)> for CellMap<L, T>
611where
612    L: Layer,
613{
614    type Output = T;
615
616    fn index(&self, index: (L, Point2<usize>)) -> &Self::Output {
617        &self[index.0][(index.1.y, index.1.x)]
618    }
619}
620
621impl<L, T> IndexMut<(L, Point2<usize>)> for CellMap<L, T>
622where
623    L: Layer,
624{
625    fn index_mut(&mut self, index: (L, Point2<usize>)) -> &mut Self::Output {
626        &mut self[index.0][(index.1.y, index.1.x)]
627    }
628}
629
630impl Default for CellMapParams {
631    fn default() -> Self {
632        Self {
633            cell_size: Vector2::new(1.0, 1.0),
634            cell_bounds: Bounds::empty(),
635            cell_boundary_precision: 1e-10,
636            rotation_in_parent_rad: 0.0,
637            position_in_parent: Vector2::zeros(),
638        }
639    }
640}
641
642impl Bounds {
643    /// Creates a new empty (zero sized) bound
644    pub fn empty() -> Self {
645        Self {
646            x: (0, 0),
647            y: (0, 0),
648        }
649    }
650
651    /// Returns if the bounds are valid or not, i.e. if the minimum is larger than the maximum.
652    pub fn is_valid(&self) -> bool {
653        self.x.0 <= self.x.1 && self.y.0 <= self.y.1
654    }
655
656    /// Creates a new bound from the given max and min cell indices in the x and y axes.
657    ///
658    /// Must satisfy:
659    ///  - $x_0 <= x_1$
660    ///  - $y_0 <= y_1$
661    pub fn new(x: (isize, isize), y: (isize, isize)) -> Result<Self, Error> {
662        let bounds = Self { x, y };
663
664        if bounds.is_valid() {
665            Ok(bounds)
666        } else {
667            Err(Error::InvalidBounds(bounds))
668        }
669    }
670
671    /// Creates a new bound from the given opposing corners of the a rectangle.
672    ///
673    /// If the corners do not satisfy `all(bottom_left <= upper_right)` the bounds will be invalid
674    /// and an error is returned.
675    pub fn from_corners(
676        bottom_left: Point2<isize>,
677        upper_right: Point2<isize>,
678    ) -> Result<Self, Error> {
679        let bounds = Self {
680            x: (bottom_left.x, upper_right.x),
681            y: (bottom_left.y, upper_right.y),
682        };
683
684        if bounds.is_valid() {
685            Ok(bounds)
686        } else {
687            Err(Error::InvalidBounds(bounds))
688        }
689    }
690
691    /// Creates a new bound from the given opposing corners of the a rectangle, but the corners do
692    /// not have to be sorted in bottom_left, upper_right order.
693    ///
694    /// This function will automatically decide which points are provided such that the bounds will
695    /// be valid.
696    pub fn from_corners_unsorted(a: Point2<isize>, b: Point2<isize>) -> Self {
697        Self {
698            x: (a.x.min(b.x), a.x.max(b.x)),
699            y: (a.y.min(b.y), a.y.max(b.y)),
700        }
701    }
702
703    /// Creates a new bound from the given corner positions, which do not have to be in any order.
704    ///
705    /// The metadata parameter will be used to map from parent frame position into a map frame.
706    pub(crate) fn from_corner_positions(
707        metadata: &CellMapMetadata,
708        a: Point2<f64>,
709        b: Point2<f64>,
710    ) -> Self {
711        // Get the map-rel cell of each point
712        let cell_a = metadata.get_cell(a);
713        let cell_b = metadata.get_cell(b);
714
715        // Build the bounds
716        Self::from_corners_unsorted(cell_a, cell_b)
717    }
718
719    /// Converts this bounds into a pair of corners, the bottom left and upper right corners
720    /// respectively.
721    pub fn as_corners(&self) -> (Point2<isize>, Point2<isize>) {
722        (
723            Point2::new(self.x.0, self.y.0),
724            Point2::new(self.x.1, self.y.1),
725        )
726    }
727
728    /// Checks if the given point is inside the bounds
729    pub fn contains(&self, point: Point2<isize>) -> bool {
730        self.x.0 <= point.x && point.x < self.x.1 && self.y.0 <= point.y && point.y < self.y.1
731    }
732
733    /// Gets the value of the point as an index into an array bounded by this `Bounds`.
734    ///
735    /// If the point is outside the bounds `None` is returned
736    pub fn get_index(&self, point: Point2<isize>) -> Option<Point2<usize>> {
737        if self.contains(point) {
738            let unchecked = unsafe { self.get_index_unchecked(point) };
739
740            // Have already checked that the point is inside the bounds, no need to check again
741            Some(Point2::new(unchecked.x as usize, unchecked.y as usize))
742        } else {
743            None
744        }
745    }
746
747    /// Gets the value of the point as an index into an array bounded by this `Bounds`.
748    ///
749    /// # Safety
750    ///
751    /// This function will not panic if `point` is outside the map, but use of the result to
752    /// index into the map is not guaranteed to be safe. It is possible for this function to return
753    /// a negative index value, which would indicate that the cell is outside the map.
754    pub unsafe fn get_index_unchecked(&self, point: Point2<isize>) -> Point2<isize> {
755        Point2::new(point.x - self.x.0, point.y - self.y.0)
756    }
757
758    /// Gets the shape of this rectangle in a format that `ndarray` will accept.
759    ///
760    /// NOTE: shape order is (y, x), not (x, y).
761    pub fn get_shape(&self) -> (usize, usize) {
762        (
763            (self.y.1 - self.y.0) as usize,
764            (self.x.1 - self.x.0) as usize,
765        )
766    }
767
768    /// Gets the number of cells as a vector.
769    pub fn get_num_cells(&self) -> Vector2<usize> {
770        let shape = self.get_shape();
771        Vector2::new(shape.1, shape.0)
772    }
773
774    /// Gets the intersection of self with other, returning `None` if the two do not intersect.
775    pub fn intersect(&self, other: &Bounds) -> Option<Bounds> {
776        Bounds::new(
777            (self.x.0.max(other.x.0), self.x.1.min(other.x.1)),
778            (self.y.0.max(other.y.0), self.y.1.min(other.y.1)),
779        )
780        .ok()
781    }
782
783    /// Get the union of `self` with `other`, effectively this is the axis aligned bounding box of
784    /// `self` and `other`.
785    ///
786    /// If both bounds are empty this bound will be empty.
787    pub fn union(&self, other: &Bounds) -> Bounds {
788        Bounds::new(
789            (self.x.0.min(other.x.0), self.x.1.max(other.x.1)),
790            (self.y.0.min(other.y.0), self.y.1.max(other.y.1)),
791        )
792        .unwrap_or_default()
793    }
794
795    /// Gets the slice of other within self, cropping other so it fits within self.
796    ///
797    /// Note that slices are a pair of (min, max) half-open bounds that describe the slice into an
798    /// array, i.e. they are indices.
799    pub fn get_slice_of_other(&self, other: &Bounds) -> Option<Vector2<(usize, usize)>> {
800        // First get intersection of the two bounds in the origin frame
801        let intersect = self.intersect(other)?;
802
803        // Rebase the intersection to be a slice relative to the start of self, i.e. subtract the
804        // min bound on each axis from both min and max of the intersection
805        Some(Vector2::new(
806            (
807                (intersect.x.0 - self.x.0) as usize,
808                (intersect.x.1 - self.x.0) as usize,
809            ),
810            (
811                (intersect.y.0 - self.y.0) as usize,
812                (intersect.y.1 - self.y.0) as usize,
813            ),
814        ))
815    }
816}
817
818impl Default for Bounds {
819    fn default() -> Self {
820        Self::empty()
821    }
822}