forked from 1b-agent/opencadstudio-upstream
fix: final-cycle review minors — decimation duplicate, endpoint-map acceptance
The last review pass over the hardening commit found no blockers; two minors are fixed here. Decimation's force-retain used index arithmetic that the seam entry pushed right after a stride doubling invalidates, duplicating the last segment at exact halving boundaries — it now compares against the last kept entry. The f64 endpoint map's first-wins buckets are 4 f32 ulp wide, so two distinct near-coincident segments can collide and hand a geom the WRONG endpoints; lookups now verify the hit against the geom's own f32 pair (within an ulp) and fall back to the widened pair on mismatch, restoring the closest-match semantics of the scan the map replaced.
This commit is contained in:
1 parent
d135b15ab6
commit
fa7ef87f00
2 files changed
+14
-6
No files matched your search
@@ -80,10 +80,11 @@ pub fn edge_snap_wire(
|
||||
total += 1;
|
||||
}
|
||||
// Force-retain the final segment so decimation never removes the far end
|
||||
// of the mesh: `(total - 1) % stride != 0` proves it is not already kept
|
||||
// (every kept entry sits at an index that is a multiple of `stride`).
|
||||
// of the mesh. The seam entry pushed right after a stride doubling is
|
||||
// NOT on the stride grid, so index arithmetic cannot prove membership —
|
||||
// compare against the last kept entry instead.
|
||||
if let Some(last) = last_pair {
|
||||
if (total - 1) % stride != 0 {
|
||||
if kept.last() != Some(&last) {
|
||||
if kept.len() >= MAX_EDGE_SEGMENTS {
|
||||
halve_kept_segments(&mut kept);
|
||||
}
|
||||
|
||||
+10
-3
@@ -2510,9 +2510,11 @@ fn line_geom_key(p1: [f32; 3], p2: [f32; 3]) -> LineGeomKey {
|
||||
/// the wire's full-precision f64 pair behind it, so every Line geom of the
|
||||
/// wire resolves with an O(1) lookup instead of rescanning the whole
|
||||
/// tessellation per geom (which made `curves_in_frame` O(points × geoms) for
|
||||
/// every wire pair on every snap). First-wins on quantisation collisions:
|
||||
/// adjacent vertices closer than the grid are indistinguishable at snap
|
||||
/// scale anyway.
|
||||
/// every wire pair on every snap). First-wins on quantisation collisions —
|
||||
/// a bucket is 4 f32 ulp wide, so two DISTINCT near-coincident segments can
|
||||
/// share one; lookups therefore verify the hit against the geom's own f32
|
||||
/// endpoints and fall back to the widened pair on mismatch, restoring the
|
||||
/// closest-match semantics of the scan this replaced.
|
||||
fn line_endpoint_map(wire: &WireModel) -> HashMap<LineGeomKey, (DVec3, DVec3)> {
|
||||
let mut map =
|
||||
HashMap::with_capacity_and_hasher(wire.points.len() / 2 + 1, Default::default());
|
||||
@@ -2545,8 +2547,13 @@ fn line_geom_endpoints_f64(
|
||||
if wire.points.len() == 2 {
|
||||
return (wp_f64(wire, 0), wp_f64(wire, 1));
|
||||
}
|
||||
let near = |v: Vec3, p: [f32; 3]| {
|
||||
let ulp = |x: f32, y: f32| (x - y).abs() <= y.abs().max(1.0) * f32::EPSILON;
|
||||
ulp(v.x, p[0]) && ulp(v.y, p[1]) && ulp(v.z, p[2])
|
||||
};
|
||||
endpoints
|
||||
.get(&line_geom_key(p1, p2))
|
||||
.filter(|(a, b)| near(a.as_vec3(), p1) && near(b.as_vec3(), p2))
|
||||
.copied()
|
||||
.unwrap_or((Vec3::from_array(p1).as_dvec3(), Vec3::from_array(p2).as_dvec3()))
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user