package net.minecraft.util; import java.util.Locale; import java.util.UUID; import java.util.function.IntPredicate; import java.util.stream.IntStream; import net.minecraft.Util; import net.minecraft.core.Vec3i; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.apache.commons.lang3.math.Fraction; import org.apache.commons.lang3.math.NumberUtils; import org.joml.Quaternionf; import org.joml.Vector3f; public class Mth { private static final long UUID_VERSION = 61440L; private static final long UUID_VERSION_TYPE_4 = 16384L; private static final long UUID_VARIANT = -4611686018427387904L; private static final long UUID_VARIANT_2 = Long.MIN_VALUE; public static final float PI = (float) Math.PI; public static final float HALF_PI = (float) (Math.PI / 2); public static final float TWO_PI = (float) (Math.PI * 2); public static final float DEG_TO_RAD = (float) (Math.PI / 180.0); public static final float RAD_TO_DEG = 180.0F / (float)Math.PI; public static final float EPSILON = 1.0E-5F; public static final float SQRT_OF_TWO = sqrt(2.0F); private static final float SIN_SCALE = 10430.378F; public static final Vector3f Y_AXIS = new Vector3f(0.0F, 1.0F, 0.0F); public static final Vector3f X_AXIS = new Vector3f(1.0F, 0.0F, 0.0F); public static final Vector3f Z_AXIS = new Vector3f(0.0F, 0.0F, 1.0F); private static final float[] SIN = Util.make(new float[65536], fs -> { for (int ix = 0; ix < fs.length; ix++) { fs[ix] = (float)Math.sin(ix * Math.PI * 2.0 / 65536.0); } }); private static final RandomSource RANDOM = RandomSource.createThreadSafe(); /** * 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 leftmo */ private static final int[] 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 }; private static final double ONE_SIXTH = 0.16666666666666666; private static final int FRAC_EXP = 8; private static final int LUT_SIZE = 257; private static final double FRAC_BIAS = Double.longBitsToDouble(4805340802404319232L); private static final double[] ASIN_TAB = new double[257]; private static final double[] COS_TAB = new double[257]; /** * sin looked up in a table */ public static float sin(float value) { return SIN[(int)(value * 10430.378F) & 65535]; } /** * cos looked up in the sin table with the appropriate offset */ public static float cos(float value) { return SIN[(int)(value * 10430.378F + 16384.0F) & 65535]; } public static float sqrt(float value) { return (float)Math.sqrt(value); } /** * {@return the greatest integer less than or equal to the float argument} */ public static int floor(float value) { int i = (int)value; return value < i ? i - 1 : i; } /** * {@return the greatest integer less than or equal to the double argument} */ public static int floor(double value) { int i = (int)value; return value < i ? i - 1 : i; } /** * Long version of floor() */ public static long lfloor(double value) { long l = (long)value; return value < l ? l - 1L : l; } public static float abs(float value) { return Math.abs(value); } /** * {@return the unsigned value of an int} */ public static int abs(int value) { return Math.abs(value); } public static int ceil(float value) { int i = (int)value; return value > i ? i + 1 : i; } public static int ceil(double value) { int i = (int)value; return value > i ? i + 1 : i; } /** * {@return the given value if between the lower and the upper bound. If the value is less than the lower bound, returns the lower bound} If the value is greater than the upper bound, returns the upper bound. * * @param value The value that is clamped. * @param min The lower bound for the clamp. * @param max The upper bound for the clamp. */ public static int clamp(int value, int min, int max) { return Math.min(Math.max(value, min), max); } public static long clamp(long value, long min, long max) { return Math.min(Math.max(value, min), max); } /** * {@return the given value if between the lower and the upper bound. If the value is less than the lower bound, returns the lower bound} If the value is greater than the upper bound, returns the upper bound. * * @param value The value that is clamped. * @param min The lower bound for the clamp. * @param max The upper bound for the clamp. */ public static float clamp(float value, float min, float max) { return value < min ? min : Math.min(value, max); } /** * {@return the given value if between the lower and the upper bound. If the value is less than the lower bound, returns the lower bound} If the value is greater than the upper bound, returns the upper bound. * * @param value The value that is clamped. * @param min The lower bound for the clamp. * @param max The upper bound for the clamp. */ public static double clamp(double value, double min, double max) { return value < min ? min : Math.min(value, max); } /** * Method for linear interpolation of doubles. * * @param start Start value for the lerp. * @param end End value for the lerp. * @param delta A value between 0 and 1 that indicates the percentage of the lerp. (0 will give the start value and 1 will give the end value) If the value is not between 0 and 1, it is clamped. */ public static double clampedLerp(double start, double end, double delta) { if (delta < 0.0) { return start; } else { return delta > 1.0 ? end : lerp(delta, start, end); } } /** * Method for linear interpolation of floats. * * @param start Start value for the lerp. * @param end End value for the lerp. * @param delta A value between 0 and 1 that indicates the percentage of the lerp. (0 will give the start value and 1 will give the end value) If the value is not between 0 and 1, it is clamped. */ public static float clampedLerp(float start, float end, float delta) { if (delta < 0.0F) { return start; } else { return delta > 1.0F ? end : lerp(delta, start, end); } } /** * {@return the maximum of the absolute value of two numbers} */ public static double absMax(double x, double y) { if (x < 0.0) { x = -x; } if (y < 0.0) { y = -y; } return Math.max(x, y); } public static int floorDiv(int dividend, int divisor) { return Math.floorDiv(dividend, divisor); } public static int nextInt(RandomSource random, int minimum, int maximum) { return minimum >= maximum ? minimum : random.nextInt(maximum - minimum + 1) + minimum; } public static float nextFloat(RandomSource random, float minimum, float maximum) { return minimum >= maximum ? minimum : random.nextFloat() * (maximum - minimum) + minimum; } public static double nextDouble(RandomSource random, double minimum, double maximum) { return minimum >= maximum ? minimum : random.nextDouble() * (maximum - minimum) + minimum; } public static boolean equal(float x, float y) { return Math.abs(y - x) < 1.0E-5F; } public static boolean equal(double x, double y) { return Math.abs(y - x) < 1.0E-5F; } public static int positiveModulo(int x, int y) { return Math.floorMod(x, y); } public static float positiveModulo(float numerator, float denominator) { return (numerator % denominator + denominator) % denominator; } public static double positiveModulo(double numerator, double denominator) { return (numerator % denominator + denominator) % denominator; } public static boolean isMultipleOf(int number, int multiple) { return number % multiple == 0; } public static byte packDegrees(float degrees) { return (byte)floor(degrees * 256.0F / 360.0F); } public static float unpackDegrees(byte degrees) { return degrees * 360 / 256.0F; } /** * Adjust the angle so that its value is in the range [-180;180) */ public static int wrapDegrees(int angle) { int i = angle % 360; if (i >= 180) { i -= 360; } if (i < -180) { i += 360; } return i; } public static float wrapDegrees(long angle) { float f = (float)(angle % 360L); if (f >= 180.0F) { f -= 360.0F; } if (f < -180.0F) { f += 360.0F; } return f; } /** * The angle is reduced to an angle between -180 and +180 by mod, and a 360 check. */ public static float wrapDegrees(float value) { float f = value % 360.0F; if (f >= 180.0F) { f -= 360.0F; } if (f < -180.0F) { f += 360.0F; } return f; } /** * The angle is reduced to an angle between -180 and +180 by mod, and a 360 check. */ public static double wrapDegrees(double value) { double d = value % 360.0; if (d >= 180.0) { d -= 360.0; } if (d < -180.0) { d += 360.0; } return d; } /** * {@return the difference between two angles in degrees} */ public static float degreesDifference(float start, float end) { return wrapDegrees(end - start); } /** * {@return the absolute of the difference between two angles in degrees} */ public static float degreesDifferenceAbs(float start, float end) { return abs(degreesDifference(start, end)); } /** * Takes a rotation and compares it to another rotation. * If the difference is greater than a given maximum, clamps the original rotation between to have at most the given difference to the actual rotation. * This is used to match the body rotation of entities to their head rotation. * @return The new value for the rotation that was adjusted */ public static float rotateIfNecessary(float rotationToAdjust, float actualRotation, float maxDifference) { float f = degreesDifference(rotationToAdjust, actualRotation); float g = clamp(f, -maxDifference, maxDifference); return actualRotation - g; } /** * Changes value by stepSize towards the limit and returns the result. * If value is smaller than limit, the result will never be bigger than limit. * If value is bigger than limit, the result will never be smaller than limit. */ public static float approach(float value, float limit, float stepSize) { stepSize = abs(stepSize); return value < limit ? clamp(value + stepSize, value, limit) : clamp(value - stepSize, limit, value); } /** * Changes the angle by stepSize towards the limit in the direction where the distance is smaller. * {@see #approach(float, float, float)} */ public static float approachDegrees(float angle, float limit, float stepSize) { float f = degreesDifference(angle, limit); return approach(angle, angle + f, stepSize); } /** * Parses the string as an integer or returns the second parameter if it fails. */ public static int getInt(String value, int defaultValue) { return NumberUtils.toInt(value, defaultValue); } /** * {@return the input value rounded up to the next highest power of two} */ public static int smallestEncompassingPowerOfTwo(int value) { int i = value - 1; i |= i >> 1; i |= i >> 2; i |= i >> 4; i |= i >> 8; i |= i >> 16; return i + 1; } /** * Is the given value a power of two? (1, 2, 4, 8, 16, ...) */ public 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. */ public static int ceillog2(int value) { value = isPowerOfTwo(value) ? value : smallestEncompassingPowerOfTwo(value); return MULTIPLY_DE_BRUIJN_BIT_POSITION[(int)(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. */ public static int log2(int value) { return ceillog2(value) - (isPowerOfTwo(value) ? 0 : 1); } public static float frac(float number) { return number - floor(number); } /** * Gets the decimal portion of the given double. For instance, {@code frac(5.5)} returns {@code .5}. */ public static double frac(double number) { return number - lfloor(number); } @Deprecated public static long getSeed(Vec3i pos) { return getSeed(pos.getX(), pos.getY(), pos.getZ()); } @Deprecated public static long getSeed(int x, int y, int z) { long l = x * 3129871 ^ z * 116129781L ^ y; l = l * l * 42317861L + l * 11L; return l >> 16; } public static UUID createInsecureUUID(RandomSource random) { long l = random.nextLong() & -61441L | 16384L; long m = random.nextLong() & 4611686018427387903L | Long.MIN_VALUE; return new UUID(l, m); } /** * Generates a random UUID using the shared random */ public static UUID createInsecureUUID() { return createInsecureUUID(RANDOM); } public static double inverseLerp(double delta, double start, double end) { return (delta - start) / (end - start); } public static float inverseLerp(float delta, float start, float end) { return (delta - start) / (end - start); } public static boolean rayIntersectsAABB(Vec3 start, Vec3 end, AABB boundingBox) { double d = (boundingBox.minX + boundingBox.maxX) * 0.5; double e = (boundingBox.maxX - boundingBox.minX) * 0.5; double f = start.x - d; if (Math.abs(f) > e && f * end.x >= 0.0) { return false; } else { double g = (boundingBox.minY + boundingBox.maxY) * 0.5; double h = (boundingBox.maxY - boundingBox.minY) * 0.5; double i = start.y - g; if (Math.abs(i) > h && i * end.y >= 0.0) { return false; } else { double j = (boundingBox.minZ + boundingBox.maxZ) * 0.5; double k = (boundingBox.maxZ - boundingBox.minZ) * 0.5; double l = start.z - j; if (Math.abs(l) > k && l * end.z >= 0.0) { return false; } else { double m = Math.abs(end.x); double n = Math.abs(end.y); double o = Math.abs(end.z); double p = end.y * l - end.z * i; if (Math.abs(p) > h * o + k * n) { return false; } else { p = end.z * f - end.x * l; if (Math.abs(p) > e * o + k * m) { return false; } else { p = end.x * i - end.y * f; return Math.abs(p) < e * n + h * m; } } } } } } public static double atan2(double y, double x) { double d = x * x + y * y; if (Double.isNaN(d)) { return Double.NaN; } else { boolean bl = y < 0.0; if (bl) { y = -y; } boolean bl2 = x < 0.0; if (bl2) { x = -x; } boolean bl3 = y > x; if (bl3) { double e = x; x = y; y = e; } double e = fastInvSqrt(d); x *= e; y *= e; double f = FRAC_BIAS + y; int i = (int)Double.doubleToRawLongBits(f); double g = ASIN_TAB[i]; double h = COS_TAB[i]; double j = f - FRAC_BIAS; double k = y * h - x * j; double l = (6.0 + k * k) * k * 0.16666666666666666; double m = g + l; if (bl3) { m = (Math.PI / 2) - m; } if (bl2) { m = Math.PI - m; } if (bl) { m = -m; } return m; } } public static float invSqrt(float number) { return org.joml.Math.invsqrt(number); } public static double invSqrt(double number) { return org.joml.Math.invsqrt(number); } /** * Computes 1/sqrt(n) using the fast inverse square root with a constant of 0x5FE6EB50C7B537AA. */ @Deprecated public static double fastInvSqrt(double number) { double d = 0.5 * number; long l = Double.doubleToRawLongBits(number); l = 6910469410427058090L - (l >> 1); number = Double.longBitsToDouble(l); return number * (1.5 - d * number * number); } public static float fastInvCubeRoot(float number) { int i = Float.floatToIntBits(number); i = 1419967116 - i / 3; float f = Float.intBitsToFloat(i); f = 0.6666667F * f + 1.0F / (3.0F * f * f * number); return 0.6666667F * f + 1.0F / (3.0F * f * f * number); } public static int hsvToRgb(float hue, float saturation, float value) { return hsvToArgb(hue, saturation, value, 0); } public static int hsvToArgb(float hue, float saturation, float value, int alpha) { int i = (int)(hue * 6.0F) % 6; float f = hue * 6.0F - i; float g = value * (1.0F - saturation); float h = value * (1.0F - f * saturation); float j = value * (1.0F - (1.0F - f) * saturation); float k; float l; float m; switch (i) { case 0: k = value; l = j; m = g; break; case 1: k = h; l = value; m = g; break; case 2: k = g; l = value; m = j; break; case 3: k = g; l = h; m = value; break; case 4: k = j; l = g; m = value; break; case 5: k = value; l = g; m = h; break; default: throw new RuntimeException("Something went wrong when converting from HSV to RGB. Input was " + hue + ", " + saturation + ", " + value); } return ARGB.color(alpha, clamp((int)(k * 255.0F), 0, 255), clamp((int)(l * 255.0F), 0, 255), clamp((int)(m * 255.0F), 0, 255)); } public static int murmurHash3Mixer(int input) { input ^= input >>> 16; input *= -2048144789; input ^= input >>> 13; input *= -1028477387; return input ^ input >>> 16; } public static int binarySearch(int min, int max, IntPredicate isTargetBeforeOrAt) { int i = max - min; while (i > 0) { int j = i / 2; int k = min + j; if (isTargetBeforeOrAt.test(k)) { i = j; } else { min = k + 1; i -= j + 1; } } return min; } public static int lerpInt(float delta, int start, int end) { return start + floor(delta * (end - start)); } public static int lerpDiscrete(float delta, int start, int end) { int i = end - start; return start + floor(delta * (i - 1)) + (delta > 0.0F ? 1 : 0); } /** * Method for linear interpolation of floats * * @param delta A value usually between 0 and 1 that indicates the percentage of the lerp. (0 will give the start value and 1 will give the end value) * @param start Start value for the lerp * @param end End value for the lerp */ public static float lerp(float delta, float start, float end) { return start + delta * (end - start); } public static Vec3 lerp(double delta, Vec3 start, Vec3 end) { return new Vec3(lerp(delta, start.x, end.x), lerp(delta, start.y, end.y), lerp(delta, start.z, end.z)); } /** * Method for linear interpolation of doubles * * @param delta A value usually between 0 and 1 that indicates the percentage of the lerp. (0 will give the start value and 1 will give the end value) * @param start Start value for the lerp * @param end End value for the lerp */ public static double lerp(double delta, double start, double end) { return start + delta * (end - start); } public static double lerp2(double delta1, double delta2, double start1, double end1, double start2, double end2) { return lerp(delta2, lerp(delta1, start1, end1), lerp(delta1, start2, end2)); } public static double lerp3( double delta1, double delta2, double delta3, double start1, double end1, double start2, double end2, double start3, double end3, double start4, double end4 ) { return lerp(delta3, lerp2(delta1, delta2, start1, end1, start2, end2), lerp2(delta1, delta2, start3, end3, start4, end4)); } public static float catmullrom(float delta, float controlPoint1, float controlPoint2, float controlPoint3, float controlPoint4) { return 0.5F * ( 2.0F * controlPoint2 + (controlPoint3 - controlPoint1) * delta + (2.0F * controlPoint1 - 5.0F * controlPoint2 + 4.0F * controlPoint3 - controlPoint4) * delta * delta + (3.0F * controlPoint2 - controlPoint1 - 3.0F * controlPoint3 + controlPoint4) * delta * delta * delta ); } public static double smoothstep(double input) { return input * input * input * (input * (input * 6.0 - 15.0) + 10.0); } public static double smoothstepDerivative(double input) { return 30.0 * input * input * (input - 1.0) * (input - 1.0); } public static int sign(double x) { if (x == 0.0) { return 0; } else { return x > 0.0 ? 1 : -1; } } /** * Linearly interpolates an angle between the start between the start and end values given as degrees. * * @param delta A value between 0 and 1 that indicates the percentage of the lerp. (0 will give the start value and 1 will give the end value) */ public static float rotLerp(float delta, float start, float end) { return start + delta * wrapDegrees(end - start); } public static double rotLerp(double delta, double start, double end) { return start + delta * wrapDegrees(end - start); } public static float rotLerpRad(float delta, float start, float end) { float f = end - start; while (f < (float) -Math.PI) { f += (float) (Math.PI * 2); } while (f >= (float) Math.PI) { f -= (float) (Math.PI * 2); } return start + delta * f; } public static float triangleWave(float input, float period) { return (Math.abs(input % period - period * 0.5F) - period * 0.25F) / (period * 0.25F); } public static float square(float value) { return value * value; } public static double square(double value) { return value * value; } public static int square(int value) { return value * value; } public static long square(long value) { return value * value; } public static double clampedMap(double input, double inputMin, double inputMax, double ouputMin, double outputMax) { return clampedLerp(ouputMin, outputMax, inverseLerp(input, inputMin, inputMax)); } public static float clampedMap(float input, float inputMin, float inputMax, float outputMin, float outputMax) { return clampedLerp(outputMin, outputMax, inverseLerp(input, inputMin, inputMax)); } public static double map(double input, double inputMin, double inputMax, double outputMin, double outputMax) { return lerp(inverseLerp(input, inputMin, inputMax), outputMin, outputMax); } public static float map(float input, float inputMin, float inputMax, float outputMin, float outputMax) { return lerp(inverseLerp(input, inputMin, inputMax), outputMin, outputMax); } public static double wobble(double input) { return input + (2.0 * RandomSource.create(floor(input * 3000.0)).nextDouble() - 1.0) * 1.0E-7 / 2.0; } /** * Rounds the given value up to a multiple of factor. * @return The smallest integer multiple of factor that is greater than or equal to the value */ public static int roundToward(int value, int factor) { return positiveCeilDiv(value, factor) * factor; } /** * Returns the smallest (closest to negative infinity) int value that is greater than or equal to the algebraic quotient. * @see java.lang.Math#floorDiv(int, int) */ public static int positiveCeilDiv(int x, int y) { return -Math.floorDiv(-x, y); } public static int randomBetweenInclusive(RandomSource random, int minInclusive, int maxInclusive) { return random.nextInt(maxInclusive - minInclusive + 1) + minInclusive; } public static float randomBetween(RandomSource random, float minInclusive, float maxExclusive) { return random.nextFloat() * (maxExclusive - minInclusive) + minInclusive; } /** * Generates a value from a normal distribution with the given mean and deviation. */ public static float normal(RandomSource random, float mean, float deviation) { return mean + (float)random.nextGaussian() * deviation; } public static double lengthSquared(double xDistance, double yDistance) { return xDistance * xDistance + yDistance * yDistance; } public static double length(double xDistance, double yDistance) { return Math.sqrt(lengthSquared(xDistance, yDistance)); } public static float length(float xDistance, float yDistance) { return (float)Math.sqrt(lengthSquared(xDistance, yDistance)); } public static double lengthSquared(double xDistance, double yDistance, double zDistance) { return xDistance * xDistance + yDistance * yDistance + zDistance * zDistance; } public static double length(double xDistance, double yDistance, double zDistance) { return Math.sqrt(lengthSquared(xDistance, yDistance, zDistance)); } public static float lengthSquared(float xDistance, float yDistance, float zDistance) { return xDistance * xDistance + yDistance * yDistance + zDistance * zDistance; } /** * Gets the value closest to zero that is not closer to zero than the given value and is a multiple of the factor. */ public static int quantize(double value, int factor) { return floor(value / factor) * factor; } public static IntStream outFromOrigin(int input, int lowerBound, int upperBound) { return outFromOrigin(input, lowerBound, upperBound, 1); } public static IntStream outFromOrigin(int input, int lowerBound, int upperBound, int steps) { if (lowerBound > upperBound) { throw new IllegalArgumentException(String.format(Locale.ROOT, "upperbound %d expected to be > lowerBound %d", upperBound, lowerBound)); } else if (steps < 1) { throw new IllegalArgumentException(String.format(Locale.ROOT, "steps expected to be >= 1, was %d", steps)); } else { return input >= lowerBound && input <= upperBound ? IntStream.iterate(input, l -> { int m = Math.abs(input - l); return input - m >= lowerBound || input + m <= upperBound; }, m -> { boolean bl = m <= input; int n = Math.abs(input - m); boolean bl2 = input + n + steps <= upperBound; if (!bl || !bl2) { int o = input - n - (bl ? steps : 0); if (o >= lowerBound) { return o; } } return input + n + steps; }) : IntStream.empty(); } } public static Quaternionf rotationAroundAxis(Vector3f axis, Quaternionf cameraOrentation, Quaternionf output) { float f = axis.dot(cameraOrentation.x, cameraOrentation.y, cameraOrentation.z); return output.set(axis.x * f, axis.y * f, axis.z * f, cameraOrentation.w).normalize(); } public static int mulAndTruncate(Fraction fraction, int factor) { return fraction.getNumerator() * factor / fraction.getDenominator(); } public static float easeInOutSine(float value) { return -(cos((float) Math.PI * value) - 1.0F) / 2.0F; } static { for (int i = 0; i < 257; i++) { double d = i / 256.0; double e = Math.asin(d); COS_TAB[i] = Math.cos(e); ASIN_TAB[i] = e; } } }