use crate::core::plot::Plot;
#[derive(Debug, Clone)]
pub struct SyncAxes {
pub sync_x: bool,
pub sync_y: bool,
prev_x: Option<(f64, f64)>,
prev_y: Option<(f64, f64)>,
}
impl Default for SyncAxes {
fn default() -> Self {
Self {
sync_x: true,
sync_y: true,
prev_x: None,
prev_y: None,
}
}
}
impl SyncAxes {
pub fn new() -> Self {
Self::default()
}
pub fn with_sync_x(mut self, on: bool) -> Self {
self.sync_x = on;
self
}
pub fn with_sync_y(mut self, on: bool) -> Self {
self.sync_y = on;
self
}
pub fn reset(&mut self) {
self.prev_x = None;
self.prev_y = None;
}
pub fn sync(&mut self, plots: &mut [&mut Plot]) {
if plots.is_empty() {
return;
}
if self.sync_x {
let source_x = match self.prev_x {
None => {
let (x0, x1, _, _) = plots[0].limits;
Some((x0, x1))
}
Some(prev) => {
plots.iter().find_map(|p| {
let (x0, x1, _, _) = p.limits;
if (x0, x1) != prev {
Some((x0, x1))
} else {
None
}
})
}
};
if let Some((x0, x1)) = source_x {
for p in plots.iter_mut() {
let (_, _, y0, y1) = p.limits;
p.limits = (x0, x1, y0, y1);
}
self.prev_x = Some((x0, x1));
}
}
if self.sync_y {
let source_y = match self.prev_y {
None => {
let (_, _, y0, y1) = plots[0].limits;
Some((y0, y1))
}
Some(prev) => plots.iter().find_map(|p| {
let (_, _, y0, y1) = p.limits;
if (y0, y1) != prev {
Some((y0, y1))
} else {
None
}
}),
};
if let Some((y0, y1)) = source_y {
for p in plots.iter_mut() {
let (x0, x1, _, _) = p.limits;
p.limits = (x0, x1, y0, y1);
}
self.prev_y = Some((y0, y1));
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn plot_with_limits(x0: f64, x1: f64, y0: f64, y1: f64) -> Plot {
let mut p = Plot::new(0);
p.limits = (x0, x1, y0, y1);
p
}
#[test]
fn first_call_copies_first_plot_to_all() {
let mut sync = SyncAxes::new();
let mut a = plot_with_limits(0.0, 5.0, -1.0, 1.0);
let mut b = plot_with_limits(0.0, 1.0, 0.0, 10.0); sync.sync(&mut [&mut a, &mut b]);
assert_eq!(b.limits, (0.0, 5.0, -1.0, 1.0));
assert_eq!(a.limits, (0.0, 5.0, -1.0, 1.0));
}
#[test]
fn changed_plot_propagates_to_others() {
let mut sync = SyncAxes::new();
let mut a = plot_with_limits(0.0, 5.0, -1.0, 1.0);
let mut b = plot_with_limits(0.0, 5.0, -1.0, 1.0);
sync.sync(&mut [&mut a, &mut b]);
a.limits = (1.0, 6.0, -1.0, 1.0);
sync.sync(&mut [&mut a, &mut b]);
assert_eq!(b.limits.0, 1.0);
assert_eq!(b.limits.1, 6.0);
}
#[test]
fn sync_x_only_leaves_y_independent() {
let mut sync = SyncAxes::new().with_sync_y(false);
let mut a = plot_with_limits(0.0, 5.0, -1.0, 1.0);
let mut b = plot_with_limits(0.0, 1.0, -2.0, 2.0);
sync.sync(&mut [&mut a, &mut b]);
assert_eq!(b.limits.0, 0.0); assert_eq!(b.limits.1, 5.0);
assert_eq!(b.limits.2, -2.0); assert_eq!(b.limits.3, 2.0);
}
#[test]
fn sync_y_only_leaves_x_independent() {
let mut sync = SyncAxes::new().with_sync_x(false);
let mut a = plot_with_limits(0.0, 5.0, -1.0, 1.0);
let mut b = plot_with_limits(10.0, 20.0, -1.0, 1.0);
sync.sync(&mut [&mut a, &mut b]);
assert_eq!(b.limits.0, 10.0); assert_eq!(b.limits.2, -1.0); }
#[test]
fn reset_reinitializes_from_first_plot() {
let mut sync = SyncAxes::new();
let mut a = plot_with_limits(0.0, 5.0, -1.0, 1.0);
let mut b = plot_with_limits(0.0, 5.0, -1.0, 1.0);
sync.sync(&mut [&mut a, &mut b]);
sync.reset();
a.limits = (100.0, 200.0, 50.0, 60.0);
b.limits = (0.0, 1.0, 0.0, 1.0);
sync.sync(&mut [&mut a, &mut b]);
assert_eq!(b.limits, (100.0, 200.0, 50.0, 60.0));
}
#[test]
fn empty_slice_is_noop() {
let mut sync = SyncAxes::new();
sync.sync(&mut []); }
}