Selected and hovered edges, and a selected face's outline, show dimmed where the model hides them
Fixes #50 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT
This commit is contained in:
1 parent
1c23620bd9
commit
1e5356e056
2 files changed
+30
No files matched your search
@@ -914,6 +914,7 @@ impl egui_wgpu::CallbackTrait for ViewportCallback {
|
||||
}
|
||||
lines(pass, &res.line, &m.lines);
|
||||
lines(pass, &res.line_hidden, &m.hidden);
|
||||
lines(pass, &res.line_hidden, &h.hidden);
|
||||
lines(pass, &res.line, &pv.lines);
|
||||
lines(pass, &res.line, &h.lines);
|
||||
for (id, q) in &res.quads {
|
||||
|
||||
@@ -929,12 +929,29 @@ fn face_tris(sc: &mut GpuScene, m: &solvecraft_engine::geom::Mesh, face: usize,
|
||||
}
|
||||
}
|
||||
|
||||
/// A highlighted edge: a halo and a core, and where the model hides it, the core dimmed (so a
|
||||
/// pick on the far side still shows).
|
||||
fn edge_lines(sc: &mut GpuScene, e: &[Vec3], core: Color32, halo: Color32, w: f32) {
|
||||
for (col, width) in [(halo, w + 3.0), (core, w)] {
|
||||
for p in e.windows(2) {
|
||||
sc.line(p[0].to_f32(), p[1].to_f32(), c4(col), width, false);
|
||||
}
|
||||
}
|
||||
for p in e.windows(2) {
|
||||
sc.hidden_line(p[0].to_f32(), p[1].to_f32(), c4(core.gamma_multiply(0.45)), (w * 0.7).max(1.0));
|
||||
}
|
||||
}
|
||||
|
||||
/// The outline of a highlighted face where the model hides it (dimmed).
|
||||
fn hidden_face_outline(sc: &mut GpuScene, m: &solvecraft_engine::geom::Mesh, face: usize, col: Color32) {
|
||||
for (ei, e) in m.edges.iter().enumerate() {
|
||||
let on = m.edge_faces.get(ei).is_some_and(|f| f.iter().any(|x| *x as usize == face));
|
||||
if on && !m.seams.get(ei).copied().unwrap_or(false) {
|
||||
for p in e.windows(2) {
|
||||
sc.hidden_line(p[0].to_f32(), p[1].to_f32(), c4(col.gamma_multiply(0.45)), 1.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn profile_fill(sc: &mut GpuScene, app: &SolveApp, sketch: u64, index: usize, fill: Color32, edge: Color32) {
|
||||
@@ -1064,6 +1081,7 @@ fn build_highlight(app: &SolveApp) -> GpuScene {
|
||||
// A live preview stands in for the body: its old faces would fight with it.
|
||||
if let Some(b) = st.body(body).filter(|_| !app.preview.replaced.contains(body)) {
|
||||
face_tris(&mut sc, &b.mesh(), *index, c4(t.sel_face), false);
|
||||
hidden_face_outline(&mut sc, &b.mesh(), *index, t.sel_edge);
|
||||
}
|
||||
}
|
||||
Sel::Body { name } => {
|
||||
@@ -2191,6 +2209,17 @@ fn view_menu(app: &mut SolveApp, ui: &mut egui::Ui, button: Rect) -> bool {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn selection_shows_where_the_model_hides_it() {
|
||||
let mut s = solvecraft_engine::Session::default();
|
||||
s.execute("solid.box", &json!({"length": 40, "width": 30, "height": 20})).unwrap();
|
||||
let mut app = SolveApp::new(s, crate::Services::default());
|
||||
assert!(build_highlight(&app).hidden.is_empty());
|
||||
// The back face: its outline is drawn (dimmed) where the box hides it.
|
||||
app.session.selection = vec![Sel::Face { body: "Body1".into(), index: 3, point: Vec3::new(20.0, 30.0, 10.0) }];
|
||||
assert!(!build_highlight(&app).hidden.is_empty());
|
||||
}
|
||||
|
||||
/// A camera basis looking from `dir` (target → eye), as the view cube draws with.
|
||||
fn basis_from(dir: Vec3) -> (Vec3, Vec3, Vec3) {
|
||||
let cam = solvecraft_engine::render::Camera::default().looking_from(dir);
|
||||
|
||||
Reference in new issue
Block a user