codexstereographic-projection-rays-and-axes.py
stereographic-projection-rays-and-axes.py
"""North-pole projection geometry; Python 3.14, Matplotlib 3.10, NumPy 2.3."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.size": 12, "figure.facecolor": "white"})
fig, (ax, eq) = plt.subplots(1, 2, figsize=(10.2, 4.7),
gridspec_kw={"width_ratios": [1.55, 1]})
theta = np.linspace(0, 2 * np.pi, 500)
ax.fill(np.cos(theta), np.sin(theta), color="#f3f6fa")
ax.plot(np.cos(theta), np.sin(theta), color="#65788c", lw=1.7)
ax.axhline(0, color="#394b59", lw=1.4)
ax.axvline(0, color="#9aa8b4", lw=0.8)
ax.annotate("", xy=(2.43, 0), xytext=(2.17, 0),
arrowprops={"arrowstyle": "->", "color": "#394b59"})
ax.annotate("", xy=(0, 1.35), xytext=(0, 1.12),
arrowprops={"arrowstyle": "->", "color": "#394b59"})
ax.text(2.4, -.16, "$X=x$")
ax.text(.08, 1.25, "$Z$")
ax.scatter([0, 0], [1, -1], c="#253746", s=35, zorder=5)
ax.text(-.6, 1.06, "$N$ (infinity)")
ax.text(-.63, -1.17, "$S$ ($z=0$)")
for x, color in [(0.5, "#2468b4"), (2.0, "#bc5d1b")]:
t = 2 / (1 + x*x)
X, Z = t*x, 1-t
ax.plot([0, x, X], [1, 0, Z], color=color, lw=2)
ax.scatter([x, X], [0, Z], color=color, s=42, zorder=6)
ax.text(.31, .08, "$z=1/2$", color="#2468b4")
ax.text(1.82, -.21, "$z=2$", color="#bc5d1b")
ax.annotate("$(4/5,0,-3/5)$", xy=(.8, -.6), xytext=(1.01, -.8),
color="#2468b4", arrowprops={"arrowstyle": "-", "color": "#2468b4"})
ax.annotate("$(4/5,0,3/5)$", xy=(.8, .6), xytext=(1.03, .82),
color="#bc5d1b", arrowprops={"arrowstyle": "-", "color": "#bc5d1b"})
ax.text(-1.23, -.22, "$Z=0$ plane", fontsize=11)
ax.set(xlim=(-1.35, 2.55), ylim=(-1.3, 1.4), aspect="equal")
ax.set_title("Real-axis section ($Y=0$)", pad=20)
ax.axis("off")
eq.plot(np.cos(theta), np.sin(theta), color="#65788c", lw=1.7)
for end in [(1.38, 0), (0, 1.38)]:
eq.annotate("", xy=end, xytext=(-end[0], -end[1]),
arrowprops={"arrowstyle": "->", "color": "#394b59"})
eq.scatter([1, 0, -1, 0], [0, 1, 0, -1], c="#2468b4", s=36, zorder=5)
for xy, label, offset in [((1, 0), "$1$", (8, 9)), ((0, 1), "$i$", (8, 5)),
((-1, 0), "$-1$", (-25, 9)), ((0, -1), "$-i$", (8, -12))]:
eq.annotate(label, xy, xytext=offset, textcoords="offset points")
eq.text(1.27, -.23, "$X=x$")
eq.text(.13, 1.31, "$Y=y$")
eq.text(-1.25, -1.63, "Equator: $Z=0$, $|z|=1$\nPositive $Y$ means positive Im$(z)$", fontsize=11)
eq.set(xlim=(-1.45, 1.55), ylim=(-1.72, 1.55), aspect="equal")
eq.set_title("Equator viewed from positive $Z$", pad=20)
eq.axis("off")
fig.suptitle("Stereographic projection from the north pole", fontsize=16, y=.98)
fig.subplots_adjust(left=.035, right=.98, top=.83, bottom=.08, wspace=.19)
fig.savefig(Path(__file__).stem + ".png", dpi=120, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()