use super::*;
use gled_model::patching::layout::scene_bounds;
#[test]
fn a_wheel_notch_zooms_both_ways() {
let speed = 1.0 / 200.0;
assert_eq!(wheel_zoom(Vec2::ZERO, speed), 1.0);
assert!(wheel_zoom(Vec2::new(0.0, 50.0), speed) > 1.0);
assert!(wheel_zoom(Vec2::new(0.0, -50.0), speed) < 1.0);
let there = wheel_zoom(Vec2::new(0.0, 50.0), speed);
let back = wheel_zoom(Vec2::new(0.0, -50.0), speed);
assert!((there * back - 1.0).abs() < 1e-5);
}
fn fit(view: &mut SceneView, canvas: Vec2, scene: Vec2, max_zoom: f32) -> f32 {
let fitted = (canvas / scene).min_elem();
let scene_modded = SceneModded::new().scene_size(scene).max_zoom(max_zoom);
let zoom = view
.zoom
.map_or(fitted, |zoom| zoom.clamp(fitted, max_zoom.max(fitted)));
let outer_rect = Rect::from_min_size(Pos2::ZERO, canvas);
let mut to_global =
TSTransform::from_translation(outer_rect.center().to_vec2() - zoom * view.center.to_vec2())
* TSTransform::from_scaling(zoom);
scene_modded.clamp_pan(outer_rect, &mut to_global);
view.center = to_global.inverse() * outer_rect.center();
view.zoom = (to_global.scaling > fitted).then_some(to_global.scaling);
zoom
}
fn visible(center: Pos2, canvas: Vec2, zoom: f32) -> Rect {
let scene = scene_bounds().size();
let scene_modded = SceneModded::new().scene_size(scene);
let outer_rect = Rect::from_min_size(Pos2::ZERO, canvas);
let mut to_global =
TSTransform::from_translation(outer_rect.center().to_vec2() - zoom * center.to_vec2())
* TSTransform::from_scaling(zoom);
scene_modded.clamp_pan(outer_rect, &mut to_global);
to_global.inverse() * outer_rect
}
#[test]
fn the_scene_never_outgrows_the_canvas() {
let scene = scene_bounds().size();
let mut view = SceneView::centred_on(scene_bounds().center());
for canvas in [
egui::vec2(914.0, 709.0),
egui::vec2(912.0, 609.0),
egui::vec2(400.0, 900.0),
egui::vec2(1400.0, 300.0),
egui::vec2(1140.0, 834.8),
] {
let zoom = fit(&mut view, canvas, scene, 8.0);
assert!(
scene.x * zoom <= canvas.x + 0.5 && scene.y * zoom <= canvas.y + 0.5,
"scene drawn at {:?} in a canvas of {canvas:?}",
scene * zoom
);
}
}
#[test]
fn a_chosen_zoom_and_centre_survive_a_resize() {
let scene = scene_bounds().size();
let mut view = SceneView {
center: egui::pos2(400.0, 300.0),
zoom: Some(2.0),
};
assert_eq!(fit(&mut view, egui::vec2(900.0, 700.0), scene, 8.0), 2.0);
assert_eq!(view.center, egui::pos2(400.0, 300.0));
}
#[test]
fn a_fitted_view_can_still_be_moved() {
let canvas = egui::vec2(900.0, 900.0);
let zoom = (canvas / scene_bounds().size()).min_elem();
let moved = visible(
scene_bounds().center() + egui::vec2(300.0, 0.0),
canvas,
zoom,
);
assert!(
moved.center().x > scene_bounds().center().x + 290.0,
"a fitted view keeps the centre it was given: {moved:?}"
);
}
#[test]
fn the_view_stays_inside_the_pan_bounds() {
let bounds = pan_bounds(scene_bounds());
let canvas = egui::vec2(900.0, 700.0);
for zoom in [0.45, 0.6, 1.0, 2.0, 8.0] {
for away in [
egui::vec2(50_000.0, 0.0),
egui::vec2(-50_000.0, 0.0),
egui::vec2(0.0, 50_000.0),
egui::vec2(0.0, -50_000.0),
egui::vec2(-9_000.0, 9_000.0),
] {
let shown = visible(scene_bounds().center() + away, canvas, zoom);
assert!(
bounds.expand(0.5).contains_rect(shown),
"at zoom {zoom} the view reached {shown:?}, outside {bounds:?}"
);
}
}
}
#[test]
fn a_view_wider_than_its_bounds_sits_in_the_middle() {
let canvas = egui::vec2(900.0, 700.0);
let zoom = canvas.x / (pan_bounds(scene_bounds()).width() * 1.5);
let shown = visible(
scene_bounds().center() + egui::vec2(5_000.0, 0.0),
canvas,
zoom,
);
assert!(
(shown.center().x - pan_bounds(scene_bounds()).center().x).abs() < 0.5,
"{shown:?}"
);
}
#[test]
fn the_bounds_tighten_without_a_jump() {
let canvas = egui::vec2(900.0, 900.0);
let pushed = scene_bounds().center() + egui::vec2(10_000.0, 0.0);
let mut previous: Option<(f32, f32)> = None;
for step in 0..=400 {
let zoom = 0.9 * 0.985_f32.powi(step);
let shown = visible(pushed, canvas, zoom);
let half = shown.width() * 0.5;
if let Some((center, previous_half)) = previous {
assert!(
(shown.center().x - center).abs() <= (half - previous_half).abs() + 0.5,
"the centre moved {} while its edge moved {} at zoom {zoom}",
shown.center().x - center,
half - previous_half
);
}
previous = Some((shown.center().x, half));
}
}