@johnmorrisdotca/dougu-sports 0.7.2 → 0.9.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/NOTICE.md +1 -1
  3. package/README.md +58 -28
  4. package/dist/emblems/ball/ball.baseball.js +2 -1
  5. package/dist/emblems/ball/ball.contrast.d.ts +4 -0
  6. package/dist/emblems/ball/ball.contrast.js +22 -0
  7. package/dist/emblems/ball/ball.core.d.ts +42 -0
  8. package/dist/emblems/ball/ball.core.js +34 -1
  9. package/dist/emblems/ball/ball.engine.d.ts +70 -4
  10. package/dist/emblems/ball/ball.engine.js +70 -20
  11. package/dist/emblems/ball/ball.family.seamed.d.ts +4 -0
  12. package/dist/emblems/ball/ball.family.seamed.js +42 -7
  13. package/dist/emblems/ball/ball.image.d.ts +8 -0
  14. package/dist/emblems/ball/ball.image.js +14 -5
  15. package/dist/emblems/ball/ball.index.js +2 -4
  16. package/dist/emblems/ball/ball.letters.d.ts +3 -0
  17. package/dist/emblems/ball/ball.letters.js +51 -0
  18. package/dist/emblems/ball/ball.print.d.ts +81 -0
  19. package/dist/emblems/ball/ball.print.js +268 -0
  20. package/dist/emblems/ball/ball.printable.d.ts +6 -0
  21. package/dist/emblems/ball/ball.printable.js +13 -0
  22. package/dist/emblems/ball/ball.region.d.ts +12 -0
  23. package/dist/emblems/ball/ball.region.js +94 -0
  24. package/dist/emblems/ball/ball.script.d.ts +15 -0
  25. package/dist/emblems/ball/ball.script.js +107 -0
  26. package/dist/emblems/ball/ball.soccer.js +7 -3
  27. package/dist/emblems/ball/ball.soccer.panels.d.ts +2 -0
  28. package/dist/emblems/ball/ball.soccer.panels.js +6 -3
  29. package/dist/emblems/ball/ball.soccer.size.d.ts +2 -0
  30. package/dist/emblems/ball/ball.soccer.size.js +8 -0
  31. package/dist/emblems/ball/ball.softball.js +2 -1
  32. package/dist/emblems/ball/ball.warp.d.ts +48 -0
  33. package/dist/emblems/ball/ball.warp.js +441 -0
  34. package/dist/emblems/emblem.core.d.ts +13 -0
  35. package/dist/emblems-entry.d.ts +1 -0
  36. package/dist/index.d.ts +5 -5
  37. package/dist/index.js +4 -5
  38. package/dist/turn-control.d.ts +24 -5
  39. package/dist/turn-control.js +67 -23
  40. package/dist/turn-full.d.ts +15 -0
  41. package/dist/turn-full.js +19 -0
  42. package/dist/turn-registry.d.ts +1 -0
  43. package/dist/turn-registry.js +3 -0
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.js +1 -1
  46. package/package.json +10 -10
@@ -0,0 +1,48 @@
1
+ import type { Surface } from "./ball.engine.ts";
2
+ import type { SafeImage } from "./ball.image.ts";
3
+ import type { V3 } from "./ball.math.ts";
4
+ export type P2 = readonly [number, number];
5
+ /** A flat shape of a drawing: its outlines, its paint, and the width of its stroke, in the drawing's own units. */
6
+ export interface Shape {
7
+ subs: Array<{
8
+ pts: P2[];
9
+ closed: boolean;
10
+ }>;
11
+ fill: string | undefined;
12
+ stroke: string | undefined;
13
+ /** The stroke's width, already scaled by the transforms the shape is under. */
14
+ width: number;
15
+ fillOpacity: number;
16
+ strokeOpacity: number;
17
+ evenodd: boolean;
18
+ }
19
+ /**
20
+ * The outlines of a path's data, curves flattened to `steps` segments at most. M, L, H, V, C, S, Q, T, A and Z, absolute and relative. A number that is not one ends the path.
21
+ */
22
+ export declare function flattenPath(d: string, steps?: number): Array<{
23
+ pts: P2[];
24
+ closed: boolean;
25
+ }>;
26
+ /**
27
+ * The shapes of a safe drawing in order, with the paint each one ends up with. A gradient is the mean of its stops, `currentColor` is `ink`, an opacity multiplies down the groups, a clip path is not applied,
28
+ * and what is in `defs` or a `clipPath` is not drawn. `box` is the drawing's own box, to size the curves' samples.
29
+ */
30
+ export declare function shapesOf(img: SafeImage, ink: string): Shape[];
31
+ /** A polyline with points added so that no segment is longer than `step`. */
32
+ export declare function densify(pts: readonly P2[], step: number, closed: boolean): P2[];
33
+ /** What puts a flat point of the drawing on the ball: plane coordinates (y down) to a point of the sphere. */
34
+ export type PlaneMap = (x: number, y: number) => V3;
35
+ /**
36
+ * Shapes drawn on the ball. The drawing's box is fitted into a square of side `size` (radians, in the plane the `map` lays on the ball) and centred; `cap` is the longest segment kept before points are added
37
+ * (radians), so a straight edge curves with the sphere.
38
+ */
39
+ export declare function drawShapes(s: Surface, shapes: readonly Shape[], box: readonly [number, number, number, number], map: PlaneMap, size: number, o?: {
40
+ opacity?: number;
41
+ colour?: string;
42
+ }): string;
43
+ /**
44
+ * A picture that is not a vector, laid on the ball in triangles. The picture is fitted into a square of side `size` radians; `href` is a data URI that was checked by the caller. Each triangle of a grid of
45
+ * `n` by `n` cells is the picture's piece carried by the one affine map that fits its three corners, clipped to the triangle (grown a little so that no seam shows); a triangle with a corner out of
46
+ * sight is left out. Returns the markup, with the picture in a definition of its own.
47
+ */
48
+ export declare function drawPicture(s: Surface, href: string, map: PlaneMap, size: number, id: string, n?: number): string;
@@ -0,0 +1,441 @@
1
+ // A drawing carried onto a sphere. A logo (an SVG made safe by `safeImage`) is flattened into outlines, every point of every outline is put on the ball with the `map` the caller gives (a flat plane
2
+ // laid on the ball at one point), and the engine draws what comes out: the fills as panels cut at the limb, the strokes as lines that thin as they turn away. Nothing is a transform of a flat picture,
3
+ // so the print curves with the ball, is foreshortened as a mark printed on a sphere is, and goes round the limb and out of sight as the ball turns. A picture that is not a vector (a photograph) is laid
4
+ // on in small triangles, each carried by an affine map of its own: close, not exact. Imports only this folder's helpers.
5
+ import { hexOf, num, readColour } from "./ball.core.js";
6
+ const IDENTITY = [1, 0, 0, 1, 0, 0];
7
+ const mul = (a, b) => [a[0] * b[0] + a[2] * b[1], a[1] * b[0] + a[3] * b[1], a[0] * b[2] + a[2] * b[3], a[1] * b[2] + a[3] * b[3], a[0] * b[4] + a[2] * b[5] + a[4], a[1] * b[4] + a[3] * b[5] + a[5]];
8
+ const at = (m, p) => [m[0] * p[0] + m[2] * p[1] + m[4], m[1] * p[0] + m[3] * p[1] + m[5]];
9
+ /** The matrix of a `transform` attribute (matrix, translate, scale, rotate, skewX, skewY); an unreadable part is left out. */
10
+ function transformOf(text) {
11
+ let m = IDENTITY;
12
+ for (const part of (text ?? "").matchAll(/(matrix|translate|scale|rotate|skewX|skewY)\s*\(([^)]*)\)/g)) {
13
+ const a = part[2].trim().split(/[\s,]+/).filter((v) => v !== "").map(Number);
14
+ if (a.some((v) => !Number.isFinite(v)))
15
+ continue;
16
+ const n = (i, d = 0) => a[i] ?? d;
17
+ let t = IDENTITY;
18
+ switch (part[1]) {
19
+ case "matrix":
20
+ if (a.length === 6)
21
+ t = [n(0), n(1), n(2), n(3), n(4), n(5)];
22
+ break;
23
+ case "translate":
24
+ t = [1, 0, 0, 1, n(0), n(1)];
25
+ break;
26
+ case "scale":
27
+ t = [n(0, 1), 0, 0, n(1, n(0, 1)), 0, 0];
28
+ break;
29
+ case "rotate": {
30
+ const r = (n(0) * Math.PI) / 180, c = Math.cos(r), s = Math.sin(r);
31
+ t = mul(mul([1, 0, 0, 1, n(1), n(2)], [c, s, -s, c, 0, 0]), [1, 0, 0, 1, -n(1), -n(2)]);
32
+ break;
33
+ }
34
+ case "skewX":
35
+ t = [1, 0, Math.tan((n(0) * Math.PI) / 180), 1, 0, 0];
36
+ break;
37
+ default:
38
+ t = [1, Math.tan((n(0) * Math.PI) / 180), 0, 1, 0, 0];
39
+ }
40
+ m = mul(m, t);
41
+ }
42
+ return m;
43
+ }
44
+ const NAMED = { black: "#000000", white: "#ffffff", red: "#ff0000", green: "#008000", blue: "#0000ff", yellow: "#ffff00", orange: "#ffa500", gray: "#808080", grey: "#808080", navy: "#000080", maroon: "#800000", purple: "#800080", teal: "#008080", silver: "#c0c0c0", gold: "#ffd700", lime: "#00ff00", aqua: "#00ffff", fuchsia: "#ff00ff", brown: "#a52a2a", pink: "#ffc0cb" };
45
+ /** Numbers of a path or a list, in order. */
46
+ const numbers = (text) => (text.match(/-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/gi) ?? []).map(Number).filter(Number.isFinite);
47
+ /**
48
+ * The outlines of a path's data, curves flattened to `steps` segments at most. M, L, H, V, C, S, Q, T, A and Z, absolute and relative. A number that is not one ends the path.
49
+ */
50
+ export function flattenPath(d, steps = 10) {
51
+ const subs = [];
52
+ const tok = /([MmLlHhVvCcSsQqTtAaZz])|(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)/gi;
53
+ const items = [];
54
+ // Arc flags may be written with no space after them ("a1 1 0 00.5.5"); the tokens are read by hand where an arc is.
55
+ let m;
56
+ let i = 0;
57
+ const upper = (c) => c.toUpperCase();
58
+ while ((m = tok.exec(d)) !== null) {
59
+ if (m[1] !== undefined)
60
+ items.push(m[1]);
61
+ else
62
+ items.push(Number(m[2]));
63
+ i++;
64
+ if (i > 40000)
65
+ break;
66
+ }
67
+ void numbers;
68
+ let x = 0, y = 0, sx = 0, sy = 0, cmd = "";
69
+ let cur;
70
+ let lastC, lastQ;
71
+ let k = 0;
72
+ const nextNum = () => {
73
+ const v = items[k];
74
+ if (typeof v === "number") {
75
+ k++;
76
+ return v;
77
+ }
78
+ return undefined;
79
+ };
80
+ const start = (px, py) => {
81
+ cur = { pts: [[px, py]], closed: false };
82
+ subs.push(cur);
83
+ };
84
+ const line = (px, py) => {
85
+ if (cur === undefined)
86
+ start(x, y);
87
+ cur.pts.push([px, py]);
88
+ x = px;
89
+ y = py;
90
+ };
91
+ const cubic = (x1, y1, x2, y2, x3, y3) => {
92
+ const n = Math.max(3, Math.min(steps, Math.ceil((Math.hypot(x1 - x, y1 - y) + Math.hypot(x2 - x1, y2 - y1) + Math.hypot(x3 - x2, y3 - y2)) / 6) + 3));
93
+ const [x0, y0] = [x, y];
94
+ for (let j = 1; j <= n; j++) {
95
+ const t = j / n, u = 1 - t;
96
+ line(u * u * u * x0 + 3 * u * u * t * x1 + 3 * u * t * t * x2 + t * t * t * x3, u * u * u * y0 + 3 * u * u * t * y1 + 3 * u * t * t * y2 + t * t * t * y3);
97
+ }
98
+ lastC = [x2, y2];
99
+ };
100
+ while (k < items.length) {
101
+ const it = items[k];
102
+ if (typeof it === "string") {
103
+ cmd = it;
104
+ k++;
105
+ if (upper(cmd) === "Z") {
106
+ if (cur !== undefined)
107
+ cur.closed = true;
108
+ x = sx;
109
+ y = sy;
110
+ cur = undefined;
111
+ lastC = lastQ = undefined;
112
+ continue;
113
+ }
114
+ }
115
+ else if (cmd === "")
116
+ break;
117
+ else if (cmd === "M")
118
+ cmd = "L";
119
+ else if (cmd === "m")
120
+ cmd = "l";
121
+ const rel = cmd === cmd.toLowerCase();
122
+ const ox = rel ? x : 0, oy = rel ? y : 0;
123
+ const c = upper(cmd);
124
+ const need = { M: 2, L: 2, H: 1, V: 1, C: 6, S: 4, Q: 4, T: 2, A: 7 }[c];
125
+ if (need === undefined)
126
+ break;
127
+ const a = [];
128
+ for (let j = 0; j < need; j++) {
129
+ const v = nextNum();
130
+ if (v === undefined)
131
+ return subs;
132
+ a.push(v);
133
+ }
134
+ const prevC = lastC, prevQ = lastQ;
135
+ lastC = lastQ = undefined;
136
+ if (c === "M") {
137
+ x = ox + a[0];
138
+ y = oy + a[1];
139
+ sx = x;
140
+ sy = y;
141
+ start(x, y);
142
+ }
143
+ else if (c === "L")
144
+ line(ox + a[0], oy + a[1]);
145
+ else if (c === "H")
146
+ line(ox + a[0], y);
147
+ else if (c === "V")
148
+ line(x, oy + a[0]);
149
+ else if (c === "C")
150
+ cubic(ox + a[0], oy + a[1], ox + a[2], oy + a[3], ox + a[4], oy + a[5]);
151
+ else if (c === "S") {
152
+ const r1 = prevC === undefined ? [x, y] : [2 * x - prevC[0], 2 * y - prevC[1]];
153
+ cubic(r1[0], r1[1], ox + a[0], oy + a[1], ox + a[2], oy + a[3]);
154
+ }
155
+ else if (c === "Q" || c === "T") {
156
+ const q = c === "Q" ? [ox + a[0], oy + a[1]] : prevQ === undefined ? [x, y] : [2 * x - prevQ[0], 2 * y - prevQ[1]];
157
+ const e = c === "Q" ? [ox + a[2], oy + a[3]] : [ox + a[0], oy + a[1]];
158
+ cubic(x + (2 / 3) * (q[0] - x), y + (2 / 3) * (q[1] - y), e[0] + (2 / 3) * (q[0] - e[0]), e[1] + (2 / 3) * (q[1] - e[1]), e[0], e[1]);
159
+ lastQ = q;
160
+ lastC = undefined;
161
+ }
162
+ else if (c === "A") {
163
+ const [rx0, ry0, rot, large, sweep] = a;
164
+ const ex = ox + a[5], ey = oy + a[6];
165
+ arc(x, y, Math.abs(rx0), Math.abs(ry0), rot, large !== 0, sweep !== 0, ex, ey).forEach((p) => line(p[0], p[1]));
166
+ }
167
+ }
168
+ return subs;
169
+ }
170
+ /** The points of an elliptical arc (the SVG endpoint form) after its start, as far as its end. */
171
+ function arc(x1, y1, rx, ry, rotDeg, large, sweep, x2, y2) {
172
+ if ((x1 === x2 && y1 === y2) || rx === 0 || ry === 0)
173
+ return [[x2, y2]];
174
+ const phi = (rotDeg * Math.PI) / 180, cp = Math.cos(phi), sp = Math.sin(phi);
175
+ const dx = (x1 - x2) / 2, dy = (y1 - y2) / 2;
176
+ const x1p = cp * dx + sp * dy, y1p = -sp * dx + cp * dy;
177
+ const lam = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);
178
+ if (lam > 1) {
179
+ rx *= Math.sqrt(lam);
180
+ ry *= Math.sqrt(lam);
181
+ }
182
+ const num2 = rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p;
183
+ const den = rx * rx * y1p * y1p + ry * ry * x1p * x1p;
184
+ const co = (large === sweep ? -1 : 1) * Math.sqrt(Math.max(0, num2 / den));
185
+ const cxp = (co * rx * y1p) / ry, cyp = (-co * ry * x1p) / rx;
186
+ const cx = cp * cxp - sp * cyp + (x1 + x2) / 2, cy = sp * cxp + cp * cyp + (y1 + y2) / 2;
187
+ const ang = (ux, uy, vx, vy) => Math.atan2(ux * vy - uy * vx, ux * vx + uy * vy);
188
+ const t1 = ang(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
189
+ let dt = ang((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry);
190
+ if (!sweep && dt > 0)
191
+ dt -= 2 * Math.PI;
192
+ if (sweep && dt < 0)
193
+ dt += 2 * Math.PI;
194
+ const n = Math.max(3, Math.ceil(Math.abs(dt) / (Math.PI / 14)));
195
+ const out = [];
196
+ for (let j = 1; j <= n; j++) {
197
+ const t = t1 + (dt * j) / n;
198
+ out.push([cp * rx * Math.cos(t) - sp * ry * Math.sin(t) + cx, sp * rx * Math.cos(t) + cp * ry * Math.sin(t) + cy]);
199
+ }
200
+ out[n - 1] = [x2, y2];
201
+ return out;
202
+ }
203
+ /**
204
+ * The shapes of a safe drawing in order, with the paint each one ends up with. A gradient is the mean of its stops, `currentColor` is `ink`, an opacity multiplies down the groups, a clip path is not applied,
205
+ * and what is in `defs` or a `clipPath` is not drawn. `box` is the drawing's own box, to size the curves' samples.
206
+ */
207
+ export function shapesOf(img, ink) {
208
+ const grads = new Map();
209
+ // The mean colour of each gradient, from its stops.
210
+ let gid;
211
+ let stops = [];
212
+ const finish = () => {
213
+ if (gid !== undefined && stops.length > 0)
214
+ grads.set(gid, hexOf([stops.reduce((a, c) => a + c[0], 0) / stops.length, stops.reduce((a, c) => a + c[1], 0) / stops.length, stops.reduce((a, c) => a + c[2], 0) / stops.length]));
215
+ gid = undefined;
216
+ stops = [];
217
+ };
218
+ const colourOf = (value) => {
219
+ if (value === undefined)
220
+ return undefined;
221
+ const v = value.trim();
222
+ if (v === "" || v === "none" || v === "transparent")
223
+ return undefined;
224
+ if (v === "currentColor")
225
+ return ink;
226
+ const ref = /^url\(\s*#([^)\s]+)\s*\)/.exec(v);
227
+ if (ref !== null)
228
+ return grads.get(ref[1]);
229
+ const rgb = readColour(v);
230
+ if (rgb !== undefined)
231
+ return hexOf(rgb);
232
+ return NAMED[v.toLowerCase()];
233
+ };
234
+ // Gradients first, wherever they are, so that a fill may name one defined later.
235
+ for (const n of img.nodes) {
236
+ if (n.kind === "close" && (n.name === "linearGradient" || n.name === "radialGradient"))
237
+ finish();
238
+ else if (n.kind !== "close" && (n.name === "linearGradient" || n.name === "radialGradient")) {
239
+ finish();
240
+ gid = n.attrs["id"];
241
+ if (n.kind === "self")
242
+ gid = undefined;
243
+ }
244
+ else if (n.name === "stop" && gid !== undefined) {
245
+ const c = colourOf(n.attrs["stop-color"] ?? "#000000");
246
+ const rgb = c === undefined ? undefined : readColour(c);
247
+ if (rgb !== undefined)
248
+ stops.push([rgb[0], rgb[1], rgb[2]]);
249
+ }
250
+ }
251
+ finish();
252
+ const scale = Math.max(img.box[2], img.box[3]);
253
+ const out = [];
254
+ const stack = [{ m: IDENTITY, fill: "#000000", stroke: "none", sw: 1, fo: 1, so: 1, op: 1, rule: "nonzero" }];
255
+ let skip = 0;
256
+ const num1 = (v, d) => {
257
+ const n = v === undefined ? NaN : parseFloat(v);
258
+ return Number.isFinite(n) ? n : d;
259
+ };
260
+ const opacityOf = (v) => Math.max(0, Math.min(1, num1(v, 1)));
261
+ for (const n of img.nodes) {
262
+ if (n.kind === "close") {
263
+ if (skip > 0 && (n.name === "defs" || n.name === "clipPath"))
264
+ skip--;
265
+ else if (skip === 0 && stack.length > 1)
266
+ stack.pop();
267
+ continue;
268
+ }
269
+ if (n.name === "defs" || n.name === "clipPath") {
270
+ if (n.kind === "open")
271
+ skip++;
272
+ continue;
273
+ }
274
+ if (skip > 0 || n.name === "linearGradient" || n.name === "radialGradient" || n.name === "stop") {
275
+ if (n.kind === "open" && skip === 0)
276
+ stack.push(stack[stack.length - 1]);
277
+ continue;
278
+ }
279
+ const parent = stack[stack.length - 1];
280
+ const a = n.attrs;
281
+ const st = {
282
+ m: a["transform"] === undefined ? parent.m : mul(parent.m, transformOf(a["transform"])),
283
+ fill: a["fill"] ?? parent.fill,
284
+ stroke: a["stroke"] ?? parent.stroke,
285
+ sw: num1(a["stroke-width"], parent.sw),
286
+ fo: a["fill-opacity"] === undefined ? parent.fo : opacityOf(a["fill-opacity"]),
287
+ so: a["stroke-opacity"] === undefined ? parent.so : opacityOf(a["stroke-opacity"]),
288
+ op: parent.op * opacityOf(a["opacity"]),
289
+ rule: a["fill-rule"] ?? parent.rule,
290
+ };
291
+ if (n.name === "g") {
292
+ if (n.kind === "open")
293
+ stack.push(st);
294
+ continue;
295
+ }
296
+ let subs = [];
297
+ const f = (k, d = 0) => num1(a[k], d);
298
+ const ring = (cx, cy, rx, ry) => Array.from({ length: 48 }, (_, i) => [cx + rx * Math.cos((i * Math.PI) / 24), cy + ry * Math.sin((i * Math.PI) / 24)]);
299
+ switch (n.name) {
300
+ case "path":
301
+ subs = flattenPath(a["d"] ?? "", Math.max(4, Math.min(14, Math.round(scale / 8))));
302
+ break;
303
+ case "rect": {
304
+ const x = f("x"), y = f("y"), w = f("width"), h = f("height");
305
+ if (w > 0 && h > 0)
306
+ subs = [{ pts: [[x, y], [x + w, y], [x + w, y + h], [x, y + h]], closed: true }];
307
+ break;
308
+ }
309
+ case "circle":
310
+ if (f("r") > 0)
311
+ subs = [{ pts: ring(f("cx"), f("cy"), f("r"), f("r")), closed: true }];
312
+ break;
313
+ case "ellipse":
314
+ if (f("rx") > 0 && f("ry") > 0)
315
+ subs = [{ pts: ring(f("cx"), f("cy"), f("rx"), f("ry")), closed: true }];
316
+ break;
317
+ case "line":
318
+ subs = [{ pts: [[f("x1"), f("y1")], [f("x2"), f("y2")]], closed: false }];
319
+ break;
320
+ case "polyline":
321
+ case "polygon": {
322
+ const v = numbers(a["points"] ?? "");
323
+ const pts = [];
324
+ for (let i = 0; i + 1 < v.length; i += 2)
325
+ pts.push([v[i], v[i + 1]]);
326
+ if (pts.length > 1)
327
+ subs = [{ pts, closed: n.name === "polygon" }];
328
+ break;
329
+ }
330
+ default:
331
+ break;
332
+ }
333
+ if (n.kind === "open")
334
+ stack.push(st);
335
+ if (subs.length === 0)
336
+ continue;
337
+ const det = Math.sqrt(Math.abs(st.m[0] * st.m[3] - st.m[1] * st.m[2]));
338
+ out.push({
339
+ subs: subs.map((s) => ({ pts: s.pts.map((p) => at(st.m, p)), closed: s.closed })),
340
+ fill: colourOf(st.fill),
341
+ stroke: colourOf(st.stroke),
342
+ width: st.sw * det,
343
+ fillOpacity: st.fo * st.op,
344
+ strokeOpacity: st.so * st.op,
345
+ evenodd: st.rule === "evenodd",
346
+ });
347
+ }
348
+ return out;
349
+ }
350
+ /** A polyline with points added so that no segment is longer than `step`. */
351
+ export function densify(pts, step, closed) {
352
+ const out = [];
353
+ const n = closed ? pts.length : pts.length - 1;
354
+ for (let i = 0; i < n; i++) {
355
+ const a = pts[i], b = pts[(i + 1) % pts.length];
356
+ const k = Math.max(1, Math.ceil(Math.hypot(b[0] - a[0], b[1] - a[1]) / step));
357
+ for (let j = 0; j < k; j++)
358
+ out.push([a[0] + ((b[0] - a[0]) * j) / k, a[1] + ((b[1] - a[1]) * j) / k]);
359
+ }
360
+ if (!closed)
361
+ out.push(pts[pts.length - 1]);
362
+ return out;
363
+ }
364
+ /**
365
+ * Shapes drawn on the ball. The drawing's box is fitted into a square of side `size` (radians, in the plane the `map` lays on the ball) and centred; `cap` is the longest segment kept before points are added
366
+ * (radians), so a straight edge curves with the sphere.
367
+ */
368
+ export function drawShapes(s, shapes, box, map, size, o) {
369
+ const k = size / Math.max(box[2], box[3]);
370
+ const cx = box[0] + box[2] / 2, cy = box[1] + box[3] / 2;
371
+ const to = (p) => [(p[0] - cx) * k, (p[1] - cy) * k];
372
+ const step = 0.07;
373
+ let out = "";
374
+ for (const sh of shapes) {
375
+ const fill = o?.colour ?? sh.fill;
376
+ const stroke = o?.colour ?? sh.stroke;
377
+ const op = o?.opacity ?? 1;
378
+ if (fill !== undefined && sh.fillOpacity > 0) {
379
+ const rings = sh.subs.filter((q) => q.pts.length > 2).map((q) => densify(q.pts.map(to), step, true).map((p) => map(p[0], p[1])));
380
+ if (rings.length > 0)
381
+ out += s.panels(rings, fill, { n: 1, ink: true, opacity: sh.fillOpacity * op === 1 ? undefined : Math.round(sh.fillOpacity * op * 100) / 100, rule: sh.evenodd ? "evenodd" : undefined });
382
+ }
383
+ if (stroke !== undefined && sh.strokeOpacity > 0 && sh.width > 0) {
384
+ for (const closed of [true, false]) {
385
+ const curves = sh.subs.filter((q) => q.closed === closed && q.pts.length > 1).map((q) => densify(q.pts.map(to), step, closed).map((p) => map(p[0], p[1])));
386
+ if (curves.length > 0)
387
+ out += s.strokes(curves, stroke, sh.width * k * s.radius, { closed, digits: 2, opacity: sh.strokeOpacity * op === 1 ? undefined : Math.round(sh.strokeOpacity * op * 100) / 100 });
388
+ }
389
+ }
390
+ }
391
+ return out;
392
+ }
393
+ /**
394
+ * A picture that is not a vector, laid on the ball in triangles. The picture is fitted into a square of side `size` radians; `href` is a data URI that was checked by the caller. Each triangle of a grid of
395
+ * `n` by `n` cells is the picture's piece carried by the one affine map that fits its three corners, clipped to the triangle (grown a little so that no seam shows); a triangle with a corner out of
396
+ * sight is left out. Returns the markup, with the picture in a definition of its own.
397
+ */
398
+ export function drawPicture(s, href, map, size, id, n = 6) {
399
+ const grid = [];
400
+ for (let iy = 0; iy <= n; iy++) {
401
+ for (let ix = 0; ix <= n; ix++) {
402
+ const u = map((ix / n - 0.5) * size, (iy / n - 0.5) * size);
403
+ const p = s.project(u);
404
+ grid.push([p[0], p[1], p[2], (ix / n) * 100, (iy / n) * 100]);
405
+ }
406
+ }
407
+ const at2 = (ix, iy) => grid[iy * (n + 1) + ix];
408
+ let defs = `<image id="${id}i" width="100" height="100" href="${href}"/>`;
409
+ let body = "";
410
+ let count = 0;
411
+ const tri = (a, b, c) => {
412
+ if (a[2] < 0.04 || b[2] < 0.04 || c[2] < 0.04)
413
+ return;
414
+ // The affine map from picture (u, v) to the screen, from the three corners.
415
+ const det = (b[3] - a[3]) * (c[4] - a[4]) - (c[3] - a[3]) * (b[4] - a[4]);
416
+ if (Math.abs(det) < 1e-9)
417
+ return;
418
+ const m = (p0, p1, p2) => {
419
+ const ma = ((p1 - p0) * (c[4] - a[4]) - (p2 - p0) * (b[4] - a[4])) / det;
420
+ const mb = ((p2 - p0) * (b[3] - a[3]) - (p1 - p0) * (c[3] - a[3])) / det;
421
+ return [ma, mb, p0 - ma * a[3] - mb * a[4]];
422
+ };
423
+ const [m0, m2, m4] = m(a[0], b[0], c[0]);
424
+ const [m1, m3, m5] = m(a[1], b[1], c[1]);
425
+ const gx = (a[0] + b[0] + c[0]) / 3, gy = (a[1] + b[1] + c[1]) / 3;
426
+ const grow = (p) => {
427
+ const dx = p[0] - gx, dy = p[1] - gy, l = Math.hypot(dx, dy) || 1;
428
+ return `${num(p[0] + (dx / l) * 0.3, 2)} ${num(p[1] + (dy / l) * 0.3, 2)}`;
429
+ };
430
+ const cid = `${id}t${count++}`;
431
+ defs += `<clipPath id="${cid}"><path d="M${grow(a)}L${grow(b)}L${grow(c)}Z"/></clipPath>`;
432
+ body += `<g clip-path="url(#${cid})"><use href="#${id}i" transform="matrix(${num(m0, 4)} ${num(m1, 4)} ${num(m2, 4)} ${num(m3, 4)} ${num(m4, 2)} ${num(m5, 2)})"/></g>`;
433
+ };
434
+ for (let iy = 0; iy < n; iy++) {
435
+ for (let ix = 0; ix < n; ix++) {
436
+ tri(at2(ix, iy), at2(ix + 1, iy), at2(ix, iy + 1));
437
+ tri(at2(ix + 1, iy), at2(ix + 1, iy + 1), at2(ix, iy + 1));
438
+ }
439
+ }
440
+ return body === "" ? "" : `<defs>${defs}</defs>${body}`;
441
+ }
@@ -1,5 +1,6 @@
1
1
  import type { FlagRenderer, PuckFace } from "./ball/ball.face.ts";
2
2
  import type { Backnumber } from "./ball/ball.core.ts";
3
+ import type { BallDecal, BallMark, BallPaint, BallScript, BallSpots, BallStamp } from "./ball/ball.print.ts";
3
4
  /** `metal`: full colour with a gradient. `line`: one outline in `currentColor`. `color`: flat fills of the main colour, with no gradient. */
4
5
  export type EmblemStyle = "metal" | "line" | "color";
5
6
  export declare const EMBLEM_STYLES: readonly ["metal", "line", "color"];
@@ -63,6 +64,18 @@ export interface EmblemOptions {
63
64
  flags?: FlagRenderer;
64
65
  /** A judogi seen from behind: the back number sewn on it, `{ name, code, band }` (see `Backnumber`). `gear:judogi-back` and `gear:judogi-blue-back` take it; anything else ignores it. */
65
66
  backnumber?: Backnumber;
67
+ /** A baseball, a softball and the classic soccer balls: the colour of the ball, one colour or two (a baseball two-tone along its seam or in halves). A string is the body colour. See `BallPaint`. Any other ball takes the body colour as a tint. */
68
+ paint?: string | BallPaint;
69
+ /** The same balls: a logo printed on the ball and wrapped onto the sphere, so it turns with the ball. An SVG string (made safe first), a `data:` image URI, or `{ svg, href, size, turn }`. See `BallDecal`. */
70
+ decal?: string | BallDecal;
71
+ /** The same balls: stamped text, like a ball's stamp. `true` is `UNOFFICIAL` under small stars over `DIGITAL BASEBALL` (the ball's own word); a string or a list of strings are your lines; an object adds stars, a made-up signature and the ink. See `BallStamp`. */
72
+ stamp?: boolean | string | readonly string[] | BallStamp;
73
+ /** The same balls: a cursive word (our own script) where a maker's mark sits. `true` is the ball's word (`Baseball`, `Softball`, `Soccer`), a string is your word. See `BallScript`. */
74
+ script?: boolean | string | BallScript;
75
+ /** The same balls: the small mark under the script. `true` is a letter in a circle, a string is that letter or an SVG of your own, an object takes `{ letter, svg, href, colour }`. `false` leaves out the circled letter the script brings. See `BallMark`. */
76
+ mark?: boolean | string | BallMark;
77
+ /** The same balls: where the logo and the lettering go, in degrees from the middle of the label area (see `BallSpots`). */
78
+ spots?: BallSpots;
66
79
  }
67
80
  /** 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. */
68
81
  export interface FrameOptions extends EmblemOptions {
@@ -19,4 +19,5 @@ export { sportEquipment } from "./emblems/emblem.equipment.ts";
19
19
  export type { SportEquipment } from "./emblems/emblem.equipment.ts";
20
20
  export { awardCase, podium } from "./emblems/emblem.display.ts";
21
21
  export type { AwardCaseOptions, AwardItem, DisplayOptions, PodiumOptions, PodiumPlace } from "./emblems/emblem.display.ts";
22
+ export type { BallDecal, BallMark, BallPaint, BallScript, BallSpots, BallStamp, Spot } from "./emblems/ball/ball.print.ts";
22
23
  export type { FaceColors, FlagRenderer, FlagRequest, FlagShape, PuckDesign, PuckFace } from "./emblems/ball/ball.face.ts";
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from "./emblems-entry.ts";
2
2
  export { ballFrame, emblemFrame } from "./emblems/emblem.api.ts";
3
- export { bindTurn } from "./turn-control.ts";
3
+ export { bindTurn } from "./turn-full.ts";
4
4
  export type { TurnAngles, TurnContainer, TurnControlOptions, TurnController, TurnEvent } from "./turn-control.ts";
5
5
  export { EMBLEM_TABLE } from "./emblems/emblem.registry.ts";
6
6
  export { AWARD_MEDAL } from "./emblems/award/medal.ts";
@@ -82,7 +82,9 @@ export { SPORT_JUDO } from "./emblems/sport/judo.ts";
82
82
  export { SPORT_KYUDO } from "./emblems/sport/kyudo.ts";
83
83
  export { SPORT_KARATE } from "./emblems/sport/karate.ts";
84
84
  export { BALL_AFL } from "./emblems/ball/ball.afl.ts";
85
- export { BALL_BASEBALL } from "./emblems/ball/ball.baseball.ts";
85
+ export { BALL_BASEBALL, BALL_SOFTBALL, BALL_SOCCER, BALL_SOCCER_COLOUR, BALL_SOCCER_ORANGE, BALL_SOCCER_YELLOW } from "./emblems/ball/ball.printable.ts";
86
+ export { printed } from "./emblems/ball/ball.print.ts";
87
+ export type { BallDecal, BallMark, BallPaint, BallScript, BallSpots, BallStamp, Spot } from "./emblems/ball/ball.print.ts";
86
88
  export { BALL_BASKETBALL } from "./emblems/ball/ball.basketball.ts";
87
89
  export { BALL_BASKETBALL_3X3 } from "./emblems/ball/ball.basketball3x3.ts";
88
90
  export { BALL_BASKETBALL_6 } from "./emblems/ball/ball.basketball6.ts";
@@ -105,15 +107,13 @@ export { BALL_SEPAK_TAKRAW } from "./emblems/ball/ball.sepaktakraw.ts";
105
107
  export { BALL_SHUTTLECOCK_FEATHER, BALL_SHUTTLECOCK_NYLON } from "./emblems/ball/ball.shuttlecock.ts";
106
108
  export { BALL_SLIOTAR } from "./emblems/ball/ball.sliotar.ts";
107
109
  export { BALL_LACROSSE, BALL_RACQUETBALL, BALL_TABLE_TENNIS, BALL_SHOT } from "./emblems/ball/ball.smooth.ts";
108
- export { BALL_SOCCER, BALL_SOCCER_COLOUR } from "./emblems/ball/ball.soccer.ts";
109
- export { 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 } from "./emblems/ball/ball.soccer.panels.ts";
110
+ export { BALL_SOCCER_26, BALL_SOCCER_18, BALL_SOCCER_14, BALL_SOCCER_12, BALL_SOCCER_6, BALL_SOCCER_26_ORANGE, BALL_SOCCER_18_ORANGE, BALL_SOCCER_14_ORANGE, BALL_SOCCER_12_ORANGE, BALL_SOCCER_6_ORANGE, BALL_SOCCER_26_YELLOW, BALL_SOCCER_18_YELLOW, BALL_SOCCER_14_YELLOW, BALL_SOCCER_12_YELLOW, BALL_SOCCER_6_YELLOW } from "./emblems/ball/ball.soccer.panels.ts";
110
111
  export { BALL_FUTSAL, BALL_BEACH_SOCCER } from "./emblems/ball/ball.soccer.indoor.ts";
111
112
  export { BALL_SOCCER_1870_HAND_STITCHED, BALL_SOCCER_1900_LACED, BALL_SOCCER_1930_18_PANEL, BALL_SOCCER_1950_12_PANEL, BALL_SOCCER_1960_32_PANEL_LEATHER } from "./emblems/ball/ball.soccer.vintage.ts";
112
113
  export { BALL_SOCCER_1970_BLACK_WHITE, BALL_SOCCER_1978_TRIAD, BALL_SOCCER_1986_DECORATED_TRIAD, BALL_SOCCER_1998_COLOURED_TRIAD, BALL_SOCCER_2002_TRIANGLE_SWEEP, BALL_SOCCER_2006_PROPELLER, BALL_SOCCER_2010_GROOVED, BALL_SOCCER_2014_RIBBON, BALL_SOCCER_2018_PIXEL, BALL_SOCCER_2022_SPEED_SHELL, BALL_SOCCER_TRAINING_HIGH_VIS } from "./emblems/ball/ball.soccer.era.ts";
113
114
  export { BALL_SOCCER_STARS_BLUE, BALL_SOCCER_STARS_RED, BALL_SOCCER_STARS_NIGHT, BALL_SOCCER_HEXAGONS_CYAN, BALL_SOCCER_HEXAGONS_MAGENTA, BALL_SOCCER_HEXAGONS_LIME, BALL_SOCCER_FLAMES_FIRE, BALL_SOCCER_FLAMES_ICE, BALL_SOCCER_FLAMES_TOXIC, BALL_SOCCER_CHEVRONS_RED, BALL_SOCCER_CHEVRONS_GREEN, BALL_SOCCER_CHEVRONS_BLUE, BALL_SOCCER_SWIRL_BLUE, BALL_SOCCER_SWIRL_RED, BALL_SOCCER_SWIRL_TEAL, BALL_SOCCER_METALLIC_GOLD, BALL_SOCCER_METALLIC_SILVER, BALL_SOCCER_METALLIC_COBALT } from "./emblems/ball/ball.soccer.store.ts";
114
115
  export { BALL_BASEBALL_LEMON_PEEL, BALL_BASEBALL_RED_BLUE, BALL_BASEBALL_BLACK_RED, BALL_BASEBALL_VINTAGE_LEATHER, BALL_SOFTBALL_11_OPTIC } from "./emblems/ball/ball.baseball.vintage.ts";
115
116
  export { BALL_VOLLEYBALL_COLOURS, BALL_VOLLEYBALL_BRAZIL, BALL_VOLLEYBALL_JAPAN, BALL_VOLLEYBALL_CANADA, BALL_VOLLEYBALL_USA, BALL_VOLLEYBALL_ITALY, BALL_VOLLEYBALL_POLAND, BALL_VOLLEYBALL_FRANCE, BALL_VOLLEYBALL_SERBIA, BALL_VOLLEYBALL_NETHERLANDS, BALL_VOLLEYBALL_GERMANY } from "./emblems/ball/ball.volleyball.flag.ts";
116
- export { BALL_SOFTBALL } from "./emblems/ball/ball.softball.ts";
117
117
  export { BALL_SQUASH_DOUBLE_YELLOW, BALL_SQUASH_YELLOW, BALL_SQUASH_RED, BALL_SQUASH_BLUE, BALL_SQUASH_GREEN } from "./emblems/ball/ball.squash.ts";
118
118
  export { BALL_TENNIS } from "./emblems/ball/ball.tennis.ts";
119
119
  export { BALL_TEQBALL } from "./emblems/ball/ball.teqball.ts";
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // its own module (`@johnmorrisdotca/dougu-sports/trophy/hockey-cup`), and for the functions alone `@johnmorrisdotca/dougu-sports/emblems`.
3
3
  export * from "./emblems-entry.js";
4
4
  export { ballFrame, emblemFrame } from "./emblems/emblem.api.js";
5
- export { bindTurn } from "./turn-control.js";
5
+ export { bindTurn } from "./turn-full.js";
6
6
  export { EMBLEM_TABLE } from "./emblems/emblem.registry.js";
7
7
  export { AWARD_MEDAL } from "./emblems/award/medal.js";
8
8
  export { AWARD_BADGE } from "./emblems/award/badge.js";
@@ -83,7 +83,8 @@ export { SPORT_JUDO } from "./emblems/sport/judo.js";
83
83
  export { SPORT_KYUDO } from "./emblems/sport/kyudo.js";
84
84
  export { SPORT_KARATE } from "./emblems/sport/karate.js";
85
85
  export { BALL_AFL } from "./emblems/ball/ball.afl.js";
86
- export { BALL_BASEBALL } from "./emblems/ball/ball.baseball.js";
86
+ export { BALL_BASEBALL, BALL_SOFTBALL, BALL_SOCCER, BALL_SOCCER_COLOUR, BALL_SOCCER_ORANGE, BALL_SOCCER_YELLOW } from "./emblems/ball/ball.printable.js";
87
+ export { printed } from "./emblems/ball/ball.print.js";
87
88
  export { BALL_BASKETBALL } from "./emblems/ball/ball.basketball.js";
88
89
  export { BALL_BASKETBALL_3X3 } from "./emblems/ball/ball.basketball3x3.js";
89
90
  export { BALL_BASKETBALL_6 } from "./emblems/ball/ball.basketball6.js";
@@ -106,15 +107,13 @@ export { BALL_SEPAK_TAKRAW } from "./emblems/ball/ball.sepaktakraw.js";
106
107
  export { BALL_SHUTTLECOCK_FEATHER, BALL_SHUTTLECOCK_NYLON } from "./emblems/ball/ball.shuttlecock.js";
107
108
  export { BALL_SLIOTAR } from "./emblems/ball/ball.sliotar.js";
108
109
  export { BALL_LACROSSE, BALL_RACQUETBALL, BALL_TABLE_TENNIS, BALL_SHOT } from "./emblems/ball/ball.smooth.js";
109
- export { BALL_SOCCER, BALL_SOCCER_COLOUR } from "./emblems/ball/ball.soccer.js";
110
- export { 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 } from "./emblems/ball/ball.soccer.panels.js";
110
+ export { BALL_SOCCER_26, BALL_SOCCER_18, BALL_SOCCER_14, BALL_SOCCER_12, BALL_SOCCER_6, BALL_SOCCER_26_ORANGE, BALL_SOCCER_18_ORANGE, BALL_SOCCER_14_ORANGE, BALL_SOCCER_12_ORANGE, BALL_SOCCER_6_ORANGE, BALL_SOCCER_26_YELLOW, BALL_SOCCER_18_YELLOW, BALL_SOCCER_14_YELLOW, BALL_SOCCER_12_YELLOW, BALL_SOCCER_6_YELLOW } from "./emblems/ball/ball.soccer.panels.js";
111
111
  export { BALL_FUTSAL, BALL_BEACH_SOCCER } from "./emblems/ball/ball.soccer.indoor.js";
112
112
  export { BALL_SOCCER_1870_HAND_STITCHED, BALL_SOCCER_1900_LACED, BALL_SOCCER_1930_18_PANEL, BALL_SOCCER_1950_12_PANEL, BALL_SOCCER_1960_32_PANEL_LEATHER } from "./emblems/ball/ball.soccer.vintage.js";
113
113
  export { BALL_SOCCER_1970_BLACK_WHITE, BALL_SOCCER_1978_TRIAD, BALL_SOCCER_1986_DECORATED_TRIAD, BALL_SOCCER_1998_COLOURED_TRIAD, BALL_SOCCER_2002_TRIANGLE_SWEEP, BALL_SOCCER_2006_PROPELLER, BALL_SOCCER_2010_GROOVED, BALL_SOCCER_2014_RIBBON, BALL_SOCCER_2018_PIXEL, BALL_SOCCER_2022_SPEED_SHELL, BALL_SOCCER_TRAINING_HIGH_VIS } from "./emblems/ball/ball.soccer.era.js";
114
114
  export { BALL_SOCCER_STARS_BLUE, BALL_SOCCER_STARS_RED, BALL_SOCCER_STARS_NIGHT, BALL_SOCCER_HEXAGONS_CYAN, BALL_SOCCER_HEXAGONS_MAGENTA, BALL_SOCCER_HEXAGONS_LIME, BALL_SOCCER_FLAMES_FIRE, BALL_SOCCER_FLAMES_ICE, BALL_SOCCER_FLAMES_TOXIC, BALL_SOCCER_CHEVRONS_RED, BALL_SOCCER_CHEVRONS_GREEN, BALL_SOCCER_CHEVRONS_BLUE, BALL_SOCCER_SWIRL_BLUE, BALL_SOCCER_SWIRL_RED, BALL_SOCCER_SWIRL_TEAL, BALL_SOCCER_METALLIC_GOLD, BALL_SOCCER_METALLIC_SILVER, BALL_SOCCER_METALLIC_COBALT } from "./emblems/ball/ball.soccer.store.js";
115
115
  export { BALL_BASEBALL_LEMON_PEEL, BALL_BASEBALL_RED_BLUE, BALL_BASEBALL_BLACK_RED, BALL_BASEBALL_VINTAGE_LEATHER, BALL_SOFTBALL_11_OPTIC } from "./emblems/ball/ball.baseball.vintage.js";
116
116
  export { BALL_VOLLEYBALL_COLOURS, BALL_VOLLEYBALL_BRAZIL, BALL_VOLLEYBALL_JAPAN, BALL_VOLLEYBALL_CANADA, BALL_VOLLEYBALL_USA, BALL_VOLLEYBALL_ITALY, BALL_VOLLEYBALL_POLAND, BALL_VOLLEYBALL_FRANCE, BALL_VOLLEYBALL_SERBIA, BALL_VOLLEYBALL_NETHERLANDS, BALL_VOLLEYBALL_GERMANY } from "./emblems/ball/ball.volleyball.flag.js";
117
- export { BALL_SOFTBALL } from "./emblems/ball/ball.softball.js";
118
117
  export { BALL_SQUASH_DOUBLE_YELLOW, BALL_SQUASH_YELLOW, BALL_SQUASH_RED, BALL_SQUASH_BLUE, BALL_SQUASH_GREEN } from "./emblems/ball/ball.squash.js";
119
118
  export { BALL_TENNIS } from "./emblems/ball/ball.tennis.js";
120
119
  export { BALL_TEQBALL } from "./emblems/ball/ball.teqball.js";