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:
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 => 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)) <= (sizeof entries as Number) / 4 or Math.abs(indexof entry - (center mod sizeof entries)) >= (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]) " {j}"
start: .3 - indexof i * .07
finish: -.25 + indexof i * .07
}
var beep = MediaPlayer {
media: Media {
source: "http://jfxtras.org/sounds/beep.wav";
}
}
var bgColor: Color;
slots[5].action = function() {beep.stop();
beep.currentTime = 0s;
beep.play();
Timeline {
keyFrames: [
at(50ms) {bgColor => GREEN tween Interpolator.EASEBOTH}
at(150ms) {bgColor => 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: "Time Wheel"
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 "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: height
}
}
}
}
}






There is an error in line 10? (unexpected type)
I can’t run the script
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…
1000 chars difference all from white space n variable names! That’s 25% of the total char count
What can I say, I like descriptive variable names.
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!
Thanks for the feedback. Please see my comment above about the copy/paste errors (it should work now).
Impressive stuff. Any chance you could post a formatted version of the source code?
It was a pain, but I reconstituted something close to the original. Enjoy!
[...] 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 [...]