"""Animations of the manoeuvres of the manoeuvre notes: initial orbit, burn(s), final orbit.

Each scenario is a list of legs (arcs of conics); the burns, with their size and sense, are computed from the states
at the ends of the legs by codes/_manim.py, and each scenario states the numbers that the note quotes, so building it
fails if they are not reproduced.

Run from the sfm-slides directory:
    python3 codes/anim-man.py                 all the animations, into img/anim-man-<name>.gif
    python3 codes/anim-man.py hohmann ...     only the named ones
    python3 codes/anim-man.py --check         build every scenario and check the note's numbers, no drawing
    python3 codes/anim-man.py --debug hohmann key frames as PNGs, in the folder given by $ANIM_DEBUG
Afterwards, from the project root:  python3 _filters/gif-frames.py   (the still frames that the PDF output uses).
(The two apse-detour animations of section 6.2 have their own script, anim-man-apse-detour.py.)
"""
import os
import sys
import numpy as np
import _figfont                                # all figure text at one scale
from _manstyle import CREAM, INK, MAROON, BLUE, GREEN, AMBER, PURPLE
from _manim import MU, RE, Orbit, Leg, Companion, Panel, animate, view, TOPDOWN, f_after

KM = lambda x: f"{x:,.0f}".replace(",", "\\,")
pi = np.pi
SCENARIOS = {}


def scenario(fn):
    SCENARIOS[fn.__name__] = fn
    return fn


# ------------------------------------------------------------------ the Hohmann transfer, LEO to GEO (section 3.1)
R1, R2 = 6678.137, 42164.0


@scenario
def hohmann():
    leo, geo, tr = Orbit.circ(R1, color=BLUE), Orbit.circ(R2, color=GREEN), Orbit.rprq(R1, R2, color=MAROON)
    legs = [Leg(leo, pi, 2*pi), Leg(tr, 0, pi), Leg(geo, pi, 1.5*pi)]
    P = Panel("LEO to GEO: the Hohmann transfer", legs, ((-60000, 52000), (-50000, 50000)), arrow=0.036,
              labels=[dict(text=f"LEO, $r_1={KM(R1)}$ km", pos=(-7000, 10000), color=BLUE, ha="right"),
                      dict(text=f"GEO, $r_2={KM(R2)}$ km", pos=(0, 45500), color=GREEN),
                      dict(text="transfer ellipse", pos=(-17700, -20500), color=MAROON)],
              expect=dict(dv=[2.42573, 1.46682], total=3.89255, leg_h=[None, 5.275, None], kinds=["prograde", "prograde"]))
    return [P], dict(K=14)


def route(title, o_from, fb1, tr, ftr, o_to, fb2, pre_h, post_h, lims, **kw):
    """Fly o_from for pre_h hours up to the true anomaly fb1, burn onto the arc ftr of the transfer orbit tr, and after
    the second burn fly o_to from its true anomaly fb2 for post_h hours."""
    legs = [Leg(o_from, f_after(o_from, fb1, -pre_h), fb1), Leg(tr, *ftr), Leg(o_to, fb2, f_after(o_to, fb2, post_h))]
    return Panel(title, legs, lims, **kw)


# ------------------------------------------------------------------ two ellipses with a common apse line (section 3.3)
O1E = dict(a=12000.0, e=0.30)
O2E = dict(a=42000.0, e=0.50)


def coax_orbits(w2):
    o1 = Orbit(O1E["a"]*(1 - O1E["e"]**2), O1E["e"], w=0.0, color=BLUE)
    o2 = Orbit(O2E["a"]*(1 - O2E["e"]**2), O2E["e"], w=w2, color=GREEN)
    return o1, o2


COAX_LIMS = ((-74000, 36000), (-44000, 44000))
COAX_LABELS = lambda w2: [dict(text="orbit 1", pos=(-4000, 13500), color=BLUE, ha="left"),
                          dict(text="orbit 2", pos=(-46000 if w2 == 0 else -46000, -39500), color=GREEN)]


@scenario
def coax_aligned():
    o1, o2 = coax_orbits(0.0)
    a1 = Orbit.rprq(8400, 63000, w=0.0, color=MAROON)
    a2 = Orbit.rprq(15600, 21000, w=pi, color=PURPLE)
    ex1 = dict(dv=[1.29675, 0.55850], total=1.85525, leg_h=[None, 9.324, None], kinds=["prograde", "prograde"])
    ex2 = dict(dv=[1.18573, 1.31337], total=2.49909, leg_h=[None, 3.422, None], kinds=["prograde", "prograde"])
    P1 = route("A1: periapsis of orbit 1 to apoapsis of orbit 2", o1, 2*pi, a1, (0, pi), o2, pi, 1.8, 6.0, COAX_LIMS,
               labels=COAX_LABELS(0.0) + [dict(text="transfer A1", pos=(-30000, -10000), color=MAROON)], expect=ex1, arrow=0.03)
    P2 = route("A2: apoapsis of orbit 1 to periapsis of orbit 2", o1, pi, a2, (0, pi), o2, 0.0, 1.8, 6.0, COAX_LIMS,
               labels=COAX_LABELS(0.0) + [dict(text="transfer A2", pos=(2000, 8000), color=PURPLE, ha="left")], expect=ex2, arrow=0.03)
    return [P1, P2], dict(K=9)


@scenario
def coax_inbound():
    o1, o2 = coax_orbits(0.0)
    a1 = Orbit.rprq(8400, 63000, w=0.0, color=MAROON)
    a2 = Orbit.rprq(15600, 21000, w=pi, color=PURPLE)
    ex1 = dict(dv=[0.55850, 1.29675], total=1.85525, leg_h=[None, 9.324, None], kinds=["retrograde", "retrograde"])
    ex2 = dict(dv=[1.31337, 1.18573], total=2.49909, leg_h=[None, 3.422, None], kinds=["retrograde", "retrograde"])
    P1 = route("B1: apoapsis of orbit 2 to periapsis of orbit 1", o2, pi, a1, (pi, 2*pi), o1, 0.0, 5.0, 2.0, COAX_LIMS,
               labels=COAX_LABELS(0.0) + [dict(text="transfer B1", pos=(-30000, 8000), color=MAROON)], expect=ex1, arrow=0.03)
    P2 = route("B2: periapsis of orbit 2 to apoapsis of orbit 1", o2, 0.0, a2, (pi, 2*pi), o1, pi, 2.0, 2.0, COAX_LIMS,
               labels=COAX_LABELS(0.0) + [dict(text="transfer B2", pos=(2000, -8000), color=PURPLE, ha="left")], expect=ex2, arrow=0.03)
    return [P1, P2], dict(K=9)


@scenario
def coax_antialigned():
    o1, o2 = coax_orbits(pi)
    t2 = Orbit.rprq(8400, 21000, w=0.0, color=MAROON)
    t1 = Orbit.rprq(15600, 63000, w=pi, color=PURPLE)
    ex2 = dict(dv=[0.37924, 2.04250], total=2.42174, leg_h=[None, 2.464, None], kinds=["prograde", "prograde"])
    ex1 = dict(dv=[2.17083, 0.19386], total=2.36469, leg_h=[None, 10.769, None], kinds=["prograde", "prograde"])
    lims = ((-36000, 74000), (-44000, 44000))
    P2 = route("T2: periapsis of orbit 1 to periapsis of orbit 2", o1, 2*pi, t2, (0, pi), o2, 0.0, 1.8, 6.0, lims,
               labels=[dict(text="orbit 1", pos=(4000, 12500), color=BLUE, ha="left"), dict(text="orbit 2", pos=(46000, -39500), color=GREEN),
                       dict(text="transfer T2", pos=(14000, 4500), color=MAROON, ha="left")], expect=ex2, arrow=0.03)
    P1 = route("T1: apoapsis of orbit 1 to apoapsis of orbit 2", o1, pi, t1, (0, pi), o2, pi, 1.8, 6.0, lims,
               labels=[dict(text="orbit 1", pos=(4000, 12500), color=BLUE, ha="left"), dict(text="orbit 2", pos=(46000, -39500), color=GREEN),
                       dict(text="transfer T1", pos=(-4000, 20000), color=PURPLE, ha="left")], expect=ex1, arrow=0.03)
    return [P2, P1], dict(K=9)


# ------------------------------------------------------------------ crossing orbits (section 3.4)
@scenario
def cross():
    o1, o2 = Orbit.rprq(8000, 30000, color=BLUE), Orbit.rprq(12000, 24000, color=GREEN)
    fs = np.arccos(-2/3)
    lims = ((-38000, 30000), (-30000, 30000))
    PL = Panel("one impulse at the crossing, $f^*=131.81^\\circ$",
               [Leg(o1, f_after(o1, fs, -1.5), fs), Leg(o2, fs, f_after(o2, fs, 5.0))], lims, arrow=0.03,
               labels=[dict(text="orbit 1", pos=(-36000, 4000), color=BLUE, ha="left"), dict(text="orbit 2", pos=(8000, 21000), color=GREEN, ha="left")],
               expect=dict(dv=[1.26058], tol=6e-5, kinds=["turns the velocity by 17.38"]))
    tr = Orbit.rprq(12000, 30000, w=0.0, color=PURPLE)
    PR = route("two impulses: apoapsis of 1 to periapsis of 2", o1, pi, tr, (pi, 2*pi), o2, 0.0, 1.5, 5.0, lims, arrow=0.03,
               labels=[dict(text="orbit 1", pos=(-36000, 4000), color=BLUE, ha="left"), dict(text="orbit 2", pos=(8000, 21000), color=GREEN, ha="left"),
                       dict(text="transfer", pos=(-9000, -2500), color=PURPLE)],
               expect=dict(dv=[0.39018, 0.23358], total=0.62376, leg_h=[None, 4.206, None], kinds=["prograde", "retrograde"]))
    return [PL, PR], dict(K=18)


# ------------------------------------------------------------------ the bi-elliptic transfer and the stopover (sections 4.1, 4.3)
def three_burns(title, rb, r2, w3, ex, lims, labels, burn_labels=None):
    leo = Orbit.circ(R1, color=BLUE)
    e1 = Orbit.rprq(R1, rb, w=0.0, color=MAROON)
    e2 = Orbit.rprq(min(rb, r2), max(rb, r2), w=0.0 if rb > r2 else pi, color=PURPLE)
    fin = Orbit.circ(r2, color=GREEN)
    legs = [Leg(leo, pi, 2*pi), Leg(e1, 0, pi), Leg(e2, pi if rb > r2 else 0, 2*pi if rb > r2 else pi), Leg(fin, 0, 0.5*pi)]
    return Panel(title, legs, lims, labels=labels, burn_labels=burn_labels or {}, expect=ex, arrow=0.03)


@scenario
def bielliptic():
    r2, rb = 15*R1, 400000.0
    P = three_burns("bi-elliptic transfer, $R=15$, $r_b=400\\,000$ km", rb, r2, 0.0,
                    dict(dv=[3.11004, 0.45088, 0.52801], total=4.08892, kinds=["prograde", "prograde", "retrograde"],
                         sum_h=(1, 3, 299.6, 0.12)),
                    ((-480000, 270000), (-230000, 230000)),
                    [dict(text="LEO", pos=(15000, -30000), color=BLUE, ha="left"),
                     dict(text=f"final circle, $r_2={KM(r2)}$ km", pos=(95000, 120000), color=GREEN, ha="left"),
                     dict(text="ellipse 1", pos=(-200000, 80000), color=MAROON),
                     dict(text="ellipse 2", pos=(-150000, -215000), color=PURPLE)],
                    {0: dict(pos=(25000, 60000), ha="left", va="bottom"), 1: dict(pos=(-465000, 90000), ha="left", va="bottom"),
                     2: dict(pos=(120000, -70000), ha="left", va="top")})
    return [P], dict(legframes=[16, 84, 84, 24])


@scenario
def stopover():
    r2, rb = 30*R1, 25*R1
    P = three_burns("stopover, $R=30$, $r_b=25\\,r_1$", rb, r2, pi,
                    dict(dv=[2.98794, 1.18531, 0.06564], total=4.23889, kinds=["prograde", "prograde", "prograde"],
                         sum_h=(1, 3, 144.2, 0.12)),
                    ((-205000, 245000), (-215000, 215000)),
                    [dict(text="LEO", pos=(12000, -30000), color=BLUE, ha="left"),
                     dict(text=f"final circle, $r_2={KM(r2)}$ km", pos=(60000, -207000), color=GREEN, ha="left"),
                     dict(text="ellipse 1", pos=(-95000, 100000), color=MAROON),
                     dict(text="ellipse 2", pos=(20000, -150000), color=PURPLE)],
                    None)
    return [P], dict(legframes=[16, 84, 84, 24])


# ------------------------------------------------------------------ phasing (sections 5.1, 5.2)
@scenario
def phasing():
    r, dth, k = 6778.0, np.radians(10.0), 1
    circ = Orbit.circ(r, color=BLUE)
    T = 2*pi*np.sqrt(r**3/MU)
    aph = r*(1 - dth/(2*pi*k))**(2/3)
    ph = Orbit.rprq(2*aph - r, r, w=pi, color=MAROON)               # its apoapsis (angle 0) is the burn point
    pre = 0.25                                                       # hours before the burn
    f0 = 2*pi - 2*pi*pre*3600/T
    legs = [Leg(circ, f0, 2*pi), Leg(ph, pi, 3*pi), Leg(circ, 0, 2*pi*0.2)]
    tgt = Companion(circ, dth + 2*pi*(-pre*3600/T), color=PURPLE, ghost=True, ms=19, label="target", label_off=(450, 450))
    P = Panel("circular phasing: a chaser $10^\\circ$ behind its target", legs, ((-9800, 9800), (-9800, 9800)),
              companions=[tgt], arrow=0.55,
              labels=[dict(text="shared orbit, $r=6778$ km", pos=(0, 8600), color=BLUE)],
              expect=dict(dv=[0.073039, 0.073039], total=0.146078, leg_h=[None, 1.4998, None], kinds=["retrograde", "prograde"], tol=6e-6))
    return [P], dict(K=70, suptitle="the chaser (black dot) flies the circle, a smaller ellipse, and the circle again; the ring is its target")


@scenario
def ell_phasing():
    a, e, dt = 10000.0, 0.3, 600.0
    o = Orbit(a*(1 - e*e), e, w=0.0, color=BLUE)
    T = 2*pi*np.sqrt(a**3/MU)
    aph = a*(1 + dt/T)**(2/3)
    lims = ((-18500, 10500), (-13500, 12500))
    # periapsis burn: r_p stays 7000 km, apoapsis grows;  apoapsis burn: r_a stays 13000 km, periapsis rises
    php = Orbit.rprq(a*(1 - e), 2*aph - a*(1 - e), w=0.0, color=MAROON)
    pha = Orbit.rprq(2*aph - a*(1 + e), a*(1 + e), w=0.0, color=PURPLE)
    pre, post = 0.6, 1.0
    Lp = [Leg(o, f_after(o, 2*pi, -pre), 2*pi), Leg(php, 0, 2*pi), Leg(o, 0, f_after(o, 0, post))]
    La = [Leg(o, f_after(o, pi, -pre), pi), Leg(pha, pi, 3*pi), Leg(o, pi, f_after(o, pi, post))]
    ghost = lambda f0: Companion(o, f0, color=INK, ghost=True, ms=17, label="no burns", label_off=(300, 500))
    P1 = Panel("burn at periapsis", Lp, lims, companions=[ghost(f_after(o, 2*pi, -pre))], arrow=0.7,
               labels=[dict(text="phasing ellipse", pos=(-3400, -12200), color=MAROON)],
               expect=dict(dv=[0.088211, 0.088211], total=0.17642, leg_h=[None, (T + dt)/3600, None], kinds=["prograde", "retrograde"], tol=6e-6))
    P2 = Panel("burn at apoapsis", La, lims, companions=[ghost(f_after(o, pi, -pre))], arrow=0.7,
               labels=[dict(text="phasing ellipse", pos=(-2600, -12200), color=PURPLE)],
               expect=dict(dv=[0.161834, 0.161834], total=0.32367, leg_h=[None, (T + dt)/3600, None], kinds=["prograde", "retrograde"], tol=6e-6))
    return [P1, P2], dict(K=26, suptitle="the craft (black dot) flies its orbit (blue), a longer ellipse once round, and the orbit again; the ring is the same craft without burns")


# ------------------------------------------------------------------ rotating the apse line with one radial impulse (section 6.1)
@scenario
def apse_radial():
    a, e, eta = 12000.0, 0.4, np.radians(30.0)
    p = a*(1 - e*e)
    o1, o2 = Orbit(p, e, w=0.0, color=BLUE), Orbit(p, e, w=eta, color=GREEN)
    lims = ((-24000, 13000), (-14500, 13500))
    out = []
    for f1, nm, kind in ((eta/2, "first crossing, $f=15^\\circ$", "radial, inward"), (eta/2 + pi, "second crossing, $f=195^\\circ$", "radial, outward")):
        legs = [Leg(o1, f_after(o1, f1, -1.0), f1), Leg(o2, f1 - eta, f_after(o2, f1 - eta, 2.5))]
        out.append(Panel(nm, legs, lims, arrow=0.07,
                         labels=[dict(text="initial orbit", pos=(-12000, 11000), color=BLUE, ha="right"), dict(text="final orbit,\nturned by $30^\\circ$", pos=(-18500, -9000), color=GREEN)],
                         expect=dict(dv=[1.30204], kinds=[kind])))
    return out, dict(K=24)


# ------------------------------------------------------------------ plane changes (section 8): the line of nodes is the x axis
I0 = np.radians(19.2)                                                                  # a transfer orbit from Sriharikota
VW = view(az=-68, el=27)


def equator(color="#8d877c"):
    return Orbit.circ(RE, color=color)


def node_legs(o_in, f_in, o_out, f_out, pre_h, post_h):
    return [Leg(o_in, f_after(o_in, f_in, -pre_h), f_in), Leg(o_out, f_out, f_after(o_out, f_out, post_h))]


@scenario
def plane_where():
    leo_i, leo_e = Orbit.circ(R1, i=I0, color=BLUE), Orbit.circ(R1, color=GREEN)
    geo_i, geo_e = Orbit.circ(R2, i=I0, color=BLUE), Orbit.circ(R2, color=GREEN)
    t_i, t_e = Orbit.rprq(R1, R2, w=0.0, i=I0, color=BLUE), Orbit.rprq(R1, R2, w=0.0, color=GREEN)
    kinds = ["turns the velocity by 19.20"]
    PA = Panel("(a) in LEO, $r=6678$ km", node_legs(leo_i, 2*pi, leo_e, 0.0, 0.5, 0.9), ((-9500, 9500), (-7800, 7800)), vw=VW, arrow=0.06,
               outlines=[equator()], expect=dict(dv=[2.57683], kinds=kinds), labels=[dict(text="before", pos=(-8800, 6200), color=BLUE, ha="left"),
                                                                              dict(text="after: equatorial", pos=(-8800, -6800), color=GREEN, ha="left")])
    PB = Panel("(b) in GEO, $r=42\\,164$ km", node_legs(geo_i, 2*pi, geo_e, 0.0, 6.0, 6.0), ((-49000, 49000), (-39000, 39000)), vw=VW, arrow=0.06,
               outlines=[equator()], expect=dict(dv=[1.02552], kinds=kinds))
    PC = Panel("(c) at the apoapsis of the LEO$\\to$GEO ellipse", node_legs(t_i, pi, t_e, pi, 4.0, 4.0), ((-52000, 20000), (-26000, 26000)), vw=VW,
               arrow=0.2, outlines=[equator()], expect=dict(dv=[0.53628], kinds=kinds))
    return [PA, PB, PC], dict(K=10, grid=(1, 3), figsize=(17.5, 6.4), dpi=66)


@scenario
def plane_strategies():
    from scipy.optimize import minimize_scalar
    v_c1, v_t1 = np.sqrt(MU/R1), np.sqrt(2*MU*R2/(R1*(R1 + R2)))
    v_t2, v_c2 = np.sqrt(2*MU*R1/(R2*(R1 + R2))), np.sqrt(MU/R2)
    lc = lambda a_, b_, ang: np.sqrt(a_*a_ + b_*b_ - 2*a_*b_*np.cos(ang))
    dstar = minimize_scalar(lambda d_: lc(v_c1, v_t1, d_) + lc(v_t2, v_c2, I0 - d_), bounds=(0, I0), method="bounded", options={"xatol": 1e-12}).x
    leo = Orbit.circ(R1, i=I0, color=BLUE)
    geo_i, geo_e = Orbit.circ(R2, i=I0, color=PURPLE), Orbit.circ(R2, color=GREEN)          # i = 19.2 deg, before burn 3; the final one
    t_inc, t_eq, t_split = (Orbit.rprq(R1, R2, w=0.0, i=ii, color=MAROON) for ii in (I0, 0.0, I0 - dstar))
    lims = ((-52000, 52000), (-41000, 46000))
    pre, post = 0.5, 6.0
    L0 = Leg(leo, f_after(leo, 2*pi, -pre), 2*pi)
    tail = lambda: Leg(geo_e, pi, f_after(geo_e, pi, post))
    common = dict(vw=VW, arrow=0.04, outlines=[equator()])
    lab_eq = dict(text="GEO, equatorial", pos=(24000, -26500), color=GREEN)
    P1 = Panel("(a) Hohmann, then a separate plane change in GEO", [L0, Leg(t_inc, 0, pi), Leg(geo_i, pi, pi), tail()], lims, **common,
               labels=[lab_eq, dict(text="GEO with $i=19.2^\\circ$,\nbefore burn 3", pos=(14000, 36200), color=PURPLE, ha="left", va="bottom")],
               expect=dict(dv=[2.42573, 1.46682, 1.02552], total=4.91807, kinds=["prograde", "prograde", "turns the velocity by 19.20"]))
    P2 = Panel("(b) the whole turn folded into the LEO burn", [L0, Leg(t_eq, 0, pi), tail()], lims, **common, labels=[lab_eq],
               expect=dict(dv=[3.82218, 1.46682], total=5.28900))
    P3 = Panel("(c) the whole turn folded into the GEO burn", [L0, Leg(t_inc, 0, pi), tail()], lims, **common, labels=[lab_eq],
               expect=dict(dv=[2.42573, 1.64363], total=4.06936))
    P4 = Panel(f"(d) the turn shared: ${np.degrees(dstar):.2f}^\\circ$ in the LEO burn, ${np.degrees(I0 - dstar):.2f}^\\circ$ in the GEO burn",
               [L0, Leg(t_split, 0, pi), tail()], lims, **common, labels=[lab_eq], expect=dict(dv=[2.43901, 1.61619], total=4.05520, tol=1.2e-4))
    return [P1, P2, P3, P4], dict(K=10, grid=(2, 2), figsize=(12.6, 11.4), dpi=66, colors=128,
                                  suptitle="blue: LEO, $i=19.2^\\circ$;  maroon: the transfer ellipse;  green: the final GEO circle, in the equatorial plane\n"
                                           "(a) only, purple dashed: the GEO circle with $i=19.2^\\circ$ that burn 2 makes, before burn 3 turns it into the equatorial one")


@scenario
def plane_node():
    r, inc, dO = 6778.0, np.radians(51.6), np.radians(10.0)
    o1, o2 = Orbit.circ(r, i=inc, node=0.0, color=BLUE), Orbit.circ(r, i=inc, node=dO, color=GREEN)
    n = np.cross(o1.R[:, 2], o2.R[:, 2]); n = n/np.linalg.norm(n)                      # the line where the two planes meet
    fa = lambda o_: float(np.arctan2(n @ o_.R[:, 1], n @ o_.R[:, 0]))
    legs = [Leg(o1, f_after(o1, fa(o1), -0.45), fa(o1)), Leg(o2, fa(o2), f_after(o2, fa(o2), 0.6))]
    P = Panel("space station orbit, $i=51.6^\\circ$: the node moved by $10^\\circ$", legs, ((-8800, 8800), (-7600, 7600)), vw=view(az=-30, el=33),
              arrow=0.05, outlines=[equator()], labels=[dict(text="before", pos=(-8300, 6300), color=BLUE, ha="left"), dict(text="after", pos=(-8300, -6700), color=GREEN, ha="left")],
              expect=dict(dv=[1.0476], tol=6e-4, kinds=["turns the velocity by 7.83"]))
    return [P], dict(K=22)


# ------------------------------------------------------------------ escape from an eccentric orbit (section 9)
@scenario
def escape():
    rp, e = R1, 0.9
    ra = rp*(1 + e)/(1 - e); a = (rp + ra)/2
    Rc = 9.25e5
    tpar = lambda q: (2/3)*np.sqrt((Rc - q)/(2*MU))*(Rc + 2*q)/3600           # the note's formula for the parabola
    fcap = lambda q: float(np.arccos(2*q/Rc - 1))
    o_p, o_a = Orbit.rprq(rp, ra, w=0.0, color=BLUE), Orbit.rprq(rp, ra, w=pi, color=BLUE)
    par_p = Orbit.parabola(rp, w=0.0, color=MAROON, fshow=(0.0, fcap(rp)))
    par_a = Orbit.parabola(ra, w=0.0, color=PURPLE, fshow=(0.0, fcap(ra)))
    lims = ((-980000, 190000), (-190000, 700000))
    half = pi*np.sqrt(a**3/MU)/3600
    P1 = Panel("burn at periapsis, after the coast down", [Leg(o_p, pi, 2*pi), Leg(par_p, 0, fcap(rp))], lims, arrow=0.08,
               labels=[dict(text="orbit, $e=0.9$", pos=(0, -60000), color=BLUE, va="top")],
               expect=dict(dv=[0.2766], leg_h=[half, tpar(rp)], kinds=["prograde"]))
    P2 = Panel("burn at apoapsis, at once", [Leg(o_a, pi, pi), Leg(par_a, 0, fcap(ra))], lims, arrow=0.08,
               labels=[dict(text="orbit, $e=0.9$", pos=(0, -60000), color=BLUE, va="top")],
               expect=dict(dv=[1.9461], leg_h=[0.0, tpar(ra)], kinds=["prograde"]))
    return [P1, P2], dict(K=1.25)


# ------------------------------------------------------------------ driver
def build(name):
    panels, kw = SCENARIOS[name]()
    return panels, kw


if __name__ == "__main__":
    args = [a for a in sys.argv[1:] if not a.startswith("--")]
    names = args or list(SCENARIOS)
    for nm in names:
        _figfont.use_scale_for(nm)
        panels, kw = build(nm)
        for P in panels:
            print(P.table())
        if "--check" in sys.argv:
            continue
        out = f"img/anim-man-{nm.replace('_', '-')}.gif"
        if "--debug" in sys.argv:
            animate(panels, out, debug=os.path.join(os.environ["ANIM_DEBUG"], nm), **kw)
        else:
            animate(panels, out, **kw)
