[orx-kdtree] Add generated and verified documentation

This commit is contained in:
Edwin Jakobs
2025-01-24 20:26:22 +01:00
parent fc4f7275fb
commit 3073e88875
3 changed files with 163 additions and 43 deletions

View File

@@ -0,0 +1,51 @@
package org.openrndr.extra.kdtree
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.Vector4
import kotlin.jvm.JvmName
/**
* Constructs a KD-Tree for a collection of 2D vectors.
*
* This function creates a KD-Tree from the given iterable collection of `Vector2` objects.
* The KD-Tree is built in a way that organizes the points for efficient spatial operations,
* such as nearest neighbor search or range queries, considering two dimensions (x and y).
*
* @return The root node of the KD-Tree representing the input collection of 2D vectors.
*/
@JvmName("kdTreeVector2")
fun Iterable<Vector2>.kdTree(): KDTreeNode<Vector2> {
val items = this.toMutableList()
return buildKDTree(items, 2, ::vector2Mapper)
}
/**
* Constructs a KD-Tree from an iterable collection of 3-dimensional `Vector3` objects.
*
* This function converts the input iterable into a mutable list and utilizes the `buildKDTree` method
* to organize the `Vector3` objects into a spatial data structure for efficient querying.
*
* @return The root node of the constructed KD-Tree containing the `Vector3` objects.
*/
@JvmName("kdTreeVector3")
fun Iterable<Vector3>.kdTree(): KDTreeNode<Vector3> {
val items = this.toMutableList()
return buildKDTree(items, 3, ::vector3Mapper)
}
/**
* Constructs a KD-Tree from the iterable collection of 4-dimensional vectors.
*
* This method uses the components of each `Vector4` (x, y, z, w) as the coordinate axes
* in a 4-dimensional space to build the KD-Tree.
*
* @receiver An iterable collection of `Vector4` objects to be organized in the KD-Tree.
* @return The root node of the constructed KD-Tree containing the `Vector4` objects.
*/
@JvmName("kdTreeVector4")
fun Iterable<Vector4>.kdTree(): KDTreeNode<Vector4> {
val items = this.toMutableList()
return buildKDTree(items, 4, ::vector4Mapper)
}

View File

@@ -1,11 +1,15 @@
package org.openrndr.extra.kdtree
import org.openrndr.collections.PriorityQueue
import kotlin.math.abs
/**
* Represents a node in a KD-Tree, a data structure for organizing points in a k-dimensional space.
*
* @param T The type of the items stored in the KD-Tree.
* @property dimensions The number of dimensions for the tree.
* @property mapper A function that extracts a coordinate value for a specific dimension from a data item.
*/
class KDTreeNode<T>(val dimensions: Int, val mapper: (T, Int) -> Double) {
var parent: KDTreeNode<T>? = null
var median: Double = 0.0
@@ -13,25 +17,89 @@ class KDTreeNode<T>(val dimensions: Int, val mapper: (T, Int) -> Double) {
var children: Array<KDTreeNode<T>?> = arrayOfNulls(2)
var item: T? = null
/**
* Determines if the current node is a leaf node in the KD-Tree.
*
* A node is considered a leaf if both of its child nodes are null,
* indicating that it does not have any children.
*
* @return `true` if the node has no children, otherwise `false`.
*/
internal val isLeaf: Boolean
get() = children[0] == null && children[1] == null
/**
* Inserts an item into the KD-Tree.
*
* This method adds a new item to the KD-Tree, determining its appropriate position
* based on the structure of the tree, the number of dimensions, and the provided
* mapping function for extracting coordinate values.
*
* @param item The item to be inserted into the KD-Tree.
* @return The newly created KDTreeNode containing the inserted item.
*/
fun insert(item: T): KDTreeNode<T> {
return insert(this, item, dimensions, mapper)
}
/**
* Removes a specified KDTreeNode from the KD-Tree.
*
* The method removes the given node from the tree while preserving the tree's structure and properties.
* If the node is a leaf, it will simply be removed; otherwise, rebalancing actions may be performed.
*
* @param node The KDTreeNode to be removed from the KD-Tree.
* @return The KDTreeNode that replaces the removed node, or null if the node is a leaf or if no replacement occurs.
*/
fun remove(node: KDTreeNode<T>): KDTreeNode<T>? {
return org.openrndr.extra.kdtree.remove(node, mapper)
return remove(node, mapper)
}
/**
* Finds the nearest neighbor to the given query within the KD-Tree.
*
* This method searches the KD-Tree to identify the closest item to the provided query point,
* based on the spatial arrangement of the tree and the dimensions defined in its construction.
*
* @param query The query point for which the nearest neighbor is to be found.
* @param includeQuery If true, the query point itself can be returned as the nearest neighbor,
* otherwise, it is excluded from the results. Default is false.
* @return The item in the KD-Tree that is the closest to the query point, or null if the tree is empty.
*/
fun findNearest(query: T, includeQuery: Boolean = false): T? = findNearest(this, query, includeQuery)
/**
* Finds the k-nearest neighbors to a given query point within the KD-Tree.
*
* This method performs a nearest-neighbor search in the KD-Tree structure to retrieve
* a list of the `k` closest items to the provided query point. The search uses the tree's
* spatial arrangement and allows for optional inclusion or exclusion of the query point itself.
*
* @param query The query point for which the k-nearest neighbors are to be found.
* @param k The number of nearest neighbors to retrieve.
* @param includeQuery If true, the query point itself can be included in the results.
* If false, the query point is excluded. Default is false.
* @return A list of the `k` nearest neighbors to the query point, ordered by proximity.
*/
fun findKNearest(query: T, k: Int, includeQuery: Boolean = false): List<T> {
return findKNearest(this, query, k, includeQuery)
}
/**
* Retrieves all items within a specified radius around a query point in the KD-Tree.
*
* This method searches the KD-Tree to find all items whose distance to the specified query point
* does not exceed the given radius. Optionally, the query point itself may be included or excluded
* from the results.
*
* @param query The query point around which items will be searched.
* @param radius The maximum radius within which items will be included in the results.
* @param includeQuery If true, the query point itself may be included in the results.
* If false, the query point is excluded. Default is false.
* @return A list of all items within the specified radius from the query point.
*/
fun findAllInRadius(query: T, radius: Double, includeQuery: Boolean = false): List<T> {
return findAllInRadius(this, query, radius, includeQuery)
}
@@ -46,23 +114,17 @@ class KDTreeNode<T>(val dimensions: Int, val mapper: (T, Int) -> Double) {
}
}
private fun <T> insertItem(root: KDTreeNode<T>, item: T): KDTreeNode<T> {
return if (root.isLeaf) {
root.item = item
root
} else {
if (root.mapper(item, root.dimension) < root.median) {
insertItem(root.children[0] ?: throw IllegalStateException("left is null"), item)
} else {
insertItem(root.children[1] ?: throw IllegalStateException("right is null"), item)
}
}
}
/**
* Builds a KD-Tree from a mutable list of items, using a mapper function to extract
* coordinate values for the specified dimensions.
*
* @param items A mutable list of items to be included in the KD-Tree.
* @param dimensions The number of dimensions to consider in the KD-Tree.
* @param mapper A function that maps an item and a dimension index to a coordinate value.
* @return The root node of the constructed KD-Tree.
*/
expect fun <T> buildKDTree(items: MutableList<T>, dimensions: Int, mapper: (T, Int) -> Double): KDTreeNode<T>
private fun <T> sqrDistance(left: T, right: T, dimensions: Int, mapper: (T, Int) -> Double): Double {
var distance = 0.0
@@ -92,7 +154,7 @@ fun <T> findAllNodes(root: KDTreeNode<T>): List<KDTreeNode<T>> {
}
fun <T> findKNearest(
private fun <T> findKNearest(
root: KDTreeNode<T>,
query: T,
k: Int,

View File

@@ -1,11 +1,19 @@
package org.openrndr.extra.kdtree
import org.openrndr.math.Vector2
import org.openrndr.math.IntVector2
import org.openrndr.math.Vector3
import org.openrndr.math.Vector4
import kotlin.jvm.JvmName
/** built-in mapper for [Vector2] */
/**
* Maps a 2D vector's dimension to its corresponding value.
*
* @param v The 2D vector whose dimension is to be mapped.
* @param dimension The dimension index to map (0 for x, any other value for y).
* @return The value of the specified dimension of the vector.
*/
fun vector2Mapper(v: Vector2, dimension: Int): Double {
return when (dimension) {
0 -> v.x
@@ -13,6 +21,13 @@ fun vector2Mapper(v: Vector2, dimension: Int): Double {
}
}
/**
* Maps the specified dimension of an IntVector2 to a Double.
*
* @param v the IntVector2 instance containing integer components x and y.
* @param dimension the dimension to map (0 for x, any other value for y).
* @return the x or y component of the vector as a Double, depending on the specified dimension.
*/
fun intVector2Mapper(v: IntVector2, dimension: Int): Double {
return when (dimension) {
0 -> v.x.toDouble()
@@ -20,8 +35,13 @@ fun intVector2Mapper(v: IntVector2, dimension: Int): Double {
}
}
/** built-in mapper for [Vector3] */
/**
* Maps a Vector3 object to one of its components (x, y, or z) based on the specified dimension.
*
* @param v the Vector3 object whose component is to be retrieved
* @param dimension the index representing the component to be retrieved (0 for x, 1 for y, others for z)
* @return the component value corresponding to the specified dimension
*/
fun vector3Mapper(v: Vector3, dimension: Int): Double {
return when (dimension) {
0 -> v.x
@@ -30,7 +50,13 @@ fun vector3Mapper(v: Vector3, dimension: Int): Double {
}
}
/** built-in mapper for [Vector4] */
/**
* Maps the components of a 4-dimensional vector based on the specified dimension index.
*
* @param v the 4-dimensional vector containing the components x, y, z, and w
* @param dimension the index of the dimension to retrieve; 0 for x, 1 for y, 2 for z, and any other value for w
* @return the value of the vector component corresponding to the specified dimension index
*/
fun vector4Mapper(v: Vector4, dimension: Int): Double {
return when (dimension) {
0 -> v.x
@@ -40,22 +66,3 @@ fun vector4Mapper(v: Vector4, dimension: Int): Double {
}
}
@JvmName("kdTreeVector2")
fun Iterable<Vector2>.kdTree(): KDTreeNode<Vector2> {
val items = this.toMutableList()
return buildKDTree(items, 2, ::vector2Mapper)
}
@JvmName("kdTreeVector3")
fun Iterable<Vector3>.kdTree(): KDTreeNode<Vector3> {
val items = this.toMutableList()
return buildKDTree(items, 3, ::vector3Mapper)
}
@JvmName("kdTreeVector4")
fun Iterable<Vector4>.kdTree(): KDTreeNode<Vector4> {
val items = this.toMutableList()
return buildKDTree(items, 4, ::vector4Mapper)
}