Use field lines to display the electric field

This commit is contained in:
2020-09-05 10:16:19 -07:00
parent 8e71e02ac1
commit 98f9cf4df5
4 changed files with 136 additions and 32 deletions

View File

@@ -1,6 +1,8 @@
use ansi_term::Color::RGB;
use crate::geom::Point;
use crate::{Material as _, SimState};
use image::{RgbImage, Rgb};
use imageproc::{pixelops, drawing};
use std::convert::TryInto as _;
use std::fs::File;
use std::path::PathBuf;
@@ -18,35 +20,94 @@ use y4m;
// -(-c).sqrt()
// }
// }
//
trait SimStateRenderExt {
fn to_image(&self) -> RgbImage;
fn e_vector(&self, xidx: u32, yidx: u32, size: u32) -> Point;
}
impl SimStateRenderExt for SimState {
fn to_image(&self) -> RgbImage {
let w = self.width().try_into().unwrap();
let h = self.height().try_into().unwrap();
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 as usize, y as usize);
//let r = norm_color(cell.bz() * consts::C);
//let r = 0;
let r = norm_color(cell.mat().mz()*1.0e-2);
let b = (55.0*cell.mat().conductivity()).min(255.0) as u8;
//let b = 0;
//let b = norm_color(cell.ey());
//let g = 0;
//let g = norm_color(cell.ex());
//let g = norm_color(curl(cell.ex(), cell.ey()));
let g = norm_color((cell.bz() * 1.0e4).into());
image.put_pixel(x, y, Rgb([r, g, b]));
}
}
for y in 0..h {
for x in 0..w {
if x % evec_spacing == 0 && y % evec_spacing == 0 {
let mut vec = self.e_vector(x, y, evec_spacing) * 0.01;
if vec.mag() > evec_spacing as f64 {
vec = vec * (evec_spacing as f64 / vec.mag());
}
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]));
}
}
}
image
}
fn e_vector(&self, xidx: u32, yidx: u32, size: u32) -> Point {
let mut e = Point::default();
let w = self.width().try_into().unwrap();
let h = self.height().try_into().unwrap();
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 {
e += self.get(x as usize, y as usize).e();
}
}
let xw = xend - xstart;
let yw = yend - ystart;
if xw == 0 || yw == 0 {
// avoid division by zero
Point::new(0.0, 0.0)
} else {
e * (1.0 / ((xw*yw) as f64))
}
}
}
trait ImageRenderExt {
fn draw_field_arrow(&mut self, center: Point, rel: Point, color: Rgb<u8>);
}
impl ImageRenderExt for RgbImage {
fn draw_field_arrow(&mut self, center: Point, rel: Point, color: Rgb<u8>) {
let start = (center - rel * 0.5).round();
let end = (center + rel * 0.5).round();
let i_start = (start.x() as _, start.y() as _);
let i_end = (end.x() as _, end.y() as _);
drawing::draw_antialiased_line_segment_mut(self, i_start, i_end, color, pixelops::interpolate);
//drawing::draw_line_segment_mut(self, i_start, i_end, color);
}
}
fn norm_color(v: f64) -> u8 {
(v * 64.0 + 128.0).max(0.0).min(255.0) as u8
}
fn simstate_to_image(state: &SimState) -> RgbImage {
let w = state.width().try_into().unwrap();
let h = state.height().try_into().unwrap();
let mut image = RgbImage::new(w, h);
for y in 0..h {
for x in 0..w {
let cell = state.get(x as usize, y as usize);
//let r = norm_color(cell.bz() * consts::C);
//let r = 0;
let r = norm_color(cell.mat().mz()*1.0e-2);
let b = (55.0*cell.mat().conductivity()).min(255.0) as u8;
//let b = 0;
//let b = norm_color(cell.ey());
//let g = 0;
//let g = norm_color(cell.ex());
//let g = norm_color(curl(cell.ex(), cell.ey()));
let g = norm_color((cell.bz() * 1.0e4).into());
image.put_pixel(x, y, Rgb([r, g, b]));
}
}
image
}
pub trait Renderer {
fn render(&mut self, state: &SimState);
}
@@ -76,7 +137,7 @@ pub struct ColorTermRenderer;
impl Renderer for ColorTermRenderer {
fn render(&mut self, state: &SimState) {
let square = "";
let im = simstate_to_image(state);
let im = state.to_image();
let buf: String = im
.enumerate_rows()
.map(|(_y, row)| {
@@ -106,7 +167,7 @@ impl Y4MRenderer {
impl Renderer for Y4MRenderer {
fn render(&mut self, state: &SimState) {
let im = simstate_to_image(state);
let im = state.to_image();
if self.encoder.is_none() {
let writer = File::create(&self.out_path).unwrap();
self.encoder = Some(y4m::encode(im.width() as usize, im.height() as usize, y4m::Ratio::new(30, 1))