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