diff --git a/Cargo.toml b/Cargo.toml index 83da5b0..5bb18e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,8 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ansi_term = "0.12" bincode = "1.3.1" +crossterm = "0.18" decorum = "0.3" dyn-clone = "1.0" enum_dispatch = "0.3" @@ -22,10 +22,11 @@ ndarray = { version = "0.13", features = ["rayon", "serde"] } piecewise-linear = "0.1" plotly = { version = "0.6", features = ["kaleido", "plotly_ndarray"], path = "../plotly/plotly" } serde = "1.0" -serde_cbor = "0.11" structopt = "0.3" threadpool = "1.8" y4m = "0.7" +# ansi_term = "0.12" +# serde_cbor = "0.11" [dev-dependencies] criterion = "0.3" diff --git a/src/bin/viewer.rs b/src/bin/viewer.rs index 2daa4a5..92d6a9c 100644 --- a/src/bin/viewer.rs +++ b/src/bin/viewer.rs @@ -1,10 +1,12 @@ -use coremem::StaticSim; +use coremem::{GenericSim as _, StaticSim}; use coremem::render::{ColorTermRenderer, Renderer as _}; use std::fs::{File, read_dir}; -use std::io::BufReader; +use std::io::{BufReader, Read as _, stdin}; +use std::ops::Deref; use std::path::{Path, PathBuf}; use structopt::StructOpt; +use crossterm::event::{Event, KeyCode}; #[derive(Debug, StructOpt)] struct Opt { @@ -16,6 +18,14 @@ struct Frame { data: StaticSim, } +impl Deref for Frame { + type Target = StaticSim; + fn deref(&self) -> &Self::Target { + &self.data + } +} + +#[derive(Default)] struct Loader { dir: PathBuf, } @@ -51,12 +61,46 @@ impl Loader { struct Viewer { viewing: Option, z: u32, + loader: Loader, renderer: ColorTermRenderer, } impl Viewer { - fn switch_frame(&mut self, frame: Frame) { - self.viewing = Some(frame); + fn prev_frame(&mut self) { + println!(""); + } + fn next_frame(&mut self) { + let next_frame = self.loader.load_next(self.viewing.as_ref()); + if next_frame.is_some() { + self.viewing = next_frame; + self.render(); + } else { + println!(""); + } + } + fn prev_depth(&mut self) { + match self.z { + 0 => { + println!(""); + }, + _ => { + self.z -= 1; + self.render(); + } + } + } + fn next_depth(&mut self) { + match self.z { + z if z+1 < self.viewing.as_ref().map(|f| f.depth()).unwrap_or(0) => { + self.z += 1; + self.render(); + }, + _ => { + println!(""); + } + } + } + fn render(&self) { self.renderer.render_z_slice(self.viewing.as_ref().map(|f| &f.data).unwrap(), self.z, &[]); } } @@ -65,6 +109,25 @@ fn main() { let opt = Opt::from_args(); let loader = Loader::new(opt.directory); let mut viewer = Viewer::default(); - let frame = loader.load_next(None); - viewer.switch_frame(frame.unwrap()); + viewer.loader = loader; + //let frame = loader.load_next(None); + //viewer.switch_frame(frame.unwrap()); + + // disable line buffering + crossterm::terminal::enable_raw_mode().unwrap(); + loop { + match crossterm::event::read().unwrap() { + Event::Key(e) => match e.code { + KeyCode::Left => viewer.prev_frame(), + KeyCode::Right => viewer.next_frame(), + KeyCode::Up => viewer.prev_depth(), + KeyCode::Down => viewer.next_depth(), + KeyCode::Char('q') => break, + _ => {}, + }, + _ => {}, + } + } + // otherwise terminal is left in a wacky state + crossterm::terminal::disable_raw_mode().unwrap(); } diff --git a/src/render.rs b/src/render.rs index f95d496..164cc05 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,16 +1,17 @@ -use ansi_term::Color::RGB; use crate::geom::{Index, Meters, Vec2, Vec3, Vec3u}; use crate::{flt::{Flt, Real}, Material as _}; use crate::mat; use crate::sim::{Cell, GenericSim}; use crate::meas::AbstractMeasurement; +use crossterm::{cursor, ExecutableCommand as _, QueueableCommand as _}; +use crossterm::style::{style, Color, PrintStyledContent, StyledContent as _}; use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _}; use log::{trace, info}; use plotly; use image::{RgbImage, Rgb}; use imageproc::{pixelops, drawing}; use std::fs::File; -use std::io::BufWriter; +use std::io::{BufWriter, Write as _}; use std::path::PathBuf; use std::sync::{Mutex, RwLock}; use y4m; @@ -274,17 +275,25 @@ pub struct ColorTermRenderer; impl Renderer for ColorTermRenderer { fn render_with_image(&self, _state: &dyn GenericSim, im: &RgbImage) { - let square = "█"; - let buf: String = im - .enumerate_rows() - .map(|(_y, row)| { - format!("{}\n", row.map(|(_x, _y, p)| { - format!("{}", RGB(p.0[0], p.0[1], p.0[2]).paint(square)) - }).collect::()) - }) - .collect(); - - println!("{}", buf); + // TODO: should scale the image to the size of the terminal! + let mut stdout = std::io::stdout(); + let (w, h) = crossterm::terminal::size().unwrap(); + stdout.queue(cursor::MoveTo(0, 0)); + for (y, row) in im.enumerate_rows() { + if y >= h.into() { + break; + } + stdout.queue(cursor::MoveDown(1)); + stdout.queue(cursor::MoveToColumn(0)); + for (_x, _y, p) in row { + stdout.queue(PrintStyledContent(style(" ").on(Color::Rgb { + r: p.0[0], + g: p.0[1], + b: p.0[2], + }))); + } + } + stdout.flush(); } }