Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/dxf_viewer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ui-events = "0.2.0"
ui-events-winit = "0.2.0"
vello = "0.6.0"
winit = { version = "0.30.10", default-features = false }
dpi = "0.1.2"

static_aabb2d_index = { version = "2.0.0", features = ["unsafe_optimizations"] }

Expand Down
281 changes: 212 additions & 69 deletions examples/dxf_viewer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#![windows_subsystem = "windows"]

use anyhow::Result;
use dpi::PhysicalPosition;
use joto_constants::u64::{INCH, MICROMETER};
use std::num::NonZeroUsize;
use std::path::{Path, PathBuf};
Expand All @@ -44,13 +45,13 @@ use tracing_subscriber::prelude::*;
use ui_events::{
ScrollDelta,
pointer::{
PointerButton, PointerButtonEvent, PointerEvent, PointerId, PointerInfo,
PointerScrollEvent, PointerType, PointerUpdate,
PointerButton, PointerButtonEvent, PointerEvent, PointerGesture, PointerGestureEvent,
PointerId, PointerInfo, PointerScrollEvent, PointerState, PointerType, PointerUpdate,
},
};
use ui_events_winit::{WindowEventReducer, WindowEventTranslation};
use vello::kurbo::{
Affine, DEFAULT_ACCURACY, ParamCurveNearest, PathSeg, Point, Rect, Shape, Stroke, Vec2,
Affine, DEFAULT_ACCURACY, ParamCurveNearest, PathSeg, Point, Rect, Shape, Size, Stroke, Vec2,
};
use vello::peniko::{Brush, Color, color::palette};
use vello::util::{RenderContext, RenderSurface};
Expand Down Expand Up @@ -86,12 +87,27 @@ enum RenderState<'s> {
Suspended(Option<Arc<Window>>),
}

#[derive(Default)]
struct GestureState {
/// Pointer currently panning.
pan: Option<PointerId>,
/// Cursor position.
cursor_pos: Point,
#[derive(Default, Debug, PartialEq)]
enum GestureState {
/// Hover highlighting.
#[default]
Hover,
/// Panning.
Pan {
/// Pointer currently panning.
pointer_id: Option<PointerId>,
/// Last pointer position.
pos: Point,
},
/// Pointer zooming about point.
DragZoomAbout {
/// Pointer performing the drag zoom.
pointer_id: Option<PointerId>,
/// Zoom center.
about: Point,
/// Last y position.
y: f64,
},
}

struct DrawingViewer {
Expand Down Expand Up @@ -241,7 +257,7 @@ impl ApplicationHandler for TabulonDxfViewer<'_> {
view_transform,
text_cull_index,
gestures: GestureState::default(),
defer_reprojection: false,
defer_reprojection: true,
pick: None,
});
}
Expand Down Expand Up @@ -278,6 +294,21 @@ impl ApplicationHandler for TabulonDxfViewer<'_> {
_ => return,
};

let size = {
let ws = window.inner_size();
Size {
width: ws.width as f64,
height: ws.height as f64,
}
};

let scale_factor = window.scale_factor();

let center = Point {
x: size.width * 0.5,
y: size.height * 0.5,
};

let mut reproject = false;
// Set if reprojection is requested as a result of a deferral.
let mut reproject_deferred = false;
Expand All @@ -289,7 +320,7 @@ impl ApplicationHandler for TabulonDxfViewer<'_> {
..
}
) {
if let Some(wet) = self.event_reducer.reduce(window.scale_factor(), &event) {
if let Some(wet) = self.event_reducer.reduce(scale_factor, &event) {
match wet {
WindowEventTranslation::Keyboard(k) => {
use ui_events::keyboard::{Key, NamedKey};
Expand All @@ -312,7 +343,11 @@ impl ApplicationHandler for TabulonDxfViewer<'_> {
..
},
button: Some(PointerButton::Primary),
state,
state:
PointerState {
position: PhysicalPosition { x, y },
..
},
}
| PointerButtonEvent {
pointer:
Expand All @@ -321,82 +356,204 @@ impl ApplicationHandler for TabulonDxfViewer<'_> {
pointer_type: PointerType::Touch,
..
},
state,
state:
PointerState {
position: PhysicalPosition { x, y },
count: 1,
..
},
..
},
) => {
if viewer.gestures.pan.is_none() {
viewer.gestures.pan = pointer_id;
viewer.gestures.cursor_pos = Point {
x: state.position.x,
y: state.position.y,
if let GestureState::Hover = viewer.gestures {
viewer.gestures = GestureState::Pan {
pointer_id,
pos: Point { x, y },
};
}
}
PointerEvent::Down(PointerButtonEvent {
pointer:
PointerInfo {
pointer_id,
pointer_type: PointerType::Touch,
..
},
state:
PointerState {
position: PhysicalPosition { x, y },
count: 2,
..
},
..
}) => {
if let GestureState::Hover = viewer.gestures {
viewer.gestures = GestureState::DragZoomAbout {
pointer_id,
about: Point { x, y },
y,
}
}
}
PointerEvent::Down(PointerButtonEvent {
pointer:
PointerInfo {
pointer_id,
pointer_type: PointerType::Mouse,
..
},
button: Some(PointerButton::Auxiliary),
state:
PointerState {
position: PhysicalPosition { x, y },
..
},
}) => {
if let GestureState::Hover = viewer.gestures {
viewer.gestures = GestureState::DragZoomAbout {
pointer_id,
about: Point { x, y },
y,
}
}
}
PointerEvent::Down(PointerButtonEvent {
pointer:
PointerInfo {
pointer_id,
pointer_type: PointerType::Mouse,
..
},
button: Some(PointerButton::Secondary),
state:
PointerState {
position: PhysicalPosition { y, .. },
..
},
}) => {
if let GestureState::Hover = viewer.gestures {
viewer.gestures = GestureState::DragZoomAbout {
pointer_id,
about: center,
y,
}
}
}
PointerEvent::Move(PointerUpdate {
pointer: PointerInfo { pointer_id, .. },
current,
current:
PointerState {
position: PhysicalPosition { x, y },
..
},
..
}) => {
let p = Point {
x: current.position.x,
y: current.position.y,
};

let p = Point { x, y };
let dp = viewer.view_transform.inverse() * p;

if viewer.gestures.pan == pointer_id {
viewer.view_transform = viewer
.view_transform
.then_translate(-(viewer.gestures.cursor_pos - p));
reproject = true;
} else if pointer_id == Some(PointerId::PRIMARY) {
let pick_dist: f64 = window.scale_factor() * 1.414;
let pick_started = Instant::now();

let pick = viewer
.picking_index
.pick(dp, pick_dist * viewer.view_scale.recip());

if viewer.pick != pick {
if let Some(pick) = pick {
let pick_duration = Instant::now()
.saturating_duration_since(pick_started);
eprintln!(
"{:#?}",
viewer.td.info.get_entity(pick).specific
);
eprintln!("Pick took {pick_duration:?}");
}
viewer.pick = pick;
match &mut viewer.gestures {
GestureState::Pan {
pointer_id: g_pointer,
pos,
} if *g_pointer == pointer_id => {
viewer.view_transform =
viewer.view_transform.then_translate(-(*pos - p));
reproject = true;
*pos = p;
}
GestureState::DragZoomAbout {
pointer_id: g_pointer,
about,
y,
} if *g_pointer == pointer_id => {
let sd = 1. + (p.y - *y) * (400.0 * scale_factor).recip();
viewer.view_transform =
viewer.view_transform.then_scale_about(sd, *about);
viewer.view_scale *= sd;
reproject = true;
*y = p.y;
}
GestureState::Hover
if pointer_id == Some(PointerId::PRIMARY) =>
{
let pick_dist: f64 = window.scale_factor() * 1.414;
let pick_started = Instant::now();

let pick = viewer
.picking_index
.pick(dp, pick_dist * viewer.view_scale.recip());

if viewer.pick != pick {
if let Some(pick) = pick {
let pick_duration = Instant::now()
.saturating_duration_since(pick_started);
eprintln!(
"{:#?}",
viewer.td.info.get_entity(pick).specific
);
eprintln!("Pick took {pick_duration:?}");
}
viewer.pick = pick;
reproject = true;
}
}
_ => {}
}

viewer.gestures.cursor_pos = p;
}
PointerEvent::Up(PointerButtonEvent {
pointer: PointerInfo { pointer_id, .. },
..
})
| PointerEvent::Cancel(PointerInfo { pointer_id, .. }) => {
if viewer.gestures.pan == pointer_id {
viewer.gestures.pan = None;
match viewer.gestures {
GestureState::Pan {
pointer_id: g_pointer,
..
}
| GestureState::DragZoomAbout {
pointer_id: g_pointer,
..
} if g_pointer == pointer_id => {
viewer.gestures = GestureState::Hover;
}
_ => {}
}
}
PointerEvent::Scroll(PointerScrollEvent { delta, .. }) => {
PointerEvent::Scroll(PointerScrollEvent {
delta,
state:
PointerState {
position: PhysicalPosition { x, y },
..
},
..
}) => {
let d = match delta {
ScrollDelta::LineDelta(_, y) => y as f64 * 0.1,
ScrollDelta::PixelDelta(pd) => pd.y * 0.05,
_ => 0.,
};

viewer.view_transform = viewer
.view_transform
.then_scale_about(1. + d, viewer.gestures.cursor_pos);
.then_scale_about(1. + d, Point { x, y });
viewer.view_scale *= 1. + d;
reproject = true;
}
PointerEvent::Gesture(PointerGestureEvent {
gesture: PointerGesture::Pinch(d),
state:
PointerState {
position: PhysicalPosition { x, y },
..
},
..
}) => {
viewer.view_transform = viewer
.view_transform
.then_scale_about(1. + d as f64, Point { x, y });
viewer.view_scale *= 1. + d as f64;
reproject = true;
}
_ => {}
}
}
Expand Down Expand Up @@ -472,20 +629,6 @@ impl ApplicationHandler for TabulonDxfViewer<'_> {
reproject = true;
}

WindowEvent::PinchGesture { delta: d, .. } => {
let Some(viewer) = &mut self.viewer else {
return;
};

viewer.view_transform = viewer
.view_transform
.then_translate(-viewer.gestures.cursor_pos.to_vec2())
.then_scale(1. + d)
.then_translate(viewer.gestures.cursor_pos.to_vec2());
viewer.view_scale *= 1. + d;
reproject = true;
}

WindowEvent::RedrawRequested => {
let wgpu::SurfaceConfiguration { width, height, .. } = surface.config;

Expand Down