张世豪
2025-12-05 2144172c7b961d4112850692ed77b46f1ae5d373
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package gecaoji; // 包声明
 
import java.awt.BasicStroke; // 引入基础描边类
import java.awt.Color; // 引入颜色类
import java.awt.Graphics2D; // 引入2D图形上下文
import java.awt.Stroke; // 引入描边接口
import java.awt.geom.Path2D; // 引入路径绘制类
import java.awt.geom.Point2D; // 引入二维点类
import java.util.ArrayList; // 引入动态数组
import java.util.List; // 引入列表接口
 
/** // 文档注释开头
 * Helper class to parse and render planned mowing paths. // 辅助类说明
 */ // 文档注释结尾
public final class lujingdraw { // 类定义,防止继承
    private static final Color PATH_COLOR = new Color(255, 0, 0, 220); // 路径颜色(红色半透明)
    private static final Color START_POINT_COLOR = new Color(0, 0, 0, 220); // 起点箭头颜色
    private static final Color END_POINT_COLOR = new Color(0, 0, 0, 220); // 终点箭头颜色
 
    private lujingdraw() { // 私有构造防止实例化
    } // 空实现
 
    /** // 文档注释开始
     * Parse the planned path string (semicolon-separated "x,y" pairs) into a list of points in meters. // 方法说明
     */ // 文档注释结束
    public static List<Point2D.Double> parsePlannedPath(String plannedPath) { // 解析路径字符串
        List<Point2D.Double> points = new ArrayList<>(); // 存放解析后的点集
        if (plannedPath == null) { // 判空
            return points; // 返回空集合
        } // if结束
 
        String normalized = plannedPath.trim(); // 去除首尾空格
        if (normalized.isEmpty() || "-1".equals(normalized)) { // 无效判断
            return points; // 返回空集合
        } // if结束
 
        int commentIndex = normalized.indexOf('#'); // 查找注释符号
        if (commentIndex >= 0) { // 找到注释
            normalized = normalized.substring(0, commentIndex).trim(); // 截取注释前内容
        } // if结束
 
        if (normalized.isEmpty()) { // 再次判空
            return points; // 返回空集合
        } // if结束
 
        String[] segments = normalized.split(";"); // 切分各段
        for (String segment : segments) { // 遍历每段
            if (segment == null) { // 判空
                continue; // 跳过
            } // if结束
            String trimmed = segment.trim(); // 去除空格
            if (trimmed.isEmpty()) { // 空段
                continue; // 跳过
            } // if结束
            String[] parts = trimmed.split(","); // 切分坐标
            if (parts.length < 2) { // 检查长度
                continue; // 跳过
            } // if结束
            try { // 数值解析
                double x = Double.parseDouble(parts[0].trim()); // 解析X坐标
                double y = Double.parseDouble(parts[1].trim()); // 解析Y坐标
                points.add(new Point2D.Double(x, y)); // 加入点集
            } catch (NumberFormatException ignored) { // 捕获格式异常
                // Ignore malformed coordinates // 忽略非法坐标
            } // try结束
        } // for结束
        return points; // 返回结果
    } // 方法结束
 
    /** // 文档注释开始
     * Draw the planned mowing path. // 绘制路径
     */ // 文档注释结束
    public static void drawPlannedPath(Graphics2D g2d, List<Point2D.Double> path, double scale) { // 绘制主方法
        if (path == null || path.size() < 2) { // 判定点数
            return; // 数据不足直接返回
        } // if结束
 
        Path2D polyline = new Path2D.Double(); // 创建折线
        boolean move = true; // 首段标记
        for (Point2D.Double point : path) { // 遍历点集
            if (move) { // 第一段
                polyline.moveTo(point.x, point.y); // 移动到首点
                move = false; // 更新标记
            } else { // 后续段
                polyline.lineTo(point.x, point.y); // 连线到下一点
            } // if结束
        } // for结束
 
        Stroke previous = g2d.getStroke(); // 保存原描边
        float strokeWidth = (float) (2.5 / Math.max(0.5, scale)); // 根据缩放计算线宽
        g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); // 设置圆头圆角描边
        g2d.setColor(PATH_COLOR); // 设置线路颜色
        g2d.draw(polyline); // 绘制折线
 
        Point2D.Double start = path.get(0); // 起点
        Point2D.Double second = path.get(1); // 第二个点
        Point2D.Double end = path.get(path.size() - 1); // 终点
        Point2D.Double prev = path.get(path.size() - 2); // 倒数第二个点
 
        drawArrowMarker(g2d, start, second, START_POINT_COLOR, scale); // 绘制起点箭头
        drawArrowMarker(g2d, prev, end, END_POINT_COLOR, scale); // 绘制终点箭头
 
        g2d.setStroke(previous); // 恢复原描边
    } // 方法结束
 
    private static void drawArrowMarker(Graphics2D g2d, Point2D.Double from, Point2D.Double to, Color color, double scale) { // 绘制箭头辅助
        if (from == null || to == null) { // 判空
            return; // 数据不足返回
        } // if结束
        double dx = to.x - from.x; // 计算X增量
        double dy = to.y - from.y; // 计算Y增量
        double length = Math.hypot(dx, dy); // 计算向量长度
        if (length == 0) { // 防止零长度
            return; // 无方向返回
        } // if结束
 
    double arrowLength = Math.max(2.5, 5.5 / Math.max(0.5, scale)); // 计算箭头长度
    arrowLength *= 0.5; // 缩小箭头尺寸为原来的一半
        double arrowWidth = arrowLength * 0.45; // 计算箭头宽度
 
        double ux = dx / length; // 单位向量X
        double uy = dy / length; // 单位向量Y
 
        double tipX = to.x; // 箭头尖端X
        double tipY = to.y; // 箭头尖端Y
        double baseX = tipX - ux * arrowLength; // 箭头底部X
        double baseY = tipY - uy * arrowLength; // 箭头底部Y
 
        double perpX = -uy; // 垂直向量X
        double perpY = ux; // 垂直向量Y
 
        double leftX = baseX + perpX * arrowWidth * 0.5; // 左侧点X
        double leftY = baseY + perpY * arrowWidth * 0.5; // 左侧点Y
        double rightX = baseX - perpX * arrowWidth * 0.5; // 右侧点X
        double rightY = baseY - perpY * arrowWidth * 0.5; // 右侧点Y
 
        Path2D arrow = new Path2D.Double(); // 创建箭头路径
        arrow.moveTo(tipX, tipY); // 起点移动到尖端
        arrow.lineTo(leftX, leftY); // 连到左侧
        arrow.lineTo(rightX, rightY); // 连到右侧
        arrow.closePath(); // 闭合形成三角形
 
        g2d.setColor(color); // 设置颜色
        g2d.fill(arrow); // 填充箭头
    } // 方法结束
} // 类结束