419 lines
34 KiB
Python
419 lines
34 KiB
Python
"""SaiCore Tool Palette, Extra Palette, Erase Palette, Background Palette, Cursors, and misc icons."""
|
|
import os
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
BASE = os.path.join(os.path.dirname(__file__), "..", "resources", "images")
|
|
DARK = (250, 250, 249) # cream-white for dark bg
|
|
INDIGO = (220, 38, 38) # red-600
|
|
INDIGO_LT = (248, 113, 113) # red-400
|
|
WHITE = (255, 255, 255)
|
|
RED = (239, 68, 68)
|
|
GREEN = (16, 185, 129)
|
|
AMBER = (245, 158, 11)
|
|
GRAY = (82, 82, 91)
|
|
LIGHT_GRAY = (229, 231, 235)
|
|
T = (0, 0, 0, 0)
|
|
|
|
def font(sz):
|
|
for n in ['C:/Windows/Fonts/segoeuib.ttf', 'C:/Windows/Fonts/arialbd.ttf']:
|
|
try: return ImageFont.truetype(n, sz)
|
|
except: pass
|
|
return ImageFont.load_default()
|
|
|
|
def new(sz=42):
|
|
img = Image.new('RGBA', (sz, sz), T)
|
|
return img, ImageDraw.Draw(img)
|
|
|
|
def save(path, img):
|
|
img.save(path, 'PNG')
|
|
print(f" {os.path.basename(path)}")
|
|
|
|
def svg(path, body, w=42, h=42):
|
|
with open(path, 'w', encoding='utf-8') as f:
|
|
f.write(f'<?xml version="1.0"?>\n<svg width="{w}" height="{h}" viewBox="0 0 {w} {h}" xmlns="http://www.w3.org/2000/svg">\n{body}</svg>\n')
|
|
print(f" {os.path.basename(path)}")
|
|
|
|
def hx(c): return f'#{c[0]:02x}{c[1]:02x}{c[2]:02x}'
|
|
|
|
def gen_tool_palette():
|
|
print("=== Tool Palette ===")
|
|
d = os.path.join(BASE, "toolPalette")
|
|
sz = 42
|
|
|
|
# Compass
|
|
img, dr = new(sz)
|
|
dr.line([21,8,12,34], fill=DARK, width=2)
|
|
dr.line([21,8,30,34], fill=DARK, width=2)
|
|
dr.ellipse([18,5,24,11], outline=DARK, width=2)
|
|
dr.ellipse([9,31,15,37], fill=DARK)
|
|
dr.line([12,34,30,34], fill=DARK, width=1)
|
|
save(os.path.join(d, "compassTool.png"), img)
|
|
svg(os.path.join(d, "compassTool.svg"), '<path d="M21 8 L12 34 M21 8 L30 34" stroke="#fafaf9" stroke-width="2"/><circle cx="21" cy="8" r="3" fill="none" stroke="#fafaf9" stroke-width="2"/><circle cx="12" cy="34" r="3" fill="#fafaf9"/><path d="M12 34 L30 34" stroke="#fafaf9" stroke-width="1"/>')
|
|
|
|
# Ruler
|
|
img, dr = new(sz)
|
|
dr.polygon([(6,28),(34,6),(36,10),(8,32)], fill=DARK)
|
|
for i in range(5):
|
|
x1, y1 = 10+i*5, 30-i*4
|
|
dr.line([(x1,y1),(x1+2,y1-3)], fill=WHITE, width=1)
|
|
save(os.path.join(d, "rulerTool.png"), img)
|
|
|
|
# Protractor
|
|
img, dr = new(sz)
|
|
dr.arc([6,6,36,36], 0, 180, fill=DARK, width=2)
|
|
dr.line([6,21,36,21], fill=DARK, width=2)
|
|
dr.arc([12,12,30,30], 0, 180, fill=DARK, width=1)
|
|
save(os.path.join(d, "protractorTool.png"), img)
|
|
|
|
# Triangle
|
|
img, dr = new(sz)
|
|
dr.polygon([(6,34),(34,34),(6,6)], outline=DARK, width=2)
|
|
save(os.path.join(d, "triangleTool.png"), img)
|
|
|
|
# Axes
|
|
img, dr = new(sz)
|
|
dr.line([21,6,21,36], fill=DARK, width=2)
|
|
dr.line([6,21,36,21], fill=DARK, width=2)
|
|
dr.polygon([(21,6),(18,10),(24,10)], fill=DARK)
|
|
dr.polygon([(36,21),(32,18),(32,24)], fill=DARK)
|
|
save(os.path.join(d, "axesTool.png"), img)
|
|
|
|
# Aristo (L-shape)
|
|
img, dr = new(sz)
|
|
dr.polygon([(6,6),(6,34),(34,34)], outline=DARK, width=2)
|
|
save(os.path.join(d, "aristoTool.png"), img)
|
|
|
|
# Magnifier
|
|
img, dr = new(sz)
|
|
dr.ellipse([8,8,28,28], outline=DARK, width=3)
|
|
dr.line([26,26,36,36], fill=DARK, width=3)
|
|
save(os.path.join(d, "magnifierTool.png"), img)
|
|
|
|
# Cache
|
|
img, dr = new(sz)
|
|
dr.rounded_rectangle([8,8,34,34], radius=4, outline=DARK, width=2)
|
|
dr.line([8,8,34,34], fill=DARK, width=1)
|
|
save(os.path.join(d, "cacheTool.png"), img)
|
|
|
|
# Mask
|
|
img, dr = new(sz)
|
|
dr.rounded_rectangle([8,8,34,34], radius=4, fill=DARK)
|
|
dr.rounded_rectangle([14,14,28,28], radius=2, fill=T[:3]+(0,))
|
|
save(os.path.join(d, "maskTool.png"), img)
|
|
|
|
# Podcast
|
|
svg(os.path.join(d, "podcast.svg"), '<circle cx="21" cy="21" r="12" fill="none" stroke="#fafaf9" stroke-width="2"/><circle cx="21" cy="21" r="4" fill="#dc2626"/><path d="M21 9 Q14 9 14 21" stroke="#fafaf9" stroke-width="2" fill="none"/>')
|
|
svg(os.path.join(d, "podcastOn.svg"), '<circle cx="21" cy="21" r="12" fill="none" stroke="#dc2626" stroke-width="2"/><circle cx="21" cy="21" r="4" fill="#dc2626"/><path d="M21 9 Q14 9 14 21" stroke="#dc2626" stroke-width="2" fill="none"/>')
|
|
|
|
def gen_extra_palette():
|
|
print("=== Extra Palette ===")
|
|
d = os.path.join(BASE, "extraPalette")
|
|
sz = 42
|
|
|
|
for name, suffix, color, fill_c in [
|
|
("blackout","",DARK,None),("blackoutOn","",INDIGO,None),
|
|
("compass","",DARK,None),("compassOn","",INDIGO,None),
|
|
("keyboard","",DARK,None),("keyboardOn","",INDIGO,None),
|
|
("mask","",DARK,None),("maskOn","",INDIGO,None),
|
|
("oEmbed","",DARK,None),
|
|
("podcast","",DARK,None),("podcastOn","",INDIGO,None),
|
|
("protractor","",DARK,None),("protractorOn","",INDIGO,None),
|
|
("ruler","",DARK,None),("rulerOn","",INDIGO,None),
|
|
("trapFlash","",DARK,None),
|
|
("webTrap","",DARK,None),("webTrapEnabled","",INDIGO,None),
|
|
]:
|
|
img, dr = new(sz)
|
|
if "blackout" in name:
|
|
dr.rounded_rectangle([8,8,34,34], radius=4, fill=color)
|
|
elif "compass" in name:
|
|
dr.line([21,8,12,34], fill=color, width=2)
|
|
dr.line([21,8,30,34], fill=color, width=2)
|
|
dr.ellipse([18,5,24,11], outline=color, width=2)
|
|
elif "keyboard" in name:
|
|
dr.rounded_rectangle([6,14,36,28], radius=2, outline=color, width=2)
|
|
for i in range(5):
|
|
for j in range(2):
|
|
dr.rectangle([10+j*5+i*5, 16, 13+j*5+i*5, 19], fill=color)
|
|
elif "mask" in name:
|
|
dr.rounded_rectangle([8,8,34,34], radius=4, fill=color)
|
|
elif "podcast" in name:
|
|
dr.ellipse([9,9,33,33], outline=color, width=2)
|
|
dr.ellipse([17,17,25,25], fill=color)
|
|
elif "protractor" in name:
|
|
dr.arc([6,6,36,36], 0, 180, fill=color, width=2)
|
|
dr.line([6,21,36,21], fill=color, width=2)
|
|
elif "ruler" in name:
|
|
dr.polygon([(6,28),(34,6),(36,10),(8,32)], fill=color)
|
|
elif "oEmbed" in name:
|
|
dr.rounded_rectangle([8,14,34,28], radius=2, outline=color, width=2)
|
|
dr.polygon([(18,18),(18,24),(24,21)], fill=color)
|
|
elif "trapFlash" in name:
|
|
dr.ellipse([8,8,34,34], outline=color, width=2)
|
|
dr.line([8,8,34,34], fill=color, width=1)
|
|
elif "webTrap" in name:
|
|
dr.ellipse([8,8,34,34], outline=color, width=2)
|
|
dr.line([8,8,34,34], fill=color, width=1)
|
|
dr.line([21,8,21,34], fill=color, width=1)
|
|
dr.line([8,21,34,21], fill=color, width=1)
|
|
save(os.path.join(d, f"{name}{suffix}.png"), img)
|
|
|
|
def gen_erase_palette():
|
|
print("=== Erase Palette ===")
|
|
d = os.path.join(BASE, "erasePalette")
|
|
for name, color in [("eraseAll",DARK),("eraseAnnotation",DARK),("eraseBackground",DARK),("eraseItem",DARK)]:
|
|
svg(os.path.join(d, f"{name}.svg"), f'<path d="M12 12 L30 30 M30 12 L12 30" stroke="{hx(color)}" stroke-width="3" stroke-linecap="round"/>')
|
|
# eraseAll - X with circle
|
|
svg(os.path.join(d, "eraseAll.svg"), '<circle cx="21" cy="21" r="14" fill="none" stroke="#fafaf9" stroke-width="2"/><path d="M15 15 L27 27 M27 15 L15 27" stroke="#ef4444" stroke-width="3" stroke-linecap="round"/>')
|
|
# eraseAnnotation - eraser with lines
|
|
svg(os.path.join(d, "eraseAnnotation.svg"), '<rect x="10" y="14" width="22" height="14" rx="3" fill="#fafaf9"/><rect x="10" y="24" width="22" height="4" rx="2" fill="#dc2626"/><path d="M4 8 L38 8 M4 38 L38 38" stroke="#fafaf9" stroke-width="1" stroke-dasharray="2,2"/>')
|
|
# eraseBackground - grid with X
|
|
svg(os.path.join(d, "eraseBackground.svg"), '<rect x="8" y="8" width="26" height="26" fill="none" stroke="#fafaf9" stroke-width="1"/><path d="M8 16 L34 16 M8 24 L34 24 M16 8 L16 34 M24 8 L24 34" stroke="#fafaf9" stroke-width="1"/><path d="M14 14 L28 28 M28 14 L14 28" stroke="#ef4444" stroke-width="2"/>')
|
|
# eraseItem - box with X
|
|
svg(os.path.join(d, "eraseItem.svg"), '<rect x="8" y="8" width="26" height="26" rx="3" fill="none" stroke="#fafaf9" stroke-width="2"/><path d="M14 14 L28 28 M28 14 L14 28" stroke="#ef4444" stroke-width="2"/>')
|
|
|
|
def gen_background_palette():
|
|
print("=== Background Palette ===")
|
|
d = os.path.join(BASE, "backgroundPalette")
|
|
bgs = [
|
|
("background1", "plain"), ("background2", "grid"), ("background3", "lines"),
|
|
("background4", "dots"), ("background5", "cross"), ("background6", "ruler"),
|
|
("background7", "dark"), ("background8", "light"),
|
|
]
|
|
for name, bg_type in bgs:
|
|
for suffix, color in [("", DARK), ("On", INDIGO)]:
|
|
c = hx(color)
|
|
if bg_type == "plain":
|
|
body = f'<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="{c}" stroke-width="2"/>'
|
|
elif bg_type == "grid":
|
|
body = f'<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="{c}" stroke-width="2"/><path d="M12 6 L12 36 M18 6 L18 36 M24 6 L24 36 M30 6 L30 36 M6 12 L36 12 M6 18 L36 18 M6 24 L36 24 M6 30 L36 30" stroke="{c}" stroke-width="0.5"/>'
|
|
elif bg_type == "lines":
|
|
body = f'<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="{c}" stroke-width="2"/><path d="M6 12 L36 12 M6 18 L36 18 M6 24 L36 24 M6 30 L36 30" stroke="{c}" stroke-width="0.5"/>'
|
|
elif bg_type == "dots":
|
|
body = f'<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="{c}" stroke-width="2"/>' + ''.join(f'<circle cx="{x}" cy="{y}" r="1" fill="{c}"/>' for x in range(12,36,6) for y in range(12,36,6))
|
|
elif bg_type == "cross":
|
|
body = f'<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="{c}" stroke-width="2"/>' + ''.join(f'<path d="M{x-2} {y} L{x+2} {y} M{x} {y-2} L{x} {y+2}" stroke="{c}" stroke-width="0.5"/>' for x in range(12,36,6) for y in range(12,36,6))
|
|
elif bg_type == "ruler":
|
|
body = f'<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="{c}" stroke-width="2"/><path d="M6 21 L36 21" stroke="{c}" stroke-width="1"/>' + ''.join(f'<path d="M{x} 18 L{x} 24" stroke="{c}" stroke-width="0.5"/>' for x in range(12,36,3))
|
|
elif bg_type == "dark":
|
|
body = f'<rect x="6" y="6" width="30" height="30" rx="3" fill="{c}"/>'
|
|
elif bg_type == "light":
|
|
body = f'<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="{c}" stroke-width="2"/><rect x="8" y="8" width="26" height="26" rx="2" fill="{c}" opacity="0.1"/>'
|
|
svg(os.path.join(d, f"{name}{suffix}.svg"), body)
|
|
# Reset grid size
|
|
svg(os.path.join(d, "resetDefaultGridSize.svg"), '<path d="M21 8 L21 34 M8 21 L34 21" stroke="#fafaf9" stroke-width="2" stroke-dasharray="3,3"/><circle cx="21" cy="21" r="4" fill="none" stroke="#dc2626" stroke-width="2"/>')
|
|
|
|
def gen_cursors():
|
|
print("=== Cursors ===")
|
|
d = os.path.join(BASE, "cursors")
|
|
sz = 32
|
|
for name, fn in [
|
|
("drawCompass", lambda dr: (dr.line([16,4,8,28], fill=DARK, width=2), dr.line([16,4,24,28], fill=DARK, width=2), dr.ellipse([13,1,19,7], outline=DARK, width=2))),
|
|
("drawRulerLine", lambda dr: (dr.line([4,28,28,4], fill=DARK, width=2), dr.ellipse([2,26,8,32], fill=DARK), dr.ellipse([26,2,32,8], fill=DARK))),
|
|
("eraser", lambda dr: dr.rounded_rectangle([6,10,26,22], radius=3, fill=DARK)),
|
|
("laser", lambda dr: (dr.ellipse([12,12,20,20], fill=RED), dr.line([16,16,28,28], fill=RED, width=2))),
|
|
("marker", lambda dr: (dr.line([6,26,26,6], fill=DARK, width=5), dr.rounded_rectangle([23,4,29,10], radius=2, fill=DARK))),
|
|
("play", lambda dr: dr.polygon([(8,6),(8,26),(26,16)], fill=DARK)),
|
|
("resize", lambda dr: (dr.line([4,16,28,16], fill=DARK, width=2), dr.polygon([(4,16),(8,12),(8,20)], fill=DARK), dr.polygon([(28,16),(24,12),(24,20)], fill=DARK))),
|
|
("rotate", lambda dr: (dr.arc([6,6,26,26], 0, 270, fill=DARK, width=2), dr.polygon([(26,6),(22,6),(26,10)], fill=DARK))),
|
|
("zoomIn", lambda dr: (dr.ellipse([6,6,22,22], outline=DARK, width=2), dr.line([20,20,28,28], fill=DARK, width=2), dr.line([10,14,18,14], fill=DARK, width=2), dr.line([14,10,14,18], fill=DARK, width=2))),
|
|
("zoomOut", lambda dr: (dr.ellipse([6,6,22,22], outline=DARK, width=2), dr.line([20,20,28,28], fill=DARK, width=2), dr.line([10,14,18,14], fill=DARK, width=2))),
|
|
]:
|
|
img, dr = new(sz)
|
|
fn(dr)
|
|
save(os.path.join(d, f"{name}.png"), img)
|
|
|
|
def gen_misc():
|
|
print("=== Misc Icons ===")
|
|
sz = 42
|
|
|
|
# Main SVG icons
|
|
d = BASE
|
|
for name, body in [
|
|
("close", '<path d="M10 10 L32 32 M32 10 L10 32" stroke="#fafaf9" stroke-width="3" stroke-linecap="round"/>'),
|
|
("closeDisabled", '<path d="M10 10 L32 32 M32 10 L10 32" stroke="#9ca3af" stroke-width="3" stroke-linecap="round"/>'),
|
|
("closeTool", '<path d="M10 10 L32 32 M32 10 L10 32" stroke="#fafaf9" stroke-width="2" stroke-linecap="round"/><circle cx="21" cy="21" r="16" fill="none" stroke="#fafaf9" stroke-width="1"/>'),
|
|
("color", '<circle cx="21" cy="21" r="10" fill="#dc2626"/>'),
|
|
("circle", '<circle cx="21" cy="21" r="12" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("duplicate", '<rect x="8" y="12" width="18" height="22" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/><rect x="16" y="8" width="18" height="22" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("duplicateDisabled", '<rect x="8" y="12" width="18" height="22" rx="2" fill="none" stroke="#9ca3af" stroke-width="2"/><rect x="16" y="8" width="18" height="22" rx="2" fill="none" stroke="#9ca3af" stroke-width="2"/>'),
|
|
("eyeClosed", '<path d="M6 16 Q21 6 36 16" fill="none" stroke="#fafaf9" stroke-width="2"/><path d="M6 16 Q21 26 36 16" fill="none" stroke="#fafaf9" stroke-width="2"/><line x1="6" y1="26" x2="36" y2="10" stroke="#ef4444" stroke-width="2"/>'),
|
|
("eyeOpened", '<path d="M6 16 Q21 6 36 16" fill="none" stroke="#fafaf9" stroke-width="2"/><path d="M6 16 Q21 26 36 16" fill="none" stroke="#fafaf9" stroke-width="2"/><circle cx="21" cy="16" r="4" fill="#fafaf9"/>'),
|
|
("flash", '<path d="M22 6 L12 24 L20 24 L18 36 L30 18 L22 18 Z" fill="#dc2626"/>'),
|
|
("font", '<text x="21" y="28" font-family="Arial" font-size="20" font-weight="bold" text-anchor="middle" fill="#fafaf9">A</text>'),
|
|
("frozen", '<path d="M21 6 L21 36 M6 21 L36 21 M9 9 L33 33 M33 9 L9 33" stroke="#dc2626" stroke-width="2" stroke-linecap="round"/>'),
|
|
("unfrozen", '<path d="M21 6 L21 36 M6 21 L36 21" stroke="#9ca3af" stroke-width="2" stroke-linecap="round"/>'),
|
|
("groupItems", '<rect x="6" y="6" width="12" height="12" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/><rect x="24" y="6" width="12" height="12" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/><rect x="6" y="24" width="12" height="12" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/><rect x="24" y="24" width="12" height="12" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("ungroupItems", '<rect x="6" y="6" width="12" height="12" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/><rect x="24" y="6" width="12" height="12" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/><rect x="6" y="24" width="12" height="12" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/><rect x="24" y="24" width="12" height="12" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/><path d="M18 18 L24 18" stroke="#ef4444" stroke-width="2"/>'),
|
|
("home", '<path d="M21 6 L36 20 L30 20 L30 36 L12 36 L12 20 L6 20 Z" fill="#fafaf9"/>'),
|
|
("locked", '<rect x="10" y="20" width="22" height="16" rx="2" fill="#fafaf9"/><path d="M14 20 L14 14 Q14 8 21 8 Q28 8 28 14 L28 20" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("unlocked", '<rect x="10" y="20" width="22" height="16" rx="2" fill="#fafaf9"/><path d="M14 20 L14 14 Q14 8 21 8 Q28 8 28 14" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("menu", '<path d="M8 14 L34 14 M8 21 L34 21 M8 28 L34 28" stroke="#fafaf9" stroke-width="2" stroke-linecap="round"/>'),
|
|
("menuDisabled", '<path d="M8 14 L34 14 M8 21 L34 21 M8 28 L34 28" stroke="#9ca3af" stroke-width="2" stroke-linecap="round"/>'),
|
|
("minus", '<path d="M8 21 L34 21" stroke="#fafaf9" stroke-width="3" stroke-linecap="round"/>'),
|
|
("plus", '<path d="M21 8 L21 34 M8 21 L34 21" stroke="#fafaf9" stroke-width="3" stroke-linecap="round"/>'),
|
|
("pin", '<path d="M16 6 L26 6 L24 18 L30 24 L12 24 L18 18 Z" fill="#dc2626"/><path d="M21 24 L21 36" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("unpin", '<path d="M16 6 L26 6 L24 18 L30 24 L12 24 L18 18 Z" fill="#9ca3af"/><path d="M21 24 L21 36" stroke="#9ca3af" stroke-width="2"/>'),
|
|
("play", '<polygon points="12,8 12,34 34,21" fill="#fafaf9"/>'),
|
|
("pause", '<rect x="12" y="8" width="6" height="26" fill="#fafaf9"/><rect x="24" y="8" width="6" height="26" fill="#fafaf9"/>'),
|
|
("stop", '<rect x="10" y="10" width="22" height="22" fill="#fafaf9"/>'),
|
|
("reload", '<path d="M21 8 A13 13 0 1 1 8 21" fill="none" stroke="#fafaf9" stroke-width="2"/><polygon points="21,4 25,10 17,10" fill="#fafaf9"/>'),
|
|
("rotate", '<path d="M21 8 A13 13 0 1 1 8 21" fill="none" stroke="#fafaf9" stroke-width="2"/><polygon points="21,4 25,10 17,10" fill="#fafaf9"/>'),
|
|
("rotateProtractor", '<path d="M21 6 A15 15 0 0 1 36 21" fill="none" stroke="#fafaf9" stroke-width="2"/><polygon points="36,16 40,21 32,21" fill="#fafaf9"/>'),
|
|
("roundeRrectangle", '<rect x="8" y="12" width="26" height="18" rx="6" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("save", '<path d="M8 8 L34 8 L34 34 L8 34 Z M14 8 L14 20 L28 20 L28 8" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("setAsBackground", '<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="#fafaf9" stroke-width="2"/><rect x="9" y="9" width="24" height="12" fill="#dc2626" opacity="0.3"/>'),
|
|
("soundOff", '<path d="M8 16 L8 26 L16 26 L24 34 L24 8 L16 16 Z" fill="#fafaf9"/><path d="M28 12 L36 28 M36 12 L28 28" stroke="#ef4444" stroke-width="2"/>'),
|
|
("soundOn", '<path d="M8 16 L8 26 L16 26 L24 34 L24 8 L16 16 Z" fill="#fafaf9"/><path d="M28 14 Q32 21 28 28" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("hflipTool", '<path d="M21 6 L21 36 M10 12 L10 30 L18 21 Z M32 12 L32 30 L24 21 Z" fill="#fafaf9"/><path d="M21 6 L21 36" stroke="#dc2626" stroke-width="1" stroke-dasharray="2,2"/>'),
|
|
("vflipTool", '<path d="M6 21 L36 21 M12 10 L30 10 L21 18 Z M12 32 L30 32 L21 24 Z" fill="#fafaf9"/>'),
|
|
("moveDown", '<path d="M21 8 L21 34 M12 26 L21 34 L30 26" stroke="#fafaf9" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>'),
|
|
("moveDownDisabled", '<path d="M21 8 L21 34 M12 26 L21 34 L30 26" stroke="#9ca3af" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>'),
|
|
("moveUp", '<path d="M21 34 L21 8 M12 16 L21 8 L30 16" stroke="#fafaf9" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>'),
|
|
("moveUpDisabled", '<path d="M21 34 L21 8 M12 16 L21 8 L30 16" stroke="#9ca3af" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>'),
|
|
("moveTool", '<path d="M21 6 L36 21 L21 36 L6 21 Z" fill="none" stroke="#fafaf9" stroke-width="2"/><circle cx="21" cy="21" r="3" fill="#dc2626"/>'),
|
|
("resetAngle", '<path d="M21 8 A13 13 0 1 1 8 21" fill="none" stroke="#fafaf9" stroke-width="2"/><path d="M21 21 L21 8" stroke="#dc2626" stroke-width="2"/>'),
|
|
("resetTool", '<path d="M21 8 A13 13 0 1 0 34 21" fill="none" stroke="#fafaf9" stroke-width="2"/><polygon points="34,16 38,21 30,21" fill="#fafaf9"/>'),
|
|
("resizeTool", '<path d="M8 8 L34 34 M8 16 L16 8 M34 26 L26 34" stroke="#fafaf9" stroke-width="2" stroke-linecap="round"/>'),
|
|
("resizeBottom", '<path d="M21 6 L21 34 M14 27 L21 34 L28 27" stroke="#fafaf9" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>'),
|
|
("resizeLeft", '<path d="M34 21 L6 21 M13 14 L6 21 L13 28" stroke="#fafaf9" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>'),
|
|
("resizeRight", '<path d="M6 21 L34 21 M27 14 L34 21 L27 28" stroke="#fafaf9" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>'),
|
|
("resizeTop", '<path d="M21 34 L21 6 M14 13 L21 6 L28 13" stroke="#fafaf9" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>'),
|
|
("resizeButton", '<circle cx="21" cy="21" r="12" fill="#dc2626"/><path d="M21 15 L21 27 M15 21 L27 21" stroke="white" stroke-width="2"/>'),
|
|
("resizeCompass", '<path d="M21 6 L14 30 M21 6 L28 30" stroke="#fafaf9" stroke-width="2"/><circle cx="21" cy="6" r="3" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("resizeRuler", '<path d="M6 28 L34 6 L36 10 L8 32 Z" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("numbersTool", '<text x="21" y="27" font-family="Arial" font-size="18" font-weight="bold" text-anchor="middle" fill="#fafaf9">123</text>'),
|
|
("angleMarker", '<path d="M6 34 L36 34 L36 6" fill="none" stroke="#fafaf9" stroke-width="2"/><path d="M6 34 A14 14 0 0 0 20 20" fill="none" stroke="#dc2626" stroke-width="2"/>'),
|
|
("centerAligned", '<path d="M8 14 L34 14 M12 21 L30 21 M8 28 L34 28" stroke="#fafaf9" stroke-width="2" stroke-linecap="round"/>'),
|
|
("leftAligned", '<path d="M8 14 L34 14 M8 21 L30 21 M8 28 L34 28" stroke="#fafaf9" stroke-width="2" stroke-linecap="round"/>'),
|
|
("rightAligned", '<path d="M8 14 L34 14 M12 21 L34 21 M8 28 L34 28" stroke="#fafaf9" stroke-width="2" stroke-linecap="round"/>'),
|
|
("notAligned", '<path d="M8 14 L34 14 M8 21 L34 21 M8 28 L34 28" stroke="#fafaf9" stroke-width="2" stroke-linecap="round"/><path d="M6 6 L36 36" stroke="#ef4444" stroke-width="1"/>'),
|
|
("z_layer_up", '<rect x="10" y="20" width="22" height="16" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/><rect x="10" y="10" width="22" height="10" rx="2" fill="#dc2626" opacity="0.3"/><path d="M21 6 L21 14 M16 10 L21 6 L26 10" stroke="#fafaf9" stroke-width="2" fill="none"/>'),
|
|
("z_layer_down", '<rect x="10" y="6" width="22" height="16" rx="2" fill="none" stroke="#fafaf9" stroke-width="2"/><rect x="10" y="22" width="22" height="10" rx="2" fill="#dc2626" opacity="0.3"/><path d="M21 28 L21 36 M16 32 L21 36 L26 32" stroke="#fafaf9" stroke-width="2" fill="none"/>'),
|
|
("increase", '<path d="M21 8 L36 30 L6 30 Z" fill="#fafaf9"/>'),
|
|
("decrease", '<path d="M21 34 L6 12 L36 12 Z" fill="#fafaf9"/>'),
|
|
("resize", '<path d="M8 8 L34 34 M8 16 L16 8" stroke="#fafaf9" stroke-width="2" stroke-linecap="round"/>'),
|
|
("rotate", '<path d="M21 8 A13 13 0 1 0 34 21" fill="none" stroke="#fafaf9" stroke-width="2"/><polygon points="34,16 38,21 30,21" fill="#fafaf9"/>'),
|
|
("addItemToCurrentPage", '<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="#fafaf9" stroke-width="2"/><path d="M21 12 L21 30 M12 21 L30 21" stroke="#dc2626" stroke-width="2"/>'),
|
|
("addItemToLibrary", '<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="#fafaf9" stroke-width="2"/><path d="M21 14 L21 28 M14 21 L28 21" stroke="#dc2626" stroke-width="2"/>'),
|
|
("addItemToNewPage", '<rect x="6" y="6" width="30" height="30" rx="3" fill="none" stroke="#fafaf9" stroke-width="2"/><line x1="21" y1="6" x2="21" y2="36" stroke="#fafaf9" stroke-width="1" stroke-dasharray="2,2"/><path d="M28 21 L34 21 M31 18 L34 21 L31 24" stroke="#dc2626" stroke-width="2" fill="none"/>'),
|
|
("addToFavorites", '<polygon points="21,6 26,16 37,17 28,25 31,36 21,30 11,36 14,25 5,17 16,16" fill="none" stroke="#fafaf9" stroke-width="2"/>'),
|
|
("removeFromFavorites", '<polygon points="21,6 26,16 37,17 28,25 31,36 21,30 11,36 14,25 5,17 16,16" fill="#dc2626"/>'),
|
|
("folder", '<path d="M6 12 L16 12 L20 8 L28 8 L28 12 L36 12 L36 34 L6 34 Z" fill="#dc2626" fill-opacity="0.15" stroke="#fafaf9" stroke-width="2" stroke-linejoin="round"/><path d="M6 10 L16 10 L20 6 L28 6 L28 10" fill="#dc2626" fill-opacity="0.3" stroke="#fafaf9" stroke-width="2" stroke-linejoin="round"/>'),
|
|
("myFolder", '<path d="M6 12 L16 12 L20 8 L28 8 L28 12 L36 12 L36 34 L6 34 Z" fill="#dc2626" fill-opacity="0.15" stroke="#fafaf9" stroke-width="2" stroke-linejoin="round"/><path d="M6 10 L16 10 L20 6 L28 6 L28 10" fill="#dc2626" fill-opacity="0.3" stroke="#fafaf9" stroke-width="2" stroke-linejoin="round"/>'),
|
|
]:
|
|
svg(os.path.join(d, f"{name}.svg"), body)
|
|
|
|
# PNG-only misc icons
|
|
for name, fn in [
|
|
("addToFavorites", lambda dr: dr.polygon([(21,6),(26,16),(37,17),(28,25),(31,36),(21,30),(11,36),(14,25),(5,17),(16,16)], outline=DARK, width=2)),
|
|
("removeFromFavorites", lambda dr: dr.polygon([(21,6),(26,16),(37,17),(28,25),(31,36),(21,30),(11,36),(14,25),(5,17),(16,16)], fill=INDIGO)),
|
|
("asc", lambda dr: (dr.line([8,30,34,30], fill=DARK, width=2), dr.line([8,20,34,20], fill=DARK, width=2), dr.line([8,10,34,10], fill=DARK, width=2), dr.polygon([(21,4),(16,10),(26,10)], fill=DARK))),
|
|
("desc", lambda dr: (dr.line([8,30,34,30], fill=DARK, width=2), dr.line([8,20,34,20], fill=DARK, width=2), dr.line([8,10,34,10], fill=DARK, width=2), dr.polygon([(21,36),(16,30),(26,30)], fill=DARK))),
|
|
("cache_circle", lambda dr: dr.ellipse([6,6,36,36], outline=DARK, width=2)),
|
|
("cache_close", lambda dr: (dr.ellipse([6,6,36,36], outline=DARK, width=2), dr.line([14,14,28,28], fill=RED, width=2), dr.line([28,14,14,28], fill=RED, width=2))),
|
|
("cache_open", lambda dr: (dr.ellipse([6,6,36,36], outline=DARK, width=2), dr.line([14,14,28,28], fill=GREEN, width=2))),
|
|
("cache_square", lambda dr: dr.rounded_rectangle([8,8,34,34], radius=2, outline=DARK, width=2)),
|
|
("collapse-all", lambda dr: (dr.line([6,10,36,10], fill=DARK, width=2), dr.polygon([(10,6),(6,10),(10,14)], fill=DARK), dr.line([6,21,36,21], fill=DARK, width=2), dr.polygon([(10,17),(6,21),(10,25)], fill=DARK), dr.line([6,32,36,32], fill=DARK, width=2), dr.polygon([(10,28),(6,32),(10,36)], fill=DARK))),
|
|
("expand-all", lambda dr: (dr.line([6,10,36,10], fill=DARK, width=2), dr.polygon([(6,6),(10,10),(6,14)], fill=DARK), dr.line([6,21,36,21], fill=DARK, width=2), dr.polygon([(6,17),(10,21),(6,25)], fill=DARK), dr.line([6,32,36,32], fill=DARK, width=2), dr.polygon([(6,28),(10,32),(6,36)], fill=DARK))),
|
|
("currentDocument", lambda dr: (dr.rounded_rectangle([8,6,34,36], radius=2, outline=DARK, width=2), dr.line([12,12,30,12], fill=DARK, width=1), dr.line([12,16,30,16], fill=DARK, width=1), dr.line([12,20,24,20], fill=DARK, width=1))),
|
|
("defaultWidgetIcon", lambda dr: (dr.rounded_rectangle([8,8,34,34], radius=4, outline=DARK, width=2), dr.ellipse([16,16,26,26], fill=INDIGO))),
|
|
("download_close", lambda dr: (dr.line([8,8,34,34], fill=RED, width=2), dr.line([34,8,8,34], fill=RED, width=2))),
|
|
("download_open", lambda dr: (dr.line([8,21,34,21], fill=GREEN, width=2), dr.polygon([(14,16),(14,26),(8,21)], fill=GREEN), dr.polygon([(28,16),(28,26),(34,21)], fill=GREEN))),
|
|
("edit-mode", lambda dr: (dr.line([8,34,28,14], fill=DARK, width=3), dr.ellipse([25,11,33,19], outline=DARK, width=2))),
|
|
("favorites", lambda dr: dr.polygon([(21,6),(26,16),(37,17),(28,25),(31,36),(21,30),(11,36),(14,25),(5,17),(16,16)], outline=DARK, width=2)),
|
|
("folder", lambda dr: (dr.rounded_rectangle([6,12,36,34], radius=3, fill=(220,38,38,40), outline=DARK, width=2), dr.polygon([(6,12),(16,12),(20,8),(28,8)], fill=DARK, outline=DARK, width=1), dr.rounded_rectangle([6,10,28,14], radius=2, fill=(220,38,38,60), outline=DARK, width=1))),
|
|
("library_close", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.line([14,14,28,28], fill=RED, width=2))),
|
|
("library_open", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.line([14,21,28,21], fill=GREEN, width=2))),
|
|
("myContent", lambda dr: (dr.rounded_rectangle([6,8,36,34], radius=2, outline=DARK, width=2), dr.line([6,16,36,16], fill=DARK, width=1))),
|
|
("myFolder", lambda dr: (dr.rounded_rectangle([6,12,36,34], radius=3, fill=(220,38,38,40), outline=DARK, width=2), dr.polygon([(6,12),(16,12),(20,8),(28,8)], fill=DARK, outline=DARK, width=1), dr.rounded_rectangle([6,10,28,14], radius=2, fill=(220,38,38,60), outline=DARK, width=1))),
|
|
("navig_arrow", lambda dr: dr.polygon([(8,8),(34,21),(8,34)], fill=DARK)),
|
|
("online", lambda dr: (dr.ellipse([8,8,34,34], outline=GREEN, width=2), dr.line([14,21,19,26], fill=GREEN, width=3), dr.line([19,26,30,15], fill=GREEN, width=3))),
|
|
("pages_close", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.line([14,14,28,28], fill=DARK, width=2), dr.line([28,14,14,28], fill=DARK, width=2))),
|
|
("pages_open", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.line([14,18,28,18], fill=DARK, width=2), dr.line([14,24,28,24], fill=DARK, width=2))),
|
|
("paletteLibrary", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.line([6,16,36,16], fill=DARK, width=1), dr.line([16,6,16,36], fill=DARK, width=1))),
|
|
("paletteNavigator", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.ellipse([14,14,28,28], outline=INDIGO, width=2))),
|
|
("save", lambda dr: (dr.rounded_rectangle([8,6,34,36], radius=2, outline=DARK, width=2), dr.rectangle([14,6,28,14], fill=DARK), dr.rectangle([14,22,28,34], outline=DARK, width=1))),
|
|
("shape", lambda dr: (dr.ellipse([6,6,24,24], outline=DARK, width=2), dr.polygon([(20,18),(36,18),(28,36)], outline=DARK, width=2))),
|
|
("tab_mask", lambda dr: dr.rounded_rectangle([2,8,40,34], radius=2, outline=DARK, width=2)),
|
|
("tools", lambda dr: (dr.line([8,34,24,18], fill=DARK, width=3), dr.ellipse([22,14,32,24], outline=DARK, width=2))),
|
|
("trash", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.line([16,4,26,4], fill=DARK, width=2))),
|
|
("trash-delete-document", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.rounded_rectangle([16,14,26,30], radius=2, outline=RED, width=1))),
|
|
("trash-delete-folder", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.rounded_rectangle([14,14,28,28], radius=2, fill=(220,38,38,40), outline=RED, width=1), dr.polygon([(14,14),(20,14),(22,10),(26,10)], fill=RED), dr.line([16,18,26,26], fill=RED, width=2), dr.line([26,18,16,26], fill=RED, width=2))),
|
|
("trash-document-page", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.line([16,14,26,14], fill=RED, width=1), dr.line([16,20,26,20], fill=RED, width=1))),
|
|
("trash-document", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.rounded_rectangle([16,14,26,30], radius=2, outline=DARK, width=1))),
|
|
("trash-empty", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2))),
|
|
("trash-folder", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.rounded_rectangle([14,14,28,28], radius=2, fill=(220,38,38,40), outline=RED, width=1), dr.polygon([(14,14),(20,14),(22,10),(26,10)], fill=RED))),
|
|
("trash-my-documents", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.ellipse([16,14,22,20], outline=DARK, width=1), dr.ellipse([24,14,30,20], outline=DARK, width=1))),
|
|
("up_arrow", lambda dr: dr.polygon([(21,6),(8,30),(34,30)], fill=DARK)),
|
|
("down_arrow", lambda dr: dr.polygon([(21,36),(8,12),(34,12)], fill=DARK)),
|
|
("left_arrow", lambda dr: dr.polygon([(6,21),(30,8),(30,34)], fill=DARK)),
|
|
("right_arrow", lambda dr: dr.polygon([(36,21),(12,8),(12,34)], fill=DARK)),
|
|
("edit-mode", lambda dr: (dr.line([8,34,28,14], fill=DARK, width=3), dr.ellipse([25,11,33,19], outline=DARK, width=2))),
|
|
("banner", lambda dr: dr.rounded_rectangle([0,0,42,42], radius=0, fill=INDIGO)),
|
|
("banner_linear", lambda dr: dr.rounded_rectangle([0,0,42,42], radius=0, fill=INDIGO)),
|
|
]:
|
|
img, dr = new(sz)
|
|
fn(dr)
|
|
save(os.path.join(d, f"{name}.png"), img)
|
|
|
|
# Ch.SaiCore.SaiCore.svg
|
|
svg(os.path.join(d, "ch.SaiCore.SaiCore.svg"), '<circle cx="21" cy="21" r="18" fill="#dc2626"/><text x="21" y="28" font-family="Arial" font-size="20" font-weight="bold" text-anchor="middle" fill="white">S</text>')
|
|
|
|
def gen_libpalette():
|
|
print("=== Lib Palette ===")
|
|
d = os.path.join(BASE, "libpalette")
|
|
if not os.path.exists(d): os.makedirs(d)
|
|
# Check what's there and regenerate
|
|
for f in os.listdir(d):
|
|
path = os.path.join(d, f)
|
|
if f.endswith('.png'):
|
|
try:
|
|
img = Image.open(path)
|
|
img = img.convert('RGBA')
|
|
# Recolor: replace green/colored with red, dark pixels with cream
|
|
pixels = img.load()
|
|
for y in range(img.height):
|
|
for x in range(img.width):
|
|
r, g, b, a = pixels[x, y]
|
|
if a > 0 and g > r and g > b:
|
|
pixels[x, y] = (INDIGO[0], INDIGO[1], INDIGO[2], a)
|
|
elif a > 0 and r == g == b and r < 128:
|
|
pixels[x, y] = (DARK[0], DARK[1], DARK[2], a)
|
|
elif a > 0 and r > 100 and g < 100 and b < 100:
|
|
pixels[x, y] = (INDIGO[0], INDIGO[1], INDIGO[2], a)
|
|
img.save(path, 'PNG')
|
|
print(f" recolored: {f}")
|
|
except: pass
|
|
|
|
def gen_print():
|
|
print("=== Print ===")
|
|
d = os.path.join(BASE, "print")
|
|
if not os.path.exists(d): os.makedirs(d)
|
|
for f in os.listdir(d):
|
|
path = os.path.join(d, f)
|
|
if f.endswith('.png'):
|
|
try:
|
|
img = Image.open(path).convert('RGBA')
|
|
pixels = img.load()
|
|
for y in range(img.height):
|
|
for x in range(img.width):
|
|
r, g, b, a = pixels[x, y]
|
|
if a > 0 and g > r and g > b:
|
|
pixels[x, y] = (INDIGO[0], INDIGO[1], INDIGO[2], a)
|
|
img.save(path, 'PNG')
|
|
print(f" recolored: {f}")
|
|
except: pass
|
|
|
|
def run():
|
|
gen_tool_palette()
|
|
gen_extra_palette()
|
|
gen_erase_palette()
|
|
gen_background_palette()
|
|
gen_cursors()
|
|
gen_misc()
|
|
gen_libpalette()
|
|
gen_print()
|
|
|
|
if __name__ == "__main__":
|
|
run()
|