1use std::f32::consts::PI;
2
3use mraphics_core::Camera;
4use nalgebra::{Point3, Rotation3, Vector3};
5use winit::event::{ElementState, MouseButton, MouseScrollDelta, WindowEvent};
6
7#[derive(Debug)]
8pub enum OrbitControlState {
9 Wait,
10 Rotate([f64; 2]),
11 Move([f64; 2]),
12}
13
14struct MouseState {
15 position: [f64; 2],
16}
17
18pub struct OrbitControl {
19 pub state: OrbitControlState,
20 mouse_state: MouseState,
21
22 pub center: Point3<f32>,
23 start_center: Point3<f32>,
24 target_center: Point3<f32>,
25
26 pub enable_zoom: bool,
27 pub radius: f32,
28 pub scale: f32,
29 pub zoom_speed: f32,
30
31 pub enable_rotate: bool,
32 pub theta: f32,
33 pub phi: f32,
34 pub phi_max: f32,
35 pub phi_min: f32,
36 pub delta_angle_max: f32,
37 pub rotate_speed: f32,
38 pub rotate_ease: f32,
39 start_phi: f32,
40 start_theta: f32,
41 target_phi: f32,
42 target_theta: f32,
43
44 pub enable_move: bool,
45 pub move_speed: f32,
46 pub move_ease: f32,
47}
48
49impl Default for OrbitControl {
50 fn default() -> Self {
51 Self {
52 state: OrbitControlState::Wait,
53 mouse_state: MouseState {
54 position: [0.0, 0.0],
55 },
56
57 center: Point3::origin(),
58 start_center: Point3::origin(),
59 target_center: Point3::origin(),
60
61 enable_zoom: true,
62 radius: 5.0,
63 scale: 1.0,
64 zoom_speed: 1.1,
65
66 enable_rotate: true,
67 theta: 0.0,
68 phi: 0.0,
69 phi_max: PI / 2.0,
70 phi_min: -PI / 2.0,
71 delta_angle_max: 0.1,
72 rotate_speed: 0.01,
73 rotate_ease: 0.01,
74 start_phi: 0.0,
75 start_theta: 0.0,
76 target_phi: 0.0,
77 target_theta: 0.0,
78
79 enable_move: true,
80 move_speed: 0.001,
81 move_ease: 0.15,
82 }
83 }
84}
85
86impl OrbitControl {
87 pub fn new() -> Self {
88 Self {
89 ..Default::default()
90 }
91 }
92
93 pub fn update<C: Camera>(&mut self, camera: &mut C) {
94 let delta_phi = ((self.target_phi - self.phi) * self.rotate_ease).min(self.delta_angle_max);
95 let delta_theta =
96 ((self.target_theta - self.theta) * self.rotate_ease).min(self.delta_angle_max);
97
98 self.phi = (self.phi + delta_phi).min(self.phi_max).max(self.phi_min);
99 self.theta += delta_theta;
100
101 self.center = self.center.lerp(&self.target_center, self.move_ease);
102
103 camera.set_center(&self.compute_camera_center());
104
105 camera.look_at(&self.center);
106 }
107
108 pub fn handle_window_event(&mut self, event: &WindowEvent) {
109 match event {
110 WindowEvent::MouseInput { state, button, .. } => match state {
111 ElementState::Released => self.state = OrbitControlState::Wait,
112 ElementState::Pressed => match button {
113 MouseButton::Left => {
114 self.start_phi = self.phi;
115 self.start_theta = self.theta;
116 self.state = OrbitControlState::Rotate(self.mouse_state.position);
117 }
118 MouseButton::Middle => {
119 self.start_center.clone_from(&self.center);
120 self.state = OrbitControlState::Move(self.mouse_state.position)
121 }
122 _ => {}
123 },
124 },
125 WindowEvent::MouseWheel { delta, .. } => match delta {
126 MouseScrollDelta::LineDelta(_, y) => {
127 self.on_mouse_wheel(*y > 0.0);
128 }
129 MouseScrollDelta::PixelDelta(pos) => {
130 self.on_mouse_wheel(pos.y > 0.0);
131 }
132 },
133 WindowEvent::CursorMoved { position, .. } => {
134 self.mouse_state.position = [position.x, position.y];
135 self.on_mouse_move([position.x, position.y]);
136 }
137 _ => {}
138 }
139 }
140
141 pub fn rotate(&mut self, delta_phi: f32, delta_theta: f32) {
142 self.target_phi = delta_phi + self.start_phi;
143 self.target_theta = delta_theta + self.start_theta;
144 }
145
146 pub fn zoom_to(&mut self, scale: f32) {
147 self.scale = scale;
148 }
149
150 pub fn shift(&mut self, delta_x: f32, delta_y: f32) {
151 let camera_center = self.compute_camera_center();
152 let z_axis = self.center.coords - &camera_center;
153 let mut x_axis = z_axis.cross(&Vector3::y());
154 let mut y_axis = x_axis.cross(&z_axis);
155
156 x_axis.set_magnitude(delta_x * self.radius * self.scale);
157 y_axis.set_magnitude(delta_y * self.radius * self.scale);
158
159 self.target_center = self.start_center + &x_axis + &y_axis;
160 }
161
162 fn on_mouse_move(&mut self, pos: [f64; 2]) {
163 if let OrbitControlState::Rotate(start_pos) = self.state {
164 if !self.enable_rotate {
165 return;
166 }
167
168 let delta_phi = self.rotate_speed * (start_pos[1] - pos[1]) as f32;
169 let delta_theta = self.rotate_speed * (pos[0] - start_pos[0]) as f32;
170 self.rotate(delta_phi, delta_theta);
171 }
172
173 if let OrbitControlState::Move(start_pos) = self.state {
174 if !self.enable_move {
175 return;
176 }
177
178 let delta_x = self.move_speed * (start_pos[0] - pos[0]) as f32;
179 let delta_y = self.move_speed * (pos[1] - start_pos[1]) as f32;
180 self.shift(delta_x, delta_y);
181 }
182 }
183
184 fn on_mouse_wheel(&mut self, positive: bool) {
185 if !self.enable_zoom {
186 return;
187 }
188
189 if positive {
190 self.zoom_to(self.scale / self.zoom_speed);
191 } else {
192 self.zoom_to(self.scale * self.zoom_speed);
193 }
194 }
195
196 fn compute_camera_center(&self) -> Vector3<f32> {
197 let rotation = Rotation3::from_euler_angles(self.phi, -self.theta, 0.0);
198 rotation.transform_vector(&Vector3::new(0.0, 0.0, self.radius * self.scale))
199 + &self.center.coords
200 }
201}