Skip to content

Instantly share code, notes, and snippets.

@bevenky
Created February 6, 2025 18:04
Show Gist options
  • Save bevenky/42771266d419abc2a0e7b746e60623ee to your computer and use it in GitHub Desktop.
Save bevenky/42771266d419abc2a0e7b746e60623ee to your computer and use it in GitHub Desktop.
import pygame
import math
import random
def main():
pygame.init()
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("True ASCII Rotating Torus")
chars = " .:,;-+=*#%@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!?"
font_size = 16
font = pygame.font.SysFont("monospace", font_size)
font_width, font_height = font.size("A")
major_radius = 50
minor_radius = 20
angles = [0, 0, 0] # roll, pitch, yaw
zoom = 1.0
zoom_speed = 0.05
rotating = False
prev_mouse_pos = (0, 0)
# --- Pre-calculate static character choices ---
num_theta_steps = 360 // 4
num_phi_steps = 360 // 3
static_chars = [[random.choice(chars) for _ in range(num_phi_steps)] for _ in range(num_theta_steps)]
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
rotating = True
prev_mouse_pos = event.pos
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
rotating = False
elif event.type == pygame.MOUSEMOTION:
if rotating:
delta_x = event.pos[0] - prev_mouse_pos[0]
delta_y = event.pos[1] - prev_mouse_pos[1]
angles[0] += delta_y * 0.015 # Roll
angles[1] += delta_x * 0.015 # Pitch
prev_mouse_pos = event.pos
elif event.type == pygame.MOUSEWHEEL:
zoom += event.y * zoom_speed
zoom = max(0.1, min(zoom, 10.0))
screen.fill((0, 0, 0))
aspect_ratio = font_width / font_height
roll, pitch, yaw = angles
char_grid = [['' for _ in range(width // font_width)] for _ in range(height // font_height)]
depth_grid = [[-float('inf')] * (width // font_width) for _ in range(height // font_height)]
theta_step_index = 0
for theta in range(0, 360, 4):
theta_rad = math.radians(theta)
phi_step_index = 0
for phi in range(0, 360, 3):
phi_rad = math.radians(phi)
x = (major_radius + minor_radius * math.cos(phi_rad)) * math.cos(theta_rad)
y = (major_radius + minor_radius * math.cos(phi_rad)) * math.sin(theta_rad)
z = minor_radius * math.sin(phi_rad)
# 3D Rotation (Yaw, Pitch, Roll)
x1 = x * math.cos(yaw) - y * math.sin(yaw)
y1 = x * math.sin(yaw) + y * math.cos(yaw)
z1 = z
x2 = x1 * math.cos(pitch) + z1 * math.sin(pitch)
y2 = y1
z2 = -x1 * math.sin(pitch) + z1 * math.cos(pitch)
x3 = x2
y3 = y2 * math.cos(roll) - z2 * math.sin(roll)
z3 = y2 * math.sin(roll) + z2 * math.cos(roll)
projected_x = int(width / 2 + x3 * aspect_ratio * 25 * zoom)
projected_y = int(height / 2 + y3 * 25 * zoom)
grid_x = projected_x // font_width
grid_y = projected_y // font_height
if 0 <= grid_x < width // font_width and 0 <= grid_y < height // font_height:
if z3 > depth_grid[grid_y][grid_x]:
depth_grid[grid_y][grid_x] = z3
# Normal vector for shading
nx = math.cos(theta_rad) * math.cos(phi_rad)
ny = math.sin(theta_rad) * math.cos(phi_rad)
nz = math.sin(phi_rad)
nx1 = nx * math.cos(yaw) - ny * math.sin(yaw)
ny1 = nx * math.sin(yaw) + ny * math.cos(yaw)
nz1 = nz
nx2 = nx1 * math.cos(pitch) + nz1 * math.sin(pitch)
ny2 = ny1
nz2 = -nx1 * math.sin(pitch) + nz1 * math.cos(pitch)
nx3 = nx2
ny3 = ny2 * math.cos(roll) - nz2 * math.sin(roll)
nz3 = ny2 * math.sin(roll) + nz2 * math.cos(roll)
brightness = nx3 * 0.0 + ny3 * 0.0 + nz3 * 1.0
brightness = max(0, min(brightness, 1))
# --- Use pre-calculated static character ---
char = static_chars[theta_step_index][phi_step_index]
# Apply brightness to the *color*, not the character choice
color_value = int(brightness * 255)
color = (color_value, color_value, color_value)
char_grid[grid_y][grid_x] = (char, color) # Store char AND color
phi_step_index += 1 # Increment phi index
theta_step_index += 1 # Increment theta index
# Draw characters from the grid, using stored color
for row_idx, row in enumerate(char_grid):
for col_idx, char_data in enumerate(row):
if char_data: # Check if char_data exists (is not an empty string)
char, color = char_data # Unpack char and color
text_surface = font.render(char, True, color)
screen.blit(text_surface, (col_idx * font_width, row_idx * font_height))
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment