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

Pie chart in JavaFX Script


Update (Juli 2009): Please note that the following blog-entry is outdated. It is using a beta version of the JavaFX 1.0 platform.

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 #

Iam getting many errors while iam running this application, I feel, the collision is taking place from both javafx MouseEvent and awt MouseEvent. could u tell me the possible way ,how to avoid this problem. Thanks in Advance.

Posted by crazy on December 14, 2009 at 10:50 AM CET #

[Trackback] Keflex 500mg.

Posted by Keflex 500mg. on March 06, 2010 at 12:34 AM CET #

[Trackback] Alprazolam online. Alprazolam er. Alprazolam.

Posted by Alprazolam-mylan. on March 06, 2010 at 02:49 AM CET #

[Trackback] Alprazolam from mexico.

Posted by Alprazolam. on March 07, 2010 at 04:56 AM CET #

[Trackback] Viagra. Viagra clips. Viagra hgh.

Posted by Impulse female viagra. on March 08, 2010 at 04:22 AM CET #

[Trackback] Viagra. Viagra hgh.

Posted by Viagra on line. on March 09, 2010 at 03:37 AM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
 

« March 2010
SunMonTueWedThuFriSat
 
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
31
   
       
Today

AddThis Feed Button

 
© Morten Nobel