deHaar
deHaar

Reputation: 18568

How to create a centerXProperty in a Rectangle to bind to?

I am developing a program for video annotation. Objects seen in a video may be marked as objects of interest and if they are interacting, the user may draw a line between two annotations. On the visible level, bject annotations are basically transparent Rectangles and relations between them are Lines. At this point it is easy to compute the center of a rectangle, but I am not able to bind the start and end of a Line to the center of the corresponding Rectangle. I have tried the following approaches:

Creating two DoubleBindings inside the rectangle class that computes the center x and y:

private DoubleBinding centerXBinding = new DoubleBinding() { 
    @Override
    protected double computeValue() {
        return getX() + getWidth() / 2;
    }
};

and then bound it to the newly created Line:

`currentRelation.startXProperty().bind(startShape.centerXBinding());`

in the controller…

The result is ok at first, the line start and end points are exactly where I want to haven them, but when a Rectangle gets dragged to another position, the line end does not move anywhere! Does anyone see the problem?

UPDATE:

The movement of a Rectangle is done by computing an offset and updating the translation values like translateX:

public class MyRectangle extends Rectangle {

    private double orgSceneX;
    private double orgSceneY;
    private double orgTranslateX;
    private double orgTranslateY;

    private void initEventHandling() {
        this.setOnMousePressed(mousePress -> {
            if (mousePress.getButton() == MouseButton.PRIMARY) {
                orgSceneX = mousePress.getSceneX();
                orgSceneY = mousePress.getSceneY();
                orgTranslateX = ((MyRectangle) mousePress.getSource()).getTranslateX();
                orgTranslateY = ((MyRectangle) mousePress.getSource()).getTranslateY();
                mousePress.consume();
            } else if (mousePress.getButton() == MouseButton.SECONDARY) {
                    System.out.println(LOG_TAG + ": right mouse button PRESS on " + this.getId() + ", event not consumed");
            }
        });

        this.setOnMouseDragged(mouseDrag -> {
            if (mouseDrag.getButton() == MouseButton.PRIMARY) {

                double offsetX = mouseDrag.getSceneX() - orgSceneX;
                double offsetY = mouseDrag.getSceneY() - orgSceneY;
                double updateTranslateX = orgTranslateX + offsetX;
                double updateTranslateY = orgTranslateY + offsetY;
                this.setTranslateX(updateTranslateX);
                this.setTranslateY(updateTranslateY);
                mouseDrag.consume();
            }
        });
    }
}

Upvotes: 0

Views: 704

Answers (1)

James_D
James_D

Reputation: 209358

Your binding needs to invalidate when either the xProperty or widthProperty are invalidated (so that anything bound to it knows to recompute). You can do this by calling the bind method in the custom binding's constructor:

private DoubleBinding centerXBinding = new DoubleBinding() { 

    {
        bind(xProperty(), widthProperty());
    }

    @Override
    protected double computeValue() {
        return getX() + getWidth() / 2;
    }
};

Note you can also do

private DoubleBinding centerXBinding = xProperty().add(widthProperty().divide(2));

The choice between the two is really just a matter of which style you prefer.

Binding to the x and width properties assumes, obviously, that you are moving the rectangle by changing one or both of those properties. If you are moving the rectangle by some other means (e.g. by changing one of its translateX or translateY properties, or by altering its list of transformations), then you need to observe the boundsInParentProperty instead:

private DoubleBinding centerXBinding = new DoubleBinding() {

    {
        bind(boundsInParentProperty());
    }

    @Override
    protected double computeValue() {
        Bounds bounds = getBoundsInParent();
        return (bounds.getMinX() + bounds.getMaxX()) / 2 ;
    }
}

This binding will give the x-coordinate of the center of the rectangle in the parent's coordinate system (which is usually the coordinate system you want).

Upvotes: 2

Related Questions