Skip to content

Commit

Permalink
Remove guava dependencies as gabe already has a lower version of it
Browse files Browse the repository at this point in the history
  • Loading branch information
xxfast committed Aug 15, 2021
1 parent 35ab2e6 commit 830fd07
Show file tree
Hide file tree
Showing 15 changed files with 134 additions and 139 deletions.
5 changes: 1 addition & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,11 @@ dependencies {
modImplementation("net.fabricmc", "fabric-language-kotlin", Fabric.Kotlin.version)
modImplementation("net.fabricmc.fabric-api", "fabric-api", Fabric.API.version)

modImplementation(Mods.libgui)
modImplementation(include(Mods.libgui)!!)
modImplementation(Mods.modmenu)
modImplementation(Mods.nbtcrafting)

implementation(Google.guava)

testRuntimeOnly(JUnit.jupiter_engine)

testImplementation(JUnit.jupiter)
testImplementation(Google.truth)
}
Expand Down
1 change: 0 additions & 1 deletion buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ object Mods {
}

object Google {
const val guava = "com.google.guava:guava:30.0-jre"
const val truth = "com.google.truth:truth:1.0.1"
}

Expand Down
58 changes: 0 additions & 58 deletions src/main/kotlin/com/xfastgames/witness/items/data/Node.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.xfastgames.witness.items.data

import com.google.common.graph.EndpointPair
import com.google.common.graph.MutableGraph
import com.google.common.graph.Traverser
import com.xfastgames.witness.utils.guava.clear
import net.minecraft.nbt.NbtCompound
import kotlin.math.hypot
import kotlin.math.pow
Expand Down Expand Up @@ -34,25 +31,6 @@ fun distance(u: Node, v: Node): Float =
@Suppress("UnstableApiUsage")
operator fun EndpointPair<Node>.contains(node: Node) = this.nodeU() == node || this.nodeV() == node

/**
* Optimize a node graph with [Ramer–Douglas–Peucker algorithm](https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm)
*/
@Suppress("UnstableApiUsage")
fun MutableGraph<Node>.optimize(starting: Node): MutableGraph<Node> {
val line: List<Node> = Traverser.forGraph(this).breadthFirst(starting).toList()
if (line.size < 2) return this

this.clear()

val mutableList: MutableList<Node> = line.toMutableList()
RamerDouglasPeucker(mutableList, 1.0, mutableList)

mutableList.chunked(2).forEach { (prevNode, nexNode) ->
putEdge(prevNode, nexNode)
}
return this
}

/** Copy pasta of https://www.rosettacode.org/wiki/Ramer-Douglas-Peucker_line_simplification#Kotlin **/
private fun perpendicularDistance(pt: Node, lineStart: Node, lineEnd: Node): Float {
var dx: Float = lineEnd.x - lineStart.x
Expand All @@ -74,40 +52,4 @@ private fun perpendicularDistance(pt: Node, lineStart: Node, lineEnd: Node): Flo
val ay: Float = pvy - pvdot * dy

return hypot(ax, ay)
}

fun RamerDouglasPeucker(pointList: List<Node>, epsilon: Double, out: MutableList<Node>) {
if (pointList.isEmpty()) return
if (pointList.size < 2) throw IllegalArgumentException("Not enough points to simplify")

// Find the point with the maximum distance from line between start and end
var dmax = 0.0f
var index = 0
val end = pointList.size - 1
for (i in 1 until end) {
val d = perpendicularDistance(pointList[i], pointList[0], pointList[end])
if (d > dmax) {
index = i; dmax = d
}
}

// If max distance is greater than epsilon, recursively simplify
if (dmax > epsilon) {
val recResults1 = mutableListOf<Node>()
val recResults2 = mutableListOf<Node>()
val firstLine = pointList.take(index + 1)
val lastLine = pointList.drop(index)
RamerDouglasPeucker(firstLine, epsilon, recResults1)
RamerDouglasPeucker(lastLine, epsilon, recResults2)

// build the result list
out.addAll(recResults1.take(recResults1.size - 1))
out.addAll(recResults2)
if (out.size < 2) throw RuntimeException("Problem assembling output")
} else {
// Just return start and end points
out.clear()
pointList.firstOrNull()?.let { out.add(it) }
pointList.lastOrNull()?.let { out.add(it) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import com.xfastgames.witness.Witness
import com.xfastgames.witness.items.KEY_PANEL
import com.xfastgames.witness.items.data.*
import com.xfastgames.witness.utils.*
import com.xfastgames.witness.utils.guava.edgeValueOf
import com.xfastgames.witness.utils.guava.incidentEdges
import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry
import net.minecraft.client.MinecraftClient
import net.minecraft.client.render.RenderLayer
Expand Down Expand Up @@ -124,7 +126,7 @@ object PuzzlePanelRenderer : BuiltinItemRendererRegistry.DynamicItemRenderer {

private fun numberOfEdgesVisible(graph: ValueGraph<Node, Edge>, node: Node): Int =
graph.incidentEdges(node).count { endpointPair ->
graph.edgeValue(endpointPair).value !in listOf(Edge.NONE, Edge.HIDDEN)
graph.edgeValueOf(endpointPair) !in listOf(Edge.NONE, Edge.HIDDEN)
}

private fun RenderContext.renderNode(graph: ValueGraph<Node, Edge>, node: Node): Unit = when {
Expand All @@ -135,7 +137,7 @@ object PuzzlePanelRenderer : BuiltinItemRendererRegistry.DynamicItemRenderer {
}

private fun RenderContext.renderEdge(graph: ValueGraph<Node, Edge>, side: EndpointPair<Node>) {
val edge: Edge = graph.edgeValue(side).value ?: return
val edge: Edge = graph.edgeValueOf(side) ?: return
val startNode: Node = side.nodeU()
val endNode: Node = side.nodeV()
val start = Vec3f(startNode.x, startNode.y, 0f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.xfastgames.witness.screens.widgets.icons.BreakIcon
import com.xfastgames.witness.screens.widgets.icons.EndIcon
import com.xfastgames.witness.screens.widgets.icons.StartIcon
import com.xfastgames.witness.utils.*
import com.xfastgames.witness.utils.guava.putEdgeValue
import io.github.cottonmc.cotton.gui.SyncedGuiDescription
import io.github.cottonmc.cotton.gui.client.BackgroundPainter
import io.github.cottonmc.cotton.gui.client.CottonInventoryScreen
Expand Down Expand Up @@ -211,7 +212,7 @@ class PuzzleComposerScreenDescription(
val neighbours: List<Node> = outputPuzzle.graph.adjacentNodes(node).toList()
val neighbourhood: MutableMap<Node, Edge> = mutableMapOf()
neighbours.forEach { neighbour ->
neighbourhood[neighbour] = outputPuzzle.graph.edgeValue(neighbour, node).get()
neighbourhood[neighbour] = outputPuzzle.graph.edgeValue(neighbour, node)
}
updatedGraph.removeNode(node)
updatedGraph.addNode(updatedNode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.xfastgames.witness.screens.solver
import com.google.common.graph.EndpointPair
import com.google.common.graph.Graph
import com.google.common.graph.MutableGraph
import com.google.common.graph.Traverser
import com.xfastgames.witness.Witness
import com.xfastgames.witness.blocks.redstone.IronPuzzleFrameBlock
import com.xfastgames.witness.entities.PuzzleFrameBlockEntity
Expand All @@ -14,6 +13,8 @@ import com.xfastgames.witness.items.data.*
import com.xfastgames.witness.screens.solver.PuzzleSolverScreen.Sounds.Instances.FOCUS_MODE_DOING_INSTANCE
import com.xfastgames.witness.sounds.LoopingSoundInstance
import com.xfastgames.witness.utils.*
import com.xfastgames.witness.utils.guava.Traverser
import com.xfastgames.witness.utils.guava.hasEdgeConnecting
import com.xfastgames.witness.utils.guava.mutableGraph
import kotlinx.coroutines.FlowPreview
import net.fabricmc.api.EnvType
Expand Down Expand Up @@ -137,7 +138,7 @@ class PuzzleSolverScreen : Screen(NarratorManager.EMPTY) {

val (clampedClickX, clampedClickY) = panelHitResult.position

val line: List<Node> = Traverser.forGraph(previousLine).breadthFirst(start).toList()
val line: List<Node> = Traverser.forGraph(previousLine).depthFirst(start).toList()
val lastNode: Node = line.last { it.modifier != Modifier.END }
val nodeBeforeLastNode: Node? = lastNode.let { line.getOrNull(line.indexOf(lastNode) - 1) }

Expand All @@ -156,9 +157,11 @@ class PuzzleSolverScreen : Screen(NarratorManager.EMPTY) {
updatedLine.putEdge(newEnd, lastNode)
}

val updatedSolution: List<Node> = Traverser.forGraph(updatedLine).breadthFirst(start).toList()
val updatedSolution: List<Node> = Traverser.forGraph(updatedLine).depthFirst(start)
val updatedEnd: Node? = updatedSolution.firstOrNull { it.modifier == Modifier.END }

// TODO: Looks like this maybe broken after removing guava traverser,
// but seems to work on standalone builds weirdly enough
when {
// If over an node, and the node is not already part of solution
overNode != null &&
Expand All @@ -182,7 +185,7 @@ class PuzzleSolverScreen : Screen(NarratorManager.EMPTY) {

val solution: List<Node> =
if (updatedLine.nodes().contains(start))
Traverser.forGraph(updatedLine).breadthFirst(start).toList()
Traverser.forGraph(updatedLine).depthFirst(start).toList()
else emptyList()

// Remove nodes that are not a part of the main line
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.xfastgames.witness.items.data.Node
import com.xfastgames.witness.items.data.Panel
import com.xfastgames.witness.items.data.getPanel
import com.xfastgames.witness.items.renderer.PuzzlePanelRenderer
import com.xfastgames.witness.utils.guava.edgeValueOf
import com.xfastgames.witness.utils.intersects
import com.xfastgames.witness.utils.rotate
import io.github.cottonmc.cotton.gui.client.BackgroundPainter
Expand Down Expand Up @@ -136,7 +137,7 @@ class WPuzzleEditor(
result
}

val edge: Edge? = edgeNodePair?.let { inputPuzzle.graph.edgeValue(it).orElse(null) }
val edge: Edge? = edgeNodePair?.let { inputPuzzle.graph.edgeValueOf(it) }

onClickListener?.onClick(node, edge, edgeNodePair)
return InputResult.PROCESSED
Expand Down
5 changes: 4 additions & 1 deletion src/main/kotlin/com/xfastgames/witness/utils/guava/Graph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import com.google.common.graph.Graph
import com.google.common.graph.GraphBuilder
import com.google.common.graph.MutableGraph

val <N> Graph<N>.adjacencyMatrix: List<List<Boolean>>
fun <N : Any> Graph<N>.hasEdgeConnecting(thisNode: N, thatNode: N): Boolean =
this.adjacentNodes(thisNode).contains(thatNode)

val <N : Any> Graph<N>.adjacencyMatrix: List<List<Boolean>>
get() = this.nodes().map { thisNode ->
this.nodes().map { thatNode ->
this.hasEdgeConnecting(thisNode, thatNode)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.xfastgames.witness.utils.guava

import com.google.common.graph.EndpointPair
import com.google.common.graph.MutableValueGraph

@Suppress("UnstableApiUsage")
fun <N : Any, E : Any> MutableValueGraph<N, E>.putEdgeValue(pair: EndpointPair<N>, edge: E) =
this.putEdgeValue(pair.nodeU(), pair.nodeV(), edge)
19 changes: 19 additions & 0 deletions src/main/kotlin/com/xfastgames/witness/utils/guava/Traverser.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.xfastgames.witness.utils.guava

import com.google.common.graph.Graph

@Suppress("UnstableApiUsage")
class Traverser<N : Any>(private val graph: Graph<N>) {

fun depthFirst(start: N, traversedNodes: MutableList<N> = mutableListOf(start)): List<N> {
val adjacentNodes: MutableSet<N> = graph.adjacentNodes(start)
val nextNode: N? = adjacentNodes.firstOrNull { node -> node !in traversedNodes }
if (nextNode != null) traversedNodes.add(nextNode)
else return traversedNodes
return depthFirst(nextNode, traversedNodes)
}

companion object {
fun <N : Any> forGraph(graph: Graph<N>): Traverser<N> = Traverser(graph)
}
}
23 changes: 16 additions & 7 deletions src/main/kotlin/com/xfastgames/witness/utils/guava/ValueGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,29 @@

package com.xfastgames.witness.utils.guava

import com.google.common.graph.EndpointPair
import com.google.common.graph.MutableValueGraph
import com.google.common.graph.ValueGraph
import com.google.common.graph.ValueGraphBuilder
import com.xfastgames.witness.utils.value

val <N, E> ValueGraph<N, E>.adjacencyMatrix: List<List<E?>>
fun <N : Any, E : Any> ValueGraph<N, E>.edgeValueOf(u: N, v: N): E? =
edgeValueOrDefault(u, v, null)

fun <N : Any, E : Any> ValueGraph<N, E>.edgeValueOf(pair: EndpointPair<N>): E? =
edgeValueOf(pair.nodeU(), pair.nodeV())

fun <N : Any, E : Any> ValueGraph<N, E>.incidentEdges(node: N): List<EndpointPair<N>> =
this.edges().filter { endpointPair -> endpointPair.contains(node) }

val <N : Any, E : Any> ValueGraph<N, E>.adjacencyMatrix: List<List<E?>>
get() = this.nodes().map { thisNode ->
this.nodes().map { thatNode ->
this.edgeValue(thisNode, thatNode).value
}
this.nodes().map { thatNode -> this.edgeValueOf(thisNode, thatNode) }
}

inline fun <N, reified E> ValueGraph<N, E>.edgeValues(): List<E> =
this.edges().map { nodePair -> this.edgeValue(nodePair).orElse(null) }.filterIsInstance<E>()
inline fun <N : Any, reified E : Any> ValueGraph<N, E>.edgeValues(): List<E> =
this.edges()
.map { nodePair -> this.edgeValue(nodePair.nodeU(), nodePair.nodeV()) }
.filterIsInstance<E>()

/***
* Adds a [nodeList] to a existing graph with the given [adjacencyMatrix]
Expand Down
1 change: 0 additions & 1 deletion src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"fabricloader": ">=0.11.6",
"fabric": "*",
"fabric-language-kotlin": "*",
"libgui": "*",
"nbtcrafting": "*",
"minecraft": "1.17.x"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@ import org.junit.jupiter.api.Test

const val KEY_GRAPH = "testGraph"

enum class Person { Bob, Alice, Rob, Maria, Mark }

@Suppress("UnstableApiUsage")
class GraphTests {

private val bottomRight = Node(0f, 0f, Modifier.START)
private val bottomLeft = Node(1f, 0f)
private val topRight = Node(0f, 1f)
private val topLeft = Node(1f, 1f, Modifier.END)

private val testGraph: MutableValueGraph<Node, Edge> = ValueGraphBuilder.undirected()
.build<Node, Edge>().apply {
putEdgeValue(bottomRight, topRight, Modifier.NORMAL)
Expand Down Expand Up @@ -153,7 +152,7 @@ class GraphTests {
@Test
fun `Test nearest Edge from bottom right to middle`() {
val actual: EdgeResult? = testGraph.nearestEdge(.5f, .5f, bottomRight)
val expect: EdgeResult = EdgeResult(.0f, .5f, EndpointPair.unordered(topRight, bottomRight))
val expect = EdgeResult(.0f, .5f, EndpointPair.unordered(topRight, bottomRight))
assertThat(actual).isEqualTo(expect)
}
}
Expand Down
69 changes: 69 additions & 0 deletions src/test/kotlin/com/xfastgames/witness/utils/GuavaTests.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.xfastgames.witness.utils

import com.google.common.graph.MutableValueGraph
import com.google.common.graph.ValueGraphBuilder
import com.google.common.truth.Truth
import com.xfastgames.witness.utils.guava.Traverser
import com.xfastgames.witness.utils.guava.add
import com.xfastgames.witness.utils.guava.adjacencyMatrix
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test

enum class Person { Bob, Alice, Rob, Maria, Mark }

@Suppress("UnstableApiUsage")
class ValueGraphTests {
private val testNodes: List<Person> = Person.values().toList()

private val testAdjacencyMatrix: List<List<Double?>> = listOf(
// Bob, Alice, Rob, Maria, Mark
listOf(null, 1.0, 0.75, null, null), // Bob
listOf(1.0, null, null, 0.75, 0.5), // Alice
listOf(0.75, null, null, 1.0, 0.5), // Rob
listOf(null, 0.75, 1.0, null, null), // Maria
listOf(null, 0.5, 0.5, null, null) // Mark
)

/**
* Based on ![graph](https://www.baeldung.com/wp-content/uploads/2018/11/graph3.jpg)
*/
private val testPersonGraph: MutableValueGraph<Person, Double> = ValueGraphBuilder.undirected()
.build<Person, Double>().apply {
putEdgeValue(Person.Bob, Person.Alice, 1.0)
putEdgeValue(Person.Bob, Person.Rob, 0.75)
putEdgeValue(Person.Alice, Person.Maria, 0.75)
putEdgeValue(Person.Maria, Person.Rob, 1.0)
putEdgeValue(Person.Alice, Person.Mark, .5)
putEdgeValue(Person.Mark, Person.Rob, .5)
}

@Nested
@DisplayName("Test grid generation")
inner class ValueGraphTests {
@Test
fun `Test if the network adjacency matrix is setup correct`() {
val actual: List<List<Double?>> = testPersonGraph.adjacencyMatrix
Truth.assertThat(actual).isEqualTo(testAdjacencyMatrix)
}

@Test
fun `Test if the network can be build from a set of nodes and adjacency matrix`() {
val actual: MutableValueGraph<Person, Double> = ValueGraphBuilder.undirected().build()
actual.add(testNodes, testAdjacencyMatrix)
Truth.assertThat(actual).isEqualTo(testPersonGraph)
}
}

@Nested
@DisplayName("Test traverser")
inner class TraverserTests {
private val testTraverser: Traverser<Person> = Traverser.forGraph(testPersonGraph.asGraph())

@Test
fun `Test depth first`() {
val actual: List<Person> = testTraverser.depthFirst(Person.Alice)
println(actual)
}
}
}
Loading

0 comments on commit 830fd07

Please sign in to comment.