import tkinter as tk
from tkinter import ttk, colorchooser, filedialog, messagebox
import math
import random
import json
from PIL import ImageGrab

class SuperComplexDrawApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Tkinter 终极复杂图形绘制工具箱")
        self.root.geometry("1280x800")
        self.root.resizable(True, True)

        # 主布局：左侧控制面板 + 右侧画布区
        self.main_pane = ttk.PanedWindow(root, orient=tk.HORIZONTAL)
        self.main_pane.pack(fill=tk.BOTH, expand=True, padx=4, pady=4)

        # 左侧控制面板
        self.left_panel = ttk.Frame(self.main_pane, width=280)
        self.main_pane.add(self.left_panel, weight=1)

        # 右侧画布
        self.canvas_container = ttk.Frame(self.main_pane)
        self.main_pane.add(self.canvas_container, weight=5)
        self.canvas = tk.Canvas(self.canvas_container, bg="#000000", cursor="cross")
        self.canvas.pack(fill=tk.BOTH, expand=True)

        # 全局绘图参数
        self.draw_color = "#00ff88"
        self.bg_color = "#000000"
        self.line_width = 2
        self.history_stack = []  # 撤销栈
        self.drag_start_x = None
        self.drag_start_y = None
        self.mouse_draw = False

        # 绑定鼠标事件
        self.canvas.bind("<ButtonPress-1>", self.on_mouse_down)
        self.canvas.bind("<B1-Motion>", self.on_mouse_move)
        self.canvas.bind("<ButtonRelease-1>", self.on_mouse_up)
        self.canvas.bind("<MouseWheel>", self.canvas_zoom)

        self.build_control_panel()
        self.root.protocol("WM_DELETE_WINDOW", self.on_close)

    def build_control_panel(self):
        row_idx = 0
        # ========== 基础设置 ==========
        ttk.Label(self.left_panel, text="=== 基础绘图设置 ===", font=("微软雅黑",12,"bold")).grid(row=row_idx, column=0, columnspan=2, pady=6)
        row_idx +=1

        ttk.Button(self.left_panel, text="画笔颜色", command=self.pick_pen_color).grid(row=row_idx, column=0, padx=3, pady=3, sticky="ew")
        ttk.Button(self.left_panel, text="画布底色", command=self.pick_bg_color).grid(row=row_idx, column=1, padx=3, pady=3, sticky="ew")
        row_idx +=1

        ttk.Label(self.left_panel, text="线条粗细:").grid(row=row_idx, column=0, sticky="w")
        self.wid_var = tk.StringVar(value="2")
        ttk.Entry(self.left_panel, textvariable=self.wid_var, width=8).grid(row=row_idx, column=1)
        row_idx +=1

        # 画布操作
        btn_frame1 = ttk.Frame(self.left_panel)
        btn_frame1.grid(row=row_idx, column=0, columnspan=2, pady=4)
        ttk.Button(btn_frame1, text="清空画布", command=self.clear_canvas).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame1, text="撤销上一步", command=self.undo_step).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame1, text="保存截图", command=self.save_canvas_img).pack(side=tk.LEFT, padx=2)
        row_idx +=1

        # ========== 基础几何图形 ==========
        ttk.Label(self.left_panel, text="=== 基础几何绘制 ===", font=("微软雅黑",12,"bold")).grid(row=row_idx, column=0, columnspan=2, pady=6)
        row_idx +=1

        geo_frame = ttk.Frame(self.left_panel)
        geo_frame.grid(row=row_idx, column=0, columnspan=2)
        ttk.Button(geo_frame, text="矩形", command=lambda:self.set_draw_mode("rect")).pack(side=tk.LEFT,padx=2)
        ttk.Button(geo_frame, text="椭圆", command=lambda:self.set_draw_mode("oval")).pack(side=tk.LEFT,padx=2)
        ttk.Button(geo_frame, text="直线", command=lambda:self.set_draw_mode("line")).pack(side=tk.LEFT,padx=2)
        ttk.Button(geo_frame, text="手绘模式", command=lambda:self.set_draw_mode("free")).pack(side=tk.LEFT,padx=2)
        row_idx +=1

        # ========== 数学参数曲线 ==========
        ttk.Label(self.left_panel, text="=== 数学参数曲线 ===", font=("微软雅黑",12,"bold")).grid(row=row_idx, column=0, columnspan=2, pady=6)
        row_idx +=1

        ttk.Button(self.left_panel, text="阿基米德螺旋", command=self.draw_spiral).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1
        ttk.Button(self.left_panel, text="多瓣玫瑰曲线", command=self.draw_rose).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1
        ttk.Button(self.left_panel, text="心形函数曲线", command=self.draw_heart).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1
        ttk.Button(self.left_panel, text="李萨如图形", command=self.draw_lissajous).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1

        # ========== 分形递归图形（高复杂度） ==========
        ttk.Label(self.left_panel, text="=== 递归分形图案 ===", font=("微软雅黑",12,"bold")).grid(row=row_idx, column=0, columnspan=2, pady=6)
        row_idx +=1

        ttk.Button(self.left_panel, text="分形雪花(科赫)", command=self.draw_koch_snow).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1
        ttk.Button(self.left_panel, text="递归分形树", command=self.draw_fractal_tree).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1
        ttk.Button(self.left_panel, text="谢尔宾斯基三角", command=self.draw_sierpinski).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1
        ttk.Button(self.left_panel, text="曼德博分形集合", command=self.draw_mandelbrot).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1
        ttk.Button(self.left_panel, text="Julia 朱利亚集", command=self.draw_julia).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1

        # ========== 混沌与动态系统 ==========
        ttk.Label(self.left_panel, text="=== 混沌动态图形 ===", font=("微软雅黑",12,"bold")).grid(row=row_idx, column=0, columnspan=2, pady=6)
        row_idx +=1
        ttk.Button(self.left_panel, text="洛伦兹吸引子(蝴蝶混沌)", command=self.draw_lorenz).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1
        ttk.Button(self.left_panel, text="动态粒子喷泉", command=self.start_particle_sys).grid(row=row_idx, column=0, columnspan=2, sticky="ew",pady=2)
        row_idx +=1

        # 绘图模式标记
        self.current_mode = None

    # 颜色选择
    def pick_pen_color(self):
        c = colorchooser.askcolor(title="选择画笔颜色")
        if c[1]:
            self.draw_color = c[1]

    def pick_bg_color(self):
        c = colorchooser.askcolor(title="选择画布背景")
        if c[1]:
            self.bg_color = c[1]
            self.canvas.config(bg=self.bg_color)

    def clear_canvas(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        self.canvas.delete(tk.ALL)

    def undo_step(self):
        if not self.history_stack:
            messagebox.showinfo("提示","无操作可撤销")
            return
        last = self.history_stack.pop()
        self.canvas.delete(tk.ALL)
        self.canvas.postscript(file="tmp_undo.ps", colormode='color')

    def save_canvas_img(self):
        path = filedialog.asksaveasfilename(defaultextension=".png", filetypes=[("PNG图片","*.png")])
        if path:
            x = self.canvas.winfo_rootx()
            y = self.canvas.winfo_rooty()
            w = self.canvas.winfo_width()
            h = self.canvas.winfo_height()
            im = ImageGrab.grab((x,y,x+w,y+h))
            im.save(path)

    def set_draw_mode(self, mode):
        self.current_mode = mode
        self.drag_start_x = None
        self.drag_start_y = None

    def get_width(self):
        try:
            return int(self.wid_var.get())
        except:
            return 2

    # 鼠标交互绘图
    def on_mouse_down(self, e):
        self.drag_start_x = e.x
        self.drag_start_y = e.y
        if self.current_mode == "free":
            self.mouse_draw = True

    def on_mouse_move(self, e):
        if self.current_mode == "free" and self.mouse_draw:
            self.canvas.create_line(self.drag_start_x, self.drag_start_y, e.x, e.y, fill=self.draw_color, width=self.get_width())
            self.drag_start_x, self.drag_start_y = e.x, e.y

    def on_mouse_up(self, e):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        mx, my = self.drag_start_x, self.drag_start_y
        ex, ey = e.x, e.y
        w = self.get_width()
        if self.current_mode == "rect":
            self.canvas.create_rectangle(mx,my,ex,ey,outline=self.draw_color, width=w)
        elif self.current_mode == "oval":
            self.canvas.create_oval(mx,my,ex,ey,outline=self.draw_color, width=w)
        elif self.current_mode == "line":
            self.canvas.create_line(mx,my,ex,ey,fill=self.draw_color, width=w)
        self.mouse_draw = False
        self.drag_start_x = self.drag_start_y = None

    def canvas_zoom(self, event):
        factor = 1.1 if event.delta>0 else 0.9
        self.canvas.scale(tk.ALL, event.x, event.y, factor, factor)

    # 1.阿基米德螺旋
    def draw_spiral(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        cx = self.canvas.winfo_width()//2
        cy = self.canvas.winfo_height()//2
        w = self.get_width()
        for i in range(1200):
            t = i * 0.04
            r = t * 3
            x = cx + r * math.cos(t)
            y = cy + r * math.sin(t)
            if i>0:
                self.canvas.create_line(px,py,x,y,fill=self.draw_color,width=w)
            px, py = x, y

    # 2.玫瑰花瓣曲线
    def draw_rose(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        cx = self.canvas.winfo_width()//2
        cy = self.canvas.winfo_height()//2
        w = self.get_width()
        n = 7
        for i in range(3600):
            t = math.radians(i/10)
            r = 180 * math.cos(n * t)
            x = cx + r * math.cos(t)
            y = cy + r * math.sin(t)
            if i>0:
                self.canvas.create_line(px,py,x,y,fill=self.draw_color,width=w)
            px, py = x, y

    # 3.心形线
    def draw_heart(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        cx = self.canvas.winfo_width()//2
        cy = self.canvas.winfo_height()//2 - 80
        w = self.get_width()
        pts = []
        for t in [i/10 for i in range(630)]:
            x = 16 * (math.sin(t)**3)
            y = 13*math.cos(t)-5*math.cos(2*t)-2*math.cos(3*t)-math.cos(4*t)
            pts.append((cx+x*8, cy-y*8))
        self.canvas.create_line(pts, fill=self.draw_color, width=w)

    # 4.李萨如图
    def draw_lissajous(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        cx = self.canvas.winfo_width()//2
        cy = self.canvas.winfo_height()//2
        w = self.get_width()
        a, b = 5, 4
        pts = []
        for t in range(0, 6280, 5):
            rad = t/1000
            x = cx + 220 * math.sin(a*rad)
            y = cy + 220 * math.cos(b*rad)
            pts.append((x,y))
        self.canvas.create_line(pts, fill=self.draw_color, width=w)

    # 5.科赫雪花
    def koch_line(self, x1,y1,x2,y2, depth):
        if depth == 0:
            self.canvas.create_line(x1,y1,x2,y2,fill=self.draw_color,width=self.get_width())
            return
        dx = x2-x1
        dy = y2-y1
        a_x = x1 + dx/3
        a_y = y1 + dy/3
        c_x = x1 + dx*2/3
        c_y = y1 + dy*2/3
        ang = math.pi/3
        b_x = a_x + (dx/3)*math.cos(ang) - (dy/3)*math.sin(ang)
        b_y = a_y + (dx/3)*math.sin(ang) + (dy/3)*math.cos(ang)
        self.koch_line(x1,y1,a_x,a_y,depth-1)
        self.koch_line(a_x,a_y,b_x,b_y,depth-1)
        self.koch_line(b_x,b_y,c_x,c_y,depth-1)
        self.koch_line(c_x,c_y,x2,y2,depth-1)

    def draw_koch_snow(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        cw = self.canvas.winfo_width()
        ch = self.canvas.winfo_height()
        size = 240
        x0 = cw//2 - size//2
        y0 = ch//2 - size//3
        p1 = (x0, y0)
        p2 = (x0+size, y0)
        ang = math.pi*2/3
        p3x = p2[0] + size*math.cos(ang)
        p3y = p2[1] + size*math.sin(ang)
        self.koch_line(p1[0],p1[1],p2[0],p2[1],3)
        self.koch_line(p2[0],p2[1],p3x,p3y,3)
        self.koch_line(p3x,p3y,p1[0],p1[1],3)

    # 6.分形树
    def fractal_branch(self, x,y,angle,length,depth):
        if depth == 0 or length < 2:
            return
        rad = math.radians(angle)
        nx = x + length * math.cos(rad)
        ny = y - length * math.sin(rad)
        self.canvas.create_line(x,y,nx,ny,fill=self.draw_color,width=self.get_width())
        self.fractal_branch(nx, ny, angle-22, length*0.72, depth-1)
        self.fractal_branch(nx, ny, angle+22, length*0.72, depth-1)

    def draw_fractal_tree(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        cx = self.canvas.winfo_width()//2
        cy = self.canvas.winfo_height() - 60
        self.fractal_branch(cx, cy, -90, 110, 10)

    # 7.谢尔宾斯基三角形
    def sierpinski(self, ax,ay,bx,by,cx,cy,depth):
        if depth == 0:
            self.canvas.create_polygon(ax,ay,bx,by,cx,cy,outline=self.draw_color,width=self.get_width(),fill="")
            return
        m1x, m1y = (ax+bx)/2, (ay+by)/2
        m2x, m2y = (bx+cx)/2, (by+cy)/2
        m3x, m3y = (ax+cx)/2, (ay+cy)/2
        self.sierpinski(ax,ay,m1x,m1y,m3x,m3y,depth-1)
        self.sierpinski(m1x,m1y,bx,by,m2x,m2y,depth-1)
        self.sierpinski(m3x,m3y,m2x,m2y,cx,cy,depth-1)

    def draw_sierpinski(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        cw = self.canvas.winfo_width()
        ch = self.canvas.winfo_height()
        top = (cw//2, 80)
        left = (cw//2-260, ch-80)
        right = (cw//2+260, ch-80)
        self.sierpinski(top[0],top[1],left[0],left[1],right[0],right[1],7)

    # 8.曼德博集合（经典复平面分形）
    def mandelbrot_point(self, cr, ci, max_iter=80):
        zr, zi = 0, 0
        for i in range(max_iter):
            zr, zi = zr*zr - zi*zi + cr, 2*zr*zi + ci
            if zr*zr + zi*zi > 4:
                return i
        return 0

    def draw_mandelbrot(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        self.canvas.update()
        w = self.canvas.winfo_width()
        h = self.canvas.winfo_height()
        x1, x2 = -2.2, 0.8
        y1, y2 = -1.2, 1.2
        step_x = (x2-x1)/w
        step_y = (y2-y1)/h
        for px in range(w):
            cr = x1 + px * step_x
            for py in range(h):
                ci = y1 + py * step_y
                it = self.mandelbrot_point(cr,ci)
                if it != 0:
                    r = (it*7) % 255
                    g = (it*13) % 255
                    b = (it*23) % 255
                    clr = f"#{r:02x}{g:02x}{b:02x}"
                    self.canvas.create_rectangle(px,py,px+1,py+1,fill=clr,outline="")
            self.root.update_idletasks()

    # 9.Julia集
    def julia_point(self,zr,zi,cr,ci,max_iter=80):
        for i in range(max_iter):
            zr, zi = zr*zr - zi*zi + cr, 2*zr*zi + ci
            if zr*zr+zi*zi>4:
                return i
        return 0

    def draw_julia(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        self.canvas.update()
        w = self.canvas.winfo_width()
        h = self.canvas.winfo_height()
        cx, cy = -0.7, 0.27015
        x1,x2 = -1.6,1.6
        y1,y2 = -1.2,1.2
        sx = (x2-x1)/w
        sy = (y2-y1)/h
        for px in range(w):
            zr = x1 + px*sx
            for py in range(h):
                zi = y1 + py*sy
                it = self.julia_point(zr,zi,cx,cy)
                if it:
                    c = it*15
                    col = f"#{c%255:02x}{(c*2)%255:02x}{(c*3)%255:02x}"
                    self.canvas.create_rectangle(px,py,px+1,py+1,fill=col,outline="")
            self.root.update_idletasks()

    # 10.洛伦兹蝴蝶混沌吸引子
    def draw_lorenz(self):
        self.history_stack.append(self.canvas.postscript(colormode='color'))
        s, r, b = 10, 28, 8/3
        x, y, z = 0.1, 0, 0
        dt = 0.008
        cx = self.canvas.winfo_width()//2
        cy = self.canvas.winfo_height()//2
        scale = 9
        prev_x = cx + x*scale
        prev_y = cy - z*scale
        w = self.get_width()
        for _ in range(12000):
            dx = s*(y - x)*dt
            dy = (x*(r-z)-y)*dt
            dz = (x*y - b*z)*dt
            x += dx
            y += dy
            z += dz
            nx = cx + x*scale
            ny = cy - z*scale
            self.canvas.create_line(prev_x,prev_y,nx,ny,fill=self.draw_color,width=w)
            prev_x, prev_y = nx, ny
            if _ % 200 == 0:
                self.root.update_idletasks()

    # 11.动态粒子喷泉系统
    def start_particle_sys(self):
        self.particles = []
        cx = self.canvas.winfo_width()//2
        cy = self.canvas.winfo_height()-40
        def spawn():
            px = cx + random.randint(-40,40)
            py = cy
            vx = random.uniform(-2.5,2.5)
            vy = random.uniform(-7,-3)
            life = 120
            self.particles.append([px,py,vx,vy,life])
        def loop():
            for p in self.particles[:]:
                p[0] += p[2]
                p[1] += p[3]
                p[3] += 0.18
                p[4] -=1
                if p[4] <=0:
                    self.particles.remove(p)
                    continue
                self.canvas.create_oval(p[0]-2,p[1]-2,p[0]+2,p[1]+2,fill=self.draw_color,outline="")
            spawn()
            self.root.after(25, loop)
        loop()

    def on_close(self):
        self.root.destroy()

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