use std::cell::{Cell, RefCell};
use std::rc::Rc;
use egui_kittest::Harness;
use egui_kittest::wgpu::{WgpuTestRenderer, create_render_state, default_wgpu_setup};
use rsplot::egui;
use rsplot::{
ActiveImageAlphaSlider, AutoscaleMode, Colormap, ImageSpec, ItemHandle, NamedItemAlphaSlider,
PlotWidget,
};
fn plot_with_image(alpha0: f32, legend: Option<&str>) -> (PlotWidget, ItemHandle) {
let rs = create_render_state(default_wgpu_setup());
rsplot::install(&rs);
let mut plot = PlotWidget::new(&rs, 0);
let pixels = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let mut spec = ImageSpec::scalar(3, 2, &pixels, Colormap::viridis(0.0, 6.0));
spec.alpha = alpha0;
let handle = plot.add_image_spec(spec);
if let Some(legend) = legend {
plot.set_item_legend(handle, legend);
}
plot.set_active_image(Some(handle));
(plot, handle)
}
#[test]
fn image_alpha_round_trips_through_the_setter() {
let (mut plot, _handle) = plot_with_image(1.0, None);
assert_eq!(plot.active_image_alpha(), Some(1.0));
assert!(plot.set_active_image_alpha(0.4));
let a = plot.active_image_alpha().expect("active image has alpha");
assert!((a - 0.4).abs() < 1e-6, "alpha setter round-trips, got {a}");
assert!(plot.set_active_image_alpha(2.0));
assert_eq!(plot.active_image_alpha(), Some(1.0));
assert!(plot.set_active_image_alpha(-1.0));
assert_eq!(plot.active_image_alpha(), Some(0.0));
}
#[test]
fn explicit_level_edit_preserves_alpha() {
let (mut plot, _handle) = plot_with_image(0.3, None);
assert!(plot.set_active_image_levels(1.0, 5.0));
let a = plot.active_image_alpha().expect("alpha retained");
assert!(
(a - 0.3).abs() < 1e-6,
"set_active_image_levels must preserve the image alpha, got {a}"
);
}
#[test]
fn raw_pixel_autoscale_preserves_alpha() {
let (mut plot, _handle) = plot_with_image(0.3, None);
assert!(plot.autoscale_active_image(AutoscaleMode::MinMax).is_some());
let a = plot.active_image_alpha().expect("alpha retained");
assert!(
(a - 0.3).abs() < 1e-6,
"autoscale_active_image must preserve the image alpha, got {a}"
);
}
#[test]
fn median_filter_preserves_alpha() {
let (mut plot, _handle) = plot_with_image(0.3, None);
assert!(plot.apply_median_filter(3, false));
let a = plot.active_image_alpha().expect("alpha retained");
assert!(
(a - 0.3).abs() < 1e-6,
"apply_median_filter must preserve the image alpha, got {a}"
);
}
#[test]
fn per_handle_alpha_addresses_an_image_by_legend() {
let (mut plot, handle) = plot_with_image(1.0, Some("img"));
let by_legend = plot.image_by_legend("img").expect("legend resolves");
assert_eq!(by_legend, handle);
assert_eq!(plot.image_alpha(handle), Some(1.0));
assert!(plot.set_image_alpha(handle, 0.25));
let a = plot.image_alpha(handle).expect("alpha retained");
assert!(
(a - 0.25).abs() < 1e-6,
"set_image_alpha round-trips, got {a}"
);
assert!(plot.image_by_legend("missing").is_none());
}
type ActiveSliderBundle = (
Rc<RefCell<PlotWidget>>,
Rc<RefCell<ActiveImageAlphaSlider>>,
Rc<Cell<egui::Rect>>,
Harness<'static>,
);
fn active_slider_harness(alpha0: f32, with_image: bool) -> ActiveSliderBundle {
let rs = create_render_state(default_wgpu_setup());
rsplot::install(&rs);
let mut plot = PlotWidget::new(&rs, 0);
if with_image {
let pixels = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let mut spec = ImageSpec::scalar(3, 2, &pixels, Colormap::viridis(0.0, 6.0));
spec.alpha = alpha0;
let handle = plot.add_image_spec(spec);
plot.set_active_image(Some(handle));
}
let plot = Rc::new(RefCell::new(plot));
let slider = Rc::new(RefCell::new(
ActiveImageAlphaSlider::new().with_orientation(rsplot::AlphaSliderOrientation::Vertical),
));
let track = Rc::new(Cell::new(egui::Rect::NOTHING));
let plot_ui = plot.clone();
let slider_ui = slider.clone();
let track_ui = track.clone();
let renderer = WgpuTestRenderer::from_render_state(rs.clone());
let harness = Harness::builder()
.with_size(egui::vec2(400.0, 400.0))
.with_pixels_per_point(1.0)
.renderer(renderer)
.build_ui(move |ui| {
let resp = slider_ui.borrow_mut().show(ui, &mut plot_ui.borrow_mut());
track_ui.set(resp.rect);
plot_ui.borrow_mut().show(ui);
});
(plot, slider, track, harness)
}
#[test]
fn active_image_slider_seeds_from_the_image_alpha() {
let (plot, slider, _track, mut harness) = active_slider_harness(0.5, true);
harness.step();
harness.step();
let a = slider.borrow().alpha();
assert!(
(a - 0.5).abs() < 0.01,
"active-image slider must seed from the image's alpha (0.5), got {a}"
);
assert_eq!(plot.borrow().active_image_alpha(), Some(0.5));
}
#[test]
fn active_image_slider_disables_without_an_active_image() {
let (plot, _slider, _track, mut harness) = active_slider_harness(1.0, false);
harness.step();
harness.step();
assert_eq!(
plot.borrow().active_image_alpha(),
None,
"no scalar active image means no addressable alpha"
);
assert_eq!(plot.borrow().active_image_handle(), None);
}
#[test]
fn dragging_the_active_image_slider_writes_alpha_back_to_the_image() {
let (plot, slider, track, mut harness) = active_slider_harness(1.0, true);
harness.step();
harness.step();
assert_eq!(plot.borrow().active_image_alpha(), Some(1.0));
let rect = track.get();
let top = egui::pos2(rect.center().x, rect.top() + rect.height() * 0.1);
let bottom = egui::pos2(rect.center().x, rect.top() + rect.height() * 0.9);
harness.drag_at(top);
harness.step();
for t in [0.0f32, 0.25, 0.5, 0.75, 1.0] {
harness.hover_at(top + (bottom - top) * t);
harness.step();
}
harness.drop_at(bottom);
harness.step();
harness.step();
let img_alpha = plot.borrow().active_image_alpha().expect("active image");
let slider_alpha = slider.borrow().alpha();
assert!(
img_alpha < 0.95,
"dragging the slider down must lower the image alpha below 1.0, got {img_alpha}"
);
assert!(
(img_alpha - slider_alpha).abs() < 1e-3,
"the image alpha ({img_alpha}) must equal the slider's value ({slider_alpha})"
);
}
#[test]
fn named_item_slider_binds_by_legend_and_disables_for_unknown() {
let rs = create_render_state(default_wgpu_setup());
rsplot::install(&rs);
let mut plot = PlotWidget::new(&rs, 0);
let pixels = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let mut spec = ImageSpec::scalar(3, 2, &pixels, Colormap::viridis(0.0, 6.0));
spec.alpha = 0.7;
let handle = plot.add_image_spec(spec);
plot.set_item_legend(handle, "img");
plot.set_active_image(Some(handle));
let plot = Rc::new(RefCell::new(plot));
let bound = Rc::new(RefCell::new(NamedItemAlphaSlider::new("img")));
let unbound = Rc::new(RefCell::new(NamedItemAlphaSlider::new("nope")));
let plot_ui = plot.clone();
let bound_ui = bound.clone();
let unbound_ui = unbound.clone();
let renderer = WgpuTestRenderer::from_render_state(rs.clone());
let mut harness = Harness::builder()
.with_size(egui::vec2(400.0, 200.0))
.with_pixels_per_point(1.0)
.renderer(renderer)
.build_ui(move |ui| {
bound_ui.borrow_mut().show(ui, &mut plot_ui.borrow_mut());
unbound_ui.borrow_mut().show(ui, &mut plot_ui.borrow_mut());
plot_ui.borrow_mut().show(ui);
});
harness.step();
harness.step();
assert_eq!(bound.borrow().legend(), "img");
let a = bound.borrow().alpha();
assert!(
(a - 0.7).abs() < 0.01,
"named slider must seed from its image's alpha (0.7), got {a}"
);
assert_eq!(
unbound.borrow().alpha(),
1.0,
"a slider naming an absent image stays at the opaque default"
);
}