[home]   [me]   [my gallery]   [my blog]   [my articles]   [my code and projects]   [my links]
 

Pie chart in JavaFX Script


This JavaFX Applet shows my first application created in JavaFX script. The applet is actually a reimplementation of my a pie chart Applet that I created using Java Scenegraph. Since JavaFX Script is using Java Scenegraph, the result of the two applets are very close, but if you look at source code, you will notice several differences.

In this blog I'll not go further into explaining these differences, but instaid let the code talk for itself :-)

PieChart.fx

package com.mortennobel.fxdemo.piechart;

import javafx.scene.*;
import javafx.scene.geometry.*;
import javafx.scene.text.*;
import javafx.scene.paint.*;
import javafx.animation.*;
import javafx.input.*;

import java.util.*;
import java.text.*;
import java.lang.Math;

/**
* A simple PieChart example
*/
public class PieChart extends CustomNode{
public attribute width : Integer = 500;
public attribute height : Integer = 500;

public function create(): Node {
// todo add support for dynamic data
var data : Map = new LinkedHashMap();
data.put("Mondays",321.0);
data.put("Tuesdays",399.0);
data.put("Wednesdays",208.0);
data.put("Thursdays",109.0);
data.put("Fridays",609.0);
data.put("Saturdays",709.0);
data.put("Sundays",582.0);

var headline : String = "Website hits per day";
var numberFormat : NumberFormat = new DecimalFormat("0 Hits (Click to select)");

var group: Group = createPieChart(data,headline, numberFormat);
return Group{
content:[
group
]
}
}

public function createPieChart(data : Map, headline : String , numberFormatter : NumberFormat ): Group{
var sum : Number = 0.0;

var groupNodes : Node[];

var dataValue : java.lang.Object[] = data.values().toArray();
for (i in dataValue ){
sum += i as Number
}

// add headline
var headlineNode : Text = createHeadline(headline);
insert headlineNode into groupNodes;

// todo add tooltip
var tooltip : PieChartTooltipText = PieChartTooltipText{}

var currentsum : Number = 0;
var g :Group;

var dataKeys : java.lang.Object[] = data.keySet().toArray();
for (i in dataKeys){
var name:String = i as String;
var value : Number = data.get(name) as Number;

var labelNode:Text = createLabelNode(name,Math.PI*2*((currentsum+value/2)/sum));
insert labelNode into groupNodes;

var tooltipString : String = numberFormatter.format(value);

// create arc shape
var arcShape:PieChartArc = PieChartArc{
currentsum:currentsum
sum:sum
value:value
// show tooltip on mouse over and set the correct text in tooltip
onMouseEntered:function(e:MouseEvent) {
tooltip.text = tooltipString;
tooltip.toogleAnimation();
};
onMouseExited:function(e:MouseEvent) {
tooltip.toogleAnimation();
};
}

insert arcShape into groupNodes;

var sourceAngle : Number = arcShape.animatedRotation;
var destAngle : Number = 360.0*((currentsum/sum)+(value/sum));

// create initial animation, where each arc moves to their final destination
var arcTimeline : Timeline = Timeline {
var begin = at (0s){
arcShape.animatedRotation => sourceAngle
},

var end = at (2s) {
arcShape.animatedRotation => destAngle tween Interpolator.EASEBOTH
}
keyFrames: [begin,end]
}
arcTimeline.start();

currentsum += value;
}
insert tooltip into groupNodes;
return Group{
content:groupNodes
translateX: width/2
translateY: height/2
// move tooltip on mouse movement
onMouseMoved: function(e:MouseEvent) {
tooltip.translateX = e.getX()+tooltip.getBoundsWidth()/2;
tooltip.translateY = e.getY()-20;
};
}
}

function createHeadline(text : String) : Text{
return Text{
font: Font{
name: "Dialog"
size: 20
style:FontStyle.BOLD
}
fill : Color.WHITE
textOrigin:TextOrigin.TOP
content: text
smooth: true
horizontalAlignment:HorizontalAlignment.CENTER
y: -180
}
}

function createLabelNode(text:String , angleRadian:Number) : Text{
var cosAngle : Number = Math.cos(angleRadian);
var sinAngle : Number = Math.sin(angleRadian);
var textDistance : Number = 135;
return Text{
content:text
font:Font{
name: "Dialog"
size: 12
style:FontStyle.PLAIN
}
smooth: true
// text-alignment depends on where the text is located
horizontalAlignment:
if (cosAngle<-0.5){
HorizontalAlignment.RIGHT
}else if (cosAngle>0.5){
HorizontalAlignment.LEFT
} else {
HorizontalAlignment.CENTER
}
verticalAlignment:
if (sinAngle<-0.5){
VerticalAlignment.BOTTOM
}else if (sinAngle>0.5){
VerticalAlignment.TOP
}else{
VerticalAlignment.CENTER
}
x : cosAngle*textDistance
y : sinAngle*textDistance
};
}
}

PieChartArc.fx

package com.mortennobel.fxdemo.piechart;

import javafx.scene.*;
import javafx.scene.geometry.*;
import javafx.scene.text.*;
import javafx.scene.paint.*;
import javafx.animation.*;
import javafx.input.*;
import javafx.scene.transform.Transform;

import java.util.*;
import java.text.*;
import java.lang.Math;

/**
* Pie chart arc
*/
public class PieChartArc extends CustomNode{

public attribute sum : Number;
public attribute value : Number;
public attribute currentsum : Number;

// The animatedRotation binds to the rotation of the arc
public attribute animatedRotation : Number = 0;

// The selected binds to how long from center an arc is displayed
public attribute selected : Number = 0;

public attribute paintOpaciy : Number = 0.5;
public attribute movementOpacity : Number = 0.5;

public function create(): Node {
var start : Number = 0;
var end : Number = 360.0*value/sum;
var color: Color = fractionToColor(currentsum/sum);
var initialRotation = 360.0*value/2/sum;
var CLICK_MOVEMENT_DISTANCE : Number = 25;
animatedRotation = initialRotation;

// create the arc shape
var arc : Arc = Arc{
radiusX: 100
radiusY: 100
startAngle: start
length: end
type: ArcType.ROUND
fill: radialGradientPaint(color)
opacity: bind Math.max(paintOpaciy,movementOpacity)
stroke: Color.GRAY
smooth:true
rotate: bind animatedRotation
}

// create the arc mouse over hover effect
var gradientPaint : Timeline = Timeline{
var beginFrame = at (0s){
paintOpaciy => 0.5
},

var endFrame = at (0.5s) {
paintOpaciy => 1 tween Interpolator.LINEAR
}
keyFrames: [beginFrame,endFrame]
autoReverse:true
toggle:true
}

// create the arc selection effect
var clickMovement : Timeline = Timeline{
var beginFrame = at (0s){
selected => 0.0;
arc.stroke => Color.GRAY;
movementOpacity => 0.5;
},

var endFrame = at (0.25s) {
selected => CLICK_MOVEMENT_DISTANCE tween Interpolator.EASEBOTH
arc.stroke => Color.WHITE tween Interpolator.LINEAR
movementOpacity => 1 tween Interpolator.LINEAR
}

keyFrames: [beginFrame,endFrame]
autoReverse:true
toggle:true
}

return Group{
content:[arc]
translateX: bind Math.cos(((animatedRotation-initialRotation)/360)*2*Math.PI)*selected
translateY: bind Math.sin(((animatedRotation-initialRotation)/360)*2*Math.PI)*selected

// add the arc effects
onMouseEntered: function(e:MouseEvent) {
gradientPaint.start();
};
onMouseExited: function(e:MouseEvent) {
gradientPaint.start();
};
onMouseClicked: function(e:MouseEvent) {
clickMovement.start();
}
}
}

function radialGradientPaint(color : Color) : RadialGradient{
return RadialGradient{
cycleMethod: CycleMethod.NO_CYCLE
proportional: false
radius: 100
stops:[
Stop{
color:deriveColor(color, 1)
offset:0
},
Stop{
color: deriveColor(color, 0.6)
offset:1
}
]
}
}

function deriveColor(color : Color, transparency : Number) : Color{

var awtColor : java.awt.Color = color.getAWTColor();
return Color.color(awtColor.getRed()/255.0, awtColor.getGreen()/255.0, awtColor.getBlue()/255.0, transparency);
}


/**
* Idea let 0-1 cycle through Red (0/3), Green (1/3), Blue (2/3) and back to Red (3/3)
* @param fraction color 'degree'
* @return Color resulting color
*/
function fractionToColor(fraction : Number ): Color{
var colorFraction : Number = (fraction mod (1/3.0))*3.0;
if (fraction<1.0/3.0){
return Color.color(1-colorFraction, colorFraction, 0);
}
else if (fraction<2.0/3){
return Color.color(0,1-colorFraction, colorFraction);
}
else{
return Color.color( colorFraction,0,1-colorFraction);
}
}
}

PieChartTooltipText.fx

package com.mortennobel.fxdemo.piechart;

import javafx.scene.*;
import javafx.scene.geometry.*;
import javafx.scene.text.*;
import javafx.scene.paint.*;
import javafx.animation.*;
import javafx.input.*;
import javafx.scene.transform.Transform;

import java.util.*;
import java.text.*;
import java.lang.Math;

/**
* A Tooltip node for the piechart
*/
public class PieChartTooltipText extends CustomNode{
public attribute text : String;
private attribute opacityAtt : Number = 0.0;

// fade in/out animation
private attribute animation : Timeline = Timeline{
var beginFrame = at (0s){
opacityAtt => 0.0
},

var endFrame = at (0.5s) {
opacityAtt => 1.0 tween Interpolator.LINEAR
}
keyFrames: [beginFrame,endFrame]
autoReverse:true
toggle:true
}

public function toogleAnimation(){
animation.start();
}


public function create(): Node {
var textLayout : Text = Text{
content: bind text
horizontalAlignment: HorizontalAlignment.CENTER
verticalAlignment: VerticalAlignment.CENTER
font: Font.fromAWTFont(new java.awt.Font("Dialog",java.awt.Font.PLAIN, 14))
}
var margin : Integer = 4;
var rect :Rectangle = Rectangle{
width: bind textLayout.getBoundsWidth()+margin*2
height: bind textLayout.getBoundsHeight()+margin*2
x : bind textLayout.getBoundsX()-margin
y : bind textLayout.getBoundsY()-margin
arcWidth: 10
arcHeight: 10
fill: Color.LIGHTGRAY
stroke: Color.GRAY
}

return Group {
content: [rect,textLayout]
opacity: bind opacityAtt
}
}
}

PieChartBackground.fx

package com.mortennobel.fxdemo.piechart;

import javafx.scene.*;
import javafx.scene.geometry.*;

import javafx.scene.paint.*;
import java.lang.Math;

/**
* A background node for the piechart
*/
public class PieChartBackground extends CustomNode{

public attribute width : Integer = 500;
public attribute height : Integer = 500;

public function create(): Node {
return Rectangle {
width : bind width, height : bind height

fill : RadialGradient{
cycleMethod : CycleMethod.NO_CYCLE
centerX : bind width/2
centerY : bind height*0.25
radius : bind Math.sqrt(Math.pow(0.75*height,2)+Math.pow(width/2,2))
proportional : false;
stops: [
Stop { offset: 0.0 color: Color.web("0x5798bf") },
Stop { offset: 1.0 color: Color.web("0x22323c") }
]
}
};
}
}

PieChartApplet.fx

package com.mortennobel.fxdemo.piechart;

import javafx.application.*;
import javafx.scene.*;
import javafx.scene.text.*;

Application{
stage: Stage{
content: [
PieChartBackground{
width: 500
height: 500
},
PieChart{}]
}
}

Source code is also available in a zipped file:

piechartfx_source.zip


 
 
 
 
Comments:

java.lang.SecurityException: Prohibited package name: java.util at java.lang.ClassLoader.preDefineClass(ClassLoader.java:534) at java.lang.ClassLoader.defineClass(ClassLoader.java:669) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124) at java.net.URLClassLoader.defineClass(URLClassLoader.java:260) at java.net.URLClassLoader.access$100(URLClassLoader.java:56) at java.net.URLClassLoader$1.run(URLClassLoader.java:195) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at sun.applet.AppletClassLoader.findClass(AppletClassLoader.java:147) at java.lang.ClassLoader.loadClass(ClassLoader.java:316) at sun.applet.AppletClassLoader.loadClass(AppletClassLoader.java:119) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:374) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:164) at com.sun.javafx.runtime.Entry.runtimeProviderLocator(Entry.java:90) at com.sun.javafx.runtime.Entry.deferTask(Entry.java:78) at com.sun.javafx.runtime.Entry.deferTask(Entry.java:69) at javafx.lang.DeferredTask.postInit$(DeferredTask.fx:48) at javafx.lang.DeferredTask.initialize$(DeferredTask.fx:34) at javafx.application.Applet.init(Applet.fx:88) at sun.applet.AppletPanel.run(AppletPanel.java:380) at java.lang.Thread.run(Thread.java:613)

Posted by Tester on september 22, 2008 at 09:41 PM CEST #

java.lang.SecurityException: Prohibited package name: java.util at java.lang.ClassLoader.preDefineClass(ClassLoader.java:534) at java.lang.ClassLoader.defineClass(ClassLoader.java:669) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)

Posted by Tester on september 22, 2008 at 09:42 PM CEST #

What version of Java are you running? JavaFX only supports Java SE 1.6 Update 10 on Windows XP, Vista and Java SE 1.6 Update 5 on Mac OS X.

Posted by Morten Nobel-Jørgensen on september 24, 2008 at 06:56 PM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
 

« november 2008
mationtofr
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
       
Today

AddThis Feed Button

 
© Morten Nobel