mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 22:10:18 +00:00
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:
1 parent
896e58e212
commit
d1c73025a0
12 files changed
+152
-56
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,
|
||||||
|
|||||||
+3
-1
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+42
-16
@@ -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,40 +287,63 @@ 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) {
|
||||||
long deltaX = (long) feature.x() - x;
|
continue;
|
||||||
long deltaZ = (long) feature.z() - z;
|
}
|
||||||
long distanceSquared = deltaX * deltaX + deltaZ * deltaZ;
|
long deltaX = (long) feature.x() - x;
|
||||||
if (nearest == null || distanceSquared < nearestDistanceSquared
|
long deltaZ = (long) feature.z() - z;
|
||||||
|| distanceSquared == nearestDistanceSquared && feature.id() < nearest.id()) {
|
long distanceSquared = deltaX * deltaX + deltaZ * deltaZ;
|
||||||
nearest = feature;
|
if (nearest == null || distanceSquared < nearestDistanceSquared
|
||||||
nearestDistanceSquared = distanceSquared;
|
|| distanceSquared == nearestDistanceSquared && feature.id() < nearest.id()) {
|
||||||
}
|
nearest = feature;
|
||||||
|
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()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
+33
-6
@@ -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");
|
||||||
|
|||||||
+7
-14
@@ -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) {
|
||||||
|
|||||||
+27
-6
@@ -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,
|
||||||
|
|||||||
+1
-1
@@ -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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user