clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

feat/tinyagent
tinyrelay / internal / seedmark / seedmark.go
seedmark.go 4.1K
1// Package seedmark renders version 1 of the Seedmark avatar design without2// JavaScript. It follows the upstream generator at c808a94; fixtures pin its3// visual output. Avatars are decorative and are not identity verification.4package seedmark56import (7 "strconv"8 "strings"9 "unicode/utf16"10)1112const Version = 11314var families = [...]string{"masks", "creatures", "botanical", "orbital", "landscapes", "glyphs", "woven", "machines"}1516var palettes = [...][4]string{17 {"#142d36", "#f5dfad", "#ed7351", "#65c4ae"},18 {"#24223c", "#f7dfbf", "#a89cfc", "#f7788b"},19 {"#163b32", "#f4eacb", "#b4cf66", "#f69c64"},20 {"#3a2330", "#ffe5b9", "#ef7689", "#8dceba"},21 {"#1d304c", "#e8eff0", "#f4b855", "#72b9d0"},22 {"#422d26", "#f8e8c8", "#e8a04d", "#72afa0"},23 {"#283526", "#f4edcf", "#d3c65e", "#ed8863"},24 {"#292745", "#efe8f5", "#d89ada", "#8fcabc"},25 {"#18343c", "#f8e4d0", "#f88c66", "#a7cee0"},26 {"#393127", "#f4ebd8", "#adb77a", "#da8c6f"},27 {"#2c2440", "#fbe8c2", "#f2b54f", "#b2a1e2"},28 {"#1b3537", "#e9f0d7", "#79c7b8", "#e5a4aa"},29}3031type recipe struct {32 Family string33 Palette [4]string34 Layout struct{ Variant, Flip, Tilt int }35 Silhouette struct{ Variant, Width, Height int }36 Features struct{ Variant, Count, Accent int }37 Texture struct{ Variant, Marks int }38}3940type rng struct{ a, b, c, d uint32 }4142func newRNG(seed, namespace string) *rng {43 var framed strings.Builder44 for _, part := range []string{"seedmark", "1", seed, namespace} {45 framed.WriteString(strconv.Itoa(len(utf16.Encode([]rune(part)))))46 framed.WriteByte(':')47 framed.WriteString(part)48 }49 a, b, c, d := uint32(1779033703), uint32(3144134277), uint32(1013904242), uint32(2773480762)50 for _, unit := range utf16.Encode([]rune(framed.String())) {51 k := uint32(unit)52 a = b ^ (a^k)*59739906753 b = c ^ (b^k)*286986023354 c = d ^ (c^k)*95127421355 d = a ^ (d^k)*271604417956 }57 a = (c ^ (a >> 18)) * 59739906758 b = (d ^ (b >> 22)) * 286986023359 c = (a ^ (c >> 17)) * 95127421360 d = (b ^ (d >> 19)) * 271604417961 return &rng{a ^ b ^ c ^ d, b ^ a, c ^ a, d ^ a}62}6364func (r *rng) integer(minimum, maximum int) int {65 t := r.a + r.b + r.d66 r.d++67 r.a = r.b ^ (r.b >> 9)68 r.b = r.c + (r.c << 3)69 r.c = ((r.c << 21) | (r.c >> 11)) + t70 return minimum + int(float64(t)/4294967296*float64(maximum-minimum+1))71}7273func recipeFor(seed string) recipe {74 var r recipe75 r.Family = families[newRNG(seed, "family").integer(0, len(families)-1)]76 r.Palette = palettes[newRNG(seed, "palette").integer(0, len(palettes)-1)]77 layout, silhouette := newRNG(seed, "layout"), newRNG(seed, "silhouette")78 features, texture := newRNG(seed, "features"), newRNG(seed, "texture")79 r.Layout.Variant, r.Layout.Flip = layout.integer(0, 3), layout.integer(0, 1)80 r.Layout.Tilt = [...]int{-12, 0, 12}[layout.integer(0, 2)]81 r.Silhouette.Variant = silhouette.integer(0, 3)82 r.Silhouette.Width, r.Silhouette.Height = silhouette.integer(26, 38), silhouette.integer(28, 42)83 r.Features.Variant, r.Features.Count = features.integer(0, 3), features.integer(2, 5)84 r.Features.Accent = features.integer(0, 1)85 r.Texture.Variant, r.Texture.Marks = texture.integer(0, 2), texture.integer(2, 4)86 return r87}8889// Avatar returns a standalone 64-pixel SVG for the full seed. The version 190// geometry remains fixed; CSS may resize the SVG without changing its design.91func Avatar(seed string) string {92 r := recipeFor(seed)93 render := map[string]func(recipe) string{"masks": masks, "creatures": creatures, "botanical": botanical, "orbital": orbital, "landscapes": landscapes, "glyphs": glyphs, "woven": woven, "machines": machines}94 texture := ""95 for i := 0; i < r.Texture.Marks; i++ {96 x := float64(8 + i*6)97 if r.Texture.Variant == 1 {98 texture += circle(x, 6, 1.5, r.Palette[3])99 } else if r.Texture.Variant == 2 {100 texture += rect(x-1, 5, 3, 3, 0, r.Palette[2])101 }102 }103 flip := "1"104 if r.Layout.Flip != 0 {105 flip = "-1"106 }107 transform := "translate(32 32) rotate(" + strconv.Itoa(r.Layout.Tilt) + ") scale(" + flip + " 1) translate(-32 -32)"108 return `<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><rect width="64" height="64" fill="` + r.Palette[0] + `"/>` + texture + group(render[r.Family](r), transform) + `</svg>`109}