codex/bessel-lens-conjugate-position-ray-geometry.py

bessel-lens-conjugate-position-ray-geometry.py
"""Draw ideal Bessel imaging geometry; Python 3.14, matplotlib 3.10.7, numpy 2.3.5.

Run from _media; writes a PNG with this script's basename to the working directory.
"""
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def dimension(ax, left, right, y, label, color="#454545"):
    ax.annotate("", (left, y), (right, y),
                arrowprops={"arrowstyle": "<->", "color": color, "lw": 1.2})
    ax.text((left + right) / 2, y + 0.08, label, ha="center", va="bottom",
            color=color, fontsize=11)


def main():
    plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
                         "axes.spines.right": False})
    fig, axes = plt.subplots(2, 1, figsize=(9.2, 6.9), facecolor="white")
    L, f, height = 45.0, 10.0, 0.5
    for ax, x in zip(axes, (15.0, 30.0)):
        v = L - x
        m = -v / x
        image_height = m * height
        assert np.isclose(1 / f, 1 / x + 1 / v)
        ax.axhline(0, color="#858585", lw=1)
        ax.plot([L, L], [-1.2, 1.8], color="#555555", lw=5, alpha=0.3)
        ax.annotate("", (0, height), (0, 0),
                    arrowprops={"arrowstyle": "-|>", "color": "#27773f", "lw": 2.5})
        ax.annotate("", (L, image_height), (L, 0),
                    arrowprops={"arrowstyle": "-|>", "color": "#27773f", "lw": 2.5})
        ax.annotate("", (x, 1.3), (x, -1.3),
                    arrowprops={"arrowstyle": "<->", "color": "#254c85", "lw": 2.3})
        other = L - x
        ax.plot([other, other], [-1.2, 1.3], ls="--", color="#aaaaaa", lw=1.4)
        # Parallel ray and undeviated central ray; coordinates meet at screen tip.
        ax.plot([0, x, L], [height, height, image_height], color="#ce671d", lw=1.8)
        ax.plot([0, x, L], [height, 0, image_height], color="#387da0", lw=1.8)
        for a, b, col in ((x, L, "#ce671d"), (0, L, "#387da0")):
            p = a + (b - a) * 0.55
            q = a + (b - a) * 0.65
            if a == x:
                y1 = height * (1 - (p - x) / f)
                y2 = height * (1 - (q - x) / f)
            else:
                y1, y2 = height * (1 - p / x), height * (1 - q / x)
            ax.annotate("", (q, y2), (p, y1),
                        arrowprops={"arrowstyle": "->", "color": col, "lw": 1.8})
        ax.scatter([x - f, x + f], [0, 0], color="#254c85", s=24, zorder=5)
        for pos, text in ((x - f, "$F$"), (x + f, "$F'$")):
            ax.text(pos, -0.18, text, ha="center", color="#254c85")
        ax.text(0, 0.65, "Fixed object", ha="center", color="#27773f")
        ax.text(L, 1.9, "Fixed screen", ha="center")
        ax.text(x, 1.48, f"Lens at x = {x:g} cm", ha="center", color="#254c85")
        ax.text(other, -1.43, "Alternative setting", ha="center", color="#777777", fontsize=10)
        ax.text(L + 1.2, image_height, f"Image\nm = {m:g}", va="center", color="#27773f")
        dimension(ax, 0, x, -1.88, f"u = {x:g} cm")
        dimension(ax, x, L, -1.88, f"v = {v:g} cm")
        dimension(ax, 15, 30, 2.12, "d = 15 cm", "#777777")
        ax.set_xlim(-4, 53)
        ax.set_ylim(-2.2, 2.55)
        ax.set_xticks([0, 15, 30, 45])
        ax.set_yticks([])
        ax.spines["left"].set_visible(False)
        ax.spines["bottom"].set_color("#bbbbbb")
        ax.set_xlabel("Position along optical axis (cm)")
    fig.suptitle("Bessel displacement: one lens, two conjugate settings", fontsize=16)
    fig.text(0.5, 0.025, "Stipulated model: L = 45 cm, f = 10 cm. Heights enlarged; not measured data.",
             ha="center", fontsize=10, color="#555555")
    fig.subplots_adjust(left=0.04, right=0.98, top=0.90, bottom=0.12, hspace=0.37)
    fig.savefig(Path(__file__).with_suffix(".png").name, dpi=130, facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()