Starburst was nearly invisible at tray icon size due to white color at 50% alpha on a 128px canvas. Now renders in Claude brand orange (#E07B53) at full opacity on a 256px canvas with proportionally scaled geometry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
123 lines
3.3 KiB
Python
123 lines
3.3 KiB
Python
"""Icon renderer: Claude starburst logo + circular usage arc."""
|
|
|
|
import math
|
|
|
|
from PIL import Image, ImageDraw
|
|
|
|
SIZE = 256
|
|
CENTER = SIZE // 2
|
|
ARC_WIDTH = 28
|
|
|
|
# Claude brand orange
|
|
_CLAUDE_ORANGE = (224, 123, 83) # #E07B53
|
|
|
|
# Color thresholds for the usage arc (10% increments)
|
|
_COLORS = [
|
|
(10, "#4CAF50"), # green
|
|
(20, "#43A047"), # green (darker)
|
|
(30, "#7CB342"), # light green
|
|
(40, "#C0CA33"), # lime
|
|
(50, "#FDD835"), # yellow
|
|
(60, "#FFC107"), # amber
|
|
(70, "#FFB300"), # amber (darker)
|
|
(80, "#FF9800"), # orange
|
|
(90, "#FF5722"), # deep orange
|
|
(100, "#F44336"), # red
|
|
]
|
|
|
|
|
|
def _hex_to_rgba(hex_color, alpha=255):
|
|
h = hex_color.lstrip("#")
|
|
return (int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16), alpha)
|
|
|
|
|
|
def _arc_color(pct):
|
|
for threshold, color in _COLORS:
|
|
if pct <= threshold:
|
|
return color
|
|
return _COLORS[-1][1]
|
|
|
|
|
|
def _draw_starburst(img):
|
|
"""Draw 8-petal starburst logo in Claude orange, full opacity."""
|
|
layer = Image.new("RGBA", (SIZE, SIZE), (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(layer)
|
|
|
|
color = (*_CLAUDE_ORANGE, 255)
|
|
|
|
num_petals = 8
|
|
petal_length = SIZE * 0.38 # fill most of the icon
|
|
petal_width = SIZE * 0.10
|
|
|
|
for i in range(num_petals):
|
|
angle = math.radians(i * (360 / num_petals))
|
|
|
|
# Tip of petal (far from center)
|
|
tip_x = CENTER + math.cos(angle) * petal_length
|
|
tip_y = CENTER + math.sin(angle) * petal_length
|
|
|
|
# Base points (perpendicular to petal direction, near center)
|
|
perp = angle + math.pi / 2
|
|
base_offset = SIZE * 0.05
|
|
bx = CENTER + math.cos(angle) * base_offset
|
|
by = CENTER + math.sin(angle) * base_offset
|
|
|
|
left_x = bx + math.cos(perp) * (petal_width / 2)
|
|
left_y = by + math.sin(perp) * (petal_width / 2)
|
|
right_x = bx - math.cos(perp) * (petal_width / 2)
|
|
right_y = by - math.sin(perp) * (petal_width / 2)
|
|
|
|
# Inner point (towards center, slight taper)
|
|
inner_x = CENTER - math.cos(angle) * (SIZE * 0.02)
|
|
inner_y = CENTER - math.sin(angle) * (SIZE * 0.02)
|
|
|
|
polygon = [(inner_x, inner_y), (left_x, left_y), (tip_x, tip_y), (right_x, right_y)]
|
|
draw.polygon(polygon, fill=color)
|
|
|
|
# Center dot
|
|
dot_r = SIZE * 0.04
|
|
draw.ellipse(
|
|
[CENTER - dot_r, CENTER - dot_r, CENTER + dot_r, CENTER + dot_r],
|
|
fill=color,
|
|
)
|
|
|
|
img = Image.alpha_composite(img, layer)
|
|
return img
|
|
|
|
|
|
def _draw_arc(img, pct):
|
|
"""Draw circular progress arc from 12 o'clock, clockwise."""
|
|
if pct <= 0:
|
|
return img
|
|
|
|
layer = Image.new("RGBA", (SIZE, SIZE), (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(layer)
|
|
|
|
color = _hex_to_rgba(_arc_color(pct))
|
|
pct = min(pct, 100)
|
|
|
|
start_angle = -90
|
|
end_angle = -90 + (pct / 100) * 360
|
|
|
|
margin = ARC_WIDTH // 2
|
|
bbox = [margin, margin, SIZE - margin, SIZE - margin]
|
|
draw.arc(bbox, start_angle, end_angle, fill=color, width=ARC_WIDTH)
|
|
|
|
img = Image.alpha_composite(img, layer)
|
|
return img
|
|
|
|
|
|
def render_icon(pct):
|
|
"""Render a 256x256 RGBA icon with starburst + usage arc.
|
|
|
|
Args:
|
|
pct: Usage percentage (0-100). Arc is invisible at 0.
|
|
|
|
Returns:
|
|
PIL.Image.Image (RGBA)
|
|
"""
|
|
img = Image.new("RGBA", (SIZE, SIZE), (0, 0, 0, 0))
|
|
img = _draw_starburst(img)
|
|
img = _draw_arc(img, pct)
|
|
return img
|