Orbit from empty space turns around the visible geometry (#464)
With "Orbit around: Point under the cursor", a drag that starts over empty space used to pivot on the point under the cursor at the target's depth, so an orbit begun near the edge of the view swung the model around an off-screen point. It now pivots on the centre of the visible bodies and sketches (the same bounds Fit frames), or on the view centre when nothing is visible. A drag that starts on the model is unchanged. Claude-Session: https://claude.ai/code/session_01HWLPLy7ug7URSK5WybjRNa Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
9062c739d8
commit
dd67472252
1 file changed
+32
-6
@@ -414,7 +414,9 @@ pub fn zoom_sign(app: &SolveApp) -> f64 {
|
||||
}
|
||||
|
||||
/// Orbit by pixel deltas: around the view's centre, or, with "orbit around the cursor", around
|
||||
/// the model point that was under the cursor when the drag began (kept still on screen).
|
||||
/// the model point that was under the cursor when the drag began (kept still on screen). A drag
|
||||
/// that begins over empty space turns around the centre of the visible geometry instead, so the
|
||||
/// model never swings around a far-off point. The pivot is chosen once, when the drag begins.
|
||||
pub fn orbit(app: &mut SolveApp, rect: egui::Rect, dx: f64, dy: f64, press: Option<Pos2>) {
|
||||
if !app.preferences.orbit_cursor {
|
||||
app.cam.orbit(dx, dy);
|
||||
@@ -438,7 +440,8 @@ pub fn orbit(app: &mut SolveApp, rect: egui::Rect, dx: f64, dy: f64, press: Opti
|
||||
}
|
||||
}
|
||||
|
||||
/// The model point under a screen position, or the point at the view's depth.
|
||||
/// The model point under a screen position. Over empty space: the centre of the visible bodies
|
||||
/// and sketches (what Fit frames), or the view's centre when nothing is visible.
|
||||
fn pivot_under(app: &SolveApp, proj: &crate::viewport::Proj, at: Pos2) -> Vec3 {
|
||||
use crate::viewport::Hit;
|
||||
for h in crate::viewport::pick(app, proj, at) {
|
||||
@@ -448,10 +451,8 @@ fn pivot_under(app: &SolveApp, proj: &crate::viewport::Proj, at: Pos2) -> Vec3 {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let (o, d) = proj.ray(at);
|
||||
let n = app.cam.back();
|
||||
let den = d.dot(n);
|
||||
if den.abs() > 1e-9 { o + d * ((app.cam.target - o).dot(n) / den) } else { app.cam.target }
|
||||
let b = solidcraft_engine::view::bounds(&app.session);
|
||||
if b.is_empty() { app.cam.target } else { b.center() }
|
||||
}
|
||||
|
||||
/// Units per millimetre and the unit's name.
|
||||
@@ -1319,6 +1320,31 @@ mod tests {
|
||||
assert!(s.distance(at) < 2.0, "{s:?}");
|
||||
}
|
||||
|
||||
/// A drag that starts over empty space turns around the visible geometry's centre, not a
|
||||
/// point far off to the side; with nothing visible it turns around the view's centre.
|
||||
#[test]
|
||||
fn orbiting_from_empty_space_turns_around_the_visible_geometry() {
|
||||
let mut app = SolveApp::new(solidcraft_engine::Session::default(), Default::default());
|
||||
app.run("solid.box", json!({"length": 40, "width": 40, "height": 40})).unwrap();
|
||||
app.preferences.orbit_cursor = true;
|
||||
let rect = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(800.0, 600.0));
|
||||
app.fit_view();
|
||||
let corner = egui::pos2(10.0, 10.0);
|
||||
let proj = crate::viewport::projection(&app, rect);
|
||||
let hits = crate::viewport::pick(&app, &proj, corner);
|
||||
use crate::viewport::Hit;
|
||||
assert!(!hits.iter().any(|h| matches!(h, Hit::Face { .. } | Hit::Edge { .. } | Hit::Vertex { .. })), "the corner is empty space");
|
||||
let centre = solidcraft_engine::view::bounds(&app.session).center();
|
||||
orbit(&mut app, rect, 40.0, 25.0, Some(corner));
|
||||
assert_eq!(app.viewport.orbit_pivot, Some(centre));
|
||||
|
||||
let mut app = SolveApp::new(solidcraft_engine::Session::default(), Default::default());
|
||||
app.preferences.orbit_cursor = true;
|
||||
app.cam.target = solidcraft_engine::geom::Vec3::new(5.0, -3.0, 2.0);
|
||||
orbit(&mut app, rect, 40.0, 25.0, Some(corner));
|
||||
assert_eq!(app.viewport.orbit_pivot, Some(solidcraft_engine::geom::Vec3::new(5.0, -3.0, 2.0)));
|
||||
}
|
||||
|
||||
/// The Y up preference turns the view: the camera's up axis follows it, startup included.
|
||||
#[test]
|
||||
fn up_axis_preference_turns_the_view() {
|
||||
|
||||
Reference in new issue
Block a user