[orx-hash-grid] Add HashGrid space partitioning

This commit is contained in:
Edwin Jakobs
2022-01-05 14:39:19 +01:00
parent 20125482a4
commit f5b2980d8d
5 changed files with 194 additions and 0 deletions

View File

@@ -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
View File

@@ -0,0 +1,3 @@
# orx-hash-grid
A 2D space partitioning for points.

View 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"))
}
}
}
}

View 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
}
}
}

View File

@@ -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',