Circuit City (shiny.md showcase) ships as a built-in third theme slot on
MapView (style_circuit, theme_select 0/1/2, set_theme with the
keep-stale restyle so switching cross-fades per tile as rebakes land):
near-black ground, charcoal volumes with gloss 1.1 sheen, dark
reflective water, and every road an emissive amber filament weighted by
class. Emissive plumbing: MapRoadRule.emissive -> StrokePassStyle ->
PaintGroup/PaintFace -> road-union center faces carry MAT_ROUTE_GLOW +
strength in param1, and the shader pushes the line color white-hot
behind the route_glow gate. Gloss is now a theme value (ShinyConfig).
Street-tree canopy/trunk colors become theme-driven (tree_canopy /
tree_trunk fill rules) so dark themes get night trees. The example's
Layers panel grows Night/Circuit City rows behaving as radios.
Shadow geometry hardening from live review: silhouette simplification
floored at 0.35 units, and a needle filter drops hair-thin boolean
slivers (area/perimeter < 0.05) that read as dark pins at building
corners.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Screenshot review at Rozengracht/Vondelpark/IJ passed: baked AO and
shadow geometry cost the GPU nothing, water/foliage are a few ALU on
minority pixels, and every toggle A/B-verified back to the classic
frame. Specular sheen defaults on only in the dark theme (it carries
the dark look); route glow stays opt-in.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Shadows are polygons, not passes: each building's roof ring is projected
along the sun's ground direction by height * shadow_len, and footprint +
projection + silhouette quads dissolve into ONE per-tile union (i_overlay
chunked, the road-union lesson) so overlaps never double-darken. Emitted
as material-6 ground fills in the ICON pass — in a city almost all
ground is road surface, and anything earlier gets painted over by the
street passes — with micro-depth 0.40, above the whole grounded road
ladder but below lifted deck bumps. The decal bakes full-dark and the
shader scales it by the live shadow_alpha uniform, so time-of-day fades
don't need a rebake. LOD gates: 3D mode, z>=14, and skip shadows under
2 px at bake magnification. Trees get soft contact-shadow discs.
Terrain: horizon-march cast shadows in the hillshade worker (17-step
exponential stride, ~15 ms per 512px region), skipped automatically
when regional relief is under 150 m. Hillshade + march light from the
one SceneSun via a new per-request sun/flag channel to the worker.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One global light (draw/src/scene_sun.rs): SceneSun POD shared by the
tile bake, ball lighting and terrain hillshade, replacing three
hardcoded sun rigs. ShinyConfig carries every shiny.md toggle; it rides
CompiledMapTheme so bake-flag flips reuse the style-epoch restyle and
stale tiles stay drawable (MapView::update_shiny).
T1: shape-0 geometry now carries its surface normal in param1/2 and a
material id in param3 (walls/roofs/water/canopy/green) — channels were
free, no vertex-format growth. DrawMapVector dispatches per-material
pixel effects behind uniform gates that default off (legacy frame when
off): T4 water noise sheen + sun glint, T4b building specular sheen
with heading-rotation highlight sweep, T5 canopy clump noise + rim and
green-area patchiness.
T2 (first slice): wall vertical AO gradient (ground-contact darkening)
and roof-edge parapet AO strips, baked at zero GPU cost behind bake_ao.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Remove the legacy flat-road fallback and A/B harness, keep patterned non-road strokes on their dedicated path, and make solid roads use the geometry/boolean mesh pipeline unconditionally. Reconcile bridge, link, and padded-tile elevation continuations generically in the tile baker and renderer.
Split ways duplicate a carriageway with an OFFSET centerline through
gores — the twin failed the direction/median gates and baked a lower
fallback height: two overlapping deck slabs at slightly different
heights, swapping mid-deck (the persistent 'road break'). Rejected
all-zero line paths now resample the field ungated with a tight cap
(2.5u, median <= 2u, 60% hit rate) — a gore twin sits within a lane of
the accepted profile and inherits it; frontage streets stay rejected.
Also: seam_probe forensic test (casing-buffer vertex dump + cross-tile
band compare) that located this; memory watchdog removed at user
request (runaway fixed and verified earlier).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- AA fringe coverage now rides per-vertex premultiplied color (inner =
face color, outer = transparent) with full shader coverage: the ramp
interpolates across the whole skirt at any magnification, where the
fwidth-normalized shader ramp pinned to one device pixel and left
magnified tilt-foreground stairs visible. Skirt sits half a ladder step
above its face so the up-screen half wins its depth tie.
- Straddling (partially lifted) rings stay visible in both level parts
but no longer join the grounded cover: a ramp segment spanning the
threshold was cutting the road underneath along its whole length —
the under-the-overpass hole at the benchmark interchange.
- /tmp/mp_start_cam boots the app at a saved viewport; the map draws its
own '@cam lon lat zoom rot tilt' readout bottom-right so any screenshot
is reproducible.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Runaway root cause, captured via self-sampling memory watchdog + boolean
input dumps: simplify_shape on 17-20k-ring soups (dense tiers at high
overzoom) spins in contour extraction for minutes while allocating
unboundedly (both i_overlay 4 and 7). Chunked dissolve (3000-ring chunks,
union of clean outlines) turns the captured hangs into 91/140 ms; the
full pan/tilt torture that blasted to 100 GB now holds 5-6 GB flat.
- i_overlay 4.5 -> 7.0.3 (API-compatible, booleans ~1.7x faster).
- Boolean input hygiene: 1/64-unit snap + degenerate ring drop.
- DzField: guard degenerate terminal-segment cap extension, clamp grid
walks (a NaN/huge coord can no longer explode cell insertion).
- AA fringe v1: miter skirts along boolean output rings, u 0.5->0 over
1px, emitted after each face at its ladder slot.
- Level-straddling rings join both grounded+lifted parts (transition
seams overlap instead of sharing an epsilon-fragile edge).
- Oneway arrows foreshorten with tilt (ground-plane glyphs).
- Debug: in-app memory watchdog (14 GB: sample own stacks -> abort),
MP_TILE_PROFILE via /tmp/mp_tile_profile, boolean repro test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bridge elevation (bridge.md M1+M2): tools/map_tiles bridge-bake solves
per-vertex road/rail dz over the OSM graph (crossing clearance, grade
ramps, deck holds, junction consensus) with AHN DSM-DTM measured deck
profiles (BigTIFF reader in geodata, WGS84->RD), baked as bridge_dz +
per-base-tile base_dz (L/F/P join to exact renderer geometry). Renderer
joins dz through parse into TileWay.dz; strokes/arrows/fills lift off
their own profiles; tunnels never deck.
Road overlay unifier: painter's algorithm as geometry — per-way segment
rects + vertex discs, i_overlay top-down subtraction cascade into
DISJOINT faces (overlay_paint_groups), triangulated once; flat render is
pixel-equal to paint order (proven 0-diff on the abstract unit case) and
tilt cannot reorder it. Legacy strokes interleave by rank; plazas join
the cascade at true alpha. @roads 0/1 A/B toggle, @abtest headless
CPU-raster diff harness (1.50% vs 2D reference on the Raampoort tile),
@cam debug camera, bridge_eval.sh aerial-vs-render loop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Terrain gets real z in tilt mode: a displaced 288x216 surface mesh plus
per-vertex ground lift for all tile geometry (roads/fills/buildings/icons)
via vertex-stage sample_lod of a terrarium-packed elevation texture, with
labels riding the same ground through a lift-aware camera delta. Depth
domains rescale per frame to stay inside the -24 budget; road passes get
a relief-scaled clearance over the surface so cities keep correct
street-vs-building order. Regional zooms (<z14) drape landcover colors
into the hillshade (full-res MVT rasterization, treeline fade) instead of
lifting km-scale polygons that cannot follow relief.
Terrain build now covers EV-trip Europe (lat 43..58, lon -5..17, 8.8 GB
GLO-30 z6-12) with sign-safe Copernicus stems, transparent no-data, and
an Alpine rock/snow ramp. Terrain requests render 4096x3072 over ~3
viewports and only re-render on real movement.
Also: NOMADS GFS wind particle layer synced to the weather clock with 3D
altitude, rain cloud deck lift, stuck-tile fixes (expiring missing set,
decode-failure backoff, restyle placeholder purge), 78-degree tilt
ceiling, and park fill z-fight fix via widened micro-depth ranks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- rain radar layer: pure-Rust KNMI HDF5 reader (superblock v0 walk,
single-chunk deflate datasets, byte-exact vs h5py), ellipsoidal polar
stereographic reprojection (20 m vs product corners), bilinear value
sampling -> smooth banded isolines, textured quad overlay through the
overlay camera, 25-frame nowcast animation; RadarSync polls at most
once per 4 min through a disk-persisted gate and caches frames on disk
- makepad-geodata + makepad-tesla crates join the workspace (overlay
builders, radar sync, NL open-data layers; transit routes now z7-14)
- Europe major-roads routing graph: nav-build --major-roads does a
ways-first scan (5.7M ways / 46M nodes / 194 s / 971 MB) and the app
falls back to it when a route leaves the regional graph — Amsterdam to
Paris routes offline (501.8 km)
- 3D flying markers: chargers/POIs/stops ride thin stalks with DYNAMIC
height (each pin clears its own building +8 m); labels, kW text,
brand and tap zones all consume the baked per-marker lift; stalks and
buildings grow together on the 2D->3D transition (per-tile flat->3D
fade heights, no replay on zoom regens)
- markers depth-honest (small bias, buildings occlude them); phong-lit
canopy/light spheres matching the buildings' NW sun; buildings tint by
BAG age in 3D; district area tints (rank 60, alpha .32); transit line
labels + stop names; follow-mode is an explicit attach/detach toggle;
rotation release schedules the label re-place (no stuck upside-down
labels after a fast spin)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Tesla-style droplet pins anchored at the tail tip (rotate around the
exact site point in tilted views); red exclusively Tesla, amber DC,
blue AC; Tesla pins show stall count, others peak kW
- in-pin text through the normal text renderer: billboard-anchored
glyphs (anchor scales with the map, glyph size constant), exempt from
label collision/repeat culling, no halo
- upright camera-delta labels: straightened labels (place names, POIs,
brands, pin text) translate with the rotation gesture but stay
horizontal — no more rotate-then-snap on regen
- per-icon zoom floors baked in vertex param4 + live icon_zoom uniform:
stale deeper-bucket tiles never flash markers on zoom-out (param4 is
lift-height only for non-icon shapes — icons stay on the ground)
- motorway exit labels (street_labels_points): carto-red name + ref
- 2D/3D mode: tilt gesture syncs app state (TiltChanged action), tilt
release near-flat settles to exact 0, mode flip re-bakes tiles
(extrusions appear/disappear without a zoom nudge)
- Simple 3D Buildings: building:part volumes with min_height bases;
outlines containing parts flatten to footprints; famous buildings
with tourism=attraction extrude instead of dying as attraction fills
- little 3D trees in tilt mode: crossed trunk quads + sphere-slice
ball canopy (architecture-model proportions)
- searchdb/mbtiles support work: reach-based distance ranking, direct
tile lookup + writer ordering
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- every baked charger pin records its normalized position + known
attributes (name, operator, city, max_kw, evses, connectors) as a
PinHit in the tile; taps hit-test a billboard rect through the same
overlay camera markers use (rotation/tilt correct), emitting
MapViewAction::PinTapped ahead of the generic tap
- examples/map: bottom-left info card — operator title, "250 kW max",
bays/connectors, site name, city; hides on plain map tap. (The
search-engine enrichment of this card is deferred by request)
- zooming out from icon level no longer splatters the stale POI carpet:
a tile baked at z16+ hides its symbol pass below icon zoom while
low-bucket pins keep drawing
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Tesla-pin refinement round:
- fast sites render the wide badge with the white bolt LEFT and the kW
number INSIDE the bubble (white pin-class text); street AC gets the
small badge; operator brand ("Tesla", "Fastned"…) reads under the
pin from z13, tier-colored
- overlay paths accept ?fast / ?slow charger-power filters and the
Layers panel splits EV chargers into fast and slow toggles
- draw order restructured: fills/casings/strokes → labels → icon pass →
pin-class text, so pins sit OVER street names while their own kW text
sits over the pins
- point labels for chargers/places/micro POIs straighten under
rotation/3D like addresses do — pins are billboards, their text now
matches
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tesla-GPS framing: charging power is first-class map data.
- icon zoom floors by max_kw: ultra-fast (>=150 kW) from z9, fast DC
(>=50) from z11, street AC posts z13 — the collision pass keeps
density sane
- icon color by tier: red ultra-fast, amber fast, blue AC
- labels from z13: "Supercharger 250 kW" for Tesla operators (red),
"{kw} kW" for other fast DC (amber); street AC stays unlabeled
- app layer toggles read app-tracked state (reading .active in the
changed() pass raced widget state — same class as the panel bug),
plus an overlays log line for future diagnosis
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- natura2000/wetlands areas label their Dutch source names (naam_n2k /
naam) in green at their centroids
- Building age: BAG bouwjaar choropleth over the building fills (rust =
pre-1800 through blue = 2010s, gray = unknown), z13-14
- Population: CBS vierkant cells (vk500/vk100) yellow-to-deep-blue by
aantal_inwoners at 45% opacity; empty cells stay transparent
- two more checkboxes in the Layers panel
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1 of the layers.md track:
- MapView grows overlay_mbtiles_paths (semicolon list) + set_overlay_paths
(stale-keeping rebuild). The loader opens each overlay mbtiles, clamps
to its metadata zoom range, and fetches ancestor tiles with a
quadrant transform when the overlay's maxzoom is below the requested
tile (fills/strokes ride the existing clippers; points bounds-filter)
- overlay MVT layers merge into the normal tile build, styled by layer
name: chargers = blue bolt icons from z12, transit stops dots z13 +
route shapes above the road net, natura2000/wetlands translucent
green tint (fill alpha now flows through fill groups) + outline,
gemeente/wijk/buurt purple dashed boundaries by admin level
- examples/map: Layers button + checkbox panel (EV chargers, Transit,
Nature areas, Districts; raster + rain marked as next phase)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
score_search_hit: settlements are near-immune to distance ("brussels"
from Amsterdam means Bruxelles, not the closest Brusselsestraat),
POIs/streets keep strong local bias, and a number token signals
address intent. nav-build --places-only scans a pbf for settlement
nodes into a compact index (Europe: 1.31M places, 67MB, 60s); the app
merges it with the regional full index, deduping same-name near hits.
Route overlay decimation now measures against the last drawn point,
bounding the error at ~1.5px — pairwise skipping compounded and
visibly reshaped the route when zoomed out.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Search results render as two-line cards (name, then category · address
· distance) instead of one cramped line. Picking a result clears the
input — the next query no longer appends to the old text, which broke
every follow-up search. The nav worker restarts on demand if a script
hot-reload wiped the channel state.
Long-press never synthesizes from a held mouse button on desktop, so
double-click now triggers the same action (set position, then route
here); hints updated accordingly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Axonometric tilt as camera uniforms: screen y compresses about the
view center and building vertices carry their height in meters in
param4, lifted toward screen-top by height * px_per_m * sin(tilt) —
tilt animates freely with zero tile rebuilds. All CPU projections
(gestures, zoom anchor, screen<->lonlat, tile-selection AABB, label
placement + cache, overlay) undo the tilt alongside the rotation.
In 3D mode building footprints come from the all-tag detail archive
(real height / building:levels tags, 8m default) replacing the base
building fills: per-edge flat-shaded wall quads (NW light) painted
north-first as the painter's occlusion approximation, then the lifted
roof. The example app gets a 3D toggle with an eased tilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Heading-up camera: MapView rotation (bearing-up degrees) as uniforms in
the map shader — geometry and map-aligned glyphs (oneway arrows) rotate
about the view center, POI symbols stay upright billboards. Rotation-
aware tile selection AABB, pan/zoom-anchor/screen<->lonlat math, overlay
projection and puck heading, label placement (street labels follow the
rotated baseline, point labels stay horizontal) and the label pan-shift
cache. The nav app eases the camera onto the travel bearing with a
shortest-arc exponential and resets to north when navigation ends.
Micro-POIs: an optional all-tag detail archive (detail_mbtiles_path)
composes over the shortbread base at icon zooms — trees (canopy discs),
benches, waste baskets, recycling, bicycle parking, playgrounds and
artwork/memorial statues, matching osm.org's park furniture. Icons
only; base labels are never duplicated.
Simulated drive now ticks on NextFrame instead of a 20 Hz timer, so
the follow camera moves once per rendered frame.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Conversion tooling (tools/map_tiles): curated Shortbread base pyramid
from a VersaTiles planet archive (bbox extract, brotli->gzip transcode
parallelized per 256x256 block), all-tag native OSM pbf detail
converter, pbf audit, and nav-build/nav-probe producing the routing
graph + search index artifacts. download_map.sh orchestrates pinned
downloads and conversions. mbtile_reader writer now skips SQLite's
lock-byte page at byte offset 1 GiB — allocating through it corrupted
every database over 1 GiB ("2nd reference to page 16385"); the 31 GB
Europe conversion passes integrity checks.
Navigation (libs/map_nav, new): region.search place/POI/street/address
index (prefix autocomplete, NL/EN category synonyms, proximity
ranking) and region.graph routing graph (CSR directed edges,
car/bike/foot speeds, oneway, turn restrictions, snap grid, A*),
maneuver generation and the NavSession map-matching state machine;
26 unit tests.
MapView interaction layer: MapViewAction, camera API + animated
fly_to, overlay.rs (route polyline with traveled dimming, markers,
position puck), per-widget mbtiles_path override, archive
minzoom/maxzoom honored for tile requests, zoom-level cross-fade over
the previous level's imagery, floating panels win hit-testing.
examples/map is the navigator app: worker-thread search, Drive/Bike/
Walk routing, simulated turn-by-turn with banner, follow camera.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>