167 lines
3.9 KiB
Go
167 lines
3.9 KiB
Go
package ui
|
|
|
|
import (
|
|
"image/color"
|
|
"math"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
"github.com/hajimehoshi/ebiten/v2/text"
|
|
"golang.org/x/image/font/basicfont"
|
|
)
|
|
|
|
var whitePixel = func() *ebiten.Image {
|
|
img := ebiten.NewImage(1, 1)
|
|
img.Fill(color.White)
|
|
return img
|
|
}()
|
|
|
|
func Rect(screen *ebiten.Image, x, y, w, h float32, clr color.Color) {
|
|
ebitenutil.DrawRect(screen, float64(x), float64(y), float64(w), float64(h), clr)
|
|
}
|
|
|
|
func RectOutline(screen *ebiten.Image, x, y, w, h, stroke float32, clr color.Color) {
|
|
if w <= 0 || h <= 0 || stroke <= 0 {
|
|
return
|
|
}
|
|
if stroke > w {
|
|
stroke = w
|
|
}
|
|
if stroke > h {
|
|
stroke = h
|
|
}
|
|
Rect(screen, x, y, w, stroke, clr)
|
|
Rect(screen, x, y+h-stroke, w, stroke, clr)
|
|
Rect(screen, x, y, stroke, h, clr)
|
|
Rect(screen, x+w-stroke, y, stroke, h, clr)
|
|
}
|
|
|
|
func Line(screen *ebiten.Image, x1, y1, x2, y2, width float32, clr color.Color) {
|
|
dx := x2 - x1
|
|
dy := y2 - y1
|
|
length := float32(math.Hypot(float64(dx), float64(dy)))
|
|
if length <= 0 || width <= 0 {
|
|
return
|
|
}
|
|
|
|
r, g, b, a := clr.RGBA()
|
|
op := &ebiten.DrawImageOptions{}
|
|
op.GeoM.Scale(float64(length), float64(width))
|
|
op.GeoM.Translate(0, -float64(width)/2)
|
|
op.GeoM.Rotate(math.Atan2(float64(dy), float64(dx)))
|
|
op.GeoM.Translate(float64(x1), float64(y1))
|
|
op.ColorM.Scale(
|
|
float64(r)/0xffff,
|
|
float64(g)/0xffff,
|
|
float64(b)/0xffff,
|
|
float64(a)/0xffff,
|
|
)
|
|
screen.DrawImage(whitePixel, op)
|
|
}
|
|
|
|
func Circle(screen *ebiten.Image, x, y, r float32, clr color.Color) {
|
|
if r <= 0 {
|
|
return
|
|
}
|
|
ri := int(math.Ceil(float64(r)))
|
|
for dy := -ri; dy <= ri; dy++ {
|
|
fy := float64(dy)
|
|
if math.Abs(fy) > float64(r) {
|
|
continue
|
|
}
|
|
halfWidth := math.Sqrt(float64(r)*float64(r) - fy*fy)
|
|
ebitenutil.DrawRect(
|
|
screen,
|
|
float64(x)-halfWidth,
|
|
float64(y)+fy,
|
|
halfWidth*2,
|
|
1,
|
|
clr,
|
|
)
|
|
}
|
|
}
|
|
|
|
func Triangle(screen *ebiten.Image, x1, y1, x2, y2, x3, y3 float32, clr color.Color) {
|
|
Polygon(screen, []Point{{x1, y1}, {x2, y2}, {x3, y3}}, clr)
|
|
}
|
|
|
|
func Star(screen *ebiten.Image, x, y, outer, inner float32, points int, clr color.Color) {
|
|
if points < 3 {
|
|
points = 5
|
|
}
|
|
poly := make([]Point, 0, points*2)
|
|
for i := 0; i < points*2; i++ {
|
|
r := outer
|
|
if i%2 == 1 {
|
|
r = inner
|
|
}
|
|
a := -math.Pi/2 + float64(i)*math.Pi/float64(points)
|
|
poly = append(poly, Point{
|
|
X: x + float32(math.Cos(a))*r,
|
|
Y: y + float32(math.Sin(a))*r,
|
|
})
|
|
}
|
|
for i := 0; i < len(poly); i++ {
|
|
next := (i + 1) % len(poly)
|
|
Triangle(screen, x, y, poly[i].X, poly[i].Y, poly[next].X, poly[next].Y, clr)
|
|
}
|
|
}
|
|
|
|
type Point struct {
|
|
X float32
|
|
Y float32
|
|
}
|
|
|
|
func Polygon(screen *ebiten.Image, points []Point, clr color.Color) {
|
|
if len(points) < 3 {
|
|
return
|
|
}
|
|
r, g, b, a := clr.RGBA()
|
|
vertices := make([]ebiten.Vertex, len(points))
|
|
for i, p := range points {
|
|
vertices[i] = ebiten.Vertex{
|
|
DstX: p.X,
|
|
DstY: p.Y,
|
|
SrcX: 0,
|
|
SrcY: 0,
|
|
ColorR: float32(r) / 0xffff,
|
|
ColorG: float32(g) / 0xffff,
|
|
ColorB: float32(b) / 0xffff,
|
|
ColorA: float32(a) / 0xffff,
|
|
}
|
|
}
|
|
indices := make([]uint16, 0, (len(points)-2)*3)
|
|
for i := 1; i < len(points)-1; i++ {
|
|
indices = append(indices, 0, uint16(i), uint16(i+1))
|
|
}
|
|
screen.DrawTriangles(vertices, indices, whitePixel, nil)
|
|
}
|
|
|
|
func Text(screen *ebiten.Image, s string, x, y int, clr color.Color, scale float64) {
|
|
drawText(screen, s, x, y, clr, scale, false)
|
|
}
|
|
|
|
func CenteredText(screen *ebiten.Image, s string, cx, cy int, clr color.Color, scale float64) {
|
|
drawText(screen, s, cx, cy, clr, scale, true)
|
|
}
|
|
|
|
func drawText(screen *ebiten.Image, s string, x, y int, clr color.Color, scale float64, centered bool) {
|
|
if scale <= 0 {
|
|
scale = 1
|
|
}
|
|
w := max(1, len(s)*7+4)
|
|
h := 17
|
|
img := ebiten.NewImage(w, h)
|
|
text.Draw(img, s, basicfont.Face7x13, 2, 13, clr)
|
|
|
|
op := &ebiten.DrawImageOptions{}
|
|
op.GeoM.Scale(scale, scale)
|
|
tx, ty := float64(x), float64(y)
|
|
if centered {
|
|
tx -= float64(w) * scale / 2
|
|
ty -= float64(h) * scale / 2
|
|
}
|
|
op.GeoM.Translate(tx, ty)
|
|
screen.DrawImage(img, op)
|
|
}
|