extern crate image;
use coulomb::pairwise::*;
use image::RgbaImage;
type Vector3 = nalgebra::Vector3<f64>;
const ASPECT_RATIO: f64 = 1.5;
const WIDTH: u32 = 400;
const HEIGHT: u32 = (WIDTH as f64 / ASPECT_RATIO) as u32;
const XMAX: f64 = 15.0;
const YMAX: f64 = XMAX / ASPECT_RATIO;
#[derive(Clone)]
struct Particle {
charge: f64,
dipole: Vector3,
pos: Vector3,
radius: f64,
}
impl Particle {
pub fn potential<T: MultipolePotential>(&self, pos: &Vector3, scheme: &T) -> Option<f64> {
let r = pos - self.pos;
let norm = r.norm();
(norm > self.radius).then(|| {
scheme.ion_potential(self.charge, norm) + scheme.dipole_potential(self.dipole, r)
})
}
}
fn main() {
let particles = [
Particle {
charge: -3.0,
dipole: Vector3::zeros(),
pos: Vector3::new(XMAX / 2.0 - 3.0, YMAX / 2.0 - 1.0, 0.0),
radius: 1.5,
},
Particle {
charge: 0.0,
dipole: Vector3::new(-5.0, 5.0, 0.0),
pos: Vector3::new(XMAX / 2.0 + 3.0, YMAX / 2.0 + 1.0, 0.0),
radius: 1.5,
},
];
let mut data = Vec::with_capacity((WIDTH * HEIGHT) as usize); let scheme = Plain::default();
for w in 0..WIDTH {
for h in 0..HEIGHT {
let pos = Vector3::new(
w as f64 * XMAX / WIDTH as f64,
h as f64 * YMAX / HEIGHT as f64,
0.0,
);
let pot: Vec<f64> = particles
.iter()
.filter_map(|p| p.potential(&pos, &scheme))
.collect();
if pot.len() == particles.len() {
data.push((w, h, pot.iter().sum::<f64>()));
}
}
}
let max = data
.iter()
.map(|(_, _, p)| p.abs())
.max_by(f64::total_cmp)
.unwrap();
let mut img = RgbaImage::new(WIDTH, HEIGHT);
let gradient = colorgrad::rd_bu();
for &(w, h, p) in &data {
let color = gradient.at(remap(p, -max, max, 0.0, 1.0)).to_rgba8();
img.put_pixel(w, h, color.into());
}
img.save("potential.png").unwrap();
}
fn remap(t: f64, a: f64, b: f64, c: f64, d: f64) -> f64 {
(t - a) * ((d - c) / (b - a)) + c
}