// 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; } }