2020-10-10 04:51:34 +00:00
|
|
|
use crate::geom::{Index, Meters, Vec2, Vec3, Vec3u};
|
2021-06-07 22:20:02 +00:00
|
|
|
use crate::real::ToFloat as _;
|
2021-06-07 22:26:26 +00:00
|
|
|
use crate::sim::{GenericSim, Sample, StaticSim};
|
2020-09-08 03:14:41 +00:00
|
|
|
use crate::meas::AbstractMeasurement;
|
2020-11-29 20:27:52 +00:00
|
|
|
use crossterm::{cursor, QueueableCommand as _};
|
|
|
|
use crossterm::style::{style, Color, PrintStyledContent};
|
2020-09-14 05:38:00 +00:00
|
|
|
use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _};
|
2020-11-29 20:27:52 +00:00
|
|
|
use log::trace;
|
2020-12-15 22:19:29 +00:00
|
|
|
use num::integer::Integer;
|
2020-11-28 05:22:42 +00:00
|
|
|
use plotly;
|
2020-09-04 23:42:29 +00:00
|
|
|
use image::{RgbImage, Rgb};
|
2020-09-05 17:16:19 +00:00
|
|
|
use imageproc::{pixelops, drawing};
|
2020-12-15 22:19:29 +00:00
|
|
|
use rayon::prelude::*;
|
2020-11-29 20:27:52 +00:00
|
|
|
use serde::{Serialize, Deserialize};
|
2020-09-04 05:34:56 +00:00
|
|
|
use std::fs::File;
|
2021-06-15 04:04:08 +00:00
|
|
|
use std::io::{BufReader, BufWriter, Write as _};
|
2020-09-04 05:34:56 +00:00
|
|
|
use std::path::PathBuf;
|
2020-11-28 05:22:42 +00:00
|
|
|
use std::sync::{Mutex, RwLock};
|
2020-09-04 23:42:29 +00:00
|
|
|
use y4m;
|
|
|
|
|
2020-09-05 21:06:53 +00:00
|
|
|
/// Accept a value from (-\inf, \inf) and return a value in (-1, 1).
|
|
|
|
/// If the input is equal to `typical`, it will be mapped to 0.5.
|
|
|
|
/// If the input is equal to -`typical`, it will be mapped to -0.5.
|
2021-06-06 08:02:28 +00:00
|
|
|
fn scale_signed(x: f32, typical: f32) -> f32 {
|
2020-09-05 21:06:53 +00:00
|
|
|
if x >= 0.0 {
|
|
|
|
scale_unsigned(x, typical)
|
|
|
|
} else {
|
|
|
|
-scale_unsigned(-x, typical)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Accept a value from [0, \inf) and return a value in [0, 1).
|
|
|
|
/// If the input is equal to `typical`, it will be mapped to 0.5.
|
2021-06-06 08:02:28 +00:00
|
|
|
fn scale_unsigned(x: f32, typical: f32) -> f32 {
|
2020-09-05 21:06:53 +00:00
|
|
|
// f(0) => 0
|
|
|
|
// f(1) => 0.5
|
|
|
|
// f(\inf) => 1
|
|
|
|
// f(x) = 1 - 1/(x+1)
|
2021-06-06 08:02:28 +00:00
|
|
|
1.0 - 1.0/(x/typical + 1.0)
|
2020-09-05 21:06:53 +00:00
|
|
|
}
|
|
|
|
|
2021-06-06 08:02:28 +00:00
|
|
|
fn scale_signed_to_u8(x: f32, typ: f32) -> u8 {
|
2020-09-05 21:06:53 +00:00
|
|
|
let norm = 128.0 + 128.0*scale_signed(x, typ);
|
|
|
|
norm as _
|
|
|
|
}
|
|
|
|
|
2021-06-06 08:02:28 +00:00
|
|
|
fn scale_unsigned_to_u8(x: f32, typ: f32) -> u8 {
|
2020-09-05 21:06:53 +00:00
|
|
|
let norm = 256.0*scale_unsigned(x, typ);
|
|
|
|
norm as _
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Scale a vector to have magnitude between [0, 1).
|
2021-06-06 08:02:28 +00:00
|
|
|
fn scale_vector(x: Vec2<f32>, typical_mag: f32) -> Vec2<f32> {
|
2020-09-05 21:06:53 +00:00
|
|
|
let new_mag = scale_unsigned(x.mag(), typical_mag);
|
|
|
|
x.with_mag(new_mag)
|
|
|
|
}
|
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
fn im_size<S: GenericSim>(state: &S, max_w: u32, max_h: u32) -> (u32, u32) {
|
2020-11-28 05:22:42 +00:00
|
|
|
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;
|
2020-12-17 23:09:53 +00:00
|
|
|
width = (width as f32 * stretch).round() as _;
|
2020-11-28 05:22:42 +00:00
|
|
|
height = max_h;
|
|
|
|
}
|
|
|
|
(width, height)
|
|
|
|
}
|
|
|
|
|
2021-06-04 03:42:10 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum FieldDisplayMode {
|
|
|
|
BzExy,
|
|
|
|
EzBxy,
|
|
|
|
BCurrent,
|
|
|
|
M,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FieldDisplayMode {
|
|
|
|
pub fn next(self) -> Self {
|
|
|
|
use FieldDisplayMode::*;
|
|
|
|
match self {
|
|
|
|
BzExy => EzBxy,
|
|
|
|
EzBxy => BCurrent,
|
|
|
|
BCurrent => M,
|
|
|
|
M => BzExy,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn prev(self) -> Self {
|
|
|
|
use FieldDisplayMode::*;
|
|
|
|
match self {
|
|
|
|
BzExy => M,
|
|
|
|
EzBxy => BzExy,
|
|
|
|
BCurrent => EzBxy,
|
|
|
|
M => BCurrent,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
|
|
pub struct RenderConfig {
|
|
|
|
mode: FieldDisplayMode,
|
2021-06-06 08:02:28 +00:00
|
|
|
scale: f32,
|
2021-06-04 03:42:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for RenderConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
mode: FieldDisplayMode::BzExy,
|
|
|
|
scale: 1.0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderConfig {
|
|
|
|
pub fn next_mode(&mut self) {
|
|
|
|
self.mode = self.mode.next();
|
|
|
|
}
|
|
|
|
pub fn prev_mode(&mut self) {
|
|
|
|
self.mode = self.mode.prev();
|
|
|
|
}
|
|
|
|
pub fn increase_range(&mut self) {
|
|
|
|
self.scale *= 2.0;
|
|
|
|
}
|
|
|
|
pub fn decrease_range(&mut self) {
|
|
|
|
self.scale *= 0.5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
struct RenderSteps<'a, S> {
|
2020-09-08 04:07:26 +00:00
|
|
|
im: RgbImage,
|
2020-12-17 07:23:54 +00:00
|
|
|
sim: &'a S,
|
2020-09-08 04:07:26 +00:00
|
|
|
meas: &'a [Box<dyn AbstractMeasurement>],
|
2020-09-26 22:13:30 +00:00
|
|
|
/// Simulation z coordinate to sample
|
|
|
|
z: u32,
|
2020-09-08 04:07:26 +00:00
|
|
|
}
|
2020-09-04 23:49:24 +00:00
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
impl<'a, S: GenericSim> RenderSteps<'a, S> {
|
2020-11-29 18:52:11 +00:00
|
|
|
/// Render using default configuration constants
|
2020-12-17 07:23:54 +00:00
|
|
|
fn render(state: &'a S, measurements: &'a [Box<dyn AbstractMeasurement>], z: u32) -> RgbImage {
|
2021-06-04 03:42:10 +00:00
|
|
|
Self::render_configured(state, measurements, z, (640, 480), RenderConfig::default())
|
2020-11-29 18:52:11 +00:00
|
|
|
}
|
|
|
|
/// Render, controlling things like the size.
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render_configured(
|
|
|
|
state: &'a S,
|
|
|
|
measurements: &'a [Box<dyn AbstractMeasurement>],
|
|
|
|
z: u32,
|
|
|
|
max_size: (u32, u32),
|
|
|
|
config: RenderConfig,
|
|
|
|
) -> RgbImage {
|
2020-11-29 18:52:11 +00:00
|
|
|
let (width, height) = im_size(state, max_size.0, max_size.1);
|
2020-10-10 03:41:38 +00:00
|
|
|
trace!("rendering at {}x{} with z={}", width, height, z);
|
2020-09-26 22:13:30 +00:00
|
|
|
let mut me = Self::new(state, measurements, width, height, z);
|
2020-10-10 03:41:38 +00:00
|
|
|
me.render_scalar_field(10.0, false, 2, |cell| {
|
2021-06-07 22:20:02 +00:00
|
|
|
let is_vacuum = cell.conductivity() != Vec3::zero() || cell.m() != Vec3::zero();
|
|
|
|
cell.conductivity().mag().to_f32() + if is_vacuum {
|
2020-10-10 03:41:38 +00:00
|
|
|
0.0
|
|
|
|
} else {
|
|
|
|
5.0
|
|
|
|
}
|
|
|
|
});
|
2021-06-04 03:42:10 +00:00
|
|
|
match config.mode {
|
|
|
|
FieldDisplayMode::BzExy => {
|
|
|
|
me.render_b_z_field(config.scale);
|
|
|
|
me.render_e_xy_field(config.scale);
|
|
|
|
},
|
|
|
|
FieldDisplayMode::EzBxy => {
|
|
|
|
me.render_e_z_field(config.scale);
|
|
|
|
me.render_b_xy_field(config.scale);
|
|
|
|
},
|
|
|
|
FieldDisplayMode::BCurrent => {
|
|
|
|
me.render_b(config.scale);
|
|
|
|
me.render_current(config.scale);
|
|
|
|
}
|
|
|
|
FieldDisplayMode::M => {
|
|
|
|
me.render_m(config.scale);
|
|
|
|
}
|
2020-09-19 05:22:54 +00:00
|
|
|
}
|
2020-09-08 04:07:26 +00:00
|
|
|
me.render_measurements();
|
|
|
|
me.im
|
2020-09-08 03:14:41 +00:00
|
|
|
}
|
2020-12-17 07:23:54 +00:00
|
|
|
fn new(sim: &'a S, meas: &'a [Box<dyn AbstractMeasurement>], width: u32, height: u32, z: u32) -> Self {
|
2020-09-08 04:07:26 +00:00
|
|
|
RenderSteps {
|
2020-09-26 02:40:49 +00:00
|
|
|
im: RgbImage::new(width, height),
|
2020-09-08 04:07:26 +00:00
|
|
|
sim,
|
2020-09-26 22:13:30 +00:00
|
|
|
meas,
|
|
|
|
z
|
2020-09-08 04:07:26 +00:00
|
|
|
}
|
2020-09-08 03:14:41 +00:00
|
|
|
}
|
2020-09-08 04:07:26 +00:00
|
|
|
|
2021-06-07 22:26:26 +00:00
|
|
|
fn get_at_px(&self, x_px: u32, y_px: u32) -> Sample {
|
2021-06-06 08:02:28 +00:00
|
|
|
let x_prop = x_px as f32 / self.im.width() as f32;
|
|
|
|
let x_m = x_prop * (self.sim.width() as f32 * self.sim.feature_size() as f32);
|
|
|
|
let y_prop = y_px as f32 / self.im.height() as f32;
|
|
|
|
let y_m = y_prop * (self.sim.height() as f32 * self.sim.feature_size() as f32);
|
|
|
|
let z_m = self.z as f32 * self.sim.feature_size() as f32;
|
2021-06-07 21:23:07 +00:00
|
|
|
self.sim.sample(Meters(Vec3::new(x_m, y_m, z_m)))
|
2020-09-26 02:40:49 +00:00
|
|
|
}
|
|
|
|
|
2020-09-19 05:22:54 +00:00
|
|
|
////////////// Ex/Ey/Bz configuration ////////////
|
2021-06-06 08:02:28 +00:00
|
|
|
fn render_b_z_field(&mut self, scale: f32) {
|
2021-06-07 21:23:07 +00:00
|
|
|
self.render_scalar_field(1.0e-4 * scale, true, 1, |cell| cell.b().z().to_f32());
|
2020-09-08 03:14:41 +00:00
|
|
|
}
|
2021-06-06 08:02:28 +00:00
|
|
|
fn render_e_xy_field(&mut self, scale: f32) {
|
|
|
|
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 100.0 * scale, |cell| cell.e().xy().to_f32());
|
2020-09-19 05:22:54 +00:00
|
|
|
// current
|
2021-06-04 03:42:10 +00:00
|
|
|
self.render_vector_field(Rgb([0x00, 0xa0, 0x30]), 1.0e-12 * scale, |cell| {
|
2021-06-07 22:20:02 +00:00
|
|
|
cell.e().elem_mul(cell.conductivity()).xy().to_f32()
|
2020-09-19 05:22:54 +00:00
|
|
|
});
|
|
|
|
}
|
2020-12-15 05:44:07 +00:00
|
|
|
////////////// Magnitude configuration /////////////
|
2021-06-06 08:02:28 +00:00
|
|
|
fn render_b(&mut self, scale: f32) {
|
2021-06-07 21:23:07 +00:00
|
|
|
self.render_scalar_field(1.0e-3 * scale, false, 1, |cell| cell.b().mag().to_f32());
|
2020-12-15 05:44:07 +00:00
|
|
|
}
|
2021-06-06 08:02:28 +00:00
|
|
|
fn render_current(&mut self, scale: f32) {
|
2021-06-04 03:42:10 +00:00
|
|
|
self.render_scalar_field(1.0e1 * scale, false, 0, |cell| {
|
2021-06-07 22:20:02 +00:00
|
|
|
cell.e().elem_mul(cell.conductivity()).mag().to_f32()
|
2020-12-15 05:44:07 +00:00
|
|
|
});
|
|
|
|
}
|
2020-09-19 05:22:54 +00:00
|
|
|
|
|
|
|
////////////// Bx/By/Ez configuration ////////////
|
2021-06-06 08:02:28 +00:00
|
|
|
fn render_e_z_field(&mut self, scale: f32) {
|
2021-06-07 21:23:07 +00:00
|
|
|
self.render_scalar_field(1e4 * scale, true, 1, |cell| cell.e().z().to_f32());
|
2020-09-19 05:22:54 +00:00
|
|
|
}
|
2021-06-06 08:02:28 +00:00
|
|
|
fn render_b_xy_field(&mut self, scale: f32) {
|
|
|
|
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 1.0e-9 * scale, |cell| cell.b().xy().to_f32());
|
2020-09-19 05:22:54 +00:00
|
|
|
}
|
|
|
|
|
2021-06-06 08:02:28 +00:00
|
|
|
fn render_m(&mut self, scale: f32) {
|
2021-06-07 22:20:02 +00:00
|
|
|
self.render_scalar_field(1.0e5 * scale, false, 1, |cell| cell.m().mag().to_f32());
|
|
|
|
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 1.0e5 * scale, |cell| cell.m().xy().to_f32());
|
2020-12-18 06:22:25 +00:00
|
|
|
}
|
|
|
|
|
2021-06-07 22:26:26 +00:00
|
|
|
fn render_vector_field<F: Fn(&Sample) -> Vec2<f32>>(&mut self, color: Rgb<u8>, typical: f32, measure: F) {
|
2020-09-26 02:40:49 +00:00
|
|
|
let w = self.im.width();
|
|
|
|
let h = self.im.height();
|
2020-09-08 04:14:26 +00:00
|
|
|
let vec_spacing = 10;
|
2020-12-15 22:19:29 +00:00
|
|
|
for y in (0..h).into_iter().step_by(vec_spacing as _) {
|
|
|
|
for x in (0..w).into_iter().step_by(vec_spacing as _) {
|
|
|
|
let vec = self.field_vector(x, y, vec_spacing, &measure);
|
|
|
|
let norm_vec = scale_vector(vec, typical);
|
|
|
|
let alpha = 0.7*scale_unsigned(vec.mag_sq(), typical * 5.0);
|
2021-06-06 08:02:28 +00:00
|
|
|
let vec = norm_vec * (vec_spacing as f32);
|
|
|
|
let center = Vec2::new(x as f32, y as f32) + Vec2::new(vec_spacing as f32, vec_spacing as f32)*0.5;
|
2020-12-15 22:19:29 +00:00
|
|
|
self.im.draw_field_arrow(center, vec, color, alpha as f32);
|
2020-09-08 03:14:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-07 22:26:26 +00:00
|
|
|
fn render_scalar_field<F: Fn(&Sample) -> f32 + Sync>(&mut self, typical: f32, signed: bool, slot: u32, measure: F) {
|
2020-12-15 22:19:29 +00:00
|
|
|
// XXX: get_at_px borrows self, so we need to clone the image to operate on it mutably.
|
|
|
|
let mut im = self.im.clone();
|
|
|
|
let w = im.width();
|
|
|
|
let h = im.height();
|
|
|
|
let samples = im.as_flat_samples_mut();
|
|
|
|
assert_eq!(samples.layout.channel_stride, 1);
|
|
|
|
assert_eq!(samples.layout.width_stride, 3);
|
|
|
|
assert_eq!(samples.layout.height_stride, 3*w as usize);
|
|
|
|
let pixel_buf: &mut [[u8; 3]] = unsafe { std::mem::transmute(samples.samples) };
|
|
|
|
pixel_buf[..(w*h) as usize].par_iter_mut().enumerate().for_each(|(idx, px)| {
|
|
|
|
let (y, x) = (idx as u32).div_rem(&w);
|
|
|
|
let cell = self.get_at_px(x, y);
|
|
|
|
let value = measure(&cell);
|
|
|
|
let scaled = if signed {
|
|
|
|
scale_signed_to_u8(value, typical)
|
|
|
|
} else {
|
|
|
|
scale_unsigned_to_u8(value, typical)
|
|
|
|
};
|
|
|
|
px[slot as usize] = scaled;
|
|
|
|
});
|
|
|
|
self.im = im;
|
2020-09-08 04:14:26 +00:00
|
|
|
}
|
2020-09-08 04:07:26 +00:00
|
|
|
fn render_measurements(&mut self) {
|
|
|
|
for (meas_no, m) in self.meas.iter().enumerate() {
|
|
|
|
let meas_string = m.eval(self.sim);
|
2020-09-08 03:14:41 +00:00
|
|
|
for (i, c) in meas_string.chars().enumerate() {
|
2020-09-14 05:38:00 +00:00
|
|
|
let glyph = BASIC_FONTS.get(c)
|
|
|
|
.or_else(|| GREEK_FONTS.get(c))
|
|
|
|
.unwrap_or_else(|| BASIC_FONTS.get('?').unwrap());
|
2020-09-08 03:14:41 +00:00
|
|
|
for (y, bmp) in glyph.iter().enumerate() {
|
|
|
|
for x in 0..8 {
|
|
|
|
if (bmp & 1 << x) != 0 {
|
|
|
|
let real_x = 2 + i as u32*8 + x;
|
2020-10-04 02:39:45 +00:00
|
|
|
if let Some(real_y) = (y as u32 + self.im.height()).checked_sub(10 + meas_no as u32 * 8) {
|
|
|
|
if real_x < self.im.width() {
|
|
|
|
self.im.put_pixel(real_x, real_y, Rgb([0, 0, 0]));
|
|
|
|
}
|
2020-09-08 03:14:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-05 17:16:19 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-04 23:42:29 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-05 17:16:19 +00:00
|
|
|
|
2021-06-07 22:26:26 +00:00
|
|
|
fn field_vector<F: Fn(&Sample) -> Vec2<f32>>(&self, xidx: u32, yidx: u32, size: u32, measure: &F) -> Vec2<f32> {
|
2020-09-26 20:25:49 +00:00
|
|
|
let mut field = Vec2::default();
|
2020-09-26 02:40:49 +00:00
|
|
|
let w = self.im.width();
|
|
|
|
let h = self.im.height();
|
2020-09-05 17:16:19 +00:00
|
|
|
let xstart = xidx.min(w);
|
|
|
|
let ystart = yidx.min(h);
|
|
|
|
let xend = (xstart + size).min(w);
|
|
|
|
let yend = (ystart + size).min(h);
|
|
|
|
for y in ystart..yend {
|
|
|
|
for x in xstart..xend {
|
2020-09-26 03:15:39 +00:00
|
|
|
field += measure(&self.get_at_px(x, y));
|
2020-09-05 17:16:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
let xw = xend - xstart;
|
|
|
|
let yw = yend - ystart;
|
|
|
|
if xw == 0 || yw == 0 {
|
|
|
|
// avoid division by zero
|
2020-09-26 20:25:49 +00:00
|
|
|
Vec2::new(0.0, 0.0)
|
2020-09-05 17:16:19 +00:00
|
|
|
} else {
|
2021-06-06 08:02:28 +00:00
|
|
|
field * (1.0 / ((xw*yw) as f32))
|
2020-09-05 17:16:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
trait ImageRenderExt {
|
2021-06-06 08:02:28 +00:00
|
|
|
fn draw_field_arrow(&mut self, center: Vec2<f32>, rel: Vec2<f32>, color: Rgb<u8>, alpha: f32);
|
2020-09-04 23:42:29 +00:00
|
|
|
}
|
2020-07-13 04:16:42 +00:00
|
|
|
|
2020-09-05 17:16:19 +00:00
|
|
|
impl ImageRenderExt for RgbImage {
|
2021-06-06 08:02:28 +00:00
|
|
|
fn draw_field_arrow(&mut self, center: Vec2<f32>, rel: Vec2<f32>, color: Rgb<u8>, alpha: f32) {
|
2020-09-05 17:16:19 +00:00
|
|
|
let start = (center - rel * 0.5).round();
|
|
|
|
let end = (center + rel * 0.5).round();
|
2020-12-17 23:09:53 +00:00
|
|
|
let i_start = (start.x().round() as _, start.y().round() as _);
|
|
|
|
let i_end = (end.x().round() as _, end.y().round() as _);
|
2020-09-05 21:21:32 +00:00
|
|
|
let interpolate_with_alpha = |left, right, left_weight| {
|
|
|
|
pixelops::interpolate(left, right, left_weight*alpha)
|
|
|
|
};
|
|
|
|
drawing::draw_antialiased_line_segment_mut(self, i_start, i_end, color, interpolate_with_alpha);
|
2020-12-18 06:22:25 +00:00
|
|
|
if i_start != i_end
|
|
|
|
&& (0..self.width() as i32).contains(&i_end.0)
|
|
|
|
&& (0..self.height() as i32).contains(&i_end.1)
|
|
|
|
{
|
|
|
|
self.put_pixel(i_end.0 as _, i_end.1 as _, Rgb([0xff, 0, 0]));
|
|
|
|
}
|
2020-09-05 17:16:19 +00:00
|
|
|
//drawing::draw_line_segment_mut(self, i_start, i_end, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
pub trait Renderer<S>: Send + Sync {
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig);
|
2020-12-17 07:23:54 +00:00
|
|
|
// {
|
|
|
|
// self.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements);
|
|
|
|
// }
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig);
|
2020-11-28 05:22:42 +00:00
|
|
|
/// 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.
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render_with_image(&self, state: &S, _im: &RgbImage, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
|
|
|
|
self.render(state, measurements, config);
|
2020-09-06 00:43:51 +00:00
|
|
|
}
|
2020-09-04 23:12:33 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
fn default_render_z_slice<S: GenericSim, R: Renderer<S>>(
|
2021-06-04 03:42:10 +00:00
|
|
|
me: &R, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig,
|
2020-12-17 07:23:54 +00:00
|
|
|
) {
|
2021-06-04 03:42:10 +00:00
|
|
|
me.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements, config);
|
2020-12-17 07:23:54 +00:00
|
|
|
}
|
|
|
|
fn default_render<S: GenericSim, R: Renderer<S>>(
|
2021-06-04 03:42:10 +00:00
|
|
|
me: &R, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig
|
2020-12-17 07:23:54 +00:00
|
|
|
) {
|
2021-06-04 03:42:10 +00:00
|
|
|
me.render_z_slice(state, state.depth() / 2, measurements, config);
|
2020-12-17 07:23:54 +00:00
|
|
|
}
|
|
|
|
|
2020-09-26 02:40:49 +00:00
|
|
|
// pub struct NumericTermRenderer;
|
|
|
|
//
|
|
|
|
// impl Renderer for NumericTermRenderer {
|
|
|
|
// fn render(&mut self, state: &SimSnapshot, _measurements: &[Box<dyn AbstractMeasurement>]) {
|
|
|
|
// for y in 0..state.height() {
|
|
|
|
// for x in 0..state.width() {
|
|
|
|
// let cell = state.get((x, y).into());
|
|
|
|
// print!(" {:>10.1e}", cell.ex());
|
|
|
|
// }
|
|
|
|
// print!("\n");
|
|
|
|
// for x in 0..state.width() {
|
|
|
|
// let cell = state.get((x, y).into());
|
|
|
|
// print!("{:>10.1e} {:>10.1e}", cell.ey(), cell.bz());
|
|
|
|
// }
|
|
|
|
// print!("\n");
|
|
|
|
// }
|
|
|
|
// print!("\n");
|
|
|
|
// }
|
|
|
|
// }
|
2020-07-13 06:00:54 +00:00
|
|
|
|
2020-11-28 20:22:22 +00:00
|
|
|
#[derive(Default)]
|
2020-07-13 06:00:54 +00:00
|
|
|
pub struct ColorTermRenderer;
|
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
impl<S: GenericSim> Renderer<S> for ColorTermRenderer {
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
|
|
|
|
default_render(self, state, measurements, config)
|
|
|
|
}
|
|
|
|
fn render_z_slice(
|
|
|
|
&self,
|
|
|
|
state: &S,
|
|
|
|
z: u32,
|
|
|
|
measurements: &[Box<dyn AbstractMeasurement>],
|
|
|
|
config: RenderConfig,
|
|
|
|
) {
|
2020-11-29 20:27:52 +00:00
|
|
|
let (max_w, mut max_h) = crossterm::terminal::size().unwrap();
|
2021-06-06 04:17:38 +00:00
|
|
|
max_h = max_h.saturating_sub(2 + measurements.len() as u16);
|
2021-06-04 03:42:10 +00:00
|
|
|
let im = RenderSteps::render_configured(state, &[], z, (max_w as _, max_h as _), config);
|
2020-11-28 21:49:30 +00:00
|
|
|
let mut stdout = std::io::stdout();
|
2020-12-06 21:31:13 +00:00
|
|
|
|
2020-11-29 20:27:52 +00:00
|
|
|
// TODO: consider clearing line-by-line for less tearing?
|
|
|
|
stdout.queue(crossterm::terminal::Clear(crossterm::terminal::ClearType::All)).unwrap();
|
|
|
|
stdout.queue(cursor::MoveTo(0, 0)).unwrap();
|
2020-12-06 21:31:13 +00:00
|
|
|
|
2020-11-29 20:27:52 +00:00
|
|
|
for row in im.rows() {
|
|
|
|
for p in row {
|
2020-11-28 21:49:30 +00:00
|
|
|
stdout.queue(PrintStyledContent(style(" ").on(Color::Rgb {
|
|
|
|
r: p.0[0],
|
|
|
|
g: p.0[1],
|
|
|
|
b: p.0[2],
|
2020-11-29 20:27:52 +00:00
|
|
|
}))).unwrap();
|
2020-11-28 21:49:30 +00:00
|
|
|
}
|
2020-12-06 21:31:13 +00:00
|
|
|
stdout.queue(cursor::MoveDown(1)).unwrap();
|
|
|
|
stdout.queue(cursor::MoveToColumn(0)).unwrap();
|
2020-11-28 21:49:30 +00:00
|
|
|
}
|
2020-12-06 21:31:13 +00:00
|
|
|
|
2021-06-04 03:42:10 +00:00
|
|
|
stdout.queue(PrintStyledContent(style(format!("fields: {:?} scale: {}", config.mode, config.scale)))).unwrap();
|
|
|
|
stdout.queue(cursor::MoveDown(1)).unwrap();
|
|
|
|
stdout.queue(cursor::MoveToColumn(1)).unwrap();
|
2020-11-29 20:27:52 +00:00
|
|
|
stdout.queue(PrintStyledContent(style(format!("z: {}", z)))).unwrap();
|
|
|
|
for m in measurements {
|
2020-12-07 03:54:02 +00:00
|
|
|
// Measurements can be slow to compute
|
|
|
|
stdout.flush().unwrap();
|
2020-11-29 20:27:52 +00:00
|
|
|
let meas_string = m.eval(state);
|
|
|
|
stdout.queue(cursor::MoveDown(1)).unwrap();
|
|
|
|
stdout.queue(cursor::MoveToColumn(1)).unwrap();
|
|
|
|
stdout.queue(PrintStyledContent(style(meas_string))).unwrap();
|
|
|
|
}
|
|
|
|
stdout.flush().unwrap();
|
2020-09-04 05:34:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Y4MRenderer {
|
|
|
|
out_path: PathBuf,
|
2020-11-28 05:22:42 +00:00
|
|
|
encoder: Mutex<Option<y4m::Encoder<File>>>,
|
2020-09-04 05:34:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Y4MRenderer {
|
2020-12-17 07:23:54 +00:00
|
|
|
pub fn new<P: Into<PathBuf>>(output: P) -> Self {
|
2020-09-04 05:34:56 +00:00
|
|
|
Self {
|
|
|
|
out_path: output.into(),
|
2020-11-28 05:22:42 +00:00
|
|
|
encoder: Mutex::new(None),
|
2020-09-04 05:34:56 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-04 23:12:33 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
impl<S: GenericSim> Renderer<S> for Y4MRenderer {
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
|
|
|
|
default_render_z_slice(self, state, z, measurements, config)
|
2020-12-17 07:23:54 +00:00
|
|
|
}
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
|
|
|
|
default_render(self, state, measurements, config)
|
2020-12-17 07:23:54 +00:00
|
|
|
}
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render_with_image(&self, _state: &S, im: &RgbImage, _meas: &[Box<dyn AbstractMeasurement>], _config: RenderConfig) {
|
2020-11-28 05:22:42 +00:00
|
|
|
{
|
|
|
|
let mut enc = self.encoder.lock().unwrap();
|
|
|
|
if enc.is_none() {
|
|
|
|
let writer = File::create(&self.out_path).unwrap();
|
|
|
|
*enc = Some(y4m::encode(im.width() as usize, im.height() as usize, y4m::Ratio::new(30, 1))
|
|
|
|
.with_colorspace(y4m::Colorspace::C444)
|
|
|
|
.write_header(writer)
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
}
|
2020-09-04 05:34:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut pix_y = Vec::new();
|
|
|
|
let mut pix_u = Vec::new();
|
|
|
|
let mut pix_v = Vec::new();
|
2020-09-04 23:42:29 +00:00
|
|
|
for &Rgb([r, g, b]) in im.pixels() {
|
2020-09-05 21:31:29 +00:00
|
|
|
let r = (r as f32) / (256.0);
|
|
|
|
let g = (g as f32) / (256.0);
|
|
|
|
let b = (b as f32) / (256.0);
|
|
|
|
let y = (0.299 * r) + (0.587 * g) + (0.114 * b);
|
|
|
|
let cb = 0.5 - (0.168_736 * r) - (0.331_264 * g) + (0.5 * b);
|
|
|
|
let cr = 0.5 + (0.5 * r) - (0.418_688 * g) - (0.081_312 * b);
|
|
|
|
pix_y.push((y * 256.0) as _);
|
|
|
|
pix_u.push((cb * 256.0) as _);
|
|
|
|
pix_v.push((cr * 256.0) as _);
|
2020-09-04 05:34:56 +00:00
|
|
|
}
|
|
|
|
|
2020-09-04 23:42:29 +00:00
|
|
|
let frame = y4m::Frame::new([&*pix_y, &*pix_u, &*pix_v], None);
|
2020-11-28 05:22:42 +00:00
|
|
|
let mut lock = self.encoder.lock().unwrap();
|
|
|
|
let enc = lock.as_mut().unwrap();
|
2020-09-19 05:22:54 +00:00
|
|
|
trace!("write_frame begin");
|
|
|
|
let ret = enc.write_frame(&frame).unwrap();
|
|
|
|
trace!("write_frame end");
|
|
|
|
ret
|
2020-07-13 06:00:54 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-06 00:43:51 +00:00
|
|
|
|
2020-11-28 05:22:42 +00:00
|
|
|
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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-10 04:51:34 +00:00
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
impl<S: GenericSim> Renderer<S> for PlotlyRenderer {
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
|
|
|
|
default_render_z_slice(self, state, z, measurements, config)
|
2020-12-17 07:23:54 +00:00
|
|
|
}
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render(&self, state: &S, _meas: &[Box<dyn AbstractMeasurement>], _config: RenderConfig) {
|
2020-11-29 20:27:52 +00:00
|
|
|
use plotly::{ImageFormat, Plot, Rgba};
|
|
|
|
// use plotly::common::Marker;
|
2020-11-28 05:22:42 +00:00
|
|
|
use plotly::layout::{AspectMode, Axis, Layout, LayoutScene};
|
2020-10-10 04:51:34 +00:00
|
|
|
let mut plot = Plot::new();
|
2020-11-28 05:22:42 +00:00
|
|
|
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);
|
|
|
|
|
2020-10-10 04:51:34 +00:00
|
|
|
let mut xv = Vec::new();
|
|
|
|
let mut yv = Vec::new();
|
|
|
|
let mut zv = Vec::new();
|
2020-11-28 05:22:42 +00:00
|
|
|
// let mut opacities = Vec::new();
|
|
|
|
let mut colors = Vec::new();
|
2020-10-10 04:51:34 +00:00
|
|
|
for z in 0..state.depth() {
|
2020-11-28 05:22:42 +00:00
|
|
|
if xv.len() >= 120000 {
|
|
|
|
add_scatter(&mut plot, &mut xv, &mut yv, &mut zv, &mut colors);
|
|
|
|
}
|
2020-10-10 04:51:34 +00:00
|
|
|
for y in 0..state.height() {
|
|
|
|
for x in 0..state.width() {
|
2020-11-28 05:22:42 +00:00
|
|
|
// if x%5 == 0 || y%5 == 0 || z%5 == 0 {
|
|
|
|
// continue;
|
|
|
|
// }
|
2020-12-17 07:23:54 +00:00
|
|
|
let cell = (state as &dyn GenericSim).get(Index(Vec3u::new(x, y, z)));
|
2020-11-28 05:22:42 +00:00
|
|
|
xv.push(x);
|
|
|
|
yv.push(y);
|
|
|
|
zv.push(z);
|
|
|
|
// opacities.push((cell.e().mag() * 0.1).min(1.0) as f64)
|
2021-06-07 22:20:02 +00:00
|
|
|
let is_vacuum = cell.conductivity() == Vec3::zero() && cell.m() == Vec3::zero();
|
|
|
|
let mat = cell.conductivity().mag().to_f32() + if is_vacuum {
|
2020-11-28 05:22:42 +00:00
|
|
|
0.0
|
|
|
|
} else {
|
|
|
|
5.0
|
|
|
|
};
|
|
|
|
//let g = scale_unsigned_to_u8(mat, 10.0);
|
2021-06-07 22:20:02 +00:00
|
|
|
//let r = scale_unsigned_to_u8(cell.m().mag(), 100.0);
|
2020-11-28 05:22:42 +00:00
|
|
|
//let b = scale_unsigned_to_u8(cell.e().mag(), 1e2);
|
2021-06-07 22:20:02 +00:00
|
|
|
let r = scale_unsigned_to_u8(cell.m().mag().to_f32(), 100.0);
|
2021-06-07 21:23:07 +00:00
|
|
|
let g = scale_unsigned_to_u8(cell.e().mag().to_f32(), 1e2);
|
|
|
|
let b = scale_unsigned_to_u8(mat, 10.0);
|
2020-11-28 05:22:42 +00:00
|
|
|
let alpha = 1.0;
|
|
|
|
colors.push(Rgba::new(r, g, b, alpha));
|
2020-10-10 04:51:34 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-28 05:22:42 +00:00
|
|
|
// 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);
|
2020-10-10 04:51:34 +00:00
|
|
|
}
|
2020-11-28 05:22:42 +00:00
|
|
|
add_scatter(&mut plot, &mut xv, &mut yv, &mut zv, &mut colors);
|
|
|
|
|
|
|
|
let name = format!("{}{}", self.out_base, state.step_no());
|
|
|
|
let (im_w, im_h) = im_size(state, 2048, 2048);
|
|
|
|
plot.save(&*name, ImageFormat::PNG, im_w as _, im_h as _, 1.0);
|
2020-10-10 04:51:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-15 03:09:52 +00:00
|
|
|
struct MultiRendererElement<S> {
|
|
|
|
step_frequency: u64,
|
|
|
|
renderer: Box<dyn Renderer<S>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
pub struct MultiRenderer<S> {
|
2021-06-15 03:09:52 +00:00
|
|
|
renderers: RwLock<Vec<MultiRendererElement<S>>>,
|
2020-09-06 00:43:51 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
impl<S> Default for MultiRenderer<S> {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
renderers: RwLock::new(Vec::new())
|
|
|
|
}
|
|
|
|
}
|
2020-09-06 00:43:51 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
impl<S> MultiRenderer<S> {
|
2020-09-06 00:43:51 +00:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Default::default()
|
|
|
|
}
|
2021-06-15 03:09:52 +00:00
|
|
|
pub fn push<R: Renderer<S> + 'static>(&self, renderer: R, step_frequency: u64) {
|
|
|
|
self.renderers.write().unwrap().push(MultiRendererElement {
|
|
|
|
step_frequency,
|
|
|
|
renderer: Box::new(renderer),
|
|
|
|
});
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
2021-06-15 03:09:52 +00:00
|
|
|
pub fn with<R: Renderer<S> + 'static>(self, renderer: R, step_frequency: u64) -> Self {
|
|
|
|
self.push(renderer, step_frequency);
|
2020-09-06 00:43:51 +00:00
|
|
|
self
|
|
|
|
}
|
2021-06-15 03:09:52 +00:00
|
|
|
pub fn any_work_for_frame(&self, frame: u64) -> bool {
|
|
|
|
self.renderers.read().unwrap().iter().any(|m| frame % m.step_frequency == 0)
|
|
|
|
}
|
2020-09-06 00:43:51 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 07:23:54 +00:00
|
|
|
impl<S: GenericSim> Renderer<S> for MultiRenderer<S> {
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
|
|
|
|
default_render_z_slice(self, state, z, measurements, config)
|
2020-12-17 07:23:54 +00:00
|
|
|
}
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
|
2020-11-28 05:22:42 +00:00
|
|
|
if self.renderers.read().unwrap().len() != 0 {
|
2021-06-04 03:42:10 +00:00
|
|
|
self.render_with_image(state, &RenderSteps::render(state, measurements, state.depth() / 2), measurements, config);
|
2020-09-06 18:24:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-04 03:42:10 +00:00
|
|
|
fn render_with_image(&self, state: &S, im: &RgbImage, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
|
2020-11-28 05:22:42 +00:00
|
|
|
for r in &*self.renderers.read().unwrap() {
|
2021-06-15 03:09:52 +00:00
|
|
|
if state.step_no() % r.step_frequency == 0 {
|
|
|
|
r.renderer.render_with_image(state, im, measurements, config);
|
|
|
|
}
|
2020-09-06 00:43:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-28 06:11:53 +00:00
|
|
|
|
2020-11-29 20:27:52 +00:00
|
|
|
#[derive(Serialize, Deserialize)]
|
2020-12-17 07:59:57 +00:00
|
|
|
pub struct SerializedFrame<S=StaticSim> {
|
|
|
|
pub state: S,
|
2020-11-29 20:27:52 +00:00
|
|
|
pub measurements: Vec<Box<dyn AbstractMeasurement>>,
|
|
|
|
}
|
|
|
|
|
2020-12-17 07:59:57 +00:00
|
|
|
impl<S: GenericSim> SerializedFrame<S> {
|
|
|
|
pub fn to_static(self) -> SerializedFrame<StaticSim> {
|
|
|
|
SerializedFrame {
|
2021-06-09 22:28:22 +00:00
|
|
|
state: GenericSim::to_static(&self.state),
|
2020-12-17 07:59:57 +00:00
|
|
|
measurements: self.measurements,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 06:11:53 +00:00
|
|
|
pub struct SerializerRenderer {
|
2021-06-15 03:46:22 +00:00
|
|
|
fmt_str: String,
|
|
|
|
prefer_static: bool,
|
2020-11-28 06:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SerializerRenderer {
|
2021-06-15 03:46:22 +00:00
|
|
|
/// `fmt_str` is a format string which will be formatted with
|
|
|
|
/// `{step_no}` set to the relevant simulation step.
|
|
|
|
pub fn new(fmt_str: &str) -> Self {
|
2020-11-28 06:11:53 +00:00
|
|
|
Self {
|
2021-06-15 03:46:22 +00:00
|
|
|
fmt_str: fmt_str.into(),
|
|
|
|
prefer_static: false,
|
2020-11-28 06:11:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-15 03:46:22 +00:00
|
|
|
/// Same as `new`, but cast to StaticSim before serializing. This tends to result in a smaller
|
|
|
|
/// file.
|
|
|
|
pub fn new_static(fmt_str: &str) -> Self {
|
|
|
|
Self {
|
|
|
|
fmt_str: fmt_str.into(),
|
|
|
|
prefer_static: true,
|
|
|
|
}
|
2020-12-17 07:23:54 +00:00
|
|
|
}
|
2021-06-15 03:46:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SerializerRenderer {
|
|
|
|
fn serialize<S: GenericSim + Serialize>(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>]) {
|
2020-11-29 20:27:52 +00:00
|
|
|
let frame = SerializedFrame {
|
2021-06-15 03:46:22 +00:00
|
|
|
state,
|
2020-11-29 20:27:52 +00:00
|
|
|
measurements: measurements.iter().cloned().collect(),
|
|
|
|
};
|
2021-06-15 03:46:22 +00:00
|
|
|
let name = self.fmt_str.replace("{step_no}", &*frame.state.step_no().to_string());
|
2020-11-28 06:37:18 +00:00
|
|
|
let out = BufWriter::new(File::create(name).unwrap());
|
|
|
|
//serde_cbor::to_writer(out, &snap).unwrap();
|
2020-11-29 20:27:52 +00:00
|
|
|
bincode::serialize_into(out, &frame).unwrap();
|
2020-11-28 06:11:53 +00:00
|
|
|
}
|
2021-06-15 04:04:08 +00:00
|
|
|
|
|
|
|
pub fn try_load<S: GenericSim + for <'a> Deserialize<'a>>(&self) -> Option<SerializedFrame<S>> {
|
|
|
|
let mut reader = BufReader::new(File::open(&*self.fmt_str).ok()?);
|
|
|
|
bincode::deserialize_from(&mut reader).ok()
|
|
|
|
}
|
2020-11-28 06:11:53 +00:00
|
|
|
}
|
2021-06-15 03:46:22 +00:00
|
|
|
|
|
|
|
impl<S: GenericSim + Serialize> Renderer<S> for SerializerRenderer {
|
|
|
|
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
|
|
|
|
default_render_z_slice(self, state, z, measurements, config)
|
|
|
|
}
|
|
|
|
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], _config: RenderConfig) {
|
|
|
|
if self.prefer_static {
|
|
|
|
self.serialize(&state.to_static(), measurements);
|
|
|
|
} else {
|
|
|
|
self.serialize(state, measurements);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|