codexchaikin-corner-cutting-iterations.py
chaikin-corner-cutting-iterations.py
"""Original closed Chaikin illustration; write the matching PNG basename to CWD.
Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
Run from _media to create the repository's image asset.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def cut_closed(points):
"""Return two points per cyclic edge, using the entire old array."""
following = np.roll(points, -1, axis=0)
refined = np.empty((2 * len(points), points.shape[1]))
refined[0::2] = 0.75 * points + 0.25 * following
refined[1::2] = 0.25 * points + 0.75 * following
return refined
def close(points):
return np.vstack((points, points[0]))
def main():
rectangle = np.array([[0., 0.], [4., 0.], [4., 2.], [0., 2.]])
stages = {0: rectangle}
for step in range(1, 6):
stages[step] = cut_closed(stages[step - 1])
fig, axes = plt.subplots(2, 2, figsize=(9, 6.8), dpi=120)
fig.patch.set_facecolor("white")
for ax, step in zip(axes.flat, (0, 1, 2, 5)):
ax.set_facecolor("white")
old = close(rectangle)
current = close(stages[step])
ax.plot(old[:, 0], old[:, 1], "--", color="#999999", lw=1.5,
label="Original polygon / hull", zorder=1)
ax.plot(current[:, 0], current[:, 1], color="#17679b", lw=2,
label="Current polygon", zorder=2)
ax.scatter(stages[step][:, 0], stages[step][:, 1],
s=24 if step < 3 else 7, color="#17679b", zorder=3)
ax.set_title(f"Iteration {step}: {len(stages[step])} vertices", fontsize=12)
ax.set(xlim=(-0.45, 4.45), ylim=(-0.4, 2.4), aspect="equal")
ax.set_xticks(range(5))
ax.set_yticks([0, 1, 2])
ax.set_xlabel("x" if step >= 2 else "", labelpad=1)
ax.set_ylabel("y", labelpad=1)
ax.spines[["top", "right"]].set_visible(False)
ax.grid(color="#eeeeee", lw=0.6)
ax.set_axisbelow(True)
fig.suptitle("Chaikin corner cutting: replace each edge by its quarter points",
fontsize=14, y=0.98)
handles, labels = axes[0, 0].get_legend_handles_labels()
fig.legend(handles, labels, loc="lower center", ncol=2,
bbox_to_anchor=(0.5, 0.045), frameon=False, fontsize=10)
fig.text(0.5, 0.025, "Every panel is a finite polygon, including iteration 5.",
ha="center", fontsize=10)
fig.tight_layout(rect=(0.01, 0.11, 0.99, 0.94), h_pad=3.5, w_pad=2)
fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()