makefig.py
2.2 kB · python · 81 lines
1import math2from pathlib import Path34import numpy as np5from PIL import Image67WIDTH = 8808HEIGHT = 7609SCALE = 36.010N = 301112SURF = (255, 255, 255)13BLUE = (0, 140, 255)14GRAY = (198, 198, 201)15INK = (0, 0, 0)1617OUT = Path("files/figures/bases-fig.png")181920def points():21 cx = WIDTH / 2.022 cy = HEIGHT / 2.023 root3 = math.sqrt(3.0)24 out = []25 for a in range(-N, N + 1):26 for b in range(-N, N + 1):27 x = cx + SCALE * (a - b / 2.0)28 y = cy - SCALE * (b * root3 / 2.0)29 if -8.0 <= x <= WIDTH + 8.0 and -8.0 <= y <= HEIGHT + 8.0:30 out.append((a, b, x, y))31 return out323334def disc(img, cx, cy, r, colour):35 x0 = max(int(cx - r - 2.0), 0)36 x1 = min(int(cx + r + 2.0) + 1, WIDTH)37 y0 = max(int(cy - r - 2.0), 0)38 y1 = min(int(cy + r + 2.0) + 1, HEIGHT)39 if x0 >= x1 or y0 >= y1:40 return41 xs = np.arange(x0, x1, dtype=np.float64) + 0.5 - cx42 ys = np.arange(y0, y1, dtype=np.float64) + 0.5 - cy43 d = np.hypot(xs[None, :], ys[:, None])44 alpha = np.clip(r + 0.5 - d, 0.0, 1.0)45 mask = alpha > 0.046 if not mask.any():47 return48 patch = img[y0:y1, x0:x1]49 tint = np.array(colour, dtype=np.float64)50 blended = np.rint(patch + (tint - patch) * alpha[:, :, None])51 img[y0:y1, x0:x1] = np.where(mask[:, :, None], blended, patch)525354def render():55 pts = points()56 img = np.empty((HEIGHT, WIDTH, 3), dtype=np.float64)57 img[:, :] = SURF58 for a, b, x, y in pts:59 if (a, b) != (0, 0) and math.gcd(a, b) != 1:60 disc(img, x, y, 2.6, GRAY)61 for a, b, x, y in pts:62 if math.gcd(a, b) == 1:63 disc(img, x, y, 5.0, BLUE)64 cx = WIDTH / 2.065 cy = HEIGHT / 2.066 disc(img, cx, cy, 6.0, INK)67 disc(img, cx, cy, 3.4, SURF)68 seen = sum(1 for a, b, _, _ in pts if math.gcd(a, b) == 1)69 total = sum(1 for a, b, _, _ in pts if (a, b) != (0, 0))70 return img.astype(np.uint8), seen, total717273def main():74 img, seen, total = render()75 OUT.parent.mkdir(parents=True, exist_ok=True)76 Image.fromarray(img, mode="RGB").save(OUT, optimize=True)77 print(f"wrote {OUT}: {WIDTH}x{HEIGHT} RGB")78 print(f"in-frame points visible = {seen}/{total} = {seen / total:.4f}")798081main()