gen-tab-icons.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. # -*- coding: utf-8 -*-
  2. """Generate Ai-DOP tabBar icons: 81x81 PNG, transparent bg, two states (gray / navy)."""
  3. import math
  4. from PIL import Image, ImageDraw
  5. S = 4 # supersample
  6. SIZE = 81 * S
  7. OUT = r"d:\Projects\Ai-DOP\SourceCode\ZZYDOP\App\src\static\tab"
  8. GRAY = (144, 147, 153, 255) # #909399
  9. NAVY = (47, 58, 82, 255) # #2F3A52
  10. def canvas():
  11. img = Image.new("RGBA", (SIZE, SIZE), (0, 0, 0, 0))
  12. return img, ImageDraw.Draw(img)
  13. def save(img, name):
  14. import os
  15. os.makedirs(OUT, exist_ok=True)
  16. img = img.resize((81, 81), Image.LANCZOS)
  17. img.save(os.path.join(OUT, name))
  18. def lw(w):
  19. return int(w * S)
  20. # ---------- home: house ----------
  21. def draw_home(color):
  22. img, d = canvas()
  23. c = SIZE / 2
  24. roof_top = 14 * S
  25. roof_half = 30 * S
  26. roof_y = 40 * S
  27. # roof (triangle outline via polygon)
  28. d.polygon([(c, roof_top), (c - roof_half, roof_y), (c + roof_half, roof_y)], fill=color)
  29. # body
  30. bx0, by0 = c - 22 * S, 42 * S
  31. bx1, by1 = c + 22 * S, 68 * S
  32. d.rectangle([bx0, by0, bx1, by1], fill=color)
  33. # door cutout
  34. dw = 9 * S
  35. d.rectangle([c - dw, 52 * S, c + dw, by1], fill=(0, 0, 0, 0))
  36. return img
  37. # ---------- todo: clipboard with check ----------
  38. def draw_todo(color):
  39. img, d = canvas()
  40. x0, y0, x1, y1 = 18 * S, 14 * S, 63 * S, 70 * S
  41. w = lw(4)
  42. # board outline (rounded approximated by rectangle)
  43. d.rectangle([x0, y0 + 4 * S, x1, y1], outline=color, width=w)
  44. # clip
  45. d.rectangle([30 * S, 10 * S, 51 * S, 20 * S], fill=color)
  46. # check mark
  47. pts = [(26 * S, 44 * S), (37 * S, 56 * S), (57 * S, 30 * S)]
  48. d.line(pts, fill=color, width=lw(5), joint="curve")
  49. return img
  50. # ---------- message: chat bubble ----------
  51. def draw_message(color):
  52. img, d = canvas()
  53. x0, y0, x1, y1 = 13 * S, 16 * S, 68 * S, 56 * S
  54. r = 10 * S
  55. d.rounded_rectangle([x0, y0, x1, y1], radius=r, fill=color)
  56. # tail
  57. d.polygon([(26 * S, y1 - 2 * S), (26 * S, 72 * S), (42 * S, y1 - 2 * S)], fill=color)
  58. # dots
  59. for i in range(3):
  60. cx = (27 + i * 13.5) * S
  61. cy = (y0 + y1) / 2
  62. rr = 3.2 * S
  63. d.ellipse([cx - rr, cy - rr, cx + rr, cy + rr], fill=(0, 0, 0, 0))
  64. return img
  65. # ---------- mine: person ----------
  66. def draw_mine(color):
  67. img, d = canvas()
  68. c = SIZE / 2
  69. # head
  70. hr = 12 * S
  71. hy = 28 * S
  72. d.ellipse([c - hr, hy - hr, c + hr, hy + hr], fill=color)
  73. # shoulders (half ellipse)
  74. sw = 26 * S
  75. sy0 = 46 * S
  76. sy1 = 88 * S
  77. d.pieslice([c - sw, sy0, c + sw, sy1], start=180, end=360, fill=color)
  78. d.rectangle([c - sw, (sy0 + sy1) / 2, c + sw, sy1], fill=(0, 0, 0, 0))
  79. # re-draw shoulders as chord + body rect to horizon
  80. d.pieslice([c - sw, sy0, c + sw, sy1], start=180, end=360, fill=color)
  81. return img
  82. def main():
  83. variants = {"": GRAY, "-active": NAVY}
  84. drawers = {"home": draw_home, "todo": draw_todo, "message": draw_message, "mine": draw_mine}
  85. for name, fn in drawers.items():
  86. for suffix, color in variants.items():
  87. save(fn(color), f"{name}{suffix}.png")
  88. print("done")
  89. if __name__ == "__main__":
  90. main()