174 lines
5.6 KiB
Java
174 lines
5.6 KiB
Java
package dev.slinky.client.rotation;
|
|
|
|
import dev.slinky.client.numeric.FloatNumeric;
|
|
import dev.slinky.client.numeric.NumericConstants;
|
|
import dev.slinky.client.numeric.NumericMath;
|
|
import java.security.SecureRandom;
|
|
|
|
public final strictfp class RotationRandom {
|
|
public static final RotationRandom SHARED = fresh();
|
|
private int a;
|
|
private int b;
|
|
private int c;
|
|
private int d;
|
|
|
|
public RotationRandom(int a, int b, int c, int d) {
|
|
if ((a | b | c | d) == 0) {
|
|
throw new IllegalArgumentException("zero random state");
|
|
} else {
|
|
this.a = a;
|
|
this.b = b;
|
|
this.c = c;
|
|
this.d = d;
|
|
}
|
|
}
|
|
|
|
private static strictfp RotationRandom fresh() {
|
|
SecureRandom seed = new SecureRandom();
|
|
int a = seed.nextInt();
|
|
int b = seed.nextInt();
|
|
int c = seed.nextInt();
|
|
int d = seed.nextInt();
|
|
if ((a | b | c | d) == 0) {
|
|
d = 1;
|
|
}
|
|
|
|
return new RotationRandom(a, b, c, d);
|
|
}
|
|
|
|
public static strictfp double now() {
|
|
return (double)System.nanoTime() / 1.0E9;
|
|
}
|
|
|
|
private strictfp int next() {
|
|
int sum = this.d + this.a;
|
|
int rotated = Integer.rotateLeft(this.b ^ this.d, 11);
|
|
int nextB = this.c ^ this.a ^ this.b;
|
|
int nextA = this.a ^ this.b ^ this.d;
|
|
int nextC = this.c ^ this.a ^ this.b << 9;
|
|
this.a = nextA;
|
|
this.b = nextB;
|
|
this.c = nextC;
|
|
this.d = rotated;
|
|
return sum;
|
|
}
|
|
|
|
public strictfp float interval(float low, float high) {
|
|
float width = high - low;
|
|
float value = (float)(this.next() >>> 8);
|
|
value *= 5.9604645E-8F;
|
|
value *= width;
|
|
return value + low;
|
|
}
|
|
|
|
public strictfp boolean chance(float threshold) {
|
|
return threshold > this.interval(0.0F, 1.0F);
|
|
}
|
|
|
|
public strictfp int[] state() {
|
|
return new int[]{this.a, this.b, this.c, this.d};
|
|
}
|
|
|
|
public static final class Gaussian {
|
|
private boolean valid;
|
|
private float cached;
|
|
|
|
public strictfp float sample(RotationRandom random) {
|
|
if (this.valid) {
|
|
this.valid = false;
|
|
return this.cached;
|
|
} else {
|
|
float x;
|
|
float y;
|
|
float radius;
|
|
do {
|
|
float value = (float)(random.next() >>> 8) * 5.9604645E-8F;
|
|
x = value + value + -1.0F;
|
|
value = (float)(random.next() >>> 8) * 5.9604645E-8F;
|
|
y = value + value + -1.0F;
|
|
radius = NumericMath.floatDistance(x, y);
|
|
} while (radius >= 1.0F || radius == 0.0F);
|
|
|
|
float ratio = FloatNumeric.log(radius) * -2.0F / radius;
|
|
float factor = FloatNumeric.sqrt(ratio);
|
|
this.cached = y * factor;
|
|
this.valid = true;
|
|
return x * factor;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static final class Segment {
|
|
private double elapsed;
|
|
private float left;
|
|
private float right;
|
|
|
|
public strictfp float sample(float low, float high, double duration, double delta, RotationRandom random) {
|
|
this.elapsed += delta;
|
|
if (this.elapsed > duration) {
|
|
this.elapsed = 0.0;
|
|
if (this.left == 0.0F && this.right == 0.0F) {
|
|
do {
|
|
this.left = draw(random, 1304864, 1304868);
|
|
this.right = draw(random, 1304864, 1304868);
|
|
} while (NumericConstants.doubleAt(1304896) > this.maskedDifference(1304880));
|
|
} else {
|
|
this.left = this.right;
|
|
|
|
do {
|
|
this.right = draw(random, 1304912, 1304916);
|
|
} while (NumericConstants.doubleAt(1304944) > this.maskedDifference(1304928));
|
|
}
|
|
}
|
|
|
|
double phase = this.elapsed / duration;
|
|
double curve = (double)(this.right - this.left) * phase + (double)this.left;
|
|
return (float)((double)(high - low) * curve + (double)low);
|
|
}
|
|
|
|
private strictfp double maskedDifference(int mask) {
|
|
return (double)Float.intBitsToFloat(Float.floatToRawIntBits(this.left - this.right) & NumericConstants.intAt(mask));
|
|
}
|
|
|
|
private static strictfp float draw(RotationRandom random, int upper, int scale) {
|
|
float value = (float)(random.next() >>> 8);
|
|
value *= NumericConstants.floatAt(scale);
|
|
value *= NumericConstants.floatAt(upper);
|
|
return value + 0.0F;
|
|
}
|
|
}
|
|
|
|
public static final class Smooth {
|
|
private double timestamp;
|
|
private float left;
|
|
private float right;
|
|
|
|
public strictfp float sample(float low, float high, double duration, double now, RotationRandom random) {
|
|
if (now > this.timestamp + duration) {
|
|
this.timestamp = now;
|
|
if (this.left == 0.0F && this.right == 0.0F) {
|
|
do {
|
|
this.left = random.interval(0.0F, 1.0F);
|
|
this.right = random.interval(0.0F, 1.0F);
|
|
} while (0.2 > (double)RotationMath.abs(this.left - this.right));
|
|
} else {
|
|
this.left = this.right;
|
|
|
|
do {
|
|
this.right = random.interval(0.0F, 1.0F);
|
|
} while (0.2 > (double)RotationMath.abs(this.left - this.right));
|
|
}
|
|
}
|
|
|
|
double phase = (now - this.timestamp) / duration;
|
|
double curve = (double)(this.right - this.left) * phase + (double)this.left;
|
|
return (float)((double)(high - low) * curve + (double)low);
|
|
}
|
|
|
|
public strictfp boolean gate(float low, float high, double duration, double now, RotationRandom random) {
|
|
float comparison = random.interval(0.0F, 1.0F);
|
|
return this.sample(low, high, duration, now, random) > comparison;
|
|
}
|
|
}
|
|
}
|