56 lines
2.4 KiB
Python
56 lines
2.4 KiB
Python
"""Render the homepage's simple native tab symbols from editable vector geometry.
|
||
Run with Python + Pillow. No external images or fonts are used.
|
||
"""
|
||
from pathlib import Path
|
||
from PIL import Image, ImageDraw
|
||
|
||
DEST = Path(__file__).resolve().parents[1] / 'src/static/tab'
|
||
SIZE, SCALE = 81, 24
|
||
FACTOR = SIZE * 4 / SCALE
|
||
|
||
def render(name, active):
|
||
image = Image.new('RGBA', (SIZE * 4, SIZE * 4))
|
||
draw = ImageDraw.Draw(image)
|
||
color = '#6b8276' if active else '#99948e'
|
||
weight = round(1.35 * FACTOR)
|
||
def box(coords): return tuple(round(value * FACTOR) for value in coords)
|
||
def line(points, width=weight):
|
||
points = [box(point) for point in points]
|
||
draw.line(points, fill=color, width=width, joint='curve')
|
||
r = width / 2
|
||
for x, y in (points[0], points[-1]):
|
||
draw.ellipse((x-r, y-r, x+r, y+r), fill=color)
|
||
if name == 'home':
|
||
roof = [(3.8,10), (12,3.2), (20.2,10)]
|
||
if active:
|
||
draw.polygon([box(p) for p in roof + [(20.2,20.4),(14.6,20.4),(14.6,13.2),(9.4,13.2),(9.4,20.4),(3.8,20.4)]], fill=color)
|
||
else:
|
||
line(roof)
|
||
line([(5.2,9.5),(5.2,20.4),(9.4,20.4),(9.4,13.2),(14.6,13.2),(14.6,20.4),(18.8,20.4),(18.8,9.5)])
|
||
elif name == 'booking':
|
||
draw.rounded_rectangle(box((4.2,5.7,19.8,21)), radius=round(3*FACTOR), outline=color, width=weight)
|
||
line([(4.5,10.5),(19.5,10.5)])
|
||
line([(8,3.2),(8,7.8)])
|
||
line([(16,3.2),(16,7.8)])
|
||
if active:
|
||
line([(8.5,15.8),(11,18),(15.8,13.8)])
|
||
elif name == 'learn':
|
||
draw.ellipse(box((3,3,21,21)), outline=color, width=weight)
|
||
points = [(15.8,8.2),(13.7,13.7),(8.2,15.8),(10.3,10.3),(15.8,8.2)]
|
||
if active:
|
||
draw.polygon([box(p) for p in points], fill=color)
|
||
else:
|
||
line(points, round(1.1*FACTOR))
|
||
elif name == 'profile':
|
||
draw.ellipse(box((8,2.8,16,10.8)), outline=color, width=weight)
|
||
draw.rounded_rectangle(box((4.2,14,19.8,21.5)), radius=round(3.6*FACTOR), outline=color, width=weight)
|
||
if active:
|
||
draw.ellipse(box((9.2,4,14.8,9.6)), fill=color)
|
||
image = image.resize((SIZE,SIZE), Image.Resampling.LANCZOS)
|
||
image.save(DEST / f"{name}{'-active' if active else ''}.png", optimize=True)
|
||
|
||
for name in ['home','booking','learn','profile']:
|
||
for active in [False,True]:
|
||
render(name,active)
|
||
print('Rendered 8 tab icons, 81×81px, sage active / warm-gray inactive.')
|