draw_icon.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. from PIL import Image
  2. import os
  3. def draw_icon(size):
  4. img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
  5. BG = (255, 107, 53)
  6. CIRC = (30, 58, 138)
  7. WHITE = (255, 255, 255)
  8. PAD = int(round(24 * size / 512.0))
  9. RX = int(round(104 * size / 512.0))
  10. ccx = size // 2
  11. ccy = size // 2
  12. ccr = int(round(112 * size / 512.0))
  13. # 1. Orange rounded square
  14. x1, y1 = PAD, PAD
  15. x2, y2 = size - PAD, size - PAD
  16. for y in range(y1, y2 + 1):
  17. for x in range(x1, x2 + 1):
  18. if _in_rrect(x, y, x1, y1, x2, y2, RX):
  19. img.putpixel((x, y), BG + (255,))
  20. # 2. Deep blue circle
  21. for y in range(ccy - ccr, ccy + ccr + 1):
  22. for x in range(ccx - ccr, ccx + ccr + 1):
  23. if (x - ccx) ** 2 + (y - ccy) ** 2 <= ccr ** 2:
  24. img.putpixel((x, y), CIRC + (255,))
  25. # 3. White play triangle — right-pointing isosceles, apex at circle's right edge
  26. # Width-to-height ratio ~2:1 for a balanced play button look
  27. # bh=ccr*0.35 ensures the left vertex stays inside the circle
  28. # At y=ccy: triangle base spans ~67% of circle diameter (visually balanced)
  29. bh = int(round(ccr * 0.35))
  30. w_tri = bh * 2 # width from left vertex to right vertex = 0.7*ccr
  31. rx2 = ccx + ccr # right vertex at circle's right edge
  32. lx = rx2 - w_tri # left vertex
  33. ty_ = ccy - bh
  34. by_ = ccy + bh
  35. for y in range(ty_, by_ + 1):
  36. frac = float(y - ty_) / float(by_ - ty_) if by_ > ty_ else 0.0
  37. xl = lx
  38. xr = lx + int(round(w_tri * 2 * frac))
  39. xl, xr = min(xl, xr), max(xl, xr)
  40. for x in range(xl, xr + 1):
  41. if 0 <= x < size and 0 <= y < size:
  42. img.putpixel((x, y), WHITE + (255,))
  43. return img
  44. def _in_rrect(px, py, x1, y1, x2, y2, r):
  45. if px < x1 or px > x2 or py < y1 or py > y2:
  46. return False
  47. if px < x1 + r and py < y1 + r:
  48. return (px - x1 - r) ** 2 + (py - y1 - r) ** 2 <= r ** 2
  49. if px > x2 - r and py < y1 + r:
  50. return (px - x2 + r) ** 2 + (py - y1 - r) ** 2 <= r ** 2
  51. if px < x1 + r and py > y2 - r:
  52. return (px - x1 - r) ** 2 + (py - y2 + r) ** 2 <= r ** 2
  53. if px > x2 - r and py > y2 - r:
  54. return (px - x2 + r) ** 2 + (py - y2 + r) ** 2 <= r ** 2
  55. return True
  56. src_size = 1024
  57. img = draw_icon(src_size)
  58. vp = img.load()
  59. ccr = int(round(112 * src_size / 512.0))
  60. bh_draw = int(round(ccr * 0.35))
  61. w_tri_draw = bh_draw * 2
  62. rx2_draw = src_size // 2 + ccr
  63. lx_draw = rx2_draw - w_tri_draw
  64. ty__draw = src_size // 2 - bh_draw
  65. by__draw = src_size // 2 + bh_draw
  66. print("ccr=%d, w_tri=%d, bh=%d, lx=%d, rx2=%d, ty_=%d, by_=%d" % (ccr, w_tri_draw, bh_draw, lx_draw, rx2_draw, ty__draw, by__draw))
  67. print("Midpoint at y=ccy: %d [expect %d]" % ((lx_draw+rx2_draw)//2, src_size//2))
  68. print("Center(512,512): %s" % str(vp[512, 512]))
  69. print("Corner(5,5): %s" % str(vp[5, 5]))
  70. # Verify triangle left vertex is inside circle
  71. # Left vertex distance from center must be < ccr
  72. left_dist = abs(lx_draw - src_size//2)
  73. # Also check top-left corner (most likely to be outside)
  74. top_lx = lx_draw
  75. top_ly = ty__draw
  76. tl_dist_sq = (top_lx - src_size//2)**2 + (top_ly - src_size//2)**2
  77. print("Left vertex dist from center: %.1f < ccr=%d: %s" % (left_dist, ccr, left_dist < ccr))
  78. print("Top-left corner dist: %.1f < ccr=%d: %s" % (tl_dist_sq**0.5, ccr, tl_dist_sq**0.5 < ccr))
  79. out_dir = "minimax-output/vector"
  80. icons_dir = "client/public/icons"
  81. os.makedirs(out_dir, exist_ok=True)
  82. img.save(os.path.join(out_dir, "icon-orange-1024.png"), "PNG")
  83. for sz in [16, 24, 32, 48, 64, 128, 256, 512]:
  84. img.resize((sz, sz), Image.NEAREST).save(os.path.join(icons_dir, "icon-%d.png" % sz), "PNG")
  85. img.resize((512, 512), Image.NEAREST).save(os.path.join(icons_dir, "logo.png"), "PNG")
  86. img.resize((16, 16), Image.NEAREST).save(os.path.join(icons_dir, "tray-icon.png"), "PNG")
  87. img.resize((32, 32), Image.NEAREST).save(os.path.join(icons_dir, "tray-icon@2x.png"), "PNG")
  88. print("Done!")