Skip to content

Commit

Permalink
feat: migrate to boring avatars
Browse files Browse the repository at this point in the history
  • Loading branch information
LLaammTTeerr committed Aug 15, 2024
1 parent fddf60c commit 49f5323
Show file tree
Hide file tree
Showing 19 changed files with 680 additions and 23 deletions.
8 changes: 8 additions & 0 deletions judge/boring_avatars/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from judge.boring_avatars.avatar import Avatar
from judge.boring_avatars.avatar_bauhaus import AvatarBauhaus
from judge.boring_avatars.avatar_beam import AvatarBeam
from judge.boring_avatars.avatar_marble import AvatarMarble
from judge.boring_avatars.avatar_pixel import AvatarPixel
from judge.boring_avatars.avatar_ring import AvatarRing
from judge.boring_avatars.avatar_sunset import AvatarSunset
from judge.boring_avatars.utilities import AvatarProperties
23 changes: 23 additions & 0 deletions judge/boring_avatars/avatar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from judge.boring_avatars.avatar_bauhaus import AvatarBauhaus
from judge.boring_avatars.avatar_beam import AvatarBeam
from judge.boring_avatars.avatar_marble import AvatarMarble
from judge.boring_avatars.avatar_pixel import AvatarPixel
from judge.boring_avatars.avatar_ring import AvatarRing
from judge.boring_avatars.avatar_sunset import AvatarSunset

DEFAULT_GENRE = 'beam'
MATCH_GENRE_FROM_STRING = {
'bauhaus': AvatarBauhaus,
'beam': AvatarBeam,
'marble': AvatarMarble,
'pixel': AvatarPixel,
'ring': AvatarRing,
'sunset': AvatarSunset,
}


def Avatar(props):
if props.genre not in MATCH_GENRE_FROM_STRING:
props.genre = DEFAULT_GENRE
avatar = MATCH_GENRE_FROM_STRING[props.genre]
return avatar(props)
100 changes: 100 additions & 0 deletions judge/boring_avatars/avatar_bauhaus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import random

from judge.boring_avatars.utilities import getBoolean, getRandomColor, getUnit, hashCode

ELEMENTS = 4
SIZE = 80


def generateColors(name, colors):
numFromName = hashCode(name)
colorRange = len(colors) if colors else None

elementsProperties = [
{
'color': getRandomColor(numFromName + i, colors, colorRange),
'translateX': getUnit(numFromName * (i + 1), SIZE / 2 - (i + 17), 1),
'translateY': getUnit(numFromName * (i + 1), SIZE / 2 - (i + 17), 2),
'rotate': getUnit(numFromName * (i + 1), 360),
'isSquare': getBoolean(numFromName, 2),
}
for i in range(ELEMENTS)
]

return elementsProperties


def AvatarBauhaus(props):
properties = generateColors(props.name, props.colors)
maskID = random.randrange(10**9, 10**10 - 1)

svg = f"""
<svg
viewBox="0 0 {SIZE} {SIZE}"
style="display: block; {props.style}"
fill="none"
role="img"
xmlns="http://www.w3.org/2000/svg"
width="{props.size}"
height="{props.size}"
>
{'<title>' + props.name + '</title>' if props.title else ''}
<mask id="{maskID}" maskUnits="userSpaceOnUse" x="0" y="0" width="{SIZE}" height="{SIZE}">
<rect width="{SIZE}" height="{SIZE}" {'rx="' + str(props.size * 2) + '"' if not props.square else ''}
fill="#FFFFFF" />
</mask>
<g mask="url(#{maskID})">
<rect width="{SIZE}" height="{SIZE}" fill="{properties[0]['color']}" />
<rect
x="{(SIZE - 60) / 2}"
y="{(SIZE - 20) / 2}"
width="{SIZE}"
height="{SIZE if properties[1]['isSquare'] else SIZE / 8}"
fill="{properties[1]['color']}"
transform="{
'translate(' +
str(properties[1]['translateX']) +
' ' +
str(properties[1]['translateY']) +
') rotate(' +
str(properties[1]['rotate']) +
' ' +
str(SIZE / 2) +
' ' +
str(SIZE / 2) +
')'
}"
/>
<circle
cx="{SIZE / 2}"
cy="{SIZE / 2}"
fill="{properties[2]['color']}"
r="{SIZE / 5}"
transform="translate({'' + str(properties[2]['translateX']) + ' ' + str(properties[2]['translateY']) + ''})"
/>
<line
x1="{0}"
y1="{SIZE / 2}"
x2="{SIZE}"
y2="{SIZE / 2}"
strokeWidth="{2}"
stroke="{properties[3]['color']}"
transform="{
'translate(' +
str(properties[3]['translateX']) +
' ' +
str(properties[3]['translateY']) +
') rotate(' +
str(properties[3]['rotate']) +
' ' +
str(SIZE / 2) +
' ' +
str(SIZE / 2) +
')'
}"
/>
</g>
</svg>
"""

return svg
108 changes: 108 additions & 0 deletions judge/boring_avatars/avatar_beam.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import random
from types import SimpleNamespace

from judge.boring_avatars.utilities import getBoolean, getContrast, getRandomColor, getUnit, hashCode

SIZE = 36


def generateData(name, colors):
numFromName = hashCode(name)
colorRange = len(colors) if colors else None
wrapperColor = getRandomColor(numFromName, colors, colorRange)
preTranslateX = getUnit(numFromName, 10, 1)
wrapperTranslateX = preTranslateX + SIZE / 9 if preTranslateX < 5 else preTranslateX
preTranslateY = getUnit(numFromName, 10, 2)
wrapperTranslateY = preTranslateY + SIZE / 9 if preTranslateY < 5 else preTranslateY

data = SimpleNamespace(
wrapperColor=wrapperColor,
faceColor=getContrast(wrapperColor),
backgroundColor=getRandomColor(numFromName + 13, colors, colorRange),
wrapperTranslateX=wrapperTranslateX,
wrapperTranslateY=wrapperTranslateY,
wrapperRotate=getUnit(numFromName, 360),
wrapperScale=1 + getUnit(numFromName, SIZE // 12) / 10,
isMouthOpen=getBoolean(numFromName, 2),
isCircle=getBoolean(numFromName, 1),
eyeSpread=getUnit(numFromName, 5),
mouthSpread=getUnit(numFromName, 3),
faceRotate=getUnit(numFromName, 10, 3),
faceTranslateX=wrapperTranslateX / 2 if wrapperTranslateX > SIZE / 6 else getUnit(numFromName, 8, 1),
faceTranslateY=wrapperTranslateY / 2 if wrapperTranslateY > SIZE / 6 else getUnit(numFromName, 7, 2),
)

return data


def AvatarBeam(props):
data = generateData(props.name, props.colors)
maskID = random.randrange(10**9, 10**10 - 1)

svg = f"""
<svg
viewBox="0 0 {SIZE} {SIZE}"
style="display: block; {props.style}"
fill="none"
role="img"
xmlns="http://www.w3.org/2000/svg"
width="{props.size}"
height="{props.size}"
>
{'<title>' + props.name + '</title>' if props.title else ''}
<mask id="{maskID}" maskUnits="userSpaceOnUse" x="0" y="0" width="{SIZE}" height="{SIZE}">
<rect width="{SIZE}" height="{SIZE}" rx="{None if props.square else SIZE * 2}" fill="#FFFFFF" />
</mask>
<g mask="url(#{maskID})">
<rect width="{SIZE}" height="{SIZE}" fill="{data.backgroundColor}" />
<rect
x="0"
y="0"
width="{SIZE}"
height="{SIZE}"
transform="translate({data.wrapperTranslateX} {data.wrapperTranslateY})
rotate({data.wrapperRotate} {SIZE / 2} {SIZE / 2}) scale({data.wrapperScale})"
fill="{data.wrapperColor}"
rx="{SIZE if data.isCircle else SIZE / 6}"
/>
<g
transform="translate({data.faceTranslateX} {data.faceTranslateY})
rotate({data.faceRotate} {SIZE / 2} {SIZE / 2})"
>
{f'''
<path
d="M15 {19 + data.mouthSpread}c2 1 4 1 6 0"
stroke="{data.faceColor}"
fill="none"
strokeLinecap="round"
/>
''' if data.isMouthOpen else f'''
<path
d="M13,{19 + data.mouthSpread} a1,0.75 0 0,0 10,0"
fill="{data.faceColor}"
/>
'''}
<rect
x="{14 - data.eyeSpread}"
y="14"
width="1.5"
height="2"
rx="1"
stroke="none"
fill="{data.faceColor}"
/>
<rect
x="{20 + data.eyeSpread}"
y="14"
width="1.5"
height="2"
rx="1"
stroke="none"
fill="{data.faceColor}"
/>
</g>
</g>
</svg>
"""

return svg
78 changes: 78 additions & 0 deletions judge/boring_avatars/avatar_marble.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import random
from types import SimpleNamespace

from judge.boring_avatars.utilities import getRandomColor, getUnit, hashCode

ELEMENTS = 3
SIZE = 80


def GenerateColors(name, colors):
numFromName = hashCode(name)
colorRange = len(colors) if colors else 0

properties = [
SimpleNamespace(
color=getRandomColor(numFromName + i, colors, colorRange),
translateX=getUnit(numFromName * (i + 1), SIZE // 10, 1),
translateY=getUnit(numFromName * (i + 1), SIZE // 10, 2),
scale=1.2 + getUnit(numFromName * (i + 1), SIZE // 20) / 10,
rotate=getUnit(numFromName * (i + 1), 360, 1),
)
for i in range(ELEMENTS)
]

return properties


def AvatarMarble(props):
properties = GenerateColors(props.name, props.colors)
maskID = random.randrange(10**9, 10**10 - 1)

svg = f"""
<svg
viewBox="0 0 {SIZE} {SIZE}"
style="display: block; {props.style}"
fill="none"
role="img"
xmlns="http://www.w3.org/2000/svg"
width="{props.size}"
height="{props.size}"
>
{f"<title>{props.name}</title>" if props.title else ''}
<mask id="{maskID}" maskUnits="userSpaceOnUse" x="0" y="0" width="{SIZE}" height="{SIZE}">
<rect width="{SIZE}" height="{SIZE}" rx="{None if props.square else SIZE * 2}" fill="#FFFFFF" />
</mask>
<g mask="url(#{maskID})">
<rect width="{SIZE}" height="{SIZE}" fill="{properties[0].color}" />
<path
filter="url(#filter_{maskID})"
d="M32.414 59.35L50.376 70.5H72.5v-71H33.728L26.5 13.381l19.057 27.08L32.414 59.35z"
fill="{properties[1].color}"
transform="translate({properties[1].translateX} {properties[1].translateY})
rotate({properties[1].rotate} {SIZE / 2} {SIZE / 2}) scale({properties[2].scale})"
/>
<path
filter="url(#filter_{maskID})"
style="mix-blend-mode: overlay;"
d="M22.216 24L0 46.75l14.108 38.129L78 86l-3.081-59.276-22.378 4.005 12.972 20.186-23.35 27.395L22.215 24z"
fill="{properties[2].color}"
transform="translate({properties[2].translateX} {properties[2].translateY})
rotate({properties[2].rotate} {SIZE / 2} {SIZE / 2}) scale({properties[2].scale})"
/>
</g>
<defs>
<filter
id="filter_{maskID}"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="7" result="effect1_foregroundBlur" />
</filter>
</defs>
</svg>
"""

return svg
Loading

0 comments on commit 49f5323

Please sign in to comment.