Skip to content

Commit 3993085

Browse files
author
Jonathan Ellis
authored
Add quadtree nearestToPoint, and Readwrite locking (#217)
1 parent 398b155 commit 3993085

File tree

3 files changed

+136
-5
lines changed

3 files changed

+136
-5
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.openrndr.extra.quadtree
2+
3+
import org.openrndr.math.Vector2
4+
5+
interface IQuadtree<T> {
6+
/**
7+
* Clears the whole tree
8+
*/
9+
fun clear()
10+
11+
/**
12+
* Finds the nearest and neighbouring objects within a radius
13+
* (needs to have a different name so there is no ambiguity when the generic object type is Vector2)
14+
*
15+
* @param element
16+
* @param radius
17+
* @return
18+
*/
19+
fun nearestToPoint(point: Vector2, radius: Double): QuadtreeQuery<T>?
20+
21+
/**
22+
* Finds the nearest and neighbouring points within a radius
23+
*
24+
* @param element
25+
* @param radius
26+
* @return
27+
*/
28+
fun nearest(element: T, radius: Double): QuadtreeQuery<T>?
29+
30+
/**
31+
* Inserts the element in the appropriate node
32+
*
33+
* @param element
34+
* @return
35+
*/
36+
fun insert(element: T): Boolean
37+
38+
/**
39+
* Finds which node the element is within (but not necessarily belonging to)
40+
*
41+
* @param element
42+
* @return
43+
*/
44+
fun findNode(element: T): Quadtree<T>?
45+
}

orx-quadtree/src/commonMain/kotlin/Quadtree.kt

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ data class QuadtreeQuery<T>(val nearest: T, val neighbours: List<T>, val quads:
1616
* @property maxObjects maximum number of objects per node
1717
* @property mapper
1818
*/
19-
class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: ((T) -> Vector2)) {
19+
class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: ((T) -> Vector2)) : IQuadtree<T> {
2020
/**
2121
* The 4 nodes of the tree
2222
*/
@@ -30,7 +30,7 @@ class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: (
3030
/**
3131
* Clears the whole tree
3232
*/
33-
fun clear() {
33+
override fun clear() {
3434
objects.clear()
3535

3636
for (i in nodes.indices) {
@@ -42,14 +42,56 @@ class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: (
4242
}
4343
}
4444

45+
/**
46+
* Finds the nearest and neighbouring objects within a radius
47+
* (needs to have a different name so there is no ambiguity when the generic object type is Vector2)
48+
*
49+
* @param point
50+
* @param radius
51+
* @return
52+
*/
53+
override fun nearestToPoint(point: Vector2, radius: Double): QuadtreeQuery<T>? {
54+
if (!bounds.contains(point)) return null
55+
56+
val r2 = radius * radius
57+
58+
val scaledBounds = Rectangle.fromCenter(point, radius * 2)
59+
val intersected: List<Quadtree<T>> = intersect(scaledBounds) ?: return null
60+
61+
var minDist = Double.MAX_VALUE
62+
val nearestObjects = mutableListOf<T>()
63+
var nearestObject: T? = null
64+
65+
for (interNode in intersected) {
66+
for (obj in interNode.objects) {
67+
val p = mapper(obj)
68+
69+
val dist = p.squaredDistanceTo(point)
70+
71+
if (dist < r2) {
72+
nearestObjects.add(obj)
73+
74+
if (dist < minDist) {
75+
minDist = dist
76+
nearestObject = obj
77+
}
78+
}
79+
}
80+
}
81+
82+
if (nearestObject == null) return null
83+
84+
return QuadtreeQuery(nearestObject, nearestObjects, intersected)
85+
}
86+
4587
/**
4688
* Finds the nearest and neighbouring points within a radius
4789
*
4890
* @param element
4991
* @param radius
5092
* @return
5193
*/
52-
fun nearest(element: T, radius: Double): QuadtreeQuery<T>? {
94+
override fun nearest(element: T, radius: Double): QuadtreeQuery<T>? {
5395
val point = mapper(element)
5496

5597
if (!bounds.contains(point)) return null
@@ -92,7 +134,7 @@ class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: (
92134
* @param element
93135
* @return
94136
*/
95-
fun insert(element: T): Boolean {
137+
override fun insert(element: T): Boolean {
96138
// only* the root needs to check this
97139
if (depth == 0) {
98140
if (!bounds.contains(mapper(element))) return false
@@ -128,7 +170,7 @@ class Quadtree<T>(val bounds: Rectangle, val maxObjects: Int = 10, val mapper: (
128170
* @param element
129171
* @return
130172
*/
131-
fun findNode(element: T): Quadtree<T>? {
173+
override fun findNode(element: T): Quadtree<T>? {
132174
val v = mapper(element)
133175

134176
if (!bounds.contains(v)) return null
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.openrndr.extra.quadtree
2+
3+
import org.openrndr.math.Vector2
4+
import java.util.concurrent.locks.ReentrantReadWriteLock
5+
import kotlin.concurrent.read
6+
import kotlin.concurrent.write
7+
8+
/**
9+
* Wraps a quadtree with a ReentrantReadWriteLock, which allows multiple concurrent
10+
* readers or one writer at a time.
11+
*/
12+
class ReadwriteQuadtree<T>(val qt: Quadtree<T>) : IQuadtree<T> {
13+
val lock = ReentrantReadWriteLock()
14+
15+
override fun clear() {
16+
lock.write {
17+
qt.clear()
18+
}
19+
}
20+
21+
override fun nearestToPoint(point: Vector2, radius: Double): QuadtreeQuery<T>? {
22+
lock.read {
23+
return qt.nearestToPoint(point, radius)
24+
}
25+
}
26+
27+
override fun nearest(element: T, radius: Double): QuadtreeQuery<T>? {
28+
lock.read {
29+
return qt.nearest(element, radius)
30+
}
31+
}
32+
33+
override fun insert(element: T): Boolean {
34+
lock.write {
35+
return qt.insert(element)
36+
}
37+
}
38+
39+
override fun findNode(element: T): Quadtree<T>? {
40+
lock.read {
41+
return qt.findNode(element)
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)