280 lines
12 KiB
Python
280 lines
12 KiB
Python
import math
|
|
import numpy as np
|
|
from PIL import Image, ImageDraw, ImageFilter
|
|
|
|
HI_SIZE = 4096
|
|
OUT_SIZE = 1024
|
|
CENTER = HI_SIZE / 2.0
|
|
|
|
# ==============================================================================
|
|
# 1. МНОГОСЛОЙНЫЙ ГЛУБОКИЙ КОСМОС: ГАЛАКТИКА, ПЛАЗМА И ТУМАННОСТИ
|
|
# ==============================================================================
|
|
bg = Image.new('RGBA', (HI_SIZE, HI_SIZE), (2, 2, 8, 255))
|
|
|
|
# Спиральные рукава галактики
|
|
galaxy = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
gdraw = ImageDraw.Draw(galaxy)
|
|
|
|
for arm in range(3):
|
|
arm_angle = arm * (2.0 * math.pi / 3.0) - 0.4
|
|
for step in range(100):
|
|
t = step / 99.0
|
|
ang = arm_angle + t * math.pi * 1.6
|
|
dist = 100 + (t**1.1) * (HI_SIZE * 0.48)
|
|
cx = CENTER + dist * math.cos(ang)
|
|
cy = CENTER + dist * math.sin(ang)
|
|
r_cloud = 260 + t * 450
|
|
|
|
if t < 0.3:
|
|
col = (75, 15, 95, int(55 * (1 - t*0.5)))
|
|
elif t < 0.65:
|
|
col = (25, 45, 125, int(60 * (1 - t*0.5)))
|
|
else:
|
|
col = (10, 85, 130, int(45 * (1 - t*0.5)))
|
|
|
|
gdraw.ellipse([cx - r_cloud, cy - r_cloud*0.7, cx + r_cloud, cy + r_cloud*0.7], fill=col)
|
|
|
|
galaxy = galaxy.filter(ImageFilter.GaussianBlur(320))
|
|
bg = Image.alpha_composite(bg, galaxy)
|
|
|
|
# Золотистый аккреционный диск
|
|
accretion = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
adraw = ImageDraw.Draw(accretion)
|
|
|
|
for i in range(25):
|
|
t = i / 24.0
|
|
w = 1900 - t * 750
|
|
h = 680 - t * 290
|
|
alpha = int(65 * (1.0 - t**1.3))
|
|
adraw.ellipse([CENTER - w, CENTER - h, CENTER + w, CENTER + h], fill=(245, 135 + int(t*40), 25 + int(t*50), alpha))
|
|
|
|
adraw.ellipse([CENTER - 1150, CENTER - 1150, CENTER + 1150, CENTER + 1150], fill=(255, 160, 40, 45))
|
|
adraw.ellipse([CENTER - 800, CENTER - 800, CENTER + 800, CENTER + 800], fill=(255, 205, 75, 55))
|
|
accretion = accretion.filter(ImageFilter.GaussianBlur(220))
|
|
bg = Image.alpha_composite(bg, accretion)
|
|
|
|
# Световые лучи (God Rays)
|
|
god_rays = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
gr_draw = ImageDraw.Draw(god_rays)
|
|
np.random.seed(333)
|
|
for _ in range(36):
|
|
r_ang = np.random.uniform(0, 2 * math.pi)
|
|
r_len = np.random.uniform(HI_SIZE * 0.35, HI_SIZE * 0.55)
|
|
r_w = np.random.uniform(30, 90)
|
|
p_end = (CENTER + r_len * math.cos(r_ang), CENTER + r_len * math.sin(r_ang))
|
|
alpha = np.random.randint(15, 40)
|
|
col = (255, 210, 110, alpha) if np.random.rand() > 0.4 else (130, 220, 255, alpha)
|
|
gr_draw.line([(CENTER, CENTER), p_end], fill=col, width=int(r_w))
|
|
|
|
god_rays = god_rays.filter(ImageFilter.GaussianBlur(120))
|
|
bg = Image.alpha_composite(bg, god_rays)
|
|
|
|
# Волшебные блестки и оптические звезды
|
|
glitter_layer = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
gldraw = ImageDraw.Draw(glitter_layer)
|
|
|
|
for _ in range(650):
|
|
sx = np.random.uniform(30, HI_SIZE - 30)
|
|
sy = np.random.uniform(30, HI_SIZE - 30)
|
|
sr = np.random.uniform(2, 8)
|
|
alpha = np.random.randint(70, 230)
|
|
tints = [(255, 255, 255), (255, 230, 170), (180, 230, 255), (255, 190, 230), (255, 215, 120)]
|
|
tint = tints[np.random.randint(len(tints))]
|
|
gldraw.ellipse([sx - sr, sy - sr, sx + sr, sy + sr], fill=(*tint, alpha))
|
|
|
|
for _ in range(55):
|
|
bx = np.random.uniform(80, HI_SIZE - 80)
|
|
by = np.random.uniform(80, HI_SIZE - 80)
|
|
if math.hypot(bx - CENTER, by - CENTER) < HI_SIZE * 0.12:
|
|
continue
|
|
b_len = np.random.uniform(25, 75)
|
|
b_glow = np.random.uniform(8, 20)
|
|
gldraw.ellipse([bx - b_glow, by - b_glow, bx + b_glow, by + b_glow], fill=(255, 240, 200, 120))
|
|
gldraw.line([(bx - b_len, by), (bx + b_len, by)], fill=(255, 255, 255, 220), width=3)
|
|
gldraw.line([(bx, by - b_len), (bx, by + b_len)], fill=(255, 255, 255, 220), width=3)
|
|
gldraw.ellipse([bx - 4, by - 4, bx + 4, by + 4], fill=(255, 255, 255, 255))
|
|
|
|
glitter_layer = glitter_layer.filter(ImageFilter.GaussianBlur(3))
|
|
bg = Image.alpha_composite(bg, glitter_layer)
|
|
|
|
# 13-лучевая звезда из Варианта А
|
|
rays_layer = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
rdraw = ImageDraw.Draw(rays_layer)
|
|
|
|
N_POINTS = 13
|
|
R_OUTER = HI_SIZE * 0.435
|
|
R_MID = HI_SIZE * 0.280
|
|
R_INNER = HI_SIZE * 0.160
|
|
|
|
angles = [(-math.pi / 2.0) + (2.0 * math.pi * i / N_POINTS) for i in range(N_POINTS)]
|
|
|
|
for i in range(N_POINTS):
|
|
a_tip = angles[i]
|
|
tip = (CENTER + R_OUTER * math.cos(a_tip), CENTER + R_OUTER * math.sin(a_tip))
|
|
|
|
a_left = a_tip - (math.pi / N_POINTS) * 0.85
|
|
a_right = a_tip + (math.pi / N_POINTS) * 0.85
|
|
|
|
p_left_base = (CENTER + R_INNER * math.cos(a_left), CENTER + R_INNER * math.sin(a_left))
|
|
p_right_base = (CENTER + R_INNER * math.cos(a_right), CENTER + R_INNER * math.sin(a_right))
|
|
|
|
p_left_mid = (CENTER + R_MID * math.cos(a_tip - 0.12), CENTER + R_MID * math.sin(a_tip - 0.12))
|
|
p_right_mid = (CENTER + R_MID * math.cos(a_tip + 0.12), CENTER + R_MID * math.sin(a_tip + 0.12))
|
|
|
|
pts_left = []
|
|
pts_right = []
|
|
for step in range(30):
|
|
t = step / 29.0
|
|
lx = (1-t)**2 * p_left_base[0] + 2*(1-t)*t * p_left_mid[0] + t**2 * tip[0]
|
|
ly = (1-t)**2 * p_left_base[1] + 2*(1-t)*t * p_left_mid[1] + t**2 * tip[1]
|
|
pts_left.append((lx, ly))
|
|
|
|
rx = (1-t)**2 * p_right_base[0] + 2*(1-t)*t * p_right_mid[0] + t**2 * tip[0]
|
|
ry = (1-t)**2 * p_right_base[1] + 2*(1-t)*t * p_right_mid[1] + t**2 * tip[1]
|
|
pts_right.append((rx, ry))
|
|
|
|
polygon = pts_left + pts_right[::-1]
|
|
rdraw.polygon(polygon, fill=(255, 195, 45, 32))
|
|
|
|
threads_glow = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
tdraw_g = ImageDraw.Draw(threads_glow)
|
|
|
|
threads_core = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
tdraw_c = ImageDraw.Draw(threads_core)
|
|
|
|
for i in range(N_POINTS):
|
|
p_tip = (CENTER + R_OUTER * math.cos(angles[i]), CENTER + R_OUTER * math.sin(angles[i]))
|
|
p_in = (CENTER + R_INNER * 0.9 * math.cos(angles[i]), CENTER + R_INNER * 0.9 * math.sin(angles[i]))
|
|
|
|
tdraw_g.line([p_in, p_tip], fill=(255, 185, 30, 170), width=34)
|
|
tdraw_c.line([p_in, p_tip], fill=(255, 220, 80, 230), width=14)
|
|
tdraw_c.line([p_in, p_tip], fill=(255, 255, 230, 255), width=6)
|
|
|
|
for i in range(N_POINTS):
|
|
p1 = (CENTER + R_OUTER * math.cos(angles[i]), CENTER + R_OUTER * math.sin(angles[i]))
|
|
p2 = (CENTER + R_OUTER * math.cos(angles[(i + 4) % N_POINTS]), CENTER + R_OUTER * math.sin(angles[(i + 4) % N_POINTS]))
|
|
tdraw_g.line([p1, p2], fill=(255, 170, 40, 95), width=20)
|
|
tdraw_c.line([p1, p2], fill=(255, 235, 140, 180), width=6)
|
|
tdraw_c.line([p1, p2], fill=(255, 255, 255, 240), width=2)
|
|
|
|
rdraw.ellipse([CENTER - R_MID, CENTER - R_MID, CENTER + R_MID, CENTER + R_MID], outline=(255, 190, 50, 150), width=8)
|
|
rdraw.ellipse([CENTER - R_INNER, CENTER - R_INNER, CENTER + R_INNER, CENTER + R_INNER], outline=(100, 220, 255, 190), width=10)
|
|
|
|
|
|
# ==============================================================================
|
|
# 2. ИДЕАЛЬНО БЕСШОВНЫЙ СТЫК КОЛЬЦА И ХВОСТА ДЕВЯТКИ (C^1 TANGENTIAL CONTINUITY)
|
|
# ==============================================================================
|
|
# Устранение угла на стыке:
|
|
# Хвостик девятки плавно вытекает по касательной (dRad/dt = 0 в точке схода).
|
|
# Траектория начинается глубже внутри кольца, поэтому стык абсолютно монолитен!
|
|
|
|
trisk_glow = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
tgdraw = ImageDraw.Draw(trisk_glow)
|
|
|
|
trisk_body = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
tbdraw = ImageDraw.Draw(trisk_body)
|
|
|
|
silk_line = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
sldraw = ImageDraw.Draw(silk_line)
|
|
|
|
R_RING = HI_SIZE * 0.082
|
|
|
|
for k in range(3):
|
|
rot = -math.pi / 2.0 + k * (2.0 * math.pi / 3.0)
|
|
spine_pts = []
|
|
widths = []
|
|
|
|
# 1. Погруженная часть (внутри дуги центрального кольца)
|
|
# Идет ровно по радиусу R_RING с постоянной толщиной 26px
|
|
in_steps = 35
|
|
for s in range(in_steps):
|
|
t = s / float(in_steps)
|
|
ang = (rot - 0.45) + t * 0.45
|
|
rad = R_RING
|
|
px = CENTER + rad * math.cos(ang)
|
|
py = CENTER + rad * math.sin(ang)
|
|
spine_pts.append((px, py))
|
|
widths.append(26.0)
|
|
|
|
# 2. Плавный тангенциальный сход наружу (u^1.6 обеспечивает производную dRad/dt = 0 в точке схода rot)
|
|
# Поэтому внешняя кромка кольца переходит в хвост БЕЗ единого излома или угла!
|
|
out_steps = 85
|
|
for s in range(1, out_steps):
|
|
u = s / float(out_steps - 1)
|
|
ang = rot + u * 1.70
|
|
rad = R_RING + (u**1.6) * (HI_SIZE * 0.155)
|
|
px = CENTER + rad * math.cos(ang)
|
|
py = CENTER + rad * math.sin(ang)
|
|
spine_pts.append((px, py))
|
|
# Плавное утончение хвоста от 26px до 3px на кончике
|
|
w = 26.0 * (1.0 - (u**0.85) * 0.88)
|
|
widths.append(max(3.0, w))
|
|
|
|
left_edge = []
|
|
right_edge = []
|
|
for i in range(len(spine_pts)):
|
|
p = spine_pts[i]
|
|
w = widths[i] / 2.0
|
|
if i == 0:
|
|
dx = spine_pts[1][0] - p[0]
|
|
dy = spine_pts[1][1] - p[1]
|
|
elif i == len(spine_pts) - 1:
|
|
dx = p[0] - spine_pts[i-1][0]
|
|
dy = p[1] - spine_pts[i-1][1]
|
|
else:
|
|
dx = spine_pts[i+1][0] - spine_pts[i-1][0]
|
|
dy = spine_pts[i+1][1] - spine_pts[i-1][1]
|
|
length = math.hypot(dx, dy) + 1e-6
|
|
nx = -dy / length
|
|
ny = dx / length
|
|
left_edge.append((p[0] + nx * w, p[1] + ny * w))
|
|
right_edge.append((p[0] - nx * w, p[1] - ny * w))
|
|
|
|
ribbon_poly = left_edge + right_edge[::-1]
|
|
|
|
tgdraw.polygon(ribbon_poly, fill=(255, 175, 30, 210))
|
|
tbdraw.polygon(ribbon_poly, fill=(255, 240, 120, 250))
|
|
for p in range(len(spine_pts) - 1):
|
|
sldraw.line([spine_pts[p], spine_pts[p+1]], fill=(255, 255, 255, 255), width=6)
|
|
|
|
# Центральное кольцо идеально сопряжено с хвостами (единая толщина 26px)
|
|
tgdraw.ellipse([CENTER - R_RING, CENTER - R_RING, CENTER + R_RING, CENTER + R_RING], outline=(255, 180, 30, 230), width=50)
|
|
tbdraw.ellipse([CENTER - R_RING, CENTER - R_RING, CENTER + R_RING, CENTER + R_RING], outline=(255, 235, 110, 255), width=26)
|
|
sldraw.ellipse([CENTER - R_RING, CENTER - R_RING, CENTER + R_RING, CENTER + R_RING], outline=(255, 255, 255, 255), width=8)
|
|
|
|
# Оптическое фотонное ядро
|
|
core = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
cdraw = ImageDraw.Draw(core)
|
|
for r_c in range(130, 0, -4):
|
|
alpha = int(255 * (1.0 - (r_c / 130.0)**1.3))
|
|
cdraw.ellipse([CENTER - r_c, CENTER - r_c, CENTER + r_c, CENTER + r_c], fill=(255, 253, 240, alpha))
|
|
|
|
for dx in range(-700, 700, 2):
|
|
fade = 1.0 - (abs(dx) / 700.0)**2
|
|
alpha = int(195 * fade)
|
|
h_w = int(14 * (1.0 - abs(dx) / 700.0) + 2)
|
|
cdraw.ellipse([CENTER + dx - 2, CENTER - h_w, CENTER + dx + 2, CENTER + h_w], fill=(185, 242, 255, alpha))
|
|
|
|
for d in range(-350, 350, 2):
|
|
fade = 1.0 - (abs(d) / 350.0)**2
|
|
alpha = int(90 * fade)
|
|
cdraw.ellipse([CENTER + d - 1, CENTER + d - 1, CENTER + d + 1, CENTER + d + 1], fill=(255, 220, 160, alpha))
|
|
|
|
# Сборка и Bloom
|
|
comp = Image.alpha_composite(bg, rays_layer)
|
|
comp = Image.alpha_composite(comp, threads_glow.filter(ImageFilter.GaussianBlur(45)))
|
|
comp = Image.alpha_composite(comp, trisk_glow.filter(ImageFilter.GaussianBlur(32)))
|
|
comp = Image.alpha_composite(comp, threads_core)
|
|
comp = Image.alpha_composite(comp, trisk_body)
|
|
comp = Image.alpha_composite(comp, silk_line)
|
|
comp = Image.alpha_composite(comp, core)
|
|
|
|
bloom = comp.filter(ImageFilter.GaussianBlur(28))
|
|
comp = Image.blend(comp, bloom, 0.22)
|
|
|
|
final_1024 = comp.resize((OUT_SIZE, OUT_SIZE), Image.Resampling.LANCZOS)
|
|
|
|
out_file = '/home/alexives/.gemini/antigravity-ide/brain/4ff924d9-3d7f-4202-9066-3ac5c3088b8e/star_13_seamless_junction_final.png'
|
|
final_1024.save(out_file, 'PNG')
|
|
print("Successfully generated SEAMLESS JUNCTION ICON at:", out_file)
|