AKA I made these changes a month ago but forgot to commit them and don't
really want to figure out what they did.
This commit is contained in:
2020-11-27 21:22:42 -08:00
parent 1a9093315a
commit 9d15e126a7
6 changed files with 228 additions and 86 deletions

View File

@@ -9,6 +9,7 @@ edition = "2018"
[dependencies] [dependencies]
ansi_term = "0.12" ansi_term = "0.12"
decorum = "0.3" decorum = "0.3"
dyn-clone = "1.0"
enum_dispatch = "0.3" enum_dispatch = "0.3"
env_logger = "0.7" env_logger = "0.7"
font8x8 = "0.2" font8x8 = "0.2"
@@ -18,7 +19,8 @@ lazy_static = "1.4"
log = "0.4" log = "0.4"
ndarray = { version = "0.13", features = ["rayon"] } ndarray = { version = "0.13", features = ["rayon"] }
piecewise-linear = "0.1" piecewise-linear = "0.1"
plotly = { version = "0.6", features = ["kaleido", "plotly_ndarray"] } plotly = { version = "0.6", features = ["kaleido", "plotly_ndarray"], path = "../plotly/plotly" }
threadpool = "1.8"
y4m = "0.7" y4m = "0.7"
[dev-dependencies] [dev-dependencies]

View File

@@ -31,23 +31,31 @@ fn main() {
let depth_px = from_m(depth); let depth_px = from_m(depth);
let size_px = Index((width_px, width_px, depth_px).into()); let size_px = Index((width_px, width_px, depth_px).into());
let mut driver = Driver::new(size_px, feat_size); let mut driver = Driver::new(size_px, feat_size);
// driver.set_steps_per_frame(8); //driver.set_steps_per_frame(8);
//driver.set_steps_per_frame(20);
//driver.set_steps_per_frame(40); //driver.set_steps_per_frame(40);
//driver.set_steps_per_frame(80); //driver.set_steps_per_frame(80);
driver.set_steps_per_frame(120); //driver.set_steps_per_frame(120);
driver.set_steps_per_frame(160);
//driver.set_steps_per_frame(200); //driver.set_steps_per_frame(200);
// driver.add_y4m_renderer(&*format!("toroid25d.5-flt{}-{}-feat{}um-{:.1e}A-{:.1e}s--radii{}um-{}um-{}um-{}um.y4m", let base = "toroid25d-7";
// std::mem::size_of::<Flt>() * 8, let _ = std::fs::create_dir(base);
// *size_px, let prefix = format!("{}/{}-flt{}-{}-feat{}um-{}mA-{}ps--radii{}um-{}um-{}um-{}um",
// m_to_um(feat_size), base,
// peak_current, base,
// current_duration, std::mem::size_of::<Flt>() * 8,
// m_to_um(conductor_outer_rad), *size_px,
// m_to_um(ferro_inner_rad), m_to_um(feat_size),
// m_to_um(ferro_outer_rad), (peak_current * 1e3) as i64,
// m_to_um(ferro_depth), (current_duration * 1e12) as i64,
// )); m_to_um(conductor_outer_rad),
driver.add_plotly_renderer(); m_to_um(ferro_inner_rad),
m_to_um(ferro_outer_rad),
m_to_um(ferro_depth),
);
let _ = std::fs::create_dir(&prefix);
driver.add_y4m_renderer(&*format!("{}.y4m", prefix));
driver.add_plotly_renderer(&*format!("{}/frame-", prefix));
let conductor_region = CylinderZ::new( let conductor_region = CylinderZ::new(
Vec2::new(half_width, half_width), Vec2::new(half_width, half_width),
conductor_outer_rad); conductor_outer_rad);

View File

@@ -8,32 +8,43 @@ use crate::stim::AbstractStimulus;
use log::{info, debug, trace}; use log::{info, debug, trace};
use std::path::PathBuf; use std::path::PathBuf;
use std::time::{Duration, SystemTime}; use std::sync::{Arc, Mutex};
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
use std::time::{Duration, Instant};
use threadpool::ThreadPool;
pub struct Driver { pub struct Driver {
pub state: SimState, pub state: SimState,
renderer: MultiRenderer, renderer: Arc<MultiRenderer>,
render_pool: ThreadPool,
render_channel: (SyncSender<()>, Receiver<()>),
steps_per_frame: u64, steps_per_frame: u64,
time_spent_stepping: Duration, time_spent_stepping: Duration,
time_spent_on_stimuli: Duration, time_spent_on_stimuli: Duration,
time_spent_rendering: Duration, time_spent_blocked_on_render: Duration,
time_spent_rendering: Arc<Mutex<Duration>>,
measurements: Vec<Box<dyn AbstractMeasurement>>, measurements: Vec<Box<dyn AbstractMeasurement>>,
stimuli: Vec<Box<dyn AbstractStimulus>>, stimuli: Vec<Box<dyn AbstractStimulus>>,
start_time: SystemTime, start_time: Instant,
last_diag_time: Instant,
} }
impl Driver { impl Driver {
pub fn new<C: Coord>(size: C, feature_size: Flt) -> Self { pub fn new<C: Coord>(size: C, feature_size: Flt) -> Self {
Driver { Driver {
state: SimState::new(size.to_index(feature_size), feature_size), state: SimState::new(size.to_index(feature_size), feature_size),
renderer: Default::default(), renderer: Arc::new(MultiRenderer::new()),
render_pool: ThreadPool::new(3),
render_channel: sync_channel(0),
steps_per_frame: 1, steps_per_frame: 1,
time_spent_stepping: Default::default(), time_spent_stepping: Default::default(),
time_spent_on_stimuli: Default::default(), time_spent_on_stimuli: Default::default(),
time_spent_blocked_on_render: Default::default(),
time_spent_rendering: Default::default(), time_spent_rendering: Default::default(),
measurements: vec![Box::new(meas::Time), Box::new(meas::Meta), Box::new(meas::Energy)], measurements: vec![Box::new(meas::Time), Box::new(meas::Meta), Box::new(meas::Energy)],
stimuli: vec![], stimuli: vec![],
start_time: SystemTime::now(), start_time: Instant::now(),
last_diag_time: Instant::now(),
} }
} }
@@ -64,8 +75,8 @@ impl Driver {
self.add_renderer(render::Y4MRenderer::new(output), &*name); self.add_renderer(render::Y4MRenderer::new(output), &*name);
} }
pub fn add_plotly_renderer(&mut self) { pub fn add_plotly_renderer(&mut self, out_base: &str) {
self.add_renderer(render::PlotlyRenderer, "plotly"); self.add_renderer(render::PlotlyRenderer::new(out_base), out_base);
} }
pub fn add_term_renderer(&mut self) { pub fn add_term_renderer(&mut self) {
@@ -150,44 +161,62 @@ impl Driver {
} }
} }
fn render(&mut self) {
let their_state = self.state.clone();
let their_measurements = self.measurements.clone();
let renderer = self.renderer.clone();
let time_spent_rendering = self.time_spent_rendering.clone();
let sender = self.render_channel.0.clone();
self.render_pool.execute(move || {
sender.send(()).unwrap();
trace!("render begin");
let start_time = Instant::now();
renderer.render(&their_state, &*their_measurements);
*time_spent_rendering.lock().unwrap() += start_time.elapsed();
trace!("render end");
});
let block_start = Instant::now();
self.render_channel.1.recv().unwrap();
self.time_spent_blocked_on_render += block_start.elapsed();
}
pub fn step(&mut self) { pub fn step(&mut self) {
if self.state.step_no() % self.steps_per_frame == 0 { if self.state.step_no() % self.steps_per_frame == 0 {
trace!("render begin"); self.render();
let start_time = SystemTime::now();
self.renderer.render(&self.state, &*self.measurements);
self.time_spent_rendering += start_time.elapsed().unwrap();
trace!("render end");
} }
{ {
trace!("stimuli begin"); trace!("stimuli begin");
let start_time = SystemTime::now(); let start_time = Instant::now();
for stim in &mut *self.stimuli { for stim in &mut *self.stimuli {
stim.apply(&mut self.state); stim.apply(&mut self.state);
} }
self.time_spent_on_stimuli += start_time.elapsed().unwrap(); self.time_spent_on_stimuli += start_time.elapsed();
} }
trace!("step begin"); trace!("step begin");
let start_time = SystemTime::now(); let start_time = Instant::now();
self.state.step(); self.state.step();
self.time_spent_stepping += start_time.elapsed().unwrap(); self.time_spent_stepping += start_time.elapsed();
trace!("step end"); trace!("step end");
let step = self.state.step_no(); if self.last_diag_time.elapsed().as_secs_f64() >= 5.0 {
if step % (10*self.steps_per_frame) == 0 { self.last_diag_time = Instant::now();
let step = self.state.step_no();
let step_time = self.time_spent_stepping.as_secs_f64(); let step_time = self.time_spent_stepping.as_secs_f64();
let stim_time = self.time_spent_on_stimuli.as_secs_f64(); let stim_time = self.time_spent_on_stimuli.as_secs_f64();
let render_time = self.time_spent_rendering.as_secs_f64(); let render_time = self.time_spent_rendering.lock().unwrap().as_secs_f64();
let overall_time = self.start_time.elapsed().unwrap().as_secs_f64(); let block_time = self.time_spent_blocked_on_render.as_secs_f64();
let overall_time = self.start_time.elapsed().as_secs_f64();
let fps = (self.state.step_no() as f64) / overall_time; let fps = (self.state.step_no() as f64) / overall_time;
let sim_time = self.state.time() as f64; let sim_time = self.state.time() as f64;
info!( info!(
"t={:.2e} frame {:06} fps: {:6.2} (sim: {:.1}s, stim: {:.1}s, render: {:.1}s, other: {:.1}s)", "t={:.2e} frame {:06} fps: {:6.2} (sim: {:.1}s, stim: {:.1}s, render: {:.1}s, blocked: {:.1}s, other: {:.1}s)",
sim_time, sim_time,
step, step,
fps, fps,
step_time, step_time,
stim_time, stim_time,
render_time, render_time,
block_time,
overall_time - step_time - stim_time - render_time overall_time - step_time - stim_time - render_time
); );
} }

View File

@@ -2,13 +2,16 @@ use crate::flt::Flt;
use crate::geom::{Meters, Region}; use crate::geom::{Meters, Region};
use crate::mat::Material as _; use crate::mat::Material as _;
use crate::sim::{Cell, GenericSim}; use crate::sim::{Cell, GenericSim};
use dyn_clone::{self, DynClone};
use std::fmt::Display; use std::fmt::Display;
use std::iter::Sum; use std::iter::Sum;
pub trait AbstractMeasurement { pub trait AbstractMeasurement: Send + DynClone {
fn eval(&self, state: &dyn GenericSim) -> String; fn eval(&self, state: &dyn GenericSim) -> String;
} }
dyn_clone::clone_trait_object!(AbstractMeasurement);
#[derive(Clone)]
pub struct Time; pub struct Time;
impl AbstractMeasurement for Time { impl AbstractMeasurement for Time {
@@ -17,6 +20,7 @@ impl AbstractMeasurement for Time {
} }
} }
#[derive(Clone)]
pub struct Meta; pub struct Meta;
impl AbstractMeasurement for Meta { impl AbstractMeasurement for Meta {
@@ -25,6 +29,7 @@ impl AbstractMeasurement for Meta {
} }
} }
#[derive(Clone)]
pub struct Label(pub String); pub struct Label(pub String);
impl Label { impl Label {
@@ -50,9 +55,10 @@ fn sum_over_region<T: Default + Sum<T>, R: Region, F: Fn(Meters, &Cell) -> T>(st
}) })
} }
#[derive(Clone)]
pub struct Current<R>(pub R); pub struct Current<R>(pub R);
impl<R: Region + Display + Sync> AbstractMeasurement for Current<R> { impl<R: Region + Clone + Display + Send + Sync> AbstractMeasurement for Current<R> {
fn eval(&self, state: &dyn GenericSim) -> String { fn eval(&self, state: &dyn GenericSim) -> String {
let current = sum_over_region(state, &self.0, |coord, _cell| state.current(coord)); let current = sum_over_region(state, &self.0, |coord, _cell| state.current(coord));
format!("I({}): ({:.2e}, {:.2e}, {:.2e})", self.0, current.x(), current.y(), current.z()) format!("I({}): ({:.2e}, {:.2e}, {:.2e})", self.0, current.x(), current.y(), current.z())
@@ -65,6 +71,7 @@ fn loc(v: Meters) -> String {
} }
/// M /// M
#[derive(Clone)]
pub struct Magnetization(pub Meters); pub struct Magnetization(pub Meters);
impl AbstractMeasurement for Magnetization { impl AbstractMeasurement for Magnetization {
@@ -75,6 +82,7 @@ impl AbstractMeasurement for Magnetization {
} }
/// B /// B
#[derive(Clone)]
pub struct MagneticFlux(pub Meters); pub struct MagneticFlux(pub Meters);
impl AbstractMeasurement for MagneticFlux { impl AbstractMeasurement for MagneticFlux {
@@ -85,6 +93,7 @@ impl AbstractMeasurement for MagneticFlux {
} }
/// H /// H
#[derive(Clone)]
pub struct MagneticStrength(pub Meters); pub struct MagneticStrength(pub Meters);
impl AbstractMeasurement for MagneticStrength { impl AbstractMeasurement for MagneticStrength {
@@ -94,6 +103,7 @@ impl AbstractMeasurement for MagneticStrength {
} }
} }
#[derive(Clone)]
pub struct ElectricField(pub Meters); pub struct ElectricField(pub Meters);
impl AbstractMeasurement for ElectricField { impl AbstractMeasurement for ElectricField {
@@ -103,6 +113,7 @@ impl AbstractMeasurement for ElectricField {
} }
} }
#[derive(Clone)]
pub struct Energy; pub struct Energy;
impl AbstractMeasurement for Energy { impl AbstractMeasurement for Energy {

View File

@@ -6,12 +6,12 @@ use crate::sim::{Cell, GenericSim};
use crate::meas::AbstractMeasurement; use crate::meas::AbstractMeasurement;
use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _}; use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _};
use log::{trace, info}; use log::{trace, info};
use plotly::{Plot, ImageFormat}; use plotly;
use plotly::heat_map::HeatMap;
use image::{RgbImage, Rgb}; use image::{RgbImage, Rgb};
use imageproc::{pixelops, drawing}; use imageproc::{pixelops, drawing};
use std::fs::File; use std::fs::File;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Mutex, RwLock};
use y4m; use y4m;
/// Accept a value from (-\inf, \inf) and return a value in (-1, 1). /// Accept a value from (-\inf, \inf) and return a value in (-1, 1).
@@ -54,6 +54,17 @@ 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) {
let mut width = max_w;
let mut height = width * state.height() / state.width();
if height > max_h {
let stretch = max_h as f32 / height as f32;
width = (width as f32 * stretch) as _;
height = max_h;
}
(width, height)
}
struct RenderSteps<'a> { struct RenderSteps<'a> {
im: RgbImage, im: RgbImage,
sim: &'a dyn GenericSim, sim: &'a dyn GenericSim,
@@ -64,14 +75,7 @@ struct RenderSteps<'a> {
impl<'a> RenderSteps<'a> { impl<'a> RenderSteps<'a> {
fn render(state: &'a dyn GenericSim, measurements: &'a [Box<dyn AbstractMeasurement>], z: u32) -> RgbImage { fn render(state: &'a dyn GenericSim, measurements: &'a [Box<dyn AbstractMeasurement>], z: u32) -> RgbImage {
let mut width = 640; let (width, height) = im_size(state, 640, 480);
let max_height = 480;
let mut height = width * state.height() / state.width();
if height > max_height {
let stretch = max_height as f32 / height as f32;
width = (width as f32 * stretch) as _;
height = max_height;
}
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);
me.render_scalar_field(10.0, false, 2, |cell| { me.render_scalar_field(10.0, false, 2, |cell| {
@@ -230,11 +234,13 @@ impl ImageRenderExt for RgbImage {
} }
} }
pub trait Renderer { pub trait Renderer: Send + Sync {
fn render(&mut self, state: &dyn GenericSim, measurements: &[Box<dyn AbstractMeasurement>]) { fn render(&self, state: &dyn GenericSim, measurements: &[Box<dyn AbstractMeasurement>]) {
self.render_with_image(state, &RenderSteps::render(state, measurements, state.depth() / 2)); self.render_with_image(state, &RenderSteps::render(state, measurements, state.depth() / 2));
} }
fn render_with_image(&mut self, state: &dyn GenericSim, _im: &RgbImage) { /// 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) {
self.render(state, &[]); self.render(state, &[]);
} }
} }
@@ -262,7 +268,7 @@ pub trait Renderer {
pub struct ColorTermRenderer; pub struct ColorTermRenderer;
impl Renderer for ColorTermRenderer { impl Renderer for ColorTermRenderer {
fn render_with_image(&mut self, _state: &dyn GenericSim, im: &RgbImage) { fn render_with_image(&self, _state: &dyn GenericSim, im: &RgbImage) {
let square = ""; let square = "";
let buf: String = im let buf: String = im
.enumerate_rows() .enumerate_rows()
@@ -279,27 +285,30 @@ impl Renderer for ColorTermRenderer {
pub struct Y4MRenderer { pub struct Y4MRenderer {
out_path: PathBuf, out_path: PathBuf,
encoder: Option<y4m::Encoder<File>>, encoder: Mutex<Option<y4m::Encoder<File>>>,
} }
impl Y4MRenderer { impl Y4MRenderer {
pub fn new<S: Into<PathBuf>>(output: S) -> Self { pub fn new<S: Into<PathBuf>>(output: S) -> Self {
Self { Self {
out_path: output.into(), out_path: output.into(),
encoder: None, encoder: Mutex::new(None),
} }
} }
} }
impl Renderer for Y4MRenderer { impl Renderer for Y4MRenderer {
fn render_with_image(&mut self, _state: &dyn GenericSim, im: &RgbImage) { fn render_with_image(&self, _state: &dyn GenericSim, im: &RgbImage) {
if self.encoder.is_none() { {
let writer = File::create(&self.out_path).unwrap(); let mut enc = self.encoder.lock().unwrap();
self.encoder = Some(y4m::encode(im.width() as usize, im.height() as usize, y4m::Ratio::new(30, 1)) if enc.is_none() {
.with_colorspace(y4m::Colorspace::C444) let writer = File::create(&self.out_path).unwrap();
.write_header(writer) *enc = Some(y4m::encode(im.width() as usize, im.height() as usize, y4m::Ratio::new(30, 1))
.unwrap() .with_colorspace(y4m::Colorspace::C444)
); .write_header(writer)
.unwrap()
);
}
} }
let mut pix_y = Vec::new(); let mut pix_y = Vec::new();
@@ -318,7 +327,8 @@ impl Renderer for Y4MRenderer {
} }
let frame = y4m::Frame::new([&*pix_y, &*pix_u, &*pix_v], None); let frame = y4m::Frame::new([&*pix_y, &*pix_u, &*pix_v], None);
let enc = self.encoder.as_mut().unwrap(); let mut lock = self.encoder.lock().unwrap();
let enc = lock.as_mut().unwrap();
trace!("write_frame begin"); trace!("write_frame begin");
let ret = enc.write_frame(&frame).unwrap(); let ret = enc.write_frame(&frame).unwrap();
trace!("write_frame end"); trace!("write_frame end");
@@ -326,44 +336,112 @@ impl Renderer for Y4MRenderer {
} }
} }
pub struct PlotlyRenderer; pub struct PlotlyRenderer {
out_base: String,
}
fn add_scatter(plot: &mut plotly::Plot, xv: &mut Vec<u32>, yv: &mut Vec<u32>, zv: &mut Vec<u32>, colors: &mut Vec<plotly::Rgba>) {
let xv = std::mem::replace(xv, Vec::new());
let yv = std::mem::replace(yv, Vec::new());
let zv = std::mem::replace(zv, Vec::new());
let colors = std::mem::replace(colors, Vec::new());
let scatter = plotly::Scatter::new3(xv, yv, zv)
.mode(plotly::common::Mode::Markers)
.marker(plotly::common::Marker::new()
.opacity(0.01)
//.size_array(sizes)
//.opacity_array(opacities)
.color_array(colors)
);
plot.add_trace(scatter);
}
impl PlotlyRenderer {
pub fn new(out_base: &str) -> Self {
Self {
out_base: out_base.into(),
}
}
}
impl Renderer for PlotlyRenderer { impl Renderer for PlotlyRenderer {
fn render(&mut self, state: &dyn GenericSim, measurements: &[Box<dyn AbstractMeasurement>]) { fn render(&self, state: &dyn GenericSim, measurements: &[Box<dyn AbstractMeasurement>]) {
use plotly::{ImageFormat, Plot, Rgba, Scatter};
use plotly::common::Marker;
use plotly::layout::{AspectMode, Axis, Layout, LayoutScene};
let mut plot = Plot::new(); let mut plot = Plot::new();
let scene = LayoutScene::new()
.x_axis(Axis::new().range(vec![0, state.width() as i32]))
.y_axis(Axis::new().range(vec![0, state.height() as i32]))
.z_axis(Axis::new().range(vec![0, state.depth() as i32]))
.aspect_mode(AspectMode::Cube);
let layout = Layout::new()
.scene(scene);
plot.set_layout(layout);
let mut xv = Vec::new(); let mut xv = Vec::new();
let mut yv = Vec::new(); let mut yv = Vec::new();
let mut zv = Vec::new(); let mut zv = Vec::new();
// let mut opacities = Vec::new();
let mut colors = Vec::new();
for z in 0..state.depth() { for z in 0..state.depth() {
if xv.len() >= 120000 {
add_scatter(&mut plot, &mut xv, &mut yv, &mut zv, &mut colors);
}
for y in 0..state.height() { for y in 0..state.height() {
for x in 0..state.width() { for x in 0..state.width() {
// if x%5 == 0 || y%5 == 0 || z%5 == 0 {
// continue;
// }
let cell = state.get(Index(Vec3u::new(x, y, z))); let cell = state.get(Index(Vec3u::new(x, y, z)));
if cell.e().mag() > 10.0 { xv.push(x);
xv.push(x); yv.push(y);
yv.push(y); zv.push(z);
zv.push(z); // opacities.push((cell.e().mag() * 0.1).min(1.0) as f64)
} let mat = cell.mat().conductivity().mag() + if cell.mat().is_vacuum() {
0.0
} else {
5.0
};
//let g = scale_unsigned_to_u8(mat, 10.0);
//let r = scale_unsigned_to_u8(cell.mat().m().mag(), 100.0);
//let b = scale_unsigned_to_u8(cell.e().mag(), 1e2);
let r = scale_unsigned_to_u8(cell.mat().m().mag(), 100.0);
let g = scale_unsigned_to_u8(cell.e().mag(), 1e2);
let b = scale_unsigned_to_u8(mat, 10.0);
let alpha = 1.0;
colors.push(Rgba::new(r, g, b, alpha));
} }
} }
// let scatter = plotly::Scatter::new3(xv, yv, zv)
// .mode(plotly::common::Mode::Markers)
// .marker(plotly::common::Marker::new()
// //.opacity(0.2)
// //.size_array(sizes)
// //.opacity_array(opacities)
// .color_array(colors)
// );
// plot.add_trace(scatter);
} }
let heat_map = HeatMap::new(xv, yv, zv); add_scatter(&mut plot, &mut xv, &mut yv, &mut zv, &mut colors);
plot.add_trace(heat_map);
let name = format!("frame{}", state.step_no()); let name = format!("{}{}", self.out_base, state.step_no());
plot.save(&*name, ImageFormat::PNG, state.width() as _, state.height() as _, 1.0); let (im_w, im_h) = im_size(state, 2048, 2048);
plot.save(&*name, ImageFormat::PNG, im_w as _, im_h as _, 1.0);
} }
} }
#[derive(Default)] #[derive(Default)]
pub struct MultiRenderer { pub struct MultiRenderer {
renderers: Vec<Box<dyn Renderer>>, renderers: RwLock<Vec<Box<dyn Renderer>>>,
} }
impl MultiRenderer { impl MultiRenderer {
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
pub fn push<R: Renderer + 'static>(&mut self, r: R) { pub fn push<R: Renderer + 'static>(&self, r: R) {
self.renderers.push(Box::new(r)); self.renderers.write().unwrap().push(Box::new(r));
} }
pub fn with<R: Renderer + 'static>(mut self, r: R) -> Self { pub fn with<R: Renderer + 'static>(mut self, r: R) -> Self {
self.push(r); self.push(r);
@@ -372,14 +450,14 @@ impl MultiRenderer {
} }
impl Renderer for MultiRenderer { impl Renderer for MultiRenderer {
fn render(&mut self, state: &dyn GenericSim, measurements: &[Box<dyn AbstractMeasurement>]) { fn render(&self, state: &dyn GenericSim, measurements: &[Box<dyn AbstractMeasurement>]) {
if self.renderers.len() != 0 { if self.renderers.read().unwrap().len() != 0 {
self.render_with_image(state, &RenderSteps::render(state, measurements, state.depth() / 2)); self.render_with_image(state, &RenderSteps::render(state, measurements, state.depth() / 2));
} }
} }
fn render_with_image(&mut self, state: &dyn GenericSim, im: &RgbImage) { fn render_with_image(&self, state: &dyn GenericSim, im: &RgbImage) {
for r in &mut self.renderers { for r in &*self.renderers.read().unwrap() {
r.render_with_image(state, im); r.render_with_image(state, im);
} }
} }

View File

@@ -1,6 +1,7 @@
use crate::{flt::{Flt, Real}, consts}; use crate::{flt::{Flt, Real}, consts};
use crate::geom::{Coord, Index, Meters, Vec3, Vec3u}; use crate::geom::{Coord, Index, Meters, Vec3, Vec3u};
use crate::mat::{self, GenericMaterial, Material}; use crate::mat::{self, GenericMaterial, Material};
use dyn_clone::{self, DynClone};
use log::trace; use log::trace;
use ndarray::{Array3, Zip}; use ndarray::{Array3, Zip};
@@ -8,7 +9,7 @@ use ndarray::parallel::prelude::*;
use std::convert::From; use std::convert::From;
use std::iter::Sum; use std::iter::Sum;
pub trait GenericSim { pub trait GenericSim: Send + Sync + DynClone {
fn sample(&self, pos: Meters) -> Cell<mat::Static>; fn sample(&self, pos: Meters) -> Cell<mat::Static>;
fn impulse_e_meters(&mut self, pos: Meters, amount: Vec3); fn impulse_e_meters(&mut self, pos: Meters, amount: Vec3);
fn impulse_h_meters(&mut self, pos: Meters, amount: Vec3); fn impulse_h_meters(&mut self, pos: Meters, amount: Vec3);
@@ -29,7 +30,20 @@ pub trait GenericSim {
fn time(&self) -> Flt { fn time(&self) -> Flt {
self.timestep() * self.step_no() as Flt self.timestep() * self.step_no() as Flt
} }
/// Take a "snapshot" of the simulation, dropping all material-specific information.
fn to_static(&self) -> SimState<mat::Static> {
let mut state = SimState::new(self.size(), self.feature_size());
Zip::from(ndarray::indices_of(&state.cells)).par_apply_assign_into(
&mut state.cells,
|(z, y, x)| {
let idx = Index((x as u32, y as u32, z as u32).into());
self.sample(idx.to_meters(self.feature_size()))
});
state
}
} }
dyn_clone::clone_trait_object!(GenericSim);
impl<'a> dyn GenericSim + 'a { impl<'a> dyn GenericSim + 'a {
pub fn get<C: Coord>(&self, at: C) -> Cell<mat::Static> { pub fn get<C: Coord>(&self, at: C) -> Cell<mat::Static> {
@@ -82,7 +96,7 @@ impl<'a> dyn GenericSim + 'a {
} }
} }
#[derive(Default)] #[derive(Default, Clone)]
pub struct SimState<M=GenericMaterial> { pub struct SimState<M=GenericMaterial> {
cells: Array3<Cell<M>>, cells: Array3<Cell<M>>,
scratch: Array3<Cell<M>>, scratch: Array3<Cell<M>>,
@@ -101,7 +115,7 @@ impl<M: Material + Default> SimState<M> {
} }
} }
impl<M: Material + Clone + Default + Send + Sync> SimState<M> { impl<M: Material + Clone + Default + Send + Sync + 'static> SimState<M> {
pub fn step(&mut self) { pub fn step(&mut self) {
use consts::real::*; use consts::real::*;
let time_step = Real::from_inner(self.timestep()); let time_step = Real::from_inner(self.timestep());
@@ -134,7 +148,7 @@ impl<M: Material + Clone + Default + Send + Sync> SimState<M> {
} }
} }
impl<M: Material> GenericSim for SimState<M> { impl<M: Material + Clone + Send + Sync + 'static> GenericSim for SimState<M> {
fn sample(&self, pos: Meters) -> Cell<mat::Static> { fn sample(&self, pos: Meters) -> Cell<mat::Static> {
// TODO: smarter sampling than nearest neighbor? // TODO: smarter sampling than nearest neighbor?
let pos_sim = pos.to_index(self.feature_size()); let pos_sim = pos.to_index(self.feature_size());