figure.py
4.8 kB · python · 149 lines
1import os23# STYLE45FONT = "Helvetica,Arial,sans-serif"6# PALETTE7INK = "#000000"8MUTED = "#8e8e93"9PALE = "#bababf"10PAPER = "#ffffff"11CONCEPT = {12 "gasket": "#00cad8",13 "carpet": "#008cff",14 "sponge": "#6768fa",15 "prime": "#ff325a",16 "bound": "#ff8f2c",17 "window": "#ff3d40",18 "control": "#8e8e93",19}20PALETTE = {21 "black": "#000000",22 "white": "#ffffff",23 "red": "#ff3d40",24 "red-light": "#ff9d95",25 "red-dark": "#a80016",26 "orange": "#ff8f2c",27 "orange-light": "#ffc093",28 "orange-dark": "#a25400",29 "yellow": "#ffd100",30 "yellow-light": "#ffe591",31 "yellow-dark": "#9e8100",32 "green": "#32cc58",33 "green-light": "#5eee79",34 "green-dark": "#007f2c",35 "mint": "#00d1bb",36 "mint-light": "#48efd8",37 "mint-dark": "#008173",38 "teal": "#00cad8",39 "teal-light": "#48e9f7",40 "teal-dark": "#007c85",41 "cyan": "#1ec9f3",42 "cyan-light": "#86e2ff",43 "cyan-dark": "#007c98",44 "blue": "#008cff",45 "blue-light": "#84bdff",46 "blue-dark": "#00559f",47 "indigo": "#6768fa",48 "indigo-light": "#9ea9ff",49 "indigo-dark": "#3c2abc",50 "purple": "#d332e9",51 "purple-light": "#f08aff",52 "purple-dark": "#870097",53 "pink": "#ff325a",54 "pink-light": "#ff9a9f",55 "pink-dark": "#a50030",56 "brown": "#b18462",57 "brown-light": "#dfaf8c",58 "brown-dark": "#754c2b",59 "gray": "#8e8e93",60 "gray-light": "#bababf",61 "gray-dark": "#56565a",62}63# PALETTE END64STROKE = 1.265THIN = 0.666MARGIN = 366768def esc(s):69 return s.replace("&", "&").replace("<", "<").replace(">", ">")7071def header(width, height):72 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)]7374def text(x, y, s, size=11, fill=INK, anchor="start", weight=None):75 w = ' font-weight="%s"' % weight if weight else ""76 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))7778def line(x1, y1, x2, y2, stroke=INK, width=STROKE):79 return '<line x1="%.1f" y1="%.1f" x2="%.1f" y2="%.1f" stroke="%s" stroke-width="%.1f"/>' % (x1, y1, x2, y2, stroke, width)8081def rect(x, y, w, h, fill, opacity=1.0):82 return '<rect x="%.1f" y="%.1f" width="%.1f" height="%.1f" fill="%s" opacity="%.2f"/>' % (x, y, w, h, fill, opacity)8384def circle(x, y, r, fill):85 return '<circle cx="%.1f" cy="%.1f" r="%.1f" fill="%s"/>' % (x, y, r, fill)8687def save(parts, path):88 with open(path, "w") as handle:89 handle.write("\n".join(parts + ["</svg>"]) + "\n")9091# STYLE END9293# BLINK9495DARK = CONCEPT["sponge"]96LIGHT = PALETTE["indigo-light"]9798def corners():99 return [(a, b, c) for a in (0, 1) for b in (0, 1) for c in (0, 1)]100101def design(code):102 return {p for p in corners() if code >> (4 * p[0] + 2 * p[1] + p[2]) & 1}103104def cells(n, code):105 D = design(code)106 ink, paper = [], []107 for x in range(4 * n):108 for y in range(4 * n):109 z = 6 * n - 2 - x - y110 if z < 0 or z >= 4 * n or z % 2:111 continue112 e = ((x // 4) % 2, (y // 4) % 2, (z // 4) % 2)113 (ink if e in D else paper).append(((x - y) // 2, (x + y) // 2))114 return ink, paper115116def panel(n, code, ox, oy, s):117 ink, paper = cells(n, code)118 us = [u for u, v in ink + paper]119 vs = [v for u, v in ink + paper]120 umin, vmax = min(us), max(vs)121 out = []122 for group, fill in ((paper, LIGHT), (ink, DARK)):123 for u, v in group:124 out.append(rect(ox + (u - umin) * s, oy + (vmax - v) * s, s, s, fill))125 w = (max(us) - umin + 1) * s126 hgt = (vmax - min(vs) + 1) * s127 out.append('<rect x="%.1f" y="%.1f" width="%.1f" height="%.1f" fill="none" stroke="%s" stroke-width="%.1f"/>' % (ox, oy, w, hgt, MUTED, THIN))128 return out, w, hgt129130def main():131 here = os.path.dirname(os.path.abspath(__file__))132 figs = os.path.join(here, os.pardir, "figures")133 os.makedirs(figs, exist_ok=True)134 s = 7.0135 left, w1, h1 = panel(5, 105, 40.0, 34.0 + 2.0 * s, s)136 right, w2, h2 = panel(7, 105, 40.0 + w1 + 74.0, 34.0, s)137 body = left + right138 labels = [(40.0 + w1 / 2, "n = 5", "ink 114 of 150 = 19/25"), (40.0 + w1 + 74.0 + w2 / 2, "n = 7", "ink 72 of 294 = 12/49")]139 for cx, top, bot in labels:140 body.append(text(cx, 24, top, size=13, anchor="middle"))141 body.append(text(cx, 34.0 + max(h1, h2) + 20.0, bot, size=11, anchor="middle"))142 body.append(text(40.0 + w1 + 37.0 + w2 / 2, 34.0 + max(h1, h2) + 42.0, "one design, two phases: the blink is exactly minus half the top Walsh coefficient", size=12, anchor="middle"))143 width = 40.0 + w1 + 74.0 + w2 + 40.0144 height = 34.0 + max(h1, h2) + 58.0145 save(header(width, height) + body, os.path.join(figs, "blink.svg"))146 print("figures/blink.svg: %d by %d" % (width, height))147148if __name__ == "__main__":149 main()