diff --git a/maplibre-winit/src/input/mod.rs b/maplibre-winit/src/input/mod.rs index 4eafbc29..41c193f9 100644 --- a/maplibre-winit/src/input/mod.rs +++ b/maplibre-winit/src/input/mod.rs @@ -59,11 +59,11 @@ impl InputController { /// Process the given winit `[winit::event::WindowEvent]`. /// Returns true if the event has been processed and false otherwise. - pub fn window_input(&mut self, event: &WindowEvent) -> bool { + pub fn window_input(&mut self, event: &WindowEvent, scale_factor: f64) -> bool { match event { WindowEvent::CursorMoved { position, .. } => { let position: (f64, f64) = position.to_owned().into(); - let position = Vector2::from(position); + let position = Vector2::from(position) / scale_factor; self.pan_handler.process_window_position(&position, false); self.query_handler.process_window_position(&position, false); self.zoom_handler.process_window_position(&position, false); @@ -99,14 +99,11 @@ impl InputController { } TouchPhase::Moved => { let position: (f64, f64) = touch.location.to_owned().into(); - self.pan_handler - .process_window_position(&Vector2::from(position), true); - self.query_handler - .process_window_position(&Vector2::from(position), true); - self.zoom_handler - .process_window_position(&Vector2::from(position), true); - self.camera_handler - .process_window_position(&Vector2::from(position), true); + let position = Vector2::from(position) / scale_factor; + self.pan_handler.process_window_position(&position, true); + self.query_handler.process_window_position(&position, true); + self.zoom_handler.process_window_position(&position, true); + self.camera_handler.process_window_position(&position, true); true } TouchPhase::Cancelled => false, diff --git a/maplibre-winit/src/lib.rs b/maplibre-winit/src/lib.rs index 2a33b471..ad8bc501 100644 --- a/maplibre-winit/src/lib.rs +++ b/maplibre-winit/src/lib.rs @@ -8,7 +8,7 @@ use maplibre::{ event_loop::{EventLoop, EventLoopProxy, SendEventError}, io::{apc::AsyncProcedureCall, scheduler::Scheduler, source_client::HttpClient}, map::Map, - window::{HeadedMapWindow, MapWindowConfig}, + window::{HeadedMapWindow, MapWindowConfig, PhysicalSize}, }; use winit::{ event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, @@ -77,6 +77,7 @@ impl EventLoop for WinitEventLoop { let mut current_frame: u64 = 0; let mut input_controller = InputController::new(0.2, 100.0, 0.1); + let mut scale_factor = 1.0; self.event_loop .run(move |event, _window_target, control_flow| { @@ -104,7 +105,7 @@ impl EventLoop for WinitEventLoop { ref event, window_id, } if window_id == map.window().id().into() => { - if !input_controller.window_input(event) { + if !input_controller.window_input(event, scale_factor) { match event { WindowEvent::CloseRequested | WindowEvent::KeyboardInput { @@ -116,14 +117,17 @@ impl EventLoop for WinitEventLoop { }, .. } => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(physical_size) => { - if let Ok(map_context) = map.context_mut() { - map_context.resize(physical_size.width, physical_size.height); + WindowEvent::Resized(winit::dpi::PhysicalSize { width, height}) => { + if let Ok(map_context) = map.context_mut() { + let size = PhysicalSize::new(*width, *height).expect("window values should not be zero"); + map_context.resize(size, scale_factor); } } - WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + WindowEvent::ScaleFactorChanged { new_inner_size: winit::dpi::PhysicalSize { width, height}, scale_factor: new_scale_factor } => { if let Ok(map_context) = map.context_mut() { - map_context.resize(new_inner_size.width, new_inner_size.height); + scale_factor = *new_scale_factor; + let size = PhysicalSize::new(*width, *height).expect("window values should not be zero"); + map_context.resize(size, scale_factor); } } _ => {} @@ -178,6 +182,7 @@ impl EventLoop for WinitEventLoop { } } } + pub struct WinitEventLoopProxy { proxy: RawEventLoopProxy, } diff --git a/maplibre-winit/src/noweb.rs b/maplibre-winit/src/noweb.rs index a07019b1..08779efe 100644 --- a/maplibre-winit/src/noweb.rs +++ b/maplibre-winit/src/noweb.rs @@ -16,7 +16,7 @@ use maplibre::{ }, render::{builder::RendererBuilder, settings::WgpuSettings, RenderPlugin}, style::Style, - window::{MapWindow, MapWindowConfig, WindowSize}, + window::{MapWindow, MapWindowConfig, PhysicalSize}, }; use winit::window::WindowBuilder; @@ -54,17 +54,17 @@ impl WinitMapWindowConfig { } impl MapWindow for WinitMapWindow { - fn size(&self) -> WindowSize { + fn size(&self) -> PhysicalSize { let size = self.window.inner_size(); #[cfg(target_os = "android")] // On android we can not get the dimensions of the window initially. Therefore, we use a // fallback until the window is ready to deliver its correct bounds. - let window_size = - WindowSize::new(size.width, size.height).unwrap_or(WindowSize::new(100, 100).unwrap()); + let window_size = PhysicalSize::new(size.width, size.height) + .unwrap_or(PhysicalSize::new(100, 100).unwrap()); #[cfg(not(target_os = "android"))] let window_size = - WindowSize::new(size.width, size.height).expect("failed to get window dimensions."); + PhysicalSize::new(size.width, size.height).expect("failed to get window dimensions."); window_size } } diff --git a/maplibre-winit/src/web.rs b/maplibre-winit/src/web.rs index 61931459..21abeb55 100644 --- a/maplibre-winit/src/web.rs +++ b/maplibre-winit/src/web.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use maplibre::window::{MapWindow, MapWindowConfig, WindowSize}; +use maplibre::window::{MapWindow, MapWindowConfig, PhysicalSize}; use winit::{platform::web::WindowBuilderExtWebSys, window::WindowBuilder}; use super::WinitMapWindow; @@ -44,10 +44,10 @@ impl MapWindowConfig for WinitMapWindowConfig { } impl MapWindow for WinitMapWindow { - fn size(&self) -> WindowSize { + fn size(&self) -> PhysicalSize { let size = self.window.inner_size(); - WindowSize::new(size.width, size.height).expect("failed to get window dimensions.") + PhysicalSize::new(size.width, size.height).expect("failed to get window dimensions.") } } diff --git a/maplibre/src/context.rs b/maplibre/src/context.rs index abda6c33..6ca16e16 100644 --- a/maplibre/src/context.rs +++ b/maplibre/src/context.rs @@ -2,6 +2,7 @@ use crate::{ render::{view_state::ViewState, Renderer}, style::Style, tcs::world::World, + window::PhysicalSize, }; /// Stores the context of the map. @@ -16,8 +17,8 @@ pub struct MapContext { } impl MapContext { - pub fn resize(&mut self, width: u32, height: u32) { - self.view_state.resize(width, height); - self.renderer.resize_surface(width, height) + pub fn resize(&mut self, size: PhysicalSize, scale_factor: f64) { + self.view_state.resize(size.to_logical(scale_factor)); + self.renderer.resize_surface(size) } } diff --git a/maplibre/src/headless/mod.rs b/maplibre/src/headless/mod.rs index fd679e68..0506c362 100644 --- a/maplibre/src/headless/mod.rs +++ b/maplibre/src/headless/mod.rs @@ -17,7 +17,7 @@ use crate::{ }, schedule::Schedule, tcs::{system::SystemContainer, world::World}, - window::{MapWindowConfig, WindowSize}, + window::{MapWindowConfig, PhysicalSize}, }; mod graph_node; @@ -34,7 +34,7 @@ pub async fn create_headless_renderer( let client = ReqwestHttpClient::new(cache_path); let mut kernel = KernelBuilder::new() .with_map_window_config(HeadlessMapWindowConfig::new( - WindowSize::new(tile_size, tile_size).unwrap(), + PhysicalSize::new(tile_size, tile_size).unwrap(), )) .with_http_client(client.clone()) .with_apc(SchedulerAsyncProcedureCall::new(TokioScheduler::new())) diff --git a/maplibre/src/headless/window.rs b/maplibre/src/headless/window.rs index c1ee92a8..6f5c79b9 100644 --- a/maplibre/src/headless/window.rs +++ b/maplibre/src/headless/window.rs @@ -1,12 +1,12 @@ -use crate::window::{MapWindow, MapWindowConfig, WindowSize}; +use crate::window::{MapWindow, MapWindowConfig, PhysicalSize}; #[derive(Clone)] pub struct HeadlessMapWindowConfig { - size: WindowSize, + size: PhysicalSize, } impl HeadlessMapWindowConfig { - pub fn new(size: WindowSize) -> Self { + pub fn new(size: PhysicalSize) -> Self { Self { size } } } @@ -20,11 +20,11 @@ impl MapWindowConfig for HeadlessMapWindowConfig { } pub struct HeadlessMapWindow { - size: WindowSize, + size: PhysicalSize, } impl MapWindow for HeadlessMapWindow { - fn size(&self) -> WindowSize { + fn size(&self) -> PhysicalSize { self.size } } diff --git a/maplibre/src/render/mod.rs b/maplibre/src/render/mod.rs index 56982b3f..74bf9ba7 100644 --- a/maplibre/src/render/mod.rs +++ b/maplibre/src/render/mod.rs @@ -67,10 +67,13 @@ pub mod view_state; pub use shaders::ShaderVertex; -use crate::render::{ - render_phase::{LayerItem, RenderPhase, TileMaskItem}, - systems::{graph_runner_system::GraphRunnerSystem, upload_system::upload_system}, - tile_view_pattern::{ViewTileSources, WgpuTileViewPattern}, +use crate::{ + render::{ + render_phase::{LayerItem, RenderPhase, TileMaskItem}, + systems::{graph_runner_system::GraphRunnerSystem, upload_system::upload_system}, + tile_view_pattern::{ViewTileSources, WgpuTileViewPattern}, + }, + window::PhysicalSize, }; pub(crate) const INDEX_FORMAT: wgpu::IndexFormat = wgpu::IndexFormat::Uint32; // Must match IndexDataType @@ -240,8 +243,8 @@ impl Renderer { }) } - pub fn resize_surface(&mut self, width: u32, height: u32) { - self.resources.surface.resize(width, height) + pub fn resize_surface(&mut self, size: PhysicalSize) { + self.resources.surface.resize(size) } /// Requests a device @@ -421,12 +424,12 @@ impl Renderer { mod tests { use crate::{ tcs::world::World, - window::{MapWindow, MapWindowConfig, WindowSize}, + window::{MapWindow, MapWindowConfig, PhysicalSize}, }; #[derive(Clone)] pub struct HeadlessMapWindowConfig { - size: WindowSize, + size: PhysicalSize, } impl MapWindowConfig for HeadlessMapWindowConfig { @@ -438,11 +441,11 @@ mod tests { } pub struct HeadlessMapWindow { - size: WindowSize, + size: PhysicalSize, } impl MapWindow for HeadlessMapWindow { - fn size(&self) -> WindowSize { + fn size(&self) -> PhysicalSize { self.size } } @@ -486,7 +489,7 @@ mod tests { let render_state = RenderResources::new(Surface::from_image( &device, &HeadlessMapWindow { - size: WindowSize::new(100, 100).expect("invalid headless map size"), + size: PhysicalSize::new(100, 100).expect("invalid headless map size"), }, &RendererSettings::default(), )); diff --git a/maplibre/src/render/resource/surface.rs b/maplibre/src/render/resource/surface.rs index ffdcd5ad..38f8a584 100644 --- a/maplibre/src/render/resource/surface.rs +++ b/maplibre/src/render/resource/surface.rs @@ -12,7 +12,7 @@ use crate::{ resource::texture::TextureView, settings::{Msaa, RendererSettings}, }, - window::{HeadedMapWindow, MapWindow, WindowSize}, + window::{HeadedMapWindow, MapWindow, PhysicalSize}, }; pub struct BufferDimensions { @@ -23,7 +23,7 @@ pub struct BufferDimensions { } impl BufferDimensions { - fn new(size: WindowSize) -> Self { + fn new(size: PhysicalSize) -> Self { let bytes_per_pixel = size_of::() as u32; let unpadded_bytes_per_row = size.width() * bytes_per_pixel; @@ -41,7 +41,7 @@ impl BufferDimensions { pub struct WindowHead { surface: wgpu::Surface, - size: WindowSize, + size: PhysicalSize, texture_format: wgpu::TextureFormat, present_mode: wgpu::PresentMode, @@ -50,7 +50,7 @@ pub struct WindowHead { impl WindowHead { pub fn resize_and_configure(&mut self, width: u32, height: u32, device: &wgpu::Device) { - self.size = WindowSize::new(width, height).unwrap(); + self.size = PhysicalSize::new(width, height).unwrap(); self.configure(device); } @@ -164,7 +164,7 @@ pub enum Head { } pub struct Surface { - size: WindowSize, + size: PhysicalSize, head: Head, } @@ -289,12 +289,12 @@ impl Surface { } } - pub fn size(&self) -> WindowSize { + pub fn size(&self) -> PhysicalSize { self.size } - pub fn resize(&mut self, width: u32, height: u32) { - self.size = WindowSize::new(width, height).expect("Invalid size for resizing the surface."); + pub fn resize(&mut self, size: PhysicalSize) { + self.size = size; } pub fn reconfigure(&mut self, device: &wgpu::Device) { diff --git a/maplibre/src/render/view_state.rs b/maplibre/src/render/view_state.rs index 135801af..42fadeae 100644 --- a/maplibre/src/render/view_state.rs +++ b/maplibre/src/render/view_state.rs @@ -15,7 +15,7 @@ use crate::{ math::{bounds_from_points, Aabb2, Aabb3, Plane}, ChangeObserver, }, - window::WindowSize, + window::{LogicalSize, PhysicalSize}, }; const VIEW_REGION_PADDING: i32 = 1; @@ -33,7 +33,7 @@ pub struct ViewState { impl ViewState { pub fn new>, P: Into>>( - window_size: WindowSize, + window_size: PhysicalSize, position: WorldCoords, zoom: Zoom, pitch: P, @@ -65,9 +65,9 @@ impl ViewState { &self.edge_insets } - pub fn resize(&mut self, width: u32, height: u32) { - self.width = width as f64; - self.height = height as f64; + pub fn resize(&mut self, size: LogicalSize) { + self.width = size.width() as f64; + self.height = size.height() as f64; } pub fn create_view_region(&self, visible_level: ZoomLevel) -> Option { @@ -469,14 +469,14 @@ mod tests { use crate::{ coords::{WorldCoords, Zoom}, render::view_state::ViewState, - window::WindowSize, + window::PhysicalSize, }; #[test] fn conform_transformation() { let fov = Deg(60.0); let mut state = ViewState::new( - WindowSize::new(800, 600).unwrap(), + PhysicalSize::new(800, 600).unwrap(), WorldCoords::at_ground(0.0, 0.0), Zoom::new(10.0), Deg(0.0), diff --git a/maplibre/src/window.rs b/maplibre/src/window.rs index 0b6f2632..e481feb6 100644 --- a/maplibre/src/window.rs +++ b/maplibre/src/window.rs @@ -4,9 +4,9 @@ use std::num::NonZeroU32; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; -/// Window of a certain [`WindowSize`]. This can either be a proper window or a headless one. +/// Window of a certain [`PhysicalSize`]. This can either be a proper window or a headless one. pub trait MapWindow { - fn size(&self) -> WindowSize; + fn size(&self) -> PhysicalSize; } /// Window which references a physical `RawWindow`. This is only implemented by headed windows and @@ -31,13 +31,13 @@ pub trait MapWindowConfig: 'static + Clone { } /// Window size with a width and an height in pixels. -#[derive(Clone, Copy, Eq, PartialEq)] -pub struct WindowSize { +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct PhysicalSize { width: NonZeroU32, height: NonZeroU32, } -impl WindowSize { +impl PhysicalSize { pub fn new(width: u32, height: u32) -> Option { Some(Self { width: NonZeroU32::new(width)?, @@ -61,3 +61,45 @@ impl WindowSize { self.height } } + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct LogicalSize { + width: NonZeroU32, + height: NonZeroU32, +} + +impl LogicalSize { + pub fn new(width: u32, height: u32) -> Option { + Some(Self { + width: NonZeroU32::new(width)?, + height: NonZeroU32::new(height)?, + }) + } + + pub fn width(&self) -> u32 { + self.width.get() + } + + pub fn width_non_zero(&self) -> NonZeroU32 { + self.width + } + + pub fn height(&self) -> u32 { + self.height.get() + } + + pub fn height_non_zero(&self) -> NonZeroU32 { + self.height + } +} + +impl PhysicalSize { + pub fn to_logical(&self, scale_factor: f64) -> LogicalSize { + let width = self.width.get() as f64 / scale_factor; + let height = self.height.get() as f64 / scale_factor; + LogicalSize { + width: NonZeroU32::new(width as u32).expect("impossible to reach"), + height: NonZeroU32::new(height as u32).expect("impossible to reach"), + } + } +}