codexpast-exam-of-the-mathematics-course-of-the-year-2-of-the-natural-sciences-course-of-the-university-of-cambridge2021paper-2-lamina-principal-axes.py
past-exam-of-the-mathematics-course-of-the-year-2-of-the-natural-sciences-course-of-the-university-of-cambridge/2021/paper-2-lamina-principal-axes.py
"""Plot the computed principal axes; save the PNG in the current directory.
Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
Run from the corresponding _media directory with the repository environment.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np
def main():
angle = np.arctan(2.0) / 2
small_axis = np.array([np.cos(angle), np.sin(angle)])
large_axis = np.array([-np.sin(angle), np.cos(angle)])
inertia = np.array([[1.0, -1.0], [-1.0, 2.0]])
assert np.allclose(inertia @ small_axis, (3 - np.sqrt(5)) / 2 * small_axis)
assert np.allclose(inertia @ large_axis, (3 + np.sqrt(5)) / 2 * large_axis)
fig, ax = plt.subplots(figsize=(6.5, 4.8))
fig.set_facecolor("white")
vertices = np.array([[-2, -1], [0, 1], [2, 1], [0, -1]])
ax.add_patch(Polygon(vertices, facecolor="#dce7ed", edgecolor="#4c626f", linewidth=1.5))
ax.axhline(0, color="0.7", linewidth=0.7, zorder=0)
ax.axvline(0, color="0.7", linewidth=0.7, zorder=0)
for direction, color, name in [(small_axis, "#1764ab", r"$I_-$"),
(large_axis, "#b44720", r"$I_+$")]:
extent = 2.1
start, end = -extent * direction, extent * direction
ax.annotate("", xy=end, xytext=start,
arrowprops={"arrowstyle": "<->", "color": color, "linewidth": 2.2})
ax.text(*(1.08 * end), name, color=color, fontsize=13, ha="center", va="center")
ax.scatter([0], [0], color="black", s=22, zorder=5)
arc_angle = np.linspace(0, angle, 80)
ax.plot(0.6 * np.cos(arc_angle), 0.6 * np.sin(arc_angle), color="#1764ab", linewidth=1)
ax.text(0.69, 0.13, r"$31.7^\circ$", color="#1764ab", fontsize=10)
ax.set_xlim(-2.5, 2.5)
ax.set_ylim(-2.25, 2.25)
ax.set_aspect("equal")
ax.set_xlabel(r"$x_1/D$")
ax.set_ylabel(r"$x_2/D$")
ax.set_xticks([-2, -1, 0, 1, 2])
ax.set_yticks([-2, -1, 0, 1, 2])
ax.spines[["top", "right"]].set_visible(False)
ax.set_title("Principal axes through the centre of mass", fontsize=12)
fig.text(0.5, 0.018, r"$I_\pm=MD^2(3\pm\sqrt{5})/6$; normal axis: $I_3=MD^2$",
ha="center", fontsize=11)
fig.tight_layout(rect=(0, 0.055, 1, 1))
fig.savefig(Path(__file__).with_suffix(".png").name, dpi=120, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()