理解JavaFX转换

huangapple go评论61阅读模式
英文:

Understanding JavaFX transforms

问题

我正在尝试理解JavaFX中的变换。

我有这个简单的例子:

public void start(Stage primaryStage) {
    double sceneWidth = 640;
    double sceneHeight = 480;
    double rectangleWidth = 10000;
    double rectangleHeight = 15000;
    double scale = 72/5000;

    Pane pane = new Pane();
    Scene scene = new Scene(pane, sceneWidth, sceneHeight);

    Rectangle rectangle = new Rectangle(rectangleWidth, rectangleHeight);

    pane.getChildren().add(rectangle);
    primaryStage.setScene(scene);
    primaryStage.show();

    pane.getTransforms().clear();
    pane.getTransforms().add(new Translate(-5000, -7500));
    pane.getTransforms().add(new Scale(scale, scale));
    pane.getTransforms().add(new Translate(320, 240));
}

这是我的意图。我的意图是将矩形放在“世界坐标”中。然后,我平移面板以将矩形的中心放在0,0处。

鉴于常见的“每英寸72像素”的图形比例,然后将其缩小到每英寸5000个单位,这就是72/5000比例的来源。5000个单位应该是72像素,72像素应该是一英寸。这将我的单位转换为像素,因此现在我的矩形应该是2" x 3"。

最后,现在我在“像素”坐标中,我使用最终的平移(320,240)将它移动到640x480舞台的中央。

理想情况下,我应该在我的窗口中看到一个2" x 3"的矩形。

最终,什么都没有显示出来。毫无疑问,矩形在某个地方漂浮,只是远离场景显示它的位置。

不管“每英寸72像素”部分的准确性如何,我应该看到一个相对合理缩放的矩形在我的显示器上。

但显然我理解有误,我很好奇是什么误解。

英文:

I'm trying to wrap my head around transforms in JavaFX.

I have this simple example:

public void start(Stage primaryStage) {
    double sceneWidth = 640;
    double sceneHeight = 480;
    double rectangleWidth = 10000;
    double rectangleHeight = 15000;
    double scale = 72/5000;

    Pane pane = new Pane();
    Scene scene = new Scene(pane, sceneWidth, sceneHeight);

    Rectangle rectangle = new Rectangle(rectangleWidth, rectangleHeight);

    pane.getChildren().add(rectangle);
    primaryStage.setScene(scene);
    primaryStage.show();

    pane.getTransforms().clear();
    pane.getTransforms().add(new Translate(-5000, -7500));
    pane.getTransforms().add(new Scale(scale, scale));
    pane.getTransforms().add(new Translate(320, 240));
}

This was my intent. My intent was to have the rectangle in "world coordinates". I then translate the pane to place the center of the rectangle at 0,0.

Given the common "72 pixels per inch" ratio of graphics, I then scale it down to 5000 units "per inch", which is where the 72/5000 scale comes from. 5000 units should be 72 pixels, 72 pixels should be one inch. This converts my units into pixels, and, so, now my rectangle should be 2" x 3".

Finally, now, that I'm in "pixel" coordinates, I move it to the middle of the 640x480 stage with the final Translate of 320,240.

Ideally, I should see a 2" x 3" rectangle in the middle of my window.

In the end, nothing is displayed. No doubt the rectangle is floating around out there somewhere, just nowhere near where the scene is displaying it.

Regardless of the accuracy of the "72/inch" part, I should see a reasonably scaled rectangle my display.

But there's clearly a misunderstanding on my part, curious what that is.

答案1

得分: 4

请注意,您正在犯一个非常常见的错误,即使用整数除法并期望结果为double类型。表达式

72/5000

计算结果为零,因此您得到了一个零的缩放值,显然您将看不到任何内容。您需要使用

double scale = 72.0/5000;

还请注意,无需假设屏幕的分辨率;您可以在运行时使用Screen API来确定它。所以,作为一个简单的示例(假设舞台显示在主屏幕上;动态查找“当前”屏幕并不太困难),您可以这样做:

double scale = Screen.getPrimary().getDpi() / 5000;

基本上,您的变换是反向的;但我将逐一讲解这一部分:

我认为您的第一个变换的意图是将坐标系统移动到原点位于中心。此时,面板仍然处于“像素单位”中,因此一个单位就是一个像素,原点位于左上角。所以要将原点移动到中心,您需要

pane.getTransforms().add(new Translate(320, 240));

请注意,矩形的左上角位于原点,所以这将把矩形的左上角放在屏幕中心。

现在进行缩放,它以原点为中心进行缩放(原点保持不变):

pane.getTransforms().add(new Scale(scale, scale));

请注意,由于原点保持不变,所以矩形的左上角在这一点上位于屏幕中心。缩放意味着“单位”现在是“世界单位”,所以面板的宽度是 640/scale=44444.44,高度是 480/scale=33333.33,虽然这不太重要。

现在,由于矩形的左上角位于中心,并且单位是“世界单位”,要使其居中,将其左移半个宽度并上移半个高度:

pane.getTransforms().add(new Translate(-5000, -7500));

您可以使用以下代码来检查矩形在场景单位(像素)中的位置:

System.out.println(rectangle.getLocalToSceneTransform().transform(rectangle.getBoundsInLocal()));

以下是所期望的结果:

public void start(Stage primaryStage) {
double sceneWidth = 640;
double sceneHeight = 480;
double rectangleWidth = 10000;
double rectangleHeight = 15000;
double scale = Screen.getPrimary().getDpi()/5000;

Pane pane = new Pane();
Scene scene = new Scene(pane, sceneWidth, sceneHeight);

Rectangle rectangle = new Rectangle(rectangleWidth, rectangleHeight);

pane.getChildren().add(rectangle);
primaryStage.setScene(scene);
primaryStage.show();

pane.getTransforms().clear();
pane.getTransforms().add(new Translate(320, 240));
pane.getTransforms().add(new Scale(scale, scale));
pane.getTransforms().add(new Translate(-5000, -7500));

System.out.println(rectangle.getLocalToSceneTransform().transform(rectangle.getBoundsInLocal()));

}

使用您最初发布的代码,您从左上角为(0,0)的10000 x 15000像素矩形开始。

然后,您将其向左平移5000像素,并向上平移7500像素。

这将使面板的原点以及矩形的左上角在场景坐标中的位置为 (-5000, -7500),矩形在场景坐标中延伸到 (5000, 7500)(因此此时它覆盖整个场景)。

然后,您按比例缩放了 72.0/5000 倍(在纠正了前述错误后)。这使矩形在场景坐标中的宽度为144,高度为216,并且由于原点在缩放变换中保持不变,因此其范围为 (-5000, -7500)(-4856, -7284),同样是在场景坐标中,当然这使其远离了场景的可见区域。

最后,您在世界坐标中(面板的坐标,因为变换应用于该节点)将其平移 (320, 240)。这在场景坐标中水平移动为 320*scale = 4.608,垂直移动为 240*scale = 3.456,从而将矩形移动到场景坐标中的范围为 (-4995.392, -7496.544)(-4851.392 ,-7280.544),仍然远离场景的可见部分。

您可以使用与上面相同的调试代码来查看矩形在场景坐标中的位置。

英文:

First note that you are falling into a very common mistake in using integer division and expecting it to result in a double. The expression

72/5000

evaluates to zero, so you have a zero scale, with which you will obviously not see anything at all. You need

double scale = 72.0/5000;

Also note that there is no need to assume the resolution of the screeen; you can use the Screen API to determine it at runtime. So, as a simple example (which assumes the stage is displayed on the primary screen; it is not too hard to find the "current" screen dynamically) you can do

double scale = Screen.getPrimary().getDpi() / 5000;

Basically, your transforms are reversed; but I will step through this piece by piece:

I think the intention of your first transform is to move the coordinate system so the origin is at the center. At this point the pane is still in "pixel units", so one unit is one pixel, and the origin is in the top left. So to move the origin to the center, you need

    pane.getTransforms().add(new Translate(320, 240));

Note the top left corner of the rectangle is at the origin, so this puts the top left corner of the rectangle at the center of the screen.

Now you do the scale, which scales about the origin (the origin remains fixed):

    pane.getTransforms().add(new Scale(scale, scale));

Note that because the origin stays fixed, the top left of the rectangle is at the center of the screen at this point. The scale means that the "units" are now "world units", so the pane width is 640/scale=44444.44 and the height is 480/scale=33333.33, though this isn't too important.

Now, since the rectangle has its top left corner at the center, and the units are "world units", to center it move it left by half its width and up by half its height:

    pane.getTransforms().add(new Translate(-5000, -7500));

You can check the position of the rectangle in the units of the scene (pixels) with

    System.out.println(rectangle.getLocalToSceneTransform().transform(rectangle.getBoundsInLocal()));

The following gives the desired outcome:

public void start(Stage primaryStage) {
    double sceneWidth = 640;
    double sceneHeight = 480;
    double rectangleWidth = 10000;
    double rectangleHeight = 15000;
    double scale = Screen.getPrimary().getDpi()/5000;

    Pane pane = new Pane();
    Scene scene = new Scene(pane, sceneWidth, sceneHeight);

    Rectangle rectangle = new Rectangle(rectangleWidth, rectangleHeight);

    pane.getChildren().add(rectangle);
    primaryStage.setScene(scene);
    primaryStage.show();

    pane.getTransforms().clear();
    pane.getTransforms().add(new Translate(320, 240));
    pane.getTransforms().add(new Scale(scale, scale));
    pane.getTransforms().add(new Translate(-5000, -7500));

    System.out.println(rectangle.getLocalToSceneTransform().transform(rectangle.getBoundsInLocal()));
    
}

With the original code you posted, you start with a 10000 by 15000 pixel rectangle with top left at 0,0 (which is the top left of the pane).

You then translate 5000 pixels left and 7500 pixels up.

This leaves the origin of the pane, and the top left of the rectangle, at (-5000, -7500) in scene coordinates, and the rectangle extends to (5000, 7500) in scene coordinates (so at this point it covers the scene).

Then you scale by a factor 72.0/5000 (after correcting the aforementioned error). This makes the rectangle width 144 and height 216 in scene coordinates, and since the origin is fixed in a scale transformation it extends from (-5000, -7500) to (-4856, -7284), again in scene coordinates. This of course places it well outside the visible area of the scene.

Finally you translate by (320, 240) in world coordinates (the coordinates of the pane, since that's the node to which the translation is applied). These equate to 320*scale = 4.608 horizontally and 240*scale = 3.456 vertically in scene coordinates, shifting the rectangle so it spans from (-4995.392, -7496.544) to (-4851.392 ,-7280.544) in scene coordinates (still far from the visible portion of the scene).

You can use the same debugging line as above to see where the rectangle lies in scene coordinates.

huangapple
  • 本文由 发表于 2023年6月5日 06:19:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76402621.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定