Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

High-level fj-viewer interface #803

Merged
merged 9 commits into from
Jul 12, 2022
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
24 changes: 7 additions & 17 deletions crates/fj-viewer/src/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::f64::consts::FRAC_PI_2;
use fj_interop::mesh::Mesh;
use fj_math::{Aabb, Point, Scalar, Transform, Triangle, Vector};

use crate::screen::{Position, Size};
use crate::screen::NormalizedPosition;

/// The camera abstraction
///
Expand Down Expand Up @@ -104,33 +104,23 @@ impl Camera {
.inverse_transform_point(&Point::<3>::origin())
}

/// Transform the position of the cursor on the near plane to model space.
/// Transform a normalized cursor position on the near plane to model space.
pub fn cursor_to_model_space(
&self,
cursor: Position,
size: Size,
cursor: NormalizedPosition,
) -> Point<3> {
let [width, height] = size.as_f64();
let aspect_ratio = width / height;

// Cursor position in normalized coordinates (-1 to +1) with
// aspect ratio taken into account.
let x = cursor.x / width * 2. - 1.;
let y = -(cursor.y / height * 2. - 1.) / aspect_ratio;

// Cursor position in camera space.
let f = (self.field_of_view_in_x() / 2.).tan() * self.near_plane();
let cursor =
Point::origin() + Vector::from([x * f, y * f, -self.near_plane()]);
let cursor = Point::origin()
+ Vector::from([cursor.x * f, cursor.y * f, -self.near_plane()]);

self.camera_to_model().inverse_transform_point(&cursor)
}

/// Compute the point on the model, that the cursor currently points to.
pub fn focus_point(
&self,
size: Size,
cursor: Option<Position>,
cursor: Option<NormalizedPosition>,
mesh: &Mesh<fj_math::Point<3>>,
) -> FocusPoint {
let cursor = match cursor {
Expand All @@ -140,7 +130,7 @@ impl Camera {

// Transform camera and cursor positions to model space.
let origin = self.position();
let cursor = self.cursor_to_model_space(cursor, size);
let cursor = self.cursor_to_model_space(cursor);
let dir = (cursor - origin).normalize();

let mut min_t = None;
Expand Down
67 changes: 19 additions & 48 deletions crates/fj-viewer/src/input/event.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,23 @@
use crate::screen::Position;
use crate::screen::NormalizedPosition;

/// An input event
pub enum Event {
/// The cursor has moved to another position
CursorMoved(Position),

/// A key has been pressed or released
Key(Key, KeyState),

/// The user scrolled
Scroll(MouseScrollDelta),
}

/// Describes a difference in the vertical mouse scroll wheel state.
/// Positive values indicate movement forward (away from the user).
pub enum MouseScrollDelta {
/// Amount in lines to scroll.
Line(f64),
/// Amount in pixels to scroll.
Pixel(f64),
}

/// A keyboard or mouse key
pub enum Key {
/// The escape key
Escape,

/// The numerical key `1`
Key1,

/// The numerical key `2`
Key2,

/// The numerical key `3`
Key3,

/// The left mouse key
MouseLeft,

/// The right mouse key
MouseRight,
}

/// Defines the meaning of a key event
pub enum KeyState {
/// A key was pressed
Pressed,

/// A key was released
Released,
/// Move the model up, down, left or right
Translate {
/// The normalized position of the cursor before input
previous: NormalizedPosition,
/// The normalized position of the cursor after input
current: NormalizedPosition,
},

/// Rotate the model around the focus point
Rotation {
/// The angle around the screen x axis to rotate (in radians)
angle_x: f64,
/// The angle around the screen y axis to rotate (in radians)
angle_y: f64,
},

/// Move the view forwards and backwards
Zoom(f64),
}
131 changes: 23 additions & 108 deletions crates/fj-viewer/src/input/handler.rs
Original file line number Diff line number Diff line change
@@ -1,136 +1,51 @@
use fj_interop::mesh::Mesh;
use fj_math::Point;

use super::{
event::KeyState, movement::Movement, rotation::Rotation, zoom::Zoom, Event,
Key,
};
use crate::{
camera::Camera,
screen::{Position, Size},
};
use super::{movement::Movement, rotation::Rotation, zoom::Zoom, Event};
use crate::camera::{Camera, FocusPoint};

/// Input handling abstraction
///
/// Takes user input and applies them to application state.
pub struct Handler {
cursor: Option<Position>,
focus_point: FocusPoint,

movement: Movement,
rotation: Rotation,
zoom: Zoom,
}

impl Handler {
/// Returns the state of the cursor position.
pub fn cursor(&self) -> Option<Position> {
self.cursor
}

/// Handle an input event
pub fn handle_event(
&mut self,
event: Event,
screen_size: Size,
mesh: &Mesh<Point<3>>,
camera: &mut Camera,
actions: &mut Actions,
) {
pub fn handle_event(&mut self, event: Event, camera: &mut Camera) {
match event {
Event::CursorMoved(position) => {
if let Some(previous) = self.cursor {
let diff_x = position.x - previous.x;
let diff_y = position.y - previous.y;

self.movement.apply(self.cursor, camera, screen_size);
self.rotation.apply(diff_x, diff_y, camera);
}

self.cursor = Some(position);
}
Event::Key(Key::Escape, KeyState::Pressed) => actions.exit = true,

Event::Key(Key::Key1, KeyState::Pressed) => {
actions.toggle_model = true
}
Event::Key(Key::Key2, KeyState::Pressed) => {
actions.toggle_mesh = true
}
Event::Key(Key::Key3, KeyState::Pressed) => {
actions.toggle_debug = true
}

Event::Key(Key::MouseLeft, KeyState::Pressed) => {
let focus_point =
camera.focus_point(screen_size, self.cursor(), mesh);

self.rotation.start(focus_point);
}
Event::Key(Key::MouseLeft, KeyState::Released) => {
self.rotation.stop();
Event::Translate { previous, current } => self.movement.apply(
previous,
current,
&self.focus_point,
camera,
),
Event::Rotation { angle_x, angle_y } => {
self.rotation
.apply(angle_x, angle_y, &self.focus_point, camera)
}
Event::Zoom(zoom_delta) => {
self.zoom.apply(zoom_delta, &self.focus_point, camera)
}
Event::Key(Key::MouseRight, KeyState::Pressed) => {
let focus_point =
camera.focus_point(screen_size, self.cursor(), mesh);

self.movement.start(focus_point, self.cursor);
}
Event::Key(Key::MouseRight, KeyState::Released) => {
self.movement.stop();
}

Event::Scroll(delta) => {
self.zoom.push(delta);
}

_ => {}
}
}

/// Update application state from user input.
pub fn update(
&mut self,
delta_t: f64,
camera: &mut Camera,
screen_size: Size,
mesh: &Mesh<Point<3>>,
) {
let focus_point = camera.focus_point(screen_size, self.cursor(), mesh);
self.zoom.apply_to_camera(delta_t, focus_point, camera);
/// A new focus point was selected (or deselected)
pub fn focus(&mut self, focus_point: FocusPoint) {
self.focus_point = focus_point;
}
}

impl Default for Handler {
fn default() -> Self {
Self {
cursor: None,
focus_point: FocusPoint::none(),

movement: Movement::new(),
rotation: Rotation::new(),
zoom: Zoom::new(),
movement: Movement,
rotation: Rotation,
zoom: Zoom,
}
}
}

/// Intermediate input state container
///
/// Used as a per frame state container for sending application state to `winit`.
#[derive(Default)]
pub struct Actions {
/// Application exit state.
pub exit: bool,

/// Toggle for the shaded display of the model.
pub toggle_model: bool,
/// Toggle for the model's wireframe.
pub toggle_mesh: bool,
/// Toggle for debug information.
pub toggle_debug: bool,
}

impl Actions {
/// Returns a new `Actions`.
pub fn new() -> Self {
Self::default()
}
}
5 changes: 1 addition & 4 deletions crates/fj-viewer/src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,4 @@ mod movement;
mod rotation;
mod zoom;

pub use self::{
event::{Event, Key, KeyState, MouseScrollDelta},
handler::{Actions, Handler},
};
pub use self::{event::Event, handler::Handler};
64 changes: 21 additions & 43 deletions crates/fj-viewer/src/input/movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,35 @@ use fj_math::{Point, Scalar, Transform, Vector};

use crate::{
camera::{Camera, FocusPoint},
screen::{Position, Size},
screen::NormalizedPosition,
};

pub struct Movement {
focus_point: FocusPoint,
cursor: Option<Position>,
}
pub struct Movement;

impl Movement {
pub fn new() -> Self {
Self {
focus_point: FocusPoint::none(),
cursor: None,
}
}

pub fn start(&mut self, focus_point: FocusPoint, cursor: Option<Position>) {
self.focus_point = focus_point;
self.cursor = cursor;
}

pub fn stop(&mut self) {
self.focus_point = FocusPoint::none();
}

pub fn apply(
&mut self,
cursor: Option<Position>,
previous: NormalizedPosition,
current: NormalizedPosition,
focus_point: &FocusPoint,
camera: &mut Camera,
size: Size,
) {
if let (Some(previous), Some(cursor)) = (self.cursor, cursor) {
let previous = camera.cursor_to_model_space(previous, size);
let cursor = camera.cursor_to_model_space(cursor, size);

if let Some(focus_point) = self.focus_point.0 {
let d1 = Point::distance(&camera.position(), &cursor);
let d2 = Point::distance(&camera.position(), &focus_point);

let diff = (cursor - previous) * d2 / d1;
let offset = camera.camera_to_model().transform_vector(&diff);

camera.translation = camera.translation
* Transform::translation(Vector::from([
offset.x,
offset.y,
Scalar::ZERO,
]));
}
let previous = camera.cursor_to_model_space(previous);
let cursor = camera.cursor_to_model_space(current);

if let Some(focus_point) = focus_point.0 {
let d1 = Point::distance(&camera.position(), &cursor);
let d2 = Point::distance(&camera.position(), &focus_point);

let diff = (cursor - previous) * d2 / d1;
let offset = camera.camera_to_model().transform_vector(&diff);

camera.translation = camera.translation
* Transform::translation(Vector::from([
offset.x,
offset.y,
Scalar::ZERO,
]));
}

self.cursor = cursor;
}
}
Loading