Skip to content

The water-coverage slider does not reach the Nauvis tile argmax #320

Description

@wormeyman

Found while porting the Nauvis tile catalog to Rust (#226). Not fixed in that port, per the standing rule: a unilateral fix on the Rust side reads as a port bug in tier 2.

What is wrong

TileResolverParams in src/noise/tiles/resolve.ts has no waterLevel field, so makeTileResolver builds its elevation tree with the default:

const elevationAt = makeElevationNauvis({ seed0, segmentationMultiplier, startingPositions });

makeElevationNauvis does accept waterLevel (elevationNauvis.ts:19, used at line 146 as max(nauvisMain - waterLevel * 2, startingIsland)). It just never gets one from here.

The call site confirms it is not supplied further up either. elevationRenderRequest.ts:570 builds renderTerrain's ctx from eight request fields and waterLevel is not among them - and it could not be, because RenderTerrainOptions.ctx is typed Omit<TileResolverParams, "seed0">. renderTerrain then builds its own early-out elevationAt the same way, so both halves are consistently at water level 0.

renderElevation on the same dispatch does receive waterLevel (elevationRenderRequest.ts:695), so the elevation view responds to the slider and the terrain view does not.

How much it costs

Measured at seed 123456 over a 2,401-point grid (x, y in -600..600 step 25), comparing waterLevel 0 against 5:

quantity differs at
elevation_nauvis 1,144 of 2,401
resolved tile 322 of 2,401 (13.4%)

So this is not a rounding-scale difference. Moving the water-coverage slider should redraw the coastline and, on the Nauvis terrain view, it does nothing.

Why no test caught it

test/resolveTile.spec.ts grades against oracle-tile-names, which is captured on the DEFAULT preset - water level 0 - so the parameter it fails to thread is at its default in every fixture. Its Task 12b block does test that climate params thread through, and waterLevel is simply not in the list.

Scope of the fix

Add waterLevel to TileResolverParams, pass it into makeElevationNauvis in both resolve.ts and renderTerrain.ts's early-out tree, and add it to the ctx block at elevationRenderRequest.ts:570. Worth checking the other three makeTileResolver callers at the same time - renderEnemies.ts:259, renderRocks.ts:189 and renderResources.ts:247 - since a rock or enemy placement gated on a tile would inherit the same blind spot.

A fix will move rendered pixels, so it needs re-scoring rather than a green gate: test/previewAgreement.spec.ts and the tier-3 Nauvis comparisons should be read before and after.

Rust side

crates/fmw-noise carries water_level correctly through NauvisCtx into elevation_nauvis, so the port has no equivalent gap. Tier 2 (test/wasmNauvisParity.spec.ts) builds its tile env from the shipped expression trees rather than from makeTileResolver, precisely so it grades the tile FORMULAS and leaves this plumbing gap visible here instead of hiding it in a checksum. One of its three cases runs at waterLevel = 5.

🤖 Generated with Claude Code

https://claude.ai/code/session_017hVzDgUq6g7LATfczGxUrE

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions