# -*- coding: utf-8 -*-
"""
Created on Wed Apr  1 04:20:40 2026

@author: pjaub
"""

import numpy as np
import matplotlib.pyplot as plt

plt.close('all')


# ==================================================
# 1) Fonction
# ==================================================
def f(x, y):
    return 20 - x**2 - y**2

def grad_f(x, y):
    return -2*x, -2*y

# ==================================================
# 2) Courbe paramétrée dans le plan
# ==================================================
def xfun(t):
    return 2 + 0.9*np.cos(t)

def yfun(t):
    return 1 + 0.9*np.sin(t)

def xpfun(t):
    return -0.9*np.sin(t)

def ypfun(t):
    return  0.9*np.cos(t)

# ==================================================
# 3) Paramètre choisi
# ==================================================
t0 = 0.5

x0 = xfun(t0)
y0 = yfun(t0)
z0 = f(x0, y0)

vx = xpfun(t0)
vy = ypfun(t0)

gx, gy = grad_f(x0, y0)

# dérivée composée
gp0 = gx*vx + gy*vy

# ==================================================
# 4) Données pour les tracés
# ==================================================
u = np.linspace(-4, 4, 220)
v = np.linspace(-4, 4, 220)
X, Y = np.meshgrid(u, v)
Z = f(X, Y)

# plan tangent au point courant
def plan_tangent(x, y):
    return z0 + gx*(x-x0) + gy*(y-y0)

xp = np.linspace(x0-1.6, x0+1.6, 40)
yp = np.linspace(y0-1.6, y0+1.6, 40)
Xp, Yp = np.meshgrid(xp, yp)
Zp = plan_tangent(Xp, Yp)

# trajectoire
T = np.linspace(0, 2*np.pi, 500)
Xt = xfun(T)
Yt = yfun(T)
Zt = f(Xt, Yt)

# fonction composée
G = f(Xt, Yt)

# tangente à g en t0
Tloc = np.linspace(t0-0.8, t0+0.8, 100)
gtan = z0 + gp0*(Tloc - t0)

# normalisation pour affichage
norm_v = np.sqrt(vx**2 + vy**2)
norm_g = np.sqrt(gx**2 + gy**2)

vx_plot = vx / norm_v
vy_plot = vy / norm_v

gx_plot = gx / norm_g
gy_plot = gy / norm_g

# vecteur tangent 3D à la courbe relevée
tx3, ty3, tz3 = vx, vy, gp0
norm_t3 = np.sqrt(tx3**2 + ty3**2 + tz3**2)
tx3 /= norm_t3
ty3 /= norm_t3
tz3 /= norm_t3

# niveau de projection sous la surface
zproj = Z.min() - 2

# ==================================================
# 5) Figure
# ==================================================
fig = plt.figure(figsize=(17, 5))
gs = fig.add_gridspec(1, 3, width_ratios=[3.4, 1.5, 1.5])

# --------------------------------------------------
# A) Surface + plan tangent + courbe + projection dessous
# --------------------------------------------------
ax1 = fig.add_subplot(gs[0, 0], projection='3d')

# surface
ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.68, edgecolor='none')

# plan tangent
#ax1.plot_surface(Xp, Yp, Zp, cmap='autumn', alpha=0.52, edgecolor='none')

# courbe relevée sur la surface
ax1.plot(Xt, Yt, Zt, color='red', linewidth=3)

# point courant
ax1.scatter(x0, y0, z0, color='black', s=60)

# tangente 3D
ax1.quiver(x0, y0, z0, tx3, ty3, tz3, length=1.6, color='black')

# -------- projection dessous --------
# courbes de niveau
ax1.contour(X, Y, Z, levels=12, zdir='z', offset=zproj, cmap='plasma')

# trajectoire projetée
ax1.plot(Xt, Yt, zproj*np.ones_like(Xt), color='red', linewidth=2)

# point projeté
ax1.scatter(x0, y0, zproj, color='black', s=40)

# gradient projeté
ax1.quiver(
    x0, y0, zproj,
    gx_plot, gy_plot, 0,
    length=1.2, color='green'
)

# vitesse projetée
ax1.quiver(
    x0, y0, zproj,
    vx_plot, vy_plot, 0,
    length=1.2, color='blue'
)

# segment vertical de liaison
ax1.plot([x0, x0], [y0, y0], [zproj, z0], color='gray', linestyle='--')

# labels
ax1.text(x0 + 1.2*gx_plot, y0 + 1.2*gy_plot, zproj, "gradient", color='green')
ax1.text(x0 + 1.2*vx_plot, y0 + 1.2*vy_plot, zproj, "(x',y')", color='blue')

ax1.set_title("Surface, plan tangent et projection dessous")
ax1.set_xlabel("x")
ax1.set_ylabel("y")
ax1.set_zlabel("z")
ax1.set_zlim(zproj, Z.max())
ax1.view_init(elev=25, azim=-55)

# --------------------------------------------------
# B) Plan xy : trajectoire + gradient + vitesse
# --------------------------------------------------
ax2 = fig.add_subplot(gs[0, 1])

ax2.plot(Xt, Yt, color='red', linewidth=2)
ax2.scatter(x0, y0, color='black', s=40)

# vitesse
ax2.quiver(
    x0, y0, vx_plot, vy_plot,
    angles='xy', scale_units='xy', scale=1,
    color='blue'
)

# gradient
ax2.quiver(
    x0, y0, gx_plot, gy_plot,
    angles='xy', scale_units='xy', scale=1,
    color='green'
)

ax2.text(x0 + 1.1*vx_plot, y0 + 1.1*vy_plot, "(x',y')", color='blue')
ax2.text(x0 + 1.1*gx_plot, y0 + 1.1*gy_plot, "gradient", color='green')

ax2.set_title(r"Dans le plan $(x,y)$")
ax2.set_xlabel("x")
ax2.set_ylabel("y")
ax2.set_aspect('equal')
ax2.set_xlim(-0.2, 4.2)
ax2.set_ylim(-1.2, 3.2)
ax2.grid()

# --------------------------------------------------
# C) Graphe de g(t)=f(x(t),y(t))
# --------------------------------------------------
ax3 = fig.add_subplot(gs[0, 2])

ax3.plot(T, G, color='purple', linewidth=2, label=r"$g(t)=f(x(t),y(t))$")
ax3.plot(Tloc, gtan, '--', color='black', linewidth=2, label="tangente en $t_0$")
ax3.scatter(t0, z0, color='black', s=40)

ax3.set_title(r"Graphe de $g(t)$")
ax3.set_xlabel("t")
ax3.set_ylabel("g(t)")
ax3.grid()
ax3.legend()

plt.tight_layout()
plt.show()

