Experiments with Kotlin Native
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ÖLAB f30961746b Update 'README.md' 2 years ago
.gradle add cli menu 2 years ago
.idea create drawing class 2 years ago
assets update readme 2 years ago
build add cli menu 2 years ago
gradle/wrapper """ 2 years ago
src add cli menu 2 years ago
.gitignore remove macos meta data file 2 years ago
LICENSE Initial commit 2 years ago
README.md Update 'README.md' 2 years ago
build.gradle.kts more sdl api 2 years ago
catmul_rom_test.txt config support 2 years ago
gradle.properties sdl progress 2 years ago
gradlew """ 2 years ago
gradlew.bat """ 2 years ago
java_pid63295.hprof sdl progress 2 years ago
settings.gradle.kts """ 2 years ago



Experiments with Kotlin Native

A generated Moire image

SDL Install

  • Linux: apt-get install libsdl2-dev
  • MacOS: brew install sdl2
Growth Example A Growth Example B
Growth example image Growth example image

Example Drawing

package drawings

import oppen.Drawing
import oppen.shapes.primitives.Line
import oppen.shapes.primitives.Point
import oppen.shapes.primitives.Rectangle
import oppen.stdlib.Degree
import oppen.stdlib.Math
import oppen.stdlib.Math.deg
import oppen.stdlib.Math.map
import oppen.stdlib.Radian
import oppen.stdlib.Random.random

private val lines = mutableListOf<Line>()
private val overlayRect = Rectangle(0, 0, 600, 600)
const val MAX_LINES = 100000f

class GrowthSDL: Drawing(600, 600) {

    private var reduction = 1.5f

    override fun initialise() {
        stroke(0, 32)
        fill(230, 75)

    override fun draw() {
        lines.forEach { line ->
            line(line.x1, line.y1, line.x2, line.y2)

    override fun key(char: Char?) {
            'N' -> {

    private fun spawnNew(){

        val c = Point(width/2, height/2)
        val length = 100f

        reduction = random(1.6f, 2.2f)

        repeat(random(4, 8)){
            grow(c, length, Math.randomDegree())

        when {
            lines.size < MAX_LINES/3 -> spawnNew()
            else -> {
                val lines = lines.size
                val opacity = map(lines.toFloat(), 0f, MAX_LINES, 128f, 45f)

    private fun grow(p: Point, length: Float, angle: Degree) {
        if (length < 2 || lines.size > MAX_LINES) return

        val p2 = p.plot(angle, length)

        lines.add(Line(p, p2))

        val i = random(3, 8)
        var a: Radian = 0.0
        repeat(i) {
            a += random(-6f, 6f).toDouble()
            grow(p2, length / reduction, a.deg())

Drawing Types

OppenNative has two types of Drawing with the same structure but for different purposes:

  • Drawing - subclass Drawing for animations and interactive work. It utilises SDL for very fast rendering. There's still some work to be done for these Drawing types, especially around complex types (If you look at the example above notice the lines and overlayRect require global scope, whereas when targetting the JVM in similar projects class scope would be correct, I want to fix this without leaking native/cinterop syntax into the Drawing API).
  • SVGDrawing - subclass SVGDrawing when you want to render in a single pass and export to SVG Note: currently broken.


There's a basic SVG helper to create vector graphics, and all OppenShape subclasses have toSVG() and toSVG(colour: String, opacity: Float) methods:

val width = 900
val height = 900
val background = "#ffffff"
val svg = SVG(width, height, background)

val line = Line(0, 0, width, height)

By printing to standard out you can write the output to file, something like: oppen_drawing.kexe > output.svg

Configuration Files

There's support for creating configuration files so you can change Drawing parameters without needing to recompile, see CatmulRomSplineSVGDrawing for an example.

val configFile = "catmul_rom_test.txt"
val config = ConfigReader(configFile).load()
val width = config.getInt("width") ?: 1200
val height = config.getInt("height") ?: 1200
val iterations = config.getInt("iterations") ?: 700
val speed = config.getFloat("speed") ?: 0.05f
val background = config.get("background") ?: "#ffffff"

The config file format is line based:

WIDTH: 1200
SPEED: 0.05