Class PerlinNoise

java.lang.Object
com.github.yellowstonegames.grid.PerlinNoise
All Implemented Interfaces:
INoise, Externalizable, Serializable

public class PerlinNoise extends Object implements INoise
"Classic" Perlin noise, written by Ken Perlin before he created Simplex Noise, with minor adjustments. This uses quintic interpolation throughout (which was an improvement found in Simplex Noise), and has a single long seed. Perlin Noise can have significant grid-aligned and 45-degree-diagonal artifacts when too few octaves are used, but sometimes this is irrelevant, such as when sampling 3D noise on the surface of a sphere.
This variant also uses different gradient vectors than the ones used in "Improved Perlin Noise." Here all the gradient vectors are unit vectors, like in the original Perlin Noise, which makes some calculations regarding the range the functions can return easier... in theory. In practice, the somewhat-flawed gradient vectors used in earlier iterations of this PerlinNoise permitted a very different range calculation, and so this class uses a carefully-crafted sigmoid function to move around the most frequent values to roughly match earlier Perlin Noise results, but without risking going out-of-range. You can adjust the sigmoid function parameters for a given dimension using setEqualization(int, float); "add" parameters closer to 0 produce sharper results, and results closer to 1 produce results closer to un-adjusted noise. Each dimension typically needs a different parameter, because of how Perlin noise works; here, the defaults are (7-dim) * 0.2f / 1.75f for each dim between 2 and 6. Why these values work well enough isn't exactly clear; dividing by 2 instead of 1.75 makes the noise too sharp in 6D, for instance. Using any value larger than 1 for "add" is generally very similar to just using 1. Large values tend to produce "cloudier" or "blurrier" results.
See Also:
  • Nested Class Summary

    Nested classes/interfaces inherited from interface INoise

    INoise.Serializer
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final PerlinNoise
     
    static final float
     
    static final float
     
    static final float
     
    static final float
     
    static final float
     
    long
     
  • Constructor Summary

    Constructors
    Constructor
    Description
     
    PerlinNoise(long seed)
     
    PerlinNoise(long seed, float eq2, float eq3, float eq4, float eq5, float eq6)
    A constructor that allows specifying the equalization values for all dimensions this supports (2 through 6 inclusive), as well as the seed.
     
  • Method Summary

    Modifier and Type
    Method
    Description
    static float
    Gets the value to optimally use for mul in equalize(float, float, float), given the value that will be used as add there.
    Creates a copy of this PerlinNoise, which should be a deep copy for any mutable state but can be shallow for immutable types such as functions.
    static float
    emphasizeSigned(float a)
    Given a float a from -1.0 to 1.0 (both inclusive), this gets a float that adjusts a to be closer to the end points of that range (if less than 0, it gets closer to -1.0, otherwise it gets closer to 1.0).
    static float
    equalize(float x, float add, float mul)
    Given inputs as x in the range -1.0 to 1.0 that are too biased towards 0.0, this "squashes" the range softly to widen it and spread it away from 0.0 without increasing bias anywhere else.
    boolean
     
    float
    getEqualization(int dimension)
     
    int
    Gets the maximum dimension supported by this generator, which is 6.
    int
    Gets the minimum dimension supported by this generator, which is 2.
    float
    getNoise(float x, float y)
    Gets 2D noise with a default or pre-set seed.
    float
    getNoise(float x, float y, float z)
    Gets 3D noise with a default or pre-set seed.
    float
    getNoise(float x, float y, float z, float w)
    Gets 4D noise with a default or pre-set seed.
    float
    getNoise(float x, float y, float z, float w, float u)
    Gets 5D noise with a default or pre-set seed.
    float
    getNoise(float x, float y, float z, float w, float u, float v)
    Gets 6D noise with a default or pre-set seed.
    float
    getNoiseWithSeed(float x, float y, float z, float w, float u, float v, long seed)
    Gets 6D noise with a specific seed.
    float
    getNoiseWithSeed(float x, float y, float z, float w, float u, long seed)
    Gets 5D noise with a specific seed.
    float
    getNoiseWithSeed(float x, float y, float z, float w, long seed)
    Gets 4D noise with a specific seed.
    float
    getNoiseWithSeed(float x, float y, float z, long seed)
    Gets 3D noise with a specific seed.
    float
    getNoiseWithSeed(float x, float y, long seed)
    Gets 2D noise with a specific seed.
    long
    Gets the current seed of the generator, as a long.
    Returns the constant String "PerN" that identifies this in serialized Strings.
    protected static float
    gradCoord2D(long seed, int x, int y, float xd, float yd)
     
    protected static float
    gradCoord3D(long seed, int x, int y, int z, float xd, float yd, float zd)
     
    protected static float
    gradCoord4D(long seed, int x, int y, int z, int w, float xd, float yd, float zd, float wd)
     
    protected static float
    gradCoord5D(long seed, int x, int y, int z, int w, int u, float xd, float yd, float zd, float wd, float ud)
     
    protected static float
    gradCoord6D(long seed, int x, int y, int z, int w, int u, int v, float xd, float yd, float zd, float wd, float ud, float vd)
     
    boolean
    Returns true because this generator can be seeded with setSeed(long) and retrieved with getSeed().
    int
     
     
    void
    setEqualization(int dimension, float value)
     
    void
    setSeed(long seed)
    Sets the seed to the given long, if long seeds are supported, or (int)seed if only int seeds are supported.
    Given a serialized String produced by stringSerialize(), reassigns this PerlinNoise to have the described state from the given String.
    Produces a String that describes everything needed to recreate this INoise in full.
     
    static float
    towardsZero(float x)
    Given a float x, this returns the second-closest float to x in the direction of zero.

    Methods inherited from class Object

    clone, finalize, getClass, notify, notifyAll, wait, wait, wait

    Methods inherited from interface INoise

    getNoise, getNoiseWithSeed, readExternal, writeExternal
  • Field Details

  • Constructor Details

    • PerlinNoise

      public PerlinNoise()
    • PerlinNoise

      public PerlinNoise(long seed)
    • PerlinNoise

      public PerlinNoise(long seed, float eq2, float eq3, float eq4, float eq5, float eq6)
      A constructor that allows specifying the equalization values for all dimensions this supports (2 through 6 inclusive), as well as the seed. Equalization values are typically less than 1.0 and get smaller as the dimension increases.
      Parameters:
      seed - any long
      eq2 - defaults to 1.0f/1.75f
      eq3 - defaults to 0.8f/1.75f
      eq4 - defaults to 0.6f/1.75f
      eq5 - defaults to 0.4f/1.75f
      eq6 - defaults to 0.2f/1.75f
    • PerlinNoise

      public PerlinNoise(PerlinNoise other)
  • Method Details

    • towardsZero

      public static float towardsZero(float x)
      Given a float x, this returns the second-closest float to x in the direction of zero. If x is 0f, -0f, Float.MIN_VALUE, -Float.MIN_VALUE, or Float.MIN_VALUE - Float.MIN_VALUE, this is undefined and will probably produce an incorrect result. This is very similar to Math.nextAfter(x, 0.0), but is defined on more platforms, and skips the closest number in the direction of zero.
      This static method is likely to be moved to MathTools at some point.
      Parameters:
      x - a non-zero float that must be finite and should not be extremely close to 0
      Returns:
      a float closer to 0 than x
    • emphasizeSigned

      public static float emphasizeSigned(float a)
      Given a float a from -1.0 to 1.0 (both inclusive), this gets a float that adjusts a to be closer to the end points of that range (if less than 0, it gets closer to -1.0, otherwise it gets closer to 1.0).
      Used to increase the frequency of high and low results, which improves the behavior of ridged and billow noise.
      The actual numbers here are just slightly off of normal "quintic" interpolation, because this tries to avoid returning any numbers that are just slightly out-of-bounds, and has to make tiny adjustments to do so.
      This static method is likely to be moved to MathTools at some point.
      Parameters:
      a - a float between -1.0f and 1.0f inclusive
      Returns:
      a float between -1.0f and 1.0f inclusive that is more likely to be near the extremes
    • getMinDimension

      public int getMinDimension()
      Gets the minimum dimension supported by this generator, which is 2.
      Specified by:
      getMinDimension in interface INoise
      Returns:
      the minimum supported dimension, 2
    • getMaxDimension

      public int getMaxDimension()
      Gets the maximum dimension supported by this generator, which is 6.
      Specified by:
      getMaxDimension in interface INoise
      Returns:
      the maximum supported dimension, 6
    • hasEfficientSetSeed

      public boolean hasEfficientSetSeed()
      Returns true because this generator can be seeded with setSeed(long) and retrieved with getSeed().
      Specified by:
      hasEfficientSetSeed in interface INoise
      Returns:
      true
    • setSeed

      public void setSeed(long seed)
      Sets the seed to the given long, if long seeds are supported, or (int)seed if only int seeds are supported. If hasEfficientSetSeed() returns true, this must be implemented and must set the seed given a long input. If this generator cannot be seeded, this is permitted to either do nothing or throw an UnsupportedOperationException. If this operation allocates or is time-intensive, then that performance cost will be passed along to getNoiseWithSeed(float, float, long), since that calls this twice unless overridden. In the case where seeding is expensive to perform, setSeed() can still be implemented while hasEfficientSetSeed() returns false. This makes the getNoiseWithSeed(float, float, long) methods avoid reseeding, and instead move their inputs around in space.
      Specified by:
      setSeed in interface INoise
      Parameters:
      seed - a long or int seed, with no restrictions unless otherwise documented
    • getSeed

      public long getSeed()
      Gets the current seed of the generator, as a long.
      Specified by:
      getSeed in interface INoise
      Returns:
      the current seed, as a long
    • getTag

      public String getTag()
      Returns the constant String "PerN" that identifies this in serialized Strings.
      Specified by:
      getTag in interface INoise
      Returns:
      a short String constant that identifies this INoise type, "PerN"
    • stringSerialize

      public String stringSerialize()
      Produces a String that describes everything needed to recreate this INoise in full. This String can be read back in by stringDeserialize(String) to reassign the described state to another INoise.
      Specified by:
      stringSerialize in interface INoise
      Returns:
      a String that describes this PerlinNoise for serialization
    • stringDeserialize

      public PerlinNoise stringDeserialize(String data)
      Given a serialized String produced by stringSerialize(), reassigns this PerlinNoise to have the described state from the given String. The serialized String must have been produced by a PerlinNoise.
      Specified by:
      stringDeserialize in interface INoise
      Parameters:
      data - a serialized String, typically produced by stringSerialize()
      Returns:
      this PerlinNoise, after being modified (if possible)
    • recreateFromString

      public static PerlinNoise recreateFromString(String data)
    • copy

      public PerlinNoise copy()
      Creates a copy of this PerlinNoise, which should be a deep copy for any mutable state but can be shallow for immutable types such as functions. This almost always just calls a copy constructor.
      Specified by:
      copy in interface INoise
      Returns:
      a copy of this PerlinNoise
    • gradCoord2D

      protected static float gradCoord2D(long seed, int x, int y, float xd, float yd)
    • gradCoord3D

      protected static float gradCoord3D(long seed, int x, int y, int z, float xd, float yd, float zd)
    • gradCoord4D

      protected static float gradCoord4D(long seed, int x, int y, int z, int w, float xd, float yd, float zd, float wd)
    • gradCoord5D

      protected static float gradCoord5D(long seed, int x, int y, int z, int w, int u, float xd, float yd, float zd, float wd, float ud)
    • gradCoord6D

      protected static float gradCoord6D(long seed, int x, int y, int z, int w, int u, int v, float xd, float yd, float zd, float wd, float ud, float vd)
    • getEqualization

      public float getEqualization(int dimension)
      Parameters:
      dimension - between 2 and 6, inclusive
      Returns:
      the add value currently used to equalize noise
    • setEqualization

      public void setEqualization(int dimension, float value)
      Parameters:
      dimension - between 2 and 6, inclusive
      value - the add value to use when equalizing noise
    • equalize

      public static float equalize(float x, float add, float mul)
      Given inputs as x in the range -1.0 to 1.0 that are too biased towards 0.0, this "squashes" the range softly to widen it and spread it away from 0.0 without increasing bias anywhere else.
      This starts with a common sigmoid function, x / sqrt(add + x * x), but instead of approaching -1 and 1 but never reaching them, this multiplies the result so the line crosses -1 when x is -1, and crosses 1 when x is 1. It has a smooth derivative, if that matters to you.
      Parameters:
      x - a float between -1 and 1
      add - if greater than 1, this will have nearly no effect; the lower this goes below 1, the more this will separate results near the center of the range. This must be greater than or equal to 0.0
      mul - typically the result of calling calculateEqualizeAdjustment(float) on add
      Returns:
      a float with a slightly different distribution from x, but still between -1 and 1
    • calculateEqualizeAdjustment

      public static float calculateEqualizeAdjustment(float add)
      Gets the value to optimally use for mul in equalize(float, float, float), given the value that will be used as add there. If mul is calculated in some other way, inputs in the -1 to 1 range won't have outputs in the -1 to 1 range from equalize().
      This is mathematically the same as using 1f / equalize(1f, add, 1f), but has a faster implementation.
      Parameters:
      add - the value that will be used as add in a call to equalize(float, float, float)
      Returns:
      the value to use as mul in equalize(float, float, float)
    • getNoise

      public float getNoise(float x, float y)
      Description copied from interface: INoise
      Gets 2D noise with a default or pre-set seed.
      Specified by:
      getNoise in interface INoise
      Parameters:
      x - x position; can be any finite float
      y - y position; can be any finite float
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive
    • getNoiseWithSeed

      public float getNoiseWithSeed(float x, float y, long seed)
      Description copied from interface: INoise
      Gets 2D noise with a specific seed. If the seed cannot be retrieved or changed per-call, then this falls back to changing the position instead of the seed; you can check if this will happen with INoise.hasEfficientSetSeed().
      Specified by:
      getNoiseWithSeed in interface INoise
      Parameters:
      x - x position; can be any finite float
      y - y position; can be any finite float
      seed - can be any long
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive
    • getNoise

      public float getNoise(float x, float y, float z)
      Description copied from interface: INoise
      Gets 3D noise with a default or pre-set seed.
      Specified by:
      getNoise in interface INoise
      Parameters:
      x - x position; can be any finite float
      y - y position; can be any finite float
      z - z position; can be any finite float
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive
    • getNoiseWithSeed

      public float getNoiseWithSeed(float x, float y, float z, long seed)
      Description copied from interface: INoise
      Gets 3D noise with a specific seed. If the seed cannot be retrieved or changed per-call, then this falls back to changing the position instead of the seed; you can check if this will happen with INoise.hasEfficientSetSeed().
      Specified by:
      getNoiseWithSeed in interface INoise
      Parameters:
      x - x position; can be any finite float
      y - y position; can be any finite float
      z - z position; can be any finite float
      seed - can be any long
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive
    • getNoise

      public float getNoise(float x, float y, float z, float w)
      Description copied from interface: INoise
      Gets 4D noise with a default or pre-set seed.
      Specified by:
      getNoise in interface INoise
      Parameters:
      x - x position; can be any finite float
      y - y position; can be any finite float
      z - z position; can be any finite float
      w - w position; can be any finite float
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive
    • getNoiseWithSeed

      public float getNoiseWithSeed(float x, float y, float z, float w, long seed)
      Description copied from interface: INoise
      Gets 4D noise with a specific seed. If the seed cannot be retrieved or changed per-call, then this falls back to changing the position instead of the seed; you can check if this will happen with INoise.hasEfficientSetSeed().
      Specified by:
      getNoiseWithSeed in interface INoise
      Parameters:
      x - x position; can be any finite float
      y - y position; can be any finite float
      z - z position; can be any finite float
      w - w position; can be any finite float
      seed - can be any long
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive
    • getNoise

      public float getNoise(float x, float y, float z, float w, float u)
      Description copied from interface: INoise
      Gets 5D noise with a default or pre-set seed.
      Specified by:
      getNoise in interface INoise
      Parameters:
      x - x position; can be any finite float
      y - y position; can be any finite float
      z - z position; can be any finite float
      w - w position; can be any finite float
      u - u position; can be any finite float
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive
    • getNoiseWithSeed

      public float getNoiseWithSeed(float x, float y, float z, float w, float u, long seed)
      Description copied from interface: INoise
      Gets 5D noise with a specific seed. If the seed cannot be retrieved or changed per-call, then this falls back to changing the position instead of the seed; you can check if this will happen with INoise.hasEfficientSetSeed().
      Specified by:
      getNoiseWithSeed in interface INoise
      Parameters:
      x - x position; can be any finite float
      y - y position; can be any finite float
      z - z position; can be any finite float
      w - w position; can be any finite float
      u - u position; can be any finite float
      seed - can be any long
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive
    • getNoise

      public float getNoise(float x, float y, float z, float w, float u, float v)
      Description copied from interface: INoise
      Gets 6D noise with a default or pre-set seed.
      Specified by:
      getNoise in interface INoise
      Parameters:
      x - x position; can be any finite float
      y - y position; can be any finite float
      z - z position; can be any finite float
      w - w position; can be any finite float
      u - u position; can be any finite float
      v - v position; can be any finite float
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive
    • getNoiseWithSeed

      public float getNoiseWithSeed(float x, float y, float z, float w, float u, float v, long seed)
      Description copied from interface: INoise
      Gets 6D noise with a specific seed. If the seed cannot be retrieved or changed per-call, then this falls back to changing the position instead of the seed; you can check if this will happen with INoise.hasEfficientSetSeed().
      Specified by:
      getNoiseWithSeed in interface INoise
      Parameters:
      x - x position; can be any finite float
      y - y position; can be any finite float
      z - z position; can be any finite float
      w - w position; can be any finite float
      u - u position; can be any finite float
      v - v position; can be any finite float
      seed - can be any long
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive
    • equals

      public boolean equals(Object o)
      Specified by:
      equals in interface INoise
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object
    • toString

      public String toString()
      Overrides:
      toString in class Object