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