JavaFX line/curve with arrow head

久未见 提交于 2019-11-28 07:01:32
José Pereda

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:

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);

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!