diff options
author | Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> | 2024-09-05 10:34:44 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-05 10:34:44 +0200 |
commit | 81aebf952893555a48441c4ac5516ac869741bb0 (patch) | |
tree | b0fd7bd1ee9116f6472ea48b6e7695e82b1f4b72 | |
parent | 6dae2df502995679bbd7742ac919d9ee467db9d8 (diff) | |
download | skyhanni-81aebf952893555a48441c4ac5516ac869741bb0.tar.gz skyhanni-81aebf952893555a48441c4ac5516ac869741bb0.tar.bz2 skyhanni-81aebf952893555a48441c4ac5516ac869741bb0.zip |
Backend: Graph Editor Split & Dissolve (#2466)
4 files changed, 95 insertions, 13 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java index bbc6caf7d..9a2806f64 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java @@ -82,6 +82,16 @@ public class GraphConfig { public int tutorialKey = Keyboard.KEY_K; @Expose + @ConfigOption(name = "Split Key", desc = "Key for splitting an edge that is between the active and the closed node.") + @ConfigEditorKeybind(defaultKey = Keyboard.KEY_NONE) + public int splitKey = Keyboard.KEY_NONE; + + @Expose + @ConfigOption(name = "Dissolve Key", desc = "Dissolve the active node into one edge if it only has two edges.") + @ConfigEditorKeybind(defaultKey = Keyboard.KEY_NONE) + public int dissolveKey = Keyboard.KEY_NONE; + + @Expose @ConfigLink(owner = GraphConfig.class, field = "enabled") public Position infoDisplay = new Position(20, 20); diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt b/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt index 89fd413f5..21440197d 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt @@ -7,12 +7,12 @@ import at.hannibal2.skyhanni.utils.json.fromJson import com.google.gson.GsonBuilder import com.google.gson.JsonElement import com.google.gson.annotations.Expose +import com.google.gson.stream.JsonToken import java.util.PriorityQueue @JvmInline value class Graph( - @Expose - val graph: List<GraphNode>, + @Expose val graph: List<GraphNode>, ) : List<GraphNode> { override val size get() = graph.size @@ -82,6 +82,10 @@ value class Graph( var tags: List<String>? = null var neighbors = mutableListOf<Pair<Int, Double>>() while (reader.hasNext()) { + if (reader.peek() != JsonToken.NAME) { + reader.skipValue() + continue + } when (reader.nextName()) { "Position" -> { position = reader.nextString().split(":").let { parts -> @@ -157,8 +161,7 @@ class GraphNode(val id: Int, val position: LorenzVec, val name: String? = null, } } -fun Graph.findShortestPathAsGraph(start: GraphNode, end: GraphNode): Graph = - this.findShortestPathAsGraphWithDistance(start, end).first +fun Graph.findShortestPathAsGraph(start: GraphNode, end: GraphNode): Graph = this.findShortestPathAsGraphWithDistance(start, end).first fun Graph.findShortestPathAsGraphWithDistance(start: GraphNode, end: GraphNode): Pair<Graph, Double> { val distances = mutableMapOf<GraphNode, Double>() @@ -199,11 +202,9 @@ fun Graph.findShortestPathAsGraphWithDistance(start: GraphNode, end: GraphNode): ) to distances[end]!! } -fun Graph.findShortestPath(start: GraphNode, end: GraphNode): List<LorenzVec> = - this.findShortestPathAsGraph(start, end).toPositionsList() +fun Graph.findShortestPath(start: GraphNode, end: GraphNode): List<LorenzVec> = this.findShortestPathAsGraph(start, end).toPositionsList() -fun Graph.findShortestDistance(start: GraphNode, end: GraphNode): Double = - this.findShortestPathAsGraphWithDistance(start, end).second +fun Graph.findShortestDistance(start: GraphNode, end: GraphNode): Double = this.findShortestPathAsGraphWithDistance(start, end).second fun Graph.toPositionsList() = this.map { it.position } diff --git a/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt b/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt index f5acf90fb..8757f19bf 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt @@ -55,7 +55,18 @@ object GraphEditor { private val edges = mutableListOf<GraphingEdge>() private var activeNode: GraphingNode? = null + set(value) { + field = value + selectedEdge = findEdgeBetweenActiveAndClosed() + checkDissolve() + } private var closedNode: GraphingNode? = null + set(value) { + field = value + selectedEdge = findEdgeBetweenActiveAndClosed() + } + + private var selectedEdge: GraphingEdge? = null private var ghostPosition: LorenzVec? = null private var seeThroughBlocks = true @@ -86,6 +97,7 @@ object GraphEditor { private val edgeColor = LorenzColor.GOLD.addOpacity(150) private val edgeDijkstraColor = LorenzColor.DARK_BLUE.addOpacity(150) + private val edgeSelectedColor = LorenzColor.DARK_RED.addOpacity(150) val scrollValue = ScrollValue() var nodesDisplay = emptyList<Renderable>() @@ -127,7 +139,11 @@ object GraphEditor { add("§eTutorial: §6${KeyboardManager.getKeyName(config.tutorialKey)}") add("§eToggle Ghost Position: §6${KeyboardManager.getKeyName(config.toggleGhostPosition)}") add(" ") - if (activeNode != null) add("§eText: §6${KeyboardManager.getKeyName(config.textKey)}") + if (activeNode != null) { + add("§eText: §6${KeyboardManager.getKeyName(config.textKey)}") + if (dissolvePossible) add("§eDissolve: §6${KeyboardManager.getKeyName(config.dissolveKey)}") + if (selectedEdge != null) add("§eSplit: §6${KeyboardManager.getKeyName(config.splitKey)}") + } } if (!inTextMode) { @@ -152,6 +168,18 @@ object GraphEditor { } } + private var dissolvePossible = false + + private fun findEdgeBetweenActiveAndClosed(): GraphingEdge? = getEdgeIndex(activeNode, closedNode)?.let { edges[it] } + + private fun checkDissolve() { + if (activeNode == null) { + dissolvePossible = false + return + } + dissolvePossible = edges.count { it.isInEdge(activeNode) } == 2 + } + @SubscribeEvent fun onGuiRender(event: GuiRenderEvent) { if (!isEnabled()) return @@ -207,7 +235,7 @@ object GraphEditor { ) } - private fun createTagName( +private fun createTagName( name: String, tag: GraphNodeTag, node: GraphingNode, @@ -333,7 +361,11 @@ object GraphEditor { private fun LorenzRenderWorldEvent.drawEdge(edge: GraphingEdge) = this.draw3DLine_nea( edge.node1.position.add(0.5, 0.5, 0.5), edge.node2.position.add(0.5, 0.5, 0.5), - if (edge !in highlightedEdges) edgeColor else edgeDijkstraColor, + when { + selectedEdge == edge -> edgeSelectedColor + edge in highlightedEdges -> edgeDijkstraColor + else -> edgeColor + }, 7, !seeThroughBlocks, ) @@ -449,6 +481,8 @@ object GraphEditor { feedBackInTutorial("Added new edge.") } else { this.edges.removeAt(edge) + checkDissolve() + selectedEdge = findEdgeBetweenActiveAndClosed() feedBackInTutorial("Removed edge.") } } @@ -466,6 +500,29 @@ object GraphEditor { inTutorialMode = !inTutorialMode ChatUtils.chat("Tutorial mode is now ${if (inTutorialMode) "active" else "inactive"}.") } + if (selectedEdge != null && config.splitKey.isKeyClicked()) { + val edge = selectedEdge ?: return + feedBackInTutorial("Split Edge into a Node and two edges.") + val middle = edge.node1.position.middle(edge.node2.position).roundLocationToBlock() + val node = GraphingNode(id++, middle) + nodes.add(node) + edges.remove(edge) + addEdge(node, edge.node1) + addEdge(node, edge.node2) + activeNode = node + } + if (dissolvePossible && config.dissolveKey.isKeyClicked()) { + feedBackInTutorial("Dissolved the node, now it is gone.") + val edgePair = edges.filter { it.isInEdge(activeNode) } + val edge1 = edgePair[0] + val edge2 = edgePair[1] + val neighbors1 = if (edge1.node1 == activeNode) edge1.node2 else edge1.node1 + val neighbors2 = if (edge2.node1 == activeNode) edge2.node2 else edge2.node1 + edges.removeAll(edgePair) + nodes.remove(activeNode) + activeNode = null + addEdge(neighbors1, neighbors2) + } } private fun save() { @@ -561,7 +618,14 @@ object GraphEditor { else null private fun addEdge(node1: GraphingNode?, node2: GraphingNode?) = - if (node1 != null && node2 != null && node1 != node2) edges.add(GraphingEdge(node1, node2)) else false + if (node1 != null && node2 != null && node1 != node2) { + val edge = GraphingEdge(node1, node2) + if (edge.isInEdge(activeNode)) { + checkDissolve() + selectedEdge = findEdgeBetweenActiveAndClosed() + } + edges.add(edge) + } else false /** Has a side effect on the graphing graph, since it runs [prune] on the graphing graph*/ private fun compileGraph(): Graph { @@ -597,6 +661,8 @@ object GraphEditor { }.flatten().distinct(), ) id = nodes.lastOrNull()?.id?.plus(1) ?: 0 + checkDissolve() + selectedEdge = findEdgeBetweenActiveAndClosed() } private val highlightedNodes = mutableSetOf<GraphingNode>() @@ -630,6 +696,7 @@ object GraphEditor { edges.clear() activeNode = null closedNode = null + dissolvePossible = false ghostPosition = null } @@ -671,7 +738,7 @@ private class GraphingNode( private class GraphingEdge(val node1: GraphingNode, val node2: GraphingNode) { - fun isInEdge(node: GraphingNode) = node1 == node || node2 == node + fun isInEdge(node: GraphingNode?) = node1 == node || node2 == node override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt index 474d6dee5..4a50d79f3 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt @@ -215,6 +215,10 @@ data class LorenzVec( return (nearestPointOnLine(startPos, endPos) - this).lengthSquared() } + fun middle(other: LorenzVec): LorenzVec = this.plus(other.minus(this) / 2) + + private operator fun div(i: Number): LorenzVec = LorenzVec(x / i.toDouble(), y / i.toDouble(), z / i.toDouble()) + companion object { fun getFromYawPitch(yaw: Double, pitch: Double): LorenzVec { |