From a8266f99b119bbd135f236af88f60897f7aee3e9 Mon Sep 17 00:00:00 2001 From: dfsek Date: Thu, 28 Jan 2021 15:26:17 -0700 Subject: [PATCH] implement NormalNormalizer --- .../com/dfsek/terra/api/math/MathUtil.java | 84 +++++++++++++++++++ .../LinearNormalizer.java | 4 +- .../noise/normalizer/NormalNormalizer.java | 43 ++++++++++ .../{samplers => normalizer}/Normalizer.java | 6 +- .../dfsek/terra/config/GenericLoaders.java | 2 +- .../loaders/config/NoiseBuilderLoader.java | 2 +- .../world/generation/config/NoiseBuilder.java | 16 +++- 7 files changed, 149 insertions(+), 8 deletions(-) rename common/src/main/java/com/dfsek/terra/api/math/noise/{samplers => normalizer}/LinearNormalizer.java (77%) create mode 100644 common/src/main/java/com/dfsek/terra/api/math/noise/normalizer/NormalNormalizer.java rename common/src/main/java/com/dfsek/terra/api/math/noise/{samplers => normalizer}/Normalizer.java (85%) diff --git a/common/src/main/java/com/dfsek/terra/api/math/MathUtil.java b/common/src/main/java/com/dfsek/terra/api/math/MathUtil.java index 7d2a2bcc1..7661ddcf1 100644 --- a/common/src/main/java/com/dfsek/terra/api/math/MathUtil.java +++ b/common/src/main/java/com/dfsek/terra/api/math/MathUtil.java @@ -98,4 +98,88 @@ public final class MathUtil { public static long squash(int first, int last) { return (((long) first) << 32) | (last & 0xffffffffL); } + + /** + * Clamp value to range of [-1, 1] + * + * @param in Value to clamp + * @return Clamped value + */ + public static double clamp(double in) { + return FastMath.min(FastMath.max(in, -1), 1); + } + + public static double compute(double p, double mu, double sigma) { + if(p < 0 || p > 1) + throw new IllegalArgumentException("Probability must be in range [0, 1]"); + if(sigma < 0) + throw new IllegalArgumentException("Standard deviation must be positive."); + if(p == 0) + return Double.NEGATIVE_INFINITY; + if(p == 1) + return Double.POSITIVE_INFINITY; + if(sigma == 0) + return mu; + double q, r, val; + + q = p - 0.5; + + if(FastMath.abs(q) <= .425) { + r = .180625 - q * q; + val = + q * (((((((r * 2509.0809287301226727 + + 33430.575583588128105) * r + 67265.770927008700853) * r + + 45921.953931549871457) * r + 13731.693765509461125) * r + + 1971.5909503065514427) * r + 133.14166789178437745) * r + + 3.387132872796366608) + / (((((((r * 5226.495278852854561 + + 28729.085735721942674) * r + 39307.89580009271061) * r + + 21213.794301586595867) * r + 5394.1960214247511077) * r + + 687.1870074920579083) * r + 42.313330701600911252) * r + 1); + } else { + if(q > 0) { + r = 1 - p; + } else { + r = p; + } + + r = FastMath.sqrt(-FastMath.log(r)); + + if(r <= 5) { + r += -1.6; + val = (((((((r * 7.7454501427834140764e-4 + + .0227238449892691845833) * r + .24178072517745061177) * + r + 1.27045825245236838258) * r + + 3.64784832476320460504) * r + 5.7694972214606914055) * + r + 4.6303378461565452959) * r + + 1.42343711074968357734) + / (((((((r * + 1.05075007164441684324e-9 + 5.475938084995344946e-4) * + r + .0151986665636164571966) * r + + .14810397642748007459) * r + .68976733498510000455) * + r + 1.6763848301838038494) * r + + 2.05319162663775882187) * r + 1); + } else { /* very close to 0 or 1 */ + r += -5; + val = (((((((r * 2.01033439929228813265e-7 + + 2.71155556874348757815e-5) * r + + .0012426609473880784386) * r + .026532189526576123093) * + r + .29656057182850489123) * r + + 1.7848265399172913358) * r + 5.4637849111641143699) * + r + 6.6579046435011037772) + / (((((((r * + 2.04426310338993978564e-15 + 1.4215117583164458887e-7) * + r + 1.8463183175100546818e-5) * r + + 7.868691311456132591e-4) * r + .0148753612908506148525) + * r + .13692988092273580531) * r + + .59983220655588793769) * r + 1); + } + + if(q < 0.0) { + val = -val; + } + } + + return mu + sigma * val; + } } diff --git a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/LinearNormalizer.java b/common/src/main/java/com/dfsek/terra/api/math/noise/normalizer/LinearNormalizer.java similarity index 77% rename from common/src/main/java/com/dfsek/terra/api/math/noise/samplers/LinearNormalizer.java rename to common/src/main/java/com/dfsek/terra/api/math/noise/normalizer/LinearNormalizer.java index bd56d7fac..814fb4ba7 100644 --- a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/LinearNormalizer.java +++ b/common/src/main/java/com/dfsek/terra/api/math/noise/normalizer/LinearNormalizer.java @@ -1,4 +1,6 @@ -package com.dfsek.terra.api.math.noise.samplers; +package com.dfsek.terra.api.math.noise.normalizer; + +import com.dfsek.terra.api.math.noise.samplers.NoiseSampler; public class LinearNormalizer extends Normalizer { private final double min; diff --git a/common/src/main/java/com/dfsek/terra/api/math/noise/normalizer/NormalNormalizer.java b/common/src/main/java/com/dfsek/terra/api/math/noise/normalizer/NormalNormalizer.java new file mode 100644 index 000000000..c388e8e55 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/math/noise/normalizer/NormalNormalizer.java @@ -0,0 +1,43 @@ +package com.dfsek.terra.api.math.noise.normalizer; + +import com.dfsek.terra.api.math.MathUtil; +import com.dfsek.terra.api.math.noise.samplers.NoiseSampler; +import net.jafama.FastMath; + +public class NormalNormalizer extends Normalizer { + + private final double[] lookup; + + public NormalNormalizer(NoiseSampler sampler, int buckets, double mean, double standardDeviation) { + super(sampler); + this.lookup = new double[buckets]; + + for(int i = 0; i < buckets; i++) { + lookup[i] = MathUtil.compute((double) i / buckets, mean, standardDeviation); + } + + } + + @Override + public double normalize(double in) { + int start = 0; + int end = lookup.length - 1; + while(start + 1 < end) { + int mid = start + (end - start) / 2; + if(lookup[mid] <= in) { + start = mid; + } else { + end = mid; + } + } + double left = FastMath.abs(lookup[start] - in); + double right = FastMath.abs(lookup[end] - in); + + double fin; + if(left <= right) { + fin = (double) start / (lookup.length); + } else fin = (double) end / (lookup.length); + + return (fin - 0.5) * 2; + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/Normalizer.java b/common/src/main/java/com/dfsek/terra/api/math/noise/normalizer/Normalizer.java similarity index 85% rename from common/src/main/java/com/dfsek/terra/api/math/noise/samplers/Normalizer.java rename to common/src/main/java/com/dfsek/terra/api/math/noise/normalizer/Normalizer.java index fbc2a4cdf..7e51f94f7 100644 --- a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/Normalizer.java +++ b/common/src/main/java/com/dfsek/terra/api/math/noise/normalizer/Normalizer.java @@ -1,4 +1,6 @@ -package com.dfsek.terra.api.math.noise.samplers; +package com.dfsek.terra.api.math.noise.normalizer; + +import com.dfsek.terra.api.math.noise.samplers.NoiseSampler; public abstract class Normalizer implements NoiseSampler { private final NoiseSampler sampler; @@ -30,6 +32,6 @@ public abstract class Normalizer implements NoiseSampler { } public enum NormalType { - LINEAR, NONE + LINEAR, NONE, NORMAL } } diff --git a/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java b/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java index 0b7e81f55..a7fc79501 100644 --- a/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java +++ b/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java @@ -6,7 +6,7 @@ import com.dfsek.terra.api.core.TerraPlugin; import com.dfsek.terra.api.math.GridSpawn; import com.dfsek.terra.api.math.ProbabilityCollection; import com.dfsek.terra.api.math.Range; -import com.dfsek.terra.api.math.noise.samplers.Normalizer; +import com.dfsek.terra.api.math.noise.normalizer.Normalizer; import com.dfsek.terra.api.world.palette.holder.PaletteHolder; import com.dfsek.terra.api.world.palette.holder.PaletteLayerHolder; import com.dfsek.terra.config.loaders.MaterialSetLoader; diff --git a/common/src/main/java/com/dfsek/terra/config/loaders/config/NoiseBuilderLoader.java b/common/src/main/java/com/dfsek/terra/config/loaders/config/NoiseBuilderLoader.java index 3b3b177f9..f2181eae9 100644 --- a/common/src/main/java/com/dfsek/terra/config/loaders/config/NoiseBuilderLoader.java +++ b/common/src/main/java/com/dfsek/terra/config/loaders/config/NoiseBuilderLoader.java @@ -5,8 +5,8 @@ import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.tectonic.exception.LoadException; import com.dfsek.tectonic.loading.ConfigLoader; import com.dfsek.tectonic.loading.TypeLoader; +import com.dfsek.terra.api.math.noise.normalizer.Normalizer; import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite; -import com.dfsek.terra.api.math.noise.samplers.Normalizer; import com.dfsek.terra.world.generation.config.NoiseBuilder; import java.lang.reflect.Type; diff --git a/common/src/main/java/com/dfsek/terra/world/generation/config/NoiseBuilder.java b/common/src/main/java/com/dfsek/terra/world/generation/config/NoiseBuilder.java index 31592022b..3ec3b2c9b 100644 --- a/common/src/main/java/com/dfsek/terra/world/generation/config/NoiseBuilder.java +++ b/common/src/main/java/com/dfsek/terra/world/generation/config/NoiseBuilder.java @@ -3,10 +3,11 @@ package com.dfsek.terra.world.generation.config; import com.dfsek.tectonic.annotations.Default; import com.dfsek.tectonic.annotations.Value; import com.dfsek.tectonic.config.ConfigTemplate; +import com.dfsek.terra.api.math.noise.normalizer.LinearNormalizer; +import com.dfsek.terra.api.math.noise.normalizer.NormalNormalizer; +import com.dfsek.terra.api.math.noise.normalizer.Normalizer; import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite; -import com.dfsek.terra.api.math.noise.samplers.LinearNormalizer; import com.dfsek.terra.api.math.noise.samplers.NoiseSampler; -import com.dfsek.terra.api.math.noise.samplers.Normalizer; @SuppressWarnings("FieldMayBeFinal") public class NoiseBuilder implements ConfigTemplate { @@ -94,6 +95,14 @@ public class NoiseBuilder implements ConfigTemplate { @Default private double linearMax = 1D; + @Value("normalize.normal.mean") + @Default + private double mean = 0D; + + @Value("normalize.normal.standard-deviation") + @Default + private double stdDev = 0.75D; + public NoiseSampler build(int seed) { FastNoiseLite noise = new FastNoiseLite(seed + seedOffset); @@ -121,7 +130,8 @@ public class NoiseBuilder implements ConfigTemplate { noise.setRotationType3D(rotationType3D); noise.setFrequency(frequency); - if(!normalType.equals(Normalizer.NormalType.NONE)) return new LinearNormalizer(noise, linearMin, linearMax); + if(normalType.equals(Normalizer.NormalType.LINEAR)) return new LinearNormalizer(noise, linearMin, linearMax); + else if(normalType.equals(Normalizer.NormalType.NORMAL)) return new NormalNormalizer(noise, 16384, mean, stdDev); return noise; }