168 lines
7.0 KiB
Python
168 lines
7.0 KiB
Python
import math
|
|
import numpy as np
|
|
from PIL import Image, ImageDraw, ImageFilter
|
|
|
|
SIZE = 1024
|
|
CENTER = SIZE / 2.0
|
|
|
|
# Base: deep dark space with glowing cosmic portal nebula
|
|
bg = Image.new('RGBA', (SIZE, SIZE), (2, 3, 14, 255))
|
|
bdraw = ImageDraw.Draw(bg)
|
|
|
|
# Distant starfield
|
|
np.random.seed(777)
|
|
tints = [(255, 255, 255), (180, 230, 255), (255, 230, 160)]
|
|
for _ in range(500):
|
|
x = np.random.randint(0, SIZE)
|
|
y = np.random.randint(0, SIZE)
|
|
br = np.random.randint(130, 255)
|
|
sz = int(np.random.choice([1, 1, 2, 2, 3]))
|
|
t = tints[np.random.randint(0, len(tints))]
|
|
bdraw.ellipse([x, y, x + sz, y + sz], fill=t + (br,))
|
|
|
|
# Ethereal violet-cyan aura
|
|
neb = Image.new('RGBA', (SIZE, SIZE), (0, 0, 0, 0))
|
|
ndraw = ImageDraw.Draw(neb)
|
|
ndraw.ellipse([60, 60, 960, 960], fill=(60, 10, 90, 120))
|
|
ndraw.ellipse([140, 140, 880, 880], fill=(0, 80, 140, 130))
|
|
ndraw.ellipse([260, 260, 760, 760], fill=(120, 60, 20, 110))
|
|
neb = neb.filter(ImageFilter.GaussianBlur(80))
|
|
bg = Image.alpha_composite(bg, neb)
|
|
|
|
N_POINTS = 13
|
|
R_TIP = SIZE * 0.465
|
|
R_INNER = SIZE * 0.285
|
|
R_MEDALLION = SIZE * 0.26
|
|
R_CORE = SIZE * 0.095
|
|
|
|
start_angle = -math.pi / 2.0
|
|
|
|
tips = []
|
|
valleys = []
|
|
for i in range(N_POINTS):
|
|
ang_tip = start_angle + i * (2.0 * math.pi / N_POINTS)
|
|
ang_val = ang_tip + (math.pi / N_POINTS)
|
|
tips.append((CENTER + R_TIP * math.cos(ang_tip), CENTER + R_TIP * math.sin(ang_tip)))
|
|
valleys.append((CENTER + R_INNER * math.cos(ang_val), CENTER + R_INNER * math.sin(ang_val)))
|
|
|
|
star_layer = Image.new('RGBA', (SIZE, SIZE), (0, 0, 0, 0))
|
|
sdraw = ImageDraw.Draw(star_layer)
|
|
|
|
GOLD_CORE = (255, 255, 245, 255)
|
|
GOLD_LIGHT = (255, 230, 120, 245)
|
|
GOLD_AURA = (255, 170, 30, 110)
|
|
CYAN_GLOW = (60, 210, 255, 190)
|
|
|
|
# Sacred curved energy rays with cyan inner aura
|
|
for i in range(N_POINTS):
|
|
ang_tip = start_angle + i * (2.0 * math.pi / N_POINTS)
|
|
ang_vl = ang_tip - (math.pi / N_POINTS)
|
|
ang_vr = ang_tip + (math.pi / N_POINTS)
|
|
|
|
p_tip = tips[i]
|
|
p_vl = valleys[(i - 1) % N_POINTS]
|
|
p_vr = valleys[i]
|
|
|
|
ctrl_r = (R_TIP + R_INNER) * 0.49
|
|
p_ctrl_l = (CENTER + ctrl_r * math.cos((ang_tip + ang_vl)*0.5 + 0.025), CENTER + ctrl_r * math.sin((ang_tip + ang_vl)*0.5 + 0.025))
|
|
p_ctrl_r = (CENTER + ctrl_r * math.cos((ang_tip + ang_vr)*0.5 - 0.025), CENTER + ctrl_r * math.sin((ang_tip + ang_vr)*0.5 - 0.025))
|
|
|
|
pts_l = [((1-t)**2 * p_vl[0] + 2*(1-t)*t * p_ctrl_l[0] + t**2 * p_tip[0],
|
|
(1-t)**2 * p_vl[1] + 2*(1-t)*t * p_ctrl_l[1] + t**2 * p_tip[1]) for t in np.linspace(0, 1, 30)]
|
|
pts_r = [((1-t)**2 * p_tip[0] + 2*(1-t)*t * p_ctrl_r[0] + t**2 * p_vr[0],
|
|
(1-t)**2 * p_tip[1] + 2*(1-t)*t * p_ctrl_r[1] + t**2 * p_vr[1]) for t in np.linspace(0, 1, 30)]
|
|
|
|
poly = [(CENTER, CENTER)] + pts_l + pts_r
|
|
sdraw.polygon(poly, fill=(40, 25, 15, 120))
|
|
|
|
# Golden luminous photon edge with cyan rim
|
|
for edge in [pts_l, pts_r]:
|
|
for p in range(len(edge) - 1):
|
|
sdraw.line([edge[p], edge[p+1]], fill=GOLD_AURA, width=8)
|
|
sdraw.line([edge[p], edge[p+1]], fill=CYAN_GLOW, width=3)
|
|
sdraw.line([edge[p], edge[p+1]], fill=GOLD_CORE, width=1)
|
|
|
|
sdraw.line([CENTER, CENTER, p_tip[0], p_tip[1]], fill=GOLD_CORE, width=2)
|
|
|
|
# Pulsing 8-pointed micro flare on each star tip
|
|
for fl_len, fl_w in [(14, 2), (7, 1)]:
|
|
sdraw.line([p_tip[0] - fl_len, p_tip[1], p_tip[0] + fl_len, p_tip[1]], fill=GOLD_CORE, width=fl_w)
|
|
sdraw.line([p_tip[0], p_tip[1] - fl_len, p_tip[0], p_tip[1] + fl_len], fill=GOLD_CORE, width=fl_w)
|
|
diag = 6
|
|
sdraw.line([p_tip[0]-diag, p_tip[1]-diag, p_tip[0]+diag, p_tip[1]+diag], fill=GOLD_LIGHT, width=1)
|
|
sdraw.line([p_tip[0]-diag, p_tip[1]+diag, p_tip[0]+diag, p_tip[1]-diag], fill=GOLD_LIGHT, width=1)
|
|
|
|
# Medallion layer
|
|
med = Image.new('RGBA', (SIZE, SIZE), (0, 0, 0, 0))
|
|
mdraw = ImageDraw.Draw(med)
|
|
|
|
mdraw.ellipse([CENTER - R_MEDALLION, CENTER - R_MEDALLION, CENTER + R_MEDALLION, CENTER + R_MEDALLION], fill=(4, 7, 22, 245), outline=GOLD_LIGHT, width=4)
|
|
mdraw.ellipse([CENTER - R_MEDALLION - 4, CENTER - R_MEDALLION - 4, CENTER + R_MEDALLION + 4, CENTER + R_MEDALLION + 4], outline=CYAN_GLOW, width=2)
|
|
mdraw.ellipse([CENTER - R_MEDALLION + 8, CENTER - R_MEDALLION + 8, CENTER + R_MEDALLION - 8, CENTER + R_MEDALLION - 8], outline=(255, 235, 160, 160), width=1)
|
|
|
|
# Magic orbital ring of 26 constellations nodes (no text/runes, just pure magic starlight nodes)
|
|
for i in range(26):
|
|
a = i * (2 * math.pi / 26.0)
|
|
rad = R_MEDALLION - 12
|
|
nx = CENTER + rad * math.cos(a)
|
|
ny = CENTER + rad * math.sin(a)
|
|
sz = 2 if i % 2 == 0 else 1
|
|
mdraw.ellipse([nx - sz, ny - sz, nx + sz, ny + sz], fill=GOLD_CORE)
|
|
|
|
# Center Portal Eye
|
|
mdraw.ellipse([CENTER - R_CORE, CENTER - R_CORE, CENTER + R_CORE, CENTER + R_CORE], fill=(2, 4, 12, 255), outline=GOLD_LIGHT, width=5)
|
|
mdraw.ellipse([CENTER - R_CORE + 4, CENTER - R_CORE + 4, CENTER + R_CORE - 4, CENTER + R_CORE - 4], outline=CYAN_GLOW, width=2)
|
|
|
|
# 3 Chrome fluid ribbons with starry sparks
|
|
for k in range(3):
|
|
rot = start_angle + k * (2.0 * math.pi / 3.0)
|
|
outer = []
|
|
inner = []
|
|
steps = 65
|
|
for s in range(steps + 1):
|
|
t = s / float(steps)
|
|
t_curv = 0.5 * (1.0 - math.cos(t * math.pi))
|
|
cr = R_CORE + t_curv * (R_MEDALLION - R_CORE - 10)
|
|
ca = rot + t * 1.50
|
|
|
|
bx = CENTER + cr * math.cos(ca)
|
|
by = CENTER + cr * math.sin(ca)
|
|
outer.append((bx, by))
|
|
|
|
thick = math.sin(t * math.pi) * 16.0
|
|
ir = max(R_CORE, cr - thick)
|
|
inner.append((CENTER + ir * math.cos(ca + 0.08), CENTER + ir * math.sin(ca + 0.08)))
|
|
|
|
ribbon = outer + inner[::-1]
|
|
mdraw.polygon(ribbon, fill=(245, 175, 45, 160))
|
|
for p in range(len(outer) - 1):
|
|
mdraw.line([outer[p], outer[p+1]], fill=GOLD_AURA, width=6)
|
|
mdraw.line([outer[p], outer[p+1]], fill=GOLD_CORE, width=2)
|
|
for p in range(len(inner) - 1):
|
|
mdraw.line([inner[p], inner[p+1]], fill=CYAN_GLOW, width=2)
|
|
|
|
# Magic stellar flare on the 3 nine tails
|
|
tip = outer[-1]
|
|
for l, w in [(12, 2), (5, 1)]:
|
|
mdraw.line([tip[0]-l, tip[1], tip[0]+l, tip[1]], fill=GOLD_CORE, width=w)
|
|
mdraw.line([tip[0], tip[1]-l, tip[0], tip[1]+l], fill=GOLD_CORE, width=w)
|
|
|
|
# Central Singularity / Quasar Flare
|
|
for l, w in [(24, 3), (12, 1)]:
|
|
mdraw.line([CENTER - l, CENTER, CENTER + l, CENTER], fill=GOLD_CORE, width=w)
|
|
mdraw.line([CENTER, CENTER - l, CENTER, CENTER + l], fill=GOLD_CORE, width=w)
|
|
mdraw.line([CENTER - 10, CENTER - 10, CENTER + 10, CENTER + 10], fill=CYAN_GLOW, width=1)
|
|
mdraw.line([CENTER - 10, CENTER + 10, CENTER + 10, CENTER - 10], fill=CYAN_GLOW, width=1)
|
|
|
|
comp = Image.alpha_composite(bg, star_layer)
|
|
comp = Image.alpha_composite(comp, med)
|
|
|
|
bloom1 = comp.filter(ImageFilter.GaussianBlur(16))
|
|
bloom2 = comp.filter(ImageFilter.GaussianBlur(4))
|
|
comp = Image.blend(comp, bloom1, 0.24)
|
|
final_img = Image.blend(comp, bloom2, 0.22)
|
|
|
|
out_file = '/home/alexives/.gemini/antigravity-ide/brain/4ff924d9-3d7f-4202-9066-3ac5c3088b8e/star_13_nebula_portal_magic.png'
|
|
final_img.save(out_file, 'PNG')
|
|
print("Saved NEBULA PORTAL MAGIC to", out_file)
|