diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/brigadier/arguments/LorenzVecArgumentType.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/brigadier/arguments/Vec3ArgumentType.kt similarity index 63% rename from src/main/java/at/hannibal2/skyhanni/config/commands/brigadier/arguments/LorenzVecArgumentType.kt rename to src/main/java/at/hannibal2/skyhanni/config/commands/brigadier/arguments/Vec3ArgumentType.kt index 19f7f39ac3cb..d7221e7aa968 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/brigadier/arguments/LorenzVecArgumentType.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/brigadier/arguments/Vec3ArgumentType.kt @@ -2,7 +2,6 @@ package at.hannibal2.skyhanni.config.commands.brigadier.arguments import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.LocationUtils -import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.RegexUtils.findMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern @@ -10,14 +9,18 @@ import com.mojang.brigadier.LiteralMessage import com.mojang.brigadier.StringReader import com.mojang.brigadier.arguments.ArgumentType import com.mojang.brigadier.exceptions.SimpleCommandExceptionType +import net.minecraft.commands.arguments.coordinates.Vec3Argument +import net.minecraft.world.phys.Vec3 -sealed class LorenzVecArgumentType : ArgumentType { +/** + * Fabric-compatible alternative to [Vec3Argument] that does not depend on server state. + */ +sealed class Vec3ArgumentType : ArgumentType { - protected abstract fun toVec(x: kotlin.Double, y: kotlin.Double, z: kotlin.Double): LorenzVec - - override fun parse(reader: StringReader): LorenzVec { + override fun parse(reader: StringReader): Vec3 { val input = if (reader.canRead() && reader.peek() == '"') reader.readQuotedString() else consumeMatch(reader) + return parseCoords(input) } @@ -33,48 +36,44 @@ sealed class LorenzVecArgumentType : ArgumentType { throw invalidCoordinates.createWithContext(reader) } - private fun parseCoords(input: String): LorenzVec { + private fun parseCoords(input: String): Vec3 { val playerPos = LocationUtils.playerLocation() for (pattern in patterns) { pattern.matchMatcher(input) { val x = if (group("x") == "~") playerPos.x else group("x").toDouble() val y = if (group("y") == "~") playerPos.y else group("y").toDouble() val z = if (group("z") == "~") playerPos.z else group("z").toDouble() - return toVec(x, y, z) + return Vec3(x, y, z) } } throw invalidCoordinates.create() } - data object Int : LorenzVecArgumentType() { - override fun toVec(x: kotlin.Double, y: kotlin.Double, z: kotlin.Double) = - LorenzVec(x.toInt(), y.toInt(), z.toInt()) - - override fun getExamples(): Collection = listOf("1 2 3", "-4 0 5", "~ 64 ~", "1:2:3", "LorenzVec(1, 2, 3)") + data object Int : Vec3ArgumentType() { + override fun getExamples(): Collection = + listOf("1 2 3", "-4 0 5", "~ 64 ~", "1:2:3", "Vec3(1, 2, 3)") } - data object Double : LorenzVecArgumentType() { - override fun toVec(x: kotlin.Double, y: kotlin.Double, z: kotlin.Double) = LorenzVec(x, y, z) - + data object Double : Vec3ArgumentType() { override fun getExamples(): Collection = - listOf("1.0 2.5 -3", "0.0 0.0 0.0", "-1.7 ~ ~", "-78.8:68.0:-28.7", "LorenzVec(-91.7, 70.0, 29.3)") + listOf("1.0 2.5 -3", "0.0 0.0 0.0", "-1.7 ~ ~", "-78.8:68.0:-28.7", "Vec3(-91.7, 70.0, 29.3)") } @SkyHanniModule companion object { - private val patternGroup = RepoPattern.group("commands.brigadier.arguments.lorenzvec") + private val patternGroup = RepoPattern.group("commands.brigadier.arguments.vec3") /** - * REGEX-TEST: LorenzVec(-91.7, 70.0, 29.3) - * REGEX-TEST: LorenzVec(1, 2, 3) - * REGEX-TEST: LorenzVec(0.0, 0.0, 0.0) - * REGEX-TEST: LorenzVec(-78.8, 68.0, -28.7) + * REGEX-TEST: Vec3(-91.7, 70.0, 29.3) + * REGEX-TEST: Vec3(1, 2, 3) + * REGEX-TEST: Vec3(0.0, 0.0, 0.0) + * REGEX-TEST: Vec3(-78.8, 68.0, -28.7) */ - private val lorenzVecPattern by patternGroup.pattern( - "lorenz", - """LorenzVec\((?-?\d+(?:\.\d+)?),\s*(?-?\d+(?:\.\d+)?),\s*(?-?\d+(?:\.\d+)?)\)""", + private val vec3Pattern by patternGroup.pattern( + "vec3", + """Vec3\((?-?\d+(?:\.\d+)?),\s*(?-?\d+(?:\.\d+)?),\s*(?-?\d+(?:\.\d+)?)\)""", ) /** @@ -101,14 +100,15 @@ sealed class LorenzVecArgumentType : ArgumentType { """(?~|-?\d+(?:\.\d+)?)\s+(?~|-?\d+(?:\.\d+)?)\s+(?~|-?\d+(?:\.\d+)?)""", ) - private val patterns = listOf(lorenzVecPattern, colonPattern, spacePattern) + private val patterns = listOf(vec3Pattern, colonPattern, spacePattern) - private val invalidCoordinates = SimpleCommandExceptionType(LiteralMessage("Invalid coordinates")) + private val invalidCoordinates = + SimpleCommandExceptionType(LiteralMessage("Invalid coordinates")) /** Only accepts integers as input */ - fun int(): LorenzVecArgumentType = Int + fun int(): Vec3ArgumentType = Int /** Accepts any number as input */ - fun double(): LorenzVecArgumentType = Double + fun double(): Vec3ArgumentType = Double } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/pathfind/NavigationHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/pathfind/NavigationHelper.kt index c33458fb6400..0452b42e4798 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/pathfind/NavigationHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/pathfind/NavigationHelper.kt @@ -5,7 +5,7 @@ import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.config.commands.CommandRegistrationEvent import at.hannibal2.skyhanni.config.commands.brigadier.BrigadierArguments import at.hannibal2.skyhanni.config.commands.brigadier.BrigadierUtils -import at.hannibal2.skyhanni.config.commands.brigadier.arguments.LorenzVecArgumentType +import at.hannibal2.skyhanni.config.commands.brigadier.arguments.Vec3ArgumentType import at.hannibal2.skyhanni.data.IslandGraphs import at.hannibal2.skyhanni.data.IslandGraphs.pathFind import at.hannibal2.skyhanni.data.model.graph.GraphNode @@ -15,12 +15,14 @@ import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.GraphUtils import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.SkyBlockUtils +import at.hannibal2.skyhanni.utils.VectorUtils.toLocalFormat import at.hannibal2.skyhanni.utils.chat.TextHelper import at.hannibal2.skyhanni.utils.chat.TextHelper.asComponent import at.hannibal2.skyhanni.utils.chat.TextHelper.onClick import at.hannibal2.skyhanni.utils.chat.TextHelper.send import at.hannibal2.skyhanni.utils.collection.CollectionUtils.sorted import at.hannibal2.skyhanni.utils.compat.hover +import at.hannibal2.skyhanni.utils.toLorenzVec @SkyHanniModule object NavigationHelper { @@ -129,8 +131,8 @@ object NavigationHelper { event.registerBrigadier("shnavigate") { description = "Using path finder to go to locations" aliases = listOf("shnav") - argCallback("coords", LorenzVecArgumentType.double()) { location -> - pathFind(location.add(-1, -1, -1), "Custom Goal", condition = { true }) + argCallback("coords", Vec3ArgumentType.double()) { location -> + pathFind(location.add(-1.0).toLorenzVec(), "Custom Goal", condition = { true }) ChatUtils.chat("Started Navigating to custom goal at §f${location.toLocalFormat()}", messageId = messageId) } argCallback("search", BrigadierArguments.greedyString(), BrigadierUtils.dynamicSuggestionProvider { getNames() }) { diff --git a/src/main/java/at/hannibal2/skyhanni/test/ParkourWaypointSaver.kt b/src/main/java/at/hannibal2/skyhanni/test/ParkourWaypointSaver.kt index c9bd175948f7..3d87ac4c281e 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/ParkourWaypointSaver.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/ParkourWaypointSaver.kt @@ -10,7 +10,6 @@ import at.hannibal2.skyhanni.test.graph.GraphEditor import at.hannibal2.skyhanni.utils.LocationUtils import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzVec -import at.hannibal2.skyhanni.utils.LorenzVec.Companion.toLorenzVec import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.ParkourHelper import at.hannibal2.skyhanni.utils.PlayerUtils @@ -18,6 +17,7 @@ import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.SkyBlockUtils import at.hannibal2.skyhanni.utils.render.WorldRenderUtils.drawFilledBoundingBox import at.hannibal2.skyhanni.utils.render.WorldRenderUtils.expandBlock +import at.hannibal2.skyhanni.utils.toLorenzVec import net.minecraft.client.Minecraft import kotlin.time.Duration.Companion.milliseconds diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index eb088fa3a949..2933f1b60c30 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -9,7 +9,7 @@ import at.hannibal2.skyhanni.config.ConfigManager import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator import at.hannibal2.skyhanni.config.commands.CommandCategory import at.hannibal2.skyhanni.config.commands.CommandRegistrationEvent -import at.hannibal2.skyhanni.config.commands.brigadier.arguments.LorenzVecArgumentType +import at.hannibal2.skyhanni.config.commands.brigadier.arguments.Vec3ArgumentType import at.hannibal2.skyhanni.config.core.config.Position import at.hannibal2.skyhanni.data.HypixelData import at.hannibal2.skyhanni.data.IslandGraphs @@ -47,7 +47,6 @@ import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.NeuInternalName import at.hannibal2.skyhanni.utils.NeuItems.getItemStack import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators -import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.ReflectionUtils.makeAccessible import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables @@ -64,6 +63,7 @@ import at.hannibal2.skyhanni.utils.render.WorldRenderUtils.drawWaypointFilled import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.renderables.addLine import at.hannibal2.skyhanni.utils.system.PlatformUtils +import at.hannibal2.skyhanni.utils.toLorenzVec import net.minecraft.client.gui.components.debug.DebugScreenDisplayer import net.minecraft.client.gui.components.debug.DebugScreenEntries import net.minecraft.client.gui.components.debug.DebugScreenEntry @@ -565,11 +565,11 @@ object SkyHanniDebugsAndTests { event.registerBrigadier("shtestwaypoint") { description = "Set a waypoint on that location" category = CommandCategory.DEVELOPER_TEST - arg("waypoint", LorenzVecArgumentType.double()) { vec -> + arg("waypoint", Vec3ArgumentType.double()) { vec -> literalCallback("pathfind") { - waypoint(getArg(vec), true) + waypoint(getArg(vec).toLorenzVec(), true) } - callback { waypoint(getArg(vec)) } + callback { waypoint(getArg(vec).toLorenzVec()) } } simpleCallback { waypoint() } diff --git a/src/main/java/at/hannibal2/skyhanni/test/graph/GraphParkour.kt b/src/main/java/at/hannibal2/skyhanni/test/graph/GraphParkour.kt index 739ac118eef3..15e0c3e522c6 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/graph/GraphParkour.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/graph/GraphParkour.kt @@ -12,9 +12,9 @@ import at.hannibal2.skyhanni.test.graph.GraphEditor.isEnabled import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzVec -import at.hannibal2.skyhanni.utils.LorenzVec.Companion.toLorenzVec import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.coroutines.CoroutineConfig +import at.hannibal2.skyhanni.utils.toLorenzVec @SkyHanniModule object GraphParkour { diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LocationUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LocationUtils.kt index c600944239dc..c366b5f4793b 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LocationUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LocationUtils.kt @@ -5,6 +5,7 @@ import at.hannibal2.skyhanni.utils.compat.MinecraftCompat import net.minecraft.core.Direction import net.minecraft.world.entity.Entity import net.minecraft.world.phys.AABB +import net.minecraft.world.phys.Vec3 import kotlin.math.PI import kotlin.math.abs import kotlin.math.atan2 @@ -337,15 +338,15 @@ object LocationUtils { return location } - fun AABB.calculateEdges(): Set> { - val bottomLeftFront = LorenzVec(minX, minY, minZ) - val bottomLeftBack = LorenzVec(minX, minY, maxZ) - val topLeftFront = LorenzVec(minX, maxY, minZ) - val topLeftBack = LorenzVec(minX, maxY, maxZ) - val bottomRightFront = LorenzVec(maxX, minY, minZ) - val bottomRightBack = LorenzVec(maxX, minY, maxZ) - val topRightFront = LorenzVec(maxX, maxY, minZ) - val topRightBack = LorenzVec(maxX, maxY, maxZ) + fun AABB.calculateEdges(): Set> { + val bottomLeftFront = Vec3(minX, minY, minZ) + val bottomLeftBack = Vec3(minX, minY, maxZ) + val topLeftFront = Vec3(minX, maxY, minZ) + val topLeftBack = Vec3(minX, maxY, maxZ) + val bottomRightFront = Vec3(maxX, minY, minZ) + val bottomRightBack = Vec3(maxX, minY, maxZ) + val topRightFront = Vec3(maxX, maxY, minZ) + val topRightBack = Vec3(maxX, maxY, maxZ) return setOf( // Bottom face diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt index b0ef2b6e61ce..a232ae587905 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt @@ -1,7 +1,9 @@ +@file:Suppress("Deprecation") + package at.hannibal2.skyhanni.utils -import at.hannibal2.skyhanni.utils.LocationUtils.calculateEdges import at.hannibal2.skyhanni.utils.NumberUtil.roundTo +import at.hannibal2.skyhanni.utils.VectorUtils.edges import com.google.gson.annotations.Expose import net.minecraft.core.BlockPos import net.minecraft.core.Rotations @@ -22,13 +24,14 @@ import kotlin.math.round import kotlin.math.sin import kotlin.math.sqrt -@Suppress("TooManyFunctions", "MemberVisibilityCanBePrivate") +@Deprecated("Use Vec3 and VectorUtils instead.", ReplaceWith("Vec3(this)")) +@Suppress("TooManyFunctions") data class LorenzVec( val x: Double, val y: Double, val z: Double, ) { - val edges by lazy { boundingToOffset(1.0, 1.0, 1.0).inflate(0.0001, 0.0001, 0.0001).calculateEdges() } + val edges: Set> by lazy { toVec3().edges.toLorenzVecPairs() } constructor() : this(0.0, 0.0, 0.0) @@ -279,16 +282,16 @@ data class LorenzVec( return LorenzVec(x, y, z) } - fun List.toLorenzVec(): LorenzVec { - if (size != 3) error("Can not transform a list of size $size to LorenzVec") - - return LorenzVec(this[0], this[1], this[2]) - } - val expandVector = LorenzVec(0.0020000000949949026, 0.0020000000949949026, 0.0020000000949949026) } } +fun List.toLorenzVec(): LorenzVec { + if (size != 3) error("Can not transform a list of size $size to LorenzVec") + + return LorenzVec(this[0], this[1], this[2]) +} + fun BlockPos.toLorenzVec(): LorenzVec = LorenzVec(x, y, z) fun Entity.getLorenzVec(): LorenzVec = position().toLorenzVec() @@ -330,3 +333,6 @@ fun Array.toLorenzVec(): LorenzVec { fun AABB.expand(vec: LorenzVec): AABB = inflate(vec.x, vec.y, vec.z) fun AABB.expand(amount: Double): AABB = inflate(amount, amount, amount) + +fun Set>.toLorenzVecPairs(): Set> = + mapTo(mutableSetOf()) { (a, b) -> a.toLorenzVec() to b.toLorenzVec() } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/PolynomialFitter.kt b/src/main/java/at/hannibal2/skyhanni/utils/PolynomialFitter.kt index 139eceee4e5e..b83536da8650 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/PolynomialFitter.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/PolynomialFitter.kt @@ -1,6 +1,5 @@ package at.hannibal2.skyhanni.utils -import at.hannibal2.skyhanni.utils.LorenzVec.Companion.toLorenzVec import kotlin.math.pow class PolynomialFitter(private val degree: Int) { diff --git a/src/main/java/at/hannibal2/skyhanni/utils/VectorUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/VectorUtils.kt new file mode 100644 index 000000000000..327b09f74953 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/VectorUtils.kt @@ -0,0 +1,276 @@ +package at.hannibal2.skyhanni.utils + +import at.hannibal2.skyhanni.utils.LocationUtils.calculateEdges +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo +import at.hannibal2.skyhanni.utils.VectorUtils.plus +import at.hannibal2.skyhanni.utils.VectorUtils.times +import com.google.gson.annotations.Expose +import net.minecraft.core.BlockPos +import net.minecraft.core.Rotations +import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket +import net.minecraft.world.entity.Entity +import net.minecraft.world.phys.AABB +import net.minecraft.world.phys.Vec3 +import kotlin.math.abs +import kotlin.math.absoluteValue +import kotlin.math.acos +import kotlin.math.ceil +import kotlin.math.cos +import kotlin.math.floor +import kotlin.math.max +import kotlin.math.min +import kotlin.math.pow +import kotlin.math.round +import kotlin.math.sin + +// TODO refactor instead of suppressing TooManyFunctions +// TODO remove unused suppress once LorenzVec is gone +@Suppress("TooManyFunctions", "unused") +object VectorUtils { + private val edgeCache = mutableMapOf>>() + + val Vec3.edges: Set> + get() = edgeCache.getOrPut(this) { + boundingToOffset(1.0, 1.0, 1.0) + .inflate(0.0001, 0.0001, 0.0001) + .calculateEdges() + } + + fun Vec3.toBlockPos(): BlockPos = + BlockPos(floor(x).toInt(), floor(y).toInt(), floor(z).toInt()) + + fun Vec3.distanceIgnoreY(other: Vec3): Double = distanceSqIgnoreY(other).pow(0.5) + + // distance -> distanceTo + + // distanceSq -> distanceToSqr + + fun Vec3.distanceChebyshevIgnoreY(other: Vec3) = max(abs(x - other.x), abs(z - other.z)) + + fun Vec3.distanceSqIgnoreY(other: Vec3): Double { + val dx = other.x - x + val dz = other.z - z + return (dx * dx + dz * dz) + } + + fun Vec3.distanceSqOnlyY(other: Vec3): Double { + val dy = other.y - y + return (dy * dy) + } + + operator fun Vec3.plus(other: Vec3): Vec3 = add(other) + + operator fun Vec3.minus(other: Vec3): Vec3 = subtract(other) + + operator fun Vec3.times(other: Vec3): Vec3 = multiply(other) + operator fun Vec3.times(other: Double): Vec3 = multiply(other, other, other) + + operator fun Vec3.div(other: Vec3) = Vec3(x / other.x, y / other.y, z / other.z) + operator fun Vec3.div(other: Double) = Vec3(x / other, y / other, z / other) + + operator fun Vec3.component1() = x + operator fun Vec3.component2() = y + operator fun Vec3.component3() = y + + fun Vec3.angleAsCos(other: Vec3) = normalize().dot(other.normalize()) + + fun Vec3.angleInRad(other: Vec3) = acos(angleAsCos(other)) + + fun Vec3.angleInDeg(other: Vec3) = Math.toDegrees(angleInRad(other)) + + fun Vec3.scaledTo(other: Vec3) = this.normalize() * other.length() + + fun Vec3.inverse() = Vec3(1.0 / x, 1.0 / y, 1.0 / z) + + fun Vec3.min(): Double = minOf(x, y, z) + fun Vec3.max(): Double = maxOf(x, y, z) + + fun Vec3.add(x: Double = 0.0, y: Double = 0.0, z: Double = 0.0): Vec3 = add(x, y, z) + + fun Vec3.subtract(x: Double = 0.0, y: Double = 0.0, z: Double = 0.0): Vec3 = subtract(x, y, z) + + fun Vec3.minOfEachElement(other: Vec3) = + Vec3(min(x, other.x), min(y, other.y), min(z, other.z)) + + fun Vec3.maxOfEachElement(other: Vec3) = + Vec3(max(x, other.x), max(y, other.y), max(z, other.z)) + + fun Vec3.printWithAccuracy(accuracy: Int, splitChar: String = " "): String { + return if (accuracy == 0) { + val x = round(x).toInt() + val y = round(y).toInt() + val z = round(z).toInt() + "$x$splitChar$y$splitChar$z" + } else { + val x = (round(x * accuracy) / accuracy) + val y = (round(y * accuracy) / accuracy) + val z = (round(z * accuracy) / accuracy) + "$x$splitChar$y$splitChar$z" + } + } + + fun Vec3.toCleanString(separator: String = ", "): String = + doubleArrayOf(x, y, z).joinToString(separator) + + fun Vec3.asStoredString(): String = "$x:$y:$z" + + fun Vec3.isNormalized(tolerance: Double = 0.01) = (lengthSqr() - 1.0).absoluteValue < tolerance + + fun Vec3.isZero(): Boolean = this == Vec3.ZERO + + fun Vec3.with(x: Double? = null, y: Double? = null, z: Double? = null): Vec3 { + require(x != null || y != null || z != null) { + "Vec3.with() must have at least one argument" + } + return Vec3(x ?: this.x, y ?: this.y, z ?: this.z) + } + + fun Vec3.toDoubleArray() = doubleArrayOf(x, y, z) + fun Vec3.toFloatArray() = floatArrayOf(x.toFloat(), y.toFloat(), z.toFloat()) + + fun Vec3.equalsIgnoreY(other: Vec3) = x == other.x && z == other.z + + fun Vec3.roundTo(precision: Int) = + Vec3(x.roundTo(precision), y.roundTo(precision), z.roundTo(precision)) + + fun Vec3.roundToBlock() = Vec3(floor(x), floor(y), floor(z)) + + fun Vec3.blockCenter(): Vec3 = roundToBlock().add(0.5) + + fun Vec3.slope(other: Vec3, factor: Double) = this + (other - this).scale(factor) + + // TODO better name. don't confuse with roundTo() + fun Vec3.roundLocation() = Vec3( + if (x < 0) x - 1 else x, + y - 1, + if (z < 0) z - 1 else z, + ) + + fun Vec3.ceil(): Vec3 = Vec3(ceil(x), ceil(y), ceil(z)) + + fun Vec3.boundingCenter(expand: Double): AABB = + AABB(x - expand, y - expand, z - expand, x + expand, y + expand, z + expand) + + fun Vec3.boundingToOffset(offX: Double, offY: Double, offZ: Double) = + AABB(x, y, z, x + offX, y + offY, z + offZ) + + fun Vec3.axisAlignedTo(other: Vec3) = AABB(x, y, z, other.x, other.y, other.z) + + fun Vec3.up(offset: Double = 1.0): Vec3 = add(0.0, y + offset, 0.0) + + fun Vec3.down(offset: Double = 1.0): Vec3 = add(0.0, y - offset, 0.0) + + fun Vec3.interpolate(other: Vec3, factor: Double): Vec3 { + require(factor in 0.0..1.0) { "Percentage must be between 0 and 1: $factor" } + + val x = (1 - factor) * x + factor * other.x + val y = (1 - factor) * y + factor * other.y + val z = (1 - factor) * z + factor * other.z + + return Vec3(x, y, z) + } + + fun Vec3.rotateXY(theta: Double) = + Vec3(x * cos(theta) - y * sin(theta), x * sin(theta) + y * cos(theta), z) + + fun Vec3.rotateXZ(theta: Double) = + Vec3(x * cos(theta) + z * sin(theta), y, -x * sin(theta) + z * cos(theta)) + + fun Vec3.rotateYZ(theta: Double) = + Vec3(x, y * cos(theta) - z * sin(theta), y * sin(theta) + z * cos(theta)) + + fun Vec3.nearestPointOnLine(startPos: Vec3, endPos: Vec3): Vec3 { + var d = endPos - startPos + val w = this - startPos + + val dp = d.lengthSqr() + var dt = 0.0 + + if (dp != dt) dt = (w.dot(d) / dp).coerceIn(0.0, 1.0) + + d *= dt + d += startPos + return d + } + + fun Vec3.distanceToLine(startPos: Vec3, endPos: Vec3): Double = + (nearestPointOnLine(startPos, endPos) - this).lengthSqr() + + fun Vec3.middle(other: Vec3): Vec3 = this + ((other - this) / 2.0) + + // format we use to send to all/party chat + fun Vec3.toChatFormat(): String = "x: ${x.toInt()}, y: ${y.toInt()}, z: ${z.toInt()}" + + // format we show in local chat or for local commands + fun Vec3.toLocalFormat(): String = "${x.toInt()} ${y.toInt()} ${z.toInt()}" + + val directions = setOf( + Vec3(1.0, 0.0, 0.0), + Vec3(-1.0, 0.0, 0.0), + Vec3(0.0, 1.0, 0.0), + Vec3(0.0, -1.0, 0.0), + Vec3(0.0, 0.0, 1.0), + Vec3(0.0, 0.0, -1.0), + ) + + fun getFromYawPitch(yawDegrees: Double, pitchDegrees: Double): Vec3 { + val yawRad: Double = (yawDegrees + 90) * Math.PI / 180 + val pitchRad: Double = (pitchDegrees + 90) * Math.PI / 180 + + val x = sin(pitchRad) * cos(yawRad) + val y = sin(pitchRad) * sin(yawRad) + val z = cos(pitchRad) + return Vec3(x, z, y) + } + + // Format: "x:y:z" + fun fromStoredString(string: String): Vec3 = + string.split(":").map(String::toDouble).toVec3() + + fun readListFromClipboard(): List = + OSUtils.readFromClipboard()?.split("\n")?.map { line -> + fromStoredString(line.replace("\"", "").replace(",", "")) + }.orEmpty() + + fun List.copyLocations() { + OSUtils.copyToClipboard(joinToString(",\n") { it.asStoredString() }) + } + + fun List.toVec3(): Vec3 { + require(size == 3) { "Can not transform a list of size $size (!= 3) to Vec3" } + return Vec3(this[0], this[1], this[2]) + } + + val expandVector = Vec3(0.002, 0.002, 0.002) + + fun BlockPos.toVec3(): Vec3 = Vec3.atLowerCornerOf(this) + + val Entity.serverPosition: Vec3 + get() = Vec3(positionCodec.base.x, positionCodec.base.y, positionCodec.base.z) + + fun Entity.getPositionLog() = PositionLog( + tick = tickCount, + position = position(), + prev = oldPosition(), + server = serverPosition, + motion = deltaMovement, + yaw = yRot, + pitch = xRot, + ) + + data class PositionLog( + @Expose val tick: Int, + @Expose val position: Vec3, + @Expose val prev: Vec3, + @Expose val server: Vec3, + @Expose val motion: Vec3, + @Expose val yaw: Float, + @Expose val pitch: Float, + ) + + fun Rotations.toVec3(): Vec3 = Vec3(x().toDouble(), y().toDouble(), z().toDouble()) + + fun ClientboundLevelParticlesPacket.toVec3() = Vec3(x, y, z) + + fun AABB.inflate(vec: Vec3): AABB = inflate(vec.x, vec.y, vec.z) +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/render/LineDrawer.kt b/src/main/java/at/hannibal2/skyhanni/utils/render/LineDrawer.kt index 81134b9fc77b..0a5a1a73b0a5 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/render/LineDrawer.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/render/LineDrawer.kt @@ -4,6 +4,7 @@ import at.hannibal2.skyhanni.events.minecraft.SkyHanniRenderWorldEvent import at.hannibal2.skyhanni.utils.LocationUtils.calculateEdges import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.collection.CollectionUtils.zipWithNext3 +import at.hannibal2.skyhanni.utils.toLorenzVecPairs import net.minecraft.world.phys.AABB import java.awt.Color @@ -81,7 +82,7 @@ class LineDrawer @PublishedApi internal constructor(val event: SkyHanniRenderWor fun drawEdges(axisAlignedBB: AABB, color: Color) { // TODO add cache. maybe on the caller site, since we can't add a lazy member in AxisAlignedBB - for ((p1, p2) in axisAlignedBB.calculateEdges()) { + for ((p1, p2) in axisAlignedBB.calculateEdges().toLorenzVecPairs()) { draw3DLine(p1, p2, color) } }