Java 如何正确获取旋转后三维图形的屏幕坐标

Java 如何正确获取旋转后三维图形的屏幕坐标,java,javafx,rotation,javafx-8,javafx-3d,Java,Javafx,Rotation,Javafx 8,Javafx 3d,我需要能够通过绘制一个矩形区域来选择我的3d模型中的许多形状,并选择位于该区域中的所有形状 如果只有x或y旋转,我可以绘制区域并选择节点。但是大多数x和y的组合给出了不正确的结果 我原以为在屏幕坐标中获取鼠标和节点的位置并进行比较是一件简单的事情,但这并没有达到预期的效果 在下面的应用程序中,您可以使用鼠标右键绘制一个区域(您必须单击一个球体才能开始,我不知道为什么,只有单击球体时才会触发子场景上的鼠标事件?)。再次单击鼠标右键(再次单击球体)将清除选择 可以单击鼠标左键并拖动以旋转模型(同样,

我需要能够通过绘制一个矩形区域来选择我的3d模型中的许多形状,并选择位于该区域中的所有形状

如果只有x或y旋转,我可以绘制区域并选择节点。但是大多数x和y的组合给出了不正确的结果

我原以为在屏幕坐标中获取鼠标和节点的位置并进行比较是一件简单的事情,但这并没有达到预期的效果

在下面的应用程序中,您可以使用鼠标右键绘制一个区域(您必须单击一个球体才能开始,我不知道为什么,只有单击球体时才会触发子场景上的鼠标事件?)。再次单击鼠标右键(再次单击球体)将清除选择

可以单击鼠标左键并拖动以旋转模型(同样,您必须从球体开始)。绕x轴旋转任意数量后,可以成功选择区域。同样,绕y轴旋转。但是,x和y旋转的组合给出了错误的结果。例如,对角拖动一个节点,结果如下所示

你有什么想法吗?或者对其他方法的建议?提前谢谢

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;

import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.Slider;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape3D;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;

public class ScreenSelection extends Application {


    private final PerspectiveCamera camera = new PerspectiveCamera(true);

    private final Group root = new Group();
    private final Group world = new Group();
    private final XFormWorld camPiv = new XFormWorld();

    private final Slider zoom = new Slider(-100, 0, -50);
    private final Button reset = new Button("Reset");

    private final Pane pane = new Pane();
    private final BorderPane main = new BorderPane();

    double mousePosX, mousePosY, mouseOldX, mouseOldY, mouseDeltaX, mouseDeltaY;
    double mouseFactorX, mouseFactorY;


public void start(Stage stage) throws Exception {

    camera.setTranslateZ(zoom.getValue());
    reset.setOnAction(eh -> {
        camPiv.reset();
        zoom.setValue(-50);
    });
    camera.setFieldOfView(60);

    camPiv.getChildren().add(camera);
    Collection<Shape3D> world = createWorld();
    RectangleSelect rs = new RectangleSelect(main, world);

    this.world.getChildren().addAll(world);
    root.getChildren().addAll(camPiv, this.world);

    SubScene subScene = new SubScene(root, -1, -1, true, SceneAntialiasing.BALANCED);
    subScene.setDepthTest(DepthTest.ENABLE);
    subScene.setCamera(camera);

    subScene.heightProperty().bind(pane.heightProperty());
    subScene.widthProperty().bind(pane.widthProperty());

    zoom.valueProperty().addListener((o, oldA, newA) -> camera.setTranslateZ(newA.doubleValue()));


    HBox controls = new HBox();
    controls.getChildren().addAll(new HBox(new Label("Zoom: "), zoom), new HBox(reset));

    pane.getChildren().addAll(controls, subScene);

    MenuBar menu = new MenuBar(new Menu("File"));
    main.setTop(menu);

    main.setCenter(pane);

    Scene scene = new Scene(main);

    subScene.setOnMousePressed((MouseEvent me) -> {
        mousePosX = me.getSceneX();
        mousePosY = me.getSceneY();
    });

    subScene.setOnMouseDragged((MouseEvent me) -> {
        if (me.isSecondaryButtonDown()) {
            rs.onMouseDragged(me);
        } else if (me.isPrimaryButtonDown()) {
            mouseOldX = mousePosX;
            mouseOldY = mousePosY;
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();
            mouseDeltaX = (mousePosX - mouseOldX);
            mouseDeltaY = (mousePosY - mouseOldY);
            camPiv.ry(mouseDeltaX * 180.0 / subScene.getWidth());
            camPiv.rx(-mouseDeltaY * 180.0 / subScene.getHeight());


        }
    });
    subScene.setOnMouseReleased((MouseEvent me) -> {
        rs.omMouseDragReleased(me);
    });
    subScene.setOnMouseClicked((MouseEvent me) -> {
        if (me.getButton() == MouseButton.SECONDARY) {
            rs.clearSelection();
        }
    });
    stage.setScene(scene);
    stage.setWidth(800);
    stage.setHeight(800);
    stage.show();

}

private Collection<Shape3D> createWorld() {

    List<Shape3D> shapes = new ArrayList<Shape3D>();

    Random random = new Random(System.currentTimeMillis());
    for (int i=0; i<4000; i++) {
        double x = (random.nextDouble() - 0.5) * 30;
        double y = (random.nextDouble() - 0.5) * 30 ;
        double z = (random.nextDouble() - 0.5) * 30 ;

        Sphere point = new Sphere(0.2);
        point.setMaterial(new PhongMaterial(Color.SKYBLUE));
        point.setPickOnBounds(false);
        point.getTransforms().add(new Translate(x, y, z));
        shapes.add(point);
    }

    return shapes;
}


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

public class XFormWorld extends Group {
    Transform rotation = new Rotate();
    Translate translate = new Translate();

    public XFormWorld() {
        getTransforms().addAll(rotation, translate);
    }

    public void reset() {
        rotation = new Rotate();
        getTransforms().set(0, rotation);

    }

    public void rx(double angle) {
        rotation = rotation.createConcatenation(new Rotate(angle, Rotate.X_AXIS));
        getTransforms().set(0, rotation);
    }

    public void ry(double angle) {
        rotation = rotation.createConcatenation(new Rotate(angle, Rotate.Y_AXIS));
        getTransforms().set(0, rotation); 
    }

    public void tx(double amount) {
        translate.setX(translate.getX() + amount);
    }

}

public class RectangleSelect  {

    private static final int START_X = 0;
    private static final int START_Y = 1;
    private static final int END_X = 2;
    private static final int END_Y = 3;

    private double[] sceneCoords = new double[2]; //mouse drag x, y in scene coords 
    private double[] screenCoords = new double[2]; //mouse drag current x, y in screen coords
    private double[] boundsInScreenCoords = new double[4]; //top left x, y, bottom right x,y in screen coords
    private Collection<Shape3D> world;

    private PhongMaterial selected = new PhongMaterial(Color.YELLOW);
    private Rectangle rectangle;

    public RectangleSelect(Pane pane, Collection<Shape3D> world) {
        sceneCoords[START_X] = Double.MIN_VALUE;
        sceneCoords[START_Y] = Double.MIN_VALUE;
        rectangle = new Rectangle();
        rectangle.setStroke(Color.RED);
        rectangle.setOpacity(0.0);
        rectangle.setMouseTransparent(true);
        rectangle.setFill(null);

        this.world = world;
        pane.getChildren().add(rectangle);
    }


    public void onMouseDragged(MouseEvent me) {
        clearSelection();
        if (sceneCoords[START_X] == Double.MIN_VALUE) {
            sceneCoords[START_X] = me.getSceneX();
            sceneCoords[START_Y] = me.getSceneY();
            screenCoords[START_X] = me.getScreenX();
            screenCoords[START_Y] = me.getScreenY();
        }
        double sceneX = me.getSceneX();
        double sceneY = me.getSceneY();
        double screenX = me.getScreenX();
        double screenY = me.getScreenY();

        double topX = Math.min(sceneCoords[START_X], sceneX);
        double bottomX = Math.max(sceneCoords[START_X], sceneX);
        double leftY = Math.min(sceneCoords[START_Y], sceneY);
        double rightY = Math.max(sceneCoords[START_Y], sceneY);

        boundsInScreenCoords[START_X] = Math.min(screenCoords[START_X], screenX);
        boundsInScreenCoords[END_X]= Math.max(screenCoords[START_X], screenX);
        boundsInScreenCoords[START_Y] = Math.min(screenCoords[START_Y], screenY);
        boundsInScreenCoords[END_Y] = Math.max(screenCoords[START_Y], screenY);

        world.forEach(this::selectIfInBounds);

        rectangle.setX(topX);
        rectangle.setY(leftY);
        rectangle.setWidth(bottomX - topX);
        rectangle.setHeight(rightY - leftY);
        rectangle.setOpacity(1.0);
    }


    private void selectIfInBounds(Shape3D node) {
        Point2D screenCoods = node.localToScreen(0.0, 0.0, 0.0);
        if (screenCoods.getX() > boundsInScreenCoords[START_X] &&
            screenCoods.getY() > boundsInScreenCoords[START_Y] &&
            screenCoods.getX() < boundsInScreenCoords[END_X] &&
            screenCoods.getY() < boundsInScreenCoords[END_Y]) {
            Material m = node.getMaterial();
            node.getProperties().put("material", m);
            node.setMaterial(selected);
        }
    }

    private void unselect(Shape3D node) {
        Material m = (Material) node.getProperties().get("material");
        if (m != null) {
            node.setMaterial(m);
        }
    }

    public void omMouseDragReleased(MouseEvent me) {
        rectangle.setOpacity(0.0);
        sceneCoords[START_X]  = Double.MIN_VALUE;
        sceneCoords[START_Y] = Double.MIN_VALUE;
    }

    public void clearSelection() {
        world.forEach(this::unselect);
    }
}   
import java.util.ArrayList;
导入java.util.Collection;
导入java.util.List;
导入java.util.Random;
导入javafx.application.application;
导入javafx.geometry.Point2D;
导入javafx.scene.DepthTest;
导入javafx.scene.Group;
导入javafx.scene.PerspectiveCamera;
导入javafx.scene.scene;
导入javafx.scene.sceneatialiasing;
导入javafx.scene.SubScene;
导入javafx.scene.control.Button;
导入javafx.scene.control.Label;
导入javafx.scene.control.Menu;
导入javafx.scene.control.MenuBar;
导入javafx.scene.control.Slider;
导入javafx.scene.input.MouseButton;
导入javafx.scene.input.MouseEvent;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.HBox;
导入javafx.scene.layout.Pane;
导入javafx.scene.paint.Color;
导入javafx.scene.paint.Material;
导入javafx.scene.paint.PhongMaterial;
导入javafx.scene.shape.Rectangle;
导入javafx.scene.shape.Shape3D;
导入javafx.scene.shape.Sphere;
导入javafx.scene.transform.Rotate;
导入javafx.scene.transform.transform;
导入javafx.scene.transform.Translate;
导入javafx.stage.stage;
公共类屏幕选择扩展了应用程序{
私人最终透视摄影机=新透视摄影机(真);
私有最终组根=新组();
私有最终组世界=新组();
私有最终XFormWorld camPiv=新XFormWorld();
私有最终滑块缩放=新滑块(-100,0,-50);
专用最终按钮重置=新按钮(“重置”);
私有最终窗格=新窗格();
private final BorderPane main=新的BorderPane();
双鼠塞,鼠塞,鼠塞,鼠塞,鼠塞,鼠塞,鼠塞,鼠塞;
双mouseFactorX,mouseFactorY;
public void start(Stage)引发异常{
setTranslateZ(zoom.getValue());
重置.设置操作(eh->{
camPiv.reset();
缩放设置值(-50);
});
摄像机。设置视野(60);
camPiv.getChildren().add(摄像头);
集合世界=createWorld();
矩形选择器=新矩形选择器(主、世界);
this.world.getChildren().addAll(world);
root.getChildren().addAll(camPiv,this.world);
亚新世亚新世=新亚新世(根,-1,-1,真,场景化。平衡);
subScene.setDepthTest(DepthTest.ENABLE);
亚场景设置摄像机(摄像机);
subScene.heightProperty().bind(pane.heightProperty());
subScene.widthProperty().bind(pane.widthProperty());
zoom.valueProperty().addListener((o,oldA,newA)->camera.setTranslateZ(newA.doubleValue());
HBox控制=新的HBox();
controls.getChildren().addAll(新HBox(新标签(“缩放”)、缩放)、新HBox(重置));
pane.getChildren().addAll(控件,子场景);
菜单栏菜单=新菜单栏(新菜单(“文件”);
main.setTop(菜单);
主设置中心(窗格);
场景=新场景(主);
subScene.setOnMousePressed((MouseEvent me)->{
mousePosX=me.getSceneX();
mousePosY=me.getSceneY();
});
subScene.setonMouseDrawed((MouseEvent me)->{
if(me.isSecondaryButtonDown()){
卢比(我);
}否则如果(me.isPrimaryButtonDown()){
mouseOldX=mousePosX;
mouseOldY=鼠标点;
mousePosX=me.getSceneX();
mousePosY=me.getSceneY();
mouseDeltaX=(mousePosX-mouseOldX);
mouseDeltaY=(mousePosY-mouseOldY);
camPiv.ry(mouseDeltaX*180.0/subScene.getWidth());
camPiv.rx(-mouseDeltaY*180.0/subScene.getHeight());
}
});
subScene.setOnMouseReleased((MouseEvent me)->{
奥穆斯德拉格雷特(me);
});
subScene.setOnMouseClicked((MouseEvent me)->{
if(me.getButton()==MouseButton.SECONDARY){
rs.clearSelection();
}
});
舞台场景;
舞台布景宽度(800);
舞台设置高度(800);
stage.show();
}
私人收藏createWorld(){
列表形状=新建ArrayList();
Random Random=新随机(System.currentTimeMillis());
对于(int i=0;i boundsInScreenCoords[START_X]&&
screenCoods.getY()>boundsInScreenCoords[开始]&&
screenCoods.getX()public Vec3d transform(Vec3d point) {
    return transform(point, point);
}
    pointOut.x = (float) (mat[0] * point.x + mat[1] * point.y
            + mat[2] * point.z + mat[3]);
    pointOut.y = (float) (mat[4] * point.x + mat[5] * point.y
            + mat[6] * point.z + mat[7]); 
camera.translateZProperty().bind(zoom.valueProperty());
camera.setTranslateZ(zoom.getValue());
zoom.valueProperty().addListener((o, oldA, newA) -> camera.setTranslateZ(newA.doubleValue()));
subScene.setPickOnBounds(true);
subScene.setOnMouseClicked(me -> {
    if (me.getButton() == MouseButton.SECONDARY && me.isStillSincePress()) {
        rs.clearSelection();
    }
});
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;

import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.Slider;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape3D;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;

public class ScreenSelectionNew extends Application {

    private final PerspectiveCamera camera = new PerspectiveCamera(true);

    private final XFormWorld world = new XFormWorld();

    private double mousePosX, mousePosY, mouseOldX, mouseOldY;

    @Override
    public void start(Stage stage) throws Exception {
        BorderPane main = new BorderPane();
        StackPane stackPane = new StackPane();

        SubScene subScene = setupSubScene(main);
        subScene.heightProperty().bind(stackPane.heightProperty());
        subScene.widthProperty().bind(stackPane.widthProperty());
        stackPane.getChildren().addAll(subScene, setupControls());

        MenuBar menu = new MenuBar(new Menu("File"));

        main.setTop(menu);
        main.setCenter(stackPane);
        Scene scene = new Scene(main);

        stage.setScene(scene);
        stage.setWidth(800);
        stage.setHeight(800);
        stage.show();
    }

    private SubScene setupSubScene(Pane parent) {
        Collection<Shape3D> worldContent = createWorld();
        world.getChildren().addAll(worldContent);

        SubScene subScene = new SubScene(world, -1, -1, true, SceneAntialiasing.BALANCED);
        subScene.setCamera(camera);
        subScene.setPickOnBounds(true);
        camera.setFieldOfView(60);

        RectangleSelect rs = new RectangleSelect(parent, worldContent);

        subScene.setOnMousePressed(me -> {
            mousePosX = me.getX();
            mousePosY = me.getY();
        });

        subScene.setOnMouseDragged(me -> {
            if (me.isSecondaryButtonDown()) {
                rs.onMouseDragged(me);
            } else if (me.isPrimaryButtonDown()) {
                mouseOldX = mousePosX;
                mouseOldY = mousePosY;
                mousePosX = me.getX();
                mousePosY = me.getY();
                double mouseDeltaX = (mousePosX - mouseOldX);
                double mouseDeltaY = (mousePosY - mouseOldY);
                world.rx(mouseDeltaY * 180.0 / subScene.getHeight());
                world.ry(-mouseDeltaX * 180.0 / subScene.getWidth());
            }
        });

        subScene.setOnMouseReleased(me -> rs.onMouseDragReleased(me));

        subScene.setOnMouseClicked(me -> {
            if (me.getButton() == MouseButton.SECONDARY && me.isStillSincePress()) {
                rs.clearSelection();
            }
        });

        return subScene;
    }

    private Pane setupControls() {
        Slider zoom = new Slider(-100, 0, -50);
        camera.translateZProperty().bind(zoom.valueProperty());

        Button reset = new Button("Reset");
        reset.setOnAction(eh -> {
            world.reset();
            zoom.setValue(-50);
        });

        HBox controls = new HBox(new Label("Zoom: "), zoom, reset);
        AnchorPane anchorPane = new AnchorPane(controls);
        anchorPane.setPickOnBounds(false);
        return anchorPane;
    }

    private Collection<Shape3D> createWorld() {

        List<Shape3D> shapes = new ArrayList<>();

        Random random = new Random(System.currentTimeMillis());
        for (int i = 0; i < 4000; i++) {
            double x = (random.nextDouble() - 0.5) * 30;
            double y = (random.nextDouble() - 0.5) * 30;
            double z = (random.nextDouble() - 0.5) * 30;

            Sphere point = new Sphere(0.2);
            point.setMaterial(new PhongMaterial(Color.SKYBLUE));
            point.getTransforms().add(new Translate(x, y, z));
            shapes.add(point);
        }

        return shapes;
    }

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

    public class XFormWorld extends Group {
        Transform rotation = new Rotate();

        public XFormWorld() {
            getTransforms().addAll(rotation);
        }

        public void reset() {
            rotation = new Rotate();
            getTransforms().set(0, rotation);
        }

        public void rx(double angle) {
            Point3D axis = new Point3D(rotation.getMxx(), rotation.getMxy(), rotation.getMxz());
            rotation = rotation.createConcatenation(new Rotate(angle, axis));
            getTransforms().set(0, rotation);
        }

        public void ry(double angle) {
            Point3D axis = new Point3D(rotation.getMyx(), rotation.getMyy(), rotation.getMyz());
            rotation = rotation.createConcatenation(new Rotate(angle, axis));
            getTransforms().set(0, rotation);
        }
    }

    public class RectangleSelect {

        private static final int START_X = 0;
        private static final int START_Y = 1;
        private static final int END_X = 2;
        private static final int END_Y = 3;

        private double[] sceneCoords = new double[2]; //mouse drag x, y in scene coords 
        private double[] screenCoords = new double[2]; //mouse drag current x, y in screen coords
        private double[] boundsInScreenCoords = new double[4]; //top left x, y, bottom right x,y in screen coords
        private Collection<Shape3D> world;

        private PhongMaterial selected = new PhongMaterial(Color.YELLOW);
        private Rectangle rectangle;

        public RectangleSelect(Pane pane, Collection<Shape3D> world) {
            sceneCoords[START_X] = Double.MIN_VALUE;
            sceneCoords[START_Y] = Double.MIN_VALUE;
            rectangle = new Rectangle();
            rectangle.setStroke(Color.RED);
            rectangle.setOpacity(0.0);
            rectangle.setMouseTransparent(true);
            rectangle.setFill(null);

            this.world = world;
            pane.getChildren().add(rectangle);
        }

        public void onMouseDragged(MouseEvent me) {
            clearSelection();
            if (sceneCoords[START_X] == Double.MIN_VALUE) {
                sceneCoords[START_X] = me.getSceneX();
                sceneCoords[START_Y] = me.getSceneY();
                screenCoords[START_X] = me.getScreenX();
                screenCoords[START_Y] = me.getScreenY();
            }
            double sceneX = me.getSceneX();
            double sceneY = me.getSceneY();
            double screenX = me.getScreenX();
            double screenY = me.getScreenY();

            double topX = Math.min(sceneCoords[START_X], sceneX);
            double bottomX = Math.max(sceneCoords[START_X], sceneX);
            double leftY = Math.min(sceneCoords[START_Y], sceneY);
            double rightY = Math.max(sceneCoords[START_Y], sceneY);

            boundsInScreenCoords[START_X] = Math.min(screenCoords[START_X], screenX);
            boundsInScreenCoords[END_X] = Math.max(screenCoords[START_X], screenX);
            boundsInScreenCoords[START_Y] = Math.min(screenCoords[START_Y], screenY);
            boundsInScreenCoords[END_Y] = Math.max(screenCoords[START_Y], screenY);

            world.forEach(this::selectIfInBounds);

            rectangle.setX(topX);
            rectangle.setY(leftY);
            rectangle.setWidth(bottomX - topX);
            rectangle.setHeight(rightY - leftY);
            rectangle.setOpacity(1.0);
        }

        private void selectIfInBounds(Shape3D node) {
            Point2D screenCoods = node.localToScreen(0.0, 0.0, 0.0);
            if (screenCoods.getX() > boundsInScreenCoords[START_X] &&
                screenCoods.getY() > boundsInScreenCoords[START_Y] &&
                screenCoods.getX() < boundsInScreenCoords[END_X] &&
                screenCoods.getY() < boundsInScreenCoords[END_Y]) {
                Material m = node.getMaterial();
                node.getProperties().put("material", m);
                node.setMaterial(selected);
            }
        }

        private void unselect(Shape3D node) {
            Material m = (Material) node.getProperties().get("material");
            if (m != null) {
                node.setMaterial(m);
            }
        }

        public void onMouseDragReleased(MouseEvent me) {
            rectangle.setOpacity(0.0);
            sceneCoords[START_X] = Double.MIN_VALUE;
            sceneCoords[START_Y] = Double.MIN_VALUE;
        }

        public void clearSelection() {
            world.forEach(this::unselect);
        }
    }
}