247 lines
12 KiB
Python
247 lines
12 KiB
Python
import math
|
|
import numpy as np
|
|
from PIL import Image, ImageDraw, ImageFilter
|
|
|
|
# 4X SUPER-SAMPLING FOR 100% BUTTERY SMOOTH ANTI-ALIASING
|
|
HI_SIZE = 4096
|
|
OUT_SIZE = 1024
|
|
CENTER = HI_SIZE / 2.0
|
|
|
|
# 1. DEEP INTERSTELLAR COSMOS AT 4096x4096
|
|
bg = Image.new('RGBA', (HI_SIZE, HI_SIZE), (2, 3, 10, 255))
|
|
|
|
# Пылевые рукава туманности (Gargantua / Milky Way)
|
|
nebula = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
ndraw = ImageDraw.Draw(nebula)
|
|
for r in range(16):
|
|
angle = math.radians(-32)
|
|
cx = CENTER + (r - 8) * 160 * math.cos(angle + math.pi/2)
|
|
cy = CENTER + (r - 8) * 160 * math.sin(angle + math.pi/2)
|
|
w = 2200 - abs(r - 8) * 140
|
|
h = 900 - abs(r - 8) * 60
|
|
col = (45, 25, 75, 45) if r % 2 == 0 else (20, 50, 95, 50)
|
|
ndraw.ellipse([cx - w, cy - h, cx + w, cy + h], fill=col)
|
|
|
|
nebula = nebula.filter(ImageFilter.GaussianBlur(360))
|
|
bg = Image.alpha_composite(bg, nebula)
|
|
|
|
# Аккреционный диск золотого гравитационного линзирования
|
|
accretion = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
adraw = ImageDraw.Draw(accretion)
|
|
adraw.ellipse([CENTER - 1500, CENTER - 900, CENTER + 1500, CENTER + 900], fill=(225, 120, 20, 40))
|
|
adraw.ellipse([CENTER - 1100, CENTER - 1100, CENTER + 1100, CENTER + 1100], fill=(255, 160, 35, 50))
|
|
adraw.ellipse([CENTER - 750, CENTER - 750, CENTER + 750, CENTER + 750], fill=(255, 200, 70, 45))
|
|
accretion = accretion.filter(ImageFilter.GaussianBlur(240))
|
|
bg = Image.alpha_composite(bg, accretion)
|
|
|
|
# Мягкие звезды (гауссовы сферы разного радиуса)
|
|
np.random.seed(42)
|
|
stars = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
sdraw = ImageDraw.Draw(stars)
|
|
for _ in range(450):
|
|
sx = np.random.uniform(50, HI_SIZE - 50)
|
|
sy = np.random.uniform(50, HI_SIZE - 50)
|
|
sr = np.random.uniform(2, 9)
|
|
brightness = np.random.randint(90, 230)
|
|
tints = [(255, 255, 255), (255, 230, 180), (190, 225, 255)]
|
|
tint = tints[np.random.randint(len(tints))]
|
|
sdraw.ellipse([sx - sr, sy - sr, sx + sr, sy + sr], fill=(*tint, brightness))
|
|
|
|
stars = stars.filter(ImageFilter.GaussianBlur(3))
|
|
bg = Image.alpha_composite(bg, stars)
|
|
|
|
# 2. РОВНО 13 ЛУЧЕЙ (ПЛАВНЫЕ ЭНЕРГЕТИЧЕСКИЕ ЛЕПЕСТКИ, А НЕ ГОЛЫЙ ЧЕРТЕЖ)
|
|
# Каждый из 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 # Концы 13 лучей
|
|
R_MID = HI_SIZE * 0.280 # Пересечение лучей
|
|
R_INNER = HI_SIZE * 0.160 # Внутренний пояс
|
|
|
|
# Угол вершины 0 направлен строго вертикально вверх (-pi/2)
|
|
angles = [(-math.pi / 2.0) + (2.0 * math.pi * i / N_POINTS) for i in range(N_POINTS)]
|
|
|
|
# Рисуем 13 изящных световых лепестков с плавным утолщением и схождением в острие
|
|
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, 28))
|
|
|
|
# Теперь рисуем сияющие гармонические нити 13-лучевой звезды (звездный полигон step=4 и step=5)
|
|
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)
|
|
|
|
# Основные 13 лучей от центра к вершинам с плавным переходом ширины
|
|
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, 160), width=32)
|
|
# Золотое тело луча
|
|
tdraw_c.line([p_in, p_tip], fill=(255, 220, 80, 220), width=14)
|
|
# Фотонная сердцевина
|
|
tdraw_c.line([p_in, p_tip], fill=(255, 255, 230, 255), width=6)
|
|
|
|
# Сакральные пересечения тридекаграммы (соединяем вершины шагом 4: 13/4)
|
|
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, 90), width=18)
|
|
tdraw_c.line([p1, p2], fill=(255, 235, 140, 170), width=6)
|
|
tdraw_c.line([p1, p2], fill=(255, 255, 255, 230), width=2)
|
|
|
|
# Сакральное золотое кольцо-опора для 13 лучей
|
|
rdraw.ellipse([CENTER - R_MID, CENTER - R_MID, CENTER + R_MID, CENTER + R_MID], outline=(255, 190, 50, 140), width=8)
|
|
rdraw.ellipse([CENTER - R_INNER, CENTER - R_INNER, CENTER + R_INNER, CENTER + R_INNER], outline=(100, 220, 255, 180), width=10)
|
|
|
|
# 3. ЦЕНТРАЛЬНЫЙ ТРИСКЕЛИОН ХРОМ-999 (ШЕЛКОВЫЕ ЛЕНТЫ С ПЛАВНЫМИ ЗАВИТКАМИ)
|
|
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)
|
|
|
|
# Единое центральное кольцо трех девяток
|
|
R_RING = HI_SIZE * 0.082
|
|
tgdraw.ellipse([CENTER - R_RING, CENTER - R_RING, CENTER + R_RING, CENTER + R_RING], outline=(255, 180, 30, 220), width=48)
|
|
tbdraw.ellipse([CENTER - R_RING, CENTER - R_RING, CENTER + R_RING, CENTER + R_RING], outline=(255, 235, 110, 255), width=24)
|
|
tbdraw.ellipse([CENTER - R_RING, CENTER - R_RING, CENTER + R_RING, CENTER + R_RING], outline=(255, 255, 255, 255), width=8)
|
|
|
|
# 3 хвоста девяток под 120 градусов с переменной шириной (каллиграфическая лента)
|
|
for k in range(3):
|
|
rot = -math.pi / 2.0 + k * (2.0 * math.pi / 3.0)
|
|
|
|
# Гладкий путь девяточки
|
|
spine_pts = []
|
|
widths = []
|
|
|
|
# 1. Выход из центрального кольца по дуге
|
|
for step in range(50):
|
|
t = step / 49.0
|
|
# Угол раскручивания
|
|
ang = rot + t * 1.55
|
|
# Радиус увеличивается от центрального кольца наружу
|
|
rad = R_RING + t * (HI_SIZE * 0.170)
|
|
px = CENTER + rad * math.cos(ang)
|
|
py = CENTER + rad * math.sin(ang)
|
|
spine_pts.append((px, py))
|
|
# Лента широкая у основания (28px) и плавно сужается к кончику (10px)
|
|
w = 32.0 * (1.0 - t * 0.65)
|
|
widths.append(w)
|
|
|
|
# 2. Изящная логарифмическая спираль завитушки (хвостик девятки красиво закручивается)
|
|
tip_pt = spine_pts[-1]
|
|
tip_ang = rot + 1.55
|
|
for f in range(40):
|
|
ft = f / 39.0
|
|
f_r = 75.0 * (1.0 - ft * 0.8)
|
|
f_a = tip_ang + ft * math.pi * 1.8
|
|
fx = tip_pt[0] + f_r * math.cos(f_a) - 75.0 * math.cos(tip_ang)
|
|
fy = tip_pt[1] + f_r * math.sin(f_a) - 75.0 * math.sin(tip_ang)
|
|
spine_pts.append((fx, fy))
|
|
widths.append(max(4.0, 11.0 * (1.0 - ft * 0.7)))
|
|
|
|
# Рисуем каллиграфическую ленту через полигоны переменной толщины
|
|
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, 180))
|
|
# Тело ленты
|
|
tbdraw.polygon(ribbon_poly, fill=(255, 240, 120, 245))
|
|
|
|
# Внутренний тонкий неоновый хребет шелка
|
|
for p in range(len(spine_pts) - 1):
|
|
tbdraw.line([spine_pts[p], spine_pts[p+1]], fill=(255, 255, 255, 255), width=6)
|
|
|
|
# Фотонное оптическое ядро (кинематографический луч и сфера света)
|
|
core = Image.new('RGBA', (HI_SIZE, HI_SIZE), (0, 0, 0, 0))
|
|
cdraw = ImageDraw.Draw(core)
|
|
|
|
# Мягкая сфера
|
|
for r_c in range(120, 0, -4):
|
|
alpha = int(255 * (1.0 - r_c / 120.0)**1.4)
|
|
cdraw.ellipse([CENTER - r_c, CENTER - r_c, CENTER + r_c, CENTER + r_c], fill=(255, 252, 235, alpha))
|
|
|
|
# Анаморфотный горизонтальный кино-луч Интерстеллара (плавное затухание)
|
|
for dx in range(-600, 600, 2):
|
|
fade = 1.0 - (abs(dx) / 600.0)**2
|
|
alpha = int(190 * fade)
|
|
h_w = int(12 * (1.0 - abs(dx) / 600.0) + 2)
|
|
cdraw.ellipse([CENTER + dx - 2, CENTER - h_w, CENTER + dx + 2, CENTER + h_w], fill=(185, 240, 255, alpha))
|
|
|
|
# 4. КОМПОЗИТ С ДВУХСТУПЕНЧАТЫМ БЛУМОМ
|
|
comp = Image.alpha_composite(bg, rays_layer)
|
|
comp = Image.alpha_composite(comp, threads_glow.filter(ImageFilter.GaussianBlur(40)))
|
|
comp = Image.alpha_composite(comp, trisk_glow.filter(ImageFilter.GaussianBlur(30)))
|
|
comp = Image.alpha_composite(comp, threads_core)
|
|
comp = Image.alpha_composite(comp, trisk_body)
|
|
comp = Image.alpha_composite(comp, core)
|
|
|
|
# Глобальный кинематографический Bloom
|
|
bloom = comp.filter(ImageFilter.GaussianBlur(24))
|
|
comp = Image.blend(comp, bloom, 0.20)
|
|
|
|
# 5. ДАУНСЕЙМПЛИНГ С 4096 ДО 1024 ЧЕРЕЗ LANCZOS (ИДЕАЛЬНЫЙ 16x АНТИАЛИАСИНГ БЕЗ ПИКСЕЛЕЙ)
|
|
final_1024 = comp.resize((OUT_SIZE, OUT_SIZE), Image.Resampling.LANCZOS)
|
|
|
|
out_path = '/home/alexives/.gemini/antigravity-ide/brain/4ff924d9-3d7f-4202-9066-3ac5c3088b8e/star_13_silky_smooth_interstellar.png'
|
|
final_1024.save(out_path, 'PNG')
|
|
print(f"Generated silky smooth 100% anti-aliased icon: {out_path}")
|