use std::{cmp, io, path::Path};
use gnuplot::Figure as GnuplotFigure;
use num_traits::AsPrimitive;
use crate::plot::Plot;
pub struct Figure {
figure: GnuplotFigure,
columns: usize,
count: usize,
plot_width_px: f32,
plot_height_px: f32,
}
pub const DPI: f32 = 72.0;
pub const DEFAULT_WIDTH_PX: f32 = 323.0;
pub const DEFAULT_HEIGHT_PX: f32 = 150.0;
impl Figure {
pub fn new() -> Self {
Figure {
figure: GnuplotFigure::new(),
columns: 1,
count: 0,
plot_width_px: DEFAULT_WIDTH_PX,
plot_height_px: DEFAULT_HEIGHT_PX,
}
}
pub fn set_columns(&mut self, columns: impl AsPrimitive<usize>) {
assert!(columns.as_() > 0, "Invalid number of columns in figure");
self.columns = columns.as_();
}
pub fn with_columns(mut self, columns: impl AsPrimitive<usize>) -> Self {
self.set_columns(columns);
self
}
pub fn set_plot_width(&mut self, plot_width_px: impl AsPrimitive<f32>) {
self.plot_width_px = plot_width_px.as_();
}
pub fn with_plot_width(mut self, plot_width_px: impl AsPrimitive<f32>) -> Self {
self.set_plot_width(plot_width_px);
self
}
pub fn set_plot_height(&mut self, plot_height_px: impl AsPrimitive<f32>) {
self.plot_height_px = plot_height_px.as_();
}
pub fn with_plot_height(mut self, plot_height_px: impl AsPrimitive<f32>) -> Self {
self.set_plot_height(plot_height_px);
self
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn add(&mut self, mut plot: impl Plot) {
if plot.is_empty() {
return;
}
self.count += 1;
self.figure.set_multiplot_layout(
(self.count as f32 / self.columns as f32).ceil() as usize,
*cmp::min(&self.count, &self.columns),
);
plot.configure(self.figure.axes2d());
}
pub fn save<P>(&mut self, path: P) -> io::Result<()>
where
P: AsRef<Path>,
{
if self.is_empty() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Could not save figure with no plots",
));
}
let columns = cmp::min(&self.count, &self.columns);
let rows = (self.count as f32 / self.columns as f32).ceil() as u32;
let plot_width_in = self.plot_width_px / DPI;
let plot_height_in = self.plot_height_px / DPI;
let width = *columns as f32 * plot_width_in;
let height = rows as f32 * plot_height_in;
match self.figure.save_to_pdf(path, width, height) {
Ok(_) => Ok(()),
Err(_) => Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Could not save figure",
)),
}
}
}
impl Default for Figure {
fn default() -> Self {
Figure::new()
}
}