#[derive(Clone, Debug, PartialEq)]
pub struct Axes {
pub xlim: (f64, f64),
pub ylim: (f64, f64),
pub xticks: Vec<f64>,
pub yticks: Vec<f64>,
pub xlabel: String,
pub ylabel: String,
pub title: String,
auto_xlim: bool,
auto_ylim: bool,
}
impl Axes {
pub fn new() -> Self {
Self {
xlim: (0.0, 1.0),
ylim: (0.0, 1.0),
xticks: evenly_spaced_ticks((0.0, 1.0), 4),
yticks: evenly_spaced_ticks((0.0, 1.0), 4),
xlabel: "x".to_owned(),
ylabel: "y".to_owned(),
title: "plot".to_owned(),
auto_xlim: true,
auto_ylim: true,
}
}
pub fn from_limits(xlim: (f64, f64), ylim: (f64, f64)) -> Self {
Self::new().with_xlim(xlim).with_ylim(ylim)
}
pub fn x_limits(self, limits: (f64, f64)) -> Self {
self.with_xlim(limits)
}
pub fn y_limits(self, limits: (f64, f64)) -> Self {
self.with_ylim(limits)
}
pub fn labels(self, x: impl Into<String>, y: impl Into<String>) -> Self {
self.with_labels(x, y)
}
pub fn title(self, title: impl Into<String>) -> Self {
self.with_title(title)
}
pub fn x_ticks(mut self, ticks: impl IntoIterator<Item = f64>) -> Self {
self.xticks = ticks.into_iter().collect();
self
}
pub fn y_ticks(mut self, ticks: impl IntoIterator<Item = f64>) -> Self {
self.yticks = ticks.into_iter().collect();
self
}
pub fn with_xlim(mut self, xlim: (f64, f64)) -> Self {
self.set_xlim(xlim);
self
}
pub fn with_ylim(mut self, ylim: (f64, f64)) -> Self {
self.set_ylim(ylim);
self
}
pub fn set_xlim(&mut self, xlim: (f64, f64)) {
self.xlim = xlim;
self.xticks = evenly_spaced_ticks(xlim, 4);
self.auto_xlim = false;
}
pub fn set_ylim(&mut self, ylim: (f64, f64)) {
self.ylim = ylim;
self.yticks = evenly_spaced_ticks(ylim, 4);
self.auto_ylim = false;
}
pub fn use_auto_xlim(&mut self) {
self.auto_xlim = true;
}
pub fn use_auto_ylim(&mut self) {
self.auto_ylim = true;
}
pub fn with_labels(mut self, xlabel: impl Into<String>, ylabel: impl Into<String>) -> Self {
self.xlabel = xlabel.into();
self.ylabel = ylabel.into();
self
}
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
pub(crate) fn update_auto_limits(
&mut self,
xlim: Option<(f64, f64)>,
ylim: Option<(f64, f64)>,
) -> bool {
let mut changed = false;
if self.auto_xlim
&& let Some(xlim) = xlim
{
changed |= self.xlim != xlim;
self.xlim = xlim;
self.xticks = evenly_spaced_ticks(xlim, 4);
}
if self.auto_ylim
&& let Some(ylim) = ylim
{
changed |= self.ylim != ylim;
self.ylim = ylim;
self.yticks = evenly_spaced_ticks(ylim, 4);
}
changed
}
}
impl Default for Axes {
fn default() -> Self {
Self::new()
}
}
fn evenly_spaced_ticks(limits: (f64, f64), intervals: usize) -> Vec<f64> {
if intervals == 0 {
return vec![limits.0];
}
(0..=intervals)
.map(|index| limits.0 + (limits.1 - limits.0) * index as f64 / intervals as f64)
.collect()
}