mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
actually using (and rediscovering) the kdtree
This commit is contained in:
@@ -17,7 +17,7 @@ import net.torvald.terrarum.sqr
|
|||||||
* Remarks:
|
* Remarks:
|
||||||
* - NOT using the fullCodePage with 2x2 mode makes it slower... skewed tree generation?
|
* - 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 dimension = 2
|
||||||
private val initialSize = 128
|
private val initialSize = 128
|
||||||
@@ -25,8 +25,8 @@ class KDHeapifiedTree() {
|
|||||||
|
|
||||||
private val root: Int = 0
|
private val root: Int = 0
|
||||||
|
|
||||||
fun findNearest(query: Point2d) =
|
fun findNearestActor(query: Point2d): ActorWBMovable =
|
||||||
getNearest(root, query, 0).get()!!
|
getNearest(root, query, 0).getActor()!!
|
||||||
|
|
||||||
private fun Int.get() = nodes[this]?.feetPosPoint
|
private fun Int.get() = nodes[this]?.feetPosPoint
|
||||||
private fun Int.getActor() = nodes[this]
|
private fun Int.getActor() = nodes[this]
|
||||||
@@ -38,6 +38,10 @@ class KDHeapifiedTree() {
|
|||||||
|
|
||||||
private val zeroPoint = Point2d(0.0, 0.0)
|
private val zeroPoint = Point2d(0.0, 0.0)
|
||||||
|
|
||||||
|
init {
|
||||||
|
create(actors, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
private fun create(points: List<ActorWBMovable?>, depth: Int, index: Int): ActorWBMovable? {
|
private fun create(points: List<ActorWBMovable?>, depth: Int, index: Int): ActorWBMovable? {
|
||||||
if (points.isEmpty()) {
|
if (points.isEmpty()) {
|
||||||
index.set(null)
|
index.set(null)
|
||||||
@@ -101,38 +105,3 @@ class KDHeapifiedTree() {
|
|||||||
|
|
||||||
private operator fun Point2d.get(index: Int) = if (index == 0) this.x else this.y
|
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
|
|
||||||
}
|
|
||||||
77
src/net/torvald/aa/KDTree.kt
Normal file
77
src/net/torvald/aa/KDTree.kt
Normal 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*/
|
||||||
@@ -481,6 +481,8 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
|
|||||||
|
|
||||||
if (!paused) {
|
if (!paused) {
|
||||||
|
|
||||||
|
WorldSimulator.resetForThisFrame()
|
||||||
|
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
// world-related updates //
|
// world-related updates //
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.gameworld
|
package net.torvald.terrarum.modulebasegame.gameworld
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import net.torvald.aa.KDHeapifiedTree
|
||||||
import net.torvald.terrarum.AppLoader
|
import net.torvald.terrarum.AppLoader
|
||||||
import net.torvald.terrarum.Terrarum
|
import net.torvald.terrarum.Terrarum
|
||||||
import net.torvald.terrarum.blockproperties.BlockCodex
|
import net.torvald.terrarum.blockproperties.BlockCodex
|
||||||
import net.torvald.terrarum.blockproperties.Fluid
|
import net.torvald.terrarum.blockproperties.Fluid
|
||||||
|
import net.torvald.terrarum.gameactors.ActorWBMovable
|
||||||
import net.torvald.terrarum.gameworld.FluidType
|
import net.torvald.terrarum.gameworld.FluidType
|
||||||
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
|
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
|
||||||
import net.torvald.terrarum.roundInt
|
import net.torvald.terrarum.roundInt
|
||||||
@@ -47,16 +49,25 @@ object WorldSimulator {
|
|||||||
val colourNone = Color(0x808080FF.toInt())
|
val colourNone = Color(0x808080FF.toInt())
|
||||||
val colourWater = Color(0x66BBFFFF.toInt())
|
val colourWater = Color(0x66BBFFFF.toInt())
|
||||||
|
|
||||||
private val world = (Terrarum.ingame!!.world)
|
private val ingame = Terrarum.ingame!!
|
||||||
|
private val world = ingame.world
|
||||||
|
|
||||||
|
private var actorsKDTree: KDHeapifiedTree? = null
|
||||||
|
|
||||||
|
fun resetForThisFrame() {
|
||||||
|
actorsKDTree = null
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun invoke(player: ActorHumanoid?, delta: Float) {
|
||||||
|
// build the kdtree that will be used during a single frame of updating
|
||||||
|
if (actorsKDTree == null)
|
||||||
|
actorsKDTree = KDHeapifiedTree(ingame.actorContainerActive.filter { it is ActorWBMovable })
|
||||||
|
|
||||||
operator fun invoke(p: ActorHumanoid?, delta: Float) {
|
|
||||||
//printdbg(this, "============================")
|
//printdbg(this, "============================")
|
||||||
|
|
||||||
if (p != null) {
|
if (player != null) {
|
||||||
updateXFrom = p.hitbox.centeredX.div(CreateTileAtlas.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
|
updateXFrom = player.hitbox.centeredX.div(CreateTileAtlas.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
|
||||||
updateYFrom = p.hitbox.centeredY.div(CreateTileAtlas.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
|
updateYFrom = player.hitbox.centeredY.div(CreateTileAtlas.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
|
||||||
updateXTo = updateXFrom + DOUBLE_RADIUS
|
updateXTo = updateXFrom + DOUBLE_RADIUS
|
||||||
updateYTo = updateYFrom + DOUBLE_RADIUS
|
updateYTo = updateYFrom + DOUBLE_RADIUS
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,15 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
|
|||||||
fun iterator() = arrayList.iterator()
|
fun iterator() = arrayList.iterator()
|
||||||
fun forEach(action: (T) -> Unit) = arrayList.forEach(action)
|
fun forEach(action: (T) -> Unit) = arrayList.forEach(action)
|
||||||
fun forEachIndexed(action: (Int, T) -> Unit) = arrayList.forEachIndexed(action)
|
fun forEachIndexed(action: (Int, T) -> Unit) = arrayList.forEachIndexed(action)
|
||||||
//fun <R> map(transformation: (T) -> R) = arrayList.map(transformation)
|
|
||||||
|
|
||||||
|
fun <R> map(transformation: (T) -> R) = arrayList.map(transformation)
|
||||||
|
|
||||||
|
fun <R> filter(function: (T) -> Boolean): List<R> {
|
||||||
|
val retList = ArrayList<R>() // sorted-ness is preserved
|
||||||
|
this.arrayList.forEach { if (function(it)) retList.add(it as R) }
|
||||||
|
return retList
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select one unsorted element from the array and put it onto the sorted spot.
|
* Select one unsorted element from the array and put it onto the sorted spot.
|
||||||
|
|||||||
Reference in New Issue
Block a user