pub mod bar_plot;
pub mod box_plot;
pub mod figure;
pub mod line_plot;
pub mod scatter_plot;
use std::fmt::Display;
use gnuplot::{AutoOption, Axes2D};
use num_traits::AsPrimitive;
use crate::fmt::MEMORY_UNITS;
const COLORS: &[&str] = &[
"#c4342b", "#0071ad", "#71ad00", "#554ec9", "#f7790d", "#e0ca3c", "#47a8bd", "#006a67",
"#f29f05",
];
const DEFAULT_FONT_FAMILY: &str = "Arial";
const DEFAULT_FONT_SIZE: f64 = 16.0;
const TIME_UNITS: &[&str] = &[
"ms", "s", "mins", "hrs", "days",
];
pub trait Plot {
fn is_empty(&self) -> bool;
fn set_font_type<T>(&mut self, font_type: T)
where
T: AsRef<str>;
fn with_font_type<T>(self, font_type: T) -> Self
where
T: AsRef<str>;
fn set_font_size(&mut self, font_size: impl AsPrimitive<f64>);
fn with_font_size(self, font_size: impl AsPrimitive<f64>) -> Self;
fn set_title<T>(&mut self, title: T)
where
T: Display;
fn with_title<T>(self, title: T) -> Self
where
T: Display;
fn set_x_label<T>(&mut self, label: T)
where
T: Display;
fn with_x_label<T>(self, label: T) -> Self
where
T: Display;
fn set_y_label<T>(&mut self, label: T)
where
T: Display;
fn with_y_label<T>(self, label: T) -> Self
where
T: Display;
fn configure(&mut self, axes: &mut Axes2D);
}
#[derive(Clone, Copy)]
pub enum AxisFormat {
Log(f64),
Memory,
Time,
Number,
}
#[derive(Clone, Copy)]
pub enum LegendPosition {
TopRight,
TopLeft,
BottomRight,
BottomLeft,
}
trait Scaler {
fn new(max: impl AsPrimitive<u64>) -> Self
where
Self: Sized;
fn scale(&self, size: f64) -> f64;
fn apply_unit(&self, label: &str) -> String;
}
struct NoScaler;
struct MemoryScaler {
unit: Option<&'static str>,
denominator: f64,
}
struct TimeScaler {
unit: Option<&'static str>,
denominator: f64,
}
struct NumberScaler {
denominator: f64,
}
impl Scaler for NoScaler {
fn new(_: impl AsPrimitive<u64>) -> Self {
NoScaler
}
fn scale(&self, size: f64) -> f64 {
size
}
fn apply_unit(&self, label: &str) -> String {
label.to_owned()
}
}
impl Scaler for MemoryScaler {
fn new(max_size: impl AsPrimitive<u64>) -> Self {
let mut max_size = max_size.as_();
let mut count: usize = 0;
let mut denominator: f64 = 1.0;
while max_size / 1024 > 0 {
denominator *= 1024.0;
max_size /= 1024;
count += 1;
}
MemoryScaler {
unit: Some(MEMORY_UNITS[count]),
denominator,
}
}
fn scale(&self, size: f64) -> f64 {
size / self.denominator
}
fn apply_unit(&self, label: &str) -> String {
match self.unit {
Some(unit) => format!("{label} ({unit})"),
None => label.to_string(),
}
}
}
impl Scaler for TimeScaler {
fn new(max_time: impl AsPrimitive<u64>) -> Self {
let mut max_time = max_time.as_();
let mut count: usize = 0;
let divisors: &[u64] = &[1000, 60, 60, 24];
let mut denominator: f64 = 1.0;
for divisor in divisors {
if max_time / divisor == 0 {
break;
}
denominator *= *divisor as f64;
max_time /= divisor;
count += 1;
}
TimeScaler {
unit: Some(TIME_UNITS[count]),
denominator,
}
}
fn scale(&self, time: f64) -> f64 {
time / self.denominator
}
fn apply_unit(&self, label: &str) -> String {
match self.unit {
Some(unit) => format!("{label} ({unit})"),
None => label.to_string(),
}
}
}
impl Scaler for NumberScaler {
fn new(max_number: impl AsPrimitive<u64>) -> Self {
let mut max_number = max_number.as_();
let mut denominator = 1.0f64;
if max_number >= 10_000 {
while max_number >= 10 {
denominator *= 10.0;
max_number /= 10;
}
}
NumberScaler {
denominator,
}
}
fn scale(&self, number: f64) -> f64 {
number / self.denominator
}
fn apply_unit(&self, label: &str) -> String {
if self.denominator > 1.0 {
format!("{label} (x10^{})", self.denominator.log10())
} else {
label.to_owned()
}
}
}
fn init_scaler(format: Option<AxisFormat>, max_value: impl AsPrimitive<u64>) -> Box<dyn Scaler> {
let no_scaler = Box::new(NoScaler::new(max_value));
let Some(format) = format else {
return no_scaler;
};
match format {
AxisFormat::Memory => Box::new(MemoryScaler::new(max_value)),
AxisFormat::Time => Box::new(TimeScaler::new(max_value)),
AxisFormat::Number => Box::new(NumberScaler::new(max_value)),
_ => no_scaler,
}
}
fn auto_option(value: Option<f64>, scaler: &dyn Scaler) -> AutoOption<f64> {
match value {
Some(value) => AutoOption::Fix(scaler.scale(value)),
None => AutoOption::Auto,
}
}
pub use crate::plot::figure::Figure;