2

This question is an extension of the below question:

How to implement collision detection on nodes which are in StackPane (in JavaFX)?.

After implementing the answer given in the above question it seems bound in local and bound in parent are same in AnchorPane but they are different in StackPane.

Please let me know the actual behaviour of the bounds of a node with respect to AnchorPane and StackPane.

Community
  • 1
  • 1
Angom
  • 721
  • 1
  • 11
  • 27

2 Answers2

0

The official Javadoc (for JavaFX 8, but the logic remains unchanged in newer versions) says:

Bounding Rectangles

[...] Each Node has a read-only boundsInLocal variable which specifies the bounding rectangle of the Node in untransformed local coordinates.

Each Node also has a read-only boundsInParent variable which specifies the bounding rectangle of the Node after all transformations have been applied, including those set in transforms, scaleX/scaleY, rotate, translateX/translateY, and layoutX/layoutY. It is called "boundsInParent" because the rectangle will be relative to the parent's coordinate system.

The behavior of the methods is the same when a Node is placed in an AnchorPane or a StackPane. The observed differences are because of the default layout of the AnchorPane (which places children in its top left corner) and the StackPane (which centers children).

enter image description here

In this example picture, there are three black rectangles. All rectangles are defined as: [x=0.0, y=0.0, width=20.0, height=30.0, fill=0x000000ff]

For all three rectangles, getBoundsInLocal is [**minX:0.0, minY:0.0,** minZ:0.0, **width:20.0, height:30.0**, depth:0.0, maxX:20.0, maxY:30.0, maxZ:0.0]

The left parent (yellow) is an AnchorPane with the default anchors. It places the rectangle in its top left corner (coordinate 0,0). As there are no other transformations (scale, rotation, ...), the boundsInParent for the rectangle in the yellow AnchorPane are therefore identical to its boundsInLocal: rect1.getBoundsInParent (AnchorPane yellow)=BoundingBox [minX:0.0, minY:0.0, minZ:0.0, width:20.0, height:30.0, depth:0.0, maxX:20.0, maxY:30.0, maxZ:0.0]

The center parent (green) is a StackPane, which centers its children by default. The x/y values of the contained rectangle's boundsInParent are therefore different from the boundsInLocal: rect2.getBoundsInParent (StackPane green)=BoundingBox [minX:24.0, minY:35.20000076293945, minZ:0.0, width:20.0, height:29.999996185302734, depth:0.0, maxX:44.0, maxY:65.19999694824219, maxZ:0.0]

The right parent (red) is another AnchorPane, but with different anchors:

AnchorPane.setRightAnchor(rect3, 0d);
AnchorPane.setBottomAnchor(rect3, 0d);

Again, x/y values of the contained rectangle's boundsInParent are therefore different from the boundsInLocal: rect3.getBoundsInParent (AnchorPane red)=BoundingBox [minX:47.20000076293945, minY:69.5999984741211, minZ:0.0, width:19.999996185302734, height:30.0, depth:0.0, maxX:67.19999694824219, maxY:99.5999984741211, maxZ:0.0]

This should show that there is no difference between the behavior/computation of the bounds in AnchorPane and StackPane - the difference is simply due to their default layouting, and is consistent in all cases.

To try for yourself, I here is the example code:

public class LocalAndParentBoundsApp extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        final Rectangle rect1 = new Rectangle(20, 30);
        final Rectangle rect2 = new Rectangle(20, 30);
        final Rectangle rect3 = new Rectangle(20, 30);

        final AnchorPane anchorPane = new AnchorPane(rect1);
        anchorPane.setPrefWidth(100);
        anchorPane.setPrefHeight(100);
        anchorPane.setStyle("-fx-background-color: yellow;");

        final StackPane stackPane = new StackPane(rect2);
        stackPane.setPrefWidth(100);
        stackPane.setPrefHeight(100);
        stackPane.setStyle("-fx-background-color: green;");

        final AnchorPane anchorPane2 = new AnchorPane(rect3);
        anchorPane2.setPrefWidth(100);
        anchorPane2.setPrefHeight(100);
        anchorPane2.setStyle("-fx-background-color: red;");
        AnchorPane.setRightAnchor(rect3, 0d);
        AnchorPane.setBottomAnchor(rect3, 0d);

        primaryStage.setTitle("LocalAndParentBounds");

        HBox root = new HBox(anchorPane, stackPane, anchorPane2);
        primaryStage.setScene(new Scene(root, 200, 100));
        primaryStage.show();

        System.out.println("rect1=" + rect1);
        System.out.println("rect1.getBoundsInLocal=" + rect1.getBoundsInLocal());
        System.out.println("rect1.getBoundsInParent (AnchorPane yellow)=" + rect1.getBoundsInParent());

        System.out.println("rect2=" + rect2);
        System.out.println("rect2.getBoundsInLocal=" + rect2.getBoundsInLocal());
        System.out.println("rect2.getBoundsInParent (StackPane green)=" + rect2.getBoundsInParent());

        System.out.println("rect3=" + rect3);
        System.out.println("rect3.getBoundsInLocal=" + rect3.getBoundsInLocal());
        System.out.println("rect3.getBoundsInParent (AnchorPane red)=" + rect3.getBoundsInParent());
    }
}
Sonnenkind
  • 74
  • 2
-1

You can use this function for detecting collision between two images but you can expands it to two node

public class CollisionController {


public synchronized static boolean haveCollision(Image i1, Image i2, Bounds b1, Bounds b2) {
    if (i2 == null || i1 == null) return false;
    if (!b1.intersects(b2)) return false;
    PixelReader pr1 = i1.getPixelReader();
    PixelReader pr2 = i2.getPixelReader();
  

    for (int x = 1; x < i1.getWidth() - 1; x++) {
        for (int y = 1; y < i1.getHeight() - 1; y++) {
            Location coordinate1 = new Location(x, y);
            Location coordinate2 = calculateSecondPointCoordinate(coordinate1, b1, b2);
            if (isSecondImageBoundContainsCurrentPoint(b2, (int) (coordinate1.getX() + b1.getMinX()), (int) (coordinate1.getY() + b1.getMinY())) && isTwoPointsHaveOpacityBiggerThanZero(pr1, pr2, coordinate1, coordinate2))
                return true;
        }
    }
    return false;
}




private static Location calculateSecondPointCoordinate(Location currentPoint, Bounds b1, Bounds b2) {
    double secondImageX = calculateCurrentCoordinateOfSecondImage((int) (currentPoint.getX() + b1.getMinX()), b2.getMinX());
    double secondImageY = calculateCurrentCoordinateOfSecondImage((int) (currentPoint.getY() + b1.getMinY()), b2.getMinY());
    return new Location(secondImageX, secondImageY);
}

private static double calculateCurrentCoordinateOfSecondImage(int currentPoint, double i2Bound) {
    return (currentPoint - i2Bound);
}

private static boolean isSecondImageBoundContainsCurrentPoint(Bounds bound, int x, int y) {
    return bound.contains(x, y);

}

private static boolean isTwoPointsHaveOpacityBiggerThanZero(PixelReader pr1, PixelReader pr2, Location currentPoint, Location secondImageCoordinate) {
    return !PixelOpacityIsZero(pr1, currentPoint.getX(), currentPoint.getY()) && !PixelOpacityIsZero(pr2, secondImageCoordinate.getX(), secondImageCoordinate.getY());
}


private static boolean PixelOpacityIsZero(PixelReader imagePixelReader, double x, double y) {
        return imagePixelReader.getColor((int) x, (int) y).getOpacity() == 0;
    
}

In this function i get two images pixels by pixelReader , then i iterate on first image (i1) pixels and then i check if pixel includes the pixels of second image then i check opacity of two pixels in both images is bigger than 0. opacity bigger than zero means pixel has rgb color there. if both conditions became true it means both have collision with each other.

public class Location {

double x;

double y;

public double getX() {
    return x;
}

public double getY() {
    return y;
}

public Location(double x, double y) {
    this.x = x;
    this.y = y;
}

}

This is location class which is used in collision function