mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-28 06:50:02 +00:00
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:
1 parent
639786537b
commit
896e58e212
24 files changed
+254
-1360
No files matched your search
@@ -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,
|
||||
|
||||
+7
-418
@@ -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,
|
||||
|
||||
@@ -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,10 +427,7 @@ 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));
|
||||
RoutingPlan fallbackRouting = requireOrganicSurface(buildRouting(grid, List.of(fallbackOutlet)));
|
||||
HashMap<HydrologyTileKey, SourceRoutingContext> fallbackContexts = new HashMap<>();
|
||||
fallbackContexts.put(key, new SourceRoutingContext(
|
||||
grid,
|
||||
@@ -468,10 +464,6 @@ public final class HydrologyPlanner {
|
||||
fallbackSelected = true;
|
||||
break;
|
||||
}
|
||||
if (fallbackSelected) {
|
||||
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<OutletCandidate> 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<RoutingQueueEntry> 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<SourceCandidate> 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<RouteCandidate> 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<Long, RouteCandidate> 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<HydrologyPoint> solveTerrainRoute(
|
||||
List<List<RouteCandidate>> layers,
|
||||
double targetStepLength,
|
||||
boolean containedSurface
|
||||
double targetStepLength
|
||||
) {
|
||||
ArrayList<List<RouteCandidate>> availableLayers = new ArrayList<>(layers);
|
||||
HashSet<TerrainTransitionKey> rejectedTransitions = new HashSet<>();
|
||||
@@ -3517,19 +3337,16 @@ 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;
|
||||
}
|
||||
}
|
||||
int pitRemoval = removeTerrainPits(availableLayers, route);
|
||||
if (pitRemoval < 0) {
|
||||
return List.of();
|
||||
@@ -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<List<RouteCandidate>> layers,
|
||||
double targetStepLength,
|
||||
boolean containedSurface,
|
||||
Set<TerrainTransitionKey> 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<List<RouteCandidate>> 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<List<RouteCandidate>> 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<List<RouteCandidate>> layers,
|
||||
boolean containedSurface,
|
||||
double[][] localPenalties,
|
||||
double[][][] transitionCosts,
|
||||
double targetStepLength
|
||||
@@ -3838,14 +3631,13 @@ public final class HydrologyPlanner {
|
||||
if (candidate.terrainResolved()) {
|
||||
continue;
|
||||
}
|
||||
RouteCandidate resolved = resolveRouteCandidate(candidate, containedSurface);
|
||||
RouteCandidate resolved = resolveRouteCandidate(candidate);
|
||||
ArrayList<RouteCandidate> 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])) {
|
||||
@@ -3860,7 +3652,6 @@ public final class HydrologyPlanner {
|
||||
);
|
||||
evaluated = true;
|
||||
}
|
||||
}
|
||||
for (int layerIndex = 1; layerIndex < route.length; layerIndex++) {
|
||||
int previousIndex = indices[layerIndex - 1];
|
||||
int currentIndex = indices[layerIndex];
|
||||
@@ -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<HydrologyPoint> smoothTerrainRoute(List<HydrologyPoint> route) {
|
||||
List<HydrologyPoint> current = List.copyOf(route);
|
||||
List<HydrologyPoint> 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<HydrologyPoint> 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<HydrologyPoint> crossing = rasterLine(start, end);
|
||||
if (crossing.size() < 3) {
|
||||
@@ -4506,7 +4209,7 @@ public final class HydrologyPlanner {
|
||||
|| routedLength / directLength > SURFACE_MAXIMUM_SINUOSITY) {
|
||||
return false;
|
||||
}
|
||||
List<HydrologyPoint> sampled = resampleContainedSurfaceCenterline(
|
||||
List<HydrologyPoint> sampled = resampleRouteCenterline(
|
||||
centerline,
|
||||
Math.max(16D, settings.routing().refinementSpacing() * 4D)
|
||||
);
|
||||
@@ -4587,7 +4290,7 @@ public final class HydrologyPlanner {
|
||||
return length;
|
||||
}
|
||||
|
||||
private List<HydrologyPoint> resampleContainedSurfaceCenterline(
|
||||
private List<HydrologyPoint> resampleRouteCenterline(
|
||||
List<HydrologyPoint> points,
|
||||
double spacing
|
||||
) {
|
||||
@@ -5540,8 +5243,7 @@ public final class HydrologyPlanner {
|
||||
List<HydrologyPoint> 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<HydrologyPoint> surfaceCourseCenterline(
|
||||
SampledGrid grid,
|
||||
List<HydrologyPoint> guide,
|
||||
long geometryId,
|
||||
boolean containedSurface
|
||||
) {
|
||||
return surfaceCourseCenterline(grid, guide, geometryId, containedSurface, !containedSurface);
|
||||
}
|
||||
|
||||
private List<HydrologyPoint> surfaceCourseCenterline(
|
||||
SampledGrid grid,
|
||||
List<HydrologyPoint> 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<List<RouteCandidate>> 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<RouteCandidate> candidates = lazyCandidates
|
||||
? unresolvedSurfaceRouteCandidates(
|
||||
List<RouteCandidate> 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<HydrologyPoint> smoothContainedSurfaceRoute(List<HydrologyPoint> route) {
|
||||
List<HydrologyPoint> current = List.copyOf(route);
|
||||
List<HydrologyPoint> 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<HydrologyPoint> accepted = null;
|
||||
for (double strength : strengths) {
|
||||
List<HydrologyPoint> 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<HydrologyPoint> smoothContainedSurfaceRoutePass(
|
||||
List<HydrologyPoint> route,
|
||||
double strength
|
||||
) {
|
||||
ArrayList<HydrologyPoint> 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<HydrologyPoint> route) {
|
||||
List<HydrologyPoint> sampled = resampleContainedSurfaceCenterline(
|
||||
private double routeTurnScore(List<HydrologyPoint> route) {
|
||||
List<HydrologyPoint> 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<HydrologyPoint> 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<RoutingQueueEntry> {
|
||||
@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<OutletCandidate> outlets,
|
||||
boolean containedSurface,
|
||||
boolean organicSurfaceRequired
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
// 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 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.");
|
||||
}
|
||||
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()
|
||||
|
||||
+6
-35
@@ -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.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);
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -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,
|
||||
|
||||
+64
-129
@@ -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,
|
||||
|
||||
+2
-19
@@ -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(
|
||||
|
||||
+9
-28
@@ -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(
|
||||
|
||||
+3
-3
@@ -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)
|
||||
);
|
||||
|
||||
+18
-32
@@ -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(),
|
||||
|
||||
+2
-11
@@ -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,
|
||||
|
||||
+4
-4
@@ -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
|
||||
|
||||
+1
-1
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
+8
-17
@@ -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)),
|
||||
|
||||
Reference in new issue
Block a user