1#![warn(missing_docs)]
3use fidget_core::render::{ImageSize, VoxelSize};
4use nalgebra::{
5 Const, DefaultAllocator, DimNameAdd, DimNameSum, Matrix3, Matrix4, OMatrix,
6 OPoint, OVector, Point2, Point3, U1, Vector2, Vector3,
7 allocator::Allocator,
8};
9use serde::{Deserialize, Serialize};
10
11#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
55pub struct View2 {
56 center: Vector2<f32>,
57 scale: f32,
58}
59
60impl Default for View2 {
61 fn default() -> Self {
62 Self {
63 scale: 1.0,
64 center: Vector2::new(0.0, 0.0),
65 }
66 }
67}
68
69impl View2 {
70 pub fn from_center_and_scale(center: Vector2<f32>, scale: f32) -> Self {
75 Self { center, scale }
76 }
77
78 pub fn components(&self) -> (Vector2<f32>, f32) {
80 (self.center, self.scale)
81 }
82
83 pub fn from_components(center: Vector2<f32>, scale: f32) -> Self {
88 Self::from_center_and_scale(center, scale)
89 }
90
91 fn scale_mat(&self) -> Matrix3<f32> {
93 Matrix3::new_scaling(self.scale)
94 }
95
96 fn translation_mat(&self) -> Matrix3<f32> {
98 Matrix3::new_translation(&self.center)
99 }
100
101 pub fn world_to_model(&self) -> Matrix3<f32> {
103 self.translation_mat() * self.scale_mat()
104 }
105
106 pub fn transform_point(&self, p: &Point2<f32>) -> Point2<f32> {
108 self.world_to_model().transform_point(p)
109 }
110
111 pub fn begin_translate(&self, start: Point2<f32>) -> TranslateHandle<2> {
113 let initial_mat = self.world_to_model();
114 TranslateHandle {
115 start: initial_mat.transform_point(&start),
116 initial_mat,
117 initial_center: self.center,
118 }
119 }
120
121 pub fn translate(
123 &mut self,
124 h: &TranslateHandle<2>,
125 pos: Point2<f32>,
126 ) -> bool {
127 let next_center = h.center(pos);
128 let changed = next_center != self.center;
129 self.center = next_center;
130 changed
131 }
132
133 pub fn zoom(&mut self, amount: f32, pos: Option<Point2<f32>>) -> bool {
137 match pos {
138 Some(before) => {
139 let pos_before = self.transform_point(&before);
140 self.scale *= amount;
141 let pos_after = self.transform_point(&before);
142 self.center += pos_before - pos_after;
143 }
144 None => {
145 self.scale *= amount;
146 }
147 }
148 amount != 1.0
149 }
150}
151
152#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
154pub struct View3 {
155 center: Vector3<f32>,
156 scale: f32,
157 yaw: f32,
158 pitch: f32,
159}
160
161impl Default for View3 {
162 fn default() -> Self {
163 Self {
164 center: Vector3::new(0.0, 0.0, 0.0),
165 scale: 1.0,
166 yaw: 0.0,
167 pitch: 0.0,
168 }
169 }
170}
171
172impl View3 {
179 pub fn from_center_and_scale(center: Vector3<f32>, scale: f32) -> Self {
184 Self {
185 center,
186 scale,
187 yaw: 0.0,
188 pitch: 0.0,
189 }
190 }
191
192 pub fn components(&self) -> (Vector3<f32>, f32, f32, f32) {
194 (self.center, self.scale, self.yaw, self.pitch)
195 }
196
197 pub fn from_components(
199 center: Vector3<f32>,
200 scale: f32,
201 yaw: f32,
202 pitch: f32,
203 ) -> Self {
204 Self {
205 center,
206 scale,
207 yaw,
208 pitch,
209 }
210 }
211
212 pub fn world_to_model(&self) -> Matrix4<f32> {
214 self.translation_mat() * self.rot_mat() * self.scale_mat()
215 }
216
217 pub fn transform_point(&self, p: &Point3<f32>) -> Point3<f32> {
219 self.world_to_model().transform_point(p)
220 }
221
222 pub fn begin_translate(&self, start: Point3<f32>) -> TranslateHandle<3> {
224 let initial_mat = self.world_to_model();
225 TranslateHandle {
226 start: initial_mat.transform_point(&start),
227 initial_mat,
228 initial_center: self.center,
229 }
230 }
231
232 fn scale_mat(&self) -> Matrix4<f32> {
234 Matrix4::new_scaling(self.scale)
235 }
236
237 fn rot_mat(&self) -> Matrix4<f32> {
239 Matrix4::from_axis_angle(
240 &nalgebra::Unit::new_normalize(Vector3::new(0.0, 0.0, 1.0)),
241 self.yaw,
242 ) * Matrix4::from_axis_angle(
243 &nalgebra::Unit::new_normalize(Vector3::new(1.0, 0.0, 0.0)),
244 self.pitch,
245 )
246 }
247
248 fn translation_mat(&self) -> Matrix4<f32> {
250 Matrix4::new_translation(&self.center)
251 }
252
253 pub fn translate(
255 &mut self,
256 h: &TranslateHandle<3>,
257 pos: Point3<f32>,
258 ) -> bool {
259 let next_center = h.center(pos);
260 let changed = next_center != self.center;
261 self.center = next_center;
262 changed
263 }
264
265 pub fn zoom(&mut self, amount: f32, pos: Option<Point3<f32>>) -> bool {
269 match pos {
270 Some(before) => {
271 let pos_before = self.transform_point(&before);
272 self.scale *= amount;
273 let pos_after = self.transform_point(&before);
274 self.center += pos_before - pos_after;
275 }
276 None => {
277 self.scale *= amount;
278 }
279 }
280 amount != 1.0
281 }
282
283 pub fn begin_rotate(&self, start: Point3<f32>) -> RotateHandle {
285 RotateHandle {
286 start,
287 initial_yaw: self.yaw,
288 initial_pitch: self.pitch,
289 }
290 }
291
292 pub fn rotate(&mut self, h: &RotateHandle, pos: Point3<f32>) -> bool {
296 let next_yaw = h.yaw(pos.x);
297 let next_pitch = h.pitch(pos.y);
298 let changed = (next_yaw != self.yaw) || (next_pitch != self.pitch);
299 self.yaw = next_yaw;
300 self.pitch = next_pitch;
301 changed
302 }
303}
304
305#[derive(Copy, Clone)]
307pub struct RotateHandle {
308 start: Point3<f32>,
310 initial_yaw: f32,
311 initial_pitch: f32,
312}
313
314const ROTATE_SPEED: f32 = 2.0;
316
317impl RotateHandle {
318 fn yaw(&self, x: f32) -> f32 {
319 (self.initial_yaw + (self.start.x - x) * ROTATE_SPEED)
320 % std::f32::consts::TAU
321 }
322 fn pitch(&self, y: f32) -> f32 {
323 (self.initial_pitch + (y - self.start.y) * ROTATE_SPEED)
324 .clamp(0.0, std::f32::consts::PI)
325 }
326}
327
328#[derive(Copy, Clone)]
330pub struct TranslateHandle<const N: usize>
331where
332 Const<N>: DimNameAdd<U1>,
333 DefaultAllocator:
334 Allocator<DimNameSum<Const<N>, U1>, DimNameSum<Const<N>, U1>>,
335 OMatrix<
336 f32,
337 <Const<N> as DimNameAdd<Const<1>>>::Output,
338 <Const<N> as DimNameAdd<Const<1>>>::Output,
339 >: Copy,
340{
341 start: OPoint<f32, Const<N>>,
343 initial_mat: OMatrix<
345 f32,
346 <Const<N> as DimNameAdd<Const<1>>>::Output,
347 <Const<N> as DimNameAdd<Const<1>>>::Output,
348 >,
349 initial_center: OVector<f32, Const<N>>,
351}
352
353impl TranslateHandle<2> {
354 fn center(&self, pos: Point2<f32>) -> Vector2<f32> {
356 let pos_model = self.initial_mat.transform_point(&pos);
357 self.initial_center - (pos_model - self.start)
358 }
359}
360
361impl TranslateHandle<3> {
362 fn center(&self, pos: Point3<f32>) -> Vector3<f32> {
364 let pos_model = self.initial_mat.transform_point(&pos);
365 self.initial_center - (pos_model - self.start)
366 }
367}
368
369#[derive(Copy, Clone)]
383pub struct Canvas2 {
384 view: View2,
385 image_size: ImageSize,
386 drag_start: Option<TranslateHandle<2>>,
387}
388
389#[derive(Copy, Clone, Debug)]
391pub struct CursorState<D> {
392 pub screen_pos: Point2<i32>,
394
395 pub drag: D,
399}
400
401impl Canvas2 {
402 pub fn new(image_size: ImageSize) -> Self {
404 Self {
405 view: View2::default(),
406 image_size,
407 drag_start: None,
408 }
409 }
410
411 pub fn components(&self) -> (View2, ImageSize) {
413 (self.view, self.image_size)
414 }
415
416 pub fn from_components(view: View2, image_size: ImageSize) -> Self {
418 Self {
419 view,
420 image_size,
421 drag_start: None,
422 }
423 }
424
425 #[must_use]
432 pub fn interact(
433 &mut self,
434 image_size: ImageSize,
435 cursor_state: Option<CursorState<bool>>,
436 scroll: f32,
437 ) -> bool {
438 self.image_size = image_size;
439 let mut changed = false;
440 let pos_screen = match cursor_state {
441 Some(cs) => {
442 if cs.drag {
443 self.begin_drag(cs.screen_pos); changed |= self.drag(cs.screen_pos);
445 } else {
446 self.end_drag();
447 }
448 Some(cs.screen_pos)
449 }
450 _ => {
451 self.end_drag();
452 None
453 }
454 };
455 changed |= self.zoom(scroll, pos_screen);
456 changed
457 }
458
459 pub fn view(&self) -> View2 {
461 self.view
462 }
463
464 pub fn image_size(&self) -> ImageSize {
466 self.image_size
467 }
468
469 pub fn resize(&mut self, image_size: ImageSize) {
471 self.image_size = image_size;
472 }
473
474 pub fn begin_drag(&mut self, pos_screen: Point2<i32>) {
478 if self.drag_start.is_none() {
479 let pos_world = self.image_size.transform_point(pos_screen);
480 self.drag_start = Some(self.view.begin_translate(pos_world));
481 }
482 }
483
484 #[must_use]
490 pub fn drag(&mut self, pos_screen: Point2<i32>) -> bool {
491 if let Some(prev) = &self.drag_start {
492 let pos_world = self.image_size.transform_point(pos_screen);
493 self.view.translate(prev, pos_world)
494 } else {
495 false
496 }
497 }
498
499 pub fn end_drag(&mut self) {
501 self.drag_start = None;
502 }
503
504 #[must_use]
511 pub fn zoom(
512 &mut self,
513 amount: f32,
514 pos_screen: Option<Point2<i32>>,
515 ) -> bool {
516 let pos_world = pos_screen.map(|p| self.image_size.transform_point(p));
517 self.view.zoom((amount / 100.0).exp2(), pos_world)
518 }
519}
520
521#[derive(Copy, Clone)]
525pub struct Canvas3 {
526 view: View3,
527 image_size: VoxelSize,
528 drag_start: Option<Drag3>,
529}
530
531#[derive(Copy, Clone)]
533pub enum DragMode {
534 Pan,
536 Rotate,
538}
539
540#[derive(Copy, Clone)]
541enum Drag3 {
542 Pan(TranslateHandle<3>),
543 Rotate(RotateHandle),
544}
545
546#[allow(missing_docs)]
549impl Canvas3 {
550 pub fn new(image_size: VoxelSize) -> Self {
551 Self {
552 view: View3::default(),
553 image_size,
554 drag_start: None,
555 }
556 }
557
558 pub fn components(&self) -> (View3, VoxelSize) {
559 (self.view, self.image_size)
560 }
561
562 pub fn from_components(view: View3, image_size: VoxelSize) -> Self {
563 Self {
564 view,
565 image_size,
566 drag_start: None,
567 }
568 }
569
570 pub fn image_size(&self) -> VoxelSize {
571 self.image_size
572 }
573
574 #[must_use]
575 pub fn interact(
576 &mut self,
577 image_size: VoxelSize,
578 cursor_state: Option<CursorState<Option<DragMode>>>,
579 scroll: f32,
580 ) -> bool {
581 let mut changed = false;
582 self.image_size = image_size;
583 let pos_screen = match cursor_state {
584 Some(cs) => {
585 if let Some(drag_mode) = cs.drag {
586 self.begin_drag(cs.screen_pos, drag_mode); changed |= self.drag(cs.screen_pos);
588 } else {
589 self.end_drag();
590 }
591 Some(cs.screen_pos)
592 }
593 _ => {
594 self.end_drag();
595 None
596 }
597 };
598 changed |= self.zoom(scroll, pos_screen);
599 changed
600 }
601
602 pub fn view(&self) -> View3 {
603 self.view
604 }
605
606 pub fn begin_drag(&mut self, pos_screen: Point2<i32>, drag_mode: DragMode) {
607 if self.drag_start.is_none() {
608 let pos_world = self.screen_to_world(pos_screen);
609 self.drag_start = Some(match drag_mode {
610 DragMode::Pan => {
611 Drag3::Pan(self.view.begin_translate(pos_world))
612 }
613 DragMode::Rotate => {
614 Drag3::Rotate(self.view.begin_rotate(pos_world))
615 }
616 });
617 }
618 }
619
620 fn screen_to_world(&self, pos_screen: Point2<i32>) -> Point3<f32> {
621 self.image_size.transform_point(Point3::new(
622 pos_screen.x,
623 pos_screen.y,
624 0,
625 ))
626 }
627
628 #[must_use]
629 pub fn drag(&mut self, pos_screen: Point2<i32>) -> bool {
630 let pos_world = self.screen_to_world(pos_screen);
631 match &self.drag_start {
632 Some(Drag3::Pan(prev)) => self.view.translate(prev, pos_world),
633 Some(Drag3::Rotate(prev)) => self.view.rotate(prev, pos_world),
634 None => false,
635 }
636 }
637
638 pub fn end_drag(&mut self) {
639 self.drag_start = None;
640 }
641
642 #[must_use]
643 pub fn zoom(
644 &mut self,
645 amount: f32,
646 pos_screen: Option<Point2<i32>>,
647 ) -> bool {
648 let pos_world = pos_screen.map(|p| self.screen_to_world(p));
649 self.view.zoom((amount / 100.0).exp2(), pos_world)
650 }
651}