JavaFx PerspectiveCamera - fixedEyeAtCameraZero flag - when true, all objects disappear - java

I want to simulate perspective distortion of boxes and rectangles. My goal is to warp the box and image, as if the camera is being tilted and moved. But I don't really follow how to use the PerspectiveCamera.
When I set the fixedEyeAtCameraZero to false, I can see the box and image on screen. But changing the window size causes weird orthographic perspective changes that aren't realistic.
On the other hand when I set fixedEyeAtCameraZero to true, all I see is a blank window.
False:
True:
Here's the code, with the offending flag at line 51.
package sample;
import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.shape.Box;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class Example_Box extends Application {
#Override
public void start(Stage stage) {
//Drawing a Box
Box box2 = new Box();
//Setting the properties of the Box
box2.setWidth(100.0);
box2.setHeight(100.0);
box2.setDepth(100.0);
//Setting the position of the box
box2.setTranslateX(30); //450
box2.setTranslateY(90);//150
box2.setTranslateZ(300);
//Setting the drawing mode of the box
box2.setDrawMode(DrawMode.LINE);
//Drawing an Image
Image image = new Image("Lenna.png");
ImageView imageView = new ImageView(image);
imageView.setTranslateX(200);
imageView.setTranslateY(150);
imageView.setTranslateZ(200);
//imageView.getTransforms().add(new Rotate(30, 50, 30));
//Creating a Group object
Group root = new Group(box2, imageView);
//Creating a scene object
Scene scene = new Scene(root, 600, 300);
//Setting camera
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateX(30);
camera.setTranslateY(0);
camera.setTranslateZ(-100);
camera.setRotationAxis(new Point3D(1,0,0));
scene.setCamera(camera);
//Setting title to the Stage
stage.setTitle("Drawing a Box");
//Adding scene to the stage
stage.setScene(scene);
//Displaying the contents of the stage
stage.show();
}
public static void main(String args[]){
launch(args);
}
}

Try to change the farClip value. By default farClip value is 100 in FX perspective camera.
camera.setFarClip(2000.0);
With above piece code addition, I could see the Box. If I move the camera further away(in Z direction), I could see Image also,
camera.setTranslateZ(-1000);
Reference: http://www.dummies.com/programming/java/javafx-add-a-perspective-camera/

Related

How to position a JavaFX context menu inside the main window?

Hi I am a JavaFX newbie and I am trying to write my first application. I want to start with an empty window and provide a popup menu that allows users to add 3D elements to the window.
I have created a simple Group containing a few trivial geometric shapes and added this group as the parent to a Scene. I define a mouse event handler for the scene and call setScene to make this the scene for my Stage (passed in to my Application's start method).
Unfortunately, I can't seem to find a way of positioning the menu correctly in response to a mouse pressed event. I get it that I need to get the X and Y coordinates from the Event, but when I pass these unchanged to the context menu show method, the menu appears in the top left-hand corner of my laptop display, rather than inside my application window.
Clearly, I need to offset these values by the origin of some other window, but what? I have tried the Scene, the Group and and the Stage, but with no success :-( This ought to be a trivial problem - where am I going wrong??
Code sample shown below:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.PointLight;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Box;
import javafx.scene.shape.Cylinder;
import javafx.scene.shape.Sphere;
import javafx.stage.Stage;
public class PopupTest extends Application {
private static final ContextMenu contextMenu = new ContextMenu();
public static void main(String[] args) {
MenuItem cut = new MenuItem("Cut");
MenuItem copy = new MenuItem("Copy");
MenuItem paste = new MenuItem("Paste");
contextMenu.getItems().addAll(cut, copy, paste);
Application.launch(args);
}
#Override
public void start(Stage stage) {
// Create a Box
Box box = new Box(100, 100, 100);
box.setTranslateX(150);
box.setTranslateY(0);
box.setTranslateZ(400);
// Create a Sphere
Sphere sphere = new Sphere(50);
sphere.setTranslateX(300);
sphere.setTranslateY(-5);
sphere.setTranslateZ(400);
// Create a Cylinder
Cylinder cylinder = new Cylinder(40, 120);
cylinder.setTranslateX(500);
cylinder.setTranslateY(-25);
cylinder.setTranslateZ(600);
// Create a Light
PointLight light = new PointLight(Color.YELLOW);
light.setTranslateX(350);
light.setTranslateY(100);
light.setTranslateZ(300);
// Create a Camera to view the 3D Shapes
PerspectiveCamera camera = new PerspectiveCamera(false);
camera.setTranslateX(100);
camera.setTranslateY(-50);
camera.setTranslateZ(300);
// Add the Shapes and the Light to the Group
Group root = new Group(box, sphere, cylinder, light);
// Create a Scene with depth buffer enabled
Scene scene = new Scene(root, 400, 300, true);
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("mouse click detected!");
if (event.isPopupTrigger()) {
// similar results with getX() vs getSceneX() etc.
System.out.println("Display menu at (" + event.getSceneX() + "," + event.getSceneY() + ")");
contextMenu.show(root, event.getSceneX(), event.getSceneY());
}
}
});
// Add the Camera to the Scene
scene.setCamera(camera);
// Add the Scene to the Stage
stage.setScene(scene);
// Set the Title of the Stage
stage.setTitle("Trying to get popup menu working");
// Display the Stage
stage.show();
}
}

Javafx Tooltip blinks when is too big

I want to display a small image and display the image in the original size when the small is hover.
I need a tooltip because the image in the original size could not fit the window.
Executable code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
String bigImageUrl = "https://cdn.lynda.com/course/184457/184457-636806635954727169-16x9.jpg";
String smallImageUrl = "https://assets.exercism.io/tracks/java-bordered-turquoise.png";
VBox root = new VBox();
root.getChildren().add(loadTooltipWithImage(smallImageUrl));
root.getChildren().add(loadTooltipWithImage(bigImageUrl));
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
private Label loadTooltipWithImage(String url) {
ImageView bigImage = new ImageView(new Image(url));
ImageView smallImage = new ImageView(new Image(url));
smallImage.setFitHeight(100.0);
smallImage.setFitWidth(100.0);
Label label = new Label();
label.setGraphic(smallImage);
Tooltip tooltip = new Tooltip();
tooltip.setGraphic(bigImage);
label.setTooltip(tooltip);
return label;
}
}
A tooltip displays an image when a node is hover, but when image is too big the node cannot be hovered by the mouse:
The node is hover
The tooltip displays image
The node is not hover
The tooltip does not display
Go to step 1
What I tried:
Set mouse transparent to true on the tooltip, but I could not
Use a pane instead of tooltip, but the window (stage) is too small for the image
Set the mouse filters, but they do not help me

Scaling Nodes in Pane within AnchorPane JavaFX

I have a 2 Nodes (One Text, the other a Circle) in a Pane, and that pane is within an AnchorPane. The reason why I did this is I wanted to use the automatic scaling property that is a result of resizing the AnchorPane to adjust the size of the Nodes (like stretching and shrinking).
I also have a test background to see if I could scale the background aswell.
My issue is that while the background scales when I resize the window, the Nodes do not.
Heres my code:
package javacode;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BackgroundImage;
import javafx.scene.layout.BackgroundPosition;
import javafx.scene.layout.BackgroundRepeat;
import javafx.scene.layout.BackgroundSize;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
public boolean debug = false;
Pane stack;
#Override
public void start(Stage stage) {
// Check for updates
/*
System.out.println("Checking for update");
boolean newUpdate = Updater.updateAvalible();
System.out.println("New update avalible: " + newUpdate);
if (newUpdate) {
Updater.showUpdatePrompt();
}
*/
// Get the debug background image
Image debugbackgroundimage = new Image(
this.getClass().getClassLoader().getResourceAsStream("resources/debugbackground.jpg"));
// Create a stack pane to add all the objects into
stack = new Pane();
// Setup the background
stack.setBackground(new Background(new BackgroundImage(debugbackgroundimage, BackgroundRepeat.NO_REPEAT,
BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(
debugbackgroundimage.getWidth(), debugbackgroundimage.getHeight(), false, false, true, true))));
// Set the preferred and minimum sizes for the stack
stack.setPrefSize(debugbackgroundimage.getWidth(), debugbackgroundimage.getHeight());
stack.setMinSize(16, 9);
// We need an anchor pane go help automatically constrain the maximum and
// minimum sizes of things
AnchorPane anchor = new AnchorPane();
AnchorPane.setTopAnchor(stack, 0.0);
AnchorPane.setBottomAnchor(stack, 0.0);
AnchorPane.setLeftAnchor(stack, 0.0);
AnchorPane.setRightAnchor(stack, 0.0);
// Add the stack to the anchor pane
anchor.getChildren().addAll(stack);
// Set the anchor background to a light gray, that way we can check for overlap
anchor.setBackground(new Background(new BackgroundFill(Color.LIGHTGRAY, CornerRadii.EMPTY, Insets.EMPTY)));
// Set the scene for the visualizer, use the anchor pane defined above
Scene mCatScene = new Scene(anchor);
// Set the contents of the window to that of the scene
stage.setScene(mCatScene);
// Show the window (stage)
stage.show();
// Add a test node to check if it scales properly
Text test = new Text(String.format("Test\nFoo bar"));
test.setFont(new Font("Arial", 15));
test.setWrappingWidth(500);
test.setFill(Color.RED);
test.setLayoutX(0);
test.setLayoutY(50);
Circle test2 = new Circle();
test2.setLayoutX(50);
test2.setLayoutY(120);
test2.setFill(Color.GREEN);
test2.setRadius(10);
stack.getChildren().add(0, test);
stack.getChildren().add(1, test2);
}
public static void main(String[] args) {
Main This = new Main();
// Check if debug mode is enabled (Basically check for console spam enabled)
try {
This.debug = Boolean.parseBoolean(args[0]);
} catch (Exception e) {
This.debug = false;
}
launch(args);
}
}
Here is the background image I used:
Edit: Here is a gif of how the background reacts (which is ideal), vs how the Nodes react (not ideal)

rotate javafx image view around custom point

I am starting to learn JavaFX
now when I try to rotate the image view using
imgv.setRotate(angle);
it rotates around its central axis
but when I try to rotate it around a custom point inside the image
using
imgv.getTransforms().add(new Rotate(Angle,custom_x,custom_y);
it rotates randomly and I can't figure its axis of the rotation
this was the same as
imgv.getTransforms().add(new Rotate(Angle,custom_x,custom_y,1.0,Rotate.Z_AXIS);
is there any way to rotate the image around a custom point
here is image explaining the position of a point if (0,0) was at the top left or at the center of the image both way I can't rotate the image around that point according to x,y
I know that I can rotate around the origin then do translation but am asking if there is a speedway to do that
thanks in advance
Here is an app that demonstrates how to accomplish what you are asking. The key parts of accomplishing this are .getTransforms().add(..) and Rotate. Comments in the code. I added the Circle so that you can actually see where the point of rotation is located.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication117 extends Application
{
#Override
public void start(Stage primaryStage)
{
Image image = new Image("http://lmsotfy.com/so.png");
ImageView imageView = new ImageView(image);
imageView.setFitHeight(400);
imageView.setFitWidth(400);
Button btn = new Button();
btn.setText("Say 'Hello World'");
//Use this Circle to help see where the rotation occurs
Circle circle = new Circle(5);
circle.setFill(Color.RED);
circle.setCenterX(100);
circle.setCenterY(300);
//Add the Rotate to the ImageView's Transforms
Rotate rotation = new Rotate();
rotation.setPivotX(circle.getCenterX());//Set the Pivot's X to be the same location as the Circle's X. This is only used to help you see the Pivot's point
rotation.setPivotY(circle.getCenterY());//Set the Pivot's Y to be the same location as the Circle's Y. This is only used to help you see the Pivot's point
imageView.getTransforms().add(rotation);//Add the Rotate to the ImageView
//Use the Button's handler to rotate the ImageView
btn.setOnAction((ActionEvent event) -> {
rotation.setAngle(rotation.getAngle() + 15);
});
Pane pane = new Pane();
pane.getChildren().addAll(imageView, circle);
VBox.setVgrow(pane, Priority.ALWAYS);
VBox vBox = new VBox(pane, new StackPane(btn));
StackPane root = new StackPane();
root.getChildren().add(vBox);
Scene scene = new Scene(root, 1080, 720);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}

how to have a scene over another in javafx

I'm writing a small game. And i want a pause menu over the blured game menu to be shown when esc is pressed in the middle of the game.
What I do is that i make a new scene which has a stackPane wrapping the past root and the pause menu root and then i set the opacity of the past root 0.4 .
Then when the resume button in the pause menu is clicked i change the opacity back to 1 and set the past scene on stage but then its freezed. does anyone know why? can anyone help me achieve this?
Here is the part i make the new scene and then i put this on stage:
StackPane wrapper = new StackPane();
previousScene = main.getPrimaryStage().getScene();
previousScene.getRoot().setOpacity(.4);
vBox.setId("pausedWrapper");
wrapper.getChildren().add(previousScene.getRoot());
wrapper.getChildren().add(vBox);
scene = new Scene(wrapper, 1200, 700);
return scene;
Here is the part i change it back to where it was:
resumeGame.setOnAction(event -> {
System.out.println("game resumed!");
previousScene.getRoot().setOpacity(1);
main.getPrimaryStage().setScene(previousScene);
});
But then it does not work and the opacity does not change back to normal and the strange thing is when i check the sound on box the music is played but the box does not get checked like everything works but the view is freezed.
A node cannot be part of two different scene graphs. This happens in your code to the root of previousScene, because it is part of both previousScene and the new scene you create in your first block of code. Most likely what is happening is that it is removed from the first scene when you add it to the second (though it is hard to tell from the code you posted).
Consider instead using a Popup to display the pauseMenu on top of the existing window, or just use a modal Stage with undecorated StageStyle, as in the following SSCCE:
import javafx.animation.Animation;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Modality;
import javafx.stage.Popup;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public class PauseExample extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(50, 50, 50, 50);
rect.setFill(Color.CORAL);
TranslateTransition animation = createAnimation(rect);
Button pauseButton = new Button("Pause");
Pane pane = new Pane(rect);
pane.setMinSize(600, 150);
BorderPane root = new BorderPane(pane, null, null, pauseButton, new Label("This is\nthe main\nscene"));
pauseButton.setOnAction(e -> {
animation.pause();
root.setEffect(new GaussianBlur());
VBox pauseRoot = new VBox(5);
pauseRoot.getChildren().add(new Label("Paused"));
pauseRoot.setStyle("-fx-background-color: rgba(255, 255, 255, 0.8);");
pauseRoot.setAlignment(Pos.CENTER);
pauseRoot.setPadding(new Insets(20));
Button resume = new Button("Resume");
pauseRoot.getChildren().add(resume);
Stage popupStage = new Stage(StageStyle.TRANSPARENT);
popupStage.initOwner(primaryStage);
popupStage.initModality(Modality.APPLICATION_MODAL);
popupStage.setScene(new Scene(pauseRoot, Color.TRANSPARENT));
resume.setOnAction(event -> {
root.setEffect(null);
animation.play();
popupStage.hide();
});
popupStage.show();
});
BorderPane.setAlignment(pauseButton, Pos.CENTER);
BorderPane.setMargin(pauseButton, new Insets(5));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private TranslateTransition createAnimation(Rectangle rect) {
TranslateTransition animation = new TranslateTransition(Duration.seconds(1), rect);
animation.setByX(400);
animation.setCycleCount(Animation.INDEFINITE);
animation.setAutoReverse(true);
animation.play();
return animation;
}
public static void main(String[] args) {
launch(args);
}
}

Categories