张世豪
17 小时以前 a541fbdc8812337de120aad3792a2033a5dd7afe
src/lujing/MowingPathGenerationPage.java
@@ -125,32 +125,65 @@
        
        // 地块边界
        boundaryArea = createInfoTextArea(boundaryValue != null ? boundaryValue : "", true, 6);
        contentPanel.add(createTextAreaSection("地块边界", boundaryArea));
        String boundaryTitle = "地块边界";
        if (boundaryValue != null && !boundaryValue.trim().isEmpty() && !"-1".equals(boundaryValue.trim())) {
            int boundaryCount = boundaryValue.split(";").length;
            boundaryTitle = "地块边界 (" + boundaryCount + "点)";
        }
        contentPanel.add(createTextAreaSection(boundaryTitle, boundaryArea));
        
        // 障碍物坐标
        obstacleArea = createInfoTextArea(obstacleValue != null ? obstacleValue : "", true, 6);
        contentPanel.add(createTextAreaSection("障碍物坐标", obstacleArea));
        String obstacleTitle = "障碍物坐标";
        if (obstacleValue != null && !obstacleValue.trim().isEmpty() && !"-1".equals(obstacleValue.trim())) {
            // 障碍物坐标格式可能是空格分隔的多个障碍物,每个障碍物用分号分隔坐标点
            // 计算所有障碍物的总坐标点数
            String[] obstacles = obstacleValue.trim().split("\\s+");
            int totalObstaclePoints = 0;
            for (String obstacle : obstacles) {
                if (obstacle != null && !obstacle.trim().isEmpty()) {
                    totalObstaclePoints += obstacle.split(";").length;
                }
            }
            if (totalObstaclePoints > 0) {
                obstacleTitle = "障碍物坐标 (" + totalObstaclePoints + "点)";
            }
        }
        contentPanel.add(createTextAreaSection(obstacleTitle, obstacleArea));
        
        // 割草宽度
        widthField = createInfoTextField(widthValue != null ? widthValue : "", true);
        contentPanel.add(createTextFieldSection("割草宽度 (厘米)", widthField));
        contentPanel.add(createTextFieldSection("割草宽度 (cm)", widthField));
        
        // 割草安全距离(只读显示)
        // 优先从Dikuai对象获取,如果Dikuai中没有,再从Device获取
        String displaySafetyDistance = "未设置";
        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;
                    }
                    displaySafetyDistance = String.format("%.2f米", distanceMeters);
                } catch (NumberFormatException e) {
                    displaySafetyDistance = "未设置";
        String safetyDistanceValue = null;
        // 首先尝试从Dikuai对象获取
        if (dikuai != null) {
            safetyDistanceValue = dikuai.getMowingSafetyDistance();
        }
        // 如果Dikuai中没有,从Device获取
        if ((safetyDistanceValue == null || "-1".equals(safetyDistanceValue) || safetyDistanceValue.trim().isEmpty())) {
            Device device = Device.getActiveDevice();
            if (device != null) {
                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;
                }
                displaySafetyDistance = String.format(Locale.US, "%.2fm", distanceMeters);
            } catch (NumberFormatException e) {
                displaySafetyDistance = "未设置";
            }
        }
        contentPanel.add(createInfoValueSection("割草安全距离", displaySafetyDistance));
@@ -162,7 +195,12 @@
        String existingPath = prepareCoordinateForEditor(dikuai.getPlannedPath());
        String pathSeed = initialGeneratedPath != null ? initialGeneratedPath : existingPath;
        pathArea = createInfoTextArea(pathSeed != null ? pathSeed : "", true, 10);
        contentPanel.add(createTextAreaSection("割草路径坐标", pathArea));
        String pathTitle = "割草路径坐标";
        if (pathSeed != null && !pathSeed.trim().isEmpty() && !"-1".equals(pathSeed.trim())) {
            int pathCount = pathSeed.split(";").length;
            pathTitle = "割草路径坐标 (" + pathCount + "点)";
        }
        contentPanel.add(createTextAreaSection(pathTitle, pathArea));
        
        JScrollPane dialogScrollPane = new JScrollPane(contentPanel);
        dialogScrollPane.setBorder(BorderFactory.createEmptyBorder());
@@ -372,7 +410,7 @@
        String rawWidthInput = widthField.getText() != null ? widthField.getText().trim() : "";
        String widthSanitized = sanitizeWidthString(widthField.getText());
        if (widthSanitized == null) {
            String message = rawWidthInput.isEmpty() ? "请先设置割草宽度(厘米)" : "割草宽度格式不正确";
                String message = rawWidthInput.isEmpty() ? "请先设置割草宽度(cm)" : "割草宽度格式不正确";
            JOptionPane.showMessageDialog(this, message, "提示", JOptionPane.WARNING_MESSAGE);
            return;
        }
@@ -459,7 +497,7 @@
        String widthStr = sanitizeWidthString(widthCmInput);
        if (widthStr == null) {
            if (showMessages) {
                String message = rawWidth.isEmpty() ? "请先设置割草宽度(厘米)" : "割草宽度格式不正确";
                String message = rawWidth.isEmpty() ? "请先设置割草宽度(cm)" : "割草宽度格式不正确";
                JOptionPane.showMessageDialog(parentComponent, message, "提示", JOptionPane.WARNING_MESSAGE);
            }
            return null;
@@ -540,9 +578,11 @@
                    // 异形地块,无障碍物 -> 调用 YixinglujingNoObstacle
                    // 注意:如果该类还没有实现,这里会抛出异常或返回null
                    try {
                        // 假设 YixinglujingNoObstacle 有类似的方法签名
                        // 如果类还没有实现,可能需要使用原来的方法作为后备
                        generated = YixinglujingNoObstacle.planPath(boundary, plannerWidth, safetyMarginStr);
                        // 调用 YixinglujingNoObstacle.planPath 获取路径段列表
                        List<YixinglujingNoObstacle.PathSegment> segments =
                            YixinglujingNoObstacle.planPath(boundary, plannerWidth, safetyMarginStr);
                        // 格式化路径段列表为字符串
                        generated = formatYixingPathSegments(segments);
                    } catch (Exception e) {
                        // 如果类还没有实现,使用原来的方法作为后备
                        if (showMessages) {
@@ -662,25 +702,38 @@
    
    /**
     * 获取安全距离字符串(米)
     * 优先从Dikuai对象获取,如果Dikuai中没有,再从Device获取
     */
    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) {
                    // 解析失败,返回null,使用默认值
        String safetyDistanceValue = null;
        // 首先尝试从Dikuai对象获取
        if (dikuai != null) {
            safetyDistanceValue = dikuai.getMowingSafetyDistance();
        }
        // 如果Dikuai中没有,从Device获取
        if ((safetyDistanceValue == null || "-1".equals(safetyDistanceValue) || safetyDistanceValue.trim().isEmpty())) {
            Device device = Device.getActiveDevice();
            if (device != null) {
                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) {
                // 解析失败,返回null,使用默认值
            }
        }
        return null;
@@ -711,6 +764,30 @@
    }
    
    /**
     * 格式化 YixinglujingNoObstacle.PathSegment 列表为坐标字符串
     */
    private String formatYixingPathSegments(List<YixinglujingNoObstacle.PathSegment> segments) {
        if (segments == null || segments.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        YixinglujingNoObstacle.Point last = null;
        for (YixinglujingNoObstacle.PathSegment segment : segments) {
            // 只添加割草工作段,跳过过渡段
            if (segment.isMowing) {
                // 如果起点与上一个终点不同,添加起点
                if (last == null || !equalsYixingPoint(last, segment.start)) {
                    appendYixingPoint(sb, segment.start);
                }
                // 添加终点
                appendYixingPoint(sb, segment.end);
                last = segment.end;
            }
        }
        return sb.toString();
    }
    /**
     * 比较两个点是否相同(使用小的容差)
     */
    private boolean equals2D(AoxinglujingNoObstacle.Point p1, AoxinglujingNoObstacle.Point p2) {
@@ -731,6 +808,27 @@
        sb.append(String.format(Locale.US, "%.6f,%.6f", point.x, point.y));
    }
    
    /**
     * 比较两个 YixinglujingNoObstacle.Point 是否相同(使用小的容差)
     */
    private boolean equalsYixingPoint(YixinglujingNoObstacle.Point p1, YixinglujingNoObstacle.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;
    }
    /**
     * 添加 YixinglujingNoObstacle.Point 到字符串构建器
     */
    private void appendYixingPoint(StringBuilder sb, YixinglujingNoObstacle.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) {