codex/rotation-counterclockwise-column-vector.py

rotation-counterclockwise-column-vector.py
"""Draw an active planar rotation; write the matching PNG to the CWD.

Tested with Python 3.14, matplotlib 3.10.7 and numpy 2.3.5.
Run from _media with MPLBACKEND=Agg and a writable MPLCONFIGDIR.
"""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    plt.rcParams.update({"font.size": 12, "font.family": "DejaVu Sans"})
    fig, ax = plt.subplots(figsize=(6.8, 5.2), layout="constrained")
    fig.get_layout_engine().set(rect=(0, 0.07, 1, 0.93))
    fig.patch.set_facecolor("white")
    ax.set_facecolor("white")
    theta = np.pi / 3
    before = np.array([2.0, 0.0])
    after = 2 * np.array([np.cos(theta), np.sin(theta)])
    t = np.linspace(0, 2 * np.pi, 361)
    ax.plot(2 * np.cos(t), 2 * np.sin(t), color="#d5dbe1", lw=1.5)
    ax.axhline(0, color="#73808d", lw=1)
    ax.axvline(0, color="#73808d", lw=1)
    for point, color in [(before, "#196ba1"), (after, "#c14b23")]:
        ax.annotate("", xy=point, xytext=(0, 0),
                    arrowprops={"arrowstyle": "-|>", "color": color,
                                "lw": 3, "mutation_scale": 20})
    t = np.linspace(0, theta, 100)
    radius = 0.72
    ax.plot(radius * np.cos(t), radius * np.sin(t), color="#4d5258", lw=1.7)
    ax.annotate("", xy=(radius * np.cos(t[-1]), radius * np.sin(t[-1])),
                xytext=(radius * np.cos(t[-8]), radius * np.sin(t[-8])),
                arrowprops={"arrowstyle": "-|>", "color": "#4d5258", "lw": 1.7})
    ax.text(0.82, 0.48, r"$\theta=\pi/3$", fontsize=13)
    ax.plot([after[0], after[0]], [0, after[1]], "--", color="#c14b23", lw=1)
    ax.plot([0, after[0]], [after[1], after[1]], "--", color="#c14b23", lw=1)
    ax.text(1.15, 1.85, r"$v'=(1,\sqrt{3})^T$", color="#c14b23", fontsize=13)
    ax.text(1.28, -0.32, r"$v=(2,0)^T$", color="#196ba1", fontsize=13)
    ax.text(-0.17, 1.8, r"$\sqrt{3}$", ha="right", color="#c14b23")
    ax.text(2.34, -0.16, "$x$", ha="center")
    ax.text(-0.15, 2.24, "$y$", ha="right")
    ax.set(xlim=(-2.25, 2.6), ylim=(-2.25, 2.4), aspect="equal")
    ax.set_xticks([-2, -1, 0, 1, 2])
    ax.set_yticks([-2, -1, 0, 1, 2])
    ax.tick_params(length=0)
    for spine in ax.spines.values():
        spine.set_visible(False)
    ax.set_title("Active counterclockwise rotation", fontsize=17, pad=14)
    fig.text(0.5, 0.02, "Column vectors · fixed Cartesian axes · length stays 2",
             ha="center", fontsize=11)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=150,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()