-
-
Save Denbergvanthijs/7f6936ca90a683d37216fd80f5750e9c to your computer and use it in GitHub Desktop.
import numpy as np | |
screen_size = 40 | |
theta_spacing = 0.07 | |
phi_spacing = 0.02 | |
illumination = np.fromiter(".,-~:;=!*#$@", dtype="<U1") | |
A = 1 | |
B = 1 | |
R1 = 1 | |
R2 = 2 | |
K2 = 5 | |
K1 = screen_size * K2 * 3 / (8 * (R1 + R2)) | |
def render_frame(A: float, B: float) -> np.ndarray: | |
""" | |
Returns a frame of the spinning 3D donut. | |
Based on the pseudocode from: https://www.a1k0n.net/2011/07/20/donut-math.html | |
""" | |
cos_A = np.cos(A) | |
sin_A = np.sin(A) | |
cos_B = np.cos(B) | |
sin_B = np.sin(B) | |
output = np.full((screen_size, screen_size), " ") # (40, 40) | |
zbuffer = np.zeros((screen_size, screen_size)) # (40, 40) | |
cos_phi = np.cos(phi := np.arange(0, 2 * np.pi, phi_spacing)) # (315,) | |
sin_phi = np.sin(phi) # (315,) | |
cos_theta = np.cos(theta := np.arange(0, 2 * np.pi, theta_spacing)) # (90,) | |
sin_theta = np.sin(theta) # (90,) | |
circle_x = R2 + R1 * cos_theta # (90,) | |
circle_y = R1 * sin_theta # (90,) | |
x = (np.outer(cos_B * cos_phi + sin_A * sin_B * sin_phi, circle_x) - circle_y * cos_A * sin_B).T # (90, 315) | |
y = (np.outer(sin_B * cos_phi - sin_A * cos_B * sin_phi, circle_x) + circle_y * cos_A * cos_B).T # (90, 315) | |
z = ((K2 + cos_A * np.outer(sin_phi, circle_x)) + circle_y * sin_A).T # (90, 315) | |
ooz = np.reciprocal(z) # Calculates 1/z | |
xp = (screen_size / 2 + K1 * ooz * x).astype(int) # (90, 315) | |
yp = (screen_size / 2 - K1 * ooz * y).astype(int) # (90, 315) | |
L1 = (((np.outer(cos_phi, cos_theta) * sin_B) - cos_A * np.outer(sin_phi, cos_theta)) - sin_A * sin_theta) # (315, 90) | |
L2 = cos_B * (cos_A * sin_theta - np.outer(sin_phi, cos_theta * sin_A)) # (315, 90) | |
L = np.around(((L1 + L2) * 8)).astype(int).T # (90, 315) | |
mask_L = L >= 0 # (90, 315) | |
chars = illumination[L] # (90, 315) | |
for i in range(90): | |
mask = mask_L[i] & (ooz[i] > zbuffer[xp[i], yp[i]]) # (315,) | |
zbuffer[xp[i], yp[i]] = np.where(mask, ooz[i], zbuffer[xp[i], yp[i]]) | |
output[xp[i], yp[i]] = np.where(mask, chars[i], output[xp[i], yp[i]]) | |
return output | |
def pprint(array: np.ndarray) -> None: | |
"""Pretty print the frame.""" | |
print(*[" ".join(row) for row in array], sep="\n") | |
if __name__ == "__main__": | |
for _ in range(screen_size * screen_size): | |
A += theta_spacing | |
B += phi_spacing | |
print("\x1b[H") | |
pprint(render_frame(A, B)) |
nice....it works
Hi, nice work, but there is a part of code you should pay attention to: lines 48..52.
One can safely assume for torus, that if (x, y) [phi1,theta1] = (x,y)[phi2, theta2], then only one of corresponding luminosities will be positive
But you are working with more complex surfaces like Mobius stripe, you may get following:
output[[2,2], [8,8]] = [10, 3] #I've simplified line 52
as you can see, you will get both wrong output and zbuffer values for x=2, y=8
still wonder, if it is still possible to fill values w/o two cycles for phi and theta
nice
nice
thanks
it's not working for me, something about no module named numpy
it's not working for me, something about no module named numpy
Try pip install numpy
in your terminal
My doughnut is not spinning :(. I'm getting snapshots for each timestamp.
cool man.....it works
nice
It works for me but not the way i wanted IDK why
Really cool!
Putting a sleep of 0.05s at for loop make it perfect.
it's fun, but recommend you need to install pip numpy first .
Uploading 2021-10-14 20-33-50_Trim.mp4…
My doughnut is not spinning :(. I'm getting snapshots for each timestamp.
if youre using VS code , then you have to put the terminal on fullscreen when you run the code. (click the red circled button in the pic attached). And it runs smoothly
for me terminal dosent show up anything after running it yk how to fix?
Awesome!
nice!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
cool!
Really cool!
Putting a sleep of 0.05s at for loop make it perfect.
what line bro?
Umm, I have took this code and copied it to one of my projects
don't worry I gave credit
Just want your permission
btw this is the link to my project
https://github.com/RathoreAgastya/terminal-in-python
Really cool!
Putting a sleep of 0.05s at for loop make it perfect.what line bro?
did you understood ?
you can import time
then add this line time.sleep(0.08)
at the top of the line 22
it's not working for me, something about no module named numpy
just download it:
pip install numpy
My donut won't stay in place and spin, it just creates new frames under itself :( I'm using pycharm
nice
Is there a way to slow down the donut? Mine is spinning like a BeyBlade.
Is there a way to slow down the donut? Mine is spinning like a BeyBlade.
For sure! Follow this instructions:
- Import sleep function from time module.
import numpy as np
from time import sleep # Add this module
# Rest of the code [...]
- Add sleep function in loop.
# [...] Rest of the code ^
if __name__ == "__main__":
for _ in range(screen_size * screen_size):
A += theta_spacing
B += phi_spacing
print("\x1b[H")
pprint(render_frame(A, B))
sleep(0.05) # Add sleep funcion here!
# Try diffrent seconds!
Complete "slowed" code :
import numpy as np
from time import sleep
screen_size = 40
theta_spacing = 0.07
phi_spacing = 0.02
illumination = np.fromiter(".,-~:;=!*#$@", dtype="<U1")
A = 1
B = 1
R1 = 1
R2 = 2
K2 = 5
K1 = screen_size * K2 * 3 / (8 * (R1 + R2))
def render_frame(A: float, B: float) -> np.ndarray:
"""
Returns a frame of the spinning 3D donut.
Based on the pseudocode from: https://www.a1k0n.net/2011/07/20/donut-math.html
"""
cos_A = np.cos(A)
sin_A = np.sin(A)
cos_B = np.cos(B)
sin_B = np.sin(B)
output = np.full((screen_size, screen_size), " ") # (40, 40)
zbuffer = np.zeros((screen_size, screen_size)) # (40, 40)
cos_phi = np.cos(phi := np.arange(0, 2 * np.pi, phi_spacing)) # (315,)
sin_phi = np.sin(phi) # (315,)
cos_theta = np.cos(theta := np.arange(0, 2 * np.pi, theta_spacing)) # (90,)
sin_theta = np.sin(theta) # (90,)
circle_x = R2 + R1 * cos_theta # (90,)
circle_y = R1 * sin_theta # (90,)
x = (np.outer(cos_B * cos_phi + sin_A * sin_B * sin_phi, circle_x) - circle_y * cos_A * sin_B).T # (90, 315)
y = (np.outer(sin_B * cos_phi - sin_A * cos_B * sin_phi, circle_x) + circle_y * cos_A * cos_B).T # (90, 315)
z = ((K2 + cos_A * np.outer(sin_phi, circle_x)) + circle_y * sin_A).T # (90, 315)
ooz = np.reciprocal(z) # Calculates 1/z
xp = (screen_size / 2 + K1 * ooz * x).astype(int) # (90, 315)
yp = (screen_size / 2 - K1 * ooz * y).astype(int) # (90, 315)
L1 = (((np.outer(cos_phi, cos_theta) * sin_B) - cos_A * np.outer(sin_phi, cos_theta)) - sin_A * sin_theta) # (315, 90)
L2 = cos_B * (cos_A * sin_theta - np.outer(sin_phi, cos_theta * sin_A)) # (315, 90)
L = np.around(((L1 + L2) * 8)).astype(int).T # (90, 315)
mask_L = L >= 0 # (90, 315)
chars = illumination[L] # (90, 315)
for i in range(90):
mask = mask_L[i] & (ooz[i] > zbuffer[xp[i], yp[i]]) # (315,)
zbuffer[xp[i], yp[i]] = np.where(mask, ooz[i], zbuffer[xp[i], yp[i]])
output[xp[i], yp[i]] = np.where(mask, chars[i], output[xp[i], yp[i]])
return output
def pprint(array: np.ndarray) -> None:
"""Pretty print the frame."""
print(*[" ".join(row) for row in array], sep="\n")
if __name__ == "__main__":
for _ in range(screen_size * screen_size):
A += theta_spacing
B += phi_spacing
print("\x1b[H")
pprint(render_frame(A, B))
sleep(0.05) # Add sleep funcion here!
# Try diffrent seconds!
Thank you very much, iijwpy! I enjoy my donut much better now. Stay blessed!
miih cant run it
cool