@johnmorrisdotca/dougu-sports 0.1.0 → 0.2.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 +38 -1
- package/NOTICE.md +9 -58
- package/README.md +168 -32
- package/dist/emblems/award/badge.d.ts +2 -2
- package/dist/emblems/award/badge.js +14 -5
- package/dist/emblems/award/crown.d.ts +1 -1
- package/dist/emblems/award/crown.js +14 -4
- package/dist/emblems/award/medal.d.ts +2 -2
- package/dist/emblems/award/medal.js +14 -5
- package/dist/emblems/award/shield.d.ts +1 -1
- package/dist/emblems/award/shield.js +10 -4
- package/dist/emblems/award/star.d.ts +1 -1
- package/dist/emblems/award/star.js +10 -4
- package/dist/emblems/ball/ball.afl.js +1 -1
- package/dist/emblems/ball/ball.basketball6.js +1 -1
- package/dist/emblems/ball/ball.core.d.ts +9 -3
- package/dist/emblems/ball/ball.core.js +13 -0
- package/dist/emblems/ball/ball.cricket.js +1 -1
- package/dist/emblems/ball/ball.engine.js +3 -3
- package/dist/emblems/ball/ball.football.js +3 -3
- package/dist/emblems/ball/ball.gaelic.js +1 -1
- package/dist/emblems/ball/ball.object.js +2 -2
- package/dist/emblems/ball/ball.shuttlecock.js +2 -2
- package/dist/emblems/ball/ball.squash.js +5 -5
- package/dist/emblems/emblem.api.d.ts +39 -1
- package/dist/emblems/emblem.api.js +20 -2
- package/dist/emblems/emblem.compose.d.ts +31 -0
- package/dist/emblems/emblem.compose.js +91 -0
- package/dist/emblems/emblem.core.d.ts +48 -2
- package/dist/emblems/emblem.core.js +30 -0
- package/dist/emblems/emblem.descriptions.d.ts +7 -0
- package/dist/emblems/emblem.descriptions.js +60 -0
- package/dist/emblems/emblem.display.d.ts +75 -0
- package/dist/emblems/emblem.display.js +214 -0
- package/dist/emblems/emblem.engrave.d.ts +50 -0
- package/dist/emblems/emblem.engrave.js +145 -0
- package/dist/emblems/emblem.equipment.d.ts +24 -0
- package/dist/emblems/emblem.equipment.js +57 -0
- package/dist/emblems/emblem.flip.d.ts +10 -0
- package/dist/emblems/emblem.flip.js +45 -0
- package/dist/emblems/emblem.ids.d.ts +19 -11
- package/dist/emblems/emblem.ids.js +21 -6
- package/dist/emblems/emblem.lathe.d.ts +3 -0
- package/dist/emblems/emblem.lathe.js +26 -4
- package/dist/emblems/emblem.medal.js +4 -4
- package/dist/emblems/emblem.pennant.d.ts +3 -0
- package/dist/emblems/emblem.pennant.js +79 -0
- package/dist/emblems/emblem.registry.js +20 -0
- package/dist/emblems/emblem.shapes.d.ts +19 -2
- package/dist/emblems/emblem.shapes.js +53 -12
- package/dist/emblems/emblem.sprite.d.ts +38 -0
- package/dist/emblems/emblem.sprite.js +65 -0
- package/dist/emblems/emblem.uri.d.ts +22 -0
- package/dist/emblems/emblem.uri.js +29 -0
- package/dist/emblems/gear/gear.badmintonracket.d.ts +1 -1
- package/dist/emblems/gear/gear.badmintonracket.js +18 -14
- package/dist/emblems/gear/gear.baseballglove.d.ts +1 -1
- package/dist/emblems/gear/gear.baseballglove.js +20 -14
- package/dist/emblems/gear/gear.budo.d.ts +17 -0
- package/dist/emblems/gear/gear.budo.js +60 -0
- package/dist/emblems/gear/gear.core.d.ts +12 -31
- package/dist/emblems/gear/gear.core.js +61 -54
- package/dist/emblems/gear/gear.cricketbat.d.ts +1 -1
- package/dist/emblems/gear/gear.cricketbat.js +16 -14
- package/dist/emblems/gear/gear.fieldhockeystick.d.ts +1 -1
- package/dist/emblems/gear/gear.fieldhockeystick.js +15 -14
- package/dist/emblems/gear/gear.footballhelmet.d.ts +1 -1
- package/dist/emblems/gear/gear.footballhelmet.js +15 -14
- package/dist/emblems/gear/gear.goaliemask.d.ts +1 -1
- package/dist/emblems/gear/gear.goaliemask.js +22 -16
- package/dist/emblems/gear/gear.goalnet.d.ts +1 -1
- package/dist/emblems/gear/gear.goalnet.js +24 -14
- package/dist/emblems/gear/gear.hockeystick.d.ts +1 -1
- package/dist/emblems/gear/gear.hockeystick.js +14 -14
- package/dist/emblems/gear/gear.index.js +5 -1
- package/dist/emblems/gear/gear.kendo.d.ts +2 -0
- package/dist/emblems/gear/gear.kendo.js +41 -0
- package/dist/emblems/gear/gear.kyudo.d.ts +2 -0
- package/dist/emblems/gear/gear.kyudo.js +31 -0
- package/dist/emblems/gear/gear.lacrossestick.d.ts +1 -1
- package/dist/emblems/gear/gear.lacrossestick.js +25 -14
- package/dist/emblems/gear/gear.skates.js +1 -1
- package/dist/emblems/gear/gear.sticks.js +2 -2
- package/dist/emblems/gear/gear.sumo.d.ts +2 -0
- package/dist/emblems/gear/gear.sumo.js +43 -0
- package/dist/emblems/gear/gear.tabletennispaddle.d.ts +1 -1
- package/dist/emblems/gear/gear.tabletennispaddle.js +15 -14
- package/dist/emblems/gear/gear.tennisracket.d.ts +1 -1
- package/dist/emblems/gear/gear.tennisracket.js +16 -14
- package/dist/emblems/pennant/championship.d.ts +2 -0
- package/dist/emblems/pennant/championship.js +4 -0
- package/dist/emblems/sport/afl.js +1 -1
- package/dist/emblems/sport/artisticswimming.js +1 -1
- package/dist/emblems/sport/cfl.d.ts +1 -1
- package/dist/emblems/sport/cfl.js +4 -4
- package/dist/emblems/sport/cricket.d.ts +1 -1
- package/dist/emblems/sport/cricket.js +13 -4
- package/dist/emblems/sport/fieldhockey.js +1 -1
- package/dist/emblems/sport/gaelic.js +1 -1
- package/dist/emblems/sport/handball.d.ts +1 -1
- package/dist/emblems/sport/handball.js +14 -4
- package/dist/emblems/sport/ifaf.d.ts +1 -1
- package/dist/emblems/sport/ifaf.js +4 -4
- package/dist/emblems/sport/judo.d.ts +2 -0
- package/dist/emblems/sport/judo.js +13 -0
- package/dist/emblems/sport/karate.d.ts +2 -0
- package/dist/emblems/sport/karate.js +13 -0
- package/dist/emblems/sport/kendo.d.ts +2 -0
- package/dist/emblems/sport/kendo.js +14 -0
- package/dist/emblems/sport/kyudo.d.ts +2 -0
- package/dist/emblems/sport/kyudo.js +15 -0
- package/dist/emblems/sport/mlb.d.ts +1 -1
- package/dist/emblems/sport/mlb.js +27 -4
- package/dist/emblems/sport/nba.d.ts +1 -1
- package/dist/emblems/sport/nba.js +11 -4
- package/dist/emblems/sport/netball.js +1 -1
- package/dist/emblems/sport/nfl.d.ts +2 -1
- package/dist/emblems/sport/nfl.js +14 -4
- package/dist/emblems/sport/nhl.d.ts +1 -1
- package/dist/emblems/sport/nhl.js +12 -4
- package/dist/emblems/sport/pickleball.js +1 -1
- package/dist/emblems/sport/racquetball.js +1 -1
- package/dist/emblems/sport/roundnet.js +1 -1
- package/dist/emblems/sport/rugby.d.ts +1 -1
- package/dist/emblems/sport/rugby.js +11 -4
- package/dist/emblems/sport/sepaktakraw.js +1 -1
- package/dist/emblems/sport/soccer.d.ts +1 -1
- package/dist/emblems/sport/soccer.js +30 -4
- package/dist/emblems/sport/squash.js +1 -1
- package/dist/emblems/sport/sumo.d.ts +2 -0
- package/dist/emblems/sport/sumo.js +15 -0
- package/dist/emblems/sport/tabletennis.js +1 -1
- package/dist/emblems/sport/tennis.d.ts +1 -1
- package/dist/emblems/sport/tennis.js +14 -4
- package/dist/emblems/sport/teqball.js +1 -1
- package/dist/emblems/sport/volleyball.d.ts +1 -1
- package/dist/emblems/sport/volleyball.js +24 -4
- package/dist/emblems/trophy/banner.d.ts +2 -0
- package/dist/emblems/trophy/banner.js +20 -0
- package/dist/emblems/trophy/baseball.js +1 -0
- package/dist/emblems/trophy/basketball.js +1 -0
- package/dist/emblems/trophy/belt.js +2 -1
- package/dist/emblems/trophy/bowl.js +1 -0
- package/dist/emblems/trophy/canadian-bowl.js +1 -0
- package/dist/emblems/trophy/championship-banner.d.ts +2 -0
- package/dist/emblems/trophy/championship-banner.js +67 -0
- package/dist/emblems/trophy/cricket-urn.js +1 -0
- package/dist/emblems/trophy/cup.d.ts +1 -1
- package/dist/emblems/trophy/cup.js +15 -4
- package/dist/emblems/trophy/football.js +2 -1
- package/dist/emblems/trophy/globe.js +1 -0
- package/dist/emblems/trophy/hockey-cup.js +2 -1
- package/dist/emblems/trophy/lidded-cup.js +1 -0
- package/dist/emblems/trophy/plaque.js +2 -1
- package/dist/emblems/trophy/ring.d.ts +2 -0
- package/dist/emblems/trophy/ring.js +30 -0
- package/dist/emblems/trophy/rugby-cup.js +1 -0
- package/dist/emblems/trophy/salver.js +1 -0
- package/dist/emblems/trophy/soccer-ball.js +2 -1
- package/dist/emblems/trophy/soccer-globe.js +1 -0
- package/dist/emblems/trophy/sumo-cup.d.ts +2 -0
- package/dist/emblems/trophy/sumo-cup.js +28 -0
- package/dist/emblems/trophy/tennis-cup.js +2 -1
- package/dist/emblems/trophy/tennis-plate.js +1 -0
- package/dist/emblems-entry.d.ts +15 -6
- package/dist/emblems-entry.js +7 -2
- package/dist/index.d.ts +17 -0
- package/dist/index.js +15 -0
- package/dist/turn-control.d.ts +101 -0
- package/dist/turn-control.js +363 -0
- package/dist/turn-entry.d.ts +2 -0
- package/dist/turn-entry.js +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -1
- package/LICENSES/Apache-2.0.txt +0 -202
- package/LICENSES/CC-BY-3.0.txt +0 -319
- package/LICENSES/upstream/game-icons.license.txt +0 -45
- package/LICENSES/upstream/noto-emoji-2D-svg.LICENSE +0 -13
- package/LICENSES/upstream/pictogrammers-mdi.LICENSE +0 -20
- package/dist/emblems/emblem.material.d.ts +0 -9
- package/dist/emblems/emblem.material.data.d.ts +0 -34
- package/dist/emblems/emblem.material.data.js +0 -85
- package/dist/emblems/emblem.material.js +0 -23
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// A flat object or an icon: a list of shapes, each with a role (the metal, its shade, a glint, a second colour, a fixed colour, ink, a line), drawn with gradients in the metal style, in flat fills in the
|
|
2
2
|
// `color` style and as one outline in the `line` style. A flat object (a medal, a crest, a plaque) also has a back face and a rim, so the turning view flips it like a coin; an icon (a sport's pictogram)
|
|
3
3
|
// is drawn as it is. Imports only the core.
|
|
4
|
-
import { bevelGradient, defsOf, degrees, escape, hexOf, mainOf, mix, n2, onColour, orbGradient, readColour, resolve, sheetGradient, wrap, } from "./emblem.core.js";
|
|
4
|
+
import { bevelGradient, defsOf, degrees, escape, hexOf, labelled, mainOf, mix, n2, onColour, orbGradient, readColour, resolve, sheetGradient, wrap, } from "./emblem.core.js";
|
|
5
|
+
import { engravingOf, linesOf, plateMarkup } from "./emblem.engrave.js";
|
|
6
|
+
import { flipSvg } from "./emblem.flip.js";
|
|
5
7
|
const use = (ctx, g) => {
|
|
6
8
|
ctx.used.push(g);
|
|
7
9
|
return g.fill;
|
|
@@ -55,14 +57,28 @@ function letters(model, ctx) {
|
|
|
55
57
|
const fs = n === 1 ? size : n === 2 ? size * 0.82 : size * 0.64;
|
|
56
58
|
return `<text x="${x}" y="${n2(y + fs * 0.36)}" font-size="${n2(fs)}" font-weight="800" font-family="system-ui,sans-serif" text-anchor="middle" fill="${ctx.style === "line" ? "currentColor" : onColour(ctx.main)}" stroke="none">${escape(ctx.initials)}</text>`;
|
|
57
59
|
}
|
|
58
|
-
/**
|
|
59
|
-
function
|
|
60
|
+
/** The lines cut into the face on `side`, as markup, or an empty string when there is nothing to cut or nowhere to cut it. */
|
|
61
|
+
function engraved(model, ctx, side) {
|
|
62
|
+
const e = ctx.engrave;
|
|
63
|
+
const plates = model.engrave;
|
|
64
|
+
if (plates === undefined)
|
|
65
|
+
return "";
|
|
66
|
+
if (side === "back")
|
|
67
|
+
return plates.back === undefined || e.back === undefined ? "" : plateMarkup(linesOf({ free: e.back }).main, plates.back, ctx.main, ctx.style);
|
|
68
|
+
if (e.free === undefined && e.label === undefined && e.year === undefined && e.rank === undefined)
|
|
69
|
+
return "";
|
|
70
|
+
const { main, year } = linesOf(e, { rank: plates.rank, yearApart: plates.year !== undefined });
|
|
71
|
+
return plateMarkup(main, plates.front, ctx.main, ctx.style) + (year === undefined || plates.year === undefined ? "" : plateMarkup([[{ text: year, weight: 1 }]], plates.year, ctx.main, ctx.style));
|
|
72
|
+
}
|
|
73
|
+
/** One face of an object as markup: its shapes (the ones an engraving replaces left out when there is one), then the engraving, and a crest's letters. */
|
|
74
|
+
function face(model, shapes, ctx, text = "") {
|
|
75
|
+
const shown = shapes.filter((x) => text === "" || x[0] === x[0].toLowerCase()).map((x) => (x[0] === x[0].toLowerCase() ? x : [x[0].toLowerCase(), x[1], x[2]]));
|
|
60
76
|
const blank = ctx.initials === "" && model.blank !== undefined ? shapesMarkup(model.blank, ctx) : "";
|
|
61
|
-
return shapesMarkup(
|
|
77
|
+
return shapesMarkup(shown, ctx) + blank + letters(model, ctx) + text;
|
|
62
78
|
}
|
|
63
79
|
function context(model, r) {
|
|
64
80
|
const main = r.tint ?? (model.color === undefined ? mainOf(model.metal, undefined) : readColour(model.color));
|
|
65
|
-
return { main, style: r.style, sheet: sheetGradient(main), orb: orbGradient(main), bevel: bevelGradient(main), ribbon: r.accent ?? model.ribbon ?? "#c0392b", initials: r.initials, used: [] };
|
|
81
|
+
return { main, style: r.style, sheet: sheetGradient(main), orb: orbGradient(main), bevel: bevelGradient(main), ribbon: r.accent ?? model.ribbon ?? "#c0392b", initials: r.initials, engrave: {}, used: [] };
|
|
66
82
|
}
|
|
67
83
|
function document(title, model, r, ctx, body) {
|
|
68
84
|
return wrap({ title, size: r.size, style: r.style, theme: r.theme, defs: r.style === "metal" ? defsOf(ctx.used) : "", body, box: model.box, edge: model.ink === true ? undefined : mix(ctx.main, -0.5), width: model.ink === true ? undefined : 0.8 });
|
|
@@ -72,8 +88,9 @@ export function defineFlat(id, en, ja, source, model) {
|
|
|
72
88
|
const draw = (o, yaw, pitch) => {
|
|
73
89
|
const r = resolve(o);
|
|
74
90
|
const ctx = context(model, r);
|
|
91
|
+
ctx.engrave = engravingOf(o);
|
|
75
92
|
if (yaw === 0 && pitch === 0)
|
|
76
|
-
return document(en, model, r, ctx, face(model, model.front, ctx));
|
|
93
|
+
return document(en, model, r, ctx, face(model, model.front, ctx, engraved(model, ctx, "front")));
|
|
77
94
|
const across = Math.cos((yaw * Math.PI) / 180);
|
|
78
95
|
const tilt = Math.cos((pitch * Math.PI) / 180);
|
|
79
96
|
const shown = across >= 0 ? model.front : (model.back ?? model.front.filter((x) => x[0] === "m" || x[0] === "o" || x[0] === "s"));
|
|
@@ -82,18 +99,19 @@ export function defineFlat(id, en, ja, source, model) {
|
|
|
82
99
|
const rim = (model.rim ?? 0) * Math.sin((yaw * Math.PI) / 180);
|
|
83
100
|
const [x0, y0, w, h] = (model.box ?? "0 0 64 64").split(" ").map(Number);
|
|
84
101
|
const thick = rim === 0 ? "" : `<g transform="translate(${n2(rim)} 0)">${shapesMarkup(model.front.filter((x) => x[0] === "m" || x[0] === "o").map((x) => ["s", x[1]]), ctx)}</g>`;
|
|
85
|
-
return document(en, model, r, ctx, `<g transform="translate(${n2(x0 + w / 2)} ${n2(y0 + h / 2)}) scale(${n2(sx)} ${n2(sy)}) translate(${n2(-x0 - w / 2)} ${n2(-y0 - h / 2)})">${thick}${face(model, shown, ctx)}</g>`);
|
|
102
|
+
return document(en, model, r, ctx, `<g transform="translate(${n2(x0 + w / 2)} ${n2(y0 + h / 2)}) scale(${n2(sx)} ${n2(sy)}) translate(${n2(-x0 - w / 2)} ${n2(-y0 - h / 2)})">${thick}${face(model, shown, ctx, engraved(model, ctx, across >= 0 ? "front" : "back"))}</g>`);
|
|
86
103
|
};
|
|
87
|
-
return { id, en, ja, source, kind: "flat", model, svg: (o) => draw(o, 0, 0), frame: (o) => draw(o, degrees(o?.yaw), degrees(o?.pitch)) };
|
|
104
|
+
return { id, en, ja, source, kind: "flat", model, svg: labelled(en, ja, (o) => draw(o, 0, 0)), frame: labelled(en, ja, (o) => draw(o, degrees(o?.yaw), degrees(o?.pitch))) };
|
|
88
105
|
}
|
|
89
|
-
/** An icon: one drawing from shapes,
|
|
106
|
+
/** An icon: one drawing from shapes. Turned, it is a thin cut-out: it squeezes like a coin, shows its front mirrored and a little darker past a quarter turn, and has a dark rim. */
|
|
90
107
|
export function defineIcon(id, en, ja, source, model) {
|
|
91
|
-
const svg = (o) => {
|
|
108
|
+
const svg = labelled(en, ja, (o) => {
|
|
92
109
|
const r = resolve(o);
|
|
93
110
|
const ctx = context(model, r);
|
|
94
111
|
return document(en, model, r, ctx, face(model, model.front, ctx));
|
|
95
|
-
};
|
|
96
|
-
|
|
112
|
+
});
|
|
113
|
+
const frame = (o) => flipSvg(svg(o), { yaw: degrees(o?.yaw), pitch: degrees(o?.pitch), line: o?.style === "line", thick: 0.03 });
|
|
114
|
+
return { id, en, ja, source, kind: "icon", model, svg, frame: labelled(en, ja, frame) };
|
|
97
115
|
}
|
|
98
116
|
/** A circle as a path, so every shape is one kind of thing. */
|
|
99
117
|
export const circlePath = (cx, cy, r) => `M${n2(cx - r)} ${n2(cy)}a${n2(r)} ${n2(r)} 0 1 0 ${n2(2 * r)} 0a${n2(r)} ${n2(r)} 0 1 0 ${n2(-2 * r)} 0Z`;
|
|
@@ -109,3 +127,26 @@ export function starPath(cx, cy, outer, inner, points = 5) {
|
|
|
109
127
|
}
|
|
110
128
|
return `${parts.join("")}Z`;
|
|
111
129
|
}
|
|
130
|
+
/** A regular polygon of `n` sides with a corner at `deg` degrees (0 is straight up), as a path. */
|
|
131
|
+
export function polygon(cx, cy, r, n, deg = 0) {
|
|
132
|
+
let d = "";
|
|
133
|
+
for (let i = 0; i < n; i++) {
|
|
134
|
+
const a = ((deg + (360 * i) / n - 90) * Math.PI) / 180;
|
|
135
|
+
d += `${i === 0 ? "M" : "L"}${n2(cx + r * Math.cos(a))} ${n2(cy + r * Math.sin(a))}`;
|
|
136
|
+
}
|
|
137
|
+
return `${d}Z`;
|
|
138
|
+
}
|
|
139
|
+
/** An ellipse turned `deg` degrees about its centre, as a path. */
|
|
140
|
+
export function tiltedEllipse(cx, cy, rx, ry, deg) {
|
|
141
|
+
const a = (deg * Math.PI) / 180;
|
|
142
|
+
const x = rx * Math.cos(a);
|
|
143
|
+
const y = rx * Math.sin(a);
|
|
144
|
+
return `M${n2(cx - x)} ${n2(cy - y)}A${n2(rx)} ${n2(ry)} ${deg} 1 1 ${n2(cx + x)} ${n2(cy + y)}A${n2(rx)} ${n2(ry)} ${deg} 1 1 ${n2(cx - x)} ${n2(cy - y)}Z`;
|
|
145
|
+
}
|
|
146
|
+
/** A bar with square ends from one point to another, `w` wide, as a path. */
|
|
147
|
+
export function bar(x1, y1, x2, y2, w) {
|
|
148
|
+
const l = Math.hypot(x2 - x1, y2 - y1) || 1;
|
|
149
|
+
const nx = (-(y2 - y1) / l) * (w / 2);
|
|
150
|
+
const ny = ((x2 - x1) / l) * (w / 2);
|
|
151
|
+
return `M${n2(x1 + nx)} ${n2(y1 + ny)}L${n2(x2 + nx)} ${n2(y2 + ny)}L${n2(x2 - nx)} ${n2(y2 - ny)}L${n2(x1 - nx)} ${n2(y1 - ny)}Z`;
|
|
152
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type EmblemLocale, type EmblemOptions } from "./emblem.core.ts";
|
|
2
|
+
import { type EmblemRef } from "./emblem.api.ts";
|
|
3
|
+
/** What `emblemSprite` takes: the options every symbol is drawn with, and the prefix of its ids. */
|
|
4
|
+
export interface SpriteOptions extends EmblemOptions {
|
|
5
|
+
/** The start of every id in the sheet (letters, digits and hyphens, starting with a letter). By default it is made from the emblems and the options, so the same call gives the same sheet. */
|
|
6
|
+
prefix?: string;
|
|
7
|
+
}
|
|
8
|
+
/** What one `<use>` takes. */
|
|
9
|
+
export interface UseOptions {
|
|
10
|
+
/** The width and height in pixels (64 by default). */
|
|
11
|
+
size?: number;
|
|
12
|
+
/** A class for the `<svg>` that holds the `<use>`. */
|
|
13
|
+
class?: string;
|
|
14
|
+
/** The `<title>`: a string writes that title, `false` drops it and hides the drawing from a screen reader. By default it is the emblem's name. */
|
|
15
|
+
title?: string | false;
|
|
16
|
+
/** The language of the default title, `"en"` or `"ja"`. */
|
|
17
|
+
locale?: EmblemLocale;
|
|
18
|
+
}
|
|
19
|
+
/** A sheet of symbols and the snippets that draw them. */
|
|
20
|
+
export interface Sprite {
|
|
21
|
+
/** One hidden `<svg>` of `<symbol>`s; put it once in the page, anywhere in `<body>`. */
|
|
22
|
+
sheet: string;
|
|
23
|
+
/** The markup for one use of a symbol: `<svg><title>…</title><use href="#…"/></svg>`. The emblem must be one the sprite was made with. */
|
|
24
|
+
use: (ref: EmblemRef, options?: UseOptions) => string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A sprite sheet: every emblem in `ids` drawn once as a `<symbol>`, with the gradients and other definitions shared and every id unique across the sheet, and a `use(id, { size, class, title })` that returns the
|
|
28
|
+
* markup for one more of it. Put `sheet` in the page once; a bracket or a table that shows the same ball or medal fifty times then adds a few nodes each time, not the whole drawing. The symbols are drawn without the
|
|
29
|
+
* grain a large ball has by default (pass `grain: true` to keep it). `style`, `tint`, `accent` and `theme` are the sheet's, for every symbol; a different look is another sprite, or an id that names its style (`"medal:gold/line"`).
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { emblemSprite } from "@johnmorrisdotca/dougu-sports/emblems";
|
|
33
|
+
* const sprite = emblemSprite(["ball:soccer", "medal:gold"]);
|
|
34
|
+
* console.log(sprite.use("ball:soccer", { size: 24 }).startsWith("<svg"));
|
|
35
|
+
* // true
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function emblemSprite(ids: readonly EmblemRef[], options?: SpriteOptions): Sprite;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// A sheet of symbols: each emblem drawn once, then used as many times as a page wants it with a `<use>` that costs a few nodes and a few dozen bytes.
|
|
2
|
+
import { EMBLEM_INK, escape, resolve } from "./emblem.core.js";
|
|
3
|
+
import { emblemOf, parseEmblem, renderEmblem } from "./emblem.api.js";
|
|
4
|
+
import { IdSpace, partsOf } from "./emblem.compose.js";
|
|
5
|
+
const hash = (text) => {
|
|
6
|
+
let h = 2166136261;
|
|
7
|
+
for (let i = 0; i < text.length; i++)
|
|
8
|
+
h = Math.imul(h ^ text.charCodeAt(i), 16777619);
|
|
9
|
+
return (h >>> 0).toString(36);
|
|
10
|
+
};
|
|
11
|
+
/** The key a reference goes by in a sprite: the id, and the style when the reference names one. */
|
|
12
|
+
function keyOf(ref) {
|
|
13
|
+
if (typeof ref !== "string")
|
|
14
|
+
return ref.id;
|
|
15
|
+
const parsed = parseEmblem(ref);
|
|
16
|
+
if (parsed === undefined)
|
|
17
|
+
throw new Error(`Unknown emblem "${ref.slice(0, 60)}". Ids look like "ball:soccer" or "medal:gold/line"; see EMBLEM_IDS.`);
|
|
18
|
+
return parsed.style === undefined ? parsed.id : `${parsed.id}/${parsed.style}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A sprite sheet: every emblem in `ids` drawn once as a `<symbol>`, with the gradients and other definitions shared and every id unique across the sheet, and a `use(id, { size, class, title })` that returns the
|
|
22
|
+
* markup for one more of it. Put `sheet` in the page once; a bracket or a table that shows the same ball or medal fifty times then adds a few nodes each time, not the whole drawing. The symbols are drawn without the
|
|
23
|
+
* grain a large ball has by default (pass `grain: true` to keep it). `style`, `tint`, `accent` and `theme` are the sheet's, for every symbol; a different look is another sprite, or an id that names its style (`"medal:gold/line"`).
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { emblemSprite } from "@johnmorrisdotca/dougu-sports/emblems";
|
|
27
|
+
* const sprite = emblemSprite(["ball:soccer", "medal:gold"]);
|
|
28
|
+
* console.log(sprite.use("ball:soccer", { size: 24 }).startsWith("<svg"));
|
|
29
|
+
* // true
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function emblemSprite(ids, options) {
|
|
33
|
+
const keys = [...new Set(ids.map(keyOf))];
|
|
34
|
+
const { prefix: asked, ...drawn } = options ?? {};
|
|
35
|
+
const prefix = typeof asked === "string" && /^[A-Za-z][\w-]{0,31}$/.test(asked) ? asked : `dgs${hash(`${keys.join(",")}|${JSON.stringify(drawn)}`)}`;
|
|
36
|
+
const theme = drawn.theme === "dark" || drawn.theme === "light" || drawn.theme === "auto" ? drawn.theme : "inherit";
|
|
37
|
+
const space = new IdSpace(prefix);
|
|
38
|
+
const symbols = [];
|
|
39
|
+
const symbolOf = new Map();
|
|
40
|
+
for (const key of keys) {
|
|
41
|
+
const svg = renderEmblem(key, { ...drawn, theme: "inherit", title: false, locale: undefined, grain: drawn.grain ?? false });
|
|
42
|
+
const parts = partsOf(svg);
|
|
43
|
+
const name = `${prefix}-${key.replace(/[^a-z0-9]+/gi, "-").toLowerCase()}`;
|
|
44
|
+
symbolOf.set(key, name);
|
|
45
|
+
symbols.push(`<symbol id="${name}" viewBox="${escape(parts.box)}">${space.add(parts)}</symbol>`);
|
|
46
|
+
}
|
|
47
|
+
const rule = theme === "auto" ? `<style>@media(prefers-color-scheme:dark){svg.${prefix}{color:${EMBLEM_INK.dark}}}</style>` : "";
|
|
48
|
+
const sheet = `<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" style="position:absolute;width:0;height:0;overflow:hidden" aria-hidden="true" focusable="false">` +
|
|
49
|
+
`${rule}${space.defs.length === 0 ? "" : `<defs>${space.defs.join("")}</defs>`}${symbols.join("")}</svg>`;
|
|
50
|
+
const color = theme === "dark" ? EMBLEM_INK.dark : theme === "light" ? EMBLEM_INK.light : "";
|
|
51
|
+
const use = (ref, o) => {
|
|
52
|
+
const key = keyOf(ref);
|
|
53
|
+
const symbol = symbolOf.get(key);
|
|
54
|
+
if (symbol === undefined)
|
|
55
|
+
throw new Error(`"${key}" is not in this sprite; it holds ${keys.slice(0, 8).join(", ")}${keys.length > 8 ? ", …" : ""}.`);
|
|
56
|
+
const size = resolve({ size: o?.size }).size;
|
|
57
|
+
const classes = [theme === "auto" ? prefix : "", typeof o?.class === "string" ? o.class.trim() : ""].filter(Boolean).join(" ");
|
|
58
|
+
const { emblem } = emblemOf(ref);
|
|
59
|
+
const title = o?.title === false ? false : typeof o?.title === "string" && o.title.trim() !== "" ? o.title.trim().slice(0, 300) : o?.locale === "ja" ? emblem.ja : emblem.en;
|
|
60
|
+
const lang = o?.locale === "ja" && typeof o?.title !== "string" ? ' lang="ja"' : "";
|
|
61
|
+
return (`<svg width="${size}" height="${size}"${title === false ? ' aria-hidden="true"' : ` role="img"${lang}`}${classes === "" ? "" : ` class="${escape(classes)}"`}${color === "" ? "" : ` color="${color}"`}>` +
|
|
62
|
+
`${title === false ? "" : `<title>${escape(title)}</title>`}<use href="#${symbol}"/></svg>`);
|
|
63
|
+
};
|
|
64
|
+
return { sheet, use };
|
|
65
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type EmblemOptions } from "./emblem.core.ts";
|
|
2
|
+
import { type EmblemRef } from "./emblem.api.ts";
|
|
3
|
+
/** What `emblemDataUri` takes: the emblem's options, and the colour of the line. */
|
|
4
|
+
export interface DataUriOptions extends EmblemOptions {
|
|
5
|
+
/** The colour of the outline and of anything drawn in the ink: `#rgb`, `#rrggbb` or `rgb()`. It wins over `theme`. Anything else is ignored. */
|
|
6
|
+
color?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Text as the body of a data address: every character that could end an attribute, a quoted `url()` or an unquoted one is percent-encoded, and the rest is left alone. */
|
|
9
|
+
export declare function encodeSvg(svg: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* An emblem as a data address, `data:image/svg+xml;utf8,…`, with every character that could break an attribute or a CSS `url()` percent-encoded, so the string goes anywhere: `img.src`, `background-image:
|
|
12
|
+
* url("…")`, `new Image().src` for a canvas. The ink is settled for you, because `currentColor` does not cross an `<img>` boundary: `color` is the line's colour; without it `theme: "dark"` is pale ink, and `"light"`
|
|
13
|
+
* and `"inherit"` (the default) are dark ink. `theme: "auto"` keeps the page's colour scheme (`prefers-color-scheme`) in the image.
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { emblemDataUri } from "@johnmorrisdotca/dougu-sports/emblems";
|
|
17
|
+
* const src = emblemDataUri("medal:gold", { size: 48 });
|
|
18
|
+
* console.log(src.startsWith("data:image/svg+xml;utf8,%3Csvg"));
|
|
19
|
+
* // true
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function emblemDataUri(ref: EmblemRef, options?: DataUriOptions): string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// An emblem as a data address, for an `<img>`, a CSS background or a canvas. An image cannot inherit its page's text colour, so the colour is settled here: the line style, a pictogram and a Material
|
|
2
|
+
// icon are drawn in `currentColor`, and that becomes a concrete colour.
|
|
3
|
+
import { EMBLEM_INK, hexOf, readColour } from "./emblem.core.js";
|
|
4
|
+
import { renderEmblem } from "./emblem.api.js";
|
|
5
|
+
/** Text as the body of a data address: every character that could end an attribute, a quoted `url()` or an unquoted one is percent-encoded, and the rest is left alone. */
|
|
6
|
+
export function encodeSvg(svg) {
|
|
7
|
+
return encodeURIComponent(svg).replace(/%(3A|2F|2C|3B|3D|40)/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))).replace(/['()*!]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* An emblem as a data address, `data:image/svg+xml;utf8,…`, with every character that could break an attribute or a CSS `url()` percent-encoded, so the string goes anywhere: `img.src`, `background-image:
|
|
11
|
+
* url("…")`, `new Image().src` for a canvas. The ink is settled for you, because `currentColor` does not cross an `<img>` boundary: `color` is the line's colour; without it `theme: "dark"` is pale ink, and `"light"`
|
|
12
|
+
* and `"inherit"` (the default) are dark ink. `theme: "auto"` keeps the page's colour scheme (`prefers-color-scheme`) in the image.
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { emblemDataUri } from "@johnmorrisdotca/dougu-sports/emblems";
|
|
16
|
+
* const src = emblemDataUri("medal:gold", { size: 48 });
|
|
17
|
+
* console.log(src.startsWith("data:image/svg+xml;utf8,%3Csvg"));
|
|
18
|
+
* // true
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function emblemDataUri(ref, options) {
|
|
22
|
+
const { color, ...rest } = options ?? {};
|
|
23
|
+
const picked = readColour(color);
|
|
24
|
+
const ink = picked !== undefined ? hexOf(picked) : rest.theme === "dark" ? EMBLEM_INK.dark : rest.theme === "auto" ? undefined : EMBLEM_INK.light;
|
|
25
|
+
let svg = renderEmblem(ref, { ...rest, theme: ink === undefined ? "auto" : "inherit" });
|
|
26
|
+
// `currentColor` becomes the ink. A drawing that asked for `auto` keeps its colour-scheme rule, with the dark ink as its light default.
|
|
27
|
+
svg = ink === undefined ? svg.replace("<style>@media", `<style>svg{color:${EMBLEM_INK.light}}@media`) : svg.replace(/currentColor/g, ink);
|
|
28
|
+
return `data:image/svg+xml;utf8,${encodeSvg(svg)}`;
|
|
29
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const GEAR_BADMINTON_RACKET: import("./gear.index.ts").Piece<import("./gear.core.ts").
|
|
1
|
+
export declare const GEAR_BADMINTON_RACKET: import("./gear.index.ts").Piece<import("./gear.core.ts").GearSpec>;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
// A badminton racket and a shuttlecock, drawn for this package: an oval strung head on a slender frame, a T-shaped throat and a grip, and a feathered shuttlecock on its cork.
|
|
2
|
+
import { capsule, drawn, ellipse, seg, strings } from "./gear.core.js";
|
|
3
|
+
const T = "rotate(34 32 32)";
|
|
4
|
+
const S = "translate(50.5 50) rotate(48) scale(.95) translate(-32 -13)";
|
|
5
|
+
export const GEAR_BADMINTON_RACKET = /* @__PURE__ */ drawn("gear:badminton-racket", "Badminton racket", "バドミントンのラケット", {
|
|
6
|
+
main: "#2f8fe0",
|
|
7
|
+
accent: "#2a2f38",
|
|
8
|
+
parts: [
|
|
9
|
+
{ d: strings(32, 15, 10.4, 13.4, 3.1), fill: "none", stroke: "#9aa7b6", w: 0.6, t: T },
|
|
10
|
+
{ d: ellipse(32, 15, 11, 14), fill: "none", stroke: "$main", w: 2.6, t: T },
|
|
11
|
+
{ d: "M24.2 24.6L30.6 36M39.8 24.6L33.4 36", fill: "none", stroke: "$main", w: 2.2, t: T },
|
|
12
|
+
{ d: capsule(32, 34, 32, 50, 2.2), fill: "#aeb6c0", t: T, cyl: 0.9, shade: 0 },
|
|
13
|
+
{ d: capsule(32, 46, 32, 62, 4.4), fill: "$accent", t: T, cyl: 0.8, shade: 0 },
|
|
14
|
+
{ d: seg(30, 52, 34, 52) + seg(30, 56, 34, 56), fill: "none", stroke: "#6a7482", w: 0.7, t: T, line: "none" },
|
|
15
|
+
{ d: "M28.4 20L24 4Q32 1 40 4L35.6 20z", fill: "#f4f6f8", stroke: "#9aa7b6", t: S, shade: 0.8 },
|
|
16
|
+
{ d: "M29.6 19L27 5M32 19.4V3.4M34.4 19L37 5", fill: "none", stroke: "#9aa7b6", w: 0.7, t: S, line: "none" },
|
|
17
|
+
{ d: "M27.6 20h8.8a4.4 4.4 0 0 1-8.8 0z", fill: "#e8cf9a", t: S, shade: 0.9 },
|
|
18
|
+
],
|
|
15
19
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const GEAR_BASEBALL_GLOVE: import("./gear.index.ts").Piece<import("./gear.core.ts").
|
|
1
|
+
export declare const GEAR_BASEBALL_GLOVE: import("./gear.index.ts").Piece<import("./gear.core.ts").GearSpec>;
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
// A baseball glove with a ball, drawn for this package: a leather mitt of four fingers and a thumb round a deep pocket, a laced web, a wrist strap and a ball in the pocket.
|
|
2
|
+
import { capsule, circle, drawn, ellipse } from "./gear.core.js";
|
|
3
|
+
export const GEAR_BASEBALL_GLOVE = /* @__PURE__ */ drawn("gear:baseball-glove", "Baseball glove", "野球のグラブ", {
|
|
4
|
+
main: "#b5702f",
|
|
5
|
+
accent: "#e6cfa0",
|
|
6
|
+
parts: [
|
|
7
|
+
{ d: capsule(17, 30, 12.6, 16, 9.6), fill: "$main", cyl: 0.8, shade: 0.3 },
|
|
8
|
+
{ d: capsule(28, 28, 26.6, 11.6, 10), fill: "$main", cyl: 0.8, shade: 0.3 },
|
|
9
|
+
{ d: capsule(39, 28.4, 40.6, 12.6, 9.8), fill: "$main", cyl: 0.8, shade: 0.3 },
|
|
10
|
+
{ d: capsule(48, 32, 52.6, 19.4, 9), fill: "$main", cyl: 0.8, shade: 0.3 },
|
|
11
|
+
{ d: capsule(12, 42, 5.8, 34.6, 10), fill: "$main", cyl: 0.8, shade: 0.3 },
|
|
12
|
+
{ d: "M9 34C7 47 16 59 32 59C48 59 58 49 56 34C53 25 45 23 36 23C27 23 13 23 9 34Z", fill: "$main", shade: 0.6 },
|
|
13
|
+
{ d: ellipse(32, 42, 15.4, 12.4), fill: "#8f5220", stroke: "#6a3a14", shade: 0, line: "stroke" },
|
|
14
|
+
{ d: "M13.4 24.2L17 29.4M23.4 20L24.6 26M34 20.6L34 26M45 22.6L42.4 27.4M8.6 36L13.4 36.6", fill: "none", stroke: "$accent", w: 1.4, line: "none" },
|
|
15
|
+
{ d: "M20 55.6Q32 61 45 55.4", fill: "none", stroke: "$accent", w: 1.6, line: "none" },
|
|
16
|
+
{ d: "M22 56.4L44 56.6L43.4 60.8Q32 63.4 23 60.8Z", fill: "#7a4318", stroke: "#4a280c", shade: 0.3, line: "none" },
|
|
17
|
+
{ d: circle(32, 41, 7.8), fill: "#f6f3ec", stroke: "#8f98a3", shade: 0.9 },
|
|
18
|
+
{ d: "M27.2 35.4Q31 41 27.2 46.6M36.8 35.4Q33 41 36.8 46.6", fill: "none", stroke: "#c4262e", w: 1, line: "none" },
|
|
19
|
+
{ d: "M8.6 35Q9.6 29 13 26.4", fill: "none", stroke: "#fff", w: 1.6, op: 0.4, line: "none", fx: true },
|
|
20
|
+
],
|
|
15
21
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PieceOptions } from "../ball/ball.core.ts";
|
|
2
|
+
/** The colours of a judo belt from the first rank to the black belt, in order, which `tint` takes. */
|
|
3
|
+
export declare const OBI_RANKS: {
|
|
4
|
+
readonly white: "#f2efe8";
|
|
5
|
+
readonly yellow: "#f2c21b";
|
|
6
|
+
readonly orange: "#ee8a1c";
|
|
7
|
+
readonly green: "#2f9a52";
|
|
8
|
+
readonly blue: "#2b64c8";
|
|
9
|
+
readonly brown: "#7a4a24";
|
|
10
|
+
readonly black: "#1f2228";
|
|
11
|
+
};
|
|
12
|
+
export type ObiRank = keyof typeof OBI_RANKS;
|
|
13
|
+
export declare const GEAR_JUDOGI: import("./gear.index.ts").Piece<import("./gear.core.ts").GearSpec>;
|
|
14
|
+
export declare const GEAR_KARATE_GI: import("./gear.index.ts").Piece<import("./gear.core.ts").GearSpec>;
|
|
15
|
+
export declare const GEAR_OBI: import("./gear.index.ts").Piece<import("./gear.core.ts").GearSpec>;
|
|
16
|
+
/** The belt of a rank, `"white"` to `"black"`: the same as `GEAR_OBI.svg({ ...options, tint: OBI_RANKS[rank] })`. An unknown rank is the black belt. */
|
|
17
|
+
export declare const obi: (rank: ObiRank, options?: PieceOptions) => string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { drawn, seg } from "./gear.core.js";
|
|
2
|
+
/** The colours of a judo belt from the first rank to the black belt, in order, which `tint` takes. */
|
|
3
|
+
export const OBI_RANKS = {
|
|
4
|
+
white: "#f2efe8",
|
|
5
|
+
yellow: "#f2c21b",
|
|
6
|
+
orange: "#ee8a1c",
|
|
7
|
+
green: "#2f9a52",
|
|
8
|
+
blue: "#2b64c8",
|
|
9
|
+
brown: "#7a4a24",
|
|
10
|
+
black: "#1f2228",
|
|
11
|
+
};
|
|
12
|
+
const CLOTH = "#f4f2ec";
|
|
13
|
+
const EDGE = "#7b8491";
|
|
14
|
+
/** A belt tied at the waist (a flat band, a knot, two ends hanging) from `x0` to `x1` at height `y`. */
|
|
15
|
+
const tied = (x0, x1, y) => {
|
|
16
|
+
const c = (x0 + x1) / 2;
|
|
17
|
+
return `M${x0} ${y}H${x1}V${y + 4}H${x0}zM${c - 3} ${y - 0.6}h6v5.2h-6zM${c - 1.4} ${y + 4.6}L${c - 4.6} ${y + 14}h3.4L${c + 0.4} ${y + 4.6}zM${c + 1.4} ${y + 4.6}L${c + 4.8} ${y + 13.4}h-3.4L${c - 0.4} ${y + 4.6}z`;
|
|
18
|
+
};
|
|
19
|
+
export const GEAR_JUDOGI = /* @__PURE__ */ drawn("gear:judogi", "Judo suit", "柔道衣", {
|
|
20
|
+
main: CLOTH,
|
|
21
|
+
accent: "#1f2228",
|
|
22
|
+
parts: [
|
|
23
|
+
{ d: "M22.4 44.4H31.6L30.8 61H21.2ZM32.4 44.4H41.6L42.8 61H33.2Z", fill: "$main", cyl: 0.6, shade: 0.3 },
|
|
24
|
+
{ d: "M23.6 9.4L8 36.4Q6.6 39.4 9.4 41.4L14.6 45Q17.4 46.4 19 43.4L29 27.4Z", fill: "$main", cyl: 0.5, shade: 0.4 },
|
|
25
|
+
{ d: "M40.4 9.4L56 36.4Q57.4 39.4 54.6 41.4L49.4 45Q46.6 46.4 45 43.4L35 27.4Z", fill: "$main", cyl: 0.5, shade: 0.4 },
|
|
26
|
+
{ d: "M23.6 9.4Q32 7 40.4 9.4L45 27.4V46H19V27.4Z", fill: "$main", cyl: 0.6, shade: 0.4 },
|
|
27
|
+
{ d: "M26 8.6L35.4 31M38 8.6L28.6 31", fill: "none", stroke: "#cfc8b8", w: 3.6, line: "none" },
|
|
28
|
+
{ d: "M26 8.6L35.4 31M38 8.6L28.6 31", fill: "none", stroke: "#8f98a3", w: 0.7, line: "stroke" },
|
|
29
|
+
{ d: "M29.2 9.4Q32 12 34.8 9.4L32 16Z", fill: "#3a414b", stroke: "none", op: 0.5, line: "none", fx: true },
|
|
30
|
+
{ d: "M13 31L19.4 39.4M11.4 34.4L17.6 42.4M47 31L40.6 39.4M48.6 34.4L42.4 42.4", fill: "none", stroke: "#a9b1bb", w: 0.7, line: "none" },
|
|
31
|
+
{ d: tied(19, 45, 40.4), fill: "$accent", stroke: EDGE, cyl: 0.8, shade: 0.2 },
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
export const GEAR_KARATE_GI = /* @__PURE__ */ drawn("gear:karate-gi", "Karate suit", "空手衣", {
|
|
35
|
+
main: CLOTH,
|
|
36
|
+
accent: "#1f2228",
|
|
37
|
+
parts: [
|
|
38
|
+
{ d: "M20.4 46H31.4L28.4 61H17.8ZM32.6 46H43.6L46.2 61H35.6Z", fill: "$main", cyl: 0.6, shade: 0.3 },
|
|
39
|
+
{ d: "M22.6 9.6L9 27.4Q7 30.4 9.6 32.4L13 34.6Q15.6 36.6 17.6 33.6L26 22Z", fill: "$main", cyl: 0.5, shade: 0.4 },
|
|
40
|
+
{ d: "M41.4 9.6L55 27.4Q57 30.4 54.4 32.4L51 34.6Q48.4 36.6 46.4 33.6L38 22Z", fill: "$main", cyl: 0.5, shade: 0.4 },
|
|
41
|
+
{ d: "M22.6 9.6Q32 7.4 41.4 9.6L44.8 28V48H19.2V28Z", fill: "$main", cyl: 0.6, shade: 0.4 },
|
|
42
|
+
{ d: "M25.4 8.8L37 37M38.6 8.8L27 37", fill: "none", stroke: "#8f98a3", w: 0.9, line: "stroke" },
|
|
43
|
+
{ d: "M28.8 9.6Q32 13 35.2 9.6L32 19Z", fill: "#3a414b", stroke: "none", op: 0.5, line: "none", fx: true },
|
|
44
|
+
{ d: tied(19.2, 44.8, 40), fill: "$accent", stroke: EDGE, cyl: 0.8, shade: 0.2 },
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
export const GEAR_OBI = /* @__PURE__ */ drawn("gear:obi", "Belt", "武道の帯", {
|
|
48
|
+
main: OBI_RANKS.black,
|
|
49
|
+
parts: [
|
|
50
|
+
{ d: "M35 34L24.4 57.6L30 61L40.6 36Z", fill: "$main", stroke: EDGE, cyl: 0.7, shade: 0.3 },
|
|
51
|
+
{ d: "M29 34L40.4 57.6L46 55.4L34.6 34Z", fill: "$main", stroke: EDGE, cyl: 0.7, shade: 0.3 },
|
|
52
|
+
{ d: seg(27.4, 55.6, 31.4, 58.4) + seg(38.4, 54.6, 42.4, 53), fill: "none", stroke: "$accent", w: 1.6, line: "none" },
|
|
53
|
+
{ d: "M2.6 23.4Q32 27.6 61.4 23.4V34.4Q32 38.6 2.6 34.4Z", fill: "$main", stroke: EDGE, cyl: 0.7, shade: 0.3 },
|
|
54
|
+
{ d: "M2.6 27.2Q32 31.4 61.4 27.2", fill: "none", stroke: "$accent", w: 1.4, line: "none" },
|
|
55
|
+
{ d: "M25.4 22.4h13.2v15.6H25.4z", fill: "$main", stroke: EDGE, cyl: 1, shade: 0.3 },
|
|
56
|
+
{ d: "M25.4 27.6h13.2M25.4 32.6h13.2", fill: "none", stroke: "$accent", w: 0.8, line: "none" },
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
/** The belt of a rank, `"white"` to `"black"`: the same as `GEAR_OBI.svg({ ...options, tint: OBI_RANKS[rank] })`. An unknown rank is the black belt. */
|
|
60
|
+
export const obi = (rank, options) => GEAR_OBI.svg({ ...options, tint: Object.hasOwn(OBI_RANKS, rank) ? OBI_RANKS[rank] : OBI_RANKS.black });
|
|
@@ -13,8 +13,12 @@ export interface Part {
|
|
|
13
13
|
line?: "stroke" | "ink" | "none";
|
|
14
14
|
/** A transform for the part. */
|
|
15
15
|
t?: string;
|
|
16
|
-
/** How much light and shade the part takes, 0 (flat) to 1 (the default). */
|
|
16
|
+
/** How much light and shade the part takes along a diagonal, 0 (flat) to 1 (the default). */
|
|
17
17
|
shade?: number;
|
|
18
|
+
/** How much a part is shaded across its own width, as a round shaft is (a bright band, a dark side), 0 to 1. Drawn in the part's own frame, so it follows the part's transform. */
|
|
19
|
+
cyl?: number;
|
|
20
|
+
/** A highlight or a shadow laid on a part: drawn in the metal style only, never flat and never as a line. */
|
|
21
|
+
fx?: boolean;
|
|
18
22
|
/** Opacity of the part in colour. */
|
|
19
23
|
op?: number;
|
|
20
24
|
}
|
|
@@ -36,9 +40,9 @@ export interface GearDef<Model = unknown> {
|
|
|
36
40
|
model: Model;
|
|
37
41
|
draw: (options: PieceOptions | undefined) => string;
|
|
38
42
|
}
|
|
39
|
-
/** A piece of gear from its drawing function.
|
|
43
|
+
/** A piece of gear from its drawing function. Turned, it flips like a coin (`flipSvg`): the front, then its mirrored back, with a thin rim. */
|
|
40
44
|
export declare function defineGear<Model>(def: GearDef<Model>): Piece<Model>;
|
|
41
|
-
/** A drawn piece: its parts become a document in
|
|
45
|
+
/** A drawn piece: its parts become a document in the metal, color or line style, whole or turned (the turn is `flipSvg`'s). */
|
|
42
46
|
export declare function drawGear(id: string, title: string, spec: GearSpec, options: PieceOptions | undefined): string;
|
|
43
47
|
/** The pieces' drawing helpers: shapes written as path strings. */
|
|
44
48
|
export declare const rect: (x: number, y: number, w: number, h: number, r?: number) => string;
|
|
@@ -49,35 +53,12 @@ export declare const poly: (...pts: Array<[number, number]>) => string;
|
|
|
49
53
|
export declare const seg: (x1: number, y1: number, x2: number, y2: number) => string;
|
|
50
54
|
/** A capsule: a thick line with round ends, as a closed path (so it has an outline in the line style). */
|
|
51
55
|
export declare function capsule(x1: number, y1: number, x2: number, y2: number, w: number): string;
|
|
52
|
-
/** The ink of a copied colour drawing worked out as a line drawing: dark fills and strokes become `currentColor`, light ones are left out, the rest are a faint tint of it. Used where no other line artwork exists. */
|
|
53
|
-
export declare function monoOf(markup: string): string;
|
|
54
56
|
/** A piece we drew: the common case of `defineGear`. */
|
|
55
57
|
export declare function drawn(id: string, en: string, ja: string, spec: GearSpec, size?: PieceSize): Piece<GearSpec>;
|
|
56
58
|
/** The strings of a racket's head: a grid of lines clipped to an ellipse. */
|
|
57
59
|
export declare function strings(cx: number, cy: number, rx: number, ry: number, step: number): string;
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
source: PieceSource;
|
|
64
|
-
/** The attribution the licence asks for: the author and the licence. */
|
|
65
|
-
credit: string;
|
|
66
|
-
/** The colour artwork's box and its (minified) markup. For a piece drawn in one colour (the glove) it is the path alone. */
|
|
67
|
-
box: string;
|
|
68
|
-
colour: string;
|
|
69
|
-
/** The line artwork: a one-path icon, or `derived` to work it out of the colour one. */
|
|
70
|
-
line: {
|
|
71
|
-
box: string;
|
|
72
|
-
d: string;
|
|
73
|
-
} | "derived";
|
|
74
|
-
/** A pale shape to lay under line artwork that is only ink (a mask's face), so that it reads as a solid thing in colour. */
|
|
75
|
-
underlay?: string;
|
|
76
|
-
/** The colour a one-path piece is filled with (a glove's leather). */
|
|
77
|
-
leather?: string;
|
|
78
|
-
/** The ink of one-colour artwork in the colour style. */
|
|
79
|
-
ink?: string;
|
|
80
|
-
size?: PieceSize;
|
|
81
|
-
}
|
|
82
|
-
/** Copied artwork as a piece: the colour version as it was drawn, the line version in `currentColor`. */
|
|
83
|
-
export declare function copied(spec: CopiedSpec): Piece<CopiedSpec>;
|
|
60
|
+
/**
|
|
61
|
+
* A shape of varying width along a curve, as a closed path: a cubic Bezier from `a` to `b` through the controls `c1` and `c2`, drawn `width(t)` wide at the fraction `t` of the way along. A bow, a blade or a feather
|
|
62
|
+
* is one of these. `steps` is how many segments each side has.
|
|
63
|
+
*/
|
|
64
|
+
export declare function swell(a: readonly [number, number], c1: readonly [number, number], c2: readonly [number, number], b: readonly [number, number], width: (t: number) => number, steps?: number): string;
|