// SPDX-License-Identifier: AGPL-3.0-only //! Chunk-space coordinates. use super::CHUNK_SIZE; use serde::{Deserialize, Serialize}; /// The three-dimensional spatial coordinates of a chunk in the world. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ChunkPos { /// The X coordinate of the chunk. pub x: i32, /// The Y coordinate of the chunk. pub y: i32, /// The Z coordinate of the chunk. pub z: i32, } impl ChunkPos { /// Initializes a new chunk position. #[must_use] pub fn new(x: i32, y: i32, z: i32) -> Self { Self { x, y, z } } /// Initializes a new chunk position from a world-space position measured in blocks. #[must_use] #[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] pub fn from_world(x: f64, y: f64, z: f64) -> Self { ChunkPos { x: (x.floor() as i32).div_euclid(CHUNK_SIZE as i32), y: (y.floor() as i32).div_euclid(CHUNK_SIZE as i32), z: (z.floor() as i32).div_euclid(CHUNK_SIZE as i32), } } } #[cfg(test)] mod tests { use super::*; #[test] fn from_world_maps_positive_positions() { // A block at 40 falls in chunk 1 (chunk 1 spans blocks 32..=63). assert_eq!(ChunkPos::from_world(40.0, 0.0, 0.0).x, 1); // The last block of chunk 0 (block 31) stays in chunk 0. assert_eq!(ChunkPos::from_world(31.0, 0.0, 0.0).x, 0); } #[test] fn from_world_floors_negative_positions() { // Block -1 belongs to chunk -1, not chunk 0: this is the div_euclid contract. assert_eq!(ChunkPos::from_world(-1.0, 0.0, 0.0).x, -1); // Block -33 belongs to chunk -2 (chunk -2 spans blocks -64..=-33). assert_eq!(ChunkPos::from_world(-33.0, 0.0, 0.0).x, -2); } #[test] fn from_world_floors_fractional_positions() { // A position of -0.5 lies inside block -1, which is in chunk -1. assert_eq!(ChunkPos::from_world(-0.5, 0.0, 0.0).x, -1); } }