Add quadtree nearestToPoint, and Readwrite locking (#217)

This commit is contained in:
Jonathan Ellis
2022-01-18 12:36:28 -06:00
committed by GitHub
parent 398b155a78
commit 3993085197
3 changed files with 136 additions and 5 deletions

View File

@@ -0,0 +1,45 @@
package org.openrndr.extra.quadtree
import org.openrndr.math.Vector2
interface IQuadtree<T> {
/**
* Clears the whole tree
*/
fun clear()
/**
* Finds the nearest and neighbouring objects within a radius
* (needs to have a different name so there is no ambiguity when the generic object type is Vector2)
*
* @param element
* @param radius
* @return
*/
fun nearestToPoint(point: Vector2, radius: Double): QuadtreeQuery<T>?
/**
* Finds the nearest and neighbouring points within a radius
*
* @param element
* @param radius
* @return
*/
fun nearest(element: T, radius: Double): QuadtreeQuery<T>?
/**
* Inserts the element in the appropriate node
*
* @param element
* @return
*/
fun insert(element: T): Boolean
/**
* Finds which node the element is within (but not necessarily belonging to)
*
* @param element
* @return
*/
fun findNode(element: T): Quadtree<T>?
}

View File

@@ -16,7 +16,7 @@ data class QuadtreeQuery<T>(val nearest: T, val neighbours: List<T>, val quads:
* @property maxObjects maximum number of objects per node
* @property mapper
*/
class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: ((T) -> Vector2)) {
class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: ((T) -> Vector2)) : IQuadtree<T> {
/**
* The 4 nodes of the tree
*/
@@ -30,7 +30,7 @@ class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: (
/**
* Clears the whole tree
*/
fun clear() {
override fun clear() {
objects.clear()
for (i in nodes.indices) {
@@ -42,6 +42,48 @@ class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: (
}
}
/**
* Finds the nearest and neighbouring objects within a radius
* (needs to have a different name so there is no ambiguity when the generic object type is Vector2)
*
* @param point
* @param radius
* @return
*/
override fun nearestToPoint(point: Vector2, radius: Double): QuadtreeQuery<T>? {
if (!bounds.contains(point)) return null
val r2 = radius * radius
val scaledBounds = Rectangle.fromCenter(point, radius * 2)
val intersected: List<Quadtree<T>> = intersect(scaledBounds) ?: return null
var minDist = Double.MAX_VALUE
val nearestObjects = mutableListOf<T>()
var nearestObject: T? = null
for (interNode in intersected) {
for (obj in interNode.objects) {
val p = mapper(obj)
val dist = p.squaredDistanceTo(point)
if (dist < r2) {
nearestObjects.add(obj)
if (dist < minDist) {
minDist = dist
nearestObject = obj
}
}
}
}
if (nearestObject == null) return null
return QuadtreeQuery(nearestObject, nearestObjects, intersected)
}
/**
* Finds the nearest and neighbouring points within a radius
*
@@ -49,7 +91,7 @@ class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: (
* @param radius
* @return
*/
fun nearest(element: T, radius: Double): QuadtreeQuery<T>? {
override fun nearest(element: T, radius: Double): QuadtreeQuery<T>? {
val point = mapper(element)
if (!bounds.contains(point)) return null
@@ -92,7 +134,7 @@ class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: (
* @param element
* @return
*/
fun insert(element: T): Boolean {
override fun insert(element: T): Boolean {
// only* the root needs to check this
if (depth == 0) {
if (!bounds.contains(mapper(element))) return false
@@ -128,7 +170,7 @@ class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: (
* @param element
* @return
*/
fun findNode(element: T): Quadtree<T>? {
override fun findNode(element: T): Quadtree<T>? {
val v = mapper(element)
if (!bounds.contains(v)) return null

View File

@@ -0,0 +1,44 @@
package org.openrndr.extra.quadtree
import org.openrndr.math.Vector2
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
/**
* Wraps a quadtree with a ReentrantReadWriteLock, which allows multiple concurrent
* readers or one writer at a time.
*/
class ReadwriteQuadtree<T>(val qt: Quadtree<T>) : IQuadtree<T> {
val lock = ReentrantReadWriteLock()
override fun clear() {
lock.write {
qt.clear()
}
}
override fun nearestToPoint(point: Vector2, radius: Double): QuadtreeQuery<T>? {
lock.read {
return qt.nearestToPoint(point, radius)
}
}
override fun nearest(element: T, radius: Double): QuadtreeQuery<T>? {
lock.read {
return qt.nearest(element, radius)
}
}
override fun insert(element: T): Boolean {
lock.write {
return qt.insert(element)
}
}
override fun findNode(element: T): Quadtree<T>? {
lock.read {
return qt.findNode(element)
}
}
}