use super::*;
use std::path::Path;
pub trait GraphMaker {
fn get_buffer<'a>(&'a self) -> &'a String;
}
pub struct Plot {
pub option_hide_bottom_border: bool,
pub option_hide_left_border: bool,
pub option_hide_right_border: bool,
pub option_hide_top_border: bool,
pub font_size_labels: f64,
pub font_size_legend: f64,
pub font_size_x_tick: f64,
pub font_size_y_tick: f64,
pub(crate) buffer: String,
}
impl Plot {
pub fn new() -> Self {
Plot {
option_hide_bottom_border: true,
option_hide_left_border: true,
option_hide_right_border: true,
option_hide_top_border: true,
font_size_labels: 0.0,
font_size_legend: 0.0,
font_size_x_tick: 0.0,
font_size_y_tick: 0.0,
buffer: String::new(),
}
}
pub fn add(&mut self, graph: &dyn GraphMaker) {
self.buffer.push_str(graph.get_buffer());
}
pub fn save(&self, output_dir: &str, filename_key: &str, filename_ext: &str) -> std::io::Result<String> {
let ext = filename_ext.replace(".", "");
let filename_py = format!("{}.py", filename_key);
let filename_fig = format!("{}.{}", filename_key, ext);
let filepath_fig = Path::new(output_dir).join(filename_fig);
let path = filepath_fig.to_string_lossy();
let commands = format!(
"{}\nfn='{}'\nplt.savefig(fn, bbox_inches='tight', bbox_extra_artists=EXTRA_ARTISTS)\nprint('figure {} created')\n",
self.buffer,
path, path,
);
call_python3(&commands, output_dir, &filename_py)
}
pub fn subplot(&mut self, row: i32, col: i32, index: i32) {
self.buffer
.push_str(&format!("plt.subplot({},{},{})\n", row, col, index));
}
pub fn subplot_horizontal_gap(&mut self, value: f64) {
self.buffer
.push_str(&format!("plt.subplots_adjust(hspace={})\n", value));
}
pub fn subplot_vertical_gap(&mut self, value: f64) {
self.buffer
.push_str(&format!("plt.subplots_adjust(vspace={})\n", value));
}
pub fn axes_equal(&mut self) {
self.buffer.push_str("plt.axis('equal')\n");
}
pub fn axes_off(&mut self) {
self.buffer.push_str("plt.axis('off')\n");
}
pub fn axes_range(&mut self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) {
self.buffer
.push_str(&format!("plt.axis([{},{},{},{}])\n", xmin, xmax, ymin, ymax));
}
pub fn axes_range_vec(&mut self, lims: &[f64]) {
self.buffer.push_str(&format!(
"plt.axis([{},{},{},{}])\n",
lims[0], lims[1], lims[2], lims[3]
));
}
pub fn axes_xmin(&mut self, xmin: f64) {
self.buffer.push_str(&format!(
"plt.axis([{},plt.axis()[1],plt.axis()[2],plt.axis()[3]])\n",
xmin
));
}
pub fn axes_xmax(&mut self, xmax: f64) {
self.buffer.push_str(&format!(
"plt.axis([plt.axis()[0],{},plt.axis()[2],plt.axis()[3]])\n",
xmax
));
}
pub fn axes_ymin(&mut self, ymin: f64) {
self.buffer.push_str(&format!(
"plt.axis([plt.axis()[0],plt.axis()[1],{},plt.axis()[3]])\n",
ymin
));
}
pub fn axes_ymax(&mut self, ymax: f64) {
self.buffer.push_str(&format!(
"plt.axis([plt.axis()[0],plt.axis()[1],plt.axis()[2],{}])\n",
ymax
));
}
pub fn axes_xrange(&mut self, xmin: f64, xmax: f64) {
self.buffer
.push_str(&format!("plt.axis([{},{},plt.axis()[2],plt.axis()[3]])\n", xmin, xmax));
}
pub fn axes_yrange(&mut self, ymin: f64, ymax: f64) {
self.buffer
.push_str(&format!("plt.axis([plt.axis()[0],plt.axis()[1],{},{}])\n", ymin, ymax));
}
pub fn grid_and_labels(&mut self, xlabel: &str, ylabel: &str) {
self.buffer.push_str(&format!(
"plt.grid(linestyle='--',color='grey',zorder=-1000)\nplt.xlabel(r'{}')\nplt.ylabel(r'{}')\n",
xlabel, ylabel,
));
}
pub fn clear_current_figure(&mut self) {
self.buffer.push_str("plt.clf()\n");
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn new_plot_works() -> Result<(), Box<dyn std::error::Error>> {
let plot = Plot::new();
assert_eq!(plot.buffer.len(), 0);
plot.save("/tmp/plotpy", "test", "svg")?;
let svg = fs::read_to_string("/tmp/plotpy/test.svg")?;
let lines = svg.lines().collect::<Vec<_>>();
assert_eq!(lines.len(), 33);
Ok(())
}
#[test]
fn subplot_functions_work() {
let mut plot = Plot::new();
plot.subplot(2, 2, 0);
plot.subplot_horizontal_gap(0.1);
plot.subplot_vertical_gap(0.2);
let correct = "plt.subplot(2,2,0)
plt.subplots_adjust(hspace=0.1)
plt.subplots_adjust(vspace=0.2)
";
assert_eq!(plot.buffer, correct);
}
#[test]
fn axes_functions_work() {
let mut plot = Plot::new();
plot.axes_equal();
plot.axes_off();
plot.axes_range(-1.0, 1.0, -1.0, 1.0);
let correct = "plt.axis('equal')
plt.axis('off')
plt.axis([-1,1,-1,1])
";
assert_eq!(plot.buffer, correct);
}
}