import serial
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from matplotlib.animation import FuncAnimation
import re

# Serial settings (change COM port and baud rate as needed)
SERIAL_PORT = '/dev/ttyACM0'   # e.g., 'COM3' on Windows or '/dev/ttyUSB0' on Linux/Mac
BAUD_RATE = 115200

# Open serial connection
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)

# Parse "roll: xx, pitch: xx, yaw: xx"
def parse_data(line):
    match = re.search(r'roll:\s*(-?\d+\.?\d*),\s*pitch:\s*(-?\d+\.?\d*),\s*yaw:\s*(-?\d+\.?\d*)', line)
    if match:
        roll = float(match.group(1))
        pitch = float(match.group(2))
        yaw = float(match.group(3))
        return roll, pitch, yaw
    return None

def euler_to_rotation_matrix(roll, pitch, yaw):
    roll = np.radians(roll)
    pitch = np.radians(pitch)
    yaw = np.radians(yaw)

    Rx = np.array([[1, 0, 0],
                   [0, np.cos(roll), -np.sin(roll)],
                   [0, np.sin(roll),  np.cos(roll)]])

    Ry = np.array([[np.cos(pitch), 0, np.sin(pitch)],
                   [0, 1, 0],
                   [-np.sin(pitch), 0, np.cos(pitch)]])

    Rz = np.array([[np.cos(yaw), -np.sin(yaw), 0],
                   [np.sin(yaw),  np.cos(yaw), 0],
                   [0, 0, 1]])

    return Rz @ Ry @ Rx

def create_box():
    l, w, h = 1, 0.5, 0.2
    return np.array([
        [-l/2, -w/20, -h/2],
        [ l/2, -w/2, -h/2],
        [ l/2,  w/2, -h/2],
        [-l/2,  w/20, -h/2],
        [-l/2, -w/20,  h/2],
        [ l/2, -w/2,  h/2],
        [ l/2,  w/2,  h/2],
        [-l/2,  w/20,  h/2],
    ])

def get_faces(vertices):
    return [
        [vertices[j] for j in [0,1,2,3]],
        [vertices[j] for j in [4,5,6,7]],
        [vertices[j] for j in [0,1,5,4]],
        [vertices[j] for j in [2,3,7,6]],
        [vertices[j] for j in [1,2,6,5]],
        [vertices[j] for j in [3,0,4,7]],
    ]

# Create plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
box = create_box()
c = ['C0'] * 1 + ['C3'] + ['C0'] * 4
collection = Poly3DCollection([], facecolors=c, edgecolors='black', alpha=0.90) #facecolors='skyblue',edgecolors='black', alpha=0.8
ax.add_collection3d(collection)

ax.set_xlim([-1, 1])
ax.set_ylim([-1, 1])
ax.set_zlim([-1, 1])
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.view_init(elev=30, azim=45)

# Animation update function
def update(frame):
    line = ser.readline().decode('utf-8').strip()
    result = parse_data(line)
    if result:
        roll, pitch, yaw = result
        R = euler_to_rotation_matrix(roll, pitch, yaw)
        rotated_box = box @ R.T
        faces = get_faces(rotated_box)
        collection.set_verts(faces)
        ax.set_title(f"Roll={roll:.1f}°, Pitch={pitch:.1f}°, Yaw={yaw:.1f}°")

ani = FuncAnimation(fig, update, interval=50)

plt.tight_layout()
plt.show()
