Navigation bar: Look At (selected face or plane, else the active sketch) and visual style (shaded with edges, shaded, wireframe; kept between runs); F6 fits the view

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-08 02:22:43 +00:00
1 parent 9c03f4bf32
commit 888ac0f5c4
4 files changed
+50 -5

No files matched your search

+9
View File
@@ -183,6 +183,15 @@ pub fn paint(p: &Painter, r: Rect, name: &str, ink: Color32, fill: Color32, acce
pen.poly(&[(3.0, 7.0), (9.0, 10.0), (9.0, 17.0), (3.0, 17.0)], pen.fill, ink);
pen.poly(&[(21.0, 7.0), (15.0, 10.0), (15.0, 17.0), (21.0, 17.0)], Color32::TRANSPARENT, a);
}
"lookat" => {
pen.poly(&[(4.0, 6.0), (16.0, 6.0), (16.0, 18.0), (4.0, 18.0)], pen.fill, ink);
pen.arrow((22.0, 12.0), (13.0, 12.0), a);
}
"style" => {
pen.poly(&[(12.0, 3.0), (20.0, 7.5), (12.0, 12.0), (4.0, 7.5)], pen.fill, ink);
pen.poly(&[(4.0, 7.5), (12.0, 12.0), (12.0, 21.0), (4.0, 16.5)], a, ink);
pen.poly(&[(12.0, 12.0), (20.0, 7.5), (20.0, 16.5), (12.0, 21.0)], Color32::TRANSPARENT, ink);
}
"sun" => {
pen.circle(12.0, 12.0, 4.5, a, ink);
for i in 0..8 {
+7
View File
@@ -61,6 +61,8 @@ pub struct UiState {
pub pick_off: Vec<String>,
/// A click on a face picks its whole body.
pub pick_bodies: bool,
/// 0: shaded with edges, 1: shaded, 2: wireframe.
pub visual_style: u8,
/// Sketches hidden one by one, and finished sketches shown although a feature uses them.
pub hidden_sketches: Vec<u64>,
pub shown_sketches: Vec<u64>,
@@ -86,6 +88,7 @@ impl Default for UiState {
dark: true,
pick_off: Vec::new(),
pick_bodies: false,
visual_style: 0,
hidden_sketches: Vec::new(),
shown_sketches: Vec::new(),
hidden_profiles: Vec::new(),
@@ -184,6 +187,7 @@ impl SolveApp {
"perspective": self.ui.perspective,
"pick_bodies": self.ui.pick_bodies,
"auto_project": self.session.auto_project,
"visual_style": self.ui.visual_style,
})
.to_string()
}
@@ -207,6 +211,9 @@ impl SolveApp {
if let Some(a) = flag("auto_project") {
self.session.auto_project = a;
}
if let Some(s) = v.get("visual_style").and_then(Value::as_u64) {
self.ui.visual_style = u8::try_from(s.min(2)).unwrap_or(0);
}
}
pub fn with_control(mut self, rx: Receiver<ControlRequest>) -> Self {
+1
View File
@@ -406,6 +406,7 @@ pub fn shortcuts(app: &mut SolveApp, ctx: &egui::Context) {
}
}
Key::S => app.ui.palette_open = true,
Key::F6 => app.animate_view("fit"),
Key::Delete | Key::Backspace => crate::viewport::delete_selection(app),
_ => {
let name = format!("{k:?}");
+33 -5
View File
@@ -155,6 +155,7 @@ fn scene_key(app: &SolveApp) -> u64 {
let mut h = std::collections::hash_map::DefaultHasher::new();
app.session.revision.hash(&mut h);
app.ui.show_grid.hash(&mut h);
app.ui.visual_style.hash(&mut h);
app.ui.show_sketches.hash(&mut h);
app.ui.hidden_bodies.hash(&mut h);
app.ui.hidden_sketches.hash(&mut h);
@@ -210,7 +211,13 @@ fn build_scene(app: &SolveApp) -> GpuScene {
let base = s.doc.materials.get(&b.name).and_then(|m| crate::theme::material_color(m)).unwrap_or(tk.body);
let col = if faded { c4(base.gamma_multiply(0.35)) } else { c4(base) };
let m = b.mesh();
for t in &m.triangles {
let (shaded, edges) = match app.ui.visual_style {
1 => (true, false),
2 => (false, true),
_ => (true, true),
};
let edge_col = if shaded { c4(tk.body_edge) } else { c4(tk.text) };
for t in m.triangles.iter().filter(|_| shaded) {
for k in t {
let i = *k as usize;
if let (Some(p), Some(n)) = (m.positions.get(i), m.normals.get(i)) {
@@ -222,12 +229,12 @@ fn build_scene(app: &SolveApp) -> GpuScene {
}
}
}
for (ei, e) in m.edges.iter().enumerate() {
for (ei, e) in m.edges.iter().enumerate().filter(|_| edges) {
if m.seams.get(ei).copied().unwrap_or(false) {
continue;
}
for w in e.windows(2) {
sc.line(w[0].to_f32(), w[1].to_f32(), c4(tk.body_edge), 1.3, false);
sc.line(w[0].to_f32(), w[1].to_f32(), edge_col, 1.3, false);
}
}
}
@@ -1483,15 +1490,34 @@ fn point_in_poly(p: Pos2, poly: &[Pos2]) -> bool {
}
/// Navigation bar (bottom centre).
/// Turn the view to face the selected planar face or plane, else the active sketch.
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)),
Sel::Plane { name } => solvecraft_engine::geom::Plane::named(name).map(|p| (p.origin, p.normal())),
_ => None,
});
match target {
Some((p, n)) => {
let mut to = app.cam.looking_from(n);
to.target = p;
app.animate_to(to);
}
None => crate::dialogs::look_at_sketch(app),
}
}
fn nav_bar(app: &mut SolveApp, ui: &mut egui::Ui, rect: Rect) {
let t = Tokens::get();
let items: [(&str, &str); 7] = [
let items: [(&str, &str); 9] = [
("orbit", "Orbit (drag; or Shift+middle / right drag)"),
("pan", "Pan (drag; or middle drag)"),
("zoom", "Zoom (drag; or wheel)"),
("fit", "Fit"),
("fit", "Fit (F6)"),
("lookat", "Look At the selected face or plane (or the active sketch)"),
("home", "Home view"),
("perspective", "Perspective / orthographic"),
("style", "Visual style: shaded with edges, shaded, wireframe"),
("settings", "Grid on/off"),
];
let w = items.len() as f32 * 30.0 + 10.0;
@@ -1521,6 +1547,8 @@ fn nav_bar(app: &mut SolveApp, ui: &mut egui::Ui, rect: Rect) {
"zoom" => app.viewport.nav = toggle(NavMode::Zoom, app.viewport.nav),
"fit" => app.animate_view("fit"),
"home" => app.animate_view("home"),
"lookat" => look_at_selection(app),
"style" => app.ui.visual_style = (app.ui.visual_style + 1) % 3,
"perspective" => app.ui.perspective = !app.ui.perspective,
_ => app.ui.show_grid = !app.ui.show_grid,
}