91 lines
4.3 KiB
JavaScript
91 lines
4.3 KiB
JavaScript
// A stroked vector font for Tempest — letters and digits as line segments in
|
|
// a 4x6 unit cell, drawn as a glow-stroke pair to match the game's phosphor
|
|
// look (same convention as Colorado Defense's VectorFont, extended to the
|
|
// full A-Z / 0-9 set this game's banners, score popups, and skill-step
|
|
// screen need).
|
|
|
|
const GLYPH_W = 4;
|
|
const GLYPH_H = 6;
|
|
const GLYPH_GAP = 1.2;
|
|
const SPACE_W = 2.6;
|
|
|
|
const GLYPHS = {
|
|
A: [[[0, 6], [2, 0], [4, 6]], [[1, 3.4], [3, 3.4]]],
|
|
B: [[[0, 0], [0, 6]], [[0, 0], [3, 0], [4, 1], [4, 2], [3, 2.8], [0, 2.8]], [[3, 2.8], [4, 3.6], [4, 5], [3, 6], [0, 6]]],
|
|
C: [[[3.6, 1], [1.2, 0], [0, 1.6], [0, 4.4], [1.2, 6], [3.6, 5]]],
|
|
D: [[[0, 0], [2.2, 0], [4, 1.6], [4, 4.4], [2.2, 6], [0, 6], [0, 0]]],
|
|
E: [[[3.4, 0], [0, 0], [0, 6], [3.4, 6]], [[0, 3], [2.4, 3]]],
|
|
F: [[[3.4, 0], [0, 0], [0, 6]], [[0, 3], [2.4, 3]]],
|
|
G: [[[3.6, 1], [1.2, 0], [0, 1.6], [0, 4.4], [1.2, 6], [3, 6], [4, 4.6], [4, 3.2], [2.2, 3.2]]],
|
|
H: [[[0, 0], [0, 6]], [[4, 0], [4, 6]], [[0, 3], [4, 3]]],
|
|
I: [[[1, 0], [3, 0]], [[2, 0], [2, 6]], [[1, 6], [3, 6]]],
|
|
J: [[[4, 0], [4, 5], [3, 6], [1, 6], [0, 5]]],
|
|
K: [[[0, 0], [0, 6]], [[4, 0], [0, 3.2]], [[1.4, 2.2], [4, 6]]],
|
|
L: [[[0, 0], [0, 6], [3.4, 6]]],
|
|
M: [[[0, 6], [0, 0], [2, 2.6], [4, 0], [4, 6]]],
|
|
N: [[[0, 6], [0, 0], [4, 6], [4, 0]]],
|
|
O: [[[1.2, 0], [2.8, 0], [4, 1.6], [4, 4.4], [2.8, 6], [1.2, 6], [0, 4.4], [0, 1.6], [1.2, 0]]],
|
|
P: [[[0, 6], [0, 0], [3, 0], [4, 1.2], [3, 2.6], [0, 2.6]]],
|
|
Q: [[[1.2, 0], [2.8, 0], [4, 1.6], [4, 4.4], [2.8, 6], [1.2, 6], [0, 4.4], [0, 1.6], [1.2, 0]], [[2.5, 4.4], [4, 6]]],
|
|
R: [[[0, 6], [0, 0], [3, 0], [4, 1.2], [3, 2.4], [0, 2.4]], [[1.6, 2.4], [4, 6]]],
|
|
S: [[[4, 1], [3, 0], [1, 0], [0, 1], [0, 2], [1, 2.8], [3, 3.2], [4, 4], [4, 5], [3, 6], [1, 6], [0, 5]]],
|
|
T: [[[0, 0], [4, 0]], [[2, 0], [2, 6]]],
|
|
U: [[[0, 0], [0, 5], [1, 6], [3, 6], [4, 5], [4, 0]]],
|
|
V: [[[0, 0], [2, 6], [4, 0]]],
|
|
W: [[[0, 0], [0.8, 6], [2, 3.2], [3.2, 6], [4, 0]]],
|
|
X: [[[0, 0], [4, 6]], [[4, 0], [0, 6]]],
|
|
Y: [[[0, 0], [2, 2.8], [4, 0]], [[2, 2.8], [2, 6]]],
|
|
Z: [[[0, 0], [4, 0], [0, 6], [4, 6]]],
|
|
0: [[[1.2, 0], [2.8, 0], [4, 1.6], [4, 4.4], [2.8, 6], [1.2, 6], [0, 4.4], [0, 1.6], [1.2, 0]]],
|
|
1: [[[1, 1], [2, 0], [2, 6]], [[1, 6], [3, 6]]],
|
|
2: [[[0, 1], [1, 0], [3, 0], [4, 1], [4, 2.4], [0, 6], [4, 6]]],
|
|
3: [[[0, 0], [4, 0], [2.4, 2.4], [4, 3.4], [4, 5], [3, 6], [1, 6], [0, 5]]],
|
|
4: [[[3, 6], [3, 0], [0, 4], [4, 4]]],
|
|
5: [[[4, 0], [0, 0], [0, 2.6], [3, 2.6], [4, 3.6], [4, 5], [3, 6], [1, 6], [0, 5]]],
|
|
6: [[[3.5, 0], [1, 0], [0, 1.5], [0, 5], [1, 6], [3, 6], [4, 5], [4, 3.6], [3, 2.6], [0, 2.6]]],
|
|
7: [[[0, 0], [4, 0], [1.5, 6]]],
|
|
8: [[[1, 0], [3, 0], [4, 1], [4, 2], [3, 2.8], [1, 2.8], [0, 2], [0, 1], [1, 0]], [[3, 2.8], [4, 3.6], [4, 5], [3, 6], [1, 6], [0, 5], [0, 3.6], [1, 2.8]]],
|
|
9: [[[0.5, 6], [3, 6], [4, 4.5], [4, 1], [3, 0], [1, 0], [0, 1], [0, 2.4], [1, 3.4], [4, 3.4]]],
|
|
'-': [[[0.8, 3], [3.2, 3]]],
|
|
'.': [[[1.8, 5.4], [2.2, 5.4], [2.2, 6], [1.8, 6], [1.8, 5.4]]],
|
|
',': [[[2.2, 5.2], [2.2, 6], [1.6, 7]]],
|
|
':': [[[1.8, 1.4], [2.2, 1.4], [2.2, 2], [1.8, 2], [1.8, 1.4]], [[1.8, 5.4], [2.2, 5.4], [2.2, 6], [1.8, 6], [1.8, 5.4]]],
|
|
'!': [[[2, 0], [2, 4]], [[1.8, 5.4], [2.2, 5.4], [2.2, 6], [1.8, 6], [1.8, 5.4]]],
|
|
};
|
|
|
|
function strokePoly(g, pts) {
|
|
g.beginPath();
|
|
g.moveTo(pts[0][0], pts[0][1]);
|
|
for (let i = 1; i < pts.length; i += 1) g.lineTo(pts[i][0], pts[i][1]);
|
|
g.strokePath();
|
|
}
|
|
|
|
export function measureVectorText(text, scale) {
|
|
let w = 0;
|
|
for (const ch of text.toUpperCase()) {
|
|
w += (ch === ' ' ? SPACE_W : GLYPH_W + GLYPH_GAP) * scale;
|
|
}
|
|
return w - GLYPH_GAP * scale;
|
|
}
|
|
|
|
export function drawVectorText(g, text, cx, cy, scale, color, options = {}) {
|
|
const { lineWidth = 3, glowWidth = 9, glowAlpha = 0.18, alpha = 1 } = options;
|
|
const totalW = measureVectorText(text, scale);
|
|
let x = cx - totalW / 2;
|
|
const y = cy - (GLYPH_H * scale) / 2;
|
|
for (const ch of text.toUpperCase()) {
|
|
if (ch === ' ') { x += SPACE_W * scale; continue; }
|
|
const strokes = GLYPHS[ch];
|
|
if (strokes) {
|
|
for (const poly of strokes) {
|
|
const pts = poly.map(([ux, uy]) => [x + ux * scale, y + uy * scale]);
|
|
g.lineStyle(glowWidth, color, glowAlpha * alpha);
|
|
strokePoly(g, pts);
|
|
g.lineStyle(lineWidth, color, alpha);
|
|
strokePoly(g, pts);
|
|
}
|
|
}
|
|
x += (GLYPH_W + GLYPH_GAP) * scale;
|
|
}
|
|
}
|