fix(simulation): region-anchor channels and linear landforms (T-1040, T-1041)

derive_chunk_context now derives channel_anchor_m and coast_anchor_m from
the Region-or-higher seed path (D-239 §10); all five voxel placement sites
measure distance to these anchors in continuous world coordinates:

- T-1040: compute_channel_state / compute_meander_reach_channel /
  in_levee_band subtract the region anchor instead of measuring from the
  world origin axis — active channels now exist in their chunks anywhere
  on the body, continuous across chunk boundaries. has_active_channel is
  honest in both directions (band-crossing gate; floor covers the
  BraidedDelta belt at short wavelengths).
- T-1041: fjord/cliff/gorge/delta drop their rem_euclid(64) chunk-frame
  folds — one valley/coastline/fan per region instead of one per chunk.
  D-239 §9 chokepoint widths preserved; two i32 wall-rise overflows on
  region-scale distances fixed by clamping before multiply.

Harness: 6 new believability tests (channel presence + gate honesty at
chunk (1000,-750), cross-boundary continuity, one-landform-per-region,
braid-belt confinement); drainage sweeps strengthened with anchor-band
chunks. Voxel golden regenerated deliberately: case A now pins a genuinely
in-channel Shallow voxel on the anchor, case C a Deep trough voxel
matching its label; case B byte-identical and L0/L1 cascade golden
untouched (no upstream leak). Review: approved, three minors addressed
(truncation-convention docs, delta gate floor, case C relocation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 13:46:38 +02:00
co-authored by Claude Fable 5
parent 49a74e93d4
commit 408bd39e63
4 changed files with 1007 additions and 282 deletions
+558 -43
View File
@@ -39,7 +39,7 @@
use std::path::PathBuf;
use std::time::Instant;
use settled_reach_server::atlas::chunk_context::derive_chunk_context;
use settled_reach_server::atlas::chunk_context::{derive_chunk_context, BasinDirection, ChunkPos};
use settled_reach_server::atlas::drainage;
use settled_reach_server::atlas::features::TerrainAnalysis;
use settled_reach_server::atlas::heightmap::BodyHeightmap;
@@ -100,6 +100,37 @@ fn derive_golden(
}
}
/// Compute the chunk position + tile coordinates sitting ON the region's
/// channel anchor at a given along-axis chunk index (T-1040/T-1041): feature
/// placement is region-anchored, so the golden pins a voxel on the anchor
/// column. Relocates automatically if the anchor derivation changes — which
/// flips the golden values anyway.
///
/// `along_chunk` must stay within region (0, 0) — i.e. in `0..16` — so the
/// probed anchor belongs to the same region as the returned chunk.
fn anchor_golden_pos(
seed: u64,
body_id: &str,
region: &RegionProfile,
along_chunk: i32,
) -> ((i32, i32), i32, i32) {
assert!(
(0..16).contains(&along_chunk),
"along_chunk must stay within region (0, 0)"
);
let probe = derive_chunk_context(seed, body_id, region, (0, 0));
let anchor = probe.channel_anchor_m;
let along_tile = along_chunk * 64 + 32;
match probe.basin_direction {
BasinDirection::North | BasinDirection::South => {
((anchor.div_euclid(64), along_chunk), anchor, along_tile)
}
BasinDirection::East | BasinDirection::West => {
((along_chunk, anchor.div_euclid(64)), along_tile, anchor)
}
}
}
/// The three fixed golden inputs. Varied families and climate states.
fn golden_cases() -> Vec<(
&'static str,
@@ -110,26 +141,45 @@ fn golden_cases() -> Vec<(
i32,
i32,
)> {
// Cases A and C pin voxels on the region channel anchor (T-1040/T-1041):
// the channel/trough centreline is region-anchored, not at the world
// origin (A) or the chunk centre (C).
let alluvial_region = make_region(
MorphologyZone::AlluvialPlain,
TectonicClass::Active,
GlaciationGrade::None,
6,
22,
18,
68,
Some(12.0),
VegetationClass::Forest,
);
let (alluvial_chunk_pos, alluvial_tx, alluvial_ty) =
anchor_golden_pos(0xdeadbeef_cafebabe_u64, "GJ144d", &alluvial_region, 12);
let fjord_region = make_region(
MorphologyZone::Fjord,
TectonicClass::Active,
GlaciationGrade::Moderate,
55,
60,
28,
55,
Some(-8.0),
VegetationClass::Barren,
);
let (fjord_chunk_pos, fjord_tx, fjord_ty) =
anchor_golden_pos(0xfeedface_0badc0de_u64, "GJ447c", &fjord_region, 5);
vec![
// Case A: AlluvialPlain — temperate forest, active channel.
// Case A: AlluvialPlain — temperate forest, on the active-channel anchor.
(
"alluvial_forest_active_channel",
0xdeadbeef_cafebabe_u64,
"GJ144d",
make_region(
MorphologyZone::AlluvialPlain,
TectonicClass::Active,
GlaciationGrade::None,
6,
22,
18,
68,
Some(12.0),
VegetationClass::Forest,
),
(10, 20),
640,
1280,
alluvial_region,
alluvial_chunk_pos,
alluvial_tx,
alluvial_ty,
),
// Case B: LavaField — barren volcanic, no channel.
(
@@ -151,26 +201,16 @@ fn golden_cases() -> Vec<(
320,
448,
),
// Case C: FjordWall — glaciated, rocky walls, deep water in trough.
// Case C: FjordWall — glaciated, rocky walls; tile on the region-anchored
// trough centreline (Deep water unless the warp nudges it onto the floor edge).
(
"fjord_wall_glaciated",
0xfeedface_0badc0de_u64,
"GJ447c",
make_region(
MorphologyZone::Fjord,
TectonicClass::Active,
GlaciationGrade::Moderate,
55,
60,
28,
55,
Some(-8.0),
VegetationClass::Barren,
),
(14, 8),
// Centre of the chunk (fjord trough) — should be Deep water, Rock.
14 * 64 + 32,
8 * 64 + 32,
fjord_region,
fjord_chunk_pos,
fjord_tx,
fjord_ty,
),
]
}
@@ -320,8 +360,12 @@ fn law_drainage_monotonicity_alluvial_sweep() {
VegetationClass::Forest,
);
let mut checked = false;
for (cx, cy) in [(0, 0), (1, 0), (0, 1), (4, 4), (8, 3)] {
checked |= assert_drainage_monotonicity(42, "GJ144d", "AlluvialPlain", &region, (cx, cy));
// Sweep a spread of region chunks plus the channel-anchor band (T-1040:
// the channel is region-anchored, so only band chunks carry wet tiles).
let mut positions = vec![(0, 0), (1, 0), (0, 1), (4, 4), (8, 3)];
positions.extend(anchor_band_chunks(42, "GJ144d", &region, (0, 0)));
for pos in positions {
checked |= assert_drainage_monotonicity(42, "GJ144d", "AlluvialPlain", &region, pos);
}
assert!(
checked,
@@ -344,8 +388,10 @@ fn law_drainage_monotonicity_meander_sweep() {
VegetationClass::Forest,
);
let mut checked = false;
for (cx, cy) in [(0, 0), (2, 1), (5, 5)] {
checked |= assert_drainage_monotonicity(99, "GJ447c", "MeanderReach", &region, (cx, cy));
let mut positions = vec![(0, 0), (2, 1), (5, 5)];
positions.extend(anchor_band_chunks(99, "GJ447c", &region, (0, 0)));
for pos in positions {
checked |= assert_drainage_monotonicity(99, "GJ447c", "MeanderReach", &region, pos);
}
assert!(
checked,
@@ -369,16 +415,27 @@ fn law_drainage_monotonicity_fjord_floor_at_sea_level() {
Some(-8.0),
VegetationClass::Barren,
);
let chunk = derive_chunk_context(42, "fjord_body", &region, (0, 0));
// The fjord trough is region-anchored (T-1041): scan the chunk whose
// cross-range contains the channel anchor — only that chunk column carries
// the deep-water trough.
let probe = derive_chunk_context(42, "fjord_body", &region, (0, 0));
let anchor_idx = probe.channel_anchor_m.div_euclid(64);
let chunk_pos = match probe.basin_direction {
BasinDirection::North | BasinDirection::South => (anchor_idx, 0),
BasinDirection::East | BasinDirection::West => (0, anchor_idx),
};
let chunk = derive_chunk_context(42, "fjord_body", &region, chunk_pos);
let (base_x, base_y) = (chunk_pos.0 * 64, chunk_pos.1 * 64);
let mut deep_elevs: Vec<i32> = vec![];
let mut dry_elevs: Vec<i32> = vec![];
// Scan the full 64×64 chunk, not a single row — the deep-water trough axis is
// not guaranteed to intersect any fixed Y, so a single-row probe could miss it
// entirely and silently pass without ever checking the sea-level claim.
// not guaranteed to intersect any fixed row, so a single-row probe could miss
// it entirely and silently pass without ever checking the sea-level claim.
for dy in 0..64i32 {
for dx in 0..64i32 {
let col = derive_voxel_column(42, "fjord_body", &region, &chunk, dx, dy);
let col =
derive_voxel_column(42, "fjord_body", &region, &chunk, base_x + dx, base_y + dy);
match col.water {
Water::Deep => deep_elevs.push(col.elevation_m),
Water::Dry => dry_elevs.push(col.elevation_m),
@@ -428,9 +485,12 @@ fn law_drainage_monotonicity_braided_delta() {
VegetationClass::Scrub,
);
let mut checked = false;
for (cx, cy) in [(0, 0), (1, 1)] {
checked |=
assert_drainage_monotonicity(17, "delta_body", "BraidedDelta", &region, (cx, cy));
// The braid belt sits on the region's channel anchor (T-1041) — sweep the
// anchor band so the law is exercised on real thread tiles.
let mut positions = vec![(0, 0), (1, 1)];
positions.extend(anchor_band_chunks(17, "delta_body", &region, (0, 0)));
for pos in positions {
checked |= assert_drainage_monotonicity(17, "delta_body", "BraidedDelta", &region, pos);
}
assert!(
checked,
@@ -898,6 +958,375 @@ fn law_climate_vegetation_airless_always_absent() {
}
}
// ---------------------------------------------------------------------------
// §2b — Region-anchored feature placement (T-1040 / T-1041, D-239 §10)
// ---------------------------------------------------------------------------
//
// T-1040: channel centrelines were anchored to the world x=0/y=0 axis — every
// chunk of a watered region claimed has_active_channel while channel voxels
// existed only near the world origin. T-1041: fjord/cliff/gorge/delta folded
// world coordinates into the 64 m chunk frame — region-scale landforms
// repeated every chunk. Both are fixed by region-anchored feature axes
// (`channel_anchor_m` / `coast_anchor_m`); these tests pin the placement
// contract far from the origin, at the ticket's example chunk (1000, −750).
#[test]
fn channel_present_in_active_chunks_far_from_origin() {
// T-1040 (a): a has_active_channel chunk at an arbitrary large world
// offset contains in-channel voxels — and the gate is honest in both
// directions (wet ⇒ gated, ungated ⇒ dry). Sweeps the 16 cross-columns of
// the region containing chunk (1000, −750) at that chunk's along index.
let region = make_region(
MorphologyZone::AlluvialPlain,
TectonicClass::Stable,
GlaciationGrade::None,
5,
20,
18,
60,
Some(15.0),
VegetationClass::Forest,
);
let (seed, body) = (42u64, "GJ144d");
let ns = basin_is_ns(seed, body, &region, (1000, -750));
let cross_base = if ns {
(1000 >> 4) << 4
} else {
(-750i32 >> 4) << 4
};
let mut any_gated_wet = false;
let mut any_gate_off = false;
let mut total_wet = 0usize;
for i in 0..16 {
let pos: ChunkPos = if ns {
(cross_base + i, -750)
} else {
(1000, cross_base + i)
};
let chunk = derive_chunk_context(seed, body, &region, pos);
let (bx, by) = (pos.0 * 64, pos.1 * 64);
let mut wet = 0usize;
for dy in 0..64i32 {
for dx in 0..64i32 {
let col = derive_voxel_column(seed, body, &region, &chunk, bx + dx, by + dy);
if col.water != Water::Dry {
wet += 1;
}
}
}
total_wet += wet;
if chunk.has_active_channel {
any_gated_wet |= wet > 0;
} else {
any_gate_off = true;
assert_eq!(
wet, 0,
"T-1040: chunk {pos:?} has no active channel but contains {wet} wet voxels \
— the chunk gate and the voxel placement disagree"
);
}
}
assert!(
total_wet > 0,
"T-1040: the region at chunk (1000, −750) must contain channel voxels \
(pre-fix: zero — channels existed only near the world-origin axis)"
);
assert!(
any_gated_wet,
"T-1040: at least one has_active_channel chunk must contain in-channel voxels"
);
assert!(
any_gate_off,
"T-1040: the channel band must not blanket the region — some chunks must gate off"
);
}
#[test]
fn channel_continuous_across_chunk_boundary_far_from_origin() {
// T-1040 (a): channel position is continuous across adjacent chunk pairs.
// Every tile derives under its PRODUCTION covering chunk; the wet band's
// midpoint may not jump at a 64 m along-boundary (chunk-frame dependence
// would jump by up to a chunk width or drop out entirely).
let region = make_region(
MorphologyZone::AlluvialPlain,
TectonicClass::Stable,
GlaciationGrade::None,
5,
20,
18,
60,
Some(15.0),
VegetationClass::Forest,
);
let (seed, body) = (42u64, "GJ144d");
let probe = derive_chunk_context(seed, body, &region, (1000, -750));
let ns = matches!(
probe.basin_direction,
BasinDirection::North | BasinDirection::South
);
let anchor = probe.channel_anchor_m;
// Wet-band midpoint of one world cross-row, sampling the full swept band.
let row_mid = |along: i32| -> Option<i32> {
let wet: Vec<i32> = (anchor - 220..anchor + 220)
.filter(|&c| {
derive_at_cross_along(seed, body, &region, ns, c, along).water != Water::Dry
})
.collect();
wet.first().map(|f| (f + wet.last().unwrap()) / 2)
};
// An interior along-boundary of the region containing chunk (1000, −750):
// between along-chunks −744 and −743 (N/S) or 1004 and 1005 (E/W).
let boundary = if ns { -743 * 64 } else { 1005 * 64 };
let mut prev: Option<i32> = None;
for along in boundary - 4..boundary + 4 {
let mid = row_mid(along).unwrap_or_else(|| {
panic!(
"T-1040: row along={along} contains no wet tiles — the channel \
dropped out at the chunk boundary {boundary}"
)
});
if let Some(p) = prev {
assert!(
(mid - p).abs() <= 12,
"T-1040: wet-band midpoint jumped {} m between adjacent rows \
{} and {} (boundary {boundary}) — channel is not continuous",
(mid - p).abs(),
along - 1,
along
);
}
prev = Some(mid);
}
}
#[test]
fn fjord_region_has_one_valley_spanning_chunks() {
// T-1041 (b): a FjordWall region contains ONE deep-water trough spanning
// its chunks — pre-fix a complete fjord cross-section repeated in every
// 64 m chunk. Representative sweep: two far regions × three along rows.
let region = make_region(
MorphologyZone::Fjord,
TectonicClass::Active,
GlaciationGrade::Moderate,
55,
60,
28,
55,
Some(-8.0),
VegetationClass::Barren,
);
let (seed, body) = (42u64, "fjord_body");
for region_chunk in [(640, -480), (-336, 992)] {
let probe = derive_chunk_context(seed, body, &region, region_chunk);
let ns = matches!(
probe.basin_direction,
BasinDirection::North | BasinDirection::South
);
let (cross_chunk, along_chunk) = if ns {
(region_chunk.0, region_chunk.1)
} else {
(region_chunk.1, region_chunk.0)
};
let cross_base = (cross_chunk >> 4) * 1024;
let along_base = (along_chunk >> 4) * 1024;
let positions: Vec<i32> = (cross_base..cross_base + 1024).collect();
for along in [along_base + 32, along_base + 512, along_base + 992] {
let clusters = count_feature_clusters(
&positions,
|c| derive_at_cross_along(seed, body, &region, ns, c, along).water == Water::Deep,
16,
);
assert_eq!(
clusters, 1,
"T-1041: FjordWall region {region_chunk:?} must contain exactly ONE \
deep-water trough across its 1024 m cross extent at along={along} \
(got {clusters}; pre-fix: one per 64 m chunk)"
);
}
}
}
#[test]
fn gorge_region_has_one_valley_spanning_chunks() {
// T-1041 (b): an IncisedGorge region contains ONE shallow-floor gorge
// spanning its chunks — not one per chunk.
let region = make_region(
MorphologyZone::MountainPass,
TectonicClass::Active,
GlaciationGrade::None,
50,
65,
5,
40,
Some(5.0),
VegetationClass::Scrub,
);
let (seed, body) = (42u64, "gorge_body");
for region_chunk in [(640, -480), (-336, 992)] {
let probe = derive_chunk_context(seed, body, &region, region_chunk);
let ns = matches!(
probe.basin_direction,
BasinDirection::North | BasinDirection::South
);
let (cross_chunk, along_chunk) = if ns {
(region_chunk.0, region_chunk.1)
} else {
(region_chunk.1, region_chunk.0)
};
let cross_base = (cross_chunk >> 4) * 1024;
let along_base = (along_chunk >> 4) * 1024;
let positions: Vec<i32> = (cross_base..cross_base + 1024).collect();
for along in [along_base + 32, along_base + 512, along_base + 992] {
let clusters = count_feature_clusters(
&positions,
|c| {
derive_at_cross_along(seed, body, &region, ns, c, along).water == Water::Shallow
},
16,
);
assert_eq!(
clusters, 1,
"T-1041: IncisedGorge region {region_chunk:?} must contain exactly ONE \
shallow gorge floor across its 1024 m cross extent at along={along} \
(got {clusters}; pre-fix: one per 64 m chunk)"
);
}
}
}
#[test]
fn cliff_coast_one_continuous_coastline_per_region() {
// T-1041 (b): a CliffCoast region has ONE continuous (warp-displaced)
// coast line on the region-anchored face — pre-fix the cliff face sat at
// intra-chunk offset 48–55 in every chunk, sawtoothing the coast at 64 m
// pitch. Transect runs along the seaward (basin) axis across the region.
let region = make_region(
MorphologyZone::CliffCoast,
TectonicClass::Active,
GlaciationGrade::None,
60,
40,
20,
30,
Some(10.0),
VegetationClass::Scrub,
);
let (seed, body) = (42u64, "cliff_body");
for region_chunk in [(656, -464), (-256, 768)] {
let probe = derive_chunk_context(seed, body, &region, region_chunk);
let coast = probe.coast_anchor_m;
let ns = matches!(
probe.basin_direction,
BasinDirection::North | BasinDirection::South
);
let (cross_chunk, along_chunk) = if ns {
(region_chunk.0, region_chunk.1)
} else {
(region_chunk.1, region_chunk.0)
};
let along_base = (along_chunk >> 4) * 1024;
let cross_fixed = cross_chunk * 64 + 32;
let positions: Vec<i32> = (along_base..along_base + 1024).collect();
// Exactly one ocean cluster (the seaward side of the one coast line).
let clusters = count_feature_clusters(
&positions,
|a| derive_at_cross_along(seed, body, &region, ns, cross_fixed, a).water == Water::Deep,
16,
);
assert_eq!(
clusters, 1,
"T-1041: CliffCoast region {region_chunk:?} must have exactly ONE ocean \
side (got {clusters} Deep clusters; pre-fix: one 64 m sawtooth per chunk)"
);
// Monotone coast: well inland of the face line → Dry; well seaward →
// Deep. Margins absorb the ±8 m domain warp (face 0..8, ledge 8..13).
for &a in &positions {
// Signed seaward distance — mirrors generate_cliff_coast.
let d = match probe.basin_direction {
BasinDirection::North | BasinDirection::West => coast - a,
BasinDirection::South | BasinDirection::East => a - coast,
};
let col = derive_at_cross_along(seed, body, &region, ns, cross_fixed, a);
if d <= -9 {
assert_eq!(
col.water,
Water::Dry,
"T-1041: tile {} m inland of the coast line must be Dry (along={a})",
-d
);
} else if d >= 21 {
assert_eq!(
col.water,
Water::Deep,
"T-1041: tile {d} m seaward of the coast line must be Deep (along={a})"
);
}
}
}
}
#[test]
fn braided_threads_confined_to_region_belt() {
// T-1041: braided threads anastomose across the region-anchored fan belt
// (anchor ± 32 m + thread width + warp) — pre-fix the same three threads
// restarted in every 64 m chunk, spreading thread water across the whole
// region's cross extent.
let region = make_region(
MorphologyZone::Delta,
TectonicClass::Active,
GlaciationGrade::None,
3,
8,
25,
50,
Some(18.0),
VegetationClass::Scrub,
);
let (seed, body) = (17u64, "delta_body");
for region_chunk in [(800, -592)] {
let probe = derive_chunk_context(seed, body, &region, region_chunk);
let ns = matches!(
probe.basin_direction,
BasinDirection::North | BasinDirection::South
);
let anchor = probe.channel_anchor_m;
let (cross_chunk, along_chunk) = if ns {
(region_chunk.0, region_chunk.1)
} else {
(region_chunk.1, region_chunk.0)
};
let cross_base = (cross_chunk >> 4) * 1024;
let along = along_chunk * 64 + 32;
let wet: Vec<i32> = (cross_base..cross_base + 1024)
.filter(|&c| {
derive_at_cross_along(seed, body, &region, ns, c, along).water == Water::Shallow
})
.collect();
assert!(
!wet.is_empty(),
"T-1041: BraidedDelta region {region_chunk:?} must contain thread water"
);
// Belt confinement: thread centres ∈ anchor ± 32, half-width ≤ 4,
// warp ≤ 8 → all thread water within anchor ± 44.
for &c in &wet {
assert!(
(c - anchor).abs() <= 44,
"T-1041: thread water at cross={c} is {} m from the fan axis {anchor} \
— outside the region belt (pre-fix: threads repeated every chunk)",
(c - anchor).abs()
);
}
// Braided, not single-thread: 1–3 thread clusters within the belt
// (three threads, possibly merged where centres overlap; warp-scale
// gap tolerance — the ±8 m warp punches small holes in a thread).
let cluster_count = count_feature_clusters(&wet, |_| true, 8);
assert!(
(1..=3).contains(&cluster_count),
"T-1041: expected 1–3 braided thread clusters in the belt, got {cluster_count}"
);
}
}
// ---------------------------------------------------------------------------
// §3 — Per-family <5 ms/chunk budget (D-239 §10)
// ---------------------------------------------------------------------------
@@ -1446,6 +1875,92 @@ fn validation_gruenfeld_skipped_not_in_wiki() {
// Shared helpers
// ---------------------------------------------------------------------------
/// Whether the region's basin runs north/south (cross axis = x). Region-scale
/// property — identical for every chunk of the region containing `region_chunk`.
fn basin_is_ns(seed: u64, body_id: &str, region: &RegionProfile, region_chunk: ChunkPos) -> bool {
let probe = derive_chunk_context(seed, body_id, region, region_chunk);
matches!(
probe.basin_direction,
BasinDirection::North | BasinDirection::South
)
}
/// The chunk positions of the channel-anchor band (anchor column ± 1) across
/// the full along-extent of the region containing `region_chunk` (T-1040).
///
/// The channel/landform centreline is region-anchored: it lives in the anchor
/// chunk column, swinging up to one meander amplitude sideways. The meander
/// wavelength (≤ ~650 m) fits inside the region's 1024 m along-extent, so the
/// centreline crosses the anchor column at least once — sweeping this band
/// guarantees wet tiles are exercised somewhere in it.
fn anchor_band_chunks(
seed: u64,
body_id: &str,
region: &RegionProfile,
region_chunk: ChunkPos,
) -> Vec<ChunkPos> {
let probe = derive_chunk_context(seed, body_id, region, region_chunk);
let anchor_idx = probe.channel_anchor_m.div_euclid(64);
let ns = matches!(
probe.basin_direction,
BasinDirection::North | BasinDirection::South
);
// Region base index on the along axis (16-chunk regions; arithmetic shift
// = floor division, correct for negative chunks).
let along_base = if ns {
(region_chunk.1 >> 4) << 4
} else {
(region_chunk.0 >> 4) << 4
};
let mut out = Vec::new();
for cross in anchor_idx - 1..=anchor_idx + 1 {
for j in along_base..along_base + 16 {
out.push(if ns { (cross, j) } else { (j, cross) });
}
}
out
}
/// Derive one voxel at world cross/along coordinates under the PRODUCTION
/// covering chunk's context (per-tile chunk lookup — exactly what a consumer
/// streaming the world does). Basis-aware: cross ⊥ basin, along ∥ basin.
fn derive_at_cross_along(
seed: u64,
body_id: &str,
region: &RegionProfile,
ns: bool,
cross: i32,
along: i32,
) -> settled_reach_server::atlas::voxel::VoxelColumn {
let (tx, ty) = if ns { (cross, along) } else { (along, cross) };
let chunk_pos = (tx.div_euclid(64), ty.div_euclid(64));
let chunk = derive_chunk_context(seed, body_id, region, chunk_pos);
derive_voxel_column(seed, body_id, region, &chunk, tx, ty)
}
/// Count clusters of positions where `pred` holds across a cross/along
/// transect, merging runs separated by gaps ≤ `gap_tolerance` (domain-warp
/// jitter can fragment a single feature by a few metres; distinct per-chunk
/// repeats are ≥ ~50 m apart and never merge).
fn count_feature_clusters(
positions: &[i32],
hits: impl Fn(i32) -> bool,
gap_tolerance: i32,
) -> usize {
let mut clusters = 0usize;
let mut last_hit: Option<i32> = None;
for &p in positions {
if hits(p) {
match last_hit {
Some(prev) if p - prev <= gap_tolerance => {}
_ => clusters += 1,
}
last_hit = Some(p);
}
}
clusters
}
/// Construct a `RegionProfile` directly from parameters, deriving the
/// dependent fields (precipitation_class, river_threshold) consistently.
fn make_region(
+8 -8
View File
@@ -3,12 +3,12 @@
"label": "alluvial_forest_active_channel",
"seed": 16045690984503098046,
"body_id": "GJ144d",
"tile_x": 640,
"tile_y": 1280,
"tile_x": 223,
"tile_y": 800,
"terrain": 0,
"vegetation": 1,
"water": 0,
"elevation_m": 11,
"vegetation": 4,
"water": 1,
"elevation_m": 8,
"cover": 0
},
{
@@ -27,12 +27,12 @@
"label": "fjord_wall_glaciated",
"seed": 18369614217980264670,
"body_id": "GJ447c",
"tile_x": 928,
"tile_y": 544,
"tile_x": 228,
"tile_y": 352,
"terrain": 3,
"vegetation": 0,
"water": 2,
"elevation_m": 1,
"elevation_m": 0,
"cover": 1
}
]