use bevy::{
camera::CameraProjection, input::mouse::MouseWheel, prelude::*, window::PrimaryWindow,
};
use bevy_deepzoom::{DeepZoom, DeepZoomConfig, DeepZoomPlugin};
const DZI_PATH: &str = "https://openseadragon.github.io/example-images/highsmith/highsmith.dzi";
const TILES_BASE_PATH: &str =
"https://openseadragon.github.io/example-images/highsmith/highsmith_files";
fn main() {
App::new()
.init_resource::<DragState>()
.add_plugins(DefaultPlugins)
.add_plugins(bevy::diagnostic::FrameTimeDiagnosticsPlugin::default())
.add_plugins(bevy::diagnostic::EntityCountDiagnosticsPlugin::default())
.add_plugins(DeepZoomPlugin)
.add_systems(Startup, spawn_camera)
.add_systems(Update, (drag_pan_camera, zoom_camera))
.run();
}
fn spawn_camera(mut commands: Commands) {
let config = DeepZoomConfig::new(DZI_PATH, TILES_BASE_PATH)
.with_max_concurrent_tile_loads(4)
.with_draw_debug_ui(true);
commands.spawn((Camera2d, DeepZoom::from_config(config)));
}
#[derive(Resource, Default)]
struct DragState {
last_cursor: Option<Vec2>,
}
fn drag_pan_camera(
window: Single<&Window, With<PrimaryWindow>>,
mouse_buttons: Res<ButtonInput<MouseButton>>,
mut drag_state: ResMut<DragState>,
camera: Single<(&Projection, &mut Transform), With<Camera2d>>,
) {
let window = window.into_inner();
let Some(cursor_position) = window.cursor_position() else {
drag_state.last_cursor = None;
return;
};
if mouse_buttons.just_released(MouseButton::Left) {
drag_state.last_cursor = None;
return;
}
if !mouse_buttons.pressed(MouseButton::Left) {
drag_state.last_cursor = Some(cursor_position);
return;
}
let Some(last_cursor) = drag_state.last_cursor else {
drag_state.last_cursor = Some(cursor_position);
return;
};
let (projection, mut transform) = camera.into_inner();
let Projection::Orthographic(projection) = projection else {
drag_state.last_cursor = Some(cursor_position);
return;
};
let delta = cursor_position - last_cursor;
let world_delta = Vec2::new(delta.x, -delta.y) * projection.area.size() / window.size();
transform.translation -= world_delta.extend(0.0);
drag_state.last_cursor = Some(cursor_position);
}
fn zoom_camera(
window: Single<&Window, With<PrimaryWindow>>,
mut mouse_wheel: MessageReader<MouseWheel>,
camera: Single<&mut Projection, With<Camera2d>>,
) {
let scroll_delta: f32 = mouse_wheel.read().map(|event| event.y).sum();
if scroll_delta.abs() <= f32::EPSILON {
return;
}
let window = window.into_inner();
let mut projection = camera.into_inner();
let Projection::Orthographic(projection) = &mut *projection else {
return;
};
projection.scale *= (1.0 - scroll_delta * 0.12).clamp(0.5, 2.0);
projection.update(window.width(), window.height());
}