diff options
-rw-r--r-- | src/lib.rs | 5 | ||||
-rw-r--r-- | src/main.rs | 70 | ||||
-rw-r--r-- | src/pattern.rs | 16 | ||||
-rw-r--r-- | src/transform.rs | 53 |
4 files changed, 115 insertions, 29 deletions
@@ -1,9 +1,12 @@ +#![feature(box_syntax)] + use color::Color; pub mod color; -pub mod transform; pub mod pattern; +pub mod transform; pub trait Sample { fn sample(&mut self, x: f64, y: f64) -> Color; + fn clone(&self) -> Box<dyn Sample>; } diff --git a/src/main.rs b/src/main.rs index d7b1349..304e1ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,9 @@ use anyhow::Result; use blubcat::{ color::Color, pattern::{Gradient, Rainbow, Sequence, Solid}, - transform::{Composite, CompositeOperation, Matrix, Polar, Translate, Transpose}, + transform::{ + Composite, CompositeOperation, Filter, FilterOperation, Matrix, Polar, Translate, Transpose, + }, Sample, }; use std::{ @@ -15,14 +17,14 @@ use std::{ process::exit, }; -static HELP: &str = " +const HELP: &str = " Usage: blubcat [OPTION...] [FILE...] Concatenate FILE(s) to standard output, colorizing the output according to OPTION. With no FILE, or when FILE is -, the standard input is read. OPTION can be PATTERN, TRANSFORM or PRESET. Patterns are stored on a stack. -<pop>,<push> indicates the number of patters popped from the stack and how many are pushed back on. +<pop>,<push> indicates the number of patterns popped from the stack and how many are pushed back on. PATTERN: 0,1 -R --rainbow Sinebow @@ -41,6 +43,8 @@ TRANSFORM: 2,1 --add 2,1 --subtract 2,1 --multiply +1,2 -D --duplicate +1,1 --abs PRESET: 0,1 --zebra @@ -57,7 +61,7 @@ fn main() -> Result<()> { let mut args = env::args().skip(1); let mut arg_extra = None; - let mut pat: Vec<Box<dyn Sample>> = vec![]; + let mut stack: Vec<Box<dyn Sample>> = vec![]; loop { let a = args.next(); @@ -81,32 +85,32 @@ fn main() -> Result<()> { } /* PRESETS */ - "--pride" => pat.push(flag_helper(&[ + "--pride" => stack.push(flag_helper(&[ "e50000", "ff8d00", "ffee00", "028121", "004cff", "770088", ])), - "--zebra" => pat.push(flag_helper(&["000000", "ffffff"])), - "--trans" => pat.push(flag_helper(&[ + "--zebra" => stack.push(flag_helper(&["000000", "ffffff"])), + "--trans" => stack.push(flag_helper(&[ "5bcffb", "f5abb9", "ffffff", "f5abb9", "5bcffb", ])), - "--ukraine" => pat.push(flag_helper(&["0057B8", "FFD700"])), + "--ukraine" => stack.push(flag_helper(&["0057B8", "FFD700"])), /* PATTERNS */ - "-R" | "--rainbow" => pat.push(box Rainbow), - "-S" | "--sequence" => pat.push(box Sequence( + "-R" | "--rainbow" => stack.push(box Rainbow), + "-S" | "--sequence" => stack.push(box Sequence( arg_next() .unwrap() .split(",") .map(|e| Color::parse(e).expect("color invalid")) .collect::<Vec<_>>(), )), - "-G" | "--gradient" => pat.push(box Gradient( + "-G" | "--gradient" => stack.push(box Gradient( arg_next() .unwrap() .split(",") .map(|e| Color::parse(e).expect("color invalid")) .collect::<Vec<_>>(), )), - "-K" | "--const" => pat.push(box Solid( + "-K" | "--const" => stack.push(box Solid( Color::parse(arg_next().expect("color expected").as_str()) .expect("invalid color"), )), @@ -115,62 +119,72 @@ fn main() -> Result<()> { "-s" | "--scale" => { let fac = arg_num(); push_queue.push(box Matrix { - inner: pat.pop().unwrap(), + inner: stack.pop().unwrap(), matrix: ((fac, 0.0), (0.0, fac)), }) } "-r" | "--rotate" => { let angle = arg_num() * PI * 2.0; push_queue.push(box Matrix { - inner: pat.pop().unwrap(), + inner: stack.pop().unwrap(), matrix: ((angle.cos(), -angle.sin()), (angle.sin(), angle.cos())), }) } "-m" | "--matrix" => push_queue.push(box Matrix { - inner: pat.pop().unwrap(), + inner: stack.pop().unwrap(), matrix: ((arg_num(), arg_num()), (arg_num(), arg_num())), }), - "-p" | "--polar" => push_queue.push(box Polar(pat.pop().unwrap())), + "-p" | "--polar" => push_queue.push(box Polar(stack.pop().unwrap())), "-t" | "--translate" => { let (x, y) = (arg_num(), arg_num()); push_queue.push(box Translate { offset: (x, y), - inner: pat.pop().unwrap(), + inner: stack.pop().unwrap(), }) } - "-T" | "--transpose" => push_queue.push(box Transpose(pat.pop().unwrap())), + "-T" | "--transpose" => push_queue.push(box Transpose(stack.pop().unwrap())), + "-d" | "--duplicate" => { + let p = stack.pop().unwrap(); + push_queue.push(p.clone()); + push_queue.push(p.clone()) + } "--add" => push_queue.push(box Composite { - a: pat.pop().unwrap(), - b: pat.pop().unwrap(), + a: stack.pop().unwrap(), + b: stack.pop().unwrap(), mode: CompositeOperation::Add, }), "--subtract" => push_queue.push(box Composite { - a: pat.pop().unwrap(), - b: pat.pop().unwrap(), + a: stack.pop().unwrap(), + b: stack.pop().unwrap(), mode: CompositeOperation::Subtract, }), "--multiply" => push_queue.push(box Composite { - a: pat.pop().unwrap(), - b: pat.pop().unwrap(), + a: stack.pop().unwrap(), + b: stack.pop().unwrap(), mode: CompositeOperation::Multiply, }), "--mix" => push_queue.push(box Composite { - a: pat.pop().unwrap(), - b: pat.pop().unwrap(), + a: stack.pop().unwrap(), + b: stack.pop().unwrap(), mode: CompositeOperation::Mix(arg_num()), }), + "--abs" => push_queue.push(box Filter { + inner: stack.pop().unwrap(), + mode: FilterOperation::Abs, + }), + _ => panic!("unknown option {}", &a), } - pat.extend(push_queue.drain(..)) + stack.extend(push_queue.drain(..)) } else { break; } } let mut inputs = args.collect::<Vec<String>>(); - let mut pat = pat.pop().unwrap_or_else(|| box Solid(Color::WHITE)); + let mut pat = stack.pop().unwrap_or_else(|| box Solid(Color::WHITE)); if let Some(a) = arg_extra { inputs.insert(0, a) diff --git a/src/pattern.rs b/src/pattern.rs index 042d0be..8049c23 100644 --- a/src/pattern.rs +++ b/src/pattern.rs @@ -1,5 +1,6 @@ use crate::{color::Color, Sample}; +#[derive(Clone)] pub struct Rainbow; impl Sample for Rainbow { fn sample(&mut self, x: f64, _y: f64) -> Color { @@ -11,15 +12,23 @@ impl Sample for Rainbow { b: (x + PI / 3.0 * 4.0).sin() * 0.5 + 0.5, }; } + fn clone(&self) -> Box<dyn Sample> { + box Clone::clone(self) + } } +#[derive(Clone)] pub struct Sequence(pub Vec<Color>); impl Sample for Sequence { fn sample(&mut self, x: f64, _y: f64) -> Color { self.0[x.rem_euclid(self.0.len() as f64) as usize] } + fn clone(&self) -> Box<dyn Sample> { + box Clone::clone(self) + } } +#[derive(Clone)] pub struct Gradient(pub Vec<Color>); impl Sample for Gradient { fn sample(&mut self, x: f64, _y: f64) -> Color { @@ -29,11 +38,18 @@ impl Sample for Gradient { let b = self.0[(index_int + 1) % self.0.len()]; Color::mix(a, b, index_error) } + fn clone(&self) -> Box<dyn Sample> { + box Clone::clone(self) + } } +#[derive(Clone)] pub struct Solid(pub Color); impl Sample for Solid { fn sample(&mut self, _x: f64, _y: f64) -> Color { self.0 } + fn clone(&self) -> Box<dyn Sample> { + box Clone::clone(self) + } } diff --git a/src/transform.rs b/src/transform.rs index ccb1850..10af82e 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -12,6 +12,12 @@ impl Sample for Matrix { ); self.inner.sample(x, y) } + fn clone(&self) -> Box<dyn Sample> { + box Self { + inner: self.inner.clone(), + matrix: self.matrix.clone(), + } + } } pub struct Polar(pub Box<dyn Sample>); @@ -21,6 +27,9 @@ impl Sample for Polar { let dist = (x * x + y * y).sqrt(); self.0.sample(ang, dist) } + fn clone(&self) -> Box<dyn Sample> { + box Self(self.0.clone()) + } } pub struct Transpose(pub Box<dyn Sample>); @@ -28,6 +37,9 @@ impl Sample for Transpose { fn sample(&mut self, x: f64, y: f64) -> Color { self.0.sample(y, x) } + fn clone(&self) -> Box<dyn Sample> { + box Self(self.0.clone()) + } } pub struct Translate { @@ -38,8 +50,42 @@ impl Sample for Translate { fn sample(&mut self, x: f64, y: f64) -> Color { self.inner.sample(x - self.offset.0, y - self.offset.1) } + fn clone(&self) -> Box<dyn Sample> { + box Self { + inner: self.inner.clone(), + offset: self.offset.clone(), + } + } +} + +#[derive(Clone, Copy)] +pub enum FilterOperation { + Abs, +} +pub struct Filter { + pub inner: Box<dyn Sample>, + pub mode: FilterOperation, +} +impl Sample for Filter { + fn sample(&mut self, x: f64, y: f64) -> Color { + let a = self.inner.sample(x, y); + match self.mode { + FilterOperation::Abs => Color { + r: a.r.abs(), + g: a.g.abs(), + b: a.b.abs(), + }, + } + } + fn clone(&self) -> Box<dyn Sample> { + box Self { + inner: self.inner.clone(), + mode: self.mode, + } + } } +#[derive(Clone, Copy)] pub enum CompositeOperation { Add, Subtract, @@ -78,4 +124,11 @@ impl Sample for Composite { }, } } + fn clone(&self) -> Box<dyn Sample> { + box Self { + a: self.a.clone(), + b: self.b.clone(), + mode: self.mode, + } + } } |