Add a 'Measurement' concept, e.g. to display current at a point.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
use ansi_term::Color::RGB;
|
||||
use crate::geom::Point;
|
||||
use crate::{Material as _, SimSnapshot, SimState};
|
||||
use crate::meas::AbstractMeasurement;
|
||||
use decorum::R64;
|
||||
use font8x8::{BASIC_FONTS, UnicodeFonts as _};
|
||||
use image::{RgbImage, Rgb};
|
||||
use imageproc::{pixelops, drawing};
|
||||
use std::fs::File;
|
||||
@@ -48,43 +50,75 @@ fn scale_vector(x: Point, typical_mag: f64) -> Point {
|
||||
x.with_mag(new_mag)
|
||||
}
|
||||
|
||||
trait SimSnapshotRenderExt {
|
||||
fn to_image(&self) -> RgbImage;
|
||||
fn e_vector(&self, xidx: u32, yidx: u32, size: u32) -> Point;
|
||||
}
|
||||
struct RenderSteps(RgbImage);
|
||||
|
||||
impl SimSnapshotRenderExt for SimSnapshot {
|
||||
fn to_image(&self) -> RgbImage {
|
||||
impl RenderSteps {
|
||||
fn render(state: &SimSnapshot, measurements: &[Box<dyn AbstractMeasurement>]) -> RgbImage {
|
||||
let w = state.width();
|
||||
let h = state.height();
|
||||
let mut me = RenderSteps(RgbImage::new(w, h));
|
||||
me.render_b_field(state);
|
||||
me.render_e_field(state);
|
||||
me.render_measurements(state, measurements);
|
||||
me.0
|
||||
}
|
||||
fn width(&self) -> u32 {
|
||||
self.0.width()
|
||||
}
|
||||
fn height(&self) -> u32 {
|
||||
self.0.height()
|
||||
}
|
||||
fn render_b_field(&mut self, state: &SimSnapshot) {
|
||||
let w = self.width();
|
||||
let h = self.height();
|
||||
let evec_spacing = 10;
|
||||
let mut image = RgbImage::new(w, h);
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let cell = self.get(x, y);
|
||||
let cell = state.get(x, y);
|
||||
let r = scale_signed_to_u8(cell.mat().mz(), 100.0);
|
||||
let b = scale_unsigned_to_u8(cell.mat().conductivity(), 10.0);
|
||||
let g = scale_signed_to_u8(cell.bz(), 1.0e-4);
|
||||
image.put_pixel(x, y, Rgb([r, g, b]));
|
||||
self.0.put_pixel(x, y, Rgb([r, g, b]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
fn render_e_field(&mut self, state: &SimSnapshot) {
|
||||
let w = self.width();
|
||||
let h = self.height();
|
||||
let evec_spacing = 10;
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
if x % evec_spacing == 0 && y % evec_spacing == 0 {
|
||||
let evec = self.e_vector(x, y, evec_spacing);
|
||||
let evec = self.e_vector(state, x, y, evec_spacing);
|
||||
let norm_vec = scale_vector(evec, 100.0);
|
||||
let alpha = scale_unsigned(evec.mag_sq(), 500.0);
|
||||
let vec = norm_vec * (evec_spacing as f64);
|
||||
let center = Point::new(x as _, y as _) + Point::new(evec_spacing as _, evec_spacing as _)*0.5;
|
||||
image.draw_field_arrow(center, vec, Rgb([0xff, 0xff, 0xff]), alpha as f32);
|
||||
self.0.draw_field_arrow(center, vec, Rgb([0xff, 0xff, 0xff]), alpha as f32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn render_measurements(&mut self, state: &SimSnapshot, measurements: &[Box<dyn AbstractMeasurement>]) {
|
||||
for (meas_no, m) in measurements.iter().enumerate() {
|
||||
let meas_string = m.eval(state);
|
||||
for (i, c) in meas_string.chars().enumerate() {
|
||||
let glyph = BASIC_FONTS.get(c).unwrap();
|
||||
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;
|
||||
let real_y = y as u32 + self.height() - 10 - meas_no as u32 * 8;
|
||||
if real_x < self.width() {
|
||||
self.0.put_pixel(real_x, real_y, Rgb([0, 0, 0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
image
|
||||
}
|
||||
|
||||
fn e_vector(&self, xidx: u32, yidx: u32, size: u32) -> Point {
|
||||
fn e_vector(&self, state: &SimSnapshot, xidx: u32, yidx: u32, size: u32) -> Point {
|
||||
let mut e = Point::default();
|
||||
let w = self.width();
|
||||
let h = self.height();
|
||||
@@ -94,7 +128,7 @@ impl SimSnapshotRenderExt for SimSnapshot {
|
||||
let yend = (ystart + size).min(h);
|
||||
for y in ystart..yend {
|
||||
for x in xstart..xend {
|
||||
e += self.get(x, y).e();
|
||||
e += state.get(x, y).e();
|
||||
}
|
||||
}
|
||||
let xw = xend - xstart;
|
||||
@@ -127,18 +161,18 @@ impl ImageRenderExt for RgbImage {
|
||||
}
|
||||
|
||||
pub trait Renderer {
|
||||
fn render(&mut self, state: &SimSnapshot) {
|
||||
self.render_with_image(state, &state.to_image());
|
||||
fn render(&mut self, state: &SimSnapshot, measurements: &[Box<dyn AbstractMeasurement>]) {
|
||||
self.render_with_image(state, &RenderSteps::render(state, measurements));
|
||||
}
|
||||
fn render_with_image(&mut self, state: &SimSnapshot, _im: &RgbImage) {
|
||||
self.render(state);
|
||||
self.render(state, &[]);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NumericTermRenderer;
|
||||
|
||||
impl Renderer for NumericTermRenderer {
|
||||
fn render(&mut self, state: &SimSnapshot) {
|
||||
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);
|
||||
@@ -236,19 +270,19 @@ impl MultiRenderer {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn render(&mut self, state: &SimState) {
|
||||
//let max_width = 1980; //< TODO: make configurable
|
||||
let max_width = 200;
|
||||
pub fn render(&mut self, state: &SimState, measurements: &[Box<dyn AbstractMeasurement>]) {
|
||||
let max_width = 1980; //< TODO: make configurable
|
||||
// let max_width = 200;
|
||||
let dec = (state.width() + max_width - 1) / max_width;
|
||||
let snap = state.snapshot(dec);
|
||||
Renderer::render(self, &snap);
|
||||
Renderer::render(self, &snap, measurements);
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for MultiRenderer {
|
||||
fn render(&mut self, state: &SimSnapshot) {
|
||||
fn render(&mut self, state: &SimSnapshot, measurements: &[Box<dyn AbstractMeasurement>]) {
|
||||
if self.renderers.len() != 0 {
|
||||
self.render_with_image(state, &state.to_image());
|
||||
self.render_with_image(state, &RenderSteps::render(state, measurements));
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user