codex/past-exam-of-the-physics-1-paper-of-the-university-of-oxford/2017-superball-contact-forces.py

past-exam-of-the-physics-1-paper-of-the-university-of-oxford/2017-superball-contact-forces.py
"""Draw the signed contact-force model; save one PNG in the current directory."""

from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
import numpy as np


def main():
    plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 12})
    fig, ax = plt.subplots(figsize=(6, 4.4), layout="constrained")
    fig.patch.set_facecolor("white")
    ax.set_facecolor("white")
    ax.add_patch(Circle((0, 1.1), 1.1, facecolor="#edf3fa", edgecolor="#315775", lw=2))
    ax.plot([-1.8, 1.8], [0, 0], color="0.25", lw=1.6)
    ax.plot(0, 1.1, "o", color="#315775", ms=4)

    def arrow(start, end, color):
        ax.annotate("", end, start,
                    arrowprops={"arrowstyle": "-|>", "lw": 2.4, "color": color})

    arrow((0, 0), (0, 0.65), "#247b57")
    ax.text(0.12, 0.3, r"$N$", color="#247b57")
    arrow((0, 1.1), (0, 0.75), "#9e3546")
    ax.text(0.13, 0.89, r"$Mg$", color="#9e3546")
    arrow((0, 0), (-1.25, 0), "#c05c12")
    ax.text(-0.9, -0.27, r"$f_t$", color="#c05c12")
    angles = np.linspace(np.deg2rad(-10), np.deg2rad(120), 80)
    sx, sy = 0.48 * np.cos(angles), 1.1 + 0.48 * np.sin(angles)
    ax.plot(sx, sy, color="#315775", lw=1.8)
    arrow((sx[-5], sy[-5]), (sx[-1], sy[-1]), "#315775")
    ax.text(-0.65, 1.8, r"$+\Omega$ anticlockwise", fontsize=11, color="#315775")
    arrow((1.4, 0.2), (1.95, 0.2), "0.45")
    arrow((1.4, 0.2), (1.4, 0.8), "0.45")
    ax.text(1.93, 0.25, r"$x$", color="0.4", fontsize=10)
    ax.text(1.45, 0.77, r"$y$", color="0.4", fontsize=10)
    ax.text(0, -0.56, r"Initial contact slip: $w_1=v_1+a\Omega_1>0$",
            ha="center", fontsize=11)
    ax.set(xlim=(-2.05, 2.2), ylim=(-0.7, 2.5), aspect="equal")
    ax.set_title("Forces at the start of rough contact", fontsize=13, pad=10)
    ax.axis("off")
    output = Path.cwd() / Path(__file__).with_suffix(".png").name
    fig.savefig(output, dpi=120, facecolor="white", metadata={"Software": "Matplotlib"})
    plt.close(fig)


if __name__ == "__main__":
    main()