From 1c8e32b4a45c1865e2a422e9949c1e996df861a6 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期四, 25 十二月 2025 16:01:17 +0800
Subject: [PATCH] 新增了凸形有障碍物地块路径规划未完成
---
dikuai.properties | 6
src/lujing/MowingPathGenerationPage.java | 48 +++++
set.properties | 8
Obstacledge.properties | 7
src/lujing/AoxinglujingHaveObstacel.java | 367 ++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 412 insertions(+), 24 deletions(-)
diff --git a/Obstacledge.properties b/Obstacledge.properties
index 2820614..e2f9f71 100644
--- a/Obstacledge.properties
+++ b/Obstacledge.properties
@@ -1,5 +1,5 @@
# 鍓茶崏鏈哄湴鍧楅殰纰嶇墿閰嶇疆鏂囦欢
-# 鐢熸垚鏃堕棿锛�2025-12-23T19:10:14.326740700
+# 鐢熸垚鏃堕棿锛�2025-12-25T16:00:09.768491900
# 鍧愭爣绯伙細WGS84锛堝害鍒嗘牸寮忥級
# ============ 鍦板潡鍩哄噯绔欓厤缃� ============
@@ -12,3 +12,8 @@
# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.originalCoords=[鍧愭爣涓瞉
# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.xyCoords=[鍧愭爣涓瞉
+# --- 鍦板潡LAND1鐨勯殰纰嶇墿 ---
+plot.LAND1.obstacle.闅滅鐗�1.shape=1
+plot.LAND1.obstacle.闅滅鐗�1.originalCoords=0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E
+plot.LAND1.obstacle.闅滅鐗�1.xyCoords=143.09,-434.11;142.20,-387.55;186.08,-379.49;224.58,-418.89;201.30,-447.54
+
diff --git a/dikuai.properties b/dikuai.properties
index d7a2ce5..129bb58 100644
--- a/dikuai.properties
+++ b/dikuai.properties
@@ -1,5 +1,5 @@
#Dikuai Properties
-#Tue Dec 23 19:10:14 CST 2025
+#Thu Dec 25 16:00:09 CST 2025
LAND1.angleThreshold=-1
LAND1.baseStationCoordinates=3949.89151752,N,11616.79267501,E
LAND1.boundaryCoordinates=-58.32,-476.37;85.99,-289.22;354.31,-329.80;293.43,-559.79
@@ -17,9 +17,9 @@
LAND1.mowingSafetyDistance=0.53
LAND1.mowingTrack=-1
LAND1.mowingWidth=200
-LAND1.plannedPath=353.646421,-330.235669;86.219211,-289.790692;-57.399120,-476.043693;293.049800,-559.155132;353.646421,-330.235669;353.131597,-332.180552;84.822383,-291.602183;83.425556,-293.413674;352.616772,-334.125435;352.101948,-336.070317;82.028728,-295.225165;80.631901,-297.036656;351.587124,-338.015200;351.072299,-339.960083;79.235073,-298.848147;77.838246,-300.659638;350.557475,-341.904966;350.042650,-343.849848;76.441418,-302.471129;75.044591,-304.282620;349.527826,-345.794731;349.013002,-347.739614;73.647763,-306.094111;72.250936,-307.905602;348.498177,-349.684497;347.983353,-351.629379;70.854109,-309.717093;69.457281,-311.528584;347.468528,-353.574262;346.953704,-355.519145;68.060454,-313.340075;66.663626,-315.151566;346.438880,-357.464028;345.924055,-359.408910;65.266799,-316.963057;63.869971,-318.774548;345.409231,-361.353793;344.894406,-363.298676;62.473144,-320.586039;61.076316,-322.397529;344.379582,-365.243559;343.864758,-367.188441;59.679489,-324.209020;58.282661,-326.020511;343.349933,-369.133324;342.835109,-371.078207;56.885834,-327.832002;55.489006,-329.643493;342.320284,-373.023090;341.805460,-374.967972;54.092179,-331.454984;52.695351,-333.266475;341.290636,-376.912855;340.775811,-378.857738;51.298524,-335.077966;49.901697,-336.889457;340.260987,-380.802621;339.746163,-382.747503;48.504869,-338.700948;47.108042,-340.512439;339.231338,-384.692386;338.716514,-386.637269;45.711214,-342.323930;44.314387,-344.135421;338.201689,-388.582152;337.686865,-390.527034;42.917559,-345.946912;41.520732,-347.758403;337.172041,-392.471917;336.657216,-394.416800;40.123904,-349.569894;38.727077,-351.381385;336.142392,-396.361683;335.627567,-398.306565;37.330249,-353.192876;35.933422,-355.004367;335.112743,-400.251448;334.597919,-402.196331;34.536594,-356.815858;33.139767,-358.627349;334.083094,-404.141214;333.568270,-406.086096;31.742939,-360.438840;30.346112,-362.250331;333.053445,-408.030979;332.538621,-409.975862;28.949285,-364.061822;27.552457,-365.873313;332.023797,-411.920745;331.508972,-413.865627;26.155630,-367.684803;24.758802,-369.496294;330.994148,-415.810510;330.479324,-417.755393;23.361975,-371.307785;21.965147,-373.119276;329.964499,-419.700276;329.449675,-421.645158;20.568320,-374.930767;19.171492,-376.742258;328.934850,-423.590041;328.420026,-425.534924;17.774665,-378.553749;16.377837,-380.365240;327.905202,-427.479807;327.390377,-429.424689;14.981010,-382.176731;13.584182,-383.988222;326.875553,-431.369572;326.360728,-433.314455;12.187355,-385.799713;10.790527,-387.611204;325.845904,-435.259338;325.331080,-437.204220;9.393700,-389.422695;7.996873,-391.234186;324.816255,-439.149103;324.301431,-441.093986;6.600045,-393.045677;5.203218,-394.857168;323.786606,-443.038869;323.271782,-444.983751;3.806390,-396.668659;2.409563,-398.480150;322.756958,-446.928634;322.242133,-448.873517;1.012735,-400.291641;-0.384092,-402.103132;321.727309,-450.818399;321.212484,-452.763282;-1.780920,-403.914623;-3.177747,-405.726114;320.697660,-454.708165;320.182836,-456.653048;-4.574575,-407.537605;-5.971402,-409.349096;319.668011,-458.597930;319.153187,-460.542813;-7.368230,-411.160586;-8.765057,-412.972077;318.638363,-462.487696;318.123538,-464.432579;-10.161885,-414.783568;-11.558712,-416.595059;317.608714,-466.377461;317.093889,-468.322344;-12.955539,-418.406550;-14.352367,-420.218041;316.579065,-470.267227;316.064241,-472.212110;-15.749194,-422.029532;-17.146022,-423.841023;315.549416,-474.156992;315.034592,-476.101875;-18.542849,-425.652514;-19.939677,-427.464005;314.519767,-478.046758;314.004943,-479.991641;-21.336504,-429.275496;-22.733332,-431.086987;313.490119,-481.936523;312.975294,-483.881406;-24.130159,-432.898478;-25.526987,-434.709969;312.460470,-485.826289;311.945645,-487.771172;-26.923814,-436.521460;-28.320642,-438.332951;311.430821,-489.716054;310.915997,-491.660937;-29.717469,-440.144442;-31.114297,-441.955933;310.401172,-493.605820;309.886348,-495.550703;-32.511124,-443.767424;-33.907951,-445.578915;309.371523,-497.495585;308.856699,-499.440468;-35.304779,-447.390406;-36.701606,-449.201897;308.341875,-501.385351;307.827050,-503.330234;-38.098434,-451.013388;-39.495261,-452.824879;307.312226,-505.275116;306.797402,-507.219999;-40.892089,-454.636370;-42.288916,-456.447860;306.282577,-509.164882;305.767753,-511.109765;-43.685744,-458.259351;-45.082571,-460.070842;305.252928,-513.054647;304.738104,-514.999530;-46.479399,-461.882333;-47.876226,-463.693824;304.223280,-516.944413;303.708455,-518.889296;-49.273054,-465.505315;-50.669881,-467.316806;303.193631,-520.834178;302.678806,-522.779061;-52.066709,-469.128297;-53.463536,-470.939788;302.163982,-524.723944;301.649158,-526.668827;-54.860363,-472.751279;-56.257191,-474.562770;301.134333,-528.613709;300.619509,-530.558592;-53.103034,-477.062540;-29.560792,-482.645749;300.104684,-532.503475;299.589860,-534.448358;-6.018549,-488.228958;17.523693,-493.812168;299.075036,-536.393240;298.560211,-538.338123;41.065935,-499.395377;64.608178,-504.978586;298.045387,-540.283006;297.530562,-542.227889;88.150420,-510.561795;111.692662,-516.145005;297.015738,-544.172771;296.500914,-546.117654;135.234905,-521.728214;158.777147,-527.311423;295.986089,-548.062537;295.471265,-550.007420;182.319389,-532.894632;205.861631,-538.477841;294.956441,-551.952302;294.441616,-553.897185;229.403874,-544.061051;252.946116,-549.644260;293.926792,-555.842068;293.411967,-557.786951;276.488358,-555.227469
+LAND1.plannedPath=292.652,-558.038;-56.017,-475.349;-54.677,-473.611;293.164,-556.104;293.676,-554.170;-53.337,-471.874;-51.998,-470.136;294.188,-552.236;294.700,-550.302;-50.658,-468.398;-49.318,-466.660;295.212,-548.368;295.724,-546.434;-47.978,-464.923;-46.638,-463.185;296.236,-544.500;296.748,-542.566;-45.298,-461.447;-43.958,-459.710;297.260,-540.632;297.772,-538.698;-42.618,-457.972;-41.278,-456.234;298.284,-536.764;298.796,-534.830;-39.938,-454.497;-38.598,-452.759;299.308,-532.896;299.820,-530.962;-37.258,-451.021;-35.918,-449.283;300.332,-529.028;300.844,-527.093;-34.578,-447.546;-33.239,-445.808;301.356,-525.159;301.868,-523.225;-31.899,-444.070;-30.559,-442.333;302.380,-521.291;302.892,-519.357;-29.219,-440.595;-27.879,-438.857;303.403,-517.423;303.915,-515.489;-26.539,-437.120;-25.199,-435.382;304.427,-513.555;304.939,-511.621;-23.859,-433.644;-22.519,-431.906;305.451,-509.687;305.963,-507.753;-21.179,-430.169;-19.839,-428.431;306.475,-505.819;306.987,-503.885;-18.499,-426.693;-17.159,-424.956;307.499,-501.951;308.011,-500.017;-15.819,-423.218;-14.480,-421.480;308.523,-498.083;309.035,-496.148;-13.140,-419.742;-11.800,-418.005;309.547,-494.214;310.059,-492.280;-10.460,-416.267;-9.120,-414.529;310.571,-490.346;311.083,-488.412;-7.780,-412.792;-6.440,-411.054;311.595,-486.478;312.107,-484.544;-5.100,-409.316;-3.760,-407.579;312.619,-482.610;313.131,-480.676;-2.420,-405.841;-1.080,-404.103;313.643,-478.742;314.155,-476.808;0.260,-402.365;1.600,-400.628;142.559,-434.057;142.519,-431.992;2.940,-398.890;4.279,-397.152;142.480,-429.928;142.440,-427.863;5.619,-395.415;6.959,-393.677;142.401,-425.798;142.362,-423.733;8.299,-391.939;9.639,-390.202;142.322,-421.668;142.283,-419.603;10.979,-388.464;12.319,-386.726;142.243,-417.539;142.204,-415.474;13.659,-384.988;14.999,-383.251;142.164,-413.409;142.125,-411.344;16.339,-381.513;17.679,-379.775;142.085,-409.279;142.046,-407.214;19.019,-378.038;20.359,-376.300;142.006,-405.150;141.967,-403.085;21.699,-374.562;23.038,-372.825;141.927,-401.020;141.888,-398.955;24.378,-371.087;25.718,-369.349;141.848,-396.890;141.809,-394.825;27.058,-367.611;28.398,-365.874;141.770,-392.761;141.730,-390.696;29.738,-364.136;31.078,-362.398;141.691,-388.631;141.679,-387.454;141.708,-387.352;141.758,-387.258;141.825,-387.175;141.908,-387.108;142.002,-387.058;142.948,-386.874;32.418,-360.661;33.758,-358.923;147.832,-385.977;152.717,-385.079;35.098,-357.185;36.438,-355.448;157.601,-384.182;162.485,-383.285;37.778,-353.710;39.118,-351.972;167.369,-382.388;172.254,-381.491;40.458,-350.234;41.797,-348.497;177.138,-380.594;182.022,-379.697;43.137,-346.759;44.477,-345.021;331.049,-412.984;331.561,-411.050;45.817,-343.284;47.157,-341.546;332.073,-409.116;332.585,-407.182;48.497,-339.808;49.837,-338.071;333.097,-405.248;333.609,-403.314;51.177,-336.333;52.517,-334.595;334.121,-401.380;334.633,-399.446;53.857,-332.857;55.197,-331.120;335.145,-397.511;335.657,-395.577;56.537,-329.382;57.877,-327.644;336.169,-393.643;336.681,-391.709;59.217,-325.907;60.556,-324.169;337.193,-389.775;337.705,-387.841;61.896,-322.431;63.236,-320.694;338.217,-385.907;338.729,-383.973;64.576,-318.956;65.916,-317.218;339.241,-382.039;339.753,-380.105;67.256,-315.480;68.596,-313.743;340.265,-378.171;340.777,-376.237;69.936,-312.005;71.276,-310.267;341.288,-374.303;341.800,-372.369;72.616,-308.530;73.956,-306.792;342.312,-370.435;342.824,-368.501;75.296,-305.054;76.636,-303.317;343.336,-366.567;343.848,-364.632;77.976,-301.579;79.315,-299.841;344.360,-362.698;344.872,-360.764;80.655,-298.103;81.995,-296.366;345.384,-358.830;345.896,-356.896;83.335,-294.628;84.675,-292.890;346.408,-354.962;346.920,-353.028;86.015,-291.153;101.614,-292.797;347.432,-351.094;347.944,-349.160;125.537,-296.415;149.460,-300.033;348.456,-347.226;348.968,-345.292;173.384,-303.651;197.307,-307.269;349.480,-343.358;349.992,-341.424;221.230,-310.887;245.153,-314.505;350.504,-339.490;351.016,-337.556;269.076,-318.123;293.000,-321.741;351.528,-335.622;352.040,-333.688;316.923,-325.359;340.846,-328.977;352.552,-331.753;222.518,-416.022;323.882,-440.061;324.394,-438.127;219.904,-413.346;217.289,-410.671;324.906,-436.193;325.418,-434.259;214.675,-407.995;212.061,-405.320;325.930,-432.325;326.442,-430.390;209.446,-402.644;206.832,-399.969;326.954,-428.456;327.466,-426.522;204.218,-397.293;201.603,-394.618;327.978,-424.588;328.489,-422.654;198.989,-391.942;196.374,-389.267;329.001,-420.720;329.513,-418.786;193.760,-386.591;191.146,-383.916;330.025,-416.852;330.537,-414.918;188.531,-381.240;224.959,-418.520;225.027,-418.606;225.076,-418.704;225.104,-418.811;225.109,-418.921;225.091,-419.029;225.051,-419.132;224.991,-419.224;201.642,-447.944;201.490,-448.033;314.667,-474.874;315.179,-472.940;202.968,-446.328;204.368,-444.605;315.691,-471.006;316.202,-469.072;205.768,-442.881;207.169,-441.158;316.714,-467.138;317.226,-465.204;208.569,-439.435;209.969,-437.711;317.738,-463.269;318.250,-461.335;211.370,-435.988;212.770,-434.265;318.762,-459.401;319.274,-457.467;214.170,-432.541;215.571,-430.818;319.786,-455.533;320.298,-453.599;216.971,-429.094;218.371,-427.371;320.810,-451.665;321.322,-449.731;219.772,-425.648;221.172,-423.924;321.834,-447.797;322.346,-445.863;222.573,-422.201;223.973,-420.478;322.858,-443.929;323.370,-441.995;225.065,-418.681
LAND1.returnPathCoordinates=-1
LAND1.returnPathRawCoordinates=-1
LAND1.returnPointCoordinates=-1
-LAND1.updateTime=2025-12-23 19\:10\:14
+LAND1.updateTime=2025-12-25 16\:00\:09
LAND1.userId=-1
diff --git a/set.properties b/set.properties
index 4d5600c..8f81828 100644
--- a/set.properties
+++ b/set.properties
@@ -1,5 +1,5 @@
#Mower Configuration Properties - Updated
-#Thu Dec 25 13:40:36 CST 2025
+#Thu Dec 25 16:00:35 CST 2025
appVersion=-1
boundaryLengthVisible=false
currentWorkLandNumber=LAND1
@@ -8,12 +8,12 @@
handheldMarkerId=1872
idleTrailDurationSeconds=60
manualBoundaryDrawingMode=false
-mapScale=0.93
+mapScale=8.30
measurementModeEnabled=false
mowerId=6258
serialAutoConnect=true
serialBaudRate=115200
serialPortName=COM15
simCardNumber=-1
-viewCenterX=-148.00
-viewCenterY=424.51
+viewCenterX=-193.03
+viewCenterY=445.26
diff --git a/src/lujing/AoxinglujingHaveObstacel.java b/src/lujing/AoxinglujingHaveObstacel.java
index 9cf387b..12e585e 100644
--- a/src/lujing/AoxinglujingHaveObstacel.java
+++ b/src/lujing/AoxinglujingHaveObstacel.java
@@ -1,23 +1,360 @@
package lujing;
+import java.util.ArrayList;
+import java.util.Collections;
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 灏氭湭瀹炵幇");
+
+ private static final double EPSILON = 1e-6;
+
+ public static class Point {
+ public double x, y;
+ public Point(double x, double y) { this.x = x; this.y = y; }
+ @Override
+ public String toString() { return String.format("%.6f,%.6f", x, y); }
}
-}
+
+ public static class PathSegment {
+ public Point start, end;
+ public boolean isMowing;
+ public PathSegment(Point start, Point end, boolean isMowing) {
+ this.start = start; this.end = end; this.isMowing = isMowing;
+ }
+ }
+
+ public abstract static class Obstacle {
+ public abstract boolean isInside(Point p);
+ public abstract List<Double> getIntersectionsX(double y, double angle);
+ }
+
+ public static class PolygonObstacle extends Obstacle {
+ public List<Point> points;
+ public PolygonObstacle(List<Point> points) { this.points = points; }
+
+ @Override
+ public boolean isInside(Point p) {
+ boolean result = false;
+ for (int i = 0, j = points.size() - 1; i < points.size(); j = i++) {
+ if ((points.get(i).y > p.y) != (points.get(j).y > p.y) &&
+ (p.x < (points.get(j).x - points.get(i).x) * (p.y - points.get(i).y) / (points.get(j).y - points.get(i).y) + points.get(i).x)) {
+ result = !result;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public List<Double> getIntersectionsX(double y, double angle) {
+ List<Point> rotated = rotatePolygon(this.points, -angle);
+ List<Double> xInts = new ArrayList<>();
+ for (int i = 0; i < rotated.size(); i++) {
+ Point p1 = rotated.get(i), p2 = rotated.get((i + 1) % rotated.size());
+ if ((p1.y <= y && p2.y > y) || (p2.y <= y && p1.y > y)) {
+ xInts.add(p1.x + (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y));
+ }
+ }
+ return xInts;
+ }
+ }
+
+ public static class CircleObstacle extends Obstacle {
+ public Point center;
+ public double radius;
+ public CircleObstacle(Point center, double radius) { this.center = center; this.radius = radius; }
+
+ @Override
+ public boolean isInside(Point p) {
+ return Math.hypot(p.x - center.x, p.y - center.y) <= radius + EPSILON;
+ }
+
+ @Override
+ public List<Double> getIntersectionsX(double y, double angle) {
+ List<Double> xInts = new ArrayList<>();
+ Point rCenter = rotatePoint(center, -angle);
+ double dy = Math.abs(y - rCenter.y);
+ if (dy < radius) {
+ double dx = Math.sqrt(radius * radius - dy * dy);
+ xInts.add(rCenter.x - dx);
+ xInts.add(rCenter.x + dx);
+ }
+ return xInts;
+ }
+ }
+
+ public static List<PathSegment> planPath(String boundaryStr, String obstacleStr, String widthStr, String marginStr) {
+ List<Point> boundary = parseCoords(boundaryStr);
+ double width = Double.parseDouble(widthStr);
+ double margin = Double.parseDouble(marginStr);
+ List<Obstacle> obstacles = parseObstacles(obstacleStr, margin);
+
+ return planPathCore(boundary, obstacles, width, margin);
+ }
+
+ private static List<PathSegment> planPathCore(List<Point> boundary, List<Obstacle> obstacles, double width, double margin) {
+ if (boundary.size() < 3) return new ArrayList<>();
+
+ ensureCCW(boundary);
+ List<Point> workArea = shrinkPolygon(boundary, margin);
+ if (workArea.size() < 3) return new ArrayList<>();
+
+ double bestAngle = findOptimalScanAngle(workArea);
+ Point firstScanStart = getFirstScanStartPoint(workArea, bestAngle, width);
+ List<Point> alignedWorkArea = alignBoundaryToStart(workArea, firstScanStart);
+
+ List<PathSegment> finalPath = new ArrayList<>();
+
+ // 1. 鍥磋竟璺緞
+ for (int i = 0; i < alignedWorkArea.size(); i++) {
+ finalPath.add(new PathSegment(alignedWorkArea.get(i), alignedWorkArea.get((i + 1) % alignedWorkArea.size()), true));
+ }
+
+ // 2. 鍐呴儴濉厖
+ Point currentPos = alignedWorkArea.get(0);
+ List<PathSegment> zigZagLines = generateOptimizedZigZag(workArea, obstacles, bestAngle, width, currentPos);
+ finalPath.addAll(zigZagLines);
+
+ return finalPath;
+ }
+
+ private static List<PathSegment> generateOptimizedZigZag(List<Point> polygon, List<Obstacle> obstacles, double angle, double width, Point startPoint) {
+ List<PathSegment> result = new ArrayList<>();
+ List<Point> rotatedPoly = rotatePolygon(polygon, -angle);
+
+ double minY = Double.MAX_VALUE, maxY = -Double.MAX_VALUE;
+ for (Point p : rotatedPoly) {
+ minY = Math.min(minY, p.y); maxY = Math.max(maxY, p.y);
+ }
+
+ Point currentPos = startPoint;
+ boolean leftToRight = true;
+
+ for (double y = minY + width; y < maxY - width / 2; y += width) {
+ List<Double> intersections = getXIntersections(rotatedPoly, y);
+ if (intersections.size() < 2) continue;
+ Collections.sort(intersections);
+
+ double xBoundaryMin = intersections.get(0);
+ double xBoundaryMax = intersections.get(intersections.size() - 1);
+
+ // 鏀堕泦褰撳墠琛屾墍鏈夐殰纰嶇墿浜ょ偣骞惰繘琛岃鍓�
+ List<Double> splitPoints = new ArrayList<>();
+ splitPoints.add(xBoundaryMin);
+ for (Obstacle obs : obstacles) {
+ List<Double> obsX = obs.getIntersectionsX(y, angle);
+ for (double ox : obsX) {
+ if (ox > xBoundaryMin && ox < xBoundaryMax) splitPoints.add(ox);
+ }
+ }
+ Collections.sort(splitPoints);
+
+ // 鏋勫缓鏈夋晥娈�
+ List<LineRange> validRanges = new ArrayList<>();
+ for (int i = 0; i < splitPoints.size() - 1; i++) {
+ double midX = (splitPoints.get(i) + splitPoints.get(i + 1)) / 2.0;
+ Point midPoint = rotatePoint(new Point(midX, y), angle);
+
+ boolean insideAnyObstacle = false;
+ for (Obstacle obs : obstacles) {
+ if (obs.isInside(midPoint)) {
+ insideAnyObstacle = true;
+ break;
+ }
+ }
+ if (!insideAnyObstacle) {
+ validRanges.add(new LineRange(splitPoints.get(i), splitPoints.get(i+1)));
+ }
+ }
+
+ // 鏍规嵁褰撳墠鏈濆悜鎺掑簭鏈夋晥娈�
+ if (!leftToRight) {
+ Collections.reverse(validRanges);
+ for (LineRange range : validRanges) {
+ double temp = range.start;
+ range.start = range.end;
+ range.end = temp;
+ }
+ }
+
+ // 杩炴帴璺緞
+ for (LineRange range : validRanges) {
+ Point pStart = rotatePoint(new Point(range.start, y), angle);
+ Point pEnd = rotatePoint(new Point(range.end, y), angle);
+
+ if (Math.hypot(currentPos.x - pStart.x, currentPos.y - pStart.y) > 0.01) {
+ result.add(new PathSegment(currentPos, pStart, false));
+ }
+ result.add(new PathSegment(pStart, pEnd, true));
+ currentPos = pEnd;
+ }
+ leftToRight = !leftToRight;
+ }
+ return result;
+ }
+
+ private static class LineRange {
+ double start, end;
+ LineRange(double s, double e) { this.start = s; this.end = e; }
+ }
+
+ // --- 闅滅鐗╄В鏋愪笌澶氳竟褰㈠鎵� ---
+ private static List<Obstacle> parseObstacles(String obsStr, double margin) {
+ List<Obstacle> list = new ArrayList<>();
+ if (obsStr == null || obsStr.trim().isEmpty()) return list;
+
+ for (String part : obsStr.split("\\$")) {
+ List<Point> pts = parseCoords(part);
+ if (pts.size() == 2) {
+ double r = Math.hypot(pts.get(0).x - pts.get(1).x, pts.get(0).y - pts.get(1).y);
+ list.add(new CircleObstacle(pts.get(0), r + margin));
+ } else if (pts.size() > 2) {
+ ensureCCW(pts);
+ list.add(new PolygonObstacle(expandPolygon(pts, margin)));
+ }
+ }
+ return list;
+ }
+
+ private static List<Point> expandPolygon(List<Point> poly, double margin) {
+ List<Point> result = new ArrayList<>();
+ int n = poly.size();
+ for (int i = 0; i < n; i++) {
+ Point pPrev = poly.get((i - 1 + n) % n);
+ Point pCurr = poly.get(i);
+ Point pNext = poly.get((i + 1) % n);
+
+ double d1x = pCurr.x - pPrev.x, d1y = pCurr.y - pPrev.y;
+ double l1 = Math.hypot(d1x, d1y);
+ double d2x = pNext.x - pCurr.x, d2y = pNext.y - pCurr.y;
+ double l2 = Math.hypot(d2x, d2y);
+
+ // 璁$畻澶栨硶绾�
+ double n1x = d1y / l1, n1y = -d1x / l1;
+ double n2x = d2y / l2, n2y = -d2x / l2;
+
+ double bx = n1x + n2x, by = n1y + n2y;
+ double bLen = Math.hypot(bx, by);
+ if (bLen < EPSILON) { bx = n1x; by = n1y; } else { bx /= bLen; by /= bLen; }
+
+ double cosHalf = n1x * bx + n1y * by;
+ double d = margin / Math.max(cosHalf, 0.1);
+ // 闄愬埗鏈�澶у鎵╋紝闃叉灏栬鐣稿彉
+ d = Math.min(d, margin * 3);
+ result.add(new Point(pCurr.x + bx * d, pCurr.y + by * d));
+ }
+ return result;
+ }
+
+ // --- 鍩虹宸ュ叿绫绘柟娉� ---
+ private static List<Point> parseCoords(String s) {
+ List<Point> list = new ArrayList<>();
+ if(s == null || s.isEmpty()) return list;
+ for (String p : s.split(";")) {
+ String[] xy = p.split(",");
+ if (xy.length >= 2) list.add(new Point(Double.parseDouble(xy[0]), Double.parseDouble(xy[1])));
+ }
+ return list;
+ }
+
+ private static void ensureCCW(List<Point> poly) {
+ double s = 0;
+ for (int i = 0; i < poly.size(); i++) {
+ Point p1 = poly.get(i), p2 = poly.get((i + 1) % poly.size());
+ s += (p2.x - p1.x) * (p2.y + p1.y);
+ }
+ if (s > 0) Collections.reverse(poly);
+ }
+
+ private static List<Point> shrinkPolygon(List<Point> polygon, double margin) {
+ List<Point> result = new ArrayList<>();
+ int n = polygon.size();
+ for (int i = 0; i < n; i++) {
+ Point pPrev = polygon.get((i - 1 + n) % n);
+ Point pCurr = polygon.get(i);
+ Point pNext = polygon.get((i + 1) % n);
+ double d1x = pCurr.x - pPrev.x, d1y = pCurr.y - pPrev.y;
+ double l1 = Math.hypot(d1x, d1y);
+ double d2x = pNext.x - pCurr.x, d2y = pNext.y - pCurr.y;
+ double l2 = Math.hypot(d2x, d2y);
+ double n1x = -d1y / l1, n1y = d1x / l1;
+ double n2x = -d2y / l2, n2y = d2x / l2;
+ double bx = n1x + n2x, by = n1y + n2y;
+ double bLen = Math.hypot(bx, by);
+ if (bLen < EPSILON) { bx = n1x; by = n1y; } else { bx /= bLen; by /= bLen; }
+ double cosHalf = n1x * bx + n1y * by;
+ double d = margin / Math.max(cosHalf, 0.1);
+ result.add(new Point(pCurr.x + bx * d, pCurr.y + by * d));
+ }
+ return result;
+ }
+
+ private static double findOptimalScanAngle(List<Point> polygon) {
+ double minH = Double.MAX_VALUE, bestA = 0;
+ for (int i = 0; i < polygon.size(); i++) {
+ Point p1 = polygon.get(i), p2 = polygon.get((i + 1) % polygon.size());
+ double angle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
+ double h = calculatePolygonHeightAtAngle(polygon, angle);
+ if (h < minH) { minH = h; bestA = angle; }
+ }
+ return bestA;
+ }
+
+ private static double calculatePolygonHeightAtAngle(List<Point> poly, double angle) {
+ double minY = Double.MAX_VALUE, maxY = -Double.MAX_VALUE;
+ double sin = Math.sin(-angle), cos = Math.cos(-angle);
+ for (Point p : poly) {
+ double ry = p.x * sin + p.y * cos;
+ minY = Math.min(minY, ry); maxY = Math.max(maxY, ry);
+ }
+ return maxY - minY;
+ }
+
+ private static List<Double> getXIntersections(List<Point> rotatedPoly, double y) {
+ List<Double> xInts = new ArrayList<>();
+ int n = rotatedPoly.size();
+ for (int i = 0; i < n; i++) {
+ Point p1 = rotatedPoly.get(i), p2 = rotatedPoly.get((i + 1) % n);
+ if ((p1.y <= y && p2.y > y) || (p2.y <= y && p1.y > y)) {
+ xInts.add(p1.x + (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y));
+ }
+ }
+ return xInts;
+ }
+
+ private static Point getFirstScanStartPoint(List<Point> polygon, double angle, double width) {
+ List<Point> rotated = rotatePolygon(polygon, -angle);
+ double minY = Double.MAX_VALUE;
+ for (Point p : rotated) minY = Math.min(minY, p.y);
+ double startY = minY + width + EPSILON;
+ List<Double> xInts = getXIntersections(rotated, startY);
+ if (xInts.isEmpty()) return polygon.get(0);
+ Collections.sort(xInts);
+ return rotatePoint(new Point(xInts.get(0), startY), angle);
+ }
+
+ private static List<Point> alignBoundaryToStart(List<Point> polygon, Point target) {
+ int bestIdx = 0; double minDist = Double.MAX_VALUE;
+ for (int i = 0; i < polygon.size(); i++) {
+ double d = Math.hypot(polygon.get(i).x - target.x, polygon.get(i).y - target.y);
+ if (d < minDist) { minDist = d; bestIdx = i; }
+ }
+ List<Point> aligned = new ArrayList<>();
+ for (int i = 0; i < polygon.size(); i++) aligned.add(polygon.get((bestIdx + i) % polygon.size()));
+ return aligned;
+ }
+
+ private static Point rotatePoint(Point p, double angle) {
+ double c = Math.cos(angle), s = Math.sin(angle);
+ return new Point(p.x * c - p.y * s, p.x * s + p.y * c);
+ }
+
+ 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;
+ }
+}
\ No newline at end of file
diff --git a/src/lujing/MowingPathGenerationPage.java b/src/lujing/MowingPathGenerationPage.java
index bbbe001..b3df47a 100644
--- a/src/lujing/MowingPathGenerationPage.java
+++ b/src/lujing/MowingPathGenerationPage.java
@@ -606,7 +606,8 @@
// 鍑稿舰鍦板潡锛屾湁闅滅鐗� -> 璋冪敤 AoxinglujingHaveObstacel
try {
// 鍋囪 AoxinglujingHaveObstacel 鏈夌被浼肩殑鏂规硶绛惧悕
- generated = AoxinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
+ List<AoxinglujingHaveObstacel.PathSegment> segments = AoxinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
+ generated = formatAoxingHaveObstaclePathSegments(segments);
} catch (Exception e) {
// 濡傛灉绫昏繕娌℃湁瀹炵幇锛屼娇鐢ㄥ師鏉ョ殑鏂规硶浣滀负鍚庡
if (showMessages) {
@@ -788,6 +789,30 @@
}
/**
+ * 鏍煎紡鍖� AoxinglujingHaveObstacel.PathSegment 鍒楄〃涓哄潗鏍囧瓧绗︿覆
+ */
+ private String formatAoxingHaveObstaclePathSegments(List<AoxinglujingHaveObstacel.PathSegment> segments) {
+ if (segments == null || segments.isEmpty()) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ AoxinglujingHaveObstacel.Point last = null;
+ for (AoxinglujingHaveObstacel.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) {
@@ -828,6 +853,27 @@
}
sb.append(String.format(Locale.US, "%.6f,%.6f", point.x, point.y));
}
+
+ /**
+ * 姣旇緝涓や釜 AoxinglujingHaveObstacel.Point 鏄惁鐩稿悓锛堜娇鐢ㄥ皬鐨勫宸級
+ */
+ private boolean equals2D(AoxinglujingHaveObstacel.Point p1, AoxinglujingHaveObstacel.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;
+ }
+
+ /**
+ * 娣诲姞 AoxinglujingHaveObstacel.Point 鍒板瓧绗︿覆鏋勫缓鍣�
+ */
+ private void appendPoint(StringBuilder sb, AoxinglujingHaveObstacel.Point point) {
+ if (sb.length() > 0) {
+ sb.append(";");
+ }
+ sb.append(String.format(Locale.US, "%.6f,%.6f", point.x, point.y));
+ }
// ========== UI杈呭姪鏂规硶 ==========
--
Gitblit v1.10.0