use crate::error_types::*;
use self::AxesVariant::*;
use crate::axes2d::*;
use crate::axes3d::*;
use crate::axes_common::*;
use crate::options::GnuplotVersion;
use crate::writer::Writer;
use std::cell::RefCell;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::process::{Child, Command, Stdio};
use std::str;
enum AxesVariant
{
Axes2DType(Axes2D),
Axes3DType(Axes3D),
NewPage,
}
impl AxesVariant
{
fn write_out(&self, writer: &mut Writer, version: GnuplotVersion)
{
match *self
{
Axes2DType(ref a) => a.write_out(writer, version),
Axes3DType(ref a) => a.write_out(writer, version),
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,
enhanced_text: bool,
output_file: String,
post_commands: String,
pre_commands: String,
gnuplot: RefCell<Option<Child>>,
version: Option<GnuplotVersion>,
}
impl Default for GnuplotVersion
{
fn default() -> GnuplotVersion
{
GnuplotVersion { major: 5, minor: 0 }
}
}
impl Figure
{
pub fn new() -> Figure
{
Figure {
axes: Vec::new(),
terminal: "".into(),
enhanced_text: true,
output_file: "".into(),
gnuplot: RefCell::new(None),
post_commands: "".into(),
pre_commands: "".into(),
version: None,
}
}
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_enhanced_text<'l>(&'l mut self, enhanced: bool) -> &'l mut Figure
{
self.enhanced_text = enhanced;
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 set_gnuplot_version(&mut self, version: Option<GnuplotVersion>) -> &mut Figure
{
self.version = version;
self
}
pub fn get_gnuplot_version(&self) -> GnuplotVersion
{
self.version.unwrap_or_default()
}
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) -> Result<&mut Figure, GnuplotInitError>
{
if self.axes.len() == 0
{
return Ok(self);
}
if self.version.is_none()
{
let output = Command::new("gnuplot").arg("--version").output()?;
if let Ok(version_string) = str::from_utf8(&output.stdout)
{
let parts: Vec<_> = version_string.split(|c| c == ' ' || c == '.').collect();
if parts.len() > 2 && parts[0] == "gnuplot"
{
if let (Ok(major), Ok(minor)) = (
i32::from_str_radix(parts[1], 10),
i32::from_str_radix(parts[2], 10),
)
{
self.version = Some(GnuplotVersion {
major: major,
minor: minor,
});
}
}
}
}
if self.gnuplot.borrow().is_none()
{
*self.gnuplot.borrow_mut() = Some(
Command::new("gnuplot")
.arg("-p")
.stdin(Stdio::piped())
.spawn()
.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();
});
Ok(self)
}
pub fn save_to_png(
&mut self, file_path: &str, width_px: u32, height_px: u32,
) -> Result<(), GnuplotInitError>
{
let former_term = self.terminal.clone();
let former_output_file = self.output_file.clone();
self.terminal = format!("pngcairo size {},{}", width_px, height_px);
self.output_file = file_path.into();
self.show()?.close();
self.terminal = former_term;
self.output_file = former_output_file;
Ok(())
}
pub fn save_to_svg(
&mut self, file_path: &str, width_px: u32, height_px: u32,
) -> Result<(), GnuplotInitError>
{
let former_term = self.terminal.clone();
let former_output_file = self.output_file.clone();
self.terminal = format!("svg size {},{}", width_px, height_px);
self.output_file = file_path.into();
self.show()?.close();
self.terminal = former_term;
self.output_file = former_output_file;
Ok(())
}
pub fn save_to_pdf(
&mut self, file_path: &str, width_in: u32, height_in: u32,
) -> Result<(), GnuplotInitError>
{
let former_term = self.terminal.clone();
let former_output_file = self.output_file.clone();
self.terminal = format!("pdfcairo size {},{}", width_in, height_in);
self.output_file = file_path.into();
self.show()?.close();
self.terminal = former_term;
self.output_file = former_output_file;
Ok(())
}
pub fn save_to_eps(
&mut self, file_path: &str, width_in: u32, height_in: u32,
) -> Result<(), GnuplotInitError>
{
let former_term = self.terminal.clone();
let former_output_file = self.output_file.clone();
self.terminal = format!("epscairo size {},{}", width_in, height_in);
self.output_file = file_path.into();
self.show()?.close();
self.terminal = former_term;
self.output_file = former_output_file;
Ok(())
}
pub fn save_to_canvas(
&mut self, file_path: &str, width_px: u32, height_px: u32,
) -> Result<(), GnuplotInitError>
{
let former_term = self.terminal.clone();
let former_output_file = self.output_file.clone();
self.terminal = format!("canvas size {},{}", width_px, height_px);
self.output_file = file_path.into();
self.show()?.close();
self.terminal = former_term;
self.output_file = former_output_file;
Ok(())
}
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 {}",
if self.enhanced_text
{
"enhanced"
}
else
{
"noenhanced"
}
);
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, self.get_gnuplot_version());
}
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().unwrap().close();
fs::read(file_path).unwrap();
fs::remove_dir_all(&tmp_path);
}