张世豪
4 天以前 32c98d4855b6178554c787103dc956d161e152b3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package lujing;
 
import java.util.ArrayList;
import java.util.List;
 
public class Qufenxingzhuang {
 
    // 容差阈值:sin(角度)。
    // 0.05 大约对应 2.8度。意味着小于3度的微小凹陷或凸起会被忽略,视为直线。
    // 如果您觉得判断依然太严,可以将此值调大到 0.1 (约5.7度)。
    private static final double ANGLE_TOLERANCE = 0.1;
 
    // 判断草地类型的主方法
    public int judgeGrassType(String coordinates) {
        try {
            // 1. 解析坐标字符串
            List<Point> points = parseCoordinates(coordinates);
 
            // 2. 数据预处理:删除最后一个闭合点
            // 原因:输入数据通常首尾相同,删除最后一个点避免重复计算
            if (!points.isEmpty()) {
                points.remove(points.size() - 1);
            }
 
            // 3. 检查点数(移除后至少需要3个点)
            if (points.size() < 3) {
                return 0; // 无法构成多边形
            }
 
            // 4. 判断形状
            if (isConvexPolygon(points)) {
                return 1; // 凸形草地
            } else {
                return 2; // 异形草地(明显的凹多边形)
            }
 
        } catch (Exception e) {
            // 解析失败或计算异常
            return 0;
        }
    }
 
    // 解析坐标字符串
    private List<Point> parseCoordinates(String coordinates) {
        List<Point> points = new ArrayList<>();
        String cleanStr = coordinates.replaceAll("[()\\[\\]{}]", "").trim();
        String[] pointStrings = cleanStr.split(";");
 
        for (String pointStr : pointStrings) {
            pointStr = pointStr.trim();
            if (pointStr.isEmpty()) continue;
            
            String[] xy = pointStr.split(",");
            if (xy.length != 2) throw new IllegalArgumentException("格式错误");
            
            points.add(new Point(Double.parseDouble(xy[0].trim()), Double.parseDouble(xy[1].trim())));
        }
        return points;
    }
 
    // 【核心修改】判断多边形是否为凸多边形(带容差)
    private boolean isConvexPolygon(List<Point> points) {
        // 先清理极度接近的点,避免除以零错误
        List<Point> cleanedPoints = cleanDuplicatePoints(points);
        int n = cleanedPoints.size();
        if (n < 3) return false;
 
        boolean hasPositive = false;
        boolean hasNegative = false;
 
        for (int i = 0; i < n; i++) {
            Point p0 = cleanedPoints.get(i);
            Point p1 = cleanedPoints.get((i + 1) % n);
            Point p2 = cleanedPoints.get((i + 2) % n);
 
            // 计算向量 p0->p1 和 p1->p2
            double dx1 = p1.x - p0.x;
            double dy1 = p1.y - p0.y;
            double dx2 = p2.x - p1.x;
            double dy2 = p2.y - p1.y;
 
            // 计算叉积
            double cross = dx1 * dy2 - dx2 * dy1;
 
            // 计算边长
            double len1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
            double len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
 
            // 防止极短边导致的计算错误
            if (len1 < 1e-6 || len2 < 1e-6) continue;
 
            // 【关键修改】计算归一化的叉积值 (相当于 sinθ)
            // 这代表了弯曲的程度,排除了边长对叉积数值大小的干扰
            double sinTheta = cross / (len1 * len2);
 
            // 应用容差逻辑:
            // 只有当弯曲程度超过阈值时,才记录方向
            if (sinTheta > ANGLE_TOLERANCE) {
                hasPositive = true; // 明显的左转(凸)
            } else if (sinTheta < -ANGLE_TOLERANCE) {
                hasNegative = true; // 明显的右转(凹)
            }
            // 如果 sinTheta 在 -0.05 到 0.05 之间,认为是直线抖动,忽略不计
 
            // 如果既有明显的向左转,又有明显的向右转,那就是异形(凹多边形)
            if (hasPositive && hasNegative) {
                return false;
            }
        }
        
        // 如果没有产生冲突的转向,或者是单纯的直线(所有点共线),视为凸形
        return true;
    }
 
    // 辅助:仅去除重复点,不再过度清理共线点(因为主逻辑已经能处理共线抖动)
    private List<Point> cleanDuplicatePoints(List<Point> points) {
        List<Point> cleaned = new ArrayList<>();
        double tolerance = 1e-6;
 
        for (Point p : points) {
            if (!cleaned.isEmpty()) {
                Point last = cleaned.get(cleaned.size() - 1);
                if (Math.abs(p.x - last.x) < tolerance && Math.abs(p.y - last.y) < tolerance) {
                    continue; // 跳过重复点
                }
            }
            cleaned.add(p);
        }
        
        // 检查首尾是否因为循环导致重复(虽然主方法删除了最后一个点,为了健壮性再查一次)
        if (cleaned.size() > 1) {
            Point first = cleaned.get(0);
            Point last = cleaned.get(cleaned.size() - 1);
            if (Math.abs(first.x - last.x) < tolerance && Math.abs(first.y - last.y) < tolerance) {
                cleaned.remove(cleaned.size() - 1);
            }
        }
        return cleaned;
    }
 
    // 点类
    private static class Point {
        double x, y;
        Point(double x, double y) { this.x = x; this.y = y; }
    }
}