codexgaussian-curvature-four-surfaces.py
gaussian-curvature-four-surfaces.py
"""Original surface patches; run from _media to write the matching PNG.
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
All plotted coordinates use one length unit. Convention: S = -dN.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.lines import Line2D
def main():
plt.rcParams.update({"font.size": 11, "figure.facecolor": "white"})
fig = plt.figure(figsize=(9.6, 8.2), layout="constrained")
fig.suptitle("Gaussian curvature: four local surface patches", fontsize=17)
red, blue = "#ae283b", "#125da3"
t = np.linspace(-0.66, 0.66, 151)
q = np.linspace(-0.66, 0.66, 37)
u, v = np.meshgrid(q, q)
zero = np.zeros_like(t)
titles = [
"Plane · upward normal\n" + r"$k_1=k_2=0,\quad K=0$",
"Cylinder · outward normal · R = 1\n" + r"$k_{\rm circ}=-1,\quad k_{\rm axial}=0,\quad K=0$",
"Sphere · outward normal · R = 1\n" + r"$k_1=k_2=-1,\quad K=1$",
"Saddle · upward normal · at the origin\n" + r"$k_x=1,\quad k_y=-1,\quad K=-1$",
]
colors = ["#a7cfca", "#ecc283", "#acd0e8", "#c6b5d8"]
surfaces = [
(u, v, np.zeros_like(u)),
(np.sin(u), v, np.cos(u)),
(u, v, np.sqrt(1 - u**2 - v**2)),
(u, v, (u**2 - v**2) / 2),
]
curves = [
((t, zero, zero), (zero, t, zero)),
((np.sin(t), zero, np.cos(t)), (zero, t, np.ones_like(t))),
((t, zero, np.sqrt(1 - t**2)), (zero, t, np.sqrt(1 - t**2))),
((t, zero, t**2 / 2), (zero, t, -t**2 / 2)),
]
for i, (surface, pair) in enumerate(zip(surfaces, curves)):
ax = fig.add_subplot(2, 2, i + 1, projection="3d", computed_zorder=False)
ax.plot_surface(*surface, color=colors[i], alpha=0.84,
edgecolor="#ffffff", linewidth=0.3, rstride=2, cstride=2, zorder=1)
for curve, color in zip(pair, (red, blue)):
ax.plot(*curve, color=color, lw=3.0, zorder=5)
z0 = 1.0 if i in (1, 2) else 0.0
ax.scatter([0], [0], [z0], color="#20252b", s=30, depthshade=False, zorder=10)
ax.quiver(0, 0, z0, 0, 0, 0.5, color="#20252b",
arrow_length_ratio=0.22, linewidth=2, zorder=11)
ax.text(0.03, 0, z0 + 0.54, "N", fontsize=12, weight="bold")
ax.set(xlim=(-0.85, 0.85), ylim=(-0.85, 0.85),
zlim=(z0 - 0.8, z0 + 0.8))
ax.set_box_aspect((1, 1, 0.85))
ax.view_init(elev=27, azim=-53)
ax.set_title(titles[i], fontsize=11.5, pad=8)
ax.set_xlabel("x", labelpad=-3)
ax.set_ylabel("y (axial)" if i == 1 else "y", labelpad=-3)
ax.set_zlabel("z", labelpad=-3)
ax.set_xticks([-0.5, 0, 0.5])
ax.set_yticks([-0.5, 0, 0.5])
ax.set_zticks([z0 - 0.5, z0, z0 + 0.5])
ax.tick_params(labelsize=8, pad=0)
for axis in (ax.xaxis, ax.yaxis, ax.zaxis):
axis.pane.set_facecolor("white")
axis.pane.set_edgecolor("#d4d9de")
handles = [Line2D([0], [0], color=red, lw=3, label="First principal direction at the point"),
Line2D([0], [0], color=blue, lw=3, label="Second principal direction at the point")]
fig.legend(handles=handles, loc="outside lower center", ncol=2,
frameon=False, fontsize=10)
output = Path(__file__).with_suffix(".png").name
fig.savefig(output, dpi=125, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()