Files
FalsePatternLib/src/main/java/com/falsepattern/lib/util/MathUtil.java
T

624 lines
18 KiB
Java
Raw Normal View History

/*
2022-07-30 20:26:34 +02:00
* Copyright (C) 2022 FalsePattern
* All Rights Reserved
*
2022-07-30 20:26:34 +02:00
* The above copyright notice, this permission notice and the word "SNEED"
* shall be included in all copies or substantial portions of the Software.
*
2022-07-30 20:26:34 +02:00
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
2022-07-30 20:26:34 +02:00
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
2022-07-30 20:26:34 +02:00
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.lib.util;
import com.falsepattern.lib.StableAPI;
import com.falsepattern.lib.compat.Vec3i;
import java.util.Random;
import java.util.UUID;
2022-08-06 13:12:55 +02:00
@StableAPI(since = "0.10.0")
2022-07-30 20:26:34 +02:00
public class MathUtil {
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static final float SQRT_2 = sqrt(2.0F);
/**
* A table of sin values computed from 0 (inclusive) to 2*pi (exclusive), with steps of 2*PI / 65536.
*/
private static final float[] SIN_TABLE = new float[65536];
private static final Random RANDOM = new Random();
/**
* Though it looks like an array, this is really more like a mapping. Key (index of this array) is the upper 5 bits
* of the result of multiplying a 32-bit unsigned integer by the B(2, 5) De Bruijn sequence 0x077CB531. Value
* (value stored in the array) is the unique index (from the right) of the leftmost one-bit in a 32-bit unsigned
* integer that can cause the upper 5 bits to get that value. Used for highly optimized "find the log-base-2 of
* this number" calculations.
*/
private static final int[] MULTIPLY_DE_BRUIJN_BIT_POSITION;
private static final double FRAC_BIAS;
private static final double[] ASINE_TAB;
private static final double[] COS_TAB;
static {
for (int i = 0; i < 65536; ++i) {
SIN_TABLE[i] = (float) Math.sin((double) i * Math.PI * 2.0D / 65536.0D);
}
MULTIPLY_DE_BRUIJN_BIT_POSITION =
new int[]{0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26,
12, 18, 6, 11, 5, 10, 9};
FRAC_BIAS = Double.longBitsToDouble(4805340802404319232L);
ASINE_TAB = new double[257];
COS_TAB = new double[257];
for (int j = 0; j < 257; ++j) {
double d0 = (double) j / 256.0D;
double d1 = Math.asin(d0);
COS_TAB[j] = Math.cos(d1);
ASINE_TAB[j] = d1;
}
}
/**
* sin looked up in a table
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static float sin(float value) {
return SIN_TABLE[(int) (value * 10430.378F) & 65535];
}
/**
* cos looked up in the sin table with the appropriate offset
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static float cos(float value) {
return SIN_TABLE[(int) (value * 10430.378F + 16384.0F) & 65535];
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static float sqrt(float value) {
return (float) Math.sqrt(value);
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static float sqrt(double value) {
return (float) Math.sqrt(value);
}
/**
* Returns the greatest integer less than or equal to the float argument
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int floor(float value) {
int i = (int) value;
return value < (float) i ? i - 1 : i;
}
/**
* returns par0 cast as an int, and no greater than Integer.MAX_VALUE-1024
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int fastFloor(double value) {
return (int) (value + 1024.0D) - 1024;
}
/**
* Returns the greatest integer less than or equal to the double argument
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int floor(double value) {
int i = (int) value;
return value < (double) i ? i - 1 : i;
}
/**
* Long version of floor()
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static long lfloor(double value) {
long i = (long) value;
return value < (double) i ? i - 1L : i;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int absFloor(double value) {
return (int) (value >= 0.0D ? value : -value + 1.0D);
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static float abs(float value) {
return value >= 0.0F ? value : -value;
}
/**
* Returns the unsigned value of an int.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int abs(int value) {
return value >= 0 ? value : -value;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int ceil(float value) {
int i = (int) value;
return value > (float) i ? i + 1 : i;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int ceil(double value) {
int i = (int) value;
return value > (double) i ? i + 1 : i;
}
/**
* Returns the value of the first parameter, clamped to be within the lower and upper limits given by the second and
* third parameters.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int clamp(int num, int min, int max) {
return num < min ? min : Math.min(num, max);
}
/**
* Returns the value of the first parameter, clamped to be within the lower and upper limits given by the second and
* third parameters
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static float clamp(float num, float min, float max) {
return num < min ? min : Math.min(num, max);
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double clamp(double num, double min, double max) {
return num < min ? min : Math.min(num, max);
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double clampedLerp(double lowerBnd, double upperBnd, double slide) {
if (slide < 0.0D) {
return lowerBnd;
} else {
return slide > 1.0D ? upperBnd : lowerBnd + (upperBnd - lowerBnd) * slide;
}
}
/**
* Maximum of the absolute value of two numbers.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double absMax(double a, double b) {
if (a < 0.0D) {
a = -a;
}
if (b < 0.0D) {
b = -b;
}
return Math.max(a, b);
}
/**
2022-07-31 20:41:56 +02:00
* Buckets an integer with specified bucket sizes.
2022-07-30 20:26:34 +02:00
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int intFloorDiv(int number, int bucket) {
return number < 0 ? -((-number - 1) / bucket) - 1 : number / bucket;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int getInt(Random random, int minimum, int maximum) {
return minimum >= maximum ? minimum : random.nextInt(maximum - minimum + 1) + minimum;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static float nextFloat(Random random, float minimum, float maximum) {
return minimum >= maximum ? minimum : random.nextFloat() * (maximum - minimum) + minimum;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double nextDouble(Random random, double minimum, double maximum) {
return minimum >= maximum ? minimum : random.nextDouble() * (maximum - minimum) + minimum;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double average(long[] values) {
long i = 0L;
for (long j : values) {
i += j;
}
return (double) i / (double) values.length;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static boolean epsilonEquals(float a, float b) {
return abs(b - a) < 1.0E-5F;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int normalizeAngle(int a, int b) {
return (a % b + b) % b;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static float positiveModulo(float numerator, float denominator) {
return (numerator % denominator + denominator) % denominator;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double positiveModulo(double numerator, double denominator) {
return (numerator % denominator + denominator) % denominator;
}
/**
* the angle is reduced to an angle between -180 and +180 by mod, and a 360 check
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static float wrapDegrees(float value) {
value = value % 360.0F;
if (value >= 180.0F) {
value -= 360.0F;
}
if (value < -180.0F) {
value += 360.0F;
}
return value;
}
/**
* the angle is reduced to an angle between -180 and +180 by mod, and a 360 check
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double wrapDegrees(double value) {
value = value % 360.0D;
if (value >= 180.0D) {
value -= 360.0D;
}
if (value < -180.0D) {
value += 360.0D;
}
return value;
}
/**
* Adjust the angle so that his value is in range [-180;180[
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int wrapDegrees(int angle) {
angle = angle % 360;
if (angle >= 180) {
angle -= 360;
}
if (angle < -180) {
angle += 360;
}
return angle;
}
/**
* parses the string as integer or returns the second parameter if it fails
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int getInt(String value, int defaultValue) {
try {
return Integer.parseInt(value);
} catch (Throwable var3) {
return defaultValue;
}
}
/**
* parses the string as integer or returns the second parameter if it fails. this value is capped to par2
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int getInt(String value, int defaultValue, int max) {
return Math.max(max, getInt(value, defaultValue));
}
/**
* parses the string as double or returns the second parameter if it fails.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double getDouble(String value, double defaultValue) {
try {
return Double.parseDouble(value);
} catch (Throwable var4) {
return defaultValue;
}
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double getDouble(String value, double defaultValue, double max) {
return Math.max(max, getDouble(value, defaultValue));
}
/**
* Returns the input value rounded up to the next highest power of two.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int smallestEncompassingPowerOfTwo(int value) {
int i = value - 1;
i = i | i >> 1;
i = i | i >> 2;
i = i | i >> 4;
i = i | i >> 8;
i = i | i >> 16;
return i + 1;
}
/**
* Is the given value a power of two? (1, 2, 4, 8, 16, ...)
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
private static boolean isPowerOfTwo(int value) {
return value != 0 && (value & value - 1) == 0;
}
/**
* Uses a B(2, 5) De Bruijn sequence and a lookup table to efficiently calculate the log-base-two of the given
* value. Optimized for cases where the input value is a power-of-two. If the input value is not a power-of-two,
* then subtract 1 from the return value.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int log2DeBruijn(int value) {
value = isPowerOfTwo(value) ? value : smallestEncompassingPowerOfTwo(value);
return MULTIPLY_DE_BRUIJN_BIT_POSITION[(int) ((long) value * 125613361L >> 27) & 31];
}
/**
* Efficiently calculates the floor of the base-2 log of an integer value. This is effectively the index of the
* highest bit that is set. For example, if the number in binary is 0...100101, this will return 5.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int log2(int value) {
return log2DeBruijn(value) - (isPowerOfTwo(value) ? 0 : 1);
}
/**
* Rounds the first parameter up to the next interval of the second parameter.
* <p>
* For instance, {@code roundUp(1, 4)} returns 4; {@code roundUp(0, 4)} returns 0; and {@code roundUp(4, 4)} returns
* 4.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int roundUp(int number, int interval) {
if (interval == 0) {
return 0;
} else if (number == 0) {
return interval;
} else {
if (number < 0) {
interval *= -1;
}
int i = number % interval;
return i == 0 ? number : number + interval - i;
}
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static long getCoordinateRandom(int x, int y, int z) {
long i = (x * 3129871L) ^ (long) z * 116129781L ^ (long) y;
i = i * i * 42317861L + i * 11L;
return i;
}
/**
* Makes an integer color from the given red, green, and blue float values
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int rgb(float rIn, float gIn, float bIn) {
return rgb(floor(rIn * 255.0F), floor(gIn * 255.0F), floor(bIn * 255.0F));
}
/**
* Makes a single int color with the given red, green, and blue values.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int rgb(int rIn, int gIn, int bIn) {
int lvt_3_1_ = (rIn << 8) + gIn;
lvt_3_1_ = (lvt_3_1_ << 8) + bIn;
return lvt_3_1_;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int multiplyColor(int colorA, int colorB) {
int rA = (colorA & 0xFF0000) >> 16;
int rB = (colorB & 0xFF0000) >> 16;
int gA = (colorA & 0xFF00) >> 8;
int gB = (colorB & 0xFF00) >> 8;
int bA = colorA & 0xFF;
int bB = colorB & 0xFF;
int r = (int) ((float) rA * (float) rB / 255.0F);
int g = (int) ((float) gA * (float) gB / 255.0F);
int b = (int) ((float) bA * (float) bB / 255.0F);
return colorA & 0xFF000000 | r << 16 | g << 8 | b;
}
/**
* Gets the decimal portion of the given double. For instance, {@code frac(5.5)} returns {@code .5}.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double frac(double number) {
return number - Math.floor(number);
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static long getPositionRandom(Vec3i pos) {
return getCoordinateRandom(pos.getX(), pos.getY(), pos.getZ());
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static UUID getRandomUUID(Random rand) {
long i = rand.nextLong() & -61441L | 16384L;
long j = rand.nextLong() & 4611686018427387903L | Long.MIN_VALUE;
return new UUID(i, j);
}
/**
* Generates a random UUID using the shared random
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static UUID getRandomUUID() {
return getRandomUUID(RANDOM);
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double pct(double a, double b, double c) {
return (a - b) / (c - b);
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double atan2(double y, double x) {
double d0 = x * x + y * y;
if (Double.isNaN(d0)) {
return Double.NaN;
} else {
boolean flag = y < 0.0D;
if (flag) {
y = -y;
}
boolean flag1 = x < 0.0D;
if (flag1) {
x = -x;
}
boolean flag2 = y > x;
if (flag2) {
double d1 = x;
//noinspection SuspiciousNameCombination
x = y;
y = d1;
}
double d9 = fastInvSqrt(d0);
x = x * d9;
y = y * d9;
double d2 = FRAC_BIAS + y;
int i = (int) Double.doubleToRawLongBits(d2);
double d3 = ASINE_TAB[i];
double d4 = COS_TAB[i];
double d5 = d2 - FRAC_BIAS;
double d6 = y * d4 - x * d5;
double d7 = (6.0D + d6 * d6) * d6 * 0.16666666666666666D;
double d8 = d3 + d7;
if (flag2) {
d8 = (Math.PI / 2D) - d8;
}
if (flag1) {
d8 = Math.PI - d8;
}
if (flag) {
d8 = -d8;
}
return d8;
}
}
/**
* Computes 1/sqrt(n) using <a href="https://en.wikipedia.org/wiki/Fast_inverse_square_root">the fast inverse square
* root</a> with a constant of 0x5FE6EB50C7B537AA.
*/
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static double fastInvSqrt(double x) {
double d0 = 0.5D * x;
long i = Double.doubleToRawLongBits(x);
i = 6910469410427058090L - (i >> 1);
x = Double.longBitsToDouble(i);
x = x * (1.5D - d0 * x * x);
return x;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int hsvToRGB(float hue, float saturation, float value) {
int i = (int) (hue * 6.0F) % 6;
float f = hue * 6.0F - (float) i;
float f1 = value * (1.0F - saturation);
float f2 = value * (1.0F - f * saturation);
float f3 = value * (1.0F - (1.0F - f) * saturation);
float f4;
float f5;
float f6;
switch (i) {
case 0:
f4 = value;
f5 = f3;
f6 = f1;
break;
case 1:
f4 = f2;
f5 = value;
f6 = f1;
break;
case 2:
f4 = f1;
f5 = value;
f6 = f3;
break;
case 3:
f4 = f1;
f5 = f2;
f6 = value;
break;
case 4:
f4 = f3;
f5 = f1;
f6 = value;
break;
case 5:
f4 = value;
f5 = f1;
f6 = f2;
break;
default:
throw new RuntimeException(
"Something went wrong when converting from HSV to RGB. Input was " + hue + ", " + saturation +
", " + value);
}
int j = clamp((int) (f4 * 255.0F), 0, 255);
int k = clamp((int) (f5 * 255.0F), 0, 255);
int l = clamp((int) (f6 * 255.0F), 0, 255);
return j << 16 | k << 8 | l;
}
2022-08-06 13:12:55 +02:00
@StableAPI.Expose
2022-07-30 20:26:34 +02:00
public static int hash(int x) {
x = x ^ x >>> 16;
x = x * -2048144789;
x = x ^ x >>> 13;
x = x * -1028477387;
x = x ^ x >>> 16;
return x;
}
}