terrain_generator/
grid.rs1use std::ops::{Index, IndexMut};
10
11#[derive(Clone, Debug, PartialEq)]
16pub struct Grid<T> {
17 width: usize,
18 height: usize,
19 data: Vec<T>,
20}
21
22impl<T: Clone> Grid<T> {
23 pub fn new(width: usize, height: usize, value: T) -> Self {
28 assert!(width > 0 && height > 0, "grid dimensions must be positive");
29 Self {
30 width,
31 height,
32 data: vec![value; width * height],
33 }
34 }
35}
36
37impl<T> Grid<T> {
38 pub fn from_vec(width: usize, height: usize, data: Vec<T>) -> Self {
43 assert!(width > 0 && height > 0, "grid dimensions must be positive");
44 assert_eq!(
45 data.len(),
46 width * height,
47 "buffer length must equal width * height"
48 );
49 Self {
50 width,
51 height,
52 data,
53 }
54 }
55
56 #[inline]
58 pub fn width(&self) -> usize {
59 self.width
60 }
61
62 #[inline]
64 pub fn height(&self) -> usize {
65 self.height
66 }
67
68 #[inline]
70 pub fn len(&self) -> usize {
71 self.data.len()
72 }
73
74 #[inline]
76 pub fn is_empty(&self) -> bool {
77 false
78 }
79
80 #[inline]
85 pub fn idx(&self, x: usize, y: usize) -> usize {
86 debug_assert!(x < self.width && y < self.height);
87 y * self.width + x
88 }
89
90 #[inline]
92 pub fn coords(&self, idx: usize) -> (usize, usize) {
93 (idx % self.width, idx / self.width)
94 }
95
96 #[inline]
98 pub fn as_slice(&self) -> &[T] {
99 &self.data
100 }
101
102 #[inline]
104 pub fn as_mut_slice(&mut self) -> &mut [T] {
105 &mut self.data
106 }
107
108 pub fn into_vec(self) -> Vec<T> {
110 self.data
111 }
112
113 pub fn iter(&self) -> std::slice::Iter<'_, T> {
115 self.data.iter()
116 }
117
118 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
120 self.data.iter_mut()
121 }
122}
123
124impl<T> Index<(usize, usize)> for Grid<T> {
125 type Output = T;
126
127 #[inline]
128 fn index(&self, (x, y): (usize, usize)) -> &T {
129 &self.data[self.idx(x, y)]
130 }
131}
132
133impl<T> IndexMut<(usize, usize)> for Grid<T> {
134 #[inline]
135 fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut T {
136 let idx = self.idx(x, y);
137 &mut self.data[idx]
138 }
139}
140
141impl<'a, T> IntoIterator for &'a Grid<T> {
142 type Item = &'a T;
143 type IntoIter = std::slice::Iter<'a, T>;
144
145 fn into_iter(self) -> Self::IntoIter {
146 self.data.iter()
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn round_trips_between_coords_and_flat_index() {
156 let grid = Grid::new(7, 5, 0u8);
157 for y in 0..5 {
158 for x in 0..7 {
159 assert_eq!(grid.coords(grid.idx(x, y)), (x, y));
160 }
161 }
162 }
163
164 #[test]
165 fn indexing_is_row_major() {
166 let mut grid = Grid::new(3, 2, 0i32);
167 grid[(2, 1)] = 42;
168 assert_eq!(grid.as_slice(), &[0, 0, 0, 0, 0, 42]);
169 }
170
171 #[test]
172 #[should_panic]
173 fn from_vec_rejects_wrong_length() {
174 let _ = Grid::from_vec(2, 2, vec![0u8; 3]);
175 }
176
177 #[test]
178 #[should_panic]
179 fn zero_dimensions_are_rejected() {
180 let _ = Grid::new(0, 4, 0u8);
181 }
182}