import tkinter as tk
import math
import random

# ------------------ 3D 数学工具 ------------------
def rotate_x(point, angle):
    """绕 X 轴旋转"""
    x, y, z = point
    rad = math.radians(angle)
    cos_a, sin_a = math.cos(rad), math.sin(rad)
    return (x, y * cos_a - z * sin_a, y * sin_a + z * cos_a)

def rotate_y(point, angle):
    """绕 Y 轴旋转"""
    x, y, z = point
    rad = math.radians(angle)
    cos_a, sin_a = math.cos(rad), math.sin(rad)
    return (x * cos_a + z * sin_a, y, -x * sin_a + z * cos_a)

def rotate_z(point, angle):
    """绕 Z 轴旋转"""
    x, y, z = point
    rad = math.radians(angle)
    cos_a, sin_a = math.cos(rad), math.sin(rad)
    return (x * cos_a - y * sin_a, x * sin_a + y * cos_a, z)

def project(point, fov=300, viewer_distance=500):
    """
    将 3D 点投影到 2D 平面（透视投影）
    fov: 视野系数，viewer_distance: 观察距离
    """
    x, y, z = point
    factor = fov / (viewer_distance + z)
    return (x * factor, y * factor)

# ------------------ 主程序 ------------------
class ComplexGraphics:
    def __init__(self, root):
        self.root = root
        self.root.title("3D 交互式旋转立方体 - Tkinter 复杂图形示例")
        self.root.geometry("800x600")

        # 创建 Canvas
        self.canvas = tk.Canvas(root, bg='black', width=800, height=600, highlightthickness=0)
        self.canvas.pack(fill=tk.BOTH, expand=True)

        # 立方体顶点（边长 100，中心在原点）
        half = 50
        self.vertices = [
            (-half, -half, -half), ( half, -half, -half),
            ( half,  half, -half), (-half,  half, -half),
            (-half, -half,  half), ( half, -half,  half),
            ( half,  half,  half), (-half,  half,  half)
        ]
        # 定义棱（顶点索引对）
        self.edges = [
            (0,1), (1,2), (2,3), (3,0),  # 前面
            (4,5), (5,6), (6,7), (7,4),  # 后面
            (0,4), (1,5), (2,6), (3,7)   # 连接前后
        ]
        # 定义面（用于填充颜色，按顶点索引）
        self.faces = [
            (0,1,2,3),  # 前面 (z = -half)
            (4,5,6,7),  # 后面 (z = +half)
            (0,1,5,4),  # 底面 (y = -half)
            (2,3,7,6),  # 顶面 (y = +half)
            (0,3,7,4),  # 左面 (x = -half)
            (1,2,6,5)   # 右面 (x = +half)
        ]
        # 为每个面赋予不同颜色（带透明度效果，但 Tkinter 不支持半透明，仅用纯色）
        self.face_colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD']

        # 控制旋转角度（可被鼠标拖动修改）
        self.angle_x = 30
        self.angle_y = 45
        self.angle_z = 0

        # 鼠标拖拽相关
        self.dragging = False
        self.last_x = 0
        self.last_y = 0

        # 生成星空背景（随机点）
        self.stars = []
        for _ in range(150):
            x = random.randint(0, 800)
            y = random.randint(0, 600)
            size = random.randint(1, 3)
            brightness = random.randint(100, 255)
            color = f'#{brightness:02x}{brightness:02x}{brightness:02x}'
            self.stars.append((x, y, size, color))

        # 绑定鼠标事件
        self.canvas.bind("<ButtonPress-1>", self.on_mouse_down)
        self.canvas.bind("<B1-Motion>", self.on_mouse_drag)
        self.canvas.bind("<ButtonRelease-1>", self.on_mouse_up)
        # 鼠标滚轮缩放（模拟远近）
        self.canvas.bind("<MouseWheel>", self.on_mouse_wheel)

        # 全局缩放系数
        self.scale = 1.0

        # 启动动画循环
        self.animate()

    # ------------------ 鼠标事件处理 ------------------
    def on_mouse_down(self, event):
        self.dragging = True
        self.last_x = event.x
        self.last_y = event.y

    def on_mouse_drag(self, event):
        if self.dragging:
            dx = event.x - self.last_x
            dy = event.y - self.last_y
            # 根据拖拽方向改变旋转角度
            self.angle_y += dx * 0.5   # 水平拖拽绕 Y 轴
            self.angle_x += dy * 0.5   # 垂直拖拽绕 X 轴
            self.last_x = event.x
            self.last_y = event.y

    def on_mouse_up(self, event):
        self.dragging = False

    def on_mouse_wheel(self, event):
        # Windows 下 event.delta 为 ±120；Linux 可能需要调整
        delta = event.delta // 120 if event.delta else 0
        self.scale *= (1.0 + delta * 0.02)
        self.scale = max(0.2, min(3.0, self.scale))  # 限制缩放范围

    # ------------------ 绘制核心 ------------------
    def draw_cube(self):
        # 清空画布（保留背景星空）
        self.canvas.delete("cube")
        self.canvas.delete("axis")

        # 1. 绘制星空（静态）
        for x, y, size, color in self.stars:
            self.canvas.create_oval(x-size, y-size, x+size, y+size, fill=color, outline='', tags="star")

        # 2. 变换所有顶点
        transformed = []
        for v in self.vertices:
            # 应用旋转
            v = rotate_x(v, self.angle_x)
            v = rotate_y(v, self.angle_y)
            v = rotate_z(v, self.angle_z)
            # 应用缩放
            v = (v[0] * self.scale, v[1] * self.scale, v[2] * self.scale)
            transformed.append(v)

        # 3. 投影到 2D
        projected = [project(v, fov=300, viewer_distance=600) for v in transformed]

        # 计算每个面的平均 Z 值（用于排序，实现简单画家算法）
        face_z = []
        for face in self.faces:
            avg_z = sum(transformed[i][2] for i in face) / len(face)
            face_z.append(avg_z)

        # 按 Z 从远到近排序（先绘制远的）
        face_order = sorted(range(len(self.faces)), key=lambda i: face_z[i], reverse=False)

        # 4. 绘制填充面（先绘制远处，再近处覆盖）
        for idx in face_order:
            face = self.faces[idx]
            points = []
            for i in face:
                x, y = projected[i]
                # 平移使中心在画布中央
                x += 400
                y += 300
                points.extend([x, y])
            self.canvas.create_polygon(points, fill=self.face_colors[idx], outline='white', width=1, tags="cube")

        # 5. 绘制棱（线框，覆盖在面上增加清晰度）
        for edge in self.edges:
            p1 = projected[edge[0]]
            p2 = projected[edge[1]]
            x1, y1 = p1[0] + 400, p1[1] + 300
            x2, y2 = p2[0] + 400, p2[1] + 300
            self.canvas.create_line(x1, y1, x2, y2, fill='white', width=2, tags="cube")

        # 6. 绘制坐标轴（辅助线）
        axis_length = 100 * self.scale
        origin = (400, 300)
        # X轴（红）
        end_x = project((axis_length, 0, 0), fov=300, viewer_distance=600)
        self.canvas.create_line(origin[0], origin[1], end_x[0]+400, end_x[1]+300,
                                fill='red', width=2, arrow=tk.LAST, tags="axis")
        # Y轴（绿）
        end_y = project((0, axis_length, 0), fov=300, viewer_distance=600)
        self.canvas.create_line(origin[0], origin[1], end_y[0]+400, end_y[1]+300,
                                fill='green', width=2, arrow=tk.LAST, tags="axis")
        # Z轴（蓝）
        end_z = project((0, 0, axis_length), fov=300, viewer_distance=600)
        self.canvas.create_line(origin[0], origin[1], end_z[0]+400, end_z[1]+300,
                                fill='blue', width=2, arrow=tk.LAST, tags="axis")

        # 添加文字标注
        self.canvas.create_text(420, 280, text='X', fill='red', tags="axis")
        self.canvas.create_text(420, 320, text='Y', fill='green', tags="axis")
        self.canvas.create_text(380, 300, text='Z', fill='blue', tags="axis")

        # 显示角度信息
        info = f"角度 X: {self.angle_x:.1f}°  Y: {self.angle_y:.1f}°  Z: {self.angle_z:.1f}°  |  缩放: {self.scale:.2f}"
        self.canvas.create_text(400, 20, text=info, fill='yellow', font=('Arial', 12), tags="cube")

    def animate(self):
        """动画循环：每帧更新角度并重绘"""
        # 如果不拖拽，可以自动旋转（这里不自动旋转，交给用户拖拽）
        # 但为了让效果更生动，可以增加微小的自转（可选）
        # 这里我们保持用户控制，所以只重绘
        self.draw_cube()
        self.root.after(30, self.animate)  # 约 33 FPS

# ------------------ 启动 ------------------
if __name__ == "__main__":
    root = tk.Tk()
    app = ComplexGraphics(root)
    root.mainloop()