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()