use crate::config::FlowConfig;
use crate::types::position::CoordinateExtent;
use crate::types::viewport::Viewport;
pub(crate) struct PanZoomResult {
pub(crate) changed: bool,
pub(crate) animate_zoom: Option<(egui::Pos2, f32)>,
}
pub(crate) fn handle_pan_zoom(
ui: &egui::Ui,
response: &egui::Response,
viewport: &mut Viewport,
config: &FlowConfig,
canvas_rect: egui::Rect,
suppress_primary_pan: bool,
) -> PanZoomResult {
let mut changed = false;
let mut animate_zoom: Option<(egui::Pos2, f32)> = None;
if config.zoom_on_scroll && response.hovered() {
let scroll = ui.input(|i| i.raw_scroll_delta.y);
if scroll != 0.0 {
let factor = (1.0 + scroll * 0.005).clamp(0.5, 2.0);
let pointer = ui
.input(|i| i.pointer.hover_pos())
.unwrap_or(canvas_rect.center());
zoom_toward(viewport, pointer, factor, config.min_zoom, config.max_zoom);
clamp_translate(viewport, &config.translate_extent, canvas_rect);
changed = true;
}
}
if config.zoom_on_pinch && response.hovered() {
let zoom_delta = ui.input(|i| i.zoom_delta());
if (zoom_delta - 1.0).abs() > f32::EPSILON {
let pointer = ui
.input(|i| i.pointer.hover_pos())
.unwrap_or(canvas_rect.center());
zoom_toward(
viewport,
pointer,
zoom_delta,
config.min_zoom,
config.max_zoom,
);
clamp_translate(viewport, &config.translate_extent, canvas_rect);
changed = true;
}
}
if config.zoom_on_double_click && response.double_clicked() {
let pointer = ui
.input(|i| i.pointer.hover_pos())
.unwrap_or(canvas_rect.center());
animate_zoom = Some((pointer, 1.5));
}
if config.pan_on_drag && response.dragged_by(egui::PointerButton::Middle) {
let delta = response.drag_delta();
viewport.x += delta.x;
viewport.y += delta.y;
clamp_translate(viewport, &config.translate_extent, canvas_rect);
changed = true;
}
if config.pan_on_drag
&& !suppress_primary_pan
&& response.dragged_by(egui::PointerButton::Primary)
{
let delta = response.drag_delta();
viewport.x += delta.x;
viewport.y += delta.y;
clamp_translate(viewport, &config.translate_extent, canvas_rect);
changed = true;
}
if config.pan_on_scroll && response.hovered() {
let scroll = ui.input(|i| i.raw_scroll_delta);
if scroll != egui::Vec2::ZERO {
use crate::types::viewport::PanOnScrollMode;
match config.pan_on_scroll_mode {
PanOnScrollMode::Free => {
viewport.x += scroll.x;
viewport.y += scroll.y;
}
PanOnScrollMode::Horizontal => {
viewport.x += scroll.x + scroll.y;
}
PanOnScrollMode::Vertical => {
viewport.y += scroll.y + scroll.x;
}
}
clamp_translate(viewport, &config.translate_extent, canvas_rect);
changed = true;
}
}
PanZoomResult {
changed,
animate_zoom,
}
}
pub(crate) fn zoom_toward(
viewport: &mut Viewport,
screen_pos: egui::Pos2,
factor: f32,
min: f32,
max: f32,
) {
let new_zoom = (viewport.zoom * factor).clamp(min, max);
let actual_factor = new_zoom / viewport.zoom;
viewport.x = screen_pos.x - (screen_pos.x - viewport.x) * actual_factor;
viewport.y = screen_pos.y - (screen_pos.y - viewport.y) * actual_factor;
viewport.zoom = new_zoom;
}
pub(crate) fn clamp_translate(
viewport: &mut Viewport,
extent: &CoordinateExtent,
canvas_rect: egui::Rect,
) {
let z = viewport.zoom;
let min_flow_x = extent.min.x;
let max_flow_x = extent.max.x;
if min_flow_x.is_finite() {
let vp_x_upper = canvas_rect.min.x - min_flow_x * z;
viewport.x = viewport.x.min(vp_x_upper);
}
if max_flow_x.is_finite() {
let vp_x_lower = canvas_rect.max.x - max_flow_x * z;
viewport.x = viewport.x.max(vp_x_lower);
}
let min_flow_y = extent.min.y;
let max_flow_y = extent.max.y;
if min_flow_y.is_finite() {
let vp_y_upper = canvas_rect.min.y - min_flow_y * z;
viewport.y = viewport.y.min(vp_y_upper);
}
if max_flow_y.is_finite() {
let vp_y_lower = canvas_rect.max.y - max_flow_y * z;
viewport.y = viewport.y.max(vp_y_lower);
}
}
pub(crate) fn calc_auto_pan(
pos: egui::Pos2,
canvas_rect: egui::Rect,
speed: f32,
distance: f32,
) -> egui::Vec2 {
let axis = |value: f32, min: f32, max: f32| -> f32 {
if value - min < distance {
let d = (value - min).max(1.0);
-(distance - d) / distance * speed
} else if max - value < distance {
let d = (max - value).max(1.0);
(distance - d) / distance * speed
} else {
0.0
}
};
egui::vec2(
axis(pos.x, canvas_rect.min.x, canvas_rect.max.x),
axis(pos.y, canvas_rect.min.y, canvas_rect.max.y),
)
}