"""Original wind-triangle and ground-progress diagram; writes cwd PNG. Tested with Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7. """ import numpy as np import matplotlib.pyplot as plt V, cross, head = 40.0, 8.0, 12.0 air = np.array([-cross, np.sqrt(V**2-cross**2)]) wind = np.array([cross, -head]) ground = air + wind threshold = np.sqrt(V**2-head**2) assert np.isclose(np.linalg.norm(air), V) assert np.isclose(ground[0], 0) and ground[1] > 0 assert np.isclose(np.sqrt(V**2-threshold**2)-head, 0) fig, (ax, bx) = plt.subplots(1, 2, figsize=(7.6, 4.4), dpi=100, facecolor='white', gridspec_kw={'width_ratios':[1,1.35]}) for origin, vector, color in [(np.zeros(2), air, '#235789'), (air, wind, '#b45222'), (np.zeros(2), ground, '#4c7c36')]: ax.annotate('', xy=origin+vector, xytext=origin, arrowprops={'arrowstyle':'->','lw':2.2,'color':color}) ax.text(-10.5, 18, 'Air-relative\n40.0 m/s', color='#235789', fontsize=8, ha='right') ax.text(-6, 40.5, 'Wind: 8 east, 12 south', color='#b45222', fontsize=8, ha='center') ax.text(1.5, 17, 'Ground\n27.19 m/s\ndue north', color='#4c7c36', fontsize=8) ax.scatter([0], [0], s=15, color='black') ax.set(xlim=(-22, 18), ylim=(-2, 46), xlabel='Eastward velocity (m/s)', ylabel='Northward velocity (m/s)', title='Vector closure') ax.set_aspect('equal', adjustable='box') ax.grid(alpha=.2) c = np.linspace(0, V, 500) progress = np.sqrt(V**2-c**2)-head bx.axhspan(-15, 0, color='#fff0e9') bx.axvspan(V, 44, color='#eeeeee') bx.plot(c, progress, color='#235789', lw=2) bx.axhline(0, color='black', lw=.8) bx.scatter([cross, threshold], [ground[1], 0], s=35, color=['#4c7c36','#b45222'], zorder=5) bx.annotate(f'Zero progress\n{threshold:.2f} m/s crosswind', (threshold,0), (12,-12), fontsize=8, arrowprops={'arrowstyle':'->'}) bx.text(21, 20, '40 m/s true airspeed\n12 m/s headwind', fontsize=8) bx.text(41.7, 10, 'No cancellation', rotation=90, ha='center', fontsize=8) bx.set(xlim=(0,44), ylim=(-15,32), xlabel='Rightward wind component (m/s)', ylabel='Along-track groundspeed (m/s)', title='Cancellation and progress limits') bx.grid(alpha=.2) fig.suptitle('Crosswind cancellation and forward ground progress', fontsize=12) fig.text(.5,.02,'Original uniform-wind, horizontal-flight model; the left velocity triangle uses equal scales.',ha='center',fontsize=8) fig.tight_layout(rect=(0,.05,1,.94)) fig.savefig('wind-correction-progress.png', facecolor='white') print({'correction_degrees':-np.degrees(np.arcsin(cross/V)), 'groundspeed':ground[1], 'progress_boundary_crosswind':threshold})