From cd9db57366171b53c10b90fef8997c69fb76e9a6 Mon Sep 17 00:00:00 2001 From: DuBearte <130190089+DuBearte@users.noreply.github.com> Date: Sat, 17 Oct 2026 04:08:09 -0400 Subject: [PATCH] Look At pressed again looks from the other side (#1886) (#1904) Thanks @DuBearte. --- crates/ui-egui/src/dialogs.rs | 23 ++++++++++++++++++++--- crates/ui-egui/src/navigation.rs | 22 ++++++++++++++++++++++ crates/ui-egui/src/sketch_palette.rs | 2 +- crates/ui-egui/src/viewport.rs | 7 ++++--- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 748117da..ff9703e2 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -3505,19 +3505,36 @@ fn plane_by_name(app: &SolveApp, name: &str) -> Option 0.999 { + if n.z.abs() > 0.999 { let x = ss.plane.x; - to.yaw = if ss.plane.normal().z > 0.0 { (-x.y).atan2(x.x) } else { x.y.atan2(x.x) }; + to.yaw = if n.z > 0.0 { (-x.y).atan2(x.x) } else { x.y.atan2(x.x) }; } to.target = ss.plane.origin; app.animate_to(to); } } +/// The side to look from at a plane with normal `n`: its own side, or with `again` the other +/// side when the view already looks from `n`, so a second Look At turns the view round (#1886). +pub(crate) fn facing(app: &SolveApp, n: Vec3, again: bool) -> Vec3 { + if again && app.cam.back().dot(n) > 0.999 { -n } else { n } +} + fn sels(d: &Dialog, i: usize) -> &[Sel] { d.inputs.get(i).map(|x| x.items.as_slice()).unwrap_or(&[]) } diff --git a/crates/ui-egui/src/navigation.rs b/crates/ui-egui/src/navigation.rs index aed11634..383f1bf7 100644 --- a/crates/ui-egui/src/navigation.rs +++ b/crates/ui-egui/src/navigation.rs @@ -915,4 +915,26 @@ mod view_key_tests { } assert!(crate::keymap::RESERVED.contains(&crate::keymap::key_name(egui::Key::Num8, egui::Modifiers::COMMAND).as_str())); } + + /// #1886: Ctrl+8 looks straight at the sketch; pressed again while the view faces it, it + /// looks from the other side, and a third press turns it back. Opening a sketch never flips. + #[test] + fn ctrl_8_again_looks_from_the_other_side() { + let mut h = Harness::new(); + h.app.preferences.look_at_anim = false; + h.app.run("sketch.create", json!({"plane": "XY"})).unwrap(); + h.frames(2); + let back_z = |h: &Harness| h.app.cam.back().z; + for want in [1.0, -1.0, 1.0] { + h.step(&json!({"key": "8", "cmd": true})).unwrap(); + h.frames(2); + assert!((back_z(&h) - want).abs() < 1e-3, "looking from z {want}: back {:?}", h.app.cam.back()); + } + // Programmatic look-at (as on opening a sketch) keeps the sketch's own side. + crate::dialogs::look_at_sketch(&mut h.app); + h.frames(2); + crate::dialogs::look_at_sketch(&mut h.app); + h.frames(2); + assert!((back_z(&h) - 1.0).abs() < 1e-3, "{:?}", h.app.cam.back()); + } } diff --git a/crates/ui-egui/src/sketch_palette.rs b/crates/ui-egui/src/sketch_palette.rs index aa81e55a..cb897822 100644 --- a/crates/ui-egui/src/sketch_palette.rs +++ b/crates/ui-egui/src/sketch_palette.rs @@ -575,7 +575,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) { app.start(id); } if look { - crate::dialogs::look_at_sketch(app); + crate::dialogs::look_at_sketch_again(app); } if finish { app.finish_sketch(); diff --git a/crates/ui-egui/src/viewport.rs b/crates/ui-egui/src/viewport.rs index 99309b3f..5ee09c0d 100644 --- a/crates/ui-egui/src/viewport.rs +++ b/crates/ui-egui/src/viewport.rs @@ -3348,7 +3348,8 @@ pub(crate) fn nearest_quarter_turn(yaw: f64) -> f64 { } /// Navigation bar (bottom centre). -/// Turn the view to face the selected planar face or plane, else the active sketch. +/// Turn the view to face the selected planar face or plane, else the active sketch; asked again +/// while it already faces it, from the other side (#1886). pub fn look_at_selection(app: &mut SolveApp) { let target = app.session.selection.iter().find_map(|s| match s { Sel::Face { body, index, point } => crate::dialogs::planar_face(&app.session, body, *index).map(|(_, n)| (*point, n)), @@ -3357,11 +3358,11 @@ pub fn look_at_selection(app: &mut SolveApp) { }); match target { Some((p, n)) => { - let mut to = app.cam.looking_from(n); + let mut to = app.cam.looking_from(crate::dialogs::facing(app, n, true)); to.target = p; app.animate_to(to); } - None => crate::dialogs::look_at_sketch(app), + None => crate::dialogs::look_at_sketch_again(app), } }