You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:54:46