javaFX中的平滑路径

时间:2016-03-18 15:48:35

标签: javafx

我在JavaFX中绘制Path,我想绘制此类型的路径(lineToarcTo): picture only for illustration

有没有简单的方法如何将弧与线(和其他弧)连接起来,以获得这样的平滑路径?

我不知道弧的周长,我只知道起点和终点。在图片中只有半圈,我还需要绘制其他类型的arcTo

我到目前为止唯一的想法是获得路径结束的方向,然后计算并加入另一个arcTo / lineTo朝这个方向。但是,我没有找到任何方法来做到这一点。

1 个答案:

答案 0 :(得分:3)

Cubic Bezier Curve由四个点定义,startend和两个"控制点" control1control2。它具有

的属性
  • 它从startend
  • 开始和结束
  • 最初(即start)它与startcontrol1
  • 之间的线段相切
  • 位于end,它与control2end
  • 之间的线段相切

曲线的形状也取决于从startcontrol1control2end的线段的大小:粗略地说这些控制了&# 34;速度"在转向终点之前,线接近控制点。

因此,要使用平滑曲线连接两个线段,可以使用三次曲线,其起点是第一个线段的末尾,其末端是第二个线段的起点。仅通过将每条线延伸到其末端(第一行)或开始(第二行)之后来计算控制点。使用相同长度的扩展将提供平衡的外观。

这是一个例子。运行此代码:在窗格上拖动鼠标以绘制一条线,然后再次绘制第二条线,这两条线将通过三次曲线连接。

import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.CubicCurve;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

public class JoinLineSegmentsWithCubic extends Application {

    private Line unconnectedLine = null ;
    private Line currentDraggingLine = null ;

    @Override
    public void start(Stage primaryStage) {
        Pane pane = new Pane();
        pane.setOnDragDetected(e -> {
            currentDraggingLine = new Line(e.getX(), e.getY(), e.getX(), e.getY());
            pane.getChildren().add(currentDraggingLine);
        });
        pane.setOnMouseDragged(e -> {
            if (currentDraggingLine != null) {
                currentDraggingLine.setEndX(e.getX());
                currentDraggingLine.setEndY(e.getY());
            }
        });
        pane.setOnMouseReleased(e -> {
            if (currentDraggingLine != null) {
                currentDraggingLine.setEndX(e.getX());
                currentDraggingLine.setEndY(e.getY());

                if (unconnectedLine != null) {
                    connect(unconnectedLine, currentDraggingLine, pane);
                }
                unconnectedLine = currentDraggingLine ;
                currentDraggingLine = null ;
            }
        });

        Scene scene = new Scene(pane, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void connect(Line line1, Line line2, Pane parent) {
        Point2D line1Start = new Point2D(line1.getStartX(), line1.getStartY());
        Point2D line1End = new Point2D(line1.getEndX(), line1.getEndY());
        Point2D line2Start = new Point2D(line2.getStartX(), line2.getStartY());
        Point2D line2End = new Point2D(line2.getEndX(), line2.getEndY());

        double line1Length = line1End.subtract(line1Start).magnitude();
        double line2Length = line2End.subtract(line2Start).magnitude();

        // average length:
        double aveLength = (line1Length + line2Length) / 2 ;

        // extend line1 in direction of line1 for aveLength:
        Point2D control1 = line1End.add(line1End.subtract(line1Start).normalize().multiply(aveLength));

        // extend line2 in (reverse) direction of line2 for aveLength:
        Point2D control2 = line2Start.add(line2Start.subtract(line2End).normalize().multiply(aveLength));

        CubicCurve cc = new CubicCurve(
                line1End.getX(), line1End.getY(), 
                control1.getX(), control1.getY(), 
                control2.getX(), control2.getY(), 
                line2Start.getX(), line2Start.getY());

        cc.setStroke(Color.BLACK);
        cc.setFill(null);

        parent.getChildren().add(cc);
    }

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

enter image description here

您还可以使用路径元素CubicCurveTo将三次贝塞尔曲线合并到路径中。下面是一个使用Path的示例,其中垂直线段由三次贝塞尔曲线连接(生成路径的方法适用于任意线段):

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;

public class SmoothPathWithCubicBezier extends Application {

    @Override
    public void start(Stage primaryStage) {
        double[] points = new double[24];
        for (int i = 0; i < 24 ; i+=8) {
            double x = (1 + i/8) * 200 ;
            points[i] = x ;
            points[i+1] = 200 ;
            points[i+2] = x ;
            points[i+3] = 400 ;
            points[i+4] = x + 100 ;
            points[i+5] = 400 ;
            points[i+6] = x+ 100 ;
            points[i+7] = 200 ;
        }
        Pane pane = new Pane();
        pane.getChildren().add(createPath(points));

        Scene scene = new Scene(pane, 800, 800);
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    // points should be an array of length a multiple of four, 
    // defining a set of lines {startX1, startY1, endX1, endY1, startX2, ...}
    // The path will consist of the straight line segments, joined by
    // cubic beziers
    private Path createPath(double[] points) {
        Path path = new Path();
        for (int i = 0 ; i < points.length; i+=4) {
            double startX = points[i];
            double startY = points[i+1];
            double endX = points[i+2];
            double endY = points[i+3];

            if (i==0) {
                MoveTo moveTo = new MoveTo(startX, startY);
                moveTo.setAbsolute(true);
                path.getElements().add(moveTo);
            } else {

                double lastStartX = points[i-4];
                double lastStartY = points[i-3];
                double lastEndX = points[i-2];
                double lastEndY = points[i-1];

                double lastLength = Math.sqrt((lastEndX-lastStartX)*(lastEndX-lastStartX)
                        + (lastEndY-lastStartY)*(lastEndY-lastStartY));
                double length = Math.sqrt((endX-startX)*(endX-startX)
                        + (endY-startY)*(endY-startY));
                double aveLength = (lastLength+length)/2;

                double control1X = lastEndX + (lastEndX-lastStartX)*aveLength/lastLength ;
                double control1Y = lastEndY + (lastEndY-lastStartY)*aveLength/lastLength ;

                double control2X = startX - (endX-startX)*aveLength/length ;
                double control2Y = startY - (endY-startY)*aveLength/length ;

                CubicCurveTo cct = new CubicCurveTo(control1X, control1Y, control2X, control2Y, startX, startY);
                cct.setAbsolute(true);
                path.getElements().add(cct);

            }
            LineTo lineTo = new LineTo(endX, endY);
            lineTo.setAbsolute(true);
            path.getElements().add(lineTo);

        }

        return path ;
    }

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

这给出了

enter image description here