Skip to content

Commit a54a297

Browse files
authored
Add common canvas abstraction (#291)
Interactions with 2D and 3D canvases are hard. Let's put them all in one place.
1 parent 39d1207 commit a54a297

12 files changed

Lines changed: 470 additions & 344 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
type and is multithreaded using Rayon, meaning it can work in WebAssembly.
77
- Changed 3D rendering and effects functions to use a new `GeometryBuffer` type,
88
which combines depth and normal data into a single image.
9+
- Add `fidget::gui` module, which defines `Canvas2` and `Canvas3`. The canvas
10+
types are stateful abstractions around a GUI canvas, with support for cursor
11+
interactions.
12+
- Change `ImageSize::transform_point` and `VoxelSize::transform_point` to take
13+
a point with `i32` coordinates (instead of `f32`). This helps us distinguish
14+
between screen (pixel) and world (floating-point) coordinates at the type
15+
level.
916

1017
# 0.3.5
1118
- Added `#[derive(Serialize, Deserialize)]` to `View2` and `View3`

demos/viewer/src/canvas2d.rs

Lines changed: 0 additions & 35 deletions
This file was deleted.

demos/viewer/src/canvas3d.rs

Lines changed: 0 additions & 60 deletions
This file was deleted.

demos/viewer/src/main.rs

Lines changed: 69 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,18 @@ use eframe::{
77
};
88
use env_logger::Env;
99
use log::{debug, error, info};
10-
use nalgebra::{Point2, Point3};
10+
use nalgebra::Point2;
1111
use notify::Watcher;
1212

13-
use fidget::render::{
14-
GeometryPixel, ImageRenderConfig, View2, View3, VoxelRenderConfig,
13+
use fidget::{
14+
gui::{Canvas2, Canvas3, CursorState, DragMode},
15+
render::{
16+
GeometryPixel, ImageRenderConfig, View2, View3, VoxelRenderConfig,
17+
},
1518
};
1619

1720
use std::{error::Error, path::Path};
1821

19-
mod canvas2d;
20-
mod canvas3d;
2122
mod draw2d;
2223
mod draw3d;
2324
mod script;
@@ -362,14 +363,8 @@ impl Mode3D {
362363

363364
#[derive(Copy, Clone)]
364365
enum RenderMode {
365-
TwoD {
366-
canvas: canvas2d::Canvas2D,
367-
mode: Mode2D,
368-
},
369-
ThreeD {
370-
canvas: canvas3d::Canvas3D,
371-
mode: Mode3D,
372-
},
366+
TwoD { canvas: Canvas2, mode: Mode2D },
367+
ThreeD { canvas: Canvas3, mode: Mode3D },
373368
}
374369

375370
impl RenderMode {
@@ -383,7 +378,7 @@ impl RenderMode {
383378
RenderMode::ThreeD { .. } => {
384379
*self = RenderMode::TwoD {
385380
// TODO get parameters from 3D camera here?
386-
canvas: Default::default(),
381+
canvas: Canvas2::new(fidget::render::ImageSize::new(0, 0)),
387382
mode: new_mode,
388383
};
389384
true
@@ -393,8 +388,11 @@ impl RenderMode {
393388
fn set_3d_mode(&mut self, new_mode: Mode3D) -> bool {
394389
match self {
395390
RenderMode::TwoD { .. } => {
391+
// TODO get parameters from 2D camera here?
396392
*self = RenderMode::ThreeD {
397-
canvas: Default::default(),
393+
canvas: Canvas3::new(fidget::render::VoxelSize::new(
394+
0, 0, 0,
395+
)),
398396
mode: new_mode,
399397
};
400398
true
@@ -440,15 +438,17 @@ impl ViewerApp {
440438
draw2d::Draw2D::init(wgpu_state);
441439
draw3d::Draw3D::init(wgpu_state);
442440

441+
// Pick a dummy image size; we'll fix it later
442+
let image_size = fidget::render::ImageSize::from(256);
443443
Self {
444444
image_data: None,
445-
image_size: fidget::render::ImageSize::from(256),
445+
image_size,
446446

447447
config_tx,
448448
image_rx,
449449

450450
mode: RenderMode::TwoD {
451-
canvas: Default::default(),
451+
canvas: Canvas2::new(image_size),
452452
mode: Mode2D::Color,
453453
},
454454
}
@@ -639,22 +639,27 @@ impl eframe::App for ViewerApp {
639639
rect.height() as u32,
640640
);
641641

642-
if let Some(pos) = r.interact_pointer_pos() {
643-
let pos =
644-
image_size.transform_point(Point2::new(pos.x, pos.y));
645-
render_changed |= canvas.drag(pos);
646-
} else {
647-
canvas.end_drag();
648-
}
649-
650-
if r.hovered() {
651-
let scroll = ctx.input(|i| i.smooth_scroll_delta.y);
652-
let mouse_pos = r.hover_pos().map(|p| {
642+
let cursor_state =
643+
match (r.interact_pointer_pos(), r.hover_pos()) {
644+
(Some(p), _) => Some((p, true)),
645+
(_, Some(p)) => Some((p, false)),
646+
(None, None) => None,
647+
}
648+
.map(|(p, drag)| {
653649
let p = p - rect.min;
654-
image_size.transform_point(Point2::new(p.x, p.y))
650+
CursorState {
651+
screen_pos: Point2::new(
652+
p.x.round() as i32,
653+
p.y.round() as i32,
654+
),
655+
drag,
656+
}
655657
});
656-
render_changed |= canvas.zoom(scroll, mouse_pos);
657-
}
658+
render_changed |= canvas.interact(
659+
image_size,
660+
cursor_state,
661+
ctx.input(|i| i.smooth_scroll_delta.y),
662+
);
658663
}
659664
RenderMode::ThreeD { canvas, .. } => {
660665
let image_size = fidget::render::VoxelSize::new(
@@ -663,34 +668,40 @@ impl eframe::App for ViewerApp {
663668
rect.width().max(rect.height()) as u32,
664669
);
665670

666-
if let Some(pos) = r.interact_pointer_pos() {
667-
let pos_world = image_size
668-
.transform_point(Point3::new(pos.x, pos.y, 0.0));
669-
let drag_mode =
670-
if r.dragged_by(egui::PointerButton::Primary) {
671-
Some(canvas3d::DragMode::Pan)
672-
} else if r.dragged_by(egui::PointerButton::Secondary) {
673-
Some(canvas3d::DragMode::Rotate)
674-
} else {
675-
None
676-
};
677-
if let Some(m) = drag_mode {
678-
render_changed |= canvas.drag(pos_world, m);
671+
let cursor_state =
672+
match (r.interact_pointer_pos(), r.hover_pos()) {
673+
(Some(p), _) => {
674+
let drag =
675+
if r.dragged_by(egui::PointerButton::Primary) {
676+
Some(DragMode::Pan)
677+
} else if r
678+
.dragged_by(egui::PointerButton::Secondary)
679+
{
680+
Some(DragMode::Rotate)
681+
} else {
682+
None
683+
};
684+
685+
Some((p, drag))
686+
}
687+
(_, Some(p)) => Some((p, None)),
688+
(None, None) => None,
679689
}
680-
} else {
681-
canvas.end_drag();
682-
}
683-
684-
if r.hovered() {
685-
let scroll = ctx.input(|i| i.smooth_scroll_delta.y);
686-
let mouse_pos =
687-
ctx.input(|i| i.pointer.hover_pos()).map(|p| {
688-
let p = p - rect.min;
689-
image_size
690-
.transform_point(Point3::new(p.x, p.y, 0.0))
691-
});
692-
render_changed |= canvas.zoom(scroll, mouse_pos);
693-
}
690+
.map(|(p, drag)| {
691+
let p = p - rect.min;
692+
CursorState {
693+
screen_pos: Point2::new(
694+
p.x.round() as i32,
695+
p.y.round() as i32,
696+
),
697+
drag,
698+
}
699+
});
700+
render_changed |= canvas.interact(
701+
image_size,
702+
cursor_state,
703+
ctx.input(|i| i.smooth_scroll_delta.y),
704+
);
694705
}
695706
}
696707

0 commit comments

Comments
 (0)