From db04c37f2662d4a806e2ffb884c4c8c0f0a0ccad Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 9 Nov 2024 18:39:47 +0900 Subject: [PATCH] vector: add (*Path).ApplyGeoM Closes #3150 Closes #3159 --- examples/fontvector/main.go | 36 ++++----------------------------- examples/vector/main.go | 40 ++++--------------------------------- vector/path.go | 21 +++++++++++++++++++ 3 files changed, 29 insertions(+), 68 deletions(-) diff --git a/examples/fontvector/main.go b/examples/fontvector/main.go index d57ad7e737e9..ce2da0c3dd6c 100644 --- a/examples/fontvector/main.go +++ b/examples/fontvector/main.go @@ -16,7 +16,6 @@ package main import ( "bytes" - "image" "image/color" "log" "math" @@ -27,28 +26,13 @@ import ( "github.com/hajimehoshi/ebiten/v2/vector" ) -var ( - whiteImage = ebiten.NewImage(3, 3) - - // whiteSubImage is an internal sub image of whiteImage. - // Use whiteSubImage at DrawTriangles instead of whiteImage in order to avoid bleeding edges. - whiteSubImage = whiteImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image) -) - -func init() { - whiteImage.Fill(color.White) -} - const ( screenWidth = 640 screenHeight = 480 ) type Game struct { - path vector.Path - vertices []ebiten.Vertex - indices []uint16 - + path vector.Path tick int } @@ -72,25 +56,13 @@ func (g *Game) Update() error { } func (g *Game) Draw(screen *ebiten.Image) { - g.vertices = g.vertices[:0] - g.indices = g.indices[:0] - op := &vector.StrokeOptions{} op.Width = 2*(float32(math.Sin(float64(g.tick)*2*math.Pi/180))+1) + 1 op.LineJoin = vector.LineJoinRound op.LineCap = vector.LineCapRound - g.vertices, g.indices = g.path.AppendVerticesAndIndicesForStroke(g.vertices, g.indices, op) - - for i := range g.vertices { - g.vertices[i].DstX += 50 - g.vertices[i].DstY += 0 - g.vertices[i].SrcX = 1 - g.vertices[i].SrcY = 1 - } - - screen.DrawTriangles(g.vertices, g.indices, whiteSubImage, &ebiten.DrawTrianglesOptions{ - AntiAlias: true, - }) + var geoM ebiten.GeoM + geoM.Translate(50, 0) + vector.StrokePath(screen, g.path.ApplyGeoM(geoM), color.White, true, op) } func (*Game) Layout(width, height int) (int, int) { diff --git a/examples/vector/main.go b/examples/vector/main.go index d8a5909f043d..5b86924c1359 100644 --- a/examples/vector/main.go +++ b/examples/vector/main.go @@ -16,7 +16,6 @@ package main import ( "fmt" - "image" "image/color" "log" "math" @@ -27,18 +26,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/vector" ) -var ( - whiteImage = ebiten.NewImage(3, 3) - - // whiteSubImage is an internal sub image of whiteImage. - // Use whiteSubImage at DrawTriangles instead of whiteImage in order to avoid bleeding edges. - whiteSubImage = whiteImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image) -) - -func init() { - whiteImage.Fill(color.White) -} - const ( screenWidth = 640 screenHeight = 480 @@ -49,9 +36,6 @@ type Game struct { aa bool line bool - - vertices []ebiten.Vertex - indices []uint16 } func (g *Game) drawEbitenText(screen *ebiten.Image, x, y int, aa bool, line bool) { @@ -162,32 +146,16 @@ func (g *Game) drawEbitenLogo(screen *ebiten.Image, x, y int, aa bool, line bool path.LineTo(unit, 4*unit) path.Close() + var geoM ebiten.GeoM + geoM.Translate(float64(x), float64(y)) if line { op := &vector.StrokeOptions{} op.Width = 5 op.LineJoin = vector.LineJoinRound - // TODO: Use vector.StrokePath, but this requries to 'shift' the path by (x, y). - g.vertices, g.indices = path.AppendVerticesAndIndicesForStroke(g.vertices[:0], g.indices[:0], op) + vector.StrokePath(screen, path.ApplyGeoM(geoM), color.RGBA{0xdb, 0x56, 0x20, 0xff}, aa, op) } else { - // TODO: Use vector.DrawFilledPath, but this requries to 'shift' the path by (x, y). - g.vertices, g.indices = path.AppendVerticesAndIndicesForFilling(g.vertices[:0], g.indices[:0]) - } - - for i := range g.vertices { - g.vertices[i].DstX = (g.vertices[i].DstX + float32(x)) - g.vertices[i].DstY = (g.vertices[i].DstY + float32(y)) - g.vertices[i].SrcX = 1 - g.vertices[i].SrcY = 1 - g.vertices[i].ColorR = 0xdb / float32(0xff) - g.vertices[i].ColorG = 0x56 / float32(0xff) - g.vertices[i].ColorB = 0x20 / float32(0xff) - g.vertices[i].ColorA = 1 + vector.DrawFilledPath(screen, path.ApplyGeoM(geoM), color.RGBA{0xdb, 0x56, 0x20, 0xff}, aa, vector.FillRuleNonZero) } - - op := &ebiten.DrawTrianglesOptions{} - op.AntiAlias = aa - op.FillRule = ebiten.FillRuleNonZero - screen.DrawTriangles(g.vertices, g.indices, whiteSubImage, op) } func (g *Game) drawArc(screen *ebiten.Image, count int, aa bool, line bool) { diff --git a/vector/path.go b/vector/path.go index 60eeae3f22ee..cee000e44971 100644 --- a/vector/path.go +++ b/vector/path.go @@ -109,6 +109,7 @@ func (s *subpath) close() { type Path struct { ops []op + // subpaths is a cached actual rendering positions. subpaths []subpath } @@ -527,6 +528,26 @@ func (p *Path) AppendVerticesAndIndicesForFilling(vertices []ebiten.Vertex, indi return vertices, indices } +// ApplyGeoM applies the given GeoM to the path and returns a new path. +func (p *Path) ApplyGeoM(geoM ebiten.GeoM) *Path { + // subpaths are not copied. + np := &Path{ + ops: make([]op, len(p.ops)), + } + for i, o := range p.ops { + x1, y1 := geoM.Apply(float64(o.p1.x), float64(o.p1.y)) + x2, y2 := geoM.Apply(float64(o.p2.x), float64(o.p2.y)) + x3, y3 := geoM.Apply(float64(o.p3.x), float64(o.p3.y)) + np.ops[i] = op{ + typ: o.typ, + p1: point{x: float32(x1), y: float32(y1)}, + p2: point{x: float32(x2), y: float32(y2)}, + p3: point{x: float32(x3), y: float32(y3)}, + } + } + return np +} + // LineCap represents the way in which how the ends of the stroke are rendered. type LineCap int