| | |
| | | package gecaoji; // 包声明 |
| | | |
| | | import java.awt.BasicStroke; // 引入基础描边类 |
| | | import java.awt.Color; // 引入颜色类 |
| | | import java.awt.Graphics2D; // 引入2D图形上下文 |
| | |
| | | import java.awt.geom.Point2D; // 引入二维点类 |
| | | import java.util.ArrayList; // 引入动态数组 |
| | | import java.util.List; // 引入列表接口 |
| | | import lujing.Lunjingguihua; // 引入路径规划类 |
| | | import org.locationtech.jts.geom.Coordinate; // 引入坐标类 |
| | | import org.locationtech.jts.geom.GeometryFactory; // 引入几何工厂类 |
| | | import org.locationtech.jts.geom.Polygon; // 引入多边形类 |
| | |
| | | // 蓝色 - 用于非作业移动路径 |
| | | private static final Color TRAVEL_PATH_COLOR = new Color(0, 0, 255); |
| | | // 虚线样式 - 用于非作业移动路径 |
| | | private static final float[] DASH_PATTERN = {10.0f, 5.0f}; |
| | | private static final float[] DASH_PATTERN = {5.0f, 5.0f}; |
| | | 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); // 终点箭头颜色 |
| | | |
| | |
| | | drawInnerBoundary(g2d, boundaryCoords, safetyDistance, scale); |
| | | } |
| | | |
| | | // 2. 尝试重新生成路径段以区分作业路径和移动路径 |
| | | List<Lunjingguihua.PathSegment> segments = null; |
| | | if (boundaryCoords != null && mowingWidth != null) { |
| | | try { |
| | | // 解析割草模式 |
| | | String mode = "parallel"; // 默认平行模式 |
| | | if (mowingPattern != null && !mowingPattern.trim().isEmpty()) { |
| | | String pattern = mowingPattern.trim().toLowerCase(); |
| | | if ("1".equals(pattern) || "spiral".equals(pattern) || "螺旋式".equals(pattern) || "螺旋".equals(pattern)) { |
| | | mode = "spiral"; |
| | | } else if ("parallel".equals(pattern) || "平行线".equals(pattern) || "平行".equals(pattern)) { |
| | | mode = "parallel"; |
| | | } |
| | | } |
| | | segments = Lunjingguihua.generatePathSegments( |
| | | boundaryCoords, |
| | | obstaclesCoords != null ? obstaclesCoords : "", |
| | | mowingWidth, |
| | | safetyDistance, |
| | | mode |
| | | ); |
| | | } catch (Exception e) { |
| | | // 如果重新生成失败,使用简单绘制方式 |
| | | segments = null; |
| | | } |
| | | } |
| | | // 2. 直接绘制路径(不再重新生成) |
| | | 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结束 |
| | | |
| | | // 3. 根据是否有段信息选择不同的绘制方式 |
| | | if (segments != null && !segments.isEmpty()) { |
| | | // 有段信息:分别绘制作业路径和移动路径 |
| | | drawPathSegments(g2d, segments, scale, arrowScale); |
| | | } else { |
| | | // 无段信息:使用简单绘制方式(所有路径使用作业路径颜色) |
| | | 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结束 |
| | | |
| | | g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); // 设置圆头圆角描边 |
| | | g2d.setColor(MOWING_PATH_COLOR); // 设置作业路径颜色(提香红70%透明度) |
| | | 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); // 倒数第二个点 |
| | | g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); // 设置圆头圆角描边 |
| | | g2d.setColor(MOWING_PATH_COLOR); // 设置作业路径颜色(提香红70%透明度) |
| | | 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, arrowScale); // 绘制起点箭头 |
| | | drawArrowMarker(g2d, prev, end, END_POINT_COLOR, scale, arrowScale); // 绘制终点箭头 |
| | | } |
| | | drawArrowMarker(g2d, start, second, START_POINT_COLOR, scale, arrowScale); // 绘制起点箭头 |
| | | drawArrowMarker(g2d, prev, end, END_POINT_COLOR, scale, arrowScale); // 绘制终点箭头 |
| | | |
| | | g2d.setStroke(previous); // 恢复原描边 |
| | | } // 方法结束 |
| | |
| | | */ // 文档注释结束 |
| | | private static void drawInnerBoundary(Graphics2D g2d, String boundaryCoords, String safetyDistanceStr, double scale) { |
| | | try { |
| | | List<Coordinate> boundary = Lunjingguihua.parseCoordinates(boundaryCoords); |
| | | List<Coordinate> boundary = parseCoordinates(boundaryCoords); |
| | | if (boundary.size() < 4) { |
| | | return; // 边界点不足 |
| | | } |
| | |
| | | } |
| | | } // 方法结束 |
| | | |
| | | /** // 文档注释开始 |
| | | * 绘制路径段(区分作业路径和移动路径) |
| | | */ // 文档注释结束 |
| | | private static void drawPathSegments(Graphics2D g2d, List<Lunjingguihua.PathSegment> segments, double scale, double arrowScale) { |
| | | if (segments == null || segments.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | float strokeWidth = (float) (2.5 / Math.max(0.5, scale)); |
| | | Stroke previous = g2d.getStroke(); |
| | | |
| | | // 分别绘制作业路径和移动路径 |
| | | Path2D.Double mowingPath = new Path2D.Double(); |
| | | Path2D.Double travelPath = new Path2D.Double(); |
| | | boolean mowingStarted = false; |
| | | boolean travelStarted = false; |
| | | |
| | | Coordinate lastMowingEnd = null; |
| | | Coordinate lastTravelEnd = null; |
| | | |
| | | for (Lunjingguihua.PathSegment seg : segments) { |
| | | if (seg == null || seg.start == null || seg.end == null) { |
| | | continue; |
| | | } |
| | | |
| | | if (seg.isMowing) { |
| | | // 作业路径 - 提香红70%透明度 |
| | | if (!mowingStarted || lastMowingEnd == null || !equals2D(lastMowingEnd, seg.start)) { |
| | | mowingPath.moveTo(seg.start.x, seg.start.y); |
| | | mowingStarted = true; |
| | | } |
| | | mowingPath.lineTo(seg.end.x, seg.end.y); |
| | | lastMowingEnd = seg.end; |
| | | } else { |
| | | // 移动路径 - 蓝色虚线 |
| | | if (!travelStarted || lastTravelEnd == null || !equals2D(lastTravelEnd, seg.start)) { |
| | | travelPath.moveTo(seg.start.x, seg.start.y); |
| | | travelStarted = true; |
| | | } |
| | | travelPath.lineTo(seg.end.x, seg.end.y); |
| | | lastTravelEnd = seg.end; |
| | | } |
| | | } |
| | | |
| | | // 绘制作业路径 |
| | | if (mowingStarted) { |
| | | g2d.setColor(MOWING_PATH_COLOR); // 提香红70%透明度 |
| | | g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); |
| | | g2d.draw(mowingPath); |
| | | } |
| | | |
| | | // 绘制移动路径(虚线) |
| | | if (travelStarted) { |
| | | g2d.setColor(TRAVEL_PATH_COLOR); // 蓝色 |
| | | g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, DASH_PATTERN, 0)); |
| | | g2d.draw(travelPath); |
| | | } |
| | | |
| | | // 绘制起点和终点箭头 |
| | | if (!segments.isEmpty()) { |
| | | Lunjingguihua.PathSegment firstSeg = segments.get(0); |
| | | if (firstSeg != null && firstSeg.start != null && segments.size() > 1) { |
| | | Lunjingguihua.PathSegment secondSeg = segments.get(1); |
| | | if (secondSeg != null && secondSeg.start != null) { |
| | | Point2D.Double start = new Point2D.Double(firstSeg.start.x, firstSeg.start.y); |
| | | Point2D.Double second = new Point2D.Double(secondSeg.start.x, secondSeg.start.y); |
| | | drawArrowMarker(g2d, start, second, START_POINT_COLOR, scale, arrowScale); |
| | | } |
| | | } |
| | | |
| | | Lunjingguihua.PathSegment lastSeg = segments.get(segments.size() - 1); |
| | | if (lastSeg != null && lastSeg.end != null && segments.size() > 1) { |
| | | Lunjingguihua.PathSegment prevSeg = segments.get(segments.size() - 2); |
| | | if (prevSeg != null && prevSeg.end != null) { |
| | | Point2D.Double prev = new Point2D.Double(prevSeg.end.x, prevSeg.end.y); |
| | | Point2D.Double end = new Point2D.Double(lastSeg.end.x, lastSeg.end.y); |
| | | drawArrowMarker(g2d, prev, end, END_POINT_COLOR, scale, arrowScale); |
| | | } |
| | | } |
| | | } |
| | | |
| | | g2d.setStroke(previous); |
| | | } // 方法结束 |
| | | |
| | | |
| | | /** // 文档注释开始 |
| | | * 比较两个坐标是否相同(容差) |
| | |
| | | g2d.setColor(color); // 设置颜色 |
| | | g2d.fill(arrow); // 填充箭头 |
| | | } // 方法结束 |
| | | |
| | | /** |
| | | * 解析坐标字符串 |
| | | */ |
| | | private static List<Coordinate> parseCoordinates(String s) { |
| | | List<Coordinate> list = new ArrayList<>(); |
| | | if (s == null || s.trim().isEmpty()) return list; |
| | | // 增强正则:处理可能存在的多种分隔符 |
| | | String[] pts = s.split("[;\\s]+"); |
| | | for (String p : pts) { |
| | | String trimmed = p.trim().replace("(", "").replace(")", ""); |
| | | if (trimmed.isEmpty()) continue; |
| | | String[] xy = trimmed.split("[,,\\s]+"); |
| | | if (xy.length >= 2) { |
| | | try { |
| | | double x = Double.parseDouble(xy[0].trim()); |
| | | double y = Double.parseDouble(xy[1].trim()); |
| | | // 过滤无效坐标 |
| | | if (!Double.isNaN(x) && !Double.isNaN(y) && !Double.isInfinite(x) && !Double.isInfinite(y)) { |
| | | list.add(new Coordinate(x, y)); |
| | | } |
| | | } catch (NumberFormatException ex) { |
| | | // 忽略解析错误的点 |
| | | } |
| | | } |
| | | } |
| | | // 确保多边形闭合 |
| | | if (list.size() > 2 && !list.get(0).equals2D(list.get(list.size() - 1))) { |
| | | list.add(new Coordinate(list.get(0))); |
| | | } |
| | | return list; |
| | | } |
| | | } // 类结束 |