Log which body/step fails inside tessellate_sat() instead of a silent None

Every early-out here (a bad-body abort, or zero triangles produced) used
to vanish with no trace anywhere, including upstream's own UI - the exact
reason the open '3D solids don't survive save/reopen' bug has resisted
diagnosis so far. Now logs the body index, which step failed, and (for the
zero-bodies case) how many surfaces/curves/pointers lift() couldn't
resolve, so the next real occurrence is diagnosable from the browser
console instead of another silent None. No behavior change - purely
diagnostic.
This commit is contained in:
AI Assistant 2026-08-27 11:39:36 +10:00
commit 6bfdd6c31d

View file

@ -27,13 +27,43 @@ pub fn tessellate_sat(
isolines: usize, isolines: usize,
) -> Option<MeshLodSet> { ) -> Option<MeshLodSet> {
let (bodies, loss) = lift(document); let (bodies, loss) = lift(document);
let body_count = bodies.len();
if bodies.is_empty() { if bodies.is_empty() {
log::warn!(
"tessellate_sat({name}): lift() returned zero bodies - {} unsupported surface(s), {} unsupported curve(s), {} broken/malformed pointer(s) - nothing to draw",
loss.surfaces.len(),
loss.curves.len(),
loss.broken.len()
);
return None; return None;
} }
let mut placed_bodies = Vec::with_capacity(bodies.len()); // Each of the three early-outs below used to be a bare `?`, which - since
for body in bodies { // this is a plain `for` loop, not a closure - aborted the *entire*
let source = body.provenance.source()?; // document (discarding every other body's perfectly good geometry, not
let transform = body_transform(document, source.index() as usize).ok()?; // just the one that failed) with zero trace anywhere, including
// upstream's own UI. That's the exact "reopen shows nothing, Zoom
// Extents finds nothing" symptom this function has been blamed for.
// Logging *which* body and *which* step failed turns the next real
// occurrence into something diagnosable from a browser console instead
// of another silent None (CMMS incident, 2026-08-27).
let mut placed_bodies = Vec::with_capacity(body_count);
for (index, body) in bodies.into_iter().enumerate() {
let Some(source) = body.provenance.source() else {
log::warn!(
"tessellate_sat({name}): body {index}/{body_count} has no provenance source - aborting the whole document instead of just this body"
);
return None;
};
let transform = match body_transform(document, source.index() as usize) {
Ok(t) => t,
Err(err) => {
log::warn!(
"tessellate_sat({name}): body {index}/{body_count} (source record {}) has no resolvable transform: {err:?} - aborting the whole document",
source.index()
);
return None;
}
};
let placement_scale = transform.map_or(1.0, |(_, _, scale)| scale.abs()); let placement_scale = transform.map_or(1.0, |(_, _, scale)| scale.abs());
let placed = if let Some((matrix, translation, scale)) = transform { let placed = if let Some((matrix, translation, scale)) = transform {
let placement = brep::Placement { let placement = brep::Placement {
@ -42,7 +72,13 @@ pub fn tessellate_sat(
z_axis: [scale * matrix[6], scale * matrix[7], scale * matrix[8]], z_axis: [scale * matrix[6], scale * matrix[7], scale * matrix[8]],
origin: translation, origin: translation,
}; };
brep::transform(&body, &placement)? let Some(placed) = brep::transform(&body, &placement) else {
log::warn!(
"tessellate_sat({name}): body {index}/{body_count} failed brep::transform() - aborting the whole document"
);
return None;
};
placed
} else { } else {
body body
}; };
@ -160,6 +196,9 @@ pub fn tessellate_sat(
} }
} }
if indices.is_empty() { if indices.is_empty() {
log::warn!(
"tessellate_sat({name}): {body_count} placed bodies produced zero triangles ({undrawn} face(s) undrawn) - every face was either missing or unrepresentable in its surface's own parameters"
);
return None; return None;
} }