"""The flight path angle, drawn on the orbit itself.

Left: one sample point in each quadrant of an ellipse (e = 0.6), each carrying
its radius, its local horizon, the velocity and the two components

    v_r     = (mu/h) e sin f        along r-hat
    v_perp  = (mu/h)(1 + e cos f)   along theta-hat

with gamma = atan2(v_r, v_perp) marked between the horizon and the velocity.
Right: the same construction for the f = 135 deg point alone, rotated so that
the local horizon lies flat -- the velocity triangle without the orbit on top
of it.

Sizes cancel in the ratio v_r/v_perp, so the picture is drawn with mu = a = 1;
only the shape (e) and the position (f) matter.
"""

import os
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Arc

os.makedirs("img", exist_ok=True)

CREAM, INK = "#fcfbf7", "#2f2a24"
MAROON, BLUE, GREEN, AMBER, PURPLE = "#6b1f1f", "#2f5ea8", "#2d6a2d", "#b45309", "#7e22ce"

plt.rcParams.update({
    "figure.facecolor": CREAM, "savefig.facecolor": CREAM, "axes.facecolor": CREAM,
    "axes.edgecolor": INK, "axes.labelcolor": INK, "text.color": INK,
    "xtick.color": INK, "ytick.color": INK, "font.size": 11,
    "axes.grid": False,
})

E_CC = 0.6                      # eccentricity of the drawn ellipse
A = 1.0                         # semi-major axis;  mu = 1 with it
P_ = A*(1.0 - E_CC**2)          # semi-latus rectum
H = np.sqrt(P_)                 # h = sqrt(mu p)
VS = 0.24                       # velocity arrows -> plot units


def state(f):
    """Position, the rotating basis, the two speeds and gamma at true anomaly f."""
    r = P_/(1.0 + E_CC*np.cos(f))
    rhat = np.array([np.cos(f), np.sin(f)])
    that = np.array([-np.sin(f), np.cos(f)])
    v_r = E_CC*np.sin(f)/H
    v_t = (1.0 + E_CC*np.cos(f))/H
    return r, r*rhat, rhat, that, v_r, v_t, np.arctan2(v_r, v_t)


fig, (axL, axR) = plt.subplots(1, 2, figsize=(14.0, 6.3),
                               gridspec_kw={"width_ratios": [1.32, 1.0]})

# ═══ left panel : one point per quadrant ════════════════════════════════════
fs = np.linspace(0.0, 2*np.pi, 900)
rs = P_/(1.0 + E_CC*np.cos(fs))
axL.plot(rs*np.cos(fs), rs*np.sin(fs), color=INK, lw=1.7, zorder=2)

# the attracting body sits at the focus; the apse line for reference
axL.plot([0.0], [0.0], "o", color=MAROON, ms=11, zorder=6)
axL.text(0.045, -0.115, "$F$", color=MAROON, fontsize=13, ha="left", va="top")
axL.plot([-A*(1 + E_CC) - 0.12, A*(1 - E_CC) + 0.12], [0, 0],
         color=INK, lw=0.8, ls=(0, (6, 5)), alpha=0.5, zorder=1)

# direction of travel: counter-clockwise, so -x across the top of the orbit
axL.annotate("", xy=(-0.52, 1.00), xytext=(0.08, 1.00),
             arrowprops=dict(arrowstyle="->", color=INK, lw=1.4, alpha=0.6))
axL.text(-0.22, 1.09, "motion", color=INK, fontsize=9.5, alpha=0.7,
         ha="center", va="bottom")

# label offsets chosen per quadrant so nothing lands on the curve
tags = {45: (0.40, 0.16), 135: (-0.20, 0.26), 225: (-0.22, -0.28), 315: (0.34, -0.20)}

for fdeg, (dx, dy) in tags.items():
    f = np.deg2rad(fdeg)
    r, Pp, rhat, that, v_r, v_t, gam = state(f)

    # radius from the focus
    axL.plot([0.0, Pp[0]], [0.0, Pp[1]], color=INK, lw=0.9, ls=":", alpha=0.75, zorder=3)

    # local horizon: perpendicular to r, drawn through the point
    hz = 0.30
    axL.plot([Pp[0] - hz*that[0], Pp[0] + hz*that[0]],
             [Pp[1] - hz*that[1], Pp[1] + hz*that[1]],
             color=GREEN, lw=1.5, ls=(0, (5, 3)), zorder=4)

    # the two components, then the velocity itself
    axL.annotate("", xy=Pp + VS*v_t*that, xytext=Pp,
                 arrowprops=dict(arrowstyle="->", color=BLUE, lw=1.6, alpha=0.95))
    axL.annotate("", xy=Pp + VS*v_r*rhat, xytext=Pp,
                 arrowprops=dict(arrowstyle="->", color=AMBER, lw=1.6, alpha=0.95))
    vvec = VS*(v_r*rhat + v_t*that)
    axL.annotate("", xy=Pp + vvec, xytext=Pp,
                 arrowprops=dict(arrowstyle="-|>", color=MAROON, lw=2.4,
                                 mutation_scale=17))
    # dotted sides closing the parallelogram
    for base in (VS*v_t*that, VS*v_r*rhat):
        axL.plot([Pp[0] + base[0], Pp[0] + vvec[0]],
                 [Pp[1] + base[1], Pp[1] + vvec[1]],
                 color=INK, lw=0.7, ls=(0, (2, 2.5)), alpha=0.6, zorder=3)

    # gamma, between the horizon and the velocity
    a_h = np.degrees(np.arctan2(that[1], that[0]))
    a_v = np.degrees(np.arctan2(vvec[1], vvec[0]))
    R = 0.19
    axL.add_patch(Arc(Pp, 2*R, 2*R, angle=0.0,
                      theta1=min(a_h, a_v), theta2=max(a_h, a_v),
                      color=MAROON, lw=1.5, zorder=5))
    a_mid = np.deg2rad(0.5*(a_h + a_v))
    axL.text(Pp[0] + 1.42*R*np.cos(a_mid), Pp[1] + 1.42*R*np.sin(a_mid),
             r"$\gamma$", color=MAROON, fontsize=12, ha="center", va="center")

    axL.plot([Pp[0]], [Pp[1]], "o", color=INK, ms=5.5, zorder=6)
    axL.text(Pp[0] + dx, Pp[1] + dy,
             f"$f = {fdeg}^\\circ$\n$\\gamma = {np.degrees(gam):+.1f}^\\circ$",
             color=INK, fontsize=10.5, ha="center", va="center")

axL.text(0.78, 1.10, "climbing\n$\\gamma > 0$", color=INK, fontsize=10.5,
         ha="center", va="center", alpha=0.9)
axL.text(0.78, -1.10, "descending\n$\\gamma < 0$", color=INK, fontsize=10.5,
         ha="center", va="center", alpha=0.9)
axL.text(0.86, 0.0, "periapsis\n$\\gamma = 0$", color=INK, fontsize=9.5,
         ha="center", va="center", alpha=0.85)
axL.text(-2.02, 0.0, "apoapsis\n$\\gamma = 0$", color=INK, fontsize=9.5,
         ha="center", va="center", alpha=0.85)

axL.set_xlim(-2.30, 1.42)
axL.set_ylim(-1.38, 1.38)
axL.set_aspect("equal")
axL.axis("off")
axL.set_title(r"$\gamma$ around an ellipse,  $e = 0.6$", fontsize=13, pad=6)

# ═══ right panel : the triangle on its own ══════════════════════════════════
f_d = np.deg2rad(135.0)
_, _, _, _, v_r, v_t, gam = state(f_d)

# drawn in the local frame: horizon along +x, r-hat (away from the focus) along +y
S = 1.0/v_t                                   # scale so v_perp is one unit wide
vx, vy = v_t*S, v_r*S

axR.plot([-0.30, 1.42], [0, 0], color=GREEN, lw=1.8, ls=(0, (5, 3)), zorder=2)
axR.text(1.42, -0.045, "local horizon  $\\hat{\\boldsymbol{\\theta}}$",
         color=GREEN, fontsize=11, ha="right", va="top")

axR.annotate("", xy=(0.0, 1.06), xytext=(0.0, -0.34),
             arrowprops=dict(arrowstyle="->", color=INK, lw=1.1, alpha=0.65))
axR.text(-0.055, 1.06, "$\\hat{\\mathbf{r}}$  (local vertical)", color=INK,
         fontsize=11, ha="right", va="center")
axR.text(-0.055, -0.30, "towards $F$", color=INK, fontsize=9.5, alpha=0.75,
         ha="right", va="center")

# components and resultant
axR.annotate("", xy=(vx, 0.0), xytext=(0, 0),
             arrowprops=dict(arrowstyle="-|>", color=BLUE, lw=2.6, mutation_scale=16))
axR.annotate("", xy=(0.0, vy), xytext=(0, 0),
             arrowprops=dict(arrowstyle="-|>", color=AMBER, lw=2.6, mutation_scale=16))
axR.annotate("", xy=(vx, vy), xytext=(0, 0),
             arrowprops=dict(arrowstyle="-|>", color=MAROON, lw=3.0, mutation_scale=18))
axR.plot([vx, vx], [0, vy], color=INK, lw=0.9, ls=(0, (2, 2.5)), alpha=0.65)
axR.plot([0, vx], [vy, vy], color=INK, lw=0.9, ls=(0, (2, 2.5)), alpha=0.65)

# right-angle tick where the components meet
q = 0.055
axR.plot([q, q, 0], [0, q, q], color=INK, lw=0.9, alpha=0.7)

R = 0.42
axR.add_patch(Arc((0, 0), 2*R, 2*R, angle=0.0, theta1=0.0,
                  theta2=np.degrees(gam), color=MAROON, lw=1.8, zorder=5))
axR.text(1.30*R*np.cos(0.5*gam), 1.30*R*np.sin(0.5*gam), r"$\gamma$",
         color=MAROON, fontsize=15, ha="center", va="center")

axR.text(vx*0.5, -0.085, r"$v_\perp = \dfrac{\mu}{h}\,(1 + e\cos f)$",
         color=BLUE, fontsize=12, ha="center", va="top")
axR.text(-0.055, vy*0.5, r"$v_r = \dfrac{\mu}{h}\,e\sin f$",
         color=AMBER, fontsize=12, ha="right", va="center")
axR.text(vx*0.46, vy*0.46 + 0.055, r"$\mathbf{v}$", color=MAROON,
         fontsize=15, ha="center", va="bottom")

axR.text(0.80, 1.18,
         r"$\tan\gamma = \dfrac{v_r}{v_\perp} = \dfrac{e\sin f}{1 + e\cos f}$",
         color=INK, fontsize=13.5, ha="center", va="center")
axR.text(0.80, 0.94, f"here $f = 135^\\circ$, $e = 0.6$:  "
                     f"$\\gamma = {np.degrees(gam):.1f}^\\circ$",
         color=INK, fontsize=10.5, ha="center", va="center", alpha=0.9)

axR.set_xlim(-0.62, 1.52)
axR.set_ylim(-0.46, 1.38)
axR.set_aspect("equal")
axR.axis("off")
axR.set_title("the velocity triangle at one point", fontsize=13, pad=6)

plt.tight_layout()
fig.savefig("img/fig-fpa-quadrants.png", dpi=160, bbox_inches="tight")
plt.close(fig)
