import tkinter as tk
from tkinter import ttk
import math

class OpticsSim:
    def __init__(self, root):
        self.root = root
        self.root.title("八年级物理｜光的反射 & 三棱镜色散")
        self.root.geometry("1000x720")
        self.W = 860
        self.H = 540
        self.canvas = tk.Canvas(root, width=self.W, height=self.H, bg="#000000")
        self.canvas.pack(pady=4)

        # 模式
        self.mode = tk.StringVar(value="reflect")  # reflect / prism
        self.show_normal = tk.BooleanVar(value=True)
        self.is_specular = tk.BooleanVar(value=True)

        # 反射参数
        self.theta_i_var = tk.DoubleVar(value=40.0)    # 入射角 i
        self.mirror_alpha_var = tk.DoubleVar(value=0.0) # 镜面转角 α

        # 三棱镜色散色表
        self.rainbow = [
            ("red", 1.510),
            ("orange", 1.515),
            ("yellow", 1.518),
            ("green", 1.522),
            ("cyan", 1.526),
            ("blue", 1.530),
            ("#4B0082", 1.535),
            ("violet", 1.540)
        ]

        # 控制面板
        ctrl = ttk.Frame(root)
        ctrl.pack()

        ttk.Radiobutton(ctrl, text="光的反射", variable=self.mode, value="reflect", command=self.draw).grid(row=0, column=0, padx=4)
        ttk.Radiobutton(ctrl, text="三棱镜色散", variable=self.mode, value="prism", command=self.draw).grid(row=0, column=1, padx=4)

        ttk.Checkbutton(ctrl, text="显示法线", variable=self.show_normal, command=self.draw).grid(row=0, column=2, padx=4)
        ttk.Checkbutton(ctrl, text="镜面反射(关闭=漫反射)", variable=self.is_specular, command=self.draw).grid(row=0, column=3, padx=4)

        ttk.Label(ctrl, text="入射角 i：").grid(row=1, column=0, padx=3)
        ttk.Scale(ctrl, from_=0, to=80, variable=self.theta_i_var, orient=tk.HORIZONTAL, length=160, command=lambda x:self.draw()).grid(row=1, column=1)

        ttk.Label(ctrl, text="镜面转角 α：").grid(row=1, column=2, padx=3)
        ttk.Scale(ctrl, from_=-30, to=30, variable=self.mirror_alpha_var, orient=tk.HORIZONTAL, length=160, command=lambda x:self.draw()).grid(row=1, column=3)

        ttk.Button(ctrl, text="潜望镜演示", command=self.draw_periscope).grid(row=1, column=4, padx=6)

        self.info_label = ttk.Label(root, text="i = 40.0° , r = 40.0° , α = 0.0°｜镜转α → 反射光线转 2α", font=("Arial",11))
        self.info_label.pack()

        self.draw()

    def deg2rad(self, d):
        return d * math.pi / 180
    def rad2deg(self, r):
        return r * 180 / math.pi

    def snell(self, th1, n1, n2):
        s = (n1/n2)*math.sin(th1)
        if abs(s)>1:
            return None
        return math.asin(s)

    def draw_reflect(self):
        cx, cy = self.W//2, self.H//2
        i_deg = self.theta_i_var.get()
        alpha_deg = self.mirror_alpha_var.get()
        i_rad = self.deg2rad(i_deg)
        alpha_rad = self.deg2rad(alpha_deg)

        # 镜面
        L = 320
        mx1 = cx - L*math.cos(alpha_rad)
        my1 = cy - L*math.sin(alpha_rad)
        mx2 = cx + L*math.cos(alpha_rad)
        my2 = cy + L*math.sin(alpha_rad)
        self.canvas.create_line(mx1, my1, mx2, my2, fill="#dddddd", width=4)

        # 法线：垂直镜面
        normal_ang = alpha_rad + math.pi/2
        if self.show_normal.get():
            nl = 220
            nx1 = cx - nl*math.cos(normal_ang)
            ny1 = cy - nl*math.sin(normal_ang)
            nx2 = cx + nl*math.cos(normal_ang)
            ny2 = cy + nl*math.sin(normal_ang)
            self.canvas.create_line(nx1, ny1, nx2, ny2, fill="#44aaff", dash=(4,4), width=2)

        # 入射光方向（相对于法线）
        in_ang = normal_ang - i_rad
        in_len = 240
        sinx = cx - in_len*math.cos(in_ang)
        siny = cy - in_len*math.sin(in_ang)
        self.canvas.create_line(sinx, siny, cx, cy, fill="#ff3333", width=3)

        if self.is_specular.get():
            # 镜面反射：r = i
            out_ang = normal_ang + i_rad
            out_len = 240
            soutx = cx + out_len*math.cos(out_ang)
            souty = cy + out_len*math.sin(out_ang)
            self.canvas.create_line(cx, cy, soutx, souty, fill="#33ff33", width=3)
        else:
            # 漫反射：多条随机散开
            import random
            for _ in range(12):
                dr = self.deg2rad(random.uniform(-35, 35))
                out_ang = normal_ang + i_rad + dr
                out_len = 160
                ox = cx + out_len*math.cos(out_ang)
                oy = cy + out_len*math.sin(out_ang)
                self.canvas.create_line(cx, cy, ox, oy, fill="#88ff88", width=2)

        self.info_label.config(text=f"入射角 i={i_deg:.1f}° , 反射角 r={i_deg:.1f}° , 镜面转角 α={alpha_deg:.1f}°｜镜转 α，反射光线转动 2α")

    def draw_prism(self):
        self.info_label.config(text="三棱镜色散：白光分解七色光，红光偏折最小，紫光最大")
        cx, cy = self.W//2, self.H//2
        side = 180
        pts = []
        for idx in range(3):
            ang = self.deg2rad(30 + idx*120)
            x = cx + side*math.cos(ang)
            y = cy + side*math.sin(ang)
            pts.append((x,y))
        self.canvas.create_polygon(pts, fill="#223344", outline="#88aacc", width=2)

        theta_in_deg = self.theta_i_var.get()
        theta_in = self.deg2rad(theta_in_deg)
        in_len = 260
        sx = cx - in_len*math.cos(theta_in)
        sy = cy - in_len*math.sin(theta_in)
        self.canvas.create_line(sx, sy, cx, cy, fill="white", width=3)

        out_len = 360
        for color, n in self.rainbow:
            tr = self.snell(theta_in, 1.0, n)
            if tr is None:
                continue
            dev = theta_in - tr
            out_ang = theta_in + dev * 1.35
            ox = cx + out_len * math.cos(out_ang)
            oy = cy + out_len * math.sin(out_ang)
            self.canvas.create_line(cx, cy, ox, oy, fill=color, width=2)

    def draw_periscope(self):
        self.canvas.delete("all")
        self.info_label.config(text="潜望镜：光线两次镜面反射，方向不变")
        # 两个平面镜，45°
        L = 160
        # 上镜面
        x1, y1 = 220, 120
        x2, y2 = 280, 180
        self.canvas.create_line(x1,y1,x2,y2, fill="#dddddd", width=3)
        # 下镜面
        x3, y3 = 220, 360
        x4, y4 = 280, 420
        self.canvas.create_line(x3,y3,x4,y4, fill="#dddddd", width=3)
        # 光路
        self.canvas.create_line(120, 150, 250, 150, fill="#ff3333", width=3)
        self.canvas.create_line(250, 150, 250, 390, fill="#33ff33", width=3)
        self.canvas.create_line(250, 390, 380, 390, fill="#ffff33", width=3)

    def draw(self, *args):
        self.canvas.delete("all")
        m = self.mode.get()
        if m == "reflect":
            self.draw_reflect()
        elif m == "prism":
            self.draw_prism()

if __name__ == "__main__":
    root = tk.Tk()
    app = OpticsSim(root)
    root.mainloop()
