Limitazione di un movimento di trascinamento del mouse di un oggetto 3D su un piano in JavaFX


Sto usando JavaFX per spostare i cubi 3D con il trascinamento del mouse. Il cubo dovrebbe rimanere sul piano attraversato dall'asse x e Z. La mia soluzione funziona abbastanza bene, tuttavia se sposto il cubo troppo velocemente con il mouse o quando incontra un oggetto con una certa profondità (asse y), si presume che il mouse si stia muovendo sull'asse y e il cubo inizi a saltare in avanti o indietro. C'è un modo per limitare il mouse al piano xz? Una soluzione più complicata sarebbe proiettare la lunghezza y di nuovo al xz-plane, ma non ho idea di come. Ho guardato JavaFX Spostando oggetti 3D , ma non sono riuscito ad adattarlo al mio caso.

Il mio codice finora:

    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);
            }
        });
   }
Author: Community, 2015-03-13

1 answers

Come hai detto, ci sono due possibili approcci per il trascinamento 3D:

  • Puro drag & drop, come si propone
  • indicazioni non proiettate, come in questa domanda .

Per gli eventi drag and drop, possiamo rilevare un evento drag sulla forma 3D, quando l'evento inizia e termina.

Il trucco qui è, invece di ascoltare gli eventi di trascinamento tra la forma, ascoltare trascinare gli eventi su un possibile bersaglio.

Definendo a obiettivo, è possibile limitare facilmente i movimenti di forma, dal momento che sarà solo aggiornare la sua posizione quando si sta trascinando solo su questo obiettivo.

Quindi, se si desidera limitare i movimenti su un piano, è possibile utilizzare un Rectangle come bersaglio.

Questo frammento mostra come funziona questo approccio:

@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();
}

Se lo esegui, vedrai che puoi scegliere la casella e trascinarla su tutto il pavimento.

In questa immagine, ho aggiunto un po ' di colore e tratto al rettangolo per vedere i limiti di trascinare.

Trascinamento 3D

Nota anche i cambiamenti nel mouse trasparente sia in scatola che in rettangolo. Il rettangolo non è trasparente solo durante il trascinamento.

 4
Author: José Pereda, 2017-05-23 12:17:43