Delete the dead v1 hydrology settings surface

- Surface inset, terrain blend and ridge tunnel settings gone; Hydraulics keeps only waterfallMinimumDrop
- Branching folded into Routing; refinementSpacing derived from the sample lattice
- Contained surface routing branch, surface incision helpers and 13 unreachable compiler members deleted
This commit is contained in:
Brian Neumann-Fopiano
2026-09-01 23:08:47 -04:00
parent 639786537b
commit 896e58e212
24 changed files with 305 additions and 1411 deletions
@@ -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,
@@ -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,
@@ -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<HydrologyPoint> raster = surfaceCourseCenterline(course);
if (raster.size() < 2) {
return false;
}
HashSet<Long> 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<HydrologyPoint> 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<HydrologyPoint> surfaceCourseCenterline(RiverCourse course) {
ArrayList<HydrologyPoint> 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<HydrologyPoint> rasterCenterline(List<HydrologyPoint> points) {
ArrayList<HydrologyPoint> 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<Long> 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<Long> cells, List<HydrologyPoint> 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<Integer> 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<Long> cells,
List<HydrologyPoint> 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<Long> 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<Long> 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) {
@@ -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,
File diff suppressed because it is too large Load Diff
@@ -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.");
}
}
}
@@ -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()
@@ -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<Double> surfaceWidth = styledStream(context, channel.getWidth(), SURFACE_WIDTH_SALT);
ProceduralStream<Double> surfaceDepth = styledStream(context, channel.getDepth(), SURFACE_DEPTH_SALT);
ProceduralStream<Double> surfaceInset = ProceduralStream.ofDouble((x, z) -> (double) channel.getInset());
ProceduralStream<Double> surfaceBlend = ProceduralStream.ofDouble((x, z) -> (double) banks.getMaximumBlendWidth());
ProceduralStream<Double> targetPool = ProceduralStream.ofDouble((x, z) -> 128D);
ProceduralStream<Double> undergroundLevel = styledStream(
context,
underground.getFluidLevel(),
@@ -589,9 +585,7 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
ProceduralStream<Double> 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);
}
@@ -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,
@@ -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
@@ -153,7 +153,7 @@ public class HydrologyModelTest {
HydraulicSegment containedLip = new HydraulicSegment(
4L,
9L,
HydrologyFeatureType.RIDGE_BORE,
HydrologyFeatureType.SURFACE_POOL,
80,
80,
4,
@@ -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(
@@ -639,33 +639,31 @@ public class HydrologyPlannerGlobalInvariantTest {
boolean undergroundEnabled,
List<HydrologyPlannerSettings.DeepFluid> 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<HydrologyPlannerSettings.DeepFluid> deepFluids,
int refinementSpacing
List<HydrologyPlannerSettings.DeepFluid> 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(
@@ -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)
);
@@ -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
@@ -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(),
@@ -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,
@@ -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,
@@ -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
@@ -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)
);
}
@@ -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,
@@ -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<ChannelIncision> incisions = channelIncisions(
tile.footprint(),
scopedCourseIds,
settings.surface().minimumSurfaceInset(),
settings.surface().banks().inset(),
settings.surface().maximumIncision()
);
for (ChannelIncision incision : incisions) {
@@ -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,
@@ -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)),