package main
import (
"fmt"
"math/bits"
"os"
"strings"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("please specify a seed string")
return
}
fmt.Println(makeAvatar(os.Args[1]))
}
func makeAvatar(seedString string) string {
var seed uint64
for _, c := range []byte(seedString) {
seed = bits.RotateLeft64(seed, 8)
seed ^= uint64(c)
}
return femaleAvatar(seed)
}
/**
* Based on TypeScript DiceBear Avatars, which in turn were inspired by 8biticon avatars:
* (MIT License, Copyright (c) 2012 Plastic Jam, Copyright (c) 2019 DiceBear)
* cf. https://github.com/DiceBear/avatars/blob/master/packages/avatars-male-sprites/src/index.ts
*/
type LCG struct {
seed uint64
}
func (g *LCG) random() uint32 {
/* Linear Congruent Generator, POSIX/glibc [de]rand48 setting, bits [47..0] are output bits */
g.seed = (25214903917*g.seed + 11) % 281474976710656
return uint32(g.seed)
}
func (g *LCG) binomial(p float32) bool {
/* Sample from Binomial distribution with probability p */
var sample = float32(g.random()) * float32(1.0/4294967295.0)
return sample > p
}
func (g *LCG) pickOne(s []string) string {
/* Pick one element from list */
var N = uint32(len(s))
return s[g.random()%N]
}
func (g *LCG) pickOneFloat(s []float32) float32 {
/* Pick one element from list - float version*/
var N = uint32(len(s))
return s[g.random()%N]
}
func (g *LCG) pickAorB(p float32, a string, b string) string {
/* Pick a or b with probability p of picking a */
if g.binomial(p) {
return a
}
return b
}
func LinearCongruentialGenerator(seed uint64) *LCG {
g := new(LCG)
g.seed = seed
return g
}
type COLOR struct {
r uint8
g uint8
b uint8
}
func Color(s string) *COLOR {
c := new(COLOR)
fmt.Sscanf(s, "#%02x%02x%02x", c.r, c.g, c.b)
return c
}
func add255(x uint8, y uint8) uint8 {
if x < 255-y {
return x + y
}
return 255
}
func sub255(x uint8, y uint8) uint8 {
if x > y {
return x - y
}
return 0
}
func (c *COLOR) brighterOrDarkerThan(ref COLOR, delta uint8) string {
/* XXX FIXME: THIS IS PROBABLY NOT CORRECT. Need to look into original implementation. */
if true {
return fmt.Sprintf("#%02x%02x%02x", add255(c.r, delta), add255(c.g, delta), add255(c.b, delta))
}
return ref.darkerThan(ref, delta)
}
func (c *COLOR) darkerThan(ref COLOR, delta uint8) string {
/* XXX FIXME: THIS IS NOT CORRECT. The original implementation does darkening in HSV space.
* cf. https://github.com/DiceBear/avatars/blob/master/packages/avatars/src/color.ts
*/
return fmt.Sprintf("#%02x%02x%02x", sub255(c.r, delta), sub255(c.g, delta), sub255(c.b, delta))
}
func (c COLOR) withAlpha(alpha float32) string {
return fmt.Sprintf("#%02x%02x%02x", c.r, c.g, c.b, uint8(255*alpha))
}
func maleAvatar(seed uint64) string {
var g = LinearCongruentialGenerator(seed)
var skinColor = g.pickOne([]string{"#FFDBAC", "#F5CFA0", "#EAC393", "#E0B687", "#CB9E6E", "#B68655", "#A26D3D", "#8D5524"})
var hairColor = Color(g.pickOne([]string{"#090806", "#2c222b", "#71635a", "#b7a69e", "#b89778", "#a56b46", "#b55239", "#8d4a43",
"#91553d", "#533d32", "#3b3024", "#554838", "#4e433f", "#504444", "#6a4e42", "#a7856a", "#977961"})).brighterOrDarkerThan(*Color(skinColor), 17)
var eyesColor = g.pickOne([]string{"#76778b", "#697b94", "#647b90", "#5b7c8b", "#588387"})
var eyebrowsColor = Color(Color(hairColor).darkerThan(*Color(skinColor), 7)).darkerThan(*Color(hairColor), 10)
var mustacheColor = Color(Color(hairColor).darkerThan(*Color(skinColor), 7)).withAlpha(g.pickOneFloat([]float32{1, 0.75, 0.5}))
var mouthColor = Color(g.pickOne([]string{"#eec1ad", "#dbac98", "#d29985"})).brighterOrDarkerThan(*Color(skinColor), 10)
var glassesColor = g.pickOne([]string{"#5f705c", "#43677d", "#5e172d", "#ffb67a", "#a04b5d", "#191919", "#323232", "#4b4b4b"})
var clothesColor = g.pickOne([]string{"#5bc0de", "#5cb85c", "#428bca", "#03396c", "#005b96", "#6497b1", "#1b85b8", "#5a5255", "#559e83", "#ae5a41", "#c3cb71", "#666547", "#ffe28a"})
var hatColor = g.pickOne([]string{"#18293b", "#2e1e05", "#989789", "#3d6ba7", "#517459", "#a62116"})
var mood = ""
if mood == "" {
mood = g.pickOne([]string{"sad", "happy", "surprised"})
}
var mouth string
if mood == "sad" {
mouth = "" +
"" +
"" +
"" +
""
} else if mood == "happy" {
mouth = "" +
"" +
"" +
""
} else if mood == "surprised" {
mouth = "" +
""
}
var s = strings.Join([]string{
"",
}, "")
m := []string{
"${skinColor}", skinColor,
"${hairColor}", hairColor,
"${eyesColor}", eyesColor,
"${eyebrowsColor}", eyebrowsColor,
"${mustacheColor}", mustacheColor,
"${mouthColor}", mouthColor,
"${glassesColor}", glassesColor,
"${clothesColor}", clothesColor,
"${hatColor}", hatColor,
}
return strings.NewReplacer(m...).Replace(s)
}
func femaleAvatar(seed uint64) string {
var g = LinearCongruentialGenerator(seed)
var skinColor = g.pickOne([]string{"#FFDBAC", "#F5CFA0", "#EAC393", "#E0B687", "#CB9E6E", "#B68655", "#A26D3D", "#8D5524"})
var hairColor = Color(g.pickOne([]string{"#090806", "#2c222b", "#71635a", "#b7a69e", "#d6c4c2", "#cabfb1", "#dcd0ba", "#fff5e1",
"#e6cea8", "#e5c8a8", "#debc99", "#b89778", "#a56b46", "#b55239", "#8d4a43", "#91553d",
"#533d32", "#3b3024", "#554838", "#4e433f", "#504444", "#6a4e42", "#a7856a", "#977961"})).brighterOrDarkerThan(*Color(skinColor), 17)
var eyesColor = g.pickOne([]string{"#76778b", "#697b94", "#647b90", "#5b7c8b", "#588387"})
var eyebrowsColor = Color(Color(hairColor).darkerThan(*Color(skinColor), 7)).darkerThan(*Color(hairColor), 10)
var accessoriesColor = g.pickOne([]string{"#daa520", "#ffd700", "#eee8aa", "#fafad2", "#d3d3d3", "#a9a9a9"})
var mouthColor = Color(g.pickOne([]string{"#dbac98", "#d29985", "#c98276", "#e35d6a", "#e32153", "#de0f0d"})).brighterOrDarkerThan(*Color(skinColor), 10)
var glassesColor = g.pickOne([]string{"#5f705c", "#43677d", "#5e172d", "#ffb67a", "#a04b5d", "#191919", "#323232", "#4b4b4b"})
var clothesColor = g.pickOne([]string{"#d11141", "#00b159", "#00aedb", "#f37735", "#ffc425", "#740001", "#ae0001", "#eeba30",
"#96ceb4", "#ffeead", "#ff6f69", "#ffcc5c", "#88d8b0"})
var hatColor = g.pickOne([]string{"#cc6192", "#2663a3", "#a62116", "#3d8a6b", "#614f8a"})
var mood = ""
if mood == "" {
mood = g.pickOne([]string{"sad", "happy", "surprised"})
}
var mouth string
if mood == "sad" {
mouth = "" +
"" +
"" +
""
} else if mood == "happy" {
mouth = "" +
"" +
"" +
"" +
"" +
"" +
""
} else if mood == "surprised" {
mouth = "" +
""
}
var s = strings.Join([]string{
"",
}, "")
m := []string{
"${skinColor}", skinColor,
"${hairColor}", hairColor,
"${eyesColor}", eyesColor,
"${eyebrowsColor}", eyebrowsColor,
"${accessoriesColor}", accessoriesColor,
"${mouthColor}", mouthColor,
"${glassesColor}", glassesColor,
"${clothesColor}", clothesColor,
"${hatColor}", hatColor,
}
return strings.NewReplacer(m...).Replace(s)
}