diff --git a/stackline/src/pane.rs b/stackline/src/pane.rs index 9ac72be..0b15b97 100644 --- a/stackline/src/pane.rs +++ b/stackline/src/pane.rs @@ -514,9 +514,7 @@ impl Pane { let x = x as i32 + dx + self.position.0 as i32; let y = y as i32 + dy + self.position.1 as i32; - if x >= 0 && y >= 0 { - tile.draw(x as usize, y as usize, surface); - } + tile.draw(x, y, surface); } } } diff --git a/stackline/src/text.rs b/stackline/src/text.rs index 0698174..d006a42 100644 --- a/stackline/src/text.rs +++ b/stackline/src/text.rs @@ -65,6 +65,7 @@ pub struct TextSurface { impl TextSurface { pub fn new(width: usize, height: usize) -> Self { + // TODO: return None if width or height don't fit in an i32 Self { width, height, diff --git a/stackline/src/tile/full.rs b/stackline/src/tile/full.rs index 5997909..cb7e61f 100644 --- a/stackline/src/tile/full.rs +++ b/stackline/src/tile/full.rs @@ -88,7 +88,7 @@ impl FullTile { /// Draws itself on a [`TextSurface`] at `(x, y)`. /// If the tile is empty, does nothing - pub fn draw(&self, x: usize, y: usize, surface: &mut TextSurface) { + pub fn draw(&self, x: i32, y: i32, surface: &mut TextSurface) { match self.cell { Some(ref cell) => cell.draw(x, y, self.state, surface), None => {} diff --git a/stackline/src/tile/mod.rs b/stackline/src/tile/mod.rs index e9ad43f..1173947 100644 --- a/stackline/src/tile/mod.rs +++ b/stackline/src/tile/mod.rs @@ -156,51 +156,24 @@ pub trait Tile: std::clone::Clone + std::fmt::Debug + Serialize + for<'d> Deseri /// Should draw itself on a [`TextSurface`]. /// The `Tile` is allowed to draw outside of its coordinates, although doing so might cause glitches. + /// + /// By default, draws a single character at the tile's location, determined by [`Tile::draw_simple`] // TODO: Use a 2d slice type #[inline] #[allow(unused_variables)] - fn draw(&self, x: usize, y: usize, state: State, surface: &mut TextSurface) { - // noop + fn draw(&self, x: i32, y: i32, state: State, surface: &mut TextSurface) { + if let (Ok(x), Ok(y)) = (x.try_into(), y.try_into()) { + surface.set(x, y, self.draw_simple(state)); + } } -} - -// #[derive(Debug)] -// pub struct AnyTile(Box); - -// impl AnyTile { -// #[inline] -// pub fn new(tile: T) -> Self { -// Self(Box::new(tile)) -// } - -// #[inline] -// pub fn update<'b>(&'b mut self, ctx: UpdateContext<'b>) { -// self.0.update(ctx) -// } - -// #[inline] -// pub fn accepts_signal(&self, direction: Direction) -> bool { -// self.0.accepts_signal(direction) -// } - -// #[inline] -// pub fn draw(&self, x: usize, y: usize, state: State, surface: &mut TextSurface) { -// self.0.draw(x, y, state, surface); -// } -// } -// impl Clone for AnyTile { -// #[inline] -// fn clone(&self) -> Self { -// Self(clone_box(self.0.as_ref())) -// } -// } - -// impl From for AnyTile { -// fn from(tile: T) -> AnyTile { -// AnyTile(Box::new(tile)) -// } -// } + /// Used by the default implementation of `draw`, + #[inline] + #[allow(unused_variables)] + fn draw_simple(&self, state: State) -> TextChar { + TextChar::default() + } +} pub mod prelude { pub use crate::prelude::*; diff --git a/stackline/tests/wire.rs b/stackline/tests/wire.rs index 50ef1f2..a8940ef 100644 --- a/stackline/tests/wire.rs +++ b/stackline/tests/wire.rs @@ -25,3 +25,14 @@ fn test_diode_loop() { run!(world, 4); assert_signal!(world, 1, 1); } + +#[test] +fn test_display_oob() { + let world = load_test!("tests/wire/diode-loop.json"); + + println!("{}", world); + + let mut surface = TextSurface::new(0, 0); + world.draw(0, 0, &mut surface); + world.draw(-1000, -1000, &mut surface); +} diff --git a/stackline/tiles/transmit.rs b/stackline/tiles/transmit.rs index 1bd886e..635470e 100644 --- a/stackline/tiles/transmit.rs +++ b/stackline/tiles/transmit.rs @@ -28,8 +28,8 @@ impl Tile for Teleporter { } } - fn draw(&self, x: usize, y: usize, state: State, surface: &mut TextSurface) { - surface.set(x, y, TextChar::from_state('P', state)); + fn draw_simple(&self, state: State) -> TextChar { + TextChar::from_state('P', state) } } @@ -166,43 +166,61 @@ impl Tile for Sender { // TODO: read self.signals to determine the state of each char // TODO: automated test - fn draw(&self, x: usize, y: usize, _state: State, surface: &mut TextSurface) { + fn draw(&self, x: i32, y: i32, _state: State, surface: &mut TextSurface) { for (prev, next) in self.path.iter().zip(self.path.iter().skip(1)) { if prev.0 != next.0 { // Draw the diode of the corner let ch = if next.0 > prev.0 { '>' } else { '<' }; - surface.set( - (x as i32 + prev.0) as usize, - (y as i32 + prev.1) as usize, - TextChar::from_state(ch, State::Idle), - ); + let x2 = x + prev.0; + let y2 = y + prev.1; + + if x2 >= 0 && y2 >= 0 { + surface.set( + x2 as usize, + y2 as usize, + TextChar::from_state(ch, State::Idle), + ); + } // Draw the horizontal line for dx in (prev.0 + 1)..(next.0) { - surface.set( - (x as i32 + dx) as usize, - (y as i32 + prev.1) as usize, - TextChar::from_state('-', State::Idle), - ); + let x2 = x + dx; + let y2 = y + prev.1; + if x2 >= 0 && y2 >= 0 { + surface.set( + x2 as usize, + y2 as usize, + TextChar::from_state('-', State::Idle), + ); + } } } else { // Draw the diode of the corner let ch = if next.1 > prev.1 { 'v' } else { '^' }; - surface.set( - (x as i32 + prev.0) as usize, - (y as i32 + prev.1) as usize, - TextChar::from_state(ch, State::Idle), - ); + let x2 = x + prev.0; + let y2 = y + prev.1; + + if x2 >= 0 && y2 >= 0 { + surface.set( + x2 as usize, + y2 as usize, + TextChar::from_state(ch, State::Idle), + ); + } // Draw the vertical line for dy in (prev.1 + 1)..(next.1) { - surface.set( - (y as i32 + prev.0) as usize, - (y as i32 + dy) as usize, - TextChar::from_state('|', State::Idle), - ); + let x2 = x + prev.0; + let y2 = y + dy; + if x2 >= 0 && y2 >= 0 { + surface.set( + x2 as usize, + y2 as usize, + TextChar::from_state('-', State::Idle), + ); + } } } } @@ -212,7 +230,7 @@ impl Tile for Sender { #[cfg(test)] mod test { use super::*; - use veccell::{VecRef, VecRefMut}; + use veccell::{VecRef}; #[test] fn test_teleporter_transmit_same_pane() { diff --git a/stackline/tiles/wire.rs b/stackline/tiles/wire.rs index a838137..a4ef0f3 100644 --- a/stackline/tiles/wire.rs +++ b/stackline/tiles/wire.rs @@ -34,14 +34,14 @@ impl Tile for Wire { self.0.contains(direction) } - fn draw(&self, x: usize, y: usize, state: State, surface: &mut TextSurface) { + fn draw_simple(&self, state: State) -> TextChar { let ch = match self.0 { Orientation::Horizontal => '-', Orientation::Vertical => '|', Orientation::Any => '+', }; - surface.set(x, y, TextChar::from_state(ch, state)); + TextChar::from_state(ch, state) } } @@ -76,7 +76,7 @@ impl Tile for Diode { direction.opposite() != self.0 } - fn draw(&self, x: usize, y: usize, state: State, surface: &mut TextSurface) { + fn draw_simple(&self, state: State) -> TextChar { let ch = match self.0 { Direction::Up => '^', Direction::Down => 'v', @@ -84,7 +84,7 @@ impl Tile for Diode { Direction::Right => '>', }; - surface.set(x, y, TextChar::from_state(ch, state)); + TextChar::from_state(ch, state) } } @@ -121,7 +121,7 @@ impl Tile for Resistor { } } - fn draw(&self, x: usize, y: usize, state: State, surface: &mut TextSurface) { + fn draw_simple(&self, state: State) -> TextChar { let ch = match self.direction { Direction::Up => '\u{2191}', // Upwards Arrow Direction::Down => '\u{2193}', // Downwards Arrow @@ -129,7 +129,7 @@ impl Tile for Resistor { Direction::Right => '\u{2192}', // Rightwards Arrow }; - surface.set(x, y, TextChar::from_state(ch, state)); + TextChar::from_state(ch, state) } }