mindustry logic execution, map- and schematic- parsing and rendering
Add schematic iterators
| -rw-r--r-- | src/data/schematic.rs | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/src/data/schematic.rs b/src/data/schematic.rs index b52e402..1c5dd34 100644 --- a/src/data/schematic.rs +++ b/src/data/schematic.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; +use std::iter::FusedIterator; use crate::block::{Block, Rotation}; +use crate::data::GridPos; pub const MAX_DIMENSION: u16 = 128; pub const MAX_BLOCKS: u32 = 128 * 128; @@ -116,4 +118,206 @@ impl Schematic } else {None} } + + pub fn pos_iter(&self) -> PosIter + { + PosIter{x: 0, y: 0, w: self.width, h: self.height} + } + + pub fn block_iter(&self) -> BlockIter + { + BlockIter{x: 0, y: 0, schematic: self, encountered: 0} + } +} + +pub struct PosIter +{ + x: u16, + y: u16, + w: u16, + h: u16, +} + +impl Iterator for PosIter +{ + type Item = GridPos; + + fn next(&mut self) -> Option<Self::Item> + { + if self.w > 0 && self.y < self.h + { + let p = GridPos(self.x, self.y); + self.x += 1; + if self.x == self.w + { + self.x = 0; + self.y += 1; + } + Some(p) + } + else {None} + } + + fn size_hint(&self) -> (usize, Option<usize>) + { + let pos = (self.x as usize) + (self.y as usize) * (self.w as usize); + let end = (self.w as usize) * (self.h as usize); + (end - pos, Some(end - pos)) + } + + fn count(self) -> usize + { + let pos = (self.x as usize) + (self.y as usize) * (self.w as usize); + let end = (self.w as usize) * (self.h as usize); + end - pos + } + + fn last(self) -> Option<Self::Item> + { + // self.y < self.h implies self.h > 0 + if self.w > 0 && self.y < self.h + { + Some(GridPos(self.w - 1, self.h - 1)) + } + else {None} + } +} + +impl FusedIterator for PosIter {} + +pub struct BlockIter<'l> +{ + x: u16, + y: u16, + schematic: &'l Schematic, + encountered: u32, +} + +impl<'l> Iterator for BlockIter<'l> +{ + type Item = (GridPos, &'static Block, Rotation); + + fn next(&mut self) -> Option<Self::Item> + { + let w = self.schematic.width; + let blocks: &[Option<Storage>] = &self.schematic.blocks; + let pos = (self.x as usize) + (self.y as usize) * (w as usize); + if blocks.len() <= pos + { + return None; + } + if let Some(ref s) = blocks[pos] + { + let p = GridPos(self.x, self.y); + self.x += 1; + if self.x == w + { + self.x = 0; + self.y += 1; + } + self.encountered += 1; + Some((p, s.0, s.1)) + } + else + { + match blocks[pos..].iter().enumerate().find(|(_, v)| v.is_some()) + { + None => + { + // move to the end of the iterator + self.x = 0; + self.y = self.schematic.height; + None + }, + Some((i, Some(s))) => + { + // compute the coordinate of this result + let i0 = i + self.x as usize; + let x = (i0 % w as usize) as u16; + let y = (i / w as usize) as u16; + self.x = x + 1; + if self.x == w + { + self.x = 0; + self.y += 1; + } + self.encountered += 1; + Some((GridPos(x, y), s.0, s.1)) + }, + _ => unreachable!("searched for Some but got None"), + } + } + } + + fn size_hint(&self) -> (usize, Option<usize>) + { + let remain = self.schematic.block_cnt - self.encountered; + (remain as usize, Some(remain as usize)) + } + + fn count(self) -> usize + { + (self.schematic.block_cnt - self.encountered) as usize + } + + fn last(self) -> Option<Self::Item> + { + let w = self.schematic.width; + let h = self.schematic.height; + // self.y < h implies h > 0 + if w > 0 && self.y < h + { + let pos = (self.x as usize) + (self.y as usize) * (w as usize); + let end = (w as usize) * (h as usize); + let blocks: &[Option<Storage>] = &self.schematic.blocks; + for i in (pos..end).rev() + { + if let Some(ref s) = blocks[i] + { + // last consumes self so we don't have to update fields + let i0 = i + self.x as usize; + let x = (i0 % w as usize) as u16; + let y = (i / w as usize) as u16; + return Some((GridPos(x, y), s.0, s.1)); + } + } + None + } + else {None} + } +} + +impl<'l> FusedIterator for BlockIter<'l> {} + +#[cfg(test)] +mod test +{ + use super::*; + + macro_rules!test_iter + { + ($name:ident, $it:expr, $($val:expr),+) => + { + #[test] + fn $name() + { + let mut it = $it; + $(test_iter!(impl it, $val);)+ + } + }; + (impl $it:ident, $val:literal) => + { + for _ in 0..$val + { + assert_ne!($it.next(), None, "iterator returned None too early"); + } + }; + (impl $it:ident, $val:expr) => + { + assert_eq!($it.next(), $val); + }; + } + + test_iter!(block_iter, Schematic::new(3, 4).pos_iter(), Some(GridPos(0, 0)), Some(GridPos(1, 0)), Some(GridPos(2, 0)), + Some(GridPos(0, 1)), 7, Some(GridPos(2, 3)), None); } |