Reputation: 18568
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 Rectangle
s and relations between them are Line
s. 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 DoubleBinding
s 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
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