diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2023-03-07 22:09:56 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-07 22:09:56 -0600 |
| commit | 5dd35c7ed82c38ef36ca28f630e8d05c5db2cbea (patch) | |
| tree | 72719e46479e7884ea535c768ab7c244ce048063 /azalea-world/src/iterators.rs | |
| parent | 719379a8a76ab0685f2bd14bebe2f0cd1e97f06b (diff) | |
| download | azalea-drasl-5dd35c7ed82c38ef36ca28f630e8d05c5db2cbea.tar.xz | |
Add World::find_block (#80)
* start adding World::find_block
* keep working on find_block
* BlockStates
* fix sorting
* update examples that use find_one_block
* azalea_block::properties
* fix tests
* add a gotoblock command to testbot
Diffstat (limited to 'azalea-world/src/iterators.rs')
| -rw-r--r-- | azalea-world/src/iterators.rs | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/azalea-world/src/iterators.rs b/azalea-world/src/iterators.rs new file mode 100644 index 00000000..53a94898 --- /dev/null +++ b/azalea-world/src/iterators.rs @@ -0,0 +1,247 @@ +//! Iterators for iterating over Minecraft blocks and chunks, based on +//! [prismarine-world's iterators](https://github.com/PrismarineJS/prismarine-world/blob/master/src/iterators.js). + +use azalea_core::{BlockPos, ChunkPos}; + +/// An octahedron iterator, useful for iterating over blocks in a world. +/// +/// ``` +/// # use azalea_core::BlockPos; +/// # use azalea_world::iterators::BlockIterator; +/// +/// let mut iter = BlockIterator::new(BlockPos::default(), 4); +/// for block_pos in iter { +/// println!("{:?}", block_pos); +/// } +/// ``` +pub struct BlockIterator { + start: BlockPos, + max_distance: u32, + + pos: BlockPos, + apothem: u32, + left: i32, + right: i32, +} +impl BlockIterator { + pub fn new(start: BlockPos, max_distance: u32) -> Self { + Self { + start, + max_distance, + + pos: BlockPos { + x: -1, + y: -1, + z: -1, + }, + apothem: 1, + left: 1, + right: 2, + } + } +} + +impl Iterator for BlockIterator { + type Item = BlockPos; + + fn next(&mut self) -> Option<Self::Item> { + if self.apothem > self.max_distance { + return None; + } + + self.right -= 1; + if self.right < 0 { + self.left -= 1; + if self.left < 0 { + self.pos.z += 2; + if self.pos.z > 1 { + self.pos.y += 2; + if self.pos.y > 1 { + self.pos.x += 2; + if self.pos.x > 1 { + self.apothem += 1; + self.pos.x = -1; + } + self.pos.y = -1; + } + self.pos.z = -1; + } + self.left = self.apothem as i32; + } + self.right = self.left; + } + let x = self.pos.x * self.right; + let y = self.pos.y * ((self.apothem as i32) - self.left); + let z = self.pos.z * ((self.apothem as i32) - (i32::abs(x) + i32::abs(y))); + Some(BlockPos { x: x, y, z } + self.start) + } +} + +/// A spiral iterator, useful for iterating over chunks in a world. Use +/// `ChunkIterator` to sort by x+y+z (Manhattan) distance. +/// +/// ``` +/// # use azalea_core::ChunkPos; +/// # use azalea_world::iterators::SquareChunkIterator; +/// +/// let mut iter = SquareChunkIterator::new(ChunkPos::default(), 4); +/// for chunk_pos in iter { +/// println!("{:?}", chunk_pos); +/// } +/// ``` +pub struct SquareChunkIterator { + start: ChunkPos, + number_of_points: u32, + + dir: ChunkPos, + + segment_len: u32, + pos: ChunkPos, + segment_passed: u32, + current_iter: u32, +} +impl SquareChunkIterator { + pub fn new(start: ChunkPos, max_distance: u32) -> Self { + Self { + start, + number_of_points: u32::pow(max_distance * 2 - 1, 2), + + dir: ChunkPos { x: 1, z: 0 }, + + segment_len: 1, + pos: ChunkPos::default(), + segment_passed: 0, + current_iter: 0, + } + } + + /// Change the distance that this iterator won't go past. + /// + /// ``` + /// # use azalea_core::ChunkPos; + /// # use azalea_world::iterators::SquareChunkIterator; + /// + /// let mut iter = SquareChunkIterator::new(ChunkPos::default(), 2); + /// while let Some(chunk_pos) = iter.next() { + /// println!("{:?}", chunk_pos); + /// } + /// iter.set_max_distance(4); + /// while let Some(chunk_pos) = iter.next() { + /// println!("{:?}", chunk_pos); + /// } + /// ``` + pub fn set_max_distance(&mut self, max_distance: u32) { + self.number_of_points = u32::pow(max_distance * 2 - 1, 2); + } +} +impl Iterator for SquareChunkIterator { + type Item = ChunkPos; + + fn next(&mut self) -> Option<Self::Item> { + if self.current_iter > self.number_of_points { + return None; + } + + let output = self.start + self.dir; + + // make a step, add the direction to the current position + self.pos.x += self.dir.x; + self.pos.z += self.dir.z; + self.segment_passed += 1; + + if self.segment_passed == self.segment_len { + // done with current segment + self.segment_passed = 0; + + // rotate directions + (self.dir.x, self.dir.z) = (-self.dir.z, self.dir.x); + + // increase segment length if necessary + if self.dir.z == 0 { + self.segment_len += 1; + } + } + self.current_iter += 1; + Some(output) + } +} + +/// A diagonal spiral iterator, useful for iterating over chunks in a world. +/// +/// ``` +/// # use azalea_core::ChunkPos; +/// # use azalea_world::iterators::ChunkIterator; +/// +/// let mut iter = ChunkIterator::new(ChunkPos::default(), 4); +/// for chunk_pos in iter { +/// println!("{:?}", chunk_pos); +/// } +/// ``` +pub struct ChunkIterator { + pub max_distance: u32, + pub start: ChunkPos, + pub pos: ChunkPos, + pub layer: i32, + pub leg: i32, +} +impl ChunkIterator { + pub fn new(start: ChunkPos, max_distance: u32) -> Self { + Self { + max_distance, + start, + pos: ChunkPos { x: 2, z: -1 }, + layer: 1, + leg: -1, + } + } +} +impl Iterator for ChunkIterator { + type Item = ChunkPos; + + fn next(&mut self) -> Option<Self::Item> { + match self.leg { + -1 => { + self.leg = 0; + return Some(self.start); + } + 0 => { + if self.max_distance == 1 { + return None; + } + self.pos.x -= 1; + self.pos.z += 1; + if self.pos.x == 0 { + self.leg = 1; + } + } + 1 => { + self.pos.x -= 1; + self.pos.z -= 1; + if self.pos.z == 0 { + self.leg = 2; + } + } + 2 => { + self.pos.x += 1; + self.pos.z -= 1; + if self.pos.x == 0 { + self.leg = 3; + } + } + 3 => { + self.pos.x += 1; + self.pos.z += 1; + if self.pos.z == 0 { + self.pos.x += 1; + self.leg = 0; + self.layer += 1; + if self.layer == self.max_distance as i32 { + return None; + } + } + } + _ => unreachable!(), + } + Some(self.start + self.pos) + } +} |
