Add k-d tree
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
82bb847812
commit
3d28b369f2
@ -0,0 +1,8 @@
|
|||||||
|
package me.eater.threedom.dom.event
|
||||||
|
|
||||||
|
import me.eater.threedom.dom.INode
|
||||||
|
import me.eater.threedom.kapt.EventName
|
||||||
|
import org.joml.Matrix4dc
|
||||||
|
|
||||||
|
@EventName("NodeModelUpdate")
|
||||||
|
data class NodeModelUpdate(val node: INode<*>, val oldModel: Matrix4dc)
|
@ -1,7 +1,9 @@
|
|||||||
package me.eater.threedom.event
|
package me.eater.threedom.event
|
||||||
|
|
||||||
|
import me.eater.threedom.generated.EventNames
|
||||||
|
|
||||||
interface EventListener {
|
interface EventListener {
|
||||||
fun <T> on(eventName: String, block: (Event<T>) -> Unit)
|
fun <T> on(eventName: String, block: (Event<T>) -> Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T> EventListener.on(noinline block: (Event<T>) -> Unit) = on(T::class.java.name, block)
|
inline fun <reified T> EventListener.on(noinline block: (Event<T>) -> Unit) = on(EventNames.getEventName<T>(), block)
|
||||||
|
@ -0,0 +1,122 @@
|
|||||||
|
package me.eater.threedom.utils
|
||||||
|
|
||||||
|
import me.eater.threedom.dom.IDocument
|
||||||
|
import me.eater.threedom.dom.INode
|
||||||
|
import me.eater.threedom.utils.joml.Vector3d
|
||||||
|
import me.eater.threedom.utils.joml.compareTo
|
||||||
|
import me.eater.threedom.utils.joml.getTranslation
|
||||||
|
import org.joml.Vector3dc
|
||||||
|
|
||||||
|
class KDTree(private val document: IDocument, private val root: Node = Node(Vector3d(0, 0, 0))) {
|
||||||
|
private val nodeLocMap = mutableMapOf<Long, Vector3dc>()
|
||||||
|
|
||||||
|
data class Node(
|
||||||
|
val vertex: Vector3dc = Vector3d(0, 0, 0),
|
||||||
|
val nodeIds: MutableSet<Long> = mutableSetOf(),
|
||||||
|
val depth: Long = 0,
|
||||||
|
var left: Node? = null,
|
||||||
|
var right: Node? = null
|
||||||
|
) {
|
||||||
|
val axis: Int
|
||||||
|
get() = (depth % 3).toInt()
|
||||||
|
|
||||||
|
fun add(translation: Vector3dc, nodeId: Long) {
|
||||||
|
var current = this
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (translation == current.vertex) {
|
||||||
|
current.nodeIds.add(nodeId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (translation[current.axis] < current.vertex[current.axis] || (translation[current.axis] == current.vertex[current.axis] && translation < current.vertex)) {
|
||||||
|
if (current.left == null) {
|
||||||
|
current.left = Node(translation, mutableSetOf(nodeId), current.depth + 1)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
current = current.left!!
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (current.right == null) {
|
||||||
|
current.right = Node(translation, mutableSetOf(nodeId), current.depth + 1)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
current = current.right!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(translation: Vector3dc, nodeId: Long) {
|
||||||
|
find(translation)?.nodeIds?.remove(nodeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun find(translation: Vector3dc): Node? {
|
||||||
|
var current: Node? = this
|
||||||
|
while (current != null) {
|
||||||
|
if (translation == current.vertex) {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
current =
|
||||||
|
if (translation[current.axis] < current.vertex[current.axis] || (translation[current.axis] == current.vertex[current.axis] && translation < current.vertex)) {
|
||||||
|
current.left
|
||||||
|
} else {
|
||||||
|
current.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun create(nodes: Collection<INode<*>>, depth: Long = 0): Node =
|
||||||
|
create(nodes.groupBy({ it.absolute.getTranslation() }) { it.nodeId } as Map<Vector3dc, Collection<Long>>,
|
||||||
|
depth)
|
||||||
|
|
||||||
|
fun create(nodes: Map<Vector3dc, Collection<Long>>, depth: Long = 0): Node {
|
||||||
|
if (nodes.isEmpty()) {
|
||||||
|
return Node()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes.size == 1) {
|
||||||
|
val (loc, onlyNodes) = nodes.entries.first()
|
||||||
|
return Node(loc, onlyNodes.toMutableSet())
|
||||||
|
}
|
||||||
|
|
||||||
|
val axis: Int = (depth % 3).toInt()
|
||||||
|
val sorted = nodes.keys.sortedBy { it[axis] }
|
||||||
|
val median = sorted.size / 2
|
||||||
|
val selected = sorted[median]
|
||||||
|
val left = sorted.slice(0..median).toSet().takeIf { it.isNotEmpty() }?.let {
|
||||||
|
nodes.filterKeys(it::contains)
|
||||||
|
}?.let { create(it, depth + 1) }
|
||||||
|
val right = sorted.slice(median + 1..sorted.size).toSet().takeIf { it.isNotEmpty() }?.let {
|
||||||
|
nodes.filterKeys(it::contains)
|
||||||
|
}?.let { create(it, depth + 1) }
|
||||||
|
return Node(selected, nodes[selected]?.toMutableSet() ?: mutableSetOf(), depth, left, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(document: IDocument, nodes: Collection<INode<*>>) : this(document, Node.create(nodes))
|
||||||
|
|
||||||
|
fun add(node: INode<*>) {
|
||||||
|
val vec = node.absolute.getTranslation()
|
||||||
|
nodeLocMap[node.nodeId] = vec
|
||||||
|
root.add(vec, node.nodeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(node: INode<*>) {
|
||||||
|
root.remove(node.absolute.getTranslation(), node.nodeId)
|
||||||
|
nodeLocMap.remove(node.nodeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun find(vertex: Vector3dc) = root.find(vertex)?.nodeIds?.mapNotNull(document::getNodeByNodeId) ?: emptyList()
|
||||||
|
|
||||||
|
fun update(node: INode<*>) {
|
||||||
|
nodeLocMap[node.nodeId]?.let { root.remove(it, node.nodeId) }
|
||||||
|
add(node)
|
||||||
|
}
|
||||||
|
}
|
@ -1,45 +0,0 @@
|
|||||||
package me.eater.threedom.utils
|
|
||||||
|
|
||||||
class MutableOrderedSetListIterator<T>(private val collection: OrderedSet<T>, private var index: Int = 0) :
|
|
||||||
MutableListIterator<T> {
|
|
||||||
|
|
||||||
override fun hasPrevious() = collection.size > (index - 1) && index > 1
|
|
||||||
|
|
||||||
override fun nextIndex() = index + 1
|
|
||||||
override fun previous(): T {
|
|
||||||
if (!hasPrevious()) {
|
|
||||||
throw NoSuchElementException()
|
|
||||||
}
|
|
||||||
|
|
||||||
index = previousIndex()
|
|
||||||
return collection[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun previousIndex() = index - 1
|
|
||||||
override fun add(element: T) {
|
|
||||||
collection.add(index, element)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hasNext() = nextIndex() < collection.size
|
|
||||||
|
|
||||||
override fun next(): T {
|
|
||||||
if (!hasNext()) {
|
|
||||||
throw NoSuchElementException()
|
|
||||||
}
|
|
||||||
|
|
||||||
index = nextIndex()
|
|
||||||
return collection[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun remove() {
|
|
||||||
collection.removeAt(index)
|
|
||||||
|
|
||||||
if (index > 0) {
|
|
||||||
index -= 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun set(element: T) {
|
|
||||||
collection[index] = element
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
package me.eater.threedom.utils
|
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class OrderedSet<T>() : MutableSet<T>, MutableList<T> {
|
|
||||||
override val size
|
|
||||||
get() = items.size
|
|
||||||
|
|
||||||
private val set: MutableSet<T> = mutableSetOf()
|
|
||||||
private val items: MutableList<T> = mutableListOf()
|
|
||||||
|
|
||||||
constructor(elements: Collection<T>) : this() {
|
|
||||||
addAll(elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun contains(element: T) = set.contains(element)
|
|
||||||
override fun containsAll(elements: Collection<T>) = set.containsAll(elements)
|
|
||||||
override fun isEmpty() = items.isEmpty()
|
|
||||||
override fun iterator() = items.iterator()
|
|
||||||
override operator fun get(index: Int) = items[index]
|
|
||||||
override fun spliterator(): Spliterator<T> = set.spliterator()
|
|
||||||
override fun indexOf(element: T): Int = items.indexOf(element)
|
|
||||||
override fun lastIndexOf(element: T): Int = indexOf(element)
|
|
||||||
override fun listIterator(): MutableOrderedSetListIterator<T> = MutableOrderedSetListIterator(this)
|
|
||||||
override fun listIterator(index: Int): MutableOrderedSetListIterator<T> = MutableOrderedSetListIterator(this, index)
|
|
||||||
override fun subList(fromIndex: Int, toIndex: Int): OrderedSet<T> = OrderedSet(items.subList(fromIndex, toIndex))
|
|
||||||
|
|
||||||
override fun add(element: T): Boolean {
|
|
||||||
if (set.add(element)) {
|
|
||||||
items.add(element)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addAll(elements: Collection<T>): Boolean {
|
|
||||||
return elements.map(::add).any()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun clear() {
|
|
||||||
set.clear()
|
|
||||||
items.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun remove(element: T): Boolean {
|
|
||||||
if (set.remove(element)) {
|
|
||||||
items.remove(element)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeAll(elements: Collection<T>): Boolean {
|
|
||||||
return elements.map(::remove).any()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun retainAll(elements: Collection<T>): Boolean {
|
|
||||||
return set.toSet().map {
|
|
||||||
if (elements.contains(it)) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
remove(it)
|
|
||||||
}
|
|
||||||
}.any()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun add(index: Int, element: T) {
|
|
||||||
if (set.add(element)) {
|
|
||||||
items.add(index, element)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addAll(index: Int, elements: Collection<T>): Boolean {
|
|
||||||
return items.addAll(index, elements.filter(set::add))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeAt(index: Int): T {
|
|
||||||
set.remove(items[index])
|
|
||||||
return items.removeAt(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun set(index: Int, element: T): T {
|
|
||||||
if (!set.add(element)) {
|
|
||||||
return element
|
|
||||||
}
|
|
||||||
|
|
||||||
val old = items[index]
|
|
||||||
items[index] = element
|
|
||||||
remove(old)
|
|
||||||
set.add(element)
|
|
||||||
return old
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +1,29 @@
|
|||||||
package me.eater.threedom.utils.joml
|
package me.eater.threedom.utils.joml
|
||||||
|
|
||||||
import org.joml.Matrix4d
|
import org.joml.Matrix4d
|
||||||
|
import org.joml.Matrix4dc
|
||||||
import org.joml.Vector3d
|
import org.joml.Vector3d
|
||||||
|
import org.joml.Vector3dc
|
||||||
|
|
||||||
fun <T : Number> Matrix4d.setTranslation(x: T, y: T, z: T): Matrix4d =
|
fun <T : Number> Matrix4dc.setTranslation(x: T, y: T, z: T): Matrix4d =
|
||||||
setTranslation(x.toDouble(), y.toDouble(), z.toDouble())
|
Matrix4d(this).setTranslation(x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
|
|
||||||
fun Matrix4d.getTranslation(): Vector3d = getTranslation(Vector3d())
|
fun Matrix4dc.getTranslation(): Vector3d = getTranslation(Vector3d())
|
||||||
|
fun Matrix4dc.mutable(): Matrix4d = if (this is Matrix4d) this else Matrix4d(this)
|
||||||
|
|
||||||
|
operator fun Matrix4dc.times(rhs: Matrix4dc) = mul(rhs, Matrix4d())
|
||||||
|
operator fun Matrix4d.times(rhs: Matrix4dc) = mul(rhs)
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
fun <T : Number> Vector3d(x: T, y: T, z: T) = Vector3d(x.toDouble(), y.toDouble(), z.toDouble())
|
fun Vector3d(x: Number, y: Number, z: Number) = Vector3d(x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
|
|
||||||
|
operator fun Vector3dc.compareTo(rhs: Vector3dc): Int {
|
||||||
|
for (i in 0..3) {
|
||||||
|
val c = this[i].compareTo(rhs[i])
|
||||||
|
if (c != 0) {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue