diff --git a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyCandidateRejection.java b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyCandidateRejection.java index e7dea19f8..db9c8b94e 100644 --- a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyCandidateRejection.java +++ b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyCandidateRejection.java @@ -8,13 +8,11 @@ public enum HydrologyCandidateRejection { SOURCE_QUOTA, SOURCE_SPACING, OUTLET_LIMIT, - RIDGE_CONTAINMENT, OUTLET_LEVEL, CAVE_CONTAINMENT, VOLUME_LIMIT, SURFACE_HEAD_RANGE, SURFACE_HEAD_DISTRIBUTION, - SURFACE_RIDGE_TUNNEL_LIMIT, SURFACE_EXPOSURE, SURFACE_DROP_UNSUPPORTED, SURFACE_CORRIDOR_UNSUPPORTED, diff --git a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyFeatureType.java b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyFeatureType.java index 65a8ee8b7..44828cf5f 100644 --- a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyFeatureType.java +++ b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyFeatureType.java @@ -5,6 +5,8 @@ public enum HydrologyFeatureType { RIFFLE, CASCADE, WATERFALL, + // No planner produces a ridge bore any more. The constant stays because its ordinal seeds the + // segment, outlet and feature ids of every constant after it on the cave and deep-fluid paths. RIDGE_BORE, UNDERGROUND_POOL, UNDERGROUND_DROP, diff --git a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyFootprintCompiler.java b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyFootprintCompiler.java index 5490c38ff..d4534035b 100644 --- a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyFootprintCompiler.java +++ b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyFootprintCompiler.java @@ -12,19 +12,15 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; final class HydrologyFootprintCompiler { private static final int FEATURE_ROLE_COUNT = 7; private static final int COURSE_FOOTPRINT_CACHE_SIZE = 8; private static final int VALIDATION_RASTER_CACHE_SIZE = 8; - private static final int MINIMUM_ENDPOINT_PROFILE_LENGTH = 48; - private static final double MINIMUM_ENDPOINT_RAMP_WIDTHS = 2D; private static final int[][] HORIZONTAL_NEIGHBORS = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; @@ -53,7 +49,7 @@ final class HydrologyFootprintCompiler { HydrologyPlannerSettings settings, HydrologyTerrainSampler sampler ) { - this(settings, sampler, HydrologyGeometrySampler.deterministic(0L, sampler)); + this(settings, sampler, HydrologyGeometrySampler.deterministic(sampler)); } HydrologyFootprintCompiler( @@ -172,404 +168,6 @@ final class HydrologyFootprintCompiler { return fullMaterializationCount; } - boolean surfaceHeadwaterRampSupported(RiverCourse course) { - Objects.requireNonNull(course, "course"); - if (course.type() != RiverCourseType.SURFACE) { - return true; - } - if (settings.routing().branching().minimumSurfaceCourseLength() - < MINIMUM_ENDPOINT_PROFILE_LENGTH) { - return true; - } - RiverFootprint footprint = compileCourse(course); - List raster = surfaceCourseCenterline(course); - if (raster.size() < 2) { - return false; - } - HashSet ownedCells = new HashSet<>(); - for (HydrologyColumnSample sample : footprint.columns().values()) { - HydrologyColumnLayer fluid = sample.primarySurfaceFluidLayer().orElse(null); - if (fluid != null - && fluid.feature().courseId() == course.id() - && fluid.feature().type().isSurface() - && fluid.channel() - && fluid.connectedFluid() - && fluid.fluidOwned() - && !fluid.oceanApron()) { - ownedCells.add(RiverFootprint.pack(sample.x(), sample.z())); - } - } - int maximumWidth = 1; - for (HydraulicSegment segment : course.segments()) { - if (segment.type().isSurface()) { - maximumWidth = Math.max(maximumWidth, segment.width()); - } - } - HydrologyPoint source = raster.getFirst(); - if (!ownedNear(ownedCells, source.x(), source.z(), maximumWidth)) { - return false; - } - int cruiseWidth = cruiseWidth(ownedCells, raster, maximumWidth); - double transitionLength = endpointTransitionLength( - ownedCells, - raster, - maximumWidth, - cruiseWidth - ); - return transitionLength >= cruiseWidth * MINIMUM_ENDPOINT_RAMP_WIDTHS; - } - - boolean surfaceIncisionContained(RiverCourse course) { - Objects.requireNonNull(course, "course"); - if (course.type() != RiverCourseType.SURFACE) { - return true; - } - RiverFootprint footprint = compileCourse(course); - for (HydrologyColumnSample sample : footprint.columns().values()) { - HydrologyColumnLayer fluid = sample.primarySurfaceFluidLayer().orElse(null); - if (fluid == null - || fluid.feature().courseId() != course.id() - || fluid.feature().type() == HydrologyFeatureType.MOUTH - || fluid.oceanApron() - || fluid.fallingFluid() - || !fluid.channel() - || !fluid.fluidOwned()) { - continue; - } - HydrologyTerrainSample basis = sampleTerrainBasis(sample.x(), sample.z()); - HydrologyTerrainSample terrain = sampleTerrain(sample.x(), sample.z()); - if (terrain == null - || basis == null - || (terrain.naturalHeight() - fluid.fluidHeadY() > permittedSurfaceIncision(terrain) - && basis.naturalHeight() - fluid.fluidHeadY() <= permittedSurfaceIncision(basis))) { - return false; - } - } - return true; - } - - boolean surfaceIncisionContained(RiverCourse course, RiverFootprint footprint) { - Objects.requireNonNull(course, "course"); - Objects.requireNonNull(footprint, "footprint"); - if (course.type() != RiverCourseType.SURFACE) { - return true; - } - for (HydrologyColumnSample sample : footprint.columns().values()) { - for (HydrologyColumnLayer fluid : sample.layers()) { - if (fluid.feature().courseId() != course.id() - || fluid.feature().type() == HydrologyFeatureType.MOUTH - || fluid.oceanApron() - || fluid.fallingFluid() - || !fluid.channel() - || !fluid.fluidOwned()) { - continue; - } - HydrologyTerrainSample basis = sampleTerrainBasis(sample.x(), sample.z()); - HydrologyTerrainSample terrain = sampleTerrain(sample.x(), sample.z()); - if (basis == null - || terrain == null - || (terrain.naturalHeight() - fluid.fluidHeadY() - > settings.surface().maximumIncision() - && basis.naturalHeight() - fluid.fluidHeadY() - <= permittedSurfaceIncision(basis))) { - return false; - } - } - } - return true; - } - - boolean surfaceBanksContained(RiverCourse course) { - Objects.requireNonNull(course, "course"); - if (course.type() != RiverCourseType.SURFACE) { - return true; - } - RiverFootprint footprint = compileCourse(course); - if (!surfaceCenterlinePublished(course, footprint) - || !surfaceTransitionCenterlinePublished(course, footprint)) { - return false; - } - for (HydrologyColumnSample sample : footprint.columns().values()) { - HydrologyColumnLayer fluid = sample.primarySurfaceFluidLayer().orElse(null); - if (fluid == null || fluid.oceanApron() || fluid.fallingFluid()) { - continue; - } - for (int[] offset : HORIZONTAL_NEIGHBORS) { - HydrologyColumnSample neighbor = footprint.sample( - sample.x() + offset[0], - sample.z() + offset[1] - ).orElse(null); - if (ownsSurfaceFluid(neighbor, course.id())) { - continue; - } - if (neighbor != null - && fluid.fluidHeadY() <= neighbor.seaLevel() - && (neighbor.ocean() || neighbor.naturalHeight() <= neighbor.seaLevel())) { - continue; - } - if (neighbor == null || neighbor.terrainHeight() <= fluid.fluidHeadY()) { - return false; - } - } - } - return true; - } - - private boolean surfaceCenterlinePublished(RiverCourse course, RiverFootprint footprint) { - for (HydraulicSegment segment : course.segments()) { - if (!segment.type().isSurface()) { - continue; - } - for (HydrologyPoint point : segment.centerline()) { - HydrologyColumnSample sample = footprint.sample(point.x(), point.z()).orElse(null); - if (ownsSurfaceFluid(sample, course.id()) - || segment.type() == HydrologyFeatureType.MOUTH - && ownsOceanApron(sample, course.id())) { - continue; - } - return false; - } - } - return true; - } - - private boolean surfaceTransitionCenterlinePublished(RiverCourse course, RiverFootprint footprint) { - for (int segmentIndex = 0; segmentIndex < course.segments().size(); segmentIndex++) { - HydraulicSegment segment = course.segments().get(segmentIndex); - if (!terrainRoofedSurfaceTransition(course, segment)) { - continue; - } - List centerline = continuousCenterline(segment); - for (int pointIndex = 0; pointIndex < centerline.size(); pointIndex++) { - HydrologyPoint point = centerline.get(pointIndex); - HydrologyColumnSample sample = footprint.sample(point.x(), point.z()).orElse(null); - if (ownsTransitionFluid(sample, course.id(), segment.id(), point.y())) { - continue; - } - boolean joinedStart = pointIndex == 0 - && segmentIndex > 0 - && segmentsJoin(course.segments().get(segmentIndex - 1), segment); - boolean joinedEnd = pointIndex == centerline.size() - 1 - && segmentIndex + 1 < course.segments().size() - && segmentsJoin(segment, course.segments().get(segmentIndex + 1)); - if ((joinedStart || joinedEnd) && ownsCourseFluid(sample, course.id(), point.y())) { - continue; - } - return false; - } - } - return true; - } - - private boolean ownsTransitionFluid( - HydrologyColumnSample sample, - long courseId, - long segmentId, - int fluidHead - ) { - if (sample == null) { - return false; - } - for (HydrologyColumnLayer layer : sample.layers()) { - if (layer.feature().courseId() == courseId - && layer.feature().segmentId() == segmentId - && layer.fluidHeadY() == fluidHead - && layer.channel() - && layer.connectedFluid() - && layer.fluidOwned()) { - return true; - } - } - return false; - } - - private boolean ownsCourseFluid(HydrologyColumnSample sample, long courseId, int fluidHead) { - if (sample == null) { - return false; - } - for (HydrologyColumnLayer layer : sample.layers()) { - if (layer.feature().courseId() == courseId - && layer.fluidHeadY() == fluidHead - && layer.channel() - && layer.connectedFluid() - && layer.fluidOwned()) { - return true; - } - } - return false; - } - - private int permittedSurfaceIncision(HydrologyTerrainSample terrain) { - int maximumIncision = settings.surface().maximumIncision(); - return Math.min( - maximumIncision, - (int) StrictMath.floor(maximumIncision * terrain.incisionMultiplier()) - ); - } - - private boolean ownsSurfaceFluid(HydrologyColumnSample sample, long courseId) { - if (sample == null) { - return false; - } - HydrologyColumnLayer fluid = sample.primarySurfaceFluidLayer().orElse(null); - return fluid != null - && fluid.fluidOwned() - && fluid.feature().courseId() == courseId; - } - - private boolean ownsOceanApron(HydrologyColumnSample sample, long courseId) { - if (sample == null) { - return false; - } - for (HydrologyColumnLayer layer : sample.layers()) { - if (layer.oceanApron() && layer.feature().courseId() == courseId) { - return true; - } - } - return false; - } - - private List surfaceCourseCenterline(RiverCourse course) { - ArrayList points = new ArrayList<>(); - for (HydraulicSegment segment : course.segments()) { - if (!segment.type().isSurface() && segment.type() != HydrologyFeatureType.RIDGE_BORE) { - break; - } - for (HydrologyPoint point : segment.centerline()) { - if (points.isEmpty() - || point.x() != points.getLast().x() - || point.z() != points.getLast().z()) { - points.add(point); - } - } - } - return rasterCenterline(points); - } - - private List rasterCenterline(List points) { - ArrayList raster = new ArrayList<>(); - for (int pairIndex = 0; pairIndex < points.size() - 1; pairIndex++) { - HydrologyPoint start = points.get(pairIndex); - HydrologyPoint end = points.get(pairIndex + 1); - int steps = Math.max(Math.abs(end.x() - start.x()), Math.abs(end.z() - start.z())); - if (steps == 0) { - continue; - } - int firstStep = raster.isEmpty() ? 0 : 1; - for (int step = firstStep; step <= steps; step++) { - double progress = step / (double) steps; - raster.add(new HydrologyPoint( - (int) StrictMath.round(start.x() + (end.x() - start.x()) * progress), - (int) StrictMath.round(start.y() + (end.y() - start.y()) * progress), - (int) StrictMath.round(start.z() + (end.z() - start.z()) * progress) - )); - } - } - return List.copyOf(raster); - } - - private boolean ownedNear(Set cells, int x, int z, int radius) { - for (int deltaZ = -radius; deltaZ <= radius; deltaZ++) { - for (int deltaX = -radius; deltaX <= radius; deltaX++) { - if (cells.contains(RiverFootprint.pack(x + deltaX, z + deltaZ))) { - return true; - } - } - } - return false; - } - - private int cruiseWidth(Set cells, List raster, int maximumWidth) { - int start = Math.min(raster.size() - 2, Math.max(48, maximumWidth * 4)); - int end = Math.min(raster.size() - 1, Math.max(start + 16, maximumWidth * 6)); - ArrayList widths = new ArrayList<>(); - for (int offset = start; offset <= end; offset++) { - if (offset <= 0 || offset >= raster.size() - 1) { - continue; - } - int width = crossSectionWidth( - cells, - raster.get(offset), - raster.get(offset - 1), - raster.get(offset + 1), - maximumWidth - ); - if (width > 0) { - widths.add(width); - } - } - if (widths.isEmpty()) { - return maximumWidth; - } - widths.sort(Integer::compare); - return Math.max(1, widths.get(widths.size() / 2)); - } - - private double endpointTransitionLength( - Set cells, - List raster, - int maximumWidth, - int cruiseWidth - ) { - int endpointWidth = crossSectionWidth( - cells, - raster.getFirst(), - raster.getFirst(), - raster.get(1), - maximumWidth - ); - int widthTolerance = Math.max(1, (int) StrictMath.ceil(cruiseWidth * 0.1D)); - if (Math.abs(endpointWidth - cruiseWidth) <= widthTolerance) { - return 0D; - } - int search = Math.max(32, cruiseWidth * 6); - double distance = 0D; - int pointIndex = 1; - while (pointIndex < raster.size() && distance <= search) { - int previousIndex = pointIndex - 1; - int nextIndex = Math.min(raster.size() - 1, pointIndex + 1); - HydrologyPoint previous = raster.get(previousIndex); - HydrologyPoint station = raster.get(pointIndex); - HydrologyPoint next = raster.get(nextIndex); - distance += StrictMath.hypot(station.x() - previous.x(), station.z() - previous.z()); - int width = crossSectionWidth(cells, station, previous, next, maximumWidth); - if (Math.abs(width - cruiseWidth) <= widthTolerance) { - return distance; - } - pointIndex++; - } - return distance; - } - - private int crossSectionWidth( - Set cells, - HydrologyPoint center, - HydrologyPoint before, - HydrologyPoint after, - int maximumWidth - ) { - double tangentX = after.x() - before.x(); - double tangentZ = after.z() - before.z(); - double tangentLength = StrictMath.hypot(tangentX, tangentZ); - if (tangentLength == 0D) { - tangentX = 1D; - tangentLength = 1D; - } - double perpendicularX = -tangentZ / tangentLength; - double perpendicularZ = tangentX / tangentLength; - int limit = Math.max(4, maximumWidth * 2); - HashSet sampled = new HashSet<>(); - for (int offset = -limit; offset <= limit; offset++) { - int x = (int) StrictMath.round(center.x() + perpendicularX * offset); - int z = (int) StrictMath.round(center.z() + perpendicularZ * offset); - long packed = RiverFootprint.pack(x, z); - if (cells.contains(packed)) { - sampled.add(packed); - } - } - return sampled.size(); - } - private ValidationCourseRaster compileValidationCourse(RiverCourse course) { CourseRasterKey rasterKey = new CourseRasterKey( course.id(), @@ -825,7 +423,6 @@ final class HydrologyFootprintCompiler { return false; } return segment.type().isSurface() - || segment.type() == HydrologyFeatureType.RIDGE_BORE || segment.type() == HydrologyFeatureType.UNDERGROUND_POOL || segment.type() == HydrologyFeatureType.UNDERGROUND_DROP || segment.type() == HydrologyFeatureType.SINKHOLE @@ -1145,21 +742,15 @@ final class HydrologyFootprintCompiler { point.x(), point.z(), segment.id(), - settings.surface().minimumTerrainBlendWidth(), - settings.surface().maximumTerrainBlendWidth() + settings.surface().banks().minimumBlendWidth(), + settings.surface().banks().maximumBlendWidth() ); if (type == HydrologyFeatureType.WATERFALL) { shoreWidth = Math.min(2.5D, shoreWidth); gradingWidth = Math.min(gradingWidth, Math.max(8D, channelRadius * 4D)); } } - if (type == HydrologyFeatureType.RIDGE_BORE - || course.type() == RiverCourseType.SURFACE - && type == HydrologyFeatureType.UNDERGROUND_DROP) { - ceiling = fluidHead + settings.surface().ridgeTunnelHeadroom(); - archedChannel = !falling; - organicBoundary = true; - } else if (type == HydrologyFeatureType.UNDERGROUND_POOL + if (type == HydrologyFeatureType.UNDERGROUND_POOL || type == HydrologyFeatureType.UNDERGROUND_DROP || type == HydrologyFeatureType.SINKHOLE) { ceiling = fluidHead + geometrySampler.sample( @@ -1213,7 +804,7 @@ final class HydrologyFootprintCompiler { if (course.type() == RiverCourseType.SURFACE && type.isSurface()) { gradingWidth = Math.max( gradingWidth, - Math.min(settings.surface().maximumTerrainBlendWidth(), channelRadius * 2D) + Math.min(settings.surface().banks().maximumBlendWidth(), channelRadius * 2D) ); } } @@ -1807,8 +1398,7 @@ final class HydrologyFootprintCompiler { private boolean terrainRoofedSurfaceTransition(RiverCourse course, HydraulicSegment segment) { return course.type() == RiverCourseType.SURFACE - && (segment.type() == HydrologyFeatureType.RIDGE_BORE - || segment.type() == HydrologyFeatureType.UNDERGROUND_POOL + && (segment.type() == HydrologyFeatureType.UNDERGROUND_POOL || segment.type() == HydrologyFeatureType.UNDERGROUND_DROP); } @@ -1943,8 +1533,7 @@ final class HydrologyFootprintCompiler { } private HydrologyPlannerSettings.ChannelShape channelShape(HydrologyFeatureType type) { - if (type.isGrotto() || type == HydrologyFeatureType.DEEP_POOL - || type == HydrologyFeatureType.RIDGE_BORE) { + if (type.isGrotto() || type == HydrologyFeatureType.DEEP_POOL) { return settings.geometry().grottos(); } if (type.isUnderground() || type == HydrologyFeatureType.DEEP_CHANNEL) { diff --git a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyGeometrySampler.java b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyGeometrySampler.java index 6157d1258..d0c758ca5 100644 --- a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyGeometrySampler.java +++ b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyGeometrySampler.java @@ -37,10 +37,7 @@ public interface HydrologyGeometrySampler { return value; } - static HydrologyGeometrySampler deterministic( - long worldSeed, - HydrologyTerrainSampler terrainSampler - ) { + static HydrologyGeometrySampler deterministic(HydrologyTerrainSampler terrainSampler) { Objects.requireNonNull(terrainSampler, "terrainSampler"); return request -> { if (request.field() == Field.UNDERGROUND_FLUID_LEVEL) { @@ -52,7 +49,6 @@ public interface HydrologyGeometrySampler { } if (request.field() == Field.SURFACE_WIDTH || request.field() == Field.SURFACE_DEPTH - || request.field() == Field.SURFACE_INSET || request.field() == Field.UNDERGROUND_WIDTH || request.field() == Field.UNDERGROUND_DEPTH) { return request.minimum(); @@ -64,11 +60,6 @@ public interface HydrologyGeometrySampler { request.z(), 0x424c454e44L ); - case TARGET_POOL_LENGTH -> HydrologyHash.mix( - worldSeed, - request.stableId(), - 0x504f4f4cL - ); case UNDERGROUND_HEADROOM -> HydrologyHash.mix( request.stableId(), request.x(), @@ -85,9 +76,7 @@ public interface HydrologyGeometrySampler { enum Field { SURFACE_WIDTH, SURFACE_DEPTH, - SURFACE_INSET, SURFACE_BLEND_WIDTH, - TARGET_POOL_LENGTH, UNDERGROUND_FLUID_LEVEL, UNDERGROUND_WIDTH, UNDERGROUND_DEPTH, diff --git a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyPlanner.java b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyPlanner.java index ff3622ba7..7be078701 100644 --- a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyPlanner.java +++ b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyPlanner.java @@ -67,7 +67,6 @@ public final class HydrologyPlanner { private static final int SURFACE_COURSE_TRANSVERSE_CANDIDATES = 2; private static final int OPTIONAL_SOURCE_REJECTIONS_PER_TARGET = 4; private static final int SURFACE_OPTIONAL_SOURCE_REJECTIONS_PER_TARGET = 8; - private static final double SURFACE_MAXIMUM_LOCAL_TURN_DEGREES = 35D; private static final double SURFACE_MAXIMUM_RENDERED_TURN_DEGREES = 50D; private static final double SURFACE_MAXIMUM_ISOLATED_TURN_DEGREES = 35D; private static final double SURFACE_MAXIMUM_ISOLATED_NEIGHBOR_TURN_DEGREES = 10D; @@ -122,7 +121,7 @@ public final class HydrologyPlanner { worldSeed, settings, sampler, - HydrologyGeometrySampler.deterministic(worldSeed, sampler), + HydrologyGeometrySampler.deterministic(sampler), -4096, footprint -> new HydrologyTerrainCaveVoxelView(sampler, settings.seaLevel(), -4096, 4096) ); @@ -138,7 +137,7 @@ public final class HydrologyPlanner { worldSeed, settings, sampler, - HydrologyGeometrySampler.deterministic(worldSeed, sampler), + HydrologyGeometrySampler.deterministic(sampler), 0, footprint -> new HydrologyTerrainCaveVoxelView(sampler, settings.seaLevel(), worldHeight) ); @@ -154,7 +153,7 @@ public final class HydrologyPlanner { worldSeed, settings, sampler, - HydrologyGeometrySampler.deterministic(worldSeed, sampler), + HydrologyGeometrySampler.deterministic(sampler), Integer.MIN_VALUE, footprint -> caveView ); @@ -171,7 +170,7 @@ public final class HydrologyPlanner { worldSeed, settings, sampler, - HydrologyGeometrySampler.deterministic(worldSeed, sampler), + HydrologyGeometrySampler.deterministic(sampler), minimumY, caveViewFactory ); @@ -235,7 +234,7 @@ public final class HydrologyPlanner { this::sampleBasis, geometrySampler, settings.seaLevel(), - settings.routing().branching().minimumSurfaceCourseLength() + settings.routing().minimumSurfaceCourseLength() ); } @@ -428,49 +427,42 @@ public final class HydrologyPlanner { boolean fallbackSelected = false; while (fallbackOutletIndex < fallbackOutlets.size()) { OutletCandidate fallbackOutlet = fallbackOutlets.get(fallbackOutletIndex++); - for (int routeMode = 0; routeMode < 1; routeMode++) { - RoutingPlan fallbackRouting = routeMode == 0 - ? requireOrganicSurface(buildRouting(grid, List.of(fallbackOutlet))) - : buildContainedSurfaceRouting(grid, List.of(fallbackOutlet)); - HashMap fallbackContexts = new HashMap<>(); - fallbackContexts.put(key, new SourceRoutingContext( - grid, - fallbackRouting, - undergroundRouting, - List.of() - )); - SourceSelection fallbackSelection = selectSources( - key, - grid, - fallbackRouting, - true, - false, - diagnostics, - fallbackContexts - ); - settleSelection( - key, - grid, - fallbackRouting, - undergroundRouting, - fallbackSelection, - true, - footprintCompiler, - refinedEdges, - sourceCompilations, - diagnostics - ); - if (!fallbackSelection.hasAcceptedSelection()) { - continue; - } - surfaceRouting = fallbackRouting; - surfaceSelection = fallbackSelection; - fallbackSelected = true; - break; - } - if (fallbackSelected) { - break; + RoutingPlan fallbackRouting = requireOrganicSurface(buildRouting(grid, List.of(fallbackOutlet))); + HashMap fallbackContexts = new HashMap<>(); + fallbackContexts.put(key, new SourceRoutingContext( + grid, + fallbackRouting, + undergroundRouting, + List.of() + )); + SourceSelection fallbackSelection = selectSources( + key, + grid, + fallbackRouting, + true, + false, + diagnostics, + fallbackContexts + ); + settleSelection( + key, + grid, + fallbackRouting, + undergroundRouting, + fallbackSelection, + true, + footprintCompiler, + refinedEdges, + sourceCompilations, + diagnostics + ); + if (!fallbackSelection.hasAcceptedSelection()) { + continue; } + surfaceRouting = fallbackRouting; + surfaceSelection = fallbackSelection; + fallbackSelected = true; + break; } if (!fallbackSelected) { break; @@ -1582,7 +1574,7 @@ public final class HydrologyPlanner { settings.underground().maximumFluidY() ); if (settings.outlets().surfaceSinkholesEnabled() - && node.terrain().naturalHeight() - settings.surface().maximumSurfaceInset() <= poolY) { + && node.terrain().naturalHeight() - settings.surface().banks().inset() <= poolY) { continue; } long outletId = HydrologyHash.mix( @@ -1712,7 +1704,7 @@ public final class HydrologyPlanner { if (surface && settings.outlets().surfaceSinkholesEnabled()) { poolY = Math.min( poolY, - landwardTerrain.naturalHeight() - settings.surface().maximumSurfaceInset() - 1 + landwardTerrain.naturalHeight() - settings.surface().banks().inset() - 1 ); poolY = Math.max( poolY, @@ -1852,7 +1844,7 @@ public final class HydrologyPlanner { && downstream.terrain().surfaceSourceAllowed() && downstream.terrain().naturalHeight() >= settings.surface().sources().minimumElevation() && distances[downstream.index()] - >= settings.routing().branching().minimumSurfaceCourseLength()) { + >= settings.routing().minimumSurfaceCourseLength()) { capacity = Math.addExact( capacity, 1_000_000L @@ -1956,7 +1948,7 @@ public final class HydrologyPlanner { OutletCandidate outlet = outlets.get(index); potential[outlet.landIndex()] = 0D; outletIndex[outlet.landIndex()] = index; - queue.add(new RoutingQueueEntry(outlet.landIndex(), 0D, index, Integer.MIN_VALUE)); + queue.add(new RoutingQueueEntry(outlet.landIndex(), 0D, index)); } while (!queue.isEmpty()) { RoutingQueueEntry currentEntry = queue.poll(); @@ -1991,8 +1983,7 @@ public final class HydrologyPlanner { queue.add(new RoutingQueueEntry( upstream.index(), candidatePotential, - currentEntry.outletIndex(), - Integer.MIN_VALUE + currentEntry.outletIndex() )); } } @@ -2002,7 +1993,6 @@ public final class HydrologyPlanner { outletIndex, routeLengths(grid, parent), List.copyOf(outlets), - false, false ); } @@ -2014,106 +2004,10 @@ public final class HydrologyPlanner { routing.outletIndex(), routing.routeLengths(), routing.outlets(), - routing.containedSurface(), true ); } - private RoutingPlan buildContainedSurfaceRouting( - SampledGrid grid, - List outlets - ) { - int nodeCount = grid.nodes().size(); - double[] potential = new double[nodeCount]; - int[] parent = new int[nodeCount]; - int[] outletIndex = new int[nodeCount]; - int[] minimumNeighborHeights = new int[nodeCount]; - int[] requiredSurfaceHeads = new int[nodeCount]; - for (GridNode node : grid.nodes()) { - minimumNeighborHeights[node.index()] = minimumNeighborHeight(grid, node); - } - Arrays.fill(potential, Double.POSITIVE_INFINITY); - Arrays.fill(parent, -1); - Arrays.fill(outletIndex, -1); - Arrays.fill(requiredSurfaceHeads, Integer.MAX_VALUE); - PriorityQueue queue = new PriorityQueue<>(); - for (int index = 0; index < outlets.size(); index++) { - OutletCandidate outlet = outlets.get(index); - int requiredSurfaceHead = minimumSurfaceRoutingHead(grid.node(outlet.landIndex())); - if (requiredSurfaceHead > maximumSurfaceRoutingHead(grid.node(outlet.landIndex()))) { - continue; - } - potential[outlet.landIndex()] = 0D; - outletIndex[outlet.landIndex()] = index; - requiredSurfaceHeads[outlet.landIndex()] = requiredSurfaceHead; - queue.add(new RoutingQueueEntry(outlet.landIndex(), 0D, index, requiredSurfaceHead)); - } - while (!queue.isEmpty()) { - RoutingQueueEntry currentEntry = queue.poll(); - if (currentEntry.potential() != potential[currentEntry.nodeIndex()] - || currentEntry.outletIndex() != outletIndex[currentEntry.nodeIndex()] - || currentEntry.requiredSurfaceHead() != requiredSurfaceHeads[currentEntry.nodeIndex()]) { - continue; - } - GridNode downstream = grid.node(currentEntry.nodeIndex()); - for (GridOffset offset : ROUTING_OFFSETS) { - GridNode upstream = grid.nodeAt(downstream.gridX() + offset.x(), downstream.gridZ() + offset.z()); - if (upstream == null || upstream.terrain().ocean() || !upstream.terrain().transitAllowed()) { - continue; - } - int requiredSurfaceHead = currentEntry.requiredSurfaceHead(); - if (maximumSurfaceRoutingHead(upstream) < requiredSurfaceHead) { - continue; - } - requiredSurfaceHead = Math.max(requiredSurfaceHead, minimumSurfaceRoutingHead(upstream)); - double edgeCost = routeCost( - upstream, - downstream, - offset, - minimumNeighborHeights[downstream.index()] - ); - double candidatePotential = currentEntry.potential() + edgeCost; - int existingParent = parent[upstream.index()]; - boolean lowerSurfaceRequirement = requiredSurfaceHead < requiredSurfaceHeads[upstream.index()]; - boolean equalSurfaceRequirement = requiredSurfaceHead == requiredSurfaceHeads[upstream.index()]; - boolean replace = lowerSurfaceRequirement || equalSurfaceRequirement - && candidatePotential < potential[upstream.index()] - 1.0E-9D; - if (!replace - && equalSurfaceRequirement - && StrictMath.abs(candidatePotential - potential[upstream.index()]) <= 1.0E-9D) { - replace = existingParent < 0 || downstream.id() < grid.node(existingParent).id(); - } - if (!replace) { - continue; - } - potential[upstream.index()] = candidatePotential; - parent[upstream.index()] = downstream.index(); - outletIndex[upstream.index()] = currentEntry.outletIndex(); - requiredSurfaceHeads[upstream.index()] = requiredSurfaceHead; - queue.add(new RoutingQueueEntry( - upstream.index(), - candidatePotential, - currentEntry.outletIndex(), - requiredSurfaceHead - )); - } - } - return new RoutingPlan( - potential, - parent, - outletIndex, - routeLengths(grid, parent), - List.copyOf(outlets), - true, - true - ); - } - - private int minimumSurfaceRoutingHead(GridNode node) { - int permittedIncision = permittedSurfaceIncision(node.terrain()); - return Math.subtractExact(node.terrain().naturalHeight(), permittedIncision); - } - private int permittedSurfaceIncision(HydrologyTerrainSample terrain) { return permittedSurfaceIncision( settings.surface().maximumIncision(), @@ -2128,13 +2022,6 @@ public final class HydrologyPlanner { ); } - private int maximumSurfaceRoutingHead(GridNode node) { - return Math.subtractExact( - node.terrain().naturalHeight(), - settings.surface().maximumSurfaceInset() - ); - } - private double routeCost( GridNode upstream, GridNode downstream, @@ -2222,7 +2109,7 @@ public final class HydrologyPlanner { addSourceDiagnostic(node, surface, stable, HydrologyCandidateRejection.ROUTE_LIMIT, diagnostics); continue; } - if (routeLength < settings.routing().branching().minimumCourseLength(surface)) { + if (routeLength < settings.routing().minimumCourseLength(surface)) { addSourceDiagnostic(node, surface, stable, HydrologyCandidateRejection.COURSE_TOO_SHORT, diagnostics); continue; } @@ -2252,13 +2139,6 @@ public final class HydrologyPlanner { Comparator candidateOrder = Comparator .comparing(SourceCandidate::required) .reversed(); - if (surface && routing.containedSurface()) { - candidateOrder = candidateOrder.thenComparing( - Comparator.comparingInt((SourceCandidate candidate) -> - routing.routeLengths()[candidate.nodeIndex()]) - .reversed() - ); - } candidates.sort(candidateOrder .thenComparing(Comparator.comparingDouble(SourceCandidate::score).reversed()) .thenComparingLong(SourceCandidate::stableId)); @@ -2535,7 +2415,7 @@ public final class HydrologyPlanner { return false; } int routeLength = routing.routeLengths()[node.index()]; - return routeLength >= settings.routing().branching().minimumCourseLength(surface) + return routeLength >= settings.routing().minimumCourseLength(surface) && routeLength <= settings.routing().maximumRouteLength(); } @@ -2712,8 +2592,7 @@ public final class HydrologyPlanner { downstreamAnchor.z(), continuation.x(), continuation.z(), - transverseCandidates, - routing.containedSurface() + transverseCandidates ); centerline = refinedEdgeCache.get( refinedEdgeKey, @@ -2723,8 +2602,7 @@ public final class HydrologyPlanner { upstreamAnchor, downstreamAnchor, continuation, - transverseCandidates, - routing.containedSurface() + transverseCandidates ) ); refinedEdges.put(edgeId, centerline); @@ -2742,8 +2620,7 @@ public final class HydrologyPlanner { downstreamAnchor.z(), continuation.x(), continuation.z(), - ROUTE_FALLBACK_TRANSVERSE_CANDIDATES, - routing.containedSurface() + ROUTE_FALLBACK_TRANSVERSE_CANDIDATES ); centerline = refinedEdgeCache.get( refinedEdgeKey, @@ -2753,13 +2630,12 @@ public final class HydrologyPlanner { upstreamAnchor, downstreamAnchor, continuation, - ROUTE_FALLBACK_TRANSVERSE_CANDIDATES, - routing.containedSurface() + ROUTE_FALLBACK_TRANSVERSE_CANDIDATES ) ); refinedEdges.put(edgeId, centerline); } - if (centerline.isEmpty() && !routing.containedSurface()) { + if (centerline.isEmpty()) { centerline = constrainedTerrainFallbackEdge( routeAnchor(upstream), routeAnchor(downstream) @@ -2989,12 +2865,9 @@ public final class HydrologyPlanner { HydrologyPoint upstream, HydrologyPoint downstream, HydrologyPoint continuation, - int transverseCandidates, - boolean containedSurface + int transverseCandidates ) { - int refinement = containedSurface - ? Math.max(1, settings.routing().refinementSpacing() / 2) - : settings.routing().refinementSpacing(); + int refinement = settings.routing().refinementSpacing(); int effectiveTransverseCandidates = transverseCandidates; double deltaX = downstream.x() - upstream.x(); double deltaZ = downstream.z() - upstream.z(); @@ -3015,7 +2888,7 @@ public final class HydrologyPlanner { if (elevationTransition) { endTangent = directTangent; } - double wormBlend = elevationTransition || containedSurface ? 0D : routeWormBlend(); + double wormBlend = elevationTransition ? 0D : routeWormBlend(); double tangentMagnitude = distance * (ROUTE_TANGENT_SCALE + wormBlend * 0.42D); if (wormBlend > 0D) { startTangent = routeWormDirection(upstream, startTangent, wormBlend); @@ -3040,7 +2913,7 @@ public final class HydrologyPlanner { tangentMagnitude, distance, progress, - elevationTransition || containedSurface ? 0D : 1D + elevationTransition ? 0D : 1D ); List candidates = routeCandidates( upstreamId, @@ -3048,15 +2921,14 @@ public final class HydrologyPlanner { nominal, progress, refinement, - elevationTransition ? 0 : effectiveTransverseCandidates, - containedSurface + elevationTransition ? 0 : effectiveTransverseCandidates ); if (candidates.isEmpty()) { return List.of(); } layers.add(candidates); } - return solveTerrainRoute(layers, distance / steps, containedSurface); + return solveTerrainRoute(layers, distance / steps); } private double routeWormBlend() { @@ -3267,8 +3139,7 @@ public final class HydrologyPlanner { RoutePosition nominal, double progress, int refinement, - int transverseCandidates, - boolean containedSurface + int transverseCandidates ) { double searchEnvelope = StrictMath.sin(StrictMath.PI * progress); LinkedHashMap candidates = new LinkedHashMap<>(); @@ -3279,14 +3150,7 @@ public final class HydrologyPlanner { int x = (int) StrictMath.round(nominal.x() - nominal.tangent().z() * offset); int z = (int) StrictMath.round(nominal.z() + nominal.tangent().x() * offset); HydrologyTerrainSample terrain = sampleLandBasis(x, z); - if (terrain == null - || !terrain.transitAllowed() - || containedSurface && !containedSurfaceRouteCandidate( - x, - z, - terrain, - nominal.tangent() - )) { + if (terrain == null || !terrain.transitAllowed()) { continue; } long packed = RiverFootprint.pack(x, z); @@ -3311,13 +3175,7 @@ public final class HydrologyPlanner { HydrologyTerrainSample fallbackTerrain = sampleLandBasis(fallbackX, fallbackZ); if (candidates.isEmpty() && fallbackTerrain != null - && fallbackTerrain.transitAllowed() - && (!containedSurface || containedSurfaceRouteCandidate( - fallbackX, - fallbackZ, - fallbackTerrain, - nominal.tangent() - ))) { + && fallbackTerrain.transitAllowed()) { long packed = RiverFootprint.pack(fallbackX, fallbackZ); double fallbackOffset = (nominal.baseX() - nominal.x()) * -nominal.tangent().z() + (nominal.baseZ() - nominal.z()) * nominal.tangent().x(); @@ -3435,20 +3293,13 @@ public final class HydrologyPlanner { return north + (south - north) * progressZ; } - private RouteCandidate resolveRouteCandidate(RouteCandidate candidate, boolean containedSurface) { + private RouteCandidate resolveRouteCandidate(RouteCandidate candidate) { if (candidate.terrainResolved()) { return candidate; } HydrologyPoint point = candidate.point(); HydrologyTerrainSample terrain = sampleLandBasis(point.x(), point.z()); - if (terrain == null - || !terrain.transitAllowed() - || containedSurface && !containedSurfaceRouteCandidate( - point.x(), - point.z(), - terrain, - candidate.tangent() - )) { + if (terrain == null || !terrain.transitAllowed()) { return new RouteCandidate( point, candidate.offset(), @@ -3476,40 +3327,9 @@ public final class HydrologyPlanner { + terrain.routingCost() * terrain.routingMultiplier(); } - private boolean containedSurfaceRouteCandidate( - int x, - int z, - HydrologyTerrainSample terrain, - Direction tangent - ) { - if (terrain.slope() >= settings.hydraulics().waterfallMinimumDrop()) { - return true; - } - int bankDistance = surfaceBankDistance(settings.surface().maximumWidth()); - int head = Math.subtractExact( - terrain.naturalHeight(), - settings.surface().maximumSurfaceInset() - ); - HydrologyPoint point = new HydrologyPoint(x, terrain.naturalHeight(), z); - return surfaceBankSupported( - point, - tangent, - bankDistance, - head, - Integer.MAX_VALUE - ) && surfaceBankSupported( - point, - tangent, - -bankDistance, - head, - Integer.MAX_VALUE - ); - } - private List solveTerrainRoute( List> layers, - double targetStepLength, - boolean containedSurface + double targetStepLength ) { ArrayList> availableLayers = new ArrayList<>(layers); HashSet rejectedTransitions = new HashSet<>(); @@ -3517,18 +3337,15 @@ public final class HydrologyPlanner { RouteCandidate[] route = selectTerrainRoute( availableLayers, targetStepLength, - containedSurface, rejectedTransitions ); if (route.length == 0) { return List.of(); } - if (!containedSurface) { - TerrainTransitionKey unsupportedTransition = unsupportedTerrainTransition(route); - if (unsupportedTransition != null) { - rejectedTransitions.add(unsupportedTransition); - continue; - } + TerrainTransitionKey unsupportedTransition = unsupportedTerrainTransition(route); + if (unsupportedTransition != null) { + rejectedTransitions.add(unsupportedTransition); + continue; } int pitRemoval = removeTerrainPits(availableLayers, route); if (pitRemoval < 0) { @@ -3549,19 +3366,15 @@ public final class HydrologyPlanner { if (points.size() < 2) { return List.of(); } - return containedSurface ? List.copyOf(points) : smoothTerrainRoute(points); + return smoothTerrainRoute(points); } } private RouteCandidate[] selectTerrainRoute( List> layers, double targetStepLength, - boolean containedSurface, Set rejectedTransitions ) { - if (containedSurface) { - return selectContainedSurfaceTerrainRoute(layers, targetStepLength); - } int layerCount = layers.size(); double[][] costs = new double[layerCount][]; int[][] predecessors = new int[layerCount][]; @@ -3630,27 +3443,9 @@ public final class HydrologyPlanner { return null; } - private RouteCandidate[] selectContainedSurfaceTerrainRoute( - List> layers, - double targetStepLength - ) { - double maximumTurn = Math.max( - SURFACE_MAXIMUM_LOCAL_TURN_DEGREES, - settings.geometry().meanders().maximumTurnDegrees() - ); - return selectCurvatureAwareTerrainRoute( - layers, - targetStepLength, - true, - maximumTurn, - 1.25D - ); - } - private RouteCandidate[] selectCurvatureAwareTerrainRoute( List> layers, double targetStepLength, - boolean containedSurface, double maximumTurn, double turnCost ) { @@ -3663,7 +3458,7 @@ public final class HydrologyPlanner { for (int layerIndex = 0; layerIndex < layerCount; layerIndex++) { int layerSize = layers.get(layerIndex).size(); localPenalties[layerIndex] = new double[layerSize]; - if (!containedSurface && layerIndex > 0 && layerIndex < layerCount - 1) { + if (layerIndex > 0 && layerIndex < layerCount - 1) { Arrays.fill(localPenalties[layerIndex], Double.NaN); } if (layerIndex == 0) { @@ -3690,7 +3485,6 @@ public final class HydrologyPlanner { boolean evaluated = evaluateSelectedSurfaceCosts( selection, layers, - containedSurface, localPenalties, transitionCosts, targetStepLength @@ -3825,7 +3619,6 @@ public final class HydrologyPlanner { private boolean evaluateSelectedSurfaceCosts( CurvatureRouteSelection selection, List> layers, - boolean containedSurface, double[][] localPenalties, double[][][] transitionCosts, double targetStepLength @@ -3838,28 +3631,26 @@ public final class HydrologyPlanner { if (candidate.terrainResolved()) { continue; } - RouteCandidate resolved = resolveRouteCandidate(candidate, containedSurface); + RouteCandidate resolved = resolveRouteCandidate(candidate); ArrayList updatedLayer = new ArrayList<>(layers.get(layerIndex)); updatedLayer.set(indices[layerIndex], resolved); layers.set(layerIndex, List.copyOf(updatedLayer)); route[layerIndex] = resolved; evaluated = true; } - if (!containedSurface) { - for (int layerIndex = 1; layerIndex < route.length - 1; layerIndex++) { - int candidateIndex = indices[layerIndex]; - if (!Double.isNaN(localPenalties[layerIndex][candidateIndex])) { - continue; - } - if (!Double.isFinite(route[layerIndex].localScore())) { - localPenalties[layerIndex][candidateIndex] = 0D; - continue; - } - localPenalties[layerIndex][candidateIndex] = surfaceRouteCandidateBankPenalty( - route[layerIndex] - ); - evaluated = true; + for (int layerIndex = 1; layerIndex < route.length - 1; layerIndex++) { + int candidateIndex = indices[layerIndex]; + if (!Double.isNaN(localPenalties[layerIndex][candidateIndex])) { + continue; } + if (!Double.isFinite(route[layerIndex].localScore())) { + localPenalties[layerIndex][candidateIndex] = 0D; + continue; + } + localPenalties[layerIndex][candidateIndex] = surfaceRouteCandidateBankPenalty( + route[layerIndex] + ); + evaluated = true; } for (int layerIndex = 1; layerIndex < route.length; layerIndex++) { int previousIndex = indices[layerIndex - 1]; @@ -3873,14 +3664,9 @@ public final class HydrologyPlanner { transitionCosts[layerIndex][previousIndex][currentIndex] = Double.POSITIVE_INFINITY; continue; } - double transition = routeTransitionCost( - previous, - current, - targetStepLength, - containedSurface - ); + double transition = routeTransitionCost(previous, current, targetStepLength); if (Double.isFinite(transition)) { - transition += surfaceRouteTransitionPenalty(previous, current, containedSurface); + transition += surfaceRouteTransitionPenalty(previous, current); } transitionCosts[layerIndex][previousIndex][currentIndex] = transition; evaluated = true; @@ -3910,17 +3696,13 @@ public final class HydrologyPlanner { private double surfaceRouteTransitionPenalty( RouteCandidate start, - RouteCandidate end, - boolean containedSurface + RouteCandidate end ) { - if (containedSurface) { - return 0D; - } int minimumBankDistance = surfaceBankDistance(settings.surface().minimumWidth()); int maximumBankDistance = surfaceBankDistance(settings.surface().maximumWidth()); int maximumBankRise = Math.addExact( settings.surface().maximumIncision(), - (int) StrictMath.ceil(settings.surface().minimumTerrainBlendWidth() * 0.5D) + (int) StrictMath.ceil(settings.surface().banks().minimumBlendWidth() * 0.5D) ); Direction tangent = direction(start.point().x(), start.point().z(), end.point().x(), end.point().z()); int endpointCeiling = Math.max(start.point().y(), end.point().y()); @@ -3944,7 +3726,7 @@ public final class HydrologyPlanner { } int head = Math.subtractExact( terrain.naturalHeight(), - settings.surface().maximumSurfaceInset() + settings.surface().banks().inset() ); penalty += surfaceRouteBankPenalty( point, @@ -3970,7 +3752,7 @@ public final class HydrologyPlanner { if (terrain.slope() < settings.hydraulics().waterfallMinimumDrop()) { int head = Math.subtractExact( terrain.naturalHeight(), - settings.surface().maximumSurfaceInset() + settings.surface().banks().inset() ); penalty += surfaceRouteBankPenalty( point, @@ -4024,7 +3806,7 @@ public final class HydrologyPlanner { private List smoothTerrainRoute(List route) { List current = List.copyOf(route); List best = current; - double bestTurnScore = containedSurfaceTurnScore(current); + double bestTurnScore = routeTurnScore(current); HydrologyPlannerSettings.Meanders meanders = settings.geometry().meanders(); int maximumPasses = Math.max(8, meanders.smoothingPasses() * 4); for (int pass = 0; pass < maximumPasses; pass++) { @@ -4047,7 +3829,7 @@ public final class HydrologyPlanner { break; } current = candidate; - double candidateTurnScore = containedSurfaceTurnScore(candidate); + double candidateTurnScore = routeTurnScore(candidate); if (candidateTurnScore < bestTurnScore) { best = candidate; bestTurnScore = candidateTurnScore; @@ -4136,13 +3918,9 @@ public final class HydrologyPlanner { private double routeTransitionCost( RouteCandidate previous, RouteCandidate current, - double targetStepLength, - boolean containedSurface + double targetStepLength ) { - boolean supported = containedSurface - ? containedSurfaceSolverTransitionSupported(previous.point(), current.point()) - : traversableTerrainTransition(previous.point(), current.point()); - if (!supported) { + if (!traversableTerrainTransition(previous.point(), current.point())) { return Double.POSITIVE_INFINITY; } return routeTransitionGeometryCost(previous, current, targetStepLength); @@ -4192,81 +3970,6 @@ public final class HydrologyPlanner { return true; } - private boolean containedSurfaceSolverTransitionSupported( - HydrologyPoint start, - HydrologyPoint end - ) { - List crossing = rasterLine(start, end); - HydrologyTerrainSample startTerrain = sampleLandBasis(start.x(), start.z()); - HydrologyTerrainSample endTerrain = sampleLandBasis(end.x(), end.z()); - if (startTerrain == null || endTerrain == null) { - return false; - } - int boundaryHeight = Math.min(startTerrain.naturalHeight(), endTerrain.naturalHeight()); - int threshold = settings.hydraulics().waterfallMinimumDrop(); - int surfaceInset = settings.surface().maximumSurfaceInset(); - int upstreamHead = Math.subtractExact(start.y(), surfaceInset); - int downstreamHead = Math.subtractExact(end.y(), surfaceInset); - Direction tangent = direction(start.x(), start.z(), end.x(), end.z()); - for (int pointIndex = 0; pointIndex < crossing.size(); pointIndex++) { - HydrologyPoint point = crossing.get(pointIndex); - HydrologyTerrainSample terrain; - if (pointIndex == 0) { - terrain = startTerrain; - } else if (pointIndex == crossing.size() - 1) { - terrain = endTerrain; - } else { - terrain = sampleLandBasis(point.x(), point.z()); - } - if (terrain == null || !terrain.transitAllowed()) { - return false; - } - if (pointIndex > 0 - && pointIndex < crossing.size() - 1 - && boundaryHeight - terrain.naturalHeight() >= threshold) { - return false; - } - double progress = crossing.size() < 2 - ? 0D - : pointIndex / (double) (crossing.size() - 1); - int head = (int) StrictMath.round( - upstreamHead + (downstreamHead - upstreamHead) * progress - ); - int permittedIncision = permittedSurfaceIncision(terrain); - if (terrain.naturalHeight() - head > permittedIncision - || !containedSurfaceRouteCandidate( - point.x(), - point.z(), - terrain, - tangent - )) { - return false; - } - } - return true; - } - - private boolean containedSurfaceTransitionSupported( - HydrologyPoint start, - HydrologyPoint end - ) { - Direction tangent = direction(start.x(), start.z(), end.x(), end.z()); - for (HydrologyPoint point : rasterLine(start, end)) { - HydrologyTerrainSample terrain = sampleLandBasis(point.x(), point.z()); - if (terrain == null - || !terrain.transitAllowed() - || !containedSurfaceRouteCandidate( - point.x(), - point.z(), - terrain, - tangent - )) { - return false; - } - } - return true; - } - private boolean crossesTerrainCrevasse(HydrologyPoint start, HydrologyPoint end) { List crossing = rasterLine(start, end); if (crossing.size() < 3) { @@ -4506,7 +4209,7 @@ public final class HydrologyPlanner { || routedLength / directLength > SURFACE_MAXIMUM_SINUOSITY) { return false; } - List sampled = resampleContainedSurfaceCenterline( + List sampled = resampleRouteCenterline( centerline, Math.max(16D, settings.routing().refinementSpacing() * 4D) ); @@ -4587,7 +4290,7 @@ public final class HydrologyPlanner { return length; } - private List resampleContainedSurfaceCenterline( + private List resampleRouteCenterline( List points, double spacing ) { @@ -5540,8 +5243,7 @@ public final class HydrologyPlanner { List points = surfaceCourseCenterline( Objects.requireNonNull(grid, "Surface course routing grid is required"), surfaceGuide, - geometryId, - routing.containedSurface() + geometryId ); if (points.size() < 2) { return null; @@ -5563,18 +5265,7 @@ public final class HydrologyPlanner { private List surfaceCourseCenterline( SampledGrid grid, List guide, - long geometryId, - boolean containedSurface - ) { - return surfaceCourseCenterline(grid, guide, geometryId, containedSurface, !containedSurface); - } - - private List surfaceCourseCenterline( - SampledGrid grid, - List guide, - long geometryId, - boolean containedSurface, - boolean lazyCandidates + long geometryId ) { if (guide.size() < 2) { return List.of(); @@ -5586,9 +5277,7 @@ public final class HydrologyPlanner { return List.of(); } ArrayList> layers = new ArrayList<>(nominal.size()); - int transverseCandidates = containedSurface - ? ROUTE_TRANSVERSE_CANDIDATES - : SURFACE_COURSE_TRANSVERSE_CANDIDATES; + int transverseCandidates = SURFACE_COURSE_TRANSVERSE_CANDIDATES; for (int pointIndex = 0; pointIndex < nominal.size(); pointIndex++) { SurfaceCurvePoint point = nominal.get(pointIndex); Direction tangent = surfaceCurveTangent(nominal, pointIndex); @@ -5607,8 +5296,7 @@ public final class HydrologyPlanner { tangent ); long candidateId = HydrologyHash.mix(geometryId, pointIndex); - List candidates = lazyCandidates - ? unresolvedSurfaceRouteCandidates( + List candidates = unresolvedSurfaceRouteCandidates( grid, geometryId, candidateId, @@ -5616,15 +5304,6 @@ public final class HydrologyPlanner { progress, refinement, transverseCandidates - ) - : routeCandidates( - geometryId, - candidateId, - position, - progress, - refinement, - transverseCandidates, - containedSurface ); if (candidates.isEmpty()) { return List.of(); @@ -5640,12 +5319,11 @@ public final class HydrologyPlanner { RouteCandidate[] route = selectCurvatureAwareTerrainRoute( availableLayers, refinement, - containedSurface, maximumTurn, SURFACE_COURSE_TURN_COST ); if (route.length == 0) { - if (lazyCandidates && addExhaustedSurfaceRouteFallbacks( + if (addExhaustedSurfaceRouteFallbacks( grid, availableLayers, nominal, @@ -5675,9 +5353,6 @@ public final class HydrologyPlanner { if (points.size() < 2 || !traversableRoute(points)) { return List.of(); } - if (containedSurface) { - return smoothContainedSurfaceRoute(points); - } return smoothTerrainRoute(points); } } @@ -5747,65 +5422,8 @@ public final class HydrologyPlanner { return true; } - private List smoothContainedSurfaceRoute(List route) { - List current = List.copyOf(route); - List best = current; - double bestScore = containedSurfaceTurnScore(best); - int maximumPasses = Math.max(8, settings.geometry().meanders().smoothingPasses() * 4); - double[] strengths = new double[]{0.25D, 0.1875D, 0.125D, 0.0625D}; - for (int pass = 0; pass < maximumPasses; pass++) { - List accepted = null; - for (double strength : strengths) { - List candidate = smoothContainedSurfaceRoutePass(current, strength); - if (candidate.size() >= 2 && containedSurfaceRouteSupported(candidate)) { - accepted = candidate; - break; - } - } - if (accepted == null || accepted.equals(current)) { - break; - } - current = accepted; - double score = containedSurfaceTurnScore(current); - if (score < bestScore) { - best = current; - bestScore = score; - } - } - return best; - } - - private List smoothContainedSurfaceRoutePass( - List route, - double strength - ) { - ArrayList smoothed = new ArrayList<>(route.size()); - smoothed.add(route.getFirst()); - for (int pointIndex = 1; pointIndex < route.size() - 1; pointIndex++) { - HydrologyPoint previous = route.get(pointIndex - 1); - HydrologyPoint point = route.get(pointIndex); - HydrologyPoint next = route.get(pointIndex + 1); - int x = (int) StrictMath.round( - previous.x() * strength - + point.x() * (1D - strength * 2D) - + next.x() * strength - ); - int z = (int) StrictMath.round( - previous.z() * strength - + point.z() * (1D - strength * 2D) - + next.z() * strength - ); - HydrologyTerrainSample terrain = sampleLandBasis(x, z); - smoothed.add(terrain == null || !terrain.transitAllowed() - ? point - : new HydrologyPoint(x, terrain.naturalHeight(), z)); - } - smoothed.add(route.getLast()); - return withoutDuplicateRoutePoints(smoothed); - } - - private double containedSurfaceTurnScore(List route) { - List sampled = resampleContainedSurfaceCenterline( + private double routeTurnScore(List route) { + List sampled = resampleRouteCenterline( route, Math.max(16D, settings.routing().refinementSpacing() * 4D) ); @@ -5829,27 +5447,12 @@ public final class HydrologyPlanner { return percentile + maximum * 0.05D; } - private boolean containedSurfaceRouteSupported(List points) { - if (!traversableRoute(points) || containsTerrainPit(points)) { - return false; - } - for (int pointIndex = 0; pointIndex < points.size() - 1; pointIndex++) { - if (!containedSurfaceTransitionSupported( - points.get(pointIndex), - points.get(pointIndex + 1) - )) { - return false; - } - } - return true; - } - private double surfaceRouteCandidateBankPenalty(RouteCandidate candidate) { int minimumBankDistance = surfaceBankDistance(settings.surface().minimumWidth()); int maximumBankDistance = surfaceBankDistance(settings.surface().maximumWidth()); int maximumBankRise = Math.addExact( settings.surface().maximumIncision(), - (int) StrictMath.ceil(settings.surface().minimumTerrainBlendWidth() * 0.5D) + (int) StrictMath.ceil(settings.surface().banks().minimumBlendWidth() * 0.5D) ); HydrologyPoint point = candidate.point(); HydrologyTerrainSample terrain = sampleLandBasis(point.x(), point.z()); @@ -5861,7 +5464,7 @@ public final class HydrologyPlanner { } int head = Math.subtractExact( terrain.naturalHeight(), - settings.surface().maximumSurfaceInset() + settings.surface().banks().inset() ); return surfaceRouteBankPenalty( point, @@ -6120,24 +5723,6 @@ public final class HydrologyPlanner { return pointX * pointX + pointZ * pointZ; } - private boolean surfaceBankSupported( - HydrologyPoint point, - Direction tangent, - int signedDistance, - int head, - int maximumBankRise - ) { - int bankX = (int) StrictMath.round(point.x() - tangent.z() * signedDistance); - int bankZ = (int) StrictMath.round(point.z() + tangent.x() * signedDistance); - HydrologyTerrainSample bank = sampleLandBasis(bankX, bankZ); - if (bank == null) { - return false; - } - int rise = bank.naturalHeight() - head; - return rise >= SURFACE_MINIMUM_NATURAL_BANK_RISE && (rise <= maximumBankRise - || bank.slope() >= settings.hydraulics().waterfallMinimumDrop()); - } - private HydrologyTerrainSample surfaceBankTerrain( HydrologyPoint point, Direction tangent, @@ -7031,8 +6616,7 @@ public final class HydrologyPlanner { int downstreamZ, int continuationX, int continuationZ, - int transverseCandidates, - boolean containedSurface + int transverseCandidates ) { } @@ -7131,16 +6715,11 @@ public final class HydrologyPlanner { private record RoutingQueueEntry( int nodeIndex, double potential, - int outletIndex, - int requiredSurfaceHead + int outletIndex ) implements Comparable { @Override public int compareTo(RoutingQueueEntry other) { - int surfaceHeadComparison = Integer.compare(requiredSurfaceHead, other.requiredSurfaceHead); - if (surfaceHeadComparison != 0) { - return surfaceHeadComparison; - } int potentialComparison = Double.compare(potential, other.potential); if (potentialComparison != 0) { return potentialComparison; @@ -7156,7 +6735,6 @@ public final class HydrologyPlanner { int[] outletIndex, int[] routeLengths, List outlets, - boolean containedSurface, boolean organicSurfaceRequired ) { } diff --git a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyPlannerSettings.java b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyPlannerSettings.java index ef2e6778c..6b1ea59f7 100644 --- a/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyPlannerSettings.java +++ b/core/src/main/java/art/arcane/iris/engine/hydrology/HydrologyPlannerSettings.java @@ -35,9 +35,9 @@ public record HydrologyPlannerSettings( Source undergroundSources = new Source(true, 0.25D, Integer.MIN_VALUE, 0, 1, 512); return new HydrologyPlannerSettings( 63, - new Routing(2048, 64, 8, 8192, 8192, new Branching(384, 192), 1.5D, 24D, 2D, 0.2D), - new Surface(true, surfaceSources, 4, 8, 2, 4, 1, 1, 10, 1.5D, 4, 32, false, 1, 1, Banks.defaults()), - new Hydraulics(80, 180, 1, 7, 24, 8), + new Routing(2048, 64, 8192, 8192, 384, 192, 1.5D, 24D, 2D, 0.2D), + new Surface(true, surfaceSources, 4, 8, 2, 4, 10, 1.5D, Banks.defaults()), + new Hydraulics(8), new Underground(true, undergroundSources, -48, 72, 3, 8, 1, 3, 6, 14, true), new Outlets( true, @@ -97,7 +97,7 @@ public record HydrologyPlannerSettings( int radius = 0; int routeDisplacement = Math.multiplyExact(routing.sampleSpacing(), 3); if (surface.enabled() && surface.sources().enabled()) { - int blendWidth = Math.max(surface.maximumTerrainBlendWidth(), surface.banks().maximumBlendWidth()); + int blendWidth = surface.banks().maximumBlendWidth(); int surfaceRadius = (int) StrictMath.ceil( surface.maximumWidth() / 2D + surface.shoreWidth() + blendWidth ); @@ -105,7 +105,7 @@ public record HydrologyPlannerSettings( surfaceRadius, (int) StrictMath.ceil(geometry.drops().basinWidth( geometry.drops().flowWidth(surface.maximumWidth()) - ) / 2D + surface.shoreWidth() + surface.maximumTerrainBlendWidth()) + ) / 2D + surface.shoreWidth() + blendWidth) ); surfaceRadius = Math.max(surfaceRadius, outlets.coastalGrotto().horizontalRadius()); surfaceRadius = Math.max(surfaceRadius, outlets.inlandGrotto().horizontalRadius()); @@ -144,18 +144,17 @@ public record HydrologyPlannerSettings( public record Routing( int tileSize, int sampleSpacing, - int refinementSpacing, int maximumRouteNodes, int maximumRouteLength, - Branching branching, + int minimumSurfaceCourseLength, + int minimumUndergroundCourseLength, double valleyPreference, double uphillPenalty, double slopePenalty, double confluenceAttraction ) { public Routing { - if (tileSize < 32 || sampleSpacing < 4 || refinementSpacing < 1 || branching == null - || tileSize % sampleSpacing != 0 || sampleSpacing % refinementSpacing != 0) { + if (tileSize < 32 || sampleSpacing < 4 || tileSize % sampleSpacing != 0) { throw new IllegalArgumentException("Routing sizes must form an exact bounded tile lattice."); } int latticeWidth = tileSize / sampleSpacing + 1; @@ -166,8 +165,12 @@ public record HydrologyPlannerSettings( if (maximumRouteNodes < latticeNodes || maximumRouteNodes > 1_000_000 || maximumRouteLength < 1) { throw new IllegalArgumentException("maximumRouteNodes must contain the complete lattice and remain bounded."); } - if (branching.minimumSurfaceCourseLength() > maximumRouteLength - || branching.minimumUndergroundCourseLength() > maximumRouteLength) { + if (minimumSurfaceCourseLength < 0 || minimumSurfaceCourseLength > 32_768 + || minimumUndergroundCourseLength < 0 || minimumUndergroundCourseLength > 32_768) { + throw new IllegalArgumentException("Hydrology course lengths are invalid."); + } + if (minimumSurfaceCourseLength > maximumRouteLength + || minimumUndergroundCourseLength > maximumRouteLength) { throw new IllegalArgumentException("Minimum course lengths cannot exceed maximum route length."); } requireFiniteNonNegative(valleyPreference, "valleyPreference"); @@ -175,17 +178,17 @@ public record HydrologyPlannerSettings( requireFiniteNonNegative(slopePenalty, "slopePenalty"); requireFiniteNonNegative(confluenceAttraction, "confluenceAttraction"); } - } - public record Branching( - int minimumSurfaceCourseLength, - int minimumUndergroundCourseLength - ) { - public Branching { - if (minimumSurfaceCourseLength < 0 || minimumSurfaceCourseLength > 32_768 - || minimumUndergroundCourseLength < 0 || minimumUndergroundCourseLength > 32_768) { - throw new IllegalArgumentException("Hydrology branching lengths are invalid."); + // Route refinement is derived from the lattice, never authored. + public static int refinementSpacing(int sampleSpacing) { + if (sampleSpacing % 4 == 0) { + return 4; } + return sampleSpacing % 2 == 0 ? 2 : 1; + } + + public int refinementSpacing() { + return refinementSpacing(sampleSpacing); } public int minimumCourseLength(boolean surface) { @@ -221,15 +224,8 @@ public record HydrologyPlannerSettings( int maximumWidth, int minimumDepth, int maximumDepth, - int minimumSurfaceInset, - int maximumSurfaceInset, int maximumIncision, double shoreWidth, - int minimumTerrainBlendWidth, - int maximumTerrainBlendWidth, - boolean ridgeTunnelsEnabled, - int maximumRidgeTunnelLength, - int ridgeTunnelHeadroom, Banks banks ) { public Surface { @@ -238,14 +234,9 @@ public record HydrologyPlannerSettings( } requireRange(minimumWidth, maximumWidth, 1, 256, "surface width"); requireRange(minimumDepth, maximumDepth, 1, 64, "surface depth"); - requireRange(minimumSurfaceInset, maximumSurfaceInset, 1, 64, "surface inset"); if (maximumIncision < 0 || !Double.isFinite(shoreWidth) || shoreWidth < 0D || shoreWidth > 32D) { throw new IllegalArgumentException("Surface incision and shore width are invalid."); } - requireRange(minimumTerrainBlendWidth, maximumTerrainBlendWidth, 0, 128, "terrain blend width"); - if (maximumRidgeTunnelLength < 0 || ridgeTunnelHeadroom < 0) { - throw new IllegalArgumentException("Ridge tunnel bounds cannot be negative."); - } } } @@ -280,21 +271,11 @@ public record HydrologyPlannerSettings( } } - public record Hydraulics( - int minimumTargetPoolLength, - int maximumTargetPoolLength, - int riffleDrop, - int maximumGradualDrop, - int maximumGradualLength, - int waterfallMinimumDrop - ) { + // The routing gates read one hydraulic threshold: the drop that makes a reach a waterfall. + public record Hydraulics(int waterfallMinimumDrop) { public Hydraulics { - requireRange(minimumTargetPoolLength, maximumTargetPoolLength, 1, 4096, "target pool length"); - if (riffleDrop < 0 || maximumGradualDrop < riffleDrop - || maximumGradualLength < Math.max(1, maximumGradualDrop) - || waterfallMinimumDrop != maximumGradualDrop + 1 - || minimumTargetPoolLength < maximumGradualLength) { - throw new IllegalArgumentException("Hydraulic transition thresholds are inconsistent."); + if (waterfallMinimumDrop < 1) { + throw new IllegalArgumentException("waterfallMinimumDrop must be at least one block."); } } } diff --git a/core/src/main/java/art/arcane/iris/engine/hydrology/RiverCourse.java b/core/src/main/java/art/arcane/iris/engine/hydrology/RiverCourse.java index e304990a0..ec95a79a6 100644 --- a/core/src/main/java/art/arcane/iris/engine/hydrology/RiverCourse.java +++ b/core/src/main/java/art/arcane/iris/engine/hydrology/RiverCourse.java @@ -88,7 +88,7 @@ public record RiverCourse( HydraulicSegment lip = segments.get(segments.size() - 3); HydraulicSegment sinkhole = segments.get(segments.size() - 2); HydraulicSegment grotto = segments.getLast(); - if (!lip.type().isSurface() && lip.type() != HydrologyFeatureType.RIDGE_BORE + if (!lip.type().isSurface() || sinkhole.type() != HydrologyFeatureType.SINKHOLE || sinkhole.drop() <= 0 || !sinkhole.receivingPool() diff --git a/core/src/main/java/art/arcane/iris/engine/hydrology/runtime/IrisHydrologyRuntime.java b/core/src/main/java/art/arcane/iris/engine/hydrology/runtime/IrisHydrologyRuntime.java index 7a3024f41..5585c94d9 100644 --- a/core/src/main/java/art/arcane/iris/engine/hydrology/runtime/IrisHydrologyRuntime.java +++ b/core/src/main/java/art/arcane/iris/engine/hydrology/runtime/IrisHydrologyRuntime.java @@ -65,9 +65,7 @@ public final class IrisHydrologyRuntime implements AutoCloseable { private static final int BIOME_PATCH_SCALE = 32; private static final long SURFACE_WIDTH_SALT = 0x5355524657494454L; private static final long SURFACE_DEPTH_SALT = 0x5355524644455054L; - private static final long SURFACE_INSET_SALT = 0x53555246494e5345L; private static final long SURFACE_BLEND_SALT = 0x53555246424c454eL; - private static final long TARGET_POOL_SALT = 0x544152474554504fL; private static final long UNDERGROUND_LEVEL_SALT = 0x554e4445524c564cL; private static final long UNDERGROUND_WIDTH_SALT = 0x554e444552574944L; private static final long UNDERGROUND_DEPTH_SALT = 0x554e444552444550L; @@ -556,9 +554,7 @@ public final class IrisHydrologyRuntime implements AutoCloseable { IrisUndergroundRiverConfig underground = rivers.getUnderground(); ProceduralStream surfaceWidth = styledStream(context, channel.getWidth(), SURFACE_WIDTH_SALT); ProceduralStream surfaceDepth = styledStream(context, channel.getDepth(), SURFACE_DEPTH_SALT); - ProceduralStream surfaceInset = ProceduralStream.ofDouble((x, z) -> (double) channel.getInset()); ProceduralStream surfaceBlend = ProceduralStream.ofDouble((x, z) -> (double) banks.getMaximumBlendWidth()); - ProceduralStream targetPool = ProceduralStream.ofDouble((x, z) -> 128D); ProceduralStream undergroundLevel = styledStream( context, underground.getFluidLevel(), @@ -589,9 +585,7 @@ public final class IrisHydrologyRuntime implements AutoCloseable { ProceduralStream stream = switch (request.field()) { case SURFACE_WIDTH -> surfaceWidth; case SURFACE_DEPTH -> surfaceDepth; - case SURFACE_INSET -> surfaceInset; case SURFACE_BLEND_WIDTH -> surfaceBlend; - case TARGET_POOL_LENGTH -> targetPool; case UNDERGROUND_FLUID_LEVEL -> undergroundLevel; case UNDERGROUND_WIDTH -> undergroundWidth; case UNDERGROUND_DEPTH -> undergroundDepth; @@ -639,7 +633,6 @@ public final class IrisHydrologyRuntime implements AutoCloseable { IrisUndergroundRiverConfig underground = rivers.getUnderground(); IrisUndergroundRiverSourceConfig undergroundSources = underground.getSources(); int routeNodes = maximumRouteNodes(routing); - int refinementSpacing = refinementSpacing(routing.getSampleSpacing()); int minimumWorldY = dimension.getMinHeight(); boolean riversEnabled = rivers.isEnabled(); boolean surfaceEnabled = riversEnabled && surface.isEnabled(); @@ -647,13 +640,10 @@ public final class IrisHydrologyRuntime implements AutoCloseable { HydrologyPlannerSettings.Routing plannerRouting = new HydrologyPlannerSettings.Routing( routing.getTileSize(), routing.getSampleSpacing(), - refinementSpacing, routeNodes, routing.getMaximumRouteLength(), - new HydrologyPlannerSettings.Branching( - routing.getMinimumSurfaceCourseLength(), - routing.getMinimumUndergroundCourseLength() - ), + routing.getMinimumSurfaceCourseLength(), + routing.getMinimumUndergroundCourseLength(), routing.getValleyPreference(), routing.getUphillPenalty(), routing.getSlopePenalty(), @@ -674,15 +664,8 @@ public final class IrisHydrologyRuntime implements AutoCloseable { maximumInt(channel.getWidth()), minimumInt(channel.getDepth()), maximumInt(channel.getDepth()), - channel.getInset(), - channel.getInset(), channel.getMaximumIncision(), banks.getShoreWidth(), - banks.getMinimumBlendWidth(), - banks.getMaximumBlendWidth(), - false, - 1, - 1, new HydrologyPlannerSettings.Banks( channel.getInset(), banks.getFreeboard(), @@ -697,14 +680,8 @@ public final class IrisHydrologyRuntime implements AutoCloseable { banks.isExposeCutStrata() ) ); - HydrologyPlannerSettings.Hydraulics plannerHydraulics = new HydrologyPlannerSettings.Hydraulics( - 80, - 180, - 1, - flow.getWaterfallMinimumDrop() - 1, - 24, - flow.getWaterfallMinimumDrop() - ); + HydrologyPlannerSettings.Hydraulics plannerHydraulics = + new HydrologyPlannerSettings.Hydraulics(flow.getWaterfallMinimumDrop()); HydrologyPlannerSettings.Source plannerUndergroundSources = new HydrologyPlannerSettings.Source( undergroundEnabled, undergroundSources.getDensity(), @@ -760,17 +737,10 @@ public final class IrisHydrologyRuntime implements AutoCloseable { plannerUnderground, plannerOutlets, plannerGeometry, - deepFluids(hydrology.getDeepFluids(), minimumWorldY, routing, refinementSpacing) + deepFluids(hydrology.getDeepFluids(), minimumWorldY, routing, plannerRouting.refinementSpacing()) ); } - static int refinementSpacing(int sampleSpacing) { - if (sampleSpacing % 4 == 0) { - return 4; - } - return sampleSpacing % 2 == 0 ? 2 : 1; - } - private static HydrologyPlannerSettings.Geometry geometry( IrisRiverGeometryConfig geometry, HydrologyPlannerSettings.ChannelShape surfaceShape @@ -877,7 +847,8 @@ public final class IrisHydrologyRuntime implements AutoCloseable { return 0; } int configuredLength = Math.max( - refinementSpacing(routing.getSampleSpacing()), configuration.getSpacing() / 3); + HydrologyPlannerSettings.Routing.refinementSpacing(routing.getSampleSpacing()), + configuration.getSpacing() / 3); return Math.min(configuredLength, routing.getTileSize() / 2); } diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyCaveCourseFilterTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyCaveCourseFilterTest.java index d13988da2..75031c863 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyCaveCourseFilterTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyCaveCourseFilterTest.java @@ -31,12 +31,12 @@ public class HydrologyCaveCourseFilterTest { }; @Test - public void rejectedRidgeBoreIsAbsentFromCoursesFootprintRendererAndLocator() { + public void rejectedCaveCourseIsAbsentFromCoursesFootprintRendererAndLocator() { HydrologyTerrainSample terrain = HydrologyTerrainSample.openLand(80, 0D, "parent"); HydraulicSegment ridge = new HydraulicSegment( 22L, 11L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 20, 20, 2, @@ -81,19 +81,19 @@ public class HydrologyCaveCourseFilterTest { assertTrue(tile.footprint().isEmpty()); assertTrue(tile.features().isEmpty()); assertTrue(tile.renderAt(8, 8).features().isEmpty()); - assertTrue(tile.nearestFeature(HydrologyFeatureType.RIDGE_BORE, 8, 8, 16).isEmpty()); + assertTrue(tile.nearestFeature(HydrologyFeatureType.UNDERGROUND_POOL, 8, 8, 16).isEmpty()); assertEquals(1, tile.diagnosticCandidates().size()); assertEquals(HydrologyCandidateRejection.CAVE_CONTAINMENT, tile.diagnosticCandidates().getFirst().rejection()); } @Test - public void generatedSurfaceConnectedCaveAroundRidgeBoreIsSealed() { + public void generatedSurfaceConnectedCaveAroundAnUndergroundReachIsSealed() { HydrologyTerrainSample terrain = HydrologyTerrainSample.openLand(80, 0D, "parent"); HydraulicSegment ridge = new HydraulicSegment( 42L, 41L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 20, 20, 2, @@ -136,7 +136,7 @@ public class HydrologyCaveCourseFilterTest { HydraulicSegment ridge = new HydraulicSegment( 420L, 410L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 20, 20, 2, @@ -218,12 +218,12 @@ public class HydrologyCaveCourseFilterTest { } @Test - public void ridgeBoreAboveFinalTerrainRejectsTheWholeCourse() { + public void undergroundReachAboveFinalTerrainRejectsTheWholeCourse() { HydrologyTerrainSample terrain = HydrologyTerrainSample.openLand(80, 0D, "parent"); HydraulicSegment ridge = new HydraulicSegment( 52L, 51L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 20, 20, 2, @@ -255,12 +255,12 @@ public class HydrologyCaveCourseFilterTest { } @Test - public void surfaceRidgeExposurePreflightChecksTheFullActionBoundaryBeforeVoxelExpansion() { + public void surfaceExposurePreflightChecksTheFullActionBoundaryBeforeVoxelExpansion() { HydrologyTerrainSample terrain = HydrologyTerrainSample.openLand(80, 0D, "parent"); HydraulicSegment ridge = new HydraulicSegment( 152L, 151L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 20, 20, 2, @@ -294,12 +294,12 @@ public class HydrologyCaveCourseFilterTest { } @Test - public void ridgeSurfaceOpeningExemptsItsCompleteSixNeighborShell() { + public void caveSurfaceOpeningExemptsItsCompleteSixNeighborShell() { HydrologyTerrainSample terrain = HydrologyTerrainSample.openLand(80, 0D, "parent"); HydraulicSegment ridge = new HydraulicSegment( 162L, 161L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 20, 20, 2, @@ -505,7 +505,7 @@ public class HydrologyCaveCourseFilterTest { HydraulicSegment ridge = new HydraulicSegment( 192L, 191L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 20, 20, 2, @@ -598,12 +598,12 @@ public class HydrologyCaveCourseFilterTest { } @Test - public void acceptedRidgeBoreRetainsItsExactPlanOnTheImmutableTile() { + public void acceptedCaveCourseRetainsItsExactPlanOnTheImmutableTile() { HydrologyTerrainSample terrain = HydrologyTerrainSample.openLand(80, 0D, "parent"); HydraulicSegment ridge = new HydraulicSegment( 82L, 81L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 20, 20, 2, @@ -959,7 +959,7 @@ public class HydrologyCaveCourseFilterTest { HydraulicSegment ridge = new HydraulicSegment( 102L, 101L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 20, 20, 2, @@ -1034,7 +1034,7 @@ public class HydrologyCaveCourseFilterTest { HydraulicSegment ridge = new HydraulicSegment( 92L, 91L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 20, 20, 2, 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 da4747681..c7ea590ea 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 @@ -16,6 +16,57 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; public class HydrologyFootprintCompilerTest { + private static final int[][] CARDINALS = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + /** + * Published dry cells cardinally adjacent to owned water whose final terrain sits below the + * water head plus the configured freeboard. + */ + private static int unsupportedBankCells(RiverFootprint footprint, long courseId) { + int freeboard = HydrologyPlannerSettings.defaults().surface().banks().freeboard(); + int unsupported = 0; + for (HydrologyColumnSample sample : footprint.columns().values()) { + HydrologyColumnLayer fluid = sample.primarySurfaceFluidLayer().orElse(null); + if (fluid == null + || fluid.feature().courseId() != courseId + || fluid.oceanApron() + || fluid.fallingFluid()) { + continue; + } + for (int[] offset : CARDINALS) { + HydrologyColumnSample neighbor = footprint + .sample(sample.x() + offset[0], sample.z() + offset[1]) + .orElse(null); + if (neighbor == null || neighbor.primarySurfaceFluidLayer().isPresent()) { + continue; + } + if (neighbor.terrainHeight() < fluid.fluidHeadY() + freeboard) { + unsupported++; + } + } + } + return unsupported; + } + + /** Deepest cut between natural terrain and the published water head over an owned channel. */ + private static int maximumSurfaceCut(RiverFootprint footprint, long courseId) { + int maximum = 0; + for (HydrologyColumnSample sample : footprint.columns().values()) { + HydrologyColumnLayer fluid = sample.primarySurfaceFluidLayer().orElse(null); + if (fluid == null + || fluid.feature().courseId() != courseId + || fluid.feature().type() == HydrologyFeatureType.MOUTH + || fluid.oceanApron() + || fluid.fallingFluid() + || !fluid.channel() + || !fluid.fluidOwned()) { + continue; + } + maximum = Math.max(maximum, sample.naturalHeight() - fluid.fluidHeadY()); + } + return maximum; + } + @Test public void surfaceChannelNeverRaisesTerrainOrPublishesUnsupportedFluid() { HydraulicSegment segment = new HydraulicSegment( @@ -78,7 +129,7 @@ public class HydrologyFootprintCompilerTest { RiverFootprint footprint = new HydrologyFootprintCompiler( HydrologyPlannerSettings.defaults(), terrain, - HydrologyGeometrySampler.deterministic(1L, terrain) + HydrologyGeometrySampler.deterministic(terrain) ).compile(List.of(course)); for (int x = 0; x <= 8; x++) { @@ -162,7 +213,7 @@ public class HydrologyFootprintCompilerTest { } @Test - public void surfaceChannelAtTerrainLevelIsReportedAsUncontained() { + public void surfaceChannelAtTerrainLevelHasNoContainingBank() { HydraulicSegment segment = new HydraulicSegment( 15L, 14L, @@ -180,7 +231,7 @@ public class HydrologyFootprintCompilerTest { HydrologyFootprintCompiler compiler = compiler(terrain); RiverFootprint footprint = compiler.compile(List.of(course)); - assertFalse(compiler.surfaceBanksContained(course)); + assertTrue(unsupportedBankCells(footprint, 14L) > 0); for (HydrologyColumnSample sample : footprint.columns().values()) { assertTrue(sample.terrainHeight() <= sample.naturalHeight()); } @@ -1026,51 +1077,6 @@ public class HydrologyFootprintCompilerTest { } } - @Test - public void surfaceBankGradingTapersAnIntersectingBoreInsteadOfOpeningItsSide() { - HydraulicSegment surface = new HydraulicSegment( - 321L, - 320L, - HydrologyFeatureType.SURFACE_POOL, - 70, - 70, - 6, - 2, - false, - false, - List.of(new HydrologyPoint(0, 70, 0), new HydrologyPoint(8, 70, 0)) - ); - HydraulicSegment ridge = new HydraulicSegment( - 322L, - 320L, - HydrologyFeatureType.RIDGE_BORE, - 74, - 74, - 4, - 2, - false, - false, - List.of(new HydrologyPoint(0, 74, 8), new HydrologyPoint(8, 74, 8)) - ); - HydrologyTerrainSampler terrain = (int x, int z) -> HydrologyTerrainSample.openLand(80, 0D, "parent"); - RiverFootprint footprint = compiler(terrain).compile(List.of(new RiverCourse( - 320L, - RiverCourseType.SURFACE, - OptionalLong.of(1L), - OptionalLong.of(2L), - "water", - 1, - List.of(), - List.of(surface, ridge) - ))); - - HydrologyColumnSample gradedBank = footprint.sample(4, 8).orElseThrow(); - assertTrue(gradedBank.layers().stream().noneMatch( - (HydrologyColumnLayer layer) -> layer.feature().type() == HydrologyFeatureType.RIDGE_BORE - )); - assertTrue(gradedBank.terrainHeight() < gradedBank.naturalHeight()); - } - @Test public void compactValidationMatchesFullRasterAcrossOverlapsOceanAndSurfaceOnlyCourses() { RiverCourse mixed = new RiverCourse( @@ -1389,64 +1395,6 @@ public class HydrologyFootprintCompilerTest { int sourceDepth = source.fluidHeadY() - source.bedY(); int cruiseDepth = cruise.fluidHeadY() - cruise.bedY(); assertTrue(Math.abs(sourceDepth - cruiseDepth) <= 1); - assertTrue(compiler.surfaceHeadwaterRampSupported(course)); - } - - @Test - public void incompleteSurfaceBoreCenterlineRejectsTheWholeCourse() { - HydraulicSegment entry = new HydraulicSegment( - 532L, - 531L, - HydrologyFeatureType.SURFACE_POOL, - 70, - 70, - 8, - 3, - false, - false, - List.of(new HydrologyPoint(0, 70, 0), new HydrologyPoint(16, 70, 0)) - ); - HydraulicSegment bore = new HydraulicSegment( - 533L, - 531L, - HydrologyFeatureType.RIDGE_BORE, - 70, - 70, - 8, - 3, - false, - false, - List.of(new HydrologyPoint(16, 70, 0), new HydrologyPoint(48, 70, 0)) - ); - HydraulicSegment exit = new HydraulicSegment( - 534L, - 531L, - HydrologyFeatureType.SURFACE_POOL, - 70, - 70, - 8, - 3, - false, - false, - List.of(new HydrologyPoint(48, 70, 0), new HydrologyPoint(64, 70, 0)) - ); - HydrologyTerrainSampler terrain = (int x, int z) -> HydrologyTerrainSample.openLand( - x == 32 ? 70 : 82, - 0D, - "parent" - ); - RiverCourse course = new RiverCourse( - 531L, - RiverCourseType.SURFACE, - OptionalLong.of(1L), - OptionalLong.of(2L), - "water", - 1, - List.of(), - List.of(entry, bore, exit) - ); - - assertFalse(compiler(terrain).surfaceBanksContained(course)); } @Test @@ -1519,17 +1467,17 @@ public class HydrologyFootprintCompilerTest { RiverFootprint lowBankFootprint = lowBankCompiler.compile(List.of(course)); HydrologyColumnSample lowBank = lowBankFootprint.sample(12, lowBankZ).orElseThrow(); - assertTrue(highCompiler.surfaceBanksContained(course)); + assertEquals(0, unsupportedBankCells(highFootprint, 528L)); assertEquals(70, lowBank.naturalHeight()); assertEquals(70, lowBank.terrainHeight()); - assertFalse(lowBankCompiler.surfaceBanksContained(course)); + assertTrue(unsupportedBankCells(lowBankFootprint, 528L) > 0); for (HydrologyColumnSample sample : lowBankFootprint.columns().values()) { assertTrue(sample.terrainHeight() <= sample.naturalHeight()); } } @Test - public void surfaceIncisionContainmentUsesFinalTerrainHeight() { + public void publishedSurfaceChannelStaysWithinTheConfiguredIncision() { HydraulicSegment channel = new HydraulicSegment( 529L, 528L, @@ -1543,28 +1491,16 @@ public class HydrologyFootprintCompilerTest { List.of(new HydrologyPoint(0, 70, 0), new HydrologyPoint(24, 70, 0)) ); RiverCourse course = course(528L, RiverCourseType.SURFACE, channel); - HydrologyFootprintCompiler supported = compiler((int x, int z) -> + HydrologyFootprintCompiler compiler = compiler((int x, int z) -> HydrologyTerrainSample.openLand(76, 0D, "parent")); - HydrologyTerrainSample routingBasis = HydrologyTerrainSample.openLand(76, 0D, "parent"); - HydrologyFootprintCompiler excessive = new HydrologyFootprintCompiler( - HydrologyPlannerSettings.defaults(), - new HydrologyFootprintCompiler.Sampling( - (int x, int z) -> HydrologyTerrainSample.openLand(83, 0D, "parent"), - request -> request.minimum(), - new CountingNaturalSampler( - routingBasis, - HydrologyRoutingTerrainSampler.NaturalClassification.LAND - ) - ) - ); - assertTrue(supported.surfaceIncisionContained(course)); - RiverFootprint excessiveFootprint = excessive.compile(List.of(course)); - assertFalse( - "center=" + excessiveFootprint.sample(12, 0), - excessive.surfaceIncisionContained(course) + RiverFootprint footprint = compiler.compile(List.of(course)); + + assertTrue( + "center=" + footprint.sample(12, 0), + maximumSurfaceCut(footprint, 528L) + <= HydrologyPlannerSettings.defaults().surface().maximumIncision() ); - assertFalse(excessive.surfaceIncisionContained(course, excessiveFootprint)); } @Test @@ -1592,7 +1528,6 @@ public class HydrologyFootprintCompilerTest { HydrologyFootprintCompiler compiler = compiler(terrain); assertTrue(compiler.compile(List.of(course)).isEmpty()); - assertFalse(compiler.surfaceBanksContained(course)); } @Test diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyModelTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyModelTest.java index 04976f70d..c8bed809d 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyModelTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyModelTest.java @@ -153,7 +153,7 @@ public class HydrologyModelTest { HydraulicSegment containedLip = new HydraulicSegment( 4L, 9L, - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.SURFACE_POOL, 80, 80, 4, diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerCrossTileCaveAdmissionIntegrationTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerCrossTileCaveAdmissionIntegrationTest.java index 32fd2f889..b8518f301 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerCrossTileCaveAdmissionIntegrationTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerCrossTileCaveAdmissionIntegrationTest.java @@ -252,8 +252,7 @@ public class HydrologyPlannerCrossTileCaveAdmissionIntegrationTest { ); return new HydrologyPlannerSettings( 63, - new HydrologyPlannerSettings.Routing(256, 16, 4, 1_089, 256, - new HydrologyPlannerSettings.Branching(16, 8), 0.5D, 12D, 0.5D, 0.2D), + new HydrologyPlannerSettings.Routing(256, 16, 1_089, 256, 16, 8, 0.5D, 12D, 0.5D, 0.2D), new HydrologyPlannerSettings.Surface( true, surfaceSources, @@ -261,18 +260,10 @@ public class HydrologyPlannerCrossTileCaveAdmissionIntegrationTest { 20, 2, 5, - 4, - 8, 12, 1.5D, - 4, - 10, - true, - 96, - 10 - , HydrologyPlannerSettings.Banks.defaults()), - new HydrologyPlannerSettings.Hydraulics(12, 40, 1, 3, 8, 4), + new HydrologyPlannerSettings.Hydraulics(4), new HydrologyPlannerSettings.Underground( true, undergroundSources, @@ -343,16 +334,8 @@ public class HydrologyPlannerCrossTileCaveAdmissionIntegrationTest { baseSurface.maximumWidth(), baseSurface.minimumDepth(), baseSurface.maximumDepth(), - baseSurface.minimumSurfaceInset(), - baseSurface.maximumSurfaceInset(), 64, baseSurface.shoreWidth(), - baseSurface.minimumTerrainBlendWidth(), - baseSurface.maximumTerrainBlendWidth(), - baseSurface.ridgeTunnelsEnabled(), - baseSurface.maximumRidgeTunnelLength(), - baseSurface.ridgeTunnelHeadroom() - , HydrologyPlannerSettings.Banks.defaults()), base.hydraulics(), new HydrologyPlannerSettings.Underground( diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerGlobalInvariantTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerGlobalInvariantTest.java index 5d3c8702c..90fb3a53a 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerGlobalInvariantTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerGlobalInvariantTest.java @@ -639,33 +639,31 @@ public class HydrologyPlannerGlobalInvariantTest { boolean undergroundEnabled, List deepFluids ) { - return settingsWithRefinement( + return plannerSettings( surfaceSources, undergroundSources, surfaceEnabled, undergroundEnabled, - deepFluids, - 4 + deepFluids ); } - private HydrologyPlannerSettings settingsWithRefinement( + private HydrologyPlannerSettings plannerSettings( HydrologyPlannerSettings.Source surfaceSources, HydrologyPlannerSettings.Source undergroundSources, boolean surfaceEnabled, boolean undergroundEnabled, - List deepFluids, - int refinementSpacing + List deepFluids ) { return new HydrologyPlannerSettings( 63, new HydrologyPlannerSettings.Routing( TILE_SIZE, 16, - refinementSpacing, 512, 256, - new HydrologyPlannerSettings.Branching(16, 8), + 16, + 8, 0.5D, 12D, 0.5D, @@ -678,18 +676,10 @@ public class HydrologyPlannerGlobalInvariantTest { 4, 1, 2, - 4, - 8, 128, 1D, - 1, - 2, - false, - 0, - 0 - , HydrologyPlannerSettings.Banks.defaults()), - new HydrologyPlannerSettings.Hydraulics(8, 16, 1, 3, 8, 4), + new HydrologyPlannerSettings.Hydraulics(4), new HydrologyPlannerSettings.Underground( undergroundEnabled, undergroundSources, @@ -724,13 +714,12 @@ public class HydrologyPlannerGlobalInvariantTest { boolean surfaceEnabled, boolean undergroundEnabled ) { - HydrologyPlannerSettings base = settingsWithRefinement( + HydrologyPlannerSettings base = plannerSettings( surfaceSources, undergroundSources, surfaceEnabled, undergroundEnabled, - List.of(), - 8 + List.of() ); HydrologyPlannerSettings.Surface surface = base.surface(); HydrologyPlannerSettings.Underground underground = base.underground(); @@ -744,16 +733,8 @@ public class HydrologyPlannerGlobalInvariantTest { surface.minimumWidth(), surface.minimumDepth(), surface.maximumDepth(), - surface.minimumSurfaceInset(), - surface.maximumSurfaceInset(), surface.maximumIncision(), surface.shoreWidth(), - surface.minimumTerrainBlendWidth(), - surface.maximumTerrainBlendWidth(), - surface.ridgeTunnelsEnabled(), - surface.maximumRidgeTunnelLength(), - surface.ridgeTunnelHeadroom() - , HydrologyPlannerSettings.Banks.defaults()), base.hydraulics(), new HydrologyPlannerSettings.Underground( diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerRoutingSamplerTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerRoutingSamplerTest.java index de95e06a8..0eb41a06b 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerRoutingSamplerTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerRoutingSamplerTest.java @@ -38,7 +38,7 @@ public class HydrologyPlannerRoutingSamplerTest { settings, detailSampler, routingSampler, - HydrologyGeometrySampler.deterministic(17L, detailSampler), + HydrologyGeometrySampler.deterministic(detailSampler), -4096, footprint -> new HydrologyTerrainCaveVoxelView(detailSampler, settings.seaLevel(), -4096, 4096) ); @@ -70,7 +70,7 @@ public class HydrologyPlannerRoutingSamplerTest { settings, detailSampler, routingSampler, - HydrologyGeometrySampler.deterministic(19L, detailSampler), + HydrologyGeometrySampler.deterministic(detailSampler), -4096, footprint -> new HydrologyTerrainCaveVoxelView(detailSampler, settings.seaLevel(), -4096, 4096) ); @@ -108,7 +108,7 @@ public class HydrologyPlannerRoutingSamplerTest { settings, detailSampler, routingSampler, - HydrologyGeometrySampler.deterministic(23L, detailSampler), + HydrologyGeometrySampler.deterministic(detailSampler), -4096, footprint -> new HydrologyTerrainCaveVoxelView(detailSampler, settings.seaLevel(), -4096, 4096) ); diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerSettingsTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerSettingsTest.java index b2329e2e9..1c1ae42cd 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerSettingsTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerSettingsTest.java @@ -11,11 +11,19 @@ import static org.junit.Assert.assertTrue; public class HydrologyPlannerSettingsTest { @Test - public void branchingUsesIndependentSurfaceAndUndergroundCourseFloors() { - HydrologyPlannerSettings.Branching branching = new HydrologyPlannerSettings.Branching(384, 192); + public void routingUsesIndependentSurfaceAndUndergroundCourseFloors() { + HydrologyPlannerSettings.Routing routing = HydrologyPlannerSettings.defaults().routing(); - assertEquals(384, branching.minimumCourseLength(true)); - assertEquals(192, branching.minimumCourseLength(false)); + assertEquals(384, routing.minimumCourseLength(true)); + assertEquals(192, routing.minimumCourseLength(false)); + } + + @Test + public void refinementSpacingIsDerivedFromTheSampleLattice() { + assertEquals(4, HydrologyPlannerSettings.Routing.refinementSpacing(64)); + assertEquals(2, HydrologyPlannerSettings.Routing.refinementSpacing(50)); + assertEquals(1, HydrologyPlannerSettings.Routing.refinementSpacing(45)); + assertEquals(4, HydrologyPlannerSettings.defaults().routing().refinementSpacing()); } @Test @@ -63,10 +71,10 @@ public class HydrologyPlannerSettingsTest { HydrologyPlannerSettings.Routing routing = new HydrologyPlannerSettings.Routing( 512, 512, - 64, 4, 256, - new HydrologyPlannerSettings.Branching(0, 0), + 0, + 0, 0D, 0D, 0D, @@ -80,25 +88,10 @@ public class HydrologyPlannerSettingsTest { 1, 1, 1, - 1, - 1, 0, 1D, - 0, - 0, - false, - 0, - 0 - , HydrologyPlannerSettings.Banks.defaults()); - HydrologyPlannerSettings.Hydraulics hydraulics = new HydrologyPlannerSettings.Hydraulics( - 8, - 8, - 0, - 0, - 1, - 1 - ); + HydrologyPlannerSettings.Hydraulics hydraulics = new HydrologyPlannerSettings.Hydraulics(1); HydrologyPlannerSettings.Grotto grotto = new HydrologyPlannerSettings.Grotto(true, 1, 1, 1, 1); HydrologyPlannerSettings.Outlets outlets = new HydrologyPlannerSettings.Outlets( true, @@ -168,20 +161,13 @@ public class HydrologyPlannerSettingsTest { assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Surface( true, new HydrologyPlannerSettings.Source(true, 1D, 0, 0, 1, 0), - 1, 1, 1, 1, 1, 1, 0, 1D, 0, 0, false, 0, 0, + 1, 1, 1, 1, 0, 1D, null)); } @Test - public void inconsistentHydraulicThresholdsAreRejected() { - assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Hydraulics( - 8, - 16, - 0, - 2, - 2, - 2 - )); + public void nonPositiveWaterfallThresholdsAreRejected() { + assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Hydraulics(0)); } @Test diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerTest.java index 9dbdc5818..07b110800 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyPlannerTest.java @@ -522,35 +522,14 @@ public class HydrologyPlannerTest { assertFalse("diagnostics=" + tile.diagnosticCandidates(), surfaceCourses(tile).isEmpty()); boolean aggregateTransition = false; for (RiverCourse course : surfaceCourses(tile)) { - double recovery = Double.POSITIVE_INFINITY; - boolean transitionActive = false; for (HydraulicSegment segment : course.segments()) { - boolean surfaceDrop = segment.type().isSurface() && segment.type().isDrop(); - if (surfaceDrop) { - if (!transitionActive) { - assertTrue( - "segments=" + course.segments(), - !Double.isFinite(recovery) - || recovery >= settings.hydraulics().minimumTargetPoolLength() - ); - } - recovery = 0D; - transitionActive = !segment.receivingPool(); - if (segment.type() == HydrologyFeatureType.WATERFALL - && segment.drop() >= settings.hydraulics().waterfallMinimumDrop()) { - aggregateTransition = true; - } + if (segment.drop() < settings.hydraulics().waterfallMinimumDrop()) { continue; } - if (segment.type() == HydrologyFeatureType.UNDERGROUND_DROP - && segment.drop() >= settings.hydraulics().waterfallMinimumDrop()) { + if (segment.type() == HydrologyFeatureType.WATERFALL + || segment.type() == HydrologyFeatureType.UNDERGROUND_DROP) { aggregateTransition = true; } - if (transitionActive && segment.receivingPool()) { - transitionActive = false; - } else if (!transitionActive && Double.isFinite(recovery)) { - recovery += segmentLength(segment); - } } } assertTrue("courses=" + surfaceCourses(tile), aggregateTransition); @@ -597,10 +576,8 @@ public class HydrologyPlannerTest { new HydrologyPlannerSettings.Routing( routing.tileSize(), routing.sampleSpacing(), - routing.refinementSpacing(), routing.maximumRouteNodes(), - routing.maximumRouteLength(), - new HydrologyPlannerSettings.Branching(96, 0), + routing.maximumRouteLength(), 96, 0, routing.valleyPreference(), routing.uphillPenalty(), routing.slopePenalty(), @@ -792,7 +769,7 @@ public class HydrologyPlannerTest { 333L, settings, coast, - HydrologyGeometrySampler.deterministic(333L, coast), + HydrologyGeometrySampler.deterministic(coast), -4096, footprint -> solidCaveView() ).plan(TILE); @@ -1134,16 +1111,8 @@ public class HydrologyPlannerTest { base.surface().maximumWidth(), base.surface().minimumDepth(), 8, - base.surface().minimumSurfaceInset(), - base.surface().maximumSurfaceInset(), 128, base.surface().shoreWidth(), - base.surface().minimumTerrainBlendWidth(), - base.surface().maximumTerrainBlendWidth(), - base.surface().ridgeTunnelsEnabled(), - base.surface().maximumRidgeTunnelLength(), - base.surface().ridgeTunnelHeadroom() - , HydrologyPlannerSettings.Banks.defaults()); HydrologyPlannerSettings shaped = new HydrologyPlannerSettings( base.seaLevel(), @@ -1432,16 +1401,8 @@ public class HydrologyPlannerTest { surface.maximumWidth(), surface.minimumDepth(), surface.maximumDepth(), - surface.minimumSurfaceInset(), - surface.maximumSurfaceInset(), surface.maximumIncision(), surface.shoreWidth(), - surface.minimumTerrainBlendWidth(), - surface.maximumTerrainBlendWidth(), - surface.ridgeTunnelsEnabled(), - 32, - surface.ridgeTunnelHeadroom() - , HydrologyPlannerSettings.Banks.defaults()), base.hydraulics(), base.underground(), @@ -1772,8 +1733,7 @@ public class HydrologyPlannerTest { ); return new HydrologyPlannerSettings( 63, - new HydrologyPlannerSettings.Routing(128, 16, 4, 512, 256, - new HydrologyPlannerSettings.Branching(0, 0), 0.5D, 12D, 0.5D, 0.1D), + new HydrologyPlannerSettings.Routing(128, 16, 512, 256, 0, 0, 0.5D, 12D, 0.5D, 0.1D), new HydrologyPlannerSettings.Surface( surfaceDensity > 0D || surfaceSources.maximumPerTile() > 0, surfaceSources, @@ -1781,18 +1741,10 @@ public class HydrologyPlannerTest { 18, 2, 4, - 4, - 8, 10, 1.5D, - 4, - 8, - true, - 64, - 8 - , HydrologyPlannerSettings.Banks.defaults()), - new HydrologyPlannerSettings.Hydraulics(12, 28, 1, 3, 8, 4), + new HydrologyPlannerSettings.Hydraulics(4), new HydrologyPlannerSettings.Underground( undergroundDensity > 0D, undergroundSources, @@ -1842,10 +1794,8 @@ public class HydrologyPlannerTest { new HydrologyPlannerSettings.Routing( 512, 64, - 4, 1024, - 2048, - new HydrologyPlannerSettings.Branching(64, 32), + 2048, 64, 32, 1.5D, 24D, 2D, @@ -1858,18 +1808,10 @@ public class HydrologyPlannerTest { surface.maximumWidth(), surface.minimumDepth(), surface.maximumDepth(), - surface.minimumSurfaceInset(), - surface.maximumSurfaceInset(), 96, surface.shoreWidth(), - surface.minimumTerrainBlendWidth(), - surface.maximumTerrainBlendWidth(), - surface.ridgeTunnelsEnabled(), - 192, - surface.ridgeTunnelHeadroom() - , HydrologyPlannerSettings.Banks.defaults()), - new HydrologyPlannerSettings.Hydraulics(12, 28, 1, 4, 12, 5), + new HydrologyPlannerSettings.Hydraulics(5), base.underground(), new HydrologyPlannerSettings.Outlets( true, @@ -2172,16 +2114,8 @@ public class HydrologyPlannerTest { surface.maximumWidth(), surface.minimumDepth(), surface.maximumDepth(), - surface.minimumSurfaceInset(), - surface.maximumSurfaceInset(), surface.maximumIncision(), surface.shoreWidth(), - surface.minimumTerrainBlendWidth(), - surface.maximumTerrainBlendWidth(), - surface.ridgeTunnelsEnabled(), - surface.maximumRidgeTunnelLength(), - surface.ridgeTunnelHeadroom() - , HydrologyPlannerSettings.Banks.defaults()), settings.hydraulics(), settings.underground(), 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 da020fc76..73adc6e59 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 @@ -192,8 +192,7 @@ public class HydrologyStyledGeometryTest { ); return new HydrologyPlannerSettings( 63, - new HydrologyPlannerSettings.Routing(128, 16, 4, 512, 256, - new HydrologyPlannerSettings.Branching(16, 8), 0.5D, 12D, 0.5D, 0.1D), + new HydrologyPlannerSettings.Routing(128, 16, 512, 256, 16, 8, 0.5D, 12D, 0.5D, 0.1D), new HydrologyPlannerSettings.Surface( surfaceDensity > 0D, surfaceSources, @@ -201,18 +200,10 @@ public class HydrologyStyledGeometryTest { 18, 2, 4, - 4, - 8, 10, 1.5D, - 4, - 8, - true, - 96, - 8 - , HydrologyPlannerSettings.Banks.defaults()), - new HydrologyPlannerSettings.Hydraulics(12, 28, 1, 3, 8, 4), + new HydrologyPlannerSettings.Hydraulics(4), new HydrologyPlannerSettings.Underground( undergroundDensity > 0D, undergroundSources, diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyTileCacheTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyTileCacheTest.java index ae59ec1aa..0f685df58 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyTileCacheTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/HydrologyTileCacheTest.java @@ -243,10 +243,10 @@ public class HydrologyTileCacheTest { ); return new HydrologyPlannerSettings( 63, - new HydrologyPlannerSettings.Routing(64, 16, 4, 128, 64, - new HydrologyPlannerSettings.Branching(0, 0), 0D, 0D, 0D, 0D), - new HydrologyPlannerSettings.Surface(false, disabled, 2, 4, 1, 2, 2, 4, 4, 1D, 2, 4, false, 0, 0, HydrologyPlannerSettings.Banks.defaults()), - new HydrologyPlannerSettings.Hydraulics(8, 16, 1, 2, 8, 3), + new HydrologyPlannerSettings.Routing(64, 16, 128, 64, 0, 0, 0D, 0D, 0D, 0D), + new HydrologyPlannerSettings.Surface(false, disabled, 2, 4, 1, 2, 4, 1D, + HydrologyPlannerSettings.Banks.defaults()), + new HydrologyPlannerSettings.Hydraulics(3), new HydrologyPlannerSettings.Underground(false, disabled, -32, 32, 2, 4, 1, 2, 3, 4, false), new HydrologyPlannerSettings.Outlets( false, @@ -282,10 +282,9 @@ public class HydrologyTileCacheTest { ); return new HydrologyPlannerSettings( 63, - new HydrologyPlannerSettings.Routing(64, 16, 4, 128, 96, - new HydrologyPlannerSettings.Branching(16, 8), 0.5D, 12D, 0.5D, 0.1D), - new HydrologyPlannerSettings.Surface(true, surfaceSources, 4, 8, 2, 3, 4, 8, 20, 1.5D, 2, 4, true, 96, 8, tileBoundedBanks()), - new HydrologyPlannerSettings.Hydraulics(8, 16, 1, 2, 8, 3), + new HydrologyPlannerSettings.Routing(64, 16, 128, 96, 16, 8, 0.5D, 12D, 0.5D, 0.1D), + new HydrologyPlannerSettings.Surface(true, surfaceSources, 4, 8, 2, 3, 20, 1.5D, tileBoundedBanks()), + new HydrologyPlannerSettings.Hydraulics(3), new HydrologyPlannerSettings.Underground(false, disabled, -32, 32, 2, 4, 1, 2, 3, 4, false), new HydrologyPlannerSettings.Outlets( true, diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/runtime/IrisHydrologyRuntimeSettingsTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/runtime/IrisHydrologyRuntimeSettingsTest.java index 9e9d5bacd..8adcdcaad 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/runtime/IrisHydrologyRuntimeSettingsTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/runtime/IrisHydrologyRuntimeSettingsTest.java @@ -114,14 +114,14 @@ public class IrisHydrologyRuntimeSettingsTest { assertEquals(8, settings.hydraulics().waterfallMinimumDrop()); assertEquals(96, settings.outlets().mouthLevelingDistance()); assertEquals(4, settings.routing().refinementSpacing()); - assertEquals(500, settings.routing().branching().minimumSurfaceCourseLength()); - assertEquals(250, settings.routing().branching().minimumUndergroundCourseLength()); + assertEquals(500, settings.routing().minimumSurfaceCourseLength()); + assertEquals(250, settings.routing().minimumUndergroundCourseLength()); assertEquals(2.5D, settings.routing().valleyPreference(), 0D); assertEquals(30D, settings.routing().uphillPenalty(), 0D); assertEquals(3D, settings.routing().slopePenalty(), 0D); assertEquals(0.4D, settings.routing().confluenceAttraction(), 0D); - assertEquals(2, IrisHydrologyRuntime.refinementSpacing(50)); - assertEquals(1, IrisHydrologyRuntime.refinementSpacing(45)); + assertEquals(2, HydrologyPlannerSettings.Routing.refinementSpacing(50)); + assertEquals(1, HydrologyPlannerSettings.Routing.refinementSpacing(45)); } @Test diff --git a/core/src/test/java/art/arcane/iris/engine/hydrology/surface/ErosionFieldCompilerTest.java b/core/src/test/java/art/arcane/iris/engine/hydrology/surface/ErosionFieldCompilerTest.java index 4d2def68b..6d598b5ba 100644 --- a/core/src/test/java/art/arcane/iris/engine/hydrology/surface/ErosionFieldCompilerTest.java +++ b/core/src/test/java/art/arcane/iris/engine/hydrology/surface/ErosionFieldCompilerTest.java @@ -172,7 +172,7 @@ public class ErosionFieldCompilerTest { return new HydrologyPlannerSettings.Surface( true, defaults.sources(), - 4, 8, 2, 4, 1, 1, 10, 1.5D, 4, 32, false, 1, 1, + 4, 8, 2, 4, 10, 1.5D, new HydrologyPlannerSettings.Banks(1, 1, 3D, 4, 32, 0D, 16, 2, 6, 1.6D, true) ); } diff --git a/core/src/test/java/art/arcane/iris/engine/mantle/components/MantleHydrologyComponentTest.java b/core/src/test/java/art/arcane/iris/engine/mantle/components/MantleHydrologyComponentTest.java index 7db4712b8..d279bbbae 100644 --- a/core/src/test/java/art/arcane/iris/engine/mantle/components/MantleHydrologyComponentTest.java +++ b/core/src/test/java/art/arcane/iris/engine/mantle/components/MantleHydrologyComponentTest.java @@ -746,10 +746,10 @@ public class MantleHydrologyComponentTest { } @Test - public void sameCourseSurfaceFluidOwnsAChangedRidgeBoundary() { + public void sameCourseSurfaceFluidOwnsAChangedCaveBoundary() { long courseId = 33L; HydrologyColumnLayer ridge = layerForCourse( - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 34L, courseId, 10L, @@ -1115,7 +1115,7 @@ public class MantleHydrologyComponentTest { "river" ); HydrologyColumnLayer cave = layer( - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 72L, 15L, 20, @@ -1158,7 +1158,7 @@ public class MantleHydrologyComponentTest { "river" ); HydrologyColumnLayer cave = layer( - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 72L, 15L, 20, @@ -1192,7 +1192,7 @@ public class MantleHydrologyComponentTest { "river" ); HydrologyColumnLayer cave = layer( - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 74L, 17L, 20, @@ -1245,7 +1245,7 @@ public class MantleHydrologyComponentTest { "lava" ); HydrologyColumnLayer cave = layer( - HydrologyFeatureType.RIDGE_BORE, + HydrologyFeatureType.UNDERGROUND_POOL, 76L, 19L, 17, @@ -1530,8 +1530,7 @@ public class MantleHydrologyComponentTest { ); return new HydrologyPlannerSettings( 63, - new HydrologyPlannerSettings.Routing(128, 16, 8, 512, 256, - new HydrologyPlannerSettings.Branching(16, 8), 0.5D, 12D, 0.5D, 0.1D), + new HydrologyPlannerSettings.Routing(128, 16, 512, 256, 16, 8, 0.5D, 12D, 0.5D, 0.1D), new HydrologyPlannerSettings.Surface( true, surfaceSources, @@ -1539,18 +1538,10 @@ public class MantleHydrologyComponentTest { 2, 2, 2, - 4, - 8, 128, 0D, - 0, - 0, - false, - 0, - 0 - , HydrologyPlannerSettings.Banks.defaults()), - new HydrologyPlannerSettings.Hydraulics(8, 16, 1, 3, 8, 4), + new HydrologyPlannerSettings.Hydraulics(4), new HydrologyPlannerSettings.Underground( false, disabledSources, diff --git a/probe/src/main/java/art/arcane/iris/probe/HydrologyPackProbe.java b/probe/src/main/java/art/arcane/iris/probe/HydrologyPackProbe.java index 84f727809..fc9fa53b1 100644 --- a/probe/src/main/java/art/arcane/iris/probe/HydrologyPackProbe.java +++ b/probe/src/main/java/art/arcane/iris/probe/HydrologyPackProbe.java @@ -1576,7 +1576,7 @@ public final class HydrologyPackProbe { publishedMorphology.observe( tile, settings.surface().minimumWidth(), - settings.routing().branching().minimumSurfaceCourseLength() + settings.routing().minimumSurfaceCourseLength() ); HydrologyPlannerSettings.Routing routing = settings.routing(); int sampleSpacing = routing.sampleSpacing(); @@ -1704,7 +1704,7 @@ public final class HydrologyPackProbe { List incisions = channelIncisions( tile.footprint(), scopedCourseIds, - settings.surface().minimumSurfaceInset(), + settings.surface().banks().inset(), settings.surface().maximumIncision() ); for (ChannelIncision incision : incisions) { diff --git a/probe/src/main/java/art/arcane/iris/probe/HydrologyPlannerProbe.java b/probe/src/main/java/art/arcane/iris/probe/HydrologyPlannerProbe.java index 100d418a7..75323ad1b 100644 --- a/probe/src/main/java/art/arcane/iris/probe/HydrologyPlannerProbe.java +++ b/probe/src/main/java/art/arcane/iris/probe/HydrologyPlannerProbe.java @@ -220,10 +220,10 @@ public final class HydrologyPlannerProbe { new HydrologyPlannerSettings.Routing( 128, 16, - 4, 512, 256, - new HydrologyPlannerSettings.Branching(32, 16), + 32, + 16, 0.5D, 12D, 0.5D, @@ -236,18 +236,11 @@ public final class HydrologyPlannerProbe { 18, 2, 4, - 4, - 8, 10, 1.5D, - 4, - 8, - true, - 96, - 8, HydrologyPlannerSettings.Banks.defaults() ), - new HydrologyPlannerSettings.Hydraulics(12, 28, 1, 3, 8, 4), + new HydrologyPlannerSettings.Hydraulics(4), new HydrologyPlannerSettings.Underground( undergroundDensity > 0D, undergroundSources, @@ -438,10 +431,10 @@ public final class HydrologyPlannerProbe { new HydrologyPlannerSettings.Routing( 256, 16, - 4, 1_089, 256, - new HydrologyPlannerSettings.Branching(32, 16), + 32, + 16, 0.5D, 12D, 0.5D, @@ -454,18 +447,11 @@ public final class HydrologyPlannerProbe { 20, 2, 5, - 4, - 8, 12, 1.5D, - 4, - 10, - true, - 96, - 10, HydrologyPlannerSettings.Banks.defaults() ), - new HydrologyPlannerSettings.Hydraulics(12, 40, 1, 3, 8, 4), + new HydrologyPlannerSettings.Hydraulics(4), new HydrologyPlannerSettings.Underground( true, undergroundSources, diff --git a/probe/src/test/java/art/arcane/iris/probe/HydrologyPlannerProbeTest.java b/probe/src/test/java/art/arcane/iris/probe/HydrologyPlannerProbeTest.java index b6131d6b8..89ace6867 100644 --- a/probe/src/test/java/art/arcane/iris/probe/HydrologyPlannerProbeTest.java +++ b/probe/src/test/java/art/arcane/iris/probe/HydrologyPlannerProbeTest.java @@ -23,7 +23,7 @@ public final class HydrologyPlannerProbeTest { } @Test - public void requiredCoverageExcludesTheRemovedRidgeBore() { + public void requiredCoverageExcludesTheUnproducedRidgeBore() { assertFalse(HydrologyPlannerProbe.REQUIRED_FEATURE_TYPES.contains(HydrologyFeatureType.RIDGE_BORE)); assertEquals( EnumSet.complementOf(EnumSet.of(HydrologyFeatureType.RIDGE_BORE)),