张世豪
90 分钟以前 8d3989dff1164588d45dbb30506da1a6e1094005
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
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<YixinglujingNoObstacle.PathSegment> path = YixinglujingNoObstacle.planPath(coordinates, widthStr, marginStr);
        // Validate that non-mowing segments do not cross outside polygon
        List<YixinglujingNoObstacle.Point> 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<YixinglujingNoObstacle.Point> 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<YixinglujingNoObstacle.Point> parseCoordinates(String coordinates) {
        List<YixinglujingNoObstacle.Point> 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<YixinglujingNoObstacle.Point> 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<YixinglujingNoObstacle.Point> 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<YixinglujingNoObstacle.Point> 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;
    }
}