Created
May 5, 2024 12:16
-
-
Save Zyko0/7bbb7cbac6625d10da903ac90cbb3758 to your computer and use it in GitHub Desktop.
Ribbon
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"image/color" | |
"log" | |
"math" | |
"github.com/hajimehoshi/ebiten/v2" | |
"github.com/hajimehoshi/ebiten/v2/ebitenutil" | |
"github.com/hajimehoshi/ebiten/v2/inpututil" | |
"github.com/hajimehoshi/ebiten/v2/vector" | |
) | |
const ( | |
ScreenWidth, ScreenHeight = 512, 512 | |
) | |
var whiteImg = ebiten.NewImage(3, 3) | |
func init() { | |
whiteImg.Fill(color.White) | |
} | |
type Game struct{} | |
type Vector struct { | |
X float32 | |
Y float32 | |
} | |
func (v *Vector) Normalize() { | |
mag := float32(math.Sqrt(float64(v.X*v.X + v.Y*v.Y))) | |
v.X /= mag | |
v.Y /= mag | |
} | |
var ( | |
boxIndices = [6]uint16{0, 1, 2, 1, 2, 3} | |
) | |
func AppendQuad(vertices []ebiten.Vertex, indices []uint16, index int, quads [2][2]Vector) ([]ebiten.Vertex, []uint16) { | |
vertices = append(vertices, | |
ebiten.Vertex{ | |
DstX: quads[0][0].X, | |
DstY: quads[0][0].Y, | |
ColorR: 1, | |
ColorG: 1, | |
ColorB: 1, | |
ColorA: 1, | |
}, | |
ebiten.Vertex{ | |
DstX: quads[0][1].X, | |
DstY: quads[0][1].Y, | |
ColorR: 1, | |
ColorG: 1, | |
ColorB: 1, | |
ColorA: 1, | |
}, | |
ebiten.Vertex{ | |
DstX: quads[1][0].X, | |
DstY: quads[1][0].Y, | |
ColorR: 1, | |
ColorG: 1, | |
ColorB: 1, | |
ColorA: 1, | |
}, | |
ebiten.Vertex{ | |
DstX: quads[1][1].X, | |
DstY: quads[1][1].Y, | |
ColorR: 1, | |
ColorG: 1, | |
ColorB: 1, | |
ColorA: 1, | |
}, | |
) | |
idx := uint16(index * 4) | |
indices = append(indices, | |
idx+0, | |
idx+1, | |
idx+2, | |
idx+1, | |
idx+3, | |
idx+2, | |
) | |
return vertices, indices | |
} | |
func lineNorm(dx, dy float32) Vector { | |
nx := Vector{float32(-dy), float32(dx)} | |
ny := Vector{float32(dy), float32(-dx)} | |
if dx < dy { | |
return ny | |
} | |
return nx | |
} | |
func calcPair(p0, p1 Vector) [2]Vector { | |
dx := p0.X - p1.X | |
dy := p0.Y - p1.Y | |
// Normal calculation | |
n := lineNorm(dx, dy) | |
n.Normalize() | |
// Width multiplication and offset it from initial point | |
return [2]Vector{ | |
{ | |
-n.X*width + float32(p0.X), | |
-n.Y*width + float32(p0.Y), | |
}, | |
{ | |
n.X*width + float32(p0.X), | |
n.Y*width + float32(p0.Y), | |
}, | |
} | |
} | |
const ( | |
width = 16 | |
dotRadius = 5 | |
) | |
var ( | |
vx []ebiten.Vertex | |
ix []uint16 | |
points = []Vector{ | |
{64, 256}, {128, 196}, {196, 156}, | |
{256, 96}, {312, 256}, {356, 228}, | |
} | |
) | |
func (g *Game) Update() error { | |
if ebiten.IsKeyPressed(ebiten.KeyEscape) { | |
return ebiten.Termination | |
} | |
// Add a point | |
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { | |
x, y := ebiten.CursorPosition() | |
points = append(points, Vector{float32(x), float32(y)}) | |
} | |
return nil | |
} | |
func (g *Game) Draw(screen *ebiten.Image) { | |
if len(points) <= 1 { | |
return | |
} | |
var pairs [][2]Vector | |
for i := 0; i < len(points)-1; i++ { | |
if i == 0 { | |
pairs = append(pairs, calcPair(points[i], points[i+1])) | |
} else { | |
// Interpolate | |
dx0, dy0 := points[i].X-points[i-1].X, points[i].Y-points[i-1].Y | |
dx1, dy1 := points[i].X-points[i+1].X, points[i].Y-points[i+1].Y | |
ny0 := lineNorm(dx0, dy0) | |
ny1 := lineNorm(dx1, dy1) | |
ny0.Normalize() | |
ny1.Normalize() | |
ny := Vector{ny0.X + ny1.X, ny0.Y + ny1.Y} | |
ny.Normalize() | |
pairs = append(pairs, [2]Vector{ | |
{ | |
-ny.X*width + float32(points[i].X), | |
-ny.Y*width + float32(points[i].Y), | |
}, | |
{ | |
ny.X*width + float32(points[i].X), | |
ny.Y*width + float32(points[i].Y), | |
}, | |
}) | |
} | |
} | |
// Add last one | |
pairs = append(pairs, calcPair(points[len(points)-1], points[len(points)-2])) | |
// Build vertices, indices out of pairs | |
vx, ix = vx[:0], ix[:0] | |
for i := 0; i < len(pairs)-1; i++ { | |
vx, ix = AppendQuad(vx, ix, i, [2][2]Vector{ | |
{pairs[i][0], pairs[i][1]}, | |
{pairs[i+1][0], pairs[i+1][1]}, | |
}) | |
} | |
screen.DrawTriangles(vx, ix, whiteImg, nil) | |
// Debug pairs | |
for _, p := range pairs { | |
vector.StrokeLine(screen, | |
p[0].X, p[0].Y, p[1].X, p[1].Y, 4, color.RGBA{0, 64, 255, 255}, true, | |
) | |
} | |
// Debug segments | |
for i := range points { | |
if i < len(points)-1 { | |
vector.StrokeLine(screen, | |
float32(points[i].X), float32(points[i].Y), | |
float32(points[i+1].X), float32(points[i+1].Y), | |
2, color.RGBA{0, 196, 0, 255}, true, | |
) | |
} | |
} | |
// Debug points | |
for _, p := range points { | |
vector.DrawFilledCircle(screen, | |
float32(p.X), float32(p.Y), dotRadius, color.RGBA{255, 0, 0, 255}, true, | |
) | |
} | |
ebitenutil.DebugPrint(screen, | |
fmt.Sprintf("TPS %.2f - FPS %.2f", | |
ebiten.ActualTPS(), ebiten.ActualFPS(), | |
), | |
) | |
} | |
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { | |
return ScreenWidth, ScreenHeight | |
} | |
func main() { | |
ebiten.SetFullscreen(true) | |
if err := ebiten.RunGame(&Game{}); err != nil { | |
log.Fatal(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment