@vector-display/font 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/VectorFont.d.ts +22 -0
- package/dist/VectorFont.d.ts.map +1 -0
- package/dist/VectorFont.js +75 -0
- package/dist/VectorFont.js.map +1 -0
- package/dist/arcadeGlyphs.d.ts +4 -0
- package/dist/arcadeGlyphs.d.ts.map +1 -0
- package/dist/arcadeGlyphs.js +87 -0
- package/dist/arcadeGlyphs.js.map +1 -0
- package/dist/fontTypes.d.ts +23 -0
- package/dist/fontTypes.d.ts.map +1 -0
- package/dist/fontTypes.js +2 -0
- package/dist/fontTypes.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
- package/src/VectorFont.ts +100 -0
- package/src/arcadeGlyphs.ts +97 -0
- package/src/fontTypes.ts +31 -0
- package/src/index.ts +4 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rick Segrest
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# @vector-display/font
|
|
2
|
+
|
|
3
|
+
An Atari-style vector stroke font and text layout for [**vector-display**](https://www.npmjs.com/package/@vector-display/core). Letters are drawn as beam lines, so text glows and flickers like everything else on a vector display.
|
|
4
|
+
|
|
5
|
+
> **Status:** early (0.x). The API may change between minor versions.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @vector-display/core @vector-display/font
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
ES modules only, with TypeScript declarations.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { DisplayList } from "@vector-display/core";
|
|
19
|
+
import { VectorFont } from "@vector-display/font";
|
|
20
|
+
|
|
21
|
+
const font = VectorFont.createArcadeFont();
|
|
22
|
+
const displayList = new DisplayList();
|
|
23
|
+
|
|
24
|
+
displayList.setColor({ red: 0.85, green: 0.95, blue: 1 });
|
|
25
|
+
font.addText(displayList, { text: "SCORE 01250", x: 40, y: 30, size: 20 });
|
|
26
|
+
font.addText(displayList, { text: "GAME OVER", x: 512, y: 384, size: 32, alignment: "center" });
|
|
27
|
+
|
|
28
|
+
const { width, height } = font.measureText("HIGH SCORE", 16);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`addText` options: `text`, `x`, `y` (top of the first line), `size` (capital-letter height in world units), and optional `alignment` (`"left"`, `"center"`, `"right"`), `rotation` (radians) and `intensity`.
|
|
32
|
+
|
|
33
|
+
## Characters
|
|
34
|
+
|
|
35
|
+
- A–Z and 0–9; lowercase letters use the capitals
|
|
36
|
+
- `. , : ; ! ? ' " - + = * / _ < > ( ) # % ©`
|
|
37
|
+
- `\n` starts a new line; characters without a glyph keep their space so columns stay aligned
|
|
38
|
+
|
|
39
|
+
The glyphs are original designs in the style of late-1970s Atari vector games, drawn on a 4 × 6 grid. Zero has a slash to distinguish it from O.
|
|
40
|
+
|
|
41
|
+
## Custom glyphs and fonts
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
// Add or replace glyphs (grid units, y down, baseline at 6):
|
|
45
|
+
const font = VectorFont.createArcadeFont({
|
|
46
|
+
"$": [{ points: [{ x: 4, y: 1 }, { x: 0, y: 1 }, { x: 0, y: 3 }, { x: 4, y: 3 }, { x: 4, y: 5 }, { x: 0, y: 5 }], isClosed: false }],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Or build a different font entirely:
|
|
50
|
+
const customFont = VectorFont.fromDefinition({ glyphs, metrics: { glyphWidth: 4, capHeight: 6, letterSpacing: 2, lineSpacing: 4 } });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Legibility:** with glow and bloom, text 16 world units tall or larger reads clearly. For smaller text, reduce bloom and endpoint brightness in the renderer.
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
MIT © Rick Segrest
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Shape, type DisplayList } from "@vector-display/core";
|
|
2
|
+
import type { FontMetrics, GlyphTable, TextMeasurement, TextRun } from "./fontTypes.js";
|
|
3
|
+
export interface VectorFontDefinition {
|
|
4
|
+
readonly glyphs: GlyphTable;
|
|
5
|
+
readonly metrics: FontMetrics;
|
|
6
|
+
}
|
|
7
|
+
export declare class VectorFont {
|
|
8
|
+
readonly metrics: FontMetrics;
|
|
9
|
+
private readonly glyphShapes;
|
|
10
|
+
private readonly glyphPlacement;
|
|
11
|
+
private constructor();
|
|
12
|
+
static fromDefinition(definition: VectorFontDefinition): VectorFont;
|
|
13
|
+
static createArcadeFont(extraGlyphs?: GlyphTable): VectorFont;
|
|
14
|
+
hasGlyph(character: string): boolean;
|
|
15
|
+
findGlyph(character: string): Shape | undefined;
|
|
16
|
+
measureText(text: string, size: number): TextMeasurement;
|
|
17
|
+
addText(displayList: DisplayList, textRun: TextRun): void;
|
|
18
|
+
private measureLineUnits;
|
|
19
|
+
private placeGlyph;
|
|
20
|
+
}
|
|
21
|
+
export default VectorFont;
|
|
22
|
+
//# sourceMappingURL=VectorFont.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VectorFont.d.ts","sourceRoot":"","sources":["../src/VectorFont.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAiB,eAAe,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEvG,MAAM,WAAW,oBAAoB;IACjC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;CACjC;AAYD,qBAAa,UAAU;IACnB,SAAgB,OAAO,EAAE,WAAW,CAAC;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyE;IAExG,OAAO,eAKN;IAED,OAAc,cAAc,CAAC,UAAU,EAAE,oBAAoB,GAAG,UAAU,CAEzE;IAGD,OAAc,gBAAgB,CAAC,WAAW,GAAE,UAAe,GAAG,UAAU,CAEvE;IAEM,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE1C;IAGM,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAErD;IAEM,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAS9D;IAGM,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB/D;IAED,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,UAAU;CAarB;eAEc,UAAU"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Shape } from "@vector-display/core";
|
|
2
|
+
import { ARCADE_FONT_METRICS, ARCADE_GLYPHS } from "./arcadeGlyphs.js";
|
|
3
|
+
const ALIGNMENT_OFFSET_FACTORS = { left: 0, center: 0.5, right: 1 };
|
|
4
|
+
export class VectorFont {
|
|
5
|
+
metrics;
|
|
6
|
+
glyphShapes;
|
|
7
|
+
glyphPlacement = { x: 0, y: 0, rotation: 0, scale: 1, intensity: 1 };
|
|
8
|
+
constructor(definition) {
|
|
9
|
+
this.metrics = definition.metrics;
|
|
10
|
+
this.glyphShapes = new Map(Object.entries(definition.glyphs).map(([character, polylines]) => [character, Shape.fromPolylines(polylines)]));
|
|
11
|
+
}
|
|
12
|
+
static fromDefinition(definition) {
|
|
13
|
+
return new VectorFont(definition);
|
|
14
|
+
}
|
|
15
|
+
// Extra glyphs replace or add to the built-in arcade glyphs.
|
|
16
|
+
static createArcadeFont(extraGlyphs = {}) {
|
|
17
|
+
return new VectorFont({ glyphs: { ...ARCADE_GLYPHS, ...extraGlyphs }, metrics: ARCADE_FONT_METRICS });
|
|
18
|
+
}
|
|
19
|
+
hasGlyph(character) {
|
|
20
|
+
return this.findGlyph(character) !== undefined;
|
|
21
|
+
}
|
|
22
|
+
// Lowercase letters fall back to uppercase glyphs, since arcade fonts had no lowercase.
|
|
23
|
+
findGlyph(character) {
|
|
24
|
+
return this.glyphShapes.get(character) ?? this.glyphShapes.get(character.toUpperCase());
|
|
25
|
+
}
|
|
26
|
+
measureText(text, size) {
|
|
27
|
+
const lines = text.split("\n");
|
|
28
|
+
const scale = size / this.metrics.capHeight;
|
|
29
|
+
const widestLineLength = Math.max(...lines.map((line) => Array.from(line).length));
|
|
30
|
+
const lineHeight = this.metrics.capHeight + this.metrics.lineSpacing;
|
|
31
|
+
return {
|
|
32
|
+
width: this.measureLineUnits(widestLineLength) * scale,
|
|
33
|
+
height: (lines.length * lineHeight - this.metrics.lineSpacing) * scale,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
// Characters without a glyph are skipped but still take up space, so columns stay aligned.
|
|
37
|
+
addText(displayList, textRun) {
|
|
38
|
+
const scale = textRun.size / this.metrics.capHeight;
|
|
39
|
+
const alignmentFactor = ALIGNMENT_OFFSET_FACTORS[textRun.alignment ?? "left"];
|
|
40
|
+
const lines = textRun.text.split("\n");
|
|
41
|
+
lines.forEach((line, lineIndex) => {
|
|
42
|
+
const characters = Array.from(line);
|
|
43
|
+
const lineStartUnits = -this.measureLineUnits(characters.length) * alignmentFactor;
|
|
44
|
+
const lineTopUnits = lineIndex * (this.metrics.capHeight + this.metrics.lineSpacing);
|
|
45
|
+
characters.forEach((character, characterIndex) => {
|
|
46
|
+
const glyph = this.findGlyph(character);
|
|
47
|
+
if (!glyph || glyph.segmentCount === 0)
|
|
48
|
+
return;
|
|
49
|
+
const offsetUnits = lineStartUnits + characterIndex * (this.metrics.glyphWidth + this.metrics.letterSpacing);
|
|
50
|
+
this.placeGlyph({ textRun, scale, offsetXUnits: offsetUnits, offsetYUnits: lineTopUnits });
|
|
51
|
+
displayList.addShape(glyph, this.glyphPlacement);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
measureLineUnits(characterCount) {
|
|
56
|
+
if (characterCount === 0)
|
|
57
|
+
return 0;
|
|
58
|
+
return characterCount * this.metrics.glyphWidth + (characterCount - 1) * this.metrics.letterSpacing;
|
|
59
|
+
}
|
|
60
|
+
placeGlyph(layout) {
|
|
61
|
+
const { textRun, scale, offsetXUnits, offsetYUnits } = layout;
|
|
62
|
+
const rotation = textRun.rotation ?? 0;
|
|
63
|
+
const cosine = Math.cos(rotation);
|
|
64
|
+
const sine = Math.sin(rotation);
|
|
65
|
+
const offsetX = offsetXUnits * scale;
|
|
66
|
+
const offsetY = offsetYUnits * scale;
|
|
67
|
+
this.glyphPlacement.x = textRun.x + offsetX * cosine - offsetY * sine;
|
|
68
|
+
this.glyphPlacement.y = textRun.y + offsetX * sine + offsetY * cosine;
|
|
69
|
+
this.glyphPlacement.rotation = rotation;
|
|
70
|
+
this.glyphPlacement.scale = scale;
|
|
71
|
+
this.glyphPlacement.intensity = textRun.intensity ?? 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export default VectorFont;
|
|
75
|
+
//# sourceMappingURL=VectorFont.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VectorFont.js","sourceRoot":"","sources":["../src/VectorFont.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arcadeGlyphs.d.ts","sourceRoot":"","sources":["../src/arcadeGlyphs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAI9D,eAAO,MAAM,mBAAmB,EAAE,WAKjC,CAAC;AAsBF,eAAO,MAAM,aAAa,EAAE,UAgE3B,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Original stroke designs in the style of late-1970s Atari vector games: uppercase only,
|
|
2
|
+
// straight strokes on a 4-wide by 6-tall grid (y down, cap line at 0, baseline at 6).
|
|
3
|
+
export const ARCADE_FONT_METRICS = {
|
|
4
|
+
glyphWidth: 4,
|
|
5
|
+
capHeight: 6,
|
|
6
|
+
letterSpacing: 2,
|
|
7
|
+
lineSpacing: 4,
|
|
8
|
+
};
|
|
9
|
+
function open(...coordinatePairs) {
|
|
10
|
+
return { points: toPoints(coordinatePairs), isClosed: false };
|
|
11
|
+
}
|
|
12
|
+
function closed(...coordinatePairs) {
|
|
13
|
+
return { points: toPoints(coordinatePairs), isClosed: true };
|
|
14
|
+
}
|
|
15
|
+
function dot(x, y) {
|
|
16
|
+
return { points: [{ x, y }], isClosed: false };
|
|
17
|
+
}
|
|
18
|
+
function toPoints(coordinatePairs) {
|
|
19
|
+
const points = [];
|
|
20
|
+
for (let index = 0; index < coordinatePairs.length; index += 2) {
|
|
21
|
+
points.push({ x: coordinatePairs[index], y: coordinatePairs[index + 1] });
|
|
22
|
+
}
|
|
23
|
+
return points;
|
|
24
|
+
}
|
|
25
|
+
export const ARCADE_GLYPHS = {
|
|
26
|
+
" ": [],
|
|
27
|
+
A: [open(0, 6, 0, 2, 2, 0, 4, 2, 4, 6), open(0, 4, 4, 4)],
|
|
28
|
+
B: [open(0, 3, 0, 0, 3, 0, 4, 1, 4, 2, 3, 3, 0, 3), open(3, 3, 4, 4, 4, 5, 3, 6, 0, 6, 0, 3)],
|
|
29
|
+
C: [open(4, 0, 0, 0, 0, 6, 4, 6)],
|
|
30
|
+
D: [closed(0, 0, 2, 0, 4, 2, 4, 4, 2, 6, 0, 6)],
|
|
31
|
+
E: [open(4, 0, 0, 0, 0, 6, 4, 6), open(0, 3, 3, 3)],
|
|
32
|
+
F: [open(4, 0, 0, 0, 0, 6), open(0, 3, 3, 3)],
|
|
33
|
+
G: [open(4, 1, 4, 0, 0, 0, 0, 6, 4, 6, 4, 3, 2, 3)],
|
|
34
|
+
H: [open(0, 0, 0, 6), open(4, 0, 4, 6), open(0, 3, 4, 3)],
|
|
35
|
+
I: [open(0, 0, 4, 0), open(2, 0, 2, 6), open(0, 6, 4, 6)],
|
|
36
|
+
J: [open(4, 0, 4, 6, 1, 6, 0, 5, 0, 4)],
|
|
37
|
+
K: [open(0, 0, 0, 6), open(4, 0, 0, 3, 4, 6)],
|
|
38
|
+
L: [open(0, 0, 0, 6, 4, 6)],
|
|
39
|
+
M: [open(0, 6, 0, 0, 2, 2, 4, 0, 4, 6)],
|
|
40
|
+
N: [open(0, 6, 0, 0, 4, 6, 4, 0)],
|
|
41
|
+
O: [closed(0, 0, 4, 0, 4, 6, 0, 6)],
|
|
42
|
+
P: [open(0, 6, 0, 0, 4, 0, 4, 3, 0, 3)],
|
|
43
|
+
Q: [closed(0, 0, 4, 0, 4, 4, 2, 6, 0, 6), open(2, 4, 4, 6)],
|
|
44
|
+
R: [open(0, 6, 0, 0, 4, 0, 4, 3, 0, 3), open(1, 3, 4, 6)],
|
|
45
|
+
S: [open(4, 0, 0, 0, 0, 3, 4, 3, 4, 6, 0, 6)],
|
|
46
|
+
T: [open(0, 0, 4, 0), open(2, 0, 2, 6)],
|
|
47
|
+
U: [open(0, 0, 0, 6, 4, 6, 4, 0)],
|
|
48
|
+
V: [open(0, 0, 2, 6, 4, 0)],
|
|
49
|
+
W: [open(0, 0, 0, 6, 2, 4, 4, 6, 4, 0)],
|
|
50
|
+
X: [open(0, 0, 4, 6), open(4, 0, 0, 6)],
|
|
51
|
+
Y: [open(0, 0, 2, 2, 4, 0), open(2, 2, 2, 6)],
|
|
52
|
+
Z: [open(0, 0, 4, 0, 0, 6, 4, 6)],
|
|
53
|
+
// A slash keeps zero distinct from the letter O.
|
|
54
|
+
"0": [closed(0, 0, 4, 0, 4, 6, 0, 6), open(0, 6, 4, 0)],
|
|
55
|
+
"1": [open(1, 1, 2, 0, 2, 6), open(1, 6, 3, 6)],
|
|
56
|
+
"2": [open(0, 0, 4, 0, 4, 3, 0, 3, 0, 6, 4, 6)],
|
|
57
|
+
"3": [open(0, 0, 4, 0, 4, 6, 0, 6), open(1, 3, 4, 3)],
|
|
58
|
+
"4": [open(0, 0, 0, 3, 4, 3), open(4, 0, 4, 6)],
|
|
59
|
+
"5": [open(4, 0, 0, 0, 0, 3, 3, 3, 4, 4, 4, 5, 3, 6, 0, 6)],
|
|
60
|
+
"6": [open(4, 0, 0, 0, 0, 6, 4, 6, 4, 3, 0, 3)],
|
|
61
|
+
"7": [open(0, 0, 4, 0, 4, 6)],
|
|
62
|
+
"8": [closed(0, 0, 4, 0, 4, 6, 0, 6), open(0, 3, 4, 3)],
|
|
63
|
+
"9": [open(4, 3, 0, 3, 0, 0, 4, 0, 4, 6, 0, 6)],
|
|
64
|
+
".": [dot(2, 6)],
|
|
65
|
+
",": [open(2, 5, 1, 7)],
|
|
66
|
+
":": [dot(2, 1.5), dot(2, 4.5)],
|
|
67
|
+
";": [dot(2, 1.5), open(2, 4.5, 1, 6.5)],
|
|
68
|
+
"!": [open(2, 0, 2, 4), dot(2, 6)],
|
|
69
|
+
"?": [open(0, 1, 0, 0, 4, 0, 4, 3, 2, 3, 2, 4), dot(2, 6)],
|
|
70
|
+
"'": [open(2, 0, 2, 2)],
|
|
71
|
+
'"': [open(1, 0, 1, 2), open(3, 0, 3, 2)],
|
|
72
|
+
"-": [open(0, 3, 4, 3)],
|
|
73
|
+
"+": [open(2, 1, 2, 5), open(0, 3, 4, 3)],
|
|
74
|
+
"=": [open(0, 2, 4, 2), open(0, 4, 4, 4)],
|
|
75
|
+
"*": [open(2, 1, 2, 5), open(0, 2, 4, 4), open(0, 4, 4, 2)],
|
|
76
|
+
"/": [open(0, 6, 4, 0)],
|
|
77
|
+
"_": [open(0, 6, 4, 6)],
|
|
78
|
+
"<": [open(4, 0, 0, 3, 4, 6)],
|
|
79
|
+
">": [open(0, 0, 4, 3, 0, 6)],
|
|
80
|
+
"(": [open(3, 0, 1, 2, 1, 4, 3, 6)],
|
|
81
|
+
")": [open(1, 0, 3, 2, 3, 4, 1, 6)],
|
|
82
|
+
"#": [open(1, 0, 1, 6), open(3, 0, 3, 6), open(0, 2, 4, 2), open(0, 4, 4, 4)],
|
|
83
|
+
"%": [open(0, 6, 4, 0), dot(0.5, 0.5), dot(3.5, 5.5)],
|
|
84
|
+
// Escaped so the key still matches when a page loads this script without a UTF-8 charset.
|
|
85
|
+
"\u00A9": [closed(1, 0, 3, 0, 4, 1, 4, 5, 3, 6, 1, 6, 0, 5, 0, 1), open(2.75, 2.25, 1.25, 2.25, 1.25, 3.75, 2.75, 3.75)],
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=arcadeGlyphs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arcadeGlyphs.js","sourceRoot":"","sources":["../src/arcadeGlyphs.ts"],"names":[],"mappings":"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AC,CAAC;IAC3D,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7C,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACjC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3B,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7C,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAA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AC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACzC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACvB,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACzC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACzC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3D,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACvB,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACvB,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACnC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACnC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7E,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACrD,0FAA0F;IAC1F,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;CAC3H,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Polyline } from "@vector-display/core";
|
|
2
|
+
export interface FontMetrics {
|
|
3
|
+
readonly glyphWidth: number;
|
|
4
|
+
readonly capHeight: number;
|
|
5
|
+
readonly letterSpacing: number;
|
|
6
|
+
readonly lineSpacing: number;
|
|
7
|
+
}
|
|
8
|
+
export type GlyphTable = Readonly<Record<string, readonly Polyline[]>>;
|
|
9
|
+
export type TextAlignment = "left" | "center" | "right";
|
|
10
|
+
export interface TextRun {
|
|
11
|
+
readonly text: string;
|
|
12
|
+
readonly x: number;
|
|
13
|
+
readonly y: number;
|
|
14
|
+
readonly size: number;
|
|
15
|
+
readonly alignment?: TextAlignment;
|
|
16
|
+
readonly rotation?: number;
|
|
17
|
+
readonly intensity?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface TextMeasurement {
|
|
20
|
+
readonly width: number;
|
|
21
|
+
readonly height: number;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=fontTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fontTypes.d.ts","sourceRoot":"","sources":["../src/fontTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGrD,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC,CAAC,CAAC;AAEvE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,OAAO;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IAEnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;IAEnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fontTypes.js","sourceRoot":"","sources":["../src/fontTypes.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { VectorFont } from "./VectorFont.js";
|
|
2
|
+
export type { VectorFontDefinition } from "./VectorFont.js";
|
|
3
|
+
export { ARCADE_FONT_METRICS, ARCADE_GLYPHS } from "./arcadeGlyphs.js";
|
|
4
|
+
export type { FontMetrics, GlyphTable, TextAlignment, TextMeasurement, TextRun } from "./fontTypes.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vector-display/font",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Atari-style vector stroke font and text layout for vector-display.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Rick Segrest",
|
|
8
|
+
"email": "rsegrest77@gmail.com",
|
|
9
|
+
"url": "https://www.ricksegrest.com"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/rsegrest/vector-display.git",
|
|
14
|
+
"directory": "packages/font"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/rsegrest/vector-display/tree/main/packages/font#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/rsegrest/vector-display/issues"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"@vector-display/source": "./src/index.ts",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"prepublishOnly": "tsc -b"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"src",
|
|
35
|
+
"!src/**/*.test.ts"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@vector-display/core": "0.1.0"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Shape, type DisplayList } from "@vector-display/core";
|
|
2
|
+
import { ARCADE_FONT_METRICS, ARCADE_GLYPHS } from "./arcadeGlyphs.js";
|
|
3
|
+
import type { FontMetrics, GlyphTable, TextAlignment, TextMeasurement, TextRun } from "./fontTypes.js";
|
|
4
|
+
|
|
5
|
+
export interface VectorFontDefinition {
|
|
6
|
+
readonly glyphs: GlyphTable;
|
|
7
|
+
readonly metrics: FontMetrics;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface MutablePlacement {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
rotation: number;
|
|
14
|
+
scale: number;
|
|
15
|
+
intensity: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const ALIGNMENT_OFFSET_FACTORS: Readonly<Record<TextAlignment, number>> = { left: 0, center: 0.5, right: 1 };
|
|
19
|
+
|
|
20
|
+
export class VectorFont {
|
|
21
|
+
public readonly metrics: FontMetrics;
|
|
22
|
+
private readonly glyphShapes: ReadonlyMap<string, Shape>;
|
|
23
|
+
private readonly glyphPlacement: MutablePlacement = { x: 0, y: 0, rotation: 0, scale: 1, intensity: 1 };
|
|
24
|
+
|
|
25
|
+
private constructor(definition: VectorFontDefinition) {
|
|
26
|
+
this.metrics = definition.metrics;
|
|
27
|
+
this.glyphShapes = new Map(
|
|
28
|
+
Object.entries(definition.glyphs).map(([character, polylines]) => [character, Shape.fromPolylines(polylines)]),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public static fromDefinition(definition: VectorFontDefinition): VectorFont {
|
|
33
|
+
return new VectorFont(definition);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Extra glyphs replace or add to the built-in arcade glyphs.
|
|
37
|
+
public static createArcadeFont(extraGlyphs: GlyphTable = {}): VectorFont {
|
|
38
|
+
return new VectorFont({ glyphs: { ...ARCADE_GLYPHS, ...extraGlyphs }, metrics: ARCADE_FONT_METRICS });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public hasGlyph(character: string): boolean {
|
|
42
|
+
return this.findGlyph(character) !== undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Lowercase letters fall back to uppercase glyphs, since arcade fonts had no lowercase.
|
|
46
|
+
public findGlyph(character: string): Shape | undefined {
|
|
47
|
+
return this.glyphShapes.get(character) ?? this.glyphShapes.get(character.toUpperCase());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public measureText(text: string, size: number): TextMeasurement {
|
|
51
|
+
const lines = text.split("\n");
|
|
52
|
+
const scale = size / this.metrics.capHeight;
|
|
53
|
+
const widestLineLength = Math.max(...lines.map((line) => Array.from(line).length));
|
|
54
|
+
const lineHeight = this.metrics.capHeight + this.metrics.lineSpacing;
|
|
55
|
+
return {
|
|
56
|
+
width: this.measureLineUnits(widestLineLength) * scale,
|
|
57
|
+
height: (lines.length * lineHeight - this.metrics.lineSpacing) * scale,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Characters without a glyph are skipped but still take up space, so columns stay aligned.
|
|
62
|
+
public addText(displayList: DisplayList, textRun: TextRun): void {
|
|
63
|
+
const scale = textRun.size / this.metrics.capHeight;
|
|
64
|
+
const alignmentFactor = ALIGNMENT_OFFSET_FACTORS[textRun.alignment ?? "left"];
|
|
65
|
+
const lines = textRun.text.split("\n");
|
|
66
|
+
lines.forEach((line, lineIndex) => {
|
|
67
|
+
const characters = Array.from(line);
|
|
68
|
+
const lineStartUnits = -this.measureLineUnits(characters.length) * alignmentFactor;
|
|
69
|
+
const lineTopUnits = lineIndex * (this.metrics.capHeight + this.metrics.lineSpacing);
|
|
70
|
+
characters.forEach((character, characterIndex) => {
|
|
71
|
+
const glyph = this.findGlyph(character);
|
|
72
|
+
if (!glyph || glyph.segmentCount === 0) return;
|
|
73
|
+
const offsetUnits = lineStartUnits + characterIndex * (this.metrics.glyphWidth + this.metrics.letterSpacing);
|
|
74
|
+
this.placeGlyph({ textRun, scale, offsetXUnits: offsetUnits, offsetYUnits: lineTopUnits });
|
|
75
|
+
displayList.addShape(glyph, this.glyphPlacement);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private measureLineUnits(characterCount: number): number {
|
|
81
|
+
if (characterCount === 0) return 0;
|
|
82
|
+
return characterCount * this.metrics.glyphWidth + (characterCount - 1) * this.metrics.letterSpacing;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private placeGlyph(layout: { textRun: TextRun; scale: number; offsetXUnits: number; offsetYUnits: number }): void {
|
|
86
|
+
const { textRun, scale, offsetXUnits, offsetYUnits } = layout;
|
|
87
|
+
const rotation = textRun.rotation ?? 0;
|
|
88
|
+
const cosine = Math.cos(rotation);
|
|
89
|
+
const sine = Math.sin(rotation);
|
|
90
|
+
const offsetX = offsetXUnits * scale;
|
|
91
|
+
const offsetY = offsetYUnits * scale;
|
|
92
|
+
this.glyphPlacement.x = textRun.x + offsetX * cosine - offsetY * sine;
|
|
93
|
+
this.glyphPlacement.y = textRun.y + offsetX * sine + offsetY * cosine;
|
|
94
|
+
this.glyphPlacement.rotation = rotation;
|
|
95
|
+
this.glyphPlacement.scale = scale;
|
|
96
|
+
this.glyphPlacement.intensity = textRun.intensity ?? 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export default VectorFont;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Polyline } from "@vector-display/core";
|
|
2
|
+
import type { FontMetrics, GlyphTable } from "./fontTypes.js";
|
|
3
|
+
|
|
4
|
+
// Original stroke designs in the style of late-1970s Atari vector games: uppercase only,
|
|
5
|
+
// straight strokes on a 4-wide by 6-tall grid (y down, cap line at 0, baseline at 6).
|
|
6
|
+
export const ARCADE_FONT_METRICS: FontMetrics = {
|
|
7
|
+
glyphWidth: 4,
|
|
8
|
+
capHeight: 6,
|
|
9
|
+
letterSpacing: 2,
|
|
10
|
+
lineSpacing: 4,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function open(...coordinatePairs: number[]): Polyline {
|
|
14
|
+
return { points: toPoints(coordinatePairs), isClosed: false };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function closed(...coordinatePairs: number[]): Polyline {
|
|
18
|
+
return { points: toPoints(coordinatePairs), isClosed: true };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function dot(x: number, y: number): Polyline {
|
|
22
|
+
return { points: [{ x, y }], isClosed: false };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function toPoints(coordinatePairs: readonly number[]): { x: number; y: number }[] {
|
|
26
|
+
const points = [];
|
|
27
|
+
for (let index = 0; index < coordinatePairs.length; index += 2) {
|
|
28
|
+
points.push({ x: coordinatePairs[index], y: coordinatePairs[index + 1] });
|
|
29
|
+
}
|
|
30
|
+
return points;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const ARCADE_GLYPHS: GlyphTable = {
|
|
34
|
+
" ": [],
|
|
35
|
+
|
|
36
|
+
A: [open(0, 6, 0, 2, 2, 0, 4, 2, 4, 6), open(0, 4, 4, 4)],
|
|
37
|
+
B: [open(0, 3, 0, 0, 3, 0, 4, 1, 4, 2, 3, 3, 0, 3), open(3, 3, 4, 4, 4, 5, 3, 6, 0, 6, 0, 3)],
|
|
38
|
+
C: [open(4, 0, 0, 0, 0, 6, 4, 6)],
|
|
39
|
+
D: [closed(0, 0, 2, 0, 4, 2, 4, 4, 2, 6, 0, 6)],
|
|
40
|
+
E: [open(4, 0, 0, 0, 0, 6, 4, 6), open(0, 3, 3, 3)],
|
|
41
|
+
F: [open(4, 0, 0, 0, 0, 6), open(0, 3, 3, 3)],
|
|
42
|
+
G: [open(4, 1, 4, 0, 0, 0, 0, 6, 4, 6, 4, 3, 2, 3)],
|
|
43
|
+
H: [open(0, 0, 0, 6), open(4, 0, 4, 6), open(0, 3, 4, 3)],
|
|
44
|
+
I: [open(0, 0, 4, 0), open(2, 0, 2, 6), open(0, 6, 4, 6)],
|
|
45
|
+
J: [open(4, 0, 4, 6, 1, 6, 0, 5, 0, 4)],
|
|
46
|
+
K: [open(0, 0, 0, 6), open(4, 0, 0, 3, 4, 6)],
|
|
47
|
+
L: [open(0, 0, 0, 6, 4, 6)],
|
|
48
|
+
M: [open(0, 6, 0, 0, 2, 2, 4, 0, 4, 6)],
|
|
49
|
+
N: [open(0, 6, 0, 0, 4, 6, 4, 0)],
|
|
50
|
+
O: [closed(0, 0, 4, 0, 4, 6, 0, 6)],
|
|
51
|
+
P: [open(0, 6, 0, 0, 4, 0, 4, 3, 0, 3)],
|
|
52
|
+
Q: [closed(0, 0, 4, 0, 4, 4, 2, 6, 0, 6), open(2, 4, 4, 6)],
|
|
53
|
+
R: [open(0, 6, 0, 0, 4, 0, 4, 3, 0, 3), open(1, 3, 4, 6)],
|
|
54
|
+
S: [open(4, 0, 0, 0, 0, 3, 4, 3, 4, 6, 0, 6)],
|
|
55
|
+
T: [open(0, 0, 4, 0), open(2, 0, 2, 6)],
|
|
56
|
+
U: [open(0, 0, 0, 6, 4, 6, 4, 0)],
|
|
57
|
+
V: [open(0, 0, 2, 6, 4, 0)],
|
|
58
|
+
W: [open(0, 0, 0, 6, 2, 4, 4, 6, 4, 0)],
|
|
59
|
+
X: [open(0, 0, 4, 6), open(4, 0, 0, 6)],
|
|
60
|
+
Y: [open(0, 0, 2, 2, 4, 0), open(2, 2, 2, 6)],
|
|
61
|
+
Z: [open(0, 0, 4, 0, 0, 6, 4, 6)],
|
|
62
|
+
|
|
63
|
+
// A slash keeps zero distinct from the letter O.
|
|
64
|
+
"0": [closed(0, 0, 4, 0, 4, 6, 0, 6), open(0, 6, 4, 0)],
|
|
65
|
+
"1": [open(1, 1, 2, 0, 2, 6), open(1, 6, 3, 6)],
|
|
66
|
+
"2": [open(0, 0, 4, 0, 4, 3, 0, 3, 0, 6, 4, 6)],
|
|
67
|
+
"3": [open(0, 0, 4, 0, 4, 6, 0, 6), open(1, 3, 4, 3)],
|
|
68
|
+
"4": [open(0, 0, 0, 3, 4, 3), open(4, 0, 4, 6)],
|
|
69
|
+
"5": [open(4, 0, 0, 0, 0, 3, 3, 3, 4, 4, 4, 5, 3, 6, 0, 6)],
|
|
70
|
+
"6": [open(4, 0, 0, 0, 0, 6, 4, 6, 4, 3, 0, 3)],
|
|
71
|
+
"7": [open(0, 0, 4, 0, 4, 6)],
|
|
72
|
+
"8": [closed(0, 0, 4, 0, 4, 6, 0, 6), open(0, 3, 4, 3)],
|
|
73
|
+
"9": [open(4, 3, 0, 3, 0, 0, 4, 0, 4, 6, 0, 6)],
|
|
74
|
+
|
|
75
|
+
".": [dot(2, 6)],
|
|
76
|
+
",": [open(2, 5, 1, 7)],
|
|
77
|
+
":": [dot(2, 1.5), dot(2, 4.5)],
|
|
78
|
+
";": [dot(2, 1.5), open(2, 4.5, 1, 6.5)],
|
|
79
|
+
"!": [open(2, 0, 2, 4), dot(2, 6)],
|
|
80
|
+
"?": [open(0, 1, 0, 0, 4, 0, 4, 3, 2, 3, 2, 4), dot(2, 6)],
|
|
81
|
+
"'": [open(2, 0, 2, 2)],
|
|
82
|
+
'"': [open(1, 0, 1, 2), open(3, 0, 3, 2)],
|
|
83
|
+
"-": [open(0, 3, 4, 3)],
|
|
84
|
+
"+": [open(2, 1, 2, 5), open(0, 3, 4, 3)],
|
|
85
|
+
"=": [open(0, 2, 4, 2), open(0, 4, 4, 4)],
|
|
86
|
+
"*": [open(2, 1, 2, 5), open(0, 2, 4, 4), open(0, 4, 4, 2)],
|
|
87
|
+
"/": [open(0, 6, 4, 0)],
|
|
88
|
+
"_": [open(0, 6, 4, 6)],
|
|
89
|
+
"<": [open(4, 0, 0, 3, 4, 6)],
|
|
90
|
+
">": [open(0, 0, 4, 3, 0, 6)],
|
|
91
|
+
"(": [open(3, 0, 1, 2, 1, 4, 3, 6)],
|
|
92
|
+
")": [open(1, 0, 3, 2, 3, 4, 1, 6)],
|
|
93
|
+
"#": [open(1, 0, 1, 6), open(3, 0, 3, 6), open(0, 2, 4, 2), open(0, 4, 4, 4)],
|
|
94
|
+
"%": [open(0, 6, 4, 0), dot(0.5, 0.5), dot(3.5, 5.5)],
|
|
95
|
+
// Escaped so the key still matches when a page loads this script without a UTF-8 charset.
|
|
96
|
+
"\u00A9": [closed(1, 0, 3, 0, 4, 1, 4, 5, 3, 6, 1, 6, 0, 5, 0, 1), open(2.75, 2.25, 1.25, 2.25, 1.25, 3.75, 2.75, 3.75)],
|
|
97
|
+
};
|
package/src/fontTypes.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Polyline } from "@vector-display/core";
|
|
2
|
+
|
|
3
|
+
// All metrics are in glyph grid units; text size scales them so capHeight equals the requested size.
|
|
4
|
+
export interface FontMetrics {
|
|
5
|
+
readonly glyphWidth: number;
|
|
6
|
+
readonly capHeight: number;
|
|
7
|
+
readonly letterSpacing: number;
|
|
8
|
+
readonly lineSpacing: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type GlyphTable = Readonly<Record<string, readonly Polyline[]>>;
|
|
12
|
+
|
|
13
|
+
export type TextAlignment = "left" | "center" | "right";
|
|
14
|
+
|
|
15
|
+
export interface TextRun {
|
|
16
|
+
readonly text: string;
|
|
17
|
+
// Top of the first line; horizontal position depends on alignment.
|
|
18
|
+
readonly x: number;
|
|
19
|
+
readonly y: number;
|
|
20
|
+
// Height of capital letters in world units.
|
|
21
|
+
readonly size: number;
|
|
22
|
+
readonly alignment?: TextAlignment;
|
|
23
|
+
// Radians, clockwise on screen, around (x, y).
|
|
24
|
+
readonly rotation?: number;
|
|
25
|
+
readonly intensity?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TextMeasurement {
|
|
29
|
+
readonly width: number;
|
|
30
|
+
readonly height: number;
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { VectorFont } from "./VectorFont.js";
|
|
2
|
+
export type { VectorFontDefinition } from "./VectorFont.js";
|
|
3
|
+
export { ARCADE_FONT_METRICS, ARCADE_GLYPHS } from "./arcadeGlyphs.js";
|
|
4
|
+
export type { FontMetrics, GlyphTable, TextAlignment, TextMeasurement, TextRun } from "./fontTypes.js";
|