// Command teleportfling-icon regenerates the application icon assets in // assets/ from scratch. It draws a rounded green square with two lighter // "fling" dots suggesting motion. // // Usage: // // teleportfling-icon // // The generated PNGs are committed to the repo and embedded into the binary // via the assets package; this tool exists so the icon can be tweaked and // regenerated without a design tool. package main import ( "bytes" "image" "image/color" "image/png" "os" ) // sizes are the icon sizes to emit. Hosts scale from these; 512 is used for // the app icon, 256 and 64 for smaller contexts. var sizes = []int{512, 256, 64} // outputDir is where the PNGs are written (the repo assets directory). const outputDir = "assets" // inRoundedRect reports whether (x,y) lies inside a square with rounded // corners of the given radius. func inRoundedRect(x, y, radius, size int) bool { m := size - 1 switch { case x >= radius && x <= m-radius: return true case y >= radius && y <= m-radius: return true } var cx, cy int if x < radius { cx = radius } else { cx = m - radius } if y < radius { cy = radius } else { cy = m - radius } dx, dy := x-cx, y-cy return dx*dx+dy*dy <= radius*radius } // render draws the icon at the given size into a PNG. func render(size int, bg, dot color.NRGBA) []byte { img := image.NewNRGBA(image.Rect(0, 0, size, size)) radius := size / 5 for y := 0; y < size; y++ { for x := 0; x < size; x++ { if inRoundedRect(x, y, radius, size) { img.SetNRGBA(x, y, bg) } } } // Two dots suggesting a flung stream, offset diagonally. for i, off := range []int{3, 9} { cx, cy, r := size/2, size/2+off*size/32, size/8-i for y := cy - r; y <= cy+r; y++ { for x := cx - r; x <= cx+r; x++ { if (x-cx)*(x-cx)+(y-cy)*(y-cy) <= r*r { img.SetNRGBA(x, y, dot) } } } } var buf bytes.Buffer _ = png.Encode(&buf, img) return buf.Bytes() } func main() { bg := color.NRGBA{R: 46, G: 125, B: 50, A: 255} // teleport green dot := color.NRGBA{R: 150, G: 220, B: 140, A: 255} //nolint:gosec // writing generated PNG assets into the repo, not secrets if err := os.MkdirAll(outputDir, 0o755); err != nil { panic(err) } for _, size := range sizes { name := "teleportfling" if size != 512 { name += "-" + itoa(size) } path := outputDir + "/" + name + ".png" //nolint:gosec // generated PNG assets, world-readable for installers if err := os.WriteFile(path, render(size, bg, dot), 0o644); err != nil { panic(err) } } } func itoa(v int) string { if v == 0 { return "0" } var buf [10]byte i := len(buf) for v > 0 { i-- buf[i] = byte('0' + v%10) v /= 10 } return string(buf[i:]) }