diff --git a/decisions/architecture.md b/decisions/architecture.md index 20d24b0b8..8540c7a06 100644 --- a/decisions/architecture.md +++ b/decisions/architecture.md @@ -710,9 +710,21 @@ Technical foundation decisions that constrain implementation: engine, client-ser - Maps to D-181 signal visibility ladder **8. Settlement Data Model** - - markers.json is stored in heightmap pixel space — every marker file declares a `grid: { w, h }` header (the heightmap dimensions, typically `1024 × 512`) and every position is `{ x, y }` integer pixels into that grid. This is the canonical storage format used by both the generator and the hand-authored template files (Edict, Vuurkloof, Røros, Cairnside, Estrade). Pixel space is what the heightmap analysis (flood-fill, A* cost grid, city placement) natively operates on; it is deterministic and avoids double-conversion through a projection. - - markers.json schema per body: `grid: {w, h}`, `cities` (name, pos `{x,y}`, population_tier, primary_function, gate_terminal, continent_id), `roads` (path polylines as `[{x,y}, ...]`, connects), `railroads` (path polylines, connects), `pois` (name, kind, pos), plus existing rivers/oceans/mountains with names filled - - Lat/lon strings are a **display-time derivation**, not a storage format. The atlas UI converts `{x, y}` + `grid: {w, h}` into an equirectangular `lat°N/S, lon°E/W` string for the city data panel and hover tooltips, using the body's radius for any great-circle distances it needs. This keeps the immersive surface without paying conversion cost in the generator, the DB, or the diff churn on hand-authored files. + - **Amendment (2026-04-15):** The original §8 (below) described marker positions as lat/lon objects and city records keyed by `population_tier`/`primary_function`/`gate_terminal`/`continent_id`. That shape was aspirational — neither the generator nor the hand-authored templates ever emitted it. Both ended up writing pixel-space row/col arrays against a `512 × 256` storage grid, and PR #129 canonizes that shape so the code and the decision stop drifting. The original prose is preserved immediately below; the current shape follows. + - **Original (2026-04-10, superseded):** markers.json schema per body: `cities` (name, lat/lon, population_tier, primary_function, gate_terminal, continent_id), `roads` (path polylines, connects), `railroads` (path polylines, connects), `pois` (name, kind, position), plus existing rivers/oceans/mountains with names filled. Population tier → city count: `floor(log10(pop/1M))`, modified by `settlement_pattern`. Gate terminal POI at largest population center, sometimes scattered to a smaller one. Moons: same depth as planets, scale with population. + - **Current canonical format:** markers.json is stored in heightmap pixel space. Every marker file declares a `grid: { w, h }` header — the generator, the 6 hand-authored templates (Lendel, Edict, Vuurkloof, Røros, Cairnside, Estrade), and all 2394 procedural seed files ship `{"w": 512, "h": 256}`. Every position is a two-element **array** `[row, col]` of integer pixels into that grid, where `row ∈ [0, h)` and `col ∈ [0, w)` (row is the first axis to match NumPy convention and the flood-fill / A* / cost-grid code that `tooling/planet-gen/` already runs in). Polyline geometry (`roads[*].path`, `railroads[*].path`, `rivers[*].path`) is `[[row, col], [row, col], ...]`. + - **markers.json top-level schema:** + - `grid`: `{"w": 512, "h": 256}` + - `cities[]`: `{id, name, kind, center: [row, col], population}` — `kind` is `capital` or `city`; `name` is empty when awaiting gemma_naming.py (#833). + - `roads[]`: `{id, name, kind, path: [[row, col], …]}` — `kind` is `commercial` by default for generated roads; hand-authored roads use `highway`, `rural`, etc. + - `railroads[]`: same shape as `roads[]`; generated default `kind` is `passenger_freight`. + - `pois[]`: `{id, name, kind, center: [row, col]}` — generated POIs are `kind: "transit"`; hand-authored POIs use `institutional`, `cultural`, `corporate`, etc. + - `rivers[]`: `{id, name, path: [[row, col], …]}` + - `oceans[]`: `{id, name, kind, center: [row, col], area_fraction}` where `kind` is `lake` | `sea` | `ocean`. + - `mountain_ranges[]`: `{id, name, center: [row, col], peak: [row, col], area_cells}` + - Pixel space is what the heightmap analysis (flood-fill, A* cost grid, city placement) natively operates on; it is deterministic and avoids double-conversion through a projection. + - Lat/lon strings are a **display-time derivation**, not a storage format. The atlas UI converts `[row, col]` + `grid: {w, h}` into an equirectangular `lat°N/S, lon°E/W` string for the city data panel and hover tooltips, using the body's radius for any great-circle distances it needs. This keeps the immersive surface without paying conversion cost in the generator, the DB, or the diff churn on hand-authored files. + - Atlas DB index (`atlas_cities`, `atlas_roads`, `atlas_railroads`, `atlas_pois`, `atlas_rivers`, `atlas_oceans`, `atlas_mountain_ranges`, `atlas_body_grids`) mirrors these scalar fields row-by-row for implant-app and development queries; polyline geometry stays in the JSON files next to the heightmaps. - Population tier → city count: `floor(log10(pop/1M))`, modified by `settlement_pattern` - Gate terminal POI: at largest population center, sometimes scattered to a smaller one - Moons: same depth as planets, scale with population @@ -739,4 +751,4 @@ Technical foundation decisions that constrain implementation: engine, client-ser --- -*53 decisions. Last updated: 2026-04-14 (D-191 §8 — markers.json canonical format is pixel space `{x, y}` with a `grid: {w, h}` header; lat/lon is a display-time derivation)* +*53 decisions. Last updated: 2026-04-15 (D-191 §8 amendment — markers.json canonical format is pixel space `[row, col]` arrays against a `512 × 256` grid, following the D-094 amendment pattern; lat/lon is a display-time derivation)* diff --git a/server/data/systems-schema.sql b/server/data/systems-schema.sql index 3c7c2303c..0e5f344de 100644 --- a/server/data/systems-schema.sql +++ b/server/data/systems-schema.sql @@ -167,7 +167,16 @@ CREATE TABLE IF NOT EXISTS bodies ( industrial_corridor TEXT, -- MVG, Gate_Corp, DSMC, Prometheus, Agricultural_Syndic -- Rendering - terrain_reference TEXT, -- heightmap path when authored, NULL otherwise + -- terrain_reference: repo-root-relative path to the body's heightmap PNG. + -- Convention (enforced by populate_terrain_reference.py and assumed by + -- generate_atlas.py and the Godot client's atlas scene loader): + -- wiki/star-systems//bodies//heightmap.png + -- where = system_id with spaces replaced by hyphens + -- (e.g. "GJ 244A" → "GJ-244A"). NULL means no heightmap has been + -- generated for this body yet. The three downstream pipelines + -- (populate, atlas generator, client loader) all assume this format — + -- changing it requires updating all three sites together. + terrain_reference TEXT, screenshot_path TEXT, -- planetary shader screenshot path updated_at TEXT DEFAULT (datetime('now')) diff --git a/wiki/economics/corporations/brands.toml b/wiki/economics/corporations/brands.toml index 40034a74e..ef357024e 100644 --- a/wiki/economics/corporations/brands.toml +++ b/wiki/economics/corporations/brands.toml @@ -7,6 +7,13 @@ # tâtonnement simulation (D-185: brands consume commodities, not the reverse). # Pricing model and cultural premium curves are Phase 3+ deliverables (D-189). # +# Phase 2 boundary: only the 4 anchor brands from D-189 §5 are authored here +# (Calloway, VGV, thrds, Bífröst Marmor). The additional ~23 brand corps +# listed in D-189 §11 are deliberately deferred to Phase 3 — Phase 2 only +# needs the demand-node plumbing + V-B01..V-B06 validation to be exercised +# end-to-end. Add new brands to this file; the importer and validators pick +# them up without schema changes. +# # Fields per [[brand_products]] entry: # brand_product_id Unique slug (corp_id + product slug) # corp_id Must match a slug in wiki/corporations/