site/tools/note-field.py (2991B)
1 #!/usr/bin/env python3
2 """Generate the hero's background: ommp's splash-screen note field.
3
4 The splash fills the terminal with dark and scatters music notes across it
5 (src/ui/widgets/about_modal.rs, render_splash_screen). This reproduces that
6 exact scatter — the same hash, the same four glyphs, the same three colours —
7 as a tile the page can repeat.
8
9 The scatter is periodic. A note falls on cell (x, y) when
10
11 (7x + 13y) mod 37 == 0
12
13 and shifting x or y by 37 adds 7*37 or 13*37 to the hash, both multiples of 37.
14 So a 37x37 tile repeats seamlessly. Which glyph and which colour a note gets
15 depends on hash/37, whose period is 444 rather than 37, so those cycle sooner
16 here than in the app. At this contrast — the brightest of the three is #3C1450
17 on #0A051E — that is not a difference anyone can see.
18
19 Rendered to a raster on this machine rather than shipped as text: the glyphs
20 live in the CJK fonts, not in the page's latin-subset JetBrains Mono, and a
21 visitor without them would get four kinds of tofu.
22 """
23
24 import subprocess
25 import sys
26
27 # src/ui/widgets/about_modal.rs
28 NOTES = ["♪", "♫", "♩", "◆"]
29 DIM_COLORS = ["#3C1450", "#143C50", "#501432"] # DIM_PURPLE, DIM_CYAN, DIM_PINK
30 PERIOD = 37
31
32 # One terminal cell, at 2x so the tile stays sharp on a hidpi screen. The 1:2
33 # ratio is roughly what a monospace cell is.
34 CELL_W, CELL_H = 20, 40
35 FONT_SIZE = 26
36
37 OUT = sys.argv[1] if len(sys.argv) > 1 else "static/img/note-field.png"
38
39
40 def font():
41 """A font with all four glyphs. Prefer the one the terminal itself falls
42 back to for U+266A, so the page's notes are the shapes ommp actually draws."""
43 listing = subprocess.run(
44 ["fc-list", ":charset=266a 266b 2669 25c6", "file"],
45 capture_output=True, text=True, check=True,
46 ).stdout
47 files = [l.split(":")[0].strip() for l in listing.splitlines() if l.strip()]
48 for preferred in ("NotoSansMonoCJK", "NotoSansCJK", "DejaVuSans"):
49 for f in files:
50 if preferred in f:
51 return f
52 if not files:
53 sys.exit("no font on this system has all four glyphs")
54 return files[0]
55
56
57 def main():
58 args = ["magick", "-size", f"{PERIOD * CELL_W}x{PERIOD * CELL_H}", "xc:none",
59 "-font", font(), "-pointsize", str(FONT_SIZE), "-gravity", "none"]
60
61 drawn = 0
62 for y in range(PERIOD):
63 for x in range(PERIOD):
64 h = (x * 7 + y * 13) & 0xFFFFFFFF
65 if h % PERIOD:
66 continue
67 glyph = NOTES[(h // PERIOD) % len(NOTES)]
68 colour = DIM_COLORS[(h // PERIOD + 1) % len(DIM_COLORS)]
69 # annotate's origin is the baseline; sit the glyph in its cell.
70 args += ["-fill", colour,
71 "-annotate", f"+{x * CELL_W + 2}+{y * CELL_H + CELL_H - 12}", glyph]
72 drawn += 1
73
74 args += ["-strip", OUT]
75 subprocess.run(args, check=True)
76 print(f" {OUT}: {drawn} notes in a {PERIOD}x{PERIOD} cell tile")
77
78
79 if __name__ == "__main__":
80 main()