use piston_window;
use piston_window::*;
use std::cmp::min;
use plotbuilder::*;
use std::f64;
use std;
pub struct Plot {
}
fn point2plot(pt: f64, min: f64, max: f64, length: f64, space: f64) -> f64 {
((pt - min) / (max - min)) * length + space
}
fn get_max(user_max: Option<f64>, values: &Vec<f64>) -> f64 {
if let Some(max) = user_max {
max
} else {
let mut max = *values.first().unwrap();
for val in values {
if *val > max {
max = *val;
}
}
max
}
}
fn get_min(user_min: Option<f64>, values: &Vec<f64>) -> f64 {
if let Some(min) = user_min {
min
} else {
let mut min = *values.first().unwrap();
for val in values {
if *val < min {
min = *val;
}
}
min
}
}
fn draw_borders(bordercol: [f32; 4], bgcol: [f32; 4], space: f64, m: f64, transform: [[f64; 3]; 2], g: &mut piston_window::G2d) {
clear(bordercol, g);
rectangle([0.0, 0.0, 1.0, 1.0],
[space - 2.0, space - 2.0, m + 4.0, m + 4.0], transform,
g);
rectangle(bgcol,
[space - 1.0, space - 1.0, m + 2.0, m + 2.0], transform,
g);
}
fn set_xy(xy: &Vec<(f64, f64)>, x_vector: &mut Vec<Vec<f64>>, y_vector: &mut Vec<Vec<f64>>) {
x_vector.push(Vec::new());
y_vector.push(Vec::new());
let last_index = x_vector.len() - 1;
for &(x, y) in xy {
x_vector[last_index].push(x);
y_vector[last_index].push(y);
}
}
fn draw_plots(window: &mut PistonWindow, xs: &Vec<Vec<f64>>, ys: &Vec<Vec<f64>>, colors: &Vec<[f32; 4]>, plot_bounds: [f64; 4]) {
let bordercol = [0.95, 0.95, 0.95, 1.0];
let bgcol = [1.0, 1.0, 1.0, 1.0];
let margin = 0.05;
let invmargin = 1.0 - 2.0 * margin;
let w = window.size().width;
let h = window.size().height;
let m = min(w, h) as f64;
let space = m * margin;
let m = m * invmargin;
let x_max = plot_bounds[0];
let y_max = plot_bounds[1];
let x_min = plot_bounds[2];
let y_min = plot_bounds[3];
while let Some(event) = window.next() {
window.draw_2d(&event, |c, g| {
draw_borders(bordercol, bgcol, space, m, c.transform, g);
});
for i in 0..colors.len() {
let color = colors[i];
let xt: Vec<f64> = xs[i].iter().map(|x| point2plot(*x, x_min, x_max, m, space)).collect();
let yt: Vec<f64> = ys[i]
.iter()
.map(|y| (2.0 * space + m) - point2plot(*y, y_min, y_max, m, space))
.collect();
window.draw_2d(&event, |c, g| {
let len = xs[i].len();
for i in 0..len - 1 {
let (xa, ya) = (xt[i + 0], yt[i + 0]);
let (xb, yb) = (xt[i + 1], yt[i + 1]);
line([color[0], color[1], color[2], color[3]],
1.0,
[xa, ya, xb, yb],
c.transform,
g);
}
});
}
}
}
fn get_plot_bounds(plot_builder: &PlotBuilder2D, xs: &Vec<Vec<f64>>, ys: &Vec<Vec<f64>>) -> [f64; 4] {
let mut max_xs: Vec<f64> = Vec::new();
let mut max_ys: Vec<f64> = Vec::new();
let mut min_xs: Vec<f64> = Vec::new();
let mut min_ys: Vec<f64> = Vec::new();
for i in 0..xs.len() {
max_xs.push(get_max(plot_builder.max_x, &xs[i]));
max_ys.push(get_max(plot_builder.max_y, &ys[i]));
min_xs.push(get_min(plot_builder.min_x, &xs[i]));
min_ys.push(get_min(plot_builder.min_y, &ys[i]));
}
let plot_bounds: [f64; 4] = [ max_xs.iter().cloned().fold(0. / 0., f64::max),
max_ys.iter().cloned().fold(0. / 0., f64::max),
min_xs.iter().cloned().fold(0. / 0., f64::min),
min_ys.iter().cloned().fold(0. / 0., f64::min)];
plot_bounds
}
impl Plot {
pub fn new2d(plot_builder: PlotBuilder2D) {
let mut window: PistonWindow = WindowSettings::new("2D plot", [720, 720])
.opengl(piston_window::OpenGL::V3_2)
.samples(4)
.exit_on_esc(true)
.build()
.unwrap();
let mut plot_builder = plot_builder;
window.set_ups(60);
let mut pvs = Vec::new();
std::mem::swap(&mut plot_builder.pvs, &mut pvs);
let mut colors: Vec<[f32; 4]> = Vec::new();
let mut x_points: Vec<Vec<f64>> = Vec::new();
let mut y_points: Vec<Vec<f64>> = Vec::new();
for pv in pvs.drain(..) {
match pv {
PlotVals2D::XyColor(ref col, ref xy) => {
set_xy(xy, &mut x_points, &mut y_points);
colors.push(col.clone());
}
_ => (),
}
}
let plot_bounds: [f64; 4] = get_plot_bounds(&plot_builder, &x_points, &y_points);
draw_plots(&mut window, &x_points, &y_points, &colors, plot_bounds);
}
}