Created
March 3, 2024 09:53
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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