forked from 1b-agent/opencadstudio-upstream
fix(block_cache): harden defn local_offset against NaN + bad-extent poison
Two latent traps in the first-pass centroid scan: - A bbox where x/y were finite but z was NaN would skip the z update; if NO entity contributed a finite z, `min[2]` stayed at `INFINITY` and `max[2]` at `NEG_INFINITY`. `(INF + NEG_INF) * 0.5 = NaN`, so the defn's `local_offset.z` was NaN, propagating to every cast point in the defn. Track `have` per axis and default missing axes to 0. - A single corrupt entity at parser-garbage coords (1e15+) silently dragged the centroid out of the real geometry's neighbourhood. Reject per-axis values above `SANE_EXTENT` so one bad bbox doesn't ruin the whole defn. Also include `Insert::insert_point` as a contributing sample so a composite block (only nested Inserts, no direct wires) still gets a non-zero offset — useful for the nested-defn AABB transforms even though no direct LocalWire stores against it. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
2f0220b1a0
commit
0d543e8c88
1 file changed
+36
-21
+36
-21
@@ -249,10 +249,19 @@ fn build_defn(
|
||||
// ── Pass 1: compute a defn-level offset near the geometric centre.
|
||||
// Falling back to [0,0,0] for empty / nested-only defns is fine —
|
||||
// precision is only at risk when stored points have large magnitudes,
|
||||
// and those come from direct wire entities.
|
||||
// and those come from direct wire entities. Walks Insert sub-entities
|
||||
// too: nested Inserts' insertion_point contribute to the centroid so a
|
||||
// composite block whose only direct children are Inserts still gets a
|
||||
// useful offset.
|
||||
//
|
||||
// Bbox values that aren't finite (corrupt entities) or that exceed
|
||||
// `SANE_EXTENT` are dropped — otherwise a single bad entity would
|
||||
// poison the centroid for the entire block. Tracked per-axis so a
|
||||
// missing-z entity doesn't taint the x/y choice (would have produced
|
||||
// `INFINITY + NEG_INFINITY = NaN` otherwise).
|
||||
let mut min = [f64::INFINITY; 3];
|
||||
let mut max = [f64::NEG_INFINITY; 3];
|
||||
let mut have_any = false;
|
||||
let mut have = [false; 3];
|
||||
for &eh in &br.entity_handles {
|
||||
let Some(entity) = doc.get_entity(eh) else {
|
||||
continue;
|
||||
@@ -262,35 +271,41 @@ fn build_defn(
|
||||
EntityType::Block(_)
|
||||
| EntityType::BlockEnd(_)
|
||||
| EntityType::AttributeDefinition(_)
|
||||
| EntityType::Insert(_)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
let bb = entity.as_entity().bounding_box();
|
||||
// For Inserts, take the insertion_point itself (acadrust's
|
||||
// `Insert::bounding_box` returns just that). One level deep is
|
||||
// enough for an offset-picker — nested Inserts inside that block
|
||||
// contribute via their own defns.
|
||||
let (bmin, bmax) = match entity {
|
||||
EntityType::Insert(ins) => (ins.insert_point, ins.insert_point),
|
||||
_ => {
|
||||
let bb = entity.as_entity().bounding_box();
|
||||
(bb.min, bb.max)
|
||||
}
|
||||
};
|
||||
let lo_arr = [bmin.x, bmin.y, bmin.z];
|
||||
let hi_arr = [bmax.x, bmax.y, bmax.z];
|
||||
for i in 0..3 {
|
||||
let lo = [bb.min.x, bb.min.y, bb.min.z][i];
|
||||
let hi = [bb.max.x, bb.max.y, bb.max.z][i];
|
||||
let lo = lo_arr[i];
|
||||
let hi = hi_arr[i];
|
||||
if !lo.is_finite() || !hi.is_finite() {
|
||||
continue;
|
||||
}
|
||||
if lo < min[i] {
|
||||
min[i] = lo;
|
||||
if lo.abs() > SANE_EXTENT || hi.abs() > SANE_EXTENT {
|
||||
continue;
|
||||
}
|
||||
if hi > max[i] {
|
||||
max[i] = hi;
|
||||
}
|
||||
have_any = true;
|
||||
if lo < min[i] { min[i] = lo }
|
||||
if hi > max[i] { max[i] = hi }
|
||||
have[i] = true;
|
||||
}
|
||||
}
|
||||
let local_offset: [f64; 3] = if have_any {
|
||||
[
|
||||
(min[0] + max[0]) * 0.5,
|
||||
(min[1] + max[1]) * 0.5,
|
||||
(min[2] + max[2]) * 0.5,
|
||||
]
|
||||
} else {
|
||||
[0.0; 3]
|
||||
};
|
||||
let local_offset: [f64; 3] = [
|
||||
if have[0] { (min[0] + max[0]) * 0.5 } else { 0.0 },
|
||||
if have[1] { (min[1] + max[1]) * 0.5 } else { 0.0 },
|
||||
if have[2] { (min[2] + max[2]) * 0.5 } else { 0.0 },
|
||||
];
|
||||
|
||||
// ── Pass 2: tessellate each sub with the chosen offset so stored
|
||||
// coordinates fit into f32 without precision loss.
|
||||
|
||||
Reference in new issue
Block a user