826220679@qq.com
15 小时以前 f94b1436d7a28c8e28d010b2cb657ab7c064e353
src/lujing/YixinglujingHaveObstacel.java
@@ -102,8 +102,8 @@
            remainingSegments.addAll(clippedSegments);
        }
        
        // 3. 重新连接路径段(弓字形连接)
        return reconnectSegments(remainingSegments);
        // 3. 重新连接路径段(弓字形连接,智能处理边界穿越)
        return reconnectSegments(remainingSegments, polygon);
    }
    
    /**
@@ -154,7 +154,6 @@
            return result;
        } else {
            // 一端在内部,一端在外部
            Point insidePoint = startInside ? segment.start : segment.end;
            Point outsidePoint = startInside ? segment.end : segment.start;
            
            List<Point> intersections = obstacle.getIntersections(segment);
@@ -178,8 +177,9 @@
    
    /**
     * 重新连接路径段,形成连续弓字形路径
     * 优化:智能处理边界穿越,当换行路径穿越边界时,沿边界行走
     */
    private static List<PathSegment> reconnectSegments(List<PathSegment> segments) {
    private static List<PathSegment> reconnectSegments(List<PathSegment> segments, List<Point> boundary) {
        if (segments.isEmpty()) return new ArrayList<>();
        
        List<PathSegment> reconnected = new ArrayList<>();
@@ -189,7 +189,9 @@
            if (seg.isMowing) {
                // 割草段:检查是否需要添加空走段
                if (distance(currentPos, seg.start) > 0.01) {
                    reconnected.add(new PathSegment(currentPos, seg.start, false));
                    // 使用智能连接方法生成换行路径
                    List<PathSegment> connectionPath = buildSmartConnection(currentPos, seg.start, boundary);
                    reconnected.addAll(connectionPath);
                }
                reconnected.add(seg);
                currentPos = seg.end;
@@ -204,6 +206,351 @@
    }
    
    /**
     * 智能连接两点:如果直线不穿越边界则直接连接,否则使用直线+边界混合路径
     * 优化逻辑:
     * 1. 如果AB线不穿越边界C,直接使用AB作为换行路线
     * 2. 如果AB线穿越了边界C,找到所有交点,将AB分成多个段
     *    - 对于在边界内部的段(如DF段、GH段),沿边界行走
     *    - 对于在边界外部的段,沿AB直线行走
     *    路径示例:A → D(直线) → F(沿边界) → G(直线) → H(沿边界) → B(直线)
     *
     * @param pointA 起点(上一段结束的终点)
     * @param pointB 终点(下一段需要割草路径的起始点)
     * @param boundary 安全内缩边界C
     * @return 连接路径段列表(全部为isMowing=false的空走段)
     */
    private static List<PathSegment> buildSmartConnection(Point pointA, Point pointB, List<Point> boundary) {
        List<PathSegment> result = new ArrayList<>();
        // 1. 检查AB直线是否穿越边界C
        if (!segmentIntersectsBoundary(pointA, pointB, boundary)) {
            // 不穿越边界,直接使用AB作为换行路线
            result.add(new PathSegment(pointA, pointB, false));
            return result;
        }
        // 2. AB线穿越了边界C,需要找到所有交点
        List<IntersectionInfo> intersections = getAllBoundaryIntersections(pointA, pointB, boundary);
        if (intersections.isEmpty()) {
            // 没有交点(不应该发生,但安全处理),使用直线
            result.add(new PathSegment(pointA, pointB, false));
            return result;
        }
        // 3. 按距离起点A的距离排序交点
        intersections.sort(Comparator.comparingDouble(inter -> distance(pointA, inter.point)));
        // 4. 构建完整的点序列:A, I1, I2, ..., In, B(I为交点)
        List<Point> pointSequence = new ArrayList<>();
        pointSequence.add(pointA);
        for (IntersectionInfo inter : intersections) {
            pointSequence.add(inter.point);
        }
        pointSequence.add(pointB);
        // 5. 处理每两个相邻点之间的段
        Point currentPos = pointA;
        for (int i = 0; i < pointSequence.size() - 1; i++) {
            Point p1 = pointSequence.get(i);
            Point p2 = pointSequence.get(i + 1);
            // 判断p1到p2的段(AB线段的一部分)是否在边界C内部(检查中点)
            Point midPoint = new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
            boolean segmentInsideBoundary = isPointInPolygon(midPoint, boundary);
            if (segmentInsideBoundary) {
                // 段在边界内部(如DF段、GH段),需要沿边界行走
                if (i == 0) {
                    // 第一个段:从A到第一个交点D
                    // 如果段在边界内部,说明A在边界内部,需要先从A沿边界走到第一个交点
                    IntersectionInfo firstInter = intersections.get(0);
                    SnapResult snapA = snapToBoundary(currentPos, boundary);
                    List<PathSegment> boundaryPath = getBoundaryPathBetweenPoints(
                        snapA.onEdge, snapA.edgeIndex,
                        firstInter.point, firstInter.edgeIndex,
                        boundary);
                    result.addAll(boundaryPath);
                    currentPos = firstInter.point;
                } else if (i == pointSequence.size() - 2) {
                    // 最后一个段:从最后一个交点H到B
                    // 需要沿边界从当前点(应该是最后一个交点H)到B在边界上的投影
                    IntersectionInfo lastInter = intersections.get(intersections.size() - 1);
                    SnapResult snapB = snapToBoundary(pointB, boundary);
                    List<PathSegment> boundaryPath = getBoundaryPathBetweenPoints(
                        currentPos, lastInter.edgeIndex,
                        snapB.onEdge, snapB.edgeIndex,
                        boundary);
                    result.addAll(boundaryPath);
                    // 如果B不在边界上,从边界投影直线到B
                    if (distance(snapB.onEdge, pointB) > 1e-6) {
                        result.add(new PathSegment(snapB.onEdge, pointB, false));
                    }
                    currentPos = pointB;
                } else {
                    // 中间段:两个交点之间的段(都在边界上),沿边界行走
                    IntersectionInfo inter1 = intersections.get(i - 1);
                    IntersectionInfo inter2 = intersections.get(i);
                    List<PathSegment> boundaryPath = getBoundaryPathBetweenPoints(
                        inter1.point, inter1.edgeIndex,
                        inter2.point, inter2.edgeIndex,
                        boundary);
                    result.addAll(boundaryPath);
                    currentPos = inter2.point;
                }
            } else {
                // 段在边界外部,可以直接沿AB直线连接(如A到D,F到G,H到B)
                if (distance(p1, p2) > 1e-6) {
                    // 如果当前点不在p1,先连接到p1
                    if (distance(currentPos, p1) > 1e-6) {
                        result.add(new PathSegment(currentPos, p1, false));
                    }
                    // 从p1直线到p2
                        result.add(new PathSegment(p1, p2, false));
                    currentPos = p2;
                }
            }
        }
        return result;
    }
    /**
     * 检查线段是否穿越边界(与边界边相交,不包括端点)
     */
    private static boolean segmentIntersectsBoundary(Point a, Point b, List<Point> boundary) {
        for (int i = 0; i < boundary.size(); i++) {
            Point c = boundary.get(i);
            Point d = boundary.get((i + 1) % boundary.size());
            // 忽略共享端点的相交
            if (isSamePoint(a, c) || isSamePoint(a, d) || isSamePoint(b, c) || isSamePoint(b, d)) {
                continue;
            }
            if (segmentsIntersect(a, b, c, d)) {
                return true;
            }
        }
        return false;
    }
    /**
     * 获取线段与边界的所有交点信息(包括点和对应边索引)
     */
    private static List<IntersectionInfo> getAllBoundaryIntersections(Point a, Point b, List<Point> boundary) {
        List<IntersectionInfo> intersections = new ArrayList<>();
        for (int i = 0; i < boundary.size(); i++) {
            Point c = boundary.get(i);
            Point d = boundary.get((i + 1) % boundary.size());
            // 忽略共享端点
            if (isSamePoint(a, c) || isSamePoint(a, d) || isSamePoint(b, c) || isSamePoint(b, d)) {
                continue;
            }
            Point intersection = getLineIntersection(a, b, c, d);
            if (intersection != null) {
                intersections.add(new IntersectionInfo(intersection, i));
            }
        }
        return intersections;
    }
    /**
     * 获取边界上两点之间的路径(沿边界行走)
     * @param start 起点(必须在边界上)
     * @param startEdgeIndex 起点所在的边索引
     * @param end 终点(必须在边界上)
     * @param endEdgeIndex 终点所在的边索引
     * @param boundary 边界点列表
     * @return 沿边界的路径段列表
     */
    private static List<PathSegment> getBoundaryPathBetweenPoints(
            Point start, int startEdgeIndex,
            Point end, int endEdgeIndex,
            List<Point> boundary) {
        List<PathSegment> result = new ArrayList<>();
        if (startEdgeIndex == endEdgeIndex) {
            // 在同一条边上,直接连接
            if (distance(start, end) > 1e-6) {
                result.add(new PathSegment(start, end, false));
            }
            return result;
        }
        int n = boundary.size();
        // 计算顺时针路径
        List<Point> pathClockwise = new ArrayList<>();
        pathClockwise.add(start);
        int curr = startEdgeIndex;
        while (curr != endEdgeIndex) {
            pathClockwise.add(boundary.get((curr + 1) % n));
            curr = (curr + 1) % n;
        }
        pathClockwise.add(end);
        // 计算逆时针路径
        List<Point> pathCounterClockwise = new ArrayList<>();
        pathCounterClockwise.add(start);
        curr = startEdgeIndex;
        while (curr != endEdgeIndex) {
            pathCounterClockwise.add(boundary.get(curr));
            curr = (curr - 1 + n) % n;
        }
        pathCounterClockwise.add(end);
        // 选择较短的路径
        List<Point> chosenPath = getPathLength(pathClockwise) < getPathLength(pathCounterClockwise)
            ? pathClockwise : pathCounterClockwise;
        // 转换为路径段
        for (int i = 0; i < chosenPath.size() - 1; i++) {
            if (distance(chosenPath.get(i), chosenPath.get(i + 1)) > 1e-6) {
                result.add(new PathSegment(chosenPath.get(i), chosenPath.get(i + 1), false));
            }
        }
        return result;
    }
    /**
     * 计算路径总长度
     */
    private static double getPathLength(List<Point> path) {
        double len = 0;
        for (int i = 0; i < path.size() - 1; i++) {
            len += distance(path.get(i), path.get(i + 1));
        }
        return len;
    }
    /**
     * 判断两个点是否相同(考虑浮点误差)
     */
    private static boolean isSamePoint(Point a, Point b) {
        return Math.abs(a.x - b.x) < 1e-6 && Math.abs(a.y - b.y) < 1e-6;
    }
    /**
     * 判断两条线段是否相交(不包括端点)
     */
    private static boolean segmentsIntersect(Point a, Point b, Point c, Point d) {
        return ccw(a, c, d) != ccw(b, c, d) && ccw(a, b, c) != ccw(a, b, d);
    }
    /**
     * 判断三点是否逆时针排列
     */
    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);
    }
    /**
     * 交点信息内部类
     */
    private static class IntersectionInfo {
        Point point;        // 交点坐标
        int edgeIndex;      // 交点所在的边界边索引
        IntersectionInfo(Point point, int edgeIndex) {
            this.point = point;
            this.edgeIndex = edgeIndex;
        }
    }
    /**
     * 边界吸附结果内部类
     */
    private static class SnapResult {
        Point onEdge;       // 在边界上的投影点
        int edgeIndex;      // 所在的边界边索引
        SnapResult(Point p, int idx) {
            this.onEdge = p;
            this.edgeIndex = idx;
        }
    }
    /**
     * 计算点到边界最近的投影点以及所在边索引
     * @param p 要吸附的点
     * @param poly 边界多边形
     * @return 吸附结果
     */
    private static SnapResult snapToBoundary(Point p, List<Point> poly) {
        double minD = Double.MAX_VALUE;
        Point bestProj = p;
        int bestIdx = -1;
        for (int i = 0; i < poly.size(); i++) {
            Point s = poly.get(i);
            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 < 1e-10) {
                double d = Math.hypot(p.x - s.x, p.y - s.y);
                if (d < minD) {
                    minD = d;
                    bestProj = s;
                    bestIdx = i;
                }
                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));
            Point proj = new Point(s.x + t * (e.x - s.x), s.y + t * (e.y - s.y));
            double d = Math.hypot(p.x - proj.x, p.y - proj.y);
            if (d < minD) {
                minD = d;
                bestProj = proj;
                bestIdx = i;
            }
        }
        return new SnapResult(bestProj, bestIdx == -1 ? 0 : bestIdx);
    }
    /**
     * 判断点是否在边界上(距离边界很近)
     * @param p 要检查的点
     * @param boundary 边界多边形
     * @return 是否在边界上
     */
    @SuppressWarnings("unused")
    private static boolean isPointOnBoundary(Point p, List<Point> boundary) {
        double threshold = 1e-4; // 阈值,考虑浮点误差
        for (int i = 0; i < boundary.size(); i++) {
            Point s = boundary.get(i);
            Point e = boundary.get((i + 1) % boundary.size());
            double dist = distToSegment(p, s, e);
            if (dist < threshold) {
                return true;
            }
        }
        return false;
    }
    /**
     * 计算点到线段的距离
     * @param p 点
     * @param s 线段起点
     * @param e 线段终点
     * @return 距离
     */
    private static double distToSegment(Point p, Point s, Point e) {
        double l2 = (s.x - e.x) * (s.x - e.x) + (s.y - e.y) * (s.y - e.y);
        if (l2 < 1e-10) {
            return Math.hypot(p.x - s.x, p.y - s.y);
        }
        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));
        return Math.hypot(p.x - (s.x + t * (e.x - s.x)), p.y - (s.y + t * (e.y - s.y)));
    }
    /**
     * 生成原始扫描路径(无障碍物版本)
     */
    private static List<PathSegment> generateGlobalScanPath(