spinning tetrahedron :3

This commit is contained in:
Felix Pankratz 2025-07-20 15:56:13 +02:00
parent 69ceed6c28
commit ec63b97756
3 changed files with 92 additions and 35 deletions

View File

@ -1,42 +1,26 @@
#!/usr/bin/env python3
from kitty import draw_to_terminal
from kitty import draw_to_terminal, get_position, set_position, hide_cursor, show_cursor #, draw_animation
from PIL import Image
import numpy as np
import trimesh
import pyrender
import pyvista as pv
sphere = pv.Tetrahedron()# Sphere()
pl=pv.Plotter(off_screen=True)
pl.add_mesh(sphere, color='tan', show_edges=False)
sphere = trimesh.creation.icosphere(subdivisions=3, radius=1.0)
# create mesh
mesh = pyrender.Mesh.from_trimesh(sphere, smooth=True, wireframe=True)
hide_cursor()
y, x = get_position()
print('\n' * 25, end='')
# Create a scene and add the mesh
scene = pyrender.Scene(bg_color = [0, 0, 0, 0])
scene.add(mesh)
# Add a directional light to illuminate the sphere
light = pyrender.DirectionalLight(color=np.ones(3), intensity=3.0)
light_pose = np.eye(4)
light_pose[:3, 3] = [0, 2, 2] # light from above and in front
scene.add(light, pose=light_pose)
# Add a camera
camera = pyrender.PerspectiveCamera(yfov=np.pi / 3.0)
camera_pose = np.array([
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 3.0], # Pull camera back
[0.0, 0.0, 0.0, 1.0]
])
scene.add(camera, pose=camera_pose)
r = pyrender.OffscreenRenderer(512, 512)
flags = pyrender.RenderFlags.RGBA
color, depth = r.render(scene, flags=flags)
#
im = Image.fromarray((color)) # .new("RGBA", (512, 512), (0, 0, 0, 0) )
draw_to_terminal(im)
frames = []
try:
while True:
pl.camera.Azimuth(1)
image = pl.screenshot(transparent_background=True, window_size=(512, 512))
frames.append(Image.fromarray(image))
set_position(y-25, x)
draw_to_terminal(Image.fromarray(image))
finally:
show_cursor()

View File

@ -1,5 +1,8 @@
#!/usr/bin/env python3
import re
import sys
import termios
import tty
from base64 import standard_b64encode
from io import BytesIO
@ -8,7 +11,7 @@ from PIL import Image, ImageDraw
def draw_to_terminal(img: Image.Image) -> None:
buffer: BytesIO = BytesIO()
img.save(buffer, format='PNG')
write_chunked(a='T', f=100, data=buffer.getvalue())
write_chunked(a='T', i=1, f=100, q=2, data=buffer.getvalue())
def serialize_gr_command(**cmd):
payload = cmd.pop('payload', None)
@ -32,6 +35,65 @@ def write_chunked(**cmd):
sys.stdout.flush()
cmd.clear()
def hide_cursor():
sys.stdout.write("\x1b[?25l")
sys.stdout.flush()
def show_cursor():
sys.stdout.write("\x1b[?25h")
sys.stdout.flush()
def set_position(y, x):
sys.stdout.write(f"\x1b[{y};{x}H")
sys.stdout.flush()
def get_position():
# Save the current terminal settings
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
# Set terminal to raw mode
tty.setraw(fd)
# Send the ESC[6n command to request cursor position
sys.stdout.write("\x1b[6n")
sys.stdout.flush()
# Read the response: ESC [ row ; col R
response = ''
while True:
ch = sys.stdin.read(1)
response += ch
if ch == 'R':
break
finally:
# Restore the terminal settings
termios.tcsetattr(fd, termios.TCSANOW, old_settings)
# Parse the response using regex
match = re.search(r'\[(\d+);(\d+)R', response)
if match:
y, x = map(int, match.groups())
return y, x
else:
raise ValueError("Failed to parse cursor position response")
# sys.stdout.write("\x1b[6n")
# sys.stdout.flush()
# response = ''
# while True:
# ch = sys.stdin.read(1)
# response += ch
# if ch == 'R':
# break
# match = re.search(r'\[(\d+);(\d+)R', response)
# if match:
# y, x = map(int, match.groups())
# return y, x
if __name__ == '__main__':
i = Image.new("RGB", (100, 100), (0, 0, 0))
d = ImageDraw.Draw(i)

11
pyproject.toml Normal file
View File

@ -0,0 +1,11 @@
[project]
name = "kglobe"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"numpy<2",
"pyrender>=0.1.45",
"pyvista>=0.45.3",
]