1#![allow(clippy::collapsible_else_if)]
25#![no_std]
26
27use kiss3d::{
28 camera::Camera,
29 event::{Action, Key, Modifiers, MouseButton, TouchAction, WindowEvent},
30 nalgebra::{Isometry3, Matrix4, Point2, Point3, UnitQuaternion, Vector3},
31 resource::ShaderUniform,
32 window::Canvas,
33};
34use trackball::{First, Fixed, Frame, Image, Orbit, Scale, Scope, Slide, Touch};
35
36pub use kiss3d;
37pub use trackball;
38
39mod input;
40pub use input::*;
41
42#[derive(Clone)]
76pub struct Trackball {
77 pub input: Input<f32>,
79 pub frame: Frame<f32>,
81 pub reset: Frame<f32>,
83 pub scope: Scope<f32>,
85
86 image: Image<f32>,
87 first: First<f32>,
88 orbit: Orbit<f32>,
89 scale: Scale<f32>,
90 slide: Slide<f32>,
91 touch: Touch<Option<u64>, f32>,
92 mouse: Option<Point2<f64>>,
93}
94
95impl Trackball {
96 #[must_use]
103 pub fn new(target: Point3<f32>, eye: &Point3<f32>, up: &Vector3<f32>) -> Trackball {
104 let frame = Frame::look_at(target, eye, up);
105 let reset = frame;
106 let scope = Scope::default();
107 let mut image = Image::new(&Frame::default(), &scope, Point2::new(800.0, 600.0));
108 image.set_passive(true);
109 image.compute(frame, scope);
110 Self {
111 input: Input::default(),
112 first: First::default(),
113 frame,
114 reset,
115 scope,
116 image,
117 orbit: Orbit::default(),
118 scale: Scale::default(),
119 slide: Slide::default(),
120 touch: Touch::default(),
121 mouse: Option::default(),
122 }
123 }
124 pub fn new_with_frustum(
131 target: Point3<f32>,
132 eye: &Point3<f32>,
133 up: &Vector3<f32>,
134 fov: impl Into<Fixed<f32>>,
135 znear: f32,
136 zfar: f32,
137 ) -> Trackball {
138 let mut trackball = Self::new(target, eye, up);
139 trackball.scope.set_fov(fov);
140 trackball.scope.set_clip_planes(znear, zfar);
141 trackball
142 }
143 fn handle_touch(
144 &mut self,
145 _canvas: &Canvas,
146 id: u64,
147 x: f64,
148 y: f64,
149 action: TouchAction,
150 _modifiers: Modifiers,
151 ) {
152 #[allow(clippy::cast_possible_truncation)]
153 let pos = Point2::new(x as f32, y as f32);
154 match action {
155 TouchAction::Start | TouchAction::Move => {
156 if action == TouchAction::Start {
157 self.slide.discard();
158 }
159 if let Some((num, pos, rot, rat)) = self.touch.compute(Some(id), pos, 0) {
160 if self.first.enabled() {
161 if let Some(vec) = self.slide.compute(pos) {
162 if let Some((pitch, yaw, yaw_axis)) =
163 self.first.compute(&vec, self.image.max())
164 {
165 self.frame.look_around(pitch, yaw, yaw_axis);
166 }
167 }
168 } else {
169 if num == 1 {
170 if let Some(rot) = self.orbit.compute(&pos, self.image.max()) {
171 self.frame.local_orbit(&rot);
172 }
173 } else {
174 if let Some(vec) = self.slide.compute(pos) {
175 self.frame.local_slide(&self.image.project_vec(&vec));
176 }
177 if num == 2 {
178 let pos = self.image.project_pos(&pos);
179 let rot = UnitQuaternion::from_axis_angle(
180 &self.frame.local_roll_axis(),
181 rot,
182 );
183 self.frame.local_orbit_around(&rot, &pos);
184 self.frame.local_scale_around(rat, &pos);
185 }
186 }
187 }
188 }
189 }
190 TouchAction::End | TouchAction::Cancel => {
191 if let Some((_num, pos)) = self.touch.discard(Some(id)) {
192 self.frame.local_slide(&self.image.project_pos(&pos).coords);
193 }
194 self.orbit.discard();
195 self.slide.discard();
196 }
197 }
198 }
199 fn handle_mouse_button(
200 &mut self,
201 _canvas: &Canvas,
202 button: MouseButton,
203 action: Action,
204 _modifiers: Modifiers,
205 ) {
206 if !self.first.enabled() {
207 if Some(button) == self.input.orbit_button() {
208 if action == Action::Press {
209 self.touch.compute(None, *self.image.pos(), 0);
210 } else {
211 self.orbit.discard();
212 if let Some((_num, pos)) = self.touch.discard(None) {
213 self.frame.local_slide(&self.image.project_pos(&pos).coords);
214 }
215 }
216 }
217 if Some(button) == self.input.slide_button() {
218 if action == Action::Press {
219 self.slide.compute(*self.image.pos());
220 } else {
221 self.slide.discard();
222 }
223 }
224 }
225 }
226 fn handle_cursor_pos(&mut self, canvas: &Canvas, x: f64, y: f64, modifiers: Modifiers) {
227 let pos = Point2::new(x, y);
228 let is_eq = |old| old == pos || old == Point2::new(pos.x.floor(), pos.y.floor());
229 if self.mouse.replace(pos).is_none_or(is_eq) {
230 return;
231 }
232 let (pos, max) = (pos.cast(), *self.image.max());
233 if self.first.enabled() {
234 if self.touch.fingers() == 0 {
235 if let Some(vec) = self.slide.compute(pos) {
236 canvas.hide_cursor(true);
237 canvas.set_cursor_grab(true);
238 if let Some((pitch, yaw, yaw_axis)) = self.first.compute(&vec, &max) {
239 self.frame.look_around(pitch, yaw, yaw_axis);
240 }
241 }
242 if pos.y <= 0.0 {
243 canvas.set_cursor_position(x, f64::from(max.y) - 2.0);
244 self.slide.discard();
245 }
246 if pos.x <= 0.0 {
247 canvas.set_cursor_position(f64::from(max.x) - 2.0, y);
248 self.slide.discard();
249 }
250 if pos.x >= max.x - 1.0 {
251 canvas.set_cursor_position(1.0, y);
252 self.slide.discard();
253 }
254 if pos.y >= max.y - 1.0 {
255 canvas.set_cursor_position(x, 1.0);
256 self.slide.discard();
257 }
258 }
259 } else {
260 self.image.set_pos(pos);
261 let orbit = self.input.orbit_button().is_some_and(|button| {
262 canvas.get_mouse_button(button) == Action::Press
263 && self.input.orbit_modifiers().is_none_or(|m| m == modifiers)
264 });
265 let slide = self.input.slide_button().is_some_and(|button| {
266 canvas.get_mouse_button(button) == Action::Press
267 && self.input.slide_modifiers().is_none_or(|m| m == modifiers)
268 });
269 if orbit && slide {
270 self.orbit.discard();
271 self.slide.discard();
272 }
273 if orbit {
274 if let Some(pos) = self.touch.compute(None, pos, 0).map(|val| val.1) {
275 if let Some(rot) = self.orbit.compute(&pos, &max) {
276 self.frame.local_orbit(&rot);
277 }
278 }
279 }
280 if slide {
281 if let Some(vec) = self.slide.compute(pos) {
282 self.frame.local_slide(&self.image.project_vec(&vec));
283 }
284 }
285 }
286 }
287 fn handle_scroll(&mut self, _canvas: &Canvas, _dx: f64, dy: f64, _modifiers: Modifiers) {
288 self.frame.local_scale_around(
289 #[allow(clippy::cast_possible_truncation)]
290 self.scale.compute(dy as f32),
291 &self.image.project_pos(self.image.pos()),
292 );
293 }
294 fn handle_key(&mut self, canvas: &Canvas, key: Key, action: Action, _modifiers: Modifiers) {
295 if Some(key) == self.input.first_key() {
296 let mid = self.image.max() * 0.5;
297 if action == Action::Press {
298 if !self.first.enabled() {
299 self.first.capture(self.frame.yaw_axis());
300 self.image.set_pos(mid);
301 }
302 } else {
303 self.slide.discard();
304 self.first.discard();
305 if self.touch.fingers() == 0 {
306 canvas.set_cursor_position(mid.x.into(), mid.y.into());
307 canvas.hide_cursor(false);
308 canvas.set_cursor_grab(false);
309 }
310 }
311 } else if action == Action::Press {
312 if Some(key) == self.input.ortho_key() {
313 self.scope.set_ortho(!self.scope.ortho());
314 } else if Some(key) == self.input.reset_key() {
315 self.frame = self.reset;
316 }
317 }
318 }
319 fn handle_framebuffer_size(&mut self, _canvas: &Canvas, w: u32, h: u32) {
320 self.image.set_max(Point2::new(w, h).cast());
321 }
322}
323
324impl Camera for Trackball {
325 fn clip_planes(&self) -> (f32, f32) {
326 self.scope.clip_planes(self.frame.distance())
327 }
328 fn view_transform(&self) -> Isometry3<f32> {
329 *self.image.view_isometry()
330 }
331 fn eye(&self) -> Point3<f32> {
332 self.frame.eye()
333 }
334 fn handle_event(&mut self, canvas: &Canvas, event: &WindowEvent) {
335 match *event {
336 WindowEvent::Touch(id, x, y, action, modifiers) => {
337 self.handle_touch(canvas, id, x, y, action, modifiers);
338 }
339 WindowEvent::MouseButton(button, action, modifiers) => {
340 self.handle_mouse_button(canvas, button, action, modifiers);
341 }
342 WindowEvent::CursorPos(x, y, modifiers) => {
343 self.handle_cursor_pos(canvas, x, y, modifiers);
344 }
345 WindowEvent::Scroll(dx, dy, modifiers) => {
346 self.handle_scroll(canvas, dx, dy, modifiers);
347 }
348 WindowEvent::Key(key, action, modifiers) => {
349 self.handle_key(canvas, key, action, modifiers);
350 }
351 WindowEvent::FramebufferSize(w, h) => {
352 self.handle_framebuffer_size(canvas, w, h);
353 }
354 _ => {}
355 }
356 }
357 #[inline]
358 fn upload(
359 &self,
360 _: usize,
361 proj: &mut ShaderUniform<Matrix4<f32>>,
362 view: &mut ShaderUniform<Matrix4<f32>>,
363 ) {
364 proj.upload(self.image.projection());
365 view.upload(self.image.view());
366 }
367 fn transformation(&self) -> Matrix4<f32> {
368 *self.image.transformation()
369 }
370 fn inverse_transformation(&self) -> Matrix4<f32> {
371 *self.image.inverse_transformation()
372 }
373 fn update(&mut self, _: &Canvas) {
374 self.image.compute(self.frame, self.scope);
375 }
376}