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

GoLang绘制多分支图形仅显示单分支问题排查求助

GoLang绘图仅显示单个分支的问题排查

我正在用GoLang编写代码,遍历一组带根节点和茎节点坐标的“分支”列表,日志显示每个分支都执行了绘制逻辑,但最终生成的PNG图片里只显示一个最小的分支。以下是逐点绘制分支的函数代码,需要排查原因并修复以显示所有分支:

myImage := image.NewRGBA(image.Rect(0, 0, 800, 600))
white := color.RGBA{255, 255, 255, 255}

//draw.Draw(myImage, image.Rect(0, 0, 800, 600), &image.Uniform{white}, image.ZP, draw.Src)
//black := color.RGBA{0, 0, 0, 255}

myFile, err := os.Create(imagePath) // ... now lets save output image
if err != nil {
    panic(err)
}
for _, branch := range branchList { // might print in arbitrary order

    /*fmt.Println("basic values: ", branch.length, branch.angle, branch.slope, " ")
    fmt.Println("coords. root:", branch.root.x, branch.root.y, "stem: ", branch.stem.x, branch.stem.y, " ")
    fmt.Println("branch roots: L:", branch.lBranchCoord.x, branch.lBranchCoord.y, "R: ", branch.rBranchCoord.x, branch.rBranchCoord.y)
    fmt.Println("branch pointers ", branch.lBranch, branch.rBranch)*/
    fmt.Println("branch values", branch)

    var x1, x2 int

    slope := (branch.stem.y - branch.root.y) / (branch.stem.x - branch.root.x)
    fmt.Println("slope: ", slope)

    if branch.root.x < branch.stem.x {
        x1 = int(branch.root.x)
        x2 = int(branch.stem.x)
    } else {
        x2 = int(branch.root.x)
        x1 = int(branch.stem.x)
    }
    if x1 < 0 {
        x1 = 0
    }
    if x2 > 800 {
        x2 = 800
    }

    y1 := branch.root.y // beware mixing up y coordiantes
    y2 := branch.stem.y

    if y1 < 0 {
        y1 = 0
    }
    if y2 > 800 {
        y2 = 800
    }
    // something is wrong with point drawing, likely having a rectangle with size 0
    if branch.stem.x != branch.root.x {
        for h := x1; h <= x2; h++ {
            // make each dot drawn 2x2 instead of 1x1
            // add a coefficient, either 1 or -1, to the values in the pixel
            // based on location
            // or, just add one to each and see what happens
            currentDot := image.Rect(h, int(float64(branch.root.y)+(float64(h-x1)*slope)),
                h+1, int(float64(branch.root.y)+(float64(h-x1)*slope))+1)

            //fmt.Println("point draw", currentDot)

            // negative coordiantes don't allow it to be drawn
            //fmt.Println("dot drawn")
            draw.Draw(myImage, currentDot, &image.Uniform{white}, image.ZP, draw.Src)
        }
    } else {
        for h := int(branch.root.y); h >= int(branch.stem.y); h-- {
            currentDot := image.Rect(int(branch.root.x), (int(branch.root.y) + h),
                int(branch.root.x), (int(branch.root.y) + h))
            //fmt.Println("dot drawn for straight line")
            draw.Draw(myImage, currentDot, &image.Uniform{white}, image.ZP, draw.Src)
        }
    }

    defer myFile.Close()
    //fmt.Println(myImage)
    png.Encode(myFile, myImage)
}

核心问题分析

1. 文件编码与关闭逻辑错误

png.Encode(myFile, myImage)和defer myFile.Close()被错误地放在了for循环内部:

  • 每次循环调用png.Encode都会将当前myImage的内容从头写入文件,覆盖之前的内容,最终文件只保留最后一次循环绘制的分支。
  • defer myFile.Close()在循环内会多次注册,函数退出时才执行关闭,多次Encode会导致文件写入异常,甚至内容截断。

2. 垂直分支绘制逻辑无效

在处理branch.stem.x == branch.root.x的垂直分支时:

  • currentDot使用image.Rect(int(branch.root.x), (int(branch.root.y) + h), int(branch.root.x), (int(branch.root.y) + h))生成了宽高为0的无效矩形,根本不会绘制任何像素。
  • 循环变量h的计算逻辑错误,会导致y坐标严重越界,进一步加剧绘制失败。

3. 斜率计算精度丢失

slope := (branch.stem.y - branch.root.y) / (branch.stem.x - branch.root.x)使用整数除法,会丢失小数精度,导致非整数斜率的分支绘制出现阶梯状偏移,甚至部分像素无法正确渲染。


修复后的代码

import (
    "image"
    "image/color"
    "image/draw"
    "image/png"
    "math"
    "os"
)

func drawBranches(branchList []Branch, imagePath string) {
    myImage := image.NewRGBA(image.Rect(0, 0, 800, 600))
    white := color.RGBA{255, 255, 255, 255}
    black := color.RGBA{0, 0, 0, 255}

    // 先填充黑色背景,让白色分支更清晰
    draw.Draw(myImage, myImage.Bounds(), &image.Uniform{black}, image.ZP, draw.Src)

    myFile, err := os.Create(imagePath)
    if err != nil {
        panic(err)
    }
    // 将defer移到循环外,确保所有绘制完成后再关闭文件
    defer myFile.Close()

    for _, branch := range branchList {
        fmt.Println("branch values", branch)

        dx := branch.stem.x - branch.root.x
        dy := branch.stem.y - branch.root.y
        // 用浮点数计算斜率,避免精度丢失
        slope := dy / dx
        fmt.Println("slope: ", slope)

        // 对坐标四舍五入,确保像素位置准确
        x1 := int(math.Round(branch.root.x))
        x2 := int(math.Round(branch.stem.x))
        if branch.root.x > branch.stem.x {
            x1, x2 = x2, x1
        }
        // 限制x坐标在图像有效范围内(0~799)
        x1 = max(x1, 0)
        x2 = min(x2, 799)

        yRoot := int(math.Round(branch.root.y))
        yStem := int(math.Round(branch.stem.y))
        // 限制y坐标在图像有效范围内(0~599)
        yRoot = max(yRoot, 0)
        yStem = min(yStem, 599)

        if dx != 0 {
            for h := x1; h <= x2; h++ {
                y := int(math.Round(branch.root.y + float64(h-x1)*slope))
                y = max(y, 0)
                y = min(y, 599)
                // 绘制2x2的点,确保矩形有效
                currentDot := image.Rect(h, y, h+2, y+2)
                // 确保矩形不超出图像边界
                currentDot = currentDot.Intersect(myImage.Bounds())
                draw.Draw(myImage, currentDot, &image.Uniform{white}, image.ZP, draw.Src)
            }
        } else {
            // 修正垂直分支绘制逻辑:遍历正确的y范围
            yStart := min(yRoot, yStem)
            yEnd := max(yRoot, yStem)
            for h := yStart; h <= yEnd; h++ {
                currentDot := image.Rect(int(branch.root.x), h, int(branch.root.x)+2, h+2)
                currentDot = currentDot.Intersect(myImage.Bounds())
                draw.Draw(myImage, currentDot, &image.Uniform{white}, image.ZP, draw.Src)
            }
        }
    }

    // 所有分支绘制完成后,再统一编码写入文件
    if err := png.Encode(myFile, myImage); err != nil {
        panic(err)
    }
}

// 辅助函数:取最大值
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

// 辅助函数:取最小值
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

额外说明

  • 新增max/min辅助函数,严格限制坐标在图像的有效像素范围内,避免越界绘制。
  • 对坐标值做四舍五入处理,让线条渲染更平滑自然。
  • 完全重构垂直分支的绘制逻辑,确保垂直方向分支能正常显示。
  • 将文件编码操作移到循环外部,确保所有分支绘制完成后再生成最终PNG文件。

内容的提问来源于stack exchange,提问作者T. J. Foster

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 21:24:58