codex/lippmann-standing-wave-depth-fringes.py

lippmann-standing-wave-depth-fringes.py
"""Draw an illustrative Lippmann recording geometry and ideal electric fringes.

Python 3.14; NumPy 2.3.5; Matplotlib 3.10.7. Run from _media.
The ideal phase-reversing reflector is not a fit to mercury or an emulsion.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np


def main():
    plt.rcParams.update({'font.size': 11, 'axes.spines.top': False,
                         'axes.spines.right': False, 'figure.facecolor': 'white'})
    fig = plt.figure(figsize=(10, 7), layout='constrained')
    grid = fig.add_gridspec(3, 1, height_ratios=[1.25, .65, 1.6])
    ax = fig.add_subplot(grid[0])
    ax.set_xlim(-250, 1450)
    ax.set_ylim(-.15, 1.8)
    ax.axis('off')
    for x, width, face, label in [(-190, 190, '#b9c0c8', 'Mercury\nreflector'),
                                  (0, 1000, '#fff3cf', 'Sensitive layer'),
                                  (1000, 190, '#d8edf5', 'Glass')]:
        ax.add_patch(Rectangle((x, 0), width, 1.15, facecolor=face, edgecolor='#52606d'))
        ax.text(x + width/2, 1.28, label, ha='center', va='bottom')
    for y, start, end, color, label in [(.77, 1370, 40, '#b14b2d', 'Incident light'),
                                       (.30, 40, 1370, '#216c9e', 'Reflected light')]:
        ax.annotate('', (end, y), (start, y), arrowprops={'arrowstyle': '->', 'color': color, 'lw': 2.2})
        ax.text(550, y+.09, label, ha='center', color=color)
    ax.set_title('Recording: incident and returning light overlap inside the layer', loc='left', pad=20)
    ax = fig.add_subplot(grid[1])
    ax.axis('off')
    ax.text(.02, .68, 'Develop and dry', weight='bold', transform=ax.transAxes)
    ax.annotate('', (.36, .68), (.26, .68), xycoords='axes fraction', arrowprops={'arrowstyle': '->', 'lw': 1.8})
    ax.text(.39, .68, 'White-light readout: the recorded depth structure selects reflected colour',
            transform=ax.transAxes, va='center', fontsize=10)
    ax.text(.02, .10, 'The exposure reflector is distinct from the later recorded structure.',
            transform=ax.transAxes, color='#52606d', fontsize=10)
    ax = fig.add_subplot(grid[2])
    z = np.linspace(0, 1000, 2001)
    k = 2*np.pi*1.5/600
    exposure = 4*np.sin(k*z)**2
    ax.plot(z, exposure, color='#a5422b', lw=2)
    for maximum in np.arange(100, 1000, 200):
        ax.axvline(maximum, color='#d6a793', ls=':', lw=1)
    ax.annotate('', (100, 4.35), (300, 4.35), arrowprops={'arrowstyle': '<->', 'color': '#243746'})
    ax.text(200, 4.48, '200 nm', ha='center')
    ax.set(xlim=(0, 1000), ylim=(0, 5), xlabel='Depth z from reflector into layer (nm)',
           ylabel='Normalized electric\nexposure proxy')
    ax.set_yticks([0, 1, 2, 3, 4])
    ax.set_title(r'Chosen model: $\lambda_0=600$ nm, $n=1.5$, ideal $r=-1$', loc='left', fontsize=11)
    ax.grid(axis='y', alpha=.2)
    fig.savefig(Path(__file__).stem+'.png', dpi=120, facecolor='white')
    plt.close(fig)


if __name__ == '__main__':
    main()