Skip to content

Commit

Permalink
Better handle scroll-to-zoom in 3D views
Browse files Browse the repository at this point in the history
Problem: when radius shrinks, scrolling no longer does much.
This makes the user think something is broken.

Solution: switch to dolly when radius is small enough.
That way scrolling will always move you closer, one way or another.
  • Loading branch information
emilk committed Jun 12, 2023
1 parent 591e7dc commit c59e694
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 7 deletions.
25 changes: 19 additions & 6 deletions crates/re_space_view_spatial/src/eye.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use egui::{lerp, NumExt as _, Rect};
use glam::Affine3A;
use macaw::{vec3, IsoTransform, Mat4, Quat, Vec3};
use macaw::{vec3, BoundingBox, IsoTransform, Mat4, Quat, Vec3};

use re_space_view::controls::{
DRAG_PAN3D_BUTTON, ROLL_MOUSE, ROLL_MOUSE_ALT, ROLL_MOUSE_MODIFIER, ROTATE3D_BUTTON,
Expand Down Expand Up @@ -300,7 +300,12 @@ impl OrbitEye {

/// Returns `true` if interaction occurred.
/// I.e. the camera changed via user input.
pub fn update(&mut self, response: &egui::Response, drag_threshold: f32) -> bool {
pub fn update(
&mut self,
response: &egui::Response,
drag_threshold: f32,
scene_bbox: &BoundingBox,
) -> bool {
let mut did_interact = false;

if response.drag_delta().length() > drag_threshold {
Expand Down Expand Up @@ -355,10 +360,18 @@ impl OrbitEye {
if zoom_factor != 1.0 {
let new_radius = self.orbit_radius / zoom_factor;

// Don't let radius go too small or too big because this might cause infinity/nan in some calculations.
// Max value is chosen with some generous margin of an observed crash due to infinity.
if f32::MIN_POSITIVE < new_radius && new_radius < 1.0e17 {
self.orbit_radius = new_radius;
let very_close = scene_bbox.size().length() / 100.0;
if very_close.is_finite() && new_radius < very_close && 1.0 < zoom_factor {
// The user may be scrolling to move the camera closer, but are not realizing
// the radius is now tiny.
// Switch to instead dolly the camera forward:
self.orbit_center += self.fwd() * very_close * zoom_factor.ln();
} else {
// Don't let radius go too small or too big because this might cause infinity/nan in some calculations.
// Max value is chosen with some generous margin of an observed crash due to infinity.
if f32::MIN_POSITIVE < new_radius && new_radius < 1.0e17 {
self.orbit_radius = new_radius;
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/re_space_view_spatial/src/ui_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,8 @@ pub fn view_3d(
let orbit_eye = state
.state_3d
.update_eye(&response, &state.scene_bbox_accum, space_cameras);
let did_interact_with_eye = orbit_eye.update(&response, orbit_eye_drag_threshold);
let did_interact_with_eye =
orbit_eye.update(&response, orbit_eye_drag_threshold, &state.scene_bbox_accum);

let orbit_eye = *orbit_eye;
let eye = orbit_eye.to_eye();
Expand Down

0 comments on commit c59e694

Please sign in to comment.