Triangulate polyhedron faces properly instead of fanning them - #88
Merged
Conversation
A fan -- (v0, vi, vi+1) for every i -- is only correct for a polygon that is both convex and planar, and BOSL2's vnf_polyhedron() routinely hands over neither. The end caps of a nurbs_sheet() are 34-gons that are concave and 3.5 units out of plane. Fanning one laid 32 triangles covering 281% of the cap's own area with 15 of them wound inside out, and the finished solid came out 11.8% larger in surface area and 5.5% larger in volume than the reference's from the same input. Ear clipping in the face's own best-fit plane now, with the normal from Newell's method so it stays meaningful when the points are not coplanar. Among the valid ears it takes the best-shaped one rather than the first: any ear gives a correct triangulation, but on a face that is not flat the choice decides how the surface folds, and first-found strung long thin triangles across the caps -- still 29% above the reference. Preferring fat ears tracks the surface, and the cap now comes out at 1102.48 square units against the reference's 1102.48. The whole model matches its surface area exactly and its volume to 0.02%. Degenerate cases fall back to a fan, which cannot make them worse: an all-collinear face has no plane to project onto, and a self-intersecting one has no valid ear to find. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
polyhedron()fan-triangulated every face:(v0, vi, vi+1)for eachi. That is only correct for a polygon that is both convex and planar, and BOSL2'svnf_polyhedron()routinely hands over neither.Reported as "the solid is triangulated differently at the ends" against a
nurbs_sheet(). Its two end caps are 34-gons that are concave (15 left turns vs 19 right) and non-planar (3.5 units out of plane):How
Ear clipping in the face's own best-fit plane, with the normal from Newell's method so it stays meaningful when the points are not coplanar.
Among the valid ears it takes the best-shaped one rather than the first. Any ear yields a correct triangulation, but on a non-flat face the choice decides how the surface folds: first-found strung long thin triangles across the caps and still came out 29% above the reference. Preferring fat ears tracks the surface, and lands on the reference's number exactly.
Degenerate cases fall back to a fan, which cannot make them worse — an all-collinear face has no plane to project onto, and a self-intersecting one has no valid ear.
Testing
Six tests. Two shapes, because one is not enough: an L catches the area error, but an L is star-shaped from its first vertex so even a fan gets its volume right. A U, where vertex 0 cannot see the far arm, catches the volume too. Plus a convex face unchanged, exact triangle counts, and a degenerate face that must not hang.
817 tests pass. Negative-controlled by restoring the fan: it fails the L's area and the U's volume, and nothing else.
🤖 Generated with Claude Code