figure.py

4.1 kB · python · 138 lines

1import math2import os34# STYLE56FONT = "Helvetica,Arial,sans-serif"7# PALETTE8INK = "#000000"9MUTED = "#8e8e93"10PALE = "#bababf"11PAPER = "#ffffff"12CONCEPT = {13    "gasket": "#00cad8",14    "carpet": "#008cff",15    "sponge": "#6768fa",16    "prime": "#ff325a",17    "bound": "#ff8f2c",18    "window": "#ff3d40",19    "control": "#8e8e93",20}21PALETTE = {22    "black": "#000000",23    "white": "#ffffff",24    "red": "#ff3d40",25    "red-light": "#ff9d95",26    "red-dark": "#a80016",27    "orange": "#ff8f2c",28    "orange-light": "#ffc093",29    "orange-dark": "#a25400",30    "yellow": "#ffd100",31    "yellow-light": "#ffe591",32    "yellow-dark": "#9e8100",33    "green": "#32cc58",34    "green-light": "#5eee79",35    "green-dark": "#007f2c",36    "mint": "#00d1bb",37    "mint-light": "#48efd8",38    "mint-dark": "#008173",39    "teal": "#00cad8",40    "teal-light": "#48e9f7",41    "teal-dark": "#007c85",42    "cyan": "#1ec9f3",43    "cyan-light": "#86e2ff",44    "cyan-dark": "#007c98",45    "blue": "#008cff",46    "blue-light": "#84bdff",47    "blue-dark": "#00559f",48    "indigo": "#6768fa",49    "indigo-light": "#9ea9ff",50    "indigo-dark": "#3c2abc",51    "purple": "#d332e9",52    "purple-light": "#f08aff",53    "purple-dark": "#870097",54    "pink": "#ff325a",55    "pink-light": "#ff9a9f",56    "pink-dark": "#a50030",57    "brown": "#b18462",58    "brown-light": "#dfaf8c",59    "brown-dark": "#754c2b",60    "gray": "#8e8e93",61    "gray-light": "#bababf",62    "gray-dark": "#56565a",63}64# PALETTE END65STROKE = 1.266THIN = 0.667MARGIN = 366869def esc(s):70    return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")7172def header(width, height):73    return ['<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 %d %d" width="%d" height="%d">' % (width, height, width, height), '<rect width="%d" height="%d" fill="%s"/>' % (width, height, PAPER)]7475def text(x, y, s, size=11, fill=INK, anchor="start", weight=None):76    w = ' font-weight="%s"' % weight if weight else ""77    return '<text x="%.1f" y="%.1f" font-family="%s" font-size="%d" fill="%s" text-anchor="%s"%s>%s</text>' % (x, y, FONT, size, fill, anchor, w, esc(s))7879def line(x1, y1, x2, y2, stroke=INK, width=STROKE):80    return '<line x1="%.1f" y1="%.1f" x2="%.1f" y2="%.1f" stroke="%s" stroke-width="%.1f"/>' % (x1, y1, x2, y2, stroke, width)8182def rect(x, y, w, h, fill, opacity=1.0):83    return '<rect x="%.1f" y="%.1f" width="%.1f" height="%.1f" fill="%s" opacity="%.2f"/>' % (x, y, w, h, fill, opacity)8485def circle(x, y, r, fill):86    return '<circle cx="%.1f" cy="%.1f" r="%.1f" fill="%s"/>' % (x, y, r, fill)8788def save(parts, path):89    with open(path, "w") as handle:90        handle.write("\n".join(parts + ["</svg>"]) + "\n")9192# STYLE END9394# CARPETS9596def r_carpet(m, n):97    d = math.gcd(m, n)98    top = (d * d - 1) * (2 * (m - 1) * (n - 1) + d * d - 1)99    bot = (m - 1) * (n - 1) * math.sqrt((3 * m - 1) * (m + 1) * (3 * n - 1) * (n + 1))100    return top / bot101102def is_prime(n):103    if n < 2:104        return False105    k = 2106    while k * k <= n:107        if n % k == 0:108            return False109        k += 1110    return True111112def carpet_svg(lo, hi, cell, pad):113    vals = list(range(lo, hi + 1, 2))114    k = len(vals)115    w = pad + k * cell + 10116    h = pad + k * cell + 10117    out = header(w, h)118    for a, m in enumerate(vals):119        for b, n in enumerate(vals):120            out.append(rect(pad + a * cell, pad + b * cell, cell, cell, CONCEPT["carpet"], r_carpet(m, n) ** 0.45))121    out.append('<rect x="%d" y="%d" width="%d" height="%d" fill="none" stroke="%s" stroke-width="%.1f"/>' % (pad, pad, k * cell, k * cell, MUTED, STROKE))122    for a, m in enumerate(vals):123        if not is_prime(m):124            continue125        x = pad + a * cell + cell / 2.0126        out.append(text(x, pad - 6, str(m), size=9, anchor="middle"))127        out.append(text(pad - 6, x + 3.2, str(m), size=9, anchor="end"))128    return out129130def main():131    here = os.path.dirname(os.path.abspath(__file__))132    out = os.path.join(here, "..", "figures")133    os.makedirs(out, exist_ok=True)134    save(carpet_svg(3, 63, 16, 40), os.path.join(out, "carpet.svg"))135    print("wrote figures/carpet.svg")136137if __name__ == "__main__":138    main()