Friday, January 25, 2013

How to create 2D Lightning Effects - with JavaFX

This time I invite you to follow my path to another nice 2D effect: Lightning.

Election night crowd, Wellington, 1931
Election night crowd, Wellington, 1931 

Friday evening, surfing. Then, suddenly I was struck by lightning. Here.

This blog post is about my journey to a decent looking lighning bolt visualisation using Scala and JavaFX.

As a side note: I really appreciate Oracles move to support JavaFX with plain Java, and thus opening up the API for all JVM based languages, otherwise the following approach would never have been possible.

So let's start:

Level -1: Create a glowing line


Well, that should be easy with JavaFX. What do we need? Just a Line and an effect? Maybe even a premade effect which is named Glow? And a Line? Well. Almost:


class GlowingLine extends javafx.application.Application {
val canvasWidth = 1024
val canvasHeight = 768
override def start(primaryStage: Stage): Unit = {
primaryStage.setTitle("Level0")
val root = new BorderPane()
val drawingBoard = new Group()
val background = {
val b = new Rectangle(0, 0, canvasWidth, canvasHeight)
b.setFill(Color.BLACK)
b
}
val refline = new Line(canvasWidth / 10, canvasHeight / 2, canvasWidth * 9 / 10, canvasHeight / 2)
refline.setStroke(Color.WHITESMOKE)
val line = new Line(canvasWidth / 10, canvasHeight / 2, canvasWidth * 9 / 10, canvasHeight / 2)
line.setStroke(Color.WHITE)
line.setStrokeWidth(4)
val bloom = new Bloom(1.0)
val blur = new GaussianBlur()
blur.setInput(bloom)
line.setEffect(blur)
drawingBoard.getChildren().addAll(background, refline, line)
root.setCenter(drawingBoard)
primaryStage.setScene(new Scene(root, canvasWidth, canvasHeight))
primaryStage.show()
}
}

This gives me a picture like this:

A glowing line (sort of)

Ok, seems ok for me. Lets proceed to

Level 0: Create jagged lines


My lightning is so far only a straight line, but I want to have a cool lightning strike. The tutorial I'm trying to reimplement here proposes to partition the line in smaller pieces of a random number. Furthermore it says the endpoints of those lines should be connected and be placed normal to the direction of the original line.

So this is what I came up with:


class JaggedLine extends javafx.application.Application {
val canvasWidth = 1024
val canvasHeight = 768
val jaggingSections = 100
val jaggedFactor = 2
override def start(primaryStage: Stage): Unit = {
primaryStage.setTitle("Level1")
val root = new BorderPane()
val drawingBoard = new Group()
val background = {
val b = new Rectangle(0, 0, canvasWidth, canvasHeight)
b.setFill(Color.BLACK)
b
}
drawingBoard.getChildren().add(background)
drawingBoard.getChildren.addAll(jaggedlines(Vec(canvasWidth / 10, canvasHeight / 2), Vec(canvasWidth * 9 / 10, canvasHeight / 2), jaggingSections, Color.WHITESMOKE))
root.setCenter(drawingBoard)
primaryStage.setScene(new Scene(root, canvasWidth, canvasHeight))
primaryStage.show()
}
def jaggedlines(source: Vec, dest: Vec, count: Int, color: Color): List[Group] = {
val totalVec = (source - dest)
val maxLen = totalVec.length
val onedir = totalVec.onedir
val length = totalVec.length / count
val normal = onedir.normal
val elongation = jaggedFactor * maxLen / count
val positions = List(source) ++ (for (i <- 1 to (count - 1)) yield source + onedir * length * i + normal * elongation * Random.nextDouble) ++ List(dest)
(for (List(a, b) <- positions.sliding(2)) yield mkLine(a, b, color)).toList
}
// poor mans helper classes and functions
case class Vec(x: Double, y: Double) {
def -(that: Vec) = Vec(that.x - x, that.y - y)
def +(that: Vec) = Vec(x + that.x, y + that.y)
def *(factor: Double) = Vec(factor * x, factor * y)
def /(l: Double) = if (l != 0) Vec(x / l, y / l) else sys.error("div.0")
def length = scala.math.sqrt(x * x + y * y)
def onedir = this / length
def normal = Vec(-y, x)
}
def mkRandColor = {
def randInt = (Random.nextFloat * 255).toInt
Color.rgb(randInt, randInt, randInt)
}
def mkLine(source: Vec, dest: Vec, color: Color): Group = {
val g = new Group()
val refline = new Line(source.x, source.y, dest.x, dest.y)
refline.setStroke(color)
val line = new Line(source.x, source.y, dest.x, dest.y)
line.setStroke(color)
line.setStrokeWidth(4)
val bloom = new Bloom(1.0)
val blur = new GaussianBlur()
blur.setInput(bloom)
line.setEffect(blur)
g.getChildren().addAll(refline, line)
g
}
}

it will output pictures like this:


This took me some time to figure out - I reinvented the 2D math for myself again, I'm sure this can be done way better. What I'm fond of is how the list of endpoints of the different line sections is traversed - have a look!

Level 1: Animation


What I would like to see is some interaction. Clicking somewhere in the canvas and then - BANG! - the lightning strikes.

What I need to do for that is just use the mouse coordinates, give the canvas a listener for it and thats all since JavaFX handles the erasing of the blurry pixels. Maybe I'll add some flickering to it, some fade outs? More glowing?

For most of my wishes there are already API's for it in the standard library, for example the very handy FadeTransition which I wanted to invent but it was already there ;-).  Have a look at this code snippet:

Fade Transitions are here, for free.
On the otther hand, I definitely wanted also to have some sound effect, which is also a one liner:



Here is the result so far:




The code for the video above is available here.

Level 2: Better layout algorithm for lightning bolts


The next step would be to improve the layout of bolts, add branches and refine the layout algorithm which is at the moment very basic to get a better visual result and to let the bolts look more "natural".

Apart from the link mentioned above, there are also other sources of information available how to generate decent looking lightning bolts. My current approach just places a list of points randomly round a normal line, but a visually more appealing result is possible for example by using the technique of "midpoint replacement". The idea is that you recursively part up a line in two halves, and move the midpoint a random amount of pixels normal to the given direction. With every nested level you may lower the amount of displacement.

So, the relevant code would look like this (inspired by Mr. KrazyDad):


With this new layoutalgorithm, the lightning bolts look much better (and also more like the ones at gamedevtut's site):



You can see the source here. Since I strive to be a well behaved functional programmer I had to swap only one function to achieve this. (My favorite joke: "Two pure functions walk in a bar. Nothing happens.")


Level 3: Branching


At the moment I support painting only one lightning, I want to have some sort of branching so that I get an even better result. This can be accomplished by randomly choosing a subelement of the main thunderbolt, then calculate a new endpoint which points at another direction, and do this again recursively (maybe at a random rate).

Branching adds much value without much pain to our lightning.  I changed the datastructures a little, now I'm saving tuples of start and endpoints in the list which I'm sending to the mkLine function - this simplifies the painting loop considerably (no more sliding windows anymore :( )

Here is the lightning with branches (and different colors):




Level 4: Adding a nice headline

I have added now some text, using a custom font:

Lightning bolts around a headline

Using true type fonts is no problem either when using JavaFX.

Level 5: Put it all together


We have all what we need for our final step - create lightnings to write something on the screen. In order to achieve this effect, we need to write the headline into a WriteableImage and use the rasterized information for source and endpoints of randomly generated bolts. By using a simple trick which prefers two points which are nearer to each other than two points with a longer distance, we achieve a nice looking effect you may have once or twice seen in games or other visual effects.

This whole application is by no means optimized, you will hear your fan soon when starting the program, but it is no wonder since we are calculating lots of stuff and are generating huge number of nodes.

Have a look at the final result of the 2D JavaFX Lightning blog post, and thanks for reading!




If you want to have a peek at the source code for the whole project, its on github

Monday, January 21, 2013

Calling Ant from Maven using a property file

Consolidating builds for several projects can be a process which sometimes needs some creativity combining the tools already available.

Most of the time it is too risky and expensive to write everything from scratch - even if it is very tempting to do so. In case of Maven and Ant, there is the well known antrun-plugin which makes it possible to integrate Ant based builds into the Maven ecosystem. 

Below is an example how to call Ant from Maven using a property file to store values which may change often.

Maybe somebody finds it useful.

<project default="main">
<target name="main">
<echo message="This line is executed in an external build.xml for javafx version: ${javafx.version}"/>
</target>
</project>
view raw build.xml hosted with ❤ by GitHub
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.ladstatt.maven</groupId>
<artifactId>example-pom-with-filtering</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>example-pom-with-filtering</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<filters>
<filter>props.properties</filter>
</filters>
<plugins>
<!-- http://mojo.codehaus.org/properties-maven-plugin/ -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0-alpha-2</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>props.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>package</phase>
<configuration>
<target>
<!-- Place any Ant task here. You can add anything you can add between
<target> and </target> in a build.xml. -->
<property name="a.version" value="${a.version}" />
<ant antfile="build.xml" />
<echo
message="This is ant called from maven for and providing a version number from a property file ${a.version}" />
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
view raw pom.xml hosted with ❤ by GitHub
a.version=1.0

Sunday, January 20, 2013

Native Drag & Drop with JavaFX

Usability is king. Enable your applications with drag and drop support to make your users happy.



JavaFX supports Drag and Drop - I've added some rudimentary DnD support for the tree visualisation program. The code for the whole project is available at github.

The relevant code parts are those here:

basic Drag and Drop Support with JavaFX 

I use the ShapeConverter class to convert the javafx based trees to a svg format - you'll find more valuable gadgets like this in the jfxtras project.

On MacOsX this code can drop directly into the browser, but not on the desktop for example. Under Windows7 it works however. Have a look at the jira for a list of open DnD JavaFX issues. (login required)

This is just a starting point for drag and drop, you can register even more listeners to make your application more user friendly - see the tutorial here.

Friday, January 18, 2013

ScalaFX - A DSL for JavaFX in Scala

ScalaFX is an opensource project which aims to provide a concise DSL for using JavaFX with Scala.


Scientific apparatus, 1939, 1
Scientific apparatus, 1939, 1


Only very recently the ScalaFX project was restructured and it is now easier to set it up and build it - Peter Pilgrim did a great job setting up the sbt build. I want to describe what is necessary to get a maven project running which uses the ScalaFX project.

7 Steps to a running ScalaFX application with maven


(If you have tried out examples of this blog you may have installed the prerequisites already, so you can skip until step 1.):


-4) Make sure maven is installed:

Get it at here.

-3) Install Mercurial: 

Get it here.

-2) Make sure you are using a recent JDK (1.7.11 or newer):

Get it here.

-1) Install the simple build tool for scala

Get it here.

0) Execute following command and say "Y"

mvn com.zenjava:javafx-maven-plugin:1.3:fix-classpath

Ok, now you should be set up. Now the ScalaFX specific steps start.

1.) Check out the ScalaFX source code

hg clone https://code.google.com/p/scalafx/

2.) Install it into your local maven repository

In short:

sbt clean compile package make-pom package-src make-pom

mvn install:install-file -DartifactId=scalafx_2.9.2 \
    -DgroupId=org.scalafx \
    -Dpackaging=jar \
    -DpomFile=scalafx-core/target/scala-2.9.2/scalafx-core_2.9.2-1.0-SNAPSHOT.pom \
    -Dfile=scalafx-core/target/scala-2.9.2/scalafx-core_2.9.2-1.0-SNAPSHOT.jar \
    -Dversion=1.0-SNAPSHOT \
    -Dsources=scalafx-core/target/scala-2.9.2/scalafx-core_2.9.2-1.0-SNAPSHOT-sources.jar


will place a maven artifact in your local repository and suddenly you are ready to start coding with ScalaFX. (Skipping tests is of course bad and ugly ;-) )

You have to reference it from any maven project via

<dependency>
<groupId>org.scalafx</groupId>
<artifactId>scalafx_2.10</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>


3) Final step: Clone example project


Anyhow, after going through all this you can clone the scalafx-example project from github. It contains a ScalaFX program which looks like this:

ScalaFX DSL


You can import the project into Eclipse via "Import maven projects" or compile it on the command line via

mvn clean package

Entering

java -jar target/scalafx-example-1.0-SNAPSHOT-jfx.jar 

will show you the following:


ScalaFX HelloWorld

For more information about ScalaFX,  go to ScalaFX.org.

Sunday, January 13, 2013

JavaFX Tree Visualization Part 2

In this blog post I'll post a follow up on my post about painting random generated trees.

My intention was first to improve the code a bit and use a more functional way to express the recursion, now I'm using case classes and more pattern matching to build up the tree. I think now it is far more understandable what is going on.



Moreover I've added logic for 'growing' the trees, albeit it doesn't look very natural how they grow ;-)

What I've discovered however is that by using timelines you can parallelize animations - if you look at the video you'll see that more than one tree grows without disturbing the others doing their thing. I don't know if it is the "correct" way to do stuff like this in JavaFX, but it seems to work.

Still I'm amazed how easy and intuitive it is to create such graphic stuff, and my processor doesn't seem to bother much that I'm painting thousands of Nodes in this little program.

Here is the source code, full source available as a standalone maven project.


package net.ladstatt.apps
import scala.collection.JavaConversions.seqAsJavaList
import scala.math.Pi
import scala.math.cos
import scala.math.sin
import scala.util.Random
import javafx.animation.Animation
import javafx.animation.KeyFrame
import javafx.animation.Timeline
import javafx.application.Application
import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.scene.Group
import javafx.scene.Scene
import javafx.scene.effect.Glow
import javafx.scene.input.MouseEvent
import javafx.scene.paint.Color
import javafx.scene.paint.CycleMethod
import javafx.scene.paint.LinearGradient
import javafx.scene.paint.Paint
import javafx.scene.paint.Stop
import javafx.scene.shape.Line
import javafx.scene.shape.Rectangle
import javafx.scene.shape.Shape
import javafx.stage.Stage
import javafx.util.Duration
/**
* See video and some comments on http://ladstatt.blogspot.com/
*/
object PlantSomeTrees {
def main(args: Array[String]): Unit = {
Application.launch(classOf[PlantSomeTrees], args: _*)
}
}
class PlantSomeTrees extends javafx.application.Application {
val canvasWidth = 800
val canvasHeight = 600
val treeDepth = 5
val trunkWidth = 7
val (minTreeSize, maxTreeSize) = (50, 80)
//val treeColor = Color.CHOCOLATE
def treeColor = mkRandColor
val initialDirection = (3 * Pi / 2)
val (minDegree, maxDegree) = (0, Pi / 4)
val growingSpeed = 96
val branchSlices = 10
override def start(stage: Stage): Unit = {
stage.setTitle("A growing forrest")
val root = new Group()
val background = {
val b = new Rectangle(0, 0, canvasWidth, canvasHeight)
val stops = List(new Stop(0, Color.BLACK), new Stop(1, Color.WHITESMOKE))
val g = new LinearGradient(0.0, 1.0, 0.0, 0.0, true, CycleMethod.NO_CYCLE, stops)
b.setFill(g)
b
}
root.getChildren.add(background)
root.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler[MouseEvent] {
def handle(event: MouseEvent) {
val broot = Branch(event.getX, event.getY, minTreeSize + (maxTreeSize - minTreeSize) * Random.nextDouble,
initialDirection, treeColor, trunkWidth, treeDepth)
var lines2Paint = traverse(mkRandomTree(broot)).toList
val tree = new Group()
tree.addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler[MouseEvent] {
def handle(event: MouseEvent) {
tree.setEffect(new Glow(1.0))
}
})
tree.addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler[MouseEvent] {
def handle(event: MouseEvent) {
tree.setEffect(new Glow(0))
}
})
root.getChildren.add(tree)
val growTimeline = new Timeline
growTimeline.setRate(growingSpeed)
growTimeline.setCycleCount(Animation.INDEFINITE)
growTimeline.getKeyFrames().add(
new KeyFrame(Duration.seconds(1),
new EventHandler[ActionEvent]() {
def handle(event: ActionEvent) {
if (!lines2Paint.isEmpty) {
val (hd :: tail) = lines2Paint
tree.getChildren.add(hd)
lines2Paint = tail
} else {
growTimeline.stop
}
}
}))
growTimeline.play()
}
})
stage.setScene(new Scene(root, canvasWidth, canvasHeight))
stage.show()
}
def mkRandDegree = (maxDegree - minDegree) * Random.nextDouble
def mkRandColor = {
def randInt = (Random.nextFloat * 255).toInt
Color.rgb(randInt, randInt, randInt)
}
sealed trait ATree
case class Branch(x: Double, y: Double, length: Double, degree: Double, color: Color, width: Int, ord: Int) extends ATree
case class SubTree(center: ATree, left: ATree, right: ATree) extends ATree
def mkRandomTree(root: Branch): ATree = {
def mkRandTree(tree: ATree): ATree =
tree match {
case Branch(x0, y0, length, d0, color, width, ord) => {
ord match {
case 0 => tree
case _ => {
val l0 = length * (1 - Random.nextDouble * 0.3)
val l1 = length * (1 - Random.nextDouble * 0.5)
val l2 = length * (1 - Random.nextDouble * 0.5)
// val l0 = length * 0.7
// val l1 = length * 0.5
// val l2 = length * 0.5
// startpoint of left and right branch
val (xm, ym) = (x0 + l0 * cos(d0), y0 + l0 * sin(d0))
val (d1, d2) = (d0 + mkRandDegree, d0 - mkRandDegree)
// val (d1, d2) = (d0 + Pi / 4, d0 - Pi / 4)
mkRandTree(SubTree(
Branch(x0, y0, length, d0, color, width, ord - 1), // trunk
Branch(xm, ym, l1, d1, color.darker, width - 1, ord - 1), // leftbranch
Branch(xm, ym, l2, d2, color.darker, width - 1, ord - 1))) // rightbranch
}
}
}
case SubTree(center, left, right) => SubTree(mkRandTree(center), mkRandTree(left), mkRandTree(right))
}
mkRandTree(root)
}
def traverse(tree: ATree): List[Shape] = {
tree match {
case Branch(x, y, l, d, c, width, ord) => mkLine(x, y, l, d, c, if (width < 1) 1 else width)
case SubTree(center, left, right) => traverse(center) ++ traverse(left) ++ traverse(right)
}
}
def mkLine(x: Double, y: Double, length: Double, degree: Double, paint: Paint, width: Int): List[Shape] = {
val (dc0, ds0) = (length / branchSlices * cos(degree), length / branchSlices * sin(degree))
(for (i <- 1 to branchSlices) yield {
val (x2, y2) = (x + i * dc0, y + i * ds0)
val l = new Line(x + (i - 1) * dc0, y + (i - 1) * ds0, x2, y2)
l.setStrokeWidth(width)
l.setStroke(paint)
l
}).toList
}
}


Saturday, January 12, 2013

Lets go green and plant some trees.

Every man should plant a tree in his lifetime. Or two. Or more.



The code:

package net.ladstatt.apps
import scala.collection.JavaConversions.seqAsJavaList
import scala.math.Pi
import scala.math.cos
import scala.math.sin
import scala.util.Random
import javafx.application.Application
import javafx.event.EventHandler
import javafx.scene.Group
import javafx.scene.Scene
import javafx.scene.effect.Glow
import javafx.scene.input.MouseEvent
import javafx.scene.paint.Color
import javafx.scene.paint.CycleMethod
import javafx.scene.paint.LinearGradient
import javafx.scene.paint.Paint
import javafx.scene.paint.Stop
import javafx.scene.shape.Line
import javafx.scene.shape.Rectangle
import javafx.stage.Stage
/**
* See video and some comments on http://ladstatt.blogspot.com/
*/
object PlantSomeTrees {
def main(args: Array[String]): Unit = {
Application.launch(classOf[PlantSomeTrees], args: _*)
}
}
class PlantSomeTrees extends javafx.application.Application {
val canvasWidth = 800
val canvasHeight = 600
val treeDepth = 6
val (minTreeSize, maxTreeSize) = (10, 35)
val treeColor = Color.CHOCOLATE
val initialDirection = 3 * Pi / 2
val (minDegree, maxDegree) = (Pi / 16, Pi / 4)
override def start(stage: Stage): Unit = {
stage.setTitle("A forrest")
val root = new Group()
val background = new Rectangle(0, 0, canvasWidth, canvasHeight)
val stops = List(new Stop(0, Color.BLACK), new Stop(1, Color.WHITESMOKE))
val g = new LinearGradient(0.0, 1.0, 0.0, 0.0, true, CycleMethod.NO_CYCLE, stops)
background.setFill(g)
root.getChildren.add(background)
root.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler[MouseEvent] {
def handle(event: MouseEvent) {
root.getChildren().addAll(mkTree(event.getX, event.getY, minTreeSize + (maxTreeSize - minTreeSize) * Random.nextDouble, initialDirection, treeDepth, treeColor))
}
})
stage.setScene(new Scene(root, canvasWidth, canvasHeight))
stage.show()
}
def mkRand = {
val r = Random.nextDouble
if (r < 0.5) r + 0.3 else r
}
def mkRandDegree = (maxDegree - minDegree) * Random.nextDouble
def mkTree(x: Double, y: Double, length: Double, degree: Double, ord: Int, color: Color): Group = {
def mkTree0(x00: Double, y00: Double, length: Double, d0: Double, ord: Int, color: Color): List[Line] = {
ord match {
case 0 => Nil
case _ => {
val (dc0, ds0) = (cos(d0), sin(d0))
val (x01, y01) = (x00 + length * dc0, y00 + length * ds0) // endpoint first stroke
val l0 = length * 0.7 * (1 + Random.nextDouble)
val l1 = length * 0.7 * (1 + Random.nextDouble)
val l2 = length * 0.7 * (1 + Random.nextDouble)
val (x10, y10) = (x00 + l0 * dc0, y00 + l0 * ds0) // startpoint second stroke
val d1 = d0 + mkRandDegree
val (dc1, ds1) = (cos(d1), sin(d1))
val (x11, y11) = (x10 + l1 * dc1, y10 + l1 * ds1) // endpoint second stroke
val d2 = d0 - mkRandDegree
val (dc2, ds2) = (cos(d2), sin(d2))
val (x12, y12) = (x10 + l2 * dc2, y10 + l2 * ds2) // endpoint third stroke
List(mkLine(x00, y00, x01, y01, ord, color),
mkLine(x10, y10, x11, y11, ord, color),
mkLine(x10, y10, x12, y12, ord, color)) ++
mkTree0(x01, y01, l0 * 0.6 * (1 + Random.nextDouble), d0, ord - 1, color.darker) ++
mkTree0(x11, y11, l1 * 0.5 * (1 + Random.nextDouble), d1, ord - 1, color.darker) ++
mkTree0(x12, y12, l2 * 0.5 * (1 + Random.nextDouble), d2, ord - 1, color.darker)
}
}
}
val g = new Group
g.getChildren().addAll(mkTree0(x, y, length, degree, ord, color))
g.addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler[MouseEvent] {
def handle(event: MouseEvent) {
g.setEffect(new Glow(0.8))
}
})
g.addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler[MouseEvent] {
def handle(event: MouseEvent) {
g.setEffect(new Glow(0))
}
})
g
}
def mkLine(x1: Double, y1: Double, x2: Double, y2: Double, width: Int, paint: Paint) = {
val l = new Line(x1, y1, x2, y2)
l.setStrokeWidth(width)
l.setStroke(paint)
l
}
}




Apart from creating a nice looking visual effect this application also demonstrates how easy it is to group graphic elements together and to bind an eventhandler to it. By using the ready made effects you can easily achieve a hoover effect for free. Maybe you play around with the parameters to create your own personal forrest. It gets quite addictive ;-)

You can also clone the project on the github page so you should be setup in no time. Just clone, change to directory, execute

mvn package 

and, after compiling, execute

java -jar target\plant-some-trees-with-javafx-1.0-SNAPSHOT-jfx.jar

Thanks for reading. Maybe you are also interested in the second part of the (organic) tree visualisation series.

Thursday, January 10, 2013

2D Water Effects with JavaFX and Scala

Join me on my way to a simple water effect using JavaFX and Scala like shown below.


Only recently I've discovered a very nice blog about game tutorials, in particular an article about a 2D water effect got my attention. It amazed me that you only need such simple arithmetic to get such a nice effect. You can read about the basic math at the original article,  see below for my first shot at an implementation in JavaFX.


package net.ladstatt.apps.watersimulation
import scala.collection.JavaConversions.seqAsJavaList
import javafx.animation.Animation
import javafx.animation.KeyFrame
import javafx.animation.Timeline
import javafx.application.Application
import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.scene.Scene
import javafx.scene.control.Button
import javafx.scene.effect.DropShadow
import javafx.scene.input.MouseEvent
import javafx.scene.layout.StackPane
import javafx.scene.paint.Color
import javafx.scene.paint.CycleMethod
import javafx.scene.paint.LinearGradient
import javafx.scene.paint.Stop
import javafx.scene.shape.Polygon
import javafx.stage.Stage
import javafx.util.Duration
/**
* A simple graphic demo in javafx
*
* Based on the ideas of http://gamedev.tutsplus.com/tutorials/implementation/make-a-splash-with-2d-water-effects/
*
* See also blog post at http://ladstatt.blogspot.co.at/2013/01/2d-water-effects-with-javafx-and-scala.html
*/
object WaterSimulation {
def main(args: Array[String]): Unit = {
Application.launch(classOf[WaterSimulation], args: _*)
}
}
class WaterSimulation extends javafx.application.Application {
val canvasWidth = 820
val canvasHeight = 600
val pointCount = 250
val margin = 20
val splash = 0.3
val targetHeight = canvasHeight / 2
case class Spring(position: Double, velocity: Double)
val hor = canvasHeight * 0.5
val splashPointCount = pointCount * splash
val displacement = hor * splash
val tension = 0.025
val dampening = 0.05
val spread = 0.25
val dx = (canvasWidth - 2 * margin) / pointCount
// see http://en.wikipedia.org/wiki/Panthalassa
val panthalassa = {
val seaLevel = (for (i <- 0 to ((pointCount - splashPointCount) / 2).toInt) yield Spring(hor, 0.toDouble)).toList
val splashPoints = (for (i <- 0 to splashPointCount.toInt) yield Spring(hor + displacement, 0.toDouble)).toList
seaLevel ++ splashPoints ++ seaLevel
}
var ocean = panthalassa
val corners = List(ocean.size * dx + margin, canvasHeight.toDouble) ++ List(margin, canvasHeight.toDouble)
def mkPoints(springs: List[Spring]) =
springs.zipWithIndex.map { case (Spring(pos, velocity), idx) => List((idx * dx + margin).toDouble, pos) }.flatten.toList ++ corners
val polys = {
val p = new Polygon(mkPoints(ocean): _*)
val stops = List(new Stop(0, Color.BLACK), new Stop(1, Color.BLUE))
val g = new LinearGradient(0, 1, 0, 0, true, CycleMethod.NO_CYCLE, stops)
p.setFill(g)
p.setEffect(new DropShadow())
p
}
def updateOcean(spread: Double, tension: Double, dampening: Double, ocean: List[Spring]): List[Spring] = {
val dampedOcean = dampAndTense(ocean, dampening, tension)
val (lefts, rights) = deltas(hor, spread, dampedOcean)
val fasterOcean = changeVelocity(dampedOcean, lefts, rights)
changePos(fasterOcean, lefts, rights)
}
def dampAndTense(ocean: List[Spring], dampening: Double, tension: Double): List[Spring] = {
ocean.map {
case Spring(pos, speed) => {
val x = pos - hor
val newSpeed = -tension * x + speed - speed * dampening
val newPos = pos + newSpeed
Spring(newPos, newSpeed)
}
}
}
def deltas(offset: Double, spread: Double, springs: List[Spring]): (List[Double], List[Double]) = {
val normedSprings = springs.map(_.position - offset)
((for (List(a, b) <- normedSprings.sliding(2)) yield spread * (b - a)).toList ::: List(0.toDouble),
0.toDouble :: (for (List(d, c) <- normedSprings.reverse.sliding(2)) yield spread * (c - d)).toList.reverse)
}
def changeVelocity(springs: List[Spring], left: List[Double], right: List[Double]): List[Spring] =
{
val velocities = left zip right map { case (a, b) => a + b }
springs zip velocities map { case (s, l) => s.copy(velocity = s.velocity + l) }
}
def changePos(springs: List[Spring], left: List[Double], right: List[Double]): List[Spring] =
{
val leftSprings = springs zip left map { case (s, l) => s.copy(position = s.position + l) }
leftSprings zip right map { case (s, l) => s.copy(position = s.position + l) }
}
override def start(primaryStage: Stage): Unit = {
primaryStage.setTitle("Almost a water simulation");
val root = new StackPane()
val b = new Button("splash!")
val timeline = new Timeline
timeline.setRate(48)
timeline.setCycleCount(Animation.INDEFINITE)
timeline.getKeyFrames().add(
new KeyFrame(Duration.seconds(1),
new EventHandler[ActionEvent]() {
def handle(event: ActionEvent) {
val newOcean = updateOcean(spread, tension, dampening, ocean)
polys.getPoints().setAll(mkPoints(newOcean).map(Double.box(_)))
ocean = newOcean
}
}))
b.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler[MouseEvent] {
def handle(event: MouseEvent) {
b.setVisible(false)
timeline.play
}
})
root.getChildren.addAll(polys, b)
primaryStage.setScene(new Scene(root, canvasWidth, canvasHeight))
primaryStage.show()
}
}
It is nice to see how much work JavaFX does behind the scenes - you just have to make sure your polygon has the right coordinate list, all the stuff needed for painting is done behind the scenes. By using a simple gradient the water looks nice, too.

Update:


I've pushed the whole project setup to github.

You may notice a small glitch at the beginning of the animation. Some minutes ago I've installed OpenJDK8 early access and started the application without recompiling, and it went away. Why buy new hardware? Just install new VMs ;-)

Monday, January 7, 2013

A sine wave with JavaFX and Scala

My goal is to create a JavaFX application which displays a sine wave like shown in the video below:


Ok.

Here is the source:


package net.ladstatt.apps.watersimulation
import javafx.application.Application
import javafx.fxml.FXML
import javafx.fxml.FXMLLoader
import javafx.fxml.Initializable
import javafx.fxml.JavaFXBuilderFactory
import javafx.scene.Parent
import javafx.scene.Scene
import javafx.scene.layout.StackPane
import javafx.stage.Stage
import java.net.URL
import java.util.ResourceBundle
import javafx.scene.Group
import javafx.scene.image.ImageView
import javafx.scene.image.WritableImage
import javafx.scene.paint.Color
import javafx.scene.control.Button
import javafx.scene.input.MouseEvent
import javafx.event.EventHandler
import javafx.scene.control.Tooltip
import javafx.scene.shape.Polygon
import javafx.scene.paint.Paint
import javafx.animation.Timeline
import javafx.animation.Animation
import javafx.animation.KeyFrame
import javafx.util.Duration
import javafx.event.ActionEvent
import javafx.scene.paint.Stop
import javafx.scene.paint.LinearGradient
import javafx.scene.paint.CycleMethod
import scala.collection.JavaConversions._
import javafx.scene.effect.DropShadow
object MovingSineWave {
def main(args: Array[String]): Unit = {
Application.launch(classOf[MovingSineWave], args: _*)
}
}
class MovingSineWave extends javafx.application.Application {
val canvasWidth = 640
val canvasHeight = 400
val pointCount = 300
val margin = 20
val targetHeight = canvasHeight / 2 // height of the wave
val hor = canvasHeight * 0.5
val displacement = hor * 0.7
val dx = (canvasWidth - 2 * margin) / pointCount
val sinVals =
(for (i <- 0 to pointCount) yield {
if (i == 0) 0 else Math.sin(2 * Math.PI / pointCount * i)
}).toList
var sinMutable = sinVals.toList
val corners = List(pointCount * dx + margin, canvasHeight.toDouble) ++ List(margin, canvasHeight.toDouble)
def mkPoints(sins: List[Double]) = sins.zipWithIndex.map { case (sv, idx) => List((idx * dx + margin).toDouble, (sv * displacement + hor)) }.flatten.toList ++ corners
val polys = {
val p = new Polygon(mkPoints(sinVals): _*)
val stops = List(new Stop(0, Color.BLACK), new Stop(1, Color.BLUE))
val g = new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, stops)
p.setFill(g)
p.setEffect(new DropShadow())
p
}
override def start(primaryStage: Stage): Unit = {
primaryStage.setTitle("Not yet a water simulation");
val root = new StackPane()
val timeline = new Timeline
timeline.setRate(48)
timeline.setCycleCount(Animation.INDEFINITE)
timeline.getKeyFrames().add(
new KeyFrame(Duration.seconds(1),
new EventHandler[ActionEvent]() {
def handle(event: ActionEvent) {
val (hd :: tail) = sinMutable
sinMutable = tail ::: List(hd)
polys.getPoints.setAll(mkPoints(sinMutable).map(Double.box(_)))
}
}))
timeline.play
root.getChildren.add(polys)
primaryStage.setScene(new Scene(root, canvasWidth, canvasHeight))
primaryStage.show()
}
}

Cut'n paste this to your scala-javafx-maven archetype and off you go. 

Friday, January 4, 2013

Compile OpenJFX RT on MacOsX

This time I want to describe what was necessary for me to compile the OpenJFX Project on my laptop, a MacBook Pro running Mountain Lion.
Quoting from the page:
As you can imagine, building a UI toolkit for many different platforms is quite complex. It requires platform specific tools such as C compilers as well as portable tools like ant. The build and project structure of JavaFX like most large projects has evolved over time and is constantly being revised.

I'm writing this early January 2013, maybe things will change in the future. I would love to see a mavenized build for OpenJFX, or even for the whole JDK which would increase adoption tremendously for the whole OpenJDK in my opinion.   

Step 1 to 10 - Checkout the source and do the rest

Mercurial, java, and ant should be installed, then the getting started guide gives you following recipe:

Here is the recipe for building OpenJFX (use a Cygwin shell on Windows):

  • Download the latest JavaFX Developer Preview binary
  • Unzip the binary and put it in ~/closed-jfx
  • mkdir -p ~/open-jfx
  • cd ~/open-jfx
  • hg clone http://hg.openjdk.java.net/openjfx/2.1/master
  • cd master
  • mkdir -p artifacts/sdk/rt
  • cp -r ~/closed-jfx/javafx-sdk2.1.0-beta/rt artifacts/sdk
  • hg clone http://hg.openjdk.java.net/openjfx/2.1/master/rt
  • cd rt
  • Edit build-defs.xml (comment out '<propertycopy name="javac.debuglevel" from="${ant.project.name}.javac.debuglevel" silent="true" override="true"/>')
  • cd javafx-ui-controls
  • ant
..... One hour later: I've followed those instructions, but got in trouble with step one (bravely continued with step 2, though). Searched for a macos binary (didn't know what to search for really?). Decided to go on. Sadly enough, I got stuck some time later with compile errors (Abstract methods not implemented by ... ?! well - this was rather strange.)

But ...


Just before giving it up, I've found after some googling the website of Peter Pilgrim, who just briefly mentioned that the getting started guide was somewhat outdated and after following his instructions I happily compiled openjfx the first time on my laptop!

For further reference, here are the necessary steps to compile OpenJFX:

mkdir openjfx-2.2/
cd openjfx-2.2/
hg clone http://hg.openjdk.java.net/openjfx/2.2/master
cd master
mkdir -p artifacts/sdk/rt/lib
cp $JAVA_HOME/jre/lib/jfxrt.jar artifacts/sdk/rt/lib
hg clone http://hg.openjdk.java.net/openjfx/2.2/master/rt
cd rt
-> edit common.properties, set property javac.debuglevel=lines,vars,source
ant clean dist


The story doesn't end here, in fact I've discovered that there are already efforts on mavenizing openjfx, at least I've found a pom.xml in the rt subdirectory.

With
hg log pom.xml
I've found out that in RT-19825 Adam Bien contributed those pom files.

After commenting the system dependency for the jfxrt.jar (since I've copied the jfxrt.jar into the jre/lib/ext directory by using zonskis maven fix classpath plugin) it happily compiled (by skipping the tests ) using following command :

mvn clean package -Dmaven.test.skip

(Disclaimer: I don't know if the maven build does the same as the ant build does - after inital commit those files seem to be untouched.)

Bottom line for me is that building/compiling (parts of/all of??) OpenJFX was easier than expected. Although I'm sure that this is not the end of the story. I would suspect that the native stuff wasn't compiled?! At least in the 'rt' directory there are only java sources...

The answer to this seems to be that this was only a small part of OpenFX. If you look at the Mercurial index page there are lots of different versions and components for OpenFX:


As you can see, I was only in one sub module of the whole game, but I hope you got the idea how to check out and build the project - it is as simple as described above!

For example, have a look in the openjfx 2.2.6 development:



mkdir openjfx-2.2.6/
cd openjfx-2.2.6/
hg clone http://hg.openjdk.java.net/openjfx/2.2.6/master
cd master
mkdir -p artifacts/sdk/rt/lib
cp $JAVA_HOME/jre/lib/jfxrt.jar artifacts/sdk/rt/lib
hg clone http://hg.openjdk.java.net/openjfx/2.2.6/master/rt
cd rt
-> edit common.properties, set property javac.debuglevel=lines,vars,source
ant clean dist

For the JDK8 branch the procedure works as described, but make sure you set your path to a JDK8. You could of course compile this also from source, but it's maybe easier to get a full JDK8 early access build from here.


ant clean dist for OpenFX-8 (in green)

Small update:

Only recently the structure of the repositories was explained a little bit, quoting Kevin Rushforth:

Each of the following should be treated as a separate forest. You would only grab one of these forests depending on which one you want.
1. The controls team forest:
openjfx/8/controls   openjfx/8/controls/rt   openjfx/8/controls/tests
2. The graphics team forest:
openjfx/8/graphics   openjfx/8/graphics/rt   openjfx/8/graphics/tests
3. The master forest:
openjfx/8/master   openjfx/8/master/rt   openjfx/8/master/tests
The team forests is where the work happens. Each team integrates into the master forest regularly (typically weekly).

Update February 2013:


Note: This tutorial was written early 2013, maybe in the meantime things have changed. There are efforts going on to restructure the build using gradle scripts, so maybe things have changed. This wiki page should be up to date and reflecting the current status of the build.

Have also a look at this video (after reading the whole blog post ;-) on how to do it:




This video shows that compiling JavaFX from source is just some clicks away. 

Wednesday, January 2, 2013

Testing with JemmyFX, JavaFX and ScalaTest

To ease the burden of the testing team, developers should always write automated tests. For JavaFX, there is a library called "JemmyFX" which provides an API to write such tests.


Harold Kantner Special Collection Photo [Photo]
Harold Kantner Special Collection Photo

Using Scala and Scalatest, your tests for JavaFX could look like this:

package org.example.scalajfx
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.FlatSpec
import org.jemmy.fx.SceneDock
import org.jemmy.fx.AppExecutor
import org.jemmy.fx.control.ToggleButtonDock
import org.jemmy.fx.control.LabeledDock
class JavaFXExampleSpec extends FlatSpec with ShouldMatchers {
AppExecutor.executeNoBlock(classOf[JfxApplication])
val scene = new SceneDock
"a button" must "be present." in {
val b = new LabeledDock(scene.asParent)
b.asText().text() should be ("Click Me")
}
}


At the moment of writing this blog post, there was no maven artifact release, but in the Sonatype Snapshot Repository there is one. As such, you would have to add the snapshot repository to your pom and add folowing dependencies to your pom:


<!-- for testing javafx applications -->
<dependency>
<groupId>net.java.jemmy</groupId>
<artifactId>JemmyFX</artifactId>
<version>0.9.3-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.java.jemmy</groupId>
<artifactId>JemmyFXBrowser</artifactId>
<version>0.9.3-SNAPSHOT</version>
 <scope>test</scope>
</dependency>
<dependency>
<groupId>net.java.jemmy</groupId>
<artifactId>Jemmy3Core</artifactId>
<version>0.9.3-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.java.jemmy</groupId>
<artifactId>Jemmy3AWT</artifactId>
<version>0.9.3-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.java.jemmy</groupId>
<artifactId>Jemmy3AWTInput</artifactId>
<version>0.9.3-SNAPSHOT</version>
<scope>test</scope>
</dependency>


A production build should not have dependencies to snapshots of course.

In order for maven to find those artifacts, you have to add the Snapshot Repository to your list of known repositories:


<repositories>
  <repository>
    <id>sonatype snapshots</id>
    <snapshots>
<enabled>true</enabled>
    </snapshots>
    <releases>
<enabled>false</enabled>
    </releases>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
  </repository>
</repositories>



JemmyFX is under heavy development at the moment, but I'm positive a stable release will get published soon. In the meantime the dependencies above give you the ability to implement JemmyFX based tests for your project. Have also a look here for some Java examples how to use JemmyFX.

The scala-javafx-archetype repository at github provides a preconfigured project using the technologies mentioned above.

Small update:


Maybe you want to have a look at the tests for JavaFX for JDK 8 - not long ago the SQE Tests were open sourced here.


nxsw031mac:tests lad$ hg pull
Rufe von http://hg.openjdk.java.net/openjfx/8/master/tests ab
Suche nach Änderungen
Füge Änderungssätze hinzu
Füge Manifeste hinzu
Füge Dateiänderungen hinzu
Fügte 18 Änderungssätze mit 1463 Änderungen an 661 Dateien hinzu
(führe "hg update" aus, um ein Arbeitsverzeichnis zu erstellen)
nxsw031mac:tests lad$ hg update
477 Dateien aktualisiert, 0 Dateien zusammengeführt, 1 Dateien entfernt, 0 Dateien ungelöst


!