feat(simulation): D-239 three-carrier foundation (T-1023/1024/1026)
First foundation slice of the Atlas-to-tile derivation model (epic T-974), building the carrier layer ahead of its T-1027+ consumers. T-1026 — Anti-squaring domain warp (D-239 §4): stateless pure fn(seed,body_id,pos)->(f64,f64), ±8m, f64 to the final voxel then as-i32 truncation for IEEE-754 cross-target determinism. New domain_warp.rs, SeedDomain::DomainWarp; golden-vector + cross-thread tests. Position math only — D-010 integer discipline preserved downstream. Marked dead_code until the T-1028 VoxelColumn pipeline consumes it. T-1023 — RegionProfile carrier (D-239 §1,§10): new RegionProfile + TectonicClass/GlaciationGrade/PrecipitationClass enums + BodyParams; derived per-region river_threshold replacing the global 200 for tile consumers. regions: BTreeMap on BodyWorldState, populated via the cascade's new RegionProfile layer (runs when body_params is Some, else falls back to Settlement). D-010 integer discipline, BTree ordering. T-1024 — District climate primitives (D-239 §2): nullable temperature_c + moisture on RegionProfile, mean-annual scalar (no clock dep; dynamic branch deferred to Q-105). Hybrid inputs — new bodies.axial_tilt_deg column imported from planet-gen body-defs (populate_axial_tilt_deg, 2611 bodies), luminosity and orbital distance derived at runtime; greenhouse + diurnal-swing tables in source-canonical climate_constants.toml. D-239 implementation note added. cargo test: 1498 passed, 0 failed. clippy clean (pre-existing large_enum_variant only). make check-systems-db: stamp fresh. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@ use crate::atlas::attractor_matching::CityRecord;
|
||||
use crate::atlas::body_world_state::BodyWorldState;
|
||||
use crate::atlas::cascade::{run_cascade_from_heightmap, CascadeLayer};
|
||||
use crate::atlas::heightmap::{load_heightmap_png, GRID_H, GRID_W};
|
||||
use crate::atlas::region_profile::BodyParams;
|
||||
use crate::atlas::skeleton_gen::{assign_all_block_tags, generate_quarter_skeleton};
|
||||
use crate::seed::SeedChain;
|
||||
use crate::simulation::generator::{CityGenerationContext, QuarterWorldState};
|
||||
@@ -76,6 +77,11 @@ pub enum GenWorkItem {
|
||||
/// time. Drives Layer-3 TerritorialStatus + spatial character (#956,
|
||||
/// D-212/214/215). `None` → `FrontierUnclaimed`.
|
||||
dominant_faction: Option<String>,
|
||||
/// Body physical parameters for the RegionProfile layer (T-1023, D-239 §1).
|
||||
/// Pre-resolved at dispatch time. `None` → region layer skipped for this body.
|
||||
/// Boxed: `BodyParams` is large relative to other variants (clippy
|
||||
/// large_enum_variant) — boxing keeps `GenWorkItem` compact.
|
||||
body_params: Option<Box<BodyParams>>,
|
||||
},
|
||||
/// Generate a Phase 1 QuarterSkeleton for this city.
|
||||
///
|
||||
@@ -358,6 +364,7 @@ fn run_work_item(item: &GenWorkItem) -> GenCompletion {
|
||||
body_seed,
|
||||
cities,
|
||||
dominant_faction,
|
||||
body_params,
|
||||
} => match load_heightmap_png(heightmap_path, body_id, *sea_level) {
|
||||
Ok(hm) => {
|
||||
// Layer 1 runs at the GRID_W×GRID_H working resolution (D-202):
|
||||
@@ -367,16 +374,22 @@ fn run_work_item(item: &GenWorkItem) -> GenCompletion {
|
||||
} else {
|
||||
hm
|
||||
};
|
||||
// Run through Layer 3 (settlement placement, #955): the enqueuer
|
||||
// pre-resolved this body's settlements onto `cities` and its
|
||||
// system faction onto `dominant_faction` (#956). A body with no
|
||||
// settlements yields empty placements at negligible cost.
|
||||
// Run through RegionProfile (T-1023, D-239 §1): includes Settlement
|
||||
// and all prior layers. RegionProfile > Settlement in CascadeLayer ord
|
||||
// so Settlement also runs when body_params is Some. When body_params
|
||||
// is None the cascade falls back to Settlement as the terminal layer.
|
||||
let up_to = if body_params.is_some() {
|
||||
CascadeLayer::RegionProfile
|
||||
} else {
|
||||
CascadeLayer::Settlement
|
||||
};
|
||||
let snapshot = run_cascade_from_heightmap(
|
||||
*body_seed,
|
||||
working,
|
||||
cities,
|
||||
dominant_faction.as_deref(),
|
||||
CascadeLayer::Settlement,
|
||||
body_params.as_deref(),
|
||||
up_to,
|
||||
);
|
||||
GenCompletion::BodyAnalyzed {
|
||||
body_id: body_id.clone(),
|
||||
@@ -480,6 +493,7 @@ mod tests {
|
||||
body_seed: SeedChain::for_body(42, body_id),
|
||||
cities: vec![],
|
||||
dominant_faction: None,
|
||||
body_params: None, // T-1023: no body params in queue-mechanic unit tests
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user