#copyright 2025 Christian Koechli tous droits réservés
#vu la qualité du code, c'est de toute façon mieux de ne pas l'utiliser ailleurs...

import tkinter as tk
from tkinter import ttk
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# Fenêtre principale
root = tk.Tk()
root.title("Phaseurs complexes (somme vectorielle)")

# Variables constantes modifiables
R_s_var = tk.DoubleVar(value=0.5)
L_s_var = tk.DoubleVar(value=0.001)
k_e_var = tk.DoubleVar(value=0.03)
p = 2
Imax = 10.0
Umax = 12.0
R_s_old = -1.0
L_s_old = -1.0
k_e_old = -1.0

# Figure matplotlib
from matplotlib import gridspec
fig = plt.figure(figsize=(10, 5))
gs = gridspec.GridSpec(1, 2, width_ratios=[1, 1])
ax = fig.add_subplot(gs[0])         # Phaseurs
ax2 = fig.add_subplot(gs[1])        # Couple moteur

canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().grid(row=0, column=2, rowspan=10, padx=10, pady=10)

def dessiner_phaseurs(I1_mag, psi_deg, Omega):
    global R_s_old, L_s_old, k_e_old #on doit déclarer les globals si on les modifie uniquement (pfff...)
    global M_valsU, M_valsOpt #en plus, il y a pas de variables locales static
    R_s = R_s_var.get()
    L_s = L_s_var.get()
    k_e = k_e_var.get()

    ax.clear()
    psi_rad = np.radians(psi_deg)
    I1 = I1_mag * np.exp(1j * psi_rad)
    omega = p * Omega

    # Vecteurs
    U_e = k_e * Omega
    U_R = R_s * I1
    U_L = 1j * omega * L_s * I1
    U1 = U_e + U_R + U_L

    # Position en chaîne
    origin = 0
    Ue_end = origin + U_e
    UR_end = Ue_end + U_R
    UL_end = UR_end + U_L

    def draw_vector(start, vec, label, color,xt,yt):
        end = start + vec
        ax.annotate("",
            xy=(end.imag, end.real), xytext=(start.imag, start.real),
            arrowprops=dict(arrowstyle="->", color=color, lw=2),
        )
        ax.text(end.imag + xt, end.real + yt, label, color=color, fontsize=9)

    draw_vector(0, U_e, 'Ui', 'green',-0.5,0)
    draw_vector(Ue_end, U_R, 'UR', 'magenta',-0.5,0)
    draw_vector(UR_end, U_L, 'UL', 'blue',-0.5,0.5)
    draw_vector(0, U1, 'U1', 'black',2,-0.5)
    draw_vector(0, I1, 'I1', 'red',-0.5,-0.5)

    # Cercle de contrainte de tension à UmaxV
    cercle = plt.Circle((0, 0), Umax, color='red', fill=False, linestyle='--', linewidth=1.5, label='Limite U')
    ax.add_patch(cercle)

    # Axes et échelle
    ax.axhline(0, color='gray', lw=0.5)
    ax.axvline(0, color='gray', lw=0.5)
    ax.set_xlim(15, -15)  # inverser l'axe Re
    ax.set_ylim(-15, 15)
    ax.set_xlabel('Re')
    ax.set_ylabel('Im')
    ax.set_title('Phaseurs complexes')
    ax.set_aspect('equal')
    ax.grid(True)
    ax.legend(loc="upper right")
    canvas.draw()

 # ------------- GRAPHE 2 : COURBE DE COUPLE  ----------------
    ax2.clear()
    Omega_vals = np.linspace(0, 500, 300)
    R_s = R_s_var.get()
    L_s = L_s_var.get()
    I1_abs = I1_mag
    psi_rad = np.radians(psi_deg_var.get())
    cos_psi = np.cos(psi_rad)
    M_lim_I = 1.5 * k_e * Imax
    if ((R_s != R_s_old) or (L_s != L_s_old) or (k_e != k_e_old)):
        M_valsU = []
        M_valsOpt = []
        for Omega_i in Omega_vals:
            omega_i = p * Omega_i
            Z_s = np.sqrt(R_s**2 + (omega_i * L_s)**2)
            phi_s = np.arctan2(omega_i * L_s, R_s)
            M = 1.5 * k_e / Z_s * (Umax - k_e * Omega_i * np.cos(phi_s))
            M = max(M, 0)  # ne pas descendre sous 0
            M = min(M , M_lim_I) #ne pas dépasser la limite
            M_valsU.append(M)
            if (k_e * Omega_i < Umax):
                epsilon_opt = np.acos(k_e*Omega_i*(omega_i*L_s/(Z_s*Umax)))-np.arctan2(R_s,omega_i * L_s)
                M = 1.5 * k_e / Z_s * (Umax*np.cos(phi_s-epsilon_opt) - k_e * Omega_i * np.cos(phi_s))
            else:
                M = 0
            M = max(M, 0)  # ne pas descendre sous 0
            M = min(M , M_lim_I) #ne pas dépasser la limite
            M_valsOpt.append(M)

        M_valsU = np.array(M_valsU)
        M_valsOpt = np.array(M_valsOpt)
        R_s_old = R_s
        L_s_old = L_s
        k_e_old = k_e
    
    # Calcul du point actuel
    omega = p * Omega
    Z_s = np.sqrt(R_s**2 + (omega * L_s)**2)
    phi_s = np.arctan2(omega * L_s, R_s)

    M_actuel = 1.5 * k_e * I1_abs * cos_psi

    # Tracé
    ax2.plot(Omega_vals, M_valsU, label="M_max(Ω)", color='red')
    ax2.plot(Omega_vals, M_valsOpt, label="M_opt(Ω)", color='orange')
    ax2.plot(Omega, M_actuel, 'bo', markersize=10, label="Point actuel")
    ax2.set_xlabel("Ω (rad/s)")
    ax2.set_ylabel("Couple M (Nm)")
    ax2.set_title("Point de fonctionnement du moteur")
    ax2.grid(True)
    ax2.set_ylim(0, M_lim_I * 1.2)
    ax2.legend()


# Curseurs dynamiques
I1_mag_var = tk.DoubleVar(value=5.0)
psi_deg_var = tk.DoubleVar(value=0.0)
Omega_var = tk.DoubleVar(value=200.0)

def update_plot(event=None):
    dessiner_phaseurs(I1_mag_var.get(), psi_deg_var.get(), Omega_var.get())
    label_I1.config(text=f"|Î₁| = {I1_mag_var.get():.2f}")
    label_psi.config(text=f"Ψ = {psi_deg_var.get():.1f}°")
    label_omega.config(text=f"Ω = {Omega_var.get():.1f}")

# Paramètres constants
ttk.Label(root, text="Paramètres").grid(row=0, column=0, columnspan=2)
ttk.Label(root, text="R_s").grid(row=1, column=0, sticky="e")
ttk.Entry(root, textvariable=R_s_var, width=6).grid(row=1, column=1)
ttk.Label(root, text="L_s").grid(row=2, column=0, sticky="e")
ttk.Entry(root, textvariable=L_s_var, width=6).grid(row=2, column=1)
ttk.Label(root, text="k_e").grid(row=3, column=0, sticky="e")
ttk.Entry(root, textvariable=k_e_var, width=6).grid(row=3, column=1)

# Curseurs avec étiquettes dynamiques
label_I1 = ttk.Label(root, text="Î₁")
label_I1.grid(row=4, column=0, columnspan=2)
scale_I1 = ttk.Scale(root, from_=0.0, to=Imax, orient='horizontal', variable=I1_mag_var,
                     command=update_plot)
scale_I1.grid(row=5, column=0, columnspan=2)

label_psi = ttk.Label(root, text="Ψ")
label_psi.grid(row=6, column=0, columnspan=2)
scale_psi = ttk.Scale(root, from_=0, to=180, orient='horizontal', variable=psi_deg_var,
                      command=update_plot)
scale_psi.grid(row=7, column=0, columnspan=2)

label_omega = ttk.Label(root, text="Ω")
label_omega.grid(row=8, column=0, columnspan=2)
scale_omega = ttk.Scale(root, from_=0.0, to=500.0, orient='horizontal', variable=Omega_var,
                        command=update_plot)
scale_omega.grid(row=9, column=0, columnspan=2)

# Réaction aux changements des constantes
for var in [R_s_var, L_s_var, k_e_var]:
    var.trace_add('write', lambda *args: update_plot())

# Affichage initial
update_plot()
root.mainloop()
