use self::AxesVariant::*;
use crate::axes2d::*;
use crate::axes3d::*;
use crate::axes_common::*;
use crate::writer::Writer;
use std::cell::RefCell;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::process::{Child, Command, Stdio};
enum AxesVariant
{
Axes2DType(Axes2D),
Axes3DType(Axes3D),
NewPage,
}
impl AxesVariant
{
fn write_out(&self, writer: &mut Writer)
{
match *self
{
Axes2DType(ref a) => a.write_out(writer),
Axes3DType(ref a) => a.write_out(writer),
NewPage =>
{
writeln!(writer, "unset multiplot");
writeln!(writer, "set multiplot");
}
}
}
fn get_common_data(&self) -> Option<&AxesCommonData>
{
match *self
{
Axes2DType(ref a) => Some(a.get_common_data()),
Axes3DType(ref a) => Some(a.get_common_data()),
NewPage => None,
}
}
}
pub struct Figure
{
axes: Vec<AxesVariant>,
terminal: String,
output_file: String,
post_commands: String,
pre_commands: String,
gnuplot: RefCell<Option<Child>>,
}
impl Figure
{
pub fn new() -> Figure
{
Figure {
axes: Vec::new(),
terminal: "".into(),
output_file: "".into(),
gnuplot: RefCell::new(None),
post_commands: "".into(),
pre_commands: "".into(),
}
}
pub fn set_terminal<'l>(&'l mut self, terminal: &str, output_file: &str) -> &'l mut Figure
{
self.terminal = terminal.into();
self.output_file = output_file.into();
self
}
pub fn set_post_commands<'l>(&'l mut self, post_commands: &str) -> &'l mut Figure
{
self.post_commands = post_commands.into();
self
}
pub fn set_pre_commands<'l>(&'l mut self, pre_commands: &str) -> &'l mut Figure
{
self.pre_commands = pre_commands.into();
self
}
pub fn axes2d(&mut self) -> &mut Axes2D
{
self.axes.push(Axes2DType(Axes2D::new()));
let l = self.axes.len();
match *&mut self.axes[l - 1]
{
Axes2DType(ref mut a) => a,
_ => unreachable!(),
}
}
pub fn axes3d(&mut self) -> &mut Axes3D
{
self.axes.push(Axes3DType(Axes3D::new()));
let l = self.axes.len();
match *&mut self.axes[l - 1]
{
Axes3DType(ref mut a) => a,
_ => unreachable!(),
}
}
pub fn new_page(&mut self) -> &mut Figure
{
self.axes.push(NewPage);
self
}
pub fn show(&mut self) -> &mut Figure
{
if self.axes.len() == 0
{
return self;
}
if self.gnuplot.borrow().is_none()
{
*self.gnuplot.borrow_mut() = Some(
Command::new("gnuplot")
.arg("-p")
.stdin(Stdio::piped())
.spawn()
.ok()
.expect("Couldn't spawn gnuplot. Make sure it is installed and available in PATH."),
);
}
self.gnuplot.borrow_mut().as_mut().map(|p| {
let stdin = p.stdin.as_mut().expect("No stdin!?");
self.echo(stdin);
stdin.flush();
});
self
}
pub fn close(&mut self) -> &mut Figure
{
if self.gnuplot.borrow().is_none()
{
return self;
}
{
let mut gnuplot = self.gnuplot.borrow_mut();
gnuplot.as_mut().map(|p| {
{
let stdin = p.stdin.as_mut().expect("No stdin!?");
writeln!(stdin, "quit");
}
p.wait();
});
*gnuplot = None;
}
self
}
pub fn clear_axes(&mut self) -> &mut Figure
{
self.axes.clear();
self
}
pub fn echo<'l, T: Writer>(&'l self, writer: &mut T) -> &'l Figure
{
let w = writer as &mut Writer;
writeln!(w, "{}", &self.pre_commands);
if self.axes.len() == 0
{
return self;
}
if self.terminal.len() > 0
{
writeln!(w, "set terminal {}", self.terminal);
}
if self.output_file.len() > 0
{
writeln!(w, "set output \"{}\"", self.output_file);
}
writeln!(w, "set termoption dashed");
writeln!(w, "set termoption enhanced");
if self.axes.len() > 1
{
writeln!(w, "set multiplot");
}
writeln!(w, "set tics front");
for e in self.axes.iter()
{
writeln!(w, "reset");
if let Some(c) = e.get_common_data()
{
c.grid_pos.map(|pos| {
let width = 1.0 / (c.grid_cols as f64);
let height = 1.0 / (c.grid_rows as f64);
let x = (pos % c.grid_cols) as f64 * width;
let y = 1.0 - (1.0 + (pos / c.grid_cols) as f64) * height;
writeln!(w, "set origin {:.12e},{:.12e}", x, y);
writeln!(w, "set size {:.12e},{:.12e}", width, height);
});
}
e.write_out(w);
}
if self.axes.len() > 1
{
writeln!(w, "unset multiplot");
}
writeln!(w, "{}", &self.post_commands);
self
}
pub fn echo_to_file<'l>(&'l self, filename: &str) -> &'l Figure
{
if self.axes.len() == 0
{
return self;
}
let mut file = BufWriter::new(File::create(filename).unwrap());
self.echo(&mut file);
file.flush();
self
}
}
#[test]
fn flush_test()
{
use std::fs;
use tempfile::TempDir;
let tmp_path = TempDir::new().unwrap().into_path();
let file_path = tmp_path.join("plot.png");
let mut fg = Figure::new();
fg.axes2d().boxes(0..5, 0..5, &[]);
fg.set_terminal("pngcairo", &*file_path.to_string_lossy());
fg.show().close();
fs::read(file_path).unwrap();
fs::remove_dir_all(&tmp_path);
}