@johnmorrisdotca/dougu-sports 0.3.1 → 0.4.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/CHANGELOG.md +27 -1
- package/NOTICE.md +1 -0
- package/README.md +27 -11
- package/dist/emblems/ball/ball.art.d.ts +24 -0
- package/dist/emblems/ball/ball.art.js +67 -0
- package/dist/emblems/ball/ball.core.d.ts +6 -0
- package/dist/emblems/ball/ball.core.js +12 -1
- package/dist/emblems/ball/ball.face.d.ts +54 -0
- package/dist/emblems/ball/ball.face.js +287 -0
- package/dist/emblems/ball/ball.image.d.ts +8 -0
- package/dist/emblems/ball/ball.image.js +122 -0
- package/dist/emblems/ball/ball.index.d.ts +3 -1
- package/dist/emblems/ball/ball.index.js +7 -5
- package/dist/emblems/ball/ball.letters.d.ts +32 -0
- package/dist/emblems/ball/ball.letters.js +142 -0
- package/dist/emblems/ball/ball.object.js +1 -1
- package/dist/emblems/ball/ball.puck.d.ts +8 -0
- package/dist/emblems/ball/ball.puck.js +19 -12
- package/dist/emblems/ball/ball.puck.print.d.ts +76 -0
- package/dist/emblems/ball/ball.puck.print.js +46 -0
- package/dist/emblems/ball/ball.signature.d.ts +4 -0
- package/dist/emblems/ball/ball.signature.js +58 -0
- package/dist/emblems/ball/ball.stack.d.ts +10 -0
- package/dist/emblems/ball/ball.stack.js +8 -1
- package/dist/emblems/emblem.api.d.ts +5 -5
- package/dist/emblems/emblem.api.js +27 -8
- package/dist/emblems/emblem.core.d.ts +6 -0
- package/dist/emblems/emblem.descriptions.js +8 -0
- package/dist/emblems/emblem.ids.d.ts +6 -1
- package/dist/emblems/emblem.ids.js +4 -1
- package/dist/emblems/emblem.registry.js +2 -2
- package/dist/emblems-entry.d.ts +3 -2
- package/dist/emblems-entry.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Vector lettering for the printed pucks: every letter and numeral is a centre line in a box 90 units tall, and the stroke width makes it bold. No font file and no system font is used, so the print
|
|
2
|
+
// looks the same everywhere and is a few hundred bytes of path. The strokes are adapted from the block letters of senshu-jerseys (the same author, the same MIT licence); here they are drawn wide, heavy and
|
|
3
|
+
// leaning a little forward, so a puck's CANADA is a sports print of its own. A line of text comes out as one path in the face's own units (the stroke width is the caller's), straight or set on an arc.
|
|
4
|
+
// A character with no glyph is left out. Imports only the core's number writer.
|
|
5
|
+
import { num } from "./ball.core.js";
|
|
6
|
+
const GLYPHS = {
|
|
7
|
+
"0": { w: 48, d: "M24 0C8 0 0 14 0 45C0 76 8 90 24 90C40 90 48 76 48 45C48 14 40 0 24 0Z" },
|
|
8
|
+
"1": { w: 28, d: "M0 24L28 0L28 90" },
|
|
9
|
+
"2": { w: 48, d: "M0 22C0 8 10 0 24 0C38 0 48 8 48 24C48 38 40 46 28 56L0 90L48 90" },
|
|
10
|
+
"3": { w: 48, d: "M2 0L46 0L24 34C40 34 48 46 48 62C48 80 38 90 24 90C10 90 2 82 0 68" },
|
|
11
|
+
"4": { w: 50, d: "M36 90L36 0L0 62L50 62" },
|
|
12
|
+
"5": { w: 48, d: "M46 0L6 0L2 40C10 34 18 32 26 32C40 32 48 46 48 62C48 80 38 90 24 90C10 90 2 82 0 70" },
|
|
13
|
+
"6": { w: 48, d: "M44 6C38 2 32 0 24 0C8 0 0 16 0 46C0 76 8 90 24 90C40 90 48 78 48 62C48 46 40 36 24 36C12 36 2 44 0 56" },
|
|
14
|
+
"7": { w: 48, d: "M0 0L48 0L16 90" },
|
|
15
|
+
"8": { w: 46, d: "M23 0C10 0 4 8 4 20C4 32 12 40 23 43C36 46 46 52 46 66C46 82 38 90 23 90C8 90 0 82 0 66C0 52 10 46 23 43C34 40 42 32 42 20C42 8 36 0 23 0Z" },
|
|
16
|
+
"9": { w: 48, d: "M4 84C10 88 16 90 24 90C40 90 48 74 48 44C48 14 40 0 24 0C8 0 0 12 0 28C0 44 8 54 24 54C36 54 46 46 48 34" },
|
|
17
|
+
A: { w: 50, d: "M0 90L25 0L50 90M10 60L40 60" },
|
|
18
|
+
B: { w: 46, d: "M0 90L0 0L26 0C40 0 44 10 44 21C44 34 36 42 26 43L0 43M26 43C40 44 46 52 46 66C46 82 40 90 26 90L0 90" },
|
|
19
|
+
C: { w: 46, d: "M46 24C44 8 36 0 24 0C8 0 0 14 0 45C0 76 8 90 24 90C36 90 44 82 46 66" },
|
|
20
|
+
D: { w: 46, d: "M0 0L0 90L20 90C40 90 46 76 46 45C46 14 40 0 20 0Z" },
|
|
21
|
+
E: { w: 42, d: "M42 0L0 0L0 90L42 90M0 44L34 44" },
|
|
22
|
+
F: { w: 40, d: "M40 0L0 0L0 90M0 44L32 44" },
|
|
23
|
+
G: { w: 46, d: "M46 24C44 8 36 0 24 0C8 0 0 14 0 45C0 76 8 90 24 90C40 90 46 78 46 58L46 50L26 50" },
|
|
24
|
+
H: { w: 46, d: "M0 0L0 90M46 0L46 90M0 44L46 44" },
|
|
25
|
+
I: { w: 0, d: "M0 0L0 90" },
|
|
26
|
+
J: { w: 38, d: "M38 0L38 64C38 82 30 90 20 90C8 90 0 82 0 66" },
|
|
27
|
+
K: { w: 46, d: "M0 0L0 90M46 0L2 50M14 36L46 90" },
|
|
28
|
+
L: { w: 38, d: "M0 0L0 90L38 90" },
|
|
29
|
+
M: { w: 58, d: "M0 90L0 0L29 62L58 0L58 90" },
|
|
30
|
+
N: { w: 46, d: "M0 90L0 0L46 90L46 0" },
|
|
31
|
+
O: { w: 48, d: "M24 0C8 0 0 14 0 45C0 76 8 90 24 90C40 90 48 76 48 45C48 14 40 0 24 0Z" },
|
|
32
|
+
P: { w: 46, d: "M0 90L0 0L28 0C42 0 46 10 46 24C46 38 40 48 26 48L0 48" },
|
|
33
|
+
Q: { w: 48, d: "M24 0C8 0 0 14 0 45C0 76 8 90 24 90C40 90 48 76 48 45C48 14 40 0 24 0ZM30 68L50 94" },
|
|
34
|
+
R: { w: 46, d: "M0 90L0 0L28 0C42 0 46 10 46 22C46 36 40 46 26 47L0 47M24 47L46 90" },
|
|
35
|
+
S: { w: 46, d: "M44 22C42 8 34 0 22 0C8 0 2 10 2 22C2 36 12 40 24 45C36 50 46 56 46 68C46 82 38 90 24 90C10 90 2 82 0 66" },
|
|
36
|
+
T: { w: 46, d: "M0 0L46 0M23 0L23 90" },
|
|
37
|
+
U: { w: 46, d: "M0 0L0 62C0 82 8 90 23 90C38 90 46 82 46 62L46 0" },
|
|
38
|
+
V: { w: 48, d: "M0 0L24 90L48 0" },
|
|
39
|
+
W: { w: 60, d: "M0 0L15 90L30 28L45 90L60 0" },
|
|
40
|
+
X: { w: 48, d: "M0 0L48 90M48 0L0 90" },
|
|
41
|
+
Y: { w: 48, d: "M0 0L24 46L48 0M24 46L24 90" },
|
|
42
|
+
Z: { w: 46, d: "M0 0L46 0L0 90L46 90" },
|
|
43
|
+
"-": { w: 24, d: "M0 45L24 45" },
|
|
44
|
+
".": { w: 0, d: "M0 80L0 90" },
|
|
45
|
+
",": { w: 0, d: "M0 80L0 92" },
|
|
46
|
+
"'": { w: 0, d: "M0 0L0 22" },
|
|
47
|
+
"/": { w: 26, d: "M0 90L26 0" },
|
|
48
|
+
":": { w: 0, d: "M0 22L0 32M0 70L0 80" },
|
|
49
|
+
"&": { w: 52, d: "M52 90L10 34C2 22 6 0 24 0C42 0 44 20 36 30L8 60C-2 76 6 90 24 90C34 90 44 80 50 60" },
|
|
50
|
+
"·": { w: 0, d: "M0 42L0 48" },
|
|
51
|
+
" ": { w: 22, d: "" },
|
|
52
|
+
};
|
|
53
|
+
/** The glyph of a character (a lower-case letter is its capital, an accented one its plain one; the bullet and the middle dot are one dot), or `undefined`. */
|
|
54
|
+
export function glyphOf(char) {
|
|
55
|
+
const plain = (char === "•" || char === "·" ? "·" : char.normalize("NFD").replace(/[̀-ͯ]/g, "").toUpperCase());
|
|
56
|
+
return Object.hasOwn(GLYPHS, plain) ? GLYPHS[plain] : undefined;
|
|
57
|
+
}
|
|
58
|
+
const GLYPH_H = 90;
|
|
59
|
+
/** The characters of a text that have a glyph, as glyphs (an unknown character is left out). */
|
|
60
|
+
const glyphsOf = (text) => [...text].map(glyphOf).filter((g) => g !== undefined);
|
|
61
|
+
/** The pen positions of a text in glyph units: the left edge of each glyph's box and the total width, from the first glyph's left edge to the last one's right edge. */
|
|
62
|
+
function setting(glyphs, o) {
|
|
63
|
+
const wide = o.wide ?? 1;
|
|
64
|
+
const stroke = o.weight * GLYPH_H;
|
|
65
|
+
const gap = Math.max(5, stroke * 0.5) + (o.track ?? 0) * GLYPH_H;
|
|
66
|
+
const at = [];
|
|
67
|
+
let x = 0;
|
|
68
|
+
for (const g of glyphs) {
|
|
69
|
+
at.push(x);
|
|
70
|
+
x += g.w * wide + stroke + gap;
|
|
71
|
+
}
|
|
72
|
+
const total = Math.max(0, x - stroke - gap);
|
|
73
|
+
return { at, total, stroke };
|
|
74
|
+
}
|
|
75
|
+
/** The width a text takes, in the face's units, with the stroke's own thickness included. */
|
|
76
|
+
export function textWidth(text, o) {
|
|
77
|
+
const glyphs = glyphsOf(text);
|
|
78
|
+
const s = setting(glyphs, o);
|
|
79
|
+
return ((s.total + s.stroke) * o.size) / GLYPH_H;
|
|
80
|
+
}
|
|
81
|
+
/** The size at which a text is as wide as `width` at most and as tall as `height` at most. */
|
|
82
|
+
export function fitSize(text, o, width, height) {
|
|
83
|
+
const w = textWidth(text, { ...o, size: 1 });
|
|
84
|
+
return Math.min(height, w === 0 ? height : width / w);
|
|
85
|
+
}
|
|
86
|
+
/** A glyph's path with every point run through `map`. The glyph tables use absolute `M L C Z` only, so each pair of numbers is a point. */
|
|
87
|
+
function mapPath(d, map) {
|
|
88
|
+
let out = "";
|
|
89
|
+
for (const part of d.match(/[MLCZ][^MLCZ]*/g) ?? []) {
|
|
90
|
+
const nums = (part.slice(1).match(/-?\d+(?:\.\d+)?/g) ?? []).map(Number);
|
|
91
|
+
const points = [];
|
|
92
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
93
|
+
const q = map([nums[i], nums[i + 1]]);
|
|
94
|
+
points.push(`${num(q[0], 3)} ${num(q[1], 3)}`);
|
|
95
|
+
}
|
|
96
|
+
out += part[0] + points.join(" ");
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
/** A point of a glyph made wide and leaning, in glyph units with the origin at the top left of its box. */
|
|
101
|
+
const shaped = (o) => (p) => [p[0] * (o.wide ?? 1) + (o.slant ?? 0) * (GLYPH_H - p[1]), p[1]];
|
|
102
|
+
/** A line of text on a straight line, centred on (`x`, `y`), as one path: draw it with a stroke of `weight * size`. */
|
|
103
|
+
export function linePath(text, o, x, y) {
|
|
104
|
+
const glyphs = glyphsOf(text);
|
|
105
|
+
const s = setting(glyphs, o);
|
|
106
|
+
const k = o.size / GLYPH_H;
|
|
107
|
+
const left = x - (s.total * k) / 2;
|
|
108
|
+
const top = y - o.size / 2;
|
|
109
|
+
const shape = shaped(o);
|
|
110
|
+
return glyphs.map((g, i) => mapPath(g.d, (p) => {
|
|
111
|
+
const q = shape(p);
|
|
112
|
+
return [left + (s.at[i] + q[0]) * k, top + q[1] * k];
|
|
113
|
+
})).join("");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A line of text set on an arc of the circle of radius `r` (the middle of the letters), centred on the angle `centre` in degrees clockwise from the top. With `inside` false the letters stand outward and read
|
|
117
|
+
* clockwise (the top of the ring); with `inside` true they stand inward and read left to right (the bottom of the ring, `centre` 180). One path.
|
|
118
|
+
*/
|
|
119
|
+
export function arcPath(text, o, r, centre, inside = false) {
|
|
120
|
+
const glyphs = glyphsOf(text);
|
|
121
|
+
const s = setting(glyphs, o);
|
|
122
|
+
const k = o.size / GLYPH_H;
|
|
123
|
+
const shape = shaped(o);
|
|
124
|
+
const base = inside ? r + o.size / 2 : r - o.size / 2;
|
|
125
|
+
const mid = (centre * Math.PI) / 180;
|
|
126
|
+
return glyphs.map((g, i) => {
|
|
127
|
+
const half = (g.w * (o.wide ?? 1)) / 2;
|
|
128
|
+
const along = (s.at[i] + half - s.total / 2) * k;
|
|
129
|
+
const a = inside ? mid - along / r : mid + along / r;
|
|
130
|
+
const sin = Math.sin(a), cos = Math.cos(a);
|
|
131
|
+
const ox = base * sin, oy = -base * cos;
|
|
132
|
+
// The glyph's own axes: along the line of reading, and down the glyph (toward the centre on the top, outward on the bottom).
|
|
133
|
+
const [ax, ay] = inside ? [-cos, -sin] : [cos, sin];
|
|
134
|
+
const [dx, dy] = inside ? [sin, -cos] : [-sin, cos];
|
|
135
|
+
return mapPath(g.d, (p) => {
|
|
136
|
+
const q = shape(p);
|
|
137
|
+
const lx = (q[0] - half * 1) * k;
|
|
138
|
+
const ly = (q[1] - GLYPH_H) * k;
|
|
139
|
+
return [ox + lx * ax + ly * dx, oy + lx * ay + ly * dy];
|
|
140
|
+
});
|
|
141
|
+
}).join("");
|
|
142
|
+
}
|
|
@@ -7,7 +7,7 @@ export function defineObject(def) {
|
|
|
7
7
|
const render = (o, turning) => {
|
|
8
8
|
const opts = resolveOptions(o);
|
|
9
9
|
const rot = rotation(radians(def.view.yaw + finite(o?.yaw), 0), radians(def.view.pitch + finite(o?.pitch), 0), radians(def.view.roll + finite(o?.roll), 0));
|
|
10
|
-
const id = `k${hashOf(`${def.id}|${opts.style}|${opts.tint ?? ""}|${opts.accent ?? ""}|${rot.map((r) => r.map((x) => x.toFixed(2)).join()).join()}`)}`;
|
|
10
|
+
const id = `k${hashOf(`${def.id}|${opts.style}|${opts.tint ?? ""}|${opts.accent ?? ""}${opts.faceKey === "" ? "" : `|f${opts.faceKey}`}|${rot.map((r) => r.map((x) => x.toFixed(2)).join()).join()}`)}`;
|
|
11
11
|
const at = (p) => {
|
|
12
12
|
const sh = def.shift ?? [0, 0, 0];
|
|
13
13
|
const k = def.scale ?? 1;
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { type StackSpec } from "./ball.stack.ts";
|
|
2
|
+
import { type PieceSize } from "./ball.core.ts";
|
|
3
|
+
/** Half the puck's thickness in units of its radius: 12.7 mm over 38.1 mm, one third. */
|
|
4
|
+
export declare const PUCK_H: number;
|
|
5
|
+
/** The puck's size and the rule it comes from: the printed pucks are the same puck. */
|
|
6
|
+
export declare const PUCK_SIZE: PieceSize;
|
|
7
|
+
/** The plain puck's drawing: black rubber, a ridged edge, and the ring a moulded top face has. A printed puck is this drawing with its print laid over the ring's place (ball.puck.print.ts). */
|
|
8
|
+
export declare const puckSpec: (extra?: Partial<StackSpec>) => StackSpec;
|
|
1
9
|
export declare const BALL_PUCK: import("./ball.core.ts").Piece<{
|
|
2
10
|
slices: number;
|
|
3
11
|
} & {
|
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
// Ice hockey puck: vulcanised black rubber, 76.2 mm across and 25.4 mm thick (so 1 : 3), with a ridged edge. Drawn as a cylinder in true 3D, so it turns. The stack's y is in units of the radius, so the half thickness is 12.7 / 38.1, one third.
|
|
2
2
|
import { drawStack } from "./ball.stack.js";
|
|
3
|
+
import {} from "./ball.core.js";
|
|
3
4
|
import { defineObject } from "./ball.object.js";
|
|
4
|
-
|
|
5
|
+
/** Half the puck's thickness in units of its radius: 12.7 mm over 38.1 mm, one third. */
|
|
6
|
+
export const PUCK_H = 25.4 / 76.2;
|
|
7
|
+
/** The puck's size and the rule it comes from: the printed pucks are the same puck. */
|
|
8
|
+
export const PUCK_SIZE = { extentMm: 76, diameterMm: 76, thicknessMm: 25, massG: [156, 170], rule: "IIHF Official Rule Book, Rule 13 Puck, 13.1 Dimensions (the puck is Rule 13, not Rule 1)", note: "IIHF figures, rounded as the rule rounds them (the NHL puck is 3 in by 1 in). Colour is not stated in the rule." };
|
|
9
|
+
/** The plain puck's drawing: black rubber, a ridged edge, and the ring a moulded top face has. A printed puck is this drawing with its print laid over the ring's place (ball.puck.print.ts). */
|
|
10
|
+
export const puckSpec = (extra) => ({
|
|
11
|
+
slices: [{ y0: -PUCK_H, y1: PUCK_H, r: 1, colour: "#26282d", outline: true }],
|
|
12
|
+
top: "#2e3036",
|
|
13
|
+
bottom: "#2e3036",
|
|
14
|
+
gloss: 0.5,
|
|
15
|
+
shade: "#000",
|
|
16
|
+
grain: { f: 1.6, a: 0.25 },
|
|
17
|
+
grooves: { count: 30, slices: [0], colour: "#000", opacity: 0.5 },
|
|
18
|
+
topRing: { r: 0.9, colour: "#4a4d56" },
|
|
19
|
+
...extra,
|
|
20
|
+
});
|
|
5
21
|
export const BALL_PUCK = /* @__PURE__ */ defineObject({
|
|
6
22
|
id: "ball:puck",
|
|
7
23
|
en: "Hockey puck",
|
|
8
24
|
ja: "アイスホッケーのパック",
|
|
9
|
-
size:
|
|
25
|
+
size: PUCK_SIZE,
|
|
10
26
|
view: { yaw: 0, pitch: 36, roll: -8 },
|
|
11
27
|
model: { slices: 1 },
|
|
12
|
-
draw: (c) => drawStack(c,
|
|
13
|
-
slices: [{ y0: -H, y1: H, r: 1, colour: "#26282d", outline: true }],
|
|
14
|
-
top: "#2e3036",
|
|
15
|
-
bottom: "#2e3036",
|
|
16
|
-
gloss: 0.5,
|
|
17
|
-
shade: "#000",
|
|
18
|
-
grain: { f: 1.6, a: 0.25 },
|
|
19
|
-
grooves: { count: 30, slices: [0], colour: "#000", opacity: 0.5 },
|
|
20
|
-
topRing: { r: 0.9, colour: "#4a4d56" },
|
|
21
|
-
}),
|
|
28
|
+
draw: (c) => drawStack(c, puckSpec()),
|
|
22
29
|
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type Piece } from "./ball.core.ts";
|
|
2
|
+
import { type PuckDesign } from "./ball.face.ts";
|
|
3
|
+
export declare const PUCK_CANADA: Piece<{
|
|
4
|
+
slices: number;
|
|
5
|
+
design: PuckDesign;
|
|
6
|
+
view: {
|
|
7
|
+
yaw: number;
|
|
8
|
+
pitch: number;
|
|
9
|
+
roll: number;
|
|
10
|
+
};
|
|
11
|
+
}>;
|
|
12
|
+
export declare const PUCK_CANADA_RED: Piece<{
|
|
13
|
+
slices: number;
|
|
14
|
+
design: PuckDesign;
|
|
15
|
+
view: {
|
|
16
|
+
yaw: number;
|
|
17
|
+
pitch: number;
|
|
18
|
+
roll: number;
|
|
19
|
+
};
|
|
20
|
+
}>;
|
|
21
|
+
export declare const PUCK_CANADA_WHITE: Piece<{
|
|
22
|
+
slices: number;
|
|
23
|
+
design: PuckDesign;
|
|
24
|
+
view: {
|
|
25
|
+
yaw: number;
|
|
26
|
+
pitch: number;
|
|
27
|
+
roll: number;
|
|
28
|
+
};
|
|
29
|
+
}>;
|
|
30
|
+
export declare const PUCK_USA: Piece<{
|
|
31
|
+
slices: number;
|
|
32
|
+
design: PuckDesign;
|
|
33
|
+
view: {
|
|
34
|
+
yaw: number;
|
|
35
|
+
pitch: number;
|
|
36
|
+
roll: number;
|
|
37
|
+
};
|
|
38
|
+
}>;
|
|
39
|
+
export declare const PUCK_USA_NAVY: Piece<{
|
|
40
|
+
slices: number;
|
|
41
|
+
design: PuckDesign;
|
|
42
|
+
view: {
|
|
43
|
+
yaw: number;
|
|
44
|
+
pitch: number;
|
|
45
|
+
roll: number;
|
|
46
|
+
};
|
|
47
|
+
}>;
|
|
48
|
+
export declare const PUCK_PLAYER: Piece<{
|
|
49
|
+
slices: number;
|
|
50
|
+
design: PuckDesign;
|
|
51
|
+
view: {
|
|
52
|
+
yaw: number;
|
|
53
|
+
pitch: number;
|
|
54
|
+
roll: number;
|
|
55
|
+
};
|
|
56
|
+
}>;
|
|
57
|
+
export declare const PUCK_SIGNED: Piece<{
|
|
58
|
+
slices: number;
|
|
59
|
+
design: PuckDesign;
|
|
60
|
+
view: {
|
|
61
|
+
yaw: number;
|
|
62
|
+
pitch: number;
|
|
63
|
+
roll: number;
|
|
64
|
+
};
|
|
65
|
+
}>;
|
|
66
|
+
export declare const PUCK_SOUVENIR: Piece<{
|
|
67
|
+
slices: number;
|
|
68
|
+
design: PuckDesign;
|
|
69
|
+
view: {
|
|
70
|
+
yaw: number;
|
|
71
|
+
pitch: number;
|
|
72
|
+
roll: number;
|
|
73
|
+
};
|
|
74
|
+
}>;
|
|
75
|
+
/** Every printed puck, in the order a gallery shows them. */
|
|
76
|
+
export declare const PUCKS: readonly Piece[];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// The printed pucks: the same black puck as `ball:puck` (76.2 mm across, 25.4 mm thick, the ridged edge), with a print on its top face. The print is drawn flat (ball.face.ts) and laid on the top by the
|
|
2
|
+
// puck's own rotation, so it foreshortens as the puck tips, turns with it, and is gone when the top is turned away (the bottom is plain rubber). The grip on the edge shows as the puck turns. All our own drawing:
|
|
3
|
+
// our own leaf and flags, our own lettering, a made-up number and name, a made-up signature; no team's or league's mark. Each puck is an id of its own with a face to start from, and any `face` option replaces it.
|
|
4
|
+
import {} from "./ball.core.js";
|
|
5
|
+
import { faceMarkup } from "./ball.face.js";
|
|
6
|
+
import { PUCK_H, PUCK_SIZE, puckSpec } from "./ball.puck.js";
|
|
7
|
+
import { defineObject } from "./ball.object.js";
|
|
8
|
+
import { drawStack } from "./ball.stack.js";
|
|
9
|
+
import { num } from "./ball.core.js";
|
|
10
|
+
/** A printed puck: a plain puck with the face laid over its top. */
|
|
11
|
+
function definePuck(def) {
|
|
12
|
+
return defineObject({
|
|
13
|
+
id: def.id,
|
|
14
|
+
en: def.en,
|
|
15
|
+
ja: def.ja,
|
|
16
|
+
size: PUCK_SIZE,
|
|
17
|
+
view: { yaw: 0, pitch: 50, roll: -6 },
|
|
18
|
+
model: { slices: 1, design: def.design },
|
|
19
|
+
draw: (c) => drawStack(c, puckSpec({
|
|
20
|
+
topRing: undefined,
|
|
21
|
+
overlay: (cx, top) => {
|
|
22
|
+
if (top.nz < 0.02)
|
|
23
|
+
return { defs: "", body: "" };
|
|
24
|
+
// The top face's plane in the box: its centre, and where a step of 1 along the puck's x and z axes goes (the face's right, and its down).
|
|
25
|
+
const o = cx.at([0, PUCK_H, 0]), ex = cx.at([1, PUCK_H, 0]), ez = cx.at([0, PUCK_H, 1]);
|
|
26
|
+
const m = [ex[0] - o[0], ex[1] - o[1], ez[0] - o[0], ez[1] - o[1], o[0], o[1]].map((v) => num(v, 4)).join(" ");
|
|
27
|
+
const face = faceMarkup(cx.opts.face, def.design, def.own, `${cx.id}p`);
|
|
28
|
+
// A light from the upper left and a falling-off at the far edge, over the print, in the box's own space so the light stays put while the puck turns.
|
|
29
|
+
const gid = `${cx.id}fl`;
|
|
30
|
+
const defs = `${face.defs}<radialGradient id="${gid}" cx=".36" cy=".3" r=".85"><stop offset="0" stop-color="#fff" stop-opacity=".34"/><stop offset=".4" stop-color="#fff" stop-opacity="0"/><stop offset="1" stop-color="#000" stop-opacity=".42"/></radialGradient>`;
|
|
31
|
+
const body = `<g transform="matrix(${m})">${face.body}</g><path d="${top.ellipse(0.985)}" fill="url(#${gid})" stroke="#000" stroke-opacity=".55" stroke-width="${num(Math.max(0.3, cx.minWidth * 0.35), 2)}"/>`;
|
|
32
|
+
return { defs, body };
|
|
33
|
+
},
|
|
34
|
+
})),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export const PUCK_CANADA = /* @__PURE__ */ definePuck({ id: "puck:canada", en: "Canada flag puck", ja: "カナダ国旗のパック", design: "canada" });
|
|
38
|
+
export const PUCK_CANADA_RED = /* @__PURE__ */ definePuck({ id: "puck:canada-red", en: "Canada red puck", ja: "カナダ赤地のパック", design: "canada-red" });
|
|
39
|
+
export const PUCK_CANADA_WHITE = /* @__PURE__ */ definePuck({ id: "puck:canada-white", en: "Canada white puck", ja: "カナダ白地のパック", design: "canada-white" });
|
|
40
|
+
export const PUCK_USA = /* @__PURE__ */ definePuck({ id: "puck:usa", en: "USA stripes puck", ja: "アメリカ星条旗のパック", design: "usa" });
|
|
41
|
+
export const PUCK_USA_NAVY = /* @__PURE__ */ definePuck({ id: "puck:usa-navy", en: "USA navy puck", ja: "アメリカ紺地のパック", design: "usa-navy" });
|
|
42
|
+
export const PUCK_PLAYER = /* @__PURE__ */ definePuck({ id: "puck:player", en: "Player number puck", ja: "選手名と背番号のパック", design: "player" });
|
|
43
|
+
export const PUCK_SIGNED = /* @__PURE__ */ definePuck({ id: "puck:signed", en: "Signed puck", ja: "サイン入りパック", design: "usa-navy", own: { signature: 7, text: "" } });
|
|
44
|
+
export const PUCK_SOUVENIR = /* @__PURE__ */ definePuck({ id: "puck:souvenir", en: "Souvenir puck", ja: "記念パック", design: "souvenir" });
|
|
45
|
+
/** Every printed puck, in the order a gallery shows them. */
|
|
46
|
+
export const PUCKS = [PUCK_CANADA, PUCK_CANADA_RED, PUCK_CANADA_WHITE, PUCK_USA, PUCK_USA_NAVY, PUCK_PLAYER, PUCK_SIGNED, PUCK_SOUVENIR];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** A small seeded random generator (mulberry32) over any seed: a number, or text hashed first. */
|
|
2
|
+
export declare function seeded(seed: number | string): () => number;
|
|
3
|
+
/** The path of a signature for a seed: a first name, a surname and an underline, in a box about 1 wide, centred on the origin. */
|
|
4
|
+
export declare function signaturePath(seed: number | string): string;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// A made-up signature: a squiggle drawn by a seeded pen, so the same seed always gives the same stroke and no seed is anyone's real hand. It is a first name and a surname, each a tall capital with a loop and a run
|
|
2
|
+
// of humps, joined by a smooth curve, and a flourish under the whole. The path lies in a box 1 wide and about 0.4 tall about the origin; the face scales, turns and inks it. Imports nothing.
|
|
3
|
+
/** A small seeded random generator (mulberry32) over any seed: a number, or text hashed first. */
|
|
4
|
+
export function seeded(seed) {
|
|
5
|
+
let h = 2166136261;
|
|
6
|
+
for (const ch of String(seed))
|
|
7
|
+
h = Math.imul(h ^ ch.codePointAt(0), 16777619);
|
|
8
|
+
let a = h >>> 0;
|
|
9
|
+
return () => {
|
|
10
|
+
a = (a + 0x6d2b79f5) >>> 0;
|
|
11
|
+
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
|
12
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
13
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const f = (v) => (Math.round(v * 1000) / 1000 + 0).toString();
|
|
17
|
+
/** A smooth curve through the points (Catmull-Rom turned into cubic Beziers). */
|
|
18
|
+
function smooth(pts) {
|
|
19
|
+
let d = `M${f(pts[0][0])} ${f(pts[0][1])}`;
|
|
20
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
21
|
+
const p0 = pts[Math.max(0, i - 1)], p1 = pts[i], p2 = pts[i + 1], p3 = pts[Math.min(pts.length - 1, i + 2)];
|
|
22
|
+
d += `C${f(p1[0] + (p2[0] - p0[0]) / 6)} ${f(p1[1] + (p2[1] - p0[1]) / 6)} ${f(p2[0] - (p3[0] - p1[0]) / 6)} ${f(p2[1] - (p3[1] - p1[1]) / 6)} ${f(p2[0])} ${f(p2[1])}`;
|
|
23
|
+
}
|
|
24
|
+
return d;
|
|
25
|
+
}
|
|
26
|
+
/** One word of a signature as a cursive run: a pen that goes round in loops while it moves right, so the strokes are the looped humps of joined writing. The first loop (the capital) is the tallest. */
|
|
27
|
+
function word(rand, x0, width, humps) {
|
|
28
|
+
const pts = [];
|
|
29
|
+
const per = 9;
|
|
30
|
+
const steps = (humps + 1) * per;
|
|
31
|
+
let env = 1;
|
|
32
|
+
for (let i = 0; i <= steps; i++) {
|
|
33
|
+
const u = i / steps;
|
|
34
|
+
const phase = (i / per) * Math.PI * 2;
|
|
35
|
+
const hump = Math.floor(i / per);
|
|
36
|
+
if (i % per === 0)
|
|
37
|
+
env = hump === 0 ? 1.7 : 0.35 + rand() * 0.75;
|
|
38
|
+
// The loop: x is pushed back as y rises, so the pen doubles back on itself at the top.
|
|
39
|
+
const rise = (1 - Math.cos(phase)) / 2;
|
|
40
|
+
const x = x0 + width * u - Math.sin(phase) * 0.045 * env;
|
|
41
|
+
const y = -rise * 0.17 * env + 0.03 * Math.sin(phase * 0.5);
|
|
42
|
+
pts.push([x, y]);
|
|
43
|
+
}
|
|
44
|
+
// A short upward tail.
|
|
45
|
+
const last = pts[pts.length - 1];
|
|
46
|
+
pts.push([last[0] + width * 0.12, last[1] - 0.06 - rand() * 0.04]);
|
|
47
|
+
return pts;
|
|
48
|
+
}
|
|
49
|
+
/** The path of a signature for a seed: a first name, a surname and an underline, in a box about 1 wide, centred on the origin. */
|
|
50
|
+
export function signaturePath(seed) {
|
|
51
|
+
const rand = seeded(seed);
|
|
52
|
+
const first = word(rand, -0.5, 0.28, 2 + Math.floor(rand() * 2));
|
|
53
|
+
const second = word(rand, -0.12, 0.5, 3 + Math.floor(rand() * 3));
|
|
54
|
+
const end = second[second.length - 1];
|
|
55
|
+
const under = [[end[0] - 0.05, end[1] + 0.13 + rand() * 0.03], [-0.05, 0.15 + rand() * 0.03], [-0.38, 0.12 + rand() * 0.04]];
|
|
56
|
+
const lift = (list) => list.map(([x, y]) => [x, y - 0.02]);
|
|
57
|
+
return smooth(lift(first)) + smooth(lift(second)) + smooth(lift([end, ...under]));
|
|
58
|
+
}
|
|
@@ -41,6 +41,16 @@ export interface StackSpec {
|
|
|
41
41
|
cy: number;
|
|
42
42
|
nz: number;
|
|
43
43
|
}) => string;
|
|
44
|
+
/** Drawn over everything, grain included, when the top face is toward you (a print): its definitions and its body, given the top face's ellipse as a path of radius `r` (a fraction of the top's). */
|
|
45
|
+
overlay?: (c: ObjectContext, top: {
|
|
46
|
+
cx: number;
|
|
47
|
+
cy: number;
|
|
48
|
+
nz: number;
|
|
49
|
+
ellipse: (r: number) => string;
|
|
50
|
+
}) => {
|
|
51
|
+
defs: string;
|
|
52
|
+
body: string;
|
|
53
|
+
};
|
|
44
54
|
}
|
|
45
55
|
export declare function drawStack(c: ObjectContext, spec: StackSpec): {
|
|
46
56
|
defs: string;
|
|
@@ -123,5 +123,12 @@ export function drawStack(c, spec) {
|
|
|
123
123
|
defs += grainDef(c.id, spec.grain.f, spec.grain.a);
|
|
124
124
|
if (line)
|
|
125
125
|
return { defs: "", body: `<g fill="none" stroke="currentColor" stroke-width="${num(Math.max(2.4, c.minWidth * 1.4), 2)}" stroke-linejoin="round" stroke-linecap="round">${body}</g>` };
|
|
126
|
-
|
|
126
|
+
let printed = "";
|
|
127
|
+
if (spec.overlay !== undefined && topVisible) {
|
|
128
|
+
const top = slices.at(-1);
|
|
129
|
+
const over = spec.overlay(c, { cx: tx, cy: ty, nz, ellipse: (r) => ellipsePath(tx, ty, 46 * radius(top, 1) * r, 46 * radius(top, 1) * r * Math.abs(nz), perpDeg) });
|
|
130
|
+
defs += over.defs;
|
|
131
|
+
printed = over.body;
|
|
132
|
+
}
|
|
133
|
+
return { defs, body: (grain ? `<g filter="url(#${c.id}n)">${body}</g>` : body) + printed };
|
|
127
134
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Emblem, EmblemLocale, EmblemOptions, EmblemStyle, EmblemTheme, FrameOptions } from "./emblem.core.ts";
|
|
2
|
-
import { type BallId, type CrestShape, type EmblemId, type MedalKind, type SportName } from "./emblem.ids.ts";
|
|
2
|
+
import { type BallId, type PuckId, type CrestShape, type EmblemId, type MedalKind, type SportName } from "./emblem.ids.ts";
|
|
3
3
|
import type { PieceSize } from "./ball/ball.core.ts";
|
|
4
4
|
import { type EmblemDescription } from "./emblem.descriptions.ts";
|
|
5
5
|
/** What can name an emblem: an id, a short name (`"cup"` for `"trophy:cup"`), either with a style (`"trophy:cup/line"`), or the emblem itself. */
|
|
@@ -40,10 +40,10 @@ export declare const emblemSvg: typeof renderEmblem;
|
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
42
|
export declare function emblemFrame(ref: EmblemRef, options?: FrameOptions): string;
|
|
43
|
-
/** Whether a value is the id of a ball. */
|
|
44
|
-
export declare const isBallId: (value: unknown) => value is BallId;
|
|
45
|
-
/** A ball rolled: the same as `emblemFrame`, for a ball's id only. A value that is not a ball is an error. */
|
|
46
|
-
export declare function ballFrame(id: BallId, options?: FrameOptions): string;
|
|
43
|
+
/** Whether a value is the id of a ball (a printed puck, `puck:canada`, is a ball too). */
|
|
44
|
+
export declare const isBallId: (value: unknown) => value is BallId | PuckId;
|
|
45
|
+
/** A ball rolled: the same as `emblemFrame`, for a ball's id only (a printed puck's too). A value that is not a ball is an error. */
|
|
46
|
+
export declare function ballFrame(id: BallId | PuckId, options?: FrameOptions): string;
|
|
47
47
|
/** Every emblem as markup in the metal style at 64 pixels, by id. Use `renderEmblem` for another style or size. */
|
|
48
48
|
export declare const EMBLEMS: Readonly<Record<EmblemId, string>>;
|
|
49
49
|
/** What an emblem is, without its drawing. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EMBLEM_STYLES } from "./emblem.core.js";
|
|
2
|
-
import { BALL_IDS, CREST_SHAPES, EMBLEM_ALIASES, EMBLEM_IDS } from "./emblem.ids.js";
|
|
2
|
+
import { BALL_IDS, CREST_SHAPES, EMBLEM_ALIASES, EMBLEM_IDS, PUCK_IDS } from "./emblem.ids.js";
|
|
3
3
|
import { EMBLEM_DESCRIPTIONS } from "./emblem.descriptions.js";
|
|
4
4
|
import { EMBLEM_TABLE } from "./emblem.registry.js";
|
|
5
5
|
/** The id and style a string names, or `undefined` when it is not an emblem. An id may end in `/metal`, `/line` or `/color`, and a trophy or an award may be written without its group (`cup`, `crown`). */
|
|
@@ -35,6 +35,22 @@ export function emblemOf(ref) {
|
|
|
35
35
|
return { emblem: EMBLEM_TABLE[parsed.id], style: parsed.style };
|
|
36
36
|
}
|
|
37
37
|
const withStyle = (style, options) => (style === undefined || options?.style !== undefined ? options : { ...options, style });
|
|
38
|
+
/**
|
|
39
|
+
* The print a puck is asked for, with an emblem id turned into the drawing it names (a puck module cannot see the registry, so it is done here) and a plain `ball:puck` that was given a face drawn as the souvenir puck,
|
|
40
|
+
* which is the generic one. Anything else is as it came.
|
|
41
|
+
*/
|
|
42
|
+
function printed(emblem, options) {
|
|
43
|
+
const face = options?.face;
|
|
44
|
+
if (typeof face !== "object" || face === null)
|
|
45
|
+
return { emblem, options };
|
|
46
|
+
const target = emblem.id === "ball:puck" ? EMBLEM_TABLE["puck:souvenir"] : emblem;
|
|
47
|
+
if (!target.id.startsWith("puck:"))
|
|
48
|
+
return { emblem, options };
|
|
49
|
+
const id = typeof face.emblem === "string" ? parseEmblem(face.emblem)?.id : undefined;
|
|
50
|
+
// The emblem is drawn flat in colour at a size that is plenty for the middle of a puck; a puck inside a puck is left plain (no face passed on).
|
|
51
|
+
const inner = id === undefined || face.image !== undefined ? undefined : EMBLEM_TABLE[id].svg({ title: false, style: "metal", grain: false, size: 160 });
|
|
52
|
+
return { emblem: target, options: inner === undefined ? options : { ...options, face: { ...face, image: inner } } };
|
|
53
|
+
}
|
|
38
54
|
/**
|
|
39
55
|
* An emblem as a document of SVG: `renderEmblem("award:medal")`, `renderEmblem("ball:soccer/line", { size: 20 })`. `style` is `metal` (full colour with gradients, the default), `line` (one outline in the
|
|
40
56
|
* page's text colour) or `color` (flat fills); `tint` replaces the main colour; `theme` is `inherit` (the default), `auto`, `light` or `dark`. A pure function: the same arguments give the same string.
|
|
@@ -48,7 +64,8 @@ const withStyle = (style, options) => (style === undefined || options?.style !==
|
|
|
48
64
|
*/
|
|
49
65
|
export function renderEmblem(ref, options) {
|
|
50
66
|
const { emblem, style } = emblemOf(ref);
|
|
51
|
-
|
|
67
|
+
const p = printed(emblem, withStyle(style, options));
|
|
68
|
+
return p.emblem.svg(p.options);
|
|
52
69
|
}
|
|
53
70
|
/** The same as `renderEmblem`. */
|
|
54
71
|
export const emblemSvg = renderEmblem;
|
|
@@ -63,15 +80,17 @@ export const emblemSvg = renderEmblem;
|
|
|
63
80
|
*/
|
|
64
81
|
export function emblemFrame(ref, options) {
|
|
65
82
|
const { emblem, style } = emblemOf(ref);
|
|
66
|
-
|
|
83
|
+
const p = printed(emblem, withStyle(style, options));
|
|
84
|
+
return p.emblem.frame(p.options);
|
|
67
85
|
}
|
|
68
|
-
/** Whether a value is the id of a ball. */
|
|
69
|
-
export const isBallId = (value) => typeof value === "string" && BALL_IDS.includes(value);
|
|
70
|
-
/** A ball rolled: the same as `emblemFrame`, for a ball's id only. A value that is not a ball is an error. */
|
|
86
|
+
/** Whether a value is the id of a ball (a printed puck, `puck:canada`, is a ball too). */
|
|
87
|
+
export const isBallId = (value) => typeof value === "string" && (BALL_IDS.includes(value) || PUCK_IDS.includes(value));
|
|
88
|
+
/** A ball rolled: the same as `emblemFrame`, for a ball's id only (a printed puck's too). A value that is not a ball is an error. */
|
|
71
89
|
export function ballFrame(id, options) {
|
|
72
90
|
if (!isBallId(id))
|
|
73
|
-
throw new Error(`"${String(id).slice(0, 60)}" is not a ball: ids look like "ball:soccer"; see BALL_IDS.`);
|
|
74
|
-
|
|
91
|
+
throw new Error(`"${String(id).slice(0, 60)}" is not a ball: ids look like "ball:soccer" or "puck:canada"; see BALL_IDS and PUCK_IDS.`);
|
|
92
|
+
const p = printed(EMBLEM_TABLE[id], options);
|
|
93
|
+
return p.emblem.frame(p.options);
|
|
75
94
|
}
|
|
76
95
|
/** Every emblem as markup in the metal style at 64 pixels, by id. Use `renderEmblem` for another style or size. */
|
|
77
96
|
export const EMBLEMS = Object.freeze(Object.fromEntries(EMBLEM_IDS.map((id) => [id, EMBLEM_TABLE[id].svg()])));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PuckFace } from "./ball/ball.face.ts";
|
|
1
2
|
/** `metal`: full colour with a gradient. `line`: one outline in `currentColor`. `color`: flat fills of the main colour, with no gradient. */
|
|
2
3
|
export type EmblemStyle = "metal" | "line" | "color";
|
|
3
4
|
export declare const EMBLEM_STYLES: readonly ["metal", "line", "color"];
|
|
@@ -50,6 +51,11 @@ export interface EmblemOptions {
|
|
|
50
51
|
* box and every turned frame (`frame`) is in the box of the turn, so nothing is cut off while it turns and the size does not change from one angle to the next.
|
|
51
52
|
*/
|
|
52
53
|
turnBox?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The print on the top face of a puck: the printed pucks (`puck:*`) start from a face of their own and this replaces any part of it, and `ball:puck` given a face is drawn as `puck:souvenir` with it. `image` takes any SVG
|
|
56
|
+
* (made safe first), `emblem` a dougu id, `flag` a country code (`ca` and `us` are drawn here; another needs `flags`, a renderer like hata's). See `PuckFace`. Anything that is not a puck ignores it.
|
|
57
|
+
*/
|
|
58
|
+
face?: PuckFace;
|
|
53
59
|
}
|
|
54
60
|
/** A turn, in degrees from the resting view (0 and 0 is the still drawing): `yaw` about the vertical axis, `pitch` the tilt toward the eye. */
|
|
55
61
|
export interface FrameOptions extends EmblemOptions {
|
|
@@ -52,6 +52,14 @@ export const EMBLEM_DESCRIPTIONS = {
|
|
|
52
52
|
"ball:squash-blue": { en: "Squash ball, blue dot", ja: "スカッシュボール(ブルードット)" },
|
|
53
53
|
"ball:shuttlecock-feather": { en: "Shuttlecock, feather", ja: "シャトルコック(水鳥の羽根)" },
|
|
54
54
|
"ball:shuttlecock-nylon": { en: "Shuttlecock, nylon", ja: "シャトルコック(ナイロン)" },
|
|
55
|
+
"puck:canada": { en: "Black puck printed with our own flag of the Canadian pattern, CANADA under it and a red ring of words", ja: "黒いパックの天面に、独自に描いたカナダ風の旗とその下の CANADA の文字、縁には文字の入った赤い輪を印刷したもの" },
|
|
56
|
+
"puck:canada-red": { en: "Black puck printed with a red face, a big white maple leaf of our own drawing, CANADA under it and a dark ring of words", ja: "黒いパックの天面に、赤地に白い大きなメープルの葉(独自の絵)、その下の CANADA の文字、縁には文字の入った暗い輪を印刷したもの" },
|
|
57
|
+
"puck:canada-white": { en: "Black puck printed with a white face, red bands left and right, a big red maple leaf and CANADA under it", ja: "黒いパックの天面に、白地に左右の赤い帯、大きな赤いメープルの葉とその下の CANADA の文字を印刷したもの" },
|
|
58
|
+
"puck:usa": { en: "Black puck printed with a disc of stars and stripes, USA lettered across it and a navy ring of words", ja: "黒いパックの天面に、星と縞模様の円盤、その上に大きく書いた USA の文字、縁には文字の入った紺の輪を印刷したもの" },
|
|
59
|
+
"puck:usa-navy": { en: "Black puck printed with a navy face, a red ring with a white rim, five stars and USA in big white letters", ja: "黒いパックの天面に、紺地に白い細線にはさまれた赤い輪、5 つの星、大きな白い USA の文字を印刷したもの" },
|
|
60
|
+
"puck:player": { en: "Black puck printed like a jersey: a made-up surname round the top, a big number and a gold ring", ja: "ジャージのように、縁の上側に沿った架空の姓、大きな背番号、金色の輪を印刷した黒いパック" },
|
|
61
|
+
"puck:signed": { en: "The navy puck with a made-up gold marker signature across the face, drawn by a seeded pen and never a real person's hand", ja: "紺地のパックの天面に、金色のマーカーで書いたような架空のサインを重ねたもの(シード値から描くので、実在の人物の筆跡ではない)" },
|
|
62
|
+
"puck:souvenir": { en: "A generic souvenir puck: a cream face, a navy ring of words you choose and a star, or your own picture", ja: "汎用の記念パック。クリーム色の地に、好きな文字を入れた紺の輪と星(または自分で用意した絵)" },
|
|
55
63
|
"gear:hockey-stick": { en: "Hockey stick and puck", ja: "アイスホッケーのスティックとパック" },
|
|
56
64
|
"gear:goalie-stick": { en: "Ice hockey goalie stick", ja: "アイスホッケーのゴーリーのスティック" },
|
|
57
65
|
"gear:hockey-skate": { en: "Hockey skate", ja: "アイスホッケーのスケート靴" },
|
|
@@ -20,6 +20,9 @@ export type CrestId = (typeof CREST_IDS)[number];
|
|
|
20
20
|
/** The balls: one for each ball and implement the sports use, in 3D, with the official size it comes from. The balls of one family (the three crickets, the five squash balls) share a module. */
|
|
21
21
|
export declare const BALL_IDS: readonly ["ball:soccer", "ball:soccer-colour", "ball:soccer-26", "ball:soccer-18", "ball:soccer-14", "ball:soccer-12", "ball:soccer-6", "ball:soccer-orange", "ball:soccer-26-orange", "ball:soccer-18-orange", "ball:soccer-14-orange", "ball:soccer-12-orange", "ball:soccer-6-orange", "ball:soccer-yellow", "ball:soccer-26-yellow", "ball:soccer-18-yellow", "ball:soccer-14-yellow", "ball:soccer-12-yellow", "ball:soccer-6-yellow", "ball:futsal", "ball:beach-soccer", "ball:teqball", "ball:basketball", "ball:basketball-6", "ball:basketball-3x3", "ball:baseball", "ball:softball", "ball:tennis", "ball:padel", "ball:sliotar", "ball:volleyball", "ball:volleyball-8", "ball:beach-volleyball", "ball:cricket-red", "ball:cricket-white", "ball:cricket-pink", "ball:football", "ball:football-cfl", "ball:football-ifaf", "ball:rugby", "ball:afl", "ball:gaelic-football", "ball:netball", "ball:pickleball-outdoor", "ball:pickleball-indoor", "ball:squash", "ball:squash-yellow", "ball:squash-green", "ball:squash-red", "ball:squash-blue", "ball:racquetball", "ball:lacrosse", "ball:field-hockey", "ball:table-tennis", "ball:handball", "ball:water-polo", "ball:golf", "ball:puck", "ball:curling-stone", "ball:shot", "ball:discus", "ball:hammer", "ball:sepak-takraw", "ball:roundnet", "ball:shuttlecock-feather", "ball:shuttlecock-nylon"];
|
|
22
22
|
export type BallId = (typeof BALL_IDS)[number];
|
|
23
|
+
/** The printed pucks: the same puck as `ball:puck` with a print on its top face (a flag, a leaf, stars and stripes, a player's name and number, a made-up signature, a souvenir's ring of words). All drawn here, none a team's or a league's own. */
|
|
24
|
+
export declare const PUCK_IDS: readonly ["puck:canada", "puck:canada-red", "puck:canada-white", "puck:usa", "puck:usa-navy", "puck:player", "puck:signed", "puck:souvenir"];
|
|
25
|
+
export type PuckId = (typeof PUCK_IDS)[number];
|
|
23
26
|
/** The pieces of gear: a piece of equipment for each sport, flat, drawn for this package, turning like a coin. */
|
|
24
27
|
export declare const GEAR_IDS: readonly ["gear:hockey-stick", "gear:goalie-stick", "gear:hockey-skate", "gear:goalie-mask", "gear:goal-net", "gear:field-hockey-stick", "gear:lacrosse-stick", "gear:cricket-bat", "gear:wicket", "gear:table-tennis-paddle", "gear:badminton-racket", "gear:tennis-racket", "gear:squash-racket", "gear:racquetball-racket", "gear:padel-racket", "gear:pickleball-paddle", "gear:roundnet-set", "gear:volleyball-net", "gear:basketball-hoop", "gear:baseball-bat", "gear:baseball-glove", "gear:football-helmet", "gear:goalposts", "gear:hurley", "gear:curling-broom", "gear:figure-skate", "gear:speed-skate", "gear:diving-board", "gear:swim-goggles", "gear:starting-blocks", "gear:relay-baton", "gear:javelin", "gear:hurdles", "gear:gunbai", "gear:mawashi", "gear:shinai", "gear:kendo-men", "gear:judogi", "gear:obi", "gear:yumi", "gear:ya", "gear:karate-gi"];
|
|
25
28
|
export type GearId = (typeof GEAR_IDS)[number];
|
|
@@ -33,7 +36,7 @@ export type PennantId = (typeof PENNANT_IDS)[number];
|
|
|
33
36
|
export type SportEmblemId = `sport:${SportName}`;
|
|
34
37
|
export declare const SPORT_IDS: readonly SportEmblemId[];
|
|
35
38
|
/** Every emblem id. */
|
|
36
|
-
export type EmblemId = AwardId | TrophyId | MedalId | CrestId | BallId | GearId | SportEmblemId | PennantId;
|
|
39
|
+
export type EmblemId = AwardId | TrophyId | MedalId | CrestId | BallId | PuckId | GearId | SportEmblemId | PennantId;
|
|
37
40
|
/** The groups of ids, in the order a gallery shows them. */
|
|
38
41
|
export declare const EMBLEM_GROUPS: {
|
|
39
42
|
readonly award: readonly ["award:medal", "award:badge", "award:crown", "award:star", "award:shield"];
|
|
@@ -41,6 +44,7 @@ export declare const EMBLEM_GROUPS: {
|
|
|
41
44
|
readonly medal: readonly ["medal:gold", "medal:silver", "medal:bronze", "medal:place-1", "medal:place-2", "medal:place-3"];
|
|
42
45
|
readonly crest: readonly ["crest:shield", "crest:roundel", "crest:diamond", "crest:pennant"];
|
|
43
46
|
readonly ball: readonly ["ball:soccer", "ball:soccer-colour", "ball:soccer-26", "ball:soccer-18", "ball:soccer-14", "ball:soccer-12", "ball:soccer-6", "ball:soccer-orange", "ball:soccer-26-orange", "ball:soccer-18-orange", "ball:soccer-14-orange", "ball:soccer-12-orange", "ball:soccer-6-orange", "ball:soccer-yellow", "ball:soccer-26-yellow", "ball:soccer-18-yellow", "ball:soccer-14-yellow", "ball:soccer-12-yellow", "ball:soccer-6-yellow", "ball:futsal", "ball:beach-soccer", "ball:teqball", "ball:basketball", "ball:basketball-6", "ball:basketball-3x3", "ball:baseball", "ball:softball", "ball:tennis", "ball:padel", "ball:sliotar", "ball:volleyball", "ball:volleyball-8", "ball:beach-volleyball", "ball:cricket-red", "ball:cricket-white", "ball:cricket-pink", "ball:football", "ball:football-cfl", "ball:football-ifaf", "ball:rugby", "ball:afl", "ball:gaelic-football", "ball:netball", "ball:pickleball-outdoor", "ball:pickleball-indoor", "ball:squash", "ball:squash-yellow", "ball:squash-green", "ball:squash-red", "ball:squash-blue", "ball:racquetball", "ball:lacrosse", "ball:field-hockey", "ball:table-tennis", "ball:handball", "ball:water-polo", "ball:golf", "ball:puck", "ball:curling-stone", "ball:shot", "ball:discus", "ball:hammer", "ball:sepak-takraw", "ball:roundnet", "ball:shuttlecock-feather", "ball:shuttlecock-nylon"];
|
|
47
|
+
readonly puck: readonly ["puck:canada", "puck:canada-red", "puck:canada-white", "puck:usa", "puck:usa-navy", "puck:player", "puck:signed", "puck:souvenir"];
|
|
44
48
|
readonly gear: readonly ["gear:hockey-stick", "gear:goalie-stick", "gear:hockey-skate", "gear:goalie-mask", "gear:goal-net", "gear:field-hockey-stick", "gear:lacrosse-stick", "gear:cricket-bat", "gear:wicket", "gear:table-tennis-paddle", "gear:badminton-racket", "gear:tennis-racket", "gear:squash-racket", "gear:racquetball-racket", "gear:padel-racket", "gear:pickleball-paddle", "gear:roundnet-set", "gear:volleyball-net", "gear:basketball-hoop", "gear:baseball-bat", "gear:baseball-glove", "gear:football-helmet", "gear:goalposts", "gear:hurley", "gear:curling-broom", "gear:figure-skate", "gear:speed-skate", "gear:diving-board", "gear:swim-goggles", "gear:starting-blocks", "gear:relay-baton", "gear:javelin", "gear:hurdles", "gear:gunbai", "gear:mawashi", "gear:shinai", "gear:kendo-men", "gear:judogi", "gear:obi", "gear:yumi", "gear:ya", "gear:karate-gi"];
|
|
45
49
|
readonly sport: readonly ("sport:sumo" | "sport:kendo" | "sport:judo" | "sport:kyudo" | "sport:karate" | "sport:nfl" | "sport:cfl" | "sport:ifaf" | "sport:nhl" | "sport:nba" | "sport:mlb" | "sport:soccer" | "sport:curling" | "sport:tennis" | "sport:volleyball" | "sport:badminton" | "sport:pickleball" | "sport:tabletennis" | "sport:padel" | "sport:squash" | "sport:racquetball" | "sport:sepaktakraw" | "sport:teqball" | "sport:roundnet" | "sport:netball" | "sport:swimming" | "sport:waterpolo" | "sport:diving" | "sport:artisticswimming" | "sport:lacrosse" | "sport:rugby" | "sport:afl" | "sport:gaelic" | "sport:cricket" | "sport:fieldhockey" | "sport:handball" | "sport:athletics" | "sport:skating")[];
|
|
46
50
|
readonly pennant: readonly ["pennant:championship"];
|
|
@@ -55,6 +59,7 @@ export declare const EMBLEM_FOLDERS: {
|
|
|
55
59
|
readonly medal: "medal";
|
|
56
60
|
readonly crest: "crest";
|
|
57
61
|
readonly ball: "ball";
|
|
62
|
+
readonly puck: "ball";
|
|
58
63
|
readonly gear: "gear";
|
|
59
64
|
readonly sport: "sport";
|
|
60
65
|
readonly pennant: "pennant";
|
|
@@ -30,6 +30,8 @@ export const BALL_IDS = [
|
|
|
30
30
|
"ball:squash-green", "ball:squash-red", "ball:squash-blue", "ball:racquetball", "ball:lacrosse", "ball:field-hockey", "ball:table-tennis", "ball:handball", "ball:water-polo", "ball:golf",
|
|
31
31
|
"ball:puck", "ball:curling-stone", "ball:shot", "ball:discus", "ball:hammer", "ball:sepak-takraw", "ball:roundnet", "ball:shuttlecock-feather", "ball:shuttlecock-nylon"
|
|
32
32
|
];
|
|
33
|
+
/** The printed pucks: the same puck as `ball:puck` with a print on its top face (a flag, a leaf, stars and stripes, a player's name and number, a made-up signature, a souvenir's ring of words). All drawn here, none a team's or a league's own. */
|
|
34
|
+
export const PUCK_IDS = ["puck:canada", "puck:canada-red", "puck:canada-white", "puck:usa", "puck:usa-navy", "puck:player", "puck:signed", "puck:souvenir"];
|
|
33
35
|
/** The pieces of gear: a piece of equipment for each sport, flat, drawn for this package, turning like a coin. */
|
|
34
36
|
export const GEAR_IDS = [
|
|
35
37
|
"gear:hockey-stick", "gear:goalie-stick", "gear:hockey-skate", "gear:goalie-mask", "gear:goal-net", "gear:field-hockey-stick", "gear:lacrosse-stick", "gear:cricket-bat", "gear:wicket",
|
|
@@ -57,6 +59,7 @@ export const EMBLEM_GROUPS = {
|
|
|
57
59
|
medal: MEDAL_IDS,
|
|
58
60
|
crest: CREST_IDS,
|
|
59
61
|
ball: BALL_IDS,
|
|
62
|
+
puck: PUCK_IDS,
|
|
60
63
|
gear: GEAR_IDS,
|
|
61
64
|
sport: SPORT_IDS,
|
|
62
65
|
pennant: PENNANT_IDS,
|
|
@@ -64,6 +67,6 @@ export const EMBLEM_GROUPS = {
|
|
|
64
67
|
/** Every id, in group order. */
|
|
65
68
|
export const EMBLEM_IDS = Object.values(EMBLEM_GROUPS).flat();
|
|
66
69
|
/** The folder under `src/emblems/` that holds a group's art, one module per item named by the id's last part: `trophy:hockey-cup` is `trophy/hockey-cup.ts`, `ball:soccer` is `ball/soccer.ts`. Each group has a folder of its own, so no two ids share a file name. */
|
|
67
|
-
export const EMBLEM_FOLDERS = { award: "award", trophy: "trophy", medal: "medal", crest: "crest", ball: "ball", gear: "gear", sport: "sport", pennant: "pennant" };
|
|
70
|
+
export const EMBLEM_FOLDERS = { award: "award", trophy: "trophy", medal: "medal", crest: "crest", ball: "ball", puck: "ball", gear: "gear", sport: "sport", pennant: "pennant" };
|
|
68
71
|
/** The short names of the trophies and awards that stand for a full id: `"cup"` is `"trophy:cup"` and `"crown"` is `"award:crown"`. */
|
|
69
72
|
export const EMBLEM_ALIASES = Object.fromEntries([...TROPHY_IDS, ...AWARD_IDS].map((id) => [id.slice(id.indexOf(":") + 1), id]));
|