feat(simulation): thread political_archetype/morphology_zone/road_entry_directions through L3->L4 dispatch (T-1039, T-1043)
Completes the GenerateSkeleton dispatch T-1022 wired (which only carried founding_orientation). build_skeleton_work_item now also threads: T-1039: - political_archetype — real value from CityPlacement, replacing the Commission stub. - morphology_zone — looked up from the covering DistrictProfile via new scale::heightmap_pixel_to_district (exported HEIGHTMAP_CELLS_PER_DISTRICT; cascade.rs drops its local CELLS_PER_REGION literal for the shared const). - arrangement_pattern — RE-DERIVED at L4 (option b) via the single pure fn attractor_matching::arrangement_pattern(&archetype, &economic_role); both inputs already at L4. No CityGenerationContext field, no stored derived state (DB-as-cache). Parity test asserts L4 re-derivation == L3-stored value. T-1043: - road_entry_directions — derived from T-1038's BodyWorldState.road_graph: octant = bearing of each incident road edge, deduped per octant, ordered by MaintenanceAuthority rank (the AdminFacing prestige-edge consumer). Feeds derive_access_points -> AccessKind::QuarterEdge; BlockJunction fallback now fires only for genuinely isolated settlements. Acceptance covered: Corporate+Fjord -> Ribbon topology (not mesh+Commission); settlement with a road -> QuarterEdge on the correct octant. 11 new tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -92,6 +92,39 @@ pub fn chunk_to_region(c: ChunkPos) -> RegionPos {
|
||||
district_to_region(chunk_to_district(c))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Working-heightmap pixel ↔ district (cascade.rs working grid)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Number of working-heightmap-grid pixels per district side on the standard
|
||||
/// cascade working resolution (~128×64 working grid; D-203, D-239 §1, T-1023).
|
||||
///
|
||||
/// This is NOT a metre-scale constant — it is the `grid_cells_per_district`
|
||||
/// parameter passed to [`crate::atlas::district_profile::derive_all_districts`].
|
||||
/// Centralised here so plugin.rs and cascade.rs share one definition and neither
|
||||
/// hard-codes `8` independently.
|
||||
///
|
||||
/// `DistrictPos = (col / HEIGHTMAP_CELLS_PER_DISTRICT, row / HEIGHTMAP_CELLS_PER_DISTRICT)`
|
||||
/// for a working-grid pixel `(row, col)` — see [`heightmap_pixel_to_district`].
|
||||
pub const HEIGHTMAP_CELLS_PER_DISTRICT: usize = 8;
|
||||
|
||||
/// Convert a working-heightmap-grid pixel coordinate `(row, col)` to the
|
||||
/// [`DistrictPos`] that covers it.
|
||||
///
|
||||
/// `CityPlacement.position` and `RoadNode.position` are both stored in
|
||||
/// working-heightmap-grid coordinates (row-major, `(row, col)` order), and the
|
||||
/// district grid is built with the same pixel grid by
|
||||
/// [`crate::atlas::district_profile::derive_all_districts`]. Integer division
|
||||
/// floors toward zero, which matches the `BTreeMap` keys inserted by `derive_all_districts`.
|
||||
#[inline]
|
||||
pub fn heightmap_pixel_to_district(pixel: (u16, u16)) -> DistrictPos {
|
||||
let cpd = HEIGHTMAP_CELLS_PER_DISTRICT as i32;
|
||||
// pixel = (row, col); DistrictPos convention is (dx=col_district, dy=row_district).
|
||||
let dx = pixel.1 as i32 / cpd;
|
||||
let dy = pixel.0 as i32 / cpd;
|
||||
(dx, dy)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The elastic seam — region ↔ planet (the only per-body-floating quantity)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user