430 lines
12 KiB
Rust
430 lines
12 KiB
Rust
use super::{
|
|
get_section_cord,
|
|
vector3::{self, Vector3},
|
|
};
|
|
use std::fmt;
|
|
use std::hash::Hash;
|
|
|
|
use crate::math::vector2::Vector2;
|
|
use num_traits::Euclid;
|
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
pub struct BlockPosIterator {
|
|
start_x: i32,
|
|
start_y: i32,
|
|
start_z: i32,
|
|
end_x: i32,
|
|
end_y: i32,
|
|
index: usize,
|
|
count: usize,
|
|
}
|
|
|
|
impl BlockPosIterator {
|
|
pub fn new(
|
|
start_x: i32,
|
|
start_y: i32,
|
|
start_z: i32,
|
|
end_x: i32,
|
|
end_y: i32,
|
|
end_z: i32,
|
|
) -> Self {
|
|
let count_x = end_x - start_x + 1;
|
|
let count_y = end_y - start_y + 1;
|
|
let count_z = end_z - start_z + 1;
|
|
let count = (count_x * count_y * count_z) as usize;
|
|
BlockPosIterator {
|
|
start_x,
|
|
start_y,
|
|
start_z,
|
|
end_x,
|
|
end_y,
|
|
index: 0,
|
|
count,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Iterator for BlockPosIterator {
|
|
type Item = BlockPos;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.index >= self.count {
|
|
return None;
|
|
}
|
|
|
|
let size_x = (self.end_x - self.start_x + 1) as usize;
|
|
let size_y = (self.end_y - self.start_y + 1) as usize;
|
|
|
|
let x_offset = self.index % size_x;
|
|
let y_offset = (self.index / size_x) % size_y;
|
|
let z_offset = (self.index / size_x) / size_y;
|
|
|
|
let x = self.start_x + x_offset as i32;
|
|
let y = self.start_y + y_offset as i32;
|
|
let z = self.start_z + z_offset as i32;
|
|
|
|
self.index += 1;
|
|
Some(BlockPos::new(x, y, z))
|
|
}
|
|
}
|
|
|
|
pub struct OutwardIterator {
|
|
center_x: i32,
|
|
center_y: i32,
|
|
center_z: i32,
|
|
range_x: i32,
|
|
range_y: i32,
|
|
range_z: i32,
|
|
max_manhattan_distance: i32,
|
|
|
|
pos: BlockPos,
|
|
manhattan_distance: i32,
|
|
limit_x: i32,
|
|
limit_y: i32,
|
|
dx: i32,
|
|
dy: i32,
|
|
swap_z: bool,
|
|
}
|
|
|
|
impl OutwardIterator {
|
|
pub fn new(center: BlockPos, range_x: i32, range_y: i32, range_z: i32) -> Self {
|
|
let max_manhattan_distance = range_x + range_y + range_z;
|
|
OutwardIterator {
|
|
center_x: center.0.x,
|
|
center_y: center.0.y,
|
|
center_z: center.0.z,
|
|
range_x,
|
|
range_y,
|
|
range_z,
|
|
max_manhattan_distance,
|
|
pos: BlockPos::ZERO,
|
|
manhattan_distance: 0,
|
|
limit_x: 0,
|
|
limit_y: 0,
|
|
dx: 0,
|
|
dy: 0,
|
|
swap_z: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Iterator for OutwardIterator {
|
|
type Item = BlockPos;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.swap_z {
|
|
self.swap_z = false;
|
|
self.pos.0.z = self.center_z - (self.pos.0.z - self.center_z);
|
|
return Some(self.pos);
|
|
}
|
|
|
|
loop {
|
|
if self.dy > self.limit_y {
|
|
self.dx += 1;
|
|
if self.dx > self.limit_x {
|
|
self.manhattan_distance += 1;
|
|
if self.manhattan_distance > self.max_manhattan_distance {
|
|
return None; // endOfData()
|
|
}
|
|
self.limit_x = self.range_x.min(self.manhattan_distance);
|
|
self.dx = -self.limit_x;
|
|
}
|
|
self.limit_y = self.range_y.min(self.manhattan_distance - self.dx.abs());
|
|
self.dy = -self.limit_y;
|
|
}
|
|
|
|
let i2 = self.dx;
|
|
let j2 = self.dy;
|
|
let k2 = self.manhattan_distance - i2.abs() - j2.abs();
|
|
|
|
if k2 <= self.range_z {
|
|
self.swap_z = k2 != 0;
|
|
self.pos =
|
|
BlockPos::new(self.center_x + i2, self.center_y + j2, self.center_z + k2);
|
|
self.dy += 1;
|
|
return Some(self.pos);
|
|
}
|
|
self.dy += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
|
/// Aka Block Position
|
|
pub struct BlockPos(pub Vector3<i32>);
|
|
|
|
impl BlockPos {
|
|
pub const ZERO: Self = Self::new(0, 0, 0);
|
|
|
|
pub const fn new(x: i32, y: i32, z: i32) -> Self {
|
|
Self(Vector3::new(x, y, z))
|
|
}
|
|
|
|
/// Iterates through all `BlockPos` within a cuboid region defined by two corner points,
|
|
/// `start` and `end`. The iteration covers all blocks inclusively between the minimum
|
|
/// and maximum coordinates of the two provided positions.
|
|
///
|
|
/// This function ensures that the iteration always proceeds from the smallest to the
|
|
/// largest coordinate in each dimension, regardless of whether `start` or `end` has a
|
|
/// higher value for a given axis.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `start` - One corner of the cuboid region.
|
|
/// * `end` - The opposite corner of the cuboid region.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// A `BlockPosIterator` that yields each `BlockPos` within the defined cuboid.
|
|
pub fn iterate(start: BlockPos, end: BlockPos) -> BlockPosIterator {
|
|
BlockPosIterator::new(
|
|
start.0.x.min(end.0.x),
|
|
start.0.y.min(end.0.y),
|
|
start.0.z.min(end.0.z),
|
|
start.0.x.max(end.0.x),
|
|
start.0.y.max(end.0.y),
|
|
start.0.z.max(end.0.z),
|
|
)
|
|
}
|
|
|
|
// Iterates through `BlockPos` objects outward from a specified `center` point.
|
|
/// The iteration order is primarily based on the **Manhattan distance** from the center.
|
|
///
|
|
/// For positions with the same Manhattan distance, the iteration prioritizes:
|
|
/// 1. **Y-offset**: From negative to positive.
|
|
/// 2. **X-offset**: From negative to positive (for the same Y-offset).
|
|
/// 3. **Z-offset**: Positive Z-offset before negative Z-offset (for the same X and Y offsets).
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `center` - The central `BlockPos` from which to start iterating.
|
|
/// * `range_x` - The maximum absolute difference allowed in the X-coordinate from the center.
|
|
/// * `range_y` - The maximum absolute difference allowed in the Y-coordinate from the center.
|
|
/// * `range_z` - The maximum absolute difference allowed in the Z-coordinate from the center.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// An `OutwardIterator` that yields `BlockPos` instances in the described outward order.
|
|
pub fn iterate_outwards(
|
|
center: BlockPos,
|
|
range_x: i32,
|
|
range_y: i32,
|
|
range_z: i32,
|
|
) -> OutwardIterator {
|
|
OutwardIterator::new(center, range_x, range_y, range_z)
|
|
}
|
|
|
|
pub fn iterate_block_pos(
|
|
start_x: i32,
|
|
start_y: i32,
|
|
start_z: i32,
|
|
end_x: i32,
|
|
end_y: i32,
|
|
end_z: i32,
|
|
) -> BlockPosIterator {
|
|
BlockPosIterator::new(start_x, start_y, start_z, end_x, end_y, end_z)
|
|
}
|
|
|
|
pub fn chunk_and_chunk_relative_position(&self) -> (Vector2<i32>, Vector3<i32>) {
|
|
(self.chunk_position(), self.chunk_relative_position())
|
|
}
|
|
|
|
pub fn chunk_position(&self) -> Vector2<i32> {
|
|
let z_chunk = self.0.z.div_euclid(16);
|
|
let x_chunk = self.0.x.div_euclid(16);
|
|
Vector2 {
|
|
x: x_chunk,
|
|
y: z_chunk,
|
|
}
|
|
}
|
|
|
|
pub fn chunk_relative_position(&self) -> Vector3<i32> {
|
|
let z_chunk = self.0.z.rem_euclid(16);
|
|
let x_chunk = self.0.x.rem_euclid(16);
|
|
Vector3 {
|
|
x: x_chunk,
|
|
y: self.0.y,
|
|
z: z_chunk,
|
|
}
|
|
}
|
|
|
|
pub fn section_relative_position(&self) -> Vector3<i32> {
|
|
let (_z_chunk, z_rem) = self.0.z.div_rem_euclid(&16);
|
|
let (_x_chunk, x_rem) = self.0.x.div_rem_euclid(&16);
|
|
let (_y_chunk, y_rem) = self.0.y.div_rem_euclid(&16);
|
|
|
|
// Since we divide by 16 remnant can never exceed u8
|
|
Vector3 {
|
|
x: x_rem,
|
|
z: z_rem,
|
|
y: y_rem,
|
|
}
|
|
}
|
|
pub fn from_i64(encoded_position: i64) -> Self {
|
|
BlockPos(Vector3 {
|
|
x: (encoded_position >> 38) as i32,
|
|
y: (encoded_position << 52 >> 52) as i32,
|
|
z: (encoded_position << 26 >> 38) as i32,
|
|
})
|
|
}
|
|
|
|
pub fn floored(x: f64, y: f64, z: f64) -> Self {
|
|
Self(Vector3::new(
|
|
x.floor() as i32,
|
|
y.floor() as i32,
|
|
z.floor() as i32,
|
|
))
|
|
}
|
|
|
|
pub fn floored_v(pos: Vector3<f64>) -> Self {
|
|
Self(Vector3::new(
|
|
pos.x.floor() as i32,
|
|
pos.y.floor() as i32,
|
|
pos.z.floor() as i32,
|
|
))
|
|
}
|
|
|
|
pub fn ceiled_v(pos: Vector3<f64>) -> Self {
|
|
Self(Vector3::new(
|
|
pos.x.ceil() as i32,
|
|
pos.y.ceil() as i32,
|
|
pos.z.ceil() as i32,
|
|
))
|
|
}
|
|
|
|
pub fn to_f64(&self) -> Vector3<f64> {
|
|
Vector3::new(
|
|
self.0.x as f64 + 0.5,
|
|
self.0.y as f64,
|
|
self.0.z as f64 + 0.5,
|
|
)
|
|
}
|
|
|
|
pub fn to_centered_f64(&self) -> Vector3<f64> {
|
|
Vector3::new(
|
|
self.0.x as f64 + 0.5,
|
|
self.0.y as f64 + 0.5,
|
|
self.0.z as f64 + 0.5,
|
|
)
|
|
}
|
|
|
|
pub fn offset(&self, offset: Vector3<i32>) -> Self {
|
|
BlockPos(self.0 + offset)
|
|
}
|
|
|
|
pub fn add(&self, x: i32, y: i32, z: i32) -> Self {
|
|
BlockPos::new(self.0.x + x, self.0.y + y, self.0.z + z)
|
|
}
|
|
|
|
pub fn offset_dir(&self, offset: Vector3<i32>, direction: i32) -> Self {
|
|
BlockPos(Vector3::new(
|
|
self.0.x + offset.x * direction,
|
|
self.0.y + offset.y * direction,
|
|
self.0.z + offset.z * direction,
|
|
))
|
|
}
|
|
|
|
pub fn up(&self) -> Self {
|
|
self.offset(Vector3::new(0, 1, 0))
|
|
}
|
|
|
|
pub fn up_height(&self, height: i32) -> Self {
|
|
self.offset(Vector3::new(0, height, 0))
|
|
}
|
|
|
|
pub fn down(&self) -> Self {
|
|
self.offset(Vector3::new(0, -1, 0))
|
|
}
|
|
|
|
pub fn down_height(&self, height: i32) -> Self {
|
|
self.offset(Vector3::new(0, -height, 0))
|
|
}
|
|
|
|
pub fn west(&self) -> Self {
|
|
self.offset(Vector3::new(-1, 0, 0))
|
|
}
|
|
|
|
pub fn north(&self) -> Self {
|
|
self.offset(Vector3::new(0, 0, -1))
|
|
}
|
|
|
|
pub fn east(&self) -> Self {
|
|
self.offset(Vector3::new(1, 0, 0))
|
|
}
|
|
|
|
pub fn south(&self) -> Self {
|
|
self.offset(Vector3::new(0, 0, 1))
|
|
}
|
|
|
|
pub fn manhattan_distance(&self, other: Self) -> i32 {
|
|
let x = (other.0.x - self.0.x).abs();
|
|
let y = (other.0.y - self.0.y).abs();
|
|
let z = (other.0.z - self.0.z).abs();
|
|
x + y + z
|
|
}
|
|
|
|
pub fn squared_distance(&self, other: &Self) -> i32 {
|
|
self.0.squared_distance_to_vec(&other.0)
|
|
}
|
|
|
|
pub fn as_long(&self) -> i64 {
|
|
((self.0.x as i64 & 0x3FFFFFF) << 38)
|
|
| ((self.0.z as i64 & 0x3FFFFFF) << 12)
|
|
| (self.0.y as i64 & 0xFFF)
|
|
}
|
|
}
|
|
|
|
impl Serialize for BlockPos {
|
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
serializer.serialize_i64(self.as_long())
|
|
}
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for BlockPos {
|
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
struct Visitor;
|
|
impl serde::de::Visitor<'_> for Visitor {
|
|
type Value = BlockPos;
|
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
formatter.write_str("An i64 int")
|
|
}
|
|
fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<Self::Value, E> {
|
|
Ok(BlockPos(Vector3 {
|
|
x: (v >> 38) as i32,
|
|
y: (v << 52 >> 52) as i32,
|
|
z: (v << 26 >> 38) as i32,
|
|
}))
|
|
}
|
|
}
|
|
deserializer.deserialize_i64(Visitor)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for BlockPos {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}, {}, {}", self.0.x, self.0.y, self.0.z)
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub const fn chunk_section_from_pos(block_pos: &BlockPos) -> Vector3<i32> {
|
|
let block_pos = block_pos.0;
|
|
Vector3::new(
|
|
get_section_cord(block_pos.x),
|
|
get_section_cord(block_pos.y),
|
|
get_section_cord(block_pos.z),
|
|
)
|
|
}
|
|
|
|
pub const fn get_local_cord(cord: i32) -> i32 {
|
|
cord & 15
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn pack_local_chunk_section(block_pos: &BlockPos) -> i16 {
|
|
let x = get_local_cord(block_pos.0.x);
|
|
let z = get_local_cord(block_pos.0.z);
|
|
let y = get_local_cord(block_pos.0.y);
|
|
vector3::packed_local(&Vector3::new(x, y, z))
|
|
}
|