将坐标从 3D 场景转换为 2D 叠加
Convert coordinates from 3D scene to 2D overlay
与 类似,但我无法使用该解决方案。
我想为 3d 形状或更像其投影绘制 2d 边框。我写了这个示例代码以供参考:
import javafx.application.Application;
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.layout.AnchorPane;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class CoordinateConversion extends Application {
@Override
public void start(Stage stage) {
var box = new Box(20, 20, 20);
box.setMaterial(new PhongMaterial(Color.TEAL));
box.getTransforms().add(new Rotate(45, new Point3D(1, 1, 1)));
var rootGroup = new Group(box);
var camera = new PerspectiveCamera(true);
camera.setFarClip(500);
camera.setTranslateZ(-100);
var aaScene = new SubScene(rootGroup, 0, 0, true, SceneAntialiasing.BALANCED);
aaScene.setCamera(camera);
var pane3d = new Pane(aaScene);
pane3d.setBackground(new Background(new BackgroundFill(Color.BEIGE, null, null)));
aaScene.widthProperty().bind(pane3d.widthProperty());
aaScene.heightProperty().bind(pane3d.heightProperty());
var overlay = new AnchorPane();
var stack = new StackPane(pane3d, overlay);
var filler = new Rectangle(0, 0, 100, 50);
filler.setFill(Color.rgb(0, 255, 0, 0.3));
var borderPane = new BorderPane();
borderPane.setCenter(stack);
borderPane.setTop(filler);
var scene = new Scene(borderPane);
stage.setScene(scene);
stage.setWidth(800);
stage.setHeight(400);
stage.show();
var sceneBounds = box.localToScene(box.getBoundsInLocal(), false);
var overlayBounds = overlay.sceneToLocal(sceneBounds);
var rect = new Rectangle(overlayBounds.getMinX(), overlayBounds.getMinY(), overlayBounds.getWidth(), overlayBounds.getHeight());
rect.setFill(null);
rect.setStroke(Color.RED);
overlay.getChildren().add(rect);
}
public static void main(String[] args) {
launch(args);
}
}
它在角落画了红色矩形,而我想要的是我添加的 "photoshoped" 橙色矩形。
基本思想是将边界从一个父对象转换到另一个父对象,因此我从一个父对象转换到场景,然后从场景到下一个父对象,正如我在 中所教的那样。关于转换我不明白的一些事情
- 如果我使用
box.localToScene(box.getBoundsInLocal(), true)
我会得到 NaN
边界,即使盒子在子场景中并且我想要场景中的坐标。
sceneBounds
和 overlayBounds
是相同的,即使叠加开始时比场景低 50 像素(因为填充)。如果没有变换,我希望叠加层和场景之间的每次转换在 y 上正好是 +-50。
我也尝试用 box.getParent().localToScene(box.getBoundsInParent())
获取 sceneBounds
但 ti 是一样的。我想这是有道理的,因为我使用父级来转换父级的边界而不是节点来转换本地的边界。
使用 Javafx 12
您有一个 "time" 问题:您将 subScene 设置为 0x0 尺寸,然后添加绑定,并为 sceneBounds
正确 进行计算在展示舞台后,return NaN
.
如果您向 pane3d.widthProperty()
添加侦听器,您将看到 sceneBounds
在 宽度更改之前 已解析,因此,您正在使用一个 0x0 子场景。所以你说得太早了。
可能的解决方案:
- 移除绑定并为子场景维度设置一个值:
var aaScene = new SubScene(rootGroup, 800, 400, true, SceneAntialiasing.BALANCED);
stage.show();
var sceneBounds = box.localToScene(box.getBoundsInLocal(), true);
...
- 保留绑定,但为子场景的高度添加一个侦听器属性(在宽度之后设置):
aaScene.heightProperty().addListener((obs, ov, nv) -> {
var sceneBounds = box.localToScene(box.getBoundsInLocal(), true);
...
});
无论哪种情况,请注意,您必须使用 localToScene(..., true)
来获取场景坐标:
If the Node does not have any SubScene or rootScene is set to true, the result point is in Scene coordinates of the Node returned by getScene().
与
我想为 3d 形状或更像其投影绘制 2d 边框。我写了这个示例代码以供参考:
import javafx.application.Application;
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.layout.AnchorPane;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class CoordinateConversion extends Application {
@Override
public void start(Stage stage) {
var box = new Box(20, 20, 20);
box.setMaterial(new PhongMaterial(Color.TEAL));
box.getTransforms().add(new Rotate(45, new Point3D(1, 1, 1)));
var rootGroup = new Group(box);
var camera = new PerspectiveCamera(true);
camera.setFarClip(500);
camera.setTranslateZ(-100);
var aaScene = new SubScene(rootGroup, 0, 0, true, SceneAntialiasing.BALANCED);
aaScene.setCamera(camera);
var pane3d = new Pane(aaScene);
pane3d.setBackground(new Background(new BackgroundFill(Color.BEIGE, null, null)));
aaScene.widthProperty().bind(pane3d.widthProperty());
aaScene.heightProperty().bind(pane3d.heightProperty());
var overlay = new AnchorPane();
var stack = new StackPane(pane3d, overlay);
var filler = new Rectangle(0, 0, 100, 50);
filler.setFill(Color.rgb(0, 255, 0, 0.3));
var borderPane = new BorderPane();
borderPane.setCenter(stack);
borderPane.setTop(filler);
var scene = new Scene(borderPane);
stage.setScene(scene);
stage.setWidth(800);
stage.setHeight(400);
stage.show();
var sceneBounds = box.localToScene(box.getBoundsInLocal(), false);
var overlayBounds = overlay.sceneToLocal(sceneBounds);
var rect = new Rectangle(overlayBounds.getMinX(), overlayBounds.getMinY(), overlayBounds.getWidth(), overlayBounds.getHeight());
rect.setFill(null);
rect.setStroke(Color.RED);
overlay.getChildren().add(rect);
}
public static void main(String[] args) {
launch(args);
}
}
它在角落画了红色矩形,而我想要的是我添加的 "photoshoped" 橙色矩形。
基本思想是将边界从一个父对象转换到另一个父对象,因此我从一个父对象转换到场景,然后从场景到下一个父对象,正如我在
- 如果我使用
box.localToScene(box.getBoundsInLocal(), true)
我会得到NaN
边界,即使盒子在子场景中并且我想要场景中的坐标。 sceneBounds
和overlayBounds
是相同的,即使叠加开始时比场景低 50 像素(因为填充)。如果没有变换,我希望叠加层和场景之间的每次转换在 y 上正好是 +-50。
我也尝试用 box.getParent().localToScene(box.getBoundsInParent())
获取 sceneBounds
但 ti 是一样的。我想这是有道理的,因为我使用父级来转换父级的边界而不是节点来转换本地的边界。
使用 Javafx 12
您有一个 "time" 问题:您将 subScene 设置为 0x0 尺寸,然后添加绑定,并为 sceneBounds
正确 进行计算在展示舞台后,return NaN
.
如果您向 pane3d.widthProperty()
添加侦听器,您将看到 sceneBounds
在 宽度更改之前 已解析,因此,您正在使用一个 0x0 子场景。所以你说得太早了。
可能的解决方案:
- 移除绑定并为子场景维度设置一个值:
var aaScene = new SubScene(rootGroup, 800, 400, true, SceneAntialiasing.BALANCED);
stage.show();
var sceneBounds = box.localToScene(box.getBoundsInLocal(), true);
...
- 保留绑定,但为子场景的高度添加一个侦听器属性(在宽度之后设置):
aaScene.heightProperty().addListener((obs, ov, nv) -> {
var sceneBounds = box.localToScene(box.getBoundsInLocal(), true);
...
});
无论哪种情况,请注意,您必须使用 localToScene(..., true)
来获取场景坐标:
If the Node does not have any SubScene or rootScene is set to true, the result point is in Scene coordinates of the Node returned by getScene().