1#![doc = env!("CARGO_PKG_DESCRIPTION")]
2#![warn(missing_docs)]
3#![warn(clippy::missing_docs_in_private_items)]
4
5use std::{
6 fmt::Debug,
7 iter::FusedIterator,
8 ops::{Index, IndexMut},
9};
10
11use bincode::{Decode, Encode};
12use itertools::iproduct;
13
14pub mod axis {
16 pub const X: u8 = 0;
18 pub const Y: u8 = 1;
20 pub const Z: u8 = 2;
22}
23
24macro_rules! coord_type {
26 ($t:ident, $max:expr, $doc:expr $(,)?) => {
27 #[doc = $doc]
28 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
29 pub struct $t<const AXIS: u8>(pub u8);
30
31 impl<const AXIS: u8> $t<AXIS> {
32 const MAX: usize = $max;
33
34 #[inline]
38 pub fn new<T: TryInto<u8>>(value: T) -> Self {
39 Self(
40 value
41 .try_into()
42 .ok()
43 .filter(|&v| (v as usize) < Self::MAX)
44 .expect("coordinate should be in the valid range"),
45 )
46 }
47
48 #[inline]
50 pub fn iter() -> impl DoubleEndedIterator<Item = $t<AXIS>>
51 + ExactSizeIterator
52 + FusedIterator
53 + Clone
54 + Debug {
55 (0..Self::MAX as u8).map($t)
56 }
57 }
58 };
59}
60
61pub const BLOCK_BITS: u8 = 4;
63pub const BLOCKS_PER_CHUNK: usize = 1 << BLOCK_BITS;
65coord_type!(
66 BlockCoord,
67 BLOCKS_PER_CHUNK,
68 "A block coordinate relative to a chunk",
69);
70
71pub type BlockX = BlockCoord<{ axis::X }>;
73
74pub type BlockY = BlockCoord<{ axis::Y }>;
76
77pub type BlockZ = BlockCoord<{ axis::Z }>;
79
80#[derive(Clone, Copy, PartialEq, Eq)]
82pub struct LayerBlockCoords {
83 pub x: BlockX,
85 pub z: BlockZ,
87}
88
89impl Debug for LayerBlockCoords {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 write!(f, "({}, {})", self.x.0, self.z.0)
92 }
93}
94
95impl LayerBlockCoords {
96 #[inline]
102 pub fn offset(&self) -> usize {
103 use BLOCKS_PER_CHUNK as N;
104 let x = self.x.0 as usize;
105 let z = self.z.0 as usize;
106 N * z + x
107 }
108}
109
110#[derive(Debug, Clone, Copy, Default, Encode, Decode)]
114pub struct LayerBlockArray<T>(pub [[T; BLOCKS_PER_CHUNK]; BLOCKS_PER_CHUNK]);
115
116impl<T> Index<LayerBlockCoords> for LayerBlockArray<T> {
117 type Output = T;
118
119 #[inline]
120 fn index(&self, index: LayerBlockCoords) -> &Self::Output {
121 &self.0[index.z.0 as usize][index.x.0 as usize]
122 }
123}
124
125impl<T> IndexMut<LayerBlockCoords> for LayerBlockArray<T> {
126 #[inline]
127 fn index_mut(&mut self, index: LayerBlockCoords) -> &mut Self::Output {
128 &mut self.0[index.z.0 as usize][index.x.0 as usize]
129 }
130}
131
132#[derive(Clone, Copy, PartialEq, Eq)]
134pub struct SectionBlockCoords {
135 pub xz: LayerBlockCoords,
137 pub y: BlockY,
139}
140
141impl SectionBlockCoords {
142 #[inline]
148 pub fn offset(&self) -> usize {
149 use BLOCKS_PER_CHUNK as N;
150 let y = self.y.0 as usize;
151 N * N * y + self.xz.offset()
152 }
153}
154
155impl Debug for SectionBlockCoords {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 write!(f, "({}, {}, {})", self.xz.x.0, self.y.0, self.xz.z.0)
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
163pub struct SectionY(pub i32);
164
165pub const CHUNK_BITS: u8 = 5;
167pub const CHUNKS_PER_REGION: usize = 1 << CHUNK_BITS;
169coord_type!(
170 ChunkCoord,
171 CHUNKS_PER_REGION,
172 "A chunk coordinate relative to a region",
173);
174
175pub type ChunkX = ChunkCoord<{ axis::X }>;
177
178pub type ChunkZ = ChunkCoord<{ axis::Z }>;
180
181#[derive(Clone, Copy, PartialEq, Eq)]
183pub struct ChunkCoords {
184 pub x: ChunkX,
186 pub z: ChunkZ,
188}
189
190impl Debug for ChunkCoords {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 write!(f, "({}, {})", self.x.0, self.z.0)
193 }
194}
195
196#[derive(Debug, Clone, Copy, Default, Encode, Decode)]
200pub struct ChunkArray<T>(pub [[T; CHUNKS_PER_REGION]; CHUNKS_PER_REGION]);
201
202impl<T> ChunkArray<T> {
203 #[inline]
205 pub fn keys() -> impl Iterator<Item = ChunkCoords> + Clone + Debug {
206 iproduct!(ChunkZ::iter(), ChunkX::iter()).map(|(z, x)| ChunkCoords { x, z })
207 }
208
209 #[inline]
211 pub fn values(&self) -> impl Iterator<Item = &T> + Clone + Debug {
212 Self::keys().map(|k| &self[k])
213 }
214
215 #[inline]
217 pub fn iter(&self) -> impl Iterator<Item = (ChunkCoords, &T)> + Clone + Debug {
218 Self::keys().map(|k| (k, &self[k]))
219 }
220}
221
222impl<T> Index<ChunkCoords> for ChunkArray<T> {
223 type Output = T;
224
225 #[inline]
226 fn index(&self, index: ChunkCoords) -> &Self::Output {
227 &self.0[index.z.0 as usize][index.x.0 as usize]
228 }
229}
230
231impl<T> IndexMut<ChunkCoords> for ChunkArray<T> {
232 #[inline]
233 fn index_mut(&mut self, index: ChunkCoords) -> &mut Self::Output {
234 &mut self.0[index.z.0 as usize][index.x.0 as usize]
235 }
236}