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

如何在JavaFX中无缝绘制由网格方块组成的网格?

迷宫网格无缝拼接问题

我正在编写迷宫生成算法,想用JavaFX(ScalaFX)绘制生成的迷宫。先尝试用小型形状组成简易网格,后续转成迷宫样式。用左上角拐角形状(如┏)搭配GridPane实现,但单元格间出现细微缝隙。代码用Scala编写,参考JavaFX文档开发,想请教:

  • 如何实现网格的无缝拼接?
  • 使用GridPane的思路是否正确?

用户提供的原始代码:

val lineLength: Int = 30

def makeClosedCell(length: Int = lineLength): Shape = {
    val wallN = Line(0,0,length,0)
    val wallW = Line(0,0,0,length)
    val closedCell: Shape = Shape.union(wallN, wallW)
    return closedCell
}

def makeOpenW(length: Int = lineLength): Shape = Line(0,0,length,0)

def makeOpenN(length: Int = lineLength): Shape = Line(0,0,0,length)

def initialiseGrid(r: GridPane, sizex: Int, sizey: Int): Unit = {
    for (i <- 0 until sizex) {
        val colConst = new ColumnConstraints()
        colConst.setPercentWidth(100.0 / sizex)
        r.getColumnConstraints().add(colConst)
    }
    for (i <- 0 until sizey) {
        val rowConst = new RowConstraints()
        rowConst.setPercentHeight(100.0 / sizey)
        r.getRowConstraints().add(rowConst)     
    }
    for(j <- sizey-1 to 0 by -1){
        for(i <- 0 until sizex){
            r.add(makeClosedCell(),i,j)
        }
        r.add(makeOpenN(),sizex,j)
    }
    for(i <- 0 until sizex){
        r.add(makeOpenW(),i,sizey)
    }
}

问题解析与解决方案

1. GridPane的思路是否正确?

GridPane用来布局网格结构是合理的,但你的实现存在两个核心问题:

  • 相邻单元格的共享边被重复绘制,叠加后容易因布局精度出现视觉缝隙
  • 使用百分比宽高会引入浮点计算误差,导致单元格边界对齐不准

2. 无缝拼接的实现方案

核心调整方向

  • 避免重复绘制共享边,每个单元格只负责自己独有的边
  • 用固定宽高替代百分比布局,消除浮点精度误差
  • 设置线条绘制规则,确保边缘不会超出单元格边界

调整后的ScalaFX代码示例

import scalafx.scene.shape.{Line, Shape, StrokeType}
import scalafx.scene.layout.{GridPane, ColumnConstraints, RowConstraints}
import scalafx.geometry.Pos

val lineLength: Int = 30

// 左上角单元格:绘制左、上两条边
def makeTopLeftCell(length: Int = lineLength): Shape = {
    val topWall = Line(0, 0, length, 0)
    val leftWall = Line(0, 0, 0, length)
    topWall.setStrokeType(StrokeType.INSIDE)
    leftWall.setStrokeType(StrokeType.INSIDE)
    Shape.union(topWall, leftWall)
}

// 第一行非首列单元格:仅绘制上边
def makeTopWall(length: Int = lineLength): Shape = {
    val wall = Line(0, 0, length, 0)
    wall.setStrokeType(StrokeType.INSIDE)
    wall
}

// 第一列非首行单元格:仅绘制左边
def makeLeftWall(length: Int = lineLength): Shape = {
    val wall = Line(0, 0, 0, length)
    wall.setStrokeType(StrokeType.INSIDE)
    wall
}

// 内部单元格:绘制右、下两条边(后续可根据迷宫逻辑动态移除)
def makeInnerCellWalls(length: Int = lineLength): Shape = {
    val rightWall = Line(length, 0, length, length)
    val bottomWall = Line(0, length, length, length)
    rightWall.setStrokeType(StrokeType.INSIDE)
    bottomWall.setStrokeType(StrokeType.INSIDE)
    Shape.union(rightWall, bottomWall)
}

def initialiseGrid(r: GridPane, sizex: Int, sizey: Int): Unit = {
    // 清除单元格间距,设置顶左对齐
    r.setHgap(0)
    r.setVgap(0)
    r.setAlignment(Pos.TOP_LEFT)

    // 设置固定宽度的列约束
    for (_ <- 0 until sizex) {
        val colConst = new ColumnConstraints()
        colConst.setPrefWidth(lineLength)
        r.getColumnConstraints.add(colConst)
    }
    // 设置固定高度的行约束
    for (_ <- 0 until sizey) {
        val rowConst = new RowConstraints()
        rowConst.setPrefHeight(lineLength)
        r.getRowConstraints.add(rowConst)
    }

    // 填充网格单元格
    for (j <- 0 until sizey) {
        for (i <- 0 until sizex) {
            val cell = (i, j) match {
                case (0, 0) => makeTopLeftCell()
                case (0, _) => makeLeftWall()
                case (_, 0) => makeTopWall()
                case _ => makeInnerCellWalls()
            }
            r.add(cell, i, j)
        }
    }

    // 添加最右侧的垂直边界
    for (j <- 0 until sizey) {
        val wall = Line(0, 0, 0, lineLength)
        wall.setStrokeType(StrokeType.INSIDE)
        r.add(wall, sizex, j)
    }

    // 添加最底部的水平边界
    for (i <- 0 until sizex) {
        val wall = Line(0, 0, lineLength, 0)
        wall.setStrokeType(StrokeType.INSIDE)
        r.add(wall, i, sizey)
    }
}

关键调整说明

  • 固定尺寸布局:用setPrefWidth/setPrefHeight替代百分比,避免浮点计算带来的对齐误差
  • StrokeType.INSIDE:让线条在单元格内部绘制,不会超出边界导致视觉缝隙
  • 消除重复绘制:每个单元格只负责自己的非共享边,避免线条叠加产生的间隙
  • 清除间距:设置Hgap和Vgap为0,确保单元格完全贴合

内容的提问来源于stack exchange,提问作者user18898152

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 04:06:24