1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
use valence_generated::chunk_view::{CHUNK_VIEW_LUT, EXTRA_VIEW_RADIUS, MAX_VIEW_DIST};
use valence_protocol::ChunkPos;
/// Represents the set of all chunk positions that a client can see, defined by
/// a center chunk position `pos` and view distance `dist`.
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
pub struct ChunkView {
/// The center position of this chunk view.
pub pos: ChunkPos,
dist: u8,
}
impl ChunkView {
/// Creates a new chunk view. `dist` is clamped to the range
/// 0..[`MAX_VIEW_DIST`].
pub const fn new(pos: ChunkPos, dist: u8) -> Self {
Self {
pos,
dist: if dist > MAX_VIEW_DIST {
MAX_VIEW_DIST
} else {
dist
},
}
}
pub const fn with_dist(self, dist: u8) -> Self {
Self::new(self.pos, dist)
}
pub const fn dist(self) -> u8 {
self.dist
}
pub const fn contains(self, pos: ChunkPos) -> bool {
let true_dist = self.dist as u64 + EXTRA_VIEW_RADIUS as u64;
self.pos.distance_squared(pos) <= true_dist * true_dist
}
/// Returns an iterator over all the chunk positions in this view. Positions
/// are sorted by the distance to [`pos`](Self::pos) in ascending order.
pub fn iter(self) -> impl DoubleEndedIterator<Item = ChunkPos> + ExactSizeIterator + Clone {
CHUNK_VIEW_LUT[self.dist as usize]
.iter()
.map(move |&(x, z)| ChunkPos {
x: self.pos.x + i32::from(x),
z: self.pos.z + i32::from(z),
})
}
/// Returns an iterator over all the chunk positions in `self`, excluding
/// the positions that overlap with `other`. Positions are sorted by the
/// distance to [`pos`](Self::pos) in ascending order.
pub fn diff(self, other: Self) -> impl DoubleEndedIterator<Item = ChunkPos> + Clone {
self.iter().filter(move |&p| !other.contains(p))
}
/// Returns a `(min, max)` tuple describing the tight axis-aligned bounding
/// box for this view. All chunk positions in the view are contained in the
/// bounding box.
///
/// # Examples
///
/// ```
/// use valence_server::{ChunkPos, ChunkView};
///
/// let view = ChunkView::new(ChunkPos::new(5, -4), 16);
/// let (min, max) = view.bounding_box();
///
/// for pos in view.iter() {
/// assert!(pos.x >= min.x && pos.x <= max.x && pos.z >= min.z && pos.z <= max.z);
/// }
/// ```
pub fn bounding_box(self) -> (ChunkPos, ChunkPos) {
let r = i32::from(self.dist) + EXTRA_VIEW_RADIUS;
(
ChunkPos::new(self.pos.x - r, self.pos.z - r),
ChunkPos::new(self.pos.x + r, self.pos.z + r),
)
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
use super::*;
#[test]
fn chunk_view_contains() {
let view = ChunkView::new(ChunkPos::new(0, 0), 32);
let positions = view.iter().collect::<BTreeSet<_>>();
for z in -64..64 {
for x in -64..64 {
let p = ChunkPos::new(x, z);
assert_eq!(view.contains(p), positions.contains(&p), "{p:?}");
}
}
}
}