Clip the cooldown fill with Geometry2D, not by hand

Every cooling vessel logged "Invalid polygon data, triangulation failed"
once per frame, for as long as its cooldown ran - 16 of them in a single
test_laser_vessel run. The fill was drawn anyway, so nothing looked wrong,
but it buried real errors in test logs.

_filled_hull cut each hull edge against the fill line itself, which is
Sutherland-Hodgman, and Sutherland-Hodgman is only sound for a CONVEX
subject. Hulls are not: the gas bomber has a tail notch and the distorder a
waist. On those it stitched the pieces back together with a zero-width
bridge, and a polygon with a zero-width bridge is not one the triangulator
accepts.

Geometry2D.intersect_polygons does the clipping properly and returns a list
of polygons, which is also the truer answer - a notched hull genuinely fills
as two puddles until the level rises past the notch. A weld-and-area guard
stays in front of the renderer for repeated vertices and slivers.

Zero triangulation errors across the vessel tests now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Loïc Wisniewski 2026-08-22 23:30:05 +02:00
commit 12bd5eb7bf
2 changed files with 82 additions and 21 deletions

View file

@ -44,8 +44,16 @@ func _process(delta: float) -> bool:
skill._process(0.0)
if first.is_ready() or not second.is_ready() or not skill.button.enabled:
return _fail("the first shot disabled more than its firing bomber")
if not first._filled_hull(fleet.ships[0].definition.hull_points, 0.0).is_empty() \
or first._filled_hull(fleet.ships[0].definition.hull_points, 0.5).size() < 3:
# _filled_hull returns a LIST of polygons: a notched hull fills as two
# separate puddles until the level rises past the notch, and the gas
# bomber's tail is exactly such a notch.
var hull: PackedVector2Array = fleet.ships[0].definition.hull_points
var empty: Array = first._filled_hull(hull, 0.0)
var half: Array = first._filled_hull(hull, 0.5)
var half_points: int = 0
for piece in half:
half_points += piece.size()
if not empty.is_empty() or half_points < 3:
return _fail("bomber cooldown did not produce an empty-to-filling hull")
if skill.projectiles.size() != 1:
return _fail("the first bomber did not launch exactly one projectile")