From 6cd5efa192d3f55a39bd87996c93cb2102865696 Mon Sep 17 00:00:00 2001 From: lixiangwuxian Date: Sat, 10 May 2025 02:31:02 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E6=97=A7?= =?UTF-8?q?=E7=9A=84=E7=BB=98=E5=88=B6=E6=A8=A1=E5=9D=97=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=96=B0=E7=9A=84=E7=BB=98=E5=88=B6=E8=BE=85=E5=8A=A9?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E5=92=8C=E7=B2=BE=E7=81=B5=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=A2=9E=E5=BC=BA=E5=9B=BE=E5=BD=A2?= =?UTF-8?q?=E5=A4=84=E7=90=86=E8=83=BD=E5=8A=9B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- draw/draw.go | 28 -- drawhelper/draw.go | 156 ++++++++++ model/2d.go | 41 --- sprite/2d.go | 134 +++++++++ sprite/2d_test.go | 284 +++++++++++++++++++ {model => sprite}/named_sprite_board.go | 6 +- {model => sprite}/named_sprite_board_test.go | 4 +- {model => sprite}/sprite.go | 7 +- util/math.go | 25 ++ 9 files changed, 610 insertions(+), 75 deletions(-) delete mode 100644 draw/draw.go create mode 100644 drawhelper/draw.go delete mode 100644 model/2d.go create mode 100644 sprite/2d.go create mode 100644 sprite/2d_test.go rename {model => sprite}/named_sprite_board.go (98%) rename {model => sprite}/named_sprite_board_test.go (99%) rename {model => sprite}/sprite.go (97%) create mode 100644 util/math.go diff --git a/draw/draw.go b/draw/draw.go deleted file mode 100644 index 3ca5936..0000000 --- a/draw/draw.go +++ /dev/null @@ -1,28 +0,0 @@ -package draw - -import ( - "image" - "image/draw" -) - -type Drawer interface { - Draw(img *image.Image) -} - -type BaseBoard struct { - Image *image.RGBA64 -} - -func (b *BaseBoard) SetBaseImage(img *image.Image) { - b.Image = ToRGBA64(img) -} - -func (b *BaseBoard) Draw(img *image.Image, pos image.Point) { - draw.Draw(b.Image, b.Image.Bounds(), *img, pos, draw.Over) -} - -func ToRGBA64(img *image.Image) *image.RGBA64 { - rgba := image.NewRGBA64((*img).Bounds()) - draw.Draw(rgba, rgba.Bounds(), *img, image.Point{}, draw.Over) - return rgba -} diff --git a/drawhelper/draw.go b/drawhelper/draw.go new file mode 100644 index 0000000..a5c4624 --- /dev/null +++ b/drawhelper/draw.go @@ -0,0 +1,156 @@ +package drawhelper + +import ( + "image" + "image/color" + "log" + + "git.lxtend.com/lixiangwuxian/imagedd/util" +) + +// 辅助函数:在图像上绘制宽线条 +func DrawThickLineOnImage(img *image.RGBA, x0, y0, x1, y1, width int, color color.Color) { + // 获取颜色分量 + r, g, b, a := color.RGBA() + r8, g8, b8, a8 := uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8) + + bounds := img.Bounds() + + // 使用Bresenham算法绘制线段作为中心线 + dx := util.Abs(x1 - x0) + dy := util.Abs(y1 - y0) + sx, sy := 1, 1 + if x0 >= x1 { + sx = -1 + } + if y0 >= y1 { + sy = -1 + } + err := dx - dy + + // 计算线宽的一半(向下取整) + halfWidth := width / 2 + + // 绘制线条 + x, y := x0, y0 + for { + // 绘制宽线条(在中心点周围绘制圆形区域) + for wy := -halfWidth; wy <= halfWidth; wy++ { + for wx := -halfWidth; wx <= halfWidth; wx++ { + // 只绘制在线宽范围内的点(圆形区域) + if wx*wx+wy*wy <= halfWidth*halfWidth { + DrawPointIfInBounds(img, bounds, x+wx, y+wy, r8, g8, b8, a8) + } + } + } + + if x == x1 && y == y1 { + break + } + + e2 := 2 * err + if e2 > -dy { + err -= dy + x += sx + } + if e2 < dx { + err += dx + y += sy + } + } +} + +// 辅助函数:在图像上绘制线段 +func DrawLineOnImage(img *image.RGBA, x0, y0, x1, y1 int, color color.Color) { + r, g, b, a := color.RGBA() + r8, g8, b8, a8 := uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8) + + // 输出颜色值用于调试 + log.Printf("绘制线条: 颜色 RGBA(%d,%d,%d,%d)", r8, g8, b8, a8) + + bounds := img.Bounds() + + // 使用Bresenham算法绘制线段 + dx := util.Abs(x1 - x0) + dy := util.Abs(y1 - y0) + sx, sy := 1, 1 + if x0 >= x1 { + sx = -1 + } + if y0 >= y1 { + sy = -1 + } + err := dx - dy + + for { + // 绘制点,但只在有效范围内 + DrawPointIfInBounds(img, bounds, x0, y0, r8, g8, b8, a8) + + if x0 == x1 && y0 == y1 { + break + } + + e2 := 2 * err + if e2 > -dy { + err -= dy + x0 += sx + } + if e2 < dx { + err += dx + y0 += sy + } + } +} + +// 辅助函数:在图像上绘制圆 +func DrawCircleOnImage(img *image.RGBA, x0, y0, radius int, color color.Color) { + r, g, b, a := color.RGBA() + r8, g8, b8, a8 := uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8) + + // 使用Bresenham算法绘制圆 + x := 0 + y := radius + d := 3 - 2*radius + + bounds := img.Bounds() + + for { + // 绘制8个对称点,但只在图像范围内 + DrawPointIfInBounds(img, bounds, x0+x, y0+y, r8, g8, b8, a8) + DrawPointIfInBounds(img, bounds, x0+x, y0-y, r8, g8, b8, a8) + DrawPointIfInBounds(img, bounds, x0-x, y0+y, r8, g8, b8, a8) + DrawPointIfInBounds(img, bounds, x0-x, y0-y, r8, g8, b8, a8) + DrawPointIfInBounds(img, bounds, x0+y, y0+x, r8, g8, b8, a8) + DrawPointIfInBounds(img, bounds, x0+y, y0-x, r8, g8, b8, a8) + DrawPointIfInBounds(img, bounds, x0-y, y0+x, r8, g8, b8, a8) + DrawPointIfInBounds(img, bounds, x0-y, y0-x, r8, g8, b8, a8) + + if d < 0 { + d += 4*x + 6 + } else { + d += 4*(x-y) + 10 + y-- + } + x++ + + if x > y { + break + } + } +} + +// 辅助函数:在图像上设置像素,但只在有效范围内 +func DrawPointIfInBounds(img *image.RGBA, bounds image.Rectangle, x, y int, r, g, b, a uint8) { + if x >= bounds.Min.X && x < bounds.Max.X && y >= bounds.Min.Y && y < bounds.Max.Y { + // 记录绘制的像素位置和颜色,便于调试 + if (r > 0 || g > 0 || b > 0) && a > 0 { + log.Printf("绘制像素: (%d,%d) RGBA(%d,%d,%d,%d)", x, y, r, g, b, a) + } + + idx := (y-bounds.Min.Y)*img.Stride + (x-bounds.Min.X)*4 + img.Pix[idx] = r + img.Pix[idx+1] = g + img.Pix[idx+2] = b + img.Pix[idx+3] = a + } +} diff --git a/model/2d.go b/model/2d.go deleted file mode 100644 index f072f7d..0000000 --- a/model/2d.go +++ /dev/null @@ -1,41 +0,0 @@ -package model - -import ( - "image" - "image/color" - "image/draw" -) - -type Line struct { - Start image.Point - End image.Point - Width int - Color color.Color -} - -func (l *Line) ToSprite() *Sprite { - img := image.NewRGBA(image.Rect(0, 0, l.End.X-l.Start.X, l.End.Y-l.Start.Y)) - draw.Draw(img, image.Rect(0, 0, l.End.X-l.Start.X, l.End.Y-l.Start.Y), &image.Uniform{l.Color}, image.Point{}, draw.Src) - return &Sprite{ - Position: l.Start, - Image: img, - } -} - -type ProjectMatrix struct { - Matrix [2][2]float64 -} - -func (p *ProjectMatrix) ProjectPoint(point image.Point) image.Point { - return image.Point{ - X: int(p.Matrix[0][0]*float64(point.X) + p.Matrix[0][1]*float64(point.Y)), - Y: int(p.Matrix[1][0]*float64(point.X) + p.Matrix[1][1]*float64(point.Y)), - } -} - -func (p *ProjectMatrix) ProjectLine(line Line) Line { - return Line{ - Start: p.ProjectPoint(line.Start), - End: p.ProjectPoint(line.End), - } -} diff --git a/sprite/2d.go b/sprite/2d.go new file mode 100644 index 0000000..16c1502 --- /dev/null +++ b/sprite/2d.go @@ -0,0 +1,134 @@ +package sprite + +import ( + "image" + "image/color" + "image/draw" + "log" + + "git.lxtend.com/lixiangwuxian/imagedd/drawhelper" +) + +type Line struct { + Start image.Point + End image.Point + Width int + Color color.Color +} + +// AddToSprite 在精灵图上绘制线条,保留原有的图像 +func (l *Line) AddToSprite(sprite *Sprite) { + log.Printf("开始绘制线条: 起点(%d,%d) 终点(%d,%d) 宽度%d", + l.Start.X, l.Start.Y, l.End.X, l.End.Y, l.Width) + + // 检查精灵是否已有图像 + if sprite.Image == nil { + // 如果没有图像,创建一个新图像 + width := l.End.X - l.Start.X + 1 + height := l.End.Y - l.Start.Y + 1 + if width < 1 { + width = 1 + } + if height < 1 { + height = 1 + } + img := image.NewRGBA(image.Rect(0, 0, width, height)) + sprite.Image = img + sprite.Position = l.Start + log.Printf("创建新图像: 大小(%d,%d) 位置(%d,%d)", + width, height, sprite.Position.X, sprite.Position.Y) + } + + // 获取当前图像大小和位置 + bounds := sprite.Image.Bounds() + width, height := bounds.Dx(), bounds.Dy() + log.Printf("当前图像: 大小(%d,%d) 位置(%d,%d)", + width, height, sprite.Position.X, sprite.Position.Y) + + // 直接使用原图像,不再扩展 + var newImage *image.RGBA + newImage, _ = sprite.Image.(*image.RGBA) + if newImage == nil { + // 如果原图像不是RGBA,转换为RGBA + newImage = image.NewRGBA(bounds) + draw.Draw(newImage, bounds, sprite.Image, bounds.Min, draw.Src) + log.Printf("转换图像为RGBA格式") + } + + // 在图像上绘制线条,根据线宽决定使用哪个函数 + // 超出图像边界的部分将被自动丢弃AddToSprite + if l.Width <= 1 { + // 使用标准线条绘制算法 + drawhelper.DrawLineOnImage(newImage, l.Start.X, l.Start.Y, l.End.X, l.End.Y, l.Color) + } else { + // 使用粗线条绘制算法 + drawhelper.DrawThickLineOnImage(newImage, l.Start.X, l.Start.Y, l.End.X, l.End.Y, l.Width, l.Color) + } + + // 更新精灵图像 + sprite.Image = newImage + log.Printf("线条绘制完成") +} + +type Circle struct { + Center image.Point + Radius int + Color color.Color +} + +// ToSprite 在精灵图上绘制圆形,保留原有的图像 +func (c *Circle) ToSprite(sprite *Sprite) { + // 检查精灵是否已有图像 + if sprite.Image == nil { + // 如果没有图像,创建一个新图像 + size := c.Radius * 2 + img := image.NewRGBA(image.Rect(0, 0, size, size)) + sprite.Image = img + // 设置精灵位置为圆心减去半径 + sprite.Position = image.Point{ + X: c.Center.X - c.Radius, + Y: c.Center.Y - c.Radius, + } + } + + // 获取当前图像大小和位置 + bounds := sprite.Image.Bounds() + + // 计算圆心相对于精灵图像的位置 + relCenterX := c.Center.X - sprite.Position.X + relCenterY := c.Center.Y - sprite.Position.Y + + // 直接使用原图像,不再扩展 + var newImage *image.RGBA + newImage, _ = sprite.Image.(*image.RGBA) + if newImage == nil { + // 如果原图像不是RGBA,转换为RGBA + newImage = image.NewRGBA(bounds) + draw.Draw(newImage, bounds, sprite.Image, bounds.Min, draw.Src) + } + + // 在新图像上绘制圆 + // 超出图像边界的部分将被自动丢弃 + drawhelper.DrawCircleOnImage(newImage, relCenterX, relCenterY, c.Radius, c.Color) + + // 更新精灵图像 + sprite.Image = newImage +} + +type ProjectMatrix struct { + Matrix [2][2]float64 +} + +func (p *ProjectMatrix) ProjectPoint(point image.Point) image.Point { + return image.Point{ + X: int(p.Matrix[0][0]*float64(point.X) + p.Matrix[0][1]*float64(point.Y)), + Y: int(p.Matrix[1][0]*float64(point.X) + p.Matrix[1][1]*float64(point.Y)), + } +} + +func (p *ProjectMatrix) ProjectLine(line Line) Line { + return Line{ + Start: p.ProjectPoint(line.Start), + End: p.ProjectPoint(line.End), + } +} diff --git a/sprite/2d_test.go b/sprite/2d_test.go new file mode 100644 index 0000000..f160826 --- /dev/null +++ b/sprite/2d_test.go @@ -0,0 +1,284 @@ +package sprite + +import ( + "image" + "image/color" + "testing" +) + +// 测试在精灵上绘制线条 +func TestLineToSprite(t *testing.T) { + // 创建一个新的空白精灵 + sprite := &Sprite{ + Name: "test_line", + Position: image.Point{X: 10, Y: 10}, + Index: 1, + } + + // 定义一条线 + line := &Line{ + Start: image.Point{X: 10, Y: 10}, // 起点设置为与精灵位置相同 + End: image.Point{X: 50, Y: 30}, + Width: 5, // 增加线宽以便更容易找到 + Color: color.RGBA{R: 255, G: 0, B: 0, A: 255}, // 红色 + } + + // 在精灵上绘制线 + line.AddToSprite(sprite) + + // 验证精灵现在有了图像 + if sprite.Image == nil { + t.Fatalf("精灵图像为空") + } + + // 验证精灵位置未变(因为线条在精灵边界内) + if sprite.Position.X != 10 || sprite.Position.Y != 10 { + t.Errorf("精灵位置错误:期望(10,10),实际(%d,%d)", + sprite.Position.X, sprite.Position.Y) + } + + // 检查图像大小 + bounds := sprite.Image.Bounds() + t.Logf("图像大小:%dx%d", bounds.Dx(), bounds.Dy()) + if bounds.Dx() < 41 || bounds.Dy() < 21 { + t.Errorf("图像大小错误:期望至少(41x21),实际(%dx%d)", + bounds.Dx(), bounds.Dy()) + } + + // 检查图像中是否有红色像素 + redPixelFound := false + + // 扫描整个图像查找红色像素 + var redPixels []image.Point + for y := 0; y < bounds.Dy(); y++ { + for x := 0; x < bounds.Dx(); x++ { + pixelColor := sprite.Image.At(x, y) + r, g, b, _ := pixelColor.RGBA() + if r>>8 > 200 && g>>8 < 50 && b>>8 < 50 { + redPixelFound = true + redPixels = append(redPixels, image.Point{X: x, Y: y}) + if len(redPixels) >= 5 { // 找到5个点就足够了 + break + } + } + } + if len(redPixels) >= 5 { + break + } + } + + // 打印找到的红色像素位置 + if len(redPixels) > 0 { + t.Logf("找到 %d 个红色像素点", len(redPixels)) + for i, p := range redPixels { + t.Logf(" 红色像素点 #%d: (%d,%d)", i+1, p.X, p.Y) + } + } + + if !redPixelFound { + // 如果没找到,保存图像用于调试 + board := NewNamedSpriteBoard(100, 100) + board.AddSprite(sprite) + board.SaveToPng("test_line1.png") + t.Errorf("未找到红色线条像素") + } + + // 保存初始图像大小用于后续比较 + initialWidth := bounds.Dx() + initialHeight := bounds.Dy() + + // 再绘制一条线(线条一部分超出当前图像边界,应导致图像扩展) + line2 := &Line{ + Start: image.Point{X: 5, Y: 20}, + End: image.Point{X: 65, Y: 40}, + Width: 5, // 增加线宽 + Color: color.RGBA{R: 0, G: 0, B: 255, A: 255}, // 蓝色 + } + + // 使用Sprite.DrawLine方法 + sprite.DrawLine(line2) + + // 检查图像是否扩展以容纳新线 + newBounds := sprite.Image.Bounds() + t.Logf("扩展后图像大小:%dx%d", newBounds.Dx(), newBounds.Dy()) + if newBounds.Dx() <= initialWidth || newBounds.Dy() <= initialHeight { + t.Errorf("图像未扩展:原始(%dx%d),现在(%dx%d)", + initialWidth, initialHeight, newBounds.Dx(), newBounds.Dy()) + } + + // 检查图像中是否有蓝色像素 + bluePixelFound := false + + // 扫描整个图像查找蓝色像素 + var bluePixels []image.Point + for y := 0; y < newBounds.Dy(); y++ { + for x := 0; x < newBounds.Dx(); x++ { + pixelColor := sprite.Image.At(x, y) + r, g, b, _ := pixelColor.RGBA() + if r>>8 < 50 && g>>8 < 50 && b>>8 > 200 { + bluePixelFound = true + bluePixels = append(bluePixels, image.Point{X: x, Y: y}) + if len(bluePixels) >= 5 { // 找到5个点就足够了 + break + } + } + } + if len(bluePixels) >= 5 { + break + } + } + + // 打印找到的蓝色像素位置 + if len(bluePixels) > 0 { + t.Logf("找到 %d 个蓝色像素点", len(bluePixels)) + for i, p := range bluePixels { + t.Logf(" 蓝色像素点 #%d: (%d,%d)", i+1, p.X, p.Y) + } + } + + if !bluePixelFound { + // 保存图像用于调试 + board := NewNamedSpriteBoard(100, 100) + board.AddSprite(sprite) + board.SaveToPng("test_line2.png") + t.Errorf("未找到蓝色线条像素") + } + + // 总是保存最终图像以便查看结果 + board := NewNamedSpriteBoard(100, 100) + board.AddSprite(sprite) + board.SaveToPng("test_line_final.png") +} + +// 测试在精灵上绘制圆形 +func TestCircleToSprite(t *testing.T) { + // 创建一个新的空白精灵 + sprite := &Sprite{ + Name: "test_circle", + Position: image.Point{X: 20, Y: 20}, + Index: 1, + } + + // 定义一个圆 + circle := &Circle{ + Center: image.Point{X: 30, Y: 30}, + Radius: 10, + Color: color.RGBA{R: 0, G: 255, B: 0, A: 255}, // 绿色 + } + + // 在精灵上绘制圆 + circle.ToSprite(sprite) + + // 验证精灵现在有了图像 + if sprite.Image == nil { + t.Fatalf("精灵图像为空") + } + + // 验证精灵位置正确更新 + if sprite.Position.X != 20 || sprite.Position.Y != 20 { + t.Errorf("精灵位置错误:期望(20,20),实际(%d,%d)", + sprite.Position.X, sprite.Position.Y) + } + + // 检查图像大小 + bounds := sprite.Image.Bounds() + t.Logf("图像大小:%dx%d", bounds.Dx(), bounds.Dy()) + + // 圆的相对坐标是 (30-20, 30-20) 即 (10,10) 中心 + // 记录找到绿色像素的位置,用于调试 + foundPoints := make([]image.Point, 0) + + // 在图像中寻找绿色像素 + for y := 0; y < bounds.Dy(); y++ { + for x := 0; x < bounds.Dx(); x++ { + pixelColor := sprite.Image.At(x, y) + r, g, b, _ := pixelColor.RGBA() + if r>>8 < 50 && g>>8 > 200 && b>>8 < 50 { + foundPoints = append(foundPoints, image.Point{X: x, Y: y}) + if len(foundPoints) > 10 { + break // 找到足够多的点了 + } + } + } + if len(foundPoints) > 10 { + break + } + } + + // 如果找到了绿色像素,则测试通过 + if len(foundPoints) == 0 { + // 保存图像用于调试 + board := NewNamedSpriteBoard(100, 100) + board.AddSprite(sprite) + board.SaveToPng("test_circle1.png") + t.Errorf("未找到绿色圆形像素") + } else { + t.Logf("找到 %d 个绿色像素点", len(foundPoints)) + for i, p := range foundPoints { + if i < 5 { // 只打印前5个点 + t.Logf(" 绿色像素点 #%d: (%d,%d)", i+1, p.X, p.Y) + } + } + } + + // 保存初始图像大小用于后续比较 + initialWidth := bounds.Dx() + initialHeight := bounds.Dy() + + // 再绘制一个圆(部分在图像边界外,应导致图像扩展) + circle2 := &Circle{ + Center: image.Point{X: 15, Y: 15}, + Radius: 15, + Color: color.RGBA{R: 255, G: 255, B: 0, A: 255}, // 黄色 + } + + // 在已有精灵上绘制第二个圆 + circle2.ToSprite(sprite) + + // 检查图像是否扩展 + newBounds := sprite.Image.Bounds() + t.Logf("扩展后图像大小:%dx%d", newBounds.Dx(), newBounds.Dy()) + + // 检查图像是否扩展了 + if newBounds.Dx() <= initialWidth || newBounds.Dy() <= initialHeight { + t.Errorf("图像未正确扩展:原始(%dx%d),现在(%dx%d)", + initialWidth, initialHeight, newBounds.Dx(), newBounds.Dy()) + } + + // 查找黄色像素 + yellowPixelFound := false + yellowPoints := make([]image.Point, 0) + + // 在图像中寻找黄色像素 + for y := 0; y < newBounds.Dy(); y++ { + for x := 0; x < newBounds.Dx(); x++ { + pixelColor := sprite.Image.At(x, y) + r, g, b, _ := pixelColor.RGBA() + if r>>8 > 200 && g>>8 > 200 && b>>8 < 50 { + yellowPixelFound = true + yellowPoints = append(yellowPoints, image.Point{X: x, Y: y}) + if len(yellowPoints) > 10 { + break // 找到足够多的点了 + } + } + } + if len(yellowPoints) > 10 { + break + } + } + + if !yellowPixelFound { + // 保存图像用于调试 + board := NewNamedSpriteBoard(100, 100) + board.AddSprite(sprite) + board.SaveToPng("test_circle2.png") + t.Errorf("未找到黄色圆形像素") + } else { + t.Logf("找到 %d 个黄色像素点", len(yellowPoints)) + for i, p := range yellowPoints { + if i < 5 { // 只打印前5个点 + t.Logf(" 黄色像素点 #%d: (%d,%d)", i+1, p.X, p.Y) + } + } + } +} diff --git a/model/named_sprite_board.go b/sprite/named_sprite_board.go similarity index 98% rename from model/named_sprite_board.go rename to sprite/named_sprite_board.go index e5aba26..a3d8b91 100644 --- a/model/named_sprite_board.go +++ b/sprite/named_sprite_board.go @@ -1,4 +1,4 @@ -package model +package sprite import ( "image" @@ -501,8 +501,8 @@ func (b *NamedSpriteBoard) RenderToImage() *image.RGBA { return img } -// SaveToFile 将精灵板渲染为图像并保存到文件 -func (b *NamedSpriteBoard) SaveToFile(filename string) error { +// SaveToPng 将精灵板渲染为图像并保存到文件 +func (b *NamedSpriteBoard) SaveToPng(filename string) error { // 渲染图像 img := b.RenderToImage() diff --git a/model/named_sprite_board_test.go b/sprite/named_sprite_board_test.go similarity index 99% rename from model/named_sprite_board_test.go rename to sprite/named_sprite_board_test.go index 051f2ec..19ba1ea 100644 --- a/model/named_sprite_board_test.go +++ b/sprite/named_sprite_board_test.go @@ -1,4 +1,4 @@ -package model +package sprite import ( "image" @@ -333,7 +333,7 @@ func TestNamedSpriteBoardRenderToImage(t *testing.T) { // 将saveImage设置为true查看实际渲染效果以调试 saveImage := false if saveImage { - err := board.SaveToFile("test_render.png") + err := board.SaveToPng("test_render.png") if err != nil { t.Errorf("保存图像失败: %v", err) } diff --git a/model/sprite.go b/sprite/sprite.go similarity index 97% rename from model/sprite.go rename to sprite/sprite.go index dac8748..8220674 100644 --- a/model/sprite.go +++ b/sprite/sprite.go @@ -1,4 +1,4 @@ -package model +package sprite import ( "image" @@ -199,6 +199,11 @@ func (s *Sprite) Project(projectMatrix *ProjectMatrix) { s.Image = newImage } +func (s *Sprite) DrawLine(line *Line) { + // 使用Line.ToSprite方法在当前精灵上绘制线条 + line.AddToSprite(s) +} + // getImageBytes 尝试提取图像的原始字节数据以加速访问 func getImageBytes(img image.Image) (*image.RGBA, []uint8, bool) { if rgba, ok := img.(*image.RGBA); ok { diff --git a/util/math.go b/util/math.go new file mode 100644 index 0000000..2e8144f --- /dev/null +++ b/util/math.go @@ -0,0 +1,25 @@ +package util + +// 辅助函数:绝对值 +func Abs(x int) int { + if x < 0 { + return -x + } + return x +} + +// 辅助函数:最小值 +func Min(a, b int) int { + if a < b { + return a + } + return b +} + +// 辅助函数:最大值 +func Max(a, b int) int { + if a > b { + return a + } + return b +}