From b309849e041e2bc958029a9e3c520a0add4731fb Mon Sep 17 00:00:00 2001 From: Colin Date: Wed, 16 Dec 2020 23:23:54 -0800 Subject: [PATCH] Parameterize the Renderer trait over GenericSim to avoid taking it dynamically Notably, this allows the SerializerRenderer to serialize the actual state to disk, as-is. That'll allow for more compact images, and potentially also a way to resume simulations. I expect the viewer will be broken for new simulations, initially. --- src/driver.rs | 67 ++++++++++++-------------------- src/render.rs | 103 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 93 insertions(+), 77 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index c3e85a2..512984d 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -16,7 +16,7 @@ use threadpool::ThreadPool; pub struct Driver { pub state: SimState, - renderer: Arc, + renderer: Arc>>, // TODO: use Rayon's thread pool? render_pool: ThreadPool, render_channel: (SyncSender<()>, Receiver<()>), @@ -51,12 +51,6 @@ impl Driver { } } -impl Driver { - pub fn dyn_state(&mut self) -> &mut dyn GenericSim { - &mut self.state - } -} - impl Driver { pub fn add_stimulus(&mut self, s: S) { self.stimuli.push(Box::new(s)) @@ -69,8 +63,30 @@ impl Driver { pub fn set_steps_per_frame(&mut self, steps_per_frame: u64) { self.steps_per_frame = steps_per_frame; } +} - fn add_renderer(&mut self, renderer: R, name: &str) { +impl Driver { + pub fn fill_region(&mut self, region: &R, mat: M) { + for z in 0..self.state.depth() { + for y in 0..self.state.height() { + for x in 0..self.state.width() { + let loc = Index((x, y, z).into()); + let meters = loc.to_meters(self.state.feature_size()); + if region.contains(meters) { + *self.state.get_mut(loc).mat_mut() = mat.clone(); + } + } + } + } + } +} + +impl Driver { + pub fn dyn_state(&mut self) -> &mut dyn GenericSim { + &mut self.state + } + + fn add_renderer> + 'static>(&mut self, renderer: R, name: &str) { info!("render to {}", name); self.renderer.push(renderer); } @@ -94,22 +110,6 @@ impl Driver { } } -impl Driver { - pub fn fill_region(&mut self, region: &R, mat: M) { - for z in 0..self.state.depth() { - for y in 0..self.state.height() { - for x in 0..self.state.width() { - let loc = Index((x, y, z).into()); - let meters = loc.to_meters(self.state.feature_size()); - if region.contains(meters) { - *self.state.get_mut(loc).mat_mut() = mat.clone(); - } - } - } - } - } -} - impl Driver { fn render(&mut self) { let their_state = self.state.clone(); @@ -182,25 +182,6 @@ impl Driver { } impl> Driver { - // pub fn add_boundary(&mut self, thickness: u32, base_conductivity: Flt) { - // for inset in 0..thickness { - // let depth = thickness - inset; - // let conductivity = base_conductivity * (depth*depth) as Flt; - // for x in inset..self.state.width() - inset { - // // left - // *self.state.get_mut((x, inset).into()).mat_mut() = mat::Static::conductor(conductivity).into(); - // // right - // *self.state.get_mut((x, self.state.height() - 1 - inset).into()).mat_mut() = mat::Static::conductor(conductivity).into(); - // } - // for y in inset..self.state.height() - inset { - // // top - // *self.state.get_mut((inset, y).into()).mat_mut() = mat::Static::conductor(conductivity).into(); - // // bottom - // *self.state.get_mut((self.state.width() - 1 - inset, y).into()).mat_mut() = mat::Static::conductor(conductivity).into(); - // } - // } - // } - pub fn add_upml_boundary(&mut self, thickness: C) { // Based on explanation here (slide 63): https://empossible.net/wp-content/uploads/2020/01/Lecture-The-Perfectly-Matched-Layer.pdf let thickness = thickness.to_index(self.state.feature_size()); diff --git a/src/render.rs b/src/render.rs index f23e9ac..e71fabd 100644 --- a/src/render.rs +++ b/src/render.rs @@ -59,7 +59,7 @@ fn scale_vector(x: Vec2, typical_mag: Flt) -> Vec2 { x.with_mag(new_mag) } -fn im_size(state: &dyn GenericSim, max_w: u32, max_h: u32) -> (u32, u32) { +fn im_size(state: &S, max_w: u32, max_h: u32) -> (u32, u32) { let mut width = max_w; let mut height = width * state.height() / state.width(); if height > max_h { @@ -70,21 +70,21 @@ fn im_size(state: &dyn GenericSim, max_w: u32, max_h: u32) -> (u32, u32) { (width, height) } -struct RenderSteps<'a> { +struct RenderSteps<'a, S> { im: RgbImage, - sim: &'a dyn GenericSim, + sim: &'a S, meas: &'a [Box], /// Simulation z coordinate to sample z: u32, } -impl<'a> RenderSteps<'a> { +impl<'a, S: GenericSim> RenderSteps<'a, S> { /// Render using default configuration constants - fn render(state: &'a dyn GenericSim, measurements: &'a [Box], z: u32) -> RgbImage { + fn render(state: &'a S, measurements: &'a [Box], z: u32) -> RgbImage { Self::render_configured(state, measurements, z, (640, 480)) } /// Render, controlling things like the size. - fn render_configured(state: &'a dyn GenericSim, measurements: &'a [Box], z: u32, max_size: (u32, u32)) -> RgbImage { + fn render_configured(state: &'a S, measurements: &'a [Box], z: u32, max_size: (u32, u32)) -> RgbImage { let (width, height) = im_size(state, max_size.0, max_size.1); trace!("rendering at {}x{} with z={}", width, height, z); let mut me = Self::new(state, measurements, width, height, z); @@ -109,7 +109,7 @@ impl<'a> RenderSteps<'a> { me.render_measurements(); me.im } - fn new(sim: &'a dyn GenericSim, meas: &'a [Box], width: u32, height: u32, z: u32) -> Self { + fn new(sim: &'a S, meas: &'a [Box], width: u32, height: u32, z: u32) -> Self { RenderSteps { im: RgbImage::new(width, height), sim, @@ -259,20 +259,30 @@ impl ImageRenderExt for RgbImage { } } -pub trait Renderer: Send + Sync { - fn render_z_slice(&self, state: &dyn GenericSim, z: u32, measurements: &[Box]) { - self.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements); - } - fn render(&self, state: &dyn GenericSim, measurements: &[Box]) { - self.render_z_slice(state, state.depth() / 2, measurements); - } +pub trait Renderer: Send + Sync { + fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box]); + // { + // self.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements); + // } + fn render(&self, state: &S, measurements: &[Box]); /// Not intended to be called directly by users; implement this if you want the image to be /// computed using default settings and you just manage where to display/save it. - fn render_with_image(&self, state: &dyn GenericSim, _im: &RgbImage, measurements: &[Box]) { + fn render_with_image(&self, state: &S, _im: &RgbImage, measurements: &[Box]) { self.render(state, measurements); } } +fn default_render_z_slice>( + me: &R, state: &S, z: u32, measurements: &[Box] +) { + me.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements); +} +fn default_render>( + me: &R, state: &S, measurements: &[Box] +) { + me.render_z_slice(state, state.depth() / 2, measurements); +} + // pub struct NumericTermRenderer; // // impl Renderer for NumericTermRenderer { @@ -296,8 +306,11 @@ pub trait Renderer: Send + Sync { #[derive(Default)] pub struct ColorTermRenderer; -impl Renderer for ColorTermRenderer { - fn render_z_slice(&self, state: &dyn GenericSim, z: u32, measurements: &[Box]) { +impl Renderer for ColorTermRenderer { + fn render(&self, state: &S, measurements: &[Box]) { + default_render(self, state, measurements) + } + fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box]) { let (max_w, mut max_h) = crossterm::terminal::size().unwrap(); max_h = max_h.saturating_sub(1 + measurements.len() as u16); let im = RenderSteps::render_configured(state, &[], z, (max_w as _, max_h as _)); @@ -338,7 +351,7 @@ pub struct Y4MRenderer { } impl Y4MRenderer { - pub fn new>(output: S) -> Self { + pub fn new>(output: P) -> Self { Self { out_path: output.into(), encoder: Mutex::new(None), @@ -346,8 +359,14 @@ impl Y4MRenderer { } } -impl Renderer for Y4MRenderer { - fn render_with_image(&self, _state: &dyn GenericSim, im: &RgbImage, _meas: &[Box]) { +impl Renderer for Y4MRenderer { + fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box]) { + default_render_z_slice(self, state, z, measurements) + } + fn render(&self, state: &S, measurements: &[Box]) { + default_render(self, state, measurements) + } + fn render_with_image(&self, _state: &S, im: &RgbImage, _meas: &[Box]) { { let mut enc = self.encoder.lock().unwrap(); if enc.is_none() { @@ -413,8 +432,11 @@ impl PlotlyRenderer { } } -impl Renderer for PlotlyRenderer { - fn render(&self, state: &dyn GenericSim, _meas: &[Box]) { +impl Renderer for PlotlyRenderer { + fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box]) { + default_render_z_slice(self, state, z, measurements) + } + fn render(&self, state: &S, _meas: &[Box]) { use plotly::{ImageFormat, Plot, Rgba}; // use plotly::common::Marker; use plotly::layout::{AspectMode, Axis, Layout, LayoutScene}; @@ -442,7 +464,7 @@ impl Renderer for PlotlyRenderer { // if x%5 == 0 || y%5 == 0 || z%5 == 0 { // continue; // } - let cell = state.get(Index(Vec3u::new(x, y, z))); + let cell = (state as &dyn GenericSim).get(Index(Vec3u::new(x, y, z))); xv.push(x); yv.push(y); zv.push(z); @@ -480,32 +502,42 @@ impl Renderer for PlotlyRenderer { } } -#[derive(Default)] -pub struct MultiRenderer { - renderers: RwLock>>, +pub struct MultiRenderer { + renderers: RwLock>>>, } -impl MultiRenderer { +impl Default for MultiRenderer { + fn default() -> Self { + Self { + renderers: RwLock::new(Vec::new()) + } + } +} + +impl MultiRenderer { pub fn new() -> Self { Default::default() } - pub fn push(&self, r: R) { + pub fn push + 'static>(&self, r: R) { self.renderers.write().unwrap().push(Box::new(r)); } - pub fn with(self, r: R) -> Self { + pub fn with + 'static>(self, r: R) -> Self { self.push(r); self } } -impl Renderer for MultiRenderer { - fn render(&self, state: &dyn GenericSim, measurements: &[Box]) { +impl Renderer for MultiRenderer { + fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box]) { + default_render_z_slice(self, state, z, measurements) + } + fn render(&self, state: &S, measurements: &[Box]) { if self.renderers.read().unwrap().len() != 0 { self.render_with_image(state, &RenderSteps::render(state, measurements, state.depth() / 2), measurements); } } - fn render_with_image(&self, state: &dyn GenericSim, im: &RgbImage, measurements: &[Box]) { + fn render_with_image(&self, state: &S, im: &RgbImage, measurements: &[Box]) { for r in &*self.renderers.read().unwrap() { r.render_with_image(state, im, measurements); } @@ -530,8 +562,11 @@ impl SerializerRenderer { } } -impl Renderer for SerializerRenderer { - fn render(&self, state: &dyn GenericSim, measurements: &[Box]) { +impl Renderer for SerializerRenderer { + fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box]) { + default_render_z_slice(self, state, z, measurements) + } + fn render(&self, state: &S, measurements: &[Box]) { let snap = state.to_static(); let frame = SerializedFrame { state: snap,