I'm trying to flip a coloured rectangle. Is it possible to use the rotateTransition to do this?
I have tried the following code:
public void rotateField(){
RotateTransition rt = new RotateTransition(Duration.millis(3000), field[4][4]);
rt.setByAngle(360);
rt.setCycleCount(1);
rt.play();
}
However, this doesn't flip the rectangle, it just rotates it.
I would like to actually flip the rectangle as you would flip a playing card.
Is it possible to use the rotateTransition class for this?
I like Sergey's solution, it is such a clever use of ScaleTransition and working in 2D means you don't need to deal with some of the complexities of 3D.
Here a couple of 3D rotation samples.
Rotates a 2D Node (an ImageView) round the Y axis (requires JavaFX 2.2 + 3D support).
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.layout.StackPane;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
public class QuickFlip extends Application {
#Override
public void start(Stage stage) throws Exception {
Node card = createCard();
stage.setScene(createScene(card));
stage.show();
RotateTransition rotator = createRotator(card);
rotator.play();
}
private Scene createScene(Node card) {
StackPane root = new StackPane();
root.getChildren().addAll(card);
Scene scene = new Scene(root, 600, 700, true, SceneAntialiasing.BALANCED);
scene.setCamera(new PerspectiveCamera());
return scene;
}
private Node createCard() {
return new ImageView(
new Image(
"http://www.ohmz.net/wp-content/uploads/2012/05/Game-of-Throne-Magic-trading-cards-2.jpg"
)
);
}
private RotateTransition createRotator(Node card) {
RotateTransition rotator = new RotateTransition(Duration.millis(10000), card);
rotator.setAxis(Rotate.Y_AXIS);
rotator.setFromAngle(0);
rotator.setToAngle(360);
rotator.setInterpolator(Interpolator.LINEAR);
rotator.setCycleCount(10);
return rotator;
}
public static void main(String[] args) {
launch();
}
}
Rotates a 3D Node (a TriangleMesh) round the Y axis (requires Java 8 + 3D support).
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CardFlip extends Application {
final Image CARD_IMAGE = new Image(
"http://fc05.deviantart.net/fs70/i/2010/345/7/7/vitam_et_mortem_by_obviouschild-d34oni2.png"
// sourced from: http://obviouschild.deviantart.com/art/Vitam-et-Mortem-189267194
);
final int W = (int) (CARD_IMAGE.getWidth() / 2);
final int H = (int) CARD_IMAGE.getHeight();
#Override
public void start(Stage stage) throws Exception {
Node card = createCard();
stage.setScene(createScene(card));
stage.show();
RotateTransition rotator = createRotator(card);
rotator.play();
}
private Scene createScene(Node card) {
StackPane root = new StackPane();
root.getChildren().addAll(card, new AmbientLight(Color.WHITE));
Scene scene = new Scene(root, W + 200, H + 200, true, SceneAntialiasing.BALANCED);
scene.setFill(Color.MIDNIGHTBLUE.darker().darker().darker().darker());
scene.setCamera(new PerspectiveCamera());
return scene;
}
private RotateTransition createRotator(Node card) {
RotateTransition rotator = new RotateTransition(Duration.millis(10000), card);
rotator.setAxis(Rotate.Y_AXIS);
rotator.setFromAngle(0);
rotator.setToAngle(360);
rotator.setInterpolator(Interpolator.LINEAR);
rotator.setCycleCount(10);
return rotator;
}
private Node createCard() {
MeshView card = new MeshView(createCardMesh());
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(CARD_IMAGE);
card.setMaterial(material);
return card;
}
private TriangleMesh createCardMesh() {
TriangleMesh mesh = new TriangleMesh();
mesh.getPoints().addAll(-1 * W/2, -1 * H/2 , 0, 1 * W/2, -1 * H/2, 0, -1 * W/2, 1 * H/2, 0, 1 * W/2, 1 * H/2, 0);
mesh.getFaces().addAll(0, 0, 2, 2, 3, 3, 3, 3, 1, 1, 0, 0);
mesh.getFaces().addAll(0, 4, 3, 7, 2, 6, 3, 7, 0, 4, 1, 5);
mesh.getTexCoords().addAll(0, 0, 0.5f, 0, 0, 1, 0.5f, 1, 0.5f, 0, 1, 0, 0.5f, 1, 1, 1);
return mesh;
}
public static void main(String[] args) {
launch();
}
}
Not in 2D world. But you can imitate card flip using two ScaleTransitions:
Rectangle front = new Rectangle(50, 50);
ScaleTransition stHideFront = new ScaleTransition(Duration.millis(1500), front);
stHideFront.setFromX(1);
stHideFront.setToX(0);
Rectangle back = new Rectangle(50, 50, Color.RED);
back.setScaleX(0);
ScaleTransition stShowBack = new ScaleTransition(Duration.millis(1500), back);
stShowBack.setFromX(0);
stShowBack.setToX(1);
stHideFront.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
stShowBack.play();
}
});
StackPane root = new StackPane();
root.getChildren().addAll(front, back);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
stHideFront.play();
Here is an extension of #jewelsea's answer. This example shows one way to flip and show the back of the card. The way I thought of this was to change the ImageView's image at half the duration of the RotateTransition.
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.layout.StackPane;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
boolean isFrontShowing = true;
Image frontImage = new Image(getClass().getResourceAsStream("image/front.jpg"));
Image backImage = new Image(getClass().getResourceAsStream("image/back.jpg"));
#Override
public void start(Stage stage) throws Exception {
ImageView card = createCard();
card.setOnMouseClicked((t) -> {
RotateTransition rotator = createRotator(card);
PauseTransition ptChangeCardFace = changeCardFace(card);
ParallelTransition parallelTransition = new ParallelTransition(rotator, ptChangeCardFace);
parallelTransition.play();
isFrontShowing = !isFrontShowing;
});
stage.setScene(createScene(card));
stage.show();
}
private Scene createScene(Node card) {
StackPane root = new StackPane();
root.getChildren().addAll(card);
Scene scene = new Scene(root, 600, 700, true, SceneAntialiasing.BALANCED);
scene.setCamera(new PerspectiveCamera());
return scene;
}
private ImageView createCard() {
ImageView card = new ImageView(frontImage);
return card;
}
private RotateTransition createRotator(ImageView card) {
RotateTransition rotator = new RotateTransition(Duration.millis(1000), card);
rotator.setAxis(Rotate.Y_AXIS);
if (isFrontShowing) {
rotator.setFromAngle(0);
rotator.setToAngle(180);
} else {
rotator.setFromAngle(180);
rotator.setToAngle(360);
}
rotator.setInterpolator(Interpolator.LINEAR);
rotator.setCycleCount(1);
return rotator;
}
private PauseTransition changeCardFace(ImageView card) {
PauseTransition pause = new PauseTransition(Duration.millis(500));
if (isFrontShowing) {
pause.setOnFinished(
e -> {
card.setImage(backImage);
});
} else {
pause.setOnFinished(
e -> {
card.setImage(frontImage);
});
}
return pause;
}
public static void main(String[] args) {
launch();
}
}
Related
I have an object, like a box in this example.
I want this box to move in a sine to the left on the sphere when the Z-axis is rotated. But after the box has made a curve, i.e. the rotation of the Z-axis is back to 0. The sphere no longer rotates downwards as it did at the start, but upwards to the right.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.*;
import javafx.stage.Stage;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import java.util.ArrayList;
public class TestFX extends Application {
public static final float WIDTH = 1400;
public static final float HEIGHT = 1000;
private final ArrayList<String> input = new ArrayList<>();
private final Sphere sphere = new Sphere(500);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Flugzeug flieger = new Flugzeug(30, 30, 50);
flieger.setMaterial(new PhongMaterial(Color.GREEN));
flieger.setTranslateZ(330);
flieger.getTransforms().add(new Rotate(20, Rotate.X_AXIS));
sphere.translateZProperty().set(710);
sphere.translateYProperty().set(420);
PhongMaterial phongMaterial = new PhongMaterial();
phongMaterial.setDiffuseMap(new Image(getClass().getResourceAsStream("Earth_diffuse_8k.png")));
sphere.setMaterial(phongMaterial);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(40000);
Group root = new Group(flieger, sphere);
Scene scene = new Scene(root, WIDTH, HEIGHT, true);
scene.setCamera(camera);
primaryStage.setTitle("FliegenFX");
primaryStage.setScene(scene);
primaryStage.show();
new AnimationTimer() {
#Override
public void handle(long now) {
initGameLogic(flieger);
dreheErde(flieger, sphere);
}
}.start();
scene.setOnKeyPressed(event -> {
String code = event.getCode().toString();
if (!input.contains(code))
input.add(code);
});
scene.setOnKeyReleased(event -> {
String code = event.getCode().toString();
input.remove(code);
});
}
// TODO Erddrehung nach Drehung beibehalten
private void dreheErde(Flugzeug flieger, Sphere erde) {
double rotationFactor = 0.005;
double drehung = flieger.getDrehung() * rotationFactor;
double amplitude = 2;
double frequency = 0.001;
float angle = (float) (Math.sin(drehung * frequency) * amplitude);
Quaternionf quat = new Quaternionf();
Matrix4f matrix4f = new Matrix4f();
Affine affine = new Affine();
quat.rotateY(angle);
quat.rotateZ(angle);
quat.rotateX(-0.001f);
quat.normalize();
quat.get(matrix4f);
float[] matrixArray = new float[16];
matrix4f.get(matrixArray);
double[] matrix = new double[16];
for (int i = 0 ; i < matrixArray.length; i++)
{
matrix[i] = matrixArray[i];
}
affine.append(matrix, MatrixType.MT_3D_4x4, 0);
erde.getTransforms().add(affine);
}
private void initGameLogic(Flugzeug flieger) {
if (input.contains("LEFT")) {
flieger.rotateByZ(-0.5);
}
if (input.contains("RIGHT")) {
flieger.rotateByZ(0.5);
}
}
private class Flugzeug extends Box{
private double drehung = 0;
private Rotate r;
private Transform t = new Rotate();
public Flugzeug(double width, double height, double depth){
super(width, height, depth);
}
public void rotateByZ(double ang){
drehung += ang;
r = new Rotate(ang, Rotate.Z_AXIS);
t = t.createConcatenation(r);
getTransforms().clear();
getTransforms().addAll(t);
}
public double getDrehung() {
return drehung;
}
}
}
I have tried many different ways with trigonometry but they have not been successful. This was for example to move the X-axis of the sphere also according to the sine or to subtract the movements of the Y and Z axis of the sphere as cosine.
I think it has to be transformed in a Rotation Matrix, but i‘ve never had matrix calculation.
Rotating a Group inside another Group in 3d scene
In this approach Box instance doesn't seem to move 'cause it's moving with the camera . Camera and box are moving together because is it's Group parent who is rotating with keyboard event . The Sphere node is in another group and is not affected . Sphere itself is rotating in Y axis
App.java
public class App extends Application {
#Override
public void start(Stage stage) {
Shape3D sphere = new Sphere(10);
PhongMaterial mat = new PhongMaterial();
mat.setDiffuseMap(new Image("https://www.h-schmidt.net/map/map.jpg"));
sphere.setMaterial(mat);
RotateTransition sphereRotation = new RotateTransition(Duration.seconds(40), sphere);
sphereRotation.setAxis(Rotate.Y_AXIS);
sphereRotation.setToAngle(360);
sphereRotation.setInterpolator(Interpolator.LINEAR);
sphereRotation.setCycleCount(Animation.INDEFINITE);
sphereRotation.play();
Shape3D box = new Box(2, 2, 2);
box.setMaterial(new PhongMaterial(Color.ORANGE));
box.setTranslateZ(-11);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-30);
var planeGroup = new Group(box, camera);
Group sphereGroup = new Group(sphere);
Group group3d = new Group(planeGroup, sphereGroup);
Scene scene = new Scene(group3d, 640, 480, true, SceneAntialiasing.BALANCED);
scene.setOnKeyPressed((t) -> {
if (t.getCode() == KeyCode.UP) {
Rotate r = new Rotate(-2);
r.setAxis(Rotate.X_AXIS);
planeGroup.getTransforms().add(r);
}
if (t.getCode() == KeyCode.LEFT) {
Rotate r = new Rotate(2);
r.setAxis(Rotate.Y_AXIS);
planeGroup.getTransforms().add(r);
}
if (t.getCode() == KeyCode.RIGHT) {
Rotate r = new Rotate(-2);
r.setAxis(Rotate.Y_AXIS);
planeGroup.getTransforms().add(r);
}
if (t.getCode() == KeyCode.DOWN) {
Rotate r = new Rotate(2);
r.setAxis(Rotate.X_AXIS);
planeGroup.getTransforms().add(r);
}
});
scene.setCamera(camera);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I have a question that as per object concerns the Canvas and its GraphiContext.
In the following code I have reported a JavaFX Application that has the Canvas as its main object.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class canvasSVG extends Application{
private Double angle = 0.0;
#Override
public void start(Stage stage) {
Canvas canvas = new Canvas(500, 500);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.GREEN);
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
String svgPath = "m 0 10 v -24 M 0 -14 L 8 -16 M -2 10 h 4 l -2 5 l -2 -5";
canvas.setOnMousePressed(e->{
if(e.getButton()==MouseButton.PRIMARY) {
System.out.println("PUT: "+ angle);
gc.save();
gc.setStroke(Color.BLUE);
gc.translate(250,250);
gc.rotate(angle);
gc.scale(10, 10);
gc.appendSVGPath(svgPath);
gc.stroke();
angle = angle+50;
gc.restore();
}
});
canvas.setOnMouseClicked(e->{
if(e.getButton()==MouseButton.SECONDARY) {
System.out.println("CLEAN "+ angle);
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.setFill(Color.GREEN);
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
});
StackPane pane = new StackPane(canvas);
Scene scene = new Scene(pane);
stage.setTitle("Canvas Demo");
stage.setScene(scene);
stage.sizeToScene();
stage.centerOnScreen();
stage.show();
}
public static void main(String[] args) {
launch(canvasSVG.class);
}
}
On clicking with the left mouse button I add the SVG path to the canvas and on clicking with the right mouse button I remove it from the canvas.
The problem is that once the rotation has happened the first time, the angle of the geometric figure in the canvas doesn't change.
The method appendSVGPath() specifies: "The coordinates are transformed by the current transform as they are added to the path and unaffected by subsequent changes to the transform." As an alternative, rotate the enclosing Canvas as shown below. Also consider this alternate approach to scaling SVGPath.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/** #see https://stackoverflow.com/a/70945900/230513 */
public class CanvasSVG extends Application {
private static final double DIM = 400;
private static final Color BG = Color.GREEN;
private final Canvas canvas = new Canvas(DIM, DIM);
private final GraphicsContext gc = canvas.getGraphicsContext2D();
private Double angle = 0.0;
#Override
public void start(Stage stage) {
clear();
draw();
canvas.setOnMousePressed(e -> {
if (e.getButton() == MouseButton.PRIMARY) {
draw();
angle += 50;
canvas.setRotate(angle);
} else if (e.getButton() == MouseButton.SECONDARY) {
clear();
angle = 50.0;
}
});
StackPane pane = new StackPane(canvas);
pane.setBackground(new Background(new BackgroundFill(
BG, CornerRadii.EMPTY, Insets.EMPTY)));
Scene scene = new Scene(pane);
stage.setTitle("Canvas Demo");
stage.setScene(scene);
stage.centerOnScreen();
stage.show();
}
private void draw() {
String svgPath = "m 0 10 v -24 M 0 -14 L 8 -16 M -2 10 h 4 l -2 5 l -2 -5";
gc.save();
gc.setStroke(Color.BLUE);
gc.translate(DIM / 2, DIM / 2);
gc.scale(10, 10);
gc.appendSVGPath(svgPath);
gc.stroke();
gc.restore();
}
private void clear() {
gc.setFill(BG);
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
public static void main(String[] args) {
launch(CanvasSVG.class);
}
}
I am trying to create a first person camera in JavaFX based on bindings. The camera and the actual position both work perfectly. The only problem is that they don’t match! As you can see in the picture, the actual position (red box) is in the middle of the circle, but the camera is outside. How can I change that? What did I do wrong?
The Player class handles the PerspectiveCamera.
package game;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.PerspectiveCamera;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.robot.Robot;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class Player extends Character {
private static final Robot ROBOT = new Robot();
private DoubleProperty relativeCenterX = new SimpleDoubleProperty();
private DoubleProperty relativeCenterY = new SimpleDoubleProperty();
protected PerspectiveCamera camera = new PerspectiveCamera();
protected Rotate xAxis = new Rotate(0, 250, 0, 0, Rotate.Y_AXIS);
protected Rotate yAxis = new Rotate(0, 0, 250, 0, Rotate.X_AXIS);
protected Translate translate = new Translate();
protected DoubleProperty centerX = new SimpleDoubleProperty();
protected DoubleProperty centerY = new SimpleDoubleProperty();
#SuppressWarnings("exports")
public Player(Stage stage) {
camera.getTransforms().addAll(xAxis, yAxis);
centerX.bind(stage.widthProperty().divide(2));
centerY.bind(stage.heightProperty().divide(2));
relativeCenterX.bind(stage.xProperty().add(centerX));
relativeCenterY.bind(stage.yProperty().add(centerY));
camera.translateXProperty().bind(posX.subtract(centerX));
camera.translateYProperty().bind(posZ);
camera.translateZProperty().bind(posY.subtract(centerY));
xAxis.angleProperty().bind(viewX.subtract(90));
yAxis.angleProperty().bind(viewY);
translate.xProperty().bind(posX);
translate.zProperty().bind(posY);
translate.yProperty().bind(posZ);
}
#SuppressWarnings("exports")
public EventHandler<KeyEvent> getKeyHandle() {
return e -> {
switch (e.getCode()) {
case A:
view(-1, 0);
break;
case D:
view(1, 0);
break;
case W:
move(1, 1, 0);
break;
case S:
move(-1, -1, 0);
break;
case SPACE:
move(0, 0, 10);
break;
case F:
move(0, 0, -10);
break;
default:
break;
}
};
}
#SuppressWarnings("exports")
public EventHandler<MouseEvent> getMouseHandle() {
return e -> {
view(e.getSceneX() - centerX.doubleValue(), centerY.doubleValue() - e.getSceneY());
Platform.runLater(() -> {
ROBOT.mouseMove(relativeCenterX.intValue(), relativeCenterY.intValue());
});
};
}
#SuppressWarnings("exports")
public PerspectiveCamera getPespectiveCamera() {
return camera;
}
}
The Character class calculates position and view.
package game;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
public abstract class Character {
protected DoubleProperty posX = new SimpleDoubleProperty();
protected DoubleProperty posY = new SimpleDoubleProperty();
protected DoubleProperty posZ = new SimpleDoubleProperty();
protected DoubleProperty viewX = new SimpleDoubleProperty();
protected DoubleProperty viewY = new SimpleDoubleProperty();
protected DoubleProperty speed = new SimpleDoubleProperty(10);
public void move(double x, double y, double z) {
double fX = Math.cos(Math.toRadians(viewX.get()));
double fY = -Math.sin(Math.toRadians(viewX.get()));
double fZ = 1;
posX.set(posX.get() + fX * x * speed.get());
posY.set(posY.get() + fY * y * speed.get());
posZ.set(posZ.get() + fZ * z);
}
public void view(double x, double y) {
viewX.set(viewX.get() + x);
viewY.set(viewY.get() + y);
}
#SuppressWarnings("exports")
public DoubleProperty posXPorperty() {
return posX;
}
#SuppressWarnings("exports")
public DoubleProperty posYPorperty() {
return posY;
}
#SuppressWarnings("exports")
public DoubleProperty posZPorperty() {
return posZ;
}
#SuppressWarnings("exports")
public DoubleProperty viewXPorperty() {
return viewX;
}
#SuppressWarnings("exports")
public DoubleProperty viewYPorperty() {
return viewY;
}
}
My Application, which shows the total graphical content.
package graphics;
import game.Player;
import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class GameStage extends Application implements Runnable {
#Override
public void run() {
launch();
}
#SuppressWarnings("exports")
#Override
public void start(Stage stage) throws Exception {
BorderPane pane = new BorderPane();
Scene scene = new Scene(pane, 500, 500);
Group content = new Group(), map = new Group();
ContentScene subscene = new ContentScene(content, map, 500, 500);
subscene.widthProperty().bind(scene.widthProperty());
subscene.heightProperty().bind(scene.heightProperty());
pane.getChildren().add(subscene);
pane.setBottom(map);
Player player = new Player(stage);
Box box = new Box(50, 50, 50);
box.translateXProperty().bind(player.posXPorperty());
box.translateYProperty().bind(player.posZPorperty());
box.translateZProperty().bind(player.posYPorperty());
box.rotateProperty().bind(player.viewXPorperty());
box.setMaterial(new PhongMaterial(Color.RED));
content.getChildren().add(box);
Rectangle rectangle = new Rectangle(5, 5);
rectangle.translateXProperty().bind(player.posXPorperty().divide(10));
rectangle.translateYProperty().bind(player.posYPorperty().divide(10));
rectangle.setFill(Color.RED);
map.getChildren().add(rectangle);
subscene.setCamera(player.getPespectiveCamera());
scene.addEventHandler(KeyEvent.KEY_PRESSED, player.getKeyHandle());
scene.addEventHandler(MouseEvent.MOUSE_MOVED, player.getMouseHandle());
scene.setFill(Color.BLACK);
Cursor cursor = Cursor.CROSSHAIR;
scene.setCursor(cursor);
stage.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() != KeyCode.F11) {
return;
}
if (stage.isFullScreen()) {
stage.setFullScreen(false);
} else {
stage.setFullScreen(true);
}
});
stage.setAlwaysOnTop(true);
stage.setScene(scene);
stage.show();
}
private class ContentScene extends SubScene {
public ContentScene(Group content, Group map, double width, double height) {
super(content, width, height, true, SceneAntialiasing.BALANCED);
PhongMaterial material = new PhongMaterial(Color.AQUA);
for (int v = 0; v < 3_600; v += 180) {
for (int y = 0; y < 500; y += 100) {
Box box = new Box(50, 50, 50);
box.setTranslateX(Math.sin(v / 10) * 1_000);
box.setTranslateY(y);
box.setTranslateZ(Math.cos(v / 10) * 1_000);
box.setMaterial(material);
content.getChildren().add(box);
Rectangle rectangle = new Rectangle(5, 5);
rectangle.translateXProperty().bind(box.translateXProperty().divide(10));
rectangle.translateYProperty().bind(box.translateZProperty().divide(10));
rectangle.setFill(Color.AQUA);
map.getChildren().add(rectangle);
}
}
}
}
}
Thanks to Thomas, I was able to solve the problem. The code now loks like this:
package graphics;
import game.Player;
import javafx.application.Application;
import javafx.scene.AmbientLight;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class GameStage extends Application implements Runnable {
#Override
public void run() {
launch();
}
#SuppressWarnings("exports")
#Override
public void start(Stage stage) throws Exception {
// Parents
BorderPane pane = new BorderPane();
Group content = new Group(new AmbientLight()), map = new Group();
Pane stackpane = new Pane(map);
// Scenes
Scene scene = new Scene(pane, 500, 500);
SubScene contentSubscene = new SubScene(content, 500, 500, true, SceneAntialiasing.BALANCED);
contentSubscene.widthProperty().bind(scene.widthProperty());
contentSubscene.heightProperty().bind(scene.heightProperty());
SubScene minimapSubscene = new SubScene(stackpane, 256, 256);
minimapSubscene.setFill(Color.DARKGREY);
pane.getChildren().add(contentSubscene);
pane.setBottom(minimapSubscene);
// Create Player
Player player = new Player(stage);
Rectangle currentPosition = new Rectangle(5, 5);
currentPosition.layoutXProperty().bind(minimapSubscene.widthProperty().divide(2));
currentPosition.layoutYProperty().bind(minimapSubscene.heightProperty().divide(2));
currentPosition.setFill(Color.RED);
stackpane.getChildren().add(currentPosition);
map.layoutXProperty().bind(player.posXPorperty().divide(-10).add(minimapSubscene.widthProperty().divide(2)));
map.layoutYProperty().bind(player.posYPorperty().divide(-10).add(minimapSubscene.heightProperty().divide(2)));
// Create Box in
PhongMaterial material = new PhongMaterial(Color.AQUA);
for (int v = 0; v < 3_600; v += 180) {
for (int y = 0; y < 500; y += 100) {
Box box = new Box(50, 50, 50);
box.setTranslateX(Math.sin(v / 10) * 1_000);
box.setTranslateY(y);
box.setTranslateZ(Math.cos(v / 10) * 1_000);
box.setMaterial(material);
content.getChildren().add(box);
Rectangle boxPosition = new Rectangle(5, 5);
boxPosition.translateXProperty().bind(box.translateXProperty().divide(10));
boxPosition.translateYProperty().bind(box.translateZProperty().divide(10));
boxPosition.setFill(Color.AQUA);
map.getChildren().add(boxPosition);
}
}
contentSubscene.setCamera(player.getPespectiveCamera());
scene.addEventHandler(KeyEvent.KEY_PRESSED, player.getKeyHandle());
scene.addEventHandler(MouseEvent.MOUSE_MOVED, player.getMouseHandle());
scene.setCursor(Cursor.CROSSHAIR);
scene.setFill(Color.WHITE);
stage.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() != KeyCode.F11) {
return;
}
if (stage.isFullScreen()) {
stage.setFullScreen(false);
} else {
stage.setFullScreen(true);
}
});
stage.setAlwaysOnTop(true);
stage.setScene(scene);
stage.show();
}
}
package game;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
public abstract class Character {
protected DoubleProperty posX = new SimpleDoubleProperty();
protected DoubleProperty posY = new SimpleDoubleProperty();
protected DoubleProperty posZ = new SimpleDoubleProperty();
protected DoubleProperty viewX = new SimpleDoubleProperty();
protected DoubleProperty viewY = new SimpleDoubleProperty();
protected DoubleProperty speed = new SimpleDoubleProperty(10);
public void move(double x, double y, double z) {
double fX = Math.cos(Math.toRadians(viewX.get()));
double fY = -Math.sin(Math.toRadians(viewX.get()));
double fZ = 1;
posX.set(posX.get() + fX * x * speed.get());
posY.set(posY.get() + fY * y * speed.get());
posZ.set(posZ.get() + fZ * z);
}
public void view(double x, double y) {
viewX.set(viewX.get() + x);
viewY.set(viewY.get() + y);
}
#SuppressWarnings("exports")
public DoubleProperty posXPorperty() {
return posX;
}
#SuppressWarnings("exports")
public DoubleProperty posYPorperty() {
return posY;
}
#SuppressWarnings("exports")
public DoubleProperty posZPorperty() {
return posZ;
}
#SuppressWarnings("exports")
public DoubleProperty viewXPorperty() {
return viewX;
}
#SuppressWarnings("exports")
public DoubleProperty viewYPorperty() {
return viewY;
}
}
package game;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.PerspectiveCamera;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.robot.Robot;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class Player extends Character {
private static final Robot ROBOT = new Robot();
private DoubleProperty relativeCenterX = new SimpleDoubleProperty();
private DoubleProperty relativeCenterY = new SimpleDoubleProperty();
protected PerspectiveCamera camera = new PerspectiveCamera(true);
protected Rotate xAxis = new Rotate(0, 250, 0, 0, Rotate.Y_AXIS);
protected Rotate yAxis = new Rotate(0, 0, 250, 0, Rotate.X_AXIS);
protected DoubleProperty centerX = new SimpleDoubleProperty();
protected DoubleProperty centerY = new SimpleDoubleProperty();
#SuppressWarnings("exports")
public Player(Stage stage) {
camera.getTransforms().addAll(xAxis, yAxis);
camera.setFieldOfView((40 + 62) / 2);
camera.setNearClip(0.1);
camera.setFarClip(100000);
camera.setVerticalFieldOfView(true);
centerX.bind(stage.widthProperty().divide(2));
centerY.bind(stage.heightProperty().divide(2));
relativeCenterX.bind(stage.xProperty().add(centerX));
relativeCenterY.bind(stage.yProperty().add(centerY));
xAxis.angleProperty().bind(viewX.subtract(90));
yAxis.angleProperty().bind(viewY);
camera.translateXProperty().bind(posX);
camera.translateZProperty().bind(posY);
camera.translateYProperty().bind(posZ);
}
#SuppressWarnings("exports")
public EventHandler<KeyEvent> getKeyHandle() {
return e -> {
switch (e.getCode()) {
case A:
view(-1, 0);
break;
case D:
view(1, 0);
break;
case W:
move(1, 1, 0);
break;
case S:
move(-1, -1, 0);
break;
case SPACE:
move(0, 0, 10);
break;
case F:
move(0, 0, -10);
break;
default:
break;
}
};
}
#SuppressWarnings("exports")
public EventHandler<MouseEvent> getMouseHandle() {
return e -> {
view(e.getSceneX() - centerX.doubleValue(), centerY.doubleValue() - e.getSceneY());
Platform.runLater(() -> {
ROBOT.mouseMove(relativeCenterX.intValue(), relativeCenterY.intValue());
});
};
}
#SuppressWarnings("exports")
public PerspectiveCamera getPespectiveCamera() {
return camera;
}
}
I am working on a javafx program to create three buttons which are "circle", "ellipse" and "reverse". The circle button is the first thing shown when the program is run, the reverse button is supposed to correspond with the movement of the rectangle around the circle. I am having trouble getting the reverse button to work, I set autoReverse to true but it isn't doing anything. My second issue with the code is when the ellipse button is clicked the class MyEllipse isn't showing the ellipse shape like it is supposed to and it's not removing the circle animation from the pane. I tried to create a new pane in the EventHandler for the buttonellipse but I am assuming that isn't the correct way to do it. Any help on these two issues would be greatly appreciated.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.animation.PathTransition;
import javafx.scene.paint.Color;
import javafx.animation.Timeline;
import javafx.scene.shape.Circle;
import javafx.util.Duration;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Ellipse;
import javafx.scene.control.Button;
import javafx.event.ActionEvent;
import javafx.scene.layout.HBox;
import javafx.event.EventHandler;
import javafx.stage.Stage;
public class exam3b extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rectangle = new Rectangle (0, 0, 25, 50);
rectangle.setFill(Color.ORANGE);
Circle circle = new Circle(115, 90, 45);
circle.setFill(Color.WHITE);
circle.setStroke(Color.BLACK);
PathTransition pt = new PathTransition();
pt.setDuration(Duration.millis(4000));
pt.setPath(circle);
pt.setNode(rectangle);
pt.setOrientation(
PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pt.setCycleCount(Timeline.INDEFINITE);
pt.setAutoReverse(false);
pt.play();
circle.setOnMousePressed(e -> pt.pause());
circle.setOnMouseReleased(e -> pt.play());
HBox panel = new HBox(10);
panel.setAlignment(Pos.BOTTOM_CENTER);
Button button = new Button("Circle");
Button buttonellipse = new Button("Ellipse");
Button reverse = new Button("Reverse");
panel.getChildren().addAll(button,buttonellipse,reverse);
button.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
reverse.setVisible(true);
}
});
reverse.setOnAction(new EventHandler<ActionEvent>() //supposed to make rectangle move in reverse direction
{
#Override
public void handle(ActionEvent e)
{
pt.setAutoReverse(true);
}
});
buttonellipse.setOnAction(new EventHandler<ActionEvent>() //button to make ellipse appear from class MyEllipse, reverse button is supposed to disappear
{
#Override
public void handle(ActionEvent e)
{
reverse.setVisible(false);
Pane ellipse = new Pane();
ellipse.getChildren().add(new MyEllipse());
}
});
Pane pane = new Pane();
pane.getChildren().addAll(panel,circle,rectangle);
Scene scene = new Scene(pane, 350, 250);
primaryStage.setTitle("exam3b");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class MyEllipse extends Pane
{
private void paint() {
getChildren().clear();
for (int i = 0; i < 16; i++) {
Ellipse e1 = new Ellipse(getWidth() / 2, getHeight() / 2,
getWidth() / 2 - 50, getHeight() / 2 - 50);
e1.setStroke(Color.color(Math.random(), Math.random(),
Math.random()));
e1.setFill(Color.WHITE);
e1.setRotate(i * 180 / 16);
getChildren().add(e1);
}
}
#Override
public void setWidth(double width) {
super.setWidth(width);
paint();
}
#Override
public void setHeight(double height) {
super.setHeight(height);
paint();
}
}
You had an issue with how you were using MyEllipse, in that you were never adding it to your root pane nor were you settign the width/height (which leads to your paint method being called). I renamed your root pane to 'root'. Created an instance of 'MyEllipse', and in the 'Ellipse' button i add the instance of 'MyEllipse' to the root pane.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.animation.PathTransition;
import javafx.scene.paint.Color;
import javafx.animation.Timeline;
import javafx.scene.shape.Circle;
import javafx.util.Duration;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Ellipse;
import javafx.scene.control.Button;
import javafx.event.ActionEvent;
import javafx.scene.layout.HBox;
import javafx.event.EventHandler;
import javafx.stage.Stage;
public class exam3b extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rectangle = new Rectangle(0, 0, 25, 50);
rectangle.setFill(Color.ORANGE);
Circle circle = new Circle(115, 90, 45);
circle.setFill(Color.WHITE);
circle.setStroke(Color.BLACK);
PathTransition pt = new PathTransition();
pt.setDuration(Duration.millis(4000));
pt.setPath(circle);
pt.setNode(rectangle);
pt.setOrientation(
PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pt.setCycleCount(Timeline.INDEFINITE);
pt.setAutoReverse(true);
pt.play();
circle.setOnMousePressed(e -> pt.pause());
circle.setOnMouseReleased(e -> pt.play());
HBox panel = new HBox(10);
panel.setAlignment(Pos.BOTTOM_CENTER);
Button button = new Button("Circle");
Button buttonellipse = new Button("Ellipse");
Button reverse = new Button("Reverse");
panel.getChildren().addAll(button, buttonellipse, reverse);
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
reverse.setVisible(true);
}
});
reverse.setOnAction(new EventHandler<ActionEvent>() //supposed to make rectangle move in reverse direction
{
#Override
public void handle(ActionEvent e) {
pt.setAutoReverse(true);
}
});
Pane root = new Pane();
MyEllipse myEllipse = new MyEllipse();
myEllipse.setWidth(200);
myEllipse.setHeight(400);
buttonellipse.setOnAction(new EventHandler<ActionEvent>() //button to make ellipse appear from class MyEllipse, reverse button is supposed to disappear
{
#Override
public void handle(ActionEvent e) {
// reverse.setVisible(false);
if (root.getChildren().contains(myEllipse)) {
root.getChildren().remove(myEllipse);
} else {
System.out.println("adding ellipse");
root.getChildren().add(myEllipse);
}
}
});
root.getChildren().addAll(panel, circle, rectangle);
Scene scene = new Scene(root, 350, 250);
primaryStage.setTitle("exam3b");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class MyEllipse extends Pane {
public MyEllipse() {
}
private void paint() {
getChildren().clear();
for (int i = 0; i < 16; i++) {
Ellipse e1 = new Ellipse(getWidth() / 2, getHeight() / 2,
getWidth() / 2 - 50, getHeight() / 2 - 50);
e1.setStroke(Color.color(Math.random(), Math.random(),
Math.random()));
e1.setFill(Color.WHITE);
e1.setStrokeWidth(1);
e1.setRotate(i * 180 / 16);
getChildren().add(e1);
}
}
#Override
public void setWidth(double width) {
super.setWidth(width);
paint();
}
#Override
public void setHeight(double height) {
super.setHeight(height);
paint();
}
}
My JavaFX slider does not change value when I'm trying to move.
My slider is inside a GridPane layout.
My code looks like this, if you need more just ask :
root = new GridPane();
root.setLayoutY(canvasHeight);
root.setGridLinesVisible(true);
root.setPadding(new Insets(10, 10, 10, 10));
root.setVgap(2);
Label lblAmount = new Label("Amount of fireworks : ");
GridPane.setConstraints(lblAmount, 0, 0);
root.getChildren().add(lblAmount);
Slider sliAmount = new Slider();
sliAmount.setMin(1);
sliAmount.setMax(10);
sliAmount.setValue(5);
sliAmount.setMaxWidth(100);
root.getChildren().add(sliAmount);
GridPane.setConstraints(sliAmount, 1, 0);
Label lblPSize = new Label("Size of particles : ");
GridPane.setConstraints(lblPSize, 0, 1);
root.getChildren().add(lblPSize);
root.getRowConstraints().add(new RowConstraints(15));
root.getColumnConstraints().add(new ColumnConstraints(140));
Here's a GIF to better understand what the problem is
Here's what it looks like in my small program :
EDIT : More code
Since my program is very small and has pretty much only the slider implemented here is all my code.
Main class
public class Main extends Application {
private final int CANVAS_WIDTH = 1600;
private final int CANVAS_HEIGHT = 750;
private final int MENU_HEIGHT = 150;
private GraphicsContext graphics;
private GUI gui;
#Override
public void start(Stage primaryStage) throws Exception{
gui = new GUI(CANVAS_WIDTH, CANVAS_HEIGHT, MENU_HEIGHT);
gui.setup();
Canvas canvas = new Canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
graphics = canvas.getGraphicsContext2D();
clearCanvas();
gui.getRoot().getChildren().add(canvas);
primaryStage.setTitle("FireworkSim by Cedric Martens");
primaryStage.setScene(new Scene(gui.getRoot(), CANVAS_WIDTH, CANVAS_HEIGHT + MENU_HEIGHT, Color.rgb(137, 182, 255)));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private void clearCanvas()
{
//graphics.setFill(Color.BLACK);
//graphics.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
}
}
GUI class
public class GUI {
private GridPane root;
private int canvasWidth;
private int canvasHeight;
private int menuHeight;
public GUI(int canvasWidth, int canvasHeight, int menuHeight)
{
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
this.menuHeight = menuHeight;
}
public void setup()
{
root = new GridPane();
root.setLayoutY(canvasHeight);
root.setGridLinesVisible(true);
root.setPadding(new Insets(10, 10, 10, 10));
root.setVgap(2);
Label lblAmount = new Label("Amount of fireworks : ");
GridPane.setConstraints(lblAmount, 0, 0);
root.getChildren().add(lblAmount);
Slider sliAmount = new Slider();
sliAmount.setMin(1);
sliAmount.setMax(10);
sliAmount.setValue(5);
sliAmount.setMaxWidth(100);
root.getChildren().add(sliAmount);
GridPane.setConstraints(sliAmount, 1, 0);
Label lblPSize = new Label("Size of particles : ");
GridPane.setConstraints(lblPSize, 0, 1);
root.getChildren().add(lblPSize);
root.getRowConstraints().add(new RowConstraints(15));
root.getColumnConstraints().add(new ColumnConstraints(140));
}
public GridPane getRoot() {
return root;
}
}
You're placing the canvas directly in the grid pane created by the GUI class, without specifying any layout properties (row and column indexes, for example). So the canvas is directly overlaying the grid pane, and receives the mouse events, preventing the slider from receiving them. Instead, use a layout pane that lays things out appropriately. For example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application {
private final int CANVAS_WIDTH = 1600;
private final int CANVAS_HEIGHT = 750;
private final int MENU_HEIGHT = 150;
private GraphicsContext graphics;
private GUI gui;
#Override
public void start(Stage primaryStage) throws Exception{
BorderPane root = new BorderPane();
gui = new GUI(CANVAS_WIDTH, CANVAS_HEIGHT, MENU_HEIGHT);
gui.setup();
Canvas canvas = new Canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
graphics = canvas.getGraphicsContext2D();
clearCanvas();
root.setCenter(canvas);
root.setBottom(gui.getRoot());
primaryStage.setTitle("FireworkSim by Cedric Martens");
primaryStage.setScene(new Scene(root, CANVAS_WIDTH, CANVAS_HEIGHT + MENU_HEIGHT, Color.rgb(137, 182, 255)));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private void clearCanvas()
{
graphics.setFill(Color.BLACK);
graphics.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
}
}