1use std::fmt;
2use std::io::Write;
3use std::ops::{Add, Sub};
4
5use anyhow::bail;
6use bitfield_struct::bitfield;
7use chunkedge_binary::{Decode, Encode};
8use chunkedge_math::{DVec3, IVec3};
9use derive_more::From;
10use thiserror::Error;
11
12use crate::direction::Direction;
13
14#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
16pub struct BlockPos {
17 pub x: i32,
18 pub y: i32,
19 pub z: i32,
20}
21
22impl BlockPos {
23 pub const fn new(x: i32, y: i32, z: i32) -> Self {
25 Self { x, y, z }
26 }
27
28 pub const fn get_in_direction(self, dir: Direction) -> Self {
39 match dir {
40 Direction::Down => BlockPos::new(self.x, self.y - 1, self.z),
41 Direction::Up => BlockPos::new(self.x, self.y + 1, self.z),
42 Direction::North => BlockPos::new(self.x, self.y, self.z - 1),
43 Direction::South => BlockPos::new(self.x, self.y, self.z + 1),
44 Direction::West => BlockPos::new(self.x - 1, self.y, self.z),
45 Direction::East => BlockPos::new(self.x + 1, self.y, self.z),
46 }
47 }
48
49 pub const fn to_center_dvec3(self) -> DVec3 {
51 DVec3::new(
52 self.x as f64 + 0.5,
53 self.y as f64 + 0.5,
54 self.z as f64 + 0.5,
55 )
56 }
57
58 pub const fn to_bottom_center_dvec3(self) -> DVec3 {
61 DVec3::new(self.x as f64 + 0.5, self.y as f64, self.z as f64 + 0.5)
62 }
63
64 pub const fn to_dvec3(self) -> DVec3 {
66 DVec3::new(self.x as f64, self.y as f64, self.z as f64)
67 }
68
69 pub const fn offset(self, x: i32, y: i32, z: i32) -> Self {
70 Self::new(self.x + x, self.y + y, self.z + z)
71 }
72
73 pub const fn packed(self) -> Result<PackedBlockPos, Error> {
74 match (self.x, self.y, self.z) {
75 (-0x2000000..=0x1ffffff, -0x800..=0x7ff, -0x2000000..=0x1ffffff) => {
76 Ok(PackedBlockPos::new()
77 .with_x(self.x)
78 .with_y(self.y)
79 .with_z(self.z))
80 }
81 _ => Err(Error(self)),
82 }
83 }
84}
85
86#[bitfield(u64)]
87#[derive(PartialEq, Eq, PartialOrd, Ord, Encode, Decode)]
88pub struct PackedBlockPos {
89 #[bits(12)]
90 pub y: i32,
91 #[bits(26)]
92 pub z: i32,
93 #[bits(26)]
94 pub x: i32,
95}
96
97impl Encode for BlockPos {
98 fn encode(&self, w: impl Write) -> anyhow::Result<()> {
99 match self.packed() {
100 Ok(p) => p.encode(w),
101 Err(e) => bail!("{e}: {self}"),
102 }
103 }
104}
105
106impl Decode<'_> for BlockPos {
107 fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
108 PackedBlockPos::decode(r).map(Into::into)
109 }
110}
111
112impl From<PackedBlockPos> for BlockPos {
113 fn from(p: PackedBlockPos) -> Self {
114 Self {
115 x: p.x(),
116 y: p.y(),
117 z: p.z(),
118 }
119 }
120}
121
122impl TryFrom<BlockPos> for PackedBlockPos {
123 type Error = Error;
124
125 fn try_from(pos: BlockPos) -> Result<Self, Self::Error> {
126 pos.packed()
127 }
128}
129
130#[derive(Copy, Clone, PartialEq, Eq, Debug, Error, From)]
131#[error("block position of {0} is out of range")]
132pub struct Error(pub BlockPos);
133
134impl From<DVec3> for BlockPos {
135 fn from(pos: DVec3) -> Self {
136 Self {
137 x: pos.x.floor() as i32,
138 y: pos.y.floor() as i32,
139 z: pos.z.floor() as i32,
140 }
141 }
142}
143
144impl From<(i32, i32, i32)> for BlockPos {
145 fn from((x, y, z): (i32, i32, i32)) -> Self {
146 BlockPos::new(x, y, z)
147 }
148}
149
150impl From<BlockPos> for (i32, i32, i32) {
151 fn from(pos: BlockPos) -> Self {
152 (pos.x, pos.y, pos.z)
153 }
154}
155
156impl From<[i32; 3]> for BlockPos {
157 fn from([x, y, z]: [i32; 3]) -> Self {
158 BlockPos::new(x, y, z)
159 }
160}
161
162impl From<BlockPos> for [i32; 3] {
163 fn from(pos: BlockPos) -> Self {
164 [pos.x, pos.y, pos.z]
165 }
166}
167
168impl Add<IVec3> for BlockPos {
169 type Output = Self;
170
171 fn add(self, rhs: IVec3) -> Self::Output {
172 Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
173 }
174}
175
176impl Sub<IVec3> for BlockPos {
177 type Output = Self;
178
179 fn sub(self, rhs: IVec3) -> Self::Output {
180 Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
181 }
182}
183
184impl Add<BlockPos> for IVec3 {
185 type Output = BlockPos;
186
187 fn add(self, rhs: BlockPos) -> Self::Output {
188 BlockPos::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
189 }
190}
191
192impl Sub<BlockPos> for IVec3 {
193 type Output = BlockPos;
194
195 fn sub(self, rhs: BlockPos) -> Self::Output {
196 BlockPos::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
197 }
198}
199
200impl fmt::Display for BlockPos {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 fmt::Debug::fmt(&(self.x, self.y, self.z), f)
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn block_position() {
213 let xzs = [
214 (-33554432, true),
215 (-33554433, false),
216 (33554431, true),
217 (33554432, false),
218 (0, true),
219 (1, true),
220 (-1, true),
221 ];
222 let ys = [
223 (-2048, true),
224 (-2049, false),
225 (2047, true),
226 (2048, false),
227 (0, true),
228 (1, true),
229 (-1, true),
230 ];
231
232 for (x, x_valid) in xzs {
233 for (y, y_valid) in ys {
234 for (z, z_valid) in xzs {
235 let pos = BlockPos::new(x, y, z);
236 if x_valid && y_valid && z_valid {
237 let c = pos.packed().unwrap();
238 assert_eq!((c.x(), c.y(), c.z()), (pos.x, pos.y, pos.z));
239 } else {
240 assert_eq!(pos.packed(), Err(Error(pos)));
241 }
242 }
243 }
244 }
245 }
246}