90 lines
2.0 KiB
Go
90 lines
2.0 KiB
Go
//go:build ignore
|
|||
|
|
|
||
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"image"
|
||
|
|
"image/color"
|
||
|
|
"image/gif"
|
||
|
|
"log"
|
||
|
|
"math"
|
||
|
|
"os"
|
||
|
|
)
|
||
|
|
|
||
|
|
const (
|
||
|
|
width = 16
|
||
|
|
height = 32
|
||
|
|
radius = 2.6
|
||
|
|
levels = 16
|
||
|
|
samples = 4
|
||
|
|
delay = 8
|
||
|
|
)
|
||
|
|
|
||
|
|
var frames = []rune("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏")
|
||
|
|
|
||
|
|
var dots = [6][2]float64{{4.5, 8}, {4.5, 16}, {4.5, 24}, {11.5, 8}, {11.5, 16}, {11.5, 24}}
|
||
|
|
|
||
|
|
func main() {
|
||
|
|
render("img/spinner-light.gif", color.RGBA{0x1a, 0x45, 0xbd, 0xff}, color.RGBA{0xfb, 0xfb, 0xfa, 0xff})
|
||
|
|
render("img/spinner-dark.gif", color.RGBA{0x8a, 0xb4, 0xff, 0xff}, color.RGBA{0x15, 0x17, 0x1a, 0xff})
|
||
|
|
}
|
||
|
|
|
||
|
|
func render(path string, fg, bg color.RGBA) {
|
||
|
|
palette := color.Palette{color.RGBA{}}
|
||
|
|
for i := 1; i <= levels; i++ {
|
||
|
|
palette = append(palette, blend(bg, fg, float64(i)/levels))
|
||
|
|
}
|
||
|
|
|
||
|
|
anim := &gif.GIF{}
|
||
|
|
for _, r := range frames {
|
||
|
|
bits := int(r - 0x2800)
|
||
|
|
img := image.NewPaletted(image.Rect(0, 0, width, height), palette)
|
||
|
|
for y := 0; y < height; y++ {
|
||
|
|
for x := 0; x < width; x++ {
|
||
|
|
level := int(math.Round(coverage(bits, x, y) * levels))
|
||
|
|
img.SetColorIndex(x, y, uint8(level))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
anim.Image = append(anim.Image, img)
|
||
|
|
anim.Delay = append(anim.Delay, delay)
|
||
|
|
anim.Disposal = append(anim.Disposal, gif.DisposalBackground)
|
||
|
|
}
|
||
|
|
|
||
|
|
f, err := os.Create(path)
|
||
|
|
if err != nil {
|
||
|
|
log.Fatal(err)
|
||
|
|
}
|
||
|
|
defer f.Close()
|
||
|
|
err = gif.EncodeAll(f, anim)
|
||
|
|
if err != nil {
|
||
|
|
log.Fatal(err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func coverage(bits, x, y int) float64 {
|
||
|
|
hit := 0
|
||
|
|
for sy := 0; sy < samples; sy++ {
|
||
|
|
for sx := 0; sx < samples; sx++ {
|
||
|
|
px := float64(x) + (float64(sx)+0.5)/samples
|
||
|
|
py := float64(y) + (float64(sy)+0.5)/samples
|
||
|
|
for i, d := range dots {
|
||
|
|
if bits&(1<<i) == 0 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if math.Hypot(px-d[0], py-d[1]) <= radius {
|
||
|
|
hit++
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return float64(hit) / (samples * samples)
|
||
|
|
}
|
||
|
|
|
||
|
|
func blend(a, b color.RGBA, t float64) color.RGBA {
|
||
|
|
mix := func(p, q uint8) uint8 {
|
||
|
|
return uint8(math.Round(float64(p) + (float64(q)-float64(p))*t))
|
||
|
|
}
|
||
|
|
return color.RGBA{mix(a.R, b.R), mix(a.G, b.G), mix(a.B, b.B), 0xff}
|
||
|
|
}
|