Jim has posted 66 posts at DZone. You can read more from them at their website. View Full User Profile

Who's Zoomin' Who? Scaling in Compiled JavaFX Script

02.04.2008
| 4570 views |
  • submit to reddit

In the Spinning Wheel Got to Go 'Round post, I showed you some basic animation in the context of a wheel that can be used to select a prize winner. Now I'd like to show you a modification that provides for zooming into a portion of the wheel, so that the audience can see the names more clearly as the wheel is spinning. Here's a screenshot of the wheel with some of the top rock songs, including the artists that performed them:

Notice that there is a green square on the lower left side of the wheel. Clicking on that square at any time (for example, while the wheel is spinning) causes the wheel to zoom in on a portion of the wheel as shown below. Clicking on the square again causes the wheel to return to its original size.

Using the Scale Transform to Zoom into a Portion of a Graphic Node

The listing below shows the updated version of WinnerWheelJFX.fx which includes this zoom feature. The WinnerWheelExample.fx file hasn't changed, and you can find that file in the post referenced above.

/*
* WinnerWheelJFX.fx - A wheel with names on it that spins
* and lands on a random name. This will
* be used initially by Java Users Groups
* to give a away door prizes. It will be
* improved upon over time, serving as an
* example of compiled JavaFX 2D graphics
* and animation.
*
* Note: The compiled JavaFX Script Key-Frame
* implementation isn't complete, so the
* animation syntax is more verbose than it
* will be in the near future.
*
* Initially developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*/
import javafx.ui.*;
import javafx.ui.animation.*;
import javafx.ui.canvas.*;
import java.io.BufferedReader;
import java.io.StringReader;
import java.lang.Math;
import java.lang.System;
import com.sun.javafx.runtime.PointerFactory;

public class WinnerWheelJFX extends CompositeNode {
public attribute names:String[];
public attribute chosenName:String;
public attribute chosenIdx:Integer;
public attribute running:Boolean = true;

public attribute zoomed:Boolean;

private static attribute maxNames = 60;
private attribute dlg:Dialog;
private attribute rand:Number;
private attribute stopValue = 1100;
private attribute a:Integer on replace (olda) {
chosenIdx = (((a * rand) % 360) / 360.0 * sizeof names).intValue();
chosenName = names[chosenIdx];
if (a >= stopValue) {
running = false;
MessageDialog {
title: "And the Winner Is..."
visible: true
message: chosenName
}
}
}
private attribute pf = PointerFactory {};
private attribute bpa = bind pf.make(a);
private attribute pa = bpa.unwrap();
private attribute interpolate = NumberValue.LINEAR;
private attribute t =
Timeline {
keyFrames: [
KeyFrame {
keyTime: 0s;
keyValues:
NumberValue {
target: pa;
value: 0;
interpolate: bind interpolate
}
},
KeyFrame {
keyTime: 15s;
keyValues:
NumberValue {
target: pa;
value: 700;
interpolate: bind interpolate
}
},
KeyFrame {
keyTime: 30s;
keyValues:
NumberValue {
target: pa;
value: stopValue
interpolate: bind interpolate
}
}
]
};

public function spin() {
running = true;
rand = Math.random() + .5;
interpolate = NumberValue.LINEAR;
t.start();
}
public function composeNode():Node {
var margin = 20;
var canvas = getCanvas();
var cX = bind canvas.width / 2;
var cY = bind canvas.height / 2;
var rad = bind Math.min(cX, cY) - margin;
var origX = bind cX - rad;
var origY = bind cY - rad;
return
Group {
transform: bind [
Scale.scale(if (zoomed) 1.8 else 1, if (zoomed) 1.8 else 1),
Translate.translate(0, if (zoomed) -0.9 * cY else 0)
]
content: [
Rect {
var zoomFillColor = Color.GREEN
x: bind cX - rad
y: bind cY + rad / 2
width: bind origX / 4
height: bind origX / 4
cursor: Cursor.HAND
fill: bind zoomFillColor
onMouseEntered:
function(cme:CanvasMouseEvent):Void {
zoomFillColor = Color.CYAN;
}
onMouseExited:
function(cme:CanvasMouseEvent):Void {
zoomFillColor = Color.GREEN;
}
onMouseClicked:
function(cme:CanvasMouseEvent):Void {
zoomed = not zoomed;
}
},
Polygon {
var spinFillColor = Color.PURPLE
points: bind [
cX - rad,
cY,
origX / 2,
cY - (origX / 4),
origX / 2,
cY + (origX / 4)
]
cursor: Cursor.HAND
fill: bind spinFillColor
onMouseEntered:
function(cme:CanvasMouseEvent):Void {
spinFillColor = Color.YELLOW;
}
onMouseExited:
function(cme:CanvasMouseEvent):Void {
spinFillColor = Color.PURPLE;
}
onMouseClicked:
function(cme:CanvasMouseEvent):Void {
spin();
}
},
Circle {
var editFillColor = Color.RED
cx: bind cX
cy: bind cY
radius: bind rad / 30
cursor: Cursor.HAND
fill: bind editFillColor
onMouseEntered:
function(cme:CanvasMouseEvent):Void {
editFillColor = Color.BLUE;
}
onMouseExited:
function(cme:CanvasMouseEvent):Void {
editFillColor = Color.RED;
}
onMouseClicked:
function(cme:CanvasMouseEvent):Void {
dlg = Dialog {
var ta = TextArea {
rows: 10
columns: 20
background: Color.WHITE
text: ""
}
modal: true
title: "Enter Up to {maxNames} Names"
visible: true
content: ta
buttons: [
Button {
text: "OK"
defaultButton: true
action:
function():Void {
names = [];
var peopleStr:String = ta.text;
var br = new BufferedReader((new StringReader(peopleStr)));
var line:String;
var i = 0;
while ((line = br.readLine()) <> null and i <= maxNames) {
insert line into names;
i++;
}
dlg.hide();
}
},
Button {
text: "Cancel"
defaultCancelButton: true
action:
function():Void {
dlg.hide();
}
}
]
};
}
},
Group {
transform: bind [
Rotate.rotate(if (running) (a * rand) % 360
else chosenIdx.doubleValue() / sizeof names * 360.0, cX, cY)
]
content: bind [
for (name in names)
Text {
transform: [
Rotate.rotate(((sizeof names - indexof name).doubleValue() /
sizeof names * 360) % 360, cX, cY)
]
font:
Font {
face: FontFace.SANSSERIF
size: 12
style: FontStyle.BOLD
}
fill: if ((indexof name % 3) == 1) Color.RED
else if ((indexof name % 3) == 2) Color.BLUE
else Color.GREEN
x: cX - rad + 5
y: cY
content: "{name}"
}
]
}
]
};
}
}

Of particular note is the portion of code, shown below, that paint the green rectangle on the screen, reacts to mouse events, and causes the wheel to be scaled and translated (moved). Note that zoomed is a Boolean attribute defined earlier in the listing.

        transform: bind [
Scale.scale(if (zoomed) 1.8 else 1, if (zoomed) 1.8 else 1),
Translate.translate(0, if (zoomed) -0.9 * cY else 0)
]
content: [
Rect {
var zoomFillColor = Color.GREEN
x: bind cX - rad
y: bind cY + rad / 2
width: bind origX / 4
height: bind origX / 4
cursor: Cursor.HAND
fill: bind zoomFillColor
onMouseEntered:
function(cme:CanvasMouseEvent):Void {
zoomFillColor = Color.CYAN;
}
onMouseExited:
function(cme:CanvasMouseEvent):Void {
zoomFillColor = Color.GREEN;
}
onMouseClicked:
function(cme:CanvasMouseEvent):Void {
zoomed = not zoomed;
}
},

As always, please leave a comment if you have any questions about this example.

Regards,
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications

Immediate eBook (PDF) download available at the book's Apress site

Published at DZone with permission of its author, Jim Weaver.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Matthew Schmidt replied on Tue, 2008/02/05 - 7:25am

Awesome.  I'd definitely be interested in seeing more JavaFX posts.  It's a potentially powerful addition to the Java language for doing graphics work. 

Mark Unknown replied on Tue, 2008/02/05 - 9:06am

I would like to see more too.

I am trying to see if I can use JavaFX in a new application. It would be nice to see some best practices (ie - The code above is good for an example but not for maintainability) and how to create actual applications - hiding and showing panels, for example.

 BTW, I've purchased the book and it is on its way. Hopefully that will help.

Jim Weaver replied on Thu, 2008/02/07 - 6:48pm

Konstantin,

The development of compiled JavaFX Script is moving quickly.  Please check my "Helping You Become a JavaFXpert" weblog to help gauge its progress, as I've been posting articles that highlight new features as they occur.

Regards,
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications
Immediate eBook (PDF) download available at the book's Apress site

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.