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
|
||||
|
||||
import me.eater.threedom.generated.EventNames
|
||||
|
||||
interface EventListener {
|
||||
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
|
||||
|
||||
import org.joml.Matrix4d
|
||||
import org.joml.Matrix4dc
|
||||
import org.joml.Vector3d
|
||||
import org.joml.Vector3dc
|
||||
|
||||
fun <T : Number> Matrix4d.setTranslation(x: T, y: T, z: T): Matrix4d =
|
||||
setTranslation(x.toDouble(), y.toDouble(), z.toDouble())
|
||||
fun <T : Number> Matrix4dc.setTranslation(x: T, y: T, z: T): Matrix4d =
|
||||
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")
|
||||
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