diff --git a/Cargo.toml b/Cargo.toml index 9b3eff2..baa0d46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ repository = "https://github.com/ricoriedel/wipe" anyhow = "1.0" clap = { version = "3.1", features = ["derive"]} crossterm = "0.23" -rand = "0.8" \ No newline at end of file +rand = "0.8" +mockall = "0.11" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f6b4bcf..d59762d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use crate::color::{ColorSampler, SimpleColorSampler}; use crate::fill::circle::CircleFillMode; use crate::fill::FillMode; use crate::fill::level::LevelFillMode; +use crate::render::{Renderer, SamplerRenderer}; use crate::sampler::ComposedSampler; use crate::surface::WriteSurface; use crate::vec::Vector; @@ -22,6 +23,7 @@ mod array; mod surface; mod animation; mod sampler; +mod render; #[derive(Copy, Clone, ArgEnum)] enum AnimationType { @@ -76,8 +78,13 @@ fn main() -> Result<(), Error> { let color = create_color(args.color[0]); let char = Box::new(SimpleCharSampler::new(args.chars)); - let _ = Box::new(ComposedSampler::new(animation, fill, color, char)); - let _ = Box::new(WriteSurface::new(stdout(), width, height)); + let sampler = Box::new(ComposedSampler::new(animation, fill, color, char)); + let surface = Box::new(WriteSurface::new(stdout(), width, height)); + + let mut renderer = Box::new(SamplerRenderer::new(surface, sampler)); + + renderer.render(0.5); + renderer.present()?; Ok(()) } diff --git a/src/render.rs b/src/render.rs new file mode 100644 index 0000000..8268cd0 --- /dev/null +++ b/src/render.rs @@ -0,0 +1,87 @@ +use anyhow::Error; +use crate::sampler::{Sample, Sampler}; +use crate::surface::Surface; +use crate::Vector; + +pub trait Renderer { + fn render(&mut self, step: f32); + fn present(&mut self) -> Result<(), Error>; +} + +pub struct SamplerRenderer { + surface: Box, + sampler: Box, +} + +impl SamplerRenderer { + pub fn new(surface: Box, + sampler: Box) -> Self { + Self { surface, sampler } + } +} + +impl Renderer for SamplerRenderer { + fn render(&mut self, step: f32) { + for x in 0..self.surface.width() { + for y in 0..self.surface.height() { + let pos = Vector::from_terminal(x, y); + let sample = self.sampler.sample(step, pos); + + match sample { + Sample::Keep => (), + Sample::Draw { char, color } => self.surface.draw(x, y, char, color), + Sample::Clear => self.surface.clear(x, y), + } + } + } + } + + fn present(&mut self) -> Result<(), Error> { + self.surface.present() + } +} + +#[cfg(test)] +mod test { + use crossterm::style::*; + use mockall::predicate::*; + use super::*; + use crate::surface::MockSurface; + use crate::sampler::MockSampler; + + #[test] + fn render() { + let mut surface = Box::new(MockSurface::new()); + let mut sampler = Box::new(MockSampler::new()); + + sampler.expect_sample().withf(|_, pos| pos.x == 0.0 && pos.y == 0.0).returning(|_,_| Sample::Clear); + sampler.expect_sample().withf(|_, pos| pos.x == 1.0 && pos.y == 0.0).returning(|_,_| Sample::Keep); + sampler.expect_sample().withf(|_, pos| pos.x == 0.0 && pos.y == 2.0).returning(|_,_| Sample::Draw { char: 'a', color: Color::Red }); + sampler.expect_sample().withf(|_, pos| pos.x == 1.0 && pos.y == 2.0).returning(|_,_| Sample::Keep); + sampler.expect_sample().withf(|_, pos| pos.x == 0.0 && pos.y == 4.0).returning(|_,_| Sample::Draw { char: 'x', color: Color::Yellow }); + sampler.expect_sample().withf(|_, pos| pos.x == 1.0 && pos.y == 4.0).returning(|_,_| Sample::Clear); + + surface.expect_width().return_const(2 as usize); + surface.expect_height().return_const(3 as usize); + surface.expect_clear().once().with(eq(0), eq(0)).return_const(()); + surface.expect_draw().once().with(eq(0), eq(1), eq('a'), eq(Color::Red)).return_const(()); + surface.expect_draw().once().with(eq(0), eq(2), eq('x'), eq(Color::Yellow)).return_const(()); + surface.expect_clear().once().with(eq(1), eq(2)).return_const(()); + + let mut renderer = SamplerRenderer::new(surface, sampler); + + renderer.render(0.5); + } + + #[test] + fn present() { + let mut surface = Box::new(MockSurface::new()); + let sampler = Box::new(MockSampler::new()); + + surface.expect_present().once().returning(|| Ok(())); + + let mut renderer = SamplerRenderer::new(surface, sampler); + + renderer.present().unwrap(); + } +} \ No newline at end of file diff --git a/src/sampler.rs b/src/sampler.rs index 3643fff..bb6a7f7 100644 --- a/src/sampler.rs +++ b/src/sampler.rs @@ -1,4 +1,5 @@ use crossterm::style::Color; +use mockall::automock; use crate::animation::Animation; use crate::char::CharSampler; use crate::color::ColorSampler; @@ -11,6 +12,7 @@ pub enum Sample { Clear, } +#[automock] pub trait Sampler { fn sample(&self, step: f32, pos: Vector) -> Sample; } diff --git a/src/surface.rs b/src/surface.rs index b0b1093..3096f5f 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -3,9 +3,11 @@ use crossterm::cursor::MoveTo; use crossterm::{ExecutableCommand, QueueableCommand}; use crossterm::style::{Color, Print, SetForegroundColor}; use crossterm::terminal::{Clear, ClearType}; +use mockall::automock; use std::io::Write; use crate::array::Array2D; +#[automock] pub trait Surface { fn width(&self) -> usize; fn height(&self) -> usize;