Orllewin home

Drawing that used to be at the Orllewin homepage


import coracle.*

class OrllewinDrawing: Drawing() {

    private var maxSpeed = 1.46f
    private var familyHopCycles = 250
    private val familyA = 0
    private val familyB = 1
    private val familyC = 2
    private val bodies = mutableListOf()
    private lateinit var wellA: Vector
    private lateinit var wellB: Vector
    private lateinit var wellC: Vector

    private var bodyColour = 0x205F77
    private var backgroundColour = 0xf5f2f0

    override fun setup() {
        size(450, 350)
        wellA = Vector((width/6) * 1.5, height/2)
        wellB = Vector(width/2, height/2)
        wellC = Vector((width/6) * 4.5, height/2)
        val minBodies = 4
        val maxBodies = 8
        repeat(random(minBodies, maxBodies).toInt()) { bodies.add(Body(familyA)) }
        repeat(random(minBodies, maxBodies).toInt()) { bodies.add(Body(familyB)) }
        repeat(random(minBodies, maxBodies).toInt()) { bodies.add(Body(familyC)) }
    }
    
    override fun draw() {
        matchWindow()
        wellA.x = (width/6f) * 1.5f
        wellB.x = (width/2f)
        wellC.x = (width/6) * 4.5f

        wellA.y = height/2f
        wellB.y = height/2f
        wellC.y = height/2f
        background(backgroundColour)
        noStroke()

        bodies.forEach { body -> body.update().draw() }
    }

    inner class Body(var family: Int){
        private var velocity = Vector(0f, 0f)
        private var location: Vector = Vector.randomPosition(width, height)
        private var inFamilyCycles = 0

        fun update(): Body{
            val gravityWell = when (family) {
                familyA -> wellA
                familyB -> wellB
                familyC -> wellC
                else -> null
            }

            gravityWell?.let{
                var direction = gravityWell - location
                direction.normalize()
                direction *= 0.02f

                velocity += direction
                location += velocity
            }

            bodies.forEach { body ->
                if(body != this && body.family == family){
                    var bodyDirection = body.location - location
                    bodyDirection.normalize()
                    bodyDirection *= 0.01f

                    velocity += bodyDirection
                    velocity.limit(maxSpeed)
                    location += velocity
                }
            }

            if(inFamilyCycles > familyHopCycles){
                when (family) {
                    familyA -> {
                        if(random(100) < 1 || location.distance(wellB) < location.distance(wellA)){
                            this.family = familyB
                            inFamilyCycles = 0
                        }
                    }
                    familyB -> {
                        if(random(100) < 10 || location.distance(wellC) < location.distance(wellB)){
                            this.family = familyC
                            inFamilyCycles = 0
                        }else if(random(100) < 10 || location.distance(wellA) < location.distance(wellB)){
                            this.family = familyA
                            inFamilyCycles = 0
                        }
                    }
                    familyC -> {
                        if(random(100) < 1 || location.distance(wellB) < location.distance(wellC)){
                            this.family = familyB
                            inFamilyCycles = 0
                        }
                    }
                }
            }

            inFamilyCycles++

            return this
        }

        fun draw(){
            noStroke()

            fill(bodyColour, 0.2f)
            circle(location.x, location.y, 25)

            fill(bodyColour)
            circle(location.x, location.y, 4)
        }
    }
}