bluestyle
bluestyle

Reputation: 11

How to implement an arrow shape in JavaFX?

I'm writing a JavaFX Application which needs to draw directed graphs. I want to implement this by shapes because I want the vertices can be dragged. I use a circle to represent a vertex, but I meet a problem when I try to find a shape to represent the directed edge.
A directed edge should looks like an arrow, my idea is to combine a line with a triangle to represent it. So I extend the javafx.scene.shape.Line class:

class Arrow extends Line {
    Polygon triangle;

    Arrow(double startX, double startY, double endX, double endY) {
        super(startX, startY, endX, endY);
        double dx = endX - startX;
        double dy = endY - startY;
        double angle = Math.atan2(dy, dx);
        triangle = new Polygon(endX, endY, endX - 8, endY + 4, endX - 8, endY - 4);
        triangle.setRotate(Math.toDegrees(angle));
        triangle.rotateProperty().bind()
            triangle.rotateProperty().bind(Bindings.createDoubleBinding(() -> { 
            double x = this.getEndX() - this.getStartX();
            double y = this.getEndY() - this.getStartY();
            double a = Math.atan2(y, x);
            return Math.toDegrees(a);
        }));
    }
}

As the vertices can be dragged, of course the edge should move at the same time, meaning that the triangle inside the arrow should move and rotate according to the line's properties. But I haven't found a way to bind the Polygon's position property with the Line's endXProperty and endYProperty.
So I want to ask how to achieve this? Or is there any better way to implement a movable arrow(directed edge) by shapes?

Upvotes: 1

Views: 2527

Answers (2)

mank
mank

Reputation: 31

I was looking an arrow just like yours it is mathematically true but needs some programming stuff I created dx and dy as property and add a listener for them and created a "Rotate" for the triangle. it's work perfect but you can bind other propertis like fill , color and etc..

import javafx.beans.binding.DoubleBinding;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.scene.transform.Rotate;

public class Arrow extends Line {

private Polygon triangle;
private Pane Parent;


public Arrow(Pane Parent) {
    this.Parent = Parent;
    Parent.getChildren().addAll(this);
    canvas();
}

private DoubleBinding dx;
private DoubleBinding dy;

private void canvas(){
    dx = endXProperty().add(startXProperty().negate());
    dy = endYProperty().add(startYProperty().negate());
    triangle = new Polygon(getEndX(), getEndY(), getEndX() - 16, getEndY() + 8, getEndX() - 16, getEndY() - 8);
    var rotate = new Rotate(0,0,0,1,Rotate.Z_AXIS);
    triangle.getTransforms().add(rotate);
    dx.addListener((observable, oldValue, newValue) -> {
        rotate.setAngle(getAngle(dy.doubleValue(), newValue.doubleValue()));
    });
    dy.addListener((observable, oldValue, newValue) -> {
        rotate.setAngle(getAngle(newValue.doubleValue(), dx.doubleValue()));
    });
    triangle.layoutXProperty().bind(endXProperty());
    triangle.layoutYProperty().bind(endYProperty());
    Parent.getChildren().add(triangle);
}

private double getAngle(double dy ,double dx){
    return Math.toDegrees(Math.atan2(dy, dx));
}

}

Upvotes: 1

findusl
findusl

Reputation: 2644

If you would add ChangeListeners to the endX and endY Property and relocate the Triangle inside the changed method, then you can move the Triangle with the Line.

Upvotes: 0

Related Questions