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