Minimap inset — the car-nav / flight-sim overview
nornir:class=design,guide,idiot
For idiots (30 seconds)
A minimap is the little map in the corner of a driving game or a flight-sim: a tiny top-down picture of the whole city with a dot for "you are here" and a wedge showing "this way I'm looking". Turn the camera and the wedge swings. That's it. In facett's 3D OSM map, turn it on with:
let map = Map3D::from_geometry("OSM 3D", &ways).with_minimap(true);
It draws in the bottom-right corner over the 3D city and never blocks the rest of the view.
What it shows
┌──────────────────────┐ • faint dots = the city's building footprints, flattened
│ · · ·· ··· · · · │ • bright dot = the camera focus ("you are here")
│ ·· ▟▛··· ·· ····· · │ • filled wedge = the view cone (which way + how wide)
│ ·· ◤◉◥ ···· ··· ·· · │
│ · ···· ····· · ·· · │ Turn the camera → the wedge rotates by the same angle.
└──────────────────────┘ Pan the target → the dot slides.
Guide (how to use)
Map3D::with_minimap(true)(chainable) orMsg::SetMinimap(true)at runtime.- Default off — additive, so existing render-proof snapshots stay byte-stable.
map.minimap_on() -> bool— is it painting.map.minimap_view() -> Option<MinimapView>— the live overview data (pure).map.world_bounds() -> Option<[f32;4]>— the ground extent the inset maps.state_json:minimap(bool) +minimap_heading_deg(the live compass bearing) — so a robot-UI matrix can assert "the overview swings with the camera" as data.
Design (why it is a pure struct, not a painter blob)
The minimap is the "minimap / overview" item from osm-3d-gaps.md §D. Its correctness is a data property — where the marker sits, which way the cone points — so it lives as a pure projection separate from any framebuffer:
minimap::MinimapView::compute(bounds, &OrbitCamera) -> MinimapView
- bounds
[min_x, min_z, max_x, max_z]— the scene's ground XZ extent (minimap::xz_bounds(&mesh.positions); the terrain quad spans the whole city). - focus_uv / eye_uv — the camera target / eye, each linearly mapped into normalised
[0,1]²overview space (u∝xright,v∝zdown). - heading — the ground-flattened look direction
(fwd.x, fwd.z)normalised. Becauseuandvare monotone linear inxandz, a world-heading rotation of θ shows up as the SAME θ rotation on the inset — the property the headless A/B proof pins (minimap_proof.rs:+90° bearing → +90° heading). - cone_half — half the view wedge, from the camera
fov_y(always> 0). heading_deg()— the compass bearing, clockwise from+z(atan2(du, dv)).focus_in_bounds()— is the marker on the inset (else panned off the edge).
Map3D::paint_minimap then reads that struct and draws the panel + the strided building-vertex scatter + the focus dot + the cone — a light HUD overlay confined to its corner (like the street-name labels), backend-agnostic: it reads the same numbers whichever RenderBackend drew the 3D city this frame.
Reuse (Law #4)
No new camera, no new renderer: reuses OrbitCamera::basis()/eye()/target math, the existing Mesh3 positions for the footprint scatter, and the same egui::Painter HUD-overlay discipline as paint_street_labels. The MinimapView is CPU-only and inject-assert-testable — no GPU, no clock, no RNG.
Proof surface
tests/minimap_proof.rs— headless data A/B: bearing→heading rotation (1:1), compass-bearing tracking, focus pan slides the marker, off-extent → out-of-bounds, positive cone, determinism. testmatrix cellminimap_overview_heading_and_marker.tests/minimap_robot.rs— live egui_kittest render: the inset paints extra ink in its bottom-right region vs the minimap-off baseline while the rest of the frame is byte-identical (confined), and orbiting swingsminimap_heading_deg. testmatrix cellminimap_robot_render_and_swing.
See also
osm-3d-gaps.md— the full OSM-3D ledger (this is §D "minimap").map3d-design.md/camera-feel-and-ground.md— the orbit camera this reads.