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.
This commit is contained in:
2020-12-16 23:23:54 -08:00
parent 75fde43aa7
commit b309849e04
2 changed files with 93 additions and 77 deletions

View File

@@ -16,7 +16,7 @@ use threadpool::ThreadPool;
pub struct Driver<M=GenericMaterial> { pub struct Driver<M=GenericMaterial> {
pub state: SimState<M>, pub state: SimState<M>,
renderer: Arc<MultiRenderer>, renderer: Arc<MultiRenderer<SimState<M>>>,
// TODO: use Rayon's thread pool? // TODO: use Rayon's thread pool?
render_pool: ThreadPool, render_pool: ThreadPool,
render_channel: (SyncSender<()>, Receiver<()>), render_channel: (SyncSender<()>, Receiver<()>),
@@ -51,12 +51,6 @@ impl<M: Default> Driver<M> {
} }
} }
impl<M: Material + Clone + Send + Sync + 'static> Driver<M> {
pub fn dyn_state(&mut self) -> &mut dyn GenericSim {
&mut self.state
}
}
impl<M: Material> Driver<M> { impl<M: Material> Driver<M> {
pub fn add_stimulus<S: AbstractStimulus + 'static>(&mut self, s: S) { pub fn add_stimulus<S: AbstractStimulus + 'static>(&mut self, s: S) {
self.stimuli.push(Box::new(s)) self.stimuli.push(Box::new(s))
@@ -69,8 +63,30 @@ impl<M: Material> Driver<M> {
pub fn set_steps_per_frame(&mut self, steps_per_frame: u64) { pub fn set_steps_per_frame(&mut self, steps_per_frame: u64) {
self.steps_per_frame = steps_per_frame; self.steps_per_frame = steps_per_frame;
} }
}
fn add_renderer<R: Renderer + 'static>(&mut self, renderer: R, name: &str) { impl<M: Material + Clone> Driver<M> {
pub fn fill_region<R: 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<M: Material + Clone + Send + Sync + 'static> Driver<M> {
pub fn dyn_state(&mut self) -> &mut dyn GenericSim {
&mut self.state
}
fn add_renderer<R: Renderer<SimState<M>> + 'static>(&mut self, renderer: R, name: &str) {
info!("render to {}", name); info!("render to {}", name);
self.renderer.push(renderer); self.renderer.push(renderer);
} }
@@ -94,22 +110,6 @@ impl<M: Material> Driver<M> {
} }
} }
impl<M: Material + Clone> Driver<M> {
pub fn fill_region<R: 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<M: Material + Clone + Default + Send + Sync + 'static> Driver<M> { impl<M: Material + Clone + Default + Send + Sync + 'static> Driver<M> {
fn render(&mut self) { fn render(&mut self) {
let their_state = self.state.clone(); let their_state = self.state.clone();
@@ -182,25 +182,6 @@ impl<M: Material + Clone + Default + Send + Sync + 'static> Driver<M> {
} }
impl<M: Material + From<mat::Conductor>> Driver<M> { impl<M: Material + From<mat::Conductor>> Driver<M> {
// 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<C: Coord>(&mut self, thickness: C) { pub fn add_upml_boundary<C: Coord>(&mut self, thickness: C) {
// Based on explanation here (slide 63): https://empossible.net/wp-content/uploads/2020/01/Lecture-The-Perfectly-Matched-Layer.pdf // 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()); let thickness = thickness.to_index(self.state.feature_size());

View File

@@ -59,7 +59,7 @@ fn scale_vector(x: Vec2, typical_mag: Flt) -> Vec2 {
x.with_mag(new_mag) x.with_mag(new_mag)
} }
fn im_size(state: &dyn GenericSim, max_w: u32, max_h: u32) -> (u32, u32) { fn im_size<S: GenericSim>(state: &S, max_w: u32, max_h: u32) -> (u32, u32) {
let mut width = max_w; let mut width = max_w;
let mut height = width * state.height() / state.width(); let mut height = width * state.height() / state.width();
if height > max_h { if height > max_h {
@@ -70,21 +70,21 @@ fn im_size(state: &dyn GenericSim, max_w: u32, max_h: u32) -> (u32, u32) {
(width, height) (width, height)
} }
struct RenderSteps<'a> { struct RenderSteps<'a, S> {
im: RgbImage, im: RgbImage,
sim: &'a dyn GenericSim, sim: &'a S,
meas: &'a [Box<dyn AbstractMeasurement>], meas: &'a [Box<dyn AbstractMeasurement>],
/// Simulation z coordinate to sample /// Simulation z coordinate to sample
z: u32, z: u32,
} }
impl<'a> RenderSteps<'a> { impl<'a, S: GenericSim> RenderSteps<'a, S> {
/// Render using default configuration constants /// Render using default configuration constants
fn render(state: &'a dyn GenericSim, measurements: &'a [Box<dyn AbstractMeasurement>], z: u32) -> RgbImage { fn render(state: &'a S, measurements: &'a [Box<dyn AbstractMeasurement>], z: u32) -> RgbImage {
Self::render_configured(state, measurements, z, (640, 480)) Self::render_configured(state, measurements, z, (640, 480))
} }
/// Render, controlling things like the size. /// Render, controlling things like the size.
fn render_configured(state: &'a dyn GenericSim, measurements: &'a [Box<dyn AbstractMeasurement>], z: u32, max_size: (u32, u32)) -> RgbImage { fn render_configured(state: &'a S, measurements: &'a [Box<dyn AbstractMeasurement>], z: u32, max_size: (u32, u32)) -> RgbImage {
let (width, height) = im_size(state, max_size.0, max_size.1); let (width, height) = im_size(state, max_size.0, max_size.1);
trace!("rendering at {}x{} with z={}", width, height, z); trace!("rendering at {}x{} with z={}", width, height, z);
let mut me = Self::new(state, measurements, 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.render_measurements();
me.im me.im
} }
fn new(sim: &'a dyn GenericSim, meas: &'a [Box<dyn AbstractMeasurement>], width: u32, height: u32, z: u32) -> Self { fn new(sim: &'a S, meas: &'a [Box<dyn AbstractMeasurement>], width: u32, height: u32, z: u32) -> Self {
RenderSteps { RenderSteps {
im: RgbImage::new(width, height), im: RgbImage::new(width, height),
sim, sim,
@@ -259,20 +259,30 @@ impl ImageRenderExt for RgbImage {
} }
} }
pub trait Renderer: Send + Sync { pub trait Renderer<S>: Send + Sync {
fn render_z_slice(&self, state: &dyn GenericSim, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) { fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]);
self.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements); // {
} // self.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements);
fn render(&self, state: &dyn GenericSim, measurements: &[Box<dyn AbstractMeasurement>]) { // }
self.render_z_slice(state, state.depth() / 2, measurements); fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>]);
}
/// Not intended to be called directly by users; implement this if you want the image to be /// 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. /// 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<dyn AbstractMeasurement>]) { fn render_with_image(&self, state: &S, _im: &RgbImage, measurements: &[Box<dyn AbstractMeasurement>]) {
self.render(state, measurements); self.render(state, measurements);
} }
} }
fn default_render_z_slice<S: GenericSim, R: Renderer<S>>(
me: &R, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]
) {
me.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements);
}
fn default_render<S: GenericSim, R: Renderer<S>>(
me: &R, state: &S, measurements: &[Box<dyn AbstractMeasurement>]
) {
me.render_z_slice(state, state.depth() / 2, measurements);
}
// pub struct NumericTermRenderer; // pub struct NumericTermRenderer;
// //
// impl Renderer for NumericTermRenderer { // impl Renderer for NumericTermRenderer {
@@ -296,8 +306,11 @@ pub trait Renderer: Send + Sync {
#[derive(Default)] #[derive(Default)]
pub struct ColorTermRenderer; pub struct ColorTermRenderer;
impl Renderer for ColorTermRenderer { impl<S: GenericSim> Renderer<S> for ColorTermRenderer {
fn render_z_slice(&self, state: &dyn GenericSim, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) { fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render(self, state, measurements)
}
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) {
let (max_w, mut max_h) = crossterm::terminal::size().unwrap(); let (max_w, mut max_h) = crossterm::terminal::size().unwrap();
max_h = max_h.saturating_sub(1 + measurements.len() as u16); max_h = max_h.saturating_sub(1 + measurements.len() as u16);
let im = RenderSteps::render_configured(state, &[], z, (max_w as _, max_h as _)); let im = RenderSteps::render_configured(state, &[], z, (max_w as _, max_h as _));
@@ -338,7 +351,7 @@ pub struct Y4MRenderer {
} }
impl Y4MRenderer { impl Y4MRenderer {
pub fn new<S: Into<PathBuf>>(output: S) -> Self { pub fn new<P: Into<PathBuf>>(output: P) -> Self {
Self { Self {
out_path: output.into(), out_path: output.into(),
encoder: Mutex::new(None), encoder: Mutex::new(None),
@@ -346,8 +359,14 @@ impl Y4MRenderer {
} }
} }
impl Renderer for Y4MRenderer { impl<S: GenericSim> Renderer<S> for Y4MRenderer {
fn render_with_image(&self, _state: &dyn GenericSim, im: &RgbImage, _meas: &[Box<dyn AbstractMeasurement>]) { fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render_z_slice(self, state, z, measurements)
}
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render(self, state, measurements)
}
fn render_with_image(&self, _state: &S, im: &RgbImage, _meas: &[Box<dyn AbstractMeasurement>]) {
{ {
let mut enc = self.encoder.lock().unwrap(); let mut enc = self.encoder.lock().unwrap();
if enc.is_none() { if enc.is_none() {
@@ -413,8 +432,11 @@ impl PlotlyRenderer {
} }
} }
impl Renderer for PlotlyRenderer { impl<S: GenericSim> Renderer<S> for PlotlyRenderer {
fn render(&self, state: &dyn GenericSim, _meas: &[Box<dyn AbstractMeasurement>]) { fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render_z_slice(self, state, z, measurements)
}
fn render(&self, state: &S, _meas: &[Box<dyn AbstractMeasurement>]) {
use plotly::{ImageFormat, Plot, Rgba}; use plotly::{ImageFormat, Plot, Rgba};
// use plotly::common::Marker; // use plotly::common::Marker;
use plotly::layout::{AspectMode, Axis, Layout, LayoutScene}; 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 { // if x%5 == 0 || y%5 == 0 || z%5 == 0 {
// continue; // 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); xv.push(x);
yv.push(y); yv.push(y);
zv.push(z); zv.push(z);
@@ -480,32 +502,42 @@ impl Renderer for PlotlyRenderer {
} }
} }
#[derive(Default)] pub struct MultiRenderer<S> {
pub struct MultiRenderer { renderers: RwLock<Vec<Box<dyn Renderer<S>>>>,
renderers: RwLock<Vec<Box<dyn Renderer>>>,
} }
impl MultiRenderer { impl<S> Default for MultiRenderer<S> {
fn default() -> Self {
Self {
renderers: RwLock::new(Vec::new())
}
}
}
impl<S> MultiRenderer<S> {
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
pub fn push<R: Renderer + 'static>(&self, r: R) { pub fn push<R: Renderer<S> + 'static>(&self, r: R) {
self.renderers.write().unwrap().push(Box::new(r)); self.renderers.write().unwrap().push(Box::new(r));
} }
pub fn with<R: Renderer + 'static>(self, r: R) -> Self { pub fn with<R: Renderer<S> + 'static>(self, r: R) -> Self {
self.push(r); self.push(r);
self self
} }
} }
impl Renderer for MultiRenderer { impl<S: GenericSim> Renderer<S> for MultiRenderer<S> {
fn render(&self, state: &dyn GenericSim, measurements: &[Box<dyn AbstractMeasurement>]) { fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render_z_slice(self, state, z, measurements)
}
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>]) {
if self.renderers.read().unwrap().len() != 0 { if self.renderers.read().unwrap().len() != 0 {
self.render_with_image(state, &RenderSteps::render(state, measurements, state.depth() / 2), measurements); 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<dyn AbstractMeasurement>]) { fn render_with_image(&self, state: &S, im: &RgbImage, measurements: &[Box<dyn AbstractMeasurement>]) {
for r in &*self.renderers.read().unwrap() { for r in &*self.renderers.read().unwrap() {
r.render_with_image(state, im, measurements); r.render_with_image(state, im, measurements);
} }
@@ -530,8 +562,11 @@ impl SerializerRenderer {
} }
} }
impl Renderer for SerializerRenderer { impl<S: GenericSim> Renderer<S> for SerializerRenderer {
fn render(&self, state: &dyn GenericSim, measurements: &[Box<dyn AbstractMeasurement>]) { fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render_z_slice(self, state, z, measurements)
}
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>]) {
let snap = state.to_static(); let snap = state.to_static();
let frame = SerializedFrame { let frame = SerializedFrame {
state: snap, state: snap,