From 6bfdd6c31d09be9320dbd4e5363e587e348e5910 Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Thu, 27 Aug 2026 11:39:36 +1000 Subject: [PATCH] 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. --- src/scene/convert/acis_kernel.rs | 49 ++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/scene/convert/acis_kernel.rs b/src/scene/convert/acis_kernel.rs index 5b74c78c..06c41e2a 100644 --- a/src/scene/convert/acis_kernel.rs +++ b/src/scene/convert/acis_kernel.rs @@ -27,13 +27,43 @@ pub fn tessellate_sat( isolines: usize, ) -> Option { let (bodies, loss) = lift(document); + let body_count = bodies.len(); 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; } - let mut placed_bodies = Vec::with_capacity(bodies.len()); - for body in bodies { - let source = body.provenance.source()?; - let transform = body_transform(document, source.index() as usize).ok()?; + // Each of the three early-outs below used to be a bare `?`, which - since + // this is a plain `for` loop, not a closure - aborted the *entire* + // document (discarding every other body's perfectly good geometry, not + // 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 placed = if let Some((matrix, translation, scale)) = transform { let placement = brep::Placement { @@ -42,7 +72,13 @@ pub fn tessellate_sat( z_axis: [scale * matrix[6], scale * matrix[7], scale * matrix[8]], 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 { body }; @@ -160,6 +196,9 @@ pub fn tessellate_sat( } } 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; }