Class RotationTools

java.lang.Object
com.github.yellowstonegames.grid.RotationTools

public final class RotationTools extends Object
This has tools for generating and applying matrix rotations, potentially in higher dimensions than the typical 2 or 3. You can use randomRotation2D(long) to very quickly generate a single 2D rotation matrix, and to get a 3D matrix, you can either build on that 2D matrix using randomRotation3D(long, float[]) or use randomRotation3D(long) to make a completely new one. The same follows for making a 4D rotation using a 3D one, and so on. You can apply a rotation to a vector with rotate(float[], float[], float[]), and there's also a method to multiply square matrices in matrixMultiply(float[], float[], float[], int).
My head hurts. Thanks to spenc on the libGDX Discord for carefully guiding me through this code, among several other people who helped a lot.
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static class 
    A wrapper around similar logic to RotationTools, but with no allocation after construction.
  • Method Summary

    Modifier and Type
    Method
    Description
    static double[]
    fillRandomDoubleRotation(long seed, int dimension, double[] out)
    Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed.
    static double[]
    fillRandomDoubleRotation(Random random, int dimension, double[] out)
    Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed.
    static double[]
    fillRandomDoubleRotation2D(long seed, double[] out)
    Fills out with a 1D double array that can be used as a 2D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    fillRandomDoubleRotation2D(Random random, double[] out)
    Fills out with a 1D double array that can be used as a 2D rotation matrix by rotate(double[], double[], double[]).
    static float[]
    fillRandomRotation(long seed, int dimension, float[] out)
    Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed.
    static float[]
    fillRandomRotation(Random random, int dimension, float[] out)
    Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed.
    static float[]
    fillRandomRotation2D(long seed, float[] out)
    Fills out with a 1D float array that can be used as a 2D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    fillRandomRotation2D(Random random, float[] out)
    Fills out with a 1D float array that can be used as a 2D rotation matrix by rotate(float[], float[], float[]).
    static double[]
    matrixMultiply(double[] lf, double[] rt, double[] out, int side)
    Multiplies two square matrices with side length side, and stores the result in out.
    static float[]
    matrixMultiply(float[] lf, float[] rt, float[] out, int side)
    Multiplies two square matrices with side length side, and stores the result in out.
    static double[]
    randomDoubleRotation(long seed, int dimension)
    Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed.
    static double[]
    randomDoubleRotation(Random random, int dimension)
    Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed.
    static double[]
    Creates a new 1D double array that can be used as a 2D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 2D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 3D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    randomDoubleRotation3D(long seed, double[] rotation2D)
    Creates a new 1D double array that can be used as a 3D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 3D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    randomDoubleRotation3D(Random random, double[] rotation2D)
    Creates a new 1D double array that can be used as a 3D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 4D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    randomDoubleRotation4D(long seed, double[] rotation3D)
    Creates a new 1D double array that can be used as a 4D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 4D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    randomDoubleRotation4D(Random random, double[] rotation3D)
    Creates a new 1D double array that can be used as a 4D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 5D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    randomDoubleRotation5D(long seed, double[] rotation4D)
    Creates a new 1D double array that can be used as a 5D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 5D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    randomDoubleRotation5D(Random random, double[] rotation4D)
    Creates a new 1D double array that can be used as a 5D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 6D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    randomDoubleRotation6D(long seed, double[] rotation5D)
    Creates a new 1D double array that can be used as a 6D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 6D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    randomDoubleRotation6D(Random random, double[] rotation5D)
    Creates a new 1D double array that can be used as a 6D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 7D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    randomDoubleRotation7D(long seed, double[] rotation6D)
    Creates a new 1D double array that can be used as a 7D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    Creates a new 1D double array that can be used as a 7D rotation matrix by rotate(double[], double[], double[]).
    static double[]
    randomDoubleRotation7D(Random random, double[] rotation6D)
    Creates a new 1D double array that can be used as a 7D rotation matrix by rotate(double[], double[], double[]).
    static long
    randomize(long state)
    A more-specific variation on Hasher.randomize1(long), this expects its inputs to change by a very large amount between every call, such as 0x9E3779B97F4A7C15L.
    static float[]
    randomRotation(long seed, int dimension)
    Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed.
    static float[]
    randomRotation(Random random, int dimension)
    Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed.
    static float[]
    randomRotation2D(long seed)
    Creates a new 1D float array that can be used as a 2D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    Creates a new 1D float array that can be used as a 2D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation3D(long seed)
    Creates a new 1D float array that can be used as a 3D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation3D(long seed, float[] rotation2D)
    Creates a new 1D float array that can be used as a 3D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    Creates a new 1D float array that can be used as a 3D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation3D(Random random, float[] rotation2D)
    Creates a new 1D float array that can be used as a 3D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation4D(long seed)
    Creates a new 1D float array that can be used as a 4D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation4D(long seed, float[] rotation3D)
    Creates a new 1D float array that can be used as a 4D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    Creates a new 1D float array that can be used as a 4D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation4D(Random random, float[] rotation3D)
    Creates a new 1D float array that can be used as a 4D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation5D(long seed)
    Creates a new 1D float array that can be used as a 5D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation5D(long seed, float[] rotation4D)
    Creates a new 1D float array that can be used as a 5D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    Creates a new 1D float array that can be used as a 5D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation5D(Random random, float[] rotation4D)
    Creates a new 1D float array that can be used as a 5D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation6D(long seed)
    Creates a new 1D float array that can be used as a 6D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation6D(long seed, float[] rotation5D)
    Creates a new 1D float array that can be used as a 6D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    Creates a new 1D float array that can be used as a 6D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation6D(Random random, float[] rotation5D)
    Creates a new 1D float array that can be used as a 6D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation7D(long seed)
    Creates a new 1D float array that can be used as a 7D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation7D(long seed, float[] rotation6D)
    Creates a new 1D float array that can be used as a 7D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    Creates a new 1D float array that can be used as a 7D rotation matrix by rotate(float[], float[], float[]).
    static float[]
    randomRotation7D(Random random, float[] rotation6D)
    Creates a new 1D float array that can be used as a 7D rotation matrix by rotate(float[], float[], float[]).
    static double[]
    rotate(double[] input, double[] rotation, double[] output)
    A "raw" rotation method that takes a rotation matrix (as a row-major 1D double array), an input vector to rotate (as a 1D double array), and an output vector to write to (as a 1D double array), and does the math to rotate input using rotation, and add the results into output.
    static double[]
    rotate(double[] input, double[] rotation, double[] output, int offsetOut)
    A "raw" rotation method that takes a rotation matrix (as a row-major 1D double array), an input vector to rotate (as a 1D double array), an output vector to write to (as a 1D double array), and an offset into the output vector to start writing there, and does the math to rotate input using rotation, and add the results into output starting at offsetOut.
    static double[]
    rotate(double[] input, int offsetIn, int sizeIn, double[] rotation, double[] output, int offsetOut)
    A "raw" rotation method that takes a rotation matrix (as a row-major 1D double array), an input vector to rotate (as a 1D double array), an output vector to write to (as a 1D double array), and an offset into the output vector to start writing there, and does the math to rotate input using rotation, and add the results into output starting at offsetOut.
    static double[]
    rotate(double[] input, int offsetIn, int sizeIn, double[] rotation, int offsetRotation, int sizeRotation, double[] output, int offsetOut)
    A "raw" rotation method that takes a rotation matrix (as a row-major 1D double array), an input vector to rotate (as a 1D double array), an output vector to write to (as a 1D double array), and offsets/sizes for those arrays to use only part of each one, and does the math to rotate input using rotation, and add the results into output starting at offsetOut.
    static float[]
    rotate(float[] input, float[] rotation, float[] output)
    A "raw" rotation method that takes a rotation matrix (as a row-major 1D float array), an input vector to rotate (as a 1D float array), and an output vector to write to (as a 1D float array), and does the math to rotate input using rotation, and add the results into output.
    static float[]
    rotate(float[] input, float[] rotation, float[] output, int offsetOut)
    A "raw" rotation method that takes a rotation matrix (as a row-major 1D float array), an input vector to rotate (as a 1D float array), an output vector to write to (as a 1D float array), and an offset into the output vector to start writing there, and does the math to rotate input using rotation, and add the results into output starting at offsetOut.
    static float[]
    rotate(float[] input, int offsetIn, int sizeIn, float[] rotation, float[] output, int offsetOut)
    A "raw" rotation method that takes a rotation matrix (as a row-major 1D float array), an input vector to rotate (as a 1D float array), an output vector to write to (as a 1D float array), and an offset into the output vector to start writing there, and does the math to rotate input using rotation, and add the results into output starting at offsetOut.
    static float[]
    rotate(float[] input, int offsetIn, int sizeIn, float[] rotation, int offsetRotation, int sizeRotation, float[] output, int offsetOut)
    A "raw" rotation method that takes a rotation matrix (as a row-major 1D float array), an input vector to rotate (as a 1D float array), an output vector to write to (as a 1D float array), and offsets/sizes for those arrays to use only part of each one, and does the math to rotate input using rotation, and add the results into output starting at offsetOut.
    static double[]
    rotateStep(long seed, double[] small, int targetSize)
    This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small.
    static double[]
    rotateStep(long seed, double[] small, int targetSize, double[] gauss, double[] house, double[] large, double[] out)
    This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small.
    static float[]
    rotateStep(long seed, float[] small, int targetSize)
    This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small.
    static float[]
    rotateStep(long seed, float[] small, int targetSize, float[] gauss, float[] house, float[] large, float[] out)
    This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small.
    static double[]
    rotateStep(Random random, double[] small, int targetSize)
    This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number generator, and uses the seed and some matrix operations to generate a random rotation based on small.
    static double[]
    rotateStep(Random random, double[] small, int targetSize, double[] gauss, double[] house, double[] large, double[] out)
    This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small.
    static float[]
    rotateStep(Random random, float[] small, int targetSize)
    This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number generator, and uses the seed and some matrix operations to generate a random rotation based on small.
    static float[]
    rotateStep(Random random, float[] small, int targetSize, float[] gauss, float[] house, float[] large, float[] out)
    This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small.
    static double[]
    slerp(double[] start, double[] end, double alpha, double[] output)
    A geometric "slerp" (spherical linear interpolation) from the input n-dimensional point start to the point in the same dimension end, moving a fraction of the distance equal to alpha, and placing the result in output (modifying it in-place).
    static float[]
    slerp(float[] start, float[] end, float alpha, float[] output)
    A geometric "slerp" (spherical linear interpolation) from the input n-dimensional point start to the point in the same dimension end, moving a fraction of the distance equal to alpha, and placing the result in output (modifying it in-place).
    static double[]
    slerp(int n, double[] start, int startOffset, double[] end, int endOffset, double alpha, double[] output, int outputOffset)
    A geometric "slerp" (spherical linear interpolation) from the input n-dimensional point start to the point in the same dimension end, moving a fraction of the distance equal to alpha, and placing the result in output (modifying it in-place).
    static float[]
    slerp(int n, float[] start, int startOffset, float[] end, int endOffset, float alpha, float[] output, int outputOffset)
    A geometric "slerp" (spherical linear interpolation) from the input n-dimensional point start to the point in the same dimension end, moving a fraction of the distance equal to alpha, and placing the result in output (modifying it in-place).

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • rotate

      public static float[] rotate(float[] input, float[] rotation, float[] output)
      A "raw" rotation method that takes a rotation matrix (as a row-major 1D float array), an input vector to rotate (as a 1D float array), and an output vector to write to (as a 1D float array), and does the math to rotate input using rotation, and add the results into output. This does not erase output before writing to it, so it can be called more than once to sum multiple rotations if so desired. The length of output can be arbitrarily large, so this is complete when it has completely processed rotation. That means this affects rotation.length / input.length items in output. Typically, if input has length n and output should receive m changes, rotation has length n*m. This does no validation on rotation, hence why it is "raw" (also because it takes its inputs as unadorned 1D arrays).
      Parameters:
      input - an input vector of length n
      rotation - a rotation matrix of length n*m or greater
      output - the output vector of length m
      Returns:
      output, potentially after modification
    • rotate

      public static float[] rotate(float[] input, float[] rotation, float[] output, int offsetOut)
      A "raw" rotation method that takes a rotation matrix (as a row-major 1D float array), an input vector to rotate (as a 1D float array), an output vector to write to (as a 1D float array), and an offset into the output vector to start writing there, and does the math to rotate input using rotation, and add the results into output starting at offsetOut. This does not erase output before writing to it, so it can be called more than once to sum multiple rotations if so desired. The length of output can be arbitrarily large, so this is complete when it has completely processed rotation. That means this affects rotation.length / input.length items in output. Typically, if input has length n and output should receive m changes, rotation has length n*m. This does no validation on rotation, hence why it is "raw" (also because it takes its inputs as unadorned 1D arrays).
      Parameters:
      input - an input vector of length n
      rotation - a rotation matrix of length n*m
      output - the output vector of length m or greater; only rotation.length / input.length items will be written to
      offsetOut - the index in output to start writing the rotated output
      Returns:
      output, potentially after modification
    • rotate

      public static float[] rotate(float[] input, int offsetIn, int sizeIn, float[] rotation, float[] output, int offsetOut)
      A "raw" rotation method that takes a rotation matrix (as a row-major 1D float array), an input vector to rotate (as a 1D float array), an output vector to write to (as a 1D float array), and an offset into the output vector to start writing there, and does the math to rotate input using rotation, and add the results into output starting at offsetOut. This does not erase output before writing to it, so it can be called more than once to sum multiple rotations if so desired. The length of output can be arbitrarily large, so this is complete when it has completely processed rotation. That means this affects rotation.length / input.length items in output. Typically, if input has length n and output should receive m changes, rotation has length n*m. This does no validation on rotation, hence why it is "raw" (also because it takes its inputs as unadorned 1D arrays).
      Parameters:
      input - an input vector that has length at least equal to offsetIn + sizeIn
      offsetIn - the index in input to start reading
      sizeIn - how many elements to read from input
      rotation - a rotation matrix of length sizeIn * m, where m is the length of an output vector
      output - the output vector of length m or greater; only rotation.length / sizeIn items will be written to
      offsetOut - the index in output to start writing the rotated output
      Returns:
      output, potentially after modification
    • rotate

      public static float[] rotate(float[] input, int offsetIn, int sizeIn, float[] rotation, int offsetRotation, int sizeRotation, float[] output, int offsetOut)
      A "raw" rotation method that takes a rotation matrix (as a row-major 1D float array), an input vector to rotate (as a 1D float array), an output vector to write to (as a 1D float array), and offsets/sizes for those arrays to use only part of each one, and does the math to rotate input using rotation, and add the results into output starting at offsetOut. This does not erase output before writing to it, so it can be called more than once to sum multiple rotations if so desired. The length of output can be arbitrarily large, so this is complete when it has completely processed rotation. That means this affects sizeRotation / input.length items in output. Typically, if input has length n and output should receive m changes, sizeRotation is n*m. This does no validation on rotation, hence why it is "raw" (also because it takes its inputs as unadorned 1D arrays).
      Parameters:
      input - an input vector that has length at least equal to offsetIn + sizeIn
      offsetIn - the index in input to start reading
      sizeIn - how many items to read from input
      rotation - a rotation matrix of length sizeIn * m, where m is the length of an output vector
      offsetRotation - the first index in rotation to start reading a rotation matrix from
      sizeRotation - how many items to read from rotation to use as a rotation matrix
      output - the output vector of length m or greater; only sizeRotation / sizeIn items will be written to
      offsetOut - the index in output to start writing the rotated output
      Returns:
      output, potentially after modification
    • slerp

      public static float[] slerp(float[] start, float[] end, float alpha, float[] output)
      A geometric "slerp" (spherical linear interpolation) from the input n-dimensional point start to the point in the same dimension end, moving a fraction of the distance equal to alpha, and placing the result in output (modifying it in-place). This does not allocate. This has undefined behavior if start and end are polar opposites; that is, points where for any coordinate a in start, that coordinate in end is -a or any positive linear scale of the point where that is true. This degenerates to a linear interpolation if either start or end is the origin, and simply returns the start if both are the origin. Otherwise, this can smoothly move points that aren't already on the unit sphere towards the distance of the other point from the origin.
      Based on the non-approximation code from an article by Volodymyr Agafonkin. Note that this is the "geometric slerp" rather than the version using quaternions in 3D (or rotors in other dimensions). It has been augmented slightly to handle start and end vectors that don't have unit length.
      Parameters:
      start - an n-dimensional point, where start.length is n
      end - another n-dimensional point, where end.length is also the same n
      alpha - between 0 and 1, inclusive; how much to travel from start towards end
      output - the first n items in this will receive the interpolated position, modifying it in-place
      Returns:
      output, after modifications.
    • slerp

      public static float[] slerp(int n, float[] start, int startOffset, float[] end, int endOffset, float alpha, float[] output, int outputOffset)
      A geometric "slerp" (spherical linear interpolation) from the input n-dimensional point start to the point in the same dimension end, moving a fraction of the distance equal to alpha, and placing the result in output (modifying it in-place). This does not allocate. This has undefined behavior if start and end are polar opposites; that is, points where for any coordinate a in start, that coordinate in end is -a or any positive linear scale of the point where that is true. This degenerates to a linear interpolation if either start or end is the origin, and simply returns the start if both are the origin. Otherwise, this can smoothly move points that aren't already on the unit sphere towards the distance of the other point from the origin.
      Based on the non-approximation code from an article by Volodymyr Agafonkin. Note that this is the "geometric slerp" rather than the version using quaternions in 3D (or rotors in other dimensions). It has been augmented slightly to handle start and end vectors that don't have unit length.
      Parameters:
      n - the dimension of the points in start, end, and output; must be 2 or more
      start - an n-dimensional point to rotate from
      startOffset - what array index to start reading from in start
      end - another n-dimensional point to rotate to
      endOffset - what array index to start reading from in start
      alpha - between 0 and 1, inclusive; how much to travel from start towards end
      output - will be modified in-place so n items, starting at outputOffset, have the result
      outputOffset - what array index to start writing to in output
      Returns:
      output, after modifications.
    • matrixMultiply

      public static float[] matrixMultiply(float[] lf, float[] rt, float[] out, int side)
      Multiplies two square matrices with side length side, and stores the result in out. The inputs lf and rt are 1D float arrays treated as row-major matrices.
      Parameters:
      lf - the left input matrix, as row-major
      rt - the right input matrix, as row-major
      out - will be modified; this is where the output is summed into, and it is not cleared beforehand
      side - side length of each input matrix and the output matrix
      Returns:
      out, potentially after modification
    • randomize

      public static long randomize(long state)
      A more-specific variation on Hasher.randomize1(long), this expects its inputs to change by a very large amount between every call, such as 0x9E3779B97F4A7C15L. This is not as high-quality on its own as any of Hasher's randomize methods, but this class assumes the other random factors in things like Distributor.normal(long) will make up for any lacking quality here.
      Parameters:
      state - change this with randomize((state += 0x9E3779B97F4A7C15L))
      Returns:
      a long that has been deterministically randomized from state
    • rotateStep

      public static float[] rotateStep(long seed, float[] small, int targetSize)
      This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small. This allocates a new targetSize * targetSize-element float array on every call and returns it. It also allocates some more temporary float arrays to work with.
      This is not meant for usage outside this class, but if you are copying or modifying parts of the code in here, then you will probably need at least one of the rotateStep() methods.
      See Stack Exchange's links here, and Graphics Gems III (specifically, the part about fast random rotation matrices, not the part about the subgroup algorithm).
      Parameters:
      seed - random number generator seed
      small - a smaller square matrix than the result should be; must have side length targetSize - 1
      targetSize - the side length of the square matrix to be returned
      Returns:
      a 1D float array that can be treated as a rotation matrix for inputs of size targetSize
    • rotateStep

      public static float[] rotateStep(long seed, float[] small, int targetSize, float[] gauss, float[] house, float[] large, float[] out)
      This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small. To avoid allocating arrays on each call to this, this method also takes four float arrays that this will clear and modify, to be used as temporary workspace. As long as the last four arguments have enough length, their contents don't matter. While gauss must have length of at least targetSize, the last three must have length of at least targetSize * targetSize.
      This is not meant for usage outside this class, but if you are copying or modifying parts of the code in here, then you will probably need at least one of the rotateStep() methods.
      See Stack Exchange's links here, and Graphics Gems III (specifically, the part about fast random rotation matrices, not the part about the subgroup algorithm).
      Parameters:
      seed - random number generator seed; may be a long or an int
      small - a smaller square matrix than the result should be; must have side length targetSize - 1, and will not be modified
      targetSize - the side length of the square matrix to be returned
      gauss - a temporary float array that will be cleared; must have length of at least targetSize
      house - a temporary float array that will be cleared; must have length of at least targetSize * targetSize
      large - a temporary float array that will be cleared; must have length of at least targetSize * targetSize
      out - the float array that will be cleared and returned; must have length of at least targetSize * targetSize
      Returns:
      out, which can be treated as a rotation matrix for inputs of size targetSize
    • randomRotation2D

      public static float[] randomRotation2D(long seed)
      Creates a new 1D float array that can be used as a 2D rotation matrix by rotate(float[], float[], float[]). Uses the given seed to get an angle using TrigTools.SIN_TABLE.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 4-element float array, meant as effectively a 2D rotation matrix
    • fillRandomRotation2D

      public static float[] fillRandomRotation2D(long seed, float[] out)
      Fills out with a 1D float array that can be used as a 2D rotation matrix by rotate(float[], float[], float[]). Scrambles the given seed with randomize(long), then gets an angle using TrigTools.SIN_TABLE and TrigTools.COS_TABLE.
      Parameters:
      seed - any long; will be scrambled
      out - a float array that must have at least 4 elements; will be cleared and returned
      Returns:
      out, meant as effectively a 2D rotation matrix
    • randomRotation3D

      public static float[] randomRotation3D(long seed)
      Creates a new 1D float array that can be used as a 3D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE and Gaussian floats using Distributor.normal(long).
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 9-element float array, meant as effectively a 3D rotation matrix
    • randomRotation3D

      public static float[] randomRotation3D(long seed, float[] rotation2D)
      Creates a new 1D float array that can be used as a 3D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get Gaussian floats using Distributor.normal(long), and uses an existing 2D rotation matrix to avoid redoing any generation work already done for 2D. There will probably be some correlation between the appearance of the 2D rotation this will build upon and the 3D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 16-element float array, meant as effectively a 4D rotation matrix
    • randomRotation4D

      public static float[] randomRotation4D(long seed)
      Creates a new 1D float array that can be used as a 4D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE and Gaussian floats using Distributor.normal(long).
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 16-element float array, meant as effectively a 4D rotation matrix
    • randomRotation4D

      public static float[] randomRotation4D(long seed, float[] rotation3D)
      Creates a new 1D float array that can be used as a 4D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get Gaussian floats using Distributor.normal(long), and uses an existing 3D rotation matrix to avoid redoing any generation work already done for 3D. There will probably be some correlation between the appearance of the 3D rotation this will build upon and the 4D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 16-element float array, meant as effectively a 4D rotation matrix
    • randomRotation5D

      public static float[] randomRotation5D(long seed)
      Creates a new 1D float array that can be used as a 5D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE and Gaussian floats using Distributor.normal(long).
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 25-element float array, meant as effectively a 5D rotation matrix
    • randomRotation5D

      public static float[] randomRotation5D(long seed, float[] rotation4D)
      Creates a new 1D float array that can be used as a 5D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get Gaussian floats using Distributor.normal(long), and uses an existing 4D rotation matrix to avoid redoing any generation work already done for 4D. There will probably be some correlation between the appearance of the 4D rotation this will build upon and the 5D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 25-element float array, meant as effectively a 5D rotation matrix
    • randomRotation6D

      public static float[] randomRotation6D(long seed)
      Creates a new 1D float array that can be used as a 6D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE and Gaussian floats using Distributor.normal(long).
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 36-element float array, meant as effectively a 6D rotation matrix
    • randomRotation6D

      public static float[] randomRotation6D(long seed, float[] rotation5D)
      Creates a new 1D float array that can be used as a 6D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get Gaussian floats using Distributor.normal(long), and uses an existing 5D rotation matrix to avoid redoing any generation work already done for 5D. There will probably be some correlation between the appearance of the 5D rotation this will build upon and the 6D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 36-element float array, meant as effectively a 6D rotation matrix
    • randomRotation7D

      public static float[] randomRotation7D(long seed)
      Creates a new 1D float array that can be used as a 7D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE and Gaussian floats using Distributor.normal(long).
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 49-element float array, meant as effectively a 7D rotation matrix
    • randomRotation7D

      public static float[] randomRotation7D(long seed, float[] rotation6D)
      Creates a new 1D float array that can be used as a 7D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get Gaussian floats using Distributor.normal(long), and uses an existing 6D rotation matrix to avoid redoing any generation work already done for 6D. There will probably be some correlation between the appearance of the 5D rotation this will build upon and the 7D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      seed - any long; will be scrambled
      rotation6D - an existing 6D rotation matrix stored in a 1D float array; often produced by randomRotation6D(long)
      Returns:
      a newly-allocated 49-element float array, meant as effectively a 7D rotation matrix
    • randomRotation

      public static float[] randomRotation(long seed, int dimension)
      Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed. For dimensions 3 and higher, this allocates some temporary arrays once per call, but unlike methods such as randomRotation4D(long), this doesn't allocate per dimension. For dimension 2, this only allocates the array it returns.
      If allocation is a concern because you are making many random rotations, you may want to consider creating a RotationTools.Rotator and using it to rotate vectors instead of using rotate(float[], float[], float[]) directly. Rotator allocates its memory upon construction and doesn't allocate after that.
      Parameters:
      seed - any long; will be scrambled
      dimension - will be clamped to at minimum 2, but there is technically no maximum
      Returns:
      a newly-allocated dimension * dimension-element float array, meant as effectively a dimension-D rotation matrix
    • fillRandomRotation

      public static float[] fillRandomRotation(long seed, int dimension, float[] out)
      Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed. For dimensions 3 and higher, this allocates some temporary arrays once per call, but unlike methods such as randomRotation4D(long), this doesn't allocate per dimension. For dimension 2, this doesn't allocate at all if out has at least length 4 (so it can store the resulting matrix).
      If allocation is a concern because you are making many random rotations, you may want to consider creating a RotationTools.Rotator and using it to rotate vectors instead of using rotate(float[], float[], float[]) directly. Rotator allocates its memory upon construction and doesn't allocate after that.
      Parameters:
      seed - any long; will be scrambled
      dimension - will be clamped to at minimum 2, but there is technically no maximum
      out - a float array that should have at least dimension * dimension elements; will be modified
      Returns:
      out, after modifications, unless it was too small or null (then this returns a new array)
    • rotateStep

      public static float[] rotateStep(Random random, float[] small, int targetSize)
      This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number generator, and uses the seed and some matrix operations to generate a random rotation based on small. This allocates a new targetSize * targetSize-element float array on every call and returns it. It also allocates some more temporary float arrays to work with.
      This is not meant for usage outside this class, but if you are copying or modifying parts of the code in here, then you will probably need at least one of the rotateStep() methods.
      See Stack Exchange's links here, and Graphics Gems III (specifically, the part about fast random rotation matrices, not the part about the subgroup algorithm).
      Parameters:
      random - random number generator; any Random from the JDK or from the juniper library works
      small - a smaller square matrix than the result should be; must have side length targetSize - 1
      targetSize - the side length of the square matrix to be returned
      Returns:
      a 1D float array that can be treated as a rotation matrix for inputs of size targetSize
    • rotateStep

      public static float[] rotateStep(Random random, float[] small, int targetSize, float[] gauss, float[] house, float[] large, float[] out)
      This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small. To avoid allocating arrays on each call to this, this method also takes four float arrays that this will clear and modify, to be used as temporary workspace. As long as the last four arguments have enough length, their contents don't matter. While gauss must have length of at least targetSize, the last three must have length of at least targetSize * targetSize.
      This is not meant for usage outside this class, but if you are copying or modifying parts of the code in here, then you will probably need at least one of the rotateStep() methods.
      See Stack Exchange's links here, and Graphics Gems III (specifically, the part about fast random rotation matrices, not the part about the subgroup algorithm).
      Parameters:
      random - random number generator; any Random from the JDK or from the juniper library works
      small - a smaller square matrix than the result should be; must have side length targetSize - 1, and will not be modified
      targetSize - the side length of the square matrix to be returned
      gauss - a temporary float array that will be cleared; must have length of at least targetSize
      house - a temporary float array that will be cleared; must have length of at least targetSize * targetSize
      large - a temporary float array that will be cleared; must have length of at least targetSize * targetSize
      out - the float array that will be cleared and returned; must have length of at least targetSize * targetSize
      Returns:
      out, which can be treated as a rotation matrix for inputs of size targetSize
    • randomRotation2D

      public static float[] randomRotation2D(Random random)
      Creates a new 1D float array that can be used as a 2D rotation matrix by rotate(float[], float[], float[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE.
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 4-element float array, meant as effectively a 2D rotation matrix
    • fillRandomRotation2D

      public static float[] fillRandomRotation2D(Random random, float[] out)
      Fills out with a 1D float array that can be used as a 2D rotation matrix by rotate(float[], float[], float[]). Scrambles the given seed with randomize(long), then gets an angle using TrigTools.SIN_TABLE_D and TrigTools.COS_TABLE_D.
      Parameters:
      random - any Random from the JDK or from the juniper library
      out - a float array that must have at least 4 elements; will be cleared and returned
      Returns:
      out, meant as effectively a 2D rotation matrix
    • randomRotation3D

      public static float[] randomRotation3D(Random random)
      Creates a new 1D float array that can be used as a 3D rotation matrix by rotate(float[], float[], float[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 9-element float array, meant as effectively a 3D rotation matrix
    • randomRotation3D

      public static float[] randomRotation3D(Random random, float[] rotation2D)
      Creates a new 1D float array that can be used as a 3D rotation matrix by rotate(float[], float[], float[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 16-element float array, meant as effectively a 4D rotation matrix
    • randomRotation4D

      public static float[] randomRotation4D(Random random)
      Creates a new 1D float array that can be used as a 4D rotation matrix by rotate(float[], float[], float[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 16-element float array, meant as effectively a 4D rotation matrix
    • randomRotation4D

      public static float[] randomRotation4D(Random random, float[] rotation3D)
      Creates a new 1D float array that can be used as a 4D rotation matrix by rotate(float[], float[], float[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 16-element float array, meant as effectively a 4D rotation matrix
    • randomRotation5D

      public static float[] randomRotation5D(Random random)
      Creates a new 1D float array that can be used as a 5D rotation matrix by rotate(float[], float[], float[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 25-element float array, meant as effectively a 5D rotation matrix
    • randomRotation5D

      public static float[] randomRotation5D(Random random, float[] rotation4D)
      Creates a new 1D float array that can be used as a 5D rotation matrix by rotate(float[], float[], float[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 25-element float array, meant as effectively a 5D rotation matrix
    • randomRotation6D

      public static float[] randomRotation6D(Random random)
      Creates a new 1D float array that can be used as a 6D rotation matrix by rotate(float[], float[], float[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 36-element float array, meant as effectively a 6D rotation matrix
    • randomRotation6D

      public static float[] randomRotation6D(Random random, float[] rotation5D)
      Creates a new 1D float array that can be used as a 6D rotation matrix by rotate(float[], float[], float[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 36-element float array, meant as effectively a 6D rotation matrix
    • randomRotation7D

      public static float[] randomRotation7D(Random random)
      Creates a new 1D float array that can be used as a 7D rotation matrix by rotate(float[], float[], float[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE and Gaussian floats using Distributor.normal(long).
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 49-element float array, meant as effectively a 7D rotation matrix
    • randomRotation7D

      public static float[] randomRotation7D(Random random, float[] rotation6D)
      Creates a new 1D float array that can be used as a 7D rotation matrix by rotate(float[], float[], float[]). Uses the given Random random to get Gaussian floats using Distributor.normal(long), and uses an existing 6D rotation matrix to avoid redoing any generation work already done for 6D. There will probably be some correlation between the appearance of the 5D rotation this will build upon and the 7D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      random - any Random from the JDK or from the juniper library
      rotation6D - an existing 6D rotation matrix stored in a 1D float array; often produced by randomRotation6D(long)
      Returns:
      a newly-allocated 49-element float array, meant as effectively a 7D rotation matrix
    • randomRotation

      public static float[] randomRotation(Random random, int dimension)
      Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed. For dimensions 3 and higher, this allocates some temporary arrays once per call, but unlike methods such as randomRotation4D(long), this doesn't allocate per dimension. For dimension 2, this only allocates the array it returns.
      If allocation is a concern because you are making many random rotations, you may want to consider creating a RotationTools.Rotator and using it to rotate vectors instead of using rotate(float[], float[], float[]) directly. Rotator allocates its memory upon construction and doesn't allocate after that.
      Parameters:
      random - any Random, such as from the JDK or from the juniper library
      dimension - will be clamped to at minimum 2, but there is technically no maximum
      Returns:
      a newly-allocated dimension * dimension-element float array, meant as effectively a dimension-D rotation matrix
    • fillRandomRotation

      public static float[] fillRandomRotation(Random random, int dimension, float[] out)
      Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed. For dimensions 3 and higher, this allocates some temporary arrays once per call, but unlike methods such as randomRotation4D(long), this doesn't allocate per dimension. For dimension 2, this doesn't allocate at all if out has at least length 4 (so it can store the resulting matrix).
      If allocation is a concern because you are making many random rotations, you may want to consider creating a RotationTools.Rotator and using it to rotate vectors instead of using rotate(float[], float[], float[]) directly. Rotator allocates its memory upon construction and doesn't allocate after that.
      Parameters:
      random - any Random from the JDK or from the juniper library
      dimension - will be clamped to at minimum 2, but there is technically no maximum
      out - a float array that should have at least dimension * dimension elements; will be modified
      Returns:
      out, after modifications, unless it was too small or null (then this returns a new array)
    • rotate

      public static double[] rotate(double[] input, double[] rotation, double[] output)
      A "raw" rotation method that takes a rotation matrix (as a row-major 1D double array), an input vector to rotate (as a 1D double array), and an output vector to write to (as a 1D double array), and does the math to rotate input using rotation, and add the results into output. This does not erase output before writing to it, so it can be called more than once to sum multiple rotations if so desired. The length of output can be arbitrarily large, so this is complete when it has completely processed rotation. That means this affects rotation.length / input.length items in output. Typically, if input has length n and output should receive m changes, rotation has length n*m. This does no validation on rotation, hence why it is "raw" (also because it takes its inputs as unadorned 1D arrays).
      Parameters:
      input - an input vector of length n
      rotation - a rotation matrix of length n*m or greater
      output - the output vector of length m
    • rotate

      public static double[] rotate(double[] input, double[] rotation, double[] output, int offsetOut)
      A "raw" rotation method that takes a rotation matrix (as a row-major 1D double array), an input vector to rotate (as a 1D double array), an output vector to write to (as a 1D double array), and an offset into the output vector to start writing there, and does the math to rotate input using rotation, and add the results into output starting at offsetOut. This does not erase output before writing to it, so it can be called more than once to sum multiple rotations if so desired. The length of output can be arbitrarily large, so this is complete when it has completely processed rotation. That means this affects rotation.length / input.length items in output. Typically, if input has length n and output should receive m changes, rotation has length n*m. This does no validation on rotation, hence why it is "raw" (also because it takes its inputs as unadorned 1D arrays).
      Parameters:
      input - an input vector of length n
      rotation - a rotation matrix of length n*m
      output - the output vector of length m or greater; only rotation.length / input.length items will be written to
      offsetOut - the index in output to start writing the rotated output
    • rotate

      public static double[] rotate(double[] input, int offsetIn, int sizeIn, double[] rotation, double[] output, int offsetOut)
      A "raw" rotation method that takes a rotation matrix (as a row-major 1D double array), an input vector to rotate (as a 1D double array), an output vector to write to (as a 1D double array), and an offset into the output vector to start writing there, and does the math to rotate input using rotation, and add the results into output starting at offsetOut. This does not erase output before writing to it, so it can be called more than once to sum multiple rotations if so desired. The length of output can be arbitrarily large, so this is complete when it has completely processed rotation. That means this affects rotation.length / input.length items in output. If input has length n and output should receive m changes, rotation has length n*m. This does no validation on rotation, hence why it is "raw" (also because it takes its inputs as unadorned 1D arrays).
      Parameters:
      input - an input vector that has length at least equal to offsetIn + sizeIn
      offsetIn - the index in input to start reading
      sizeIn - how many elements to read from input
      rotation - a rotation matrix of length sizeIn * m, where m is the length of an output vector
      output - the output vector of length m or greater; only rotation.length / sizeIn items will be written to
      offsetOut - the index in output to start writing the rotated output
    • rotate

      public static double[] rotate(double[] input, int offsetIn, int sizeIn, double[] rotation, int offsetRotation, int sizeRotation, double[] output, int offsetOut)
      A "raw" rotation method that takes a rotation matrix (as a row-major 1D double array), an input vector to rotate (as a 1D double array), an output vector to write to (as a 1D double array), and offsets/sizes for those arrays to use only part of each one, and does the math to rotate input using rotation, and add the results into output starting at offsetOut. This does not erase output before writing to it, so it can be called more than once to sum multiple rotations if so desired. The length of output can be arbitrarily large, so this is complete when it has completely processed rotation. That means this affects sizeRotation / input.length items in output. Typically, if input has length n and output should receive m changes, sizeRotation is n*m. This does no validation on rotation, hence why it is "raw" (also because it takes its inputs as unadorned 1D arrays).
      Parameters:
      input - an input vector that has length at least equal to offsetIn + sizeIn
      offsetIn - the index in input to start reading
      sizeIn - how many items to read from input
      rotation - a rotation matrix of length sizeIn * m, where m is the length of an output vector
      offsetRotation - the first index in rotation to start reading a rotation matrix from
      sizeRotation - how many items to read from rotation to use as a rotation matrix
      output - the output vector of length m or greater; only sizeRotation / sizeIn items will be written to
      offsetOut - the index in output to start writing the rotated output
      Returns:
      output, potentially after modification
    • slerp

      public static double[] slerp(double[] start, double[] end, double alpha, double[] output)
      A geometric "slerp" (spherical linear interpolation) from the input n-dimensional point start to the point in the same dimension end, moving a fraction of the distance equal to alpha, and placing the result in output (modifying it in-place). This does not allocate. This has undefined behavior if start and end are polar opposites; that is, points where for any coordinate a in start, that coordinate in end is -a or any positive linear scale of the point where that is true. This degenerates to a linear interpolation if either start or end is the origin, and simply returns the start if both are the origin. Otherwise, this can smoothly move points that aren't already on the unit sphere towards the distance of the other point from the origin.
      Unlike the float version of this method, this calls Math.acos(double) and Math.sin(double) for higher precision. This is expected to be somewhat slower than using the approximations from TrigTools.
      Based on the non-approximation code from an article by Volodymyr Agafonkin. Note that this is the "geometric slerp" rather than the version using quaternions in 3D (or rotors in other dimensions). It has been augmented slightly to handle start and end vectors that don't have unit length.
      Parameters:
      start - an n-dimensional point, where start.length is n
      end - another n-dimensional point, where end.length is also the same n
      alpha - between 0 and 1, inclusive; how much to travel from start towards end
      output - the first n items in this will receive the interpolated position, modifying it in-place
      Returns:
      output, after modifications.
    • slerp

      public static double[] slerp(int n, double[] start, int startOffset, double[] end, int endOffset, double alpha, double[] output, int outputOffset)
      A geometric "slerp" (spherical linear interpolation) from the input n-dimensional point start to the point in the same dimension end, moving a fraction of the distance equal to alpha, and placing the result in output (modifying it in-place). This does not allocate. This has undefined behavior if start and end are polar opposites; that is, points where for any coordinate a in start, that coordinate in end is -a or any positive linear scale of the point where that is true. This degenerates to a linear interpolation if either start or end is the origin, and simply returns the start if both are the origin. Otherwise, this can smoothly move points that aren't already on the unit sphere towards the distance of the other point from the origin.
      Unlike the float version of this method, this calls Math.acos(double) and Math.sin(double) for higher precision. This is expected to be somewhat slower than using the approximations from TrigTools.
      Based on the non-approximation code from an article by Volodymyr Agafonkin. Note that this is the "geometric slerp" rather than the version using quaternions in 3D (or rotors in other dimensions). It has been augmented slightly to handle start and end vectors that don't have unit length.
      Parameters:
      n - the dimension of the points in start, end, and output; must be 2 or more
      start - an n-dimensional point to rotate from
      startOffset - what array index to start reading from in start
      end - another n-dimensional point to rotate to
      endOffset - what array index to start reading from in start
      alpha - between 0 and 1, inclusive; how much to travel from start towards end
      output - will be modified in-place so n items, starting at outputOffset, have the result
      outputOffset - what array index to start writing to in output
      Returns:
      output, after modifications.
    • matrixMultiply

      public static double[] matrixMultiply(double[] lf, double[] rt, double[] out, int side)
      Multiplies two square matrices with side length side, and stores the result in out. The inputs lf and rt are 1D double arrays treated as row-major matrices.
      Parameters:
      lf - the left input matrix, as row-major
      rt - the right input matrix, as row-major
      out - will be modified; this is where the output is summed into, and it is not cleared beforehand
      side - side length of each input matrix and the output matrix
    • rotateStep

      public static double[] rotateStep(long seed, double[] small, int targetSize)
      This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small. This allocates a new targetSize * targetSize-element double array on every call and returns it. It also allocates some more temporary double arrays to work with.
      This is not meant for usage outside this class, but if you are copying or modifying parts of the code in here, then you will probably need at least one of the rotateStep() methods.
      See Stack Exchange's links here, and Graphics Gems III (specifically, the part about fast random rotation matrices, not the part about the subgroup algorithm).
      Parameters:
      seed - random number generator seed
      small - a smaller square matrix than the result should be; must have side length targetSize - 1
      targetSize - the side length of the square matrix to be returned
      Returns:
      a 1D double array that can be treated as a rotation matrix for inputs of size targetSize
    • rotateStep

      public static double[] rotateStep(long seed, double[] small, int targetSize, double[] gauss, double[] house, double[] large, double[] out)
      This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small. To avoid allocating arrays on each call to this, this method also takes four double arrays that this will clear and modify, to be used as temporary workspace. As long as the last four arguments have enough length, their contents don't matter. While gauss must have length of at least targetSize, the last three must have length of at least targetSize * targetSize.
      This is not meant for usage outside this class, but if you are copying or modifying parts of the code in here, then you will probably need at least one of the rotateStep() methods.
      See Stack Exchange's links here, and Graphics Gems III (specifically, the part about fast random rotation matrices, not the part about the subgroup algorithm).
      Parameters:
      seed - random number generator seed; may be a long or an int
      small - a smaller square matrix than the result should be; must have side length targetSize - 1, and will not be modified
      targetSize - the side length of the square matrix to be returned
      gauss - a temporary double array that will be cleared; must have length of at least targetSize
      house - a temporary double array that will be cleared; must have length of at least targetSize * targetSize
      large - a temporary double array that will be cleared; must have length of at least targetSize * targetSize
      out - the double array that will be cleared and returned; must have length of at least targetSize * targetSize
      Returns:
      out, which can be treated as a rotation matrix for inputs of size targetSize
    • randomDoubleRotation2D

      public static double[] randomDoubleRotation2D(long seed)
      Creates a new 1D double array that can be used as a 2D rotation matrix by rotate(double[], double[], double[]). Uses the given seed to get an angle using TrigTools.SIN_TABLE_D.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 4-element double array, meant as effectively a 2D rotation matrix
    • fillRandomDoubleRotation2D

      public static double[] fillRandomDoubleRotation2D(long seed, double[] out)
      Fills out with a 1D double array that can be used as a 2D rotation matrix by rotate(double[], double[], double[]). Scrambles the given seed with randomize(long), then gets an angle using TrigTools.SIN_TABLE_D and TrigTools.COS_TABLE_D.
      Parameters:
      seed - any long; will be scrambled
      out - a double array that must have at least 4 elements; will be cleared and returned
      Returns:
      out, meant as effectively a 2D rotation matrix
    • randomDoubleRotation3D

      public static double[] randomDoubleRotation3D(long seed)
      Creates a new 1D double array that can be used as a 3D rotation matrix by rotate(double[], double[], double[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE_D and Gaussian doubles using Distributor.normal(long).
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 9-element double array, meant as effectively a 3D rotation matrix
    • randomDoubleRotation3D

      public static double[] randomDoubleRotation3D(long seed, double[] rotation2D)
      Creates a new 1D double array that can be used as a 3D rotation matrix by rotate(double[], double[], double[]). Uses the given long seed to get Gaussian doubles using Distributor.normal(long), and uses an existing 2D rotation matrix to avoid redoing any generation work already done for 2D. There will probably be some correlation between the appearance of the 2D rotation this will build upon and the 3D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 16-element double array, meant as effectively a 4D rotation matrix
    • randomDoubleRotation4D

      public static double[] randomDoubleRotation4D(long seed)
      Creates a new 1D double array that can be used as a 4D rotation matrix by rotate(double[], double[], double[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE_D and Gaussian doubles using Distributor.normal(long).
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 16-element double array, meant as effectively a 4D rotation matrix
    • randomDoubleRotation4D

      public static double[] randomDoubleRotation4D(long seed, double[] rotation3D)
      Creates a new 1D double array that can be used as a 4D rotation matrix by rotate(double[], double[], double[]). Uses the given long seed to get Gaussian doubles using Distributor.normal(long), and uses an existing 3D rotation matrix to avoid redoing any generation work already done for 3D. There will probably be some correlation between the appearance of the 3D rotation this will build upon and the 4D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 16-element double array, meant as effectively a 4D rotation matrix
    • randomDoubleRotation5D

      public static double[] randomDoubleRotation5D(long seed)
      Creates a new 1D double array that can be used as a 5D rotation matrix by rotate(double[], double[], double[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE_D and Gaussian doubles using Distributor.normal(long).
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 25-element double array, meant as effectively a 5D rotation matrix
    • randomDoubleRotation5D

      public static double[] randomDoubleRotation5D(long seed, double[] rotation4D)
      Creates a new 1D double array that can be used as a 5D rotation matrix by rotate(double[], double[], double[]). Uses the given long seed to get Gaussian doubles using Distributor.normal(long), and uses an existing 4D rotation matrix to avoid redoing any generation work already done for 4D. There will probably be some correlation between the appearance of the 4D rotation this will build upon and the 5D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 25-element double array, meant as effectively a 5D rotation matrix
    • randomDoubleRotation6D

      public static double[] randomDoubleRotation6D(long seed)
      Creates a new 1D double array that can be used as a 6D rotation matrix by rotate(double[], double[], double[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE_D and Gaussian doubles using Distributor.normal(long).
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 36-element double array, meant as effectively a 6D rotation matrix
    • randomDoubleRotation6D

      public static double[] randomDoubleRotation6D(long seed, double[] rotation5D)
      Creates a new 1D double array that can be used as a 6D rotation matrix by rotate(double[], double[], double[]). Uses the given long seed to get Gaussian doubles using Distributor.normal(long), and uses an existing 5D rotation matrix to avoid redoing any generation work already done for 5D. There will probably be some correlation between the appearance of the 5D rotation this will build upon and the 6D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 36-element double array, meant as effectively a 6D rotation matrix
    • randomDoubleRotation7D

      public static double[] randomDoubleRotation7D(long seed)
      Creates a new 1D double array that can be used as a 7D rotation matrix by rotate(double[], double[], double[]). Uses the given long seed to get an angle using TrigTools.SIN_TABLE_D and Gaussian doubles using Distributor.normal(long).
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 49-element double array, meant as effectively a 7D rotation matrix
    • randomDoubleRotation7D

      public static double[] randomDoubleRotation7D(long seed, double[] rotation6D)
      Creates a new 1D double array that can be used as a 7D rotation matrix by rotate(double[], double[], double[]). Uses the given long seed to get Gaussian doubles using Distributor.normal(long), and uses an existing 6D rotation matrix to avoid redoing any generation work already done for 6D. There will probably be some correlation between the appearance of the 6D rotation this will build upon and the 7D rotation this produces, but other factors may make this irrelevant when used for noise.
      Parameters:
      seed - any long; will be scrambled
      Returns:
      a newly-allocated 49-element double array, meant as effectively a 7D rotation matrix
    • randomDoubleRotation

      public static double[] randomDoubleRotation(long seed, int dimension)
      Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed. For dimensions 3 and higher, this allocates some temporary arrays once per call, but unlike methods such as randomDoubleRotation4D(long), this doesn't allocate per dimension. For dimension 2, this only allocates the array it returns.
      If allocation is a concern because you are making many random rotations, you may want to consider creating a RotationTools.Rotator and using it to rotate vectors instead of using rotate(double[], double[], double[]) directly. Rotator allocates its memory upon construction and doesn't allocate after that.
      Parameters:
      seed - any long; will be scrambled
      dimension - will be clamped to at minimum 2, but there is technically no maximum
      Returns:
      a newly-allocated dimension * dimension-element double array, meant as effectively a dimension-D rotation matrix
    • fillRandomDoubleRotation

      public static double[] fillRandomDoubleRotation(long seed, int dimension, double[] out)
      Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed. For dimensions 3 and higher, this allocates some temporary arrays once per call, but unlike methods such as randomRotation4D(long), this doesn't allocate per dimension. For dimension 2, this doesn't allocate at all if out has at least length 4 (so it can store the resulting matrix).
      If allocation is a concern because you are making many random rotations, you may want to consider creating a RotationTools.Rotator and using it to rotate vectors instead of using rotate(double[], double[], double[]) directly. Rotator allocates its memory upon construction and doesn't allocate after that.
      Parameters:
      seed - any long; will be scrambled
      dimension - will be clamped to at minimum 2, but there is technically no maximum
      out - a float array that should have at least dimension * dimension elements; will be modified
      Returns:
      out, after modifications, unless it was too small or null (then this returns a new array)
    • rotateStep

      public static double[] rotateStep(Random random, double[] small, int targetSize)
      This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number generator, and uses the seed and some matrix operations to generate a random rotation based on small. This allocates a new targetSize * targetSize-element double array on every call and returns it. It also allocates some more temporary double arrays to work with.
      This is not meant for usage outside this class, but if you are copying or modifying parts of the code in here, then you will probably need at least one of the rotateStep() methods.
      See Stack Exchange's links here, and Graphics Gems III (specifically, the part about fast random rotation matrices, not the part about the subgroup algorithm).
      Parameters:
      random - random number generator; any Random from the JDK or from the juniper library works
      small - a smaller square matrix than the result should be; must have side length targetSize - 1
      targetSize - the side length of the square matrix to be returned
      Returns:
      a 1D double array that can be treated as a rotation matrix for inputs of size targetSize
    • rotateStep

      public static double[] rotateStep(Random random, double[] small, int targetSize, double[] gauss, double[] house, double[] large, double[] out)
      This is just part of a larger rotation generator; it takes a target size (the side length of the matrix this will return), another matrix small (with a side length 1 less than targetSize), and a random number seed, and uses the seed and some matrix operations to generate a random rotation based on small. To avoid allocating arrays on each call to this, this method also takes four double arrays that this will clear and modify, to be used as temporary workspace. As long as the last four arguments have enough length, their contents don't matter. While gauss must have length of at least targetSize, the last three must have length of at least targetSize * targetSize.
      This is not meant for usage outside this class, but if you are copying or modifying parts of the code in here, then you will probably need at least one of the rotateStep() methods.
      See Stack Exchange's links here, and Graphics Gems III (specifically, the part about fast random rotation matrices, not the part about the subgroup algorithm).
      Parameters:
      random - random number generator; any Random from the JDK or from the juniper library works
      small - a smaller square matrix than the result should be; must have side length targetSize - 1, and will not be modified
      targetSize - the side length of the square matrix to be returned
      gauss - a temporary double array that will be cleared; must have length of at least targetSize
      house - a temporary double array that will be cleared; must have length of at least targetSize * targetSize
      large - a temporary double array that will be cleared; must have length of at least targetSize * targetSize
      out - the double array that will be cleared and returned; must have length of at least targetSize * targetSize
      Returns:
      out, which can be treated as a rotation matrix for inputs of size targetSize
    • randomDoubleRotation2D

      public static double[] randomDoubleRotation2D(Random random)
      Creates a new 1D double array that can be used as a 2D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D.
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 4-element double array, meant as effectively a 2D rotation matrix
    • fillRandomDoubleRotation2D

      public static double[] fillRandomDoubleRotation2D(Random random, double[] out)
      Fills out with a 1D double array that can be used as a 2D rotation matrix by rotate(double[], double[], double[]). Scrambles the given seed with randomize(long), then gets an angle using TrigTools.SIN_TABLE_D and TrigTools.COS_TABLE_D.
      Parameters:
      random - any Random from the JDK or from the juniper library
      out - a double array that must have at least 4 elements; will be cleared and returned
      Returns:
      out, meant as effectively a 2D rotation matrix
    • randomDoubleRotation3D

      public static double[] randomDoubleRotation3D(Random random)
      Creates a new 1D double array that can be used as a 3D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 9-element double array, meant as effectively a 3D rotation matrix
    • randomDoubleRotation3D

      public static double[] randomDoubleRotation3D(Random random, double[] rotation2D)
      Creates a new 1D double array that can be used as a 3D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 16-element double array, meant as effectively a 4D rotation matrix
    • randomDoubleRotation4D

      public static double[] randomDoubleRotation4D(Random random)
      Creates a new 1D double array that can be used as a 4D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 16-element double array, meant as effectively a 4D rotation matrix
    • randomDoubleRotation4D

      public static double[] randomDoubleRotation4D(Random random, double[] rotation3D)
      Creates a new 1D double array that can be used as a 4D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 16-element double array, meant as effectively a 4D rotation matrix
    • randomDoubleRotation5D

      public static double[] randomDoubleRotation5D(Random random)
      Creates a new 1D double array that can be used as a 5D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 25-element double array, meant as effectively a 5D rotation matrix
    • randomDoubleRotation5D

      public static double[] randomDoubleRotation5D(Random random, double[] rotation4D)
      Creates a new 1D double array that can be used as a 5D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 25-element double array, meant as effectively a 5D rotation matrix
    • randomDoubleRotation6D

      public static double[] randomDoubleRotation6D(Random random)
      Creates a new 1D double array that can be used as a 6D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 36-element double array, meant as effectively a 6D rotation matrix
    • randomDoubleRotation6D

      public static double[] randomDoubleRotation6D(Random random, double[] rotation5D)
      Creates a new 1D double array that can be used as a 6D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 36-element double array, meant as effectively a 6D rotation matrix
    • randomDoubleRotation7D

      public static double[] randomDoubleRotation7D(Random random)
      Creates a new 1D double array that can be used as a 7D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 49-element double array, meant as effectively a 7D rotation matrix
    • randomDoubleRotation7D

      public static double[] randomDoubleRotation7D(Random random, double[] rotation6D)
      Creates a new 1D double array that can be used as a 7D rotation matrix by rotate(double[], double[], double[]). Uses the given Random to get an angle using TrigTools.SIN_TABLE_D, and also calls Random.nextGaussian().
      Parameters:
      random - any Random from the JDK or from the juniper library
      Returns:
      a newly-allocated 49-element double array, meant as effectively a 7D rotation matrix
    • randomDoubleRotation

      public static double[] randomDoubleRotation(Random random, int dimension)
      Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed. For dimensions 3 and higher, this allocates some temporary arrays once per call, but unlike methods such as randomDoubleRotation4D(long), this doesn't allocate per dimension. For dimension 2, this only allocates the array it returns.
      If allocation is a concern because you are making many random rotations, you may want to consider creating a RotationTools.Rotator and using it to rotate vectors instead of using rotate(double[], double[], double[]) directly. Rotator allocates its memory upon construction and doesn't allocate after that.
      Parameters:
      random - any Random from the JDK or from the juniper library
      dimension - will be clamped to at minimum 2, but there is technically no maximum
      Returns:
      a newly-allocated dimension * dimension-element double array, meant as effectively a dimension-D rotation matrix
    • fillRandomDoubleRotation

      public static double[] fillRandomDoubleRotation(Random random, int dimension, double[] out)
      Iteratively calculates a rotation matrix for the given dimension, randomly generating it with the given seed. For dimensions 3 and higher, this allocates some temporary arrays once per call, but unlike methods such as randomRotation4D(long), this doesn't allocate per dimension. For dimension 2, this doesn't allocate at all if out has at least length 4 (so it can store the resulting matrix).
      If allocation is a concern because you are making many random rotations, you may want to consider creating a RotationTools.Rotator and using it to rotate vectors instead of using rotate(double[], double[], double[]) directly. Rotator allocates its memory upon construction and doesn't allocate after that.
      Parameters:
      random - any Random from the JDK or from the juniper library
      dimension - will be clamped to at minimum 2, but there is technically no maximum
      out - a float array that should have at least dimension * dimension elements; will be modified
      Returns:
      out, after modifications, unless it was too small or null (then this returns a new array)