Skip to main content

dioxus_three/
input.rs

1//! Input handling and raycasting support for Dioxus Three
2//!
3//! Provides pointer event handling, raycasting for 3D object selection,
4//! and gesture recognition for touch devices.
5
6/// A unique identifier for entities in the 3D scene
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub struct EntityId(pub usize);
9
10impl std::fmt::Display for EntityId {
11    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12        write!(f, "Entity({})", self.0)
13    }
14}
15
16/// Information about a raycast hit
17#[derive(Debug, Clone)]
18pub struct HitInfo {
19    /// The entity that was hit
20    pub entity_id: EntityId,
21    /// The intersection point in world coordinates
22    pub point: Vector3,
23    /// The surface normal at the intersection
24    pub normal: Vector3,
25    /// UV coordinates at the intersection point (if available)
26    pub uv: Option<Vector2>,
27    /// Distance from the ray origin to the hit point
28    pub distance: f32,
29    /// The index of the face that was hit (if applicable)
30    pub face_index: Option<usize>,
31    /// Instance ID for instanced meshes
32    pub instance_id: Option<usize>,
33}
34
35/// 2D vector for UV coordinates, mouse positions, etc.
36#[derive(Debug, Clone, Copy, Default, PartialEq)]
37pub struct Vector2 {
38    pub x: f32,
39    pub y: f32,
40}
41
42impl Vector2 {
43    pub fn new(x: f32, y: f32) -> Self {
44        Self { x, y }
45    }
46}
47
48/// 3D vector for positions, normals, etc.
49#[derive(Debug, Clone, Copy, Default, PartialEq)]
50pub struct Vector3 {
51    pub x: f32,
52    pub y: f32,
53    pub z: f32,
54}
55
56impl Vector3 {
57    pub const ZERO: Self = Self {
58        x: 0.0,
59        y: 0.0,
60        z: 0.0,
61    };
62    pub const UP: Self = Self {
63        x: 0.0,
64        y: 1.0,
65        z: 0.0,
66    };
67    pub const RIGHT: Self = Self {
68        x: 1.0,
69        y: 0.0,
70        z: 0.0,
71    };
72    pub const FORWARD: Self = Self {
73        x: 0.0,
74        y: 0.0,
75        z: 1.0,
76    };
77
78    pub fn new(x: f32, y: f32, z: f32) -> Self {
79        Self { x, y, z }
80    }
81
82    pub fn length(&self) -> f32 {
83        (self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
84    }
85
86    pub fn normalize(&self) -> Self {
87        let len = self.length();
88        if len > 0.0 {
89            Self {
90                x: self.x / len,
91                y: self.y / len,
92                z: self.z / len,
93            }
94        } else {
95            *self
96        }
97    }
98
99    pub fn distance(&self, other: &Self) -> f32 {
100        (*self - *other).length()
101    }
102}
103
104impl std::ops::Add for Vector3 {
105    type Output = Self;
106    fn add(self, rhs: Self) -> Self::Output {
107        Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
108    }
109}
110
111impl std::ops::Sub for Vector3 {
112    type Output = Self;
113    fn sub(self, rhs: Self) -> Self::Output {
114        Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
115    }
116}
117
118impl std::ops::Mul<f32> for Vector3 {
119    type Output = Self;
120    fn mul(self, rhs: f32) -> Self::Output {
121        Self::new(self.x * rhs, self.y * rhs, self.z * rhs)
122    }
123}
124
125/// Mouse button types
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub enum MouseButton {
128    Left,
129    Middle,
130    Right,
131    Back,
132    Forward,
133}
134
135/// Cursor styles for pointer events
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
137pub enum CursorStyle {
138    #[default]
139    Default,
140    Pointer,
141    Grab,
142    Grabbing,
143    Crosshair,
144    Move,
145    Text,
146    Wait,
147    Help,
148    None,
149}
150
151impl CursorStyle {
152    pub fn as_css(&self) -> &'static str {
153        match self {
154            CursorStyle::Default => "default",
155            CursorStyle::Pointer => "pointer",
156            CursorStyle::Grab => "grab",
157            CursorStyle::Grabbing => "grabbing",
158            CursorStyle::Crosshair => "crosshair",
159            CursorStyle::Move => "move",
160            CursorStyle::Text => "text",
161            CursorStyle::Wait => "wait",
162            CursorStyle::Help => "help",
163            CursorStyle::None => "none",
164        }
165    }
166}
167
168/// Configuration for raycasting behavior
169#[derive(Debug, Clone, Copy, PartialEq)]
170pub struct RaycastConfig {
171    /// Enable raycasting
172    pub enabled: bool,
173    /// Whether to recursively check children
174    pub recursive: bool,
175    /// Maximum distance to check
176    pub max_distance: f32,
177    /// Layer mask for filtering (if implemented)
178    pub layer_mask: Option<u32>,
179}
180
181impl Default for RaycastConfig {
182    fn default() -> Self {
183        Self {
184            enabled: true,
185            recursive: true,
186            max_distance: 1000.0,
187            layer_mask: None,
188        }
189    }
190}
191
192/// Pointer event data
193#[derive(Debug, Clone)]
194pub struct PointerEvent {
195    /// The hit information (if any)
196    pub hit: Option<HitInfo>,
197    /// Screen position of the pointer
198    pub screen_position: Vector2,
199    /// Normalized device coordinates (-1 to 1)
200    pub ndc_position: Vector2,
201    /// Mouse button that triggered the event (if applicable)
202    pub button: Option<MouseButton>,
203    /// Whether shift key is pressed
204    pub shift_key: bool,
205    /// Whether ctrl/cmd key is pressed
206    pub ctrl_key: bool,
207    /// Whether alt key is pressed
208    pub alt_key: bool,
209}
210
211impl PointerEvent {
212    /// Set the cursor style for this interaction
213    /// (Note: Actual cursor change happens via JavaScript)
214    pub fn set_cursor(&self, _style: CursorStyle) {
215        // This is a placeholder - actual implementation will
216        // communicate with JavaScript to change cursor
217    }
218}
219
220/// Pointer drag event data
221#[derive(Debug, Clone)]
222pub struct PointerDragEvent {
223    /// Current hit information (if any)
224    pub hit: Option<HitInfo>,
225    /// Initial hit when drag started
226    pub start_hit: Option<HitInfo>,
227    /// Current screen position
228    pub screen_position: Vector2,
229    /// Screen position where drag started
230    pub start_screen_position: Vector2,
231    /// Current world position (projected to hit plane)
232    pub world_position: Vector3,
233    /// World position where drag started
234    pub start_world_position: Vector3,
235    /// Delta movement since last frame
236    pub delta: Vector2,
237    /// Total delta since drag started
238    pub total_delta: Vector2,
239    /// Mouse button being held
240    pub button: MouseButton,
241}
242
243/// Gesture types for touch devices
244#[derive(Debug, Clone, Copy, PartialEq)]
245pub enum GestureEvent {
246    /// Pinch gesture with scale factor and center point
247    Pinch { scale: f32, center: Vector2 },
248    /// Two-finger rotation
249    Rotate { angle: f32, center: Vector2 },
250    /// Two-finger pan
251    Pan { delta: Vector2 },
252}
253
254/// Raycaster for manual raycasting operations
255#[derive(Debug, Clone)]
256pub struct Raycaster {
257    pub origin: Vector3,
258    pub direction: Vector3,
259    pub near: f32,
260    pub far: f32,
261}
262
263impl Raycaster {
264    pub fn new(origin: Vector3, direction: Vector3) -> Self {
265        Self {
266            origin,
267            direction: direction.normalize(),
268            near: 0.0,
269            far: 1000.0,
270        }
271    }
272
273    /// Cast a ray from camera through screen position
274    pub fn from_camera(camera: &Camera, _screen_pos: Vector2) -> Self {
275        // This would be implemented in platform-specific code
276        // For now, placeholder
277        Self::new(camera.position, Vector3::FORWARD)
278    }
279
280    /// Get a point at a specific distance along the ray
281    pub fn at(&self, distance: f32) -> Vector3 {
282        self.origin + self.direction * distance
283    }
284}
285
286/// Camera information for raycasting
287#[derive(Debug, Clone)]
288pub struct Camera {
289    pub position: Vector3,
290    pub target: Vector3,
291    pub up: Vector3,
292    pub fov: f32,
293    pub aspect: f32,
294    pub near: f32,
295    pub far: f32,
296}
297
298impl Camera {
299    pub fn new(position: Vector3, target: Vector3) -> Self {
300        Self {
301            position,
302            target,
303            up: Vector3::UP,
304            fov: 75.0_f32.to_radians(),
305            aspect: 1.0,
306            near: 0.1,
307            far: 1000.0,
308        }
309    }
310}
311
312/// Event handler types for pointer events
313pub type PointerEventHandler = Box<dyn Fn(PointerEvent)>;
314pub type PointerDragEventHandler = Box<dyn Fn(PointerDragEvent)>;
315pub type GestureEventHandler = Box<dyn Fn(GestureEvent)>;
316
317/// Input state tracking
318#[derive(Debug, Clone, Default)]
319pub struct InputState {
320    /// Current pointer position
321    pub pointer_position: Vector2,
322    /// Whether pointer is currently down
323    pub pointer_down: bool,
324    /// Current cursor style
325    pub cursor_style: CursorStyle,
326    /// Currently pressed keys
327    pub keys_pressed: Vec<String>,
328    /// Mouse delta since last frame
329    pub mouse_delta: Vector2,
330}
331
332impl InputState {
333    pub fn is_key_pressed(&self, key: &str) -> bool {
334        self.keys_pressed
335            .iter()
336            .any(|k| k.eq_ignore_ascii_case(key))
337    }
338}
339
340#[cfg(test)]
341mod tests {
342    use super::*;
343
344    #[test]
345    fn test_vector3_operations() {
346        let a = Vector3::new(1.0, 2.0, 3.0);
347        let b = Vector3::new(4.0, 5.0, 6.0);
348
349        let sum = a + b;
350        assert_eq!(sum.x, 5.0);
351        assert_eq!(sum.y, 7.0);
352        assert_eq!(sum.z, 9.0);
353
354        let diff = b - a;
355        assert_eq!(diff.x, 3.0);
356        assert_eq!(diff.y, 3.0);
357        assert_eq!(diff.z, 3.0);
358
359        let scaled = a * 2.0;
360        assert_eq!(scaled.x, 2.0);
361        assert_eq!(scaled.y, 4.0);
362        assert_eq!(scaled.z, 6.0);
363    }
364
365    #[test]
366    fn test_vector3_distance() {
367        let a = Vector3::new(0.0, 0.0, 0.0);
368        let b = Vector3::new(3.0, 4.0, 0.0);
369
370        assert_eq!(a.distance(&b), 5.0);
371    }
372
373    #[test]
374    fn test_entity_id_display() {
375        let id = EntityId(42);
376        assert_eq!(format!("{}", id), "Entity(42)");
377    }
378}