From ca3536ce691e8726ceebbf6c058bd009ebab62ab Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 5 Jun 2022 14:52:50 +0200 Subject: initial --- src/main.rs | 225 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 src/main.rs (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..757b842 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,225 @@ +#![feature(box_syntax)] +pub mod color; + +use anyhow::Result; +use color::Color; +use std::{ + env, + f64::consts::PI, + fs::File, + io::{stdin, BufRead, BufReader, Read}, + process::exit, +}; + +static HELP: &str = " +Usage: blubcat PATTERN [FILE]... +Concatenate FILE(s) to standard output, colorizing the output according to OPTION(s). + +With no FILE, or when FILE is -, the standard input is read. +If PATTERN is repeated only the last one will be used. + +PATTERN: + -R --rainbow Rainbow + -S --sequence Sequences colors on the X-axis + -G --gradient Interpolates the colors on the X-axis + Apply TRANSFORM to PATTERN + + +TRANSFORM: + -r --rotate + -s --scale + -m --matrix + +PRESET: + -P --pride Pride flag + +COLOR: + + black white red green blue yellow cyan magenta + +"; + +fn main() -> Result<()> { + let mut args = env::args().skip(1); + + let mut pat: Box = box Solid(Color::WHITE); + + loop { + let a = args.next(); + let mut arg_next = || args.next(); + let mut arg_num = || { + arg_next() + .expect("value expected") + .parse::() + .expect("not a number") + }; + + if let Some(a) = a { + if !a.starts_with("-") || a == "--" || a == "-" { + break; + } + match a.as_str() { + "-?" | "--help" => { + println!("{}", HELP); + exit(0) + } + + /* PRESETS */ + "-P" | "--pride" => { + pat = box Transform { + matrix: ((1.0 / 12.0, 1.0 / 12.0), (0.0, 0.0)), + inner: box Extend(box Sequence(vec![ + Color::parse("e50000").unwrap(), + Color::parse("ff8d00").unwrap(), + Color::parse("ffee00").unwrap(), + Color::parse("028121").unwrap(), + Color::parse("004cff").unwrap(), + Color::parse("770088").unwrap(), + ])), + } + } + + /* PATTERNS */ + "-R" | "--rainbow" => pat = box Extend(box Rainbow), + "-S" | "--sequence" => { + pat = box Extend(box Sequence( + arg_next() + .unwrap() + .split(",") + .map(|e| Color::parse(e).expect("color invalid")) + .collect::>(), + )) + } + "-G" | "--gradient" => { + pat = box Extend(box Gradient( + arg_next() + .unwrap() + .split(",") + .map(|e| Color::parse(e).expect("color invalid")) + .collect::>(), + )) + } + + /* TRANSFORMS */ + "-s" | "--scale" => { + let fac = arg_num(); + pat = box Transform { + inner: pat, + matrix: ((fac, 0.0), (0.0, fac)), + } + } + "-r" | "--rotate" => { + let angle = arg_num() * PI * 2.0; + pat = box Transform { + inner: pat, + matrix: ((angle.cos(), -angle.sin()), (angle.sin(), angle.cos())), + } + } + "-m" | "--matrix" => { + pat = box Transform { + inner: pat, + matrix: ((arg_num(), arg_num()), (arg_num(), arg_num())), + } + } + _ => panic!("unknown option {}", &a), + } + } else { + break; + } + } + + let inputs = args.collect::>(); + + if inputs.len() == 0 { + colorize(stdin(), &mut pat)? + } else { + for f in inputs { + let file = File::open(f)?; + colorize(file, &mut pat)?; + } + } + Ok(()) +} + +fn colorize(file: impl Read, sampler: &mut Box) -> Result<()> { + let mut file = BufReader::new(file); + let mut line = String::new(); + for y in 0.. { + if file.read_line(&mut line)? == 0 { + break; + } + for (x, c) in line.chars().enumerate() { + let (r, g, b) = sampler.sample(x as f64, y as f64).as_rgb888(); + print!("\x1b[38;2;{r};{g};{b}m{c}") + } + line.clear(); + } + Ok(()) +} + +trait Sample2D { + fn sample(&mut self, x: f64, y: f64) -> Color; +} +trait Sample1D { + fn sample(&mut self, x: f64) -> Color; +} + +struct Extend(Box); +impl Sample2D for Extend { + fn sample(&mut self, x: f64, _y: f64) -> Color { + self.0.sample(x) + } +} + +struct Transform { + pub inner: Box, + pub matrix: ((f64, f64), (f64, f64)), +} +impl Sample2D for Transform { + fn sample(&mut self, x: f64, y: f64) -> Color { + let (x, y) = ( + x * self.matrix.0 .0 + y * self.matrix.0 .1, + x * self.matrix.1 .0 + y * self.matrix.1 .1, + ); + self.inner.sample(x, y) + } +} + +struct Rainbow; +impl Sample1D for Rainbow { + fn sample(&mut self, x: f64) -> Color { + static PI: f64 = 3.14; + let x = x * PI * 2.0; + return Color { + r: (x + PI / 3.0 * 0.0).sin() * 0.5 + 0.5, + g: (x + PI / 3.0 * 2.0).sin() * 0.5 + 0.5, + b: (x + PI / 3.0 * 4.0).sin() * 0.5 + 0.5, + }; + } +} + +struct Sequence(pub Vec); +impl Sample1D for Sequence { + fn sample(&mut self, x: f64) -> Color { + self.0[(x * self.0.len() as f64).floor() as usize % self.0.len()] + } +} + +struct Gradient(pub Vec); +impl Sample1D for Gradient { + fn sample(&mut self, x: f64) -> Color { + let index = x * self.0.len() as f64; + let index_int = index.floor() as usize; + let index_error = index % 1.0; + let a = self.0[index_int % self.0.len()]; + let b = self.0[(index_int + 1) % self.0.len()]; + Color::mix(a, b, index_error) + } +} + +struct Solid(Color); +impl Sample2D for Solid { + fn sample(&mut self, _x: f64, _y: f64) -> Color { + self.0 + } +} -- cgit v1.2.3-70-g09d2