minedmap_types/
lib.rs

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
14/// Const generic AXIS arguments for coordinate types
15pub mod axis {
16	/// The X axis
17	pub const X: u8 = 0;
18	/// The Y axis (height)
19	pub const Y: u8 = 1;
20	/// The Z axis
21	pub const Z: u8 = 2;
22}
23
24/// Generates a generic coordinate type with a given range
25macro_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			/// Constructs a new value
35			///
36			/// Will panic if the value is not in the valid range
37			#[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			/// Returns an iterator over all possible values of the type
49			#[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
61/// Number of bits required to store a block coordinate
62pub const BLOCK_BITS: u8 = 4;
63/// Number of blocks per chunk in each dimension
64pub 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
71/// A block X coordinate relative to a chunk
72pub type BlockX = BlockCoord<{ axis::X }>;
73
74/// A block Y coordinate relative to a chunk section
75pub type BlockY = BlockCoord<{ axis::Y }>;
76
77/// A block Z coordinate relative to a chunk
78pub type BlockZ = BlockCoord<{ axis::Z }>;
79
80/// X and Z coordinates of a block in a chunk
81#[derive(Clone, Copy, PartialEq, Eq)]
82pub struct LayerBlockCoords {
83	/// The X coordinate
84	pub x: BlockX,
85	/// The Z coordinate
86	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	/// Computes a block's offset in various data structures
97	///
98	/// Many chunk data structures store block and biome data in the same
99	/// order. This method computes the offset at which the data for the
100	/// block at a given coordinate is stored.
101	#[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/// Generic array for data stored per block of a chunk layer
111///
112/// Includes various convenient iteration functions.
113#[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/// X, Y and Z coordinates of a block in a chunk section
133#[derive(Clone, Copy, PartialEq, Eq)]
134pub struct SectionBlockCoords {
135	/// The X and Z coordinates
136	pub xz: LayerBlockCoords,
137	/// The Y coordinate
138	pub y: BlockY,
139}
140
141impl SectionBlockCoords {
142	/// Computes a block's offset in various data structures
143	///
144	/// Many chunk data structures store block and biome data in the same
145	/// order. This method computes the offset at which the data for the
146	/// block at a given coordinate is stored.
147	#[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/// A section Y coordinate
162#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
163pub struct SectionY(pub i32);
164
165/// Number of bits required to store a chunk coordinate
166pub const CHUNK_BITS: u8 = 5;
167/// Number of chunks per region in each dimension
168pub 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
175/// A chunk X coordinate relative to a region
176pub type ChunkX = ChunkCoord<{ axis::X }>;
177
178/// A chunk Z coordinate relative to a region
179pub type ChunkZ = ChunkCoord<{ axis::Z }>;
180
181/// A pair of chunk coordinates relative to a region
182#[derive(Clone, Copy, PartialEq, Eq)]
183pub struct ChunkCoords {
184	/// The X coordinate
185	pub x: ChunkX,
186	/// The Z coordinate
187	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/// Generic array for data stored per chunk of a region
197///
198/// Includes various convenient iteration functions.
199#[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	/// Iterates over all possible chunk coordinate pairs used as [ChunkArray] keys
204	#[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	/// Iterates over all values stored in the [ChunkArray]
210	#[inline]
211	pub fn values(&self) -> impl Iterator<Item = &T> + Clone + Debug {
212		Self::keys().map(|k| &self[k])
213	}
214
215	/// Iterates over pairs of chunk coordinate pairs and corresponding stored values
216	#[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}