from __future__ import annotations from pathlib import Path from PIL import Image, ImageDraw, ImageFilter def lerp(a: float, b: float, t: float) -> float: return a + (b - a) * t def make_gradient(size: int) -> Image.Image: img = Image.new("RGBA", (size, size), (0, 0, 0, 255)) px = img.load() top = (8, 17, 40) bottom = (17, 54, 95) for y in range(size): t = y / (size - 1) color = tuple(int(lerp(top[i], bottom[i], t)) for i in range(3)) + (255,) for x in range(size): px[x, y] = color return img def rounded_rect_mask(size: int, radius: int) -> Image.Image: m = Image.new("L", (size, size), 0) d = ImageDraw.Draw(m) d.rounded_rectangle((0, 0, size - 1, size - 1), radius=radius, fill=255) return m def draw_icon(size: int = 1024) -> Image.Image: base = make_gradient(size) draw = ImageDraw.Draw(base) # Soft vignette vignette = Image.new("RGBA", (size, size), (0, 0, 0, 0)) vd = ImageDraw.Draw(vignette) vd.ellipse((-size * 0.25, -size * 0.15, size * 1.25, size * 1.15), fill=(255, 255, 255, 26)) vd.ellipse((-size * 0.1, size * 0.55, size * 1.1, size * 1.5), fill=(0, 0, 0, 70)) vignette = vignette.filter(ImageFilter.GaussianBlur(radius=size * 0.06)) base = Image.alpha_composite(base, vignette) draw = ImageDraw.Draw(base) # Grid lines grid_color = (190, 215, 255, 34) margin = int(size * 0.16) for i in range(1, 5): y = int(lerp(margin, size - margin, i / 5)) draw.line((margin, y, size - margin, y), fill=grid_color, width=max(1, size // 512)) # Candlestick bodies/wicks center_x = size // 2 widths = int(size * 0.09) gap = int(size * 0.05) candles = [ (center_x - widths - gap, 0.62, 0.33, 0.57, 0.36, (18, 214, 130, 255)), (center_x, 0.72, 0.40, 0.45, 0.65, (255, 88, 88, 255)), (center_x + widths + gap, 0.58, 0.30, 0.52, 0.34, (18, 214, 130, 255)), ] for x, low, high, body_top, body_bottom, color in candles: x = int(x) y_low = int(size * low) y_high = int(size * high) y_a = int(size * body_top) y_b = int(size * body_bottom) y_top = min(y_a, y_b) y_bottom = max(y_a, y_b) wick_w = max(3, size // 180) draw.line((x, y_high, x, y_low), fill=(220, 235, 255, 220), width=wick_w) bw = widths draw.rounded_rectangle( (x - bw // 2, y_top, x + bw // 2, y_bottom), radius=max(6, size // 64), fill=color, ) # Trend arrows arrow_green = (45, 237, 147, 255) arrow_red = (255, 77, 77, 255) up = [ (int(size * 0.20), int(size * 0.70)), (int(size * 0.29), int(size * 0.61)), (int(size * 0.25), int(size * 0.61)), (int(size * 0.25), int(size * 0.52)), (int(size * 0.15), int(size * 0.52)), (int(size * 0.15), int(size * 0.61)), (int(size * 0.11), int(size * 0.61)), ] down = [ (int(size * 0.80), int(size * 0.34)), (int(size * 0.71), int(size * 0.43)), (int(size * 0.75), int(size * 0.43)), (int(size * 0.75), int(size * 0.52)), (int(size * 0.85), int(size * 0.52)), (int(size * 0.85), int(size * 0.43)), (int(size * 0.89), int(size * 0.43)), ] draw.polygon(up, fill=arrow_green) draw.polygon(down, fill=arrow_red) # Rounded-square icon mask mask = rounded_rect_mask(size, radius=int(size * 0.23)) out = Image.new("RGBA", (size, size), (0, 0, 0, 0)) out.paste(base, (0, 0), mask) # Subtle border bd = ImageDraw.Draw(out) bd.rounded_rectangle( (2, 2, size - 3, size - 3), radius=int(size * 0.23), outline=(255, 255, 255, 44), width=max(2, size // 256), ) return out def main() -> None: out_path = Path("assets/icon/ManeshTrader.png") out_path.parent.mkdir(parents=True, exist_ok=True) img = draw_icon(1024) img.save(out_path) print(f"Wrote {out_path}") if __name__ == "__main__": main()