826220679@qq.com
15 小时以前 f94b1436d7a28c8e28d010b2cb657ab7c064e353
src/lujing/YixinglujingNoObstacle.java
@@ -90,10 +90,14 @@
            boolean endInside = isPointInPolygon(s.end, polygon);
            boolean intersects = segmentIntersectsBoundary(s.start, s.end, polygon);
            if (!s.isMowing) {
                // 非作业段统一替换为沿边界路径
                List<Point> path = getBoundaryPathWithSnap(s.start, s.end, polygon);
                for (int i = 0; i < path.size() - 1; i++) {
                    sanitized.add(new PathSegment(path.get(i), path.get(i+1), false));
                // 非作业段:若AB直线安全(不跨越边界且中点在内),保留直线;否则沿边界替换
                if (isSegmentSafe(s.start, s.end, polygon)) {
                    sanitized.add(s);
                } else {
                    List<Point> path = getBoundaryPathWithSnap(s.start, s.end, polygon);
                    for (int i = 0; i < path.size() - 1; i++) {
                        sanitized.add(new PathSegment(path.get(i), path.get(i+1), false));
                    }
                }
            } else {
                if (!startInside || !endInside || intersects) {
@@ -240,9 +244,9 @@
            }
            PathSegment s = lineSegmentsInRow.get(idxInRow);
            // 首次连接或跨区连接均强制沿边界,避免穿越凹陷区
            // 首次或跨区连接:使用智能换行路径(优先直线最短,必要时沿边界)
            if (Math.hypot(currentPos.x - s.start.x, currentPos.y - s.start.y) > 0.01) {
                addBoundaryConnection(segments, currentPos, s.start, polygon);
                addSafeConnection(segments, currentPos, s.start, polygon);
                firstSegmentConnected = true;
            }
            segments.add(s);
@@ -369,14 +373,139 @@
    }
    private static void addSafeConnection(List<PathSegment> segments, Point start, Point end, List<Point> polygon) {
        if (!FORCE_BOUNDARY_TRAVEL && isSegmentSafe(start, end, polygon)) {
            segments.add(new PathSegment(start, end, false));
            return;
        // 使用新的智能换行路径算法
        List<PathSegment> connectionPath = generateSmartConnectionPath(start, end, polygon);
        segments.addAll(connectionPath);
    }
    /**
     * 智能换行路径生成:根据AB线段与安全边界C的交点,生成混合路径
     * 逻辑:
     * 1. 如果AB线段不穿越边界,直接返回AB直线
     * 2. 如果AB线段穿越边界,找到所有交点(如D, F, G, H),生成路径:
     *    A -> D -> (沿边界D到F) -> F -> G -> (沿边界G到H) -> H -> B
     */
    private static List<PathSegment> generateSmartConnectionPath(Point A, Point B, List<Point> polygon) {
        List<PathSegment> result = new ArrayList<>();
        // 1. 检查AB直线是否安全(不穿越边界且中点在内部);与是否强制沿边界无关
        if (isSegmentSafe(A, B, polygon)) {
            result.add(new PathSegment(A, B, false));
            return result;
        }
        List<Point> path = getBoundaryPathWithSnap(start, end, polygon);
        for (int i = 0; i < path.size() - 1; i++) {
            segments.add(new PathSegment(path.get(i), path.get(i+1), false));
        // 2. 获取AB与边界的所有交点
        List<IntersectionPoint> intersections = getSegmentBoundaryIntersections(A, B, polygon);
        // 3. 如果没有交点但不安全,说明整条线都在外部,强制沿边界
        if (intersections.isEmpty()) {
            List<Point> path = getBoundaryPathWithSnap(A, B, polygon);
            for (int i = 0; i < path.size() - 1; i++) {
                result.add(new PathSegment(path.get(i), path.get(i+1), false));
            }
            return result;
        }
        // 4. 根据交点成对处理,考虑A/B是否在内侧
        Point currentPos = A;
        boolean startInside = isPointInPolygon(A, polygon);
        boolean endInside = isPointInPolygon(B, polygon);
        for (int i = 0; i < intersections.size(); i += 2) {
            IntersectionPoint entry = intersections.get(i);
            // 从当前位置到第一个交点:起点在内→直线;起点在外→沿边界到交点
            if (Math.hypot(currentPos.x - entry.point.x, currentPos.y - entry.point.y) > 1e-6) {
                if (startInside) {
                    result.add(new PathSegment(currentPos, entry.point, false));
                } else {
                    List<Point> pathToEntry = getBoundaryPathWithSnap(currentPos, entry.point, polygon);
                    for (int j = 0; j < pathToEntry.size() - 1; j++) {
                        result.add(new PathSegment(pathToEntry.get(j), pathToEntry.get(j+1), false));
                    }
                }
            }
            // 检查是否有配对的离开点
            if (i + 1 < intersections.size()) {
                IntersectionPoint exit = intersections.get(i + 1);
                // 从进入点D到离开点F:沿边界行走
                List<Point> boundaryPath = getBoundaryPathBetweenPoints(
                    entry.point, entry.edgeIndex,
                    exit.point, exit.edgeIndex,
                    polygon
                );
                for (int j = 0; j < boundaryPath.size() - 1; j++) {
                    result.add(new PathSegment(boundaryPath.get(j), boundaryPath.get(j+1), false));
                }
                currentPos = exit.point;
                // 之后的起点视为内侧
                startInside = true;
            } else {
                // 如果没有配对的离开点,说明终点在外部,沿边界到B
                List<Point> path = getBoundaryPathWithSnap(entry.point, B, polygon);
                for (int j = 0; j < path.size() - 1; j++) {
                    result.add(new PathSegment(path.get(j), path.get(j+1), false));
                }
                return result;
            }
        }
        // 5. 从最后一个离开点到终点B:直线段(在内部)
        if (Math.hypot(currentPos.x - B.x, currentPos.y - B.y) > 1e-6) {
            if (endInside) {
                result.add(new PathSegment(currentPos, B, false));
            } else {
                List<Point> pathToB = getBoundaryPathWithSnap(currentPos, B, polygon);
                for (int j = 0; j < pathToB.size() - 1; j++) {
                    result.add(new PathSegment(pathToB.get(j), pathToB.get(j+1), false));
                }
            }
        }
        return result;
    }
    /**
     * 在已知边界边索引的情况下,沿边界获取两点之间的最短路径
     */
    private static List<Point> getBoundaryPathBetweenPoints(
        Point start, int startEdgeIdx,
        Point end, int endEdgeIdx,
        List<Point> polygon) {
        int n = polygon.size();
        // 如果在同一条边上,直接连接
        if (startEdgeIdx == endEdgeIdx) {
            return Arrays.asList(start, end);
        }
        // 正向路径(顺边)
        List<Point> pathFwd = new ArrayList<>();
        pathFwd.add(start);
        int curr = startEdgeIdx;
        while (curr != endEdgeIdx) {
            pathFwd.add(polygon.get((curr + 1) % n));
            curr = (curr + 1) % n;
        }
        pathFwd.add(end);
        // 反向路径(逆边)
        List<Point> pathRev = new ArrayList<>();
        pathRev.add(start);
        curr = startEdgeIdx;
        while (curr != endEdgeIdx) {
            pathRev.add(polygon.get(curr));
            curr = (curr - 1 + n) % n;
        }
        pathRev.add(end);
        // 返回几何长度更短的路径
        return getPathLength(pathFwd) <= getPathLength(pathRev) ? pathFwd : pathRev;
    }
    // 强制沿边界绕行的连接(不做直线安全判断),用来在同一扫描行的多个作业段之间跳转
@@ -470,6 +599,70 @@
        return ccw(a, c, d) != ccw(b, c, d) && ccw(a, b, c) != ccw(a, b, d);
    }
    // 获取线段AB与边界多边形的所有交点,按照距离A点的顺序排序
    private static class IntersectionPoint {
        Point point;           // 交点坐标
        int edgeIndex;        // 交点所在的边界边索引
        double distFromStart; // 距离起点A的距离
        boolean isEntry;      // true表示进入边界,false表示离开边界
        IntersectionPoint(Point p, int idx, double dist, boolean entry) {
            this.point = p;
            this.edgeIndex = idx;
            this.distFromStart = dist;
            this.isEntry = entry;
        }
    }
    // 计算线段AB与多边形边界的所有交点
    private static List<IntersectionPoint> getSegmentBoundaryIntersections(Point A, Point B, List<Point> polygon) {
        List<IntersectionPoint> intersections = new ArrayList<>();
        for (int i = 0; i < polygon.size(); i++) {
            Point C = polygon.get(i);
            Point D = polygon.get((i + 1) % polygon.size());
            Point intersection = getLineSegmentIntersection(A, B, C, D);
            if (intersection != null) {
                if (isSamePoint(intersection, A) || isSamePoint(intersection, B)) continue;
                double dist = Math.hypot(intersection.x - A.x, intersection.y - A.y);
                intersections.add(new IntersectionPoint(intersection, i, dist, false));
            }
        }
        // 距离排序
        Collections.sort(intersections, (i1, i2) -> Double.compare(i1.distFromStart, i2.distFromStart));
        // 去重近似相同交点(顶点双交)
        List<IntersectionPoint> deduped = new ArrayList<>();
        for (IntersectionPoint ip : intersections) {
            boolean dup = false;
            for (IntersectionPoint kept : deduped) {
                if (Math.abs(ip.point.x - kept.point.x) < 1e-6 && Math.abs(ip.point.y - kept.point.y) < 1e-6) { dup = true; break; }
            }
            if (!dup) deduped.add(ip);
        }
        return deduped;
    }
    // 计算两条线段的交点(如果存在)
    private static Point getLineSegmentIntersection(Point p1, Point p2, Point p3, Point p4) {
        double x1 = p1.x, y1 = p1.y;
        double x2 = p2.x, y2 = p2.y;
        double x3 = p3.x, y3 = p3.y;
        double x4 = p4.x, y4 = p4.y;
        double denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
        if (Math.abs(denom) < 1e-10) return null; // 平行或重合
        double t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom;
        double u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom;
        // 检查交点是否在两条线段上(使用略微宽松的范围以处理浮点误差)
        double epsilon = 1e-6;
        if (t >= -epsilon && t <= 1 + epsilon && u >= -epsilon && u <= 1 + epsilon) {
            return new Point(x1 + t * (x2 - x1), y1 + t * (y2 - y1));
        }
        return null;
    }
    private static boolean ccw(Point a, Point b, Point c) {
        return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x);
    }