com.github.tommyettinger.random.LineWobble Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of juniper Show documentation
Show all versions of juniper Show documentation
Serializable pseudo-random number generators and distributions.
/*
* Copyright (c) 2023 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.github.tommyettinger.random;
import com.github.tommyettinger.digital.BitConversion;
/**
* Provides 1D noise methods that can be queried at any point on a line to get a continuous random value.
* These each use some form of low-quality, high-speed unary hash on the floor and ceiling of a float or double value,
* then interpolate between the hash results.
*/
public class LineWobble {
/**
* A mix of the smooth transitions of a sine wave with (seeded) random peaks and valleys between -1.0 and
* 1.0 (both exclusive). The pattern this will produce will be completely different if the seed changes, and it is
* suitable for 1D noise. Uses a simple method of cubic interpolation between random values, where a random value is
* used without modification when given an integer for {@code value}. Note that this uses a different type of
* interpolation than a sine wave would, and the shape here uses cubic interpolation.
* @param seed a long seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a double that typically changes slowly, by less than 1.0, with direction changes at integer inputs
* @return a pseudo-random double between -1.0 and 1.0 (both exclusive), smoothly changing with value
*/
public static double wobble(long seed, double value)
{
long floor = (long) Math.floor(value);
// the closest long that is less than value
// gets a random start and endpoint. there's a sequence of start and end values for each seed, and changing the
// seed changes the start and end values unpredictably (so use the same seed for one curving line).
final long z = seed + floor * 0x6C8E9CF570932BD5L;
final double start = ((z ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L ^ 0x9E3779B97F4A7C15L) * 0x0.fffffffffffffbp-63,
end = ((z + 0x6C8E9CF570932BD5L ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L ^ 0x9E3779B97F4A7C15L) * 0x0.fffffffffffffbp-63;
// gets the fractional part of value
value -= floor;
// cubic interpolation to smooth the curve
value *= value * (3.0 - 2.0 * value);
// interpolate between start and end based on how far we are between the start and end points of this section
return (1.0 - value) * start + value * end;
}
/**
* A mix of the smooth transitions of a sine wave with (seeded) random peaks and valleys between -1f and
* 1f (both exclusive). The pattern this will produce will be completely different if the seed changes, and it is
* suitable for 1D noise. Uses a simple method of cubic interpolation between random values, where a random value is
* used without modification when given an integer for {@code value}. Note that this uses a different type of
* interpolation than a sine wave would, and the shape here uses cubic interpolation.
* @param seed a long seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a float that typically changes slowly, by less than 2.0, with direction changes at integer inputs
* @return a pseudo-random float between -1f and 1f (both exclusive), smoothly changing with value
*/
public static float wobble(long seed, float value)
{
final int floor = ((int)(value + 0x1p14) - 0x4000);
final long z = seed + floor * 0x6C8E9CF570932BD5L;
final float start = ((z ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L ^ 0x9E3779B97F4A7C15L) * 0x0.ffffffp-63f,
end = ((z + 0x6C8E9CF570932BD5L ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L ^ 0x9E3779B97F4A7C15L) * 0x0.ffffffp-63f;
value -= floor;
value *= value * (3f - 2f * value);
return (1f - value) * start + value * end;
}
/**
* A variant on {@link #wobble(long, double)} that takes an int seed instead of a long, and is optimized for
* usage on GWT. Like the version with a long seed, this uses cubic interpolation between random peak or valley
* points; only the method of generating those random peaks and valleys has changed.
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a double that typically changes slowly, by less than 2.0, with direction changes at integer inputs
* @return a pseudo-random double between -1.0 and 1.0 (both exclusive), smoothly changing with value
*/
public static double wobble(int seed, double value)
{
final int floor = (int) Math.floor(value);
int z = seed + floor * 0xBE56D;
final double start = ((z ^ 0xD1B54A35) * 0x1D2BC3 ^ 0xD1B54A35) * 0x0.fffffffffffffbp-31,
end = ((z + 0xBE56D ^ 0xD1B54A35) * 0x1D2BC3 ^ 0xD1B54A35) * 0x0.fffffffffffffbp-31;
value -= floor;
value *= value * (3.0 - 2.0 * value);
return (1.0 - value) * start + value * end;
}
/**
* A variant on {@link #wobble(long, float)} that takes an int seed instead of a long, and is optimized for
* usage on GWT. Like the version with a long seed, this uses cubic interpolation between random peak or valley
* points; only the method of generating those random peaks and valleys has changed.
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a float that typically changes slowly, by less than 2.0, with direction changes at integer inputs
* @return a pseudo-random float between -1f and 1f (both exclusive), smoothly changing with value
*/
public static float wobble(int seed, float value)
{
final int floor = ((int)(value + 0x1p14) - 0x4000);
int z = seed + floor * 0xBE56D;
final float start = ((z ^ 0xD1B54A35) * 0x1D2BC3 ^ 0xD1B54A35) * 0x0.ffffffp-31f,
end = ((z + 0xBE56D ^ 0xD1B54A35) * 0x1D2BC3 ^ 0xD1B54A35) * 0x0.ffffffp-31f;
value -= floor;
value *= value * (3 - 2 * value);
return (1 - value) * start + value * end;
}
/**
* Sway smoothly using bicubic interpolation between 4 points (the two integers before t and the two after).
* This pretty much never produces steep changes between peaks and valleys; this may make it more useful for things
* like generating terrain that can be walked across in a side-scrolling game.
* @param seed any int
* @param t a distance traveled; should change by less than 1 between calls, and should be less than about 10000
* @return a smoothly-interpolated swaying value between -1 and 1, both exclusive
*/
public static float bicubicWobble(int seed, float t)
{
// int fast floor, from libGDX
final int floor = ((int)(t + 0x1p14) - 0x4000);
// what we add here ensures that at the very least, the upper half will have some non-zero bits.
long s = seed + 0x9E3779B97F4A7C15L;
// fancy XOR-rotate-rotate is a way to mix bits both up and down without multiplication.
s = (s ^ (s << 21 | s >>> 43) ^ (s << 50 | s >>> 14)) + floor;
// we use a different technique here, relative to other wobble methods.
// to avoid frequent multiplication and replace it with addition by constants, we track 3 variables, each of
// which updates with a different large, negative long increment. when we want to get a result, we just XOR
// m, n, and o, and use only the upper bits (by multiplying by a tiny fraction).
final long m = s * 0xD1B54A32D192ED03L;
final long n = s * 0xABC98388FB8FAC03L;
final long o = s * 0x8CB92BA72F3D8DD7L;
//4.8186754E-20f == 0x0.FFFFFFp-63f * 0.4444444f; it gets about as close as we can go to 1.0
final float a = (m ^ n ^ o) * 4.8186754E-20f;
final float b = (m + 0xD1B54A32D192ED03L ^ n + 0xABC98388FB8FAC03L ^ o + 0x8CB92BA72F3D8DD7L) * 4.8186754E-20f;
final float c = (m + 0xA36A9465A325DA06L ^ n + 0x57930711F71F5806L ^ o + 0x1972574E5E7B1BAEL) * 4.8186754E-20f;
final float d = (m + 0x751FDE9874B8C709L ^ n + 0x035C8A9AF2AF0409L ^ o + 0xA62B82F58DB8A985L) * 4.8186754E-20f;
t -= floor;
// this is bicubic interpolation, inlined
final float p = (d - c) - (a - b);
return (t * (t * t * p + t * ((a - b) - p) + (c - a)) + b);
}
/**
* A 1D "noise" method that produces smooth transitions like a sine wave, but also wrapping around at pi * 2 so this
* can be used to get smoothly-changing random angles. Has (seeded) random peaks and valleys where it
* slows its range of change, but can return any value from 0 to 6.283185307179586f, or pi * 2. The pattern this
* will produce will be completely different if the seed changes, and the value is expected to be something other
* than an angle, like time. Uses a simple method of cubic interpolation between random values, where a random value
* is used without modification when given an integer for {@code value}. Note that this uses a different type of
* interpolation than a sine wave would, and the shape here uses cubic interpolation.
* @param seed a long seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a float that typically changes slowly, by less than 1.0, with possible direction changes at integer inputs
* @return a pseudo-random float between 0f and 6.283185307179586f (both inclusive), smoothly changing with value and wrapping
*/
public static float wobbleAngle(long seed, float value)
{
final int floor = ((int)(value + 0x1p14) - 0x4000);
final long z = seed + floor * 0x6C8E9CF570932BD5L;
float start = (((z ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L ^ 0x9E3779B97F4A7C15L) >>> 1) * 0x1p-63f,
end = (((z + 0x6C8E9CF570932BD5L ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L ^ 0x9E3779B97F4A7C15L) >>> 1) * 0x1p-63f;
value -= floor;
value *= value * (3f - 2f * value);
end = end - start + 1.5f;
end -= (int)end + 0.5f;
start += end * value + 1;
return (start - (int)start) * 6.283185307179586f;
}
/**
* A 1D "noise" method that produces smooth transitions like a sine wave, but also wrapping around at 360.0 so this
* can be used to get smoothly-changing random angles. Has (seeded) random peaks and valleys where it
* slows its range of change, but can return any value from 0 to 360.0f . The pattern this
* will produce will be completely different if the seed changes, and the value is expected to be something other
* than an angle, like time. Uses a simple method of cubic interpolation between random values, where a random value
* is used without modification when given an integer for {@code value}. Note that this uses a different type of
* interpolation than a sine wave would, and the shape here uses cubic interpolation.
* @param seed a long seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a float that typically changes slowly, by less than 1.0, with possible direction changes at integer inputs
* @return a pseudo-random float between 0f and 360.0f (both inclusive), smoothly changing with value and wrapping
*/
public static float wobbleAngleDeg(long seed, float value)
{
final int floor = ((int)(value + 0x1p14) - 0x4000);
final long z = seed + floor * 0x6C8E9CF570932BD5L;
float start = (((z ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L ^ 0x9E3779B97F4A7C15L) >>> 1) * 0x1p-63f,
end = (((z + 0x6C8E9CF570932BD5L ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L ^ 0x9E3779B97F4A7C15L) >>> 1) * 0x1p-63f;
value -= floor;
value *= value * (3f - 2f * value);
end = end - start + 1.5f;
end -= (int)end + 0.5f;
start += end * value + 1;
return (start - (int)start) * 360.0f;
}
/**
* A 1D "noise" method that produces smooth transitions like a sine wave, but also wrapping around at 1.0
* so this can be used to get smoothly-changing random angles in turns. Has (seeded) random peaks and valleys where
* it slows its range of change, but can return any value from 0 to 1.0. The pattern this
* will produce will be completely different if the seed changes, and the value is expected to be something other
* than an angle, like time. Uses a simple method of cubic interpolation between random values, where a random value
* is used without modification when given an integer for {@code value}. Note that this uses a different type of
* interpolation than a sine wave would, and the shape here uses cubic interpolation.
* @param seed a long seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a float that typically changes slowly, by less than 1.0, with possible direction changes at integer inputs
* @return a pseudo-random float between 0.0f and 1.0f (both inclusive), smoothly changing with value and wrapping
*/
public static float wobbleAngleTurns(long seed, float value)
{
final int floor = ((int)(value + 0x1p14) - 0x4000);
final long z = seed + floor * 0x6C8E9CF570932BD5L;
float start = (((z ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L ^ 0x9E3779B97F4A7C15L) >>> 1) * 0x1p-63f,
end = (((z + 0x6C8E9CF570932BD5L ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L ^ 0x9E3779B97F4A7C15L) >>> 1) * 0x1p-63f;
value -= floor;
value *= value * (3f - 2f * value);
end = end - start + 1.5f;
end -= (int)end + 0.5f;
start += end * value + 1;
return (start - (int)start);
}
/**
* A 1D "noise" method that produces smooth transitions like a sine wave, but also wrapping around at pi * 2 so this
* can be used to get smoothly-changing random angles. Has (seeded) random peaks and valleys where it
* slows its range of change, but can return any value from 0 to 6.283185307179586f, or pi * 2. The pattern this
* will produce will be completely different if the seed changes, and the value is expected to be something other
* than an angle, like time. Uses a simple method of cubic interpolation between random values, where a random value
* is used without modification when given an integer for {@code value}. Note that this uses a different type of
* interpolation than a sine wave would, and the shape here uses cubic interpolation.
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a float that typically changes slowly, by less than 1.0, with possible direction changes at integer inputs
* @return a pseudo-random float between 0f and 6.283185307179586f (both inclusive), smoothly changing with value and wrapping
*/
public static float wobbleAngle(int seed, float value)
{
// int fast floor, from libGDX
final int floor = ((int)(value + 0x1p14) - 0x4000);
// the above is equivalent to the following for floats between -16384 and 2147467263:
// int floor = (int) value;
// if(value < floor) --floor;
// gets roughly-random values for the start and end, involving the seed also.
int z = seed + floor * 0xBE56D;
float start = (((z ^ 0xD1B54A35) * 0x1D2BC3 ^ 0xD1B54A35) >>> 1) * 0x1p-31f,
end = (((z + 0xBE56D ^ 0xD1B54A35) * 0x1D2BC3 ^ 0xD1B54A35) >>> 1) * 0x1p-31f;
value -= floor;
// makes the changes smoother by slowing down near start or end.
value *= value * (3f - 2f * value);
// lerpAngle code
end = end - start + 1.5f;
end -= (int)end + 0.5f;
start += end * value + 1;
return (start - (int)start) * 6.283185307179586f;
}
/**
* A 1D "noise" method that produces smooth transitions like a sine wave, but also wrapping around at 360.0 so this
* can be used to get smoothly-changing random angles. Has (seeded) random peaks and valleys where it
* slows its range of change, but can return any value from 0 to 360.0f . The pattern this
* will produce will be completely different if the seed changes, and the value is expected to be something other
* than an angle, like time. Uses a simple method of cubic interpolation between random values, where a random value
* is used without modification when given an integer for {@code value}. Note that this uses a different type of
* interpolation than a sine wave would, and the shape here uses cubic interpolation.
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a float that typically changes slowly, by less than 1.0, with possible direction changes at integer inputs
* @return a pseudo-random float between 0f and 360.0f (both inclusive), smoothly changing with value and wrapping
*/
public static float wobbleAngleDeg(int seed, float value)
{
final int floor = ((int)(value + 0x1p14) - 0x4000);
int z = seed + floor * 0xBE56D;
float start = (((z ^ 0xD1B54A35) * 0x1D2BC3 ^ 0xD1B54A35) >>> 1) * 0x1p-31f,
end = (((z + 0xBE56D ^ 0xD1B54A35) * 0x1D2BC3 ^ 0xD1B54A35) >>> 1) * 0x1p-31f;
value -= floor;
value *= value * (3f - 2f * value);
end = end - start + 1.5f;
end -= (int)end + 0.5f;
start += end * value + 1;
return (start - (int)start) * 360.0f;
}
/**
* A 1D "noise" method that produces smooth transitions like a sine wave, but also wrapping around at 1.0
* so this can be used to get smoothly-changing random angles in turns. Has (seeded) random peaks and valleys where
* it slows its range of change, but can return any value from 0 to 1.0. The pattern this
* will produce will be completely different if the seed changes, and the value is expected to be something other
* than an angle, like time. Uses a simple method of cubic interpolation between random values, where a random value
* is used without modification when given an integer for {@code value}. Note that this uses a different type of
* interpolation than a sine wave would, and the shape here uses cubic interpolation.
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a float that typically changes slowly, by less than 1.0, with possible direction changes at integer inputs
* @return a pseudo-random float between 0.0f and 1.0f (both inclusive), smoothly changing with value and wrapping
*/
public static float wobbleAngleTurns(int seed, float value)
{
final int floor = ((int)(value + 0x1p14) - 0x4000);
int z = seed + floor * 0xBE56D;
float start = (((z ^ 0xD1B54A35) * 0x1D2BC3 ^ 0xD1B54A35) >>> 1) * 0x1p-31f,
end = (((z + 0xBE56D ^ 0xD1B54A35) * 0x1D2BC3 ^ 0xD1B54A35) >>> 1) * 0x1p-31f;
value -= floor;
value *= value * (3f - 2f * value);
end = end - start + 1.5f;
end -= (int)end + 0.5f;
start += end * value + 1;
return (start - (int)start);
}
/**
* Very similar to {@link #wobble(long, float)}, but only tolerates non-negative {@code value} and wraps value when
* it gets too close to {@code modulus}. Could find various uses when wrapping is needed.
* One such potential use is for looping animations; if the modulus is set so that {@code value}
* equals {@code modulus} (or an integer multiple of it) exactly when the animation starts and ends, the animation
* will then loop seamlessly, at least for anything that depends on this method.
*
* The wobble methods have a shape where they flatten out when {@code value} is an integer, so typically you should
* add a value smaller than 1 to value at each call if you want it to change smoothly. For the wrapped methods, the
* modulus is also the number of times the curve will flatten out over the course of a cycle.
*
* Note that {@code wobbleWrapped(seed, 0, modulus)} and {@code wobbleWrapped(seed, modulus, modulus)}
* will produce the same value as long as modulus is positive (and not large enough to incur precision loss).
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a non-negative float that typically changes slowly, by less than 2.0, with direction changes at integer inputs
* @param modulus where to wrap value if it gets too high; must be positive
* @return a pseudo-random float between -1f and 1f (both exclusive), smoothly changing with value
*/
public static float wobbleWrapped(long seed, float value, int modulus)
{
final int floor = (int) value;
final float start = (((seed + floor % modulus) * 0x6C8E9CF570932BD5L ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L) * 0x0.ffffffp-63f,
end = (((seed + (floor + 1) % modulus) * 0x6C8E9CF570932BD5L ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L) * 0x0.ffffffp-63f;
value -= floor;
value *= value * (3 - 2 * value);
return (1 - value) * start + value * end;
}
/**
* Very similar to {@link #wobble(long, float)}, but only tolerates non-negative {@code value} and wraps value when
* it gets too close to {@code modulus}. Could find various uses when wrapping is needed. One such
* potential use is for looping animations; if the modulus is set so that {@code value} equals {@code modulus} (or
* an integer multiple of it) exactly when the animation starts and ends, the animation will then loop seamlessly,
* at least for anything that depends on this method.
*
* The wobble methods have a shape where they flatten out when {@code value} is an integer, so typically you should
* add a value smaller than 1 to value at each call if you want it to change smoothly. For the wrapped methods, the
* modulus is also the number of times the curve will flatten out over the course of a cycle.
*
* Note that {@code wobbleWrappedTight(seed, 0, modulus)} and {@code wobbleWrappedTight(seed, modulus, modulus)}
* will produce the same value as long as modulus is positive (and not large enough to incur precision loss).
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a non-negative float that typically changes slowly, by less than 2.0, with direction changes at integer inputs
* @param modulus where to wrap value if it gets too high; must be positive
* @return a pseudo-random float between 0f and 1f (both inclusive), smoothly changing with value
*/
public static float wobbleWrappedTight(long seed, float value, int modulus)
{
final int floor = (int) value;
final float start = (((seed + floor % modulus) * 0x6C8E9CF570932BD5L ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L >>> 1) * 0x0.ffffffp-63f,
end = (((seed + (floor + 1) % modulus) * 0x6C8E9CF570932BD5L ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5C323L >>> 1) * 0x0.ffffffp-63f;
value -= floor;
value *= value * (3 - 2 * value);
return (1 - value) * start + value * end;
}
/**
* Very similar to {@link #wobble(int, float)}, but only tolerates non-negative {@code value} and wraps value when
* it gets too close to {@code modulus}. Used by {@link #generateLookupTable(int, int, int, int)}, but could find
* uses elsewhere. One such potential use is for looping animations; if the modulus is set so that {@code value}
* equals {@code modulus} (or an integer multiple of it) exactly when the animation starts and ends, the animation
* will then loop seamlessly, at least for anything that depends on this method.
*
* The wobble methods have a shape where they flatten out when {@code value} is an integer, so typically you should
* add a value smaller than 1 to value at each call if you want it to change smoothly. For the wrapped methods, the
* modulus is also the number of times the curve will flatten out over the course of a cycle.
*
* Note that {@code wobbleWrapped(seed, 0, modulus)} and {@code wobbleWrapped(seed, modulus, modulus)}
* will produce the same value as long as modulus is positive (and not large enough to incur precision loss).
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a non-negative float that typically changes slowly, by less than 2.0, with direction changes at integer inputs
* @param modulus where to wrap value if it gets too high; must be positive
* @return a pseudo-random float between -1f and 1f (both exclusive), smoothly changing with value
*/
public static float wobbleWrapped(int seed, float value, int modulus)
{
final int floor = (int) value;
final float start = (((seed + floor % modulus) * 0xBE56D ^ 0xD1B54A35) * 0x1D2BC3) * 0x0.ffffffp-31f,
end = (((seed + (floor + 1) % modulus) * 0xBE56D ^ 0xD1B54A35) * 0x1D2BC3) * 0x0.ffffffp-31f;
value -= floor;
value *= value * (3 - 2 * value);
return (1 - value) * start + value * end;
}
/**
* Very similar to {@link #wobble(int, float)}, but only tolerates non-negative {@code value} and wraps value when
* it gets too close to {@code modulus}. Used by
* {@link #generateSplineLookupTable(int, int, int, int, float, float)}, but could find uses elsewhere. One such
* potential use is for looping animations; if the modulus is set so that {@code value} equals {@code modulus} (or
* an integer multiple of it) exactly when the animation starts and ends, the animation will then loop seamlessly,
* at least for anything that depends on this method.
*
* The wobble methods have a shape where they flatten out when {@code value} is an integer, so typically you should
* add a value smaller than 1 to value at each call if you want it to change smoothly. For the wrapped methods, the
* modulus is also the number of times the curve will flatten out over the course of a cycle.
*
* Note that {@code wobbleWrappedTight(seed, 0, modulus)} and {@code wobbleWrappedTight(seed, modulus, modulus)}
* will produce the same value as long as modulus is positive (and not large enough to incur precision loss).
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes; this should not change between calls
* @param value a non-negative float that typically changes slowly, by less than 2.0, with direction changes at integer inputs
* @param modulus where to wrap value if it gets too high; must be positive
* @return a pseudo-random float between 0f and 1f (both inclusive), smoothly changing with value
*/
public static float wobbleWrappedTight(int seed, float value, int modulus)
{
final int floor = (int) value;
final float start = (((seed + floor % modulus) * 0xBE56D ^ 0xD1B54A35) * 0x1D2BC3 >>> 1) * 0x1.0p-31f,
end = (((seed + (floor + 1) % modulus) * 0xBE56D ^ 0xD1B54A35) * 0x1D2BC3 >>> 1) * 0x1.0p-31f;
value -= floor;
value *= value * (3 - 2 * value);
return (1 - value) * start + value * end;
}
/**
* Creates a wrapping lookup table of {@code size} float items for a wobbling line, using a specific count of points
* where the wobble can reach a peak or valley, a number of octaves to refine the wobble, and a seed.
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes
* @param size how many items to have in the returned float array
* @param points effectively, the frequency; how many possible peak/valley points will appear in the first octave
* @param octaves a positive int that adds more detail as it goes higher; cannot be more than 31
* @return the wrapping lookup table of float values between -1 and 1
*/
public static float[] generateLookupTable(int seed, int size, int points, int octaves) {
if(size <= 0) return new float[0];
float[] table = new float[size];
points = Math.min(Math.max(points, 1), size);
octaves = Math.min(Math.max(octaves, 1), 31);
int totalStrength = (1 << octaves) - 1;
float divisor = 1f / totalStrength;
float strength = Integer.highestOneBit(totalStrength) * divisor;
float frequency = (float)points / (float) size;
for (int o = 0; o < octaves; o++, strength *= 0.5f, frequency += frequency, seed = seed * 0xFAB ^ 0x4321ABCD) {
for (int i = 0; i < size; i++) {
table[i] += wobbleWrapped(seed, i * frequency, points) * strength;
}
}
return table;
}
/**
* Creates a wrapping lookup table of {@code size} float items for a wobbling line, using a specific count of points
* where the wobble can reach a peak or valley, a number of octaves to refine the wobble, and a seed. This also
* takes a shape and turning parameter that it uses to finish the lookup table by running every item through a call
* to {@link com.github.tommyettinger.digital.MathTools#barronSpline(float, float, float)}; this can be useful to
* change how the noise is distributed from slightly favoring extremes (the default) to preferring central values
* (when shape is between 0 and 1) or preferring more extreme values (when shape is more than 1).
* @param seed an int seed that will determine the pattern of peaks and valleys this will generate as value changes
* @param size how many items to have in the returned float array
* @param points effectively, the frequency; how many possible peak/valley points will appear in the first octave
* @param octaves a positive int that adds more detail as it goes higher; cannot be more than 31
* @param shape must be greater than or equal to 0; values greater than 1 are "normal interpolations"
* @param turning a value between 0.0 and 1.0, inclusive, where the shape changes
* @return the wrapping lookup table of float values between 0 and 1, both inclusive
*/
public static float[] generateSplineLookupTable(int seed, int size, int points, int octaves, float shape, float turning) {
if(size <= 0) return new float[0];
float[] table = new float[size];
points = Math.min(Math.max(points, 1), size);
octaves = Math.min(Math.max(octaves, 1), 31);
int totalStrength = (1 << octaves) - 1;
float divisor = 1f / totalStrength;
float strength = Integer.highestOneBit(totalStrength) * divisor;
float frequency = (float)points / (float) size;
for (int o = 0; o < octaves; o++, strength *= 0.5f, frequency += frequency, seed = seed * 0xFAB ^ 0x4321ABCD) {
for (int i = 0; i < size; i++) {
table[i] += wobbleWrappedTight(seed, i * frequency, points) * strength;
}
}
for (int i = 0; i < size; i++) {
final float x = table[i];
final float d = turning - x;
final int f = BitConversion.floatToIntBits(d) >> 31, n = f | 1;
table[i] = ((turning * n - f) * (x + f)) / (Float.MIN_NORMAL - f + (x + shape * d) * n) - f;
}
return table;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy