fix: faces a sheet fold moves keep their names (#535) (#585)

Faces that a sheet fold or unfold moves keep their names: points on a moved panel are taken back through the panel they lie on to where the sheet held them before the feature, and named from the faces there. Un-ignores the #535 regression test. Fixes #535.

Co-authored-by: jonaswre <jonas.wrede@beyondit.gmbh>
This commit is contained in:
Jonas Wrede authored and GitHub committed 2026-10-13 04:28:27 -04:00
1 parent abff7a49bb
commit 9e2a1eb993
5 files changed
+71 -2

No files matched your search

+46
View File
@@ -1327,6 +1327,39 @@ impl SheetBody {
v
}
/// Where a world point of this sheet lay in `before`, the same sheet before a feature
/// changed its folds: the point goes back to flat coordinates through the panel it lies on
/// and is folded again the way `before` folded that material. A panel a fold made was part of
/// the panel it was folded out of. `None` off every panel (on a bend).
pub(crate) fn point_before(&self, before: &SheetBody, p: Vec3) -> Option<Vec3> {
const TOL: f64 = 1e-4;
for (pi, r) in self.panels() {
let Some(inv) = mat_inverse(&self.world(pi)) else { continue };
let q = apply_point(&inv, p);
if q.z < -TOL || q.z > self.t + TOL {
continue;
}
let q2 = Vec2::new(q.x, q.y);
let pts = dedup(&r.outer.ccw().polyline(1e-3));
if !point_in(&pts, q2) && !near_outline(&pts, q2, TOL) {
continue;
}
// Panels `before` did not have yet came out of their parent's (bounded walk).
let mut owner = pi;
for _ in 0..self.flanges.len() {
match owner {
Some(i) if i >= before.flanges.len() => owner = self.flanges.get(i).and_then(|f| f.parent),
_ => break,
}
}
if owner.is_some_and(|i| i >= before.flanges.len()) {
return None;
}
return Some(apply_point(&before.world(owner), q));
}
None
}
/// The panel edge nearest a world point: (panel, edge a, edge b in flat coordinates, on the
/// top face?, distance).
pub fn edge_at(&self, p: Vec3) -> Option<(Option<usize>, Vec2, Vec2, bool, f64)> {
@@ -2409,6 +2442,19 @@ fn merge_loops(polys: &[Vec<Vec2>], work: &mut FlatWork) -> Result<Vec<Vec<Vec2>
Ok(loops)
}
/// Whether `q` lies within `tol` of the closed polygon `poly`'s outline.
fn near_outline(poly: &[Vec2], q: Vec2, tol: f64) -> bool {
let m = poly.len();
(0..m).any(|i| match (poly.get(i), poly.get((i + 1) % m)) {
(Some(&a), Some(&b)) => {
let d = b - a;
let t = if d.len2() > 1e-18 { ((q - a).dot(d) / d.len2()).clamp(0.0, 1.0) } else { 0.0 };
(a + d * t).dist(q) < tol
}
_ => false,
})
}
fn signed_area(p: &[Vec2]) -> f64 {
let m = p.len();
(0..m).filter_map(|i| Some(p.get(i)?.cross(*p.get((i + 1) % m)?))).sum::<f64>() / 2.0