package lujing; import java.util.*; public class DebugPath { public static void main(String[] args) { String coordinates = "50.39,50.57;74.32,50.40;78.69,24.37;65.76,20.70;65.76,36.07;52.31,33.80;50.04,19.30;41.30,22.10"; String widthStr = "1"; String marginStr = "0.53"; List path = YixinglujingNoObstacle.planPath(coordinates, widthStr, marginStr); // Validate that non-mowing segments do not cross outside polygon List rawPoints = parseCoordinates("50.39,50.57;74.32,50.40;78.69,24.37;65.76,20.70;65.76,36.07;52.31,33.80;50.04,19.30;41.30,22.10"); YixinglujingNoObstacle.ensureCounterClockwise(rawPoints); List polygonInset = YixinglujingNoObstacle.getInsetPolygon(rawPoints, Double.parseDouble(marginStr)); int total = path.size(); int mowing = 0, travel = 0, unsafe = 0; for (YixinglujingNoObstacle.PathSegment s : path) { if (s.isMowing) { mowing++; continue; } travel++; boolean safe = isSegmentSafe(s.start, s.end, polygonInset); if (!safe) { // 允许“沿边界”的旅行段:两端都贴近边界即可视为安全 double d1 = minDistanceToBoundary(s.start, polygonInset); double d2 = minDistanceToBoundary(s.end, polygonInset); if (d1 <= 0.2 && d2 <= 0.2) { // treat as safe boundary-following segment continue; } unsafe++; } } System.out.println("Segments total=" + total + ", mowing=" + mowing + ", travel=" + travel + ", unsafeTravel=" + unsafe); if (unsafe == 0) { System.out.println("Validation OK: no travel segment crosses the inset boundary."); } else { System.out.println("Validation FAILED: some travel segments cross boundary: " + unsafe); } } // --- Helpers copied for validation --- private static List parseCoordinates(String coordinates) { List points = new ArrayList<>(); String[] pairs = coordinates.split(";"); for (String pair : pairs) { String[] xy = pair.split(","); if (xy.length == 2) points.add(new YixinglujingNoObstacle.Point(Double.parseDouble(xy[0]), Double.parseDouble(xy[1]))); } return points; } private static boolean isSegmentSafe(YixinglujingNoObstacle.Point p1, YixinglujingNoObstacle.Point p2, List polygon) { YixinglujingNoObstacle.Point mid = new YixinglujingNoObstacle.Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2); if (!isPointInPolygon(mid, polygon)) return false; for (int i = 0; i < polygon.size(); i++) { YixinglujingNoObstacle.Point a = polygon.get(i); YixinglujingNoObstacle.Point b = polygon.get((i + 1) % polygon.size()); if (segmentsIntersect(p1, p2, a, b)) return false; } return true; } private static boolean segmentsIntersect(YixinglujingNoObstacle.Point a, YixinglujingNoObstacle.Point b, YixinglujingNoObstacle.Point c, YixinglujingNoObstacle.Point d) { return ccw(a, c, d) != ccw(b, c, d) && ccw(a, b, c) != ccw(a, b, d); } private static boolean ccw(YixinglujingNoObstacle.Point a, YixinglujingNoObstacle.Point b, YixinglujingNoObstacle.Point c) { return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x); } private static boolean isPointInPolygon(YixinglujingNoObstacle.Point p, List polygon) { boolean result = false; for (int i = 0, j = polygon.size() - 1; i < polygon.size(); j = i++) { if ((polygon.get(i).y > p.y) != (polygon.get(j).y > p.y) && (p.x < (polygon.get(j).x - polygon.get(i).x) * (p.y - polygon.get(i).y) / (polygon.get(j).y - polygon.get(i).y) + polygon.get(i).x)) { result = !result; } } return result; } private static double minDistanceToBoundary(YixinglujingNoObstacle.Point p, List poly) { double minD = Double.MAX_VALUE; for (int i = 0; i < poly.size(); i++) { YixinglujingNoObstacle.Point s = poly.get(i); YixinglujingNoObstacle.Point e = poly.get((i + 1) % poly.size()); double l2 = (s.x - e.x)*(s.x - e.x) + (s.y - e.y)*(s.y - e.y); if (l2 == 0) { minD = Math.min(minD, Math.hypot(p.x - s.x, p.y - s.y)); continue; } double t = ((p.x - s.x) * (e.x - s.x) + (p.y - s.y) * (e.y - s.y)) / l2; t = Math.max(0, Math.min(1, t)); double px = s.x + t * (e.x - s.x); double py = s.y + t * (e.y - s.y); double d = Math.hypot(p.x - px, p.y - py); minD = Math.min(minD, d); } return minD; } }