/* * This file is part of FalsePatternLib. * * Copyright (C) 2022-2025 FalsePattern * All Rights Reserved * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * FalsePatternLib 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, only version 3 of the License. * * FalsePatternLib 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with FalsePatternLib. If not, see . */ package com.falsepattern.lib.util; import com.falsepattern.lib.StableAPI; import com.falsepattern.lib.compat.Vec3i; import lombok.experimental.UtilityClass; import java.util.Random; import java.util.UUID; @UtilityClass @StableAPI(since = "0.10.0") public final class MathUtil { @StableAPI.Expose 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 */ @StableAPI.Expose 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 */ @StableAPI.Expose public static float cos(float value) { return SIN_TABLE[(int) (value * 10430.378F + 16384.0F) & 65535]; } @StableAPI.Expose public static float sqrt(float value) { return (float) Math.sqrt(value); } @StableAPI.Expose public static float sqrt(double value) { return (float) Math.sqrt(value); } /** * Returns the greatest integer less than or equal to the float argument */ @StableAPI.Expose 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 */ @StableAPI.Expose public static int fastFloor(double value) { return (int) (value + 1024.0D) - 1024; } /** * Returns the greatest integer less than or equal to the double argument */ @StableAPI.Expose public static int floor(double value) { int i = (int) value; return value < (double) i ? i - 1 : i; } /** * Long version of floor() */ @StableAPI.Expose public static long lfloor(double value) { long i = (long) value; return value < (double) i ? i - 1L : i; } @StableAPI.Expose public static int absFloor(double value) { return (int) (value >= 0.0D ? value : -value + 1.0D); } @StableAPI.Expose public static float abs(float value) { return value >= 0.0F ? value : -value; } /** * Returns the unsigned value of an int. */ @StableAPI.Expose public static int abs(int value) { return value >= 0 ? value : -value; } @StableAPI.Expose public static int ceil(float value) { int i = (int) value; return value > (float) i ? i + 1 : i; } @StableAPI.Expose 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. */ @StableAPI.Expose 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 */ @StableAPI.Expose public static float clamp(float num, float min, float max) { return num < min ? min : Math.min(num, max); } @StableAPI.Expose public static double clamp(double num, double min, double max) { return num < min ? min : Math.min(num, max); } @StableAPI.Expose 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. */ @StableAPI.Expose 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); } /** * Buckets an integer with specified bucket sizes. */ @StableAPI.Expose public static int intFloorDiv(int number, int bucket) { return number < 0 ? -((-number - 1) / bucket) - 1 : number / bucket; } @StableAPI.Expose public static int getInt(Random random, int minimum, int maximum) { return minimum >= maximum ? minimum : random.nextInt(maximum - minimum + 1) + minimum; } @StableAPI.Expose public static float nextFloat(Random random, float minimum, float maximum) { return minimum >= maximum ? minimum : random.nextFloat() * (maximum - minimum) + minimum; } @StableAPI.Expose public static double nextDouble(Random random, double minimum, double maximum) { return minimum >= maximum ? minimum : random.nextDouble() * (maximum - minimum) + minimum; } @StableAPI.Expose public static double average(long[] values) { long i = 0L; for (long j : values) { i += j; } return (double) i / (double) values.length; } @StableAPI.Expose public static boolean epsilonEquals(float a, float b) { return abs(b - a) < 1.0E-5F; } @StableAPI.Expose public static int normalizeAngle(int a, int b) { return (a % b + b) % b; } @StableAPI.Expose public static float positiveModulo(float numerator, float denominator) { return (numerator % denominator + denominator) % denominator; } @StableAPI.Expose 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 */ @StableAPI.Expose 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 */ @StableAPI.Expose 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[ */ @StableAPI.Expose 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 */ @StableAPI.Expose 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 */ @StableAPI.Expose 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. */ @StableAPI.Expose public static double getDouble(String value, double defaultValue) { try { return Double.parseDouble(value); } catch (Throwable var4) { return defaultValue; } } @StableAPI.Expose 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. */ @StableAPI.Expose 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, ...) */ @StableAPI.Expose 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. */ @StableAPI.Expose 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. */ @StableAPI.Expose 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. *

* For instance, {@code roundUp(1, 4)} returns 4; {@code roundUp(0, 4)} returns 0; and {@code roundUp(4, 4)} returns * 4. */ @StableAPI.Expose 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; } } @StableAPI.Expose 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 */ @StableAPI.Expose 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. */ @StableAPI.Expose 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_; } @StableAPI.Expose 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}. */ @StableAPI.Expose public static double frac(double number) { return number - Math.floor(number); } @StableAPI.Expose public static long getPositionRandom(Vec3i pos) { return getCoordinateRandom(pos.getX(), pos.getY(), pos.getZ()); } @StableAPI.Expose 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 */ @StableAPI.Expose public static UUID getRandomUUID() { return getRandomUUID(RANDOM); } @StableAPI.Expose public static double pct(double a, double b, double c) { return (a - b) / (c - b); } @StableAPI.Expose 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 the fast inverse square * root with a constant of 0x5FE6EB50C7B537AA. */ @StableAPI.Expose 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; } @StableAPI.Expose 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; } @StableAPI.Expose 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; } }