Steve On Java

Hacking JavaFX and Java with Agility
  • rss
  • Home
  • About
  • Contact
  • SvJugFX
  • Prize Spinner

30 Lines of JavaFX

steveonjava | September 29, 2009

What can you do in 30 lines of JavaFX?  JFXStudio is running a contest right now to find this out…

http://jfxstudio.wordpress.com/2009/08/31/jfxstudio-challenge-small-is-the-new-big/

I strongly encourage you to give it a try.  So much so that I took a quick stab at putting together a sample myself this evening.  Welcome to the Time Wheel:

Please enable Javascript and Flash to view this Blip.tv video.

Everything is rendered using JavaFX Shape and Text primitives with clever PerspectiveTransform effects applied.  I also made use of an overlay gradient Flooded and Blended on an outer group in order to create the top and bottom shadow giving it a rounded 3d appearance.

Below is the full code for the application (clearly I was going for the 3000 character limit).  The formatted version was about 200 lines long and over 4000 characters, but by shortening variable names, removing white space, and merging lines, I was able to bring it down substantially.  This is definitely not a recommended coding practice, but met the contest constraints:

import javafx.scene.*;import javafx.scene.effect.*;import javafx.scene.layout.*;import javafx.scene.paint.*;import javafx.scene.paint.Color.*;
import javafx.scene.shape.*;import javafx.scene.text.*;import javafx.stage.*;import javafx.util.Math;import javafx.animation.*;import javafx.scene.media.*;
var ez=Interpolator.EASEBOTH;class Slot{var n:Node[];var v:Number on replace{
Timeline{keyFrames:at(.9s){c => v tween ez}}.play();a();}
var a:function();var c:Number;var e:String[];var s:Number;var f:Number;
init{var entryHeight=bind Math.sin(Math.PI/sizeof e)*h;n=for(entry in e)Stack{
var textLine:Text;content:[Group{var w1=bind Math.max(w,textLine.boundsInLocal.width);
content:[Rectangle{width:bind w1,height:bind h/12,fill:bind if(indexof entry mod 3==0){LIGHTGRAY} else if(indexof entry mod 3==1){DARKGRAY}
else{hsb(360*Math.abs((indexof entry*2-sizeof e as Number)/sizeof e),.4,.7)}}]}
textLine=Text{content:bind entry font:bind Font.font(null,h/12*.75)}]
visible:bind Math.abs(indexof entry-(c mod sizeof e))<=(sizeof e as Number)/4 or Math.abs(indexof entry-(c mod sizeof e))>=(sizeof e as Number)*3/4
effect:PerspectiveTransform{var sw=bind 360.0/sizeof e;var a=bind(indexof entry-c mod sizeof e)/sizeof e*360 + 90-sw/2;def sr=bind Math.toRadians(a + 90);
def er=bind Math.toRadians(a + 90 + sw);def r=bind h/2;var uy=bind r + Math.sin(er)*r;uly:bind uy ury:bind uy var ly=bind r + Math.sin(sr)*r;
lly:bind ly lry:bind ly ulx:bind Math.cos(er)*r*s;llx:bind Math.cos(sr)*r*s;urx:bind w-Math.cos(er)*r*f;lrx:bind w-Math.cos(sr)*r*f;}}}}
var x=45;var y=10;var w=24;var h=200;var ss=for(i in [1..9])Slot{e:for(j in [0..9])"{j}"s:.3-indexof i*.07 f:-.25 + indexof i*.07}
var so=MediaPlayer{media:Media{source:"http://jfxtras.org/sounds/beep.wav";}}
var bg:Color;ss[5].a=function(){so.stop();so.currentTime=0s;so.play();
Timeline{keyFrames:[at(50ms){bg => GREEN tween ez}at(150ms){bg => BLACK tween ez}]}.play();}
var d:java.util.Date;
Timeline{repeatCount:Timeline.INDEFINITE keyFrames:KeyFrame{time:1ms canSkip:true action:function(){d=new java.util.Date();
ss[8].c=java.lang.System.currentTimeMillis()mod 10;ss[7].c=(java.lang.System.currentTimeMillis()mod 100 as Number)/10;
ss[6].c=(java.lang.System.currentTimeMillis()mod 1000 as Number)/100;ss[5].v=d.getSeconds()mod 10;ss[4].v=d.getSeconds()/10;
ss[3].v=d.getMinutes()mod 10;ss[2].v=d.getMinutes()/10;ss[1].v=d.getHours()mod 10;ss[0].v=d.getHours()/10;}}}.play();
Stage{width:400 height:300 title:"Time Wheel"scene:Scene{fill:bind bg content:Group{content:[for(s in ss)Panel{
translateX:x + indexof s*30 +(Math.min(indexof s,7)/2)*10 translateY:y content:s.n height:h width:w}Text{content:bind "The time is now: {d}",fill:WHITE,x:58,y:242}]
effect:Blend{mode:BlendMode.SRC_ATOP topInput:Flood{paint:LinearGradient{endX:0 stops:[
Stop{offset:0,color:color(0,0,0,.9)}Stop{offset:.3,color:TRANSPARENT}Stop{offset:.7,color:TRANSPARENT}Stop{offset:1,color:color(0,0,0,.9)}]}
y:y width:400 height:h}}}}}

There is still time to get your own submission in for the contest. The official deadline is Wednesday at midnight, so with a little hard work and determination you can easily crank out 30 lines of JavaFX goodness!

Update:  By popular demand, here is a cleanly formatted version of the same code:

import javafx.scene.*;
import javafx.scene.effect.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.paint.Color.*;
import javafx.scene.shape.*;
import javafx.scene.text.*;
import javafx.stage.*;
import javafx.util.Math;
import javafx.animation.*;
import javafx.scene.media.*;

class Slot {
    var node:Node[];
    var value:Number on replace {
            Timeline {keyFrames: at(.9s) {center =&gt; value tween Interpolator.EASEBOTH}}.play();
        action();
    }
    var action:function();
    var center:Number;
    var entries:String[];
    var start:Number;
    var finish:Number;

    init {
        var entryHeight = bind Math.sin(Math.PI / sizeof entries) * height;
        node = for (entry in entries) Stack {
            var textLine: Text;
            content: [
                Group {
                    var w1 = bind Math.max(width,textLine.boundsInLocal.width);
                    content: [
                        Rectangle {
                            width: bind w1
                            height: bind height/12
                            fill: bind if (indexof entry mod 3 == 0) {
                                LIGHTGRAY
                            } else if (indexof entry mod 3 == 1) {
                                DARKGRAY
                            } else {
                                hsb(360 * Math.abs((indexof entry*2-sizeof entries as Number) / sizeof entries), .4, .7)
                            }
                        }
                    ]
                }
                textLine = Text {
                    content: bind entry
                    font: bind Font.font(null, height / 12 * .75)
                }
            ]
            visible: bind Math.abs(indexof entry - (center mod sizeof entries)) &lt;= (sizeof entries as Number) / 4 or Math.abs(indexof entry - (center mod sizeof entries)) &gt;= (sizeof entries as Number) * 3/4
            effect: PerspectiveTransform {
                var sweep = bind 360.0 / sizeof entries;
                var action = bind(indexof entry - center mod sizeof entries) / sizeof entries * 360 + 90 - sweep/2;
                def startRadians = bind Math.toRadians(action + 90);
                def endRadians = bind Math.toRadians(action + 90 + sweep);
                def radius = bind height/2;
                var uy = bind radius + Math.sin(endRadians) * radius;
                uly: bind uy
                ury: bind uy
                var ly = bind radius + Math.sin(startRadians) * radius;
                lly: bind ly
                lry: bind ly
                ulx: bind Math.cos(endRadians) * radius * start
                llx: bind Math.cos(startRadians) * radius * start
                urx: bind width-Math.cos(endRadians)*radius*finish
                lrx: bind width-Math.cos(startRadians)*radius*finish
            }
        }
    }
}
var x = 45;
var y = 10;
var width = 24;
var height = 200;
var slots = for(i in [1..9]) Slot {
    entries: for(j in [0..9]) &quot; {j}&quot;
    start: .3 - indexof i * .07
    finish: -.25 + indexof i * .07
}
var beep = MediaPlayer {
    media: Media {
        source: &quot;http://jfxtras.org/sounds/beep.wav&quot;;
    }
}
var bgColor: Color;
slots[5].action = function() {beep.stop();
    beep.currentTime = 0s;
    beep.play();
    Timeline {
        keyFrames: [
            at(50ms) {bgColor =&gt; GREEN tween Interpolator.EASEBOTH}
            at(150ms) {bgColor =&gt; BLACK tween Interpolator.EASEBOTH}
        ]
    }.play();
}
var d: java.util.Date;

Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames: KeyFrame {
        time: 1ms
        canSkip: true
        action: function() {
            d = new java.util.Date();
            slots[8].center = java.lang.System.currentTimeMillis() mod 10;
            slots[7].center = (java.lang.System.currentTimeMillis() mod 100 as Number) / 10;
            slots[6].center = (java.lang.System.currentTimeMillis() mod 1000 as Number) / 100;
            slots[5].value = d.getSeconds() mod 10;
            slots[4].value = d.getSeconds() / 10;
            slots[3].value = d.getMinutes() mod 10;
            slots[2].value = d.getMinutes() / 10;
            slots[1].value = d.getHours() mod 10;
            slots[0].value = d.getHours() / 10;
        }
    }
}.play();

Stage {
    width: 400
    height: 300
    title: &quot;Time Wheel&quot;
    scene: Scene {
        fill: bind bgColor
        content: Group {
            content: [
                for(start in slots) Panel {
                    translateX: x + indexof start*30 +(Math.min(indexof start,7)/2)*10
                    translateY: y content: start.node height: height width: width
                }
                Text {
                    content: bind &quot;The time is now: {d}&quot;
                    fill: WHITE
                    x: 58
                    y: 242
                }
            ]
            effect: Blend {
                mode: BlendMode.SRC_ATOP
                topInput: Flood {
                    paint: LinearGradient {
                        endX: 0
                        stops: [
                            Stop {offset: 0, color: color(0,0,0,.9)}
                            Stop {offset: .3, color: TRANSPARENT}
                            Stop {offset: .7, color: TRANSPARENT}
                            Stop {offset: 1, color: color(0,0,0,.9)}
                        ]
                    }
                    y: y
                    width: 400
                    height: height
                }
            }
        }
    }
}
 
Categories
JavaFX, contest
Tags
contest, JavaFX, jfxstudio, time
Comments rss
Comments rss
Trackback
Trackback

« WidgetFX Contest Winners! Hanging out on the Ranch »

9 Responses to “30 Lines of JavaFX”

  1. Iban says:
    September 29, 2009 at 4:24 am

    There is an error in line 10? (unexpected type)

    I can’t run the script :-(

    Reply
  2. Mi says:
    September 29, 2009 at 6:57 am

    1000 chars difference all from white space n variable names! That’s 25% of the total char count

    Reply
  3. PhiLho says:
    September 29, 2009 at 8:34 am

    Copy/paste in a file, I get a compilation error at line 11 (which is 10 in your curious code area starting to number lines at 0!):
    Test.fx:11: unexpected type
    required: variable
    found : value
    visible:bind Math.abs(indexof entry-(c mod sizeof e))=(sizeof e as Number)*3/4
    ^
    Note: Test.fx uses or overrides a deprecated API.
    Note: Recompile with -Xlint:deprecation for details.
    1 error

    Is that JavaFX 1.1 or before?

    Oh, and the Click To Play link goes to a .flv file!

    Beside, it looks nice, well done!

    Reply
  4. Matthew says:
    September 29, 2009 at 2:33 pm

    Impressive stuff. Any chance you could post a formatted version of the source code?

    Reply
  5. Java desktop links of the week, October 5 | Jonathan Giles says:
    October 4, 2009 at 3:09 pm

    [...] winner has been announced.. A number of people have posted their code for the challenge, including Stephen Chin, Carl Dea, Muhammad Hakim, Sergey Surikov, Vinu and Philippe [...]

    Reply
  6. steveonjava says:
    September 29, 2009 at 8:57 am

    The dog ate my homework!!! (there were some signs that got nuked in the conversion) The sample code is fixed and tested, give it another try…

    Reply
  7. steveonjava says:
    September 29, 2009 at 8:57 am

    What can I say, I like descriptive variable names. :)

    Reply
  8. steveonjava says:
    September 29, 2009 at 8:58 am

    Thanks for the feedback. Please see my comment above about the copy/paste errors (it should work now).

    Reply
  9. steveonjava says:
    September 30, 2009 at 3:16 am

    It was a pain, but I reconstituted something close to the original. Enjoy!

    Reply

Leave a Reply

Click here to cancel reply.

Publications

The most comprehensive book on the JavaFX Platform!

Free Refcard covering JavaFX 1.2

Categories

  • Announcements (11)
  • contest (3)
  • Events (14)
  • JavaFX (29)
  • JavaFX Mobile (1)
  • jfxtras (9)
  • Liferay (2)
  • presentation (4)
  • pro javafx (2)
  • SvJugFx (6)
  • Uncategorized (21)
  • Video (2)
  • widgetfx (9)

Affiliations

Awards

2009 JavaOne Rock Star!

rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox