codexpast-exam-of-the-physics-course-of-the-year-1-of-the-natural-sciences-course-of-the-university-of-cambridge2014paper-1-ia2014-disc-forces.py
past-exam-of-the-physics-course-of-the-year-1-of-the-natural-sciences-course-of-the-university-of-cambridge/2014/paper-1-ia2014-disc-forces.py
"""Original educational diagram. Python 3.14, NumPy and Matplotlib; output is saved in the current working directory."""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
plt.rcParams.update({'font.size':10,'figure.facecolor':'white','axes.facecolor':'white'})
from matplotlib.patches import Circle
th=np.deg2rad(28); u=np.array([np.cos(th),np.sin(th)]); n=np.array([-np.sin(th),np.cos(th)])
c=np.array([0.,0.]); radius=.65; contact=c-radius*n
fig,ax=plt.subplots(figsize=(6.6,4.6),layout='constrained')
ax.add_patch(Circle(c,radius,facecolor='#e7f0f7',edgecolor='#273746',lw=2))
line=np.array([contact-1.7*u,contact+1.6*u]);ax.plot(line[:,0],line[:,1],color='#9aa3aa',lw=3)
ax.scatter(*contact,s=26,color='black',zorder=5);ax.scatter(*c,s=16,color='black')
def arrow(start,end,label,offset=(0,0),color='#225c91'):
ax.annotate('',xy=end,xytext=start,arrowprops={'arrowstyle':'-|>','lw':2.2,'color':color,'mutation_scale':16})
p=end+np.array(offset);ax.text(*p,label,ha='center',va='center',color=color,fontsize=12)
arrow(c,c+1.65*u,r'$T$',(.12,.05))
arrow(c,c+np.array([0,-1.5]),r'$Mg$',(.22,0),'#a03a35')
arrow(contact,contact+1.95*n,r'$N$',(-.14,.07),'#34806c')
arrow(contact,contact-1.2*u,r'$F$',(-.12,-.12),'#a47316')
start=c+.95*n;arrow(start,start+.8*u,r'$q>0$',(.16,.03),'#66717a')
ax.text(0,1.65,'Uphill acceleration: static friction points downhill',ha='center',fontsize=11)
ax.text(0,-1.9,r'$Fa=I(q/a),\quad I=Ma^2/2$',ha='center',fontsize=12)
ax.set(xlim=(-1.9,1.9),ylim=(-2.05,1.9),aspect='equal');ax.axis('off')
fig.savefig('paper-1-ia2014-disc-forces.png',dpi=150,facecolor='white');plt.close(fig)