Skip to main content

engvis_core/
input.rs

1use crate::camera::OrbitCamera;
2
3/// Represents a rectangular area in screen coordinates (pixels)
4#[derive(Debug, Clone, Default)]
5pub struct ViewportRect {
6    pub min_x: f64,
7    pub min_y: f64,
8    pub max_x: f64,
9    pub max_y: f64,
10}
11
12impl ViewportRect {
13    pub fn contains(&self, x: f64, y: f64) -> bool {
14        x >= self.min_x && x <= self.max_x && y >= self.min_y && y <= self.max_y
15    }
16}
17
18/// Tracks raw input state for orbit camera control
19#[derive(Debug, Clone)]
20pub struct InputState {
21    pub left_mouse_down: bool,
22    pub right_mouse_down: bool,
23    pub middle_mouse_down: bool,
24    pub last_cursor_x: f64,
25    pub last_cursor_y: f64,
26    pub cursor_x: f64,
27    pub cursor_y: f64,
28    pub scroll_delta: f32,
29    pub egui_wants_pointer: bool,
30    /// The central 3D viewport rect in screen pixels; only mouse events inside this area affect the camera
31    pub viewport_rect: ViewportRect,
32}
33
34impl Default for InputState {
35    fn default() -> Self {
36        Self {
37            left_mouse_down: false,
38            right_mouse_down: false,
39            middle_mouse_down: false,
40            last_cursor_x: 0.0,
41            last_cursor_y: 0.0,
42            cursor_x: 0.0,
43            cursor_y: 0.0,
44            scroll_delta: 0.0,
45            egui_wants_pointer: false,
46            viewport_rect: ViewportRect::default(),
47        }
48    }
49}
50
51impl InputState {
52    /// Check if the cursor is currently inside the 3D viewport area
53    fn cursor_in_viewport(&self) -> bool {
54        self.viewport_rect.contains(self.cursor_x, self.cursor_y)
55    }
56
57    /// Apply accumulated input to camera, then reset deltas.
58    /// Only applies camera operations when the cursor is inside the 3D viewport area
59    /// and egui does not want the pointer.
60    pub fn apply_to_camera(&mut self, camera: &mut OrbitCamera, _window_size: [u32; 2]) {
61        let in_viewport = self.cursor_in_viewport();
62
63        if self.left_mouse_down && !self.egui_wants_pointer && in_viewport {
64            let dx = (self.cursor_x - self.last_cursor_x) as f32;
65            let dy = (self.cursor_y - self.last_cursor_y) as f32;
66            let sensitivity = 0.005;
67            camera.orbit(-dx * sensitivity, -dy * sensitivity);
68        }
69        if self.right_mouse_down && !self.egui_wants_pointer && in_viewport {
70            let dx = (self.cursor_x - self.last_cursor_x) as f32;
71            let dy = (self.cursor_y - self.last_cursor_y) as f32;
72            let pan_speed = camera.distance * 0.002;
73            camera.pan(-dx * pan_speed, dy * pan_speed);
74        }
75        if self.scroll_delta.abs() > f32::EPSILON && !self.egui_wants_pointer && in_viewport {
76            camera.zoom(self.scroll_delta * 0.1);
77        }
78        self.scroll_delta = 0.0;
79        self.last_cursor_x = self.cursor_x;
80        self.last_cursor_y = self.cursor_y;
81    }
82}