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

Moving the model should work without a focus point #806

Merged
merged 2 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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.

4 changes: 4 additions & 0 deletions crates/fj-viewer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ path = "../fj-interop"
version = "0.7.0"
path = "../fj-math"

[dependencies.fj-operations]
version = "0.7.0"
path = "../fj-operations"

Comment on lines +31 to +34
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a bit unfortunate to add the dependency on fj-operations and (indirectly) fj-kernel. It would probably be better to move ProcessedShape to fj-interop instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I didn't really like adding the dependency either. Let me give your suggestion a go.

[dependencies.egui]
version = "0.18.1"

Expand Down
40 changes: 18 additions & 22 deletions crates/fj-viewer/src/camera.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Viewer camera module
use std::f64::consts::FRAC_PI_2;

use fj_interop::mesh::Mesh;
use fj_math::{Aabb, Point, Scalar, Transform, Triangle, Vector};
use fj_operations::shape_processor::ProcessedShape;

use crate::screen::NormalizedPosition;

Expand Down Expand Up @@ -121,21 +121,25 @@ impl Camera {
pub fn focus_point(
&self,
cursor: Option<NormalizedPosition>,
mesh: &Mesh<fj_math::Point<3>>,
shape: &ProcessedShape,
) -> FocusPoint {
let cursor = match cursor {
Some(cursor) => cursor,
None => return FocusPoint::none(),
};
self.calculate_focus_point(cursor, shape)
.unwrap_or_else(|| FocusPoint(shape.aabb.center()))
}

fn calculate_focus_point(
&self,
cursor: Option<NormalizedPosition>,
shape: &ProcessedShape,
) -> Option<FocusPoint> {
// Transform camera and cursor positions to model space.
let origin = self.position();
let cursor = self.cursor_to_model_space(cursor);
let cursor = self.cursor_to_model_space(cursor?);
let dir = (cursor - origin).normalize();

let mut min_t = None;

for triangle in mesh.triangles() {
for triangle in shape.mesh.triangles() {
let t = Triangle::from_points(triangle.points).cast_local_ray(
origin,
dir,
Expand All @@ -150,7 +154,7 @@ impl Camera {
}
}

FocusPoint(min_t.map(|t| origin + dir * t))
Some(FocusPoint(origin + dir * min_t?))
}

/// Access the transform from camera to model space.
Expand Down Expand Up @@ -211,17 +215,9 @@ impl Camera {
}
}

/// The point on the model that the cursor is currently pointing at.
/// The point around which camera movement happens.
///
/// Such a point might or might not exist, depending on whether the cursor is
/// pointing at the model or not.
pub struct FocusPoint(pub Option<Point<3>>);

impl FocusPoint {
/// Construct the "none" instance of `FocusPoint`
///
/// This instance represents the case that no focus point exists.
pub fn none() -> Self {
Self(None)
}
}
/// This will be the point on the model that the cursor is currently pointing at if such a point exists,
/// falling back to the center point of the model's bounding volume otherwise.
#[derive(Clone, Copy)]
pub struct FocusPoint(pub Point<3>);
30 changes: 11 additions & 19 deletions crates/fj-viewer/src/input/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,36 @@ use crate::camera::{Camera, FocusPoint};
///
/// Takes user input and applies them to application state.
pub struct Handler {
focus_point: FocusPoint,

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

impl Handler {
/// Handle an input event
pub fn handle_event(&mut self, event: Event, camera: &mut Camera) {
pub fn handle_event(
&mut self,
event: Event,
focus_point: FocusPoint,
camera: &mut Camera,
) {
match event {
Event::Translate { previous, current } => self.movement.apply(
previous,
current,
&self.focus_point,
camera,
),
Event::Translate { previous, current } => {
self.movement.apply(previous, current, focus_point, camera)
}
Event::Rotation { angle_x, angle_y } => {
self.rotation
.apply(angle_x, angle_y, &self.focus_point, camera)
self.rotation.apply(angle_x, angle_y, focus_point, camera)
}
Event::Zoom(zoom_delta) => {
self.zoom.apply(zoom_delta, &self.focus_point, camera)
self.zoom.apply(zoom_delta, 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 {
focus_point: FocusPoint::none(),

movement: Movement,
rotation: Rotation,
zoom: Zoom,
Expand Down
24 changes: 11 additions & 13 deletions crates/fj-viewer/src/input/movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,23 @@ impl Movement {
&mut self,
previous: NormalizedPosition,
current: NormalizedPosition,
focus_point: &FocusPoint,
focus_point: FocusPoint,
camera: &mut Camera,
) {
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 d1 = Point::distance(&camera.position(), &cursor);
let d2 = Point::distance(&camera.position(), &focus_point.0);

let diff = (cursor - previous) * d2 / d1;
let offset = camera.camera_to_model().transform_vector(&diff);
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,
]));
}
camera.translation = camera.translation
* Transform::translation(Vector::from([
offset.x,
offset.y,
Scalar::ZERO,
]));
}
}
8 changes: 3 additions & 5 deletions crates/fj-viewer/src/input/rotation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use fj_math::{Point, Transform, Vector};
use fj_math::{Transform, Vector};

use crate::camera::{Camera, FocusPoint};

Expand All @@ -9,12 +9,10 @@ impl Rotation {
&self,
angle_x: f64,
angle_y: f64,
focus_point: &FocusPoint,
focus_point: FocusPoint,
camera: &mut Camera,
) {
let rotate_around: Vector<3> =
focus_point.0.unwrap_or_else(Point::origin).coords;
let rotate_around = Transform::translation(rotate_around);
let rotate_around = Transform::translation(focus_point.0.coords);

// the model rotates not the camera, so invert the transform
let camera_rotation = camera.rotation.inverse();
Expand Down
7 changes: 2 additions & 5 deletions crates/fj-viewer/src/input/zoom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@ impl Zoom {
pub fn apply(
&mut self,
zoom_delta: f64,
focus_point: &FocusPoint,
focus_point: FocusPoint,
camera: &mut Camera,
) {
let distance = match focus_point.0 {
Some(fp) => (fp - camera.position()).magnitude(),
None => camera.position().coords.magnitude(),
};
let distance = (focus_point.0 - camera.position()).magnitude();
let displacement = zoom_delta * distance.into_f64();
camera.translation = camera.translation
* Transform::translation(Vector::from([0.0, 0.0, -displacement]));
Expand Down
49 changes: 28 additions & 21 deletions crates/fj-window/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub fn run(

let mut previous_cursor = None;
let mut held_mouse_button = None;
let mut focus_pending = false;
let mut focus_point = None;

let mut input_handler = input::Handler::default();
let mut renderer = block_on(Renderer::new(&window))?;
Expand Down Expand Up @@ -111,8 +111,6 @@ pub fn run(
.on_event(&renderer.egui.context, window_event);
}

focus_pending |= focus_event(&event);

// fj-window events
match event {
Event::WindowEvent {
Expand Down Expand Up @@ -166,16 +164,6 @@ pub fn run(
};
}
Event::MainEventsCleared => {
if let (Some(shape), Some(camera)) = (&shape, &mut camera) {
// Only perform focus calculation once per frame (if pending)
if focus_pending {
input_handler.focus(
camera.focus_point(previous_cursor, &shape.mesh),
);
}
focus_pending = false;
}

window.window().request_redraw();
}
Event::RedrawRequested(_) => {
Expand All @@ -193,14 +181,32 @@ pub fn run(
}

// fj-viewer input events
// These can fire multiple times per frame

if let (Some(shape), Some(camera), Some(should_focus)) =
(&shape, &camera, focus_event(&event))
{
if should_focus {
// Don't unnecessarily recalculate focus point
if focus_point.is_none() {
focus_point =
Some(camera.focus_point(previous_cursor, shape));
}
} else {
focus_point = None;
}
}

let input_event = input_event(
&event,
&window,
&held_mouse_button,
&mut previous_cursor,
);
if let (Some(input_event), Some(camera)) = (input_event, &mut camera) {
input_handler.handle_event(input_event, camera);
if let (Some(input_event), Some(fp), Some(camera)) =
(input_event, focus_point, &mut camera)
{
input_handler.handle_event(input_event, fp, camera);
}
});
}
Expand Down Expand Up @@ -258,8 +264,9 @@ fn input_event(
}
}

/// Returns true if new focus point should be calculated based on this event
fn focus_event(event: &Event<()>) -> bool {
/// Returns true/false if focus point point should be created/removed
/// None means no change to focus point is needed
fn focus_event(event: &Event<()>) -> Option<bool> {
match event {
Event::WindowEvent {
event:
Expand All @@ -270,14 +277,14 @@ fn focus_event(event: &Event<()>) -> bool {
},
..
} => match state {
ElementState::Pressed => true,
ElementState::Released => false,
ElementState::Pressed => Some(true),
ElementState::Released => Some(false),
},
Event::WindowEvent {
event: WindowEvent::MouseWheel { .. },
..
} => true,
_ => false,
} => Some(true),
_ => None,
}
}

Expand Down