| | |
| | | |
| | | /** |
| | | * 障碍物绘制类 - 负责绘制地块中的障碍物 |
| | | * |
| | | * 注意:障碍物图层需要处于地块和导航路径上方。 |
| | | * 在 MapRenderer.renderMap() 中,绘制顺序应为: |
| | | * 1. 地块边界(底层) |
| | | * 2. 导航路径(中层) |
| | | * 3. 障碍物(顶层,显示在地块和导航路径上方) |
| | | */ |
| | | public class Obstacledraw { |
| | | |
| | | // 颜色定义 |
| | | private static final Color CIRCLE_FILL_COLOR = new Color(255, 182, 193, 120); // 圆形填充色 - 浅粉红 |
| | | private static final Color CIRCLE_FILL_COLOR = new Color(128, 128, 128, 128); // 圆形填充色 - 灰色,透明度50% |
| | | private static final Color CIRCLE_BORDER_COLOR = new Color(199, 21, 133); // 圆形边框色 - 深粉红 |
| | | private static final Color POLYGON_FILL_COLOR = new Color(173, 216, 230, 120); // 多边形填充色 - 浅蓝 |
| | | private static final Color POLYGON_FILL_COLOR = new Color(128, 128, 128, 128); // 多边形填充色 - 灰色,透明度50% |
| | | private static final Color POLYGON_BORDER_COLOR = new Color(25, 25, 112); // 多边形边框色 - 深蓝 |
| | | private static final Color OBSTACLE_LABEL_COLOR = Color.BLACK; |
| | | private static final Color OBSTACLE_POINT_COLOR = Color.RED; |
| | | |
| | | // 尺寸定义 |
| | | private static final double OBSTACLE_POINT_SIZE = 0.1; // 障碍物控制点大小(米) |
| | | private static final float DEFAULT_BORDER_WIDTH = 1.0f; |
| | | private static final float SELECTED_BORDER_WIDTH = 2.5f; |
| | | // 边界线宽度与地块边界线宽度一致:3 / Math.max(0.5, scale) |
| | | private static final float BOUNDARY_STROKE_BASE = 3.0f; // 与地块边界线宽度一致 |
| | | private static final float SELECTED_WIDTH_MULTIPLIER = 1.5f; // 选中时的宽度倍数 |
| | | |
| | | /** |
| | | * 绘制地块的所有障碍物 |
| | |
| | | g2d.setColor(CIRCLE_FILL_COLOR); |
| | | g2d.fill(circle); |
| | | |
| | | // 设置边框颜色和宽度 |
| | | // 设置边框颜色和宽度(与地块边界线宽度一致) |
| | | float strokeWidth = (float)(BOUNDARY_STROKE_BASE / Math.max(0.5, scale)); |
| | | if (isSelected) { |
| | | g2d.setColor(CIRCLE_BORDER_COLOR.darker()); |
| | | g2d.setStroke(new BasicStroke(SELECTED_BORDER_WIDTH / (float)scale)); |
| | | // 选中时稍微加粗 |
| | | g2d.setStroke(new BasicStroke(strokeWidth * SELECTED_WIDTH_MULTIPLIER)); |
| | | } else { |
| | | g2d.setColor(CIRCLE_BORDER_COLOR); |
| | | g2d.setStroke(new BasicStroke(DEFAULT_BORDER_WIDTH / (float)scale)); |
| | | g2d.setStroke(new BasicStroke(strokeWidth)); |
| | | } |
| | | g2d.draw(circle); |
| | | |
| | |
| | | g2d.setColor(POLYGON_FILL_COLOR); |
| | | g2d.fill(polygon); |
| | | |
| | | // 设置边框颜色和宽度 |
| | | // 设置边框颜色和宽度(与地块边界线宽度一致) |
| | | float strokeWidth = (float)(BOUNDARY_STROKE_BASE / Math.max(0.5, scale)); |
| | | if (isSelected) { |
| | | g2d.setColor(POLYGON_BORDER_COLOR.darker()); |
| | | g2d.setStroke(new BasicStroke(SELECTED_BORDER_WIDTH / (float)scale)); |
| | | // 选中时稍微加粗 |
| | | g2d.setStroke(new BasicStroke(strokeWidth * SELECTED_WIDTH_MULTIPLIER)); |
| | | } else { |
| | | g2d.setColor(POLYGON_BORDER_COLOR); |
| | | g2d.setStroke(new BasicStroke(DEFAULT_BORDER_WIDTH / (float)scale)); |
| | | g2d.setStroke(new BasicStroke(strokeWidth)); |
| | | } |
| | | g2d.draw(polygon); |
| | | |
| | |
| | | |
| | | /** |
| | | * 绘制障碍物标签 |
| | | * 文字大小与"缩放"文字一致(11号字体),且不随地图缩放变化 |
| | | */ |
| | | private static void drawObstacleLabel(Graphics2D g2d, Obstacledge.Obstacle obstacle, |
| | | double scale) { |
| | |
| | | return; |
| | | } |
| | | |
| | | // 计算标签位置(中心点) |
| | | double centerX = 0; |
| | | double centerY = 0; |
| | | // 保存当前变换 |
| | | AffineTransform originalTransform = g2d.getTransform(); |
| | | |
| | | for (Obstacledge.XYCoordinate coord : xyCoords) { |
| | | centerX += coord.getX(); |
| | | centerY += coord.getY(); |
| | | double centerX; |
| | | double centerY; |
| | | |
| | | Obstacledge.ObstacleShape shape = obstacle.getShape(); |
| | | if (shape == Obstacledge.ObstacleShape.CIRCLE) { |
| | | Obstacledge.XYCoordinate centerCoord = xyCoords.get(0); |
| | | centerX = centerCoord.getX(); |
| | | centerY = centerCoord.getY(); |
| | | } else { |
| | | Point2D.Double centroid = computePolygonCentroid(xyCoords); |
| | | centerX = centroid.x; |
| | | centerY = centroid.y; |
| | | } |
| | | |
| | | centerX /= xyCoords.size(); |
| | | centerY /= xyCoords.size(); |
| | | // 将世界坐标转换为屏幕坐标 |
| | | Point2D.Double worldPoint = new Point2D.Double(centerX, centerY); |
| | | Point2D.Double screenPoint = new Point2D.Double(); |
| | | originalTransform.transform(worldPoint, screenPoint); |
| | | |
| | | // 获取障碍物名称和形状 |
| | | // 恢复原始变换以使用屏幕坐标绘制 |
| | | g2d.setTransform(new AffineTransform()); |
| | | |
| | | // 获取障碍物名称 |
| | | String obstacleName = obstacle.getObstacleName(); |
| | | String shapeDesc = obstacle.getShape().getDescription(); |
| | | if (obstacleName == null || obstacleName.trim().isEmpty()) { |
| | | obstacleName = "障碍物"; |
| | | } else { |
| | | obstacleName = obstacleName.trim(); |
| | | } |
| | | |
| | | // 设置字体和颜色 |
| | | // 设置字体和颜色(与"缩放"文字一致) |
| | | g2d.setColor(OBSTACLE_LABEL_COLOR); |
| | | |
| | | // 根据缩放比例调整字体大小 |
| | | int fontSize = (int)(10 / scale); |
| | | fontSize = Math.max(8, Math.min(fontSize, 14)); // 限制字体大小范围 |
| | | |
| | | g2d.setFont(new Font("微软雅黑", Font.PLAIN, fontSize)); |
| | | g2d.setFont(new Font("微软雅黑", Font.PLAIN, 11)); // 与"缩放"文字大小一致 |
| | | |
| | | // 绘制标签 |
| | | String label = String.format("%s(%s)", obstacleName, shapeDesc); |
| | | String label = obstacleName; |
| | | FontMetrics metrics = g2d.getFontMetrics(); |
| | | int textWidth = metrics.stringWidth(label); |
| | | int textHeight = metrics.getHeight(); |
| | | |
| | | // 在中心点绘制标签 |
| | | int textX = (int)(centerX - textWidth / 2.0); |
| | | int textY = (int)(centerY + textHeight / 4.0); // 稍微向下偏移 |
| | | // 在屏幕坐标中心点绘制标签 |
| | | int textX = (int)(screenPoint.x - textWidth / 2.0); |
| | | int textY = (int)(screenPoint.y + textHeight / 4.0); // 稍微向下偏移 |
| | | |
| | | g2d.drawString(label, textX, textY); |
| | | |
| | | // 恢复原始变换 |
| | | g2d.setTransform(originalTransform); |
| | | } |
| | | |
| | | private static Point2D.Double computePolygonCentroid(List<Obstacledge.XYCoordinate> xyCoords) { |
| | | double area = 0.0; |
| | | double cx = 0.0; |
| | | double cy = 0.0; |
| | | int n = xyCoords.size(); |
| | | for (int i = 0; i < n; i++) { |
| | | Obstacledge.XYCoordinate current = xyCoords.get(i); |
| | | Obstacledge.XYCoordinate next = xyCoords.get((i + 1) % n); |
| | | double x0 = current.getX(); |
| | | double y0 = current.getY(); |
| | | double x1 = next.getX(); |
| | | double y1 = next.getY(); |
| | | double cross = x0 * y1 - x1 * y0; |
| | | area += cross; |
| | | cx += (x0 + x1) * cross; |
| | | cy += (y0 + y1) * cross; |
| | | } |
| | | |
| | | double areaFactor = area * 0.5; |
| | | if (Math.abs(areaFactor) < 1e-9) { |
| | | double avgX = 0.0; |
| | | double avgY = 0.0; |
| | | for (Obstacledge.XYCoordinate coord : xyCoords) { |
| | | avgX += coord.getX(); |
| | | avgY += coord.getY(); |
| | | } |
| | | int size = Math.max(1, xyCoords.size()); |
| | | return new Point2D.Double(avgX / size, avgY / size); |
| | | } |
| | | |
| | | double centroidX = cx / (6.0 * areaFactor); |
| | | double centroidY = cy / (6.0 * areaFactor); |
| | | return new Point2D.Double(centroidX, centroidY); |
| | | } |
| | | |
| | | /** |