fertig-classic-games/src/games/geometrywars/GeometryWarsVectorFont.js

118 lines
5.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Stroked vector font for Geometry Wars — the glyph table is the generic
// 4x6-cell set from TempestVectorFont.js / DefenderVectorFont.js (copied, per
// this repo's convention), extended with a few symbols, plus italic skew,
// left/right alignment, letter spacing and a separate glow colour so titles
// and HUD numbers can read as neon tubes.
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]]],
'+': [[[0.6, 3], [3.4, 3]], [[2, 1.6], [2, 4.4]]],
'.': [[[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]]],
'?': [[[0, 1], [1, 0], [3, 0], [4, 1], [4, 2], [2, 3.4], [2, 4.2]], [[1.8, 5.4], [2.2, 5.4], [2.2, 6], [1.8, 6], [1.8, 5.4]]],
'/': [[[3.6, 0], [0.4, 6]]],
"'": [[[2, 0], [2, 1.6]]],
'%': [[[3.6, 0], [0.4, 6]], [[0.4, 0.4], [1.4, 0.4], [1.4, 1.6], [0.4, 1.6], [0.4, 0.4]], [[2.6, 4.4], [3.6, 4.4], [3.6, 5.6], [2.6, 5.6], [2.6, 4.4]]],
'×': [[[0.6, 1.6], [3.4, 4.6]], [[3.4, 1.6], [0.6, 4.6]]],
};
function advance(ch, spacing) {
return ch === ' ' ? SPACE_W + spacing : GLYPH_W + GLYPH_GAP + spacing;
}
export function measureVectorText(text, scale, spacing = 0) {
let w = 0;
for (const ch of String(text).toUpperCase()) w += advance(ch, spacing) * scale;
return Math.max(0, w - (GLYPH_GAP + spacing) * scale);
}
export function vectorTextHeight(scale) { return GLYPH_H * scale; }
function strokeGlyphs(g, text, x0, y0, scale, skew, spacing) {
let x = x0;
for (const ch of text) {
const strokes = GLYPHS[ch];
if (strokes) {
for (const poly of strokes) {
g.beginPath();
for (let i = 0; i < poly.length; i += 1) {
const [ux, uy] = poly[i];
const px = x + (ux + (GLYPH_H - uy) * skew) * scale;
const py = y0 + uy * scale;
if (i === 0) g.moveTo(px, py); else g.lineTo(px, py);
}
g.strokePath();
}
}
x += advance(ch, spacing) * scale;
}
}
// (x, y) is the anchor: centre/left/right edge per `align`, vertical centre.
export function drawVectorText(g, text, x, y, scale, color, options = {}) {
const {
lineWidth = 3, glowWidth = 9, glowAlpha = 0.2, alpha = 1,
align = 'center', skew = 0, spacing = 0, glowColor = color, glow = true,
} = options;
const str = String(text).toUpperCase();
const w = measureVectorText(str, scale, spacing);
let x0 = x - w / 2;
if (align === 'left') x0 = x;
else if (align === 'right') x0 = x - w;
const y0 = y - (GLYPH_H * scale) / 2;
if (glow && glowWidth > 0 && glowAlpha > 0) {
g.lineStyle(glowWidth, glowColor, glowAlpha * alpha);
strokeGlyphs(g, str, x0, y0, scale, skew, spacing);
}
g.lineStyle(lineWidth, color, alpha);
strokeGlyphs(g, str, x0, y0, scale, skew, spacing);
return w;
}
export function formatScore(n) {
return Math.floor(n).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}