package avatar import ( "bytes" "encoding/binary" "image/png" "math/rand" "clapclap/internal/uuid" "github.com/fogleman/gg" ) func Generate(u uuid.UUID, size int, gridSize int) (*bytes.Buffer, error) { // 1. Convert the first 8 bytes of the UUID into an int64 seed seed := int64(binary.BigEndian.Uint64(u.UUID[:8])) // 2. Initialize a local random generator with this specific seed rng := rand.New(rand.NewSource(seed)) // 3. Pastel Background using our deterministic rng bgH := rng.Float64() bgL := rng.Float64()*0.15 + 0.80 // 0.8 to 0.95 bgS := rng.Float64()*0.30 + 0.40 // 0.4 to 0.7 bgR, bgG, bgB := hslToRGB(bgH, bgS, bgL) dc := gg.NewContext(size, size) dc.SetRGB255(bgR, bgG, bgB) dc.Clear() padding := 40.0 gridAreaSize := float64(size) - (padding * 2) blockSize := gridAreaSize / float64(gridSize) mirrorMap := map[int]int{0: 0, 1: 2, 2: 1, 3: 4, 4: 3, -1: -1} compHBase := bgH + 0.5 if compHBase > 1.0 { compHBase -= 1.0 } gridShapes := make([][]int, gridSize) gridColors := make([][][3]int, gridSize) // 4. Build Grid Data for row := range gridSize { var rowShapesLeft []int var rowColorsLeft [][3]int for col := 0; col < gridSize/2+1; col++ { if rng.Float64() < 0.6 { rowShapesLeft = append(rowShapesLeft, rng.Intn(5)) shapeH := compHBase + (rng.Float64()*0.2 - 0.1) if shapeH > 1.0 { shapeH -= 1.0 } if shapeH < 0.0 { shapeH += 1.0 } shapeL := rng.Float64()*0.2 + 0.4 shapeS := rng.Float64()*0.3 + 0.6 sR, sG, sB := hslToRGB(shapeH, shapeS, shapeL) rowColorsLeft = append(rowColorsLeft, [3]int{sR, sG, sB}) } else { rowShapesLeft = append(rowShapesLeft, -1) rowColorsLeft = append(rowColorsLeft, [3]int{0, 0, 0}) } } gridShapes[row] = append([]int(nil), rowShapesLeft...) gridColors[row] = append([][3]int(nil), rowColorsLeft...) for colIdx := (gridSize / 2) - 1; colIdx >= 0; colIdx-- { gridShapes[row] = append(gridShapes[row], mirrorMap[rowShapesLeft[colIdx]]) gridColors[row] = append(gridColors[row], rowColorsLeft[colIdx]) } } // 5. Draw Shapes for row := range gridSize { for col := range gridSize { shapeType := gridShapes[row][col] if shapeType == -1 { continue } c := gridColors[row][col] dc.SetRGB255(c[0], c[1], c[2]) left := padding + float64(col)*blockSize top := padding + float64(row)*blockSize right := left + blockSize bottom := top + blockSize switch shapeType { case 0: dc.DrawRectangle(left, top, blockSize, blockSize) case 1: dc.MoveTo(left, top) dc.LineTo(right, top) dc.LineTo(left, bottom) dc.ClosePath() case 2: dc.MoveTo(left, top) dc.LineTo(right, top) dc.LineTo(right, bottom) dc.ClosePath() case 3: dc.MoveTo(right, top) dc.LineTo(right, bottom) dc.LineTo(left, bottom) dc.ClosePath() case 4: dc.MoveTo(left, top) dc.LineTo(right, bottom) dc.LineTo(left, bottom) dc.ClosePath() } dc.Fill() } } // 6. Encode to PNG buffer buf := new(bytes.Buffer) if err := png.Encode(buf, dc.Image()); err != nil { return nil, err } return buf, nil } // Helper function to convert HSL to RGB func hslToRGB(h, s, l float64) (r, g, b int) { var r1, g1, b1 float64 if s == 0 { r1, g1, b1 = l, l, l } else { var q float64 if l < 0.5 { q = l * (1 + s) } else { q = l + s - l*s } p := 2*l - q r1 = hueToRGB(p, q, h+1.0/3.0) g1 = hueToRGB(p, q, h) b1 = hueToRGB(p, q, h-1.0/3.0) } return int(r1 * 255), int(g1 * 255), int(b1 * 255) } func hueToRGB(p, q, t float64) float64 { if t < 0 { t += 1 } if t > 1 { t -= 1 } if t < 1.0/6.0 { return p + (q-p)*6*t } if t < 1.0/2.0 { return q } if t < 2.0/3.0 { return p + (q-p)*(2.0/3.0-t)*6 } return p }