codexrectangle-metric-versus-affine-symmetry.py
rectangle-metric-versus-affine-symmetry.py
"""Show one affine rectangle symmetry that does not preserve Euclidean length."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
points = np.array([[2., 1.], [-2., 1.], [-2., -1.], [2., -1.]])
transform = np.array([[0., -2.], [.5, 0.]])
image = points @ transform.T
fig, axes = plt.subplots(1, 2, figsize=(12.2, 5.3), dpi=100, layout="constrained")
fig.set_facecolor("white")
for ax, vertices, title, length in zip(axes, [points, image], ["Original side: length 4", "Image side: length 2"], [4, 2]):
outline = np.vstack([points, points[0]])
ax.plot(outline[:, 0], outline[:, 1], color="#647680", linewidth=2)
ax.scatter(vertices[:, 0], vertices[:, 1], color="#34495e", s=47, zorder=4)
# Track the same ordered edge, rather than coloring the whole object.
edge = vertices[:2]
ax.annotate("", xy=edge[1], xytext=edge[0], arrowprops={"arrowstyle": "->", "color": "#d17a22", "lw": 3})
for label, point in zip(["A", "B", "C", "D"], vertices):
offset = (9 if point[0] >= 0 else -19, 9 if point[1] >= 0 else -21)
ax.annotate(label, point, xytext=offset, textcoords="offset points", fontsize=12)
midpoint = edge.mean(axis=0)
offset = (0, 20) if length == 4 else (40, 0)
ax.annotate(f"Length {length}", midpoint, xytext=offset, textcoords="offset points", fontsize=11, color="#bd661b", ha="center", va="center")
ax.set(xlim=(-2.85, 2.85), ylim=(-1.95, 1.95), xlabel="x", ylabel="y", title=title)
ax.set_aspect("equal", adjustable="box")
ax.set_xticks(range(-2, 3)); ax.set_yticks(range(-1, 2)); ax.grid(alpha=.17)
ax.axhline(0, color="#aaaaaa", linewidth=.7);ax.axvline(0, color="#aaaaaa", linewidth=.7)
fig.suptitle("Affine symmetry T(x, y) = (−2y, x/2): same vertex set, different side length", fontsize=14)
fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()