use std::cell::RefCell;
use std::rc::Rc;
use egui_kittest::Harness;
use egui_kittest::wgpu::{WgpuTestRenderer, create_render_state, default_wgpu_setup};
use siplot::egui;
use siplot::{PlotWidget, SnappingMode};
fn plot_rendered(
build: impl FnOnce(&mut PlotWidget),
) -> (Rc<RefCell<PlotWidget>>, Harness<'static>) {
let rs = create_render_state(default_wgpu_setup());
siplot::install(&rs);
let mut plot = PlotWidget::new(&rs, 0);
build(&mut plot);
let plot = Rc::new(RefCell::new(plot));
let plot_ui = plot.clone();
let renderer = WgpuTestRenderer::from_render_state(rs.clone());
let mut harness = Harness::builder()
.with_size(egui::vec2(400.0, 400.0))
.with_pixels_per_point(1.0)
.renderer(renderer)
.build_ui(move |ui| {
plot_ui.borrow_mut().show(ui);
});
harness.step();
harness.step();
(plot, harness)
}
fn plot_with_line() -> (Rc<RefCell<PlotWidget>>, Harness<'static>) {
plot_rendered(|plot| {
let xs: Vec<f64> = (0..=10).map(|i| i as f64).collect();
let ys = xs.clone();
plot.add_curve(&xs, &ys, egui::Color32::from_rgb(0, 120, 255));
})
}
#[test]
fn snap_lands_on_the_nearest_curve_vertex() {
let (plot, _harness) = plot_with_line();
let plot = plot.borrow();
let snap = plot
.snap_cursor([5.0, 5.0], SnappingMode::CURVE)
.expect("a cursor on the (5,5) vertex must snap to it");
assert!(
(snap.data[0] - 5.0).abs() < 1e-9 && (snap.data[1] - 5.0).abs() < 1e-9,
"snapped to the wrong vertex: {:?}",
snap.data
);
}
#[test]
fn snap_returns_none_when_no_vertex_is_within_the_threshold() {
let (plot, _harness) = plot_with_line();
let plot = plot.borrow();
assert!(
plot.snap_cursor([5.5, 4.5], SnappingMode::CURVE).is_none(),
"a cursor far from every vertex must not snap"
);
}
#[test]
fn disabled_mode_never_snaps() {
let (plot, _harness) = plot_with_line();
let plot = plot.borrow();
assert!(
plot.snap_cursor([5.0, 5.0], SnappingMode::DISABLED)
.is_none(),
"SnappingMode::DISABLED must never snap"
);
assert!(
plot.snap_cursor([5.0, 5.0], SnappingMode::SCATTER)
.is_none(),
"a scatter-only mode must not snap a curve vertex"
);
}
#[test]
fn scatter_points_snap_under_scatter_mode_only() {
let (plot, _harness) = plot_rendered(|plot| {
let xs: Vec<f64> = (0..=10).map(|i| i as f64).collect();
let ys = xs.clone();
plot.add_scatter(&xs, &ys, egui::Color32::from_rgb(255, 120, 0));
});
let plot = plot.borrow();
let snap = plot
.snap_cursor([5.0, 5.0], SnappingMode::SCATTER)
.expect("SCATTER mode must snap to a scatter point");
assert!(
(snap.data[0] - 5.0).abs() < 1e-9 && (snap.data[1] - 5.0).abs() < 1e-9,
"snapped to the wrong scatter point: {:?}",
snap.data
);
assert!(
plot.snap_cursor([5.0, 5.0], SnappingMode::CURVE).is_none(),
"CURVE mode must not snap a scatter point (kind filtering)"
);
}
#[test]
fn uncached_transform_yields_no_snap() {
let rs = create_render_state(default_wgpu_setup());
siplot::install(&rs);
let mut plot = PlotWidget::new(&rs, 0);
let xs: Vec<f64> = (0..=10).map(|i| i as f64).collect();
let ys = xs.clone();
plot.add_curve(&xs, &ys, egui::Color32::from_rgb(0, 120, 255));
assert!(
plot.snap_cursor([5.0, 5.0], SnappingMode::CURVE).is_none(),
"snapping before any frame is rendered must return None"
);
}