Spring pools at headwaters and ring-ordered feature search

- channel.springWidthRatio / springLength open every surface river in a pool that shrinks where the ground falls away
- Banks settings keep structural invariants only; authoring bounds stay in the validator
- Feature searches walk tiles outward and stop early; find and goto report progress per ring
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-01 23:13:47 -04:00
1 parent 896e58e212
commit d1c73025a0
12 files changed
+145 -49

No files matched your search

@@ -161,7 +161,9 @@ public class CommandFind implements DirectorExecutor {
query.profileKey(), query.profileKey(),
origin.getBlockX(), origin.getBlockX(),
origin.getBlockZ(), origin.getBlockZ(),
maximumDistance maximumDistance,
(int visited) -> sendStructureMessage(target, commandSender,
C.GRAY + "Searched " + visited + " hydrology tiles for " + type + "...")
).orElse(null); ).orElse(null);
if (feature == null) { if (feature == null) {
sendStructureMessage(target, commandSender, sendStructureMessage(target, commandSender,
@@ -176,7 +176,9 @@ final class ModdedLocateCommands {
MinecraftServer server = source.getServer(); MinecraftServer server = source.getServer();
IrisModdedCommands.ok(source, "Searching accepted hydrology plans for " + type + "..."); IrisModdedCommands.ok(source, "Searching accepted hydrology plans for " + type + "...");
MultiBurst.burst.completeValueAsync(() -> runtime.nearestFeature( MultiBurst.burst.completeValueAsync(() -> runtime.nearestFeature(
query.types(), query.profileKey(), originX, originZ, maximumDistance).orElse(null)) query.types(), query.profileKey(), originX, originZ, maximumDistance,
(int visited) -> server.execute(() -> IrisModdedCommands.ok(source,
"Searched " + visited + " hydrology tiles for " + type + "..."))).orElse(null))
.whenComplete((HydrologyFeatureRef feature, Throwable error) -> server.execute(() -> { .whenComplete((HydrologyFeatureRef feature, Throwable error) -> server.execute(() -> {
if (error != null) { if (error != null) {
ModdedIrisLog.error("Hydrology locate failed for {}", type, error); ModdedIrisLog.error("Hydrology locate failed for {}", type, error);
@@ -365,6 +365,8 @@ final class PackHydrologyValidator {
validateRange(path, channel, "depth", 1D, 64D, 2D, 4D, errors); validateRange(path, channel, "depth", 1D, 64D, 2D, 4D, errors);
PackJsonFieldChecks.validateOptionalIntegerRange(path, channel, "inset", 0, 3, errors); PackJsonFieldChecks.validateOptionalIntegerRange(path, channel, "inset", 0, 3, errors);
PackJsonFieldChecks.validateOptionalIntegerRange(path, channel, "maximumIncision", 1, 32, errors); PackJsonFieldChecks.validateOptionalIntegerRange(path, channel, "maximumIncision", 1, 32, errors);
PackJsonFieldChecks.validateOptionalDoubleRange(path, channel, "springWidthRatio", 1D, 4D, errors);
PackJsonFieldChecks.validateOptionalIntegerRange(path, channel, "springLength", 4, 96, errors);
PackJsonFieldChecks.validateOptionalDoubleRange(path, channel, "roughness", 0D, 1D, errors); PackJsonFieldChecks.validateOptionalDoubleRange(path, channel, "roughness", 0D, 1D, errors);
PackJsonFieldChecks.validateOptionalIntegerRange(path, channel, "roughnessWavelength", 4, 64, errors); PackJsonFieldChecks.validateOptionalIntegerRange(path, channel, "roughnessWavelength", 4, 64, errors);
} }
@@ -251,23 +251,28 @@ public record HydrologyPlannerSettings(
int cascadeRun, int cascadeRun,
int waterfallMinimumDrop, int waterfallMinimumDrop,
double mouthFlareRatio, double mouthFlareRatio,
double springWidthRatio,
int springLength,
boolean exposeCutStrata boolean exposeCutStrata
) { ) {
// Structural invariants only; authoring bounds live in the pack validator.
public Banks { public Banks {
if (inset < 1 || inset > 16 || freeboard < 1 || freeboard > 8 if (inset < 0 || freeboard < 0
|| !Double.isFinite(blendSlope) || blendSlope < 1D || blendSlope > 16D || !Double.isFinite(blendSlope) || blendSlope <= 0D
|| minimumBlendWidth < 1 || maximumBlendWidth < minimumBlendWidth || maximumBlendWidth > 128 || minimumBlendWidth < 1 || maximumBlendWidth < minimumBlendWidth
|| !Double.isFinite(roughness) || roughness < 0D || roughness > 1D || !Double.isFinite(roughness) || roughness < 0D || roughness > 1D
|| roughnessWavelength < 3 || roughnessWavelength > 128 || roughnessWavelength < 1
|| cascadeRun < 1 || cascadeRun > 16 || cascadeRun < 1
|| waterfallMinimumDrop < 2 || waterfallMinimumDrop > 24 || waterfallMinimumDrop < 1
|| !Double.isFinite(mouthFlareRatio) || mouthFlareRatio < 1D || mouthFlareRatio > 4D) { || !Double.isFinite(mouthFlareRatio) || mouthFlareRatio < 1D
|| !Double.isFinite(springWidthRatio) || springWidthRatio < 1D
|| springLength < 1) {
throw new IllegalArgumentException("Surface bank settings are invalid."); throw new IllegalArgumentException("Surface bank settings are invalid.");
} }
} }
public static Banks defaults() { public static Banks defaults() {
return new Banks(1, 1, 3D, 4, 32, 0.25D, 16, 2, 6, 1.6D, true); return new Banks(1, 1, 3D, 4, 32, 0.25D, 16, 2, 6, 1.6D, 2.5D, 24, true);
} }
} }
@@ -57,6 +57,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.IntConsumer;
public final class IrisHydrologyRuntime implements AutoCloseable { public final class IrisHydrologyRuntime implements AutoCloseable {
private static final int MAXIMUM_CACHE_TILES = 64; private static final int MAXIMUM_CACHE_TILES = 64;
@@ -286,30 +287,53 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
int x, int x,
int z, int z,
int maximumDistance int maximumDistance
) {
return nearestFeature(types, profileKey, x, z, maximumDistance, (int visited) -> { });
}
/**
* Walks tiles outward from the origin, planning each on demand, and stops as soon as no unvisited
* ring can hold a nearer feature. {@code progress} receives the running tile count after every ring.
*/
public Optional<HydrologyFeatureRef> nearestFeature(
Set<HydrologyFeatureType> types,
String profileKey,
int x,
int z,
int maximumDistance,
IntConsumer progress
) { ) {
Objects.requireNonNull(types); Objects.requireNonNull(types);
Objects.requireNonNull(progress);
if (types.isEmpty() || maximumDistance < 0) { if (types.isEmpty() || maximumDistance < 0) {
return Optional.empty(); return Optional.empty();
} }
int tileSize = settings.routing().tileSize(); int tileSize = settings.routing().tileSize();
int publicationRadius = settings.publicationRadius(); int publicationRadius = settings.publicationRadius();
int minimumTileX = tileCoordinate((long) x - maximumDistance - publicationRadius, tileSize);
int maximumTileX = tileCoordinate((long) x + maximumDistance + publicationRadius, tileSize);
int minimumTileZ = tileCoordinate((long) z - maximumDistance - publicationRadius, tileSize);
int maximumTileZ = tileCoordinate((long) z + maximumDistance + publicationRadius, tileSize);
long tileCount = HydrologyFeatureSearchBounds.tileCount( long tileCount = HydrologyFeatureSearchBounds.tileCount(
x, z, maximumDistance, tileSize, publicationRadius); x, z, maximumDistance, tileSize, publicationRadius);
if (tileCount > MAXIMUM_FEATURE_SEARCH_TILES) { if (tileCount > MAXIMUM_FEATURE_SEARCH_TILES) {
throw new IllegalArgumentException("Hydrology feature search exceeds the bounded tile limit."); throw new IllegalArgumentException("Hydrology feature search exceeds the bounded tile limit.");
} }
int originTileX = tileCoordinate(x, tileSize);
int originTileZ = tileCoordinate(z, tileSize);
int ringLimit = HydrologyFeatureSearch.ringLimit(maximumDistance, tileSize, publicationRadius);
HydrologyFeatureRef nearest = null; HydrologyFeatureRef nearest = null;
long nearestDistanceSquared = Long.MAX_VALUE; long nearestDistanceSquared = Long.MAX_VALUE;
for (int tileZ = minimumTileZ; tileZ <= maximumTileZ; tileZ++) { int visited = 0;
for (int tileX = minimumTileX; tileX <= maximumTileX; tileX++) { for (int ring = 0; ring <= ringLimit; ring++) {
HydrologyTile tile = cache.get(new HydrologyTileKey(tileX, tileZ)); long lowerBound = HydrologyFeatureSearch.lowerBound(ring, tileSize, publicationRadius);
if (nearest != null && lowerBound * lowerBound > nearestDistanceSquared) {
break;
}
for (HydrologyTileKey key : HydrologyFeatureSearch.ring(originTileX, originTileZ, ring)) {
HydrologyTile tile = cache.get(key);
visited++;
HydrologyFeatureRef feature = tile.nearestFeature( HydrologyFeatureRef feature = tile.nearestFeature(
types, profileKey, x, z, maximumDistance).orElse(null); types, profileKey, x, z, maximumDistance).orElse(null);
if (feature != null) { if (feature == null) {
continue;
}
long deltaX = (long) feature.x() - x; long deltaX = (long) feature.x() - x;
long deltaZ = (long) feature.z() - z; long deltaZ = (long) feature.z() - z;
long distanceSquared = deltaX * deltaX + deltaZ * deltaZ; long distanceSquared = deltaX * deltaX + deltaZ * deltaZ;
@@ -319,7 +343,7 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
nearestDistanceSquared = distanceSquared; nearestDistanceSquared = distanceSquared;
} }
} }
} progress.accept(visited);
} }
return Optional.ofNullable(nearest); return Optional.ofNullable(nearest);
} }
@@ -677,6 +701,8 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
flow.getCascadeRun(), flow.getCascadeRun(),
flow.getWaterfallMinimumDrop(), flow.getWaterfallMinimumDrop(),
surface.getMouths().getFlareRatio(), surface.getMouths().getFlareRatio(),
channel.getSpringWidthRatio(),
channel.getSpringLength(),
banks.isExposeCutStrata() banks.isExposeCutStrata()
) )
); );
@@ -9,7 +9,6 @@ import java.util.Objects;
public final class ChannelProfileBuilder { public final class ChannelProfileBuilder {
private static final int SMOOTHING_RADIUS = 16; private static final int SMOOTHING_RADIUS = 16;
private static final int HEADWATER_TAPER = 48;
private static final int MOUTH_FLARE = 32; private static final int MOUTH_FLARE = 32;
private final HydrologyPlannerSettings.Surface surface; private final HydrologyPlannerSettings.Surface surface;
@@ -65,11 +64,15 @@ public final class ChannelProfileBuilder {
} }
double[] smoothWidth = smooth(width); double[] smoothWidth = smooth(width);
double[] smoothDepth = smooth(depth); double[] smoothDepth = smooth(depth);
int taper = Math.min(HEADWATER_TAPER, count / 2); // The headwater opens as a spring pool: wider and one block deeper, narrowing to the cruise width.
for (int station = 0; station < taper; station++) { // Ground that falls away across the pool shrinks it so it never demands a cut the valley solver rejects.
double progress = SurfaceNoise.smoothStep(station / (double) taper); int spring = Math.min(surface.banks().springLength(), count / 2);
smoothWidth[station] = 1D + (smoothWidth[station] - 1D) * progress; double springRatio = surface.banks().springWidthRatio();
smoothDepth[station] = 1D + (smoothDepth[station] - 1D) * progress; for (int station = 0; station < spring; station++) {
double remaining = 1D - SurfaceNoise.smoothStep(station / (double) spring);
double localRatio = 1D + (springRatio - 1D) * springRoom(centerline, station, smoothWidth[station] * springRatio / 2D);
smoothWidth[station] *= 1D + (localRatio - 1D) * remaining;
smoothDepth[station] += remaining;
} }
if (directOcean) { if (directOcean) {
int flare = Math.min(MOUTH_FLARE, count / 3); int flare = Math.min(MOUTH_FLARE, count / 3);
@@ -87,6 +90,30 @@ public final class ChannelProfileBuilder {
return new ChannelProfile(halfWidth, smoothDepth, bank); return new ChannelProfile(halfWidth, smoothDepth, bank);
} }
/** Fraction of the spring pool the ground allows: 1 on level ground, 0 where the pool ring would sit half the permitted cut lower. */
private double springRoom(SurfaceCenterline centerline, int station, double reach) {
int centerX = centerline.x()[station];
int centerZ = centerline.z()[station];
HydrologyTerrainSample center = sampler.sample(centerX, centerZ);
if (center == null) {
return 1D;
}
double normalX = centerline.normalX(station);
double normalZ = centerline.normalZ(station);
int lowest = center.naturalHeight();
for (double direction = -1D; direction <= 1D; direction += 2D) {
HydrologyTerrainSample side = sampler.sample(
(int) StrictMath.round(centerX + normalX * reach * direction),
(int) StrictMath.round(centerZ + normalZ * reach * direction));
if (side != null) {
lowest = Math.min(lowest, side.naturalHeight());
}
}
double drop = center.naturalHeight() - lowest;
double allowance = Math.max(1D, surface.maximumIncision() / 2D);
return Math.max(0D, Math.min(1D, 1D - drop / allowance));
}
static double[] smooth(double[] values) { static double[] smooth(double[] values) {
int count = values.length; int count = values.length;
double[] smoothed = new double[count]; double[] smoothed = new double[count];
@@ -38,6 +38,16 @@ public class IrisSurfaceRiverChannelConfig {
@Desc("Wavelength in blocks of the outline and bed variation.") @Desc("Wavelength in blocks of the outline and bed variation.")
private int roughnessWavelength = 16; private int roughnessWavelength = 16;
@MinNumber(1)
@MaxNumber(4)
@Desc("Width of the spring pool at the headwater relative to the channel width; 1 starts the river at its normal width.")
private double springWidthRatio = 2.5D;
@MinNumber(4)
@MaxNumber(96)
@Desc("Blocks over which the spring pool narrows back to the channel width.")
private int springLength = 24;
private static IrisStyledRange range(double min, double max, double zoom) { private static IrisStyledRange range(double min, double max, double zoom) {
IrisGeneratorStyle style = new IrisGeneratorStyle(NoiseStyle.IRIS).zoomed(zoom); IrisGeneratorStyle style = new IrisGeneratorStyle(NoiseStyle.IRIS).zoomed(zoom);
return new IrisStyledRange(min, max, style); return new IrisStyledRange(min, max, style);
@@ -288,7 +288,9 @@ public class PackHydrologyValidatorTest {
"inset": 4, "inset": 4,
"maximumIncision": 33, "maximumIncision": 33,
"roughness": 1.5, "roughness": 1.5,
"roughnessWavelength": 65 "roughnessWavelength": 65,
"springWidthRatio": 5,
"springLength": 97
} }
} }
} }
@@ -303,7 +305,9 @@ public class PackHydrologyValidatorTest {
"inset": -1, "inset": -1,
"maximumIncision": 0, "maximumIncision": 0,
"roughness": -0.1, "roughness": -0.1,
"roughnessWavelength": 3 "roughnessWavelength": 3,
"springWidthRatio": 0.5,
"springLength": 3
} }
} }
} }
@@ -318,6 +322,10 @@ public class PackHydrologyValidatorTest {
assertContains(aboveResult.errors(), "maximumIncision must be at most 32"); assertContains(aboveResult.errors(), "maximumIncision must be at most 32");
assertContains(aboveResult.errors(), "roughness must be at most 1.0"); assertContains(aboveResult.errors(), "roughness must be at most 1.0");
assertContains(aboveResult.errors(), "roughnessWavelength must be at most 64"); assertContains(aboveResult.errors(), "roughnessWavelength must be at most 64");
assertContains(aboveResult.errors(), "springWidthRatio must be at most 4.0");
assertContains(aboveResult.errors(), "springLength must be at most 96");
assertContains(belowResult.errors(), "springWidthRatio must be at least 1.0");
assertContains(belowResult.errors(), "springLength must be at least 4");
assertContains(belowResult.errors(), "width.min must be at least 1.0"); assertContains(belowResult.errors(), "width.min must be at least 1.0");
assertContains(belowResult.errors(), "depth.min must be at least 1.0"); assertContains(belowResult.errors(), "depth.min must be at least 1.0");
assertContains(belowResult.errors(), "inset must be at least 0"); assertContains(belowResult.errors(), "inset must be at least 0");
@@ -144,20 +144,13 @@ public class HydrologyPlannerSettingsTest {
assertEquals(32, banks.maximumBlendWidth()); assertEquals(32, banks.maximumBlendWidth());
assertEquals(6, banks.waterfallMinimumDrop()); assertEquals(6, banks.waterfallMinimumDrop());
assertTrue(banks.exposeCutStrata()); assertTrue(banks.exposeCutStrata());
assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks( assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks(-1, 1, 3D, 4, 32, 0.25D, 16, 2, 6, 1.6D, 2.5D, 24, true));
0, 1, 3D, 4, 32, 0.25D, 16, 2, 6, 1.6D, true)); assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks(1, -1, 3D, 4, 32, 0.25D, 16, 2, 6, 1.6D, 2.5D, 24, true));
assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks( assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks(1, 1, 0D, 4, 32, 0.25D, 16, 2, 6, 1.6D, 2.5D, 24, true));
1, 0, 3D, 4, 32, 0.25D, 16, 2, 6, 1.6D, true)); assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks(1, 1, 3D, 40, 32, 0.25D, 16, 2, 6, 1.6D, 2.5D, 24, true));
assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks( assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks(1, 1, 3D, 4, 32, 0.25D, 16, 2, 0, 1.6D, 2.5D, 24, true));
1, 1, 0.5D, 4, 32, 0.25D, 16, 2, 6, 1.6D, true)); assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks(1, 1, 3D, 4, 32, 0.25D, 16, 0, 6, 1.6D, 2.5D, 24, true));
assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks( assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks(1, 1, 3D, 4, 32, 1.5D, 16, 2, 6, 1.6D, 2.5D, 24, true));
1, 1, 3D, 40, 32, 0.25D, 16, 2, 6, 1.6D, true));
assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks(
1, 1, 3D, 4, 32, 0.25D, 16, 2, 1, 1.6D, true));
assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks(
1, 1, 3D, 4, 32, 0.25D, 16, 0, 6, 1.6D, true));
assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Banks(
1, 1, 3D, 4, 32, 1.5D, 16, 2, 6, 1.6D, true));
assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Surface( assertThrows(IllegalArgumentException.class, () -> new HydrologyPlannerSettings.Surface(
true, true,
new HydrologyPlannerSettings.Source(true, 1D, 0, 0, 1, 0), new HydrologyPlannerSettings.Source(true, 1D, 0, 0, 1, 0),
@@ -303,7 +303,7 @@ public class HydrologyTileCacheTest {
// Small-tile settings need a blend envelope that fits inside the bounded cross-tile admission period. // Small-tile settings need a blend envelope that fits inside the bounded cross-tile admission period.
private static HydrologyPlannerSettings.Banks tileBoundedBanks() { private static HydrologyPlannerSettings.Banks tileBoundedBanks() {
return new HydrologyPlannerSettings.Banks(1, 1, 3D, 4, 4, 0.25D, 16, 2, 6, 1.6D, true); return new HydrologyPlannerSettings.Banks(1, 1, 3D, 4, 4, 0.25D, 16, 2, 6, 1.6D, 2.5D, 24, true);
} }
private HydrologyTerrainSample featureTerrain(int x, int z) { private HydrologyTerrainSample featureTerrain(int x, int z) {
@@ -8,6 +8,7 @@ import art.arcane.iris.engine.hydrology.HydrologyTerrainSampler;
import org.junit.Test; import org.junit.Test;
import java.util.List; import java.util.List;
import java.util.function.IntBinaryOperator;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -20,7 +21,7 @@ public class ChannelProfileBuilderTest {
}; };
@Test @Test
public void cruiseWidthIsConstantAndTapersFromTheHeadwater() { public void headwaterOpensAsASpringPoolThatNarrowsToTheCruiseWidth() {
SurfaceCenterline centerline = straight(400); SurfaceCenterline centerline = straight(400);
ChannelProfile profile = builder(1D, 1D).build(centerline, "water", false); ChannelProfile profile = builder(1D, 1D).build(centerline, "water", false);
@@ -28,11 +29,14 @@ public class ChannelProfileBuilderTest {
assertEquals(3D, profile.halfWidth()[200], 1.0E-9D); assertEquals(3D, profile.halfWidth()[200], 1.0E-9D);
assertEquals(3D, profile.halfWidth()[399], 1.0E-9D); assertEquals(3D, profile.halfWidth()[399], 1.0E-9D);
assertEquals(3D, profile.depth()[200], 1.0E-9D); assertEquals(3D, profile.depth()[200], 1.0E-9D);
assertTrue(profile.halfWidth()[0] < 1D); assertEquals(3D * 2.5D, profile.halfWidth()[0], 1.0E-9D);
for (int station = 1; station < 48; station++) { assertEquals(4D, profile.depth()[0], 1.0E-9D);
assertTrue(profile.halfWidth()[station] >= profile.halfWidth()[station - 1]); for (int station = 1; station <= 24; station++) {
assertTrue(profile.halfWidth()[station] <= profile.halfWidth()[station - 1]);
assertTrue(profile.depth()[station] <= profile.depth()[station - 1]);
} }
assertTrue(profile.depth()[0] >= 1D); assertEquals(3D, profile.halfWidth()[24], 1.0E-9D);
assertEquals(3D, profile.depth()[24], 1.0E-9D);
} }
@Test @Test
@@ -55,9 +59,26 @@ public class ChannelProfileBuilderTest {
assertEquals(6D * 1.25D + 2D, profile.collar(200, 0.25D), 1.0E-9D); assertEquals(6D * 1.25D + 2D, profile.collar(200, 0.25D), 1.0E-9D);
} }
@Test
public void groundFallingAwayAcrossThePoolShrinksTheSpringPool() {
SurfaceCenterline centerline = straight(400);
ChannelProfile sloped = builder(1D, 1D, (int x, int z) -> 80 - Math.abs(z) / 2).build(centerline, "water", false);
ChannelProfile cliff = builder(1D, 1D, (int x, int z) -> 80 - Math.abs(z) * 8).build(centerline, "water", false);
assertTrue(sloped.halfWidth()[0] > 3D);
assertTrue(sloped.halfWidth()[0] < 3D * 2.5D);
assertEquals(3D, cliff.halfWidth()[0], 1.0E-9D);
assertEquals(4D, cliff.depth()[0], 1.0E-9D);
assertEquals(3D, sloped.halfWidth()[24], 1.0E-9D);
}
private static ChannelProfileBuilder builder(double widthMultiplier, double depthMultiplier) { private static ChannelProfileBuilder builder(double widthMultiplier, double depthMultiplier) {
return builder(widthMultiplier, depthMultiplier, (int x, int z) -> 80);
}
private static ChannelProfileBuilder builder(double widthMultiplier, double depthMultiplier, IntBinaryOperator height) {
HydrologyTerrainSampler sampler = (int x, int z) -> new HydrologyTerrainSample( HydrologyTerrainSampler sampler = (int x, int z) -> new HydrologyTerrainSample(
80, height.applyAsInt(x, z),
0D, 0D,
false, false,
false, false,
@@ -173,7 +173,7 @@ public class ErosionFieldCompilerTest {
true, true,
defaults.sources(), defaults.sources(),
4, 8, 2, 4, 10, 1.5D, 4, 8, 2, 4, 10, 1.5D,
new HydrologyPlannerSettings.Banks(1, 1, 3D, 4, 32, 0D, 16, 2, 6, 1.6D, true) new HydrologyPlannerSettings.Banks(1, 1, 3D, 4, 32, 0D, 16, 2, 6, 1.6D, 2.5D, 24, true)
); );
} }