figure.py
3.7 kB · python · 134 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("&", "&").replace("<", "<").replace(">", ">")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# GASKET9596HERE = os.path.dirname(os.path.abspath(__file__))97OUT = os.path.join(HERE, os.pardir, "figures")9899F = [(0, 0), (1, 0), (0, 1)]100LEVEL = 5101PAD = 18102CELL = 17103R = 4.0104105def points(n):106 pts = [(0, 0)]107 for _ in range(n):108 pts = [(2 * x + f[0], 2 * y + f[1]) for (x, y) in pts for f in F]109 return pts110111def svg():112 side = 2 ** LEVEL113 span = (side - 1) * CELL114 w = span + 2 * PAD115 rows = header(w, w)116 vis = 0117 for (x, y) in points(LEVEL):118 cx = PAD + x * CELL119 cy = PAD + span - y * CELL120 if math.gcd(x, y) == 1:121 vis += 1122 rows.append(circle(cx, cy, R, CONCEPT["gasket"]))123 else:124 rows.append('<circle cx="%.1f" cy="%.1f" r="%.1f" fill="none" stroke="%s" stroke-width="%.1f"/>' % (cx, cy, R, MUTED, STROKE))125 return rows, vis126127def main():128 os.makedirs(OUT, exist_ok=True)129 rows, vis = svg()130 save(rows, os.path.join(OUT, "gasket.svg"))131 print("gasket.svg: level %d, %d points, %d visible" % (LEVEL, 3 ** LEVEL, vis))132133if __name__ == "__main__":134 main()