570 lines
15 KiB
Java
570 lines
15 KiB
Java
package com.hypixel.hytale.math.util;
|
|
|
|
import com.hypixel.hytale.math.vector.Vector3d;
|
|
import com.hypixel.hytale.math.vector.Vector3f;
|
|
import com.hypixel.hytale.math.vector.Vector3i;
|
|
import java.util.concurrent.ThreadLocalRandom;
|
|
import javax.annotation.Nonnull;
|
|
|
|
public class MathUtil {
|
|
public static final double EPSILON_DOUBLE = Math.ulp(1.0);
|
|
public static final float EPSILON_FLOAT = Math.ulp(1.0F);
|
|
public static float PITCH_EDGE_PADDING = 0.01F;
|
|
|
|
public static int abs(int i) {
|
|
int mask = i >> 31;
|
|
return i + mask ^ mask;
|
|
}
|
|
|
|
public static int floor(double d) {
|
|
int i = (int)d;
|
|
if (i <= d) {
|
|
return i;
|
|
} else {
|
|
return d < -2.1474836E9F ? Integer.MIN_VALUE : i - 1;
|
|
}
|
|
}
|
|
|
|
public static int ceil(double d) {
|
|
int i = (int)d;
|
|
if (!(d > 0.0) || d == i) {
|
|
return i;
|
|
} else {
|
|
return d > 2.147483647E9 ? Integer.MAX_VALUE : i + 1;
|
|
}
|
|
}
|
|
|
|
public static int randomInt(int min, int max) {
|
|
return ThreadLocalRandom.current().nextInt(min, max);
|
|
}
|
|
|
|
public static double randomDouble(double min, double max) {
|
|
return min + Math.random() * (max - min);
|
|
}
|
|
|
|
public static float randomFloat(float min, float max) {
|
|
return min + (float)Math.random() * (max - min);
|
|
}
|
|
|
|
public static double round(double d, int p) {
|
|
double pow = Math.pow(10.0, p);
|
|
return Math.round(d * pow) / pow;
|
|
}
|
|
|
|
public static boolean within(double val, double min, double max) {
|
|
return val >= min && val <= max;
|
|
}
|
|
|
|
public static double minValue(double v, double a, double c) {
|
|
if (a < v) {
|
|
v = a;
|
|
}
|
|
|
|
if (c < v) {
|
|
v = c;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
public static int minValue(int v, int a, int c) {
|
|
if (a < v) {
|
|
v = a;
|
|
}
|
|
|
|
if (c < v) {
|
|
v = c;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
public static double maxValue(double v, double a, double b, double c) {
|
|
if (a > v) {
|
|
v = a;
|
|
}
|
|
|
|
if (b > v) {
|
|
v = b;
|
|
}
|
|
|
|
if (c > v) {
|
|
v = c;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
public static double maxValue(double v, double a, double b) {
|
|
if (a > v) {
|
|
v = a;
|
|
}
|
|
|
|
if (b > v) {
|
|
v = b;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
public static byte maxValue(byte v, byte a, byte b) {
|
|
if (a > v) {
|
|
v = a;
|
|
}
|
|
|
|
if (b > v) {
|
|
v = b;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
public static byte maxValue(byte v, byte a, byte b, byte c) {
|
|
if (a > v) {
|
|
v = a;
|
|
}
|
|
|
|
if (b > v) {
|
|
v = b;
|
|
}
|
|
|
|
if (c > v) {
|
|
v = c;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
public static int maxValue(int v, int a, int b) {
|
|
if (a > v) {
|
|
v = a;
|
|
}
|
|
|
|
if (b > v) {
|
|
v = b;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
public static double lengthSquared(double x, double y) {
|
|
return x * x + y * y;
|
|
}
|
|
|
|
public static double length(double x, double y) {
|
|
return Math.sqrt(lengthSquared(x, y));
|
|
}
|
|
|
|
public static double lengthSquared(double x, double y, double z) {
|
|
return x * x + y * y + z * z;
|
|
}
|
|
|
|
public static double length(double x, double y, double z) {
|
|
return Math.sqrt(lengthSquared(x, y, z));
|
|
}
|
|
|
|
public static double maxValue(double v, double a) {
|
|
return a > v ? a : v;
|
|
}
|
|
|
|
public static double clipToZero(double v) {
|
|
return clipToZero(v, EPSILON_DOUBLE);
|
|
}
|
|
|
|
public static double clipToZero(double v, double epsilon) {
|
|
return v >= -epsilon && v <= epsilon ? 0.0 : v;
|
|
}
|
|
|
|
public static float clipToZero(float v) {
|
|
return clipToZero(v, EPSILON_FLOAT);
|
|
}
|
|
|
|
public static float clipToZero(float v, float epsilon) {
|
|
return v >= -epsilon && v <= epsilon ? 0.0F : v;
|
|
}
|
|
|
|
public static boolean closeToZero(double v) {
|
|
return closeToZero(v, EPSILON_DOUBLE);
|
|
}
|
|
|
|
public static boolean closeToZero(double v, double epsilon) {
|
|
return v >= -epsilon && v <= epsilon;
|
|
}
|
|
|
|
public static boolean closeToZero(float v) {
|
|
return closeToZero(v, EPSILON_FLOAT);
|
|
}
|
|
|
|
public static boolean closeToZero(float v, float epsilon) {
|
|
return v >= -epsilon && v <= epsilon;
|
|
}
|
|
|
|
public static double clamp(double v, double min, double max) {
|
|
if (v > max) {
|
|
return v < min ? min : max;
|
|
} else {
|
|
return v < min ? min : v;
|
|
}
|
|
}
|
|
|
|
public static float clamp(float v, float min, float max) {
|
|
if (v > max) {
|
|
return v < min ? min : max;
|
|
} else {
|
|
return v < min ? min : v;
|
|
}
|
|
}
|
|
|
|
public static int clamp(int v, int min, int max) {
|
|
if (v > max) {
|
|
return v < min ? min : max;
|
|
} else {
|
|
return v < min ? min : v;
|
|
}
|
|
}
|
|
|
|
public static long clamp(long v, long min, long max) {
|
|
if (v > max) {
|
|
return v < min ? min : max;
|
|
} else {
|
|
return v < min ? min : v;
|
|
}
|
|
}
|
|
|
|
public static int getPercentageOf(int index, int max) {
|
|
return (int)(index / (max - 1.0) * 100.0);
|
|
}
|
|
|
|
public static double percent(int v, int total) {
|
|
return total == 0 ? 0.0 : v * 100.0 / total;
|
|
}
|
|
|
|
public static int fastRound(float f) {
|
|
return fastFloor(f + 0.5F);
|
|
}
|
|
|
|
public static long fastRound(double d) {
|
|
return fastFloor(d + 0.5);
|
|
}
|
|
|
|
public static int fastFloor(float f) {
|
|
int i = (int)f;
|
|
if (i <= f) {
|
|
return i;
|
|
} else {
|
|
return f < -2.1474836E9F ? Integer.MIN_VALUE : i - 1;
|
|
}
|
|
}
|
|
|
|
public static long fastFloor(double d) {
|
|
long i = (long)d;
|
|
if (i <= d) {
|
|
return i;
|
|
} else {
|
|
return d < -9.223372E18F ? Long.MIN_VALUE : i - 1L;
|
|
}
|
|
}
|
|
|
|
public static int fastCeil(float f) {
|
|
int i = (int)f;
|
|
if (!(f > 0.0F) || f == i) {
|
|
return i;
|
|
} else {
|
|
return f > 2.1474836E9F ? Integer.MAX_VALUE : i + 1;
|
|
}
|
|
}
|
|
|
|
public static long fastCeil(double d) {
|
|
long i = (long)d;
|
|
if (!(d > 0.0) || d == i) {
|
|
return i;
|
|
} else {
|
|
return d > 9.223372E18F ? Long.MAX_VALUE : i + 1L;
|
|
}
|
|
}
|
|
|
|
private MathUtil() {
|
|
}
|
|
|
|
public static float halfFloatToFloat(int hbits) {
|
|
int mant = hbits & 1023;
|
|
int exp = hbits & 31744;
|
|
if (exp == 31744) {
|
|
exp = 261120;
|
|
} else if (exp != 0) {
|
|
exp += 114688;
|
|
if (mant == 0 && exp > 115712) {
|
|
return Float.intBitsToFloat((hbits & 32768) << 16 | exp << 13 | 1023);
|
|
}
|
|
} else if (mant != 0) {
|
|
exp = 115712;
|
|
|
|
do {
|
|
mant <<= 1;
|
|
exp -= 1024;
|
|
} while ((mant & 1024) == 0);
|
|
|
|
mant &= 1023;
|
|
}
|
|
|
|
return Float.intBitsToFloat((hbits & 32768) << 16 | (exp | mant) << 13);
|
|
}
|
|
|
|
public static int halfFloatFromFloat(float fval) {
|
|
int fbits = Float.floatToIntBits(fval);
|
|
int sign = fbits >>> 16 & 32768;
|
|
int val = (fbits & 2147483647) + 4096;
|
|
if (val >= 1199570944) {
|
|
if ((fbits & 2147483647) >= 1199570944) {
|
|
return val < 2139095040 ? sign | 31744 : sign | 31744 | (fbits & 8388607) >>> 13;
|
|
} else {
|
|
return sign | 31743;
|
|
}
|
|
} else if (val >= 947912704) {
|
|
return sign | val - 939524096 >>> 13;
|
|
} else if (val < 855638016) {
|
|
return sign;
|
|
} else {
|
|
val = (fbits & 2147483647) >>> 23;
|
|
return sign | (fbits & 8388607 | 8388608) + (8388608 >>> val - 102) >>> 126 - val;
|
|
}
|
|
}
|
|
|
|
public static int byteCount(int i) {
|
|
if (i > 65535) {
|
|
return 4;
|
|
} else if (i > 255) {
|
|
return 2;
|
|
} else {
|
|
return i > 0 ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
public static int packInt(int x, int z) {
|
|
return x << 16 | z & 65535;
|
|
}
|
|
|
|
public static int unpackLeft(int packed) {
|
|
int i = packed >> 16 & 65535;
|
|
if ((i & 32768) != 0) {
|
|
i |= -65536;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
public static int unpackRight(int packed) {
|
|
int i = packed & 65535;
|
|
if ((i & 32768) != 0) {
|
|
i |= -65536;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
public static long packLong(int left, int right) {
|
|
return (long)left << 32 | right & 4294967295L;
|
|
}
|
|
|
|
public static int unpackLeft(long packed) {
|
|
return (int)(packed >> 32);
|
|
}
|
|
|
|
public static int unpackRight(long packed) {
|
|
return (int)packed;
|
|
}
|
|
|
|
@Nonnull
|
|
public static Vector3i rotateVectorYAxis(@Nonnull Vector3i vector, int angle, boolean clockwise) {
|
|
float radAngle = (float) (Math.PI / 180.0) * angle;
|
|
int x1;
|
|
int z1;
|
|
if (clockwise) {
|
|
x1 = (int)(vector.x * TrigMathUtil.cos(radAngle) - vector.z * TrigMathUtil.sin(radAngle));
|
|
z1 = (int)(vector.x * TrigMathUtil.sin(radAngle) + vector.z * TrigMathUtil.cos(radAngle));
|
|
} else {
|
|
x1 = (int)(vector.x * TrigMathUtil.cos(radAngle) + vector.z * TrigMathUtil.sin(radAngle));
|
|
z1 = (int)(-vector.x * TrigMathUtil.sin(radAngle) + vector.z * TrigMathUtil.cos(radAngle));
|
|
}
|
|
|
|
return new Vector3i(x1, vector.y, z1);
|
|
}
|
|
|
|
@Nonnull
|
|
public static Vector3d rotateVectorYAxis(@Nonnull Vector3d vector, int angle, boolean clockwise) {
|
|
float radAngle = (float) (Math.PI / 180.0) * angle;
|
|
double x1;
|
|
double z1;
|
|
if (clockwise) {
|
|
x1 = vector.x * TrigMathUtil.cos(radAngle) - vector.z * TrigMathUtil.sin(radAngle);
|
|
z1 = vector.x * TrigMathUtil.sin(radAngle) + vector.z * TrigMathUtil.cos(radAngle);
|
|
} else {
|
|
x1 = vector.x * TrigMathUtil.cos(radAngle) + vector.z * TrigMathUtil.sin(radAngle);
|
|
z1 = -vector.x * TrigMathUtil.sin(radAngle) + vector.z * TrigMathUtil.cos(radAngle);
|
|
}
|
|
|
|
return new Vector3d(x1, vector.y, z1);
|
|
}
|
|
|
|
public static float wrapAngle(float angle) {
|
|
angle %= (float) (Math.PI * 2);
|
|
if (angle <= (float) -Math.PI) {
|
|
angle += (float) (Math.PI * 2);
|
|
} else if (angle > (float) Math.PI) {
|
|
angle -= (float) (Math.PI * 2);
|
|
}
|
|
|
|
return angle;
|
|
}
|
|
|
|
public static float lerp(float a, float b, float t) {
|
|
return lerpUnclamped(a, b, clamp(t, 0.0F, 1.0F));
|
|
}
|
|
|
|
public static float lerpUnclamped(float a, float b, float t) {
|
|
return a + t * (b - a);
|
|
}
|
|
|
|
public static double lerp(double a, double b, double t) {
|
|
return lerpUnclamped(a, b, clamp(t, 0.0, 1.0));
|
|
}
|
|
|
|
public static double lerpUnclamped(double a, double b, double t) {
|
|
return a + t * (b - a);
|
|
}
|
|
|
|
public static float shortAngleDistance(float a, float b) {
|
|
float distance = (b - a) % (float) (Math.PI * 2);
|
|
return 2.0F * distance % (float) (Math.PI * 2) - distance;
|
|
}
|
|
|
|
public static float lerpAngle(float a, float b, float t) {
|
|
return a + shortAngleDistance(a, b) * t;
|
|
}
|
|
|
|
public static double floorMod(double x, double y) {
|
|
return x - Math.floor(x / y) * y;
|
|
}
|
|
|
|
public static double compareAngle(double a, double b) {
|
|
double diff = b - a;
|
|
return floorMod(diff + Math.PI, Math.PI * 2) - Math.PI;
|
|
}
|
|
|
|
public static double percentile(@Nonnull long[] sortedData, double percentile) {
|
|
if (sortedData.length == 1) {
|
|
return sortedData[0];
|
|
} else if (percentile >= 1.0) {
|
|
return sortedData[sortedData.length - 1];
|
|
} else {
|
|
double position = (sortedData.length + 1) * percentile;
|
|
double n = percentile * (sortedData.length - 1) + 1.0;
|
|
long left;
|
|
long right;
|
|
if (position >= 1.0) {
|
|
left = sortedData[floor(n) - 1];
|
|
right = sortedData[floor(n)];
|
|
} else {
|
|
left = sortedData[0];
|
|
right = sortedData[1];
|
|
}
|
|
|
|
if (left == right) {
|
|
return left;
|
|
} else {
|
|
double part = n - floor(n);
|
|
return left + part * (right - left);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static double distanceToLineSq(double x, double y, double ax, double ay, double bx, double by) {
|
|
double dx0 = x - ax;
|
|
double dy0 = y - ay;
|
|
double dx1 = bx - ax;
|
|
double dy1 = by - ay;
|
|
return distanceToLineSq(x, y, ax, ay, bx, by, dx0, dy0, dx1, dy1);
|
|
}
|
|
|
|
public static double distanceToLineSq(double x, double y, double ax, double ay, double bx, double by, double dxAx, double dyAy, double dBxAx, double dByAy) {
|
|
double t = dxAx * dBxAx + dyAy * dByAy;
|
|
t /= dBxAx * dBxAx + dByAy * dByAy;
|
|
double px = ax;
|
|
double py = ay;
|
|
if (t > 1.0) {
|
|
px = bx;
|
|
py = by;
|
|
} else if (t > 0.0) {
|
|
px = ax + t * dBxAx;
|
|
py = ay + t * dByAy;
|
|
}
|
|
|
|
dBxAx = x - px;
|
|
dByAy = y - py;
|
|
return dBxAx * dBxAx + dByAy * dByAy;
|
|
}
|
|
|
|
public static double distanceToInfLineSq(double x, double y, double ax, double ay, double bx, double by) {
|
|
double dx0 = x - ax;
|
|
double dy0 = y - ay;
|
|
double dx1 = bx - ax;
|
|
double dy1 = by - ay;
|
|
return distanceToInfLineSq(x, y, ax, ay, dx0, dy0, dx1, dy1);
|
|
}
|
|
|
|
public static double distanceToInfLineSq(double x, double y, double ax, double ay, double dxAx, double dyAy, double dBxAx, double dByAy) {
|
|
double t = dxAx * dBxAx + dyAy * dByAy;
|
|
t /= dBxAx * dBxAx + dByAy * dByAy;
|
|
double px = ax + t * dBxAx;
|
|
double py = ay + t * dByAy;
|
|
dBxAx = x - px;
|
|
dByAy = y - py;
|
|
return dBxAx * dBxAx + dByAy * dByAy;
|
|
}
|
|
|
|
public static int sideOfLine(double x, double y, double ax, double ay, double bx, double by) {
|
|
return (ax - x) * (by - y) - (ay - y) * (bx - x) >= 0.0 ? 1 : -1;
|
|
}
|
|
|
|
public static Vector3f getRotationForHitNormal(Vector3f normal) {
|
|
if (normal == null) {
|
|
return Vector3f.ZERO;
|
|
} else if (normal.y == 1.0F) {
|
|
return Vector3f.ZERO;
|
|
} else if (normal.y == -1.0F) {
|
|
return new Vector3f(0.0F, 0.0F, (float) Math.PI);
|
|
} else if (normal.x == 1.0F) {
|
|
return new Vector3f(0.0F, 0.0F, (float) (-Math.PI / 2));
|
|
} else if (normal.x == -1.0F) {
|
|
return new Vector3f(0.0F, 0.0F, (float) (Math.PI / 2));
|
|
} else if (normal.z == 1.0F) {
|
|
return new Vector3f((float) (Math.PI / 2), 0.0F, 0.0F);
|
|
} else {
|
|
return normal.z == -1.0F ? new Vector3f((float) (-Math.PI / 2), 0.0F, 0.0F) : Vector3f.ZERO;
|
|
}
|
|
}
|
|
|
|
public static String getNameForHitNormal(Vector3f normal) {
|
|
if (normal == null) {
|
|
return "UP";
|
|
} else if (normal.y == 1.0F) {
|
|
return "UP";
|
|
} else if (normal.y == -1.0F) {
|
|
return "DOWN";
|
|
} else if (normal.x == 1.0F) {
|
|
return "WEST";
|
|
} else if (normal.x == -1.0F) {
|
|
return "EAST";
|
|
} else if (normal.z == 1.0F) {
|
|
return "NORTH";
|
|
} else {
|
|
return normal.z == -1.0F ? "SOUTH" : "UP";
|
|
}
|
|
}
|
|
|
|
public static float mapToRange(float value, float valueMin, float valueMax, float rangeMin, float rangeMax) {
|
|
float alpha = (value - valueMin) / (valueMax - valueMin);
|
|
return rangeMin + alpha * (rangeMax - rangeMin);
|
|
}
|
|
}
|