feat(simulation): climate-derived fields + frozen 17-zone morphology (T-1025, T-1027)
T-1025 (D-239 §2) — climate-derived fields, all from the temperature(+moisture) primitive: - precipitation_class + glaciation_grade re-keyed to (temperature_c, moisture_q) inputs (were keyed off raw body params); river_threshold signature follows. - New VegetationClass (Absent/Barren/Scrub/Forest/RiparianScrub/RiparianThicket) + derive_vegetation: treeline bands x elevation, structural Forest->Scrub->Barren no-skip, riparian 1-3 tile band; airless (temp None) -> Absent. - 21 tests. T-1027 (D-239 §5/§6/§7) — morphology classifier: - FROZEN 17-zone MorphologyZone enum exactly per D-239 §6 (OpenOcean..Wetland, repr(u8) pinned). Removed legacy Sea/CoastalLowland/Island/Canyon/Unknown; all consumers (skeleton_gen street_topology) remapped to nearest D-239 zone. - 8-family gated decision tree (LavaField..AlluvialPlain fallback) over integer inputs; hard gates: Fjord>=GlaciationGrade2, LavaField=Volcanic tectonic, lithology slope/form laws (§8). 4 sub-classification zones (TidalFlat, Estuarine, Alpine, Wetland) derived from family + elevation/water. - Build-time compatibility-matrix invariant tests (§7): MeanderReach<->Volcanic and Volcanic<->Fjord forbidden adjacencies structurally prevented by gate order. - 13 tests incl 17-variant completeness + discriminant pinning. D-010 integer discipline throughout. cargo test 1534 pass, clippy -D warnings clean, fmt clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+993
-110
File diff suppressed because it is too large
Load Diff
@@ -1046,19 +1046,31 @@ enum Topology {
|
||||
}
|
||||
|
||||
fn street_topology(m: &MorphologyZone) -> Topology {
|
||||
// D-234 morphology-gated trunk topology.
|
||||
// Updated for D-239 §6 frozen 17-zone vocabulary (T-1027).
|
||||
match m {
|
||||
MorphologyZone::Fjord | MorphologyZone::Canyon | MorphologyZone::MountainPass => {
|
||||
Topology::Ribbon
|
||||
}
|
||||
// Ribbon: linear/constrained terrain — follow the single axis.
|
||||
MorphologyZone::Fjord
|
||||
| MorphologyZone::CliffCoast
|
||||
| MorphologyZone::MountainPass
|
||||
| MorphologyZone::Alpine
|
||||
| MorphologyZone::ValleyFloor => Topology::Ribbon,
|
||||
|
||||
// HubSpoke: water-enclosed or island-like contexts — radiate from a centre.
|
||||
MorphologyZone::Delta
|
||||
| MorphologyZone::Estuarine
|
||||
| MorphologyZone::OpenOcean
|
||||
| MorphologyZone::Lake
|
||||
| MorphologyZone::Sea
|
||||
| MorphologyZone::Island => Topology::HubSpoke,
|
||||
| MorphologyZone::DuneStrand
|
||||
| MorphologyZone::TidalFlat => Topology::HubSpoke,
|
||||
|
||||
// Mesh: flat/open terrain — any pattern.
|
||||
MorphologyZone::AlluvialPlain
|
||||
| MorphologyZone::MeanderReach
|
||||
| MorphologyZone::CoastalLowland
|
||||
| MorphologyZone::Unknown => Topology::Mesh,
|
||||
| MorphologyZone::RiverBank
|
||||
| MorphologyZone::BraidedPlain
|
||||
| MorphologyZone::Wetland
|
||||
| MorphologyZone::Volcanic => Topology::Mesh,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1748,7 +1760,7 @@ mod tests {
|
||||
#[test]
|
||||
fn hub_spoke_shares_a_common_node() {
|
||||
let aps = derive_access_points(&[0, 2, 4, 6], &[]);
|
||||
let corridors = derive_corridors(&aps, &MorphologyZone::Island);
|
||||
let corridors = derive_corridors(&aps, &MorphologyZone::OpenOcean);
|
||||
// Every spoke touches the hub.
|
||||
let hub = central_node(&aps) as u16;
|
||||
assert!(corridors.iter().all(|c| c.from == hub || c.to == hub));
|
||||
@@ -1771,14 +1783,14 @@ mod tests {
|
||||
let inland = subdivide_block_footprints(
|
||||
70,
|
||||
&BulkClass::NonPhysical,
|
||||
&MorphologyZone::CoastalLowland,
|
||||
&MorphologyZone::RiverBank,
|
||||
None,
|
||||
SeedChain::root(3),
|
||||
);
|
||||
let quay = subdivide_block_footprints(
|
||||
70,
|
||||
&BulkClass::NonPhysical,
|
||||
&MorphologyZone::CoastalLowland,
|
||||
&MorphologyZone::RiverBank,
|
||||
Some(Edge::North),
|
||||
SeedChain::root(3),
|
||||
);
|
||||
@@ -1796,14 +1808,14 @@ mod tests {
|
||||
let inland = subdivide_block_footprints(
|
||||
10,
|
||||
&BulkClass::BulkSolid,
|
||||
&MorphologyZone::CoastalLowland,
|
||||
&MorphologyZone::RiverBank,
|
||||
None,
|
||||
SeedChain::root(11),
|
||||
);
|
||||
let quay = subdivide_block_footprints(
|
||||
10,
|
||||
&BulkClass::BulkSolid,
|
||||
&MorphologyZone::CoastalLowland,
|
||||
&MorphologyZone::RiverBank,
|
||||
Some(Edge::North),
|
||||
SeedChain::root(11),
|
||||
);
|
||||
|
||||
@@ -946,38 +946,65 @@ pub struct DoorSpec {
|
||||
pub interior_descriptor: InteriorDescriptor,
|
||||
}
|
||||
|
||||
/// Region-level morphology zone (D-228, D-232, D-234).
|
||||
/// Region-level morphology zone — D-239 §6 frozen 17-zone vocabulary (T-1027).
|
||||
///
|
||||
/// **FREEZE POINT**: this enum is the canonical freeze per D-239 §6.
|
||||
/// Adding, renaming, or removing a zone **requires a D-record amendment**
|
||||
/// (reviewer-enforced). Classifier tuning that merely re-classifies a region
|
||||
/// is NOT a vocabulary change and stays free.
|
||||
///
|
||||
/// Zones ≠ families. Four zones — TidalFlat, Estuarine, Alpine, Wetland —
|
||||
/// are derived sub-classifications (family + elevation/water-height), not
|
||||
/// distinct generator families.
|
||||
///
|
||||
/// Shared by all tiles in a region; constrains street geometry (D-234)
|
||||
/// and acts as a soft weight on cultural-template eligibility (D-232).
|
||||
/// Carried on `CityGenerationContext` (D-233 amend to D-199).
|
||||
///
|
||||
/// Integer-discriminant, D-010 compliant. `repr(u8)` pins values for
|
||||
/// serialisation stability; append-only.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Default)]
|
||||
#[repr(u8)]
|
||||
pub enum MorphologyZone {
|
||||
/// Steep-sided inlet — ribbon/hub-and-spoke streets only; pier geometry on water edges.
|
||||
Fjord,
|
||||
/// Deep-water ocean — hub-and-spoke; perimeter access priority.
|
||||
OpenOcean = 0,
|
||||
/// Lake body — hub-and-spoke; perimeter access toward water.
|
||||
Lake = 1,
|
||||
/// Tidal flat (sub-classification): low coastal plain exposed at low tide.
|
||||
/// Derived from CliffCoast/AlluvialPlain family + tidal signal + low elevation.
|
||||
TidalFlat = 2,
|
||||
/// Dune strand — ribbon/hub-and-spoke; wind-aligned lot orientation.
|
||||
DuneStrand = 3,
|
||||
/// Cliff coast — ribbon only; vertical face at water edge.
|
||||
CliffCoast = 4,
|
||||
/// Fjord — ribbon/hub-and-spoke; pier geometry; requires GlaciationGrade ≥ 2.
|
||||
Fjord = 5,
|
||||
/// River delta / braided channel — hub-and-spoke following channels; bridges as forced nodes.
|
||||
Delta,
|
||||
/// Meandering river reach — any pattern.
|
||||
MeanderReach,
|
||||
Delta = 6,
|
||||
/// Estuarine (sub-classification): brackish tidal zone at river mouth.
|
||||
/// Derived from Delta/AlluvialPlain family + tidal signal + low elevation.
|
||||
Estuarine = 7,
|
||||
/// Flat alluvial plain — any pattern; primary default for plains settlements.
|
||||
#[default]
|
||||
AlluvialPlain,
|
||||
/// Open ocean surface (deep-water context) — hub-and-spoke; perimeter access priority.
|
||||
OpenOcean,
|
||||
/// Lake shore — hub-and-spoke; perimeter access toward water.
|
||||
Lake,
|
||||
/// Interior sea body.
|
||||
Sea,
|
||||
AlluvialPlain = 8,
|
||||
/// River bank — gentle approach; pier geometry on water-facing edges.
|
||||
RiverBank = 9,
|
||||
/// Meandering river reach — any pattern.
|
||||
MeanderReach = 10,
|
||||
/// Braided channel plain — hub-and-spoke; gravel/sand substrate.
|
||||
BraidedPlain = 11,
|
||||
/// Valley floor — ribbon or mesh; enclosed by flanking slopes.
|
||||
ValleyFloor = 12,
|
||||
/// Mountain pass terrain — ribbon only; elevation steps as block boundaries.
|
||||
MountainPass,
|
||||
/// Coastal lowland — any pattern; pier geometry on water-facing edges.
|
||||
CoastalLowland,
|
||||
/// Island context — hub-and-spoke; perimeter access priority.
|
||||
Island,
|
||||
/// Canyon floor — ribbon or hub-and-spoke only.
|
||||
Canyon,
|
||||
/// Unknown / unclassified — fallback to AlluvialPlain behaviour.
|
||||
Unknown,
|
||||
MountainPass = 13,
|
||||
/// Alpine (sub-classification): above treeline high-altitude zone.
|
||||
/// Derived from IncisedGorge/ValleyFloor family + high elevation.
|
||||
Alpine = 14,
|
||||
/// Volcanic — lava field / shield slope; requires TectonicClass::Volcanic.
|
||||
Volcanic = 15,
|
||||
/// Wetland (sub-classification): low-gradient, saturated or seasonally flooded.
|
||||
/// Derived from AlluvialPlain/Delta family + low slope + high moisture.
|
||||
Wetland = 16,
|
||||
}
|
||||
|
||||
/// Built-form archetype derived from a settlement's dominant commodity (D-233).
|
||||
|
||||
Reference in New Issue
Block a user