JavaFX六边形地图避免公共边重复绘制的技术问询
解决方案:避免六边形公共边重复绘制 + 地图居中偏移
针对你遇到的两个问题,我分别给出具体的实现方案:
一、解决内部公共边重复绘制的问题
你最初的方案是每个六边形单独创建Polygon,导致相邻六边形的公共边被绘制两次,视觉上会显得比外边框粗。这里推荐两种高效的解决思路:
思路1:使用Canvas手动绘制,仅绘制每条边一次
这种方法通过记录已绘制的边,确保每条无向边只被画一次,性能和效果都很好。
首先定义一个Edge类来表示无向边(重写equals和hashCode来判断重复边):
import java.util.Objects; class Edge { private final Point p1, p2; public Edge(Point p1, Point p2) { // 标准化边的存储顺序,确保(A,B)和(B,A)被视为同一条边 if ((p1.getX() < p2.getX()) || (p1.getX() == p2.getX() && p1.getY() <= p2.getY())) { this.p1 = p1; this.p2 = p2; } else { this.p1 = p2; this.p2 = p1; } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Edge edge = (Edge) o; return p1.equals(edge.p1) && p2.equals(edge.p2); } @Override public int hashCode() { return Objects.hash(p1, p2); } }
然后用Canvas绘制地图:
private void drawHexMapWithCanvas() { // 清空原有内容 this.root.getChildren().clear(); Canvas canvas = new Canvas(this.root.getWidth(), this.root.getHeight()); GraphicsContext gc = canvas.getGraphicsContext2D(); gc.setStroke(Color.BLACK); gc.setLineWidth(1); Set<Edge> drawnEdges = new HashSet<>(); Point offset = calculateMapOffset(); // 后面会实现这个偏移计算方法 for (Hex hex : map.getMap()) { Point[] corners = screen.polygonCorners(hex); // 遍历六边形的每条边(最后一个顶点连接回第一个) for (int i = 0; i < corners.length; i++) { Point current = corners[i]; Point next = corners[(i + 1) % corners.length]; Edge edge = new Edge(current, next); if (!drawnEdges.contains(edge)) { // 应用偏移量后绘制 gc.strokeLine( current.getX() + offset.getX(), current.getY() + offset.getY(), next.getX() + offset.getX(), next.getY() + offset.getY() ); drawnEdges.add(edge); } } } this.root.getChildren().add(canvas); }
思路2:合并所有六边形为单一形状(仅适用于不需要内部边框的场景)
如果你只需要地图的外轮廓,可以用Shape.union合并所有六边形,自动消除内部公共边:
private void addHexagonsAsCombinedShape() { this.root.getChildren().clear(); Shape combinedShape = null; Point offset = calculateMapOffset(); for (Hex hex : map.getMap()) { Point[] corners = screen.polygonCorners(hex); Double[] points = new Double[12]; int i = 0; for (Point point : corners) { points[i] = point.getX() + offset.getX(); points[i+1] = point.getY() + offset.getY(); i += 2; } Polygon hexPoly = new Polygon(points); hexPoly.setFill(Color.TRANSPARENT); if (combinedShape == null) { combinedShape = hexPoly; } else { combinedShape = Shape.union(combinedShape, hexPoly); } } combinedShape.setStroke(Color.BLACK); this.root.getChildren().add(combinedShape); }
注意:这种方法会丢失六边形的内部分隔线,只保留外边框,适合特定场景。
二、解决地图左上角偏移,实现居中显示
需要先计算整个地图的边界范围,再根据Pane的尺寸计算偏移量,将地图平移到Pane中心:
1. 计算地图偏移量
private Point calculateMapOffset() { double minX = Double.MAX_VALUE; double maxX = Double.MIN_VALUE; double minY = Double.MAX_VALUE; double maxY = Double.MIN_VALUE; // 遍历所有六边形顶点,找到地图的边界 for (Hex hex : map.getMap()) { Point[] corners = screen.polygonCorners(hex); for (Point p : corners) { minX = Math.min(minX, p.getX()); maxX = Math.max(maxX, p.getX()); minY = Math.min(minY, p.getY()); maxY = Math.max(maxY, p.getY()); } } // 获取Pane的当前尺寸(如果Pane未完成布局,建议监听布局事件) double paneWidth = this.root.getWidth(); double paneHeight = this.root.getHeight(); // 计算让地图居中的偏移量 double offsetX = (paneWidth - (maxX - minX)) / 2 - minX; double offsetY = (paneHeight - (maxY - minY)) / 2 - minY; return new Point(offsetX, offsetY); }
2. 支持窗口尺寸变化
如果需要在窗口resize时保持地图居中,添加尺寸监听:
// 在初始化方法中添加监听 private void initMap() { this.root.widthProperty().addListener((observable, oldValue, newValue) -> drawHexMapWithCanvas()); this.root.heightProperty().addListener((observable, oldValue, newValue) -> drawHexMapWithCanvas()); // 首次绘制 drawHexMapWithCanvas(); }
这样无论窗口怎么调整,地图都会自动居中显示。
内容的提问来源于stack exchange,提问作者Dylan Russell
相关产品推荐
相关产品推荐

