From 6996505d706ab55043d9bcb0897997b2f52484cb Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Thu, 15 Oct 2026 05:06:07 +0300 Subject: [PATCH] orbit: turn about the surface under the pointer With nothing selected, Shift+MMB orbit pivoted on the surface at the view centre, so grabbing a small part off-centre flung the model away. It now pivots on the solid or mesh surface under the pointer where the drag began, falling back to the view centre and then the camera target (#1389). Co-Authored-By: Claude Opus 5.5 --- src/app/update/viewport.rs | 4 ++++ src/scene/mod.rs | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/app/update/viewport.rs b/src/app/update/viewport.rs index 1996ba6e8..7a63147ad 100644 --- a/src/app/update/viewport.rs +++ b/src/app/update/viewport.rs @@ -1436,9 +1436,13 @@ impl OpenCADStudio { if sel.orbit.pivot.is_none() { let scene = &self.tabs[i].scene; let bounds = scene.active_model_tile_bounds(vp_size.0, vp_size.1); + // The surface under the pointer where the drag + // began, so the model turns about what is grabbed. + let grabbed = iced::Point::new(last.x - bounds.x, last.y - bounds.y); sel.orbit.pivot = Some( scene .orbit_pivot() + .or_else(|| scene.surface_pivot_at(bounds, grabbed)) .or_else(|| scene.view_center_surface_pivot(bounds)) .unwrap_or_else(|| scene.camera.borrow().target), ); diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 47f988779..2f173a262 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -11322,14 +11322,22 @@ impl Scene { } pub fn view_center_surface_pivot(&self, bounds: iced::Rectangle) -> Option { + self.surface_pivot_at(bounds, iced::Point::new(bounds.width * 0.5, bounds.height * 0.5)) + } + + /// The nearest solid or mesh surface under `local` (relative to `bounds`). + pub fn surface_pivot_at( + &self, + bounds: iced::Rectangle, + local: iced::Point, + ) -> Option { let camera = self.camera.borrow(); let view_rot = camera.view_proj_rte(bounds); let eye = camera.eye(); drop(camera); let meshes = self.interaction_meshes_arc(); - let center = iced::Point::new(bounds.width * 0.5, bounds.height * 0.5); pick::hit_test::mesh_click_point( - center, + local, meshes.iter().filter_map(|set| { let handle = set.entity_handle()?; let mesh = set.geometry_lods().first()?;