codex/relativity-light-clock.py

relativity-light-clock.py
"""Draw relativity-light-clock.png in cwd with an opaque white background.

Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
Coordinates are spatial paths over time, not a simultaneous photograph.
"""

import matplotlib

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


BLUE = "#1766a6"
ORANGE = "#bd580b"
GREY = "#aeb8bf"


def arrow(ax, start, end, color):
    ax.annotate("", xy=end, xytext=start,
                arrowprops={"arrowstyle": "->", "color": color,
                            "lw": 2.5, "shrinkA": 3, "shrinkB": 3})


def mirror(ax, x, y, active):
    ax.plot([x - 0.16, x + 0.16], [y, y], lw=5,
            color="#33424c" if active else "#dce1e5", solid_capstyle="butt")


def main():
    beta, h = 0.6, 1.0
    gamma = 1 / np.sqrt(1 - beta**2)
    half_ct = gamma * h
    drift = beta * half_ct
    spatial = np.array([[0, 0], [drift, h], [2 * drift, 0]])
    ct = np.array([0, half_ct, 2 * half_ct])
    np.testing.assert_allclose([gamma, half_ct, drift], [1.25, 1.25, 0.75])
    np.testing.assert_allclose(np.linalg.norm(np.diff(spatial, axis=0), axis=1),
                               np.diff(ct))
    np.testing.assert_allclose(spatial[:, 0], beta * ct)
    np.testing.assert_allclose(gamma * (ct - beta * spatial[:, 0]), [0, h, 2 * h])
    np.testing.assert_allclose(-(ct[-1] - ct[0])**2 + (spatial[-1, 0])**2,
                               -(2 * h)**2)

    plt.rcParams.update({"font.size": 12, "axes.titlesize": 15})
    fig, axes = plt.subplots(1, 2, figsize=(10.8, 5.9), facecolor="white")
    fig.subplots_adjust(left=0.065, right=0.98, bottom=0.33, top=0.80, wspace=0.24)
    fig.suptitle("A light clock moving at 0.6c: the same pulse, two spatial paths",
                 fontsize=17, y=0.965)
    for ax in axes:
        ax.set_facecolor("white")
        ax.set_xlim(-0.4, 1.95)
        ax.set_ylim(-0.2, 1.35)
        ax.set_aspect("equal")
        ax.set_xticks([0, 0.75, 1.5])
        ax.set_yticks([0, 1])
        ax.spines[["top", "right"]].set_visible(False)
        ax.grid(alpha=0.15)

    rest, lab = axes
    rest.set_title("Clock rest frame S′")
    rest.set_xlabel("x′ (m)")
    rest.set_ylabel("y′ (m)")
    mirror(rest, 0, 0, True)
    mirror(rest, 0, h, True)
    rest.annotate("", xy=(0, h), xytext=(0, 0),
                  arrowprops={"arrowstyle": "<->", "color": BLUE, "lw": 2.5,
                              "shrinkA": 4, "shrinkB": 4})
    rest.text(0.22, 0.51, "Out: 1 m\nBack: 1 m", color=BLUE, va="center")
    rest.text(0.22, 1.04, "B: reflect", va="center")
    rest.text(0.22, 0.04, "A: emit; C: return", va="center")
    rest.text(1.24, 0.97, "Mirror gap\nh = 1 m", ha="center", va="center")

    lab.set_title("Laboratory frame S")
    lab.set_xlabel("x (m)")
    lab.set_ylabel("y (m)")
    for index, (x, y) in enumerate(spatial):
        mirror(lab, x, 0, index != 1)
        mirror(lab, x, h, index == 1)
    arrow(lab, spatial[0], spatial[1], ORANGE)
    arrow(lab, spatial[1], spatial[2], ORANGE)
    lab.plot([0, drift, drift], [0, 0, h], "--", color=GREY, lw=1.5)
    lab.plot([drift - 0.09, drift - 0.09, drift], [0, 0.09, 0.09], color=GREY)
    lab.text(0.17, 0.69, "1.25 m", color=ORANGE, rotation=53, ha="center")
    lab.text(1.32, 0.72, "1.25 m", color=ORANGE, rotation=-53, ha="center")
    lab.text(drift / 2, -0.14, "0.75 m drift", fontsize=11, ha="center")
    lab.text(drift + 0.10, 0.43, "1 m", fontsize=11, color="#65727c")
    for (x, y), label, offset in zip(spatial, "ABC", [(-16, 14), (0, 12), (17, 14)]):
        lab.annotate(label, (x, y), xytext=offset, textcoords="offset points",
                     ha="center", fontsize=13, fontweight="bold")

    fig.text(0.27, 0.16, "Return tick: Δτ = 2 m / c ≈ 6.67 ns", ha="center", color=BLUE)
    fig.text(0.76, 0.16, "Return tick: Δt = 2.5 m / c ≈ 8.34 ns", ha="center", color=ORANGE)
    fig.text(0.5, 0.105, "γ = 1.25: longer path at the same light speed → longer laboratory tick",
             ha="center", fontsize=13)
    fig.text(0.5, 0.045, "Spatial paths over a tick; laboratory mirror marks are snapshots at A, B and C.",
             ha="center", fontsize=11, color="#555555")
    fig.savefig("relativity-light-clock.png", dpi=110, facecolor="white", transparent=False)
    plt.close(fig)
    print("Verified gamma=1.25; each null leg has length 1.25 m and drift 0.75 m;")
    print("return tick has ct=2.5 m, c*tau=2 m and invariant interval -4 m^2.")


if __name__ == "__main__":
    main()