"""f05_vdw_molecular -- two-panel schematic.
Left: excluded volume between two hard spheres of diameter sigma; the
excluded-volume sphere of radius sigma shown in teal; annotation
b = 2 pi N_A sigma^3 / 3 (SPEC.md vdW co-volume relation).
Right: central molecule with amber attraction shell and inward arrows from
neighbours; annotation "mean-field attraction -> -a/V^2".
Pure schematic built from matplotlib primitives.

Note: the canvas uses unit data coordinates (blank()) on a non-square
figure (W x H inches), so a patch with equal x/y radius in data units would
render as an ellipse. ASPECT = W/H corrects for this: circ() draws true
circles by scaling the y-radius by ASPECT.
"""
import sys
sys.path.insert(0, "/home/claude/eos_lecture/code")
from eosstyle import *
from matplotlib.patches import Ellipse, FancyArrowPatch
import numpy as np

W, H = 10.0, 5.0
ASPECT = W / H
fig, ax = blank(W, H)

# The picture is shown 8.6 in wide on the slide (scale 0.86), so the point
# sizes below are chosen to land above the 10.5 pt floor as displayed.
FS_PANEL = 15.0   # panel headings
FS_EQ    = 15.0   # boxed result
FS_NOTE  = 13.0   # leader-line annotations
FS_CAP   = 12.5   # explanatory caption
FS_MOL   = 12.0   # numbers inside the molecules


def circ(cx, cy, r, **kw):
    e = Ellipse((cx, cy), width=2*r, height=2*r*ASPECT, **kw)
    ax.add_patch(e)
    return e


# divider between panels
ax.plot([0.5, 0.5], [0.06, 0.90], color=MIST, lw=1.0, zorder=1)

# ---------------------------------------------------------------- LEFT PANEL
ax.text(0.25, 0.96, "Excluded volume", ha="center", va="top", fontsize=FS_PANEL,
        color=NAVY, weight="bold")

cx1, cy1 = 0.16, 0.46
cx2, cy2 = 0.30, 0.46
r_mol = 0.045   # molecular radius sigma/2 (drawing units, x-scale)

# two hard spheres (graphite fill, solid)
circ(cx1, cy1, r_mol, facecolor="#DCE2E5", edgecolor=GRAPHITE, lw=1.4, zorder=4)
circ(cx2, cy2, r_mol, facecolor="#DCE2E5", edgecolor=GRAPHITE, lw=1.4, zorder=4)
ax.text(cx1, cy1, "1", ha="center", va="center", fontsize=FS_MOL, color=INK, zorder=5)
ax.text(cx2, cy2, "2", ha="center", va="center", fontsize=FS_MOL, color=INK, zorder=5)

# excluded-volume sphere: radius sigma (= 2 * r_mol) centred on molecule 1,
# the locus forbidden to the centre of molecule 2
circ(cx1, cy1, 2*r_mol, facecolor=TEAL, edgecolor=TEAL, alpha=0.18, lw=1.6, zorder=2)
circ(cx1, cy1, 2*r_mol, facecolor="none", edgecolor=TEAL, lw=1.6, linestyle="--", zorder=3)

# sigma dimension line between centres
ax.annotate("", xy=(cx2, cy1-0.115), xytext=(cx1, cy1-0.115),
            arrowprops=dict(arrowstyle="<->", color=GRAPHITE, lw=1.3))
ax.text((cx1+cx2)/2, cy1-0.145, r"$\sigma$", ha="center", va="top", fontsize=FS_NOTE,
        color=INK)

# annotation moved into the empty band above the spheres (it used to run into
# the vertical rule that divides the two panels) and joined to the dashed
# excluded-volume circle by a short leader
ax.text(0.235, 0.775, "excluded-volume sphere\n(radius $\\sigma$, volume $\\propto\\sigma^3$)",
        ha="center", va="center", fontsize=FS_NOTE, color=TEAL, linespacing=1.3)
ax.annotate("", xy=(cx1 + 2*r_mol*np.cos(np.deg2rad(72)),
                    cy1 + 2*r_mol*ASPECT*np.sin(np.deg2rad(72))),
            xytext=(0.215, 0.718),
            arrowprops=dict(arrowstyle="-", color=TEAL, lw=1.0, alpha=0.8))

ax.text(0.23, 0.205, r"$b = \dfrac{2\pi}{3}\,N_A\,\sigma^3$",
        ha="center", va="center", fontsize=FS_EQ, color=INK,
        bbox=dict(boxstyle="round,pad=0.35", fc="#F4F6F7", ec=GRAPHITE, lw=1.1))
ax.text(0.23, 0.072, "each molecule denies its neighbours\naccess to a sphere of volume $\\propto\\sigma^3$",
        ha="center", va="center", fontsize=FS_CAP, color=SLATE, linespacing=1.3)

# ---------------------------------------------------------------- RIGHT PANEL
ax.text(0.75, 0.96, "Mean-field attraction", ha="center", va="top", fontsize=FS_PANEL,
        color=NAVY, weight="bold")

ccx, ccy = 0.75, 0.52
r_c = 0.028
r_shell = 0.085

circ(ccx, ccy, r_shell, facecolor=AMBER, edgecolor=AMBER, alpha=0.16, zorder=2)
circ(ccx, ccy, r_shell, facecolor="none", edgecolor=AMBER, lw=1.4, linestyle="--", zorder=3)
circ(ccx, ccy, r_c, facecolor=NAVY, edgecolor=GRAPHITE, lw=1.3, zorder=5)
ax.text(ccx, ccy, "0", ha="center", va="center", fontsize=FS_MOL, color="white", zorder=6)

n_nb = 6
rad = 0.145
r_nb = r_c*0.8
angles = np.deg2rad(np.array([15, 75, 135, 195, 255, 315]))
for ang in angles:
    nx, ny = ccx + rad*np.cos(ang), ccy + rad*ASPECT*np.sin(ang)
    circ(nx, ny, r_nb, facecolor="#DCE2E5", edgecolor=GRAPHITE, lw=1.1, zorder=4)
    # arrow from neighbour inward to the shell edge
    ex, ey = ccx + r_shell*np.cos(ang), ccy + r_shell*ASPECT*np.sin(ang)
    gap = r_nb + 0.012
    sx, sy = nx - gap*np.cos(ang), ny - gap*ASPECT*np.sin(ang)
    arr = FancyArrowPatch((sx, sy), (ex, ey), arrowstyle="-|>", mutation_scale=9,
                           linewidth=1.3, color=RUST, zorder=3, shrinkA=0, shrinkB=0)
    ax.add_patch(arr)

# shell label with leader line (mirrors the left-panel labelling style)
lbl_ang = np.deg2rad(50)
lex, ley = ccx + r_shell*np.cos(lbl_ang), ccy + r_shell*ASPECT*np.sin(lbl_ang)
ax.text(0.985, 0.795, "attraction shell\n(range $\\sim\\sigma$ to\nfew $\\sigma$)",
        ha="right", va="center", fontsize=FS_CAP, color=AMBER, linespacing=1.25)
ax.annotate("", xy=(lex, ley), xytext=(0.864, 0.762),
            arrowprops=dict(arrowstyle="-", color=AMBER, lw=1.0, alpha=0.85))

ax.text(0.75, 0.085, r"mean-field attraction $\;\rightarrow\; -\dfrac{a}{V^2}$",
        ha="center", va="center", fontsize=FS_EQ, color=INK,
        bbox=dict(boxstyle="round,pad=0.35", fc="#F4F6F7", ec=GRAPHITE, lw=1.1))

save(fig, "f05_vdw_molecular")
