targat
targat

Reputation: 25

How to connect a line to circle (not the center) using java fx

I am using javafx I wont to Connect a line to 2 circle the connection must be to the surface of the circle not the center

And when the circle move the line connect to the best point in the circle. when connecting, the line and the circle must not overlap

Thank you

Edit

The solution i am looking for require that

  1. the line intersect with the circle so the tangent is not the solution
  2. if the line virtually continue it must intersect with the center of the circle

Upvotes: 0

Views: 1358

Answers (1)

fabian
fabian

Reputation: 82461

Assuming the position is only modified using the centerX and centerY properties, this can be done by simply adding listeners to those properties.

The ends of the line can be determined by using the center of a circle translated a distance equal to the radius in direction of the other center.

Note that this doesn't take the stroke widths into account:

public static Point2D getDirection(Circle c1, Circle c2) {
    return new Point2D(c2.getCenterX() - c1.getCenterX(), c2.getCenterY() - c1.getCenterY()).normalize();
}

public static void connect(Circle c1, Circle c2, Line line) {
    InvalidationListener startInvalidated = observable -> {
        Point2D dir = getDirection(c1, c2);
        Point2D diff = dir.multiply(c1.getRadius());
        line.setStartX(c1.getCenterX() + diff.getX());
        line.setStartY(c1.getCenterY() + diff.getY());
    };
    InvalidationListener endInvalidated = observable -> {
        Point2D dir = getDirection(c2, c1);
        Point2D diff = dir.multiply(c2.getRadius());
        line.setEndX(c2.getCenterX() + diff.getX());
        line.setEndY(c2.getCenterY() + diff.getY());
    };
    c1.centerXProperty().addListener(startInvalidated);
    c1.centerYProperty().addListener(startInvalidated);
    c1.radiusProperty().addListener(startInvalidated);

    startInvalidated.invalidated(null);

    c2.centerXProperty().addListener(endInvalidated);
    c2.centerYProperty().addListener(endInvalidated);
    c2.radiusProperty().addListener(endInvalidated);

    endInvalidated.invalidated(null);
}

@Override
public void start(Stage primaryStage) {
    Circle c1 = new Circle(100, 100, 50, null);
    c1.setStroke(Color.BLUE);

    Circle c2 = new Circle(200, 200, 50, null);
    c2.setStroke(Color.RED);

    Line line = new Line();

    connect(c1, c2, line);

    Pane pane = new Pane(line, c1, c2);

    // demonstrate update during movement
    Timeline timeline = new Timeline(
            new KeyFrame(Duration.ZERO, new KeyValue(c1.centerXProperty(), 100)),
            new KeyFrame(Duration.ZERO, new KeyValue(c1.centerYProperty(), 100)),
            new KeyFrame(Duration.seconds(1), new KeyValue(c1.centerXProperty(), 300)),
            new KeyFrame(Duration.seconds(1), new KeyValue(c1.centerYProperty(), 50)),
            new KeyFrame(Duration.ZERO, new KeyValue(c2.centerXProperty(), 200)),
            new KeyFrame(Duration.ZERO, new KeyValue(c2.centerYProperty(), 200)),
            new KeyFrame(Duration.seconds(1), new KeyValue(c2.centerXProperty(), 100)),
            new KeyFrame(Duration.seconds(1), new KeyValue(c2.centerYProperty(), 100))
    );

    timeline.setCycleCount(Animation.INDEFINITE);
    timeline.setAutoReverse(true);

    timeline.play();

    Scene scene = new Scene(pane, 500, 500);

    primaryStage.setScene(scene);
    primaryStage.show();
}

If you use larger stroke widths, you may need to take them into account...

Note that the whole problem becomes a lot easier, if the fill of the circles with fully opaque paint and don't modify the blend effects, since the circles after the line will result in the circles being drawn on top of the line in that case, which means connecting the centers would suffice.

Upvotes: 1

Related Questions