Restreindre un mouvement de glisser souris objet 3D à un plan dans JavaFX


J'utilise JavaFX pour déplacer des cubes 3D par glisser la souris. Le cube doit rester sur le plan enjambé par les axes x et z. Ma solution fonctionne assez bien, cependant si je déplace le cube trop vite avec ma souris ou lorsqu'il rencontre un objet avec une certaine profondeur (axe y), on suppose que la souris se déplace sur l'axe y et que le cube commence à sauter en avant ou en arrière. Existe-t-il un moyen de restreindre la souris au plan xz? Une solution plus compliquée serait de projeter la longueur y vers le xz-plane, mais je n'ai aucune idée comment. J'ai regardé JavaFX Déplaçant des objets 3D , mais je n'ai pas pu l'adapter à mon cas.

Mon code jusqu'à présent:

    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

Comme vous l'avez mentionné, il y a deux approches possibles pour le glisser 3D:

  • Glisser-déposer pur, comme vous le proposez
  • directions sans projection, comme dans cette question .

Pour les événements de glisser-déposer, nous pouvons détecter un événement de glisser sur la forme 3D, lorsque l'événement commence et se termine.

L'astuce ici est, au lieu d'écouter les événements de glisser entre les deux sur la forme, écoutez glisser sur les événements sur une cible possible.

En définissant un cible, vous pouvez restreindre facilement les mouvements de la forme, car vous ne mettrez à jour sa position que lorsque vous la faites glisser uniquement sur cette cible.

Donc, si vous voulez restreindre les mouvements de l'avion, vous pouvez utiliser un Rectangle comme cible.

Cet extrait montre comment fonctionne cette approche:

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

Si vous l'exécutez, vous verrez que vous pouvez choisir la boîte et la faire glisser sur tout le sol.

Dans cette image, j'ai ajouté un peu de couleur et de trait au rectangle pour voir les limites de glisser.

3D drag

Notez également les changements dans la souris transparente à la fois dans la boîte et le rectangle. Le rectangle n'est pas transparent à la souris uniquement pendant le glisser-déposer.

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