import coracle.Drawing
import coracle.Vector
import coracle.random
import coracle.shapes.Circle
import coracle.shapes.Point
class FallingRain: Drawing() {
val populationCount = 2000
val bodyCount = 20
val boids = mutableListOf()
val bodies = mutableListOf()
var elapsed = 0
override fun setup() {
size(450, 450)
repeat(bodyCount){
bodies.add(Body(random(width), random(height), random(15, 40).toInt()))
}
repeat(populationCount){
boids.add(Boid(randomPoint()))
}
}
override fun draw() {
noStroke()
fill(0x44aa66)
bodies.forEach { body ->
body.draw()
}
stroke(0xffffff)
boids.forEach { boid ->
boid
.iterate()
.avoidBodies()
.checkBounds()
.draw()
}
foreground(0x000000, 0.1f)
}
inner class Body(x: Float, y: Float, r: Int): Circle(x, y, r)
inner class Boid(p: Point): Vector(p){
var age = 0
var deathAge = random(100, 340)
private var velocity = Vector(0f, 0f)
private var maxSpeed = 2f
fun iterate(): Boid{
var direction = direction(Vector(x, height))
direction *= 0.1f
velocity += direction
velocity.limit(maxSpeed)
this.x += velocity.x
this.y += velocity.y
age++
return this
}
fun avoidBodies(): Boid{
bodies.forEach { body ->
if(distance(body) < body.r + 10){
var direction = direction(body)
direction *= -0.2f
velocity += direction
velocity.limit(maxSpeed/2)
this.x += velocity.x
this.y += velocity.y
}
}
return this
}
fun checkBounds(): Boid{
if(age >= deathAge || x < 0 || x > width || y < 0 || y > height ){
val respawn = randomPoint()
x = respawn.x
y = respawn.y
age = 0
deathAge = random(100, 340)
}
return this
}
}
private fun randomPoint(): Point {
var invalid = true
var point = Point(random(width), random(height))
while(invalid){
val body = bodies.find { body ->
body.contains(point)
}
if(body == null){
invalid = false
}else{
point = Point(random(width), random(height))
}
}
return point
}
}