First Steps with Kotlin

Kotlin is a remarkable language to make working with the JVM ergonomic and modern. It achieves simplicity, developer experience, power and compatability with legacy code. Nobody should code in Java any more: Kotlin is just better: you will write less code, it will be more likely to work and it will be more maintainable. (There is a tiny runtime cost but if you are worrying about that in 2019 I don't think you are thinking clearly.) Can we use it with Processing? (Spoiler: yes, it works well.)

Skip straight to the complete Kotlin Processing Boilerplate on Github.

Convenience

Working with Java Processing is a bit awkward. Bascially everything is lumped together into PApplet (to keep things simple for beginners). You can use PGraphics but that comes with pitfalls. Kotlin can address these kinds of issues quite nicely. Below we define in fully type checked way an extensible way to apply functions to an existing class and a DSL-ish concise way to invoke. Notice how from within the Drawings we can call member methods of PApplet, but the code lives in different places and no need for subclassing.

The calling code is just something like this(exampleFour) in context of PApplet draw.

typealias Drawing = PApplet.() -> Unit
operator fun PApplet.invoke(drawing: Drawing) = this.drawing()

val isoConv = isoMap(50f)

val exampleOne: Drawing = {
    translate(320f, 440f)

    strokeWeight(2f)

    for(i in 0..10) {
        stroke(0f,0f,10 * i.f)
        val (x1, y1) = isoConv(i.f, 0f, 0f)
        val (x2, y2) = isoConv(i.f, 0f, 10f)
        val (x3, y3) = isoConv(i.f, 10f, 10f)
        val (x4, y4) = isoConv(i.f, 10f, 20f)
        line(x1, y1, x2, y2)
        line(x2, y2, x3, y3)
        line(x3, y3, x4, y4)
        PApplet.print("$x1 $y1 is from $i")
    }

    for(i in 10 downTo 0) {
        stroke(0f,0f,10 * i.f)
        val (x0, y0) = isoConv(10f, 0f, i.f)
        val (x1, y1) = isoConv(10f, 10f, i.f)
        val (x2, y2) = isoConv(20f, 5f, i.f)
        val (x3, y3) = isoConv(20f, 10f, i.f)
        val (x4, y4) = isoConv(30f, 10f, i.f)
        line(x0, y0, x1, y1)
        line(x1, y1, x2, y2)
        line(x2, y2, x3, y3)
        line(x3, y3, x4, y4)
        PApplet.print("$x1 $y1 is from $i")
    }
}

val exampleTwo: Drawing = {
    translate(320f, 440f)

    val n = 150
    for(i in 30 downTo 0) {
        var points = mutableListOf<Pair<Float, Float>>()
        for(j in 0..n) {
            points.add(isoConv(j.f/5, 0.5f * (i + 1) * sin(j.f/15f) - 20 * noise(j.f/100, i.f/100), i.f))
        }

        strokeWeight(4f)
        stroke(0f,0f,100f)

        for(j in 0 until n) {
            val (x1, y1) = points[j]
            val (x2, y2) = points[j + 1]
            line(x1, y1, x2, y2)
        }

        strokeWeight(2f)
        stroke(0f,0f,0f)

        for(j in 0 until n) {
            val (x1, y1) = points[j]
            val (x2, y2) = points[j + 1]
            line(x1, y1, x2, y2)
        }

    }
}

Extend the core language

Conversions between int and float are frequent in Processing. How do we make this nice(r). Kotlin lets us add functionality to existing classes. Here are a few things I end up adding to most projects.

val Int.f: Float
get() = this.toFloat()

val Float.i: Int
get() = this.toInt()
        
        val <T> List<T>.sample: T
get() = this[Random.nextInt(this.size)]

val <T> MutableSet<T>.sample: T
get() = this.elementAt(Random.nextInt(this.size))

Getting fancier stuff like this is possible:

val <T> List<T>.pairwise: List<Pair<T,T>>
get() {
if(this.size > 1) {
    var pairs = mutableListOf<Pair<T,T>>()
    for(i in 0 .. (this.size - 2)) {
        pairs.add(this[i] to this[i + 1])
    }
    return pairs
} else {
    return listOf()
}
}

Higher Order

Kotlin lets you write higher order, powerful code. For example here I wrote code to randomply pick from a set of functions. Sure there are other ways of doing this but to quickly experiment with variations the ability to create lightweight functions and pass them around/randomly pick from them is handy. Kotlin allows you to quickly experiment and to write high quality prodution code.

object Generate {
    fun moduleDrawers(): List<(PApplet, Float, Float, Float) -> Unit> {
        return (0 until 5).map {i ->
            when(Random.nextInt(5)) {
                0 -> { app: PApplet, x: Float, y: Float, delta: Float ->
                    app.stroke(200f, 80f, 80f)
                    app.line(x, y,x + delta, y + delta)
                }
                1 -> { app: PApplet, x: Float, y: Float, delta: Float ->
                    app.stroke(205f, 80f, 40f)
                    app.line(x + delta, y,x + delta, y + delta)
                }
                2 -> { app: PApplet, x: Float, y: Float, delta: Float ->
                        app.stroke(195f, 80f, 60f)
                    app.line(x + delta, y,x , y )
                }
                3 -> { app: PApplet, x: Float, y: Float, delta: Float ->
                    app.ellipse(x, y, delta / 2f, delta / 2f)
                }
                else -> { app: PApplet, x: Float, y: Float, delta: Float ->
                    app.noStroke()
                    app.fill(215f, 80f, 60f)
                    app.ellipse(x + delta/2f, y + delta/2f, delta/2f, delta/2f)
                    app.noFill()
                }
            }
        }
    }
}

var drawers = Generate.moduleDrawers()

override fun draw() {
  background(40f,10f,95f)
  noStroke()
  strokeWeight(5f)
  for (i in 0 until 32) {
      for (j in 0 until 32) {
          val x = i * delta
          val y = j * delta
          drawers[Random.nextInt(drawers.size)](this, x, y, delta)
      }
  }
}

Setup

You can use Gradle in Kotlin. It is pretty well documented but has changed a lot and few people do so it took a while to figure out a nice setup. Create a build.gradle.kts like:

plugins {
    application
    kotlin("jvm") version "1.3.10"
}

application {
    mainClassName = "processing.Hello"
}

dependencies {
    compile(kotlin("stdlib"))
    compile("org.processing:core:3.3.7")
}

repositories {
    jcenter()
    mavenCentral()
}

task<Jar>("uberJar") {
    appendix = "uber"

    manifest {
        attributes(
            mutableMapOf<String, String>(
                "Base-Name" to "Processing",
                "Main-Class" to "processing.Hello"
        ))
    }

    from(sourceSets.main.get().output)

    dependsOn(configurations.runtimeClasspath)
    from(Callable {
        configurations.runtimeClasspath.filter { it.name.endsWith("jar") }.map { zipTree(it) }
    })
}

Put you sources in src/main/kotlin/[package], tests in src/test/kotlin/[package][package] so that gradle knows where to find them. The boilerplate below will allow you to build easily with Gradle but also run the file in e.g. IntelliJ. It assumes you set up a class like class HelloProcessing() : PApplet() where you write your actual code.


class Hello {
  companion object {
  @JvmStatic
      fun main(args: Array<String>) {
          PApplet.main("processing.HelloProcessing")
      }
  }
}

fun main(args: Array<String>) {
  PApplet.main("processing.HelloProcessing")
}

To test add the junit dependency as below. If you put your tests in the default place then you shouldn't need to do anything more.


dependencies {
  compile(kotlin("stdlib"))
  compile("org.processing:core:3.3.7")
  testImplementation("junit:junit:4.12")
}

Source

The complete Kotlin Processing Boilerplate on Github.