From 32c98d4855b6178554c787103dc956d161e152b3 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期五, 19 十二月 2025 19:45:30 +0800
Subject: [PATCH] 增加了异形分析逻辑

---
 device.properties                        |    8 
 dikuai.properties                        |   16 
 src/lujing/Qufenxingzhuang.java          |  146 +++++++++
 src/lujing/YixinglujingHaveObstacel.java |   23 +
 src/zhangaiwu/AddDikuai.java             |   61 ++++
 set.properties                           |    8 
 Obstacledge.properties                   |    2 
 shoudongbianjie.properties               |    6 
 src/lujing/AoxinglujingNoObstacle.java   |  331 ++++++++++++++++++++++
 src/lujing/MowingPathGenerationPage.java |  218 ++++++++++++-
 src/lujing/YixinglujingNoObstacle.java   |   23 +
 src/lujing/AoxinglujingHaveObstacel.java |   23 +
 src/lujing/Lunjingguihua.java            |   30 +
 13 files changed, 848 insertions(+), 47 deletions(-)

diff --git a/Obstacledge.properties b/Obstacledge.properties
index 54617c9..8bb0956 100644
--- a/Obstacledge.properties
+++ b/Obstacledge.properties
@@ -1,5 +1,5 @@
 # 鍓茶崏鏈哄湴鍧楅殰纰嶇墿閰嶇疆鏂囦欢
-# 鐢熸垚鏃堕棿锛�2025-12-18T19:44:44.126173900
+# 鐢熸垚鏃堕棿锛�2025-12-19T19:40:57.347052
 # 鍧愭爣绯伙細WGS84锛堝害鍒嗘牸寮忥級
 
 # ============ 鍦板潡鍩哄噯绔欓厤缃� ============
diff --git a/device.properties b/device.properties
index 36d438d..4961429 100644
--- a/device.properties
+++ b/device.properties
@@ -1,5 +1,5 @@
 #Updated device properties
-#Fri Dec 19 12:08:39 CST 2025
+#Fri Dec 19 19:43:54 CST 2025
 BupdateTime=-1
 GupdateTime=-1
 baseStationCardNumber=-1
@@ -11,15 +11,15 @@
 differentialAge=-1
 heading=-1
 mowerBladeHeight=-1
-mowerLength=3.00
+mowerLength=0.40
 mowerLightStatus=-1
 mowerModel=-1
 mowerName=-1
 mowerNumber=-1
 mowerStartStatus=-1
-mowerWidth=2.00
+mowerWidth=0.20
 mowingHeight=-1
-mowingSafetyDistance=1.86
+mowingSafetyDistance=0.28
 mowingWidth=-1
 pitch=-1
 positioningStatus=-1
diff --git a/dikuai.properties b/dikuai.properties
index 885a34d..351dfed 100644
--- a/dikuai.properties
+++ b/dikuai.properties
@@ -1,21 +1,21 @@
 #Dikuai Properties
-#Thu Dec 18 19:44:44 CST 2025
+#Fri Dec 19 19:44:17 CST 2025
 LAND1.angleThreshold=-1
 LAND1.baseStationCoordinates=3949.89151752,N,11616.79267501,E
-LAND1.boundaryCoordinates=2.87,-0.19;6.73,-1.23;22.76,1.06;27.88,3.65;35.84,9.96;38.40,10.35;41.91,8.49;44.09,4.86;43.49,2.92;33.76,0.59;30.22,-1.26;30.09,-3.80;34.19,-28.28;32.68,-29.99;30.12,-30.19;28.89,-28.59;25.58,-6.77;24.09,-4.26;21.10,-3.94;-10.36,-8.92;-11.69,-8.57;-12.75,-4.68;-12.23,-3.28;-6.21,-2.34;0.00,0.00;1.44,-0.09;2.87,-0.19
-LAND1.boundaryOriginalCoordinates=39.831524,116.279912,49.30;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;39.831256,116.280261,49.30;39.831253,116.280245,49.20;39.831254,116.280231,49.22;39.831258,116.280220,49.10;39.831268,116.280216,49.17;39.831281,116.280214,49.14;39.831295,116.280212,49.14;39.831308,116.280209,49.17;39.831321,116.280206,49.14;39.831334,116.280204,49.17;39.831348,116.280202,49.21;39.831362,116.280198,49.21;39.831375,116.280195,49.23;39.831390,116.280193,49.25;39.831405,116.280189,49.20;39.831420,116.280187,49.19;39.831436,116.280185,49.21;39.831450,116.280182,49.19;39.831464,116.280177,49.17;39.831478,116.280171,49.14;39.831487,116.280160,49.25;39.831491,116.280143,49.21;39.831490,116.280125,49.18;39.831487,116.280107,49.23;39.831485,116.280089,49.27;39.831483,116.280072,49.24;39.831482,116.280054,49.25;39.831480,116.280037,49.25;39.831477,116.280020,49.30;39.831475,116.280004,49.31;39.831473,116.279989,49.30;39.831472,116.279973,49.23;39.831470,116.279958,49.26;39.831468,116.279941,49.29;39.831465,116.279925,49.33;39.831463,116.279908,49.34;39.831462,116.279891,49.35;39.831461,116.279874,49.33;39.831458,116.279859,49.34;39.831456,116.279843,49.32;39.831454,116.279827,49.32;39.831452,116.279813,49.42;39.831450,116.279798,49.41;39.831448,116.279783,49.36;39.831447,116.279769,49.26;39.831445,116.279757,49.24;39.831445,116.279747,49.38;39.831448,116.279741,49.27;39.831455,116.279736,49.26;39.831463,116.279733,49.26;39.831473,116.279731,49.26;39.831483,116.279729,49.25;39.831491,116.279730,49.26;39.831496,116.279735,49.22;39.831497,116.279748,49.27;39.831498,116.279762,49.27;39.831500,116.279776,49.34;39.831502,116.279791,49.32;39.831504,116.279805,49.27;39.831507,116.279820,49.28;39.831510,116.279835,49.20;39.831513,116.279849,49.25;39.831517,116.279860,49.30;39.831520,116.279864,49.32;39.831520,116.279865,49.34;39.831522,116.279873,49.25;39.831524,116.279878,49.25;39.831525,116.279878,49.24
+LAND1.boundaryCoordinates=-10.36,-8.92;-12.10,-7.80;-12.66,-3.77;-4.96,-1.98;-6.04,-3.37;-7.12,-4.76;-8.20,-6.14;-9.28,-7.53;-10.36,-8.92
+LAND1.boundaryOriginalCoordinates=39.831445,116.279757,49.24;39.831445,116.279747,49.38;39.831448,116.279741,49.27;39.831455,116.279736,49.26;39.831463,116.279733,49.26;39.831473,116.279731,49.26;39.831483,116.279729,49.25;39.831491,116.279730,49.26;39.831496,116.279735,49.22;39.831497,116.279748,49.27;39.831498,116.279762,49.27;39.831500,116.279776,49.34;39.831502,116.279791,49.32;39.831504,116.279805,49.27;39.831507,116.279820,49.28
 LAND1.boundaryPointInterval=-1
-LAND1.createTime=2025-12-18 17\:12\:20
+LAND1.createTime=2025-12-19 18\:31\:53
 LAND1.intelligentSceneAnalysis=-1
-LAND1.landArea=483.34
-LAND1.landName=123
+LAND1.landArea=26.75
+LAND1.landName=21233
 LAND1.landNumber=LAND1
 LAND1.mowingBladeWidth=0.40
 LAND1.mowingOverlapDistance=0.06
 LAND1.mowingPattern=骞宠绾�
 LAND1.mowingTrack=-1
 LAND1.mowingWidth=34
-LAND1.plannedPath=35.722,9.394;38.565,9.844;39.065,9.579;35.179,8.964;34.636,8.534;39.565,9.314;40.065,9.049;34.094,8.104;33.551,7.673;40.565,8.784;41.066,8.519;33.009,7.243;32.466,6.813;41.566,8.254;41.804,7.947;31.923,6.383;31.381,5.953;41.993,7.633;42.182,7.319;30.838,5.523;30.296,5.093;42.371,7.004;42.560,6.690;29.753,4.663;29.210,4.232;42.748,6.375;0.018,-0.389;0.094,-0.377;1.645,-0.475;-1.557,-0.982;42.937,6.061;28.668,3.802;28.125,3.372;43.126,5.747;-3.133,-1.576;2.993,-0.606;3.797,-0.823;-4.708,-2.169;43.315,5.432;27.172,2.877;26.181,2.376;43.503,5.118;-11.978,-3.664;4.602,-1.040;5.407,-1.257;-12.114,-4.030;43.685,4.803;25.191,1.875;24.200,1.374;43.573,4.441;-12.250,-4.396;6.212,-1.474;15.278,-0.383;-12.346,-4.755;43.461,4.079;23.210,0.873;-12.256,-5.085;43.349,3.717;43.237,3.355;-12.166,-5.415;-12.076,-5.745;40.411,2.563;36.170,1.548;-11.986,-6.075;-11.896,-6.405;33.250,0.741;32.306,0.247;-11.806,-6.735;-11.717,-7.065;31.361,-0.246;30.416,-0.740;-11.627,-7.395;-11.537,-7.725;29.861,-1.172;29.836,-1.520;-11.447,-8.055;-11.095,-8.344;29.818,-1.867;29.801,-2.215;21.190,-3.578;22.488,-3.716;29.783,-2.562;29.765,-2.909;23.785,-3.855;24.424,-4.098;29.747,-3.256;29.730,-3.603;24.611,-4.413;24.798,-4.728;29.739,-3.945;29.795,-4.281;24.985,-5.042;25.171,-5.357;29.852,-4.616;29.908,-4.951;25.358,-5.672;25.545,-5.986;29.964,-5.287;30.020,-5.622;25.732,-6.301;25.915,-6.616;30.076,-5.957;30.132,-6.293;25.982,-6.950;26.033,-7.286;30.189,-6.628;30.245,-6.964;26.084,-7.622;26.135,-7.958;30.301,-7.299;30.357,-7.634;26.185,-8.295;26.236,-8.631;30.413,-7.970;30.469,-8.305;26.287,-8.967;26.338,-9.303;30.526,-8.640;30.582,-8.976;26.389,-9.639;26.440,-9.975;30.638,-9.311;30.694,-9.646;26.491,-10.312;26.542,-10.648;30.750,-9.982;30.806,-10.317;26.593,-10.984;26.644,-11.320;30.862,-10.652;30.919,-10.988;26.695,-11.656;26.746,-11.992;30.975,-11.323;31.031,-11.658;26.797,-12.328;26.848,-12.665;31.087,-11.994;31.143,-12.329;26.899,-13.001;26.950,-13.337;31.199,-12.664;31.256,-13.000;27.001,-13.673;27.052,-14.009;31.312,-13.335;31.368,-13.670;27.103,-14.345;27.154,-14.682;31.424,-14.006;31.480,-14.341;27.205,-15.018;27.256,-15.354;31.536,-14.676;31.593,-15.012;27.307,-15.690;27.358,-16.026;31.649,-15.347;31.705,-15.682;27.409,-16.362;27.460,-16.699;31.761,-16.018;31.817,-16.353;27.511,-17.035;27.562,-17.371;31.873,-16.688;31.930,-17.024;27.613,-17.707;27.664,-18.043;31.986,-17.359;32.042,-17.694;27.715,-18.379;27.766,-18.716;32.098,-18.030;32.154,-18.365;27.817,-19.052;27.868,-19.388;32.210,-18.701;32.267,-19.036;27.919,-19.724;27.970,-20.060;32.323,-19.371;32.379,-19.707;28.021,-20.396;28.072,-20.733;32.435,-20.042;32.491,-20.377;28.123,-21.069;28.174,-21.405;32.547,-20.713;32.604,-21.048;28.225,-21.741;28.276,-22.077;32.660,-21.383;32.716,-21.719;28.327,-22.413;28.378,-22.749;32.772,-22.054;32.828,-22.389;28.429,-23.086;28.480,-23.422;32.884,-22.725;32.941,-23.060;28.531,-23.758;28.582,-24.094;32.997,-23.395;33.053,-23.731;28.633,-24.430;28.684,-24.766;33.109,-24.066;33.165,-24.401;28.735,-25.103;28.786,-25.439;33.221,-24.737;33.278,-25.072;28.837,-25.775;28.888,-26.111;33.334,-25.407;33.390,-25.743;28.939,-26.447;28.990,-26.783;33.446,-26.078;33.502,-26.413;29.041,-27.120;29.092,-27.456;33.558,-26.749;33.615,-27.084;29.143,-27.792;29.194,-28.128;33.671,-27.419;33.727,-27.755;29.258,-28.462;29.494,-28.769;33.783,-28.090;33.524,-28.475;29.730,-29.076;29.966,-29.383;33.170,-28.876;32.817,-29.276;30.202,-29.690
+LAND1.plannedPath=-5.830841,-2.644443;-12.323966,-4.153883;-12.276978,-4.492026;-6.161845,-3.070456;-6.492848,-3.496470;-12.229991,-4.830169;-12.183003,-5.168312;-6.823852,-3.922484;-7.154855,-4.348497;-12.136016,-5.506455;-12.089028,-5.844598;-7.487144,-4.774810;-7.821079,-5.201505;-12.042040,-6.182741;-11.995053,-6.520885;-8.155014,-5.628200;-8.700347,-6.104039;-11.948065,-6.859028;-11.901077,-7.197171;-10.070186,-6.771548;-11.440025,-7.439057;-11.854090,-7.535314
 LAND1.returnPointCoordinates=-1
-LAND1.updateTime=2025-12-18 19\:44\:44
+LAND1.updateTime=2025-12-19 19\:44\:17
 LAND1.userId=-1
diff --git a/set.properties b/set.properties
index 357c32f..e145c4c 100644
--- a/set.properties
+++ b/set.properties
@@ -1,5 +1,5 @@
 #Mower Configuration Properties - Updated
-#Fri Dec 19 17:40:36 CST 2025
+#Fri Dec 19 19:44:56 CST 2025
 appVersion=-1
 boundaryLengthVisible=false
 currentWorkLandNumber=LAND1
@@ -8,12 +8,12 @@
 handheldMarkerId=1872
 idleTrailDurationSeconds=60
 manualBoundaryDrawingMode=false
-mapScale=5.63
+mapScale=41.56
 measurementModeEnabled=false
 mowerId=860
 serialAutoConnect=true
 serialBaudRate=115200
 serialPortName=COM15
 simCardNumber=-1
-viewCenterX=-15.67
-viewCenterY=9.92
+viewCenterX=8.76
+viewCenterY=4.44
diff --git a/shoudongbianjie.properties b/shoudongbianjie.properties
index d121ab7..b08be21 100644
--- a/shoudongbianjie.properties
+++ b/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)
-#Fri Dec 19 16:55:22 CST 2025
-boundaryCoordinates=2.64,9.22;1.40,10.29;9.39,20.06;19.16,22.54;19.69,12.60;13.48,8.34;6.20,6.74
+#Fri Dec 19 19:18:05 CST 2025
+boundaryCoordinates=-7.66,-10.73;-9.27,-9.14;-5.87,-8.78
 email=789
 language=zh
 lastLoginTime=-1
 password=123
-pointCount=7
+pointCount=3
 registrationTime=-1
 status=-1
 userId=-1
diff --git a/src/lujing/AoxinglujingHaveObstacel.java b/src/lujing/AoxinglujingHaveObstacel.java
new file mode 100644
index 0000000..9cf387b
--- /dev/null
+++ b/src/lujing/AoxinglujingHaveObstacel.java
@@ -0,0 +1,23 @@
+package lujing;
+
+import java.util.List;
+
+/**
+ * 鏈夐殰纰嶇墿鍑稿舰鍦板潡璺緞瑙勫垝绫�
+ */
+public class AoxinglujingHaveObstacel {
+    
+    /**
+     * 鐢熸垚璺緞
+     * @param boundaryCoordsStr 鍦板潡杈圭晫鍧愭爣瀛楃涓� "x1,y1;x2,y2;..."
+     * @param obstacleCoordsStr 闅滅鐗╁潗鏍囧瓧绗︿覆
+     * @param mowingWidthStr 鍓茶崏瀹藉害瀛楃涓诧紝濡� "0.34"
+     * @param safetyMarginStr 瀹夊叏杈硅窛瀛楃涓诧紝濡� "0.2"
+     * @return 璺緞鍧愭爣瀛楃涓诧紝鏍煎紡 "x1,y1;x2,y2;..."
+     */
+    public static String planPath(String boundaryCoordsStr, String obstacleCoordsStr, String mowingWidthStr, String safetyMarginStr) {
+        // TODO: 瀹炵幇鍑稿舰鍦板潡鏈夐殰纰嶇墿璺緞瑙勫垝绠楁硶
+        // 鐩墠浣跨敤榛樿鏂规硶浣滀负涓存椂瀹炵幇
+        throw new UnsupportedOperationException("AoxinglujingHaveObstacel.planPath 灏氭湭瀹炵幇");
+    }
+}
diff --git a/src/lujing/AoxinglujingNoObstacle.java b/src/lujing/AoxinglujingNoObstacle.java
new file mode 100644
index 0000000..75717ba
--- /dev/null
+++ b/src/lujing/AoxinglujingNoObstacle.java
@@ -0,0 +1,331 @@
+package lujing;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 鏃犻殰纰嶇墿鍑稿舰鑽夊湴璺緞瑙勫垝绫� (浼樺寲鐗�)
+ */
+public class AoxinglujingNoObstacle {
+
+    // 浼樺寲锛氬紩鍏ユ瀬灏忓�肩敤浜庢诞鐐规暟姣旇緝锛屽鐞嗗嚑浣曠簿搴﹁宸�
+    private static final double EPSILON = 1e-6;
+
+    /**
+     * 璺緞娈电被
+     */
+    public static class PathSegment {
+        public Point start;
+        public Point end;
+        public boolean isMowing; // true涓哄壊鑽夊伐浣滄锛宖alse涓鸿繃娓℃
+
+        public PathSegment(Point start, Point end, boolean isMowing) {
+            this.start = start;
+            this.end = end;
+            this.isMowing = isMowing;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("[%s -> %s, isMowing=%b]", start, end, isMowing);
+        }
+    }
+
+    /**
+     * 鍧愭爣鐐圭被
+     */
+    public static class Point {
+        double x, y;
+
+        public Point(double x, double y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("(%.4f, %.4f)", x, y);
+        }
+    }
+
+    /**
+     * 瀵瑰鍏紑鐨勯潤鎬佽皟鐢ㄦ柟娉� (淇濈暀瀛楃涓插叆鍙傛牸寮�)
+     *
+     * @param boundaryCoordsStr 鍦板潡杈圭晫鍧愭爣瀛楃涓� "x1,y1;x2,y2;..."
+     * @param mowingWidthStr    鍓茶崏瀹藉害瀛楃涓诧紝濡� "0.34"
+     * @param safetyMarginStr   瀹夊叏杈硅窛瀛楃涓诧紝濡� "0.2"
+     * @return 璺緞娈靛垪琛�
+     */
+    public static List<PathSegment> planPath(String boundaryCoordsStr, String mowingWidthStr, String safetyMarginStr) {
+        // 1. 瑙f瀽鍙傛暟 (浼樺寲锛氬崟鐙彁鍙栬В鏋愰�昏緫)
+        List<Point> originalPolygon = parseCoords(boundaryCoordsStr);
+        double mowingWidth;
+        double safetyMargin;
+
+        try {
+            mowingWidth = Double.parseDouble(mowingWidthStr);
+            safetyMargin = Double.parseDouble(safetyMarginStr);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("鍓茶崏瀹藉害鎴栧畨鍏ㄨ竟璺濇牸寮忛敊璇�");
+        }
+
+        // 2. 璋冪敤鏍稿績绠楁硶
+        return planPathCore(originalPolygon, mowingWidth, safetyMargin);
+    }
+
+    /**
+     * 鏍稿績绠楁硶閫昏緫 (寮虹被鍨嬪叆鍙傦紝渚夸簬娴嬭瘯鍜屽唴閮ㄨ皟鐢�)
+     */
+    private static List<PathSegment> planPathCore(List<Point> originalPolygon, double mowingWidth, double safetyMargin) {
+        // 浼樺寲锛氫笁瑙掑舰涔熸槸鍚堟硶鐨勫嚫澶氳竟褰紝闄愬埗鏀逛负灏忎簬3
+        if (originalPolygon == null || originalPolygon.size() < 3) {
+            throw new IllegalArgumentException("澶氳竟褰㈠潗鏍囩偣涓嶈冻3涓紝鏃犳硶鏋勬垚鏈夋晥鍖哄煙");
+        }
+
+        // 纭繚澶氳竟褰㈡槸閫嗘椂閽堟柟鍚�
+        ensureCCW(originalPolygon);
+
+        // 1. 鏍规嵁瀹夊叏杈硅窛鍐呯缉
+        List<Point> shrunkPolygon = shrinkPolygon(originalPolygon, safetyMargin);
+
+        // 浼樺寲锛氬唴缂╁悗濡傛灉澶氳竟褰㈠け鏁堬紙渚嬪鍦板潡澶獎锛屽唴缂╁悗娑堝け锛夛紝鐩存帴杩斿洖绌鸿矾寰勶紝閬垮厤鍚庣画鎶ラ敊
+        if (shrunkPolygon.size() < 3) {
+            // 杩欓噷鍙互璁板綍鏃ュ織锛氬湴鍧楄繃灏忥紝鏃犳硶婊¤冻瀹夊叏璺濈浣滀笟
+            return new ArrayList<>();
+        }
+
+        // 2. 璁$畻鏈�闀胯竟瑙掑害 (浣滀负鎵弿鏂瑰悜)
+        double angle = calculateLongestEdgeAngle(originalPolygon);
+
+        // 3. & 4. 鏃嬭浆鎵弿骞跺壀瑁�
+        List<PathSegment> mowingLines = generateClippedMowingLines(shrunkPolygon, angle, mowingWidth);
+
+        // 5. 寮撳瓧褰㈣繛鎺�
+        return connectPathSegments(mowingLines);
+    }
+
+    // ================= 鏍稿績绠楁硶杈呭姪鏂规硶 =================
+
+    private static List<Point> shrinkPolygon(List<Point> polygon, double margin) {
+        List<Point> newPoints = new ArrayList<>();
+        int n = polygon.size();
+
+        for (int i = 0; i < n; i++) {
+            Point p1 = polygon.get(i);
+            Point p2 = polygon.get((i + 1) % n);
+            Point p0 = polygon.get((i - 1 + n) % n);
+
+            Line line1 = offsetLine(p1, p2, margin);
+            Line line0 = offsetLine(p0, p1, margin);
+
+            Point intersection = getIntersection(line0, line1);
+            
+            // 浼樺寲锛氬鍔犻潪绌哄垽鏂紝涓斿鏋滀氦鐐瑰紓甯歌繙锛堝皷瑙掓晥搴旓級锛屽疄闄呭伐绋嬩腑鍙兘闇�瑕佸垏瑙掑鐞�
+            // 杩欓噷鏆備繚鐣欏熀纭�閫昏緫锛屼絾鍦ㄥ嚫澶氳竟褰腑閫氬父娌¢棶棰�
+            if (intersection != null) {
+                newPoints.add(intersection);
+            }
+        }
+        return newPoints;
+    }
+
+    private static double calculateLongestEdgeAngle(List<Point> polygon) {
+        double maxDistSq = -1;
+        double angle = 0;
+        int n = polygon.size();
+
+        for (int i = 0; i < n; i++) {
+            Point p1 = polygon.get(i);
+            Point p2 = polygon.get((i + 1) % n);
+            double dx = p2.x - p1.x;
+            double dy = p2.y - p1.y;
+            double distSq = dx * dx + dy * dy;
+
+            if (distSq > maxDistSq) {
+                maxDistSq = distSq;
+                angle = Math.atan2(dy, dx);
+            }
+        }
+        return angle;
+    }
+
+    private static List<PathSegment> generateClippedMowingLines(List<Point> polygon, double angle, double width) {
+        List<PathSegment> segments = new ArrayList<>();
+        
+        // 鏃嬭浆鑷虫按骞�
+        List<Point> rotatedPoly = rotatePolygon(polygon, -angle);
+
+        double minY = Double.MAX_VALUE;
+        double maxY = -Double.MAX_VALUE;
+        for (Point p : rotatedPoly) {
+            if (p.y < minY) minY = p.y;
+            if (p.y > maxY) maxY = p.y;
+        }
+
+        // 浼樺寲锛氳捣濮嬫壂鎻忕嚎澧炲姞涓�涓井灏忕殑鍋忕Щ閲� EPSILON
+        // 閬垮厤鎵弿绾挎濂借惤鍦ㄩ《鐐逛笂锛屽鑷翠氦鐐瑰垽鏂�昏緫鍑虹幇浜屼箟鎬�
+        double currentY = minY + width / 2.0 + EPSILON;
+
+        while (currentY < maxY) {
+            List<Double> xIntersections = new ArrayList<>();
+            int n = rotatedPoly.size();
+            
+            for (int i = 0; i < n; i++) {
+                Point p1 = rotatedPoly.get(i);
+                Point p2 = rotatedPoly.get((i + 1) % n);
+
+                // 浼樺寲锛氬拷鐣ユ按骞崇嚎娈� (p1.y == p2.y)锛岄伩鍏嶉櫎闆堕敊璇紝姘村钩绾挎涓嶅弬涓庡瀭鐩存壂鎻忕嚎姹備氦
+                if (Math.abs(p1.y - p2.y) < EPSILON) continue;
+
+                // 鍒ゆ柇绾挎鏄惁璺ㄨ秺 currentY
+                // 浣跨敤涓ユ牸鐨勪笉绛夊紡閫昏緫閰嶅悎鑼冨洿鍒ゆ柇
+                double minP = Math.min(p1.y, p2.y);
+                double maxP = Math.max(p1.y, p2.y);
+
+                if (currentY >= minP && currentY < maxP) {
+                    // 绾挎�ф彃鍊兼眰X
+                    double x = p1.x + (currentY - p1.y) * (p2.x - p1.x) / (p2.y - p1.y);
+                    xIntersections.add(x);
+                }
+            }
+
+            Collections.sort(xIntersections);
+
+            // 鎻愬彇绾挎锛岄�氬父鏄垚瀵瑰嚭鐜�
+            if (xIntersections.size() >= 2) {
+                // 鍙栨渶宸﹀拰鏈�鍙崇偣杩炴帴锛堝簲瀵瑰彲鑳藉嚭鐜扮殑寰皬璁$畻璇樊瀵艰嚧鐨勫涓氦鐐癸級
+                double xStart = xIntersections.get(0);
+                double xEnd = xIntersections.get(xIntersections.size() - 1);
+
+                // 鍙湁褰撶嚎娈甸暱搴﹀ぇ浜庢瀬灏忓�兼椂鎵嶆坊鍔狅紝閬垮厤鐢熸垚鍣偣璺緞
+                if (xEnd - xStart > EPSILON) {
+                    Point rStart = rotatePoint(new Point(xStart, currentY), angle); // 杩欓噷鐨刢urrentY瀹為檯涓婂甫浜唀psilon锛岃繕鍘熸椂娌¢棶棰�
+                    Point rEnd = rotatePoint(new Point(xEnd, currentY), angle);
+                    segments.add(new PathSegment(rStart, rEnd, true));
+                }
+            }
+
+            currentY += width;
+        }
+
+        return segments;
+    }
+
+    private static List<PathSegment> connectPathSegments(List<PathSegment> lines) {
+        List<PathSegment> result = new ArrayList<>();
+        if (lines.isEmpty()) return result;
+
+        for (int i = 0; i < lines.size(); i++) {
+            PathSegment currentLine = lines.get(i);
+            Point actualStart, actualEnd;
+
+            // 寮撳瓧褰㈣鍒掞細鍋舵暟琛屾鍚戯紝濂囨暟琛屽弽鍚�
+            if (i % 2 == 0) {
+                actualStart = currentLine.start;
+                actualEnd = currentLine.end;
+            } else {
+                actualStart = currentLine.end;
+                actualEnd = currentLine.start;
+            }
+
+            // 娣诲姞杩囨浮娈�
+            if (i > 0) {
+                Point prevEnd = result.get(result.size() - 1).end;
+                // 鍙湁褰撹窛绂荤‘瀹炲瓨鍦ㄦ椂鎵嶆坊鍔犺繃娓℃锛堥伩鍏嶉噸鍚堢偣锛�
+                if (distanceSq(prevEnd, actualStart) > EPSILON) {
+                    result.add(new PathSegment(prevEnd, actualStart, false));
+                }
+            }
+
+            result.add(new PathSegment(actualStart, actualEnd, true));
+        }
+        return result;
+    }
+
+    // ================= 鍩虹鍑犱綍宸ュ叿 (浼樺寲鐗�) =================
+
+    private static List<Point> parseCoords(String s) {
+        List<Point> list = new ArrayList<>();
+        if (s == null || s.trim().isEmpty()) return list;
+        
+        String[] parts = s.split(";");
+        for (String part : parts) {
+            String[] xy = part.split(",");
+            if (xy.length >= 2) {
+                try {
+                    double x = Double.parseDouble(xy[0].trim());
+                    double y = Double.parseDouble(xy[1].trim());
+                    list.add(new Point(x, y));
+                } catch (NumberFormatException e) {
+                    // 蹇界暐鏍煎紡閿欒鐨勫崟涓偣
+                }
+            }
+        }
+        return list;
+    }
+
+    // Shoelace鍏紡鍒ゆ柇鏂瑰悜骞惰皟鏁�
+    private static void ensureCCW(List<Point> polygon) {
+        double sum = 0;
+        for (int i = 0; i < polygon.size(); i++) {
+            Point p1 = polygon.get(i);
+            Point p2 = polygon.get((i + 1) % polygon.size());
+            sum += (p2.x - p1.x) * (p2.y + p1.y);
+        }
+        // 鍋囪鏍囧噯绗涘崱灏斿潗鏍囩郴锛宻um > 0 涓洪『鏃堕拡锛岄渶瑕佸弽杞负閫嗘椂閽�
+        if (sum > 0) {
+            Collections.reverse(polygon);
+        }
+    }
+
+    private static class Line {
+        double a, b, c; 
+        public Line(double a, double b, double c) { this.a = a; this.b = b; this.c = c; }
+    }
+
+    private static Line offsetLine(Point p1, Point p2, double dist) {
+        double dx = p2.x - p1.x;
+        double dy = p2.y - p1.y;
+        double len = Math.sqrt(dx * dx + dy * dy);
+        
+        // 闃叉闄や互0
+        if (len < EPSILON) return new Line(0, 0, 0);
+
+        double nx = -dy / len;
+        double ny = dx / len;
+
+        double newX = p1.x + nx * dist;
+        double newY = p1.y + ny * dist;
+
+        double a = -dy;
+        double b = dx;
+        double c = -a * newX - b * newY;
+        return new Line(a, b, c);
+    }
+
+    private static Point getIntersection(Line l1, Line l2) {
+        double det = l1.a * l2.b - l2.a * l1.b;
+        if (Math.abs(det) < EPSILON) return null; // 骞宠
+        double x = (l1.b * l2.c - l2.b * l1.c) / det;
+        double y = (l2.a * l1.c - l1.a * l2.c) / det;
+        return new Point(x, y);
+    }
+
+    private static Point rotatePoint(Point p, double angle) {
+        double cos = Math.cos(angle);
+        double sin = Math.sin(angle);
+        return new Point(p.x * cos - p.y * sin, p.x * sin + p.y * cos);
+    }
+
+    private static List<Point> rotatePolygon(List<Point> poly, double angle) {
+        List<Point> res = new ArrayList<>();
+        for (Point p : poly) {
+            res.add(rotatePoint(p, angle));
+        }
+        return res;
+    }
+    
+    private static double distanceSq(Point p1, Point p2) {
+        return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);
+    }
+}
\ No newline at end of file
diff --git a/src/lujing/Lunjingguihua.java b/src/lujing/Lunjingguihua.java
index 1f19fae..e9329c1 100644
--- a/src/lujing/Lunjingguihua.java
+++ b/src/lujing/Lunjingguihua.java
@@ -22,7 +22,22 @@
         throw new IllegalStateException("Utility class");
     }
 
-    // 5鍙傛暟鏍稿績鐢熸垚鏂规硶
+    /**
+     * 鐢熸垚鍓茶崏璺緞娈靛垪琛紙5鍙傛暟鐗堟湰锛�
+     * 鏍规嵁缁欏畾鐨勫杈瑰舰杈圭晫銆侀殰纰嶇墿銆佸壊鑽夊搴︺�佸畨鍏ㄨ窛绂诲拰妯″紡锛岀敓鎴愬畬鏁寸殑鍓茶崏璺緞瑙勫垝
+     * 
+     * @param polygonCoords 澶氳竟褰㈣竟鐣屽潗鏍囧瓧绗︿覆锛屾牸寮忥細"x1,y1;x2,y2;x3,y3;..." 鎴� "(x1,y1);(x2,y2);..."
+     *                      鏀寔鍒嗗彿銆佺┖鏍笺�侀�楀彿绛夊绉嶅垎闅旂锛屼細鑷姩闂悎澶氳竟褰�
+     * @param obstaclesCoords 闅滅鐗╁潗鏍囧瓧绗︿覆锛屾牸寮忥細"(x1,y1;x2,y2;...)" 澶氫釜闅滅鐗╃敤鎷彿鍒嗛殧
+     *                        濡傛灉涓虹┖鎴杗ull锛屽垯瑙嗕负鏃犻殰纰嶇墿
+     * @param mowingWidth 鍓茶崏瀹藉害锛堢背锛夛紝瀛楃涓叉牸寮忥紝濡� "0.34" 琛ㄧず34鍘樼背
+     *                    濡傛灉涓虹┖鎴栨棤娉曡В鏋愶紝榛樿浣跨敤0.34绫�
+     * @param safetyDistStr 瀹夊叏杈硅窛锛堢背锛夛紝瀛楃涓叉牸寮忥紝鐢ㄤ簬鍐呯缉杈圭晫鍜岄殰纰嶇墿澶栨墿
+     *                      濡傛灉涓虹┖鎴杗ull锛屽皢鑷姩璁$畻涓� width/2 + 0.2绫筹紝纭繚鍓茶崏鏈哄疄浣撳畬鍏ㄥ湪鐣屽唴
+     * @param modeStr 璺緞妯″紡瀛楃涓诧紝"1" 鎴� "spiral" 琛ㄧず铻烘棆妯″紡锛屽叾浠栧�艰〃绀哄钩琛屾ā寮忥紙榛樿锛�
+     * @return 璺緞娈靛垪琛紝姣忎釜PathSegment鍖呭惈璧风偣銆佺粓鐐瑰拰鏄惁涓哄壊鑽夋鐨勬爣蹇�
+     *         濡傛灉澶氳竟褰㈠潗鏍囦笉瓒�4涓偣锛屽皢鎶涘嚭IllegalArgumentException寮傚父
+     */
     public static List<PathSegment> generatePathSegments(String polygonCoords,
                                                          String obstaclesCoords,
                                                          String mowingWidth,
@@ -44,7 +59,18 @@
         return planner.generate();
     }
 
-    // 4鍙傛暟閲嶈浇锛岄�傞厤 AddDikuai.java
+    /**
+     * 鐢熸垚鍓茶崏璺緞娈靛垪琛紙4鍙傛暟鐗堟湰锛�
+     * 杩欐槸5鍙傛暟鐗堟湰鐨勯噸杞芥柟娉曪紝瀹夊叏璺濈鍙傛暟鑷姩璁句负null锛岀郴缁熷皢浣跨敤榛樿璁$畻鍊�
+     * 涓昏鐢ㄤ簬閫傞厤 AddDikuai.java 绛変笉闇�瑕佹寚瀹氬畨鍏ㄨ窛绂荤殑鍦烘櫙
+     * 
+     * @param polygonCoords 澶氳竟褰㈣竟鐣屽潗鏍囧瓧绗︿覆锛屾牸寮忥細"x1,y1;x2,y2;x3,y3;..."
+     * @param obstaclesCoords 闅滅鐗╁潗鏍囧瓧绗︿覆锛屾牸寮忥細"(x1,y1;x2,y2;...)"锛屽彲涓虹┖
+     * @param mowingWidth 鍓茶崏瀹藉害锛堢背锛夛紝瀛楃涓叉牸寮忥紝濡� "0.34"
+     * @param modeStr 璺緞妯″紡瀛楃涓诧紝"1" 鎴� "spiral" 琛ㄧず铻烘棆妯″紡锛屽叾浠栧�艰〃绀哄钩琛屾ā寮�
+     * @return 璺緞娈靛垪琛紝姣忎釜PathSegment鍖呭惈璧风偣銆佺粓鐐瑰拰鏄惁涓哄壊鑽夋鐨勬爣蹇�
+     *         瀹夊叏璺濈灏嗚嚜鍔ㄨ绠椾负 width/2 + 0.2绫�
+     */
     public static List<PathSegment> generatePathSegments(String polygonCoords,
                                                          String obstaclesCoords,
                                                          String mowingWidth,
diff --git a/src/lujing/MowingPathGenerationPage.java b/src/lujing/MowingPathGenerationPage.java
index 4bc81b4..73a8fde 100644
--- a/src/lujing/MowingPathGenerationPage.java
+++ b/src/lujing/MowingPathGenerationPage.java
@@ -15,8 +15,14 @@
 import dikuai.Dikuai;
 import lujing.Lunjingguihua;
 import lujing.ObstaclePathPlanner;
+import lujing.Qufenxingzhuang;
+import lujing.AoxinglujingNoObstacle;
+import lujing.YixinglujingNoObstacle;
+import lujing.AoxinglujingHaveObstacel;
+import lujing.YixinglujingHaveObstacel;
 import org.locationtech.jts.geom.Coordinate;
 import gecaoji.Device;
+import java.util.Locale;
 
 /**
  * 鐢熸垚鍓茶崏璺緞椤甸潰
@@ -494,8 +500,24 @@
             obstacles = obstacles.replace("\r\n", " ").replace('\r', ' ').replace('\n', ' ');
         }
 
+        // 鑾峰彇瀹夊叏璺濈
+        String safetyMarginStr = getSafetyDistanceString();
+        if (safetyMarginStr == null) {
+            // 濡傛灉娌℃湁璁剧疆瀹夊叏璺濈锛屼娇鐢ㄩ粯璁ゅ�硷細鍓茶崏瀹藉害鐨勪竴鍗� + 0.2绫�
+            double defaultSafetyDistance = widthMeters / 2.0 + 0.2;
+            safetyMarginStr = BigDecimal.valueOf(defaultSafetyDistance)
+                .setScale(3, RoundingMode.HALF_UP)
+                .stripTrailingZeros()
+                .toPlainString();
+        }
+
         String mode = normalizeExistingMowingPattern(modeInput);
         try {
+            // 1. 棣栧厛鍒ゆ柇鍦板潡绫诲瀷锛堝嚫褰㈣繕鏄紓褰級
+            Qufenxingzhuang shapeJudger = new Qufenxingzhuang();
+            int grassType = shapeJudger.judgeGrassType(boundary);
+            // grassType: 0=鏃犳硶鍒ゆ柇, 1=鍑稿舰, 2=寮傚舰
+            
             // 瑙f瀽闅滅鐗╁垪琛�
             List<List<Coordinate>> obstacleList = Lunjingguihua.parseObstacles(obstacles);
             if (obstacleList == null) {
@@ -505,35 +527,110 @@
             // 鍒ゆ柇鏄惁鏈夋湁鏁堢殑闅滅鐗╋細鍙湁褰撹В鏋愭垚鍔熶笖鍒楄〃涓嶄负绌烘椂锛屾墠璁や负鏈夐殰纰嶇墿
             boolean hasValidObstacles = !obstacleList.isEmpty();
             
-            String generated;
+            String generated = null;
             
+            // 2. 鏍规嵁鍦板潡绫诲瀷鍜屾槸鍚︽湁闅滅鐗╋紝璋冪敤涓嶅悓鐨勮矾寰勭敓鎴愮被
             if (!hasValidObstacles) {
-                // 闅滅鐗╁潗鏍囦笉瀛樺湪鎴栦负绌烘椂锛屼娇鐢↙unjingguihua绫荤殑鏂规硶鐢熸垚璺緞
-                generated = Lunjingguihua.generatePathFromStrings(
-                    boundary,
-                    obstacles != null ? obstacles : "",
-                    plannerWidth,
-                    null,  // safetyDistStr锛屼娇鐢ㄩ粯璁ゅ��
-                    mode
-                );
-            } else {
-                // 鏈夋湁鏁堥殰纰嶇墿鏃讹紝浣跨敤ObstaclePathPlanner澶勭悊璺緞鐢熸垚
-                List<Coordinate> polygon = Lunjingguihua.parseCoordinates(boundary);
-                if (polygon.size() < 4) {
-                    if (showMessages) {
-                        JOptionPane.showMessageDialog(parentComponent, "澶氳竟褰㈠潗鏍囨暟閲忎笉瓒筹紝鑷冲皯闇�瑕佷笁涓偣",
-                            "閿欒", JOptionPane.ERROR_MESSAGE);
+                // 鏃犻殰纰嶇墿鐨勬儏鍐�
+                if (grassType == 1) {
+                    // 鍑稿舰鍦板潡锛屾棤闅滅鐗� -> 璋冪敤 AoxinglujingNoObstacle
+                    List<AoxinglujingNoObstacle.PathSegment> segments = 
+                        AoxinglujingNoObstacle.planPath(boundary, plannerWidth, safetyMarginStr);
+                    generated = formatAoxingPathSegments(segments);
+                } else if (grassType == 2) {
+                    // 寮傚舰鍦板潡锛屾棤闅滅鐗� -> 璋冪敤 YixinglujingNoObstacle
+                    // 娉ㄦ剰锛氬鏋滆绫昏繕娌℃湁瀹炵幇锛岃繖閲屼細鎶涘嚭寮傚父鎴栬繑鍥瀗ull
+                    try {
+                        // 鍋囪 YixinglujingNoObstacle 鏈夌被浼肩殑鏂规硶绛惧悕
+                        // 濡傛灉绫昏繕娌℃湁瀹炵幇锛屽彲鑳介渶瑕佷娇鐢ㄥ師鏉ョ殑鏂规硶浣滀负鍚庡
+                        generated = YixinglujingNoObstacle.planPath(boundary, plannerWidth, safetyMarginStr);
+                    } catch (Exception e) {
+                        // 濡傛灉绫昏繕娌℃湁瀹炵幇锛屼娇鐢ㄥ師鏉ョ殑鏂规硶浣滀负鍚庡
+                        if (showMessages) {
+                            System.err.println("YixinglujingNoObstacle 灏氭湭瀹炵幇锛屼娇鐢ㄩ粯璁ゆ柟娉�: " + e.getMessage());
+                        }
+                        generated = Lunjingguihua.generatePathFromStrings(
+                            boundary, obstacles != null ? obstacles : "", plannerWidth, safetyMarginStr, mode);
                     }
-                    return null;
+                } else {
+                    // 鏃犳硶鍒ゆ柇鍦板潡绫诲瀷锛屼娇鐢ㄥ師鏉ョ殑鏂规硶浣滀负鍚庡
+                    if (showMessages) {
+                        JOptionPane.showMessageDialog(parentComponent, "鏃犳硶鍒ゆ柇鍦板潡绫诲瀷锛屼娇鐢ㄩ粯璁よ矾寰勭敓鎴愭柟娉�", 
+                            "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+                    }
+                    generated = Lunjingguihua.generatePathFromStrings(
+                        boundary, obstacles != null ? obstacles : "", plannerWidth, safetyMarginStr, mode);
                 }
-
-                // 鏈夐殰纰嶇墿鏃朵娇鐢ㄥ壊鑽夊搴︾殑涓�鍗� + 0.05绫抽澶栧畨鍏ㄨ窛绂�
-                double safetyDistance = widthMeters / 2.0 + 0.05;
-
-                ObstaclePathPlanner pathPlanner = new ObstaclePathPlanner(
-                    polygon, widthMeters, mode, obstacleList, safetyDistance);
-                List<Lunjingguihua.PathSegment> segments = pathPlanner.generate();
-                generated = Lunjingguihua.formatPathSegments(segments);
+            } else {
+                // 鏈夐殰纰嶇墿鐨勬儏鍐�
+                if (grassType == 1) {
+                    // 鍑稿舰鍦板潡锛屾湁闅滅鐗� -> 璋冪敤 AoxinglujingHaveObstacel
+                    try {
+                        // 鍋囪 AoxinglujingHaveObstacel 鏈夌被浼肩殑鏂规硶绛惧悕
+                        generated = AoxinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
+                    } 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);
+                    }
+                } 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);
+                    }
+                } else {
+                    // 鏃犳硶鍒ゆ柇鍦板潡绫诲瀷锛屼娇鐢ㄥ師鏉ョ殑鏂规硶浣滀负鍚庡
+                    if (showMessages) {
+                        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);
+                }
             }
             
             String trimmed = generated != null ? generated.trim() : "";
@@ -564,6 +661,77 @@
         return null;
     }
     
+    /**
+     * 鑾峰彇瀹夊叏璺濈瀛楃涓诧紙绫筹級
+     */
+    private String getSafetyDistanceString() {
+        Device device = Device.getActiveDevice();
+        if (device != null) {
+            String safetyDistanceValue = device.getMowingSafetyDistance();
+            if (safetyDistanceValue != null && !"-1".equals(safetyDistanceValue) && !safetyDistanceValue.trim().isEmpty()) {
+                try {
+                    double distanceMeters = Double.parseDouble(safetyDistanceValue.trim());
+                    // 濡傛灉鍊煎ぇ浜�100锛岃涓烘槸鍘樼背锛岄渶瑕佽浆鎹负绫�
+                    if (distanceMeters > 100) {
+                        distanceMeters = distanceMeters / 100.0;
+                    }
+                    return BigDecimal.valueOf(distanceMeters)
+                        .setScale(3, RoundingMode.HALF_UP)
+                        .stripTrailingZeros()
+                        .toPlainString();
+                } catch (NumberFormatException e) {
+                    // 瑙f瀽澶辫触锛岃繑鍥瀗ull锛屼娇鐢ㄩ粯璁ゅ��
+                }
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * 鏍煎紡鍖� AoxinglujingNoObstacle.PathSegment 鍒楄〃涓哄潗鏍囧瓧绗︿覆
+     */
+    private String formatAoxingPathSegments(List<AoxinglujingNoObstacle.PathSegment> segments) {
+        if (segments == null || segments.isEmpty()) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        AoxinglujingNoObstacle.Point last = null;
+        for (AoxinglujingNoObstacle.PathSegment segment : segments) {
+            // 鍙坊鍔犲壊鑽夊伐浣滄锛岃烦杩囪繃娓℃
+            if (segment.isMowing) {
+                // 濡傛灉璧风偣涓庝笂涓�涓粓鐐逛笉鍚岋紝娣诲姞璧风偣
+                if (last == null || !equals2D(last, segment.start)) {
+                    appendPoint(sb, segment.start);
+                }
+                // 娣诲姞缁堢偣
+                appendPoint(sb, segment.end);
+                last = segment.end;
+            }
+        }
+        return sb.toString();
+    }
+    
+    /**
+     * 姣旇緝涓や釜鐐规槸鍚︾浉鍚岋紙浣跨敤灏忕殑瀹瑰樊锛�
+     */
+    private boolean equals2D(AoxinglujingNoObstacle.Point p1, AoxinglujingNoObstacle.Point p2) {
+        if (p1 == null || p2 == null) {
+            return p1 == p2;
+        }
+        double tolerance = 1e-6;
+        return Math.abs(p1.x - p2.x) < tolerance && Math.abs(p1.y - p2.y) < tolerance;
+    }
+    
+    /**
+     * 娣诲姞鐐瑰埌瀛楃涓叉瀯寤哄櫒
+     */
+    private void appendPoint(StringBuilder sb, AoxinglujingNoObstacle.Point point) {
+        if (sb.length() > 0) {
+            sb.append(";");
+        }
+        sb.append(String.format(Locale.US, "%.6f,%.6f", point.x, point.y));
+    }
+    
     // ========== UI杈呭姪鏂规硶 ==========
     
     private JTextArea createInfoTextArea(String text, boolean editable, int rows) {
diff --git a/src/lujing/Qufenxingzhuang.java b/src/lujing/Qufenxingzhuang.java
new file mode 100644
index 0000000..60439ed
--- /dev/null
+++ b/src/lujing/Qufenxingzhuang.java
@@ -0,0 +1,146 @@
+package lujing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Qufenxingzhuang {
+
+    // 瀹瑰樊闃堝�硷細sin(瑙掑害)銆�
+    // 0.05 澶х害瀵瑰簲 2.8搴︺�傛剰鍛崇潃灏忎簬3搴︾殑寰皬鍑归櫡鎴栧嚫璧蜂細琚拷鐣ワ紝瑙嗕负鐩寸嚎銆�
+    // 濡傛灉鎮ㄨ寰楀垽鏂緷鐒跺お涓ワ紝鍙互灏嗘鍊艰皟澶у埌 0.1 (绾�5.7搴�)銆�
+    private static final double ANGLE_TOLERANCE = 0.1;
+
+    // 鍒ゆ柇鑽夊湴绫诲瀷鐨勪富鏂规硶
+    public int judgeGrassType(String coordinates) {
+        try {
+            // 1. 瑙f瀽鍧愭爣瀛楃涓�
+            List<Point> points = parseCoordinates(coordinates);
+
+            // 2. 鏁版嵁棰勫鐞嗭細鍒犻櫎鏈�鍚庝竴涓棴鍚堢偣
+            // 鍘熷洜锛氳緭鍏ユ暟鎹�氬父棣栧熬鐩稿悓锛屽垹闄ゆ渶鍚庝竴涓偣閬垮厤閲嶅璁$畻
+            if (!points.isEmpty()) {
+                points.remove(points.size() - 1);
+            }
+
+            // 3. 妫�鏌ョ偣鏁帮紙绉婚櫎鍚庤嚦灏戦渶瑕�3涓偣锛�
+            if (points.size() < 3) {
+                return 0; // 鏃犳硶鏋勬垚澶氳竟褰�
+            }
+
+            // 4. 鍒ゆ柇褰㈢姸
+            if (isConvexPolygon(points)) {
+                return 1; // 鍑稿舰鑽夊湴
+            } else {
+                return 2; // 寮傚舰鑽夊湴锛堟槑鏄剧殑鍑瑰杈瑰舰锛�
+            }
+
+        } catch (Exception e) {
+            // 瑙f瀽澶辫触鎴栬绠楀紓甯�
+            return 0;
+        }
+    }
+
+    // 瑙f瀽鍧愭爣瀛楃涓�
+    private List<Point> parseCoordinates(String coordinates) {
+        List<Point> points = new ArrayList<>();
+        String cleanStr = coordinates.replaceAll("[()\\[\\]{}]", "").trim();
+        String[] pointStrings = cleanStr.split(";");
+
+        for (String pointStr : pointStrings) {
+            pointStr = pointStr.trim();
+            if (pointStr.isEmpty()) continue;
+            
+            String[] xy = pointStr.split(",");
+            if (xy.length != 2) throw new IllegalArgumentException("鏍煎紡閿欒");
+            
+            points.add(new Point(Double.parseDouble(xy[0].trim()), Double.parseDouble(xy[1].trim())));
+        }
+        return points;
+    }
+
+    // 銆愭牳蹇冧慨鏀广�戝垽鏂杈瑰舰鏄惁涓哄嚫澶氳竟褰紙甯﹀宸級
+    private boolean isConvexPolygon(List<Point> points) {
+        // 鍏堟竻鐞嗘瀬搴︽帴杩戠殑鐐癸紝閬垮厤闄や互闆堕敊璇�
+        List<Point> cleanedPoints = cleanDuplicatePoints(points);
+        int n = cleanedPoints.size();
+        if (n < 3) return false;
+
+        boolean hasPositive = false;
+        boolean hasNegative = false;
+
+        for (int i = 0; i < n; i++) {
+            Point p0 = cleanedPoints.get(i);
+            Point p1 = cleanedPoints.get((i + 1) % n);
+            Point p2 = cleanedPoints.get((i + 2) % n);
+
+            // 璁$畻鍚戦噺 p0->p1 鍜� p1->p2
+            double dx1 = p1.x - p0.x;
+            double dy1 = p1.y - p0.y;
+            double dx2 = p2.x - p1.x;
+            double dy2 = p2.y - p1.y;
+
+            // 璁$畻鍙夌Н
+            double cross = dx1 * dy2 - dx2 * dy1;
+
+            // 璁$畻杈归暱
+            double len1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
+            double len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
+
+            // 闃叉鏋佺煭杈瑰鑷寸殑璁$畻閿欒
+            if (len1 < 1e-6 || len2 < 1e-6) continue;
+
+            // 銆愬叧閿慨鏀广�戣绠楀綊涓�鍖栫殑鍙夌Н鍊� (鐩稿綋浜� sin胃)
+            // 杩欎唬琛ㄤ簡寮洸鐨勭▼搴︼紝鎺掗櫎浜嗚竟闀垮鍙夌Н鏁板�煎ぇ灏忕殑骞叉壈
+            double sinTheta = cross / (len1 * len2);
+
+            // 搴旂敤瀹瑰樊閫昏緫锛�
+            // 鍙湁褰撳集鏇茬▼搴﹁秴杩囬槇鍊兼椂锛屾墠璁板綍鏂瑰悜
+            if (sinTheta > ANGLE_TOLERANCE) {
+                hasPositive = true; // 鏄庢樉鐨勫乏杞紙鍑革級
+            } else if (sinTheta < -ANGLE_TOLERANCE) {
+                hasNegative = true; // 鏄庢樉鐨勫彸杞紙鍑癸級
+            }
+            // 濡傛灉 sinTheta 鍦� -0.05 鍒� 0.05 涔嬮棿锛岃涓烘槸鐩寸嚎鎶栧姩锛屽拷鐣ヤ笉璁�
+
+            // 濡傛灉鏃㈡湁鏄庢樉鐨勫悜宸﹁浆锛屽張鏈夋槑鏄剧殑鍚戝彸杞紝閭e氨鏄紓褰紙鍑瑰杈瑰舰锛�
+            if (hasPositive && hasNegative) {
+                return false;
+            }
+        }
+        
+        // 濡傛灉娌℃湁浜х敓鍐茬獊鐨勮浆鍚戯紝鎴栬�呮槸鍗曠函鐨勭洿绾匡紙鎵�鏈夌偣鍏辩嚎锛夛紝瑙嗕负鍑稿舰
+        return true;
+    }
+
+    // 杈呭姪锛氫粎鍘婚櫎閲嶅鐐癸紝涓嶅啀杩囧害娓呯悊鍏辩嚎鐐癸紙鍥犱负涓婚�昏緫宸茬粡鑳藉鐞嗗叡绾挎姈鍔級
+    private List<Point> cleanDuplicatePoints(List<Point> points) {
+        List<Point> cleaned = new ArrayList<>();
+        double tolerance = 1e-6;
+
+        for (Point p : points) {
+            if (!cleaned.isEmpty()) {
+                Point last = cleaned.get(cleaned.size() - 1);
+                if (Math.abs(p.x - last.x) < tolerance && Math.abs(p.y - last.y) < tolerance) {
+                    continue; // 璺宠繃閲嶅鐐�
+                }
+            }
+            cleaned.add(p);
+        }
+        
+        // 妫�鏌ラ灏炬槸鍚﹀洜涓哄惊鐜鑷撮噸澶嶏紙铏界劧涓绘柟娉曞垹闄や簡鏈�鍚庝竴涓偣锛屼负浜嗗仴澹�у啀鏌ヤ竴娆★級
+        if (cleaned.size() > 1) {
+            Point first = cleaned.get(0);
+            Point last = cleaned.get(cleaned.size() - 1);
+            if (Math.abs(first.x - last.x) < tolerance && Math.abs(first.y - last.y) < tolerance) {
+                cleaned.remove(cleaned.size() - 1);
+            }
+        }
+        return cleaned;
+    }
+
+    // 鐐圭被
+    private static class Point {
+        double x, y;
+        Point(double x, double y) { this.x = x; this.y = y; }
+    }
+}
\ No newline at end of file
diff --git a/src/lujing/YixinglujingHaveObstacel.java b/src/lujing/YixinglujingHaveObstacel.java
new file mode 100644
index 0000000..d46ca08
--- /dev/null
+++ b/src/lujing/YixinglujingHaveObstacel.java
@@ -0,0 +1,23 @@
+package lujing;
+
+import java.util.List;
+
+/**
+ * 鏈夐殰纰嶇墿寮傚舰鍦板潡璺緞瑙勫垝绫�
+ */
+public class YixinglujingHaveObstacel {
+    
+    /**
+     * 鐢熸垚璺緞
+     * @param boundaryCoordsStr 鍦板潡杈圭晫鍧愭爣瀛楃涓� "x1,y1;x2,y2;..."
+     * @param obstacleCoordsStr 闅滅鐗╁潗鏍囧瓧绗︿覆
+     * @param mowingWidthStr 鍓茶崏瀹藉害瀛楃涓诧紝濡� "0.34"
+     * @param safetyMarginStr 瀹夊叏杈硅窛瀛楃涓诧紝濡� "0.2"
+     * @return 璺緞鍧愭爣瀛楃涓诧紝鏍煎紡 "x1,y1;x2,y2;..."
+     */
+    public static String planPath(String boundaryCoordsStr, String obstacleCoordsStr, String mowingWidthStr, String safetyMarginStr) {
+        // TODO: 瀹炵幇寮傚舰鍦板潡鏈夐殰纰嶇墿璺緞瑙勫垝绠楁硶
+        // 鐩墠浣跨敤榛樿鏂规硶浣滀负涓存椂瀹炵幇
+        throw new UnsupportedOperationException("YixinglujingHaveObstacel.planPath 灏氭湭瀹炵幇");
+    }
+}
diff --git a/src/lujing/YixinglujingNoObstacle.java b/src/lujing/YixinglujingNoObstacle.java
new file mode 100644
index 0000000..1919731
--- /dev/null
+++ b/src/lujing/YixinglujingNoObstacle.java
@@ -0,0 +1,23 @@
+package lujing;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * 鏃犻殰纰嶇墿寮傚舰鍦板潡璺緞瑙勫垝绫�
+ */
+public class YixinglujingNoObstacle {
+    
+    /**
+     * 鐢熸垚璺緞
+     * @param boundaryCoordsStr 鍦板潡杈圭晫鍧愭爣瀛楃涓� "x1,y1;x2,y2;..."
+     * @param mowingWidthStr 鍓茶崏瀹藉害瀛楃涓诧紝濡� "0.34"
+     * @param safetyMarginStr 瀹夊叏杈硅窛瀛楃涓诧紝濡� "0.2"
+     * @return 璺緞鍧愭爣瀛楃涓诧紝鏍煎紡 "x1,y1;x2,y2;..."
+     */
+    public static String planPath(String boundaryCoordsStr, String mowingWidthStr, String safetyMarginStr) {
+        // TODO: 瀹炵幇寮傚舰鍦板潡鏃犻殰纰嶇墿璺緞瑙勫垝绠楁硶
+        // 鐩墠浣跨敤榛樿鏂规硶浣滀负涓存椂瀹炵幇
+        throw new UnsupportedOperationException("YixinglujingNoObstacle.planPath 灏氭湭瀹炵幇");
+    }
+}
diff --git a/src/zhangaiwu/AddDikuai.java b/src/zhangaiwu/AddDikuai.java
index b80b9ec..c7cb4bd 100644
--- a/src/zhangaiwu/AddDikuai.java
+++ b/src/zhangaiwu/AddDikuai.java
@@ -33,6 +33,7 @@
 import zhuye.Shouye;
 import zhuye.Coordinate;
 import zhuye.buttonset;
+import gecaoji.Device;
 
 /**
  * 鏂板鍦板潡瀵硅瘽妗� - 澶氭楠よ〃鍗曡璁�
@@ -67,6 +68,7 @@
     private JComboBox<String> mowingPatternCombo;
     private JTextField mowingWidthField; // 鍓茶崏鏈哄壊鍒�瀹藉害
     private JTextField overlapDistanceField; // 鐩搁偦琛岄噸鍙犺窛绂�
+    private JTextField mowingSafetyDistanceField; // 鍓茶崏瀹夊叏璺濈
     private JLabel calculatedMowingWidthLabel; // 璁$畻鍚庣殑鍓茶崏瀹藉害鏄剧ず
     private JPanel previewPanel;
     private Map<String, JPanel> drawingOptionPanels = new HashMap<>();
@@ -1044,6 +1046,65 @@
         
         calculatedWidthPanel.add(calculatedWidthDisplayPanel);
         settingsPanel.add(calculatedWidthPanel);
+        settingsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+        
+        // 鍓茶崏瀹夊叏璺濈
+        JPanel safetyDistancePanel = createFormGroupWithoutHint("鍓茶崏瀹夊叏璺濈");
+        JPanel safetyDistanceInputPanel = new JPanel(new BorderLayout());
+        safetyDistanceInputPanel.setBackground(WHITE);
+        safetyDistanceInputPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 48));
+        safetyDistanceInputPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        
+        // 鑾峰彇榛樿鍊�
+        String defaultSafetyDistance = "0.5"; // 榛樿鍊�
+        try {
+            Device device = Device.getGecaoji();
+            if (device != null) {
+                String safetyDistance = device.getMowingSafetyDistance();
+                if (safetyDistance != null && !safetyDistance.trim().isEmpty() && !"-1".equals(safetyDistance.trim())) {
+                    defaultSafetyDistance = safetyDistance.trim();
+                }
+            }
+        } catch (Exception e) {
+            // 濡傛灉鑾峰彇澶辫触锛屼娇鐢ㄩ粯璁ゅ��
+        }
+        
+        mowingSafetyDistanceField = new JTextField(defaultSafetyDistance);
+        mowingSafetyDistanceField.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
+        mowingSafetyDistanceField.setBorder(BorderFactory.createCompoundBorder(
+            BorderFactory.createLineBorder(BORDER_COLOR, 2),
+            BorderFactory.createEmptyBorder(10, 12, 10, 12)
+        ));
+        
+        // 娣诲姞鏂囨湰妗嗙劍鐐规晥鏋�
+        mowingSafetyDistanceField.addFocusListener(new FocusAdapter() {
+            @Override
+            public void focusGained(FocusEvent e) {
+                mowingSafetyDistanceField.setBorder(BorderFactory.createCompoundBorder(
+                    BorderFactory.createLineBorder(PRIMARY_COLOR, 2),
+                    BorderFactory.createEmptyBorder(10, 12, 10, 12)
+                ));
+            }
+            
+            @Override
+            public void focusLost(FocusEvent e) {
+                mowingSafetyDistanceField.setBorder(BorderFactory.createCompoundBorder(
+                    BorderFactory.createLineBorder(BORDER_COLOR, 2),
+                    BorderFactory.createEmptyBorder(10, 12, 10, 12)
+                ));
+            }
+        });
+        
+        JLabel safetyDistanceUnitLabel = new JLabel("绫�");
+        safetyDistanceUnitLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+        safetyDistanceUnitLabel.setForeground(LIGHT_TEXT);
+        safetyDistanceUnitLabel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
+        
+        safetyDistanceInputPanel.add(mowingSafetyDistanceField, BorderLayout.CENTER);
+        safetyDistanceInputPanel.add(safetyDistanceUnitLabel, BorderLayout.EAST);
+        
+        safetyDistancePanel.add(safetyDistanceInputPanel);
+        settingsPanel.add(safetyDistancePanel);
         settingsPanel.add(Box.createRigidArea(new Dimension(0, 25)));
         
         stepPanel.add(settingsPanel);

--
Gitblit v1.10.0