Add more controls to the viewer (scale and field display mode)

This commit is contained in:
2021-06-03 20:42:10 -07:00
parent d953c79916
commit 3021bd025f
6 changed files with 161 additions and 69 deletions

View File

@@ -70,6 +70,66 @@ fn im_size<S: GenericSim>(state: &S, max_w: u32, max_h: u32) -> (u32, u32) {
(width, height)
}
#[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,
scale: Flt,
}
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;
}
}
struct RenderSteps<'a, S> {
im: RgbImage,
sim: &'a S,
@@ -81,10 +141,16 @@ struct RenderSteps<'a, S> {
impl<'a, S: GenericSim> RenderSteps<'a, S> {
/// Render using default configuration constants
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), RenderConfig::default())
}
/// Render, controlling things like the size.
fn render_configured(state: &'a S, 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),
config: RenderConfig,
) -> 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);
@@ -95,18 +161,22 @@ impl<'a, S: GenericSim> RenderSteps<'a, S> {
5.0
}
});
//me.render_scalar_field(100.0, true, 0, |cell| cell.mat().m().mag());
if false {
me.render_b_z_field();
me.render_e_xy_field();
} else if true {
me.render_e_z_field();
me.render_b_xy_field();
} else if false {
me.render_b();
me.render_current();
} else {
me.render_m();
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);
}
}
me.render_measurements();
me.im
@@ -130,37 +200,37 @@ impl<'a, S: GenericSim> RenderSteps<'a, S> {
}
////////////// Ex/Ey/Bz configuration ////////////
fn render_b_z_field(&mut self) {
self.render_scalar_field(1.0e-4, true, 1, |cell| cell.b().z());
fn render_b_z_field(&mut self, scale: Flt) {
self.render_scalar_field(1.0e-4 * scale, true, 1, |cell| cell.b().z());
}
fn render_e_xy_field(&mut self) {
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 100.0, |cell| cell.e().xy());
fn render_e_xy_field(&mut self, scale: Flt) {
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 100.0 * scale, |cell| cell.e().xy());
// current
self.render_vector_field(Rgb([0x00, 0xa0, 0x30]), 1.0e-12, |cell| {
self.render_vector_field(Rgb([0x00, 0xa0, 0x30]), 1.0e-12 * scale, |cell| {
cell.e().elem_mul(cell.mat().conductivity()).xy()
});
}
////////////// Magnitude configuration /////////////
fn render_b(&mut self) {
self.render_scalar_field(1.0e-3, false, 1, |cell| cell.b().mag());
fn render_b(&mut self, scale: Flt) {
self.render_scalar_field(1.0e-3 * scale, false, 1, |cell| cell.b().mag());
}
fn render_current(&mut self) {
self.render_scalar_field(1.0e1, false, 0, |cell| {
fn render_current(&mut self, scale: Flt) {
self.render_scalar_field(1.0e1 * scale, false, 0, |cell| {
cell.e().elem_mul(cell.mat().conductivity()).mag()
});
}
////////////// Bx/By/Ez configuration ////////////
fn render_e_z_field(&mut self) {
self.render_scalar_field(1e4, true, 1, |cell| cell.e().z());
fn render_e_z_field(&mut self, scale: Flt) {
self.render_scalar_field(1e4 * scale, true, 1, |cell| cell.e().z());
}
fn render_b_xy_field(&mut self) {
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 1.0e-9, |cell| cell.b().xy());
fn render_b_xy_field(&mut self, scale: Flt) {
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 1.0e-9 * scale, |cell| cell.b().xy());
}
fn render_m(&mut self) {
self.render_scalar_field(1.0e5, false, 1, |cell| cell.mat().m().mag());
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 1.0e5, |cell| cell.mat().m().xy());
fn render_m(&mut self, scale: Flt) {
self.render_scalar_field(1.0e5 * scale, false, 1, |cell| cell.mat().m().mag());
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 1.0e5 * scale, |cell| cell.mat().m().xy());
}
fn render_vector_field<F: Fn(&Cell<mat::Static>) -> Vec2>(&mut self, color: Rgb<u8>, typical: Flt, measure: F) {
@@ -273,27 +343,27 @@ impl ImageRenderExt for RgbImage {
}
pub trait Renderer<S>: Send + Sync {
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]);
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig);
// {
// self.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements);
// }
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>]);
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig);
/// 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: &S, _im: &RgbImage, measurements: &[Box<dyn AbstractMeasurement>]) {
self.render(state, measurements);
fn render_with_image(&self, state: &S, _im: &RgbImage, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
self.render(state, measurements, config);
}
}
fn default_render_z_slice<S: GenericSim, R: Renderer<S>>(
me: &R, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]
me: &R, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig,
) {
me.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements);
me.render_with_image(state, &RenderSteps::render(state, measurements, z), measurements, config);
}
fn default_render<S: GenericSim, R: Renderer<S>>(
me: &R, state: &S, measurements: &[Box<dyn AbstractMeasurement>]
me: &R, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig
) {
me.render_z_slice(state, state.depth() / 2, measurements);
me.render_z_slice(state, state.depth() / 2, measurements, config);
}
// pub struct NumericTermRenderer;
@@ -320,13 +390,19 @@ fn default_render<S: GenericSim, R: Renderer<S>>(
pub struct ColorTermRenderer;
impl<S: GenericSim> Renderer<S> for ColorTermRenderer {
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render(self, state, measurements)
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>]) {
fn render_z_slice(
&self,
state: &S,
z: u32,
measurements: &[Box<dyn AbstractMeasurement>],
config: RenderConfig,
) {
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 _));
let im = RenderSteps::render_configured(state, &[], z, (max_w as _, max_h as _), config);
let mut stdout = std::io::stdout();
// TODO: consider clearing line-by-line for less tearing?
@@ -345,6 +421,9 @@ impl<S: GenericSim> Renderer<S> for ColorTermRenderer {
stdout.queue(cursor::MoveToColumn(0)).unwrap();
}
stdout.queue(PrintStyledContent(style(format!("fields: {:?} scale: {}", config.mode, config.scale)))).unwrap();
stdout.queue(cursor::MoveDown(1)).unwrap();
stdout.queue(cursor::MoveToColumn(1)).unwrap();
stdout.queue(PrintStyledContent(style(format!("z: {}", z)))).unwrap();
for m in measurements {
// Measurements can be slow to compute
@@ -373,13 +452,13 @@ impl Y4MRenderer {
}
impl<S: GenericSim> Renderer<S> for Y4MRenderer {
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render_z_slice(self, state, z, measurements)
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>]) {
default_render(self, state, measurements)
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
default_render(self, state, measurements, config)
}
fn render_with_image(&self, _state: &S, im: &RgbImage, _meas: &[Box<dyn AbstractMeasurement>]) {
fn render_with_image(&self, _state: &S, im: &RgbImage, _meas: &[Box<dyn AbstractMeasurement>], _config: RenderConfig) {
{
let mut enc = self.encoder.lock().unwrap();
if enc.is_none() {
@@ -446,10 +525,10 @@ impl PlotlyRenderer {
}
impl<S: GenericSim> Renderer<S> for PlotlyRenderer {
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render_z_slice(self, state, z, measurements)
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, _meas: &[Box<dyn AbstractMeasurement>]) {
fn render(&self, state: &S, _meas: &[Box<dyn AbstractMeasurement>], _config: RenderConfig) {
use plotly::{ImageFormat, Plot, Rgba};
// use plotly::common::Marker;
use plotly::layout::{AspectMode, Axis, Layout, LayoutScene};
@@ -541,18 +620,18 @@ impl<S> MultiRenderer<S> {
}
impl<S: GenericSim> Renderer<S> for MultiRenderer<S> {
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render_z_slice(self, state, z, measurements)
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>]) {
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
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, config);
}
}
fn render_with_image(&self, state: &S, im: &RgbImage, measurements: &[Box<dyn AbstractMeasurement>]) {
fn render_with_image(&self, state: &S, im: &RgbImage, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
for r in &*self.renderers.read().unwrap() {
r.render_with_image(state, im, measurements);
r.render_with_image(state, im, measurements, config);
}
}
}
@@ -585,10 +664,10 @@ impl SerializerRenderer {
}
impl<S: GenericSim + Clone + Serialize> Renderer<S> for SerializerRenderer {
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>]) {
default_render_z_slice(self, state, z, measurements)
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>]) {
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], _config: RenderConfig) {
let frame = SerializedFrame {
state: state.clone(),
measurements: measurements.iter().cloned().collect(),