1

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.

Will Hartung
  • 115,893
  • 19
  • 128
  • 203
  • 3
    The `Translate` transform's units are already in pixels, I believe. And the `Scale` transform is a factor (e.g., a scale of `1.2` will scale the node to 120%). – Slaw Jun 04 '23 at 23:10
  • 2
    Well, there's also [this issue](https://stackoverflow.com/questions/4685450/int-division-why-is-the-result-of-1-3-0). Whatever you do with the transforms, if you have a scale of zero you're not likely to see much. – James_D Jun 04 '23 at 23:14
  • *"I then translate the pane to place the center of the rectangle at 0,0."*. This is a misunderstanding. The rectangle is contained in the pane, so translating the pane will move the rectangle with it. You can't change the position of the rectangle relative to the pane by translating the pane. The rectangle still has its top left corner at (0,0) in the pane's coordinate system. – James_D Jun 05 '23 at 01:31

1 Answers1

4

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.

James_D
  • 201,275
  • 16
  • 291
  • 322