1. 程式人生 > >JavaFX 圈選 仿百度地圖路線

JavaFX 圈選 仿百度地圖路線

在這裡插入圖片描述
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Polyline;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;

public class PolygonTest2 extends Application {
// 最近一次儲存的箭尾
Point2D arrowTailPoint;
// 最近的箭是否已繪製
boolean isHasLastArrow;
// 上一個居中折線的點
Point2D lastPolylinePoint2D;
// 居中折線的起點
Point2D startPolylinePoint2D;

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

@Override
public void start(Stage primaryStage) {
    Polygon polygon = new Polygon();
    polygon.setStroke(Color.GREEN);
    polygon.setStrokeWidth(8);
    polygon.setFill(null);

    Polyline polyline = new Polyline();
    polyline.setStrokeWidth(2);
    polyline.setStroke(Color.YELLOW);
    DropShadow polylineDropShadow = new DropShadow();
    polylineDropShadow.setRadius(5);
    polyline.setEffect(polylineDropShadow);

    Pane pane = new Pane();
    pane.setPrefWidth(1000);
    pane.setMaxHeight(500);

    // 箭尾間隔
    double arrowInternal = 100;
    // 箭身長度
    double arrowLength = 10;
    // 界線和居中折線間的距離
    double polylineDistance = polygon.getStrokeWidth() / 2;
    // 箭身中點垂足到箭頭一側終點的距離,是箭頭斜度的關鍵引數
    double arrowHeadAngleKeyValue = polylineDistance - 2;

    // 一側折線(在順時針時表現為外側,在逆時針時表現為內側)
    Polyline firstSidePolyline = new Polyline();
    firstSidePolyline.setStroke(Color.BLACK);
    firstSidePolyline.setStrokeWidth(1);
    // 另一側折線(在順時針時表現為內側,在逆時針時表現為外側)
    Polyline secondSidePolyline = new Polyline();
    secondSidePolyline.setStroke(Color.BLACK);
    secondSidePolyline.setStrokeWidth(1);

    List<Polyline> arrowHeadPolylineList = new ArrayList<>();
    List<Polyline> arrowBodyPolylineList = new ArrayList<>();

    Label label1 = new Label("測試標籤1");
    label1.setTranslateX(50);
    label1.setTranslateY(50);
    Label label2 = new Label("測試標籤2");
    label2.setTranslateX(100);
    label2.setTranslateY(100);
    Label label3 = new Label("測試標籤3");
    label3.setTranslateX(150);
    label3.setTranslateY(150);
    // 加入測試標籤
    pane.getChildren().addAll(label1,label2,label3);

    pane.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            if (event.getButton().equals(MouseButton.PRIMARY)) {
                // 清空
                pane.getChildren().clear();
                arrowHeadPolylineList.clear();
                arrowBodyPolylineList.clear();

                polygon.getPoints().clear();
                firstSidePolyline.getPoints().clear();
                secondSidePolyline.getPoints().clear();
                polyline.getPoints().clear();

                // 加入測試標籤
                pane.getChildren().addAll(label1,label2,label3);

                // 加入折線
                pane.getChildren().add(polyline);
                polyline.getPoints().add(event.getX());
                polyline.getPoints().add(event.getY());

                polygon.getPoints().add(event.getX());
                polygon.getPoints().add(event.getY());
                arrowTailPoint = new Point2D(event.getX(), event.getY());
                lastPolylinePoint2D = new Point2D(event.getX(), event.getY());
                startPolylinePoint2D = new Point2D(event.getX(), event.getY());
                isHasLastArrow = false;
            }
        }
    });

    pane.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            if (event.getButton().equals(MouseButton.PRIMARY)) {
                polygon.getPoints().add(event.getX());
                polygon.getPoints().add(event.getY());
                polyline.getPoints().add(event.getX());
                polyline.getPoints().add(event.getY());
                // 繪製兩側界線(平移居中線)
                // 上一個點
                double lastPolylinePointX = lastPolylinePoint2D.getX();
                double lastPolylinePointY = lastPolylinePoint2D.getY();
                // 當前點
                double currentPlylinePointX = event.getX();
                double currentPlylinePointY = event.getY();
                // 當前點和上一個點的橫縱座標差
                double polylinePointDeleteX = currentPlylinePointX - lastPolylinePointX;
                double polylinePointDeleteY = currentPlylinePointY - lastPolylinePointY;
                // 折線垂直線斜率
                double polylineVerticalArc = Math.PI / 2 - (Math.atan(Math.abs(polylinePointDeleteY / polylinePointDeleteX)));
                // 內外折線和居中折線的橫向和縱向距離
                double polylineInternalX = polylineDistance * Math.cos(polylineVerticalArc);
                double polylineInternalY = polylineDistance * Math.sin(polylineVerticalArc);
                // 折線加點
                firstSidePolyline.getPoints().addAll(new Double[]{currentPlylinePointX + ((currentPlylinePointY >= lastPolylinePointY) ? -1 : 1) * polylineInternalX, currentPlylinePointY + ((currentPlylinePointX <= lastPolylinePointX) ? -1 : 1) * polylineInternalY});
                secondSidePolyline.getPoints().addAll(new Double[]{currentPlylinePointX + ((currentPlylinePointY >= lastPolylinePointY) ? 1 : -1) * polylineInternalX, currentPlylinePointY + ((currentPlylinePointX <= lastPolylinePointX) ? 1 : -1) * polylineInternalY});
                // 儲存當前點
                lastPolylinePoint2D = new Point2D(currentPlylinePointX, currentPlylinePointY);

                // 繪製箭(根據箭尾和箭頭垂直線得到箭頭兩側終點)
                if (!isHasLastArrow && arrowTailPoint.distance(new Point2D(event.getX(), event.getY())) >= arrowLength) {
                    // 箭尾座標
                    double arrowTailX = arrowTailPoint.getX();
                    double arrowTailY = arrowTailPoint.getY();
                    // 初始箭頭座標
                    double arrowHeadX = event.getX();
                    double arrowHeadY = event.getY();
                    // 實際箭身長
                    double arrowRealLength = new Point2D(arrowHeadX, arrowHeadY).distance(new Point2D(arrowTailX, arrowTailY));
                    // 最終箭頭座標
                    arrowHeadX = arrowTailX + (arrowLength / arrowRealLength) * (arrowHeadX - arrowTailX);
                    arrowHeadY = arrowTailY + (arrowLength / arrowRealLength) * (arrowHeadY - arrowTailY);
                    // 箭身中點座標
                    double arrowMiddleX = (arrowTailX + arrowHeadX) / 2;
                    double arrowMiddleY = (arrowTailY + arrowHeadY) / 2;
                    // 箭頭和尾的橫縱座標差
                    double arrowHTDeleteX = arrowHeadX - arrowTailX;
                    double arrowHTDeleteY = arrowHeadY - arrowTailY;
                    // 箭身垂直線斜率對應的弧度
                    double arrowVerticalArc = Math.PI / 2 - (Math.atan(Math.abs(arrowHTDeleteY / arrowHTDeleteX)));
                    // 箭頭兩側終點與箭身中點的橫向和縱向距離
                    double arrowHSMInternalX = arrowHeadAngleKeyValue * Math.cos(arrowVerticalArc);
                    double arrowHSMInternalY = arrowHeadAngleKeyValue * Math.sin(arrowVerticalArc);
                    //System.out.println(arrowHSMInternalX + "--箭頭兩側終點與箭身中點的橫向和縱向距離--" + arrowHSMInternalY);
                    // 箭頭兩側終點
                    double arrowHeadSideX1;
                    double arrowHeadSideX2;
                    double arrowHeadSideY1;
                    double arrowHeadSideY2;
                    if (arrowHTDeleteX * arrowHTDeleteY <= 0) {
                        // 箭身呈矩陣副對角線方向
                        arrowHeadSideX1 = arrowMiddleX + arrowHSMInternalX;
                        arrowHeadSideY1 = arrowMiddleY + arrowHSMInternalY;
                        arrowHeadSideX2 = arrowMiddleX - arrowHSMInternalX;
                        arrowHeadSideY2 = arrowMiddleY - arrowHSMInternalY;
                    } else {
                        // 箭身呈矩陣主對角線方向
                        arrowHeadSideX1 = arrowMiddleX + arrowHSMInternalX;
                        arrowHeadSideY1 = arrowMiddleY - arrowHSMInternalY;
                        arrowHeadSideX2 = arrowMiddleX - arrowHSMInternalX;
                        arrowHeadSideY2 = arrowMiddleY + arrowHSMInternalY;
                    }

                    //System.out.println(arrowHeadSideX1 + "_" + arrowHeadSideY1 + "--箭頭兩側終點--" + arrowHeadSideX2 + "_" + arrowHeadSideY2);
                    // 繪製箭頭
                    Polyline arrowHeadPolyline = new Polyline();
                    arrowHeadPolyline.setStroke(Color.WHITE);
                    arrowHeadPolyline.setStrokeWidth(2);
                    arrowHeadPolyline.getPoints().add(arrowHeadSideX1);
                    arrowHeadPolyline.getPoints().add(arrowHeadSideY1);
                    arrowHeadPolyline.getPoints().add(arrowHeadX);
                    arrowHeadPolyline.getPoints().add(arrowHeadY);
                    arrowHeadPolyline.getPoints().add(arrowHeadSideX2);
                    arrowHeadPolyline.getPoints().add(arrowHeadSideY2);
                    // 加入箭頭
                    arrowHeadPolylineList.add(arrowHeadPolyline);
                    // 繪製箭身
                    /*Polyline arrowBodyPolyline = new Polyline();
                    arrowBodyPolyline.setStroke(Color.WHITE);
                    arrowBodyPolyline.setStrokeWidth(2);
                    arrowBodyPolyline.getPoints().add(arrowTailX);
                    arrowBodyPolyline.getPoints().add(arrowTailY);
                    arrowBodyPolyline.getPoints().add(arrowHeadX);
                    arrowBodyPolyline.getPoints().add(arrowHeadY);
                    // 加入箭身
                    arrowBodyPolylineList.add(arrowBodyPolyline);*/

                    isHasLastArrow = true;
                }

                // 儲存下一個箭尾
                if (arrowTailPoint.distance(new Point2D(event.getX(), event.getY())) >= arrowInternal) {
                    arrowTailPoint = new Point2D(event.getX(), event.getY());
                    isHasLastArrow = false;
                }
            }
        }
    });

    pane.addEventHandler(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            // 清空
            pane.getChildren().clear();
            // 加入測試標籤
            pane.getChildren().addAll(label1,label2,label3);
            // 加入居中折線對應的多邊形、兩側界線
            pane.getChildren().addAll(polygon, firstSidePolyline, secondSidePolyline);
            // 加入箭頭
            for(Polyline polyline : arrowHeadPolylineList){
                pane.getChildren().add(polyline);
            }

            // 加入箭身
            for(Polyline polyline : arrowBodyPolylineList){
                pane.getChildren().add(polyline);
            }
            // 繪製兩側界線
            // 當前點
            double currentPlylinePointX = event.getX();
            double currentPlylinePointY = event.getY();
            // 終點
            double endPolylinePointX = startPolylinePoint2D.getX();
            double endPolylinePointY = startPolylinePoint2D.getY();
            // 當前點和上一個點的橫縱座標差
            double polylinePointDeleteX = currentPlylinePointX - endPolylinePointX;
            double polylinePointDeleteY = currentPlylinePointY - endPolylinePointY;
            // 折線垂直線斜率
            double polylineVerticalArc = Math.PI / 2 - (Math.atan(Math.abs(polylinePointDeleteY / polylinePointDeleteX)));
            // 內外折線和居中折線的橫向和縱向距離
            double polylineInternalX = polylineDistance * Math.cos(polylineVerticalArc);
            double polylineInternalY = polylineDistance * Math.sin(polylineVerticalArc);
            // 折線加點
            firstSidePolyline.getPoints().addAll(new Double[]{endPolylinePointX + ((endPolylinePointY >= currentPlylinePointY) ? -1 : 1) * polylineInternalX, endPolylinePointY + ((endPolylinePointX <= currentPlylinePointX) ? -1 : 1) * polylineInternalY});
            secondSidePolyline.getPoints().addAll(new Double[]{endPolylinePointX + ((endPolylinePointY >= currentPlylinePointY) ? 1 : -1) * polylineInternalX, endPolylinePointY + ((endPolylinePointX <= currentPlylinePointX) ? 1 : -1) * polylineInternalY});

            Polygon polygonTemp = new Polygon();
            polygonTemp.getPoints().addAll(polygon.getPoints());
            List<Label> labelList = new ArrayList<>();
            labelList.add(label1);
            labelList.add(label2);
            labelList.add(label3);
            // 被選中的圖形物件
            List<Label> selectedElmentList = new ArrayList<>();
            for(Label label : labelList){
                if (isIntersectWithPolygon(polygonTemp, label)) {
                    selectedElmentList.add(label);
                }
            }

            String alertInfo;
            if(selectedElmentList.size() == 0){
                alertInfo = "你未圈選任何測試標籤!";
            }else{
                alertInfo = "你圈選了";
            }

            for(int i = 0;i < selectedElmentList.size();i++){
                Label label = selectedElmentList.get(i);
                alertInfo += label.getText() + (i < selectedElmentList.size() - 1 ? "、" : "。");
            }

            Alert alert = new Alert(Alert.AlertType.INFORMATION, alertInfo, ButtonType.FINISH);
            alert.show();
        }
    });

    primaryStage.setScene(new Scene(pane, 1000, 500));
    primaryStage.show();
}

private boolean isIntersectWithPolygon(Polygon polygon, Label label) {
    try {
        if (polygon != null && label != null) {
            Node displayNode = label;
            List<Shape> shapeList = getShapeListFromNode(displayNode);

            for (Shape shape : shapeList) {
                // 圈選的圖形特別複雜時,會產生效率問題
                Shape intersectShape = Shape.intersect(polygon, shape);
                if (intersectShape.getBoundsInLocal().getWidth() != -1) {
                    return true;
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return false;
}

private static List<Shape> getShapeListFromNode(Node node) {
    List<Shape> shapeList = new ArrayList<Shape>();
    if (node instanceof Parent) {
        Parent parent = (Parent) node;
        ObservableList<Node> subNodeList = parent.getChildrenUnmodifiable();
        for (Node subNode : subNodeList) {
            List<Shape> subNodeShapeList = getShapeListFromNode(subNode);
            shapeList.addAll(subNodeShapeList);
        }
    } else if (node instanceof Shape) {
        shapeList.add((Shape) node);
    }
    return shapeList;
}

}