@johnmorrisdotca/dougu-sports 0.4.1 → 0.5.1

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 (44) hide show
  1. package/CHANGELOG.md +42 -1
  2. package/NOTICE.md +6 -4
  3. package/README.md +10 -8
  4. package/dist/emblems/ball/ball.baseball.vintage.d.ts +10 -0
  5. package/dist/emblems/ball/ball.baseball.vintage.js +70 -0
  6. package/dist/emblems/ball/ball.core.d.ts +4 -0
  7. package/dist/emblems/ball/ball.core.js +11 -1
  8. package/dist/emblems/ball/ball.decal.d.ts +56 -0
  9. package/dist/emblems/ball/ball.decal.js +146 -0
  10. package/dist/emblems/ball/ball.engine.d.ts +7 -1
  11. package/dist/emblems/ball/ball.engine.js +10 -2
  12. package/dist/emblems/ball/ball.family.panelled.d.ts +5 -0
  13. package/dist/emblems/ball/ball.family.panelled.js +12 -5
  14. package/dist/emblems/ball/ball.family.seamed.d.ts +8 -1
  15. package/dist/emblems/ball/ball.family.seamed.js +13 -3
  16. package/dist/emblems/ball/ball.index.js +6 -1
  17. package/dist/emblems/ball/ball.soccer.era.d.ts +23 -0
  18. package/dist/emblems/ball/ball.soccer.era.js +302 -0
  19. package/dist/emblems/ball/ball.soccer.store.d.ts +19 -0
  20. package/dist/emblems/ball/ball.soccer.store.js +176 -0
  21. package/dist/emblems/ball/ball.soccer.vintage.d.ts +11 -0
  22. package/dist/emblems/ball/ball.soccer.vintage.js +135 -0
  23. package/dist/emblems/ball/ball.tilings.d.ts +26 -0
  24. package/dist/emblems/ball/ball.tilings.js +87 -0
  25. package/dist/emblems/ball/ball.volleyball.flag.d.ts +12 -0
  26. package/dist/emblems/ball/ball.volleyball.flag.js +36 -0
  27. package/dist/emblems/emblem.core.d.ts +2 -0
  28. package/dist/emblems/emblem.descriptions.js +72 -5
  29. package/dist/emblems/emblem.ids.d.ts +5 -5
  30. package/dist/emblems/emblem.ids.js +8 -4
  31. package/dist/emblems/gear/gear.budo.d.ts +39 -3
  32. package/dist/emblems/gear/gear.budo.js +311 -50
  33. package/dist/emblems/gear/gear.index.js +2 -2
  34. package/dist/emblems/sport/cfl.js +3 -3
  35. package/dist/emblems/sport/ifaf.js +3 -3
  36. package/dist/emblems/sport/judo.js +11 -6
  37. package/dist/emblems/sport/karate.js +10 -6
  38. package/dist/emblems/sport/nfl.d.ts +2 -0
  39. package/dist/emblems/sport/nfl.js +31 -12
  40. package/dist/index.d.ts +7 -2
  41. package/dist/index.js +6 -1
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/package.json +1 -1
@@ -0,0 +1,176 @@
1
+ // Generic patterns of the kind a sports shop has on its wall, each in three colourways: stars, hexagon outlines, flames, chevrons, a two-tone swirl and a metallic glossy finish. Every pattern is our own
2
+ // drawing on a construction of the family (the 32 panels, or gores that run from pole to pole), and the balls are the size of an 11-a-side ball (IFAB Law 2). No logo, no brand, no team.
3
+ import { defineBall } from "./ball.engine.js";
4
+ import { mix } from "./ball.core.js";
5
+ import { frameAt, place, spiral, star, tongue, turned } from "./ball.decal.js";
6
+ import { shrink, truncatedIcosahedron } from "./ball.polyhedra.js";
7
+ import { gorePoint, gores } from "./ball.tilings.js";
8
+ import { SOCCER_SIZE } from "./ball.soccer.size.js";
9
+ const VIEW = { yaw: 30, pitch: 24, roll: 8 };
10
+ const GLOSS = { gloss: 0.8, shade: "#2a3450", grain: { f: 1.1, a: 0.16 } };
11
+ const lit = (s, seams, width = 0.45) => s.strokes(seams, "#fff", width, { deco: true, opacity: 0.45, dx: 0.3, dy: 0.35 });
12
+ const ball = (id, en, ja, spec) => defineBall({ id, en, ja, size: SOCCER_SIZE, spec });
13
+ // Stars: a five-pointed star on every panel, on a white or a night-blue ground.
14
+ const stars = (w) => ({
15
+ view: VIEW,
16
+ base: w.main,
17
+ ground: w.ground,
18
+ mat: GLOSS,
19
+ draw(s, base, accent) {
20
+ const t = truncatedIcosahedron();
21
+ const dark = accent ?? w.dark;
22
+ let out = "";
23
+ for (const p of t.pentagons)
24
+ out += s.panel(p, dark, { ink: true });
25
+ const big = star(5, 0.2, 0.09);
26
+ const small = star(5, 0.12, 0.054);
27
+ out += s.panels([
28
+ ...t.hexagonCentres.map((c, i) => place(frameAt(c, t.hexagonPentagonSides[i][0]), turned(big, 0.35))),
29
+ ...t.pentagonCentres.map((c, i) => place(frameAt(c, t.pentagons[i][0]), small)),
30
+ ], base, { n: 1 });
31
+ out += s.strokes(t.edges, mix(dark, 0.2), 1.05);
32
+ return out + lit(s, t.edges);
33
+ },
34
+ });
35
+ // Hexagon outlines: two outlines inside each hexagon, one inside the other, like a honeycomb; the pentagons are solid.
36
+ const hexagons = (w) => ({
37
+ view: VIEW,
38
+ base: w.main,
39
+ ground: w.ground,
40
+ mat: GLOSS,
41
+ draw(s, base, accent) {
42
+ const t = truncatedIcosahedron();
43
+ const dark = accent ?? w.dark;
44
+ let out = "";
45
+ for (const p of t.pentagons)
46
+ out += s.panel(p, dark, { ink: true });
47
+ t.hexagons.forEach((h, i) => {
48
+ out += s.strokes([shrink(h, t.hexagonCentres[i], 0.28), shrink(h, t.hexagonCentres[i], 0.58)], base, 1.1, { closed: true });
49
+ });
50
+ out += s.strokes(t.edges, mix(dark, 0.2), 1.1);
51
+ return out + lit(s, t.edges);
52
+ },
53
+ });
54
+ // Flames: five tongues of flame leave each dark pentagon, an outer one and a lighter inner one.
55
+ const flames = (w, inner) => ({
56
+ view: VIEW,
57
+ base: w.main,
58
+ ground: w.ground,
59
+ mat: GLOSS,
60
+ draw(s, base, accent) {
61
+ const t = truncatedIcosahedron();
62
+ const core = accent ?? inner;
63
+ let out = "";
64
+ for (const p of t.pentagons)
65
+ out += s.panel(p, w.dark, { ink: true });
66
+ const flame = (r0, r1, a, wd) => tongue(spiral(r0, r1, a, 0.75), (u) => wd * Math.min(1, u * 6 + 0.3) * (1 - u) ** 0.75, 3);
67
+ const frames = t.pentagonCentres.map((c, i) => frameAt(c, t.pentagons[i][0]));
68
+ const rings = (r0, r1, da, wd) => frames.flatMap((f) => [0, 1, 2, 3, 4].map((k) => place(f, flame(r0, r1, Math.PI / 5 + (2 * Math.PI * k) / 5 + da, wd))));
69
+ out += s.panels(rings(0.19, 0.64, -0.1, 0.125), base, { n: 1 });
70
+ out += s.panels(rings(0.21, 0.45, -0.03, 0.06), core, { n: 1 });
71
+ return out + s.strokes(t.edges, mix(w.dark, 0.5), 1);
72
+ },
73
+ });
74
+ // Chevrons: eight gores from pole to pole, every other one coloured, crossed by three bands that bend into a V on each gore, so that round the ball they make a zigzag.
75
+ const chevrons = (w) => ({
76
+ view: { yaw: 24, pitch: 24, roll: 0 },
77
+ base: w.main,
78
+ ground: w.ground,
79
+ mat: GLOSS,
80
+ draw(s, base, accent) {
81
+ const dark = accent ?? w.dark;
82
+ const tilt = [0, 22, 0];
83
+ const g = gores(8, 0, tilt);
84
+ const at = gorePoint(8, 0, tilt);
85
+ let out = "";
86
+ for (let i = 0; i < 8; i += 2)
87
+ out += s.panel(g.panels[i], base, { n: 1 });
88
+ for (let i = 0; i < 8; i++) {
89
+ for (const s0 of [-0.28, 0.02, 0.32]) {
90
+ const upper = [], lower = [];
91
+ for (let j = 0; j <= 4; j++) {
92
+ const u = j / 4;
93
+ const dip = 0.34 * Math.abs(u - 0.5);
94
+ upper.push(at(i, u, s0 + 0.12 + dip));
95
+ lower.push(at(i, u, s0 + dip));
96
+ }
97
+ const band = [...lower, ...upper.reverse()];
98
+ out += s.panel(band, dark, { n: 2 });
99
+ if (s.line)
100
+ out += s.strokes([band], dark, 0.6, { closed: true });
101
+ }
102
+ }
103
+ out += s.strokes(g.seams, mix(dark, 0.3), 0.7);
104
+ return out + lit(s, g.seams, 0.4);
105
+ },
106
+ });
107
+ // Swirl: four gores that twist round the ball, two of them in one colour and two in the other.
108
+ const swirl = (w) => ({
109
+ view: { yaw: 20, pitch: 24, roll: 0 },
110
+ base: w.main,
111
+ ground: w.ground,
112
+ mat: { gloss: 0.9, shade: "#2a3450", grain: { f: 0.8, a: 0.08 } },
113
+ draw(s, base) {
114
+ const g = gores(4, 330, [0, 24, 0], 24, 6);
115
+ let out = "";
116
+ for (const { ring, gore } of g.pieces) {
117
+ if (gore % 2 === 1)
118
+ continue;
119
+ out += s.panel(ring, base, { n: 4, ink: true });
120
+ out += s.strokes([ring], base, 0.5, { closed: true, deco: true });
121
+ }
122
+ out += s.strokes(g.seams, mix(w.dark, 0.2), 0.9);
123
+ return out + lit(s, g.seams, 0.5);
124
+ },
125
+ });
126
+ // Metallic: the 32 panels in a polished finish that mirrors the sky above and the ground below, a light metal on the hexagons and a dark one on the pentagons.
127
+ const metallic = (w) => ({
128
+ view: VIEW,
129
+ base: w.dark,
130
+ ground: w.main,
131
+ mat: { gloss: 1, metal: true, shade: mix(w.dark, -0.4), rim: 0.8 },
132
+ draw(s, base) {
133
+ const t = truncatedIcosahedron();
134
+ let out = "";
135
+ for (const p of t.pentagons)
136
+ out += s.panel(p, base, { ink: true });
137
+ out += s.strokes(t.edges, mix(base, -0.2), 1.1);
138
+ return out + lit(s, t.edges, 0.55);
139
+ },
140
+ });
141
+ const BLUE = { ground: "#f4f6fb", main: "#1f56c4", dark: "#12306f" };
142
+ const RED = { ground: "#f8f4f2", main: "#d8312f", dark: "#6e1313" };
143
+ const NIGHT = { ground: "#16275a", main: "#f2c230", dark: "#0a1230" };
144
+ const CYAN = { ground: "#f4f8fa", main: "#12a8c8", dark: "#143a52" };
145
+ const MAGENTA = { ground: "#faf4f8", main: "#d62a8c", dark: "#4a1a52" };
146
+ const LIME = { ground: "#f6f9ee", main: "#5fb81a", dark: "#1b3a12" };
147
+ const FIRE = { ground: "#f6f1e8", main: "#ff6a13", dark: "#1b1b1f" };
148
+ const ICE = { ground: "#f2f6fa", main: "#1e8cff", dark: "#0d1b3d" };
149
+ const TOXIC = { ground: "#f3f6ea", main: "#2bbf5a", dark: "#241a3a" };
150
+ const CHEV_RED = { ground: "#f8f6f2", main: "#d6312e", dark: "#1f2128" };
151
+ const CHEV_GREEN = { ground: "#f8f6ee", main: "#1fa65a", dark: "#f0c81c" };
152
+ const CHEV_BLUE = { ground: "#f6f7fa", main: "#1f5fbf", dark: "#f08a24" };
153
+ const SWIRL_BLUE = { ground: "#f6f6f2", main: "#1f5fbf", dark: "#143f86" };
154
+ const SWIRL_RED = { ground: "#1f2128", main: "#d6312e", dark: "#d6312e" };
155
+ const SWIRL_TEAL = { ground: "#14b0c8", main: "#f08a24", dark: "#0c6d80" };
156
+ const GOLD = { ground: "#e3b73a", main: "#e3b73a", dark: "#7a560f" };
157
+ const SILVER = { ground: "#cdd3db", main: "#cdd3db", dark: "#4d5662" };
158
+ const COBALT = { ground: "#5b93e0", main: "#5b93e0", dark: "#15306a" };
159
+ export const BALL_SOCCER_STARS_BLUE = /* @__PURE__ */ ball("ball:soccer-stars-blue", "Soccer, stars, blue", "星柄・青", stars(BLUE));
160
+ export const BALL_SOCCER_STARS_RED = /* @__PURE__ */ ball("ball:soccer-stars-red", "Soccer, stars, red", "星柄・赤", stars(RED));
161
+ export const BALL_SOCCER_STARS_NIGHT = /* @__PURE__ */ ball("ball:soccer-stars-night", "Soccer, stars, night", "星柄・夜空", stars(NIGHT));
162
+ export const BALL_SOCCER_HEXAGONS_CYAN = /* @__PURE__ */ ball("ball:soccer-hexagons-cyan", "Soccer, hexagons, cyan", "六角形柄・シアン", hexagons(CYAN));
163
+ export const BALL_SOCCER_HEXAGONS_MAGENTA = /* @__PURE__ */ ball("ball:soccer-hexagons-magenta", "Soccer, hexagons, magenta", "六角形柄・マゼンタ", hexagons(MAGENTA));
164
+ export const BALL_SOCCER_HEXAGONS_LIME = /* @__PURE__ */ ball("ball:soccer-hexagons-lime", "Soccer, hexagons, lime", "六角形柄・ライム", hexagons(LIME));
165
+ export const BALL_SOCCER_FLAMES_FIRE = /* @__PURE__ */ ball("ball:soccer-flames-fire", "Soccer, flames, fire", "炎柄・ファイア", flames(FIRE, "#ffd21f"));
166
+ export const BALL_SOCCER_FLAMES_ICE = /* @__PURE__ */ ball("ball:soccer-flames-ice", "Soccer, flames, ice", "炎柄・アイス", flames(ICE, "#c4e8ff"));
167
+ export const BALL_SOCCER_FLAMES_TOXIC = /* @__PURE__ */ ball("ball:soccer-flames-toxic", "Soccer, flames, toxic", "炎柄・トキシック", flames(TOXIC, "#dcf23a"));
168
+ export const BALL_SOCCER_CHEVRONS_RED = /* @__PURE__ */ ball("ball:soccer-chevrons-red", "Soccer, chevrons, red", "シェブロン柄・赤", chevrons(CHEV_RED));
169
+ export const BALL_SOCCER_CHEVRONS_GREEN = /* @__PURE__ */ ball("ball:soccer-chevrons-green", "Soccer, chevrons, green", "シェブロン柄・緑", chevrons(CHEV_GREEN));
170
+ export const BALL_SOCCER_CHEVRONS_BLUE = /* @__PURE__ */ ball("ball:soccer-chevrons-blue", "Soccer, chevrons, blue", "シェブロン柄・青", chevrons(CHEV_BLUE));
171
+ export const BALL_SOCCER_SWIRL_BLUE = /* @__PURE__ */ ball("ball:soccer-swirl-blue", "Soccer, swirl, blue", "渦巻き柄・青", swirl(SWIRL_BLUE));
172
+ export const BALL_SOCCER_SWIRL_RED = /* @__PURE__ */ ball("ball:soccer-swirl-red", "Soccer, swirl, red", "渦巻き柄・赤", swirl(SWIRL_RED));
173
+ export const BALL_SOCCER_SWIRL_TEAL = /* @__PURE__ */ ball("ball:soccer-swirl-teal", "Soccer, swirl, teal", "渦巻き柄・ティール", swirl(SWIRL_TEAL));
174
+ export const BALL_SOCCER_METALLIC_GOLD = /* @__PURE__ */ ball("ball:soccer-metallic-gold", "Soccer, metallic, gold", "メタリック・金", metallic(GOLD));
175
+ export const BALL_SOCCER_METALLIC_SILVER = /* @__PURE__ */ ball("ball:soccer-metallic-silver", "Soccer, metallic, silver", "メタリック・銀", metallic(SILVER));
176
+ export const BALL_SOCCER_METALLIC_COBALT = /* @__PURE__ */ ball("ball:soccer-metallic-cobalt", "Soccer, metallic, cobalt", "メタリック・コバルト", metallic(COBALT));
@@ -0,0 +1,11 @@
1
+ import { type BallSpec } from "./ball.engine.ts";
2
+ /** The late 1800s: eight long panels sewn by hand with a visible cross of thread, and a button where they meet at each end. */
3
+ export declare const BALL_SOCCER_1870_HAND_STITCHED: import("./ball.core.ts").Piece<BallSpec>;
4
+ /** The 1900s: eight panels, a round cap at each end, and a slit across which a lace is drawn to shut the casing once the bladder is in. */
5
+ export declare const BALL_SOCCER_1900_LACED: import("./ball.core.ts").Piece<BallSpec>;
6
+ /** The 1930s: eighteen strips, six panels of three, in two tones of leather, the seams stitched in two rows. */
7
+ export declare const BALL_SOCCER_1930_18_PANEL: import("./ball.core.ts").Piece<BallSpec>;
8
+ /** The 1950s: twelve pentagons, in four tones of chestnut leather so that no two that touch match, the seams stitched. */
9
+ export declare const BALL_SOCCER_1950_12_PANEL: import("./ball.core.ts").Piece<BallSpec>;
10
+ /** The 1960s: the 32 panels (twelve pentagons and twenty hexagons) in leather, tan hexagons and chestnut pentagons, stitched along the seams. */
11
+ export declare const BALL_SOCCER_1960_32_PANEL_LEATHER: import("./ball.core.ts").Piece<BallSpec>;
@@ -0,0 +1,135 @@
1
+ // Vintage soccer balls, in worn leather (tan, brown, chestnut) with the stitching of their time, each on the construction of its period: the hand-stitched casing of the late 1800s (eight long panels and a
2
+ // button at each end), the laced ball of the 1900s (eight panels, a round cap at each end and a lace across a slit), the 18 strips of the 1930s (six panels of three strips), the 12 panels of the
3
+ // 1950s and the 32 panels of the 1960s. The panels are our own layouts of the sources in docs/references.md; no maker's ball, mark or pattern is drawn.
4
+ import { defineBall } from "./ball.engine.js";
5
+ import { blend, mix } from "./ball.core.js";
6
+ import { across, lacing, noise, running, scuffs } from "./ball.decal.js";
7
+ import { paint18, seams18 } from "./ball.family.panelled.js";
8
+ import { smallCircle } from "./ball.math.js";
9
+ import { truncatedIcosahedron } from "./ball.polyhedra.js";
10
+ import { dodecahedron, gorePoint, gores } from "./ball.tilings.js";
11
+ import { SOCCER_SIZE } from "./ball.soccer.size.js";
12
+ /** The ball the Laws describe from 1872: a sphere 27 to 28 inches (68.6 to 71.1 cm) round. The Laws' mass for the early years is left out, because the secondary sources read disagree on the figure and the year. */
13
+ const OLD_SIZE = {
14
+ extentMm: 698.5 / Math.PI,
15
+ diameterMm: 698.5 / Math.PI,
16
+ circumferenceMm: 698.5,
17
+ rule: "Laws of the Game of the Football Association, revised 1872: the ball is a sphere 27 to 28 inches round",
18
+ note: "Before 1872 there was no rule. Diameter worked out from the middle of the circumference range. No mass is given, because the early figures could not be confirmed in a primary text.",
19
+ };
20
+ const LEATHER = { gloss: 0.22, shade: "#2b1608", grain: { f: 2.1, a: 0.34 } };
21
+ const THREAD = "#e9d8b0";
22
+ /** A tone of leather: no two panels of old leather are alike. */
23
+ const tone = (base, i, spread = 0.2) => mix(base, (noise(i, 4) - 0.5) * spread);
24
+ /** The seam of leather: a dark crease, with the highlight of its raised edge. */
25
+ const crease = (s, seams, colour, width = 1.3) => s.strokes(seams, colour, width) + s.strokes(seams, "#fff", width * 0.3, { deco: true, opacity: 0.3, dx: 0.25, dy: 0.3 });
26
+ const spec = (base, view, draw, shade = LEATHER.shade) => ({ view, base, mat: { ...LEATHER, shade }, draw });
27
+ /** The long gores, each one panel painted in a tone of the leather (a gore is a lune, so it is cut at the limb as one piece). */
28
+ function paintGores(s, g, base) {
29
+ let out = "";
30
+ g.panels.forEach((ring, i) => {
31
+ out += s.panel(ring, tone(base, i), { n: 1 });
32
+ });
33
+ return out;
34
+ }
35
+ const POLES = [(g) => g.seams[0][0], (g) => g.seams[0][g.seams[0].length - 1]];
36
+ /** The late 1800s: eight long panels sewn by hand with a visible cross of thread, and a button where they meet at each end. */
37
+ export const BALL_SOCCER_1870_HAND_STITCHED = /* @__PURE__ */ defineBall({
38
+ id: "ball:soccer-1870-hand-stitched",
39
+ en: "Soccer ball, hand-stitched",
40
+ ja: "手縫いのサッカーボール",
41
+ size: OLD_SIZE,
42
+ spec: spec("#b98a55", { yaw: 20, pitch: 28, roll: -8 }, (s, base, accent) => {
43
+ const g = gores(8, 0, [0, 28, 0], 24, 4);
44
+ let out = paintGores(s, g, base);
45
+ out += scuffs(s, mix(base, -0.6), mix(base, 0.55), { dark: 18, pale: 8, seed: 2 });
46
+ out += crease(s, g.seams, mix(base, -0.62));
47
+ out += s.strokes(across(g.seams, 0.085, 0.024, 0.013), accent ?? THREAD, 0.75, { deco: true });
48
+ for (const pole of POLES) {
49
+ out += s.dots([pole(g)], 0.1, mix(base, -0.38), { light: true });
50
+ out += s.dots([pole(g)], 0.045, mix(base, -0.62), { light: false });
51
+ }
52
+ return out;
53
+ }),
54
+ });
55
+ /** The 1900s: eight panels, a round cap at each end, and a slit across which a lace is drawn to shut the casing once the bladder is in. */
56
+ export const BALL_SOCCER_1900_LACED = /* @__PURE__ */ defineBall({
57
+ id: "ball:soccer-1900-laced",
58
+ en: "Soccer ball, laced",
59
+ ja: "紐つきのサッカーボール",
60
+ size: OLD_SIZE,
61
+ spec: spec("#8a5a30", { yaw: 24, pitch: 22, roll: 6 }, (s, base, accent) => {
62
+ const g = gores(8, 0, [0, 18, 0], 24, 4);
63
+ const at = gorePoint(8, 0, [0, 18, 0]);
64
+ let out = paintGores(s, g, base);
65
+ const poles = POLES.map((p) => p(g));
66
+ const rim = poles.map((p) => smallCircle(p, 0.32, 40));
67
+ poles.forEach((p, i) => {
68
+ out += s.panel(rim[i], mix(base, -0.16), { n: 1 });
69
+ });
70
+ out += scuffs(s, mix(base, -0.6), mix(base, 0.5), { dark: 18, pale: 8, seed: 5 });
71
+ out += crease(s, g.seams.map((c) => c.slice(3, 22)), mix(base, -0.62), 1.2) + crease(s, rim, mix(base, -0.62), 1.2);
72
+ out += s.strokes(across(g.seams.map((c) => c.slice(3, 22)), 0.1, 0.02), accent ?? THREAD, 0.6, { deco: true });
73
+ const lace = lacing(at(7, 0.5, 0), at(7, 0.5, 0.2), 0.62, 6, 0.032);
74
+ out += s.stroke(lace.slit, mix(base, -0.75), 2.4);
75
+ out += s.dots(lace.eyelets, 0.011, mix(base, -0.7), { light: false });
76
+ out += s.strokes(lace.crossings, accent ?? THREAD, 0.85, { deco: true });
77
+ out += s.strokes(lace.ends, accent ?? THREAD, 0.85, { deco: true });
78
+ return out;
79
+ }),
80
+ });
81
+ /** The 1930s: eighteen strips, six panels of three, in two tones of leather, the seams stitched in two rows. */
82
+ export const BALL_SOCCER_1930_18_PANEL = /* @__PURE__ */ defineBall({
83
+ id: "ball:soccer-1930-18-panel",
84
+ en: "Soccer ball, 18 strips",
85
+ ja: "18枚貼りの革ボール",
86
+ size: OLD_SIZE,
87
+ spec: spec("#9c4f26", { yaw: 28, pitch: 22, roll: 0 }, (s, base, accent) => {
88
+ const light = blend(base, "#d9a56a", 0.55);
89
+ const seams = seams18();
90
+ let out = paint18(s, [[base, light], [light, base], [base, light], [light, base], [base, light], [light, base]]);
91
+ out += scuffs(s, mix(base, -0.6), mix(base, 0.55), { dark: 11, pale: 5, seed: 3 });
92
+ out += crease(s, seams, mix(base, -0.6), 1.15);
93
+ out += s.strokes(running(seams, 0.13), accent ?? THREAD, 0.6, { deco: true });
94
+ return out;
95
+ }),
96
+ });
97
+ /** The 1950s: twelve pentagons, in four tones of chestnut leather so that no two that touch match, the seams stitched. */
98
+ export const BALL_SOCCER_1950_12_PANEL = /* @__PURE__ */ defineBall({
99
+ id: "ball:soccer-1950-12-panel",
100
+ en: "Soccer ball, 12 panels",
101
+ ja: "12枚貼りの革ボール",
102
+ size: OLD_SIZE,
103
+ spec: spec("#7a3a1c", { yaw: 30, pitch: 24, roll: 8 }, (s, base, accent) => {
104
+ const g = dodecahedron();
105
+ const fill = [mix(base, 0.1), mix(base, -0.12), mix(base, 0.26), mix(base, 0.02)];
106
+ let out = "";
107
+ g.panels.forEach((p, i) => {
108
+ out += s.panel(p, fill[g.colour[i]], { n: 6, ink: g.colour[i] === 1 });
109
+ });
110
+ out += scuffs(s, mix(base, -0.6), mix(base, 0.55), { dark: 12, pale: 5, seed: 7 });
111
+ out += crease(s, g.seams, mix(base, -0.6), 1.2);
112
+ out += s.strokes(running(g.seams, 0.11), accent ?? THREAD, 0.55, { deco: true });
113
+ return out;
114
+ }),
115
+ });
116
+ /** The 1960s: the 32 panels (twelve pentagons and twenty hexagons) in leather, tan hexagons and chestnut pentagons, stitched along the seams. */
117
+ export const BALL_SOCCER_1960_32_PANEL_LEATHER = /* @__PURE__ */ defineBall({
118
+ id: "ball:soccer-1960-32-panel-leather",
119
+ en: "Soccer ball, 32 leather",
120
+ ja: "32枚貼りの革ボール",
121
+ size: SOCCER_SIZE,
122
+ spec: {
123
+ ...spec("#6e3517", { yaw: 30, pitch: 24, roll: 8 }, (s, base, accent) => {
124
+ const t = truncatedIcosahedron();
125
+ let out = "";
126
+ for (const p of t.pentagons)
127
+ out += s.panel(p, base, { ink: true });
128
+ out += scuffs(s, mix(base, -0.5), "#f0d2a8", { dark: 6, pale: 3, seed: 11 });
129
+ out += crease(s, t.edges, mix(base, -0.55), 1.1);
130
+ out += s.strokes(running(t.edges, 0.13), accent ?? THREAD, 0.5, { deco: true });
131
+ return out;
132
+ }),
133
+ ground: "#d6a976",
134
+ },
135
+ });
@@ -58,4 +58,30 @@ export interface Gores {
58
58
  * whole is then tilted by the rotation `tilt` (yaw, pitch and roll in degrees) so that the poles are not on the ball's spin axis.
59
59
  */
60
60
  export declare function gores(count?: number, twist?: number, tilt?: readonly [number, number, number], steps?: number, bands?: number): Gores;
61
+ /** The point of a gore: gore `i` of `count`, `u` of the way across it (0 on its left seam, 1 on its right) and `s` from the south pole (-1) to the north (1), turned by `tilt` like `gores`. */
62
+ export declare function gorePoint(count?: number, twist?: number, tilt?: readonly [number, number, number]): (i: number, u: number, s: number) => V3;
63
+ /** Colours the centres with `k` colours so that no two neighbours (centres at angle `gap` or less apart) share one, by search; the result is the colour of each. */
64
+ export declare function colourCentres(centres: readonly V3[], gap: number, k?: number): number[];
65
+ type Icosahedron = Tiling & {
66
+ colour: number[];
67
+ };
68
+ /**
69
+ * The 20-panel layout: an icosahedron. Its 12 vertices are the cyclic permutations of (0, +-1, +-phi); its 20 triangles are about the directions of a dodecahedron's vertices. `colour` is a
70
+ * four-colouring of the triangles (no two that share an edge match), found by search.
71
+ */
72
+ export declare function icosahedron(): Icosahedron;
73
+ export interface Octahedron {
74
+ /** The eight triangular panels, with their edges bent into S curves (dense rings: use one sample per edge). */
75
+ panels: V3[][];
76
+ centres: V3[];
77
+ /** Every one of the 12 edges once, as an S curve from one end to the other. */
78
+ seams: V3[][];
79
+ /** 0 or 1, so that no two panels that share an edge match. */
80
+ parity: number[];
81
+ }
82
+ /**
83
+ * The 8-panel layout: an octahedron whose twelve edges are bent into S curves, a quarter of the way along to one side and three quarters of the way to the other, so the eight triangles are
84
+ * twisted into curved panels. The curve is the same turned half a turn about the edge's middle, and it is carried to itself by the cube's rotations, so the panels are alike and the layout has a hand.
85
+ */
86
+ export declare function octahedron8(lift?: number, steps?: number): Octahedron;
61
87
  export {};
@@ -210,3 +210,90 @@ export function gores(count = 6, twist = 150, tilt = [0, 0, 0], steps = 24, band
210
210
  }
211
211
  return { panels, centres, seams, pieces };
212
212
  }
213
+ /** The point of a gore: gore `i` of `count`, `u` of the way across it (0 on its left seam, 1 on its right) and `s` from the south pole (-1) to the north (1), turned by `tilt` like `gores`. */
214
+ export function gorePoint(count = 6, twist = 150, tilt = [0, 0, 0]) {
215
+ const m = rotation((tilt[0] * Math.PI) / 180, (tilt[1] * Math.PI) / 180, (tilt[2] * Math.PI) / 180);
216
+ return (i, u, s) => apply(m, lonLat((2 * Math.PI * (i + u)) / count + ((twist * Math.PI) / 180) * (s / 2), (s * Math.PI) / 2));
217
+ }
218
+ /** Colours the centres with `k` colours so that no two neighbours (centres at angle `gap` or less apart) share one, by search; the result is the colour of each. */
219
+ export function colourCentres(centres, gap, k = 4) {
220
+ const colour = centres.map(() => -1);
221
+ const next = (i, j) => dot(centres[i], centres[j]) > Math.cos(gap) - 1e-6 && i !== j;
222
+ const paint = (i) => {
223
+ if (i === centres.length)
224
+ return true;
225
+ for (let c = 0; c < k; c++) {
226
+ if (centres.every((_, j) => j >= i || !next(i, j) || colour[j] !== c)) {
227
+ colour[i] = c;
228
+ if (paint(i + 1))
229
+ return true;
230
+ }
231
+ }
232
+ colour[i] = -1;
233
+ return false;
234
+ };
235
+ paint(0);
236
+ return colour;
237
+ }
238
+ let icosa;
239
+ /**
240
+ * The 20-panel layout: an icosahedron. Its 12 vertices are the cyclic permutations of (0, +-1, +-phi); its 20 triangles are about the directions of a dodecahedron's vertices. `colour` is a
241
+ * four-colouring of the triangles (no two that share an edge match), found by search.
242
+ */
243
+ export function icosahedron() {
244
+ if (icosa !== undefined)
245
+ return icosa;
246
+ const verts = [];
247
+ for (const a of signs)
248
+ for (const b of signs)
249
+ verts.push(norm([0, a, b * PHI]), norm([a, b * PHI, 0]), norm([b * PHI, 0, a]));
250
+ const near = (a, b) => Math.abs(dot(a, b) - 1 / Math.sqrt(5)) < 1e-6;
251
+ const centres = [];
252
+ for (let i = 0; i < 12; i++)
253
+ for (let j = i + 1; j < 12; j++)
254
+ for (let k = j + 1; k < 12; k++)
255
+ if (near(verts[i], verts[j]) && near(verts[i], verts[k]) && near(verts[j], verts[k]))
256
+ centres.push(norm(add(add(verts[i], verts[j]), verts[k])));
257
+ const t = tiling(verts, centres, centres.map(() => 0), 5);
258
+ // Triangles that share an edge have centres at the angle whose cosine is sqrt 5 / 3.
259
+ icosa = { ...t, colour: colourCentres(centres, Math.acos(Math.sqrt(5) / 3) + 1e-6, 4) };
260
+ return icosa;
261
+ }
262
+ const octas = new Map();
263
+ /**
264
+ * The 8-panel layout: an octahedron whose twelve edges are bent into S curves, a quarter of the way along to one side and three quarters of the way to the other, so the eight triangles are
265
+ * twisted into curved panels. The curve is the same turned half a turn about the edge's middle, and it is carried to itself by the cube's rotations, so the panels are alike and the layout has a hand.
266
+ */
267
+ export function octahedron8(lift = 0.16, steps = 14) {
268
+ const hit = octas.get(lift * 1000 + steps);
269
+ if (hit !== undefined)
270
+ return hit;
271
+ const edge = (a, b) => {
272
+ const [p, q] = keyOf(a) < keyOf(b) ? [a, b] : [b, a];
273
+ const pts = Array.from({ length: steps + 1 }, (_, j) => bent(p, q, j / steps, lift, 0.5));
274
+ return keyOf(a) < keyOf(b) ? pts : pts.reverse();
275
+ };
276
+ const panels = [];
277
+ const centres = [];
278
+ const parity = [];
279
+ const seams = [];
280
+ const seen = new Set();
281
+ for (const sx of signs)
282
+ for (const sy of signs)
283
+ for (const sz of signs) {
284
+ const a = [sx, 0, 0], b = [0, sy, 0], c = [0, 0, sz];
285
+ panels.push([...edge(a, b).slice(0, -1), ...edge(b, c).slice(0, -1), ...edge(c, a).slice(0, -1)]);
286
+ centres.push(norm([sx, sy, sz]));
287
+ parity.push(sx * sy * sz > 0 ? 0 : 1);
288
+ for (const [p, q] of [[a, b], [b, c], [c, a]]) {
289
+ const key = [keyOf(p), keyOf(q)].sort().join("|");
290
+ if (seen.has(key))
291
+ continue;
292
+ seen.add(key);
293
+ seams.push(edge(p, q));
294
+ }
295
+ }
296
+ const result = { panels, centres, seams, parity };
297
+ octas.set(lift * 1000 + steps, result);
298
+ return result;
299
+ }
@@ -0,0 +1,12 @@
1
+ import { type BallSpec } from "./ball.engine.ts";
2
+ export declare const BALL_VOLLEYBALL_COLOURS: import("./ball.core.ts").Piece<BallSpec>;
3
+ export declare const BALL_VOLLEYBALL_BRAZIL: import("./ball.core.ts").Piece<BallSpec>;
4
+ export declare const BALL_VOLLEYBALL_JAPAN: import("./ball.core.ts").Piece<BallSpec>;
5
+ export declare const BALL_VOLLEYBALL_CANADA: import("./ball.core.ts").Piece<BallSpec>;
6
+ export declare const BALL_VOLLEYBALL_USA: import("./ball.core.ts").Piece<BallSpec>;
7
+ export declare const BALL_VOLLEYBALL_ITALY: import("./ball.core.ts").Piece<BallSpec>;
8
+ export declare const BALL_VOLLEYBALL_POLAND: import("./ball.core.ts").Piece<BallSpec>;
9
+ export declare const BALL_VOLLEYBALL_FRANCE: import("./ball.core.ts").Piece<BallSpec>;
10
+ export declare const BALL_VOLLEYBALL_SERBIA: import("./ball.core.ts").Piece<BallSpec>;
11
+ export declare const BALL_VOLLEYBALL_NETHERLANDS: import("./ball.core.ts").Piece<BallSpec>;
12
+ export declare const BALL_VOLLEYBALL_GERMANY: import("./ball.core.ts").Piece<BallSpec>;
@@ -0,0 +1,36 @@
1
+ // Volleyballs in national colours: the 18-panel indoor ball (six groups of three strips) with its panels in the colours of a country's flag, three colours to a ball. They are only colours: no flag is drawn,
2
+ // no crest or federation's mark, and the ball is the indoor ball of the FIVB's rule. `colors` takes any three colours for any of them, and `ball:volleyball-colours` is the same ball with colours of your own.
3
+ import { defineBall } from "./ball.engine.js";
4
+ import { strips18 } from "./ball.family.panelled.js";
5
+ import { BALL_VOLLEYBALL } from "./ball.volleyball.js";
6
+ const WHITE = "#f6f4ee";
7
+ /** The ball with the colours of one flag: `tint` replaces the first colour, `accent` the second and `colors` all three. The six faces take the three colours in pairs, so each colour is the outer strips of two faces and the middle strip of two others. */
8
+ function nation(id, en, ja, colours, view = { yaw: 28, pitch: 22, roll: 0 }) {
9
+ return defineBall({
10
+ id,
11
+ en,
12
+ ja,
13
+ size: BALL_VOLLEYBALL.size,
14
+ spec: {
15
+ view,
16
+ base: colours[0],
17
+ ground: "#e9e7e0",
18
+ mat: { gloss: 0.75, shade: "#1a2a50", grain: { f: 1.2, a: 0.12 } },
19
+ draw: (s, base, accent, picked) => {
20
+ const [a, b, c] = picked ?? [base, accent ?? colours[1], colours[2]];
21
+ return strips18(s, [[a, b], [b, c], [c, a], [a, c], [b, a], [c, b]], "#3a4560");
22
+ },
23
+ },
24
+ });
25
+ }
26
+ export const BALL_VOLLEYBALL_COLOURS = /* @__PURE__ */ nation("ball:volleyball-colours", "Volleyball, three colours", "三色のバレーボール", ["#f3c81c", "#1f5fbf", WHITE]);
27
+ export const BALL_VOLLEYBALL_BRAZIL = /* @__PURE__ */ nation("ball:volleyball-brazil", "Volleyball, Brazil", "バレー(ブラジル)", ["#009c3b", "#ffdf00", "#002776"]);
28
+ export const BALL_VOLLEYBALL_JAPAN = /* @__PURE__ */ nation("ball:volleyball-japan", "Volleyball, Japan", "バレー(日本)", [WHITE, "#bc002d", "#7d0020"]);
29
+ export const BALL_VOLLEYBALL_CANADA = /* @__PURE__ */ nation("ball:volleyball-canada", "Volleyball, Canada", "バレー(カナダ)", ["#d52b1e", WHITE, "#9b1c14"]);
30
+ export const BALL_VOLLEYBALL_USA = /* @__PURE__ */ nation("ball:volleyball-usa", "Volleyball, USA", "バレー(アメリカ)", ["#b22234", WHITE, "#3c3b6e"]);
31
+ export const BALL_VOLLEYBALL_ITALY = /* @__PURE__ */ nation("ball:volleyball-italy", "Volleyball, Italy", "バレー(イタリア)", ["#009246", WHITE, "#ce2b37"]);
32
+ export const BALL_VOLLEYBALL_POLAND = /* @__PURE__ */ nation("ball:volleyball-poland", "Volleyball, Poland", "バレー(ポーランド)", [WHITE, "#dc143c", "#9b1030"]);
33
+ export const BALL_VOLLEYBALL_FRANCE = /* @__PURE__ */ nation("ball:volleyball-france", "Volleyball, France", "バレー(フランス)", ["#0055a4", WHITE, "#ef4135"]);
34
+ export const BALL_VOLLEYBALL_SERBIA = /* @__PURE__ */ nation("ball:volleyball-serbia", "Volleyball, Serbia", "バレー(セルビア)", ["#c6363c", "#0c4076", WHITE]);
35
+ export const BALL_VOLLEYBALL_NETHERLANDS = /* @__PURE__ */ nation("ball:volleyball-netherlands", "Volleyball, Netherlands", "バレー(オランダ)", ["#ae1c28", WHITE, "#21468b"]);
36
+ export const BALL_VOLLEYBALL_GERMANY = /* @__PURE__ */ nation("ball:volleyball-germany", "Volleyball, Germany", "バレー(ドイツ)", ["#1b1b1b", "#dd0000", "#ffce00"]);
@@ -56,6 +56,8 @@ export interface EmblemOptions {
56
56
  * (made safe first), `emblem` a dougu id, `flag` a country code (`ca` and `us` are drawn here; another needs the `flags` option, a renderer like hata's). See `PuckFace`. Anything that is not a puck ignores it.
57
57
  */
58
58
  face?: PuckFace;
59
+ /** A ball with panels in several colours (the national volleyballs, `ball:volleyball-flag` and the rest): three colours for its panels, each a hex colour or `rgb()`; one that is not a colour is dropped, and fewer than three are repeated. Anything else ignores it. */
60
+ colors?: readonly string[];
59
61
  /** A puck: a renderer for the flag codes this package does not draw (`face.flag`): `(code, { shape: "round" | "square" | "rect", size, theme }) => svg`, synchronous, the same as taisen-brackets takes (hata's `flag()` is asynchronous, so load the flags first). */
60
62
  flags?: FlagRenderer;
61
63
  }