fix(ui): mouth-ring radius floored at 2x stroke — the radius-smaller-than-stroke draw_arc regime

Two separable findings from the live A/B (real driver): (1) the
original zero-ring report was a viewport-framing crop — the ring
fired all along at screen (1755,-191), above the frame under the
COVER fit; recentering via set_view proved the path live. (2) The
real bug once in-frame: at Quarter fit zoom the compensated ring
radius (5.0/7.5=0.667 canvas) fell below the floored stroke (1.0
canvas) and draw_arc's stroke filled its own hole — a solid blob,
not a ring. Bracket: 1x stroke=blob, 1.5x=hollow recovers, 2x=clean.
Fix: zoom_compensated_ring_radius() floors the radius at 2x the
paired stroke, wired through _zs_ring_radius() for both ring and
halo arcs; verified live producing a clean hollow double-ring. The
third member of the Godot sub-canvas-unit rasterizer family (width
floor, stroke-vs-width sites, now radius-vs-stroke) — all recorded
for the T-1176 render-mechanism discussion. 10 pin tests in the
stroke-width suite; revert-verified (floor drop -> named failures);
full client suite 3956/3956; gdlint clean.

Tickets: T-1170

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 15:47:33 +02:00
co-authored by Claude Fable 5
parent c804f2c239
commit f9ab3088ab
3 changed files with 197 additions and 3 deletions
@@ -140,3 +140,113 @@ func test_zoom_compensated_stroke_width_zero_zoom_does_not_blow_up() -> void:
assert_bool(is_finite(result)).override_failure_message(
"a degenerate zero view_zoom must not produce inf/NaN"
).is_true()
# =============================================================================
# T-1170 live round (2026-07-23, coordinator's mouth-ring finding):
# zoom_compensated_ring_radius() — the RADIUS-SMALLER-THAN-STROKE regime.
# Same A/B-bracket discipline as the stroke-width suite above, applied to
# draw_arc() RING markers (mouth rings), whose radius AND stroke are both
# small zoom-compensated values that can cross each other.
##
## Live A/B evidence (temporary instrumentation, since reverted): at the
## PRODUCTION Quarter-rung radius/stroke pair (radius=0.667, stroke=1.0
## canvas-local units, Quarter fit zoom 7.5), draw_arc() rendered a SOLID
## BLOB, not a hollow ring — confirmed via a re-centered live capture (the
## ORIGINAL "zero ring pixels" symptom was a separate viewport-framing crop,
## not this bug — see zoom_compensated_ring_radius()'s own doc). Bracket
## (stroke fixed at 1.0 canvas-local, radius varied): 0.51 (~stroke/2) ->
## blob; 1.0 (=stroke, the production case) -> blob; 1.5 (1.5x stroke) ->
## hollow ring recovers; 2.0 (2x stroke) -> hollow ring, cleaner. Floor set
## at 2x with margin over the observed 1.0x-blob/1.5x-hollow transition.
# =============================================================================
## At zoom=1.0 with a radius comfortably above stroke*2 already, the floor
## must be a no-op — identical to zoom_compensated_size() directly.
func test_zoom_compensated_ring_radius_no_op_when_radius_already_clears_the_floor() -> void:
var radius: float = AtlasWindowGeometryNature.zoom_compensated_ring_radius(5.0, 1.5, 1.0)
assert_float(radius).is_equal_approx(5.0, 0.0001)
## The EXACT regression shape this round closes: at Quarter's real fit zoom
## (7.5, live capture), MOUTH_RING_RADIUS (5.0) divides to 0.667
## canvas-local — BELOW its own paired stroke (1.5/7.5 floored to 1.0 via
## zoom_compensated_stroke_width) — pinned directly, not just asserted.
## zoom_compensated_ring_radius() must instead return stroke * 2.0 (the
## floor), never the sub-floor raw division result that produced the blob.
func test_zoom_compensated_ring_radius_quarter_mouth_ring_hits_the_floor() -> void:
var view_zoom := 7.5 # LENDEL's live Quarter fit zoom, capture-confirmed
var raw_radius := 5.0 # MOUTH_RING_RADIUS
var stroke: float = AtlasWindowGeometryNature.zoom_compensated_stroke_width(1.5, view_zoom)
var unfloored: float = AtlasWindowGeometryNature.zoom_compensated_size(raw_radius, view_zoom)
assert_float(unfloored).override_failure_message(
"regression pin: the OLD unfloored radius must be AT/BELOW the paired"
+ " stroke at this zoom — this is the exact numeric shape of the blob bug"
).is_less_equal(stroke)
var floored: float = AtlasWindowGeometryNature.zoom_compensated_ring_radius(
raw_radius, stroke, view_zoom
)
assert_float(floored).override_failure_message(
"zoom_compensated_ring_radius() must clamp to stroke * 2.0 (the floor),"
+ " not the sub-floor unfloored value that renders as a solid blob"
).is_equal_approx(stroke * AtlasWindowGeometryNature.RING_RADIUS_STROKE_MULTIPLIER, 0.0001)
## Regression pin (the stroke-width suite's own "effective on-screen value"
## shape, applied to the radius/stroke RATIO instead of an absolute value):
## for the mouth ring AND halo (the two draw_arc() ring markers in this
## cluster), at Quarter's real fit zoom, the floored radius must be AT LEAST
## RING_RADIUS_STROKE_MULTIPLIER times its own paired stroke — the actual
## geometric property that keeps the ring hollow, verified directly rather
## than just re-checking the numeric floor value in isolation.
func test_ring_radius_stays_at_least_the_multiplier_above_its_stroke_at_quarter_zoom() -> void:
var view_zoom := 7.5
# (raw_radius_px, raw_stroke_px) pairs — the mouth ring and halo's own
# literal call-site arguments in _draw_mouth().
for pair in [[5.0, 1.5], [8.0, 1.0]]:
var raw_radius: float = pair[0]
var raw_stroke: float = pair[1]
var stroke: float = AtlasWindowGeometryNature.zoom_compensated_stroke_width(
raw_stroke, view_zoom
)
var radius: float = AtlasWindowGeometryNature.zoom_compensated_ring_radius(
raw_radius, stroke, view_zoom
)
assert_float(radius).override_failure_message(
(
"radius %.3f must be at least %.1fx its paired stroke %.3f — a ratio"
+ " below this rendered as a SOLID BLOB in the live A/B bracket,"
+ " never a hollow ring"
)
% [radius, AtlasWindowGeometryNature.RING_RADIUS_STROKE_MULTIPLIER, stroke]
).is_greater_equal(stroke * AtlasWindowGeometryNature.RING_RADIUS_STROKE_MULTIPLIER - 0.0001)
## At a LOW zoom (e.g. Region's tiny orbital fit, or any zoom where the
## naive radius is already well clear of the floor), the floor must NOT
## engage — matching zoom_compensated_stroke_width()'s own
## does-not-engage-at-low-zoom guarantee. Pins that this is a one-direction
## safety net, not a blanket override.
func test_zoom_compensated_ring_radius_does_not_engage_at_low_zoom() -> void:
var view_zoom := 0.0063 # Lendel's real orbital fit zoom
var raw_radius := 5.0 # MOUTH_RING_RADIUS
var stroke: float = AtlasWindowGeometryNature.zoom_compensated_stroke_width(1.5, view_zoom)
var floored: float = AtlasWindowGeometryNature.zoom_compensated_ring_radius(
raw_radius, stroke, view_zoom
)
var unfloored: float = AtlasWindowGeometryNature.zoom_compensated_size(raw_radius, view_zoom)
assert_float(floored).override_failure_message(
"at a zoom where the naive radius is already far above the floor, the"
+ " floor must be a no-op — identical to zoom_compensated_size()"
).is_equal_approx(unfloored, 0.0001)
## A degenerate zero (or negative) view_zoom must not divide-by-zero/produce
## infinity/NaN — same total-function guarantee as the stroke-width sibling.
func test_zoom_compensated_ring_radius_zero_zoom_does_not_blow_up() -> void:
var stroke: float = AtlasWindowGeometryNature.zoom_compensated_stroke_width(1.5, 0.0)
var result: float = AtlasWindowGeometryNature.zoom_compensated_ring_radius(5.0, stroke, 0.0)
assert_bool(is_finite(result)).override_failure_message(
"a degenerate zero view_zoom must not produce inf/NaN"
).is_true()