사용자 도구

사이트 도구


plotille:rotating_cube

Plotille: rotating cube

rotating_cube.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
# original: https://github.com/asciimoo/drawille/blob/master/examples/rotating_cube.py
 
# drawille의 rotating cube를 plotille를 사용하도록 포팅
 
import math
import random
import sys
import threading
import time
from queue import Queue
 
import numpy as np
import plotille
from IPython import embed
from plotille import Canvas
 
 
def keyboard(queue):
    import platform
 
    if platform.system() == 'Windows':
        import msvcrt
        from msvcrt import kbhit
        from msvcrt import getch
 
        while True:
            if msvcrt.kbhit():
                queue.put(msvcrt.getch())
 
    elif platform.system() == 'Linux':
        import curses
        import time
 
        stdscr = curses.initscr()
        stdscr.refresh()
 
        while True:
            queue.put(stdscr.getch())
 
 
class Screen:
    def __init__(self, width, height, fps=20):
        super().__init__()
        self.width = width
        self.height = height
        self.fps = fps
 
    def clear(self):
        sys.stdout.write('\x1b[2j\n\033c\n\x1bc')  # clear
        sys.stdout.write('\u001b[1000D')  # 커서 왼쪽으로 이동
        sys.stdout.write(f'\u001b[1000A') # 커서 위로 이동
        sys.stdout.flush()
 
    def write(self, content):
        sys.stdout.write('\u001b[1000D')  # 커서 왼쪽으로 이동
        sys.stdout.write(f'\u001b[{self.height}A')  # 커서 위로 이동
        sys.stdout.write(content)
        sys.stdout.flush()  # 화면에 출력
        time.sleep(1 / self.fps)
 
 
class Point3D:
    def __init__(self, x, y, z):
        self.x, self.y, self.z = float(x), float(y), float(z)
 
    def transform(self, trans):
        p = np.ones(4)
        p[0] = self.x
        p[1] = self.y
        p[2] = self.z
        p_prime = trans @ p.reshape(-1, 1)  # 변환
        self.x = p_prime[0, 0]
        self.y = p_prime[1, 0]
        self.z = p_prime[2, 0]
 
    def project(self, win_width, win_height, fov, viewer_distance):
        """ Transforms this 3D point to 2D using a perspective projection. """
        factor = fov / (viewer_distance + self.z)
        x = self.x * factor + win_width / 2
        y = -self.y * factor + win_height / 2
        return Point3D(x / win_height, y / win_width, 1)
 
    def __repr__(self):
        return f'{self.x:.2f}-{self.y:.2f}-{self.z:.2f}'
 
 
def T(dx, dy, dz):
    " 이동 변환 "
    T = np.eye(4)
    T[0, 3] = dx
    T[1, 3] = dy
    T[2, 3] = dz
    return T
 
 
def R(ax, ay, az):
    " 회전변환 "
    # X
    rad = np.radians(ax)
    cosa = math.cos(rad)
    sina = math.sin(rad)
    RX = np.eye(4)
    RX[1, 1] = cosa
    RX[1, 2] = -sina
    RX[2, 1] = sina
    RX[2, 2] = cosa
    # y
    rad = np.radians(ay)
    cosa = math.cos(rad)
    sina = math.sin(rad)
    RY = np.eye(4)
    RY[0, 0] = cosa
    RY[0, 2] = -sina
    RY[2, 0] = sina
    RY[2, 2] = cosa
    # z
    rad = np.radians(az)
    cosa = math.cos(rad)
    sina = math.sin(rad)
    RZ = np.eye(4)
    RZ[0, 0] = cosa
    RZ[0, 1] = sina
    RZ[1, 0] = -sina
    RZ[1, 1] = cosa
    return RZ @ RY @ RX
 
 
vs = [
    Point3D(-20,20,-20),
    Point3D(20,20,-20),
    Point3D(20,-20,-20),
    Point3D(-20,-20,-20),
    Point3D(-20,20,20),
    Point3D(20,20,20),
    Point3D(20,-20,20),
    Point3D(-20,-20,20)
]
 
# Define the vertices that compose each of the 6 faces. These numbers are
# indices to the vertices list defined above.
faces = [
    (0, 1, 2, 3),
    (1, 5, 6, 2),
    (5, 4, 7, 6),
    (4, 0, 3, 7),
    (0, 4, 5, 1),
    (3, 2, 6, 7)
]
 
 
if __name__ == '__main__':
 
    screen = Screen(height=20, width=40, fps=20)
    screen.clear()
 
    # 변환 행렬
    TR = R(2, 3, 5) @ T(0, 0, 0)
 
    key = Queue()
    threading.Thread(target=keyboard, args=(key,), daemon=True).start()
 
    colors = ['white', 'yellow', 'green', 'blue', 'red']
 
    while True:
        if not key.empty() and key.get() == b' ':
            colors = colors[1:] + colors[:1]
 
        canvas = Canvas(height=screen.height, width=screen.width)
 
        t = list()
        for i, v in enumerate(vs):
            # Rotate the point around X axis, then around Y axis, and finally around Z axis.
            v.transform(TR)
            # Transform the point from 3D to 2D
            v = v.project(
                win_width=50, 
                win_height=50, 
                fov=50, 
                viewer_distance=100
            )
            t.append(v)
 
        for f in faces:
            canvas.line(t[f[0]].x, t[f[0]].y, t[f[1]].x, t[f[1]].y, color=colors[0])
            canvas.line(t[f[1]].x, t[f[1]].y, t[f[2]].x, t[f[2]].y, color=colors[0])
            canvas.line(t[f[2]].x, t[f[2]].y, t[f[3]].x, t[f[3]].y, color=colors[0])
            canvas.line(t[f[3]].x, t[f[3]].y, t[f[0]].x, t[f[0]].y, color=colors[0])
 
        screen.write(canvas.plot())
plotille/rotating_cube.txt · 마지막으로 수정됨: 2024/03/23 02:42 저자 127.0.0.1