"""
三棱镜色散动画（pygame）
- 白光从左侧射入三棱镜，在棱镜内分成七条色光
- 七条色光（红橙黄绿青蓝紫）逐条出现，每条间隔 0.35 秒
- 每条光线出现时有一小段"生长"动画，末端标注颜色名
- 全部出现后停留 2.5 秒，然后自动循环
操作：ESC 或关闭窗口退出
"""
import os
os.environ.setdefault("SDL_VIDEO_WINDOW_POS", "200,100")

import sys
import math
import random
import pygame

# ===================== 基础设置 =====================
pygame.init()
WIDTH, HEIGHT = 900, 560
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("三棱镜色散")
clock = pygame.time.Clock()

# 中文字体：按优先级找一个系统里有的字体，找不到就用默认字体（中文可能显示为方块）
_FONT_CANDIDATES = [
    "microsoftyahei", "simhei", "simsun", "pingfang sc", "hiragino sans gb",
    "wenquanyi micro hei", "noto sans cjk sc", "source han sans cn", "droid sans fallback",
]
_FONT_NAME = next((n for n in _FONT_CANDIDATES if n in pygame.font.get_fonts()), None)
FONT   = pygame.font.SysFont(_FONT_NAME, 22)
TITLE  = pygame.font.SysFont(_FONT_NAME, 34, bold=True)
HINT   = pygame.font.SysFont(_FONT_NAME, 18)

BG       = (10, 12, 30)
WHITE    = (255, 255, 235)
GLASS    = (150, 195, 230, 90)     # 棱镜玻璃填充（含透明度）
GLASS_ED = (215, 238, 255)         # 棱镜边框

# 七色光谱：颜色 + 名称 + 折射角（度）。紫光偏折最大，红光最小
SPECTRUM = [
    ((255,  40,  40), "红", 22),
    ((255, 145,  20), "橙", 24),
    ((255, 228,  40), "黄", 26),
    (( 50, 215,  80), "绿", 28),
    (( 40, 200, 215), "青", 30),
    (( 60,  95, 240), "蓝", 32),
    ((155,  60, 220), "紫", 34),
]

# ===================== 几何 =====================
# 等边三角形棱镜，尖端朝上
APEX  = (500, 110)
L_BOT = (410, 330)
R_BOT = (590, 330)
PRISM = [APEX, L_BOT, R_BOT]

IN_START = (90, 195)      # 白光起点
IN_HIT   = (455, 236)     # 白光打到棱镜左侧面的点
EXIT     = (525, 258)     # 色光从右侧面射出的点
RAY_LEN  = 350             # 出射光线长度

# ===================== 时间参数（秒）=====================
T_WHITE_TRAVEL = 0.7     # 白光射到棱镜所用时间
T_FIRST_RAY    = 0.9     # 第一条色光开始出现的时间
INTERVAL       = 0.35    # ★ 每条光线间隔 0.35 秒
RAY_GROW       = 0.40    # 单条光线的生长时长
PAUSE_AFTER    = 2.5     # 七条全部出现后的停留时间

CYCLE = T_FIRST_RAY + len(SPECTRUM) * INTERVAL + RAY_GROW + PAUSE_AFTER

# ===================== 背景星星 =====================
random.seed(7)
STARS = [(random.randint(0, WIDTH), random.randint(0, HEIGHT),
          random.uniform(0, math.tau), random.uniform(0.6, 1.8))
         for _ in range(90)]

# ===================== 绘制函数 =====================
def text(font, msg, color, center):
    """在指定中心位置画一行文字"""
    surf = font.render(msg, True, color)
    screen.blit(surf, surf.get_rect(center=center))

def draw_stars(t):
    for x, y, ph, sp in STARS:
        b = 90 + 70 * math.sin(t * sp + ph)
        r = 1 if b < 130 else 2
        c = int(b)
        pygame.draw.circle(screen, (c, c, c + 15), (x, y), r)

def glow_line(start, end, color, width=3, radius=6, alpha_max=55):
    """带辉光的线段：先画几层越来越粗的半透明线，再画实心主线"""
    layer = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
    for i in range(radius, 0, -1):
        a = int(alpha_max * (radius - i + 1) / radius)
        pygame.draw.line(layer, (*color, a), start, end, width + i * 2)
    screen.blit(layer, (0, 0))
    pygame.draw.line(screen, color, start, end, width)

def draw_white_light(p):
    """p: 0→1，白光从起点延伸到棱镜"""
    if p <= 0:
        return
    p = min(p, 1.0)
    x = IN_START[0] + (IN_HIT[0] - IN_START[0]) * p
    y = IN_START[1] + (IN_HIT[1] - IN_START[1]) * p
    glow_line(IN_START, (x, y), WHITE, width=5, radius=9, alpha_max=70)
    # 入射点的小闪光
    if p >= 1:
        flash(IN_HIT, WHITE, 10)

def flash(pos, color, r):
    layer = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
    for i in range(r, 0, -1):
        pygame.draw.circle(layer, (*color, int(90 * i / r)), pos, i)
    screen.blit(layer, (0, 0))

def draw_prism():
    layer = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
    pygame.draw.polygon(layer, GLASS, PRISM)
    screen.blit(layer, (0, 0))
    pygame.draw.polygon(screen, GLASS_ED, PRISM, 2)
    # 左侧面高光
    pygame.draw.line(screen, (255, 255, 255), APEX, L_BOT, 1)
    # 内部微弱的白色折射痕迹（入射点 → 出射点）
    inner = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
    pygame.draw.line(inner, (255, 255, 220, 60), IN_HIT, EXIT, 3)
    screen.blit(inner, (0, 0))

def ray_end(color_idx, length):
    ang = math.radians(SPECTRUM[color_idx][2])
    return (EXIT[0] + math.cos(ang) * length,
            EXIT[1] + math.sin(ang) * length)

def draw_ray(i, grow):
    """grow: 0→1，画第 i 条色光"""
    color = SPECTRUM[i][0]
    end = ray_end(i, RAY_LEN * min(grow, 1.0))
    glow_line(EXIT, end, color, width=3, radius=5, alpha_max=45)
    if grow >= 1.0:
        flash(end, color, 7)
        lp = ray_end(i, RAY_LEN + 26)
        text(FONT, SPECTRUM[i][1], color, (int(lp[0]), int(lp[1])))

def draw_title_and_hint():
    text(TITLE, "三 棱 镜 色 散", (225, 235, 255), (WIDTH // 2, 42))
    text(HINT, "ESC 退出", (110, 115, 150), (WIDTH - 78, HEIGHT - 24))

# ===================== 主循环 =====================
def main():
    running = True
    while running:
        for e in pygame.event.get():
            if e.type == pygame.QUIT:
                running = False
            if e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE:
                running = False

        t = (pygame.time.get_ticks() / 1000.0) % CYCLE
        screen.fill(BG)
        draw_stars(pygame.time.get_ticks() / 1000.0)

        # 1) 白光：先飞向棱镜，之后一直亮着
        draw_white_light(min(t / T_WHITE_TRAVEL, 1.0) if t < T_WHITE_TRAVEL else 1.0)

        # 2) 棱镜
        draw_prism()

        # 3) 七条色光逐条出现
        shown = 0
        for i in range(len(SPECTRUM)):
            start = T_FIRST_RAY + i * INTERVAL
            if t >= start:
                draw_ray(i, (t - start) / RAY_GROW)
                if t >= start + RAY_GROW:
                    shown += 1
            else:
                break

        # 4) 全部出现后，在底部显示一行说明
        if shown == len(SPECTRUM):
            text(FONT, "白光由多种色光组成，玻璃对不同颜色的光折射程度不同，于是被分解成七色光谱",
                 (175, 185, 215), (WIDTH // 2, HEIGHT - 45))

        draw_title_and_hint()
        pygame.display.flip()
        clock.tick(60)

    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()
