fix(ui): PR #195 review round — attractor stroke widths zoom-compensated (Tyre I1/I2/I3)
I1: _draw_attractor_shape's three stroke-width args (Confluence arc, Coastal/NaturalHarbor arc, Oasis spokes) were raw screen-space literals — Godot multiplies stroke widths by canvas scale exactly like radii, so at the Region orbital fit zoom the outlines rasterized at ~0.01px, the identical sub-pixel class the dot/ring compensation fixed, missed on glyph internals (and attractors are Region-only — precisely where it bites). Widths now arrive pre-compensated via a px_w param, keeping the primitive pure. Regression pin: a source-scan test asserting no draw_arc/draw_line in the function carries a bare numeric width (the draw-smoke suite documents its own vacuous-pass mode, so source-scan is the environment-independent gate); revert-verified by name. I2: D-226 visibility-direction sentence — Araminta's fade-down inversion recorded as pre-T-1170 with its single revisit point named. I3: class-header call-site claim corrected (enter() funnels through _enter_at_rung). Tickets: T-1156 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -216,3 +216,40 @@ func test_draw_with_zero_grid_dims_returns_before_any_draw_call() -> void:
|
||||
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", layer1))
|
||||
o._draw() # grid_w<=0 -> returns before touching the canvas — safe to call directly
|
||||
assert_that(o.get_layer1()).is_not_null()
|
||||
|
||||
|
||||
## PR #195 review (Tyre I1) regression pin: every stroke-WIDTH argument in
|
||||
## _draw_attractor_shape() must route through the pre-compensated `px_w`
|
||||
## param, never a raw numeric literal — Godot multiplies stroke widths by the
|
||||
## canvas scale exactly like radii, so a raw `2.0` rasterizes at ~0.01px at
|
||||
## the Region orbital fit zoom (the identical sub-pixel failure the dot/ring
|
||||
## zoom compensation fixed, missed on glyph outlines in the first pass). The
|
||||
## draw-smoke suite cannot gate this (its own header documents the vacuous-
|
||||
## pass mode under X11 BadMatch), so this is a SOURCE-SCAN pin: parse the
|
||||
## overlay script's _draw_attractor_shape body and assert no draw_arc/
|
||||
## draw_line call carries a bare numeric width literal. Crude but
|
||||
## environment-independent, and it pins the exact regression class (someone
|
||||
## reintroducing a literal width in a new glyph arm).
|
||||
func test_attractor_shape_stroke_widths_are_never_raw_literals() -> void:
|
||||
var src: String = (
|
||||
FileAccess.get_file_as_string("res://ui/implant/apps/atlas/atlas_window_nature_overlay.gd")
|
||||
)
|
||||
var fn_start := src.find("func _draw_attractor_shape(")
|
||||
assert_that(fn_start).override_failure_message(
|
||||
"_draw_attractor_shape must exist in atlas_window_nature_overlay.gd"
|
||||
).is_not_equal(-1)
|
||||
var next_fn := src.find("\nfunc ", fn_start + 1)
|
||||
var body := src.substr(fn_start, (next_fn - fn_start) if next_fn != -1 else -1)
|
||||
var stroke_re := RegEx.new()
|
||||
# A draw_arc/draw_line call whose FINAL (width) argument is a bare numeric
|
||||
# literal: `, <digits[.digits]>)` at call end. px_w-scaled forms
|
||||
# (`px_w`, `2.0 * px_w`) do not match.
|
||||
stroke_re.compile("draw_(arc|line)\\([^\\n]*,\\s*\\d+(\\.\\d+)?\\s*\\)")
|
||||
var hits := stroke_re.search_all(body)
|
||||
var offenders: Array[String] = []
|
||||
for hit in hits:
|
||||
offenders.append(hit.get_string())
|
||||
assert_array(offenders).override_failure_message(
|
||||
"raw numeric stroke width(s) in _draw_attractor_shape — route through"
|
||||
+ " px_w (PR #195 Tyre I1): %s" % [offenders]
|
||||
).is_empty()
|
||||
|
||||
@@ -32,8 +32,10 @@ extends Node2D
|
||||
## through AtlasWindowViewer's own _on_atlas_layers_received() — the viewer
|
||||
## stays at the gdlint max-file-lines cap with this node needing zero new
|
||||
## lines in that function. Requested once per body entry via request_layer1()
|
||||
## (called from the viewer's own enter()/_enter_at_rung()/_enter_tile_mode() —
|
||||
## one line each), which owns clearing stale data on a body change itself
|
||||
## (call sites: the viewer's _enter_at_rung() and _enter_tile_mode() — every
|
||||
## fresh descent funnels through one of those two; enter() itself is a thin
|
||||
## wrapper over _enter_at_rung and has no call of its own), which owns
|
||||
## clearing stale data on a body change itself
|
||||
## (see that function's own doc, no separate reset() call needed) — cached
|
||||
## thereafter, rivers are static per body, no re-request on pan/zoom/rung
|
||||
## crossing.
|
||||
@@ -338,21 +340,27 @@ func _draw_attractors(ctx: Dictionary) -> void:
|
||||
var p: Vector2 = _pos(float(pos_rc[0]), float(pos_rc[1]), ctx)
|
||||
var size: float = _zs(5.0 + strength * 4.0, ctx)
|
||||
var color: Color = AtlasOverlayColors.sub_biome_color(str(a.get("sub_biome", "")))
|
||||
_draw_attractor_shape(str(a.get("attractor_type", "")), p, size, color)
|
||||
_draw_attractor_shape(str(a.get("attractor_type", "")), p, size, color, _zs(1.0, ctx))
|
||||
|
||||
|
||||
## Attractor type -> marker shape — verbatim from the retired
|
||||
## atlas_marker_overlay.gd _draw_attractor_shape(), ported unchanged. `size`
|
||||
## arrives ALREADY zoom-compensated from _draw_attractors() — this function
|
||||
## stays a pure "draw at this literal size" primitive with no ctx/zoom
|
||||
## knowledge of its own, matching the retired code's own signature exactly.
|
||||
func _draw_attractor_shape(atype: String, pos: Vector2, size: float, color: Color) -> void:
|
||||
## Attractor type -> marker shape — from the retired atlas_marker_overlay.gd
|
||||
## _draw_attractor_shape(). `size` AND `px_w` (the 1-screen-px stroke unit)
|
||||
## both arrive ALREADY zoom-compensated from _draw_attractors() — this
|
||||
## function stays a pure "draw at these literal dimensions" primitive with no
|
||||
## ctx/zoom knowledge of its own. px_w exists because Godot multiplies stroke
|
||||
## WIDTH args by the canvas scale exactly like radii (PR #195 review, Tyre
|
||||
## I1: the retired code's raw 1.0/2.0 widths rasterized at ~0.01px at the
|
||||
## Region orbital fit zoom — the same sub-pixel failure the dot/ring
|
||||
## compensation fixed, missed on glyph outlines).
|
||||
func _draw_attractor_shape(
|
||||
atype: String, pos: Vector2, size: float, color: Color, px_w: float
|
||||
) -> void:
|
||||
match atype:
|
||||
"RiverMouth":
|
||||
draw_circle(pos, size, color)
|
||||
"Confluence":
|
||||
draw_circle(pos, size * 0.8, color)
|
||||
draw_arc(pos, size * 1.3, 0.0, TAU, 12, color, 1.0)
|
||||
draw_arc(pos, size * 1.3, 0.0, TAU, 12, color, px_w)
|
||||
"Alpine", "PassEntrance":
|
||||
var pts := PackedVector2Array(
|
||||
[
|
||||
@@ -363,11 +371,11 @@ func _draw_attractor_shape(atype: String, pos: Vector2, size: float, color: Colo
|
||||
)
|
||||
draw_colored_polygon(pts, color)
|
||||
"Coastal", "NaturalHarbor":
|
||||
draw_arc(pos, size, PI * 0.15, PI * 0.85, 10, color, 2.0)
|
||||
draw_arc(pos, size, PI * 0.15, PI * 0.85, 10, color, 2.0 * px_w)
|
||||
"Oasis":
|
||||
draw_circle(pos, size * 0.5, color)
|
||||
for i in range(6):
|
||||
var ang: float = TAU * float(i) / 6.0
|
||||
draw_line(pos, pos + Vector2(cos(ang), sin(ang)) * size, color, 1.0)
|
||||
draw_line(pos, pos + Vector2(cos(ang), sin(ang)) * size, color, px_w)
|
||||
_:
|
||||
draw_circle(pos, size * 0.6, color)
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user