Why does Transform in JavaFX looks like 3D? - java

Looks like Transform class in JavaFX contains 3 coordinates (x, y and z).
What will happen if I rotate out of plane? Will it do perspective?
UPDATE
Actually object disappears if rotated out of plane even by 1 degree. Why?
public class TransformTry extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Drawing Operations Test");
Group root = new Group();
Canvas canvas = new Canvas(300, 250);
//canvas.getTransforms().add(new Rotate(1, new Point3D(0, 1, 0))); // square disappears
canvas.getTransforms().add(new Rotate(1, new Point3D(0, 0, 1))); // rotates correctly
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.strokeRect(-50, -50, 100, 100);
root.getChildren().add(canvas);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
UPDATE 2
I found a way to use perspective camera, but it works strange: it looks always from above the center of a window, so out-of-plane object changes on window resize:
public class ShapeTry01 extends Application {
#Override
public void start(Stage stage) throws Exception {
Shape shape = new Rectangle(100,100,50,50);
shape.setStroke(Color.RED);
shape.setFill(null);
Group group = new Group();
group.getChildren().add(shape);
//group.getTransforms().add(new Rotate(10, new Point3D(0, 0, 1)));
group.getTransforms().add(new Rotate(10, new Point3D(0, 1, 0)));
PerspectiveCamera camera = new PerspectiveCamera();
camera.setFieldOfView(45);
Scene scene = new Scene(group);
scene.setCamera( camera );
stage.setTitle("ShapeTry01");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX 2 was started with 3D in mind, thus a lot of API has z coordinate mentioned. But actually useful 3D was added only in JavaFX8 (to be released in next year).
You can see documentation here: http://docs.oracle.com/javafx/8/3d_graphics/jfxpub-3d_graphics.htm and try developers preview: https://jdk8.java.net/download.html

Related

Javafx: Dialog y position doesn't work properly

Please, consider the following code:
public class JavaFxTest4 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button button = new Button();
button.setText("dialog");
button.setOnAction((e) -> {
Dialog<?> d = new Dialog<>();
final Window window = d.getDialogPane().getScene().getWindow();
Stage stage = (Stage) window;
stage.setMinHeight(450);
stage.setMaxHeight(450);
stage.setHeight(450);
stage.setMinWidth(600);
stage.setMaxWidth(600);
stage.setWidth(600);
window.setY(300); //<---- note this line
window.setX(660); //<---- note this line
d.showAndWait();
});
VBox root = new VBox();
root.getChildren().addAll(button);
var scene = new Scene(root, 1920, 1000);
primaryStage.setScene(scene);
primaryStage.show();
}
}
As you can see window position is 660 (x) and 300 (y). And this is the result:
As you can see x position is correct, but y position is not. Is this a bug or I misunderstand something? I use javafx 19-ea+3 and openjdk version "14.0.2" on Ubuntu 20.
It is a bug in JavaFX. Issue is here

Entering and exiting application with JavaFX

I have strange problems with JavaFX that look a lot like a bug. I want to do the following:
Entering fullscren when starting my application
Press escape to exit the application (not fullscreen, the entire application)
So far, I have the following code:
public class AppTest extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) {
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(0);
});
stage.setFullScreenExitHint("Press ESCAPE to exit");
stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
stage.setFullScreen(true);
Rectangle2D screenBounds = Screen.getPrimary().getBounds();
stage.setX(screenBounds.getMinX());
stage.setY(screenBounds.getMinY());
double screenWidth = screenBounds.getWidth();
double screenHeight = screenBounds.getHeight();
stage.setWidth(screenWidth);
stage.setHeight(screenHeight);
Group root = new Group();
Scene scene = new Scene(root);
stage.setScene(scene);
scene.setOnKeyTyped(event -> {
if(event.getCode() == KeyCode.ESCAPE) {
stage.close();
}
});
Canvas canvas = new Canvas(screenWidth, screenHeight);
root.getChildren().add(canvas);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.BLUE);
gc.fillRect(0,0, screenWidth, screenHeight);
stage.show();
}
}
I'm on macOS.
In general it goes fullscreen. I'm saying in general because the real version of this code doesn't always. Sometimes, it is just a maximized window.
Then, when pressing escape, I get a maximized window instead of exiting the application.
How can I fix that?
Change:
scene.setOnKeyTyped
To:
scene.setOnKeyReleased
This explains why.

Listen for transformation changes

Is there way to listen for transformation changes? I'd like to get notified when for example a cube has turned. Especially I'am interested in getLocalToSceneTransform.
Here is my try:
#Override
public void start(Stage primaryStage) throws Exception {
final Group root = new Group();
final Scene scene = new Scene(root);
final Box cube = new Box(1, 1, 1);
cube.setRotationAxis(Rotate.Y_AXIS);
cube.setMaterial(new PhongMaterial(Color.RED));
root.getChildren().add(cube);
cube.getLocalToSceneTransform().addEventHandler(TransformChangedEvent.TRANSFORM_CHANGED, (e) -> {
// never get called
System.out.println("Transformation has changed");
});
final Camera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-4);
scene.setCamera(camera);
final Timeline turnCube = new Timeline();
turnCube.getKeyFrames().add(new KeyFrame(Duration.seconds(0), new KeyValue(cube.rotateProperty(), 0)));
turnCube.getKeyFrames().add(new KeyFrame(Duration.seconds(5), new KeyValue(cube.rotateProperty(), 360)));
turnCube.setCycleCount(Timeline.INDEFINITE);
turnCube.play();
primaryStage.setWidth(1024);
primaryStage.setHeight(768);
primaryStage.setScene(scene);
primaryStage.show();
}
While the cube is turned the EventHandler get never called.
You can add a changeListener to the cube's localToSceneTransformProperty() to get notified when the cube is rotated.
cube.localToSceneTransformProperty().addListener((value, oldValue, newValue) -> {
System.out.println("Transformation has changed");
});

JavaFX : draw sharp thin lines

I am wondering how to draw sharp thin lines using JavaFX. I would like my lines to be black, and 1 pixel high. Here is what I have at the moment:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
root.setSnapToPixel(true);
Scene scene = new Scene(root,400,400);
Line line = new Line();
Line line2 = new Line();
line.setStartX(0.0f);
line.setEndX(100f);
line.setStartY(30f);
line.setEndY(30f);
line.setStrokeWidth(1f);
line.setStrokeType(StrokeType.OUTSIDE);
line.setStroke(Color.BLACK);
line2.setStartX(50.0f);
line2.setEndX(200f);
line2.setStartY(100f);
line2.setEndY(100f);
line2.setStrokeWidth(1f);
line2.setStrokeType(StrokeType.OUTSIDE);
line2.setStroke(Color.BLACK);
root.getChildren().addAll(line, line2);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Here is what I get :
The lines are quite thick, and calling the setStrokeWidth() method with a value < 1 has no effect on the height, but makes the black color fade off. Any idea how to get a 1 pixel high line?
I can achieve it by using rectangles with a height of 1 pixel, but it seems a bit dirty.
If you use StrokeType.CENTERED, and start the x/y values on a half unit, then the lines appear to be hairlines to me.
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
root.setSnapToPixel(true);
Scene scene = new Scene(root, 400, 400);
Line line = new Line();
Line line2 = new Line();
line.setStartX(0.5);
line.setEndX(100.5);
line.setStartY(30.5);
line.setEndY(30.5);
line.setStrokeWidth(1.0);
line.setStrokeType(StrokeType.CENTERED);
line.setStroke(Color.BLACK);
line2.setStartX(50.5);
line2.setEndX(200.5);
line2.setStartY(100.5);
line2.setEndY(100.5);
line2.setStrokeWidth(1.0);
line2.setStrokeType(StrokeType.CENTERED);
line2.setStroke(Color.BLACK);
root.getChildren().addAll(line, line2);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
My guess is that unit numbers in JavaFX are for the corners of the pixels, so specifying location + 0.5 places the line in the middle of said pixel.

How to apply a blur only to a rectangular area within a pane?

Let's say I've got a StackPane which has a BackgroundImage as background and another StackPane (or another component, if neccessary) as a child. The child covers only a part of the parent StackPane.
I'd like to know how to apply a GaussianBlur just to the area the child covers, so that the BackgroundImageis blurry in this area.
The size of the child changes when the parent is resized. It would be perfect to get a solution that will resize just in time, too.
If you want to do it manually, you can use the snapshot function to create a snapshot image, blur it and apply it to the child every time the parent is resized.
However, invoking snapshot all the time will cause performance loss. I rather suggest you create 2 images, one normal and one blurred, and display a viewport of the blurred one.
Here's a more "complex" example with a circle where the viewport isn't sufficient. The clip method is used in this case:
public class Lens extends Application {
Image image = new Image( "http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/800px-Siberischer_tiger_de_edit02.jpg");
CirclePane circlePane;
#Override
public void start(Stage primaryStage) {
ImageView normalImageView = new ImageView( image);
ImageView blurredImageView = new ImageView( image);
blurredImageView.setEffect(new GaussianBlur( 40));
Group root = new Group();
root.getChildren().addAll( normalImageView);
Scene scene = new Scene( root, 1024, 768);
primaryStage.setScene( scene);
primaryStage.show();
// pane with clipped area
circlePane = new CirclePane( blurredImageView);
makeDraggable( circlePane);
root.getChildren().addAll( circlePane);
}
public static void main(String[] args) {
launch(args);
}
private class CirclePane extends Pane {
ImageView blurredImageView;
ImageView clippedView = new ImageView();
public CirclePane( ImageView blurredImageView) {
this.blurredImageView = blurredImageView;
// new imageview
update();
getChildren().addAll( clippedView);
}
public void update() {
// create circle
Circle circle = new Circle( 200);
circle.relocate( getLayoutX(), getLayoutY());
// clip image by circle
blurredImageView.setClip(circle);
// non-clip area should be transparent
SnapshotParameters parameters = new SnapshotParameters();
parameters.setFill(Color.TRANSPARENT);
// new image from clipped image
WritableImage wim = null;
wim = blurredImageView.snapshot(parameters, wim);
clippedView.setImage( wim);
}
}
// make node draggable
class DragContext {
double x;
double y;
}
public void makeDraggable( Node node) {
final DragContext dragDelta = new DragContext();
node.setOnMousePressed(mouseEvent -> {
dragDelta.x = node.getBoundsInParent().getMinX() - mouseEvent.getScreenX();
dragDelta.y = node.getBoundsInParent().getMinY() - mouseEvent.getScreenY();
});
node.setOnMouseDragged(mouseEvent -> {
node.relocate( mouseEvent.getScreenX() + dragDelta.x, mouseEvent.getScreenY() + dragDelta.y);
circlePane.update();
});
}
}
Just click on the circle and drag it around.

Categories