From bef3d3eba35e9b70d5eeab9d70fa0350be2ae1c2 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 25 Sep 2026 20:30:19 +0200 Subject: [PATCH] refactor: replace TryGetOtherEndpoint's dead bool with a structural loop bound TryGetOtherEndpoint's bool return was dead control flow: when a mutant flipped its `return false` guard to `return true`, `other` stayed a clone of `coord` (only one component would have been incremented on the true path that never runs for that guard), so the caller compared a sample against itself and behaved identically either way. That produced the equivalent survivors at Extract's :92 continue and the guard's :198 return. Bound the edge loop with the same condition (coord[axis] < cellCounts[axis]) directly, computing `other` inline once the bound holds. One path instead of two that happened to agree; the helper is gone, and so is the `continue` that used to depend on it. Behaviour is unchanged: verified with a before/after run of the existing suite plus a temporary harness comparing Extract's Positions/Quads/QuadEdges against ca220a5's code on sphere, non-cubic, open-plane (each axis), checkerboard, and minimal 2x2x2 fixtures - all byte-for-byte identical. Co-Authored-By: Claude Sonnet 5 --- .../CultGeometrySurfaceNets.cs | 79 +++++++++---------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/src/GameCult.Geometry/CultGeometrySurfaceNets.cs b/src/GameCult.Geometry/CultGeometrySurfaceNets.cs index eb5c9844..22c47df7 100644 --- a/src/GameCult.Geometry/CultGeometrySurfaceNets.cs +++ b/src/GameCult.Geometry/CultGeometrySurfaceNets.cs @@ -57,7 +57,6 @@ namespace GameCult.Geometry var sizeX = samples.GetLength(0); var sizeY = samples.GetLength(1); var sizeZ = samples.GetLength(2); - var sizes = new[] { sizeX, sizeY, sizeZ }; var cellCounts = new[] { sizeX - 1, sizeY - 1, sizeZ - 1 }; var originValue = new float3(origin.X, origin.Y, origin.Z); @@ -87,39 +86,42 @@ namespace GameCult.Geometry for (byte axis = 0; axis < 3; axis++) { - if (!TryGetOtherEndpoint(sizes, coord, axis, out var other)) + // Only an edge whose far endpoint is still inside the grid can cross into a + // quad. Bounding the loop with this one condition, instead of a helper that + // returned a bool for the caller to branch on, leaves one path through the + // rest of the body instead of two that happened to produce the same result. + if (coord[axis] < cellCounts[axis]) { - continue; + var other = (int[])coord.Clone(); + other[axis] += 1; + + var inside = IsInside(samples[x, y, z], isoValue); + var otherInside = IsInside(samples[other[0], other[1], other[2]], isoValue); + if (inside != otherInside && TryGetQuadCells(cellCounts, coord, axis, out var cells)) + { + // The four cells around a crossing edge each contain that same crossing + // edge among their own twelve, so each of them is mixed and has a + // vertex: this indexer cannot miss without the extraction invariant + // already being broken. + var v = new uint[4]; + for (var i = 0; i < 4; i++) v[i] = vertexIndex[cells[i]]; + + // Orientation comes only from which endpoint is inside and the axis's + // fixed handedness (OrientationSign) - never from the quad's own + // geometry, so degenerate (zero-area) quads still orient consistently + // with their neighbours. + if (inside == (OrientationSign[axis] < 0)) + { + (v[1], v[3]) = (v[3], v[1]); + } + + quads.Add(v[0]); + quads.Add(v[1]); + quads.Add(v[2]); + quads.Add(v[3]); + quadEdges.Add(new CultGeometryGridEdge(x, y, z, axis)); + } } - - var inside = IsInside(samples[x, y, z], isoValue); - var otherInside = IsInside(samples[other[0], other[1], other[2]], isoValue); - if (inside == otherInside) continue; - - if (!TryGetQuadCells(cellCounts, coord, axis, out var cells)) - { - continue; - } - - // The four cells around a crossing edge each contain that same crossing edge - // among their own twelve, so each of them is mixed and has a vertex: this - // indexer cannot miss without the extraction invariant already being broken. - var v = new uint[4]; - for (var i = 0; i < 4; i++) v[i] = vertexIndex[cells[i]]; - - // Orientation comes only from which endpoint is inside and the axis's fixed - // handedness (OrientationSign) - never from the quad's own geometry, so - // degenerate (zero-area) quads still orient consistently with their neighbours. - if (inside == (OrientationSign[axis] < 0)) - { - (v[1], v[3]) = (v[3], v[1]); - } - - quads.Add(v[0]); - quads.Add(v[1]); - quads.Add(v[2]); - quads.Add(v[3]); - quadEdges.Add(new CultGeometryGridEdge(x, y, z, axis)); } } @@ -192,20 +194,11 @@ namespace GameCult.Geometry return math.lerp(first, second, amount); } - private static bool TryGetOtherEndpoint(int[] sizes, int[] coord, byte axis, out int[] other) - { - other = (int[])coord.Clone(); - if (coord[axis] + 1 >= sizes[axis]) return false; - - other[axis] += 1; - return true; - } - // The four cells that share the grid edge from (x, y, z) to (x, y, z) + e_axis, listed as a // perimeter loop around the edge (not crossed diagonally) so the caller can build a quad // directly from them. The edge's own coordinate is unchanged for all four cells: it is - // guaranteed to already index a valid cell there because TryGetOtherEndpoint verified - // coord[axis] + 1 < sizes[axis], i.e. coord[axis] < cellCounts[axis]. + // guaranteed to already index a valid cell there because the caller's own loop bound + // already verified coord[axis] < cellCounts[axis]. private static bool TryGetQuadCells(int[] cellCounts, int[] coord, byte axis, out (int, int, int)[] cells) { cells = Array.Empty<(int, int, int)>();