import tkinter as tk
from tkinter import ttk, Scale, BooleanVar
import math
import random
import time

class ComplexArtCanvas:
    def __init__(self, root):
        self.root = root
        self.root.title("高复杂度图形｜优化流畅版")
        self.root.geometry("1300x820")
        self.is_drawing = False  # 绘制锁，防止并发绘图卡死
        self.refresh_task = None

        # 布局
        self.left_frame = tk.Frame(root, width=220, bg="#222222")
        self.left_frame.pack(side="left", fill="y")
        self.canvas_frame = tk.Frame(root)
        self.canvas_frame.pack(side="right", fill="both", expand=True)
        self.canvas = tk.Canvas(self.canvas_frame, bg="#000000")
        self.canvas.pack(fill="both", expand=True)

        # 鼠标
        self.drag_x = 0
        self.drag_y = 0
        self.offset_x = 0
        self.offset_y = 0
        self.scale_rate = 1.0
        self.canvas.bind("<ButtonPress-1>", self.on_press)
        self.canvas.bind("<B1-Motion>", self.on_drag)
        self.canvas.bind("<MouseWheel>", self.on_zoom)

        # 参数
        self.color_mode = tk.StringVar(value="rainbow")
        self.iter_mandel = tk.IntVar(value=60)
        self.fractal_depth = tk.IntVar(value=4)
        self.spiral_num = tk.IntVar(value=12)
        self.draw_mandel = BooleanVar(value=False)   # 默认关闭曼德博，需要再开
        self.draw_koch = BooleanVar(value=True)
        self.draw_sier = BooleanVar(value=True)
        self.draw_spiral = BooleanVar(value=True)
        self.draw_dot = BooleanVar(value=False)      # 默认关闭点阵

        self.build_control_panel()
        self.root.after(300, self.render_all_complex_graph)

    def build_control_panel(self):
        tk.Label(self.left_frame, text="控制面板", fg="white", bg="#222", font=("黑体",14,"bold")).pack(pady=10)
        tk.Label(self.left_frame, text="分形迭代深度", fg="white", bg="#222").pack()
        Scale(self.left_frame, from_=1, to=6, variable=self.fractal_depth, orient=tk.HORIZONTAL, bg="#444", fg="white", command=self.delay_refresh).pack(fill="x", padx=10)
        tk.Label(self.left_frame, text="曼德博精度", fg="white", bg="#222").pack()
        Scale(self.left_frame, from_=20, to=150, variable=self.iter_mandel, orient=tk.HORIZONTAL, bg="#444", fg="white", command=self.delay_refresh).pack(fill="x", padx=10)
        tk.Label(self.left_frame, text="螺旋条数", fg="white", bg="#222").pack()
        Scale(self.left_frame, from_=3, to=25, variable=self.spiral_num, orient=tk.HORIZONTAL, bg="#444", fg="white", command=self.delay_refresh).pack(fill="x", padx=10)
        tk.Label(self.left_frame, text="色彩方案", fg="white", bg="#222").pack()
        cmb = ttk.Combobox(self.left_frame, textvariable=self.color_mode, values=["rainbow", "cold", "warm", "neon"])
        cmb.pack(pady=3)
        cmb.bind("<<ComboboxSelected>>", lambda e: self.delay_refresh())

        tk.Checkbutton(self.left_frame, text="绘制曼德博集合(卡)", variable=self.draw_mandel, bg="#222", fg="white", selectcolor="#333", command=self.delay_refresh).pack(anchor="w", padx=15)
        tk.Checkbutton(self.left_frame, text="绘制科赫雪花", variable=self.draw_koch, bg="#222", fg="white", selectcolor="#333", command=self.delay_refresh).pack(anchor="w", padx=15)
        tk.Checkbutton(self.left_frame, text="谢尔宾斯基三角", variable=self.draw_sier, bg="#222", fg="white", selectcolor="#333", command=self.delay_refresh).pack(anchor="w", padx=15)
        tk.Checkbutton(self.left_frame, text="阿基米德螺旋群", variable=self.draw_spiral, bg="#222", fg="white", selectcolor="#333", command=self.delay_refresh).pack(anchor="w", padx=15)
        tk.Checkbutton(self.left_frame, text="随机点阵(卡)", variable=self.draw_dot, bg="#222", fg="white", selectcolor="#333", command=self.delay_refresh).pack(anchor="w", padx=15)

        tk.Button(self.left_frame, text="手动刷新", bg="#0088cc", fg="white", command=self.render_all_complex_graph).pack(pady=10, fill="x", padx=15)
        tk.Button(self.left_frame, text="清空画布", bg="#661111", fg="white", command=self.clear_canvas).pack(pady=5, fill="x", padx=15)

    def get_color(self, value, max_val):
        ratio = value / max_val if max_val != 0 else 0
        mode = self.color_mode.get()
        if mode == "rainbow":
            r = int(255 * math.sin(ratio * math.pi * 2))
            g = int(255 * math.sin(ratio * math.pi * 2 + 2 * math.pi / 3))
            b = int(255 * math.sin(ratio * math.pi * 2 + 4 * math.pi / 3))
        elif mode == "cold":
            r = int(30 + 50 * ratio)
            g = int(80 + 120 * ratio)
            b = int(150 + 105 * ratio)
        elif mode == "warm":
            r = int(180 + 75 * ratio)
            g = int(40 + 120 * ratio)
            b = int(20 * ratio)
        else:
            r = int(255 * (ratio**0.5))
            g = int(255 * ((1-ratio)**0.5))
            b = 255
        return f"#{max(0,min(255,r)):02x}{max(0,min(255,g)):02x}{max(0,min(255,b)):02x}"

    def delay_refresh(self, *args):
        # 防抖：短时间多次操作只执行最后一次绘制
        if self.refresh_task is not None:
            self.root.after_cancel(self.refresh_task)
        self.refresh_task = self.root.after(180, self.render_all_complex_graph)

    def on_press(self, event):
        self.drag_x = event.x
        self.drag_y = event.y

    def on_drag(self, event):
        dx = event.x - self.drag_x
        dy = event.y - self.drag_y
        self.offset_x += dx
        self.offset_y += dy
        self.drag_x = event.x
        self.drag_y = event.y
        self.delay_refresh()

    def on_zoom(self, event):
        factor = 1.1 if event.delta > 0 else 0.9
        self.scale_rate *= factor
        self.delay_refresh()

    def clear_canvas(self):
        self.canvas.delete("all")

    # 优化版曼德博，加大采样间隔，大幅减少计算量
    def draw_mandelbrot(self):
        w = self.canvas.winfo_width()
        h = self.canvas.winfo_height()
        if w < 50 or h < 50 or self.is_drawing:
            return
        cx = w/2 + self.offset_x
        cy = h/2 + self.offset_y
        zoom = self.scale_rate
        x_left = (cx - w//2)/zoom - 2.5
        x_right = (cx + w//2)/zoom + 1
        y_top = (cy - h//2)/zoom - 1.25
        y_bottom = (cy + h//2)/zoom + 1.25
        max_iter = self.iter_mandel.get()
        step_x = (x_right - x_left) / w
        step_y = (y_bottom - y_top) / h
        sample_step = 6  # 原3改成6，像素计算量砍半

        for px in range(0, w, sample_step):
            for py in range(0, h, sample_step):
                x0 = x_left + px * step_x
                y0 = y_top + py * step_y
                x, y = 0.0, 0.0
                iter_cnt = 0
                while x*x + y*y <= 4 and iter_cnt < max_iter:
                    x, y = x**2 - y**2 + x0, 2*x*y + y0
                    iter_cnt += 1
                fill_color = self.get_color(iter_cnt, max_iter)
                self.canvas.create_rectangle(px, py, px+sample_step-1, py+sample_step-1, fill=fill_color, outline="")

    def koch_curve(self, x1, y1, x2, y2, depth):
        if depth == 0:
            self.canvas.create_line(x1, y1, x2, y2, fill=self.get_color(random.random(),1), width=1)
            return
        dx = x2 - x1
        dy = y2 - y1
        seg_x1, seg_y1 = x1 + dx/3, y1 + dy/3
        seg_x2, seg_y2 = x1 + dx*2/3, y1 + dy*2/3
        angle = math.pi / 3
        rx = seg_x1 + (dx/3)*math.cos(angle) - (dy/3)*math.sin(angle)
        ry = seg_y1 + (dx/3)*math.sin(angle) + (dy/3)*math.cos(angle)
        self.koch_curve(x1, y1, seg_x1, seg_y1, depth-1)
        self.koch_curve(seg_x1, seg_y1, rx, ry, depth-1)
        self.koch_curve(rx, ry, seg_x2, seg_y2, depth-1)
        self.koch_curve(seg_x2, seg_y2, x2, y2, depth-1)

    def draw_koch_snowflake(self):
        depth = self.fractal_depth.get()
        w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
        center_x = w//2 + self.offset_x
        center_y = h//2 + self.offset_y
        r = 160 * self.scale_rate
        pts = []
        for i in range(3):
            ang = math.radians(i*120 - 90)
            pts.append((center_x + r * math.cos(ang), center_y + r * math.sin(ang)))
        self.koch_curve(pts[0][0], pts[0][1], pts[1][0], pts[1][1], depth)
        self.koch_curve(pts[1][0], pts[1][1], pts[2][0], pts[2][1], depth)
        self.koch_curve(pts[2][0], pts[2][1], pts[0][0], pts[0][1], depth)

    def draw_sierpinski(self, p1, p2, p3, depth):
        if depth == 0:
            self.canvas.create_polygon(p1, p2, p3, outline=self.get_color(random.random(),1), fill="")
            return
        mid1 = ((p1[0]+p2[0])/2, (p1[1]+p2[1])/2)
        mid2 = ((p2[0]+p3[0])/2, (p2[1]+p3[1])/2)
        mid3 = ((p3[0]+p1[0])/2, (p3[1]+p1[1])/2)
        self.draw_sierpinski(p1, mid1, mid3, depth-1)
        self.draw_sierpinski(mid1, p2, mid2, depth-1)
        self.draw_sierpinski(mid3, mid2, p3, depth-1)

    def render_sier(self):
        dep = self.fractal_depth.get()
        w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
        cx = w//4 + self.offset_x
        cy = h//4 + self.offset_y
        size = 140 * self.scale_rate
        p1 = (cx, cy - size)
        p2 = (cx - size*0.866, cy + size*0.5)
        p3 = (cx + size*0.866, cy + size*0.5)
        self.draw_sierpinski(p1, p2, p3, dep)

    def draw_spiral_group(self):
        num = self.spiral_num.get()
        w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
        cx = w * 0.75 + self.offset_x
        cy = h * 0.5 + self.offset_y
        max_radius = 220 * self.scale_rate
        for s_idx in range(num):
            base_angle = (2 * math.pi / num) * s_idx
            points = []
            # 减少采样点数
            for theta in range(0, 720*4, 4):
                rad = math.radians(theta)
                r = max_radius * (rad / (math.pi*12))
                x = cx + r * math.cos(rad + base_angle)
                y = cy + r * math.sin(rad + base_angle)
                points.append((x, y))
            for i in range(len(points)-1):
                c = self.get_color(s_idx/num, 1)
                self.canvas.create_line(points[i], points[i+1], fill=c, width=1.2)

    def draw_random_dots(self):
        w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
        count = 600  # 原来2200 → 600
        for _ in range(count):
            x = random.randint(0, w) + self.offset_x
            y = random.randint(0, h) + self.offset_y
            r_size = random.uniform(1,3) * self.scale_rate
            c = self.get_color(random.random(), 1)
            self.canvas.create_oval(x, y, x+r_size, y+r_size, fill=c, outline="")

    def render_all_complex_graph(self):
        if self.is_drawing:
            return
        self.is_drawing = True
        self.canvas.delete("all")
        try:
            if self.draw_mandel.get():
                self.draw_mandelbrot()
            if self.draw_koch.get():
                self.draw_koch_snowflake()
            if self.draw_sier.get():
                self.render_sier()
            if self.draw_spiral.get():
                self.draw_spiral_group()
            if self.draw_dot.get():
                self.draw_random_dots()
        finally:
            self.is_drawing = False

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