Repository navigation
Expand file tree
/
Copy pathfont.js
More file actions
106 lines (101 loc) · 5.71 KB
/
Copy pathfont.js
File metadata and controls
106 lines (101 loc) · 5.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// font.js: a triangle-lattice typeface in the style of the "agi" drawing.
// Glyphs live on a u 0..3 × v 0..6 grid (v up). A glyph point maps to lattice vertex (u+v, -v), so
// "vertical" strokes lean 60° like italic and horizontal strokes sit on lattice rows. Chamfers run
// top-right and bottom-left, the directions that stay on lattice lines.
const GLYPHS = (() => {
const O = [[0, 1], [0, 6], [2, 6], [3, 5], [3, 0], [1, 0], [0, 1]];
const P = [[0, 0], [0, 6], [2, 6], [3, 5], [3, 3], [0, 3]];
return {
A: [[[0, 0], [0, 6], [2, 6], [3, 5], [3, 0]], [[0, 3], [3, 3]]],
B: [[[0, 0], [0, 6], [2, 6], [3, 5], [3, 4], [2, 3], [0, 3]], [[2, 3], [3, 2], [3, 0], [0, 0]]],
C: [[[3, 6], [0, 6], [0, 1], [1, 0], [3, 0]]],
D: [[[0, 0], [0, 6], [2, 6], [3, 5], [3, 0], [0, 0]]],
E: [[[3, 6], [0, 6], [0, 0], [3, 0]], [[0, 3], [2.2, 3]]],
F: [[[3, 6], [0, 6], [0, 0]], [[0, 3], [2.2, 3]]],
G: [[[3, 6], [0, 6], [0, 1], [1, 0], [3, 0], [3, 3], [1.5, 3]]],
H: [[[0, 0], [0, 6]], [[3, 0], [3, 6]], [[0, 3], [3, 3]]],
I: [[[1.5, 0], [1.5, 6]], [[.5, 6], [2.5, 6]], [[.5, 0], [2.5, 0]]],
J: [[[3, 6], [3, 0], [1, 0], [0, 1], [0, 2]]],
K: [[[0, 0], [0, 6]], [[3, 6], [0.4, 3], [3, 0]]],
L: [[[0, 6], [0, 0], [3, 0]]],
M: [[[0, 0], [0, 6], [1.5, 3.5], [3, 6], [3, 0]]],
N: [[[0, 0], [0, 6], [3, 0], [3, 6]]],
O: [O],
P: [P],
Q: [O, [[2, 1.5], [3.5, -.5]]],
R: [P, [[1.2, 3], [3, 0]]],
S: [[[3, 6], [1, 6], [0, 5], [0, 3], [3, 3], [3, 1], [2, 0], [0, 0]]],
T: [[[0, 6], [3, 6]], [[1.5, 6], [1.5, 0]]],
U: [[[0, 6], [0, 1], [1, 0], [3, 0], [3, 6]]],
V: [[[0, 6], [1.5, 0], [3, 6]]],
W: [[[0, 6], [0, 0], [1.5, 2.5], [3, 0], [3, 6]]],
X: [[[0, 6], [3, 0]], [[0, 0], [3, 6]]],
Y: [[[0, 6], [1.5, 3], [3, 6]], [[1.5, 3], [1.5, 0]]],
Z: [[[0, 6], [3, 6], [0, 0], [3, 0]]],
0: [O],
1: [[[.3, 4.8], [1.5, 6], [1.5, 0]], [[.3, 0], [2.7, 0]]],
2: [[[0, 6], [2, 6], [3, 5], [3, 3], [0, 3], [0, 0], [3, 0]]],
3: [[[0, 6], [2, 6], [3, 5], [3, 0], [0, 0]], [[1, 3], [3, 3]]],
4: [[[0, 6], [0, 3], [3, 3]], [[3, 6], [3, 0]]],
5: [[[3, 6], [0, 6], [0, 3.4], [2, 3.4], [3, 2.4], [3, 0], [0, 0]]],
6: [[[3, 6], [0, 6], [0, 1], [1, 0], [3, 0], [3, 3], [0, 3]]],
7: [[[0, 6], [3, 6], [3, 5], [1, 0]]],
8: [[[0, 1], [0, 6], [2, 6], [3, 5], [3, 0], [1, 0], [0, 1]], [[0, 3], [3, 3]]],
9: [[[3, 3], [0, 3], [0, 6], [2, 6], [3, 5], [3, 0], [0, 0]]],
'-': [[[.5, 3], [2.5, 3]]],
'_': [[[0, 0], [3, 0]]],
'=': [[[0, 4], [3, 4]], [[0, 2], [3, 2]]],
'+': [[[0, 3], [3, 3]], [[1.5, 1.5], [1.5, 4.5]]],
'*': [[[0, 3], [3, 3]], [[.6, 1.5], [2.4, 4.5]], [[.6, 4.5], [2.4, 1.5]]],
'/': [[[0, 0], [3, 6]]],
'<': [[[3, 5.5], [0, 3], [3, .5]]],
'>': [[[0, 5.5], [3, 3], [0, .5]]],
'(': [[[2.2, 6], [1, 4.8], [1, 1.2], [2.2, 0]]],
')': [[[.8, 6], [2, 4.8], [2, 1.2], [.8, 0]]],
'{': [[[2.6, 6], [1.6, 6], [1.6, 3.6], [.6, 3], [1.6, 2.4], [1.6, 0], [2.6, 0]]],
'}': [[[.4, 6], [1.4, 6], [1.4, 3.6], [2.4, 3], [1.4, 2.4], [1.4, 0], [.4, 0]]],
'[': [[[2.4, 6], [1, 6], [1, 0], [2.4, 0]]],
']': [[[.6, 6], [2, 6], [2, 0], [.6, 0]]],
'.': [[[1.2, 0], [1.2, .3]]],
',': [[[1.4, .6], [.8, -1]]],
':': [[[1.2, 0], [1.2, .3]], [[1.2, 3.6], [1.2, 3.9]]],
';': [[[1.4, .6], [.8, -1]], [[1.2, 3.6], [1.2, 3.9]]],
'!': [[[1.5, 6], [1.5, 2]], [[1.5, .4], [1.5, 0]]],
'?': [[[0, 6], [3, 6], [3, 3], [1.5, 3], [1.5, 2]], [[1.5, .4], [1.5, 0]]],
'"': [[[.8, 6], [.8, 4.5]], [[2.2, 6], [2.2, 4.5]]],
"'": [[[1.5, 6], [1.5, 4.5]]],
'&': [[[3, 0], [0, 4.5], [0, 6], [2, 6], [2, 4.5], [0, 2], [0, 0], [2, 0], [3, 1.5]]],
'#': [[[.8, 0], [.8, 6]], [[2.2, 0], [2.2, 6]], [[0, 2], [3, 2]], [[0, 4], [3, 4]]],
'%': [[[0, 0], [3, 6]], [[0, 6], [0, 5]], [[3, 0], [3, 1]]],
'$': [[[3, 5], [0, 5], [0, 3], [3, 3], [3, 1], [0, 1]], [[1.5, 6.3], [1.5, -.3]]],
'|': [[[1.5, -1], [1.5, 7]]],
' ': [],
};
})();
const FONT_ADV = 5.7, FONT_SX = 4 / 3; // advance in u units; glyphs are drawn a third wider than their grid
// Width of a string in local units at a given size (includes the italic lean of the last glyph).
function textWidth(str, size = 1) { return ((String(str).length - 1) * FONT_ADV + 3 * FONT_SX + 3) * size; }
// Build a shape for a string. (x, y) is the baseline start (left) unless o.align = 'center' | 'right'.
// o.w: stroke width in local units (default scales with size). o.show: number of glyphs visible (typing).
// Text is crispest at integer sizes (1, 2, 3…); the baseline is snapped to the lattice unless o.snap === false.
function textShape(str, x, y, size = 1, o = {}) {
str = String(str).toUpperCase();
const w = o.w ?? Math.max(.62, .7 * size), wid = textWidth(str, size);
let x0 = o.align === 'center' ? x - wid / 2 : o.align === 'right' ? x - wid : x;
// Snap so horizontal strokes sit mid-row (solid bands, not diamond chains) and slanted ones on lattice lines.
// (odd sizes: strokes are an odd number of rows, centred mid-row; even sizes: centred on a lattice line.
// A stroke width of size·H makes horizontals exactly `size` rows thick.)
if (o.snap !== false) { const j = Math.round(y / H); y = j * H - (Math.round(size) % 2 ? H / 2 : 0); x0 = Math.round(x0 - j / 2) + j / 2; }
const shapes = [], n = Math.min(str.length, o.show ?? str.length);
for (let c = 0; c < n; c++) {
const g = GLYPHS[str[c]] ?? GLYPHS['?'];
for (const pl of g) shapes.push(Shape.stroke(pl.map(([u, v]) => [x0 + (c * FONT_ADV + u * FONT_SX + v / 2) * size, y - v * H * size]), w));
}
return shapes.length ? Shape.union(...shapes) : null;
}
// Paint text with a pen. Returns the pen.
function text(p, str, x, y, size, col, o = {}) {
const s = textShape(str, x, y, size, o);
if (s) p.fill(s, col, o);
return p;
}