Joris Kinable
Joris Kinable

Reputation: 2411

Javafx 8 drawing a line between translated nodes

How do you draw a line between the centers of translated nodes? Given for example the following code snippet:

public class Test extends Application{
    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();

        Circle circle1=new Circle(10, Color.GREEN);
        root.getChildren().add(circle1);
        Circle circle2=new Circle(10, Color.RED);
        root.getChildren().add(circle2);

        Scene scene = new Scene(root, 300, 250);
        primaryStage.setScene(scene);
        primaryStage.show();

        circle1.setTranslateX(100);

        Line line=new Line(circle1.getCenterX(), circle1.getCenterY(), circle2.getCenterX(), circle2.getCenterY());
        root.getChildren().add(line);
    }
    public static void main(String[] args) {
        launch(args);
    }
}

Running this application will clearly show a red and a green circle. However, there won't be a line because each of the centers of the circles are at the coordinates (0,0). Nevertheless, the circles do not cover each other because one of the circles is translated. This doesn't work:

Line line=new Line(circle1.getCenterX()+circle1.getTranslateX(), circle1.getCenterY()+circle1.getTranslateY(), circle2.getCenterX()+circle2.getTranslateX(), circle2.getCenterY()+circle2.getTranslateY());

Finally, let's assume that there is an approach to draw a line connecting the centers of the two circles. If, after the line is drawn, I would invoke circle2.setTranslateX(50);, how do I ensure that the endpoint of the line on the side of circle2 moves accordingly?

Upvotes: 1

Views: 5217

Answers (1)

James_D
James_D

Reputation: 209299

A StackPane is a managed layout pane, meaning that it manages the positions of its child nodes (by default it centers them); the translation is applied after the StackPane positions the nodes. This is why the circles appear in different locations but the line is not where you expect. Using a Pane instead of a StackPane will make things work as you expect.

To keep the line in the correct position relative to the circles when the circles are repositioned dynamically, bind the startX, startY, endX, and endY properties, instead of just setting them.

import javafx.animation.Animation;
import javafx.animation.ParallelTransition;
import javafx.animation.SequentialTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;

public class LineConnectingCircles extends Application{
    @Override
    public void start(Stage primaryStage) {
        Pane root = new Pane();

        Circle circle1=new Circle(10, Color.GREEN);
        root.getChildren().add(circle1);
        Circle circle2=new Circle(10, Color.RED);
        root.getChildren().add(circle2);

        // move circles so we can see them:

        circle1.setTranslateX(100);

        circle2.setTranslateY(50);


        Line line = new Line();

        // bind ends of line:
        line.startXProperty().bind(circle1.centerXProperty().add(circle1.translateXProperty()));
        line.startYProperty().bind(circle1.centerYProperty().add(circle1.translateYProperty()));
        line.endXProperty().bind(circle2.centerXProperty().add(circle2.translateXProperty()));
        line.endYProperty().bind(circle2.centerYProperty().add(circle2.translateYProperty()));

        root.getChildren().add(line);

        // create some animations for the circles to test the line binding:

        Button button = new Button("Animate");

        TranslateTransition circle1Animation = new TranslateTransition(Duration.seconds(1), circle1);
        circle1Animation.setByY(150);


        TranslateTransition circle2Animation = new TranslateTransition(Duration.seconds(1), circle2);
        circle2Animation.setByX(150);

        ParallelTransition animation = new ParallelTransition(circle1Animation, circle2Animation);

        animation.setAutoReverse(true);
        animation.setCycleCount(2);
        button.disableProperty().bind(animation.statusProperty().isEqualTo(Animation.Status.RUNNING));
        button.setOnAction(e -> animation.play());

        BorderPane.setAlignment(button, Pos.CENTER);
        BorderPane.setMargin(button, new Insets(10));

        Scene scene = new Scene(new BorderPane(root, null, null, button, null), 300, 250);
        primaryStage.setScene(scene);
        primaryStage.show();

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

Upvotes: 4

Related Questions