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}