Skip to content

Instantly share code, notes, and snippets.

@vishrutkmr7
Created March 3, 2024 09:53
Show Gist options
  • Save vishrutkmr7/24e3593b30192d197f1ce2d601d92534 to your computer and use it in GitHub Desktop.
Save vishrutkmr7/24e3593b30192d197f1ce2d601d92534 to your computer and use it in GitHub Desktop.
Python implementation of the classic spinning donut rendering algorithm, inspired by the C/C++ code and explanation on a1k0n.net (https://www.a1k0n.net/2011/07/20/donut-math.html)
import math
import numpy as np
# Constants and screen setup
theta_spacing = 0.07
phi_spacing = 0.02
R1 = 1
R2 = 2
K2 = 5
screen_width = 80 # Example screen width
screen_height = 24 # Example screen height
K1 = screen_width * K2 * 3 / (8 * (R1 + R2))
def render_frame(A, B):
cosA, sinA = math.cos(A), math.sin(A)
cosB, sinB = math.cos(B), math.sin(B)
output = [[" " for _ in range(screen_width)] for _ in range(screen_height)]
zbuffer = [
[-float("inf") for _ in range(screen_width)] for _ in range(screen_height)
]
for theta in np.arange(0, 2 * math.pi, theta_spacing):
costheta, sintheta = math.cos(theta), math.sin(theta)
for phi in np.arange(0, 2 * math.pi, phi_spacing):
cosphi, sinphi = math.cos(phi), 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 # One over z
xp = int(screen_width / 2 + K1 * ooz * x)
yp = int(screen_height / 2 - K1 * ooz * y)
# Luminance calculation
L = (
cosphi * costheta * sinB
- cosA * costheta * sinphi
- sinA * sintheta
+ cosB * (cosA * sintheta - costheta * sinA * sinphi)
)
# Ensure xp and yp are within the screen bounds
if (
L > 0
and 0 <= xp < screen_width
and 0 <= yp < screen_height
and ooz > zbuffer[yp][xp]
):
zbuffer[yp][xp] = ooz
luminance_index = int(L * 8)
# Clamp luminance_index to ensure it's within the bounds
luminance_index = max(0, min(luminance_index, len(".,-~:;=!*#$@") - 1))
output[yp][xp] = ".,-~:;=!*#$@"[luminance_index]
# Output to screen
print("\x1b[H") # Bring cursor to "home" location
for line in output:
print("".join(line))
# Example usage
A, B = 1.0, 1.0 # Example angles
render_frame(A, B)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment