From 30ef2fc9c8dd8e767512585b2f6cd535098864c6 Mon Sep 17 00:00:00 2001 From: FalsePattern <30945458+FalsePattern@users.noreply.github.com> Date: Sat, 30 Jul 2022 20:26:34 +0200 Subject: [PATCH] new math class --- .../com/falsepattern/lib/compat/BlockPos.java | 7 +- .../com/falsepattern/lib/compat/Vec3i.java | 4 +- .../com/falsepattern/lib/util/MathUtil.java | 623 ++++++++++++++++++ 3 files changed, 630 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/falsepattern/lib/util/MathUtil.java diff --git a/src/main/java/com/falsepattern/lib/compat/BlockPos.java b/src/main/java/com/falsepattern/lib/compat/BlockPos.java index 5636710..da67b47 100644 --- a/src/main/java/com/falsepattern/lib/compat/BlockPos.java +++ b/src/main/java/com/falsepattern/lib/compat/BlockPos.java @@ -21,6 +21,7 @@ package com.falsepattern.lib.compat; import com.falsepattern.lib.StableAPI; +import com.falsepattern.lib.util.MathUtil; import com.google.common.collect.AbstractIterator; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @@ -52,7 +53,7 @@ public class BlockPos extends Vec3i { */ public static final BlockPos ORIGIN = new BlockPos(0, 0, 0); private static final Logger LOGGER = LogManager.getLogger(); - private static final int NUM_X_BITS = 1 + MathHelper.calculateLogBaseTwo(MathHelper.roundUpToPowerOfTwo(30000000)); + private static final int NUM_X_BITS = 1 + MathUtil.log2(MathUtil.smallestEncompassingPowerOfTwo(30000000)); private static final int NUM_Z_BITS = NUM_X_BITS; private static final int NUM_Y_BITS = 64 - NUM_X_BITS - NUM_Z_BITS; private static final long Y_MASK = (1L << NUM_Y_BITS) - 1L; @@ -431,7 +432,7 @@ public class BlockPos extends Vec3i { } public BlockPos.MutableBlockPos setPos(double x, double y, double z) { - return setPos(MathHelper.floor_double(x), MathHelper.floor_double(y), MathHelper.floor_double(z)); + return setPos(MathUtil.floor(x), MathUtil.floor(y), MathUtil.floor(z)); } public BlockPos.MutableBlockPos setPos(int x, int y, int z) { @@ -483,7 +484,7 @@ public class BlockPos extends Vec3i { } public static BlockPos.PooledMutableBlockPos retain(double x, double y, double z) { - return retain(MathHelper.floor_double(x), MathHelper.floor_double(y), MathHelper.floor_double(z)); + return retain(MathUtil.floor(x), MathUtil.floor(y), MathUtil.floor(z)); } @SideOnly(Side.CLIENT) diff --git a/src/main/java/com/falsepattern/lib/compat/Vec3i.java b/src/main/java/com/falsepattern/lib/compat/Vec3i.java index 23a2c14..200817c 100644 --- a/src/main/java/com/falsepattern/lib/compat/Vec3i.java +++ b/src/main/java/com/falsepattern/lib/compat/Vec3i.java @@ -22,6 +22,8 @@ package com.falsepattern.lib.compat; import com.falsepattern.lib.StableAPI; import javax.annotation.concurrent.Immutable; + +import com.falsepattern.lib.util.MathUtil; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -54,7 +56,7 @@ public class Vec3i implements Comparable { * @param z the z */ public Vec3i(double x, double y, double z) { - this(MathHelper.floor_double(x), MathHelper.floor_double(y), MathHelper.floor_double(z)); + this(MathUtil.floor(x), MathUtil.floor(y), MathUtil.floor(z)); } public int compareTo(@NonNull Vec3i vec) { diff --git a/src/main/java/com/falsepattern/lib/util/MathUtil.java b/src/main/java/com/falsepattern/lib/util/MathUtil.java new file mode 100644 index 0000000..4fbcef5 --- /dev/null +++ b/src/main/java/com/falsepattern/lib/util/MathUtil.java @@ -0,0 +1,623 @@ +/** + * Copyright (C) 2022 FalsePattern + * All Rights Reserved + *

+ * The above copyright notice, this permission notice and the word "SNEED" + * shall be included in all copies or substantial portions of the Software. + *

+ * 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. + *

+ * 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. + *

+ * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +package com.falsepattern.lib.util; + +import com.falsepattern.lib.StableAPI; +import com.falsepattern.lib.compat.Vec3i; + +import java.util.Random; +import java.util.UUID; + +public class MathUtil { + @StableAPI(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + public static float cos(float value) { + return SIN_TABLE[(int) (value * 10430.378F + 16384.0F) & 65535]; + } + + @StableAPI(since = "0.10.0") + public static float sqrt(float value) { + return (float) Math.sqrt(value); + } + + @StableAPI(since = "0.10.0") + public static float sqrt(double value) { + return (float) Math.sqrt(value); + } + + /** + * Returns the greatest integer less than or equal to the float argument + */ + @StableAPI(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + public static int floor(double value) { + int i = (int) value; + return value < (double) i ? i - 1 : i; + } + + /** + * Long version of floor() + */ + @StableAPI(since = "0.10.0") + public static long lfloor(double value) { + long i = (long) value; + return value < (double) i ? i - 1L : i; + } + + @StableAPI(since = "0.10.0") + public static int absFloor(double value) { + return (int) (value >= 0.0D ? value : -value + 1.0D); + } + + @StableAPI(since = "0.10.0") + public static float abs(float value) { + return value >= 0.0F ? value : -value; + } + + /** + * Returns the unsigned value of an int. + */ + @StableAPI(since = "0.10.0") + public static int abs(int value) { + return value >= 0 ? value : -value; + } + + @StableAPI(since = "0.10.0") + public static int ceil(float value) { + int i = (int) value; + return value > (float) i ? i + 1 : i; + } + + @StableAPI(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + public static float clamp(float num, float min, float max) { + return num < min ? min : Math.min(num, max); + } + + @StableAPI(since = "0.10.0") + public static double clamp(double num, double min, double max) { + return num < min ? min : Math.min(num, max); + } + + @StableAPI(since = "0.10.0") + 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(since = "0.10.0") + 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 specifed bucket sizes. + */ + @StableAPI(since = "0.10.0") + public static int intFloorDiv(int number, int bucket) { + return number < 0 ? -((-number - 1) / bucket) - 1 : number / bucket; + } + + @StableAPI(since = "0.10.0") + public static int getInt(Random random, int minimum, int maximum) { + return minimum >= maximum ? minimum : random.nextInt(maximum - minimum + 1) + minimum; + } + + @StableAPI(since = "0.10.0") + public static float nextFloat(Random random, float minimum, float maximum) { + return minimum >= maximum ? minimum : random.nextFloat() * (maximum - minimum) + minimum; + } + + @StableAPI(since = "0.10.0") + public static double nextDouble(Random random, double minimum, double maximum) { + return minimum >= maximum ? minimum : random.nextDouble() * (maximum - minimum) + minimum; + } + + @StableAPI(since = "0.10.0") + public static double average(long[] values) { + long i = 0L; + + for (long j : values) { + i += j; + } + + return (double) i / (double) values.length; + } + + @StableAPI(since = "0.10.0") + public static boolean epsilonEquals(float a, float b) { + return abs(b - a) < 1.0E-5F; + } + + @StableAPI(since = "0.10.0") + public static int normalizeAngle(int a, int b) { + return (a % b + b) % b; + } + + @StableAPI(since = "0.10.0") + public static float positiveModulo(float numerator, float denominator) { + return (numerator % denominator + denominator) % denominator; + } + + @StableAPI(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + public static double getDouble(String value, double defaultValue) { + try { + return Double.parseDouble(value); + } catch (Throwable var4) { + return defaultValue; + } + } + + @StableAPI(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + public static double frac(double number) { + return number - Math.floor(number); + } + + @StableAPI(since = "0.10.0") + public static long getPositionRandom(Vec3i pos) { + return getCoordinateRandom(pos.getX(), pos.getY(), pos.getZ()); + } + + @StableAPI(since = "0.10.0") + 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(since = "0.10.0") + public static UUID getRandomUUID() { + return getRandomUUID(RANDOM); + } + + @StableAPI(since = "0.10.0") + public static double pct(double a, double b, double c) { + return (a - b) / (c - b); + } + + @StableAPI(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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(since = "0.10.0") + 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; + } +} \ No newline at end of file