Fix chamfered crisp edges: anchor the QEM quadric solve at the edge midpoint#78
Merged
Conversation
…e chamfering The optimal-point solve in QEM collapse used a raw determinant inverse with an absolute singularity threshold. Along a straight crease the quadric is rank 2 - every point on the crease line has identical cost - so the solve was ill-conditioned and collapse targets slid arbitrarily far along the edge line. Vertices ended up out of order along the crease and the triangulation chamfered across corners: every vertex still exactly on the surface, mesh watertight, but crisp edges visibly cut off (measured 1.64 units of crease deviation on an axis-aligned box at res 128; zero after this change). Replace it with the same truncated-eigen pseudo-inverse the contouring QEF uses (extracted as solveSymmetric3Anchored), anchored at the edge midpoint so rank-deficient directions keep the midpoint coordinate, plus a fallback to endpoint/midpoint candidates if the solved point lands far outside the edge neighborhood. Costs slightly higher triangle counts near creases (box at res 128: 434 -> 1828 tris) in exchange for exact edges. Adds a regression test measuring true crease-line coverage (max point-to-triangle distance) through dual contour + error-bounded simplify on a box. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🚀 Preview deployed: https://fix-crease-chamfer.sinter.pages.dev (updates on every push to this PR) |
kmatzen
marked this pull request as ready for review
July 26, 2026 02:41
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.
Problem
After #77, exported meshes show defects along crisp edges: corners visibly chamfered / cut off, even though every vertex sits exactly on the surface and the mesh is watertight.
Root cause is in
simplifyMesh's collapse-target solve. Along a straight crease the combined quadric is rank 2 — every point on the crease line has identical (zero) cost — so the raw determinant inverse inoptimalQwas ill-conditioned there and let collapse targets slide arbitrarily far along the crease line. Vertices ended up out of order along the edge, and the triangulation chamfered across the corner. The surface-snap pass can't repair it because nothing is off the surface.Measured on an axis-aligned box at res 128 (max distance from the true crease lines to the mesh, point-to-triangle):
The rotated box mostly escaped (0.005) because float noise conditions the system — which is why the existing rotated-corner test didn't catch it.
Fix
Replace the determinant solve with the same truncated-eigen pseudo-inverse the contouring QEF already uses, extracted from
qef.tsassolveSymmetric3Anchored, anchored at the edge midpoint: rank-deficient directions keep the midpoint coordinate instead of being dragged by noise, which also preserves vertex ordering along a crease. A guard falls back to endpoint/midpoint candidates if the solved point ever lands far outside the edge's own neighborhood.Cost: crease-adjacent regions decimate less aggressively (box at res 128: 434 → 1828 triangles). Flat-face decimation is unaffected.
Tests
tsc --noEmitclean.🤖 Generated with Claude Code