fix: edge keys lose precision above 2.1M vertices, breaking T-junction repair and its check - #113
Open
Dgmtnz wants to merge 1 commit into
Open
fix: edge keys lose precision above 2.1M vertices, breaking T-junction repair and its check#113Dgmtnz wants to merge 1 commit into
Dgmtnz wants to merge 1 commit into
Conversation
…d its check meshRepair packs an edge as `a * 4294967296 + b` (a * 2^32 + b) into a JS number. float64 holds that exactly only while it stays inside 2^53, i.e. up to a = 2^21 = 2,097,152 vertices. Above that distinct edges collide onto one key. diag-edgekey-collision.mjs (added) builds a closed torus whose manifoldness follows from its grid topology rather than from any measurement, so a counter that disagrees is wrong by construction: 1.54M vertices -> 0 non-manifold edges (below the threshold) 2.52M vertices -> 210,422 non-manifold edges (mesh is perfect) 3.74M vertices -> 819,608 non-manifold edges (mesh is perfect) In countEdgeDefects the collision is a false alarm on a good file: a 400mm sphere exported at 0.35mm / 5M triangles reported 185,146 non-manifold edges that staged measurement showed were not in the mesh at any pipeline stage. In resolveTJunctions it is a correctness bug. A genuine boundary edge (count 1) colliding with another reads as count 2, so its T-junction is silently left unrepaired, and decoding the key back with `b = k % 4294967296` yields vertex ids that were never on that edge, feeding wrong candidates to the split search. Both now key on exact Int32 pairs via a new IntPairMap in meshIndex.js, over a dense edge table. This also removes a second ceiling in countEdgeDefects, which counted in a JS Map and throws past V8's ~16.7M entry cap (~11M triangles). Below 2.1M vertices the old keys were exact, so this is a no-op there: pipeline fingerprints are unchanged across sphere, cube-with-fillets, cone, holed-plate and sliver-strip inputs at 10 model/texture/resolution combinations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
meshRepair.jspacks an edge asa * 4294967296 + b(a * 2^32 + b) into a JS number. float64 carries 53 bits of integer precision, so that is exact only up toa = 2^21 = 2,097,152vertices. Above that, distinct edges collide onto one key.Why it matters
In
countEdgeDefectsit is a false alarm on a good file. Colliding edges sum their incidence counts and trip the> 2non-manifold test.diag-edgekey-collision.mjs(added) builds a closed torus whose manifoldness follows from its grid topology rather than from any measurement, so a counter that disagrees is wrong by construction:On a real export — 400 mm sphere, crystal texture, 0.35 mm / 5 M triangles — the app reported 185,146 non-manifold edges. Measuring edge defects after every pipeline stage (subdivide → regularize → displace → decimate → snap → repair) showed the mesh had zero at all of them. The file was fine; the check was wrong.
In
resolveTJunctionsit is a correctness bug, because that function repairs rather than measures:b = k % 4294967296returns vertex ids that were never on that edge, feeding wrong candidates to the on-segment search.So above ~2.1 M vertices the repair pass has been quietly degrading.
Change
Both now key on exact
Int32pairs via a newIntPairMapinmeshIndex.js, over a dense edge table. This also removes a second ceiling incountEdgeDefects: it counted in a JSMap, which throws past V8's ~16.7 M entry cap — reachable at ~11 M triangles.Risk
Below 2.1 M vertices the old keys were exact, so this is a no-op there. Verified with
bench-pipeline.mjs: 10/10 fingerprints unchanged, across sphere, cube-with-0.4 mm-fillets, cone (high-valence apex), holed plate (boundary loops, hard creases) and a sliver strip.Caveats
bench-decim-quality.mjsorbench-tri-estimate.mjs: they need3DBenchy.stl,Barry Bear.stletc., which aren't in the repo. The models above are generated (test-models/mkstl-adversarial.mjs, happy to include).