186 lines
7.4 KiB
Python
186 lines
7.4 KiB
Python
"""
|
|
SaiCore Dark-Red Theme - Complete icon regeneration.
|
|
All icons redesigned with dark-red palette: #dc2626 primary, #f87171 light,
|
|
#991b1b dark, #fca5a5 accent, white on dark backgrounds.
|
|
Icons are drawn as clean, modern flat designs with rounded shapes.
|
|
"""
|
|
import os, math
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
BASE = os.path.join(os.path.dirname(__file__), "..", "resources", "images")
|
|
|
|
# Dark-Red Theme Palette
|
|
RED = (220, 38, 38) # #dc2626 - primary
|
|
RED_LT = (248, 113, 113) # #f87171 - light variant
|
|
RED_DK = (153, 27, 27) # #991b1b - dark variant
|
|
RED_XL = (252, 165, 165) # #fca5a5 - accent light
|
|
GOLD = (245, 158, 11) # #f59e0b - highlight
|
|
WHITE = (255, 255, 255)
|
|
CREAM = (250, 250, 249) # near-white for dark bg
|
|
DARK = (26, 10, 10) # #1a0a0a - darkest bg
|
|
SURFACE = (45, 21, 23) # #2d1517 - surface
|
|
GRAY = (82, 82, 91) # #52525b - borders
|
|
GREEN = (16, 185, 129) # success
|
|
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}'
|
|
|
|
R = hx(RED)
|
|
RL = hx(RED_LT)
|
|
RD = hx(RED_DK)
|
|
RX = hx(RED_XL)
|
|
G = hx(GOLD)
|
|
W = hx(WHITE)
|
|
CR = hx(CREAM)
|
|
GR = hx(GRAY)
|
|
SU = hx(SURFACE)
|
|
DK = hx(DARK)
|
|
|
|
# ============================================================
|
|
# STYLUS PALETTE
|
|
# ============================================================
|
|
def gen_stylus():
|
|
print("=== Stylus Palette ===")
|
|
d = os.path.join(BASE, "stylusPalette")
|
|
sz = 42
|
|
|
|
def pen(color, arrow=False):
|
|
img, dr = new(sz)
|
|
# Pen body - diagonal with rounded cap
|
|
dr.line([(10,32),(30,12)], fill=color, width=5)
|
|
dr.ellipse([27,9,33,15], fill=color)
|
|
# Nib
|
|
dr.polygon([(7,35),(13,35),(10,31)], fill=color)
|
|
# Highlight stripe
|
|
dr.line([(12,30),(28,14)], fill=RED_LT, width=1)
|
|
if arrow: dr.polygon([(34,34),(40,31),(40,37)], fill=RED)
|
|
return img
|
|
|
|
def marker(color, arrow=False):
|
|
img, dr = new(sz)
|
|
dr.line([(10,32),(30,12)], fill=color, width=7)
|
|
dr.rounded_rectangle([27,5,35,13], radius=2, fill=color)
|
|
dr.polygon([(6,36),(14,36),(10,40)], fill=color)
|
|
dr.line([(12,30),(28,14)], fill=RED_LT, width=2)
|
|
if arrow: dr.polygon([(34,34),(40,31),(40,37)], fill=RED)
|
|
return img
|
|
|
|
def eraser(color, arrow=False):
|
|
img, dr = new(sz)
|
|
dr.rounded_rectangle([10,14,32,28], radius=4, fill=color)
|
|
dr.rounded_rectangle([10,23,32,28], radius=2, fill=RED)
|
|
# Highlight
|
|
dr.rounded_rectangle([12,16,30,18], radius=1, fill=RED_LT)
|
|
if arrow: dr.polygon([(34,34),(40,31),(40,37)], fill=RED)
|
|
return img
|
|
|
|
for name, img in [
|
|
("pen.png", pen(CREAM)), ("penOn.png", pen(RED)),
|
|
("penArrow.png", pen(CREAM, True)), ("penOnArrow.png", pen(RED, True)),
|
|
("marker.png", marker(CREAM)), ("markerOn.png", marker(RED)),
|
|
("markerArrow.png", marker(CREAM, True)), ("markerOnArrow.png", marker(RED, True)),
|
|
("eraser.png", eraser(CREAM)), ("eraserOn.png", eraser(RED)),
|
|
("eraserArrow.png", eraser(CREAM, True)), ("eraserOnArrow.png", eraser(RED, True)),
|
|
]: save(os.path.join(d, name), img)
|
|
|
|
# SVGs
|
|
for base, w in [("pen", 5), ("marker", 7), ("eraser", 0)]:
|
|
for suffix, color in [("", CR), ("On", R)]:
|
|
if base == "eraser":
|
|
body = f'<rect x="10" y="14" width="22" height="14" rx="4" fill="{color}"/><rect x="10" y="23" width="22" height="5" rx="2" fill="{R}"/><rect x="12" y="16" width="18" height="2" rx="1" fill="{RL}"/>'
|
|
else:
|
|
body = f'<path d="M10 32 L30 12" stroke="{color}" stroke-width="{w}" stroke-linecap="round"/><circle cx="30" cy="12" r="3" fill="{color}"/><polygon points="7,35 13,35 10,31" fill="{color}"/><path d="M12 30 L28 14" stroke="{RL}" stroke-width="1"/>'
|
|
svg(os.path.join(d, f"{base}{suffix}.svg"), body)
|
|
|
|
# Line
|
|
for suffix, color in [("", CREAM), ("On", RED)]:
|
|
img, dr = new(sz)
|
|
dr.line([(8,32),(34,10)], fill=color, width=3)
|
|
dr.ellipse([5,29,11,35], fill=color)
|
|
dr.ellipse([31,7,37,13], fill=color)
|
|
save(os.path.join(d, f"line{suffix}.png"), img)
|
|
|
|
# Text
|
|
for suffix, color in [("", CREAM), ("On", RED)]:
|
|
img, dr = new(sz)
|
|
dr.text((13,7), "T", fill=color, font=font(26))
|
|
save(os.path.join(d, f"text{suffix}.png"), img)
|
|
|
|
# Hand
|
|
for suffix, color, play in [("",CREAM,False),("On",RED,False),("Play",CREAM,True),("PlayOn",RED,True)]:
|
|
img, dr = new(sz)
|
|
dr.rounded_rectangle([14,18,28,32], radius=5, fill=color)
|
|
for ox in [-6,-2,2,6]:
|
|
dr.rounded_rectangle([20+ox-1,10,20+ox+1,20], radius=1, fill=color)
|
|
dr.rounded_rectangle([10,22,14,28], radius=2, fill=color)
|
|
if play: dr.polygon([(30,18),(30,28),(37,23)], fill=WHITE)
|
|
save(os.path.join(d, f"hand{suffix}.png"), img)
|
|
|
|
# Arrow
|
|
for suffix, color in [("", CREAM), ("On", RED)]:
|
|
img, dr = new(sz)
|
|
dr.line([(8,34),(28,12)], fill=color, width=3)
|
|
dr.polygon([(28,12),(22,12),(28,18)], fill=color)
|
|
save(os.path.join(d, f"arrow{suffix}.png"), img)
|
|
|
|
# Capture
|
|
for suffix, color in [("", CREAM), ("On", RED)]:
|
|
img, dr = new(sz)
|
|
for i in range(8, 34, 6):
|
|
dr.line([i,8,min(i+3,34),8], fill=color, width=2)
|
|
dr.line([i,34,min(i+3,34),34], fill=color, width=2)
|
|
dr.line([8,i,8,min(i+3,34)], fill=color, width=2)
|
|
dr.line([34,i,34,min(i+3,34)], fill=color, width=2)
|
|
save(os.path.join(d, f"captureArea{suffix}.png"), img)
|
|
|
|
# Laser
|
|
for suffix, color in [("", CREAM), ("On", RED)]:
|
|
img, dr = new(sz)
|
|
dr.rounded_rectangle([8,19,26,23], radius=2, fill=color)
|
|
dr.ellipse([24,16,34,26], fill=RED)
|
|
dr.line([26,21,34,21], fill=RED_LT, width=2)
|
|
save(os.path.join(d, f"laser{suffix}.png"), img)
|
|
|
|
# Zoom
|
|
for suffix, direction in [("In",CREAM),("InOn",RED),("Out",CREAM),("OutOn",RED)]:
|
|
img, dr = new(sz)
|
|
dr.ellipse([8,8,28,28], outline=suffix_color, width=3)
|
|
dr.line([26,26,36,36], fill=suffix_color, width=3)
|
|
if "In" in suffix:
|
|
dr.line([14,18,22,18], fill=suffix_color, width=3)
|
|
dr.line([18,14,18,22], fill=suffix_color, width=3)
|
|
else:
|
|
dr.line([14,18,22,18], fill=suffix_color, width=3)
|
|
save(os.path.join(d, f"zoom{suffix}.png"), img)
|
|
|
|
# Restore zoom
|
|
img, dr = new(sz)
|
|
dr.ellipse([8,8,28,28], outline=CREAM, width=3)
|
|
dr.line([26,26,36,36], fill=CREAM, width=3)
|
|
dr.text((12,12), "1:1", fill=CREAM, font=font(8))
|
|
save(os.path.join(d, "restoreZoom.png"), img)
|
|
|
|
# Snap
|
|
for suffix, color in [("", CREAM), ("On", RED)]:
|
|
svg(os.path.join(d, f"snap{suffix}.svg"),
|
|
f'<path d="M21 8 L21 34 M8 21 L34 21" stroke="{hx(color)}" stroke-width="2" stroke-dasharray="3,3"/><circle cx="21" cy="21" r="5" fill="none" stroke="{hx(color)}" stroke-width="2"/>')
|