codex/past-exam-of-the-mathematics-course-of-the-year-2-of-the-natural-sciences-course-of-the-university-of-cambridge/2014/paper-2-2014-ib-string-modes.py

past-exam-of-the-mathematics-course-of-the-year-2-of-the-natural-sciences-course-of-the-university-of-cambridge/2014/paper-2-2014-ib-string-modes.py
"""Original three-bead transverse-mode sketches. Python 3.14, NumPy 2.3, Matplotlib 3.10.
Run in the corresponding _media directory; the opaque white PNG is written to cwd.
"""
from pathlib import Path
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np

fig,axes=plt.subplots(1,3,figsize=(10,4.3),facecolor='white')
x=np.arange(5)
shapes=[(1,np.sqrt(2),1),(1,0,-1),(1,-np.sqrt(2),1)]
frequencies=[r'$\omega_1^2=(2-\sqrt{2})\tau/(m\ell)$',r'$\omega_2^2=2\tau/(m\ell)$',r'$\omega_3^2=(2+\sqrt{2})\tau/(m\ell)$']
labels=[r'$(1,\sqrt{2},1)$',r'$(1,0,-1)$',r'$(1,-\sqrt{2},1)$']
for j,(ax,shape,freq,label) in enumerate(zip(axes,shapes,frequencies,labels),1):
    z=np.r_[0,.45*np.array(shape),0]
    for wall in [-.09,4]:ax.add_patch(Rectangle((wall,-.85),.09,1.7,color='#b8c2cd'))
    ax.plot([0,4],[0,0],ls='--',lw=1,color='#929eac')
    ax.plot(x,z,color='#235b82',lw=2)
    ax.scatter(x[1:4],z[1:4],s=190,facecolor='#e3edf5',edgecolor='#235b82',zorder=3)
    for i in range(1,4):
        ax.text(i,z[i],str(i),ha='center',va='center',fontsize=9,zorder=4)
    ax.text(2,-1.1,label,ha='center',fontsize=12)
    ax.text(2,-1.46,freq,ha='center',fontsize=11)
    ax.set(xlim=(-.35,4.35),ylim=(-1.7,1.05),title=f'Mode {j}')
    ax.set_aspect('equal');ax.axis('off')
fig.suptitle('Three independent shapes of a string with three equal loads',fontsize=15,y=.96)
fig.text(.5,.07,'Fixed walls • dashed equilibrium line • common arbitrary displacement scale',ha='center',fontsize=11)
fig.text(.5,.02,'Each shape oscillates sinusoidally; joining segments show the instantaneous string profile.',ha='center',fontsize=10)
fig.subplots_adjust(left=.025,right=.98,top=.85,bottom=.17,wspace=.18)
fig.savefig(Path.cwd()/Path(__file__).with_suffix('.png').name,dpi=145,facecolor='white')
plt.close(fig)