fix(viewcube): land face clicks upright, never upside-down
A drawing opened with a twisted view made a TOP-face click keep the twist, so north ended up at the bottom. Add Camera::snap_to_face, which snaps to a canonical orientation — north (UCS +Y) up for top/bottom, world (UCS +Z) up for the side elevations — ignoring the current up-sense. Face clicks use it; edges/corners still preserve their up so they spin smoothly around the clicked feature. The Home button routes through the same path, giving a reliable reset for a twisted view. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f00c48bebb
commit
012ec3febc
2 changed files with 41 additions and 7 deletions
|
|
@ -4512,13 +4512,27 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
let eye_dir = r_ucs.transform_vector3(region.snap_direction());
|
let eye_dir = r_ucs.transform_vector3(region.snap_direction());
|
||||||
|
|
||||||
|
// Faces snap to a canonical upright orientation (never upside
|
||||||
|
// down); edges/corners keep the current up-sense so they spin
|
||||||
|
// smoothly around the clicked feature.
|
||||||
|
let is_face = matches!(region, scene::CubeRegion::Face(_));
|
||||||
if self.tabs[i].scene.active_viewport.is_some() {
|
if self.tabs[i].scene.active_viewport.is_some() {
|
||||||
self.tabs[i]
|
if is_face {
|
||||||
.scene
|
self.tabs[i]
|
||||||
.snap_active_viewport_to_direction(eye_dir, r_ucs);
|
.scene
|
||||||
|
.mutate_active_viewport_camera(|c| c.snap_to_face(eye_dir, r_ucs));
|
||||||
|
} else {
|
||||||
|
self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.snap_active_viewport_to_direction(eye_dir, r_ucs);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut cam = self.tabs[i].scene.camera.borrow_mut();
|
let mut cam = self.tabs[i].scene.camera.borrow_mut();
|
||||||
cam.snap_to_direction(eye_dir, r_ucs);
|
if is_face {
|
||||||
|
cam.snap_to_face(eye_dir, r_ucs);
|
||||||
|
} else {
|
||||||
|
cam.snap_to_direction(eye_dir, r_ucs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.tabs[i].scene.camera_generation += 1;
|
self.tabs[i].scene.camera_generation += 1;
|
||||||
self.command_line
|
self.command_line
|
||||||
|
|
|
||||||
|
|
@ -390,11 +390,31 @@ impl Camera {
|
||||||
self.sync_yaw_pitch();
|
self.sync_yaw_pitch();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Jump to the default "home" view — top-down (looking along −Z), expressed
|
/// Snap to a canonical face view: looks along `eye_dir` with a fixed
|
||||||
/// in the active UCS so it lands square to the user's coordinate frame.
|
/// upright orientation — north (UCS +Y) up for top/bottom, world (UCS +Z)
|
||||||
|
/// up for the side elevations. Unlike [`snap_to_direction`] this ignores
|
||||||
|
/// the current up-sense, so a face click always lands square and never
|
||||||
|
/// upside-down, even when the drawing opened with a twisted view.
|
||||||
|
pub fn snap_to_face(&mut self, eye_dir: Vec3, ucs: glam::Mat4) {
|
||||||
|
let new_eye = eye_dir.normalize_or(Vec3::Z);
|
||||||
|
let uy = ucs.transform_vector3(Vec3::Y).normalize_or(Vec3::Y);
|
||||||
|
let uz = ucs.transform_vector3(Vec3::Z).normalize_or(Vec3::Z);
|
||||||
|
// Looking along the UCS Z axis (top/bottom) has no "world up" to use,
|
||||||
|
// so fall back to north (+Y); every side view uses world up.
|
||||||
|
let up_ref = if new_eye.dot(uz).abs() > 0.9 { uy } else { uz };
|
||||||
|
let projected = up_ref - new_eye * up_ref.dot(new_eye);
|
||||||
|
let new_up = projected.normalize_or(uy);
|
||||||
|
let new_right = new_up.cross(new_eye).normalize();
|
||||||
|
let mat = glam::Mat3::from_cols(new_right, new_up, new_eye);
|
||||||
|
self.rotation = Quat::from_mat3(&mat).normalize();
|
||||||
|
self.sync_yaw_pitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Jump to the default "home" view — a canonical top-down view (north up),
|
||||||
|
/// expressed in the active UCS. Doubles as a "reset" for a twisted view.
|
||||||
pub fn home_view(&mut self, ucs: glam::Mat4) {
|
pub fn home_view(&mut self, ucs: glam::Mat4) {
|
||||||
let dir = ucs.transform_vector3(Vec3::Z);
|
let dir = ucs.transform_vector3(Vec3::Z);
|
||||||
self.snap_to_direction(dir, ucs);
|
self.snap_to_face(dir, ucs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Roll the camera about its own view axis by `angle` radians. The gaze
|
/// Roll the camera about its own view axis by `angle` radians. The gaze
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue