cell_map/
lib.rs

1//! # `cell-map`: many-layer 2D cellular maps
2//!
3//! This crate provides the `CellMap` type, a 2D map with many layers comprised of
4//! cells that can store arbitrary data. It is based on
5//! [ANYbotics/grid_map](https://github.com/ANYbotics/grid_map), a C++ ROS package
6//! which provides the same type of data structre.
7//!
8//! `CellMap` uses `ndarray::Array2` to store its data in an efficient and
9//! scalable format. It also uses `nalgebra` types for expressing vectors and
10//! points.
11//!
12//! ## Getting Started
13//!
14//! ### Layers
15//!
16//! Each layer of the cell map is represented by its own `ndarray::Array2` array.
17//! The map indexes each layer by an enum implementing the `Layer` trait. A derive
18//! macro is provided to simplify this, for example:
19//!
20//! ```rust
21//! use cell_map::Layer;
22//!
23//! #[derive(Layer, Clone, Debug)]
24//! enum MyLayer {
25//!     Height,
26//!     Gradient,
27//!     Roughness
28//! }
29//! ```
30//!
31//! The `Layer` trait is required to be `Clone`, and is recommended to be `Debug`.
32//!
33//! ### Creating a `CellMap`
34//!
35//! To create a new map:
36//!
37//! ```rust
38//! use cell_map::{CellMap, CellMapParams, Layer, Bounds};
39//! use nalgebra::Vector2;
40//!
41//! # #[derive(Layer, Clone, Debug)]
42//! # enum MyLayer {
43//! #     Height,
44//! #     Gradient,
45//! #     Roughness
46//! # }
47//! // Creates a new 5x5 map where each cell is 1.0 units wide, which is centred on (0, 0), with
48//! // all elements initialised to 1.0. The `cell_bounds` parameter allows you to choose how many
49//! // cells are in the map by giving the min and max cell indexes (including negative indexes).
50//! let my_map = CellMap::<MyLayer, f64>::new_from_elem(
51//!     CellMapParams {
52//!         cell_size: Vector2::new(1.0, 1.0),
53//!         cell_bounds: Bounds::new((0, 5), (0, 5)).unwrap(),
54//!         ..Default::default()
55//!     },
56//!     1.0,
57//! );
58//! ```
59//!
60//! ### Iterating Over Cells
61//!
62//! [`CellMap`] provides methods to produce iterators over its data:
63//!   - [`CellMap::iter()`] gives an iterator over all cells in every layer of the map
64//!   - [`CellMap::window_iter()`] gives an iterator over rectangular windows into the map
65//!   - [`CellMap::line_iter()`] gives an iterator of cells between two world points
66//!
67//! All iterators also provide a mutable variant, and more iterators are planned
68//! in the future!
69//!
70//! You can modify iterators so they produce their indexes, as well as controlling which layers the
71//! data comes from. See [`iterators`] for more information.
72//!
73//! ```rust
74//! # use cell_map::{CellMap, CellMapParams, Layer, Bounds};
75//! # use nalgebra::Vector2;
76//! #
77//! # #[derive(Layer, Clone, Debug)]
78//! # enum MyLayer {
79//! #     Height,
80//! #     Gradient,
81//! #     Roughness
82//! # }
83//! #
84//! # // Creates a new 5x5 map where each cell is 1.0 units wide, which is centred on (0, 0), with
85//! # // all elements initialised to 1.0.
86//! # let mut my_map = CellMap::<MyLayer, f64>::new_from_elem(
87//! #     CellMapParams {
88//! #         cell_size: Vector2::new(1.0, 1.0),
89//! #         cell_bounds: Bounds::new((0, 5), (0, 5)).unwrap(),
90//! #         ..Default::default()
91//! #     },
92//! #     1.0,
93//! # );
94//! // Check all the cells in our map are 1, this will be true
95//! assert!(my_map.iter().all(|&v| v == 1.0));
96//!
97//! // Use a window iterator to change all cells not on the border of the map to 2
98//! my_map.window_iter_mut(Vector2::new(1, 1)).unwrap().for_each(|mut v| {
99//!     v[(1, 1)] = 2.0;
100//! });
101//!
102//! // Overwrite all values on the Roughness layer to be zero
103//! my_map.iter_mut().layer(MyLayer::Roughness).for_each(|v| *v = 0.0);
104//!
105//! // Check that our map is how we expect it
106//! for ((layer, cell), &value) in my_map.iter().indexed() {
107//!     if matches!(layer, MyLayer::Roughness) {
108//!         assert_eq!(value, 0.0);
109//!     }
110//!     else if cell.x == 0 || cell.x == 4 || cell.y == 0 || cell.y == 4 {
111//!         assert_eq!(value, 1.0);
112//!     }
113//!     else {
114//!         assert_eq!(value, 2.0);
115//!     }
116//! }
117//! ```
118
119#![warn(missing_docs)]
120#![warn(missing_copy_implementations)]
121#![warn(missing_debug_implementations)]
122
123// ------------------------------------------------------------------------------------------------
124// MODULES
125// ------------------------------------------------------------------------------------------------
126
127#[macro_use]
128mod macros;
129
130pub(crate) mod cell_map;
131pub mod cell_map_file;
132pub mod error;
133pub(crate) mod extensions;
134pub mod iterators;
135mod layer;
136mod map_metadata;
137#[cfg(test)]
138mod tests;
139
140// ------------------------------------------------------------------------------------------------
141// EXPORTS
142// ------------------------------------------------------------------------------------------------
143
144pub use crate::cell_map::{Bounds, CellMap, CellMapParams};
145pub use cell_map_macro::Layer;
146pub use error::Error;
147pub use layer::Layer;
148
149// ------------------------------------------------------------------------------------------------
150// USEFUL TEST UTILITIES
151// ------------------------------------------------------------------------------------------------
152
153#[cfg(feature = "debug_maps")]
154use serde::Serialize;
155
156/// Writes the given map to the given location, prepending "_debug_" to the name.
157#[cfg(feature = "debug_maps")]
158pub fn write_debug_map<L: Layer + Serialize, T: Serialize + Clone>(
159    map: &CellMap<L, T>,
160    name: &str,
161) {
162    #[cfg(feature = "debug_maps")]
163    {
164        let file_name = &format!("_debug_{}_map.json", name);
165        map.write_json(file_name)
166            .expect("Failed to write debug map!");
167    }
168}
169#[cfg(test)]
170#[macro_use]
171pub(crate) mod test_utils {
172
173    use serde::Serialize;
174
175    use crate::Layer;
176
177    #[derive(Clone, Copy, Debug, Serialize)]
178    #[allow(dead_code)]
179    pub enum TestLayers {
180        Layer0,
181        Layer1,
182        Layer2,
183    }
184
185    // Have to do a manual impl because the derive doesn't like working inside this crate, for some
186    // reason
187    impl Layer for TestLayers {
188        const NUM_LAYERS: usize = 3;
189        const FIRST: Self = Self::Layer0;
190        fn to_index(&self) -> usize {
191            match self {
192                Self::Layer0 => 0,
193                Self::Layer1 => 1,
194                Self::Layer2 => 2,
195            }
196        }
197
198        fn from_index(index: usize) -> Self {
199            match index {
200                0 => Self::Layer0,
201                1 => Self::Layer1,
202                2 => Self::Layer2,
203                _ => panic!(
204                    "Got a layer index of {} but there are only {} layers",
205                    index,
206                    Self::NUM_LAYERS
207                ),
208            }
209        }
210
211        fn all() -> Vec<Self> {
212            vec![Self::Layer0, Self::Layer1, Self::Layer2]
213        }
214    }
215}