Restricting a 3D object mouse drag movement to a plane in JavaFX

杀马特。学长 韩版系。学妹 提交于 2019-12-06 02:33:08

问题


I'm using JavaFX to move 3D cubes around by mouse drag. The cube should stay on the plane spanned by x and z axis. My solution works fairly well, however if I move the cube too fast with my mouse or when it encounters an object with a certain depth (y-Axis), it is assumed, that the mouse is moving on the y-Axis and the cube starts jumping forward or backwards. Is there a way to restrict the mouse to the xz-plane? A more complicated solution would be projecting the y length back to the xz-plane, but I got no clue how. I looked at JavaFX Moving 3D Objects, but couldn't adapt it to my case.

My code so far:

    private volatile double relMousePosX;
    private volatile double relMousePosZ;

    public void enableDragMove(PaneBox paneBox) {
        Group paneBoxGroup = paneBox.get();

        paneBoxGroup.setOnMousePressed((MouseEvent me) -> {
            if(isSelected(paneBox) && MouseButton.PRIMARY.equals(me.getButton())) {
                relMousePosX = me.getX();
                relMousePosZ = me.getZ();
            }
        });

        paneBoxGroup.setOnMouseDragged((MouseEvent me) -> {
            if(paneBoxGroup.focusedProperty().get() && MouseButton.PRIMARY.equals(me.getButton())) {
                setDragInProgress(paneBox, true);
                System.out.println(me.getY()); // should stay small value, but jumps to higher values at times, creating the problem.
                paneBoxGroup.setCursor(Cursor.MOVE);
                paneBox.setTranslateX(paneBox.getTranslateX() + (me.getX() - relMousePosX));
                paneBox.setTranslateZ(paneBox.getTranslateZ() + (me.getZ() - relMousePosZ));
            }
        });

        paneBoxGroup.setOnMouseReleased((MouseEvent me) -> {
            if(paneBoxGroup.focusedProperty().get() && MouseButton.PRIMARY.equals(me.getButton())) {
                setDragInProgress(paneBox, false);
                paneBoxGroup.setCursor(Cursor.DEFAULT);
            }
        });
   }

回答1:


As you have mentioned, there two possible approachs for 3D dragging:

  • Pure drag & drop, like you propose
  • unprojecting directions, like in this question.

For drag and drop events, we can detect a drag event on the 3D shape, when the event starts and finishes.

The trick here is, instead of listening to the in between dragging events on the shape, listen to drag over events on a possible target.

By defining a target, you can restrict easily the shape movements, since you will only update its position when you are dragging it only over this target.

So if you want to restrict movements on a plane, you can use a Rectangle as the target.

This snippet shows how this approach works:

@Override 
public void start(Stage stage) {
    final PerspectiveCamera cam = new PerspectiveCamera();
    cam.setFieldOfView(20);
    cam.setFarClip(10000);
    cam.setNearClip(0.01);
    cam.getTransforms().addAll(new Rotate(60,Rotate.X_AXIS),new Translate(-200,-200,300));

    final Group root = new Group();

    final Box floor = new Box(500, 500, 1);
    floor.setTranslateX(200);
    floor.setTranslateY(200);
    floor.setTranslateZ(50);
    floor.setMaterial(new PhongMaterial(Color.YELLOW));
    root.getChildren().add(floor);

    final Box box = new Box(50, 50, 50);
    box.setMaterial(new PhongMaterial(Color.RED));
    root.getChildren().add(box);

    final Rectangle rectangle = new Rectangle(400, 400, Color.TRANSPARENT);
    rectangle.setMouseTransparent(true);
    rectangle.setDepthTest(DepthTest.DISABLE);
    root.getChildren().add(rectangle);

    // D&D starts
    box.setOnDragDetected((MouseEvent event)-> {
        box.setMouseTransparent(true);
        rectangle.setMouseTransparent(false);
        box.setCursor(Cursor.MOVE);
        box.startFullDrag();
    });

    // D&D ends
    box.setOnMouseReleased((MouseEvent event)-> {
        box.setMouseTransparent(false);
        rectangle.setMouseTransparent(true);
        box.setCursor(Cursor.DEFAULT);
    });

    // While D&D, only confined to the rectangle
    rectangle.setOnMouseDragOver((MouseDragEvent event)-> {
        Point3D coords = event.getPickResult().getIntersectedPoint();
        coords = rectangle.localToParent(coords);
        box.setTranslateX(coords.getX());
        box.setTranslateY(coords.getY());
        box.setTranslateZ(coords.getZ());
    });

    final Scene scene = new Scene(root, 800, 600, true);
    scene.setCamera(cam);

    stage.setScene(scene);
    stage.setTitle("JavaFX 3D Drag&Drop");
    stage.show();
}

If you run it, you will see that you can pick the box and drag it all over the floor.

In this picture, I've added some color and stroke to the rectangle to see the limits of dragging.

Note also the changes in mouse transparent both in box and rectangle. The rectangle is not mouse transparent only during the dragging.



来源:https://stackoverflow.com/questions/29034080/restricting-a-3d-object-mouse-drag-movement-to-a-plane-in-javafx

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!