codexprincipal-and-mean-curvature-comparison.py
principal-and-mean-curvature-comparison.py
"""Generate signed curvature comparison; Python 3.14, matplotlib 3.10.7, numpy 2.3.5.
Run from _media; the opaque PNG is written to the current directory.
"""
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, axes = plt.subplots(1, 3, figsize=(10.8, 4.7), sharey=True)
models = [
("Outward sphere", (-1, -1), r"$\ell=R$", r"$K\ell^2=1$"),
("Outward cylinder", (-1, 0), r"$\ell=R$", r"$K\ell^2=0$"),
("Catenoid waist", (1, -1), r"$\ell=a$", r"$K\ell^2=-1$"),
]
colors = ["#c44c28", "#2278aa", "#7151a1"]
for ax, (name, ks, scale, gaussian) in zip(axes, models):
values = np.array([*ks, np.mean(ks)])
ax.bar([0, 1, 2], values, color=colors, width=0.52)
ax.scatter([0, 1, 2], values, c=colors, s=55, zorder=4)
ax.axhline(0, color="#555555", lw=1)
for x, value in enumerate(values):
offset = 0.10 if value >= 0 else -0.16
ax.text(x, value + offset, f"{value:g}", ha="center", va="center")
ax.set_xticks([0, 1, 2], [r"$k_1\ell$", r"$k_2\ell$", r"$H\ell$"])
ax.set_ylim(-1.42, 1.40)
ax.set_yticks([-1, -0.5, 0, 0.5, 1])
ax.set_title(name + "\n" + scale, pad=12)
ax.text(0.5, -0.36, gaussian + "\n" + rf"$2H\ell={2 * values[2]:g}$", transform=ax.transAxes, ha="center")
ax.spines[["top", "right"]].set_visible(False)
ax.grid(axis="y", alpha=0.15)
ax.set_axisbelow(True)
axes[0].set_ylabel("Signed curvature × length scale")
fig.suptitle(r"$S=-dN$ • $H=(k_1+k_2)/2$ • $K=k_1k_2$", y=0.98, fontsize=15)
fig.subplots_adjust(left=0.08, right=0.98, top=0.73, bottom=0.31, wspace=0.30)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=120, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()