'Bind the Z position relative to the scene in JavaFX

I currently have a project I am working on in JavaFX that simulates our solar system. The way it is made, a planet (which extends the Sphere class) orbits on a 2D plane, meaning the Z never gets changed according to the planet object. This was done to simplify the calculations of the orbit. The path the planet goes on is then drawn on this same plane. It looks a little bit like this:

Planet and its' orbit

I then rotate the group that contains the planet and the orbit to get a result that looks like this:

Planet and orbit group rotated

It is done by adding a transform to the group:

Group planeteSeule = new Group(PLANETES[i]);
planeteSeule.getTransforms().addAll(
    new Rotate(infoPlanetes[i].inclination, Rotate.X_AXIS),
    new Rotate(infoPlanetes[i].inclination, Rotate.Y_AXIS));

The problem with the way this is done is that there is no real way to bind the Z value the planet is at according to the scene (the Z value on the planet object itself is always 0), which is something that I need to get working to be able to interact with the planet on the Z-Axis. Is there any way to bind the Z to another property according to where it is in the scene and not according to the X it has in the group?



Solution 1:[1]

I am trying to access this z, the one not of the planet in the group, but the one of the planet in the world.

As outlined here, "the JavaFX node picking implementation will do that for you." Starting from this example, I enlarged the Sphere, added Text, and implemented a pair of mouse listeners. Move the mouse to the red planet, and the handler shows its name; this works no matter how the group is rotated. Print the parameter t, a MouseEvent, to see the coordinates; use the PickResult explicitly, as seen here; or simply update related model elements as desired.

text = new Text(edge / 5, -edge / 3 , "");
text.setFill(Color.BLUE);
text.setFont(new Font(20));
//sphere.setOnMouseEntered(t -> System.out.println(t));
sphere.setOnMouseEntered(t -> text.setText("Mars"));
sphere.setOnMouseExited(t -> text.setText(""));

See also JavaFX: Working with JavaFX Graphics: 7 Picking.

mars


import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.Sphere;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
import javafx.stage.Stage;

/**
 * @see https://stackoverflow.com/q/72282224/230513
 * @see https://stackoverflow.com/a/37755149/230513
 * @see https://stackoverflow.com/a/37743539/230513
 * @see https://stackoverflow.com/a/37370840/230513
 */
public class TriadBox extends Application {

    private static final double SIZE = 300;
    private final Content content = Content.create(SIZE);
    private double mousePosX, mousePosY, mouseOldX, mouseOldY, mouseDeltaX, mouseDeltaY;

    private static final class Content {

        private static final double WIDTH = 3;
        private final Xform group = new Xform();
        private final Group cube = new Group();
        private final Group axes = new Group();
        private final Box xAxis;
        private final Box yAxis;
        private final Box zAxis;
        private final Box box;
        private final Sphere sphere;
        private final Text text;

        private static Content create(double size) {
            Content c = new Content(size);
            c.cube.getChildren().addAll(c.box, c.sphere, c.text);
            c.axes.getChildren().addAll(c.xAxis, c.yAxis, c.zAxis);
            c.group.getChildren().addAll(c.cube, c.axes);
            return c;
        }

        private Content(double size) {
            double edge = 3 * size / 4;
            xAxis = createBox(edge, WIDTH, WIDTH, edge);
            yAxis = createBox(WIDTH, edge / 2, WIDTH, edge);
            zAxis = createBox(WIDTH, WIDTH, edge / 4, edge);
            box = new Box(edge, edge / 2, edge / 4);
            box.setDrawMode(DrawMode.LINE);
            sphere = new Sphere(24);
            PhongMaterial redMaterial = new PhongMaterial();
            redMaterial.setDiffuseColor(Color.CORAL.darker());
            redMaterial.setSpecularColor(Color.CORAL);
            sphere.setMaterial(redMaterial);
            sphere.setTranslateX(edge / 2);
            sphere.setTranslateY(-edge / 4);
            sphere.setTranslateZ(-edge / 8);
            text = new Text(edge / 5, -edge / 3 , "");
            text.setFill(Color.BLUE);
            text.setFont(new Font(20));
            sphere.setOnMouseEntered(t -> text.setText("Mars"));
            sphere.setOnMouseExited(t -> text.setText(""));
        }

        private Box createBox(double w, double h, double d, double edge) {
            Box b = new Box(w, h, d);
            b.setMaterial(new PhongMaterial(Color.AQUA));
            b.setTranslateX(-edge / 2 + w / 2);
            b.setTranslateY(edge / 4 - h / 2);
            b.setTranslateZ(edge / 8 - d / 2);
            return b;
        }
    }

    private static class Xform extends Group {

        private final Point3D px = new Point3D(1.0, 0.0, 0.0);
        private final Point3D py = new Point3D(0.0, 1.0, 0.0);
        private Rotate r;
        private Transform t = new Rotate();

        public void rx(double angle) {
            r = new Rotate(angle, px);
            this.t = t.createConcatenation(r);
            this.getTransforms().clear();
            this.getTransforms().addAll(t);
        }

        public void ry(double angle) {
            r = new Rotate(angle, py);
            this.t = t.createConcatenation(r);
            this.getTransforms().clear();
            this.getTransforms().addAll(t);
        }

        public void rz(double angle) {
            r = new Rotate(angle);
            this.t = t.createConcatenation(r);
            this.getTransforms().clear();
            this.getTransforms().addAll(t);
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("JavaFX 3D");
        Scene scene = new Scene(content.group, SIZE * 2, SIZE * 2, true);
        primaryStage.setScene(scene);
        scene.setFill(Color.BLACK);
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setFarClip(SIZE * 6);
        camera.setTranslateZ(-2 * SIZE);
        scene.setCamera(camera);
        scene.setOnMousePressed((MouseEvent e) -> {
            mousePosX = e.getSceneX();
            mousePosY = e.getSceneY();
            mouseOldX = e.getSceneX();
            mouseOldY = e.getSceneY();
        });
        scene.setOnMouseDragged((MouseEvent e) -> {
            mouseOldX = mousePosX;
            mouseOldY = mousePosY;
            mousePosX = e.getSceneX();
            mousePosY = e.getSceneY();
            mouseDeltaX = (mousePosX - mouseOldX);
            mouseDeltaY = (mousePosY - mouseOldY);
            if (e.isShiftDown()) {
                content.group.rz(-mouseDeltaX * 180.0 / scene.getWidth());
            } else if (e.isPrimaryButtonDown()) {
                content.group.rx(+mouseDeltaY * 180.0 / scene.getHeight());
                content.group.ry(-mouseDeltaX * 180.0 / scene.getWidth());
            } else if (e.isSecondaryButtonDown()) {
                camera.setTranslateX(camera.getTranslateX() - mouseDeltaX * 0.1);
                camera.setTranslateY(camera.getTranslateY() - mouseDeltaY * 0.1);
                camera.setTranslateZ(camera.getTranslateZ() + mouseDeltaY);
            }
        });
        scene.setOnScroll((final ScrollEvent e) -> {
            camera.setTranslateZ(camera.getTranslateZ() + e.getDeltaY());
        });
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1