Skip to content

ground-plan GP3 follow-up: walls from a slope budget (draft, not ready) - #3185

Merged
17 commits merged into
ground-planfrom
claude/gp3-wall-budget
Nov 7, 2026
Merged

17 commits merged into
ground-planfrom
claude/gp3-wall-budget

Conversation

@timohueser

@timohueser timohueser commented Nov 6, 2026 •

Copy link
Copy Markdown
Owner

Draft. Lands through #3188; close this PR as included when #3188 merges. #3188's branch holds every commit of this branch (head 35c8f49 is its ancestor), D1 (slopes from the paved outline) and ground-plan f518fd8, merged in (head df48507). One PR into ground-plan gives one diff, one CI run on the real base and one merge. A merge of #3188 into this branch first would only move a branch that nothing else uses. The current gates, the WASM numbers and the renders are in #3188. This body describes this branch at 35c8f49 (rebased onto ground-plan c3f5693); the text below is not updated past it.

Summary

A wall class's cut rises to a bank budget B: the soft greatest fill of the other lines present. Where a higher road's bank needs more room than there is, the lower road's edge gets one wall, and the wall's top follows B. Paths and tracks never wall: their cut steepens up to its own limit. One wall per pair of roads, at the lower edge.

What the branch does, in order:

  • Budget (37664cd, cd21b52): counts(j, k) is the one predicate for which fills count in B. Rust and WGSL. New rule value heights.budget_round_m = 2.0. The WALL line bit (bit 11) marks the wall classes.
  • Legs (4ff3225, eed1381, 1eca72d, 94301d0): a legged way keeps its own shoulder layers. They are labelled per leg only in the legs' near zones. Leg records run past their cuts to the span ends.
  • Junction corner (e02141c): a corner arc has one height: the arms' paved fields weighted by d^-4 and presence. Before, each arm took the arc as its own outline at its own height. At 46.82475, 8.40130 that lifted the ground 3.9 m beside the lower road.
  • Pieces past a core (2860b29): a face that a line hosts by name has the paved field inside the host's core and the slope past it. This removes the 0.68 m step in the i07 wall top.
  • Walls only on wall classes (e66d19b, 2f78a49): a corner's look word carries its line's wall class. A shoulder without a line takes its nearest way's class. The piece shader draws a step as a wall only beside a wall class. A step at a track or path edge stays a grass bank.
  • No berm (f00e766): the fill envelope and B are soft greatests, means weighted by (1 - (max - v) / k)^2. These never rise above the greatest fill. A smooth maximum lifted a near-tie by up to k/4 = 0.5 m.
  • Held (4c726e0): a Held step needs a holder that differs from the paved host, as on ground-plan.
  • MAGIC (be6a431): 0x4f42_520c. ground-plan's tip has 5208, and its history used 5209 to 520b.
  • Tests and names (74017f4): one test for the corner arc's height and one for a hosted face past its core. The layer loops run to LAYERS, and the corner ring reads SHOULDER.
  • B from the two greatest fills (da249bf, delta review D1): B now has the same form as the fill envelope. It is the soft greatest of the two greatest counted fills, with a width that grows from 0 at the greatest one's outline. Before, B was a mean over all counted fills of constant width. It sank by up to 0.43 m, so the ground stepped down 0.2 m at an upper road's own edge beside a junction. Two tests: no step at that edge, and no berm in a level T junction.
  • Named bit (f123e56, D4): WALL_SIDE = 1 << FINE_SHIFT in piece.wgsl.

Review findings (adversarial review of 6c529d7)

# Finding Now
1 Smooth maxima lift the ground at near-ties Fixed (f00e766, da249bf). Reviewer's probes: T junction corner 0.157 m to 0.004 m. Hairpin apex: 0.157 m to 0.004 m at y = 0, 0.27 m to 0.125 m at y = 2. The 0.125 m is the apex road's own edge: the slope there equals the fill from that edge within 1.3 mm. Two roads 1.5 m apart on a fill: hump +0.095 m to a dip of -0.33 m.
2 MAGIC collides with ground-plan Fixed: rebased, MAGIC 520c. Check 7 and GPU are measured on the rebased tree.
3 Every plain shoulder, tracks included, walls Fixed (2f78a49). Proof: r5-track-* shots.
4 Held without a holder other than the host Fixed (4c726e0). Of the 15 steps that moved from Held to unnamed, D1 caused 13; they are gone at f123e56. Two remain (list below).
5 WASM size guard over budget Passes on the rebased tree (numbers below).
6 Phone and GPU cost not measured Unmeasured. Only native benches exist.
7 Bank ratio 1:1 for paths and tracks Proposed, not decided. Unchanged (owner).
8 Wrong claims in the body This body replaces the old one. The literal layer indices are fixed.
9 Duplicates; missing tests at[k].4 and WGSL banks removed (f00e766). Two tests added (74017f4).

Delta review (of 74017f4)

# Finding Now
D1 B sinks below the greatest fill: a 0.2 m step at an upper road's own edge, counted as Apart Fixed (da249bf). The reviewer's T-edge scene now has no step over 1 cm, and its test fails on 74017f4.
D2 The rounds lower the slope by up to 0.18 m: a concave fillet at junctions Not changed. The look is shown in the r6-junction-* shots (an ordinary T junction, 46.8182, 8.4015).
D3 Wall class at mixed road and track junctions Open point (below).
D4 Literal wall-side bit in piece.wgsl Fixed (f123e56).
D5 MAGIC after #3186 520c here. The PR that lands second takes 520e.

Measurements at f123e56 (fixture, own target)

Check ground-plan c3f5693 74017f4 f123e56
GP6 steps / inside / unnamed 5,606 / 26 / 14 5,549 / 21 / 26 5,537 / 21 / 12
GP6 Apart steps (greatest) 803 (5.54 m) 983 (5.54 m) 955 (5.54 m)
GP6 border bit-equal, host missing 245,760 of 245,760, 0 same same
Check 1: partition, BentBorder, Fragment, kinks, short features 0, 92, 4, 18, 162 0, 92, 4, 18, 162 not touched (heights and WGSL only)
Check 2 0 of 480 0 of 480 not touched
Check 7 (native digest in WASM) 256 of 256 not touched
GPU = Rust (model probe, 94,207 points) max 0.119 mm max 0.119 mm, kinds differ 0
Tests, clippy -D warnings 104, clean 106 passed, clean
check:wgsl, vitest src/lib/trailview, npm run check clean, 407, 0 clean, 407 of 407, 0 errors
WASM size guard 690,806 / 704,512 B gz; 1,796,916 / 1,802,240 B raw 690,816 / 704,512 B gz; 1,796,908 / 1,802,240 B raw: ok

Apart is 152 steps more than on ground-plan (955 against 803). Net by face pair:

Face pair ground-plan f123e56
Shoulder without a line, slope 68 218
Platform, slope 635 643
Carriageway, shoulder without a line 0 8
Junction region, shoulder 93 79

The 172 head steps with no ground-plan step within 0.6 m, by class:

  • Walls by design (wall class, lower edge, held by another line's bank): 0. Those walls are named (Held, NoRoom), not Apart.
  • Legged-shoulder pieces (a line-less shoulder whose host is a leg): 4 steps, 0.02 to 0.06 m. Largest: 46.8024071, 8.4487140; 46.8024059, 8.4487127; 46.8145492, 8.3819351.
  • Region edges: 9 steps, 0.01 to 0.05 m. Largest: 46.8212715, 8.3977374; 46.8153611, 8.3823507; 46.7930816, 8.3961954.
  • Other: 159 steps, 0.01 to 0.81 m.
    • 148 of them are shoulder-to-slope steps of 1 to 8 cm. In 134 the slope is the host's own cone; in 14 it is the terrain. Their cause is not traced.
    • 6 are paved-to-paved steps and 5 are platform-to-slope steps.
    • Largest: 46.8231212, 8.3978296 (+0.81 m); 46.8183570, 8.4051557 (-0.08 m); 46.8194023, 8.3942931 (+0.07 m). The 0.81 m step is a carriageway hosted by name past its host's core, which 2860b29 puts on the slope, next to a line-less shoulder of the same host at the paved height.

The two steps that stay unnamed after the Held rule (finding 4), at a residential road's edge: 46.8217323, 8.4049841 (-0.10 m) and 46.8218243, 8.4049972 (+0.17 m). The other 10 unnamed steps are the same as on ground-plan. In the synthetic hairpin test, one step of 0.2 m stands at the upper leg's own edge where the legs overlap. The test counts it; it is the same with a hard B.

Reviewer's probes at f123e56: the T-junction corner berm is 0.004 m (6c529d7: 0.157 m). The upper edge in the T-edge scene has no step. The hairpin apex is 0.004 m at y = 0 and 0.125 m at y = 2; the 0.125 m is the apex road's own edge, and the slope there equals that edge's fill within 1.3 mm. The gap between two close roads on a fill dips 0.33 m with no hump. Two arcs: the greatest change is 2.7 mm per cm.

Crease cells (second difference over a 0.1 m grid, 20 m squares, slope points; > 0.1 m / > 0.3 m / worst):

Place 6c529d7 f123e56
d-hairpin tip 46.82712, 8.39097 341 / 43 / 0.47 m 341 / 43 / 0.47 m
u-loop 46.8246, 8.4042 364 / 68 / 0.55 m 335 / 68 / 0.55 m
junction corner 46.82475, 8.40130 27 / 0 / 0.14 m 43 / 0 / 0.14 m
i07 46.82169, 8.40134 57 / 0 / 0.30 m 52 / 0 / 0.30 m

At the junction corner, 16 cells now pass 0.1 m by up to 0.02 m. They lie along the line where the upper road's fill meets the other fill. The round there is a little narrower now, because it no longer lifts.

Brief build breaks (after f123e56)

  • B1, fixed (35c8f49): a rail bed gives a road no wall. The road's budget counted the rail bed's fill. At 46.8199, 8.3878 the road edge is at 992.95 m, the terrain at 993.45 m and the rail bed at 994.14 m, 0.9 m away. B was the rail's fill at 993.71 m, so the road got a 0.76 m stone wall with a flat top. Now a budget comes from road surfaces only, and a rail bed's own steps draw as banks. The step stands at the rail bed again, as on ground-plan (b1-railroad-*). GP6 unnamed stays 12; Apart 962; GPU = Rust, max 0.119 mm.
  • B2, not fixed (decision needed): torn shards at the fork in conn-track-off-road-top. The walls branch causes them, not the junction record. 2860b29 puts asphalt past its host's core on the slope. At this fork the track's shared asphalt ramps up 2.1 m over 1.25 m toward the road, into steep, dark facets. Keeping asphalt level and letting only shoulders follow the slope cleans the view. But GP6 unnamed rises from 12 to 19, mostly at the u-loop (a 1.74 m shoulder step). No rule today meets both gates; slopes from the paved outline would.

Open points (owner)

  • Slopes from the face plan's paved outline (the heights design review): decided in the morning. Until then, the u-loop's shared asphalt follows the slope past the legs' cores (a shading line), and the d-hairpin tip keeps its crease.
  • Bank ratio 1:1 for paths and tracks: proposed.
  • Phone and GPU cost of heights.wgsl: unmeasured.
  • Wall class at mixed junctions (D3): a junction region with no material line takes its first through line's wall class. A shoulder without a line takes the class of the way whose core lies nearest, not of its host. At a junction of a road and a track, a wall can therefore draw as a bank, or a bank as a wall. No shot covers this.
  • The concave fillet at junctions (D2): the owner decides whether the look is accepted.

Public docs: not changed.

Mode: change #3132
Requirements: none

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

timohueser and others added 13 commits November 7, 2026 02:05
…to the other lines' fills

Work of the stopped implementer, judged sound against the approved design:
- roads.json: cut and fill 2:3 for the wall classes, 1:1 for paths and tracks.
- records: the WALL line flag (leg_wall).
- heights: B = the greatest fill of the other records; a wall class lifts its cut to B, another
  class steepens toward it up to its steepest cut. Step CutWins is now Held.
- legs: cut where two parts' slope bands meet beside each other; Legs::host, foot, runs, the near
  zones and the legged corner-ring rule are gone.

The hairpin cut test still expects the old turn leg.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…et in heights.wgsl

- slope(): B is a pass over the outputs of cones() with one predicate (counts); a class without
  walls steepens toward B by cones() at its cut_max; a slope side in another record's budget is
  a bank (Cause::bank), which names its step Held, also at a region's edge.
- LAYERS, VERGE and SLOPE name the band layers; no literal layer index or count.
- heights.wgsl: the same budget; the wall flag rides in HRec.rank (H_WALL above H_FLAGS).
- MAGIC 0x4f42_5208; line flag bits 12 to 15 documented as free.
- Tests: a road below another walls only its own edge, with a top that never jumps and ends in a
  bank; a hairpin a step high gets one wall at its lower leg.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…runs near another

Own shoulder layers over a whole legged line made labels depend on a cut far away: two cells that
saw the line to different ends disagreed on the shoulder's material at their border (check 2, 4
faults). Without own layers, the plain host of a shoulder in two legs' cores stepped inside the
face (GP6 inside 35). Now the own layers hold the stretches near another leg (Legs::near), where a
band of one leg can lie in another's core: check 2 clean, GP6 inside 28.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ay's own

The rings of a point whose arm lies on a legged way's stretch near another leg go to that way's own
shoulder layers, so the nearest leg hosts them: without it, a ring between two legs took the first
of two equally near legs and stepped 3.4 m inside its face. GP6 inside 26, as on ground-plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed only in the legs' near zones

Own layers on near stretches only (bf19550) made 15 shoulder slivers where a stretch's square end
crossed the plain layer's mitre. Back to the own layers of the whole line and the point-ring rule of
ground-plan, with the near zones that keep the labels local: a piece in a leg's near zone takes its
nearest leg as host, a piece outside stays plain. Legs::host and its verge rule stay deleted.

check 1: Sliver 0, Fragment 4, partition 0, short features 162 (as ground-plan); check 2 0 of 480;
GP6 inside 26, unnamed 17, host missing 0, border bit-equal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s run past their cuts

Creases on the d-hairpin island had three causes, each now gone by a general rule:
- The cut's rise to the budget was a hard max(cut, B): where a lower leg's cut met the upper leg's
  bank, two planes met in a straight crease. The rise is now a smooth maximum whose width grows
  from 0 at the record's outline to budget_round_m (as the toe round), so a wall's foot stays exact.
- B and the fill envelope were hard maxima: now smooth (B's width as the rise's; the envelope's a
  constant budget_round_m = 2 m). B rises no steeper than the steepest fill plus toe_per_m / 4.
- A leg's record ended at its cut, where its frame ran on straight past the end: each leg's record
  now runs on past its cut to the samples next to its cluster's span ends (legs::spans), where no
  two of its parts conflict.

New rule value heights.budget_round_m (2.0; HeightRules word before step_m, Rust and WGSL).
Tests 101/101; check 1 as ground-plan; check 2 0; GP6 inside 25, unnamed 18; GPU = Rust 0.119 mm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…height

Each arm of a corner arc took the arc as its outline at its own height. Where two arms of a
junction differ by a step, the higher arm's fill started at the arc beside the lower arm's edge,
and its bank budget raised a wall of the full step there (4.46 m at the junction corner
46.82475, 8.40130). The arc now has one height: the arms' paved fields weighted by d^-4 and by
presence. Arms that agree share one field, so nothing changes there. Rust and WGSL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e slope

A band piece that a leg hosts can lie past that leg's core: at the i07 tongue tip a shoulder piece
of leg 36 lies 1.0 to 1.3 m past its core. It took the host's paved height, while the bank beside
it starts at the core outline, so the wall's top jumped by 0.68 m where the slope tongue ends.
A face that a line hosts by name now takes the paved field inside the core and the slope past it,
reached over region_fade_m; a junction region's edge toward such a face takes the same height.
Rust and WGSL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…step

The piece shader drew a step as a wall only beside a paved look, so a step beside a shoulder that
looks like ground was a bank of grass. A shoulder without a line now carries a road-surface code in
its ground look word, and the wall test reads that code or a paved look.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ad surface

The wall test drew a step as a wall beside any paved look or any shoulder, so a track's or a
path's edge step became a stone wall. A corner's look word now carries the wall class of its line
(M_WALL) in a bit the pieces do not otherwise read; a shoulder without a line takes its nearest
way's class. The piece shader's wall test reads only that bit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…se above the greatest fill

A smooth maximum of width k lies up to k / 4 above both terms where two fills nearly tie: at every
corner arc, between close roads and where two legs of one line overlap. With budget_round_m = 2
it raised a berm of 0.16 m beside a level T junction, 0.27 m at a hairpin apex and a 0.1 m hump
between two close roads on a fill. Now the envelope and B are soft greatests: means weighted by
(1 - (max - v) / k)^2, which never rise above the greatest fill and give a tie its own value. The
envelope rounds only its two greatest fills, from 0 at the greatest one's outline, so it has no
jump where the greatest changes. The cause of a slope point is the bound that holds it before the
rounds, and ground above the least cut stands on that cut's bank, so the walls keep their names.
Rust and WGSL; the duplicate fill arrays go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d host again

A step at a line's own edge whose slope its own fill holds through a budget was named Held. The
design puts no step at an upper edge, so such a step is a fault and stays unnamed: Held needs a
holder that is not the paved side's host, as on ground-plan, and the cause's bank flag goes. GP6
unnamed goes from 11 to 26; the hairpin test counts its one such step in each mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ground-plan's tip uses 0x4f42_5208 for its STOP_ARM layout, and 5209 to 520b were used in its
history. The merged layout (STOP_ARM, the WALL bit, the 20-word rule block) gets its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
timohueser and others added 4 commits November 7, 2026 02:12
…ed face past its core; layers by name

One focused test each: a corner arc between two arms a step apart lies at the nearer arm's
height, and a face hosted by name has the paved field in its host's core and the slope past it.
The face plan's layer loops run to LAYERS and the corner ring reads the SHOULDER layer, so no
literal layer index is left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o greatest fills from the greatest one's outline

B was a mean over all counted fills with a constant 2 m width. It sank below the greatest fill by
up to 0.21, 0.34 or 0.43 m with one, two or three other fills near it, so beside a junction above a
lower road the ground stepped down 0.2 m at the upper road's own edge. B now takes the same form
as the fill envelope: the two greatest counted fills, with a width that grows from 0 at the
greatest one's outline. It is exact at that edge and still never rises above the greatest fill.
One soft_top serves both, and its comment gives the true sink (0.106 k). Two tests: no step at an
upper road's edge beside a junction above a lower road, and no berm in a level T junction's corners.
Rust and WGSL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
piece.wgsl read a corner's wall-side bit as the literal 268435456u. It is now WALL_SIDE, 1 << FINE_SHIFT,
with the reason the bit is free: the pieces do not read the corridors' fine offset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A road's bank budget counted every other line's fill, a rail bed's too. Beside a rail bed 1.2 m up
with no room for a bank, the road's cut rose to the rail's fill and the road's edge got a stone wall
of up to 0.8 m with a flat top, where ground-plan and develop show a grass bank. A budget now comes
from road surfaces only, and a rail bed's own steps draw as banks, so the step stands at the rail
bed as on ground-plan. Rust, WGSL and a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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