From 6945cbfd9b357309e60514f8b5ec73e3e9606e25 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Tue, 1 Sep 2026 23:21:44 -0400 Subject: [PATCH] Spring pool tests and single source marker - Only the centerline cell of the first station carries the source feature - Headwater and styled geometry tests expect the spring pool - Transect table starts with the first 24 stations --- .../surface/SurfaceFootprintCompiler.java | 3 ++- .../hydrology/HydrologyFootprintCompilerTest.java | 15 ++++++++++----- .../hydrology/HydrologyStyledGeometryTest.java | 6 ++++-- .../art/arcane/iris/probe/RiverTransectProbe.java | 11 ++++++++--- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/art/arcane/iris/engine/hydrology/surface/SurfaceFootprintCompiler.java b/core/src/main/java/art/arcane/iris/engine/hydrology/surface/SurfaceFootprintCompiler.java index d0ecd1b38..78c160134 100644 --- a/core/src/main/java/art/arcane/iris/engine/hydrology/surface/SurfaceFootprintCompiler.java +++ b/core/src/main/java/art/arcane/iris/engine/hydrology/surface/SurfaceFootprintCompiler.java @@ -82,7 +82,8 @@ public final class SurfaceFootprintCompiler { HydraulicSegment segment = exposed.get(stations.segmentIndex()[column.station()]); int flowX = (int) StrictMath.round(centerline.tangentX()[column.station()]); int flowZ = (int) StrictMath.round(centerline.tangentZ()[column.station()]); - boolean source = column.station() == 0 && column.role() == SurfaceRole.CHANNEL && !column.apron(); + boolean source = column.station() == 0 && column.role() == SurfaceRole.CHANNEL && !column.apron() + && column.x() == centerline.x()[0] && column.z() == centerline.z()[0]; int y = column.role() == SurfaceRole.CHANNEL ? column.headY() : column.height(); HydrologyFeatureRef feature = features.feature(segment, column.role(), source, column.x(), y, column.z(), flowX, flowZ); columns.add(new SurfaceLayerColumn( diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyFootprintCompilerTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyFootprintCompilerTest.java index c7ea590ea..b94ff5153 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyFootprintCompilerTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyFootprintCompilerTest.java @@ -1367,7 +1367,7 @@ public class HydrologyFootprintCompilerTest { } @Test - public void surfaceHeadwaterTapersUpToChannelWidthWithoutARadialBasin() { + public void surfaceHeadwaterOpensAsASpringPoolThatNarrowsToTheChannel() { HydraulicSegment segment = new HydraulicSegment( 521L, 520L, @@ -1385,16 +1385,21 @@ public class HydrologyFootprintCompilerTest { HydrologyFootprintCompiler compiler = compiler(terrain); RiverFootprint footprint = compiler.compile(List.of(course)); - assertTrue(surfaceFluidLayer(footprint, -4, 0) == null); - assertTrue(wetHalfWidth(footprint, 0) < wetHalfWidth(footprint, 32)); - assertTrue(wetHalfWidth(footprint, 32) <= wetHalfWidth(footprint, 56)); + assertTrue(wetHalfWidth(footprint, 0) > wetHalfWidth(footprint, 32)); + assertTrue(wetHalfWidth(footprint, 0) >= 2 * wetHalfWidth(footprint, 56)); + assertTrue(Math.abs(wetHalfWidth(footprint, 32) - wetHalfWidth(footprint, 56)) <= 1); + for (int x = 1; x <= 24; x++) { + assertTrue(wetHalfWidth(footprint, x) <= wetHalfWidth(footprint, x - 1) + 1); + } HydrologyColumnLayer source = surfaceFluidLayer(footprint, 0, 0); HydrologyColumnLayer cruise = surfaceFluidLayer(footprint, 56, 0); assertTrue(source != null); assertTrue(cruise != null); + assertTrue(source.feature().source()); + assertFalse(surfaceFluidLayer(footprint, 0, 3).feature().source()); int sourceDepth = source.fluidHeadY() - source.bedY(); int cruiseDepth = cruise.fluidHeadY() - cruise.bedY(); - assertTrue(Math.abs(sourceDepth - cruiseDepth) <= 1); + assertTrue(sourceDepth >= cruiseDepth + 1); } @Test diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyStyledGeometryTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyStyledGeometryTest.java index 73adc6e59..ff7155356 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyStyledGeometryTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyStyledGeometryTest.java @@ -74,9 +74,11 @@ public class HydrologyStyledGeometryTest { RiverCourse surface = courses(first, RiverCourseType.SURFACE).getFirst(); DrainageNode surfaceSource = first.node(surface.sourceNodeId().orElseThrow()).orElseThrow(); HydraulicSegment surfaceStart = surface.segments().getFirst(); - int maximumSurfaceWidth = surfaceSettings.surface().maximumWidth() + // The first segment carries the spring pool: wider by the spring ratio and one block deeper. + int maximumSurfaceWidth = (int) Math.ceil(surfaceSettings.surface().maximumWidth() + * surfaceSettings.surface().banks().springWidthRatio()) + (surfaceStart.receivingPool() ? 2 : 0); - int maximumSurfaceDepth = surfaceSettings.surface().maximumDepth() + int maximumSurfaceDepth = surfaceSettings.surface().maximumDepth() + 1 + (surfaceStart.receivingPool() ? 1 : 0); assertTrue(surfaceStart.width() >= surfaceSettings.surface().minimumWidth()); assertTrue(surfaceStart.width() <= maximumSurfaceWidth); diff --git a/probe/src/main/java/art/arcane/iris/probe/RiverTransectProbe.java b/probe/src/main/java/art/arcane/iris/probe/RiverTransectProbe.java index 9d1486152..9313c1e0a 100644 --- a/probe/src/main/java/art/arcane/iris/probe/RiverTransectProbe.java +++ b/probe/src/main/java/art/arcane/iris/probe/RiverTransectProbe.java @@ -47,6 +47,7 @@ public final class RiverTransectProbe { private static final int MAXIMUM_PLAN_COLUMNS = 6_000_000; private static final int MAXIMUM_DETAILS = 16; private static final int TABLE_HALF_SECTION = 16; + private static final int[] HEADWATER_SECTIONS = {0, 6, 12, 18, 24}; private RiverTransectProbe() { } @@ -495,10 +496,14 @@ public final class RiverTransectProbe { IrisComplex complex, IrisHydrologyRuntime runtime ) throws IOException { - int[] stations = sectionStations(centerline.size()); + int[] spaced = sectionStations(centerline.size()); + int[] stations = new int[HEADWATER_SECTIONS.length + spaced.length]; + for (int index = 0; index < HEADWATER_SECTIONS.length; index++) { + stations[index] = Math.min(centerline.size() - 1, HEADWATER_SECTIONS[index]); + } + System.arraycopy(spaced, 0, stations, HEADWATER_SECTIONS.length, spaced.length); StringBuilder text = new StringBuilder(); - for (int section = 0; section < SECTIONS; section++) { - int station = stations[section]; + for (int station : stations) { text.append("station ").append(station) .append(" at ").append(centerline.x()[station]).append(',').append(centerline.z()[station]) .append(" head=").append(column(complex, runtime, centerline.x()[station], centerline.z()[station]).water())