import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider

# --- 그래프 및 슬라이더 초기 설정 ---
fig, ax = plt.subplots(figsize=(8, 8))
plt.subplots_adjust(bottom=0.35) # 슬라이더 4개를 위한 공간 확보
ax.set_aspect('equal', adjustable='box')

# 그리드 생성
grid_lines = np.linspace(-10, 10, 21)
grid_x, grid_y = np.meshgrid(grid_lines, grid_lines)
original_grid = np.vstack([grid_x.ravel(), grid_y.ravel()])

# 초기값 설정
init_theta1, init_theta2 = 0, 90
init_lambda1, init_lambda2 = 1.0, 1.0

# --- 슬라이더 UI 생성 ---
ax_th1 = plt.axes([0.25, 0.20, 0.65, 0.03])
ax_th2 = plt.axes([0.25, 0.15, 0.65, 0.03])
ax_l1 = plt.axes([0.25, 0.10, 0.65, 0.03])
ax_l2 = plt.axes([0.25, 0.05, 0.65, 0.03])

theta1_slider = Slider(ax=ax_th1, label='θ1 (v1_angle)', valmin=0, valmax=360, valinit=init_theta1)
theta2_slider = Slider(ax=ax_th2, label='θ2 (v2_angle)', valmin=0, valmax=360, valinit=init_theta2)
lambda1_slider = Slider(ax=ax_l1, label='λ1 (v1_scale)', valmin=-5.0, valmax=5.0, valinit=init_lambda1)
lambda2_slider = Slider(ax=ax_l2, label='λ2 (v2_scale)', valmin=-5.0, valmax=5.0, valinit=init_lambda2)

# --- 핵심 업데이트 함수 ---
def update(val):
    ax.clear()
    
    theta1 = np.deg2rad(theta1_slider.val)
    theta2 = np.deg2rad(theta2_slider.val)
    lambda1 = lambda1_slider.val
    lambda2 = lambda2_slider.val
    
    v1 = np.array([np.cos(theta1), np.sin(theta1)])
    v2 = np.array([np.cos(theta2), np.sin(theta2)])
    
    P = np.array([v1, v2]).T
    
    # 두 고유벡터가 거의 평행할 때 (선형 종속) 오류 방지
    if abs(np.linalg.det(P)) < 1e-3:
        ax.text(0, 0, "Error: Eigenvectors are linearly dependent!", ha='center', va='center', color='red', fontsize=12)
    else:
        D = np.array([[lambda1, 0], [0, lambda2]])
        P_inv = np.linalg.inv(P)
        A = P @ D @ P_inv
        transformed_grid = A @ original_grid
        
        # 변환된 격자 그리기 (실선)
        ax.plot(transformed_grid[0,:].reshape(21,21), transformed_grid[1,:].reshape(21,21), 'gray', alpha=0.8)
        ax.plot(transformed_grid[0,:].reshape(21,21).T, transformed_grid[1,:].reshape(21,21).T, 'gray', alpha=0.8)

    # 원래 격자 그리기 (점선)
    ax.plot(original_grid[0,:].reshape(21,21), original_grid[1,:].reshape(21,21), 'k', alpha=0.2, linestyle=':')
    ax.plot(original_grid[0,:].reshape(21,21).T, original_grid[1,:].reshape(21,21).T, 'k', alpha=0.2, linestyle=':')
    
    # 변환된 고유벡터 그리기 (색상 화살표)
    ax.quiver(0, 0, v1[0]*lambda1, v1[1]*lambda1, angles='xy', scale_units='xy', scale=1, color='red', width=0.01)
    ax.quiver(0, 0, v2[0]*lambda2, v2[1]*lambda2, angles='xy', scale_units='xy', scale=1, color='blue', width=0.01)
    
    # 그래프 설정
    title_line1 = f"v1=(cos({theta1_slider.val:.0f}°), sin({theta1_slider.val:.0f}°)) = ({v1[0]:.2f}, {v1[1]:.2f})"
    title_line2 = f"λ1={lambda1:.1f}"
    title_line3 = f"v2=(cos({theta2_slider.val:.0f}°), sin({theta2_slider.val:.0f}°)) = ({v2[0]:.2f}, {v2[1]:.2f})"
    title_line4 = f"λ2={lambda2:.1f}"
    ax.set_title(f"{title_line1}\\n{title_line2}\\n{title_line3}\\n{title_line4}")
    ax.set_xlim(-5, 5)
    ax.set_ylim(-5, 5)
    ax.grid(True)
    fig.canvas.draw_idle()

# --- 이벤트 연결 ---
theta1_slider.on_changed(update)
theta2_slider.on_changed(update)
lambda1_slider.on_changed(update)
lambda2_slider.on_changed(update)

# 초기 화면 그리기
update(None)
plt.show()