Avoid neighbours spatial hash
Similar to Avoid neighbours but implemented with a spatial hash allowing higher numbers of agents.
import coracle.*
import coracle.shapes.Rect
import examples.algorithms.SpatialHashCirclePacking
import kotlin.math.cos
import kotlin.math.sin
class PerlinNoiseDrawing3: Drawing() {
val spatialHash = SpatialHash(12, 12)
val agentCount = 2000
var scale = 0.01f
var speed = 0.8f
var elapsed = 0
var frame = 0
override fun setup() {
(450, 400)
size(agentCount){
repeat.add(Agent(random(width), random(height)))
spatialHash}
}
override fun draw() {
(0xffffff, 0.75f)
stroke++
frame
spatialHash.iterate()
.remap()
++
elapsedif(elapsed > 350){
.newSeed()
Perlin= random(0.001f, 0.02f)
scale = random(0.8f, 2f)
speed = 0
elapsed }
(0x000000, 0.035f)
foreground}
class SpatialHash(private val columns: Int, private val rows: Int){
inner val cellPopulations = HashMap>()
val cellWidth: Int = width/columns
val cellHeight: Int = height/rows
var initialised = false
{
init var index = -1
(rows * columns){
repeat++
index[index] = mutableListOf()
cellPopulations}
}
fun add(c: Agent): SpatialHash {
val index = getIndexHash(c.x.toInt(), c.y.toInt())
val population = cellPopulations[index]
?.add(c)
population
= true
initialised return this
}
fun getIndexHash(x: Int, y: Int): Int {
val col = (x * columns / (width + 1))
val row = (y * rows / (height + 1))
return row * columns + col
}
fun iterate(): SpatialHash{
.forEach { cellCollection ->
cellPopulations.value.forEach { agent ->
cellCollection
agent.avoidNearest()
.updateFlowField()
.checkBounds()
.draw()
}
}
return this
}
fun remap(){
.values.forEachIndexed { index, agents ->
cellPopulations.indices.reversed().forEach { i ->
agentsval agent = agents[i]
val correctIndex = getIndexHash(agent.x.toInt(), agent.y.toInt())
if(correctIndex != index){
.removeAt(i)
agents(agent)
add}
}
}
}
}
class Agent(x: Float, y: Float): Vector(x, y) {
inner
var age = 0
var deathAge = random(100, 340)
fun updateFlowField(): Agent{
if(frame % 3 != 0) return this
++
ageval a = TAU * Perlin.noise(x * scale, y * scale)
var direction = direction(Vector( x + (cos(a)).toFloat(), y + (sin(a) ).toFloat()))
.normalize()
direction*= 0.8f
direction this.x += direction.x * speed
this.y += direction.y * speed
return this
}
fun getNeighbourhoodAgents(): List{
val neighbours = mutableListOf()
val thisIndex = spatialHash.getIndexHash(this.x.toInt(), this.y.toInt())
val leftIndex = spatialHash.getIndexHash(this.x.toInt() - spatialHash.cellWidth, this.y.toInt())
val rightIndex = spatialHash.getIndexHash(this.x.toInt() + spatialHash.cellWidth, this.y.toInt())
val topIndex = spatialHash.getIndexHash(this.x.toInt(), this.y.toInt() - spatialHash.cellHeight)
val bottomIndex = spatialHash.getIndexHash(this.x.toInt(), this.y.toInt() + spatialHash.cellHeight)
.addAll(spatialHash.cellPopulations[thisIndex] ?: listOf())
neighbours.addAll(spatialHash.cellPopulations[leftIndex] ?: listOf())
neighbours.addAll(spatialHash.cellPopulations[rightIndex] ?: listOf())
neighbours.addAll(spatialHash.cellPopulations[topIndex] ?: listOf())
neighbours.addAll(spatialHash.cellPopulations[bottomIndex] ?: listOf())
neighbours
return neighbours
}
fun avoidNearest(): Agent{
var closestDistance = Float.MAX_VALUE
var closestIndex = -1
val agents = getNeighbourhoodAgents()
.forEachIndexed { index, other ->
agentsif(other != this){
val distance = distance(other)
if(distance < closestDistance){
= index
closestIndex = distance
closestDistance }
}
}
if(closestIndex != -1){
val closest = agents[closestIndex]
if(distance(closest) < 35) {
var direction = direction(closest)
.normalize()
direction*= -0.3f
direction
this.x += direction.x
this.y += direction.y
}
}
return this
}
fun checkBounds(): Agent{
if(age >= deathAge || x < 0 || x > width || y < 0 || y > height ){
= random(width)
x = random(height)
y = 0
age = random(100, 340)
deathAge }
return this
}
fun draw() = point(x, y)
}
}