Skip to content

Triangulate polyhedron faces properly instead of fanning them - #88

Merged
revarbat merged 1 commit into
mainfrom
fix-face-triangulation
Aug 11, 2026
Merged

Triangulate polyhedron faces properly instead of fanning them#88
revarbat merged 1 commit into
mainfrom
fix-face-triangulation

Conversation

@revarbat

Copy link
Copy Markdown
Member

polyhedron() fan-triangulated every face: (v0, vi, vi+1) for each i. That is only correct for a polygon that is both convex and planar, and BOSL2's vnf_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):

fan now reference
one cap's triangles 32 32 32
one cap's area 2981.25 — 281% of the polygon 1102.48 1102.48
triangles wound inside out 15 of 32 0 0
whole model surface area +11.78% +0.00%
whole model volume +5.50% +0.02%

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

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>
@revarbat
revarbat merged commit 77e618c into main Aug 11, 2026
3 checks passed
@revarbat
revarbat deleted the fix-face-triangulation branch August 11, 2026 02:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant