mirror of
https://github.com/ricoriedel/wipe.git
synced 2024-12-22 14:16:48 +00:00
Add sampler
This commit is contained in:
parent
56e630204a
commit
8d75146744
35
Cargo.lock
generated
35
Cargo.lock
generated
@ -38,6 +38,12 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.24.0"
|
||||
@ -63,6 +69,19 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
@ -293,12 +312,27 @@ version = "0.6.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
@ -435,5 +469,6 @@ version = "2.0.0"
|
||||
dependencies = [
|
||||
"approx",
|
||||
"crossterm",
|
||||
"derive_more",
|
||||
"mockall",
|
||||
]
|
||||
|
@ -8,6 +8,7 @@ authors = ["Rico Riedel"]
|
||||
|
||||
[dependencies]
|
||||
crossterm = "0.24"
|
||||
derive_more = "0.99"
|
||||
|
||||
[dev-dependencies]
|
||||
mockall = "0.11"
|
||||
|
@ -1,27 +1,40 @@
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum CharSample {
|
||||
Keep,
|
||||
Draw(char),
|
||||
Clear,
|
||||
}
|
||||
|
||||
#[cfg_attr(test, mockall::automock)]
|
||||
pub trait CharConverter {
|
||||
fn convert(&self, level: f32) -> char;
|
||||
fn convert(&self, level: f32) -> CharSample;
|
||||
}
|
||||
|
||||
pub struct CharConverterImpl {
|
||||
chars: String,
|
||||
count: usize,
|
||||
count: f32,
|
||||
}
|
||||
|
||||
impl CharConverterImpl {
|
||||
pub fn new(chars: String) -> Self {
|
||||
let count = chars.chars().count();
|
||||
let count = chars.chars().count() as f32;
|
||||
|
||||
Self { chars, count }
|
||||
}
|
||||
}
|
||||
|
||||
impl CharConverter for CharConverterImpl {
|
||||
fn convert(&self, level: f32) -> char {
|
||||
let len = self.count as f32;
|
||||
let index = (level * len).rem_euclid(len) as usize;
|
||||
fn convert(&self, level: f32) -> CharSample {
|
||||
if level < 0.0 {
|
||||
CharSample::Keep
|
||||
} else if level < 1.0 {
|
||||
let index = (level * self.count) as usize;
|
||||
let char = self.chars.chars().nth(index).unwrap();
|
||||
|
||||
self.chars.chars().nth(index).unwrap()
|
||||
CharSample::Draw(char)
|
||||
} else {
|
||||
CharSample::Clear
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,37 +43,26 @@ mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn convert_negative_index() {
|
||||
fn convert_keep() {
|
||||
let converter = CharConverterImpl::new("abc".to_string());
|
||||
|
||||
assert_eq!('c', converter.convert(-0.2));
|
||||
assert_eq!(CharSample::Keep, converter.convert(-0.1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_index_zero() {
|
||||
let converter = CharConverterImpl::new("abc".to_string());
|
||||
fn convert_draw() {
|
||||
let converter = CharConverterImpl::new("xyz".to_string());
|
||||
|
||||
assert_eq!('a', converter.convert(0.0));
|
||||
assert_eq!(CharSample::Draw('x'), converter.convert(0.0));
|
||||
assert_eq!(CharSample::Draw('y'), converter.convert(0.5));
|
||||
assert_eq!(CharSample::Draw('z'), converter.convert(0.9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert() {
|
||||
let converter = CharConverterImpl::new("abc".to_string());
|
||||
fn convert_clear() {
|
||||
let converter = CharConverterImpl::new("123".to_string());
|
||||
|
||||
assert_eq!('b', converter.convert(0.5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_index_one() {
|
||||
let converter = CharConverterImpl::new("abc".to_string());
|
||||
|
||||
assert_eq!('a', converter.convert(1.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_index_above_one() {
|
||||
let converter = CharConverterImpl::new("abc".to_string());
|
||||
|
||||
assert_eq!('b', converter.convert(1.5));
|
||||
assert_eq!(CharSample::Clear, converter.convert(1.0));
|
||||
assert_eq!(CharSample::Clear, converter.convert(1.5));
|
||||
}
|
||||
}
|
||||
|
@ -1,55 +0,0 @@
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum Level {
|
||||
Keep,
|
||||
Draw(f32),
|
||||
Clear,
|
||||
}
|
||||
|
||||
#[cfg_attr(test, mockall::automock)]
|
||||
pub trait LevelConverter {
|
||||
fn convert(&self, level: f32) -> Level;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LevelConverterImpl;
|
||||
|
||||
impl LevelConverter for LevelConverterImpl {
|
||||
fn convert(&self, level: f32) -> Level {
|
||||
if level < 0.0 {
|
||||
Level::Keep
|
||||
} else if level < 1.0 {
|
||||
Level::Draw(level)
|
||||
} else {
|
||||
Level::Clear
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn convert_keep() {
|
||||
let converter = LevelConverterImpl::default();
|
||||
|
||||
assert_eq!(Level::Keep, converter.convert(-0.1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_draw() {
|
||||
let converter = LevelConverterImpl::default();
|
||||
|
||||
assert_eq!(Level::Draw(0.0), converter.convert(0.0));
|
||||
assert_eq!(Level::Draw(0.5), converter.convert(0.5));
|
||||
assert_eq!(Level::Draw(0.9), converter.convert(0.9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_clear() {
|
||||
let converter = LevelConverterImpl::default();
|
||||
|
||||
assert_eq!(Level::Clear, converter.convert(1.0));
|
||||
assert_eq!(Level::Clear, converter.convert(1.5));
|
||||
}
|
||||
}
|
@ -1,44 +1,30 @@
|
||||
use crate::convert::char::CharConverter;
|
||||
use crate::convert::char::{CharConverter, CharSample};
|
||||
use crate::convert::color::ColorConverter;
|
||||
use crate::convert::level::{Level, LevelConverter};
|
||||
use crossterm::style::Color;
|
||||
|
||||
mod char;
|
||||
mod color;
|
||||
mod level;
|
||||
pub mod char;
|
||||
pub mod color;
|
||||
|
||||
#[cfg_attr(test, mockall::automock)]
|
||||
pub trait Converter {
|
||||
fn char(&self, level: f32) -> char;
|
||||
fn char(&self, level: f32) -> CharSample;
|
||||
fn color(&self, level: f32) -> Color;
|
||||
fn level(&self, level: f32) -> Level;
|
||||
}
|
||||
|
||||
pub struct ConverterImpl<T1, T2, T3> {
|
||||
#[derive(derive_more::Constructor)]
|
||||
pub struct ConverterImpl<T1, T2> {
|
||||
char: T1,
|
||||
color: T2,
|
||||
level: T3,
|
||||
}
|
||||
|
||||
impl<T1, T2, T3> ConverterImpl<T1, T2, T3> {
|
||||
pub fn new(char: T1, color: T2, level: T3) -> Self {
|
||||
Self { char, color, level }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T1: CharConverter, T2: ColorConverter, T3: LevelConverter> Converter
|
||||
for ConverterImpl<T1, T2, T3>
|
||||
{
|
||||
fn char(&self, level: f32) -> char {
|
||||
impl<T1: CharConverter, T2: ColorConverter> Converter for ConverterImpl<T1, T2> {
|
||||
fn char(&self, level: f32) -> CharSample {
|
||||
self.char.convert(level)
|
||||
}
|
||||
|
||||
fn color(&self, level: f32) -> Color {
|
||||
self.color.convert(level)
|
||||
}
|
||||
|
||||
fn level(&self, level: f32) -> Level {
|
||||
self.level.convert(level)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -46,51 +32,34 @@ mod test {
|
||||
use super::*;
|
||||
use crate::convert::char::MockCharConverter;
|
||||
use crate::convert::color::MockColorConverter;
|
||||
use crate::convert::level::MockLevelConverter;
|
||||
use mockall::predicate::eq;
|
||||
use mockall::predicate::*;
|
||||
|
||||
#[test]
|
||||
fn char() {
|
||||
let mut char = MockCharConverter::new();
|
||||
let color = MockColorConverter::new();
|
||||
let level = MockLevelConverter::new();
|
||||
|
||||
char.expect_convert().with(eq(4.0)).return_const('M');
|
||||
char.expect_convert()
|
||||
.with(eq(4.0))
|
||||
.return_const(CharSample::Draw('M'));
|
||||
|
||||
let converter = ConverterImpl::new(char, color, level);
|
||||
let converter = ConverterImpl::new(char, color);
|
||||
|
||||
assert_eq!('M', converter.char(4.0));
|
||||
assert_eq!(CharSample::Draw('M'), converter.char(4.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color() {
|
||||
let char = MockCharConverter::new();
|
||||
let mut color = MockColorConverter::new();
|
||||
let level = MockLevelConverter::new();
|
||||
|
||||
color
|
||||
.expect_convert()
|
||||
.with(eq(2.0))
|
||||
.return_const(Color::Yellow);
|
||||
|
||||
let converter = ConverterImpl::new(char, color, level);
|
||||
let converter = ConverterImpl::new(char, color);
|
||||
|
||||
assert_eq!(Color::Yellow, converter.color(2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn level() {
|
||||
let char = MockCharConverter::new();
|
||||
let color = MockColorConverter::new();
|
||||
let mut level = MockLevelConverter::new();
|
||||
|
||||
level
|
||||
.expect_convert()
|
||||
.with(eq(3.0))
|
||||
.return_const(Level::Draw(2.0));
|
||||
|
||||
let converter = ConverterImpl::new(char, color, level);
|
||||
|
||||
assert_eq!(Level::Draw(2.0), converter.level(3.0));
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ pub mod printer;
|
||||
pub mod convert;
|
||||
pub mod term;
|
||||
mod vec;
|
||||
mod sampler;
|
||||
|
||||
fn main() -> Result<(), error::Error> {
|
||||
Ok(())
|
||||
|
@ -17,6 +17,7 @@ pub trait PatternFactory {
|
||||
fn create(&self, config: &Config) -> Box<dyn Pattern>;
|
||||
}
|
||||
|
||||
#[cfg_attr(test, mockall::automock)]
|
||||
pub trait Pattern {
|
||||
fn sample(&self, pos: Vector) -> f32;
|
||||
}
|
||||
|
142
src/sampler.rs
Normal file
142
src/sampler.rs
Normal file
@ -0,0 +1,142 @@
|
||||
use crate::convert::char::CharSample;
|
||||
use crate::convert::Converter;
|
||||
use crate::pattern::{Config, Pattern, PatternFactory};
|
||||
use crate::vec::Vector;
|
||||
use crossterm::style::Color;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum Sample {
|
||||
Keep,
|
||||
Draw { char: char, color: Color },
|
||||
Clear,
|
||||
}
|
||||
|
||||
pub trait SamplerFactory {
|
||||
fn create(&self, config: &Config) -> Box<dyn Sampler>;
|
||||
}
|
||||
|
||||
pub trait Sampler {
|
||||
fn sample(&self, pos: Vector) -> Sample;
|
||||
}
|
||||
|
||||
#[derive(derive_more::Constructor)]
|
||||
pub struct SamplerFactoryImpl<T> {
|
||||
char: Box<dyn PatternFactory>,
|
||||
color: Box<dyn PatternFactory>,
|
||||
converter: Rc<T>,
|
||||
}
|
||||
|
||||
#[derive(derive_more::Constructor)]
|
||||
pub struct SamplerImpl<T> {
|
||||
char: Box<dyn Pattern>,
|
||||
color: Box<dyn Pattern>,
|
||||
converter: Rc<T>,
|
||||
}
|
||||
|
||||
impl<T: Converter + 'static> SamplerFactory for SamplerFactoryImpl<T> {
|
||||
fn create(&self, config: &Config) -> Box<dyn Sampler> {
|
||||
Box::new(SamplerImpl::new(
|
||||
self.char.create(config),
|
||||
self.color.create(config),
|
||||
self.converter.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Converter> Sampler for SamplerImpl<T> {
|
||||
fn sample(&self, pos: Vector) -> Sample {
|
||||
match self.converter.char(self.char.sample(pos)) {
|
||||
CharSample::Keep => Sample::Keep,
|
||||
CharSample::Draw(char) => Sample::Draw {
|
||||
char,
|
||||
color: self.converter.color(self.color.sample(pos)),
|
||||
},
|
||||
CharSample::Clear => Sample::Clear,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::convert::MockConverter;
|
||||
use crate::pattern::MockPattern;
|
||||
use mockall::predicate::eq;
|
||||
|
||||
#[test]
|
||||
fn sample_keep() {
|
||||
let color = MockPattern::new();
|
||||
let mut char = MockPattern::new();
|
||||
let mut converter = MockConverter::new();
|
||||
|
||||
char.expect_sample().return_const(3.0);
|
||||
converter.expect_char().return_const(CharSample::Keep);
|
||||
|
||||
let sampler = SamplerImpl::new(Box::new(char), Box::new(color), Rc::new(converter));
|
||||
|
||||
assert_eq!(Sample::Keep, sampler.sample(Vector::default()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_draw() {
|
||||
let mut char = MockPattern::new();
|
||||
let mut color = MockPattern::new();
|
||||
let mut converter = MockConverter::new();
|
||||
|
||||
char.expect_sample().return_const(3.0);
|
||||
color.expect_sample().return_const(2.0);
|
||||
converter.expect_char().return_const(CharSample::Draw('M'));
|
||||
converter.expect_color().return_const(Color::Red);
|
||||
|
||||
let sampler = SamplerImpl::new(Box::new(char), Box::new(color), Rc::new(converter));
|
||||
|
||||
assert_eq!(
|
||||
Sample::Draw {
|
||||
char: 'M',
|
||||
color: Color::Red
|
||||
},
|
||||
sampler.sample(Vector::default())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_clear() {
|
||||
let color = MockPattern::new();
|
||||
let mut char = MockPattern::new();
|
||||
let mut converter = MockConverter::new();
|
||||
|
||||
char.expect_sample().return_const(3.0);
|
||||
converter.expect_char().return_const(CharSample::Clear);
|
||||
|
||||
let sampler = SamplerImpl::new(Box::new(char), Box::new(color), Rc::new(converter));
|
||||
|
||||
assert_eq!(Sample::Clear, sampler.sample(Vector::default()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_args_correct() {
|
||||
let mut char = MockPattern::new();
|
||||
let mut color = MockPattern::new();
|
||||
let mut converter = MockConverter::new();
|
||||
|
||||
char.expect_sample()
|
||||
.with(eq(Vector::new(4.0, 2.0)))
|
||||
.return_const(6.0);
|
||||
color
|
||||
.expect_sample()
|
||||
.with(eq(Vector::new(4.0, 2.0)))
|
||||
.return_const(7.0);
|
||||
converter
|
||||
.expect_char()
|
||||
.with(eq(6.0))
|
||||
.return_const(CharSample::Draw('A'));
|
||||
converter
|
||||
.expect_color()
|
||||
.with(eq(7.0))
|
||||
.return_const(Color::Reset);
|
||||
|
||||
SamplerImpl::new(Box::new(char), Box::new(color), Rc::new(converter))
|
||||
.sample(Vector::new(4.0, 2.0));
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use std::ops::Sub;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Default)]
|
||||
pub struct Vector {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
|
Loading…
Reference in New Issue
Block a user