Get mouse position on node relative to it's coordinates and transforms


Question

I have 3D scene and I have pane inside the scene which has a rotate transform by x axis, I want to use this pane as strategic game board, but I have problem.

when I enter mouse inside a pane it gives me wrong position of cursor.

For example when I enter mouse from upper left corner(red circle) inside pane(rotated pane with black border) it should shows me (0,0) as cursor position inside the pane, But it shows something like (200 , 400).

rotated pane with black border

How can I solve this problem?

OR in other words, how can I get the mouse coordinate on node relative to node and its transforms?

Here is an example:

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.RotateBuilder;
import javafx.stage.Stage;


public class JFXRotationXOrds  extends Application{

    @Override
    public void start(Stage primaryStage) throws Exception {
        VBox root = new VBox();
        root.getChildren().add(new Rectangle(20, 20, Color.BLUE));  
        root.getChildren().add(new Circle(20, Color.RED));
        //root.rotateProperty().set(30);
        root.getTransforms().add(RotateBuilder.create().angle(-30).pivotX(0).pivotY(100).axis(new Point3D(1, 0, 0)).build());
        root.setStyle("-fx-border-color: black; -fx-border-width:5; ");

        root.setOnMouseMoved(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent arg0) {
                if(arg0.getEventType() == MouseEvent.MOUSE_MOVED){
                    System.out.println(arg0.getX() + "," + arg0.getY());
                }

            }
        });

        Scene scene = new Scene(root, 200, 500);
        primaryStage.setTitle("Rotation Coordinates Example");
        primaryStage.setScene(scene);
        scene.setCamera(PerspectiveCameraBuilder.create().fieldOfView(10).build());
        primaryStage.show();

    }

    public static void main(String[] args){
        Application.launch(args);
    }
}
1
4
2/27/2014 2:57:53 PM

Accepted Answer

Update

The problem has been fixed in the JDK8 Early Access Release. You can either download that version, or wait until it comes out. It was fixed in February as part of this ticket: RT-28129

Edited

I entered a ticket on the JavaFX Jira. You can follow it to see the status updates.

I've updated the demo to reflect your problem. It seemed to work when the transformation was using the z-axis (yesterday - different for me today), but not when the transformation was on the X or Y axes.

Hopefully this helps you some.

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Point3D;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.RotateBuilder;
import javafx.stage.Stage;


public class JFXRotationXOrds  extends Application{

    @Override
    public void start(Stage primaryStage) throws Exception {
        VBox root = new VBox();
        final Rotate rotate = RotateBuilder.create().angle(80).pivotX(100).pivotY(100).pivotZ(0).axis(new Point3D(1,0,0)).build();
        root.getTransforms().add(rotate);
        root.setStyle("-fx-border-color: black; -fx-border-width:5; ");

        root.setOnMouseMoved(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent arg0) {
                if(arg0.getEventType() == MouseEvent.MOUSE_MOVED){
                    System.out.println(arg0.getSceneX() + "," + arg0.getSceneY());
                }
            }
        });

        Scene scene = new Scene(root, 200, 500);
        PerspectiveCamera camera = new PerspectiveCamera();
        scene.setCamera(camera);
        primaryStage.setTitle("BorderPane Example");
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    public static void main(String[] args){
        Application.launch(args);
    }
}
2
8/27/2013 3:35:22 PM

As you didn't post any of your code this might sound obvious but did you add you mouse event handler to the pane ? If so (in my attempt to recreate your problem) the event.getX() and event.getY() methods returned the exepected position.

(did you use getSceneX() and getSceneY() ? in this case change to getX() and getY())

Another way is to correct the mouse position you get (position on the scene) by the position of your pane.

you can do this for x, y, z axes :

while (node != null){
   shift += node.getLayoutY(); 
   node = node.getParent();
}

then substract this shift to the position of the pointer you get

Edit : After looking at your code it seems that you are adding the MouseEvent handler to the root object. Therefore the event is triggered when the mouse is on the root object. If you add it to your rectangle for example the position of the mouse will be relative to the rectangle.

The following code is working for me (but maybe it doesn't reproduce well your problem).

public class JFXRotationXOrds extends Application{

@Override
public void start(Stage primaryStage) throws Exception {
    VBox root = new VBox();

    Rectangle rect = new Rectangle(20, 20, Color.BLUE);
    rect.setOnMouseMoved(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent arg0) {
            if(arg0.getEventType() == MouseEvent.MOUSE_MOVED){
                System.out.println("Rect : " + arg0.getX() + "," + arg0.getY());
            }

        }
    });
    root.getChildren().add(rect);  
    root.getChildren().add(new Circle(20, Color.RED));
    //root.rotateProperty().set(30);
    root.getTransforms().add(RotateBuilder.create().angle(30).pivotX(0).pivotY(100).pivotZ(100).build());
    root.setStyle("-fx-border-color: black; -fx-border-width:5; ");

    root.setOnMouseMoved(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent arg0) {
            if(arg0.getEventType() == MouseEvent.MOUSE_MOVED){
                System.out.println(arg0.getX() + "," + arg0.getY());
            }

        }
    });

    Scene scene = new Scene(root, 200, 500);
    primaryStage.setTitle("Rotation Coordinates Example");
    primaryStage.setScene(scene);
    primaryStage.show();

}

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

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon