density_mesh_core/
map.rs

1use crate::Scalar;
2use serde::{Deserialize, Serialize};
3
4/// Error thrown during density map generation.
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum DensityMapError {
7    /// Wrong data length.
8    /// (provided, expected)
9    WrongDataLength(usize, usize),
10}
11
12/// Density map that contains density data and steepness per pixel.
13#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
14pub struct DensityMap {
15    width: usize,
16    height: usize,
17    scale: usize,
18    data: Vec<Scalar>,
19    steepness: Vec<Scalar>,
20}
21
22impl DensityMap {
23    /// Create new density map.
24    ///
25    /// # Arguments
26    /// * `width` - Columns.
27    /// * `height` - Rows.
28    /// * `scale` - Scale.
29    /// * `data` - Raw pixel data.
30    ///
31    /// # Returns
32    /// Density map or error.
33    ///
34    /// # Examples
35    /// ```
36    /// use density_mesh_core::prelude::*;
37    ///
38    /// assert!(DensityMap::new(2, 2, 1, vec![0, 1, 2, 3]).is_ok());
39    /// assert_eq!(
40    ///     DensityMap::new(1, 2, 1, vec![0, 1, 2, 3]),
41    ///     Err(DensityMapError::WrongDataLength(4, 2)),
42    /// );
43    /// ```
44    pub fn new(
45        width: usize,
46        height: usize,
47        scale: usize,
48        data: Vec<u8>,
49    ) -> Result<Self, DensityMapError> {
50        if data.len() == width * height {
51            let data = data
52                .into_iter()
53                .map(|v| v as Scalar / 255.0)
54                .collect::<Vec<_>>();
55            let steepness = (0..data.len())
56                .map(|i| {
57                    let col = (i % width) as isize;
58                    let row = (i / width) as isize;
59                    let mut result = 0.0;
60                    for x in (col - 1)..(col + 1) {
61                        for y in (row - 1)..(row + 1) {
62                            let a = Self::raw_value(x, y, width, height, &data);
63                            let b = Self::raw_value(x + 1, y, width, height, &data);
64                            let c = Self::raw_value(x + 1, y + 1, width, height, &data);
65                            let d = Self::raw_value(x, y + 1, width, height, &data);
66                            let ab = (a - b).abs();
67                            let cd = (c - d).abs();
68                            let ac = (a - c).abs();
69                            let bd = (b - d).abs();
70                            let ad = (a - d).abs();
71                            let bc = (b - c).abs();
72                            result += (ab + cd + ac + bd + ad + bc) / 12.0;
73                        }
74                    }
75                    result
76                })
77                .collect::<Vec<_>>();
78            Ok(Self {
79                width,
80                height,
81                scale,
82                data,
83                steepness,
84            })
85        } else {
86            Err(DensityMapError::WrongDataLength(data.len(), width * height))
87        }
88    }
89
90    /// Returns scale.
91    pub fn scale(&self) -> usize {
92        self.scale
93    }
94
95    /// Returns scaled width.
96    pub fn width(&self) -> usize {
97        self.width * self.scale.max(1)
98    }
99
100    /// Returns scaled height.
101    pub fn height(&self) -> usize {
102        self.height * self.scale.max(1)
103    }
104
105    /// Returns unscaled width.
106    pub fn unscaled_width(&self) -> usize {
107        self.width
108    }
109
110    /// Returns unscaled height.
111    pub fn unscaled_height(&self) -> usize {
112        self.height
113    }
114
115    /// Returns values buffer.
116    pub fn values(&self) -> &[Scalar] {
117        &self.data
118    }
119
120    /// Returns steepness buffer.
121    pub fn steepness(&self) -> &[Scalar] {
122        &self.steepness
123    }
124
125    /// Returns value at given point or 0 if out of bounds.
126    ///
127    /// # Arguments
128    /// * `point` - (X, Y)
129    pub fn value_at_point(&self, point: (isize, isize)) -> Scalar {
130        let scale = self.scale.max(1) as isize;
131        let col = point.0 / scale;
132        let row = point.1 / scale;
133        if col >= 0 && col < self.width as _ && row >= 0 && row < self.height as _ {
134            self.data
135                .get(row as usize * self.width + col as usize)
136                .copied()
137                .unwrap_or(0.0)
138        } else {
139            0.0
140        }
141    }
142
143    /// Returns steepness at given point or 0 if out of bounds.
144    ///
145    /// # Arguments
146    /// * `point` - (X, Y)
147    pub fn steepness_at_point(&self, point: (isize, isize)) -> Scalar {
148        let scale = self.scale.max(1) as isize;
149        let col = point.0 / scale;
150        let row = point.1 / scale;
151        if col >= 0 && col < self.width as _ && row >= 0 && row < self.height as _ {
152            self.steepness
153                .get(row as usize * self.width + col as usize)
154                .copied()
155                .unwrap_or(0.0)
156        } else {
157            0.0
158        }
159    }
160
161    /// Returns iterator over values and steepness buffers.
162    ///
163    /// # Examples
164    /// ```
165    /// use density_mesh_core::prelude::*;
166    ///
167    /// let map = DensityMap::new(2, 2, 1, vec![2, 2, 4, 4])
168    ///     .unwrap()
169    ///     .value_steepness_iter()
170    ///     .collect::<Vec<_>>();
171    /// assert_eq!(
172    ///     map,
173    ///     vec![
174    ///         (0, 0, 0.007843138, 0.011764706),
175    ///         (1, 0, 0.007843138, 0.011764707),
176    ///         (0, 1, 0.015686275, 0.01633987),
177    ///         (1, 1, 0.015686275, 0.01633987),
178    ///     ],
179    /// );
180    /// ```
181    pub fn value_steepness_iter<'a>(
182        &'a self,
183    ) -> impl Iterator<Item = (usize, usize, Scalar, Scalar)> + 'a {
184        self.data
185            .iter()
186            .zip(self.steepness.iter())
187            .enumerate()
188            .map(move |(i, (v, s))| (i % self.width, i / self.width, *v, *s))
189    }
190
191    /// Change density map region data (replace "pixels") - this recalculates internals.
192    ///
193    /// # Arguments
194    /// * `col` - Column index.
195    /// * `row` - Row index.
196    /// * `width` - Number of columns.
197    /// * `height` - Number of rows.
198    /// * `data` - Data to replace with.
199    pub fn change(
200        &mut self,
201        col: usize,
202        row: usize,
203        width: usize,
204        height: usize,
205        data: Vec<u8>,
206    ) -> Result<(), DensityMapError> {
207        if col == 0 && row == 0 && width == self.width && height == self.height {
208            *self = Self::new(width, height, self.scale, data)?;
209            Ok(())
210        } else if data.len() == width * height {
211            for (i, v) in data.into_iter().enumerate() {
212                let x = col + i % width;
213                let y = row + i / width;
214                self.data[y * self.width + x] = v as Scalar / 255.0;
215            }
216            let fx = col.checked_sub(1).unwrap_or(col);
217            let fy = row.checked_sub(1).unwrap_or(row);
218            let tx = (col + width + 1).min(self.width);
219            let ty = (row + height + 1).min(self.height);
220            for row in fy..ty {
221                for col in fx..tx {
222                    let mut result = 0.0;
223                    {
224                        let col = col as isize;
225                        let row = row as isize;
226                        for x in (col - 1)..(col + 1) {
227                            for y in (row - 1)..(row + 1) {
228                                let a = Self::raw_value(x, y, self.width, self.height, &self.data);
229                                let b =
230                                    Self::raw_value(x + 1, y, self.width, self.height, &self.data);
231                                let c = Self::raw_value(
232                                    x + 1,
233                                    y + 1,
234                                    self.width,
235                                    self.height,
236                                    &self.data,
237                                );
238                                let d =
239                                    Self::raw_value(x, y + 1, self.width, self.height, &self.data);
240                                let ab = (a - b).abs();
241                                let cd = (c - d).abs();
242                                let ac = (a - c).abs();
243                                let bd = (b - d).abs();
244                                let ad = (a - d).abs();
245                                let bc = (b - c).abs();
246                                result += (ab + cd + ac + bd + ad + bc) / 12.0;
247                            }
248                        }
249                    }
250                    self.steepness[row * self.width + col] = result;
251                }
252            }
253            Ok(())
254        } else {
255            Err(DensityMapError::WrongDataLength(data.len(), width * height))
256        }
257    }
258
259    fn raw_value(x: isize, y: isize, w: usize, h: usize, data: &[Scalar]) -> Scalar {
260        if x >= 0 && x < w as _ && y >= 0 && y < h as _ {
261            data[y as usize * w + x as usize]
262        } else {
263            0.0
264        }
265    }
266}