From e75eb3c7555b3baf97b21273ec3dfcc5be1fd9a7 Mon Sep 17 00:00:00 2001 From: Louis Capitanchik <contact@louiscap.co> Date: Sat, 18 May 2024 16:35:22 +0100 Subject: [PATCH] Scale mouse position events by window area --- src/input.rs | 7 ++++++- src/lib.rs | 2 ++ src/unproject.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/unproject.rs diff --git a/src/input.rs b/src/input.rs index 2dccc46..aa3d228 100644 --- a/src/input.rs +++ b/src/input.rs @@ -12,6 +12,7 @@ use crate::{ event_dispatcher::EventDispatcher, input_event::InputEvent, }; +use crate::unproject::UnprojectManager; pub(crate) fn process_events(world: &mut World) { let mut input_events = Vec::new(); @@ -28,6 +29,7 @@ pub(crate) fn process_events(world: &mut World) { ResMut<CustomEventReader<MouseWheel>>, ResMut<CustomEventReader<ReceivedCharacter>>, ResMut<CustomEventReader<KeyboardInput>>, + UnprojectManager, ), _, _, @@ -43,6 +45,7 @@ pub(crate) fn process_events(world: &mut World) { mut custom_event_mouse_wheel, mut custom_event_char_input, mut custom_event_keyboard, + projector, )| { if let Some(event) = custom_event_reader_cursor .0 @@ -50,7 +53,9 @@ pub(crate) fn process_events(world: &mut World) { .last() { // Currently, we can only handle a single MouseMoved event at a time so everything but the last needs to be skipped - input_events.push(InputEvent::MouseMoved(event.position.into())); + if let Ok(pos) = projector.unproject(event.position) { + input_events.push(InputEvent::MouseMoved(pos.into())); + } } for event in custom_event_mouse_button.0.read(&mouse_button_input_events) { diff --git a/src/lib.rs b/src/lib.rs index 0929cb7..12cbb1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,8 @@ mod widget_state; pub mod widgets; mod window_size; +pub(crate) mod unproject; + use context::KayakRootContext; pub use window_size::WindowSize; diff --git a/src/unproject.rs b/src/unproject.rs new file mode 100644 index 0000000..dff2687 --- /dev/null +++ b/src/unproject.rs @@ -0,0 +1,47 @@ +use bevy::ecs::system::SystemParam; +use bevy::math::Vec2; +use bevy::prelude::{OrthographicProjection, Query, Vec4Swizzles, Window, With}; +use bevy::utils::thiserror::Error; +use bevy::window::PrimaryWindow; +use crate::CameraUIKayak; + +pub fn unproject_from_physical( + point: Vec2, + window: &Window, + projection: &OrthographicProjection, +) -> Vec2 { + let projection_area = projection.area; + let projection_width = projection_area.width(); + let projection_height = projection_area.height(); + + let window_width = window.width(); + let window_height = window.height(); + + let width_ratio = projection_width / window_width; + let height_ratio = projection_height / window_height; + + point * Vec2::new(width_ratio, height_ratio) +} + +#[derive(SystemParam)] +pub struct UnprojectManager<'w, 's> { + windows: Query<'w, 's, &'static Window, With<PrimaryWindow>>, + camera: Query<'w, 's, &'static OrthographicProjection, With<CameraUIKayak>>, +} + +#[derive(Debug, Error)] +#[error("Invalid ECS state to perform unprojection")] +pub struct InvalidUnprojection; + +impl<'w, 's> UnprojectManager<'w, 's> { + pub fn unproject(&self, position: Vec2) -> Result<Vec2, InvalidUnprojection> { + let window = self.windows.get_single().map_err(|_| InvalidUnprojection)?; + let camera = self.camera.get_single().map_err(|_| InvalidUnprojection)?; + + Ok(unproject_from_physical( + position, + window, + camera, + )) + } +} -- GitLab