Skip to content

Commit

Permalink
Merge pull request #803 from jeevcat/sam/viewer-interface
Browse files Browse the repository at this point in the history
High-level fj-viewer interface
  • Loading branch information
hannobraun authored Jul 12, 2022
2 parents edf7a66 + 9db1be4 commit 01b0e7e
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 413 deletions.
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

0 comments on commit 01b0e7e

Please sign in to comment.