From 5ae9bbe3583384afab8eb95a134ccb74aee6487a Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期四, 25 十二月 2025 13:46:38 +0800
Subject: [PATCH] 曾加修改密码功能

---
 src/lujing/AoxinglujingNoObstacle.java |  545 +++++++++++++++++++++++------------------------------
 1 files changed, 239 insertions(+), 306 deletions(-)

diff --git a/src/lujing/AoxinglujingNoObstacle.java b/src/lujing/AoxinglujingNoObstacle.java
index 95f5f96..4bdf628 100644
--- a/src/lujing/AoxinglujingNoObstacle.java
+++ b/src/lujing/AoxinglujingNoObstacle.java
@@ -5,381 +5,314 @@
 import java.util.List;
 
 /**
- * 鏃犻殰纰嶇墿鍑稿舰鑽夊湴璺緞瑙勫垝绫� (缁堟瀬浼樺寲鐗�)
- * 鐗规�э細
- * 1. 鏈�灏忔姇褰卞搴︽柟鍚戦�夋嫨 (鏁堢巼鏈�楂橈紝杞集鏈�灏�)
- * 2. 杈圭紭杞粨浼樺厛鍒囧壊 (鏃犳瑙掕鐩�)
- * 3. 鏀寔澶栭儴浼犲叆宸插寘鍚噸鍙犵巼鐨勫搴﹀弬鏁�
+ * 鍑稿舰鑽夊湴璺緞瑙勫垝 (鍥磋竟浼樺寲鐗�)
+ * 浼樺寲閲嶇偣锛氬洿杈瑰潗鏍囧榻愭壂鎻忚捣鐐广�佸叏璺緞杩炶疮鎬�
  */
 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 {
         public double x, y;
-
-        public Point(double x, double y) {
-            this.x = x;
-            this.y = y;
-        }
-
+        public Point(double x, double y) { this.x = x; this.y = y; }
         @Override
-        public String toString() {
-            return String.format("(%.4f, %.4f)", x, y);
+        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;
         }
     }
 
     /**
-     * 瀵瑰鍏紑鐨勯潤鎬佽皟鐢ㄦ柟娉�
-     *
-     * @param boundaryCoordsStr 鍦板潡杈圭晫鍧愭爣瀛楃涓� "x1,y1;x2,y2;..."
-     * @param mowingWidthStr    鏈夋晥鍓茶崏瀹藉害瀛楃涓� (宸插寘鍚噸鍙犵巼)锛屽 "0.30"
-     * @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;
+        double width = Double.parseDouble(mowingWidthStr);
+        double margin = Double.parseDouble(safetyMarginStr);
 
-        try {
-            mowingWidth = Double.parseDouble(mowingWidthStr);
-            safetyMargin = Double.parseDouble(safetyMarginStr);
-        } catch (NumberFormatException e) {
-            throw new IllegalArgumentException("鍓茶崏瀹藉害鎴栧畨鍏ㄨ竟璺濇牸寮忛敊璇�");
-        }
-
-        // 2. 璋冪敤鏍稿績绠楁硶
-        return planPathCore(originalPolygon, mowingWidth, safetyMargin);
+        return planPathCore(originalPolygon, width, margin);
     }
 
     /**
-     * 鏍稿績绠楁硶閫昏緫
+     * 鏍稿績璺緞瑙勫垝閫昏緫
+     * 
+     * @param originalPolygon 鍘熷澶氳竟褰㈤《鐐瑰垪琛�
+     * @param width 鍓茶崏瀹藉害
+     * @param margin 瀹夊叏杈硅窛
+     * @return 瑙勫垝濂界殑璺緞娈靛垪琛�
      */
-    private static List<PathSegment> planPathCore(List<Point> originalPolygon, double width, double safetyMargin) {
-        if (originalPolygon == null || originalPolygon.size() < 3) {
-            return new ArrayList<>(); // 鎴栨姏鍑哄紓甯革紝瑙嗕笟鍔¢渶姹傝�屽畾
-        }
+    private static List<PathSegment> planPathCore(List<Point> originalPolygon, double width, double margin) {
+        if (originalPolygon.size() < 3) return new ArrayList<>();
 
-        // 纭繚澶氳竟褰㈡槸閫嗘椂閽堟柟鍚�
+        // 1. 纭繚閫嗘椂閽堝苟杩涜瀹夊叏鍐呯缉
         ensureCCW(originalPolygon);
+        List<Point> workArea = shrinkPolygon(originalPolygon, margin);
+        if (workArea.size() < 3) return new ArrayList<>();
 
-        // 1. 鏍规嵁瀹夊叏杈硅窛鍐呯缉锛屽緱鍒板疄闄呬綔涓氬尯鍩�
-        List<Point> workAreaPolygon = shrinkPolygon(originalPolygon, safetyMargin);
+        // 2. 棰勮绠楁渶浼樿搴﹀拰濉厖璺緞鐨勭涓�涓偣
+        double bestAngle = findOptimalScanAngle(workArea);
+        Point firstScanStart = getFirstScanStartPoint(workArea, bestAngle, width);
 
-        // 濡傛灉鍐呯缉鍚庡尯鍩熷け鏁堬紙濡傚湴鍧楀お灏忥級锛岃繑鍥炵┖璺緞
-        if (workAreaPolygon.size() < 3) {
-            return new ArrayList<>();
-        }
+        // 3. 瀵归綈鍥磋竟璧风偣锛氳鍥磋竟鐨勬渶鍚庝竴涓偣鍒氬ソ杩炴帴鎵弿濉厖鐨勮捣鐐�
+        List<Point> alignedWorkArea = alignBoundaryToStart(workArea, firstScanStart);
 
         List<PathSegment> finalPath = new ArrayList<>();
 
-        // 2. [浼樺寲] 浼樺厛鐢熸垚杞粨璺緞 (Contour Pass)
-        // 娌夸綔涓氳竟鐣岃蛋涓�鍦堬紝纭繚杈圭紭鏁撮綈涓旀棤閬楁紡
-        addContourPath(workAreaPolygon, finalPath);
-
-        // 3. [浼樺寲] 璁$畻鏈�浣虫壂鎻忚搴�
-        // 瀵绘壘璁╁杈瑰舰鎶曞奖楂樺害鏈�灏忕殑瑙掑害锛屼粠鑰屾渶灏忓寲杞集娆℃暟
-        double bestAngle = findOptimalScanAngle(workAreaPolygon);
-
-        // 4. 鐢熸垚鍐呴儴寮撳瓧褰㈣矾寰�
-        // 鐩存帴浣跨敤浼犲叆鐨� width (宸插寘鍚噸鍙犵巼)
-        List<PathSegment> zigZagPaths = generateClippedMowingLines(workAreaPolygon, bestAngle, width);
-
-        // 5. 杩炴帴杞粨璺緞鍜屽紦瀛楀舰璺緞
-        if (!finalPath.isEmpty() && !zigZagPaths.isEmpty()) {
-            Point contourEnd = finalPath.get(finalPath.size() - 1).end;
-            Point zigzagStart = zigZagPaths.get(0).start;
-            
-            // 濡傛灉杞粨缁堢偣涓庡紦瀛楀舰璧风偣涓嶉噸鍚堬紝娣诲姞杩囨浮娈�
-            if (distanceSq(contourEnd, zigzagStart) > EPSILON) {
-                finalPath.add(new PathSegment(contourEnd, zigzagStart, false));
-            }
+        // 4. 銆愮涓�闃舵銆戞坊鍔犲洿杈瑰潗鏍囪矾寰�
+        for (int i = 0; i < alignedWorkArea.size(); i++) {
+            Point p1 = alignedWorkArea.get(i);
+            Point p2 = alignedWorkArea.get((i + 1) % alignedWorkArea.size());
+            finalPath.add(new PathSegment(p1, p2, true));
         }
 
-        // 6. 鍚堝苟寮撳瓧褰㈣矾寰�
-        finalPath.addAll(connectPathSegments(zigZagPaths));
+        // 5. 銆愮浜岄樁娈点�戠敓鎴愬唴閮ㄥ紦瀛楀舰璺緞
+        // 浠庡洿杈归棴鍚堢偣锛坅lignedWorkArea.get(0)锛夊紑濮嬭繛鎺�
+        Point currentPos = alignedWorkArea.get(0);
+        List<PathSegment> zigZagLines = generateZigZagPath(workArea, bestAngle, width, currentPos);
+        
+        finalPath.addAll(zigZagLines);
 
         return finalPath;
     }
 
-    // ================= 鏍稿績閫昏緫杈呭姪鏂规硶 =================
-
     /**
-     * 娣诲姞杞粨璺緞 (鍥寸潃澶氳竟褰㈣蛋涓�鍦�)
+     * 瀵绘壘寮撳瓧褰㈢殑绗竴鏉$嚎鐨勮捣鐐�
+     * 
+     * @param polygon 澶氳竟褰㈤《鐐瑰垪琛�
+     * @param angle 鎵弿瑙掑害
+     * @param width 鍓茶崏瀹藉害
+     * @return 鎵弿璧风偣鐨勫潗鏍�
      */
-    private static void addContourPath(List<Point> polygon, List<PathSegment> path) {
-        int n = polygon.size();
-        for (int i = 0; i < n; i++) {
-            Point p1 = polygon.get(i);
-            Point p2 = polygon.get((i + 1) % n);
-            path.add(new PathSegment(p1, p2, true));
-        }
-    }
-
-    /**
-     * 瀵绘壘鏈�浼樻壂鎻忚搴� (鏈�灏忔姇褰遍珮搴︽硶)
-     */
-    private static double findOptimalScanAngle(List<Point> polygon) {
-        double minHeight = Double.MAX_VALUE;
-        double bestAngle = 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 currentAngle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
-
-            // 璁$畻鍦ㄨ繖涓搴︿笅鐨勬姇褰遍珮搴�
-            double height = calculatePolygonHeightAtAngle(polygon, currentAngle);
-
-            if (height < minHeight) {
-                minHeight = height;
-                bestAngle = currentAngle;
-            }
-        }
-        return bestAngle;
-    }
-
-    /**
-     * 璁$畻澶氳竟褰㈠湪鐗瑰畾鏃嬭浆瑙掑害涓嬬殑Y杞存姇褰遍珮搴�
-     */
-    private static double calculatePolygonHeightAtAngle(List<Point> poly, double angle) {
+    private static Point getFirstScanStartPoint(List<Point> polygon, double angle, double width) {
+        List<Point> rotated = rotatePolygon(polygon, -angle);
         double minY = Double.MAX_VALUE;
-        double maxY = -Double.MAX_VALUE;
+        for (Point p : rotated) minY = Math.min(minY, p.y);
 
-        double cos = Math.cos(-angle);
-        double sin = Math.sin(-angle);
-
-        for (Point p : poly) {
-            // 鍙渶璁$畻鏃嬭浆鍚庣殑Y鍧愭爣
-            double rotatedY = p.x * sin + p.y * cos;
-            if (rotatedY < minY) minY = rotatedY;
-            if (rotatedY > maxY) maxY = rotatedY;
-        }
-        return maxY - minY;
+        double startY = minY + width + EPSILON;
+        List<Double> xIntersections = getXIntersections(rotated, startY);
+        if (xIntersections.isEmpty()) return polygon.get(0);
+        
+        Collections.sort(xIntersections);
+        return rotatePoint(new Point(xIntersections.get(0), startY), 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;
-        }
-
-        // 璧峰鎵弿绾夸綅缃細
-        // 浠� minY + width/2 寮�濮嬶紝鍥犱负涔嬪墠宸茬粡璧颁簡杞粨绾�(Contour Pass)銆�
-        // 杞粨绾胯礋璐f竻鐞嗚竟缂樺尯鍩燂紝鍐呴儴濉厖绾夸繚鎸� width 鐨勯棿璺濆嵆鍙��
-        // 鍔犱笂 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);
-
-                // 蹇界暐姘村钩绾挎
-                if (Math.abs(p1.y - p2.y) < EPSILON) continue;
-
-                double minP = Math.min(p1.y, p2.y);
-                double maxP = Math.max(p1.y, p2.y);
-
-                if (currentY >= minP && currentY < maxP) {
-                    double x = p1.x + (currentY - p1.y) * (p2.x - p1.x) / (p2.y - p1.y);
-                    xIntersections.add(x);
-                }
+    /**
+     * 閲嶇粍澶氳竟褰㈤《鐐癸紝浣垮緱绱㈠紩0鐨勭偣鏈�闈犺繎濉厖璧风偣
+     * 
+     * @param polygon 澶氳竟褰㈤《鐐瑰垪琛�
+     * @param target 鐩爣鐐癸紙濉厖璧风偣锛�
+     * @return 閲嶇粍鍚庣殑澶氳竟褰㈤《鐐瑰垪琛�
+     */
+    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;
             }
-
-            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);
-                    Point rEnd = rotatePoint(new Point(xEnd, currentY), angle);
-                    segments.add(new PathSegment(rStart, rEnd, true));
-                }
-            }
-            // 姝ヨ繘
-            currentY += width;
         }
-
-        return segments;
+        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 List<PathSegment> connectPathSegments(List<PathSegment> lines) {
+    /**
+     * 鐢熸垚寮撳瓧褰㈡壂鎻忚矾寰�
+     * 
+     * @param polygon 澶氳竟褰㈤《鐐瑰垪琛�
+     * @param angle 鎵弿瑙掑害
+     * @param width 鍓茶崏瀹藉害
+     * @param startPoint 璧峰鐐�
+     * @return 寮撳瓧褰㈣矾寰勬鍒楄〃
+     */
+    private static List<PathSegment> generateZigZagPath(List<Point> polygon, double angle, double width, Point startPoint) {
         List<PathSegment> result = new ArrayList<>();
-        if (lines.isEmpty()) return result;
+        List<Point> rotated = rotatePolygon(polygon, -angle);
 
-        for (int i = 0; i < lines.size(); i++) {
-            PathSegment currentLine = lines.get(i);
-            Point actualStart, actualEnd;
+        double minY = Double.MAX_VALUE, maxY = -Double.MAX_VALUE;
+        for (Point p : rotated) {
+            minY = Math.min(minY, p.y);
+            maxY = Math.max(maxY, p.y);
+        }
 
-            // 寮撳瓧褰㈣鍒掞細鍋舵暟琛屾鍚戯紝濂囨暟琛屽弽鍚�
-            if (i % 2 == 0) {
-                actualStart = currentLine.start;
-                actualEnd = currentLine.end;
-            } else {
-                actualStart = currentLine.end;
-                actualEnd = currentLine.start;
+        Point currentPos = startPoint;
+        boolean leftToRight = true;
+        // 璧风偣浠� minY + width 寮�濮嬶紝鍥犱负杈圭紭宸茬粡鍥磋竟鍓茶繃
+        for (double y = minY + width; y < maxY - width / 2; y += width) {
+            List<Double> xInt = getXIntersections(rotated, y);
+            if (xInt.size() < 2) continue;
+            Collections.sort(xInt);
+
+            double xStart = leftToRight ? xInt.get(0) : xInt.get(xInt.size() - 1);
+            double xEnd = leftToRight ? xInt.get(xInt.size() - 1) : xInt.get(0);
+
+            Point pS = rotatePoint(new Point(xStart, y), angle);
+            Point pE = rotatePoint(new Point(xEnd, y), angle);
+
+            // 娣诲姞杩囨浮娈� (濡傛灉鏄粠鍥磋竟鍒囨崲杩囨潵鎴栬�呮崲琛�)
+            if (Math.hypot(currentPos.x - pS.x, currentPos.y - pS.y) > 0.05) {
+                result.add(new PathSegment(currentPos, pS, false));
             }
-
-            // 娣诲姞杩囨浮娈�
-            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));
+            result.add(new PathSegment(pS, pE, true));
+            currentPos = pE;
+            leftToRight = !leftToRight;
         }
         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;
-    }
-
-    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);
-        }
-        if (sum > 0) {
-            Collections.reverse(polygon);
-        }
-    }
-
-    private static List<Point> shrinkPolygon(List<Point> polygon, double margin) {
-        List<Point> newPoints = new ArrayList<>();
-        int n = polygon.size();
-
+    /**
+     * 鑾峰彇鎵弿绾夸笌澶氳竟褰㈢殑浜ょ偣X鍧愭爣鍒楄〃
+     * 
+     * @param rotatedPoly 鏃嬭浆鍚庣殑澶氳竟褰�
+     * @param y 鎵弿绾跨殑Y鍧愭爣
+     * @return 浜ょ偣X鍧愭爣鍒楄〃
+     */
+    private static List<Double> getXIntersections(List<Point> rotatedPoly, double y) {
+        List<Double> xIntersections = new ArrayList<>();
+        int n = rotatedPoly.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);
+            Point p1 = rotatedPoly.get(i);
+            Point p2 = rotatedPoly.get((i + 1) % n);
+            if ((p1.y <= y && p2.y > y) || (p2.y <= y && p1.y > y)) {
+                double x = p1.x + (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y);
+                xIntersections.add(x);
             }
         }
-        return newPoints;
+        return xIntersections;
     }
 
-    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; }
+    // --- 鍑犱綍鍩虹宸ュ叿 ---
+
+    /**
+     * 澶氳竟褰㈠唴缂╋紙璁$畻瀹夊叏宸ヤ綔鍖哄煙锛�
+     * 
+     * @param polygon 鍘熷澶氳竟褰�
+     * @param margin 鍐呯缉璺濈
+     * @return 鍐呯缉鍚庣殑澶氳竟褰�
+     */
+    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 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);
-
-        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);
+    /**
+     * 瀵绘壘鏈�浼樻壂鎻忚搴︼紙浣挎壂鎻忕嚎鏁伴噺鏈�灏戯級
+     * 
+     * @param polygon 澶氳竟褰�
+     * @return 鏈�浼樿搴︼紙寮у害锛�
+     */
+    private static double findOptimalScanAngle(List<Point> polygon) {
+        double minH = Double.MAX_VALUE;
+        double 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 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);
+    /**
+     * 璁$畻澶氳竟褰㈠湪鐗瑰畾瑙掑害涓嬬殑楂樺害锛堟姇褰遍暱搴︼級
+     * 
+     * @param poly 澶氳竟褰�
+     * @param angle 瑙掑害
+     * @return 楂樺害
+     */
+    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;
     }
 
+    /**
+     * 鏃嬭浆鐐�
+     * 
+     * @param p 鐐�
+     * @param angle 鏃嬭浆瑙掑害
+     * @return 鏃嬭浆鍚庣殑鐐�
+     */
     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);
+        double c = Math.cos(angle), s = Math.sin(angle);
+        return new Point(p.x * c - p.y * s, p.x * s + p.y * c);
     }
 
+    /**
+     * 鏃嬭浆澶氳竟褰�
+     * 
+     * @param poly 澶氳竟褰�
+     * @param angle 鏃嬭浆瑙掑害
+     * @return 鏃嬭浆鍚庣殑澶氳竟褰�
+     */
     private static List<Point> rotatePolygon(List<Point> poly, double angle) {
         List<Point> res = new ArrayList<>();
-        for (Point p : poly) {
-            res.add(rotatePoint(p, angle));
-        }
+        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);
+    /**
+     * 纭繚澶氳竟褰㈤《鐐逛负閫嗘椂閽堥『搴�
+     * 
+     * @param poly 澶氳竟褰�
+     */
+    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);
+    }
+
+    /**
+     * 瑙f瀽鍧愭爣瀛楃涓�
+     * 
+     * @param s 鍧愭爣瀛楃涓� (鏍煎紡: "x1,y1;x2,y2;...")
+     * @return 鐐瑰垪琛�
+     */
+    private static List<Point> parseCoords(String s) {
+        List<Point> list = new ArrayList<>();
+        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;
     }
 }
\ No newline at end of file

--
Gitblit v1.10.0