Added diverse noise styles

- Dune and Strata add dunes and layered bands
- Wood and Marble add warped grain and mineral veins
- Gabor and Scales add oriented waves and overlapping scallops
- Chladni and Kaleidoscope add nodal lines and mirrored motifs
- Menger Sponge and Circuit add recursive voids and connected traces
- New styles use the shared scale and full seeds
- Tests cover tile seams, volume variation, bounds, and octaves
This commit is contained in:
Brian Neumann-Fopiano
2026-09-05 19:33:53 -04:00
parent 645edcff5c
commit 14cfe3ed68
22 changed files with 1183 additions and 3 deletions
@@ -553,6 +553,36 @@ public enum NoiseStyle {
@Desc("Seeded spiral arms form swirling eddies in 2D and winding funnels in 3D.")
VORTEX(rng -> new CNG(rng, NoiseType.VORTEX, 1D, 1), NoiseType.VORTEX),
@Desc("Seeded crescent dunes with asymmetric slopes and smooth height-dependent drift.")
DUNE(rng -> new CNG(rng, NoiseType.DUNE, 1D, 1), NoiseType.DUNE),
@Desc("Folded sedimentary bands with varying thickness through the volume.")
STRATA(rng -> new CNG(rng, NoiseType.STRATA, 1D, 1), NoiseType.STRATA),
@Desc("Distorted growth rings and knot-like forms with variation through height.")
WOOD(rng -> new CNG(rng, NoiseType.WOOD, 1D, 1), NoiseType.WOOD),
@Desc("Seeded oriented wave packets form a sparse directional ripple texture.")
GABOR(rng -> new CNG(rng, NoiseType.GABOR, 1D, 1), NoiseType.GABOR),
@Desc("Turbulent stone veins separate broad smooth regions.")
MARBLE(rng -> new CNG(rng, NoiseType.MARBLE, 1D, 1), NoiseType.MARBLE),
@Desc("Overlapping scalloped scales form a seeded tiled surface.")
SCALES(rng -> new CNG(rng, NoiseType.SCALES, 1D, 1), NoiseType.SCALES),
@Desc("Standing-wave nodes form plate-like figures that change through height.")
CHLADNI(rng -> new CNG(rng, NoiseType.CHLADNI, 1D, 1), NoiseType.CHLADNI),
@Desc("Mirrored wedge motifs repeat at a local scale with seeded variation.")
KALEIDOSCOPE(rng -> new CNG(rng, NoiseType.KALEIDOSCOPE, 1D, 1), NoiseType.KALEIDOSCOPE),
@Desc("Recursive cubic voids form a three-dimensional sponge with square-hole slices.")
MENGER_SPONGE(rng -> new CNG(rng, NoiseType.MENGER_SPONGE, 1D, 1), NoiseType.MENGER_SPONGE),
@Desc("Orthogonal tracks and ring pads form connected circuits that shift through height.")
CIRCUIT(rng -> new CNG(rng, NoiseType.CIRCUIT, 1D, 1), NoiseType.CIRCUIT),
;
private final CNGFactory f;
@@ -0,0 +1,34 @@
package art.arcane.iris.util.project.noise;
public final class ChladniNoise extends PatternNoise {
private final int firstMode;
private final int secondMode;
private final double phase;
private final double width;
public ChladniNoise(long seed) {
long hash = mix(seed);
firstMode = 2 + (int) (hash & 1L);
secondMode = firstMode + 1 + (int) ((hash >>> 1) & 1L);
phase = (hash >>> 11) * 0x1.0p-52D;
width = 0.22D + ((hash >>> 3) & 255L) * (0.12D / 255D);
}
@Override
protected double sample(double x, double y, double z) {
double u = (x + phase) * Math.PI;
double v = (z + phase) * Math.PI;
double first = Math.cos(firstMode * u) * Math.cos(secondMode * v);
double second = Math.cos(secondMode * u) * Math.cos(firstMode * v);
double field = first * Math.cos(y * Math.PI * 0.5D)
- second * Math.cos(y * Math.PI * 0.75D);
double node = Math.max(0D, 1D - Math.abs(field) / width);
return node * node * (3D - 2D * node);
}
private static long mix(long value) {
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
return value ^ (value >>> 31);
}
}
@@ -0,0 +1,73 @@
package art.arcane.iris.util.project.noise;
public final class CircuitNoise extends PatternNoise {
private final long seed;
private final double phaseX;
private final double phaseZ;
public CircuitNoise(long seed) {
this.seed = seed;
phaseX = (mix(seed) >>> 11) * 0x1.0p-53;
phaseZ = (mix(seed + 0x9E3779B97F4A7C15L) >>> 11) * 0x1.0p-53;
}
@Override
protected double sample(double x, double y, double z) {
x += phaseX;
z += phaseZ;
if (y != 0D) {
x += y * 0.13D + 0.17D * Math.sin(y * Math.PI * 0.5D);
z += 0.11D * Math.sin(y * Math.PI * 0.75D);
}
long cellX = (long) Math.floor(x);
long cellZ = (long) Math.floor(z);
x -= cellX;
z -= cellZ;
long hash = mix(seed ^ cellX * 0x9E3779B97F4A7C15L ^ cellZ * 0xD1B54A32D192ED03L);
double distance;
double padDistance;
if ((hash & 3L) == 0L) {
distance = Math.min(Math.abs(x - 0.5D), Math.abs(z - 0.5D));
double dx = x - 0.5D;
double dz = z - 0.5D;
padDistance = Math.sqrt(dx * dx + dz * dz);
} else {
if ((hash & 1L) != 0L) {
x = 1D - x;
}
distance = Math.sqrt(Math.min(routeDistanceSquared(x, z), routeDistanceSquared(1D - x, 1D - z)));
double dx = x - 0.75D;
double dz = z - 0.25D;
double oppositeX = x - 0.25D;
double oppositeZ = z - 0.75D;
padDistance = Math.sqrt(Math.min(dx * dx + dz * dz, oppositeX * oppositeX + oppositeZ * oppositeZ));
}
double trace = smooth(1D - distance / 0.045D);
double pad = smooth(1D - Math.abs(padDistance - 0.085D) / 0.03D);
return Math.max(trace, pad) * smooth(padDistance / 0.045D);
}
private static double routeDistanceSquared(double x, double z) {
double top = segmentDistanceSquared(x - 0.5D, z, 0D, 0.25D);
double bend = segmentDistanceSquared(z - 0.25D, x, 0.5D, 0.75D);
double middle = segmentDistanceSquared(x - 0.75D, z, 0.25D, 0.5D);
double right = segmentDistanceSquared(z - 0.5D, x, 0.75D, 1D);
return Math.min(Math.min(top, bend), Math.min(middle, right));
}
private static double segmentDistanceSquared(double perpendicular, double along, double start, double end) {
double excess = Math.max(start - along, Math.max(along - end, 0D));
return perpendicular * perpendicular + excess * excess;
}
private static double smooth(double value) {
double bounded = Math.clamp(value, 0D, 1D);
return bounded * bounded * (3D - 2D * bounded);
}
private static long mix(long value) {
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
return value ^ (value >>> 31);
}
}
@@ -0,0 +1,61 @@
package art.arcane.iris.util.project.noise;
public final class DuneNoise extends PatternNoise {
private static final double TAU = Math.PI * 2D;
private final long seed;
private final double cosine;
private final double sine;
private final double phase;
public DuneNoise(long seed) {
this.seed = seed;
phase = unit(mix(seed)) * TAU;
cosine = Math.cos(phase);
sine = Math.sin(phase);
}
@Override
protected double sample(double x, double y, double z) {
double wind = x * cosine + z * sine + y * 0.17D;
double across = z * cosine - x * sine + 0.12D * Math.sin(y * 0.7D + phase);
long cellWind = (long) Math.floor(wind);
long cellAcross = (long) Math.floor(across);
double dune = 0D;
for (int offsetWind = -1; offsetWind <= 1; offsetWind++) {
long gridWind = cellWind + offsetWind;
for (int offsetAcross = -1; offsetAcross <= 1; offsetAcross++) {
long gridAcross = cellAcross + offsetAcross;
long hash = mix(seed ^ gridWind * 0x9E3779B97F4A7C15L ^ gridAcross * 0xD1B54A32D192ED03L);
double centerWind = gridWind + 0.2D + (hash & 65535L) * (0.6D / 65535D);
double centerAcross = gridAcross + 0.2D + ((hash >>> 16) & 65535L) * (0.6D / 65535D);
double width = 0.32D + ((hash >>> 32) & 255L) * (0.16D / 255D);
double transverse = (across - centerAcross) / width;
double transverseSquared = transverse * transverse;
if (transverseSquared >= 1D) {
continue;
}
double crest = centerWind + 0.43D * transverseSquared;
double offset = wind - crest;
double windward = 0.52D + ((hash >>> 40) & 255L) * (0.14D / 255D);
double leeward = 0.1D + ((hash >>> 48) & 255L) * (0.035D / 255D);
double slope = Math.max(0D, 1D - Math.abs(offset) / (offset < 0D ? windward : leeward));
double horns = 1D - transverseSquared * transverseSquared;
double height = 0.62D + (hash >>> 56) * (0.38D / 255D);
dune = Math.max(dune, height * horns * horns * slope * slope * (3D - 2D * slope));
}
}
double ripples = 0.04D + 0.035D * Math.cos(wind * TAU * 2D + 0.5D * Math.sin(across * 1.7D + phase));
return ripples + (1D - ripples) * dune;
}
private static long mix(long value) {
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
return value ^ (value >>> 31);
}
private static double unit(long value) {
return (value >>> 11) * 0x1.0p-53D;
}
}
@@ -0,0 +1,72 @@
package art.arcane.iris.util.project.noise;
import java.util.SplittableRandom;
public final class GaborNoise extends PatternNoise {
private static final double TAU = Math.PI * 2D;
private static final double RADIUS_SQUARED = 0.95D * 0.95D;
private static final double SPREAD = 0.6D / 65535D;
private final long seed;
private final double directionX;
private final double directionY;
private final double directionZ;
public GaborNoise(long seed) {
this.seed = seed;
SplittableRandom random = new SplittableRandom(seed);
double angle = random.nextDouble(TAU);
double elevation = random.nextDouble(-0.55D, 0.55D);
double horizontal = Math.sqrt(1D - elevation * elevation);
directionX = Math.cos(angle) * horizontal;
directionY = elevation;
directionZ = Math.sin(angle) * horizontal;
}
@Override
protected double sample(double x, double y, double z) {
long cellX = (long) Math.floor(x);
long cellY = (long) Math.floor(y);
long cellZ = (long) Math.floor(z);
double sum = 0D;
double weights = 0.12D;
for (int offsetY = -1; offsetY <= 1; offsetY++) {
long pointY = cellY + offsetY;
long hashY = pointY * 0xC2B2AE3D27D4EB4FL;
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
long pointZ = cellZ + offsetZ;
long hashYZ = hashY ^ pointZ * 0x165667B19E3779F9L;
for (int offsetX = -1; offsetX <= 1; offsetX++) {
long pointX = cellX + offsetX;
long hash = mix(seed ^ pointX * 0x9E3779B97F4A7C15L ^ hashYZ);
if ((hash & 3L) == 0L) {
continue;
}
double dx = x - (pointX + 0.2D + (hash & 65535L) * SPREAD);
double dy = y - (pointY + 0.2D + ((hash >>> 16) & 65535L) * SPREAD);
double dz = z - (pointZ + 0.2D + ((hash >>> 32) & 65535L) * SPREAD);
double distanceSquared = dx * dx + dy * dy + dz * dz;
if (distanceSquared >= RADIUS_SQUARED) {
continue;
}
double envelope = 1D - distanceSquared / RADIUS_SQUARED;
double weight = envelope * envelope * envelope;
long wave = mix(hash);
double waveX = directionX + ((wave & 1023L) / 1023D - 0.5D) * 0.35D;
double waveY = directionY + (((wave >>> 10) & 1023L) / 1023D - 0.5D) * 0.35D;
double waveZ = directionZ + (((wave >>> 20) & 1023L) / 1023D - 0.5D) * 0.35D;
double phase = (wave >>> 32) * (TAU / 4294967296D);
sum += weight * Math.cos(TAU * 3D * (waveX * dx + waveY * dy + waveZ * dz) + phase);
weights += weight;
}
}
}
return 0.5D + 0.5D * sum / weights;
}
private static long mix(long value) {
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
return value ^ (value >>> 31);
}
}
@@ -0,0 +1,43 @@
package art.arcane.iris.util.project.noise;
public final class KaleidoscopeNoise extends PatternNoise {
private static final double TAU = Math.PI * 2D;
private final long seed;
public KaleidoscopeNoise(long seed) {
this.seed = seed;
}
@Override
protected double sample(double x, double y, double z) {
z += 0.5D;
long cellX = (long) Math.floor(x);
long cellZ = (long) Math.floor(z);
double dx = x - cellX - 0.5D;
double dz = z - cellZ - 0.5D;
double radiusSquared = dx * dx + dz * dz;
if (radiusSquared >= 0.25D) {
return 0D;
}
long hash = mix(seed ^ cellX * 0x9E3779B97F4A7C15L ^ cellZ * 0xD1B54A32D192ED03L);
int wedges = 4 + (int) (hash & 3L);
double phase = (hash >>> 11) * 0x1.0p-53D * TAU;
double sector = TAU / wedges;
double angle = Math.atan2(dz, dx) + phase + y * 0.35D;
angle = Math.abs(angle - Math.floor(angle / sector + 0.5D) * sector);
double radius = Math.sqrt(radiusSquared);
double u = radius * Math.cos(angle);
double v = radius * Math.sin(angle);
double motif = 0.5D + 0.5D * Math.cos(16D * u + 2.5D * Math.cos(18D * v + phase + y));
double envelope = 1D - radiusSquared * 4D;
return motif * envelope * envelope;
}
private static long mix(long value) {
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
return value ^ (value >>> 31);
}
}
@@ -0,0 +1,36 @@
package art.arcane.iris.util.project.noise;
import java.util.SplittableRandom;
public final class MarbleNoise extends PatternNoise {
private static final double TAU = Math.PI * 2D;
private final FastNoiseDouble distortion;
private final double directionX;
private final double directionY;
private final double directionZ;
private final double phase;
public MarbleNoise(long seed) {
SplittableRandom random = new SplittableRandom(seed);
distortion = new FastNoiseDouble(random.nextLong());
distortion.setFrequency(1D);
double angle = random.nextDouble(TAU);
directionX = Math.cos(angle);
directionY = random.nextDouble(0.25D, 0.65D);
directionZ = Math.sin(angle);
phase = random.nextDouble(TAU);
}
@Override
protected double sample(double x, double y, double z) {
double broad = distortion.GetSimplex(x * 0.45D, y * 0.45D, z * 0.45D);
double detail = distortion.GetSimplex(x * 1.3D + 17.3D, y * 1.3D - 11.7D, z * 1.3D + 5.9D);
double warp = broad * 1.1D + detail * 0.2D;
double position = x * directionX + y * directionY + z * directionZ;
double ridge = Math.sin(TAU * (position + warp) + phase);
double vein = 1D / (1D + 90D * ridge * ridge);
double stone = 0.85D + 0.15D * broad;
return Math.clamp(0.1D + 0.84D * stone * (1D - vein), 0D, 1D);
}
}
@@ -0,0 +1,48 @@
package art.arcane.iris.util.project.noise;
public final class MengerSpongeNoise extends PatternNoise {
private static final int DEPTH = 3;
private final double phaseX;
private final double phaseY;
private final double phaseZ;
public MengerSpongeNoise(long seed) {
long hash = mix(seed);
phaseX = (hash & 0x1FFFFFL) * 0x1.0p-21D;
phaseY = ((hash >>> 21) & 0x1FFFFFL) * (0x1.0p-21D / 3D);
phaseZ = (hash >>> 42) * 0x1.0p-22D;
}
@Override
protected double sample(double x, double y, double z) {
x += phaseX;
y += phaseY;
z += phaseZ;
x -= Math.floor(x);
y -= Math.floor(y);
z -= Math.floor(z);
for (int level = 0; level < DEPTH; level++) {
x *= 3D;
y *= 3D;
z *= 3D;
int cellX = (int) x;
int cellY = (int) y;
int cellZ = (int) z;
int middleAxes = (cellX == 1 ? 1 : 0) + (cellY == 1 ? 1 : 0) + (cellZ == 1 ? 1 : 0);
if (middleAxes >= 2) {
return 0D;
}
x -= cellX;
y -= cellY;
z -= cellZ;
}
return 1D;
}
private static long mix(long value) {
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
return value ^ (value >>> 31);
}
}
@@ -92,7 +92,17 @@ public enum NoiseType {
QUASICRYSTAL(QuasicrystalNoise::new, 1D / 64D),
TRUCHET(TruchetNoise::new, 1D / 64D),
CRATER(CraterNoise::new, 1D / 64D),
VORTEX(VortexNoise::new, 1D / 64D);
VORTEX(VortexNoise::new, 1D / 64D),
DUNE(DuneNoise::new, 1D / 64D),
STRATA(StrataNoise::new, 1D / 64D),
WOOD(WoodNoise::new, 1D / 64D),
GABOR(GaborNoise::new, 1D / 64D),
MARBLE(MarbleNoise::new, 1D / 64D),
SCALES(ScalesNoise::new, 1D / 64D),
CHLADNI(ChladniNoise::new, 1D / 64D),
KALEIDOSCOPE(KaleidoscopeNoise::new, 1D / 64D),
MENGER_SPONGE(MengerSpongeNoise::new, 1D / 64D),
CIRCUIT(CircuitNoise::new, 1D / 64D);
private final NoiseFactory f;
private final double coordinateScale;
@@ -0,0 +1,61 @@
package art.arcane.iris.util.project.noise;
public final class ScalesNoise extends PatternNoise {
private static final double TAU = Math.PI * 2D;
private static final double RADIUS = 0.5D;
private static final double ROW_HEIGHT = 0.5D;
private static final double GROOVE_WIDTH = 0.055D;
private final long seed;
private final double phaseX;
private final double phaseZ;
public ScalesNoise(long seed) {
this.seed = seed;
phaseX = (mix(seed) >>> 11) * 0x1.0p-53D;
phaseZ = (mix(seed + 0x9E3779B97F4A7C15L) >>> 11) * 0x1.0p-53D;
}
@Override
protected double sample(double x, double y, double z) {
x += phaseX + Math.sin(y * TAU * 0.5D) * 0.22D + y * 0.15D;
z += phaseZ + Math.sin(y * TAU * 0.25D) * 0.12D + y * 0.2D;
long row = (long) Math.floor(z / ROW_HEIGHT);
double groove = 0D;
double shade = 0D;
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
long pointZ = row + offsetZ;
double shift = (pointZ & 1L) * 0.5D;
long column = (long) Math.floor(x - shift);
double dz = z - pointZ * ROW_HEIGHT;
for (int offsetX = -1; offsetX <= 1; offsetX++) {
long pointX = column + offsetX;
double dx = x - (pointX + shift);
double radiusSquared = dx * dx + dz * dz;
double distance;
if (dz >= 0D) {
distance = Math.abs(Math.sqrt(radiusSquared) - RADIUS);
} else {
double endpoint = Math.abs(dx) - RADIUS;
distance = Math.sqrt(endpoint * endpoint + dz * dz);
}
double rim = Math.max(0D, 1D - distance / GROOVE_WIDTH);
if (rim > 0D || radiusSquared < RADIUS * RADIUS && dz > 0D) {
long hash = mix(seed ^ pointX * 0x9E3779B97F4A7C15L ^ pointZ * 0xC2B2AE3D27D4EB4FL);
double variation = 0.75D + (hash >>> 11) * 0x1.0p-53D * 0.25D;
groove = Math.max(groove, rim * rim * (3D - 2D * rim) * variation);
double inside = Math.max(0D, 1D - radiusSquared / (RADIUS * RADIUS));
double rise = Math.clamp(dz / 0.12D, 0D, 1D);
shade = Math.max(shade, inside * rise * rise * (3D - 2D * rise) * variation);
}
}
}
return 0.72D + 0.22D * shade - 0.65D * groove;
}
private static long mix(long value) {
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
return value ^ (value >>> 31);
}
}
@@ -0,0 +1,37 @@
package art.arcane.iris.util.project.noise;
import java.util.SplittableRandom;
public final class StrataNoise extends PatternNoise {
private static final double TAU = Math.PI * 2D;
private final double cosine;
private final double sine;
private final double phaseA;
private final double phaseB;
private final double phaseC;
public StrataNoise(long seed) {
SplittableRandom random = new SplittableRandom(seed);
double angle = random.nextDouble(TAU);
cosine = Math.cos(angle);
sine = Math.sin(angle);
phaseA = random.nextDouble(TAU);
phaseB = random.nextDouble(TAU);
phaseC = random.nextDouble(TAU);
}
@Override
protected double sample(double x, double y, double z) {
double along = x * cosine + z * sine;
double across = z * cosine - x * sine;
double fold = 1.1D * Math.sin(along * 1.1D + phaseA)
+ 0.35D * Math.sin(along * 2.7D + across * 0.8D + phaseB + y * 0.4D)
+ 0.2D * Math.sin(across * 1.7D + y * 0.9D + phaseC);
double layer = TAU * (across + y * 0.75D + fold);
double thickness = layer + 0.55D * Math.sin(layer * 0.25D + phaseB);
double broadBand = 0.5D + 0.5D * Math.sin(thickness);
double lamina = 0.5D + 0.5D * Math.sin(thickness * 3D + phaseC);
return 0.82D * broadBand + 0.18D * lamina;
}
}
@@ -0,0 +1,56 @@
package art.arcane.iris.util.project.noise;
public final class WoodNoise extends PatternNoise {
private static final double TAU = Math.PI * 2D;
private final long seed;
private final double cosine;
private final double sine;
private final double phase;
public WoodNoise(long seed) {
this.seed = seed;
phase = (mix(seed) >>> 11) * 0x1.0p-53D * TAU;
cosine = Math.cos(phase);
sine = Math.sin(phase);
}
@Override
protected double sample(double x, double y, double z) {
double along = (z * cosine - x * sine) * 0.45D + y * 0.17D;
double across = x * cosine + z * sine + y * 0.07D + 0.11D * Math.sin(along * 0.85D + phase);
double grainPhase = across * TAU * 3D + 0.18D * Math.sin(along * 2.1D + phase);
double value = 0.12D * (0.5D + 0.5D * Math.sin(grainPhase));
double weightSum = 0.12D;
long cellAcross = (long) Math.floor(across);
long cellAlong = (long) Math.floor(along);
for (int offsetAcross = -1; offsetAcross <= 1; offsetAcross++) {
long gridAcross = cellAcross + offsetAcross;
for (int offsetAlong = -1; offsetAlong <= 1; offsetAlong++) {
long gridAlong = cellAlong + offsetAlong;
long hash = mix(seed ^ gridAcross * 0x9E3779B97F4A7C15L ^ gridAlong * 0xD1B54A32D192ED03L);
double deltaAcross = across - (gridAcross + 0.3D + (hash & 65535L) * (0.4D / 65535D));
double deltaAlong = along - (gridAlong + 0.3D + ((hash >>> 16) & 65535L) * (0.4D / 65535D));
double radius = 0.5D + ((hash >>> 32) & 255L) * (0.12D / 255D);
double distanceSquared = deltaAcross * deltaAcross + deltaAlong * deltaAlong;
double envelope = 1D - distanceSquared / (radius * radius);
if (envelope <= 0D) {
continue;
}
double rings = Math.sqrt(distanceSquared + 0.0004D) * TAU * 5D;
double ringPhase = ((hash >>> 40) & 65535L) * (TAU / 65535D);
double knot = 0.5D + 0.5D * Math.sin(rings + ringPhase);
double weight = 3D * envelope * envelope;
value += knot * weight;
weightSum += weight;
}
}
return value / weightSum;
}
private static long mix(long value) {
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
return value ^ (value >>> 31);
}
}
@@ -0,0 +1,69 @@
package art.arcane.iris.util.project.noise;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ChladniNoiseTest {
@Test
public void horizontalPlateHasMirroredNodalLinesAndDarkAntinodes() {
for (long seed : new long[]{0L, 1337L, Long.MIN_VALUE}) {
ChladniNoise noise = new ChladniNoise(seed);
double minimum = 1D;
for (int index = 0; index < 256; index++) {
double x = index * 0.031D - 4D;
double z = index * -0.047D + 3D;
assertEquals(1D, noise.noise(x, x), 0D);
assertEquals(noise.noise(x, z), noise.noise(z, x), 1.0E-12D);
minimum = Math.min(minimum, noise.noise(x, z));
}
assertTrue(minimum < 0.01D);
}
}
@Test
public void standingModesChangeContinuouslyWithHeight() {
ChladniNoise noise = new ChladniNoise(1337L);
double variation = 0D;
for (int index = 0; index < 512; index++) {
double x = index * 0.019D - 5D;
double z = index * -0.023D + 3D;
assertEquals(noise.noise(x, z), noise.noise(x, 0D, z), 0D);
for (double y : new double[]{-2D, 0D, 1D, 468750D}) {
assertEquals(noise.noise(x, y - 1.0E-8D, z),
noise.noise(x, y + 1.0E-8D, z), 1.0E-5D);
}
variation += Math.abs(noise.noise(x, z) - noise.noise(x, 0.7D, z));
}
assertTrue(variation > 20D);
}
@Test
public void upperSeedBitsChangeThePlate() {
ChladniNoise first = new ChladniNoise(1337L);
ChladniNoise second = new ChladniNoise(1337L + (1L << 32));
double variation = 0D;
for (int index = 0; index < 256; index++) {
variation += Math.abs(first.noise(index * 0.019D) - second.noise(index * 0.019D));
}
assertTrue(variation > 10D);
}
@Test
public void octaveSamplesStayBoundedAndRepeatableAtWorldBorders() {
ChladniNoise first = new ChladniNoise(Long.MAX_VALUE);
ChladniNoise second = new ChladniNoise(Long.MAX_VALUE);
for (int octaves : new int[]{1, 3, 16}) {
first.setOctaves(octaves);
second.setOctaves(octaves);
for (int index = 0; index < 128; index++) {
double x = -468750D + index * (937500D / 127D);
double value = first.noise(x, x * 0.37D, x * -0.71D);
assertTrue(Double.isFinite(value));
assertTrue(value >= 0D && value <= 1D);
assertEquals(value, second.noise(x, x * 0.37D, x * -0.71D), 0D);
}
}
}
}
@@ -0,0 +1,45 @@
package art.arcane.iris.util.project.noise;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class CircuitNoiseTest {
@Test
public void tracksMeetAtTilePortsWithoutBoundaryJumps() {
CircuitNoise noise = new CircuitNoise(0L);
double maximum = 0D;
for (int boundary = -8; boundary <= 8; boundary++) {
for (int step = 0; step < 256; step++) {
double z = step * 0.019D - 2D;
assertEquals(noise.noise(boundary - 1E-8D, z), noise.noise(boundary + 1E-8D, z), 1E-5D);
maximum = Math.max(maximum, noise.noise(boundary, z));
}
}
assertTrue(maximum > 0.99D);
}
@Test
public void circuitHasOpenBoardTracesAndIntermediateEdges() {
CircuitNoise noise = new CircuitNoise(1337L);
int open = 0;
int trace = 0;
int edge = 0;
for (int x = 0; x < 256; x++) {
for (int z = 0; z < 256; z++) {
double value = noise.noise(x / 64D, z / 64D);
if (value == 0D) {
open++;
} else if (value > 0.8D) {
trace++;
} else {
edge++;
}
}
}
assertTrue(open > 32768);
assertTrue(trace > 1500);
assertTrue(edge > 1500);
}
}
@@ -0,0 +1,40 @@
package art.arcane.iris.util.project.noise;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class GaborNoiseTest {
@Test
public void wavePacketsHaveDirectionalDetailAndQuietGaps() {
GaborNoise noise = new GaborNoise(1337L);
double xx = 0D;
double xz = 0D;
double zz = 0D;
int quiet = 0;
int dark = 0;
int light = 0;
for (int row = 0; row < 128; row++) {
for (int column = 0; column < 128; column++) {
double x = column * 0.0625D - 4D;
double z = row * 0.0625D - 4D;
double value = noise.noise(x, z);
quiet += Math.abs(value - 0.5D) < 0.03D ? 1 : 0;
dark += value < 0.3D ? 1 : 0;
light += value > 0.7D ? 1 : 0;
double dx = noise.noise(x + 0.001D, z) - noise.noise(x - 0.001D, z);
double dz = noise.noise(x, z + 0.001D) - noise.noise(x, z - 0.001D);
xx += dx * dx;
xz += dx * dz;
zz += dz * dz;
}
}
double spread = Math.sqrt((xx - zz) * (xx - zz) + 4D * xz * xz);
double major = xx + zz + spread;
double minor = xx + zz - spread;
assertTrue(major > minor * 3D);
assertTrue(quiet > 1000);
assertTrue(dark > 500);
assertTrue(light > 500);
}
}
@@ -0,0 +1,92 @@
package art.arcane.iris.util.project.noise;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class KaleidoscopeNoiseTest {
@Test
public void wedgesMirrorAndRepeatAroundTheTileCenter() {
KaleidoscopeNoise noise = new KaleidoscopeNoise(0L);
for (int index = 0; index < 128; index++) {
double angle = index * 0.037D;
double radius = 0.05D + index * (0.4D / 128D);
double x = radius * Math.cos(angle);
double z = radius * Math.sin(angle);
double value = noise.noise(0.5D + x, z);
assertEquals(value, noise.noise(0.5D + x, -z), 1.0E-12D);
assertEquals(value, noise.noise(0.5D - z, x), 1.0E-12D);
}
}
@Test
public void compactMotifsMeetContinuouslyAtTileAndWedgeBoundaries() {
double epsilon = 1.0E-8D;
for (long seed : new long[]{0L, 1337L, Long.MIN_VALUE}) {
KaleidoscopeNoise noise = new KaleidoscopeNoise(seed);
for (int index = 0; index < 256; index++) {
double position = index * 0.027D - 3D;
double height = index * 0.017D - 2D;
assertEquals(noise.noise(-epsilon, height, position),
noise.noise(epsilon, height, position), 1.0E-6D);
assertEquals(noise.noise(position, height, 0.5D - epsilon),
noise.noise(position, height, 0.5D + epsilon), 1.0E-6D);
assertEquals(noise.noise(0.5D - epsilon, height, 0D),
noise.noise(0.5D + epsilon, height, 0D), 1.0E-5D);
}
}
KaleidoscopeNoise noise = new KaleidoscopeNoise(0L);
for (double boundary : new double[]{-Math.PI, -Math.PI * 0.75D, 0D, Math.PI * 0.25D, Math.PI}) {
assertEquals(noise.noise(0.5D + 0.3D * Math.cos(boundary - epsilon),
0.3D * Math.sin(boundary - epsilon)),
noise.noise(0.5D + 0.3D * Math.cos(boundary + epsilon),
0.3D * Math.sin(boundary + epsilon)), 1.0E-5D);
}
}
@Test
public void heightAndUpperSeedBitsChangeTheMotifs() {
KaleidoscopeNoise first = new KaleidoscopeNoise(1337L);
KaleidoscopeNoise second = new KaleidoscopeNoise(1337L + (1L << 32));
double heightVariation = 0D;
double seedVariation = 0D;
double maximum = 0D;
for (int index = 0; index < 512; index++) {
double x = index * 0.037D - 5D;
double z = index * -0.023D + 3D;
double value = first.noise(x, z);
assertEquals(value, first.noise(x, 0D, z), 0D);
assertEquals(value, first.noise(x, 1.0E-8D, z), 1.0E-5D);
heightVariation += Math.abs(value - first.noise(x, 1.25D, z));
seedVariation += Math.abs(value - second.noise(x, z));
maximum = Math.max(maximum, value);
}
assertTrue(heightVariation > 10D);
assertTrue(seedVariation > 10D);
assertTrue(maximum > 0.7D);
double oneDimensionalVariation = 0D;
for (int index = 0; index < 256; index++) {
oneDimensionalVariation += Math.abs(first.noise(index * 0.013D)
- second.noise(index * 0.013D));
}
assertTrue(oneDimensionalVariation > 10D);
}
@Test
public void octaveSamplesStayBoundedAndRepeatableAtWorldBorders() {
KaleidoscopeNoise first = new KaleidoscopeNoise(Long.MAX_VALUE);
KaleidoscopeNoise second = new KaleidoscopeNoise(Long.MAX_VALUE);
for (int octaves : new int[]{1, 3, 16}) {
first.setOctaves(octaves);
second.setOctaves(octaves);
for (int index = 0; index < 128; index++) {
double x = -468750D + index * (937500D / 127D);
double value = first.noise(x, x * 0.37D, x * -0.71D);
assertTrue(Double.isFinite(value));
assertTrue(value >= 0D && value <= 1D);
assertEquals(value, second.noise(x, x * 0.37D, x * -0.71D), 0D);
}
}
}
}
@@ -0,0 +1,29 @@
package art.arcane.iris.util.project.noise;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class MarbleNoiseTest {
@Test
public void narrowVeinsCrossPredominantlySoftStone() {
MarbleNoise noise = new MarbleNoise(1337L);
int dark = 0;
int soft = 0;
double minimum = 1D;
double maximum = 0D;
for (int row = 0; row < 128; row++) {
for (int column = 0; column < 128; column++) {
double value = noise.noise(column * 0.0625D - 4D, row * 0.0625D - 4D);
dark += value < 0.3D ? 1 : 0;
soft += value > 0.65D ? 1 : 0;
minimum = Math.min(minimum, value);
maximum = Math.max(maximum, value);
}
}
assertTrue(minimum < 0.15D);
assertTrue(maximum > 0.85D);
assertTrue(dark > 250 && dark < 2500);
assertTrue(soft > 10000);
}
}
@@ -0,0 +1,114 @@
package art.arcane.iris.util.project.noise;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class MaterialPatternNoiseTest {
@Test
public void materialPatternsStayBoundedSeededAndVolumetric() {
for (long seed : new long[]{0L, 813753L, Long.MIN_VALUE, Long.MAX_VALUE}) {
PatternNoise[] patterns = patterns(seed);
PatternNoise[] repeated = patterns(seed);
PatternNoise[] otherSeed = patterns(seed ^ (1L << 47));
for (int pattern = 0; pattern < patterns.length; pattern++) {
PatternNoise noise = patterns[pattern];
double seedDifference = 0D;
double heightDifference = 0D;
for (int octaves : new int[]{1, 4, 16}) {
noise.setOctaves(octaves);
repeated[pattern].setOctaves(octaves);
otherSeed[pattern].setOctaves(octaves);
for (double origin : new double[]{-468750D, 0D, 468750D}) {
for (int i = 0; i < 128; i++) {
double x = origin + i * 0.173D;
double y = i * 0.071D - 4D;
double z = -origin + i * 0.319D;
double value = noise.noise(x, y, z);
assertTrue(Double.isFinite(value) && value >= 0D && value <= 1D);
assertEquals(value, repeated[pattern].noise(x, y, z), 0D);
assertEquals(noise.noise(x, z), noise.noise(x, 0D, z), 0D);
seedDifference += Math.abs(value - otherSeed[pattern].noise(x, y, z));
heightDifference += Math.abs(value - noise.noise(x, y + 0.713D, z));
}
}
}
assertTrue(noise.getClass().getSimpleName(), seedDifference > 10D);
assertTrue(noise.getClass().getSimpleName(), heightDifference > 10D);
}
}
}
@Test
public void materialPatternsHaveNoCoordinateBoundaryJumps() {
for (long seed : new long[]{0L, 813753L, Long.MAX_VALUE}) {
for (PatternNoise noise : patterns(seed)) {
for (double boundary : new double[]{-468750D, -2D, 0D, 3D, 468750D}) {
for (int i = 0; i < 128; i++) {
double coordinate = i * 0.073D - 4D;
assertEquals(noise.noise(boundary - 1E-7D, 0.317D, coordinate),
noise.noise(boundary + 1E-7D, 0.317D, coordinate), 1E-4D);
assertEquals(noise.noise(coordinate, 0.317D, boundary - 1E-7D),
noise.noise(coordinate, 0.317D, boundary + 1E-7D), 1E-4D);
}
}
}
}
}
@Test
public void duneWindwardSlopeIsLongerAndHornsBendDownwind() {
DuneNoise dune = new DuneNoise(0L);
assertTrue(dune.noise(0.02D, 0.2D) > dune.noise(0.38D, 0.2D) + 0.2D);
double across = 0.2D + 0.32D * 0.65D;
double crest = 0.2D + 0.43D * 0.65D * 0.65D;
assertTrue(dune.noise(crest, across) > dune.noise(0.2D, across) + 0.05D);
assertTrue(dune.noise(0.2D, 0.2D) > dune.noise(crest, across));
}
@Test
public void woodKnotsCarryClosedGrowthRings() {
WoodNoise wood = new WoodNoise(0L);
double minimum = 1D;
double maximum = 0D;
for (int i = 0; i < 64; i++) {
double angle = i * Math.PI / 32D;
double across = 0.3D + Math.cos(angle) * 0.12D;
double along = 0.3D + Math.sin(angle) * 0.12D;
double x = across - 0.11D * Math.sin(along * 0.85D);
double value = wood.noise(x, along / 0.45D);
minimum = Math.min(minimum, value);
maximum = Math.max(maximum, value);
}
double brightRingX = 0.34D - 0.11D * Math.sin(0.3D * 0.85D);
assertTrue(maximum - minimum < 0.22D);
assertTrue(wood.noise(brightRingX, 0.3D / 0.45D) > maximum + 0.4D);
}
@Test
public void strataContainsRepeatedHighAndLowBands() {
StrataNoise strata = new StrataNoise(813753L);
int highBands = 0;
int lowBands = 0;
double previous = strata.noise(0D, -8D);
double current = strata.noise(0D, -8D + 0.008D);
for (int i = 2; i < 2048; i++) {
double next = strata.noise(0D, -8D + i * 0.008D);
if (current > previous && current > next && current > 0.75D) {
highBands++;
}
if (current < previous && current < next && current < 0.25D) {
lowBands++;
}
previous = current;
current = next;
}
assertTrue(highBands >= 6);
assertTrue(lowBands >= 6);
}
private static PatternNoise[] patterns(long seed) {
return new PatternNoise[]{new DuneNoise(seed), new StrataNoise(seed), new WoodNoise(seed)};
}
}
@@ -0,0 +1,83 @@
package art.arcane.iris.util.project.noise;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class MengerSpongeNoiseTest {
@Test
public void threeSubdivisionsRetainExactlyTwentyCubedCells() {
MengerSpongeNoise noise = new MengerSpongeNoise(0L);
int retained = 0;
for (int x = 0; x < 27; x++) {
for (int y = 0; y < 27; y++) {
for (int z = 0; z < 27; z++) {
retained += (int) noise.noise((x + 0.5D) / 27D,
(y + 0.5D) / 27D, (z + 0.5D) / 27D);
}
}
}
assertEquals(8000, retained);
}
@Test
public void faceSliceContainsThreeLevelsOfSquareHoles() {
MengerSpongeNoise noise = new MengerSpongeNoise(0L);
assertEquals(0D, noise.noise(0.5D, 0D, 0.5D), 0D);
assertEquals(0D, noise.noise(1D / 6D, 0D, 1D / 6D), 0D);
assertEquals(0D, noise.noise(1D / 18D, 0D, 1D / 18D), 0D);
assertEquals(1D, noise.noise(1D / 54D, 0D, 1D / 54D), 0D);
assertEquals(0D, noise.noise(0.5D, 0.5D, 0.05D), 0D);
assertEquals(0D, noise.noise(0.05D, 0.5D, 0.5D), 0D);
int retained = 0;
for (int x = 0; x < 27; x++) {
for (int z = 0; z < 27; z++) {
retained += (int) noise.noise((x + 0.5D) / 27D, (z + 0.5D) / 27D);
}
}
assertEquals(512, retained);
}
@Test
public void negativeTilesRepeatAndAllAxesAndUpperSeedBitsAffectGeometry() {
MengerSpongeNoise first = new MengerSpongeNoise(1337L);
MengerSpongeNoise second = new MengerSpongeNoise(1337L + (1L << 32));
double xVariation = 0D;
double yVariation = 0D;
double zVariation = 0D;
double seedVariation = 0D;
for (int index = 0; index < 512; index++) {
double x = index * 0.019D - 4D;
double z = index * -0.031D + 2D;
double value = first.noise(x, z);
assertEquals(value, first.noise(x, 0D, z), 0D);
assertEquals(value, first.noise(x - 2D, 0D, z + 3D), 0D);
xVariation += Math.abs(value - first.noise(x + 0.137D, z));
yVariation += Math.abs(value - first.noise(x, 0.419D, z));
zVariation += Math.abs(value - first.noise(x, z + 0.237D));
seedVariation += Math.abs(value - second.noise(x, z));
}
assertTrue(xVariation > 20D);
assertTrue(yVariation > 20D);
assertTrue(zVariation > 20D);
assertTrue(seedVariation > 20D);
}
@Test
public void octaveSamplesStayBoundedAndRepeatableAtWorldBorders() {
MengerSpongeNoise first = new MengerSpongeNoise(Long.MAX_VALUE);
MengerSpongeNoise second = new MengerSpongeNoise(Long.MAX_VALUE);
for (int octaves : new int[]{1, 3, 16}) {
first.setOctaves(octaves);
second.setOctaves(octaves);
for (int index = 0; index < 128; index++) {
double x = -468750D + index * (937500D / 127D);
double value = first.noise(x, x * 0.37D, x * -0.71D);
assertTrue(Double.isFinite(value));
assertTrue(value >= 0D && value <= 1D);
assertEquals(value, second.noise(x, x * 0.37D, x * -0.71D), 0D);
}
}
}
}
@@ -12,7 +12,9 @@ import static org.junit.Assert.assertTrue;
public class PatternNoiseTest {
private static final NoiseStyle[] STYLES = {
NoiseStyle.GYROID, NoiseStyle.QUASICRYSTAL, NoiseStyle.TRUCHET,
NoiseStyle.CRATER, NoiseStyle.VORTEX
NoiseStyle.CRATER, NoiseStyle.VORTEX,
NoiseStyle.DUNE, NoiseStyle.STRATA, NoiseStyle.WOOD, NoiseStyle.GABOR, NoiseStyle.MARBLE,
NoiseStyle.SCALES, NoiseStyle.CHLADNI, NoiseStyle.KALEIDOSCOPE, NoiseStyle.MENGER_SPONGE, NoiseStyle.CIRCUIT
};
@Test
@@ -100,7 +102,9 @@ public class PatternNoiseTest {
private static PatternNoise[] patterns(long seed) {
return new PatternNoise[]{new GyroidNoise(seed), new QuasicrystalNoise(seed),
new TruchetNoise(seed), new CraterNoise(seed), new VortexNoise(seed)};
new TruchetNoise(seed), new CraterNoise(seed), new VortexNoise(seed),
new DuneNoise(seed), new StrataNoise(seed), new WoodNoise(seed), new GaborNoise(seed), new MarbleNoise(seed),
new ScalesNoise(seed), new ChladniNoise(seed), new KaleidoscopeNoise(seed), new MengerSpongeNoise(seed), new CircuitNoise(seed)};
}
private static void assertUnit(PatternNoise noise, double value) {
@@ -0,0 +1,38 @@
package art.arcane.iris.util.project.noise;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ScalesNoiseTest {
private static final double ZERO_SEED_PHASE_Z = 0.8833108082136426D;
@Test
public void groovesTraceStaggeredSemicirclesAroundRaisedFaces() {
ScalesNoise noise = new ScalesNoise(0L);
for (int row = -3; row <= 3; row++) {
double x = (row & 1) * 0.5D;
double z = row * 0.5D - ZERO_SEED_PHASE_Z;
assertTrue(noise.noise(x, z + 0.2D) > 0.75D);
for (int point = 1; point < 12; point++) {
double angle = point * Math.PI / 12D;
assertTrue(noise.noise(x + Math.cos(angle) * 0.5D, z + Math.sin(angle) * 0.5D) < 0.24D);
}
}
}
@Test
public void staggeredRowAndColumnBoundariesRemainContinuous() {
ScalesNoise noise = new ScalesNoise(0L);
for (int row = -4; row <= 4; row++) {
double z = row * 0.5D - ZERO_SEED_PHASE_Z;
for (int point = 0; point < 512; point++) {
double x = point / 128D - 2D;
assertEquals(noise.noise(x, z - 1.0E-8D), noise.noise(x, z + 1.0E-8D), 1.0E-5D);
assertEquals(noise.noise(row * 0.5D - 1.0E-8D, x),
noise.noise(row * 0.5D + 1.0E-8D, x), 1.0E-5D);
}
}
}
}
@@ -0,0 +1,105 @@
package art.arcane.iris.util.project.noise;
import org.junit.Test;
import java.util.SplittableRandom;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class TextureNoiseTest {
@Test
public void texturesKeepUnitRangeAndExactHorizontalSlicesThroughSixteenOctaves() {
SplittableRandom random = new SplittableRandom(715L);
for (long seed : new long[]{0L, 1337L, Long.MIN_VALUE, Long.MAX_VALUE}) {
for (PatternNoise noise : textures(seed)) {
for (int octaves : new int[]{1, 4, 16}) {
noise.setOctaves(octaves);
for (int index = 0; index < 128; index++) {
double x = random.nextDouble(-468750D, 468750D);
double y = random.nextDouble(-32D, 32D);
double z = random.nextDouble(-468750D, 468750D);
double value = noise.noise(x, y, z);
assertTrue(noise.getClass().getSimpleName(), Double.isFinite(value) && value >= 0D && value <= 1D);
assertEquals(noise.noise(x, z), noise.noise(x, 0D, z), 0D);
assertEquals(noise.noise(x), noise.noise(x, 0D, 0D), 0D);
}
}
}
}
}
@Test
public void texturesUseTheFullSeedAndEveryCoordinate() {
PatternNoise[] first = textures(1337L);
PatternNoise[] same = textures(1337L);
PatternNoise[] highSeed = textures(1337L + (1L << 32));
for (int style = 0; style < first.length; style++) {
double seedDifference = 0D;
double xDifference = 0D;
double yDifference = 0D;
double zDifference = 0D;
for (int index = 0; index < 256; index++) {
double x = index * 0.073D - 4D;
double y = index * 0.011D - 1D;
double z = index * -0.113D + 3D;
double value = first[style].noise(x, y, z);
assertEquals(value, same[style].noise(x, y, z), 0D);
seedDifference += Math.abs(value - highSeed[style].noise(x, y, z));
xDifference += Math.abs(value - first[style].noise(x + 0.31D, y, z));
yDifference += Math.abs(value - first[style].noise(x, y + 0.37D, z));
zDifference += Math.abs(value - first[style].noise(x, y, z + 0.41D));
}
assertTrue(first[style].getClass().getSimpleName(), seedDifference > 4D);
assertTrue(first[style].getClass().getSimpleName(), xDifference > 4D);
assertTrue(first[style].getClass().getSimpleName(), yDifference > 4D);
assertTrue(first[style].getClass().getSimpleName(), zDifference > 4D);
}
}
@Test
public void octaveChangesAddDetailAndRestoreTheOriginalSamples() {
for (PatternNoise noise : textures(701L)) {
double[] original = new double[128];
for (int index = 0; index < original.length; index++) {
original[index] = noise.noise(index * 0.071D, 0.193D, index * -0.113D);
}
noise.setOctaves(4);
double difference = 0D;
for (int index = 0; index < original.length; index++) {
difference += Math.abs(original[index] - noise.noise(index * 0.071D, 0.193D, index * -0.113D));
}
assertTrue(noise.getClass().getSimpleName(), difference > 1D);
noise.setOctaves(0);
for (int index = 0; index < original.length; index++) {
assertEquals(original[index], noise.noise(index * 0.071D, 0.193D, index * -0.113D), 0D);
}
noise.setOctaves(16);
double maximum = noise.noise(0.173D, 0.193D, -0.391D);
noise.setOctaves(Integer.MAX_VALUE);
assertEquals(maximum, noise.noise(0.173D, 0.193D, -0.391D), 0D);
}
}
@Test
public void textureSamplingHasNoIntegerCellSeams() {
for (PatternNoise noise : textures(913L)) {
for (int boundary = -4; boundary <= 4; boundary++) {
for (int index = 0; index < 64; index++) {
double first = index * 0.073D - 2D;
double second = index * -0.113D + 3D;
assertEquals(noise.noise(boundary - 1.0E-8D, first, second),
noise.noise(boundary + 1.0E-8D, first, second), 1.0E-5D);
assertEquals(noise.noise(first, boundary - 1.0E-8D, second),
noise.noise(first, boundary + 1.0E-8D, second), 1.0E-5D);
assertEquals(noise.noise(first, second, boundary - 1.0E-8D),
noise.noise(first, second, boundary + 1.0E-8D), 1.0E-5D);
}
}
}
}
private static PatternNoise[] textures(long seed) {
return new PatternNoise[]{new GaborNoise(seed), new MarbleNoise(seed), new ScalesNoise(seed)};
}
}