From c264001ee0889d235fe20fd54b3e757a0552641a Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Fri, 12 Aug 2022 14:18:13 +0200 Subject: [PATCH] :sparkles: :fire: Refactor Tile::draw, wip Blink structure --- stackline/src/context.rs | 50 ++++++++++++++++++++++++++++++++++--- stackline/src/lib.rs | 2 +- stackline/src/pane.rs | 18 ++++++------- stackline/src/tile/full.rs | 4 +-- stackline/src/tile/mod.rs | 9 ++++--- stackline/src/utils.rs | 20 +++++++++++++++ stackline/src/world.rs | 16 ++++++++++-- stackline/tiles/transmit.rs | 8 +++--- 8 files changed, 102 insertions(+), 25 deletions(-) diff --git a/stackline/src/context.rs b/stackline/src/context.rs index 7b238cd..9dfe5d4 100644 --- a/stackline/src/context.rs +++ b/stackline/src/context.rs @@ -1,4 +1,5 @@ use super::*; +use std::time::Duration; use veccell::{VecRef, VecRefMut}; /** Provides an interface between a [`Tile`] and its parent [`Pane`] during [`Tile::update`]. @@ -483,8 +484,8 @@ impl UpdateCommit { pub struct InitContext<'a> { pub world: &'a World, pub pane: &'a Pane, - pub world_coordinates: (i32, i32), - pub pane_coordinates: (String, usize, usize), + pub world_coords: (i32, i32), + pub pane_coords: (String, usize, usize), } impl<'a> InitContext<'a> { @@ -495,11 +496,52 @@ impl<'a> InitContext<'a> { Some(InitContext { world, pane, - world_coordinates: ( + world_coords: ( position.0 + coordinates.1 as i32, position.1 + coordinates.2 as i32, ), - pane_coordinates: coordinates, + pane_coords: coordinates, + }) + } +} + +#[derive(Clone)] +#[non_exhaustive] +pub struct DrawContext<'a> { + pub state: State, + + pub pane: &'a Pane, + pub world_coords: (i32, i32), + pub pane_coords: (usize, usize), + pub surface_coords: (i32, i32), + + pub blink: Blink, +} + +impl<'a> DrawContext<'a> { + pub fn new( + pane: &'a Pane, + pane_coords: (usize, usize), + visual_shift: (i32, i32), + blink: Blink, + ) -> Option { + let tile = pane.get(pane_coords)?; + let pane_position = pane.position(); + let world_coords = ( + pane_position.0 + pane_coords.0 as i32, + pane_position.1 + pane_coords.1 as i32, + ); + + Some(DrawContext { + state: tile.state(), + pane, + world_coords, + pane_coords, + surface_coords: ( + world_coords.0 + visual_shift.0, + world_coords.1 + visual_shift.1, + ), + blink, }) } } diff --git a/stackline/src/lib.rs b/stackline/src/lib.rs index ad8b438..2396c1e 100644 --- a/stackline/src/lib.rs +++ b/stackline/src/lib.rs @@ -39,7 +39,7 @@ pub mod prelude { pub use crate::text::{TextChar, TextSurface}; - pub use crate::context::{InitContext, UpdateContext}; + pub use crate::context::{DrawContext, InitContext, UpdateContext}; pub use crate::signal::{Signal, Value}; pub use crate::tile::Tile; pub use crate::utils::*; diff --git a/stackline/src/pane.rs b/stackline/src/pane.rs index 15e8b81..ed19789 100644 --- a/stackline/src/pane.rs +++ b/stackline/src/pane.rs @@ -523,12 +523,12 @@ impl Pane { /// Draws the Pane at `(dx + self.position.0, dy + self.position.1)` on a [`TextSurface`]. /// Empty tiles will leave the `TextSurface` untouched, but tiles are free to modify the characters around them. - pub fn draw(&self, dx: i32, dy: i32, surface: &mut TextSurface) { + pub fn draw(&self, dx: i32, dy: i32, surface: &mut TextSurface, blink: Blink) { for (x, y, tile) in self.tiles_iter() { - let x = x as i32 + dx + self.position.0 as i32; - let y = y as i32 + dy + self.position.1 as i32; + let ctx = DrawContext::new(self, (x, y), (dx, dy), blink.clone()) + .unwrap_or_else(|| unreachable!()); - tile.draw(x, y, surface); + tile.draw(surface, ctx); } } } @@ -561,7 +561,7 @@ mod test { ); test_set_signal!(pane, (0, 0), Direction::Right); - pane.draw(0, 0, &mut surface); + pane.draw(0, 0, &mut surface, Blink::default()); assert_eq!(surface.get(0, 0).unwrap().ch, '-'); assert_eq!(surface.get(1, 0).unwrap().ch, '|'); @@ -574,7 +574,7 @@ mod test { // With offset (1, 0) let mut surface = TextSurface::new(3, 3); - pane.draw(1, 0, &mut surface); + pane.draw(1, 0, &mut surface, Blink::default()); assert_eq!(surface.get(1, 0).unwrap().ch, '-'); assert_eq!(surface.get(2, 0).unwrap().ch, '|'); @@ -587,7 +587,7 @@ mod test { // With offset (0, 1) let mut surface = TextSurface::new(3, 3); - pane.draw(0, 1, &mut surface); + pane.draw(0, 1, &mut surface, Blink::default()); assert_eq!(surface.get(0, 1).unwrap().ch, '-'); assert_eq!(surface.get(1, 1).unwrap().ch, '|'); @@ -600,7 +600,7 @@ mod test { // Draw outside of bounds with offset (2, 2) let mut surface = TextSurface::new(3, 3); - pane.draw(2, 2, &mut surface); + pane.draw(2, 2, &mut surface, Blink::default()); assert_eq!(surface.get(2, 2).unwrap().ch, '-'); for y in 0..3 { @@ -632,7 +632,7 @@ mod test { let mut surface = TextSurface::new(9, 9); - pane.draw(5, 3, &mut surface); + pane.draw(5, 3, &mut surface, Blink::default()); assert_eq!(surface.get(5 + 2, 3 + 1).unwrap().ch, '-'); assert_eq!(surface.get(5 + 3, 3 + 1).unwrap().ch, '|'); diff --git a/stackline/src/tile/full.rs b/stackline/src/tile/full.rs index cb570a8..a53d667 100644 --- a/stackline/src/tile/full.rs +++ b/stackline/src/tile/full.rs @@ -94,9 +94,9 @@ impl FullTile { /// Draws itself on a [`TextSurface`] at `(x, y)`. /// If the tile is empty, does nothing - pub fn draw(&self, x: i32, y: i32, surface: &mut TextSurface) { + pub fn draw<'b>(&'b self, surface: &mut TextSurface, ctx: DrawContext<'b>) { if let Some(cell) = &self.cell { - cell.draw(x, y, self.state, surface); + cell.draw(surface, ctx); } } } diff --git a/stackline/src/tile/mod.rs b/stackline/src/tile/mod.rs index 6f9916e..ea25298 100644 --- a/stackline/src/tile/mod.rs +++ b/stackline/src/tile/mod.rs @@ -169,9 +169,12 @@ pub trait Tile: std::clone::Clone + std::fmt::Debug + Serialize + for<'d> Deseri // TODO: Use a 2d slice type #[inline] #[allow(unused_variables)] - 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)); + fn draw<'b>(&'b self, surface: &mut TextSurface, ctx: DrawContext<'b>) { + if let (Ok(x), Ok(y)) = ( + ctx.surface_coords.0.try_into(), + ctx.surface_coords.1.try_into(), + ) { + surface.set(x, y, self.draw_simple(ctx.state)); } } diff --git a/stackline/src/utils.rs b/stackline/src/utils.rs index cbc6e1d..59bbe44 100644 --- a/stackline/src/utils.rs +++ b/stackline/src/utils.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use std::time::Duration; /// Represents one or many undirected orientation(s), since we are in a 2D grid, /// this may either be [Horizontal](Orientation::Horizontal), [Vertical](Orientation::Vertical) or both ([Any](Orientation::Any)) @@ -125,6 +126,25 @@ impl Default for State { } } +/// Stores information for blinking +#[derive(Clone, Debug, Serialize, Deserialize, Default)] +#[non_exhaustive] +pub struct Blink { + pub elapsed: Duration, + pub blink_speed: Duration, +} + +impl Blink { + pub fn new(elapsed: Duration, blink_speed: Duration) -> Self { + Self { + elapsed, + blink_speed, + } + } + + // TODO: methods for blinking text +} + #[cfg(test)] mod test { use super::*; diff --git a/stackline/src/world.rs b/stackline/src/world.rs index 52c9258..aa92224 100644 --- a/stackline/src/world.rs +++ b/stackline/src/world.rs @@ -1,17 +1,27 @@ use super::*; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use std::time::{Duration, Instant}; use veccell::VecRef; #[derive(Debug, Serialize, Deserialize)] pub struct World { panes: HashMap, + + #[serde(default = "Instant::now")] + #[serde(skip)] + blink_start: Instant, + #[serde(default)] + blink_speed: Duration, } impl World { pub fn new() -> Self { Self { panes: HashMap::new(), + + blink_start: Instant::now(), + blink_speed: Duration::default(), } } @@ -149,8 +159,9 @@ impl World { } pub fn draw(&self, dx: i32, dy: i32, surface: &mut TextSurface) { + let blink = Blink::new(Instant::now() - self.blink_start, self.blink_speed); for pane in self.panes.values() { - pane.draw(dx, dy, surface); + pane.draw(dx, dy, surface, blink.clone()); } } @@ -177,9 +188,10 @@ impl std::fmt::Display for World { let height = (bounds.3 - bounds.2) as usize; let mut surface = TextSurface::new(width, height); + let blink = Blink::new(Instant::now() - self.blink_start, self.blink_speed); for pane in self.panes.values() { - pane.draw(bounds.0, bounds.2, &mut surface); + pane.draw(bounds.0, bounds.2, &mut surface, blink.clone()); } ::fmt(&surface, f) diff --git a/stackline/tiles/transmit.rs b/stackline/tiles/transmit.rs index 28354ca..16d8b85 100644 --- a/stackline/tiles/transmit.rs +++ b/stackline/tiles/transmit.rs @@ -134,7 +134,7 @@ impl Sender { impl Tile for Sender { fn init<'b>(&'b mut self, context: InitContext<'b>) { - self.calculate_path(context.world_coordinates, context.world) + self.calculate_path(context.world_coords, context.world) } fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { @@ -168,11 +168,11 @@ impl Tile for Sender { // TODO: read self.signals to determine the state of each char // TODO: automated test - fn draw(&self, x: i32, y: i32, _state: State, surface: &mut TextSurface) { + fn draw(&self, surface: &mut TextSurface, ctx: DrawContext) { let (x, y) = if self.path.len() > 0 { - (x - self.path[0].0, y - self.path[0].1) + (ctx.surface_coords.0 - self.path[0].0, ctx.surface_coords.1 - self.path[0].1) } else { - (x, y) + (ctx.surface_coords.0, ctx.surface_coords.1) }; let mut count = 0;