Playing with Perspective
Thejavafx.scene.effect.PerspectiveTransform class
implements an effect that provides a non-affine transformation of
its input content, which is specified by this class's
input variable -- null implies that the
content is taken from the node to which this effect is attached.
| An affine transform (such as a translation) transforms straight lines into straight lines. Furthermore, it guarantees that parallel lines remain parallel after the transformation. Although the perspective transform also transforms straight lines into straight lines, it typically doesn't transform parallel lines into parallel lines. |
PerspectiveTransform is useful for giving content a
"faux" three-dimensional appearance. Whether the content appears
somewhat three-dimensional or not depends upon the values that are
assigned to the following variables:
-
llxidentifies the x coordinate of the output location onto which the lower-left corner of the content is mapped. -
llyidentifies the y coordinate of the output location onto which the lower-left corner of the content is mapped. -
lrxidentifies the x coordinate of the output location onto which the lower-right corner of the content is mapped. -
lryidentifies the y coordinate of the output location onto which the lower-right corner of the content is mapped. -
ulxidentifies the x coordinate of the output location onto which the upper-left corner of the content is mapped. -
ulyidentifies the y coordinate of the output location onto which the upper-left corner of the content is mapped. -
urxidentifies the x coordinate of the output location onto which the upper-right corner of the content is mapped. -
uryidentifies the y coordinate of the output location onto which the upper-right corner of the content is mapped.
According to PerspectiveTransform's documentation, it
doesn't adjust the coordinates of mouse input events, or adjust
the results of any methods that measure containment on a
Node to compensate for perspective.
I've created an application that lets you play with the
PerspectiveTransform class by dynamically modifying
the previously listed variables. Figure 1 reveals this
application's user interface.
Figure 1: The user interface presents an image (courtesy of Petr Kratochvil) followed by eight slider controls.
Adjust each slider to change the X or Y perspective of an image corner from 0 (the slider's leftmost value, which indicates no perspective) to half the width or height. Figure 2 reveals how the perspective changes as you manipulate the slider controls.
Figure 2: The image's perspective changes as you move the sliders.
I created this application as a NetBeans IDE 6.5.1 with JavaFX 1.2
PTDemo project. In addition to the image shown in
Figure 1, PTDemo consists of a single
Main.fx source file, which is presented in Listing 1.
/*
* Main.fx
*/
package ptdemo;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
var image: Image = Image
{
url: "{__DIR__}res/still_life.jpg"
}
var llx: Slider;
var lly: Slider;
var lrx: Slider;
var lry: Slider;
var ulx: Slider;
var uly: Slider;
var urx: Slider;
var ury: Slider;
Stage
{
title: "PerspectiveTransform Demo"
var scene: Scene
scene: scene = Scene
{
width: 350
height: 450
var iv: ImageView
var hb: HBox
content:
[
iv = ImageView
{
translateX: bind (scene.width-iv.layoutBounds.width)/2
translateY: 20
image: image
effect: PerspectiveTransform
{
llx: bind llx.value
lly: bind image.height-lly.value
lrx: bind image.width-lrx.value
lry: bind image.height-lry.value
ulx: bind ulx.value
uly: bind uly.value
urx: bind image.width-urx.value
ury: bind ury.value
}
}
hb = HBox
{
spacing: 30
translateX: bind (scene.width-hb.layoutBounds.width)/2
translateY: iv.layoutBounds.height+40
content:
[
VBox
{
spacing: 10
content:
[
Label
{
text: "ULX"
}
Label
{
text: "ULY"
}
Label
{
text: "URX"
}
Label
{
text: "URY"
}
Label
{
text: "LRX"
}
Label
{
text: "LRY"
}
Label
{
text: "LLX"
}
Label
{
text: "LLY"
}
]
}
VBox
{
spacing: 10
content:
[
ulx = Slider
{
min: 0
max: image.width/2
}
uly = Slider
{
min: 0
max: image.height/2
}
urx = Slider
{
min: 0
max: image.width/2
}
ury = Slider
{
min: 0
max: image.height/2
}
lrx = Slider
{
min: 0
max: image.width/2
}
lry = Slider
{
min: 0
max: image.height/2
}
llx = Slider
{
min: 0
max: image.width/2
}
lly = Slider
{
min: 0
max: image.height/2
}
]
}
]
}
]
}
}
Listing 1: Main.fx
Although the listing should be mostly straightforward, there are two items that deserve clarification:
-
I place the eight
Labelinstances in oneVBox, and the eightSliderinstances in a secondVBoxbecause I found doing so to be convenient for vertically aligning the sliders as well as the labels. -
Because of a bug in the
Sliderimplementation (which will hopefully be fixed in the next JavaFX release), I'm forced to assign0tomin, andimage.width/2orimage.height/2tomax. I then bind either the slider's result as is, or subtracted from the image's width or height to the appropriatePerspectiveTransformvariable. Without the bug, the subtractions wouldn't have been necessary. For example, given thellySliderinstance, I'd assignimage.height/2tominandimage.heighttomax(andvalue), and specifylly: lly.valuein thePerspectiveTransformclass literal.
Conclusion
Because this article's example provides only a basic demonstration ofPerspectiveTransform, I refer you to Josh
Marinacci's 3-D
Display Shelf With the PerspectiveTransform JavaFX sample for
a more advanced demonstration.
Note: Application created with JavaFX 1.2 (via NetBeans IDE 6.5.1) on top of Java SE 6u16.









