import tkinter as tk
from tkinter import ttk, messagebox
import math
import colorsys

class FractalApp:
    def __init__(self, root):
        self.root = root
        self.root.title("020. 最复杂的图形 —— 分形生成器")
        self.root.geometry("900x700")
        self.root.resizable(False, False)

        self.canvas = tk.Canvas(root, width=850, height=600, bg="black")
        self.canvas.pack(pady=10)

        # 控制面板
        ctrl = tk.Frame(root)
        ctrl.pack(pady=5)

        tk.Label(ctrl, text="选择分形：", font=("微软雅黑", 11)).pack(side=tk.LEFT)

        self.choice = ttk.Combobox(ctrl, width=20, state="readonly")
        self.choice['values'] = (
            "科赫雪花",
            "谢尔宾斯基三角形",
            "分形树",
            "曼德勃罗集",
            "龙形曲线"
        )
        self.choice.current(0)
        self.choice.pack(side=tk.LEFT, padx=5)

        tk.Label(ctrl, text="  迭代次数：").pack(side=tk.LEFT)
        self.iter_spin = tk.Spinbox(ctrl, from_=1, to=10, width=4)
        self.iter_spin.pack(side=tk.LEFT, padx=2)
        self.iter_spin.delete(0, tk.END)
        self.iter_spin.insert(0, "4")

        tk.Button(ctrl, text="生成", bg="#4CAF50", fg="white",
                  font=("微软雅黑", 10), width=10,
                  command=self.generate).pack(side=tk.LEFT, padx=10)

        tk.Button(ctrl, text="清空", bg="#f44336", fg="white",
                  font=("微软雅黑", 10), width=10,
                  command=lambda: self.canvas.delete("all")).pack(side=tk.LEFT)

        # 信息标签
        self.info = tk.Label(root, text="", font=("Consolas", 10), fg="gray")
        self.info.pack()

    def generate(self):
        self.canvas.delete("all")
        fractal = self.choice.get()
        try:
            n = int(self.iter_spin.get())
        except:
            n = 4

        cx, cy = 425, 300

        if fractal == "科赫雪花":
            self.draw_koch(cx, cy, n)
        elif fractal == "谢尔宾斯基三角形":
            self.draw_sierpinski(cx, cy, n)
        elif fractal == "分形树":
            self.draw_fractal_tree(cx, cy, n)
        elif fractal == "曼德勃罗集":
            self.draw_mandelbrot()
        elif fractal == "龙形曲线":
            self.draw_dragon(cx, cy, n)

    # ==================== 科赫雪花 ====================
    def koch(self, x1, y1, x2, y2, n):
        if n == 0:
            self.canvas.create_line(x1, y1, x2, y2, fill="cyan", width=1)
            return

        dx = (x2 - x1) / 3
        dy = (y2 - y1) / 3

        x3 = x1 + dx
        y3 = y1 + dy
        x5 = x1 + 2 * dx
        y5 = y1 + 2 * dy

        # 等边三角形的顶点
        x4 = (x3 + x5) / 2 - math.sin(math.pi / 3) * (y5 - y3)
        y4 = (y3 + y5) / 2 + math.sin(math.pi / 3) * (x5 - x3)

        self.koch(x1, y1, x3, y3, n - 1)
        self.koch(x3, y3, x4, y4, n - 1)
        self.koch(x4, y4, x5, y5, n - 1)
        self.koch(x5, y5, x2, y2, n - 1)

    def draw_koch(self, cx, cy, n):
        self.info.config(text="科赫雪花：用不断细分的等边三角形构成的无限边界曲线")
        r = 250
        pts = []
        for i in range(3):
            angle = -math.pi / 2 + 2 * math.pi * i / 3
            pts.append((cx + r * math.cos(angle), cy + r * math.sin(angle)))

        for i in range(3):
            self.koch(pts[i][0], pts[i][1], pts[(i + 1) % 3][0], pts[(i + 1) % 3][1], n)

    # ==================== 谢尔宾斯基三角形 ====================
    def sierpinski(self, x1, y1, x2, y2, x3, y3, n):
        if n == 0:
            self.canvas.create_polygon(x1, y1, x2, y2, x3, y3,
                                      fill="", outline="magenta", width=1)
            return

        mx1 = (x1 + x2) / 2
        my1 = (y1 + y2) / 2
        mx2 = (x2 + x3) / 2
        my2 = (y2 + y3) / 2
        mx3 = (x1 + x3) / 2
        my3 = (y1 + y3) / 2

        self.sierpinski(x1, y1, mx1, my1, mx3, my3, n - 1)
        self.sierpinski(mx1, my1, x2, y2, mx2, my2, n - 1)
        self.sierpinski(mx3, my3, mx2, my2, x3, y3, n - 1)

    def draw_sierpinski(self, cx, cy, n):
        self.info.config(text="谢尔宾斯基三角形：不断挖去中心三角形的递归结构")
        r = 280
        h = r * math.sqrt(3) / 2
        pts = [
            (cx, cy - h * 2 / 3),
            (cx - r, cy + h / 3),
            (cx + r, cy + h / 3)
        ]
        self.sierpinski(pts[0][0], pts[0][1],
                        pts[1][0], pts[1][1],
                        pts[2][0], pts[2][1], n)

    # ==================== 分形树 ====================
    def fractal_tree(self, x, y, angle, length, n, color):
        if n == 0 or length < 2:
            return

        x2 = x + length * math.cos(angle)
        y2 = y + length * math.sin(angle)

        # 根据深度变色
        if n > 3:
            c = "saddlebrown"
        elif n > 1:
            c = "forestgreen"
        else:
            c = "limegreen"

        self.canvas.create_line(x, y, x2, y2, fill=c, width=max(1, n))

        new_len = length * 0.7
        self.fractal_tree(x2, y2, angle - math.pi / 6, new_len, n - 1, c)
        self.fractal_tree(x2, y2, angle + math.pi / 6, new_len, n - 1, c)

    def draw_fractal_tree(self, cx, cy, n):
        self.info.config(text="分形树：模拟自然界中树枝的递归生长模式")
        self.fractal_tree(cx, cy, -math.pi / 2, 120, n, "brown")

    # ==================== 曼德勃罗集 ====================
    def draw_mandelbrot(self):
        self.info.config(text="曼德勃罗集：z = z² + c 迭代不发散的复数集合（最著名的分形）")
        w, h = 850, 600
        xmin, xmax = -2.2, 0.8
        ymin, ymax = -1.2, 1.2

        max_iter = 50

        for px in range(0, w, 2):
            for py in range(0, h, 2):
                x0 = xmin + (xmax - xmin) * px / w
                y0 = ymin + (ymax - ymin) * py / h

                x, y = 0, 0
                iteration = 0

                while x * x + y * y <= 4 and iteration < max_iter:
                    x_new = x * x - y * y + x0
                    y = 2 * x * y + y0
                    x = x_new
                    iteration += 1

                if iteration == max_iter:
                    color = "black"
                else:
                    # 映射到彩色
                    hue = iteration / max_iter
                    r, g, b = colorsys.hsv_to_rgb(hue, 1.0, 1.0)
                    color = f"#{int(r*255):02x}{int(g*255):02x}{int(b*255):02x}"

                self.canvas.create_rectangle(px, py, px + 1, py + 1,
                                            fill=color, outline="")

    # ==================== 龙形曲线 ====================
    def dragon(self, points, n):
        if n == 0:
            return points

        new_pts = [points[0]]
        for i in range(len(points) - 1):
            x1, y1 = points[i]
            x2, y2 = points[i + 1]

            # 取中点，然后旋转90度
            mx = (x1 + x2) / 2
            my = (y1 + y2) / 2

            dx = x2 - x1
            dy = y2 - y1

            # 旋转90度
            nx = mx - dy / 2
            ny = my + dx / 2

            new_pts.append((nx, ny))
            new_pts.append(points[i + 1])

        return new_pts

    def draw_dragon(self, cx, cy, n):
        self.info.config(text="龙形曲线：反复折叠一条线段产生的空间填充曲线")
        # 初始线段
        pts = [(cx - 200, cy), (cx + 200, cy)]

        for _ in range(n):
            pts = self.dragon(pts, 0)

        for i in range(len(pts) - 1):
            x1, y1 = pts[i]
            x2, y2 = pts[i + 1]
            ratio = i / len(pts)
            r = int(255 * ratio)
            b = int(255 * (1 - ratio))
            color = f"#{r:02x}00{b:02x}"
            self.canvas.create_line(x1, y1, x2, y2, fill=color, width=1)


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