Self organising lattice

import coracle.Drawing
import coracle.Vector
import coracle.random
import coracle.randomInt

class SelfOrganising: Drawing() {

    val worldColour = 0xf5f2f0
    val boidColour = 0x000000
    var boids = mutableListOf()
    val cellWidth = 30

    var inited = false

    val birthWaitMax = 50
    var birthWait = randomInt(birthWaitMax)

    private lateinit var blackHole: Vector


    override fun setup() {
        size(450, 450)
        blackHole = Vector(width/2, height/2)
        boids.add(Boid())
    }

    override fun draw() {
        background(worldColour)

        if(frame >= birthWait && boids.size < 150){
            boids.add(Boid())
            frame = 0
            birthWait = randomInt(birthWaitMax)
        }

        boids.forEach { boid ->
            boid.update().draw()
        }
    }

    inner class Boid{

        private val maxSpeed = 0.3f
        private var location = Vector(width/2 + random(-5, 5), height/2 + random(-5, 5))
        private var velocity = Vector(0, 0)

        fun update(): Boid {

            var blackholeDirection = blackHole - location
            blackholeDirection.normalize()
            blackholeDirection *= 0.05f

            velocity += blackholeDirection
            location += velocity

            var closestDistance = Float.MAX_VALUE
            var closestIndex = -1

            if(boids.size == 1) return this

            boids.forEachIndexed { index, other ->
                if(other != this){
                    val distance = location.distance(other.location)
                    if(distance < closestDistance){
                        closestIndex = index
                        closestDistance = distance
                    }
                }

                var direction = location.direction(other.location)
                direction.normalize()
                direction *= (0.002f)/boids.size.toFloat()

                velocity += (direction)
                velocity.limit(maxSpeed)

            }

            val closest = boids[closestIndex]

            if(closestDistance < cellWidth) {
                var direction = location.direction(closest.location)
                direction.normalize()
                direction *= -0.4f

                velocity += (direction)
                velocity.limit(maxSpeed)

            }

            location += (velocity)

            if (this.location.x > width + cellWidth) this.location.x = -cellWidth.toFloat()
            if (this.location.x < -cellWidth) this.location.x = width.toFloat() + cellWidth
            if (this.location.y > height + cellWidth) this.location.y = -cellWidth.toFloat()
            if (this.location.y < -cellWidth) this.location.y = height.toFloat() + cellWidth

            return this
        }

        fun draw(){
            noStroke()
            fill(boidColour, 0.15f)
            circle(location.x, location.y, (cellWidth/2))

            fill(boidColour, 0.7f)
            circle(location.x, location.y, 1)
        }
    }
}