-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate_icons.py
More file actions
148 lines (124 loc) · 4.9 KB
/
Copy pathgenerate_icons.py
File metadata and controls
148 lines (124 loc) · 4.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
"""Generate Growth Math icon PNG set programmatically.
Outputs:
icons/icon-16.png
icons/icon-32.png
icons/icon-48.png
icons/icon-128.png
Uses Pillow only (no SVG renderer dependency). Mirrors icons/icon.svg design:
rounded-square brand-gradient background + white inner panel + calculator-style
grid. Tuned per-size so the design remains legible even at 16px.
"""
import os, sys
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
OUT_DIR = Path(__file__).parent / 'icons'
OUT_DIR.mkdir(parents=True, exist_ok=True)
def gradient(size, c1, c2, vertical=False):
"""Linear gradient between two RGB tuples."""
w = h = size
img = Image.new('RGB', (w, h), c1)
px = img.load()
for y in range(h):
for x in range(w):
t = (x + y) / (w + h - 2) if not vertical else y / max(1, h - 1)
r = int(c1[0] + (c2[0] - c1[0]) * t)
g = int(c1[1] + (c2[1] - c1[1]) * t)
b = int(c1[2] + (c2[2] - c1[2]) * t)
px[x, y] = (r, g, b)
return img
def round_corners(img, radius):
"""Apply rounded corners with alpha mask."""
mask = Image.new('L', img.size, 0)
d = ImageDraw.Draw(mask)
d.rounded_rectangle((0, 0, img.width, img.height), radius=radius, fill=255)
out = Image.new('RGBA', img.size, (0, 0, 0, 0))
out.paste(img, (0, 0), mask)
return out
def draw_icon(size):
"""Draw calculator-grid icon at given size, returns PIL Image (RGBA)."""
# Brand colours
BRAND_1 = (217, 108, 255) # #D96CFF
BRAND_2 = (80, 109, 212) # #506DD4
INK = (42, 43, 75) # #2A2B4B
LIGHT = (241, 247, 255) # #F1F7FF
LINE = (208, 222, 255) # #D0DEFF
radius_outer = max(2, int(size * 28 / 128))
radius_inner = max(2, int(size * 14 / 128))
# 1. Brand gradient background
bg = gradient(size, BRAND_1, BRAND_2)
bg = round_corners(bg, radius_outer)
# 2. White inner panel
inner_pad = int(size * 20 / 128)
inner_w = size - 2 * inner_pad
inner = Image.new('RGBA', (inner_w, inner_w), (255, 255, 255, 245))
inner = round_corners(inner, radius_inner)
bg.paste(inner, (inner_pad, inner_pad), inner)
d = ImageDraw.Draw(bg)
# Coordinates inside inner panel
pad = int(size * 28 / 128)
inner_right = size - pad
# 3. Display strip (dark)
disp_y = int(size * 28 / 128)
disp_h = int(size * 14 / 128)
d.rounded_rectangle(
(pad, disp_y, inner_right, disp_y + disp_h),
radius=max(1, int(size * 3 / 128)),
fill=INK,
)
# 4. Chart line on display (brand-1 colour)
if size >= 48:
line_y_base = disp_y + disp_h // 2
line_points = []
n_points = 8
pattern = [0.5, 0.3, -0.2, 0.0, -0.4, -0.2, -0.5, -0.3]
for i in range(n_points):
x = pad + 4 + int((inner_right - pad - 8) * i / (n_points - 1))
y = line_y_base + int(disp_h * 0.3 * pattern[i])
line_points.append((x, y))
d.line(line_points, fill=BRAND_1, width=max(1, int(size * 2 / 128)))
# 5. Button grid (3 rows × 4 cols) below display
grid_top = disp_y + disp_h + int(size * 6 / 128)
grid_bottom = size - pad
cell_size = int(size * 14 / 128)
gap = int(size * 2 / 128)
grid_w = 4 * cell_size + 3 * gap
grid_h = 4 * cell_size + 3 * gap
grid_start_x = (size - grid_w) // 2
grid_radius = max(1, int(size * 3 / 128))
for row in range(3):
for col in range(4):
x = grid_start_x + col * (cell_size + gap)
y = grid_top + row * (cell_size + gap)
# right column = brand gradient (operators)
if col == 3:
d.rounded_rectangle((x, y, x + cell_size, y + cell_size),
radius=grid_radius, fill=BRAND_2)
else:
d.rounded_rectangle((x, y, x + cell_size, y + cell_size),
radius=grid_radius, fill=LIGHT,
outline=LINE, width=max(1, int(size / 128)))
# 6. Bottom row — wide equals + small =
eq_y = grid_top + 3 * (cell_size + gap)
eq_h = int(cell_size * 0.6)
eq_wide_w = 3 * cell_size + 2 * gap
d.rounded_rectangle(
(grid_start_x, eq_y, grid_start_x + eq_wide_w, eq_y + eq_h),
radius=grid_radius, fill=LIGHT, outline=LINE, width=max(1, int(size / 128)),
)
d.rounded_rectangle(
(grid_start_x + 3 * (cell_size + gap), eq_y,
grid_start_x + 3 * (cell_size + gap) + cell_size, eq_y + eq_h),
radius=grid_radius, fill=BRAND_2,
)
return bg
def main():
sizes = [16, 32, 48, 128]
# Also generate higher-res 256 + 512 for store listings if needed
extra_sizes = [256, 512, 1024]
for s in sizes + extra_sizes:
img = draw_icon(s)
out = OUT_DIR / f'icon-{s}.png'
img.save(out, format='PNG', optimize=True)
sys.stderr.write(f' wrote {out} ({s}x{s})\n')
if __name__ == '__main__':
main()