codexpythagorean-square-areas.py
pythagorean-square-areas.py
"""Draw the exact square areas on a 3-4-5 right triangle; output in cwd.
Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np
def polygon_area(vertices):
x, y = vertices.T
return abs(np.dot(x, np.roll(y, -1)) - np.dot(y, np.roll(x, -1))) / 2
def main():
triangle = np.array([[0, 0], [4, 0], [0, 3]])
squares = [
(np.array([[0, 0], [0, 3], [-3, 3], [-3, 0]]), 9,
"#e5eefb", "#245b9b", (-1.5, 1.5), "3² = 9"),
(np.array([[0, 0], [4, 0], [4, -4], [0, -4]]), 16,
"#fff0d5", "#9b650c", (2, -2.2), "4² = 16"),
(np.array([[4, 0], [0, 3], [3, 7], [7, 4]]), 25,
"#e1f2e9", "#287551", (3.5, 3.5), "5² = 25"),
]
assert np.dot(triangle[1] - triangle[0], triangle[2] - triangle[0]) == 0
assert polygon_area(triangle) == 6
assert sorted(np.linalg.norm(np.roll(triangle, -1, axis=0) - triangle,
axis=1)) == [3, 4, 5]
for vertices, area, *_ in squares:
edges = np.roll(vertices, -1, axis=0) - vertices
assert polygon_area(vertices) == area
assert np.all(np.sum(edges * edges, axis=1) == area)
assert np.all(np.sum(edges * np.roll(edges, -1, axis=0), axis=1) == 0)
plt.rcParams.update({"font.size": 13, "figure.facecolor": "white"})
fig, ax = plt.subplots(figsize=(6.8, 7.0), facecolor="white")
fig.subplots_adjust(left=0.02, right=0.98, bottom=0.08, top=0.88)
ax.set_facecolor("white")
ax.set_aspect("equal")
for vertices, _, face, edge, center, label in squares:
ax.add_patch(Polygon(vertices, facecolor=face, edgecolor=edge, linewidth=2))
ax.text(*center, label, color=edge, ha="center", va="center",
fontsize=18, fontweight="bold")
ax.add_patch(Polygon(triangle, facecolor="#f5f5f5", edgecolor="#333333",
linewidth=2.2))
ax.plot([0.35, 0.35, 0], [0, 0.35, 0.35], color="#333333", linewidth=1.5)
ax.text(0.47, 0.29, "90°", fontsize=10)
ax.text(1.25, 0.93, "Triangle\narea = 6", ha="center", va="center", fontsize=11)
ax.text(-0.22, 1.7, "3", ha="right", color="#245b9b", fontsize=16,
bbox={"facecolor": "#e5eefb", "edgecolor": "none", "pad": 1})
ax.text(2.5, -0.18, "4", ha="center", va="top", color="#9b650c", fontsize=16)
ax.text(2.25, 1.85, "5", ha="center", color="#287551", fontsize=16,
bbox={"facecolor": "#e1f2e9", "edgecolor": "none", "pad": 1})
ax.set_xlim(-3.55, 7.55)
ax.set_ylim(-4.45, 7.45)
ax.axis("off")
fig.suptitle("Pythagorean theorem: square areas", fontsize=19, y=0.97)
fig.text(0.5, 0.92, "The marked right angle is an assumption, not a guess.",
ha="center", fontsize=11)
fig.text(0.5, 0.035, "Lengths: 3, 4, 5 • Square areas: 9 + 16 = 25",
ha="center", fontsize=13)
output = Path.cwd() / (Path(__file__).stem + ".png")
fig.savefig(output, dpi=140, facecolor="white", transparent=False)
plt.close(fig)
print(f"Saved {output.name}; right angle, side lengths and areas checked.")
if __name__ == "__main__":
main()