|
20 | 20 | # --------------------------------------------------------------------------- |
21 | 21 | # |
22 | 22 | # The runtime renderer in xkcd-mathjax3.js takes each glyph's outline, applies |
23 | | -# a coordinate-threshold extension (shift points past cutX by Nx, splice |
24 | | -# jittered cubic sub-segments across the gap; same for cutY along an axis |
25 | | -# optionally tilted by leanDeg), and produces an SVG path sized to the |
26 | | -# requested overlay. Per-glyph parameters live here so they travel with |
| 23 | +# a coordinate-threshold extension (shift points past each cut by N, splice |
| 24 | +# jittered cubic sub-segments across the gap; each cut may optionally lean |
| 25 | +# its extension axis), and produces an SVG path sized to the requested |
| 26 | +# overlay. Per-glyph parameters live here so they travel with |
27 | 27 | # the font: when the font is rebuilt this step regenerates the embedded |
28 | 28 | # data and the renderer picks up the new shapes without code changes. |
29 | 29 |
|
30 | 30 | EXTENSIBLE_MARKER_BEGIN = '// ── BEGIN GENERATED GLYPH DATA ──' |
31 | 31 | EXTENSIBLE_MARKER_END = '// ── END GENERATED GLYPH DATA ──' |
32 | 32 | MATHJAX_JS = '../xkcd-mathjax3.js' |
33 | 33 |
|
34 | | -# cutXPct / cutYPct : cut threshold as % of bbox along that axis |
35 | | -# leanDeg : Y-extension axis lean from vertical (deg) |
36 | | -# unitsPerSeg : sub-segment density along the inserted gap |
37 | | -# amp : jitter amplitude in font units |
| 34 | +# cuts : ordered list of {axis: 'x'|'y', pct: %, lean?: deg}. pct is |
| 35 | +# the cut threshold along axis as % of bbox; optional lean |
| 36 | +# rotates the extension axis from vertical (Y-cuts only). |
| 37 | +# unitsPerSeg : sub-segment density along the inserted gap |
| 38 | +# amp : jitter amplitude in font units |
38 | 39 | EXTENSIBLE_CONFIG = { |
39 | | - 'emdash': {'cutXPct': 50, 'cutYPct': 50, 'leanDeg': 0.0, 'unitsPerSeg': 120, 'amp': 4}, |
40 | | - 'radical': {'cutXPct': 70, 'cutYPct': 50, 'leanDeg': 0.0, 'unitsPerSeg': 60, 'amp': 5}, |
41 | | - 'radical.tall': {'cutXPct': 56, 'cutYPct': 45, 'leanDeg': -2.0, 'unitsPerSeg': 45, 'amp': 3}, |
| 40 | + 'emdash': {'cuts': [{'axis': 'x', 'pct': 50}], 'unitsPerSeg': 120, 'amp': 4}, |
| 41 | + 'radical': {'cuts': [{'axis': 'x', 'pct': 70}, {'axis': 'y', 'pct': 50}], 'unitsPerSeg': 60, 'amp': 5}, |
| 42 | + 'radical.tall': {'cuts': [{'axis': 'x', 'pct': 56}, {'axis': 'y', 'pct': 45, 'lean': -2.0}], 'unitsPerSeg': 45, 'amp': 3}, |
42 | 43 | # tall { — two Y cuts (above and below the central joint) so stretch is |
43 | 44 | # symmetric and the joint shape stays fixed. Used for any \begin{cases} |
44 | 45 | # with 2+ rows; xkcd-mathjax3.js always overlays this glyph, never the |
45 | | - # basic braceleft. |
46 | | - 'braceleft.tall': {'cutXPct': 50, 'cutYPct': [33, 67], 'leanDeg': 0.0, 'unitsPerSeg': 45, 'amp': 3}, |
| 46 | + # basic braceleft. Highest pct first so each successive cut's threshold |
| 47 | + # stays in the already-extended region. |
| 48 | + 'braceleft.tall': {'cuts': [{'axis': 'y', 'pct': 67}, {'axis': 'y', 'pct': 33}], 'unitsPerSeg': 45, 'amp': 3}, |
47 | 49 | # tall ( — single mid-height Y cut; no joint to preserve, so the stretch |
48 | 50 | # extends a uniform vertical mid-section. parenright.tall is the X-mirror |
49 | 51 | # of this glyph and is produced at render time in xkcd-mathjax3.js, so it |
50 | 52 | # isn't listed here. |
51 | | - 'parenleft.tall': {'cutXPct': 50, 'cutYPct': 50, 'leanDeg': 0.0, 'unitsPerSeg': 45, 'amp': 3}, |
| 53 | + 'parenleft.tall': {'cuts': [{'axis': 'y', 'pct': 50}], 'unitsPerSeg': 45, 'amp': 3}, |
52 | 54 | # [ — the regular bracketleft (U+005B) extends fine on Y because its body |
53 | 55 | # is a near-straight vertical stroke; no dedicated .tall artwork needed. |
54 | 56 | # Single mid-Y cut leaves the top/bottom serifs intact and inserts uniform |
55 | 57 | # jittered stroke through the middle. bracketright is X-mirrored at |
56 | 58 | # render time in xkcd-mathjax3.js, so it isn't listed here. |
57 | | - 'bracketleft': {'cutXPct': 50, 'cutYPct': 32, 'leanDeg': 0.0, 'unitsPerSeg': 45, 'amp': 3}, |
| 59 | + 'bracketleft': {'cuts': [{'axis': 'y', 'pct': 32}], 'unitsPerSeg': 45, 'amp': 3}, |
58 | 60 | # → (U+2192). Cut early in the X axis (in the tail, before the head) so |
59 | 61 | # extension lengthens the shaft and the arrowhead shape is preserved. |
60 | | - # Y cut not used — \overrightarrow / \vec only ever stretches in X. |
61 | | - 'arrowright': {'cutXPct': 39, 'cutYPct': 50, 'leanDeg': 0.0, 'unitsPerSeg': 120, 'amp': 4}, |
| 62 | + # Stretches only in X — \overrightarrow / \vec never need vertical growth. |
| 63 | + 'arrowright': {'cuts': [{'axis': 'x', 'pct': 39}], 'unitsPerSeg': 120, 'amp': 4}, |
62 | 64 | } |
63 | 65 |
|
64 | 66 |
|
@@ -120,8 +122,9 @@ def _as_js(data): |
120 | 122 | lines.append(' advance: %s,' % g['advance']) |
121 | 123 | lines.append(' bbox: {xmin: %s, ymin: %s, xmax: %s, ymax: %s},' |
122 | 124 | % (bb['xmin'], bb['ymin'], bb['xmax'], bb['ymax'])) |
123 | | - lines.append(' config: {cutXPct: %s, cutYPct: %s, leanDeg: %s, unitsPerSeg: %s, amp: %s},' |
124 | | - % (cfg['cutXPct'], cfg['cutYPct'], cfg['leanDeg'], cfg['unitsPerSeg'], cfg['amp'])) |
| 125 | + cuts_js = ', '.join(json.dumps(c, separators=(', ', ': ')) for c in cfg['cuts']) |
| 126 | + lines.append(' config: {cuts: [%s], unitsPerSeg: %s, amp: %s},' |
| 127 | + % (cuts_js, cfg['unitsPerSeg'], cfg['amp'])) |
125 | 128 | lines.append(' commands: [') |
126 | 129 | for cmd in g['commands']: |
127 | 130 | lines.append(' ' + json.dumps(cmd) + ',') |
|
0 commit comments