Parameterize 2d geometry types over the Float type

I'll work toward parameterizing more geometry (Vec3); this
is just a starting point.
This commit is contained in:
2021-06-06 01:02:28 -07:00
parent f4e9a938a3
commit a0765e3ec0
8 changed files with 231 additions and 181 deletions

View File

@@ -91,7 +91,7 @@ fn main() {
Meters((half_width + 0.5 * (ferro_inner_rad + ferro_outer_rad), half_width, half_depth).into()) Meters((half_width + 0.5 * (ferro_inner_rad + ferro_outer_rad), half_width, half_depth).into())
)); ));
let center = Vec2::new(half_width as _, half_width as _); let center = Vec2::new(half_width as Flt, half_width as Flt);
for z_px in 0..depth_px { for z_px in 0..depth_px {
for y_px in 0..width_px { for y_px in 0..width_px {
@@ -99,9 +99,9 @@ fn main() {
let loc = Index((x_px, y_px, z_px).into()); let loc = Index((x_px, y_px, z_px).into());
let d = Vec2::new(to_m(x_px), to_m(y_px)) - center; let d = Vec2::new(to_m(x_px), to_m(y_px)) - center;
let r = d.mag(); let r = d.mag();
if (conductor_inner_rad as _..conductor_outer_rad as _).contains(&r) { if (conductor_inner_rad as Flt..conductor_outer_rad as Flt).contains(&r) {
driver.state.put_material(loc, mat::db::conductor(conductivity).into()); driver.state.put_material(loc, mat::db::conductor(conductivity).into());
} else if (ferro_inner_rad as _..ferro_outer_rad as _).contains(&r) { } else if (ferro_inner_rad as Flt..ferro_outer_rad as Flt).contains(&r) {
let half_depth_px = from_m(half_depth); let half_depth_px = from_m(half_depth);
let ferro_depth_px = from_m(ferro_depth); let ferro_depth_px = from_m(ferro_depth);
if (half_depth_px-ferro_depth_px/2 .. half_depth_px+(ferro_depth_px+1)/2).contains(&z_px) { if (half_depth_px-ferro_depth_px/2 .. half_depth_px+(ferro_depth_px+1)/2).contains(&z_px) {

View File

@@ -8,69 +8,68 @@ pub use units::{Coord, Meters, Index};
pub use vec::{Vec2, Vec3}; pub use vec::{Vec2, Vec3};
pub use vecu::Vec3u; pub use vecu::Vec3u;
use crate::flt::{Flt, Real}; use crate::flt;
use crate::real::Real;
use std::ops::Add; use std::ops::Add;
fn real(f: Flt) -> Real {
Real::from_inner(f)
}
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
pub struct Line2d { pub struct Line2d<R=flt::Real> {
from: Vec2, from: Vec2<R>,
to: Vec2, to: Vec2<R>,
} }
impl Add for Line2d { impl<R: Real> Add for Line2d<R> {
type Output = Line2d; type Output = Self;
fn add(self, other: Line2d) -> Line2d { fn add(self, other: Self) -> Self {
Line2d { Line2d {
from: self.from + Vec2::new(0.0, other.y(self.from.x())), from: self.from + Vec2::new(R::zero(), other.y(self.from.x())),
to: self.to + Vec2::new(0.0, other.y(self.to.x())), to: self.to + Vec2::new(R::zero(), other.y(self.to.x())),
} }
} }
} }
impl Line2d { impl<R> Line2d<R> {
pub fn new(from: Vec2, to: Vec2) -> Self { pub fn new(from: Vec2<R>, to: Vec2<R>) -> Self {
Self { Self {
from, from,
to, to,
} }
} }
pub fn x(&self, y: Flt) -> Flt {
self.shifted(Vec2::new(0.0, -y)).x_intercept()
}
pub fn y(&self, x: Flt) -> Flt {
(self.from.y + (real(x) - self.from.x) * self.slope()).into()
}
pub fn at_x(&self, x: Flt) -> Vec2 {
Vec2::new(x, self.y(x))
} }
pub fn from(&self) -> Vec2 { impl<R: Real> Line2d<R> {
pub fn from(&self) -> Vec2<R> {
self.from self.from
} }
pub fn to(&self) -> Vec2 { pub fn to(&self) -> Vec2<R> {
self.to self.to
} }
pub fn x_intercept(&self) -> Flt { pub fn x(&self, y: R) -> R {
self.shifted(Vec2::new(R::zero(), -y)).x_intercept()
}
pub fn y(&self, x: R) -> R {
(self.from.y + (x - self.from.x) * self.slope()).into()
}
pub fn at_x(&self, x: R) -> Vec2<R> {
Vec2::new(x, self.y(x))
}
pub fn x_intercept(&self) -> R {
let t = (-self.from.y) / (self.to.y - self.from.y); let t = (-self.from.y) / (self.to.y - self.from.y);
(self.from.x + t * (self.to.x - self.from.x)).into() (self.from.x + t * (self.to.x - self.from.x)).into()
} }
pub fn as_vector(&self) -> Vec2 { pub fn as_vector(&self) -> Vec2<R> {
self.to + (-self.from) self.to + (-self.from)
} }
pub fn length_sq(&self) -> Flt { pub fn length_sq(&self) -> R {
let Vec2 { x, y } = self.as_vector(); let Vec2 { x, y } = self.as_vector();
(x*x + y*y).into() (x*x + y*y).into()
} }
pub fn distance_sq(&self, pt: Vec2) -> Flt { pub fn distance_sq(&self, pt: Vec2<R>) -> R {
// source: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points // source: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points
let d = self.as_vector(); let d = self.as_vector();
let twice_area = d.y*pt.x - d.x*pt.y + self.to.x*self.from.y - self.to.y*self.from.x; let twice_area = d.y*pt.x - d.x*pt.y + self.to.x*self.from.y - self.to.y*self.from.x;
@@ -86,8 +85,7 @@ impl Line2d {
self.to.x > self.from.x self.to.x > self.from.x
} }
pub fn contains_x(&self, x: Flt) -> bool { pub fn contains_x(&self, x: R) -> bool {
let x = real(x);
if self.is_ascending_x() { if self.is_ascending_x() {
x >= self.from.x && x < self.to.x x >= self.from.x && x < self.to.x
} else { } else {
@@ -95,8 +93,7 @@ impl Line2d {
} }
} }
pub fn contains_y(&self, y: Flt) -> bool { pub fn contains_y(&self, y: R) -> bool {
let y = real(y);
if self.is_ascending_x() { if self.is_ascending_x() {
y >= self.from.y && y < self.to.y y >= self.from.y && y < self.to.y
} else { } else {
@@ -104,15 +101,15 @@ impl Line2d {
} }
} }
pub fn min_x(&self) -> Flt { pub fn min_x(&self) -> R {
self.from.x.min(self.to.x).into() self.from.x.min_or_undefined(&self.to.x)
} }
pub fn max_x(&self) -> Flt { pub fn max_x(&self) -> R {
self.from.x.max(self.to.x).into() self.from.x.max_or_undefined(&self.to.x)
} }
pub fn clamp_x(&self, x: Flt) -> Flt { pub fn clamp_x(&self, x: R) -> R {
match x { match x {
v if v < self.min_x() => self.min_x(), v if v < self.min_x() => self.min_x(),
v if v > self.max_x() => self.max_x(), v if v > self.max_x() => self.max_x(),
@@ -120,24 +117,23 @@ impl Line2d {
} }
} }
pub fn clamp_by_x(&self, x: Flt) -> Vec2 { pub fn clamp_by_x(&self, x: R) -> Vec2<R> {
self.at_x(self.clamp_x(x)) self.at_x(self.clamp_x(x))
} }
pub fn slope(&self) -> Flt { pub fn slope(&self) -> R {
let s = (self.to.y - self.from.y) / (self.to.x - self.from.x); (self.to.y - self.from.y) / (self.to.x - self.from.x)
s.into()
} }
/// Move the coordinate along the line toward some desired `y(x)` value. /// Move the coordinate along the line toward some desired `y(x)` value.
/// The returned value could be OOB: check `self.contains_x` to find out. /// The returned value could be OOB: check `self.contains_x` to find out.
pub fn move_toward_y_unclamped(&self, start_x: Flt, target_y: Flt) -> Flt { pub fn move_toward_y_unclamped(&self, start_x: R, target_y: R) -> R {
let start_y = self.y(start_x); let start_y = self.y(start_x);
let delta_x = real(target_y - start_y) / self.slope(); let delta_x = (target_y - start_y) / self.slope();
(real(start_x) + delta_x).into() start_x + delta_x
} }
pub fn shifted(&self, p: Vec2) -> Line2d { pub fn shifted(&self, p: Vec2<R>) -> Self {
Line2d { Line2d {
from: self.from + p, from: self.from + p,
to: self.to + p to: self.to + p
@@ -146,39 +142,40 @@ impl Line2d {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Polygon2d { pub struct Polygon2d<R=flt::Real> {
points: Vec<Vec2>, points: Vec<Vec2<R>>,
} }
impl Polygon2d { impl<R> Polygon2d<R> {
pub fn new(points: Vec<Vec2>) -> Self { pub fn new(points: Vec<Vec2<R>>) -> Self {
Self { Self {
points points
} }
} }
}
pub fn segments<'a>(&'a self) -> impl Iterator<Item=Line2d> + 'a { impl<R: Real> Polygon2d<R> {
pub fn segments<'a>(&'a self) -> impl Iterator<Item=Line2d<R>> + 'a {
(0..self.points.len()).into_iter().map(move |i| { (0..self.points.len()).into_iter().map(move |i| {
let from = self.points[i]; let from = self.points[i];
let to = *self.points.get(i+1).unwrap_or_else(|| &self.points[0]); let to = *self.points.get(i+1).unwrap_or_else(|| &self.points[0]);
Line2d { from, to } Line2d { from, to }
}) })
} }
pub fn min_x(&self) -> R {
pub fn min_x(&self) -> Flt { self.points.iter().map(|p| p.x).reduce(|p, q| p.min_or_undefined(&q)).unwrap()
self.points.iter().map(|p| p.x).min().unwrap().into()
} }
pub fn max_x(&self) -> Flt { pub fn max_x(&self) -> R {
self.points.iter().map(|p| p.x).max().unwrap().into() self.points.iter().map(|p| p.x).reduce(|p, q| p.max_or_undefined(&q)).unwrap()
} }
pub fn min_y(&self) -> Flt { pub fn min_y(&self) -> R {
self.points.iter().map(|p| p.y).min().unwrap().into() self.points.iter().map(|p| p.y).reduce(|p, q| p.min_or_undefined(&q)).unwrap()
} }
pub fn max_y(&self) -> Flt { pub fn max_y(&self) -> R {
self.points.iter().map(|p| p.y).max().unwrap().into() self.points.iter().map(|p| p.y).reduce(|p, q| p.max_or_undefined(&q)).unwrap()
} }
} }

View File

@@ -32,7 +32,7 @@ impl CylinderZ {
#[typetag::serde] #[typetag::serde]
impl Region for CylinderZ { impl Region for CylinderZ {
fn contains(&self, p: Meters) -> bool { fn contains(&self, p: Meters) -> bool {
p.xy().distance_sq(self.center) <= (self.radius * self.radius).into() p.xy().distance_sq(self.center) <= self.radius * self.radius
} }
} }

View File

@@ -1,4 +1,5 @@
use crate::flt::{Flt, Real}; use crate::flt::{self, Flt};
use crate::real::Real;
use super::Vec3u; use super::Vec3u;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
@@ -7,35 +8,31 @@ use std::fmt;
use std::iter::Sum; use std::iter::Sum;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub};
fn round(f: Real) -> Real {
Real::from_inner(f.into_inner().round())
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub struct Vec2 { pub struct Vec2<R=flt::Real> {
pub x: Real, pub x: R,
pub y: Real, pub y: R,
} }
impl Add for Vec2 { impl<R: Real> Add for Vec2<R> {
type Output = Vec2; type Output = Self;
fn add(self, other: Vec2) -> Vec2 { fn add(self, other: Self) -> Self {
let mut ret = self.clone(); let mut ret = self.clone();
ret += other; ret += other;
ret ret
} }
} }
impl AddAssign for Vec2 { impl<R: Real> AddAssign for Vec2<R> {
fn add_assign(&mut self, other: Vec2) { fn add_assign(&mut self, other: Self) {
self.x += other.x; self.x += other.x;
self.y += other.y; self.y += other.y;
} }
} }
impl Neg for Vec2 { impl<R: Real> Neg for Vec2<R> {
type Output = Vec2; type Output = Self;
fn neg(self) -> Vec2 { fn neg(self) -> Self {
Vec2 { Vec2 {
x: -self.x, x: -self.x,
y: -self.y, y: -self.y,
@@ -43,16 +40,16 @@ impl Neg for Vec2 {
} }
} }
impl Sub for Vec2 { impl<R: Real> Sub for Vec2<R> {
type Output = Vec2; type Output = Self;
fn sub(self, other: Vec2) -> Vec2 { fn sub(self, other: Self) -> Self {
self + (-other) self + (-other)
} }
} }
impl Mul<Flt> for Vec2 { impl<R: Real> Mul<R> for Vec2<R> {
type Output = Vec2; type Output = Self;
fn mul(self, s: Flt) -> Vec2 { fn mul(self, s: R) -> Self {
Vec2 { Vec2 {
x: self.x * s, x: self.x * s,
y: self.y * s, y: self.y * s,
@@ -60,69 +57,77 @@ impl Mul<Flt> for Vec2 {
} }
} }
impl Into<(Flt, Flt)> for Vec2 { impl<R> Into<(R, R)> for Vec2<R> {
fn into(self) -> (Flt, Flt) { fn into(self) -> (R, R) {
(self.x(), self.y())
}
}
impl Into<(Real, Real)> for Vec2 {
fn into(self) -> (Real, Real) {
(self.x, self.y) (self.x, self.y)
} }
} }
impl Vec2 { impl<R> Vec2<R> {
pub fn new(x: Flt, y: Flt) -> Self { pub fn new<R2: Into<R>>(x: R2, y: R2) -> Self {
Self { Self {
x: x.into(), x: x.into(),
y: y.into(), y: y.into(),
} }
} }
}
pub fn x(&self) -> Flt { impl<R: Real> Vec2<R> {
pub fn zero() -> Self {
Self::new(R::zero(), R::zero())
}
pub fn unit() -> Self {
Self::new(R::one(), R::one())
}
pub fn x(&self) -> R {
self.x.into() self.x.into()
} }
pub fn y(&self) -> Flt { pub fn y(&self) -> R {
self.y.into() self.y.into()
} }
pub fn round(&self) -> Vec2 { pub fn to_f32(&self) -> Vec2<f32> {
Vec2::new(self.x.to_f32(), self.y.to_f32())
}
pub fn round(&self) -> Self {
Vec2 { Vec2 {
x: round(self.x), x: self.x.round(),
y: round(self.y), y: self.y.round(),
} }
} }
pub fn distance_sq(&self, other: Self) -> Flt { pub fn distance_sq(&self, other: Self) -> R {
(*self - other).mag_sq() (*self - other).mag_sq()
} }
pub fn mag_sq(&self) -> Flt { pub fn mag_sq(&self) -> R {
(self.x*self.x + self.y*self.y).into() self.x*self.x + self.y*self.y
} }
pub fn mag(&self) -> Flt { pub fn mag(&self) -> R {
self.mag_sq().sqrt() self.mag_sq().sqrt()
} }
pub fn with_mag(&self, new_mag: Flt) -> Vec2 { pub fn with_mag(&self, new_mag: R) -> Self {
if new_mag == 0.0 { if new_mag.is_zero() {
// avoid div-by-zero if self.mag() == 0 and new_mag == 0 // avoid div-by-zero if self.mag() == 0 and new_mag == 0
Vec2::new(0.0, 0.0) Vec2::new(R::zero(), R::zero())
} else { } else {
let scale = Real::from_inner(new_mag) / self.mag(); let scale = new_mag / self.mag();
self.clone() * scale.into_inner() *self * scale
} }
} }
} }
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Vec3 { pub struct Vec3 {
pub(crate) x: Real, pub(crate) x: flt::Real,
pub(crate) y: Real, pub(crate) y: flt::Real,
pub(crate) z: Real, pub(crate) z: flt::Real,
} }
impl Vec3 { impl Vec3 {
@@ -161,13 +166,13 @@ impl Vec3 {
self.z.into() self.z.into()
} }
pub fn set_x(&mut self, x: Flt) { pub fn set_x(&mut self, x: Flt) {
self.x = Real::from_inner(x); self.x = flt::Real::from_inner(x);
} }
pub fn set_y(&mut self, y: Flt) { pub fn set_y(&mut self, y: Flt) {
self.y = Real::from_inner(y); self.y = flt::Real::from_inner(y);
} }
pub fn set_z(&mut self, z: Flt) { pub fn set_z(&mut self, z: Flt) {
self.z = Real::from_inner(z); self.z = flt::Real::from_inner(z);
} }
pub fn xy(&self) -> Vec2 { pub fn xy(&self) -> Vec2 {
Vec2::new(self.x(), self.y()) Vec2::new(self.x(), self.y())
@@ -237,7 +242,7 @@ impl Vec3 {
// avoid div-by-zero if self.mag() == 0 and new_mag == 0 // avoid div-by-zero if self.mag() == 0 and new_mag == 0
Self::zero() Self::zero()
} else { } else {
let scale = Real::from_inner(new_mag) / self.mag(); let scale = flt::Real::from_inner(new_mag) / self.mag();
self.clone() * scale.into_inner() self.clone() * scale.into_inner()
} }
} }
@@ -267,8 +272,8 @@ impl From<(Flt, Flt, Flt)> for Vec3 {
} }
} }
impl From<(Real, Real, Real)> for Vec3 { impl From<(flt::Real, flt::Real, flt::Real)> for Vec3 {
fn from((x, y, z): (Real, Real, Real)) -> Self { fn from((x, y, z): (flt::Real, flt::Real, flt::Real)) -> Self {
Self { x, y, z } Self { x, y, z }
} }
} }
@@ -318,17 +323,17 @@ impl Neg for Vec3 {
} }
} }
impl MulAssign<Real> for Vec3 { impl MulAssign<flt::Real> for Vec3 {
fn mul_assign(&mut self, other: Real) { fn mul_assign(&mut self, other: flt::Real) {
self.x *= other; self.x *= other;
self.y *= other; self.y *= other;
self.z *= other; self.z *= other;
} }
} }
impl Mul<Real> for Vec3 { impl Mul<flt::Real> for Vec3 {
type Output = Self; type Output = Self;
fn mul(self, other: Real) -> Self { fn mul(self, other: flt::Real) -> Self {
let mut work = self.clone(); let mut work = self.clone();
work *= other; work *= other;
work work
@@ -352,15 +357,15 @@ impl Mul<Flt> for Vec3 {
} }
} }
impl DivAssign<Real> for Vec3 { impl DivAssign<flt::Real> for Vec3 {
fn div_assign(&mut self, other: Real) { fn div_assign(&mut self, other: flt::Real) {
*self *= Real::from_inner(1.0) / other; *self *= flt::Real::from_inner(1.0) / other;
} }
} }
impl Div<Real> for Vec3 { impl Div<flt::Real> for Vec3 {
type Output = Self; type Output = Self;
fn div(self, other: Real) -> Self { fn div(self, other: flt::Real) -> Self {
let mut work = self.clone(); let mut work = self.clone();
work /= other; work /= other;
work work

View File

@@ -12,6 +12,7 @@ pub mod geom;
pub mod mat; pub mod mat;
pub mod meas; pub mod meas;
pub mod post; pub mod post;
pub mod real;
pub mod render; pub mod render;
pub mod sim; pub mod sim;
pub mod stim; pub mod stim;

View File

@@ -249,24 +249,24 @@ impl MHCurve {
let line = segments.next().unwrap_or_else(|| { let line = segments.next().unwrap_or_else(|| {
panic!("failed to find segment for h:{}, m:{}, {:?}", h, m, self.geom.segments().collect::<Vec<_>>()); panic!("failed to find segment for h:{}, m:{}, {:?}", h, m, self.geom.segments().collect::<Vec<_>>());
}); });
if line.contains_y(m.into()) && line.is_ascending() == is_ascending { if line.contains_y(m) && line.is_ascending() == is_ascending {
if line.contains_x(h.into()) && line.distance_sq(Vec2::new(h.into(), m.into())) < 1.0e-6 { if line.contains_x(h) && line.distance_sq(Vec2::new(h, m)) < 1.0e-6 {
// (h, m) resides on this line // (h, m) resides on this line
break line; break line;
} else { } else {
// need to move the point toward this line // need to move the point toward this line
let h_intercept = line.x(m.into()); let h_intercept = line.x(m);
break Line2d::new(Vec2::new(h.into(), m.into()), Vec2::new(h_intercept.into(), m.into())); break Line2d::new(Vec2::new(h, m), Vec2::new(h_intercept, m));
} }
} }
}; };
trace!("active segment: {:?}", active_segment); trace!("active segment: {:?}", active_segment);
// Find some m(h) on the active_segment such that sum(h) = h + m(h) = target_hm // Find some m(h) on the active_segment such that sum(h) = h + m(h) = target_hm
let sum_h = active_segment + Line2d::new(Vec2::new(0.0, 0.0), Vec2::new(1.0, 1.0)); let sum_h = active_segment + Line2d::new(Vec2::zero(), Vec2::unit());
trace!("sum_h: {:?}", sum_h); trace!("sum_h: {:?}", sum_h);
let new_h = if sum_h.to().y() != sum_h.from().y() { let new_h = if sum_h.to().y() != sum_h.from().y() {
sum_h.move_toward_y_unclamped(h.into(), target_hm.into()) sum_h.move_toward_y_unclamped(h, target_hm)
} else { } else {
// avoid a division-by-zero. // avoid a division-by-zero.
// We could be anywhere along this line, but we prefer the endpoint // We could be anywhere along this line, but we prefer the endpoint

50
src/real.rs Normal file
View File

@@ -0,0 +1,50 @@
use std::ops::AddAssign;
use decorum::cmp::IntrinsicOrd;
/// This exists to allow configuration over # of bits (f32 v.s. f64) as well as
/// constraints.
pub trait Real: decorum::Real + IntrinsicOrd + AddAssign {
fn to_f32(&self) -> f32 {
self.to_f64() as _
}
fn to_f64(&self) -> f64 {
self.to_f32() as _
}
}
impl Real for f32 {
fn to_f32(&self) -> f32 {
*self
}
}
impl Real for f64 {
fn to_f64(&self) -> f64 {
*self
}
}
impl Real for decorum::R32 {
fn to_f32(&self) -> f32 {
self.into_inner()
}
}
impl Real for decorum::R64 {
fn to_f64(&self) -> f64 {
self.into_inner()
}
}
impl Real for decorum::N32 {
fn to_f32(&self) -> f32 {
self.into_inner()
}
}
impl Real for decorum::N64 {
fn to_f64(&self) -> f64 {
self.into_inner()
}
}

View File

@@ -1,5 +1,5 @@
use crate::geom::{Index, Meters, Vec2, Vec3, Vec3u}; use crate::geom::{Index, Meters, Vec2, Vec3, Vec3u};
use crate::{flt::{Flt, Real}, Material as _, MaterialExt as _}; use crate::{Material as _, MaterialExt as _};
use crate::mat; use crate::mat;
use crate::sim::{Cell, GenericSim, StaticSim}; use crate::sim::{Cell, GenericSim, StaticSim};
use crate::meas::AbstractMeasurement; use crate::meas::AbstractMeasurement;
@@ -22,7 +22,7 @@ use y4m;
/// Accept a value from (-\inf, \inf) and return a value in (-1, 1). /// 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.
/// 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.
fn scale_signed(x: Flt, typical: Flt) -> Flt { fn scale_signed(x: f32, typical: f32) -> f32 {
if x >= 0.0 { if x >= 0.0 {
scale_unsigned(x, typical) scale_unsigned(x, typical)
} else { } else {
@@ -32,29 +32,26 @@ fn scale_signed(x: Flt, typical: Flt) -> Flt {
/// Accept a value from [0, \inf) and return a value in [0, 1). /// 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. /// If the input is equal to `typical`, it will be mapped to 0.5.
fn scale_unsigned(x: Flt, typical: Flt) -> Flt { fn scale_unsigned(x: f32, typical: f32) -> f32 {
// f(0) => 0 // f(0) => 0
// f(1) => 0.5 // f(1) => 0.5
// f(\inf) => 1 // f(\inf) => 1
// f(x) = 1 - 1/(x+1) // f(x) = 1 - 1/(x+1)
let x = Real::from_inner(x); 1.0 - 1.0/(x/typical + 1.0)
let typ = Real::from_inner(typical);
let y = Real::from_inner(1.0) - Real::from_inner(1.0)/(x/typ + 1.0);
y.into()
} }
fn scale_signed_to_u8(x: Flt, typ: Flt) -> u8 { fn scale_signed_to_u8(x: f32, typ: f32) -> u8 {
let norm = 128.0 + 128.0*scale_signed(x, typ); let norm = 128.0 + 128.0*scale_signed(x, typ);
norm as _ norm as _
} }
fn scale_unsigned_to_u8(x: Flt, typ: Flt) -> u8 { fn scale_unsigned_to_u8(x: f32, typ: f32) -> u8 {
let norm = 256.0*scale_unsigned(x, typ); let norm = 256.0*scale_unsigned(x, typ);
norm as _ norm as _
} }
/// Scale a vector to have magnitude between [0, 1). /// Scale a vector to have magnitude between [0, 1).
fn scale_vector(x: Vec2, typical_mag: Flt) -> Vec2 { fn scale_vector(x: Vec2<f32>, typical_mag: f32) -> Vec2<f32> {
let new_mag = scale_unsigned(x.mag(), typical_mag); let new_mag = scale_unsigned(x.mag(), typical_mag);
x.with_mag(new_mag) x.with_mag(new_mag)
} }
@@ -103,7 +100,7 @@ impl FieldDisplayMode {
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct RenderConfig { pub struct RenderConfig {
mode: FieldDisplayMode, mode: FieldDisplayMode,
scale: Flt, scale: f32,
} }
impl Default for RenderConfig { impl Default for RenderConfig {
@@ -155,7 +152,7 @@ impl<'a, S: GenericSim> RenderSteps<'a, S> {
trace!("rendering at {}x{} with z={}", width, height, z); trace!("rendering at {}x{} with z={}", width, height, z);
let mut me = Self::new(state, measurements, width, height, z); let mut me = Self::new(state, measurements, width, height, z);
me.render_scalar_field(10.0, false, 2, |cell| { me.render_scalar_field(10.0, false, 2, |cell| {
cell.mat().conductivity().mag() + if cell.mat().is_vacuum() { cell.mat().conductivity().mag() as f32 + if cell.mat().is_vacuum() {
0.0 0.0
} else { } else {
5.0 5.0
@@ -191,49 +188,49 @@ impl<'a, S: GenericSim> RenderSteps<'a, S> {
} }
fn get_at_px(&self, x_px: u32, y_px: u32) -> Cell { fn get_at_px(&self, x_px: u32, y_px: u32) -> Cell {
let x_prop = x_px as Flt / self.im.width() as Flt; let x_prop = x_px as f32 / self.im.width() as f32;
let x_m = x_prop * (self.sim.width() as Flt * self.sim.feature_size()); let x_m = x_prop * (self.sim.width() as f32 * self.sim.feature_size() as f32);
let y_prop = y_px as Flt / self.im.height() as Flt; let y_prop = y_px as f32 / self.im.height() as f32;
let y_m = y_prop * (self.sim.height() as Flt * self.sim.feature_size()); let y_m = y_prop * (self.sim.height() as f32 * self.sim.feature_size() as f32);
let z_m = self.z as Flt * self.sim.feature_size(); let z_m = self.z as f32 * self.sim.feature_size() as f32;
self.sim.sample(Meters(Vec3::new(x_m, y_m, z_m))) self.sim.sample(Meters(Vec3::new(x_m as _, y_m as _, z_m as _)))
} }
////////////// Ex/Ey/Bz configuration //////////// ////////////// Ex/Ey/Bz configuration ////////////
fn render_b_z_field(&mut self, scale: Flt) { fn render_b_z_field(&mut self, scale: f32) {
self.render_scalar_field(1.0e-4 * scale, true, 1, |cell| cell.b().z()); self.render_scalar_field(1.0e-4 * scale, true, 1, |cell| cell.b().z() as f32);
} }
fn render_e_xy_field(&mut self, scale: Flt) { fn render_e_xy_field(&mut self, scale: f32) {
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 100.0 * scale, |cell| cell.e().xy()); self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 100.0 * scale, |cell| cell.e().xy().to_f32());
// current // current
self.render_vector_field(Rgb([0x00, 0xa0, 0x30]), 1.0e-12 * scale, |cell| { self.render_vector_field(Rgb([0x00, 0xa0, 0x30]), 1.0e-12 * scale, |cell| {
cell.e().elem_mul(cell.mat().conductivity()).xy() cell.e().elem_mul(cell.mat().conductivity()).xy().to_f32()
}); });
} }
////////////// Magnitude configuration ///////////// ////////////// Magnitude configuration /////////////
fn render_b(&mut self, scale: Flt) { fn render_b(&mut self, scale: f32) {
self.render_scalar_field(1.0e-3 * scale, false, 1, |cell| cell.b().mag()); self.render_scalar_field(1.0e-3 * scale, false, 1, |cell| cell.b().mag() as f32);
} }
fn render_current(&mut self, scale: Flt) { fn render_current(&mut self, scale: f32) {
self.render_scalar_field(1.0e1 * scale, false, 0, |cell| { self.render_scalar_field(1.0e1 * scale, false, 0, |cell| {
cell.e().elem_mul(cell.mat().conductivity()).mag() cell.e().elem_mul(cell.mat().conductivity()).mag() as f32
}); });
} }
////////////// Bx/By/Ez configuration //////////// ////////////// Bx/By/Ez configuration ////////////
fn render_e_z_field(&mut self, scale: Flt) { fn render_e_z_field(&mut self, scale: f32) {
self.render_scalar_field(1e4 * scale, true, 1, |cell| cell.e().z()); self.render_scalar_field(1e4 * scale, true, 1, |cell| cell.e().z() as f32);
} }
fn render_b_xy_field(&mut self, scale: Flt) { 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()); self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 1.0e-9 * scale, |cell| cell.b().xy().to_f32());
} }
fn render_m(&mut self, scale: Flt) { fn render_m(&mut self, scale: f32) {
self.render_scalar_field(1.0e5 * scale, false, 1, |cell| cell.mat().m().mag()); self.render_scalar_field(1.0e5 * scale, false, 1, |cell| cell.mat().m().mag() as f32);
self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 1.0e5 * scale, |cell| cell.mat().m().xy()); self.render_vector_field(Rgb([0xff, 0xff, 0xff]), 1.0e5 * scale, |cell| cell.mat().m().xy().to_f32());
} }
fn render_vector_field<F: Fn(&Cell<mat::Static>) -> Vec2>(&mut self, color: Rgb<u8>, typical: Flt, measure: F) { fn render_vector_field<F: Fn(&Cell<mat::Static>) -> Vec2<f32>>(&mut self, color: Rgb<u8>, typical: f32, measure: F) {
let w = self.im.width(); let w = self.im.width();
let h = self.im.height(); let h = self.im.height();
let vec_spacing = 10; let vec_spacing = 10;
@@ -242,13 +239,13 @@ impl<'a, S: GenericSim> RenderSteps<'a, S> {
let vec = self.field_vector(x, y, vec_spacing, &measure); let vec = self.field_vector(x, y, vec_spacing, &measure);
let norm_vec = scale_vector(vec, typical); let norm_vec = scale_vector(vec, typical);
let alpha = 0.7*scale_unsigned(vec.mag_sq(), typical * 5.0); let alpha = 0.7*scale_unsigned(vec.mag_sq(), typical * 5.0);
let vec = norm_vec * (vec_spacing as Flt); let vec = norm_vec * (vec_spacing as f32);
let center = Vec2::new(x as _, y as _) + Vec2::new(vec_spacing as _, vec_spacing as _)*0.5; let center = Vec2::new(x as f32, y as f32) + Vec2::new(vec_spacing as f32, vec_spacing as f32)*0.5;
self.im.draw_field_arrow(center, vec, color, alpha as f32); self.im.draw_field_arrow(center, vec, color, alpha as f32);
} }
} }
} }
fn render_scalar_field<F: Fn(&Cell<mat::Static>) -> Flt + Sync>(&mut self, typical: Flt, signed: bool, slot: u32, measure: F) { fn render_scalar_field<F: Fn(&Cell<mat::Static>) -> f32 + Sync>(&mut self, typical: f32, signed: bool, slot: u32, measure: F) {
// XXX: get_at_px borrows self, so we need to clone the image to operate on it mutably. // 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 mut im = self.im.clone();
let w = im.width(); let w = im.width();
@@ -294,7 +291,7 @@ impl<'a, S: GenericSim> RenderSteps<'a, S> {
} }
} }
fn field_vector<F: Fn(&Cell<mat::Static>) -> Vec2>(&self, xidx: u32, yidx: u32, size: u32, measure: &F) -> Vec2 { fn field_vector<F: Fn(&Cell<mat::Static>) -> Vec2<f32>>(&self, xidx: u32, yidx: u32, size: u32, measure: &F) -> Vec2<f32> {
let mut field = Vec2::default(); let mut field = Vec2::default();
let w = self.im.width(); let w = self.im.width();
let h = self.im.height(); let h = self.im.height();
@@ -313,17 +310,17 @@ impl<'a, S: GenericSim> RenderSteps<'a, S> {
// avoid division by zero // avoid division by zero
Vec2::new(0.0, 0.0) Vec2::new(0.0, 0.0)
} else { } else {
field * (1.0 / ((xw*yw) as Flt)) field * (1.0 / ((xw*yw) as f32))
} }
} }
} }
trait ImageRenderExt { trait ImageRenderExt {
fn draw_field_arrow(&mut self, center: Vec2, rel: Vec2, color: Rgb<u8>, alpha: f32); fn draw_field_arrow(&mut self, center: Vec2<f32>, rel: Vec2<f32>, color: Rgb<u8>, alpha: f32);
} }
impl ImageRenderExt for RgbImage { impl ImageRenderExt for RgbImage {
fn draw_field_arrow(&mut self, center: Vec2, rel: Vec2, color: Rgb<u8>, alpha: f32) { fn draw_field_arrow(&mut self, center: Vec2<f32>, rel: Vec2<f32>, color: Rgb<u8>, alpha: f32) {
let start = (center - rel * 0.5).round(); let start = (center - rel * 0.5).round();
let end = (center + rel * 0.5).round(); let end = (center + rel * 0.5).round();
let i_start = (start.x().round() as _, start.y().round() as _); let i_start = (start.x().round() as _, start.y().round() as _);
@@ -569,9 +566,9 @@ impl<S: GenericSim> Renderer<S> for PlotlyRenderer {
//let g = scale_unsigned_to_u8(mat, 10.0); //let g = scale_unsigned_to_u8(mat, 10.0);
//let r = scale_unsigned_to_u8(cell.mat().m().mag(), 100.0); //let r = scale_unsigned_to_u8(cell.mat().m().mag(), 100.0);
//let b = scale_unsigned_to_u8(cell.e().mag(), 1e2); //let b = scale_unsigned_to_u8(cell.e().mag(), 1e2);
let r = scale_unsigned_to_u8(cell.mat().m().mag(), 100.0); let r = scale_unsigned_to_u8(cell.mat().m().mag() as f32, 100.0);
let g = scale_unsigned_to_u8(cell.e().mag(), 1e2); let g = scale_unsigned_to_u8(cell.e().mag() as f32, 1e2);
let b = scale_unsigned_to_u8(mat, 10.0); let b = scale_unsigned_to_u8(mat as f32, 10.0);
let alpha = 1.0; let alpha = 1.0;
colors.push(Rgba::new(r, g, b, alpha)); colors.push(Rgba::new(r, g, b, alpha));
} }