mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-05 03:25:58 +00:00
Preview: the ghost leaves out the surface the changed body keeps
The old body was drawn as ghost whole, behind the new body by a fixed depth bias. A curved face the change re-meshes (a boss face whose boundary a fillet moves) has a new triangulation that crosses the old one by up to the chord error, far past that bias, so the ghost showed through it in slivers. Old triangles lying on the new body's surface are now left out; the removed material is still shown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -662,7 +662,16 @@ pub fn build(before: &ModelState, after: &ModelState, c: Colors) -> Built {
|
||||
continue;
|
||||
}
|
||||
let m = b.mesh();
|
||||
// A changed body's surface it keeps is not removed material: drawn as ghost there it
|
||||
// lies on the new body's face, and where that face is curved and meshed anew (its
|
||||
// boundary moved) the two triangulations cross by up to their chord error, far past the
|
||||
// ghost's depth bias, so the ghost showed through the new face in slivers.
|
||||
let kept = if gone { None } else { after.body(&b.name).and_then(|a| SurfaceIndex::new(&a.mesh())) };
|
||||
for t in &m.triangles {
|
||||
let Some(corners) = m.tri(t) else { continue };
|
||||
if kept.as_ref().is_some_and(|k| on_surface(k, corners)) {
|
||||
continue;
|
||||
}
|
||||
for k in t {
|
||||
let i = *k as usize;
|
||||
if let (Some(p), Some(n)) = (m.positions.get(i), m.normals.get(i)) {
|
||||
@@ -709,6 +718,12 @@ pub fn build(before: &ModelState, after: &ModelState, c: Colors) -> Built {
|
||||
Built { scene: sc, sketches, replaced, error: None, bounds: Aabb3::EMPTY, applied: None, tapered: Vec::new(), stopped: None }
|
||||
}
|
||||
|
||||
/// Does the triangle lie on the surface `index` holds: its corners, the middles of its sides
|
||||
/// and its centroid all on it?
|
||||
fn on_surface(index: &SurfaceIndex, [a, b, c]: [Vec3; 3]) -> bool {
|
||||
[a, b, c, (a + b) / 2.0, (b + c) / 2.0, (c + a) / 2.0, (a + b + c) / 3.0].into_iter().all(|p| index.contains(p))
|
||||
}
|
||||
|
||||
/// Which of the `nf` faces of `m` are new surface, i.e. not on the old mesh? Each is decided by
|
||||
/// a vote of points on it (triangle centroids spread over the face). One pass over `m`'s
|
||||
/// triangles gathers the centroids, and the old mesh is indexed once, so the cost stays
|
||||
@@ -1299,6 +1314,29 @@ mod tests {
|
||||
assert_eq!(s.undo.len(), undo);
|
||||
}
|
||||
|
||||
/// A fillet running into a boss: the boss's cylinder face keeps its surface but is meshed
|
||||
/// anew (the fillet's end moves its boundary), so the ghost leaves it out. Drawn as ghost it
|
||||
/// crossed the new triangulation and showed through the face in slivers.
|
||||
#[test]
|
||||
fn a_fillet_preview_ghosts_only_the_removed_material() {
|
||||
let s = session_of(&[
|
||||
("solid.box", json!({"length": 60, "width": 40, "height": 30, "center": [0, 0, 15]})),
|
||||
("solid.cylinder", json!({"radius": 12, "height": 50, "base": [10, -20, 0], "operation": "join"})),
|
||||
]);
|
||||
let b = compute(&s, &[("solid.fillet".into(), json!({"edges": [[-16, -20, 30]], "radius": 4}))], colors()).unwrap();
|
||||
assert_eq!(b.replaced, vec!["Body1".to_string()]);
|
||||
// Ghost triangles on the boss's side, well away from the fillet.
|
||||
let on_boss = b.scene.ghost.as_chunks::<28>().0.as_chunks::<3>().0.iter().filter(|t| {
|
||||
t.iter().all(|v| {
|
||||
let f = |k: usize| f32::from_le_bytes([v[4 * k], v[4 * k + 1], v[4 * k + 2], v[4 * k + 3]]) as f64;
|
||||
let (x, y, z) = (f(0), f(1), f(2));
|
||||
(((x - 10.0).powi(2) + (y + 20.0).powi(2)).sqrt() - 12.0).abs() < 1e-3 && z > 36.0
|
||||
})
|
||||
});
|
||||
assert_eq!(on_boss.count(), 0, "the boss keeps its surface: no ghost on it");
|
||||
assert!(!b.scene.ghost.is_empty(), "the edge the fillet removes is still shown as ghost");
|
||||
}
|
||||
|
||||
/// A cut shows its tool see-through and replaces nothing.
|
||||
#[test]
|
||||
fn cut_preview_scene() {
|
||||
|
||||
Reference in New Issue
Block a user