import tkinter as tk
import math

# Torus parameters
theta_spacing = 0.07
phi_spacing = 0.02
R1 = 1
R2 = 2
K2 = 5

chars = ".,-~:;=!*#$@"  # luminance chars

# Base character grid (like "screen resolution")
screen_width = 80
screen_height = 24

# Tkinter setup
root = tk.Tk()
root.title("Spinning Torus Demo")

# Get window size and calculate scaling
window_width = 800
window_height = 400
root.geometry(f"{window_width}x{window_height}")

# Calculate character cell size to fit screen dimensions
cell_width = window_width // screen_width
cell_height = window_height // screen_height
font_size = min(cell_width, cell_height)
font = ("Courier", font_size)

canvas = tk.Canvas(root, width=window_width, height=window_height, bg="black")
canvas.pack()

# Pre-create text items for the character grid
text_items = []
for y in range(screen_height):
    row = []
    for x in range(screen_width):
        item = canvas.create_text(
            x*cell_width, y*cell_height,
            text=' ',
            anchor='nw',
            fill='white',
            font=font
        )
        row.append(item)
    text_items.append(row)

# Calculate K1 based on screen width
K1 = screen_width * K2 * 3 / (8 * (R1 + R2))

def render_frame(A, B):
    cosA = math.cos(A)
    sinA = math.sin(A)
    cosB = math.cos(B)
    sinB = math.sin(B)

    output = [[' ' for _ in range(screen_height)] for _ in range(screen_width)]
    zbuffer = [[0 for _ in range(screen_height)] for _ in range(screen_width)]

    theta = 0
    while theta < 2 * math.pi:
        costheta = math.cos(theta)
        sintheta = math.sin(theta)

        phi = 0
        while phi < 2 * math.pi:
            cosphi = math.cos(phi)
            sinphi = math.sin(phi)

            circlex = R2 + R1 * costheta
            circley = R1 * sintheta

            x = circlex * (cosB * cosphi + sinA * sinB * sinphi) - circley * cosA * sinB
            y = circlex * (sinB * cosphi - sinA * cosB * sinphi) + circley * cosA * cosB
            z = K2 + cosA * circlex * sinphi + circley * sinA
            ooz = 1 / z

            xp = int(screen_width / 2 + K1 * ooz * x)
            yp = int(screen_height / 2 - K1 * ooz * y)

            L = cosphi * costheta * sinB - cosA * costheta * sinphi - sinA * sintheta + cosB * (cosA * sintheta - costheta * sinA * sinphi)

            if L > 0:
                if 0 <= xp < screen_width and 0 <= yp < screen_height:
                    if ooz > zbuffer[xp][yp]:
                        zbuffer[xp][yp] = ooz
                        luminance_index = int(L * 8)
                        if luminance_index >= len(chars):
                            luminance_index = len(chars) - 1
                        output[xp][yp] = chars[luminance_index]

            phi += phi_spacing
        theta += theta_spacing

    # Update Tkinter canvas
    for y in range(screen_height):
        for x in range(screen_width):
            canvas.itemconfigure(text_items[y][x], text=output[x][y])

# Animation loop
A = 0
B = 0
def animate():
    global A, B
    render_frame(A, B)
    A += 0.07
    B += 0.03
    root.after(30, animate)

animate()
root.mainloop()
