codexopen-and-closed-sets-on-line.py
open-and-closed-sets-on-line.py
"""Draw membership for four open/closed cases in the usual real line.
Tested with Python 3.14, NumPy 2.3.5, and Matplotlib 3.10.7.
Run via make _media/open-and-closed-sets-on-line.png; save to cwd.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
BLUE = "#176b9c"
GREY = "#bdc6ce"
def number_line(ax, y):
ax.annotate("", xy=(1.42, y), xytext=(-0.42, y), zorder=1,
arrowprops={"arrowstyle": "<->", "color": GREY, "lw": 1.4})
for x in (0, 1):
ax.plot([x, x], [y - 0.045, y + 0.045], color=GREY, lw=1)
ax.text(x, y - 0.21, str(x), ha="center", va="top", fontsize=11)
def interval(ax, y, closed_left, closed_right):
ax.plot([0, 1], [y, y], color=BLUE, lw=5, solid_capstyle="butt")
for x, included in ((0, closed_left), (1, closed_right)):
ax.scatter([x], [y], s=95, facecolors=BLUE if included else "white",
edgecolors=BLUE, linewidths=2, zorder=4)
def main():
fig, ax = plt.subplots(figsize=(9.6, 5.7), facecolor="white")
fig.subplots_adjust(left=0.04, right=0.98, top=0.88, bottom=0.09)
ax.set(xlim=(-1.52, 1.65), ylim=(-0.46, 4.92))
ax.axis("off")
fig.suptitle("Open and closed: four possibilities", fontsize=19, y=0.97)
ax.text(0.5, 4.77, "Ambient space: the real line with its usual topology",
ha="center", fontsize=12)
rows = np.array([4.08, 3.06, 2.04, 0.98])
for y, label, expression, endpoints in zip(
rows,
("Open only", "Closed only", "Neither", "Both (clopen)"),
(r"$(0,1)$", r"$[0,1]$", r"$[0,1)$", r"$\varnothing$ and $\mathbb{R}$"),
((False, False), (True, True), (True, False), None),
):
ax.text(-1.45, y + 0.07, label, fontsize=14, va="bottom")
ax.text(-1.45, y - 0.22, expression, fontsize=14, va="top")
if endpoints is not None:
number_line(ax, y)
interval(ax, y, *endpoints)
else:
number_line(ax, y + 0.22)
ax.text(1.49, y + 0.22, r"$\varnothing$", fontsize=14, va="center")
ax.text(0.5, y + 0.44, "No points included", ha="center", fontsize=11)
number_line(ax, y - 0.40)
ax.annotate("", xy=(1.42, y - 0.40), xytext=(-0.42, y - 0.40),
arrowprops={"arrowstyle": "<->", "color": BLUE,
"lw": 4, "mutation_scale": 17})
ax.text(1.49, y - 0.40, r"$\mathbb{R}$", fontsize=14, va="center")
fig.text(0.5, 0.025, "Filled circle: included endpoint | Hollow circle: excluded endpoint",
ha="center", fontsize=11)
fig.savefig(Path.cwd() / f"{Path(__file__).stem}.png", dpi=125,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()