[orx-hash-grid] Add HashGrid space partitioning
This commit is contained in:
@@ -31,6 +31,8 @@ def multiplatformModules = [
|
||||
"orx-shader-phrases",
|
||||
"orx-shapes",
|
||||
"orx-quadtree",
|
||||
"orx-hash-grid"
|
||||
|
||||
]
|
||||
|
||||
def doNotPublish = ["openrndr-demos"]
|
||||
|
||||
3
orx-hash-grid/README.md
Normal file
3
orx-hash-grid/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# orx-hash-grid
|
||||
|
||||
A 2D space partitioning for points.
|
||||
93
orx-hash-grid/build.gradle.kts
Normal file
93
orx-hash-grid/build.gradle.kts
Normal file
@@ -0,0 +1,93 @@
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
kotlin("plugin.serialization")
|
||||
}
|
||||
|
||||
val kotlinxSerializationVersion: String by rootProject.extra
|
||||
val kotestVersion: String by rootProject.extra
|
||||
val junitJupiterVersion: String by rootProject.extra
|
||||
val jvmTarget: String by rootProject.extra
|
||||
val kotlinApiVersion: String by rootProject.extra
|
||||
val kotlinVersion: String by rootProject.extra
|
||||
val kotlinLoggingVersion: String by rootProject.extra
|
||||
val kluentVersion: String by rootProject.extra
|
||||
val openrndrVersion: String by rootProject.extra
|
||||
val openrndrOS: String by rootProject.extra
|
||||
val spekVersion: String by rootProject.extra
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
compilations {
|
||||
val demo by creating {
|
||||
defaultSourceSet {
|
||||
kotlin.srcDir("src/demo")
|
||||
dependencies {
|
||||
implementation(project(":orx-camera"))
|
||||
implementation("org.openrndr:openrndr-application:$openrndrVersion")
|
||||
implementation("org.openrndr:openrndr-extensions:$openrndrVersion")
|
||||
runtimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion")
|
||||
runtimeOnly("org.openrndr:openrndr-gl3-natives-$openrndrOS:$openrndrVersion")
|
||||
implementation(compilations["main"]!!.output.allOutputs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
compilations.all {
|
||||
kotlinOptions.jvmTarget = jvmTarget
|
||||
kotlinOptions.apiVersion = kotlinApiVersion
|
||||
}
|
||||
testRuns["test"].executionTask.configure {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
js(IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(project(":orx-parameters"))
|
||||
implementation(project(":orx-shader-phrases"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlinxSerializationVersion")
|
||||
implementation("org.openrndr:openrndr-application:$openrndrVersion")
|
||||
implementation("org.openrndr:openrndr-draw:$openrndrVersion")
|
||||
implementation("org.openrndr:openrndr-filter:$openrndrVersion")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
|
||||
implementation("io.github.microutils:kotlin-logging:$kotlinLoggingVersion")
|
||||
}
|
||||
}
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test-common"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion")
|
||||
implementation("io.kotest:kotest-assertions-core:$kotestVersion")
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val jvmTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test-common"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
implementation(kotlin("test-junit5"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion")
|
||||
runtimeOnly("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion")
|
||||
runtimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion")
|
||||
implementation("org.spekframework.spek2:spek-dsl-jvm:$spekVersion")
|
||||
implementation("org.amshove.kluent:kluent:$kluentVersion")
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val jsTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test-js"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
orx-hash-grid/src/commonMain/kotlin/HashGrid.kt
Normal file
95
orx-hash-grid/src/commonMain/kotlin/HashGrid.kt
Normal file
@@ -0,0 +1,95 @@
|
||||
package org.openrndr.extra.hashgrid
|
||||
|
||||
import org.openrndr.math.Vector2
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.random.Random
|
||||
|
||||
private fun Double.fastFloor(): Int {
|
||||
return if (this >= 0) this.toInt() else this.toInt() - 1
|
||||
}
|
||||
|
||||
private data class GridCoords(val x: Int, val y: Int) {
|
||||
fun offset(i: Int, j: Int): GridCoords = copy(x = x + i, y = y + j)
|
||||
}
|
||||
|
||||
private class Cell(
|
||||
var xMin: Double = Double.POSITIVE_INFINITY,
|
||||
var xMax: Double = Double.NEGATIVE_INFINITY,
|
||||
var yMin: Double = Double.POSITIVE_INFINITY,
|
||||
var yMax: Double = Double.NEGATIVE_INFINITY,
|
||||
) {
|
||||
val points = mutableListOf<Vector2>()
|
||||
fun insert(point: Vector2) {
|
||||
points.add(point)
|
||||
xMin = min(xMin, point.x)
|
||||
xMax = max(xMax, point.x)
|
||||
yMin = min(yMin, point.y)
|
||||
yMax = max(yMax, point.y)
|
||||
}
|
||||
|
||||
fun squaredDistanceTo(query: Vector2): Double {
|
||||
val width = xMax - xMin
|
||||
val height = yMax - yMin
|
||||
val x = (xMin + xMax) / 2.0
|
||||
val y = (yMin + yMax) / 2.0
|
||||
val dx = max(abs(query.x - x) - width / 2, 0.0)
|
||||
val dy = max(abs(query.y - y) - height / 2, 0.0)
|
||||
return dx * dx + dy * dy
|
||||
}
|
||||
}
|
||||
|
||||
class HashGrid(val radius: Double) {
|
||||
private val cells = mutableMapOf<GridCoords, Cell>()
|
||||
val cellSize = radius / sqrt(2.0)
|
||||
private inline fun coords(v: Vector2): GridCoords {
|
||||
val x = (v.x / cellSize).fastFloor()
|
||||
val y = (v.y / cellSize).fastFloor()
|
||||
return GridCoords(x, y)
|
||||
}
|
||||
|
||||
fun points() = sequence {
|
||||
for (cell in cells.values) {
|
||||
for (point in cell.points) {
|
||||
yield(point)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun random(random: Random = Random.Default) : Vector2 {
|
||||
return cells.values.random(random).points.random()
|
||||
}
|
||||
|
||||
fun insert(point: Vector2) {
|
||||
val gc = coords(point)
|
||||
val cell = cells.getOrPut(gc) { Cell() }
|
||||
cell.insert(point)
|
||||
}
|
||||
|
||||
fun isFree(query: Vector2): Boolean {
|
||||
val c = coords(query)
|
||||
if (cells[c] == null) {
|
||||
for (j in -2..2) {
|
||||
for (i in -2..2) {
|
||||
if (i == 0 && j == 0) {
|
||||
continue
|
||||
}
|
||||
val n = c.offset(i, j)
|
||||
val nc = cells[n]
|
||||
if (nc != null && nc.squaredDistanceTo(query) <= radius * radius) {
|
||||
for (p in nc.points) {
|
||||
if (p.squaredDistanceTo(query) <= radius * radius) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ include 'openrndr-demos',
|
||||
'orx-jvm:orx-git-archiver-gradle',
|
||||
'orx-glslify',
|
||||
'orx-gradient-descent',
|
||||
'orx-hash-grid',
|
||||
'orx-integral-image',
|
||||
'orx-interval-tree',
|
||||
'orx-jumpflood',
|
||||
|
||||
Reference in New Issue
Block a user