Kotlin + JavaFX Canvas

With my new favorite language Kotlin, I’ve been able to code in more amazing ways than before. Last time, I ported P5.js to Kotlin. This time, I tried JavaFX Canvas.

 

JavaFX-Kotlin Basics

Here is the bare-bones code to run JavaFX in Kotlin:

 class Barebones() : Application(){  
   override fun start(stage: Stage){  
     val root = StackPane()  
     //add any elements to the root!!!
     val scene = Scene(root)  
     stage.title = "JavaFX Canvas + Kotlin"  
     stage.scene = scene  
     stage.show()  

   }  
   companion object {  
     @JvmStatic  
     fun main(args: Array<String>) {  
       launch(Barebones::class.java)  
     }  
   }  
 }  

 

Wow, gotta get used to that @JVMStatic and companion object. 🙂 There is a full-blown Kotlin framework for JavaFX called TornadoFX. You may want to check it out.

 

CanvasKT

First I created a simple and basic wrapper in creating canvas animations. I’d temporarily call it “CanvasKT”. To make animations, first:

 
val sketch = Sketch{
    setup{
        //insert all initialization here
    }
    draw {
        //invoked continuously after setup
    }
}

 

Here we instantiated Sketch then assigned it to a variable. Inside the Sketch, you can call the setup and draw function. You are fee to invoke any GraphicsContext operations inside them.  draw and setup are similar to Processing’s – anything inside setup will be executed at the beginning of the animation, followed by the continuous execution of draw.

The following will produce a spiral animation:

var startingTheta = 0.0  
val sketch = Sketch {

    var startingTheta = 0.0

    draw {
        val width = canvas.width
        val height = canvas.height
        save()
        fill = WHITE
        fillRect(0.0, 0.0, width, height)
        fill = ORANGE
        translate(width / 2.0, height / 2.0)
        val maxRadius = max(width, height) / 2.0 + 50.0
        var theta = startingTheta
        var r = 0.0
        while (r < maxRadius) {
            val y = r * sin(toRadians(theta))
            val x = r * cos(toRadians(theta))
            fillOval(x, y, 15.0, 15.0)
            theta += 0.6
            r += 0.05
        }
        restore()
        startingTheta += 0.5
    }
}

 

Now we need a Canvas to paint to. We can use a subclass of Canvas I created called Canvas2D, and pass sketch to its constructor.

val canvas = Canvas2D(sketch, 720.0, 400.0)  

Don’t forget to call the start() function to initiate the animation.
Then, just add this canvas to your stackpane or anywhere you want.
Here’s the full code:

 
class Spiral() : Application(){
    override fun start(stage: Stage){
        val root = StackPane()
        val canvas = Canvas2D(sketch, 720.0, 400.0)
        root.children.add(canvas)
        val scene = Scene(root)
        stage.title = "JavaFX Canvas + Kotlin"
        stage.scene = scene
        stage.show()
        canvas.start()
    }
    companion object {
        @JvmStatic
        fun main(args: Array) {
            launch(Spiral::class.java)
        }
    }
}

 

Run it:

Spiral
Spiral

 

The App.run()

I’ve also created a function to instantly create a Stage and layout the canvas automatically:

 class Simple(){
    companion object {
        @JvmStatic
        fun main(args: Array) {
            App.run(sketch, 400.0, 400.0)
        }
    }
}

val sketch = Sketch{
    setup{
        fill = Color.ORANGE
    }
    draw {
        val r = Random()
        fillRect(r.nextInt(640).toDouble(), r.nextInt(360).toDouble(), 10.0, 10.0)
    }
}

 

App.run(sketch, 400.0, 400.0) creates a stage + canvas with 400 width and 400 height to run our sketch. We can totally omit the width and height parameters so that they default to 640 and 320 respectively. Very handy when you want to see your sketches immediately and get rid of those boilerplates. 🙂

Run:

random
Random

 

Environment Variables

 
class Variables {
    companion object {
        @JvmStatic
        fun main(args: Array) {
            App.run(sketch)
        }
    }
}

val sketch = Sketch {
    draw {
        val x = mouseX
        val y = mouseY
        val clicked = isMousePressed
        val time = now
        clearRect(0.0, 0.0, canvas.width, canvas.height)
        fillText("Mouse X: $x", 50.0, 50.0)
        fillText("Mouse Y: $y", 50.0, 80.0)
        fillText("Mouse Clicked: $clicked", 50.0, 120.0)
        fillText("timestamp in nanosec: $time", 50.0, 150.0)
    }
}

All environment variables are immutable.

Run:

stamp

Mouse Events

 

val sketch = Sketch{

    mouseClicked {

    }

    mouseDragged {

    }

    mouseEntered{

    }

    mouseExited {

    }

    mousePressed {

    }

    mouseReleased {

    }
}

 

 

 

Now we have achieved a Processing-style of creating drawings/sketches!

In my next post, we will try to integrate Kotlin with Processing

 

Github

Leave a Reply