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