Class PhantomNoise

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

public class PhantomNoise extends Object implements INoise
Arbitrary-dimensional continuous noise that maintains most of the same style even as the dimensionality gets fairly high. If you know what dimension of noise you need, and it's covered by Noise (meaning it's 2D, 3D, 4D, 5D, or 6D noise), then using Noise with the Noise.FOAM noise type will give you a significantly faster version of approximately the same algorithm. If your noise has an unknown dimension count, this won't really work either, since PhantomNoise needs to do some preparation and allocation for a specific dimension in its constructor. But, if you know what the range of dimensions is, and the lowest is at least 2D, you can make one PhantomNoise per dimension easily enough.
The algorithm this uses is surprisingly simple. To produce N-D noise, it makes N+1 calls to N-D value noise, with each call using a different rotation of the same size of grid. It "domain warps" value noise calls after the first, adding the previous value noise result to one coordinate of the next value noise call. To get a PhantomNoise result, it averages all the value noise calls and curves the output range so it doesn't get more biased toward 0 with higher dimensions (which would happen with a pure average of a rising number of variables). The curving here uses the MathTools.barronSpline(double, double, double) method for adjustable bias/gain; it looks like this for 2D, like this for 3D, and like this for 4D. There's some preparation this does in the constructor, which eliminates the need for allocations during noise generation. For N-D PhantomNoise, this makes N+1 double arrays, one for each rotation for a value noise call, and each rotation array has N items. The rotations match the vertices of an N-simplex, so a triangle in 2D, a tetrahedron in 3D, etc. It also stores two working-room arrays, each with N+1 double items, two frequently-edited int arrays of the floors of doubles it's using, and of modified versions of those floors to be hashed (each with N+1 items), and interestingly, a Hasher hash functor, seeded in the PhantomNoise constructor.
At higher dimensions, Simplex noise (what Noise.SIMPLEX produces) starts to change how it looks compared to lower dimensions. PhantomNoise, on the other hand, maintains a fairly consistent blob-like organic look, such as this 6D PhantomNoise sample. Red and purple mark the highest and lowest possible values, respectively, and while they appear plenty in PhantomNoise, they are absent in this 6D SeededNoise sample. There may be differences in how the inputs are handled between the two samples, but 6D Simplex generally suffers from "the curse of dimensionality" more-so than PhantomNoise (or Perlin noise like Noise.PERLIN, somewhat surprisingly), with the "curse" affecting the density of information in higher-dimensional space.
See Also:
  • Nested Class Summary

    Nested classes/interfaces inherited from interface INoise

    INoise.Serializer
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    int
    How many dimensions of noise to generate; usually at least 2.
    protected int[]
    Stores the floors of each input to valueNoise().
    com.github.tommyettinger.digital.Hasher
    Effectively, this contains the seed for the noise.
    protected int[]
    Passed to Hasher.hash(long, float[]) after modifications to get a random-seeming int for each vertex on the square-like grid in valueNoise().
    protected float[]
    Used with the INoise methods to assign individual arguments into a saved array, rather than allocating a new one.
    static final PhantomNoise
     
    static final PhantomNoise
     
    static final PhantomNoise
     
    static final PhantomNoise
     
    static final PhantomNoise
     
    static final PhantomNoise
     
    static final PhantomNoise
     
    protected float
    Just 1f / (dim + 1f) .
    protected float[]
    Stores the dim + 1 dot-products of each vertex with each input.
    float
    This should go up linearly with dimension, typically, and is usually 0.825 times dim.
    protected float[][]
    Stores dim + 1 vertices, each an array made of dim floats.
    protected float[]
    Assigned in getNoise() to a rotated version of the input, and used in valueNoise() to get the rotated-grid value noise result.
  • Constructor Summary

    Constructors
    Constructor
    Description
    Builds a PhantomNoise with the 0xFEEDBEEF1337CAFEL as its seed, that takes 3 inputs per noise call, and uses 0.825f * 3 as the sharpness (for contrast).
    PhantomNoise(long seed, int dimension)
    Builds a PhantomNoise with the specified seed, that takes dimension inputs per noise call, and uses 0.825f * dimension to determine the sharpness (for contrast).
    PhantomNoise(long seed, int dimension, float sharpness)
    Builds a PhantomNoise with the specified seed, that takes dimension inputs per noise call, and uses the given sharpness to determine contrast.
  • Method Summary

    Modifier and Type
    Method
    Description
    Creates a copy of this INoise, which should be a deep copy for any mutable state but can be shallow for immutable types such as functions.
    boolean
     
    int
    Gets the maximum dimension supported by this generator, such as 2 for a generator that only is defined for flat surfaces, or 7 for one that is defined up to the highest dimension this interface knows about (7D).
    int
    Gets the minimum dimension supported by this generator, such as 2 for a generator that only is defined for flat surfaces, or 3 for one that is only defined for 3D or higher-dimensional spaces.
    float
    getNoise(float... args)
     
    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
    getNoise(float x, float y, float z, float w, float u, float v, float m)
    Gets 7D noise with a default or pre-set seed.
    float
    getNoise2D(float x, float y)
     
    long
    Gets the current seed of the generator, as a long even if the seed is stored internally as an int.
    Returns a typically-four-character String constant that should uniquely identify this INoise as well as possible.
    boolean
    Returns true if this generator can be seeded with INoise.setSeed(long) during each call to obtain noise, or false if calling setSeed() is slow enough or allocates enough that alternative approaches should be used.
    float
    noise(int used, float... args)
     
    reassign(long seed, int dimension, float sharpness)
    Reassigns all members of this class at once, doing the same work the constructor does, but without creating a new PhantomNoise.
     
    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 INoise.stringSerialize(), reassigns this INoise to have the described state from the given String.
    Produces a String that describes everything needed to recreate this INoise in full.
    protected float
    valueNoise(int dim)
     
    protected float
     

    Methods inherited from class Object

    clone, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • hasher

      public com.github.tommyettinger.digital.Hasher hasher
      Effectively, this contains the seed for the noise.
    • dim

      public int dim
      How many dimensions of noise to generate; usually at least 2.
    • sharpness

      public float sharpness
      This should go up linearly with dimension, typically, and is usually 0.825 times dim. It can be raised to make the noise more starkly black and white, or lowered to have more mid-gray values.
    • inverse

      protected float inverse
      Just 1f / (dim + 1f) .
    • working

      protected transient float[] working
      Assigned in getNoise() to a rotated version of the input, and used in valueNoise() to get the rotated-grid value noise result. Has dim + 1 elements, but one is effectively a changing "seed-like" input to differentiate each rotation.
    • points

      protected transient float[] points
      Stores the dim + 1 dot-products of each vertex with each input.
    • input

      protected transient float[] input
      Used with the INoise methods to assign individual arguments into a saved array, rather than allocating a new one.
    • vertices

      protected transient float[][] vertices
      Stores dim + 1 vertices, each an array made of dim floats. These are the vertices of a simplex (an analogue to a triangle or tetrahedron) in dim dimensions.
    • floors

      protected transient int[] floors
      Stores the floors of each input to valueNoise(). These are used to compute cubic interpolations of working to smooth the value noise. This has dim + 1 items, because working stores an extra float to differentiate rotations.
    • hashFloors

      protected transient int[] hashFloors
      Passed to Hasher.hash(long, float[]) after modifications to get a random-seeming int for each vertex on the square-like grid in valueNoise(). Has dim + 1 items, because working stores an extra float to differentiate rotations.
    • instance2D

      public static final PhantomNoise instance2D
    • instance3D

      public static final PhantomNoise instance3D
    • instance4D

      public static final PhantomNoise instance4D
    • instance5D

      public static final PhantomNoise instance5D
    • instance6D

      public static final PhantomNoise instance6D
    • instance7D

      public static final PhantomNoise instance7D
    • instance8D

      public static final PhantomNoise instance8D
  • Constructor Details

    • PhantomNoise

      public PhantomNoise()
      Builds a PhantomNoise with the 0xFEEDBEEF1337CAFEL as its seed, that takes 3 inputs per noise call, and uses 0.825f * 3 as the sharpness (for contrast).
    • PhantomNoise

      public PhantomNoise(long seed, int dimension)
      Builds a PhantomNoise with the specified seed, that takes dimension inputs per noise call, and uses 0.825f * dimension to determine the sharpness (for contrast).
      Parameters:
      seed - may be any long; if the seed is the same and the same inputs are given, the noise will be identical
      dimension - how many inputs it will take each time getNoise(float...) is called
    • PhantomNoise

      public PhantomNoise(long seed, int dimension, float sharpness)
      Builds a PhantomNoise with the specified seed, that takes dimension inputs per noise call, and uses the given sharpness to determine contrast.
      Parameters:
      seed - may be any long; if the seed is the same and the same inputs are given, the noise will be identical
      dimension - how many inputs it will take each time getNoise(float...) is called
      sharpness - typically about 0.825f * dimension; higher makes more contrasting noise
  • Method Details

    • reassign

      public PhantomNoise reassign(long seed, int dimension, float sharpness)
      Reassigns all members of this class at once, doing the same work the constructor does, but without creating a new PhantomNoise. This can also avoid recreating arrays if dim is equal to dimension.
      Parameters:
      seed - may be any long; if the seed is the same and the same inputs are given, the noise will be identical
      dimension - how many inputs it will take each time getNoise(float...) is called
      sharpness - typically about 0.825f * dimension; higher makes more contrasting noise
      Returns:
      this PhantonNoise, after modification
    • getTag

      public String getTag()
      Description copied from interface: INoise
      Returns a typically-four-character String constant that should uniquely identify this INoise as well as possible. If a duplicate tag is already registered and INoise.Serializer.register(INoise) attempts to register the same tag again, a message is printed to System.err. The default implementation returns the String (NO), which is essentially an invalid tag, meant to indicate that this was not fully implemented. Implementing this is required for any usage of Serializer.
      Specified by:
      getTag in interface INoise
      Returns:
      a short String constant that identifies this INoise type
    • stringSerialize

      public String stringSerialize()
      Description copied from interface: INoise
      Produces a String that describes everything needed to recreate this INoise in full. This String can be read back in by INoise.stringDeserialize(String) to reassign the described state to another INoise. The syntax here should always start and end with the ` character, which is used by INoise.stringDeserialize(String) to identify the portion of a String that can be read back. The ` character should not be otherwise used unless to serialize another INoise that this uses.
      If you use Base to produce String representations for numeric fields in an INoise, Base.BASE10 is strongly recommended; in most cases, you can just use string concatenation with the fields separated by the tilde character, "~". For printing a float field with Base to a StringBuilder sb, use Base.BASE10.appendGeneral(sb, field).
      The default implementation throws an UnsupportedOperationException only. INoise classes do not have to implement any serialization methods, but they aren't serializable by the methods in this class or in INoise.Serializer unless they do implement this, INoise.getTag(), INoise.stringDeserialize(String), and INoise.copy().
      Specified by:
      stringSerialize in interface INoise
      Returns:
      a String that describes this INoise for serialization
    • stringDeserialize

      public PhantomNoise stringDeserialize(String data)
      Description copied from interface: INoise
      Given a serialized String produced by INoise.stringSerialize(), reassigns this INoise to have the described state from the given String. The serialized String must have been produced by the same class as this object is.
      Any class that implements INoise.stringSerialize() should also implement this method in a compatible way. Using base-10 is expected for most uses. Using Base.BASE10.readFloat(data, start, end) may be useful to parse only the part of data between start and end.
      The default implementation throws an UnsupportedOperationException only. INoise classes do not have to implement any serialization methods, but they aren't serializable by the methods in this class or in INoise.Serializer unless they do implement this, INoise.getTag(), INoise.stringSerialize(), and INoise.copy().
      Specified by:
      stringDeserialize in interface INoise
      Parameters:
      data - a serialized String, typically produced by INoise.stringSerialize()
      Returns:
      this INoise, after being modified (if possible)
    • recreateFromString

      public static PhantomNoise recreateFromString(String data)
    • copy

      public PhantomNoise copy()
      Description copied from interface: INoise
      Creates a copy of this INoise, 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.
      The default implementation throws an UnsupportedOperationException only. Implementors are strongly encouraged to implement this in general, and that is required to use an INoise class with INoise.Serializer.
      Specified by:
      copy in interface INoise
      Returns:
      a copy of this INoise
    • valueNoise

      protected float valueNoise(int dim)
    • valueNoise2D

      protected float valueNoise2D()
    • getNoise

      public float getNoise(float... args)
    • noise

      public float noise(int used, float... args)
    • getNoise2D

      public float getNoise2D(float x, float y)
    • equals

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

      public int getMinDimension()
      Description copied from interface: INoise
      Gets the minimum dimension supported by this generator, such as 2 for a generator that only is defined for flat surfaces, or 3 for one that is only defined for 3D or higher-dimensional spaces.
      Specified by:
      getMinDimension in interface INoise
      Returns:
      the minimum supported dimension, from 2 to 7 inclusive
    • getMaxDimension

      public int getMaxDimension()
      Description copied from interface: INoise
      Gets the maximum dimension supported by this generator, such as 2 for a generator that only is defined for flat surfaces, or 7 for one that is defined up to the highest dimension this interface knows about (7D).
      Specified by:
      getMaxDimension in interface INoise
      Returns:
      the maximum supported dimension, from 2 to 7 inclusive
    • hasEfficientSetSeed

      public boolean hasEfficientSetSeed()
      Description copied from interface: INoise
      Returns true if this generator can be seeded with INoise.setSeed(long) during each call to obtain noise, or false if calling setSeed() is slow enough or allocates enough that alternative approaches should be used. You can always call setSeed() on your own, but generators that don't have any seed won't do anything. Generators that return false for this method will generally behave differently when comparing how INoise.getNoiseWithSeed(float, float, long) changes the seed and how setSeed() does.
      Specified by:
      hasEfficientSetSeed in interface INoise
      Returns:
      whether INoise.setSeed(long) should be efficient to call in every INoise.getNoiseWithSeed(float, float, long) call
    • getSeed

      public long getSeed()
      Description copied from interface: INoise
      Gets the current seed of the generator, as a long even if the seed is stored internally as an int. This must be implemented, but if the generator doesn't have a seed that can be expressed as a long (potentially using BitConversion.floatToIntBits(float)), this can just return 0.
      Specified by:
      getSeed in interface INoise
      Returns:
      the current seed, as a long
    • setSeed

      public void setSeed(long seed)
      Description copied from interface: INoise
      Sets the seed to the given long, if long seeds are supported, or (int)seed if only int seeds are supported. If this generator cannot be seeded, this should do nothing, and should not throw an exception. If this operation allocates or is time-intensive, then INoise.hasEfficientSetSeed() should return false. That method is checked in INoise.getNoiseWithSeed(float, float, long), and if it returns false, the noise call will avoid calling setSeed(). You can always at least try to set the seed, even if it does nothing or is heavy on performance, and doing it a few times each frame should typically be fine for any generator. In the case this is called thousands of times each frame, check INoise.hasEfficientSetSeed().
      Specified by:
      setSeed in interface INoise
      Parameters:
      seed - a long or int seed, with no restrictions unless otherwise documented
    • 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
    • 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
    • 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
    • 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
    • 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
    • getNoise

      public float getNoise(float x, float y, float z, float w, float u, float v, float m)
      Description copied from interface: INoise
      Gets 7D 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
      m - m position; can be any finite float
      Returns:
      a noise value between -1.0f and 1.0f, both inclusive