aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs5
-rw-r--r--src/main.rs70
-rw-r--r--src/pattern.rs16
-rw-r--r--src/transform.rs53
4 files changed, 115 insertions, 29 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 76b35c9..fb7d5a2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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,
+ }
+ }
}