【发布时间】:2018-11-20 06:29:01
【问题描述】:
我需要能够通过绘制一个矩形区域在我的 3d 模型中选择多个形状,并且选择位于该区域中的所有形状。
如果只有 x 或 y 旋转,我可以绘制区域并选择节点。但是 x 和 y 的大多数组合都会给出不正确的结果。
我认为获取屏幕坐标中的鼠标和节点位置并比较它们是一件简单的事情,但这并没有按预期工作。
在下面的应用程序中,您可以使用鼠标右键绘制一个区域(您必须单击一个球体才能开始,我不知道为什么,只有在您单击一个时才会触发子场景上的鼠标事件领域?)。再次右键单击(再次在球体上)清除选择。
你可以左键拖动来旋转模型(同样你必须从一个球体开始)。在绕 x 轴旋转任意量后,您可以成功选择一个区域。同样绕 y 轴旋转。然而,x 和 y 旋转的组合给出了错误的结果。例如对角拖动一个节点,你会得到如下所示的结果。
Result of selection after x and y rotation
对出了什么问题有任何想法吗?或建议其他方法来解决这个问题?提前致谢
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);
}
}
}
【问题讨论】:
-
请正确缩进代码。另外,
omMouseDragReleased有一个错字。 -
这些问题已在JDK-8205008 下得到修复。
标签: java javafx rotation javafx-8 javafx-3d