diff --git a/build.gradle b/build.gradle index 9abe104b..63c1b972 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { allprojects { group 'org.openrndr.extra' - version '0.0.24' + version '0.0.25' } repositories { diff --git a/orx-interval-tree/src/main/kotlin/IntervalTree.kt b/orx-interval-tree/src/main/kotlin/IntervalTree.kt new file mode 100644 index 00000000..9048b34c --- /dev/null +++ b/orx-interval-tree/src/main/kotlin/IntervalTree.kt @@ -0,0 +1,95 @@ +class IntervalNode(val center: Double) { + val overlapBegin = mutableListOf>() + val overlapEnd = mutableListOf>() + + var left: IntervalNode? = null + var right: IntervalNode? = null + + fun queryPoint(x: Double): MutableList { + val results = mutableListOf() + queryPoint(x, results) + return results + } + + private fun queryPoint(x: Double, results: MutableList) { + if (x < center) { + for ((start, item) in overlapBegin) { + if (start <= x) { + results.add(item) + } else { + break + } + } + left?.queryPoint(x, results) + } else if (x > center) { + for ((end, item) in overlapEnd) { + if (end > x) { + results.add(item) + } else { + break + } + } + right?.queryPoint(x, results) + } else if (x == center) { + results.addAll(overlapBegin.map { it.second }) + } + } +} + + +fun buildIntervalTree(items: List, intervalFunction: (T) -> Pair): IntervalNode { + val ranges = items.map { intervalFunction(it) } + val center = ranges.sumByDouble { (it.first + it.second) / 2.0 } / ranges.size + val node = IntervalNode(center) + val leftItems = mutableListOf() + val rightItems = mutableListOf() + for (item in items) { + val interval = intervalFunction(item) + if (interval.first <= center && interval.second > center) { + node.overlapBegin.add(Pair(interval.first, item)) + node.overlapEnd.add(Pair(interval.second, item)) + } else if (interval.second <= center) { + leftItems.add(item) + } else if (interval.first > center) { + rightItems.add(item) + } + } + node.overlapBegin.sortBy { it.first } + node.overlapEnd.sortByDescending { it.first } + + if (leftItems.isNotEmpty()) { + node.left = buildIntervalTree(leftItems, intervalFunction) + } + if (rightItems.isNotEmpty()) { + node.right = buildIntervalTree(rightItems, intervalFunction) + } + return node +} + +fun time(f: () -> Unit) { + val start = System.currentTimeMillis() + f() + val end = System.currentTimeMillis() + println("that took ${end - start}ms") +} + +fun main() { + class Test(val start: Double, val end: Double) + + val items = List(100000) { Test(Math.random(), 1.0 + Math.random()) } + val root = buildIntervalTree(items) { + Pair(it.start, it.end) + } + + time { + for (i in 0 until 10000) { + val results = items.filter { it.start <= 0.05 && it.end > 0.05 } + } + } + + time { + for (i in 0 until 10000) { + root.queryPoint(0.05) + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 4905e364..872af521 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,6 +6,7 @@ include 'orx-camera', 'orx-file-watcher', 'orx-filter-extension', 'orx-integral-image', + 'orx-interval-tree', 'orx-jumpflood', 'orx-kdtree', 'orx-mesh-generators',