JavaFX line/curve with arrow head

后端 未结 1 580
没有蜡笔的小新
没有蜡笔的小新 2020-12-08 23:01

I\'m creating a graph in JavaFX which is supposed to be connected by directed edges. Best would be a bicubic curve. Does anyone know how to do add the arrow heads?

T

相关标签:
1条回答
  • 2020-12-08 23:33

    Since you're already dealing with shapes (curves), the best approach for the arrows is just keep adding more shapes to the group, using Path.

    Based on this answer, I've added two methods: one for getting any point of the curve at a given parameter between 0 (start) and 1 (end), one for getting the tangent to the curve at that point.

    With these methods now you can draw an arrow tangent to the curve at any point. And we use them to create two at the start (0) and at the end (1):

    @Override
    public void start(Stage primaryStage) {
    
        Group root = new Group();
    
        // bending curve
        Rectangle srcRect1 = new Rectangle(100,100,50,50);
        Rectangle dstRect1 = new Rectangle(300,300,50,50);
    
        CubicCurve curve1 = new CubicCurve( 125, 150, 125, 225, 325, 225, 325, 300);
        curve1.setStroke(Color.BLACK);
        curve1.setStrokeWidth(1);
        curve1.setFill( null);
    
        double size=Math.max(curve1.getBoundsInLocal().getWidth(),
                             curve1.getBoundsInLocal().getHeight());
        double scale=size/4d;
    
        Point2D ori=eval(curve1,0);
        Point2D tan=evalDt(curve1,0).normalize().multiply(scale);
        Path arrowIni=new Path();
        arrowIni.getElements().add(new MoveTo(ori.getX()+0.2*tan.getX()-0.2*tan.getY(),
                                            ori.getY()+0.2*tan.getY()+0.2*tan.getX()));
        arrowIni.getElements().add(new LineTo(ori.getX(), ori.getY()));
        arrowIni.getElements().add(new LineTo(ori.getX()+0.2*tan.getX()+0.2*tan.getY(),
                                            ori.getY()+0.2*tan.getY()-0.2*tan.getX()));
    
        ori=eval(curve1,1);
        tan=evalDt(curve1,1).normalize().multiply(scale);
        Path arrowEnd=new Path();
        arrowEnd.getElements().add(new MoveTo(ori.getX()-0.2*tan.getX()-0.2*tan.getY(),
                                            ori.getY()-0.2*tan.getY()+0.2*tan.getX()));
        arrowEnd.getElements().add(new LineTo(ori.getX(), ori.getY()));
        arrowEnd.getElements().add(new LineTo(ori.getX()-0.2*tan.getX()+0.2*tan.getY(),
                                            ori.getY()-0.2*tan.getY()-0.2*tan.getX()));
    
        root.getChildren().addAll(srcRect1, dstRect1, curve1, arrowIni, arrowEnd);
    
        primaryStage.setScene(new Scene(root, 800, 600));
        primaryStage.show();
    }
    
    /**
     * Evaluate the cubic curve at a parameter 0<=t<=1, returns a Point2D
     * @param c the CubicCurve 
     * @param t param between 0 and 1
     * @return a Point2D 
     */
    private Point2D eval(CubicCurve c, float t){
        Point2D p=new Point2D(Math.pow(1-t,3)*c.getStartX()+
                3*t*Math.pow(1-t,2)*c.getControlX1()+
                3*(1-t)*t*t*c.getControlX2()+
                Math.pow(t, 3)*c.getEndX(),
                Math.pow(1-t,3)*c.getStartY()+
                3*t*Math.pow(1-t, 2)*c.getControlY1()+
                3*(1-t)*t*t*c.getControlY2()+
                Math.pow(t, 3)*c.getEndY());
        return p;
    }
    
    /**
     * Evaluate the tangent of the cubic curve at a parameter 0<=t<=1, returns a Point2D
     * @param c the CubicCurve 
     * @param t param between 0 and 1
     * @return a Point2D 
     */
    private Point2D evalDt(CubicCurve c, float t){
        Point2D p=new Point2D(-3*Math.pow(1-t,2)*c.getStartX()+
                3*(Math.pow(1-t, 2)-2*t*(1-t))*c.getControlX1()+
                3*((1-t)*2*t-t*t)*c.getControlX2()+
                3*Math.pow(t, 2)*c.getEndX(),
                -3*Math.pow(1-t,2)*c.getStartY()+
                3*(Math.pow(1-t, 2)-2*t*(1-t))*c.getControlY1()+
                3*((1-t)*2*t-t*t)*c.getControlY2()+
                3*Math.pow(t, 2)*c.getEndY());
        return p;
    }
    

    And this is what it looks like:

    CubicCurve with arrows

    If you move the control points, you'll see that the arrows are already well oriented:

    CubicCurve curve1 = new CubicCurve( 125, 150, 55, 285, 375, 155, 325, 300);
    

    CubicCurve with arrows

    0 讨论(0)
提交回复
热议问题