package net.torvald.aa import net.torvald.terrarum.Point2d import net.torvald.terrarum.gameactors.ActorWBMovable import net.torvald.terrarum.sqr /** * k-d Tree that uses binary heap instead of binary tree to improve data locality * * * -- I couldn't observe any significant boost in performance but this one seems * to give 3-4 more frames per second. * * Created by minjaesong on 2017-01-02. * * * Remarks: * - NOT using the fullCodePage with 2x2 mode makes it slower... skewed tree generation? */ class KDHeapifiedTree() { private val dimension = 2 private val initialSize = 128 private val nodes = Array(initialSize, { null }) private val root: Int = 0 fun findNearest(query: Point2d) = getNearest(root, query, 0).get()!! private fun Int.get() = nodes[this]?.feetPosPoint private fun Int.getActor() = nodes[this] private fun Int.getLeft() = this * 2 + 1 private fun Int.getRight() = this * 2 + 2 private fun Int.set(value: ActorWBMovable?) { nodes[this] = value } private fun Int.setLeftChild(value: ActorWBMovable?) { nodes[this.getLeft()] = value } private fun Int.setRightChild(value: ActorWBMovable?) { nodes[this.getRight()] = value } private val zeroPoint = Point2d(0.0, 0.0) private fun create(points: List, depth: Int, index: Int): ActorWBMovable? { if (points.isEmpty()) { index.set(null) return null } else { val items = points.sortedBy { if (it != null) it.feetPosPoint[depth % dimension] else Double.POSITIVE_INFINITY } val halfItems = items.size shr 1 index.setLeftChild(create(items.subList(0, halfItems), depth + 1, index.getLeft())) index.setRightChild(create(items.subList(halfItems + 1, items.size), depth + 1, index.getRight())) index.set(items[halfItems]) return index.getActor() } } private fun getNearest(currentNode: Int, query: Point2d, depth: Int): Int { //println("depth, $depth") val direction = currentNode.compare(query, depth % dimension) val next = if (direction < 0) currentNode.getLeft() else currentNode.getRight() val other = if (direction < 0) currentNode.getRight() else currentNode.getLeft() var best = if (next.get() == null) currentNode else getNearest(next, query, depth + 1) // traverse to leaf if (currentNode.get()!!.distSqr(query) < best.get()!!.distSqr(query)) { best = currentNode } if (other.get() != null) { if (currentNode.get()!!.dimDistSqr(query, depth % dimension) < best.get()!!.distSqr(query)) { val bestCandidate = getNearest(other, query, depth + 1) if (bestCandidate.get()!!.distSqr(query) < best.get()!!.distSqr(query)) { best = bestCandidate } } } return best // work back up } private fun expandArray() { val prevNodes = nodes.copyOf() Array(prevNodes.size * 2, { null }) create(prevNodes.toList(), 0, 0) } fun Int.compare(other: Point2d, dimension: Int) = other[dimension] - this.get()!![dimension] private fun Point2d.dimDistSqr(other: Point2d, dimension: Int) = other[dimension].minus(this[dimension]).sqr() 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 }