张世豪
22 小时以前 1175f5fbe8fd832943880bfc37c0e2a451a0688a
删除了几个类优化了路径生成的逻辑
已修改9个文件
已删除3个文件
1979 ■■■■■ 文件已修改
Obstacledge.properties 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
dikuai.properties 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
set.properties 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
shoudongbianjie.properties 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dikuai/Dikuaiguanli.java 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dikuai/daohangyulan.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/gecaoji/lujingdraw.java 197 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lujing/Lunjingguihua.java 417 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lujing/MowingPathGenerationPage.java 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lujing/ObstaclePathPlanner.java 546 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lujing/luoxuan.java 408 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/zhangaiwu/AddDikuai.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Obstacledge.properties
@@ -1,5 +1,5 @@
# å‰²è‰æœºåœ°å—障碍物配置文件
# ç”Ÿæˆæ—¶é—´ï¼š2025-12-25T16:37:39.564155300
# ç”Ÿæˆæ—¶é—´ï¼š2025-12-25T19:02:14.286913600
# åæ ‡ç³»ï¼šWGS84(度分格式)
# ============ åœ°å—基准站配置 ============
dikuai.properties
@@ -1,8 +1,8 @@
#Dikuai Properties
#Thu Dec 25 16:37:39 CST 2025
#Thu Dec 25 19:02:14 CST 2025
LAND1.angleThreshold=-1
LAND1.baseStationCoordinates=3949.89151752,N,11616.79267501,E
LAND1.boundaryCoordinates=-58.32,-476.37;85.99,-289.22;354.31,-329.80;293.43,-559.79
LAND1.boundaryCoordinates=121.86,-611.32;130.67,-577.12;173.17,-587.48;167.47,-621.17
LAND1.boundaryOriginalCoordinates=39.831522,116.279873,49.25;39.831524,116.279878,49.25;39.831525,116.279878,49.24;39.831524,116.279912,49.30;39.831524,116.279911,49.29;39.831523,116.279911,49.23;39.831521,116.279915,49.31;39.831517,116.279925,49.34;39.831514,116.279940,49.30;39.831514,116.279957,49.28;39.831516,116.279974,49.28;39.831518,116.279991,49.29;39.831521,116.280008,49.24;39.831524,116.280025,49.30;39.831526,116.280042,49.24;39.831529,116.280059,49.29;39.831529,116.280076,49.26;39.831530,116.280093,49.32;39.831531,116.280110,49.28;39.831533,116.280127,49.28;39.831535,116.280144,49.26;39.831539,116.280161,49.27;39.831544,116.280175,49.25;39.831551,116.280190,49.24;39.831558,116.280204,49.26;39.831566,116.280219,49.26;39.831574,116.280234,49.22;39.831583,116.280248,49.24;39.831591,116.280260,49.24;39.831600,116.280272,49.23;39.831608,116.280285,49.18;39.831615,116.280298,49.12;39.831618,116.280312,49.11;39.831618,116.280328,49.12;39.831615,116.280342,49.15;39.831610,116.280356,49.21;39.831602,116.280369,49.23;39.831592,116.280379,49.25;39.831581,116.280388,49.25;39.831569,116.280394,49.19;39.831559,116.280395,49.23;39.831552,116.280387,49.28;39.831547,116.280373,49.32;39.831544,116.280357,49.33;39.831541,116.280340,49.29;39.831539,116.280324,49.27;39.831536,116.280307,49.24;39.831534,116.280290,49.25;39.831531,116.280273,49.26;39.831527,116.280257,49.28;39.831522,116.280242,49.21;39.831514,116.280232,49.28;39.831504,116.280229,49.24;39.831491,116.280230,49.33;39.831478,116.280233,49.34;39.831466,116.280236,49.31;39.831454,116.280239,49.31;39.831441,116.280242,49.26;39.831429,116.280244,49.23;39.831416,116.280247,49.25;39.831402,116.280250,49.22;39.831389,116.280253,49.25;39.831376,116.280256,49.26;39.831364,116.280258,49.24;39.831351,116.280261,49.25;39.831338,116.280265,49.26;39.831324,116.280268,49.20;39.831311,116.280271,49.16;39.831298,116.280274,49.17;39.831285,116.280277,49.22;39.831271,116.280278,49.16;39.831261,116.280273,49.23
LAND1.boundaryOriginalXY=-1
LAND1.boundaryPointInterval=-1
@@ -16,11 +16,11 @@
LAND1.mowingPattern=平行线
LAND1.mowingSafetyDistance=0.53
LAND1.mowingTrack=-1
LAND1.mowingWidth=500
LAND1.mowingWidth=50
LAND1.obstacleCoordinates=-1
LAND1.plannedPath=353.646421,-330.235669;86.219211,-289.790692;-57.399120,-476.043693;293.049800,-559.155132;353.646421,-330.235669;352.359360,-335.097876;82.727142,-294.319420;79.235073,-298.848147;351.072299,-339.960083;349.785238,-344.822290;75.743005,-303.376874;72.250936,-307.905602;348.498177,-349.684497;347.211116,-354.546703;68.758867,-312.434329;65.266799,-316.963057;345.924055,-359.408910;344.636994,-364.271117;61.774730,-321.491784;58.282661,-326.020511;343.349933,-369.133324;342.062872,-373.995531;54.790593,-330.549239;51.298524,-335.077966;340.775811,-378.857738;339.488750,-383.719945;47.806455,-339.606694;44.314387,-344.135421;338.201689,-388.582152;336.914628,-393.444358;40.822318,-348.664148;37.330249,-353.192876;335.627567,-398.306565;334.340506,-403.168772;33.838181,-357.721603;30.346112,-362.250331;333.053445,-408.030979;331.766384,-412.893186;26.854043,-366.779058;23.361975,-371.307785;330.479324,-417.755393;329.192263,-422.617600;19.869906,-375.836513;16.377837,-380.365240;327.905202,-427.479807;326.618141,-432.342013;12.885769,-384.893968;9.393700,-389.422695;325.331080,-437.204220;324.044019,-442.066427;5.901631,-393.951422;2.409563,-398.480150;322.756958,-446.928634;321.469897,-451.790841;-1.082506,-403.008877;-4.574575,-407.537605;320.182836,-456.653048;318.895775,-461.515255;-8.066643,-412.066332;-11.558712,-416.595059;317.608714,-466.377461;316.321653,-471.239668;-15.050781,-421.123787;-18.542849,-425.652514;315.034592,-476.101875;313.747531,-480.964082;-22.034918,-430.181242;-25.526987,-434.709969;312.460470,-485.826289;311.173409,-490.688496;-29.019055,-439.238696;-32.511124,-443.767424;309.886348,-495.550703;308.599287,-500.412910;-36.003193,-448.296151;-39.495261,-452.824879;307.312226,-505.275116;306.025165,-510.137323;-42.987330,-457.353606;-46.479399,-461.882333;304.738104,-514.999530;303.451043,-519.861737;-49.971467,-466.411061;-53.463536,-470.939788;302.163982,-524.723944;300.876921,-529.586151;-56.955605,-475.468516;-6.018549,-488.228958;299.589860,-534.448358;298.302799,-539.310565;52.837057,-502.186981;111.692662,-516.145005;297.015738,-544.172771;295.728677,-549.034978;170.548268,-530.103028;229.403874,-544.061051;294.441616,-553.897185
LAND1.plannedPath=122.510775,-610.918324;167.039920,-620.534901;172.565118,-587.878071;131.052742,-577.758818;122.510775,-610.918324;122.635602,-610.433754;167.123414,-620.041405;167.206909,-619.547910;122.760428,-609.949185;122.885254,-609.464616;167.290403,-619.054415;167.373897,-618.560919;123.010080,-608.980047;123.134906,-608.495477;167.457392,-618.067424;167.540886,-617.573928;123.259733,-608.010908;123.384559,-607.526339;167.624380,-617.080433;167.707875,-616.586937;123.509385,-607.041769;123.634211,-606.557200;167.791369,-616.093442;167.874863,-615.599947;123.759037,-606.072631;123.883864,-605.588061;167.958358,-615.106451;168.041852,-614.612956;124.008690,-605.103492;124.133516,-604.618923;168.125346,-614.119460;168.208841,-613.625965;124.258342,-604.134353;124.383168,-603.649784;168.292335,-613.132470;168.375829,-612.638974;124.507994,-603.165215;124.632821,-602.680645;168.459324,-612.145479;168.542818,-611.651983;124.757647,-602.196076;124.882473,-601.711507;168.626312,-611.158488;168.709807,-610.664993;125.007299,-601.226937;125.132125,-600.742368;168.793301,-610.171497;168.876795,-609.678002;125.256952,-600.257799;125.381778,-599.773229;168.960290,-609.184506;169.043784,-608.691011;125.506604,-599.288660;125.631430,-598.804091;169.127278,-608.197516;169.210773,-607.704020;125.756256,-598.319521;125.881083,-597.834952;169.294267,-607.210525;169.377761,-606.717029;126.005909,-597.350383;126.130735,-596.865813;169.461256,-606.223534;169.544750,-605.730038;126.255561,-596.381244;126.380387,-595.896675;169.628244,-605.236543;169.711739,-604.743048;126.505214,-595.412106;126.630040,-594.927536;169.795233,-604.249552;169.878727,-603.756057;126.754866,-594.442967;126.879692,-593.958398;169.962221,-603.262561;170.045716,-602.769066;127.004518,-593.473828;127.129344,-592.989259;170.129210,-602.275571;170.212704,-601.782075;127.254171,-592.504690;127.378997,-592.020120;170.296199,-601.288580;170.379693,-600.795084;127.503823,-591.535551;127.628649,-591.050982;170.463187,-600.301589;170.546682,-599.808094;127.753475,-590.566412;127.878302,-590.081843;170.630176,-599.314598;170.713670,-598.821103;128.003128,-589.597274;128.127954,-589.112704;170.797165,-598.327607;170.880659,-597.834112;128.252780,-588.628135;128.377606,-588.143566;170.964153,-597.340617;171.047648,-596.847121;128.502433,-587.658996;128.627259,-587.174427;171.131142,-596.353626;171.214636,-595.860130;128.752085,-586.689858;128.876911,-586.205288;171.298131,-595.366635;171.381625,-594.873139;129.001737,-585.720719;129.126564,-585.236150;171.465119,-594.379644;171.548614,-593.886149;129.251390,-584.751580;129.376216,-584.267011;171.632108,-593.392653;171.715602,-592.899158;129.501042,-583.782442;129.625868,-583.297872;171.799097,-592.405662;171.882591,-591.912167;129.750694,-582.813303;129.875521,-582.328734;171.966085,-591.418672;172.049580,-590.925176;130.000347,-581.844165;130.125173,-581.359595;172.133074,-590.431681;172.216568,-589.938185;130.249999,-580.875026;130.374825,-580.390457;172.300063,-589.444690;172.383557,-588.951195;130.499652,-579.905887;130.624478,-579.421318;172.467051,-588.457699;172.550546,-587.964204;130.749304,-578.936749;130.874130,-578.452179;157.378188,-584.176033
LAND1.returnPathCoordinates=-1
LAND1.returnPathRawCoordinates=-1
LAND1.returnPointCoordinates=-1
LAND1.updateTime=2025-12-25 16\:37\:39
LAND1.updateTime=2025-12-25 19\:02\:14
LAND1.userId=-1
set.properties
@@ -1,5 +1,5 @@
#Mower Configuration Properties - Updated
#Thu Dec 25 16:37:55 CST 2025
#Thu Dec 25 19:34:03 CST 2025
appVersion=-1
boundaryLengthVisible=false
currentWorkLandNumber=LAND1
@@ -8,12 +8,12 @@
handheldMarkerId=1872
idleTrailDurationSeconds=60
manualBoundaryDrawingMode=false
mapScale=11.95
mapScale=5.20
measurementModeEnabled=false
mowerId=6288
serialAutoConnect=true
serialBaudRate=115200
serialPortName=COM15
simCardNumber=-1
viewCenterX=-148.00
viewCenterY=424.51
viewCenterX=-141.32
viewCenterY=608.58
shoudongbianjie.properties
@@ -1,11 +1,11 @@
#\u624B\u52A8\u7ED8\u5236\u8FB9\u754C\u5750\u6807 - \u683C\u5F0F: x1,y1;x2,y2;...;xn,yn (\u5355\u4F4D:\u7C73,\u7CBE\u786E\u5230\u5C0F\u6570\u70B9\u540E2\u4F4D)
#Tue Dec 23 17:49:02 CST 2025
boundaryCoordinates=-99.64,193.56;185.77,182.30;61.84,424.89;237.59,415.88;235.34,539.80;-26.03,544.31
#Thu Dec 25 19:00:22 CST 2025
boundaryCoordinates=121.86,-611.32;130.67,-577.12;173.17,-587.48;167.47,-621.17
email=789
language=zh
lastLoginTime=-1
password=123
pointCount=6
pointCount=4
registrationTime=-1
status=-1
userId=-1
src/dikuai/Dikuaiguanli.java
@@ -1,5 +1,4 @@
package dikuai;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
@@ -21,8 +20,6 @@
import java.util.Objects;
import java.util.Properties;
import java.util.Locale;
import lujing.Lunjingguihua;
import lujing.MowingPathGenerationPage;
import publicway.Fuzhibutton;
import publicway.Lookbutton;
@@ -1277,6 +1274,11 @@
            public boolean savePlannedPath(Dikuai dikuai, String value) {
                return saveFieldAndRefresh(dikuai, "plannedPath", value);
            }
            @Override
            public boolean saveMowingSafetyDistance(Dikuai dikuai, String value) {
                return saveFieldAndRefresh(dikuai, "mowingSafetyDistance", value);
            }
        };
        // æ˜¾ç¤ºè·¯å¾„规划页面
@@ -1319,14 +1321,8 @@
            }
        }
        String modeValue = sanitizeValueOrNull(dikuai.getMowingPattern());
        String initialGenerated = attemptMowingPathPreview(
            boundaryValue,
            obstacleValue,
            widthValue,
            modeValue,
            this,
            false
        );
        // ä¸å†é¢„先生成路径,由路径规划页面处理
        String initialGenerated = null;
        showMowingPathDialog(dikuai, baseStationValue, boundaryValue, obstacleValue, widthValue, modeValue, initialGenerated);
    }
@@ -1366,6 +1362,11 @@
            public boolean savePlannedPath(Dikuai dikuai, String value) {
                return saveFieldAndRefresh(dikuai, "plannedPath", value);
            }
            @Override
            public boolean saveMowingSafetyDistance(Dikuai dikuai, String value) {
                return saveFieldAndRefresh(dikuai, "mowingSafetyDistance", value);
            }
        };
        
        // ä½¿ç”¨æ–°çš„独立页面类
@@ -1384,78 +1385,19 @@
        dialog.setVisible(true);
    }
    private String attemptMowingPathPreview(
        String boundaryInput,
        String obstacleInput,
        String widthCmInput,
        String modeInput,
        Component parentComponent,
        boolean showMessages) {
        String boundary = sanitizeValueOrNull(boundaryInput);
        if (boundary == null) {
            if (showMessages) {
                JOptionPane.showMessageDialog(parentComponent, "当前地块未设置边界坐标,无法生成路径", "提示", JOptionPane.WARNING_MESSAGE);
            }
            return null;
        }
        String rawWidth = widthCmInput != null ? widthCmInput.trim() : "";
        String widthStr = sanitizeWidthString(widthCmInput);
        if (widthStr == null) {
            if (showMessages) {
                String message = rawWidth.isEmpty() ? "请先设置割草宽度(厘米)" : "割草宽度格式不正确";
                JOptionPane.showMessageDialog(parentComponent, message, "提示", JOptionPane.WARNING_MESSAGE);
            }
            return null;
        }
        double widthCm;
        try {
            widthCm = Double.parseDouble(widthStr);
        } catch (NumberFormatException ex) {
            if (showMessages) {
                JOptionPane.showMessageDialog(parentComponent, "割草宽度格式不正确", "提示", JOptionPane.WARNING_MESSAGE);
            }
            return null;
        }
        if (widthCm <= 0) {
            if (showMessages) {
                JOptionPane.showMessageDialog(parentComponent, "割草宽度必须大于0", "提示", JOptionPane.WARNING_MESSAGE);
            }
            return null;
        }
        double widthMeters = widthCm / 100.0d;
        String plannerWidth = BigDecimal.valueOf(widthMeters)
            .setScale(3, RoundingMode.HALF_UP)
            .stripTrailingZeros()
            .toPlainString();
        String obstacles = sanitizeValueOrNull(obstacleInput);
        if (obstacles != null) {
            obstacles = obstacles.replace("\r\n", " ").replace('\r', ' ').replace('\n', ' ');
        }
        String mode = normalizeExistingMowingPattern(modeInput);
        try {
            String generated = Lunjingguihua.generatePathFromStrings(boundary, obstacles, plannerWidth, mode);
            String trimmed = generated != null ? generated.trim() : "";
            if (trimmed.isEmpty()) {
                if (showMessages) {
                    JOptionPane.showMessageDialog(parentComponent, "未生成有效的割草路径,请检查地块数据", "提示", JOptionPane.INFORMATION_MESSAGE);
                }
                return null;
            }
            if (showMessages) {
                JOptionPane.showMessageDialog(parentComponent, "割草路径已生成", "成功", JOptionPane.INFORMATION_MESSAGE);
            }
            return trimmed;
        } catch (IllegalArgumentException ex) {
            if (showMessages) {
                JOptionPane.showMessageDialog(parentComponent, "生成割草路径失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
            }
        } catch (Exception ex) {
            if (showMessages) {
                JOptionPane.showMessageDialog(parentComponent, "生成割草路径时发生异常: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
            }
        }
        return null;
    }
    /**
     * å°è¯•生成割草路径预览
     * ç”¨äºŽåœ¨å¯¹è¯æ¡†ä¸­å®žæ—¶é¢„览或生成路径,包含参数校验和错误提示逻辑
     *
     * @param boundaryInput åœ°å—边界坐标字符串
     * @param obstacleInput éšœç¢ç‰©åæ ‡å­—符串
     * @param widthCmInput å‰²è‰å®½åº¦ï¼ˆåŽ˜ç±³ï¼‰
     * @param modeInput å‰²è‰æ¨¡å¼ï¼ˆå¹³è¡Œ/螺旋)
     * @param parentComponent ç”¨äºŽæ˜¾ç¤ºæç¤ºæ¡†çš„父组件
     * @param showMessages æ˜¯å¦æ˜¾ç¤ºæˆåŠŸ/失败的提示框
     * @return ç”Ÿæˆçš„路径字符串,如果生成失败或校验未通过则返回 null
     */
    private JTextArea createInfoTextArea(String text, boolean editable, int rows) {
        JTextArea area = new JTextArea(text);
src/dikuai/daohangyulan.java
@@ -9,7 +9,6 @@
import java.util.ArrayList;
import zhuye.Shouye;
import zhuye.MapRenderer;
import gecaoji.Device;
import gecaoji.Gecaoji;
import gecaoji.lujingdraw;
import publicway.buttonset;
@@ -90,73 +89,9 @@
            return;
        }
        // 2. å°è¯•重新生成完整路径段(包含围边和作业路径)
        // è¿™æ ·å¯ä»¥ç¡®ä¿å¯¼èˆªé¢„览时,割草机先沿着内缩边界走一圈,再走割草路径
        List<lujing.Lunjingguihua.PathSegment> segments = null;
        String boundaryCoords = dikuai.getBoundaryCoordinates();
        String mowingWidth = dikuai.getMowingBladeWidth(); // æ³¨æ„ï¼šè¿™é‡Œåº”该用割草宽度,而不是割刀宽度,通常是一样的
        // å¦‚果没有割草宽度,尝试从Device获取
        if (mowingWidth == null || mowingWidth.trim().isEmpty() || "-1".equals(mowingWidth.trim())) {
             Device device = Device.getActiveDevice();
             if (device != null) {
                 mowingWidth = device.getMowingWidth();
             }
        }
        // å¦‚果还是没有,使用默认值
        if (mowingWidth == null || mowingWidth.trim().isEmpty() || "-1".equals(mowingWidth.trim())) {
            mowingWidth = "0.34";
        }
        String safetyDistance = dikuai.getMowingSafetyDistance();
        String obstaclesCoords = dikuai.getObstacleCoordinates();
        String mowingPattern = dikuai.getMowingPattern();
        if (boundaryCoords != null && !boundaryCoords.trim().isEmpty() && !"-1".equals(boundaryCoords.trim())) {
            try {
                // è§£æžå‰²è‰æ¨¡å¼
                String mode = "parallel"; // é»˜è®¤å¹³è¡Œæ¨¡å¼
                if (mowingPattern != null && !mowingPattern.trim().isEmpty()) {
                    String pattern = mowingPattern.trim().toLowerCase();
                    if ("1".equals(pattern) || "spiral".equals(pattern) || "螺旋式".equals(pattern) || "螺旋".equals(pattern)) {
                        mode = "spiral";
                    } else if ("parallel".equals(pattern) || "平行线".equals(pattern) || "平行".equals(pattern)) {
                        mode = "parallel";
                    }
                }
                // è°ƒç”¨è·¯å¾„规划生成完整路径段
                segments = lujing.Lunjingguihua.generatePathSegments(
                    boundaryCoords,
                    obstaclesCoords != null ? obstaclesCoords : "",
                    mowingWidth,
                    safetyDistance,
                    mode
                );
            } catch (Exception e) {
                // å¦‚果重新生成失败,segments ä¸º null
                System.err.println("导航预览重新生成路径失败: " + e.getMessage());
            }
        }
        // 3. æž„建最终导航路径点列表
        pathPoints = new ArrayList<>();
        if (segments != null && !segments.isEmpty()) {
            // å¦‚果成功生成了路径段,使用路径段构建点列表
            // è¿™æ ·åŒ…含了围边路径和作业路径,以及它们之间的连接
            lujing.Lunjingguihua.PathSegment firstSeg = segments.get(0);
            pathPoints.add(new Point2D.Double(firstSeg.start.x, firstSeg.start.y));
            for (lujing.Lunjingguihua.PathSegment seg : segments) {
                // æ·»åŠ ç»ˆç‚¹ï¼ˆèµ·ç‚¹å·²ç»åœ¨ä¸Šä¸€æ¬¡å¾ªçŽ¯æˆ–åˆå§‹åŒ–æ—¶æ·»åŠ äº†ï¼‰
                // æ³¨æ„ï¼šè¿™é‡Œå‡è®¾è·¯å¾„段是连续的,或者我们只关心端点
                // å¦‚果段之间不连续(有空走),generatePathSegments åº”该已经生成了连接段(isMowing=false)
                pathPoints.add(new Point2D.Double(seg.end.x, seg.end.y));
            }
        } else {
            // å¦‚果生成失败,回退到使用原始解析的路径点
            // è¿™é€šå¸¸åªåŒ…含作业路径,可能没有围边
            pathPoints = rawPathPoints;
        }
        // ç›´æŽ¥ä½¿ç”¨è§£æžå‡ºæ¥çš„路径点
        pathPoints = rawPathPoints;
        
        if (pathPoints == null || pathPoints.size() < 2) {
             JOptionPane.showMessageDialog(null, "无法构建有效的导航路径", "错误", JOptionPane.ERROR_MESSAGE);
src/gecaoji/lujingdraw.java
@@ -1,5 +1,4 @@
package gecaoji; // åŒ…声明
import java.awt.BasicStroke; // å¼•入基础描边类
import java.awt.Color; // å¼•入颜色类
import java.awt.Graphics2D; // å¼•å…¥2D图形上下文
@@ -8,7 +7,6 @@
import java.awt.geom.Point2D; // å¼•入二维点类
import java.util.ArrayList; // å¼•入动态数组
import java.util.List; // å¼•入列表接口
import lujing.Lunjingguihua; // å¼•入路径规划类
import org.locationtech.jts.geom.Coordinate; // å¼•入坐标类
import org.locationtech.jts.geom.GeometryFactory; // å¼•入几何工厂类
import org.locationtech.jts.geom.Polygon; // å¼•入多边形类
@@ -24,7 +22,7 @@
    // è“è‰² - ç”¨äºŽéžä½œä¸šç§»åŠ¨è·¯å¾„
    private static final Color TRAVEL_PATH_COLOR = new Color(0, 0, 255);
    // è™šçº¿æ ·å¼ - ç”¨äºŽéžä½œä¸šç§»åŠ¨è·¯å¾„
    private static final float[] DASH_PATTERN = {10.0f, 5.0f};
    private static final float[] DASH_PATTERN = {5.0f, 5.0f};
    private static final Color START_POINT_COLOR = new Color(0, 0, 0, 220); // èµ·ç‚¹ç®­å¤´é¢œè‰²
    private static final Color END_POINT_COLOR = new Color(0, 0, 0, 220); // ç»ˆç‚¹ç®­å¤´é¢œè‰²
@@ -107,62 +105,29 @@
            drawInnerBoundary(g2d, boundaryCoords, safetyDistance, scale);
        }
        
        // 2. å°è¯•重新生成路径段以区分作业路径和移动路径
        List<Lunjingguihua.PathSegment> segments = null;
        if (boundaryCoords != null && mowingWidth != null) {
            try {
                // è§£æžå‰²è‰æ¨¡å¼
                String mode = "parallel"; // é»˜è®¤å¹³è¡Œæ¨¡å¼
                if (mowingPattern != null && !mowingPattern.trim().isEmpty()) {
                    String pattern = mowingPattern.trim().toLowerCase();
                    if ("1".equals(pattern) || "spiral".equals(pattern) || "螺旋式".equals(pattern) || "螺旋".equals(pattern)) {
                        mode = "spiral";
                    } else if ("parallel".equals(pattern) || "平行线".equals(pattern) || "平行".equals(pattern)) {
                        mode = "parallel";
                    }
                }
                segments = Lunjingguihua.generatePathSegments(
                    boundaryCoords,
                    obstaclesCoords != null ? obstaclesCoords : "",
                    mowingWidth,
                    safetyDistance,
                    mode
                );
            } catch (Exception e) {
                // å¦‚果重新生成失败,使用简单绘制方式
                segments = null;
            }
        }
        // 2. ç›´æŽ¥ç»˜åˆ¶è·¯å¾„(不再重新生成)
        Path2D polyline = new Path2D.Double(); // åˆ›å»ºæŠ˜çº¿
        boolean move = true; // é¦–段标记
        for (Point2D.Double point : path) { // éåŽ†ç‚¹é›†
            if (move) { // ç¬¬ä¸€æ®µ
                polyline.moveTo(point.x, point.y); // ç§»åŠ¨åˆ°é¦–ç‚¹
                move = false; // æ›´æ–°æ ‡è®°
            } else { // åŽç»­æ®µ
                polyline.lineTo(point.x, point.y); // è¿žçº¿åˆ°ä¸‹ä¸€ç‚¹
            } // if结束
        } // for结束
        
        // 3. æ ¹æ®æ˜¯å¦æœ‰æ®µä¿¡æ¯é€‰æ‹©ä¸åŒçš„绘制方式
        if (segments != null && !segments.isEmpty()) {
            // æœ‰æ®µä¿¡æ¯ï¼šåˆ†åˆ«ç»˜åˆ¶ä½œä¸šè·¯å¾„和移动路径
            drawPathSegments(g2d, segments, scale, arrowScale);
        } else {
            // æ— æ®µä¿¡æ¯ï¼šä½¿ç”¨ç®€å•绘制方式(所有路径使用作业路径颜色)
            Path2D polyline = new Path2D.Double(); // åˆ›å»ºæŠ˜çº¿
            boolean move = true; // é¦–段标记
            for (Point2D.Double point : path) { // éåŽ†ç‚¹é›†
                if (move) { // ç¬¬ä¸€æ®µ
                    polyline.moveTo(point.x, point.y); // ç§»åŠ¨åˆ°é¦–ç‚¹
                    move = false; // æ›´æ–°æ ‡è®°
                } else { // åŽç»­æ®µ
                    polyline.lineTo(point.x, point.y); // è¿žçº¿åˆ°ä¸‹ä¸€ç‚¹
                } // if结束
            } // for结束
            g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); // è®¾ç½®åœ†å¤´åœ†è§’描边
            g2d.setColor(MOWING_PATH_COLOR); // è®¾ç½®ä½œä¸šè·¯å¾„颜色(提香红70%透明度)
            g2d.draw(polyline); // ç»˜åˆ¶æŠ˜çº¿
            Point2D.Double start = path.get(0); // èµ·ç‚¹
            Point2D.Double second = path.get(1); // ç¬¬äºŒä¸ªç‚¹
            Point2D.Double end = path.get(path.size() - 1); // ç»ˆç‚¹
            Point2D.Double prev = path.get(path.size() - 2); // å€’数第二个点
        g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); // è®¾ç½®åœ†å¤´åœ†è§’描边
        g2d.setColor(MOWING_PATH_COLOR); // è®¾ç½®ä½œä¸šè·¯å¾„颜色(提香红70%透明度)
        g2d.draw(polyline); // ç»˜åˆ¶æŠ˜çº¿
        Point2D.Double start = path.get(0); // èµ·ç‚¹
        Point2D.Double second = path.get(1); // ç¬¬äºŒä¸ªç‚¹
        Point2D.Double end = path.get(path.size() - 1); // ç»ˆç‚¹
        Point2D.Double prev = path.get(path.size() - 2); // å€’数第二个点
            drawArrowMarker(g2d, start, second, START_POINT_COLOR, scale, arrowScale); // ç»˜åˆ¶èµ·ç‚¹ç®­å¤´
            drawArrowMarker(g2d, prev, end, END_POINT_COLOR, scale, arrowScale); // ç»˜åˆ¶ç»ˆç‚¹ç®­å¤´
        }
        drawArrowMarker(g2d, start, second, START_POINT_COLOR, scale, arrowScale); // ç»˜åˆ¶èµ·ç‚¹ç®­å¤´
        drawArrowMarker(g2d, prev, end, END_POINT_COLOR, scale, arrowScale); // ç»˜åˆ¶ç»ˆç‚¹ç®­å¤´
        g2d.setStroke(previous); // æ¢å¤åŽŸæè¾¹
    } // æ–¹æ³•结束
@@ -172,7 +137,7 @@
     */ // æ–‡æ¡£æ³¨é‡Šç»“束
    private static void drawInnerBoundary(Graphics2D g2d, String boundaryCoords, String safetyDistanceStr, double scale) {
        try {
            List<Coordinate> boundary = Lunjingguihua.parseCoordinates(boundaryCoords);
            List<Coordinate> boundary = parseCoordinates(boundaryCoords);
            if (boundary.size() < 4) {
                return; // è¾¹ç•Œç‚¹ä¸è¶³
            }
@@ -258,89 +223,7 @@
        }
    } // æ–¹æ³•结束
    
    /** // æ–‡æ¡£æ³¨é‡Šå¼€å§‹
     * ç»˜åˆ¶è·¯å¾„段(区分作业路径和移动路径)
     */ // æ–‡æ¡£æ³¨é‡Šç»“束
    private static void drawPathSegments(Graphics2D g2d, List<Lunjingguihua.PathSegment> segments, double scale, double arrowScale) {
        if (segments == null || segments.isEmpty()) {
            return;
        }
        float strokeWidth = (float) (2.5 / Math.max(0.5, scale));
        Stroke previous = g2d.getStroke();
        // åˆ†åˆ«ç»˜åˆ¶ä½œä¸šè·¯å¾„和移动路径
        Path2D.Double mowingPath = new Path2D.Double();
        Path2D.Double travelPath = new Path2D.Double();
        boolean mowingStarted = false;
        boolean travelStarted = false;
        Coordinate lastMowingEnd = null;
        Coordinate lastTravelEnd = null;
        for (Lunjingguihua.PathSegment seg : segments) {
            if (seg == null || seg.start == null || seg.end == null) {
                continue;
            }
            if (seg.isMowing) {
                // ä½œä¸šè·¯å¾„ - æé¦™çº¢70%透明度
                if (!mowingStarted || lastMowingEnd == null || !equals2D(lastMowingEnd, seg.start)) {
                    mowingPath.moveTo(seg.start.x, seg.start.y);
                    mowingStarted = true;
                }
                mowingPath.lineTo(seg.end.x, seg.end.y);
                lastMowingEnd = seg.end;
            } else {
                // ç§»åŠ¨è·¯å¾„ - è“è‰²è™šçº¿
                if (!travelStarted || lastTravelEnd == null || !equals2D(lastTravelEnd, seg.start)) {
                    travelPath.moveTo(seg.start.x, seg.start.y);
                    travelStarted = true;
                }
                travelPath.lineTo(seg.end.x, seg.end.y);
                lastTravelEnd = seg.end;
            }
        }
        // ç»˜åˆ¶ä½œä¸šè·¯å¾„
        if (mowingStarted) {
            g2d.setColor(MOWING_PATH_COLOR); // æé¦™çº¢70%透明度
            g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
            g2d.draw(mowingPath);
        }
        // ç»˜åˆ¶ç§»åŠ¨è·¯å¾„ï¼ˆè™šçº¿ï¼‰
        if (travelStarted) {
            g2d.setColor(TRAVEL_PATH_COLOR); // è“è‰²
            g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, DASH_PATTERN, 0));
            g2d.draw(travelPath);
        }
        // ç»˜åˆ¶èµ·ç‚¹å’Œç»ˆç‚¹ç®­å¤´
        if (!segments.isEmpty()) {
            Lunjingguihua.PathSegment firstSeg = segments.get(0);
            if (firstSeg != null && firstSeg.start != null && segments.size() > 1) {
                Lunjingguihua.PathSegment secondSeg = segments.get(1);
                if (secondSeg != null && secondSeg.start != null) {
                    Point2D.Double start = new Point2D.Double(firstSeg.start.x, firstSeg.start.y);
                    Point2D.Double second = new Point2D.Double(secondSeg.start.x, secondSeg.start.y);
                    drawArrowMarker(g2d, start, second, START_POINT_COLOR, scale, arrowScale);
                }
            }
            Lunjingguihua.PathSegment lastSeg = segments.get(segments.size() - 1);
            if (lastSeg != null && lastSeg.end != null && segments.size() > 1) {
                Lunjingguihua.PathSegment prevSeg = segments.get(segments.size() - 2);
                if (prevSeg != null && prevSeg.end != null) {
                    Point2D.Double prev = new Point2D.Double(prevSeg.end.x, prevSeg.end.y);
                    Point2D.Double end = new Point2D.Double(lastSeg.end.x, lastSeg.end.y);
                    drawArrowMarker(g2d, prev, end, END_POINT_COLOR, scale, arrowScale);
                }
            }
        }
        g2d.setStroke(previous);
    } // æ–¹æ³•结束
    
    /** // æ–‡æ¡£æ³¨é‡Šå¼€å§‹
     * æ¯”较两个坐标是否相同(容差)
@@ -392,4 +275,36 @@
        g2d.setColor(color); // è®¾ç½®é¢œè‰²
        g2d.fill(arrow); // å¡«å……箭头
    } // æ–¹æ³•结束
    /**
     * è§£æžåæ ‡å­—符串
     */
    private static List<Coordinate> parseCoordinates(String s) {
        List<Coordinate> list = new ArrayList<>();
        if (s == null || s.trim().isEmpty()) return list;
        // å¢žå¼ºæ­£åˆ™ï¼šå¤„理可能存在的多种分隔符
        String[] pts = s.split("[;\\s]+");
        for (String p : pts) {
            String trimmed = p.trim().replace("(", "").replace(")", "");
            if (trimmed.isEmpty()) continue;
            String[] xy = trimmed.split("[,,\\s]+");
            if (xy.length >= 2) {
                try {
                    double x = Double.parseDouble(xy[0].trim());
                    double y = Double.parseDouble(xy[1].trim());
                    // è¿‡æ»¤æ— æ•ˆåæ ‡
                    if (!Double.isNaN(x) && !Double.isNaN(y) && !Double.isInfinite(x) && !Double.isInfinite(y)) {
                        list.add(new Coordinate(x, y));
                    }
                } catch (NumberFormatException ex) {
                    // å¿½ç•¥è§£æžé”™è¯¯çš„点
                }
            }
        }
        // ç¡®ä¿å¤šè¾¹å½¢é—­åˆ
        if (list.size() > 2 && !list.get(0).equals2D(list.get(list.size() - 1))) {
            list.add(new Coordinate(list.get(0)));
        }
        return list;
    }
} // ç±»ç»“束
src/lujing/Lunjingguihua.java
ÎļþÒÑɾ³ý
src/lujing/MowingPathGenerationPage.java
@@ -1,25 +1,12 @@
package lujing;
import javax.swing.*;
import javax.swing.SwingUtilities;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import dikuai.Dikuai;
import lujing.Lunjingguihua;
import lujing.ObstaclePathPlanner;
import lujing.Qufenxingzhuang;
import lujing.AoxinglujingNoObstacle;
import lujing.YixinglujingNoObstacle;
import publicway.Fuzhibutton;
import lujing.AoxinglujingHaveObstacel;
import lujing.YixinglujingHaveObstacel;
import org.locationtech.jts.geom.Coordinate;
import gecaoji.Device;
import java.util.Locale;
@@ -49,6 +36,7 @@
        boolean saveObstacleCoordinates(Dikuai dikuai, String baseStationValue, String obstacleValue);
        boolean saveMowingWidth(Dikuai dikuai, String value);
        boolean savePlannedPath(Dikuai dikuai, String value);
        boolean saveMowingSafetyDistance(Dikuai dikuai, String value);
    }
    
    private final Dikuai dikuai;
@@ -262,30 +250,35 @@
     * é¢„览路径
     */
    private void previewPath() {
        // å…ˆä¿å­˜å½“前路径到地块(临时保存,用于预览)
        String pathNormalized = normalizeCoordinateInput(pathArea.getText());
        // ç›´æŽ¥ä»Žæ–‡æœ¬åŸŸèŽ·å–è·¯å¾„æ•°æ®
        String rawPath = pathArea.getText();
        String pathNormalized = normalizeCoordinateInput(rawPath);
        if (!"-1".equals(pathNormalized)) {
            // è§„范化路径数据:支持换行、空格等分隔符
            pathNormalized = pathNormalized
                .replace("\r\n", ";")
                .replace('\r', ';')
                .replace('\n', ';')
                .replaceAll(";+", ";")
                .replaceAll("\\s*;\\s*", ";")
                .trim();
                .replaceAll("\\s+", ";") // å°†æ‰€æœ‰ç©ºç™½å­—符替换为分号
                .replaceAll(";+", ";");  // åˆå¹¶è¿žç»­åˆ†å·
            // åŽ»é™¤é¦–å°¾åˆ†å·
            if (pathNormalized.startsWith(";")) pathNormalized = pathNormalized.substring(1);
            if (pathNormalized.endsWith(";")) pathNormalized = pathNormalized.substring(0, pathNormalized.length() - 1);
            if (pathNormalized.isEmpty()) {
                pathNormalized = "-1";
            }
        }
        
        if ("-1".equals(pathNormalized)) {
            JOptionPane.showMessageDialog(this, "请先生成割草路径", "提示", JOptionPane.INFORMATION_MESSAGE);
            JOptionPane.showMessageDialog(this, "请先生成割草路径或在文本框中输入有效坐标", "提示", JOptionPane.INFORMATION_MESSAGE);
            return;
        }
        
        // ä¸´æ—¶ä¿å­˜è·¯å¾„到地块对象(不持久化)
        if (saveCallback != null) {
            saveCallback.savePlannedPath(dikuai, pathNormalized);
        }
        // æ³¨æ„ï¼šé¢„览时不自动保存路径到地块,仅使用文本域中的数据进行预览
        // åªæœ‰ç‚¹å‡»"保存路径"按钮时才持久化数据
        
        // ä¿å­˜å½“前页面状态,用于返回时恢复
        String currentBaseStation = baseStationField.getText();
@@ -302,12 +295,16 @@
        String boundaryInput = normalizeCoordinateInput(boundaryArea.getText());
        final String boundary;
        if (!"-1".equals(boundaryInput)) {
            String processed = boundaryInput.replace("\r\n", ";")
            String processed = boundaryInput
                .replace("\r\n", ";")
                .replace('\r', ';')
                .replace('\n', ';')
                .replaceAll(";+", ";")
                .replaceAll("\\s*;\\s*", ";")
                .trim();
                .replaceAll("\\s+", ";")
                .replaceAll(";+", ";");
            if (processed.startsWith(";")) processed = processed.substring(1);
            if (processed.endsWith(";")) processed = processed.substring(0, processed.length() - 1);
            if (processed.isEmpty()) {
                boundary = dikuai.getBoundaryCoordinates();
            } else {
@@ -457,6 +454,12 @@
            dikuai.setMowingWidth(widthNormalized);
            dikuai.setPlannedPath(pathNormalized);
            dikuai.setObstacleCoordinates(obstacleNormalized);
            // èŽ·å–å¹¶æ›´æ–°å®‰å…¨è·ç¦»
            String safetyDistance = getSafetyDistanceString();
            if (safetyDistance != null) {
                dikuai.setMowingSafetyDistance(safetyDistance);
            }
        }
        
        // è°ƒç”¨å›žè°ƒä¿å­˜æ•°æ®
@@ -481,6 +484,15 @@
                JOptionPane.showMessageDialog(this, "无法保存割草路径", "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }
            // ä¿å­˜å®‰å…¨è·ç¦»
            String safetyDistance = getSafetyDistanceString();
            if (safetyDistance != null) {
                if (!saveCallback.saveMowingSafetyDistance(dikuai, safetyDistance)) {
                    JOptionPane.showMessageDialog(this, "无法保存割草安全距离", "错误", JOptionPane.ERROR_MESSAGE);
                    return;
                }
            }
        }
        
        JOptionPane.showMessageDialog(this, "割草路径已保存", "成功", JOptionPane.INFORMATION_MESSAGE);
@@ -543,7 +555,20 @@
        
        String obstacles = sanitizeValueOrNull(obstacleInput);
        if (obstacles != null) {
            obstacles = obstacles.replace("\r\n", " ").replace('\r', ' ').replace('\n', ' ');
            // æŒ‰ç…§ç”¨æˆ·è¦æ±‚,多个障碍物之间用 $ ç¬¦å·åˆ†éš”
            // å¦‚果输入中包含 $,则保留 $,否则将换行符替换为 $
            if (obstacles.contains("$")) {
                // å·²ç»æ˜¯ $ åˆ†éš”的格式,只需清理换行符
                obstacles = obstacles.replace("\r\n", "").replace('\r', ' ').replace('\n', ' ');
            } else {
                // å°è¯•将换行符转换为 $,或者如果是一行则保持原样
                // è¿™é‡Œå‡è®¾ç”¨æˆ·å¯èƒ½ç”¨æ¢è¡Œåˆ†éš”多个障碍物
                // ä½†æ ¹æ®éœ€æ±‚描述,似乎输入本身就应该是 $ åˆ†éš”的,或者我们需要处理成 $ åˆ†éš”
                // ä¸ºäº†å…¼å®¹æ€§ï¼Œå¦‚果用户输入的是换行分隔的多个障碍物,我们将其转换为 $ åˆ†éš”
                // ä½†é€šå¸¸éšœç¢ç‰©åæ ‡æ˜¯ä¸€ä¸²åæ ‡ç‚¹ï¼Œå¦‚果用户没有显式用 $ åˆ†éš”,我们很难区分是同一个障碍物的点还是多个障碍物
                // å› æ­¤ï¼Œè¿™é‡Œä¸»è¦å¤„理清理工作,具体的解析逻辑在各实现类中处理
                obstacles = obstacles.replace("\r\n", " ").replace('\r', ' ').replace('\n', ' ');
            }
        }
        // èŽ·å–å®‰å…¨è·ç¦»
@@ -564,19 +589,10 @@
            int grassType = shapeJudger.judgeGrassType(boundary);
            // grassType: 0=无法判断, 1=凸形, 2=异形
            
            // è§£æžéšœç¢ç‰©åˆ—表
            List<List<Coordinate>> obstacleList = Lunjingguihua.parseObstacles(obstacles);
            if (obstacleList == null) {
                obstacleList = new ArrayList<>();
            }
            // åˆ¤æ–­æ˜¯å¦æœ‰æœ‰æ•ˆçš„障碍物:只有当解析成功且列表不为空时,才认为有障碍物
            boolean hasValidObstacles = !obstacleList.isEmpty();
            String generated = null;
            
            // 2. æ ¹æ®åœ°å—类型和是否有障碍物,调用不同的路径生成类
            if (!hasValidObstacles) {
            if (!hasObstacleInput) {
                // æ— éšœç¢ç‰©çš„æƒ…况
                if (grassType == 1) {
                    // å‡¸å½¢åœ°å—,无障碍物 -> è°ƒç”¨ AoxinglujingNoObstacle
@@ -585,100 +601,43 @@
                    generated = formatAoxingPathSegments(segments);
                } else if (grassType == 2) {
                    // å¼‚形地块,无障碍物 -> è°ƒç”¨ YixinglujingNoObstacle
                    // æ³¨æ„ï¼šå¦‚果该类还没有实现,这里会抛出异常或返回null
                    try {
                        // è°ƒç”¨ YixinglujingNoObstacle.planPath èŽ·å–è·¯å¾„æ®µåˆ—è¡¨
                        List<YixinglujingNoObstacle.PathSegment> segments =
                            YixinglujingNoObstacle.planPath(boundary, plannerWidth, safetyMarginStr);
                        // æ ¼å¼åŒ–路径段列表为字符串
                        generated = formatYixingPathSegments(segments);
                    } catch (Exception e) {
                        // å¦‚果类还没有实现,使用原来的方法作为后备
                        if (showMessages) {
                            System.err.println("YixinglujingNoObstacle å°šæœªå®žçŽ°ï¼Œä½¿ç”¨é»˜è®¤æ–¹æ³•: " + e.getMessage());
                        }
                        generated = Lunjingguihua.generatePathFromStrings(
                            boundary, obstacles != null ? obstacles : "", plannerWidth, safetyMarginStr, mode);
                    }
                    // è°ƒç”¨ YixinglujingNoObstacle.planPath èŽ·å–è·¯å¾„æ®µåˆ—è¡¨
                    List<YixinglujingNoObstacle.PathSegment> segments =
                        YixinglujingNoObstacle.planPath(boundary, plannerWidth, safetyMarginStr);
                    // æ ¼å¼åŒ–路径段列表为字符串
                    generated = formatYixingPathSegments(segments);
                } else {
                    // æ— æ³•判断地块类型,使用原来的方法作为后备
                    // æ— æ³•判断地块类型,默认按凸形处理或提示
                    if (showMessages) {
                        JOptionPane.showMessageDialog(parentComponent, "无法判断地块类型,使用默认路径生成方法",
                        JOptionPane.showMessageDialog(parentComponent, "无法判断地块类型,尝试按凸形地块处理",
                            "提示", JOptionPane.WARNING_MESSAGE);
                    }
                    generated = Lunjingguihua.generatePathFromStrings(
                        boundary, obstacles != null ? obstacles : "", plannerWidth, safetyMarginStr, mode);
                    List<AoxinglujingNoObstacle.PathSegment> segments =
                        AoxinglujingNoObstacle.planPath(boundary, plannerWidth, safetyMarginStr);
                    generated = formatAoxingPathSegments(segments);
                }
            } else {
                // æœ‰éšœç¢ç‰©çš„æƒ…况
                if (grassType == 1) {
                    // å‡¸å½¢åœ°å—,有障碍物 -> è°ƒç”¨ AoxinglujingHaveObstacel
                    try {
                        // å‡è®¾ AoxinglujingHaveObstacel æœ‰ç±»ä¼¼çš„æ–¹æ³•签名
                        List<AoxinglujingHaveObstacel.PathSegment> segments = AoxinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
                        generated = formatAoxingHaveObstaclePathSegments(segments);
                    } catch (Exception e) {
                        // å¦‚果类还没有实现,使用原来的方法作为后备
                        if (showMessages) {
                            System.err.println("AoxinglujingHaveObstacel å°šæœªå®žçŽ°ï¼Œä½¿ç”¨é»˜è®¤æ–¹æ³•: " + e.getMessage());
                        }
                        List<Coordinate> polygon = Lunjingguihua.parseCoordinates(boundary);
                        if (polygon.size() < 4) {
                            if (showMessages) {
                                JOptionPane.showMessageDialog(parentComponent, "多边形坐标数量不足,至少需要三个点",
                                    "错误", JOptionPane.ERROR_MESSAGE);
                            }
                            return null;
                        }
                        double safetyDistance = Double.parseDouble(safetyMarginStr);
                        ObstaclePathPlanner pathPlanner = new ObstaclePathPlanner(
                            polygon, widthMeters, mode, obstacleList, safetyDistance);
                        List<Lunjingguihua.PathSegment> segments = pathPlanner.generate();
                        generated = Lunjingguihua.formatPathSegments(segments);
                    }
                    // ä¼ å…¥å‚数:boundary(A), obstacles(B), plannerWidth(C), safetyMarginStr(D)
                    List<AoxinglujingHaveObstacel.PathSegment> segments =
                        AoxinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
                    generated = formatAoxingHaveObstaclePathSegments(segments);
                } else if (grassType == 2) {
                    // å¼‚形地块,有障碍物 -> è°ƒç”¨ YixinglujingHaveObstacel
                    try {
                        // å‡è®¾ YixinglujingHaveObstacel æœ‰ç±»ä¼¼çš„æ–¹æ³•签名
                        generated = YixinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
                    } catch (Exception e) {
                        // å¦‚果类还没有实现,使用原来的方法作为后备
                        if (showMessages) {
                            System.err.println("YixinglujingHaveObstacel å°šæœªå®žçŽ°ï¼Œä½¿ç”¨é»˜è®¤æ–¹æ³•: " + e.getMessage());
                        }
                        List<Coordinate> polygon = Lunjingguihua.parseCoordinates(boundary);
                        if (polygon.size() < 4) {
                            if (showMessages) {
                                JOptionPane.showMessageDialog(parentComponent, "多边形坐标数量不足,至少需要三个点",
                                    "错误", JOptionPane.ERROR_MESSAGE);
                            }
                            return null;
                        }
                        double safetyDistance = Double.parseDouble(safetyMarginStr);
                        ObstaclePathPlanner pathPlanner = new ObstaclePathPlanner(
                            polygon, widthMeters, mode, obstacleList, safetyDistance);
                        List<Lunjingguihua.PathSegment> segments = pathPlanner.generate();
                        generated = Lunjingguihua.formatPathSegments(segments);
                    }
                    // ä¼ å…¥å‚数:boundary(A), obstacles(B), plannerWidth(C), safetyMarginStr(D)
                    // æ³¨æ„ï¼šYixinglujingHaveObstacel.planPath è¿”回 String
                    generated = YixinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
                } else {
                    // æ— æ³•判断地块类型,使用原来的方法作为后备
                    // æ— æ³•判断地块类型,默认按凸形处理或提示
                    if (showMessages) {
                        JOptionPane.showMessageDialog(parentComponent, "无法判断地块类型,使用默认路径生成方法",
                        JOptionPane.showMessageDialog(parentComponent, "无法判断地块类型,尝试按凸形地块处理",
                            "提示", JOptionPane.WARNING_MESSAGE);
                    }
                    List<Coordinate> polygon = Lunjingguihua.parseCoordinates(boundary);
                    if (polygon.size() < 4) {
                        if (showMessages) {
                            JOptionPane.showMessageDialog(parentComponent, "多边形坐标数量不足,至少需要三个点",
                                "错误", JOptionPane.ERROR_MESSAGE);
                        }
                        return null;
                    }
                    double safetyDistance = Double.parseDouble(safetyMarginStr);
                    ObstaclePathPlanner pathPlanner = new ObstaclePathPlanner(
                        polygon, widthMeters, mode, obstacleList, safetyDistance);
                    List<Lunjingguihua.PathSegment> segments = pathPlanner.generate();
                    generated = Lunjingguihua.formatPathSegments(segments);
                    List<AoxinglujingHaveObstacel.PathSegment> segments =
                        AoxinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
                    generated = formatAoxingHaveObstaclePathSegments(segments);
                }
            }
            
src/lujing/ObstaclePathPlanner.java
ÎļþÒÑɾ³ý
src/lujing/luoxuan.java
ÎļþÒÑɾ³ý
src/zhangaiwu/AddDikuai.java
@@ -23,7 +23,7 @@
import dikuai.Gecaokuanjisuan;
import dikuai.Gecaoanquanjuli;
import bianjie.Bianjieyouhuatoxy;
import lujing.Lunjingguihua;
import lujing.Qufenxingzhuang;
import lujing.AoxinglujingNoObstacle;
import lujing.YixinglujingNoObstacle;
@@ -1469,17 +1469,10 @@
                    YixinglujingNoObstacle.planPath(boundaryCoords, widthMetersStr, safetyDistanceMetersStr);
                plannedPath = formatYixingPathSegments(segments);
            } else {
                // æ— æ³•判断地块类型,使用默认方法作为后备
                JOptionPane.showMessageDialog(this, "无法判断地块类型,使用默认路径生成方法",
                // æ— æ³•判断地块类型
                JOptionPane.showMessageDialog(this, "无法判断地块类型,无法生成路径",
                    "提示", JOptionPane.WARNING_MESSAGE);
                String plannerMode = resolvePlannerMode(patternDisplay);
                plannedPath = Lunjingguihua.generatePathFromStrings(
                    boundaryCoords,
                    obstacleCoords != null ? obstacleCoords : "",
                    widthMetersStr,
                    safetyDistanceMetersStr,
                    plannerMode
                );
                plannedPath = "";
            }
            
            if (!isMeaningfulValue(plannedPath)) {