codex/past-exam-of-the-mathematics-course-of-the-university-of-oxford/2017/prelims/mathematics-iv-resit-pendulum-modes.py

past-exam-of-the-mathematics-course-of-the-university-of-oxford/2017/prelims/mathematics-iv-resit-pendulum-modes.py
"""Draw Question 4(b)'s modes; run in the mirrored _media directory.

Tested with Python 3.14.4, matplotlib 3.10.7 and numpy 2.3.5;
dependencies are declared in the repository's pyproject.toml.
Output is written only to the current working directory.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def draw_mode(ax, signs, title, frequency):
    pivots = np.array([0.0, 2.0])
    shifts = 0.24 * np.array(signs)
    bob_y = -np.sqrt(1 - shifts**2)
    ax.plot([-0.45, 2.45], [0, 0], color="black", lw=2)
    for i, (pivot, shift, y) in enumerate(zip(pivots, shifts, bob_y)):
        ax.plot([pivot, pivot], [0, -1.17], "--", color="0.75", lw=1)
        ax.plot([pivot, pivot + shift], [0, y], color="#234e70", lw=2)
        ax.plot(pivot, 0, "ko", ms=4)
        ax.plot(pivot + shift, y, "o", color="#234e70", ms=13)
        ax.annotate("", xy=(pivot + shift, -1.35), xytext=(pivot, -1.35),
                    arrowprops={"arrowstyle": "->", "lw": 2, "color": "#b84724"})
        ax.text(pivot, -1.55, rf"$x_{i+1}$", ha="center", fontsize=12)
    left, right = pivots + shifts
    spring_x = np.linspace(left + 0.10, right - 0.10, 85)
    phase = np.linspace(0, 12 * np.pi, len(spring_x))
    ax.plot(spring_x, bob_y[0] + 0.045 * np.sin(phase), color="#75613a", lw=1.7)
    ax.set_title(title, fontsize=13, pad=8)
    ax.text(1, -1.88, frequency, ha="center", fontsize=14)
    ax.set(xlim=(-0.5, 2.5), ylim=(-2.05, 0.2), aspect="equal")
    ax.axis("off")


if __name__ == "__main__":
    fig, axes = plt.subplots(1, 2, figsize=(8.4, 3.5), facecolor="white")
    draw_mode(axes[0], (1, 1), "Together: equal displacements", r"$\omega_+=\sqrt{g/\ell}$")
    draw_mode(axes[1], (1, -1), "Opposite: equal and opposite displacements", r"$\omega_-=\sqrt{g/\ell+2k/m}$")
    fig.tight_layout(pad=1.2)
    fig.savefig(Path.cwd() / "mathematics-iv-resit-pendulum-modes.png", dpi=140,
                facecolor="white", transparent=False)
    plt.close(fig)