Add more controls to the viewer (scale and field display mode)
This commit is contained in:
197
src/render.rs
197
src/render.rs
@@ -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(),
|
||||
|
Reference in New Issue
Block a user