actually using (and rediscovering) the kdtree

This commit is contained in:
minjaesong
2019-04-18 23:42:54 +09:00
parent 62b687c86b
commit 5a95f1c21a
5 changed files with 111 additions and 44 deletions

View File

@@ -17,7 +17,7 @@ import net.torvald.terrarum.sqr
* Remarks:
* - NOT using the fullCodePage with 2x2 mode makes it slower... skewed tree generation?
*/
class KDHeapifiedTree() {
class KDHeapifiedTree(actors: List<ActorWBMovable>) {
private val dimension = 2
private val initialSize = 128
@@ -25,8 +25,8 @@ class KDHeapifiedTree() {
private val root: Int = 0
fun findNearest(query: Point2d) =
getNearest(root, query, 0).get()!!
fun findNearestActor(query: Point2d): ActorWBMovable =
getNearest(root, query, 0).getActor()!!
private fun Int.get() = nodes[this]?.feetPosPoint
private fun Int.getActor() = nodes[this]
@@ -38,6 +38,10 @@ class KDHeapifiedTree() {
private val zeroPoint = Point2d(0.0, 0.0)
init {
create(actors, 0, 0)
}
private fun create(points: List<ActorWBMovable?>, depth: Int, index: Int): ActorWBMovable? {
if (points.isEmpty()) {
index.set(null)
@@ -101,38 +105,3 @@ class KDHeapifiedTree() {
private operator fun Point2d.get(index: Int) = if (index == 0) this.x else this.y
}
fun intLog2(number: Int): Int {
var number = number
if (number == 0) return 0
var log = 0
if (number and 0xffff0000.toInt() != 0) {
number = number ushr 16
log = 16
}
if (number >= 256) {
number = number ushr 8
log += 8
}
if (number >= 16) {
number = number ushr 4
log += 4
}
if (number >= 4) {
number = number ushr 2
log += 2
}
return log + number.ushr(1)
}
fun nextPowerOfTwo(number: Int): Int {
var number = number - 1
number = number or (number shr 1)
number = number or (number shr 2)
number = number or (number shr 4)
number = number or (number shr 8)
number = number or (number shr 16)
number++
number += if (number == 0) 1 else 0
return number
}

View File

@@ -0,0 +1,77 @@
package net.torvald.aa
/**
* Created by minjaesong on 2019-04-18.
*/
/*class KDTree(points: List<ActorWithBody>) {
companion object {
const val DIMENSION = 2
}
private val root: KDNode?
init {
root = create(points, 0)
}
fun findNearest(query: ActorWithBody) = getNearest(root!!, query, 0)
private fun create(points: List<ActorWithBody>, depth: Int): KDNode? {
if (points.isEmpty()) {
return null
}
else {
val items = points.sortedBy { it.getDimensionalPoint(depth) }
val currentIndex = items.size shr 1
return KDNode(
create(items.subList(0, currentIndex), depth + 1),
create(items.subList(currentIndex + 1, items.size), depth + 1),
items[currentIndex],
items[currentIndex].hitbox.clone()
)
}
}
private fun getNearest(currentNode: KDNode, query: ActorWithBody, depth: Int = 0): KDNode {
val direction = currentNode.compare(query, depth % DIMENSION)
val next = if (direction < 0) currentNode.left else currentNode.right
val other = if (direction < 0) currentNode.right else currentNode.left
var best = if (next == null) currentNode else getNearest(next, query, depth + 1) // traverse to leaf
if (currentNode.position.distSqr(query) < best.position.distSqr(query)) {
best = currentNode
}
if (other != null) {
if (currentNode.position.dimDistSqr(query, depth % DIMENSION) < best.position.distSqr(query)) {
val bestCandidate = getNearest(other, query, depth + 1)
if (bestCandidate.position.distSqr(query) < best.position.distSqr(query)) {
best = bestCandidate
}
}
}
return best // work back up
}
data class KDNode(val left: KDNode?, val right: KDNode?, val actor: ActorWithBody, val position: Hitbox) {
fun compare(other: ActorWithBody, dimension: Int) = other.getDimensionalPoint(dimension) - this.position.getDimensionalPoint(dimension)
}
private fun Hitbox.distSqr(other: Hitbox) {
var dist = 0.0
for (i in 0 until DIMENSION)
dist += (this.getDimensionalPoint(i) - other.getDimensionalPoint(i)).sqr()
}
private fun Hitbox.dimDistSqr(other: Hitbox, dimension: Int) = other.getDimensionalPoint(dimension).minus(this.getDimensionalPoint(dimension)).sqr()
}
internal fun ActorWithBody.getDimensionalPoint(depth: Int) = this.hitbox.getDimensionalPoint(depth)
// TODO take ROUNDWORLD into account
internal fun Hitbox.getDimensionalPoint(depth: Int) =
if (depth % KDTree.DIMENSION == 0) this.canonicalX else this.canonicalY*/