@sportsdataverse/sdvplot 0.0.0-stage → 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/LICENSE +21 -0
- package/NOTICE.md +109 -0
- package/README.md +984 -2
- package/dist/_web-D7Aa5Rd8.d.ts +69 -0
- package/dist/aaf-GJ3SH34T.js +13 -0
- package/dist/aaf-HCYN5T6Y.js +37 -0
- package/dist/aaf-ZAE5KS2Y.js +13 -0
- package/dist/ahl-E66MDVCU.js +66 -0
- package/dist/ahl-NPQQWZEV.js +249 -0
- package/dist/ahl-QRDU73XX.js +309 -0
- package/dist/axis-CraWQRUJ.d.ts +49 -0
- package/dist/bins.d.ts +174 -0
- package/dist/bins.js +4 -0
- package/dist/bins.js.map +1 -0
- package/dist/cfb-F42SVS4X.js +5000 -0
- package/dist/cfb-P3SHEYRZ.js +692 -0
- package/dist/cfb-WXRZANLP.js +10226 -0
- package/dist/chartjs-surface.d.ts +42 -0
- package/dist/chartjs-surface.js +65 -0
- package/dist/chartjs-surface.js.map +1 -0
- package/dist/chartjs.d.ts +162 -0
- package/dist/chartjs.js +342 -0
- package/dist/chartjs.js.map +1 -0
- package/dist/chunk-4C3GX6QO.js +92 -0
- package/dist/chunk-4C3GX6QO.js.map +1 -0
- package/dist/chunk-57WFSI4Q.js +128 -0
- package/dist/chunk-57WFSI4Q.js.map +1 -0
- package/dist/chunk-7ULOCCFL.js +313 -0
- package/dist/chunk-7ULOCCFL.js.map +1 -0
- package/dist/chunk-AMXASQHT.js +120 -0
- package/dist/chunk-AMXASQHT.js.map +1 -0
- package/dist/chunk-B2MP37SR.js +175 -0
- package/dist/chunk-B2MP37SR.js.map +1 -0
- package/dist/chunk-C2CDY5OG.js +137 -0
- package/dist/chunk-C2CDY5OG.js.map +1 -0
- package/dist/chunk-DYMUVE6K.js +127 -0
- package/dist/chunk-DYMUVE6K.js.map +1 -0
- package/dist/chunk-E7G7VSLV.js +43 -0
- package/dist/chunk-E7G7VSLV.js.map +1 -0
- package/dist/chunk-H2C3FMMA.js +300 -0
- package/dist/chunk-H2C3FMMA.js.map +1 -0
- package/dist/chunk-J4P4HDBF.js +175 -0
- package/dist/chunk-J4P4HDBF.js.map +1 -0
- package/dist/chunk-J6R2LEJ7.js +58 -0
- package/dist/chunk-J6R2LEJ7.js.map +1 -0
- package/dist/chunk-M3XD2OSN.js +132 -0
- package/dist/chunk-M3XD2OSN.js.map +1 -0
- package/dist/chunk-OIILARXI.js +30 -0
- package/dist/chunk-OIILARXI.js.map +1 -0
- package/dist/chunk-RPVVJY74.js +170 -0
- package/dist/chunk-RPVVJY74.js.map +1 -0
- package/dist/chunk-UL5M6KDE.js +205 -0
- package/dist/chunk-UL5M6KDE.js.map +1 -0
- package/dist/chunk-WQT4IVLX.js +10 -0
- package/dist/chunk-WQT4IVLX.js.map +1 -0
- package/dist/chunk-ZAEQXFJM.js +556 -0
- package/dist/chunk-ZAEQXFJM.js.map +1 -0
- package/dist/cricket-KPCYG2HL.js +493 -0
- package/dist/cricket-NDXLE25B.js +116 -0
- package/dist/cricket-YUUF7P6M.js +160 -0
- package/dist/cytoscape.d.ts +188 -0
- package/dist/cytoscape.js +219 -0
- package/dist/cytoscape.js.map +1 -0
- package/dist/d3.d.ts +234 -0
- package/dist/d3.js +198 -0
- package/dist/d3.js.map +1 -0
- package/dist/deckgl.d.ts +232 -0
- package/dist/deckgl.js +197 -0
- package/dist/deckgl.js.map +1 -0
- package/dist/echarts.d.ts +121 -0
- package/dist/echarts.js +228 -0
- package/dist/echarts.js.map +1 -0
- package/dist/echl-3CR4RGFC.js +241 -0
- package/dist/echl-NPC32C23.js +107 -0
- package/dist/echl-PI33DGJC.js +64 -0
- package/dist/export.d.ts +130 -0
- package/dist/export.js +258 -0
- package/dist/export.js.map +1 -0
- package/dist/g2.d.ts +251 -0
- package/dist/g2.js +102 -0
- package/dist/g2.js.map +1 -0
- package/dist/headshots-B2J_LOO8.d.ts +40 -0
- package/dist/index.d.ts +183 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/interact.d.ts +567 -0
- package/dist/interact.js +809 -0
- package/dist/interact.js.map +1 -0
- package/dist/lightweight-charts.d.ts +217 -0
- package/dist/lightweight-charts.js +155 -0
- package/dist/lightweight-charts.js.map +1 -0
- package/dist/maplibre.d.ts +271 -0
- package/dist/maplibre.js +175 -0
- package/dist/maplibre.js.map +1 -0
- package/dist/marks-DH-tQIP-.d.ts +75 -0
- package/dist/mbb-3TRLVW3H.js +3619 -0
- package/dist/mbb-DQUHCHZP.js +5111 -0
- package/dist/mbb-PRAIO2P5.js +371 -0
- package/dist/milb-6IQ6NCEW.js +291 -0
- package/dist/milb-EYY5MD4T.js +1752 -0
- package/dist/milb-EYZ4SZLI.js +797 -0
- package/dist/mlb-4PBUJSVQ.js +35 -0
- package/dist/mlb-FHPY3GQJ.js +701 -0
- package/dist/mlb-REVGL3N2.js +665 -0
- package/dist/mui.d.ts +338 -0
- package/dist/mui.js +184 -0
- package/dist/mui.js.map +1 -0
- package/dist/nba-ATT775GS.js +485 -0
- package/dist/nba-LKM4UHVU.js +482 -0
- package/dist/nba-YX7PXM3R.js +35 -0
- package/dist/nbagl-3WGZCZ4S.js +37 -0
- package/dist/nbagl-LP4XJRAL.js +229 -0
- package/dist/nbagl-VTTOU7LN.js +499 -0
- package/dist/ncaa_baseball-UV75LNVR.js +2670 -0
- package/dist/ncaa_baseball-VD5J5C72.js +419 -0
- package/dist/ncaa_baseball-YRBS32D6.js +4592 -0
- package/dist/ncaa_mhockey-IMYHS7QS.js +1033 -0
- package/dist/ncaa_mhockey-TXTKWHE3.js +115 -0
- package/dist/ncaa_mhockey-U3MFW5VT.js +663 -0
- package/dist/ncaa_softball-6WR6KS64.js +420 -0
- package/dist/ncaa_softball-RZCTIE56.js +2654 -0
- package/dist/ncaa_softball-VOD4PVGY.js +4599 -0
- package/dist/ncaa_whockey-6XXGUTBK.js +607 -0
- package/dist/ncaa_whockey-AKKYKY67.js +53 -0
- package/dist/ncaa_whockey-KBEKXFBB.js +293 -0
- package/dist/nfl-BDB2HYAR.js +625 -0
- package/dist/nfl-EHYHDLJD.js +37 -0
- package/dist/nfl-VF4NJCHY.js +514 -0
- package/dist/nfl_gsis-UY64JO37.js +4 -0
- package/dist/nhl-6WPHME7H.js +511 -0
- package/dist/nhl-EMIJFZHY.js +884 -0
- package/dist/nhl-ISFUKRFU.js +37 -0
- package/dist/nivo.d.ts +193 -0
- package/dist/nivo.js +8 -0
- package/dist/nivo.js.map +1 -0
- package/dist/ohl-2FEM554G.js +113 -0
- package/dist/ohl-C5Q4W6JB.js +32 -0
- package/dist/ohl-TQ4J46VC.js +66 -0
- package/dist/perspective.d.ts +198 -0
- package/dist/perspective.js +171 -0
- package/dist/perspective.js.map +1 -0
- package/dist/phf-H7POU2KW.js +32 -0
- package/dist/phf-K5IFMZUM.js +13 -0
- package/dist/phf-TWJI3BOW.js +20 -0
- package/dist/placement-UWkqUzpa.d.ts +32 -0
- package/dist/plot.d.ts +451 -0
- package/dist/plot.js +712 -0
- package/dist/plot.js.map +1 -0
- package/dist/plotly.d.ts +129 -0
- package/dist/plotly.js +360 -0
- package/dist/plotly.js.map +1 -0
- package/dist/pwhl-OX3BO4JI.js +53 -0
- package/dist/pwhl-W44OC3C6.js +17 -0
- package/dist/pwhl-ZJGG3H7W.js +23 -0
- package/dist/qmjhl-5HISEHLL.js +35 -0
- package/dist/qmjhl-DA3UHKE5.js +125 -0
- package/dist/qmjhl-YG4BOQPF.js +614 -0
- package/dist/react.d.ts +377 -0
- package/dist/react.js +522 -0
- package/dist/react.js.map +1 -0
- package/dist/recharts.d.ts +162 -0
- package/dist/recharts.js +8 -0
- package/dist/recharts.js.map +1 -0
- package/dist/resolve-CQW-jzvr.d.ts +28 -0
- package/dist/selection-D4T3PkRt.d.ts +140 -0
- package/dist/shadcn.d.ts +83 -0
- package/dist/shadcn.js +88 -0
- package/dist/shadcn.js.map +1 -0
- package/dist/shots.d.ts +364 -0
- package/dist/shots.js +86 -0
- package/dist/shots.js.map +1 -0
- package/dist/soccer-7BH3ZLVL.js +7898 -0
- package/dist/soccer-MRU25OGE.js +2636 -0
- package/dist/soccer-PF4IPPVA.js +12342 -0
- package/dist/surface-TR7YFJW3.js +4 -0
- package/dist/surface-TR7YFJW3.js.map +1 -0
- package/dist/surfaces-MIG7HOFU.js +6 -0
- package/dist/surfaces-MIG7HOFU.js.map +1 -0
- package/dist/surfaces-W-odWLpT.d.ts +47 -0
- package/dist/svelte/HeadshotMark.svelte +25 -0
- package/dist/svelte/HeadshotMark.svelte.d.ts +10 -0
- package/dist/svelte/HeadshotMark.svelte.d.ts.map +1 -0
- package/dist/svelte/LogoMark.svelte +25 -0
- package/dist/svelte/LogoMark.svelte.d.ts +10 -0
- package/dist/svelte/LogoMark.svelte.d.ts.map +1 -0
- package/dist/svelte/Marks.svelte +25 -0
- package/dist/svelte/Marks.svelte.d.ts +10 -0
- package/dist/svelte/Marks.svelte.d.ts.map +1 -0
- package/dist/svelte/Surface.svelte +29 -0
- package/dist/svelte/Surface.svelte.d.ts +9 -0
- package/dist/svelte/Surface.svelte.d.ts.map +1 -0
- package/dist/svelte/SurfaceBackdrop.svelte +26 -0
- package/dist/svelte/SurfaceBackdrop.svelte.d.ts +11 -0
- package/dist/svelte/SurfaceBackdrop.svelte.d.ts.map +1 -0
- package/dist/svelte/index.d.ts +13 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +11 -0
- package/dist/svelte-core.d.ts +139 -0
- package/dist/svelte-core.js +123 -0
- package/dist/svelte-core.js.map +1 -0
- package/dist/tanstack.d.ts +240 -0
- package/dist/tanstack.js +110 -0
- package/dist/tanstack.js.map +1 -0
- package/dist/testing.d.ts +75 -0
- package/dist/testing.js +239 -0
- package/dist/testing.js.map +1 -0
- package/dist/three.d.ts +223 -0
- package/dist/three.js +50 -0
- package/dist/three.js.map +1 -0
- package/dist/tiers-BN4md-GB.d.ts +65 -0
- package/dist/types-C7n6gJF3.d.ts +62 -0
- package/dist/ufl-7VLZTLTT.js +143 -0
- package/dist/ufl-CCMBLPSW.js +100 -0
- package/dist/ufl-F2BJRXME.js +16 -0
- package/dist/unovis.d.ts +127 -0
- package/dist/unovis.js +108 -0
- package/dist/unovis.js.map +1 -0
- package/dist/uplot.d.ts +120 -0
- package/dist/uplot.js +110 -0
- package/dist/uplot.js.map +1 -0
- package/dist/usfl-4CR5RMZJ.js +32 -0
- package/dist/usfl-LD7HP2YK.js +14 -0
- package/dist/usfl-VU4YSJQD.js +19 -0
- package/dist/ushl-CPG2LB4L.js +73 -0
- package/dist/ushl-PQL6HJAD.js +88 -0
- package/dist/ushl-ZT2ATPHK.js +22 -0
- package/dist/vega.d.ts +85 -0
- package/dist/vega.js +362 -0
- package/dist/vega.js.map +1 -0
- package/dist/victory.d.ts +107 -0
- package/dist/victory.js +38 -0
- package/dist/victory.js.map +1 -0
- package/dist/wbb-3KHCDYTK.js +5087 -0
- package/dist/wbb-BMX53Q3Q.js +369 -0
- package/dist/wbb-QNRN2CJJ.js +3620 -0
- package/dist/whl-4ATYZWUB.js +97 -0
- package/dist/whl-RVFPL6MH.js +79 -0
- package/dist/whl-Y7ASKYMM.js +28 -0
- package/dist/wnba-FWX5SFVI.js +232 -0
- package/dist/wnba-GX6HZR4B.js +220 -0
- package/dist/wnba-NTMWIPP6.js +20 -0
- package/dist/xfl-43R2JMJK.js +30 -0
- package/dist/xfl-H3BXQPZZ.js +16 -0
- package/dist/xfl-XON7A7FI.js +69 -0
- package/package.json +366 -3
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { InputError } from './chunk-E7G7VSLV.js';
|
|
2
|
+
import { surface } from '@sportsdataverse/sporty';
|
|
3
|
+
|
|
4
|
+
function loops(points) {
|
|
5
|
+
const out = [];
|
|
6
|
+
let loop = [];
|
|
7
|
+
let away = 0;
|
|
8
|
+
for (const p of points) {
|
|
9
|
+
const first = loop[0];
|
|
10
|
+
loop.push(p);
|
|
11
|
+
if (first === void 0) continue;
|
|
12
|
+
if (p[0] !== first[0] || p[1] !== first[1]) away++;
|
|
13
|
+
else if (away >= 2) {
|
|
14
|
+
out.push(loop);
|
|
15
|
+
loop = [];
|
|
16
|
+
away = 0;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (away >= 2) out.push(loop);
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
var turn = (a, b, c) => (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
|
|
23
|
+
function ring(loop) {
|
|
24
|
+
const r = loop.filter((p, i) => {
|
|
25
|
+
const q = loop[(i + 1) % loop.length];
|
|
26
|
+
return p[0] !== q[0] || p[1] !== q[1];
|
|
27
|
+
});
|
|
28
|
+
const flat = (p, i) => {
|
|
29
|
+
const [a, c] = [r[(i - 1 + r.length) % r.length], r[(i + 1) % r.length]];
|
|
30
|
+
return Math.abs(turn(a, p, c)) <= 1e-9 * Math.hypot(p[0] - a[0], p[1] - a[1]) * Math.hypot(c[0] - p[0], c[1] - p[1]);
|
|
31
|
+
};
|
|
32
|
+
for (let i = r.findIndex(flat); i >= 0 && r.length >= 3; i = r.findIndex(flat)) r.splice(i, 1);
|
|
33
|
+
const area = signedArea(r);
|
|
34
|
+
return area < 0 ? r.reverse() : r;
|
|
35
|
+
}
|
|
36
|
+
function signedArea(r) {
|
|
37
|
+
let s = 0;
|
|
38
|
+
for (let i = 0; i < r.length; i++) {
|
|
39
|
+
const [a, b] = [r[i], r[(i + 1) % r.length]];
|
|
40
|
+
s += a[0] * b[1] - b[0] * a[1];
|
|
41
|
+
}
|
|
42
|
+
return s / 2;
|
|
43
|
+
}
|
|
44
|
+
function empty(r) {
|
|
45
|
+
let [x0, y0, x1, y1] = [
|
|
46
|
+
Number.POSITIVE_INFINITY,
|
|
47
|
+
Number.POSITIVE_INFINITY,
|
|
48
|
+
Number.NEGATIVE_INFINITY,
|
|
49
|
+
Number.NEGATIVE_INFINITY
|
|
50
|
+
];
|
|
51
|
+
for (const [x, y] of r)
|
|
52
|
+
[x0, y0, x1, y1] = [Math.min(x0, x), Math.min(y0, y), Math.max(x1, x), Math.max(y1, y)];
|
|
53
|
+
return r.length < 3 || Math.abs(signedArea(r)) <= 1e-9 * ((x1 - x0) ** 2 + (y1 - y0) ** 2);
|
|
54
|
+
}
|
|
55
|
+
function simple(r) {
|
|
56
|
+
const n = r.length;
|
|
57
|
+
const side = (a, b, c) => Math.sign(turn(a, b, c));
|
|
58
|
+
for (let i = 0; i < n; i++)
|
|
59
|
+
for (let j = i + 2; j < n; j++) {
|
|
60
|
+
if (i === 0 && j === n - 1) continue;
|
|
61
|
+
const [a, b, c, d] = [r[i], r[(i + 1) % n], r[j], r[(j + 1) % n]];
|
|
62
|
+
if (side(a, b, c) * side(a, b, d) >= 0 || side(c, d, a) * side(c, d, b) >= 0) continue;
|
|
63
|
+
const s = turn(c, d, a) / (turn(c, d, a) - turn(c, d, b));
|
|
64
|
+
const x = [a[0] + s * (b[0] - a[0]), a[1] + s * (b[1] - a[1])];
|
|
65
|
+
return [
|
|
66
|
+
...simple([...r.slice(0, i + 1), x, ...r.slice(j + 1)]),
|
|
67
|
+
...simple([x, ...r.slice(i + 1, j + 1)])
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
return [[...r]];
|
|
71
|
+
}
|
|
72
|
+
function triangulate(r) {
|
|
73
|
+
const n = r.length;
|
|
74
|
+
const prev = Array.from({ length: n }, (_, i2) => (i2 - 1 + n) % n);
|
|
75
|
+
const next = Array.from({ length: n }, (_, i2) => (i2 + 1) % n);
|
|
76
|
+
const convex = (i2) => turn(r[prev[i2]], r[i2], r[next[i2]]) > 0;
|
|
77
|
+
const reflex = /* @__PURE__ */ new Set();
|
|
78
|
+
for (let i2 = 0; i2 < n; i2++) if (!convex(i2)) reflex.add(i2);
|
|
79
|
+
const inside = (p, a, b, c) => turn(a, b, p) >= 0 && turn(b, c, p) >= 0 && turn(c, a, p) >= 0;
|
|
80
|
+
const ear = (i2) => {
|
|
81
|
+
if (!convex(i2)) return false;
|
|
82
|
+
const [a, b, c] = [prev[i2], i2, next[i2]];
|
|
83
|
+
for (const j of reflex) {
|
|
84
|
+
if (j === a || j === b || j === c) continue;
|
|
85
|
+
const p = r[j];
|
|
86
|
+
const same = (q) => p[0] === r[q][0] && p[1] === r[q][1];
|
|
87
|
+
if (!same(a) && !same(c) && inside(p, r[a], r[b], r[c])) return false;
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
};
|
|
91
|
+
const out = [];
|
|
92
|
+
let i = 0;
|
|
93
|
+
let left = n;
|
|
94
|
+
let tried = 0;
|
|
95
|
+
while (left > 3) {
|
|
96
|
+
if (ear(i) || tried > left) {
|
|
97
|
+
const [a, c] = [prev[i], next[i]];
|
|
98
|
+
out.push(a, i, c);
|
|
99
|
+
next[a] = c;
|
|
100
|
+
prev[c] = a;
|
|
101
|
+
reflex.delete(i);
|
|
102
|
+
for (const k of [a, c]) if (convex(k)) reflex.delete(k);
|
|
103
|
+
left--;
|
|
104
|
+
tried = 0;
|
|
105
|
+
i = c;
|
|
106
|
+
} else {
|
|
107
|
+
i = next[i];
|
|
108
|
+
tried++;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
out.push(prev[i], i, next[i]);
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
function linear(color, name) {
|
|
115
|
+
if (!/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(color))
|
|
116
|
+
throw new InputError(
|
|
117
|
+
`surfaceMesh: ${name}'s fill ${JSON.stringify(color)} is not a hex colour (#rrggbb or #rrggbbaa)`
|
|
118
|
+
);
|
|
119
|
+
let c = color.slice(1);
|
|
120
|
+
if (c.length <= 4) c = [...c].map((d) => d + d).join("");
|
|
121
|
+
const lin = (s) => s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
122
|
+
const [r, g, b] = [0, 2, 4].map((i) => lin(Number.parseInt(c.slice(i, i + 2), 16) / 255));
|
|
123
|
+
return [r, g, b, c.length === 8 ? Number.parseInt(c.slice(6), 16) / 255 : 1];
|
|
124
|
+
}
|
|
125
|
+
var drawn = (f) => f.points.length > 0 && f.points.every(([x, y]) => Number.isFinite(x) && Number.isFinite(y)) && !(f.fill.length === 9 && f.fill.endsWith("00"));
|
|
126
|
+
function surfaceMesh(sport, league, o = {}) {
|
|
127
|
+
const scene = surface(sport, league, o);
|
|
128
|
+
const [x0, y0, x1, y1] = scene.bbox;
|
|
129
|
+
const parts = [];
|
|
130
|
+
if (scene.background !== void 0)
|
|
131
|
+
parts.push({
|
|
132
|
+
name: "background",
|
|
133
|
+
color: scene.background,
|
|
134
|
+
ring: ring([
|
|
135
|
+
[x0, y0],
|
|
136
|
+
[x1, y0],
|
|
137
|
+
[x1, y1],
|
|
138
|
+
[x0, y1]
|
|
139
|
+
])
|
|
140
|
+
});
|
|
141
|
+
for (const f of scene.features)
|
|
142
|
+
if (f.kind === "polygon" && drawn(f))
|
|
143
|
+
for (const l of loops(f.points))
|
|
144
|
+
for (const s of simple(ring(l))) {
|
|
145
|
+
const r = ring(s);
|
|
146
|
+
if (!empty(r)) parts.push({ name: f.name, color: f.fill, ring: r });
|
|
147
|
+
}
|
|
148
|
+
const vertices = parts.reduce((s, p) => s + p.ring.length, 0);
|
|
149
|
+
const positions = new Float32Array(vertices * 3);
|
|
150
|
+
const colors = new Float32Array(vertices * 4);
|
|
151
|
+
const indices = new Uint32Array(parts.reduce((s, p) => s + 3 * (p.ring.length - 2), 0));
|
|
152
|
+
const out = [];
|
|
153
|
+
let v = 0;
|
|
154
|
+
let i = 0;
|
|
155
|
+
for (const { name, color, ring: r } of parts) {
|
|
156
|
+
const rgba = linear(color, name);
|
|
157
|
+
r.forEach(([x, y], k) => {
|
|
158
|
+
positions.set([x, y, 0], 3 * (v + k));
|
|
159
|
+
colors.set(rgba, 4 * (v + k));
|
|
160
|
+
});
|
|
161
|
+
const tris = triangulate(r);
|
|
162
|
+
indices.set(
|
|
163
|
+
tris.map((t) => t + v),
|
|
164
|
+
i
|
|
165
|
+
);
|
|
166
|
+
out.push({ name, color, vertexStart: v, vertexCount: r.length, indexStart: i, indexCount: tris.length });
|
|
167
|
+
v += r.length;
|
|
168
|
+
i += tris.length;
|
|
169
|
+
}
|
|
170
|
+
return { positions, colors, indices, loops: out, units: scene.units, bbox: scene.bbox };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export { surfaceMesh };
|
|
174
|
+
//# sourceMappingURL=chunk-B2MP37SR.js.map
|
|
175
|
+
//# sourceMappingURL=chunk-B2MP37SR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/three/surface.ts"],"names":["i","sportySurface"],"mappings":"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","file":"chunk-B2MP37SR.js","sourcesContent":["import { type PolygonFeature, surface as sportySurface } from \"@sportsdataverse/sporty\";\nimport type { LeagueOf, OptionsOf, Sport, Units } from \"@sportsdataverse/sporty\";\nimport { InputError } from \"../errors.js\";\n\n/** One closed loop of `surfaceMesh`: where its vertices and triangles sit in the mesh buffers. */\nexport interface SurfaceLoop {\n /** The sporty feature it comes from (e.g. \"infield_dirt\"), or \"background\" for the surface's own background. */\n name: string;\n /** The feature's sporty fill, `#rrggbb` or `#rrggbbaa`, as sporty gives it. */\n color: string;\n /** Its first vertex and number of vertices. */\n vertexStart: number;\n vertexCount: number;\n /** Its first index and number of indices (three per triangle). */\n indexStart: number;\n indexCount: number;\n}\n/**\n * A sporty playing surface as one triangle mesh in the z = 0 plane, in the surface's own units and frame (feet for a\n * baseball field or a rink, metres for a pitch; origin where sporty puts it), ready for a three.js `BufferGeometry`.\n */\nexport interface SurfaceMesh {\n /** x, y, z per vertex; z is 0. */\n positions: Float32Array;\n /** r, g, b, a per vertex, 0 to 1, in linear sRGB (three.js's working colour space): a vertex-colour attribute. */\n colors: Float32Array;\n /** Three vertex indices per triangle, counter-clockwise seen from +z, back to front. */\n indices: Uint32Array;\n /** Every loop, in drawing order (later ones lie over earlier ones in the same plane). */\n loops: SurfaceLoop[];\n units: Units;\n /** The surface's extent, `[x0, y0, x1, y1]`. */\n bbox: readonly [x0: number, y0: number, x1: number, y1: number];\n}\n\ntype Pt = readonly [x: number, y: number];\n/**\n * A feature's path as closed loops. sporty (after sportyR) draws some features as two loops in one path, such as a\n * faceoff circle's two half rings each closing back on its first point: an SVG or canvas fill draws that as it is, but\n * a triangulation of the path as one ring fills the wrong side of it (deck.gl's earcut did). A loop ends where the path\n * comes back to that loop's first point, after at least two other points (a repeated first point does not end it):\n * the rule of `sdvplot/deckgl` and sdvplot.pydeck. ponytail: a loop inside another would be filled, not cut out as a\n * hole; sporty builds every ring from two half rings, so none is.\n */\nfunction loops(points: readonly Pt[]): Pt[][] {\n const out: Pt[][] = [];\n let loop: Pt[] = [];\n let away = 0; // points of this loop that are not its first\n for (const p of points) {\n const first = loop[0];\n loop.push(p);\n if (first === undefined) continue;\n if (p[0] !== first[0] || p[1] !== first[1]) away++;\n else if (away >= 2) {\n out.push(loop);\n loop = [];\n away = 0;\n }\n }\n if (away >= 2) out.push(loop); // an open path, closed like a fill\n return out;\n}\n\nconst turn = (a: Pt, b: Pt, c: Pt): number => (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);\n\n/**\n * A loop as a simple counter-clockwise ring: no repeated point, no point on a straight run between its neighbours\n * (or a spike back on itself), which add nothing to the area and leave an ear-clipper with zero-area ears.\n */\nfunction ring(loop: readonly Pt[]): Pt[] {\n const r = loop.filter((p, i) => {\n const q = loop[(i + 1) % loop.length] as Pt;\n return p[0] !== q[0] || p[1] !== q[1];\n });\n // flat: the corner's sine under 1e-9, i.e. straight up to rounding\n const flat = (p: Pt, i: number): boolean => {\n const [a, c] = [r[(i - 1 + r.length) % r.length] as Pt, r[(i + 1) % r.length] as Pt];\n return (\n Math.abs(turn(a, p, c)) <=\n 1e-9 * Math.hypot(p[0] - a[0], p[1] - a[1]) * Math.hypot(c[0] - p[0], c[1] - p[1])\n );\n };\n for (let i = r.findIndex(flat); i >= 0 && r.length >= 3; i = r.findIndex(flat)) r.splice(i, 1);\n const area = signedArea(r);\n return area < 0 ? r.reverse() : r;\n}\nfunction signedArea(r: readonly Pt[]): number {\n let s = 0;\n for (let i = 0; i < r.length; i++) {\n const [a, b] = [r[i] as Pt, r[(i + 1) % r.length] as Pt];\n s += a[0] * b[1] - b[0] * a[1];\n }\n return s / 2;\n}\n/**\n * No area to fill: a path that goes out along a line and back along it (the NLL goal fan), which a fill leaves\n * invisible, as sporty's own SVG does, or the sliver left where a ring touched itself.\n */\nfunction empty(r: readonly Pt[]): boolean {\n let [x0, y0, x1, y1] = [\n Number.POSITIVE_INFINITY,\n Number.POSITIVE_INFINITY,\n Number.NEGATIVE_INFINITY,\n Number.NEGATIVE_INFINITY,\n ];\n for (const [x, y] of r)\n [x0, y0, x1, y1] = [Math.min(x0, x), Math.min(y0, y), Math.max(x1, x), Math.max(y1, y)];\n return r.length < 3 || Math.abs(signedArea(r)) <= 1e-9 * ((x1 - x0) ** 2 + (y1 - y0) ** 2);\n}\n\n/**\n * A ring that crosses itself split at each crossing into rings that do not, each to be filled: what an SVG fill shows\n * of a figure-of-eight. sporty has a few: the NHL goaltender's trapezoids and the faceoff hash marks nearest centre\n * ice twist at their corners by an inch. Rings come back un-oriented (`ring` orients them).\n */\nfunction simple(r: readonly Pt[]): Pt[][] {\n const n = r.length;\n const side = (a: Pt, b: Pt, c: Pt): number => Math.sign(turn(a, b, c));\n for (let i = 0; i < n; i++)\n for (let j = i + 2; j < n; j++) {\n if (i === 0 && j === n - 1) continue; // neighbours across the closing edge\n const [a, b, c, d] = [r[i] as Pt, r[(i + 1) % n] as Pt, r[j] as Pt, r[(j + 1) % n] as Pt];\n if (side(a, b, c) * side(a, b, d) >= 0 || side(c, d, a) * side(c, d, b) >= 0) continue;\n const s = turn(c, d, a) / (turn(c, d, a) - turn(c, d, b)); // where the crossing is along a -> b\n const x: Pt = [a[0] + s * (b[0] - a[0]), a[1] + s * (b[1] - a[1])];\n return [\n ...simple([...r.slice(0, i + 1), x, ...r.slice(j + 1)]),\n ...simple([x, ...r.slice(i + 1, j + 1)]),\n ];\n }\n return [[...r]];\n}\n\n/**\n * Ear clipping of a simple counter-clockwise ring: n - 2 triangles, as indices into it. An ear is a convex corner whose\n * triangle holds no other corner of the ring; only reflex corners can lie in one, so only they are checked.\n * ponytail: O(n²) in the worst case; sporty's longest loop is about 800 points.\n */\nfunction triangulate(r: readonly Pt[]): number[] {\n const n = r.length;\n const prev = Array.from({ length: n }, (_, i) => (i - 1 + n) % n);\n const next = Array.from({ length: n }, (_, i) => (i + 1) % n);\n const convex = (i: number): boolean =>\n turn(r[prev[i] as number] as Pt, r[i] as Pt, r[next[i] as number] as Pt) > 0;\n const reflex = new Set<number>();\n for (let i = 0; i < n; i++) if (!convex(i)) reflex.add(i);\n const inside = (p: Pt, a: Pt, b: Pt, c: Pt): boolean =>\n turn(a, b, p) >= 0 && turn(b, c, p) >= 0 && turn(c, a, p) >= 0;\n const ear = (i: number): boolean => {\n if (!convex(i)) return false;\n const [a, b, c] = [prev[i] as number, i, next[i] as number];\n for (const j of reflex) {\n if (j === a || j === b || j === c) continue;\n const p = r[j] as Pt;\n const same = (q: number): boolean => p[0] === (r[q] as Pt)[0] && p[1] === (r[q] as Pt)[1];\n if (!same(a) && !same(c) && inside(p, r[a] as Pt, r[b] as Pt, r[c] as Pt)) return false;\n }\n return true;\n };\n const out: number[] = [];\n let i = 0;\n let left = n;\n let tried = 0;\n while (left > 3) {\n // ponytail: with no ear left (a ring that is not simple), clip the corner anyway rather than loop forever\n if (ear(i) || tried > left) {\n const [a, c] = [prev[i] as number, next[i] as number];\n out.push(a, i, c);\n next[a] = c;\n prev[c] = a;\n reflex.delete(i);\n for (const k of [a, c]) if (convex(k)) reflex.delete(k);\n left--;\n tried = 0;\n i = c;\n } else {\n i = next[i] as number;\n tried++;\n }\n }\n out.push(prev[i] as number, i, next[i] as number);\n return out;\n}\n\n/**\n * A feature's fill (\"#rgb\", \"#rgba\", \"#rrggbb\" or \"#rrggbbaa\") as r, g, b in linear sRGB and a, each 0 to 1. Anything\n * else (a CSS colour name given through `colorUpdates`, say) is an InputError naming the feature: a vertex colour\n * cannot be parsed from it.\n */\nfunction linear(color: string, name: string): [number, number, number, number] {\n if (!/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(color))\n throw new InputError(\n `surfaceMesh: ${name}'s fill ${JSON.stringify(color)} is not a hex colour (#rrggbb or #rrggbbaa)`,\n );\n let c = color.slice(1);\n if (c.length <= 4) c = [...c].map((d) => d + d).join(\"\");\n const lin = (s: number): number => (s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4);\n const [r, g, b] = [0, 2, 4].map((i) => lin(Number.parseInt(c.slice(i, i + 2), 16) / 255));\n return [r as number, g as number, b as number, c.length === 8 ? Number.parseInt(c.slice(6), 16) / 255 : 1];\n}\n\nconst drawn = (f: PolygonFeature): boolean =>\n f.points.length > 0 &&\n f.points.every(([x, y]) => Number.isFinite(x) && Number.isFinite(y)) &&\n !(f.fill.length === 9 && f.fill.endsWith(\"00\"));\n\n/**\n * sporty's playing surface for `sport` and `league` (sporty's own league keys, e.g. `surfaceMesh(\"baseball\", \"mlb\")`,\n * `surfaceMesh(\"hockey\", \"nhl\")`) as one triangle mesh in the z = 0 plane: plain buffers (positions, linear-sRGB\n * vertex colours, indices), with no three.js import, ready for `new BufferGeometry()` in three.js or the `<Surface3D>`\n * component in `sdvplot/react`. World units: the surface's own (feet for a baseball field or a rink, metres for a\n * pitch), in sporty's frame: a baseball field has home plate's point at the origin and +y towards centre field,\n * Statcast's frame, so `statcastPitchPath` points draw over it unconverted; the other surfaces have the origin at\n * their centre. The surface's background colour (a baseball field's outfield grass) is the first loop, over the\n * extent; then each closed loop of each polygon feature, in sporty's drawing order (a faceoff circle is two loops,\n * its half rings), filled with its colour, alpha included. Hidden features (a `#rrggbb00` fill) and text (a football\n * field's numbers) are left out, and sporty's heights (goal frames, backboards) are flattened: z is 0 throughout.\n * Every loop lies in one plane, so draw the mesh in order without depth writes (`depthWrite: false`), first\n * (`renderOrder` -1), with `vertexColors`, `transparent` and `toneMapped: false`, as `<Surface3D>` does: later loops\n * then paint over earlier ones with no z-fighting. `o` is passed to sporty's `surface()` (`colorUpdates`, `units`,\n * `rotation`, ...).\n *\n * @example\n * ```ts\n * import { surfaceMesh } from \"@sportsdataverse/sdvplot/three\";\n *\n * const field = surfaceMesh(\"baseball\", \"mlb\");\n * // in three.js: geometry.setAttribute(\"position\", new BufferAttribute(field.positions, 3)), and so on\n * field.loops.map((l) => l.name);\n * ```\n */\nexport function surfaceMesh<S extends Sport>(\n sport: S,\n league: LeagueOf<S>,\n o: OptionsOf<S> = {} as OptionsOf<S>,\n): SurfaceMesh {\n const scene = sportySurface(sport, league, o);\n const [x0, y0, x1, y1] = scene.bbox;\n const parts: { name: string; color: string; ring: Pt[] }[] = [];\n if (scene.background !== undefined)\n parts.push({\n name: \"background\",\n color: scene.background,\n ring: ring([\n [x0, y0],\n [x1, y0],\n [x1, y1],\n [x0, y1],\n ]),\n });\n // ponytail: features draw in array order (sporty's zIndex is its position); sort here if that ever changes\n for (const f of scene.features)\n if (f.kind === \"polygon\" && drawn(f))\n for (const l of loops(f.points))\n for (const s of simple(ring(l))) {\n const r = ring(s);\n if (!empty(r)) parts.push({ name: f.name, color: f.fill, ring: r });\n }\n const vertices = parts.reduce((s, p) => s + p.ring.length, 0);\n const positions = new Float32Array(vertices * 3);\n const colors = new Float32Array(vertices * 4);\n const indices = new Uint32Array(parts.reduce((s, p) => s + 3 * (p.ring.length - 2), 0));\n const out: SurfaceLoop[] = [];\n let v = 0;\n let i = 0;\n for (const { name, color, ring: r } of parts) {\n const rgba = linear(color, name);\n r.forEach(([x, y], k) => {\n positions.set([x, y, 0], 3 * (v + k));\n colors.set(rgba, 4 * (v + k));\n });\n const tris = triangulate(r);\n indices.set(\n tris.map((t) => t + v),\n i,\n );\n out.push({ name, color, vertexStart: v, vertexCount: r.length, indexStart: i, indexCount: tris.length });\n v += r.length;\n i += tris.length;\n }\n return { positions, colors, indices, loops: out, units: scene.units, bbox: scene.bbox };\n}\n"]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { InputError } from './chunk-E7G7VSLV.js';
|
|
2
|
+
|
|
3
|
+
// src/bins/hexbin.ts
|
|
4
|
+
var THIRD_PI = Math.PI / 3;
|
|
5
|
+
var ANGLES = [0, THIRD_PI, 2 * THIRD_PI, 3 * THIRD_PI, 4 * THIRD_PI, 5 * THIRD_PI];
|
|
6
|
+
function hexbin(points, o) {
|
|
7
|
+
if (!(Number.isFinite(o.radius) && o.radius > 0))
|
|
8
|
+
throw new InputError(`hexbin radius must be a finite number > 0, got ${String(o.radius)}`);
|
|
9
|
+
const dx = o.radius * 2 * Math.sin(THIRD_PI);
|
|
10
|
+
const dy = o.radius * 1.5;
|
|
11
|
+
const byId = /* @__PURE__ */ new Map();
|
|
12
|
+
const bins = [];
|
|
13
|
+
for (const p of points) {
|
|
14
|
+
let px = +o.x(p);
|
|
15
|
+
let py = +o.y(p);
|
|
16
|
+
if (Number.isNaN(px) || Number.isNaN(py)) continue;
|
|
17
|
+
py = py / dy;
|
|
18
|
+
let pj = Math.round(py);
|
|
19
|
+
px = px / dx - (pj & 1) / 2;
|
|
20
|
+
let pi = Math.round(px);
|
|
21
|
+
const py1 = py - pj;
|
|
22
|
+
if (Math.abs(py1) * 3 > 1) {
|
|
23
|
+
const px1 = px - pi;
|
|
24
|
+
const pi2 = pi + (px < pi ? -1 : 1) / 2;
|
|
25
|
+
const pj2 = pj + (py < pj ? -1 : 1);
|
|
26
|
+
const px2 = px - pi2;
|
|
27
|
+
const py2 = py - pj2;
|
|
28
|
+
if (px1 * px1 + py1 * py1 > px2 * px2 + py2 * py2) {
|
|
29
|
+
pi = pi2 + (pj & 1 ? 1 : -1) / 2;
|
|
30
|
+
pj = pj2;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const id = `${pi}-${pj}`;
|
|
34
|
+
let bin = byId.get(id);
|
|
35
|
+
if (bin === void 0) {
|
|
36
|
+
bin = Object.assign([], { x: (pi + (pj & 1) / 2) * dx || 0, y: pj * dy || 0 });
|
|
37
|
+
byId.set(id, bin);
|
|
38
|
+
bins.push(bin);
|
|
39
|
+
}
|
|
40
|
+
bin.push(p);
|
|
41
|
+
}
|
|
42
|
+
return bins;
|
|
43
|
+
}
|
|
44
|
+
function hexagonPoints(r) {
|
|
45
|
+
return ANGLES.map((a) => [Math.sin(a) * r, -Math.cos(a) * r]);
|
|
46
|
+
}
|
|
47
|
+
function hexagonPath(r) {
|
|
48
|
+
let x0 = 0;
|
|
49
|
+
let y0 = 0;
|
|
50
|
+
const steps = ANGLES.map((a) => {
|
|
51
|
+
const x1 = Math.sin(a) * r;
|
|
52
|
+
const y1 = -Math.cos(a) * r;
|
|
53
|
+
const step = `${x1 - x0},${y1 - y0}`;
|
|
54
|
+
x0 = x1;
|
|
55
|
+
y0 = y1;
|
|
56
|
+
return step;
|
|
57
|
+
});
|
|
58
|
+
return `m${steps.join("l")}z`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/bins/squarebin.ts
|
|
62
|
+
function positive(name, v) {
|
|
63
|
+
if (!(Number.isFinite(v) && v > 0))
|
|
64
|
+
throw new InputError(`binner ${name} must be a finite number > 0, got ${String(v)}`);
|
|
65
|
+
return v;
|
|
66
|
+
}
|
|
67
|
+
var EQUAL_AREA = Math.sqrt(1.5 * Math.sqrt(3));
|
|
68
|
+
function squarebin(points, o) {
|
|
69
|
+
const s = o.side;
|
|
70
|
+
if (!(Number.isFinite(s) && s > 0))
|
|
71
|
+
throw new InputError(`squarebin side must be a finite number > 0, got ${String(s)}`);
|
|
72
|
+
const byId = /* @__PURE__ */ new Map();
|
|
73
|
+
const bins = [];
|
|
74
|
+
for (const p of points) {
|
|
75
|
+
const px = +o.x(p);
|
|
76
|
+
const py = +o.y(p);
|
|
77
|
+
if (Number.isNaN(px) || Number.isNaN(py)) continue;
|
|
78
|
+
const i = Math.round(px / s);
|
|
79
|
+
const j = Math.round(py / s);
|
|
80
|
+
const id = `${i}-${j}`;
|
|
81
|
+
let bin = byId.get(id);
|
|
82
|
+
if (bin === void 0) {
|
|
83
|
+
bin = Object.assign([], { x: i * s || 0, y: j * s || 0 });
|
|
84
|
+
byId.set(id, bin);
|
|
85
|
+
bins.push(bin);
|
|
86
|
+
}
|
|
87
|
+
bin.push(p);
|
|
88
|
+
}
|
|
89
|
+
return bins;
|
|
90
|
+
}
|
|
91
|
+
function squarePoints(s) {
|
|
92
|
+
const h = s / 2;
|
|
93
|
+
return [
|
|
94
|
+
[-h, -h],
|
|
95
|
+
[h, -h],
|
|
96
|
+
[h, h],
|
|
97
|
+
[-h, h]
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
function squarePath(s) {
|
|
101
|
+
return `m${-s / 2},${-s / 2}h${s}v${s}h${-s}z`;
|
|
102
|
+
}
|
|
103
|
+
function cellPoints(shape, size) {
|
|
104
|
+
return shape === "square" ? squarePoints(size) : hexagonPoints(size);
|
|
105
|
+
}
|
|
106
|
+
function cellPath(shape, size) {
|
|
107
|
+
return shape === "square" ? squarePath(size) : hexagonPath(size);
|
|
108
|
+
}
|
|
109
|
+
function binner(o) {
|
|
110
|
+
const shape = o.shape;
|
|
111
|
+
if (shape !== void 0 && shape !== "hex" && shape !== "square")
|
|
112
|
+
throw new InputError(`binner shape must be "hex" or "square", got ${String(shape)}`);
|
|
113
|
+
if (o.shape !== "square") {
|
|
114
|
+
const radius = positive("radius", o.radius);
|
|
115
|
+
return {
|
|
116
|
+
shape: "hex",
|
|
117
|
+
size: radius,
|
|
118
|
+
lattice: { radius },
|
|
119
|
+
bins: (points, a) => hexbin(points, { radius, x: a.x, y: a.y }),
|
|
120
|
+
cell: (s = radius) => hexagonPath(s)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (!("side" in o) && o.equalArea !== true)
|
|
124
|
+
throw new InputError("binner square needs side, or radius with equalArea: true");
|
|
125
|
+
const side = "side" in o ? positive("side", o.side) : positive("radius", o.radius) * EQUAL_AREA;
|
|
126
|
+
return {
|
|
127
|
+
shape: "square",
|
|
128
|
+
size: side,
|
|
129
|
+
lattice: { shape: "square", side },
|
|
130
|
+
bins: (points, a) => squarebin(points, { side, x: a.x, y: a.y }),
|
|
131
|
+
cell: (s = side) => squarePath(s)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { binner, cellPath, cellPoints, hexagonPath, hexbin, squarePath, squarebin };
|
|
136
|
+
//# sourceMappingURL=chunk-C2CDY5OG.js.map
|
|
137
|
+
//# sourceMappingURL=chunk-C2CDY5OG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bins/hexbin.ts","../src/bins/squarebin.ts"],"names":[],"mappings":"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","file":"chunk-C2CDY5OG.js","sourcesContent":["// hexbin() and hexagonPath() port d3-hexbin 0.2.2 src/hexbin.js:1-2, :23-72 (Copyright 2012-2016 Mike Bostock,\n// BSD-3-Clause; full notice in NOTICE.md). Same lattice, bin order and centres, so bins equal d3-hexbin's.\nimport { InputError } from \"../errors.js\";\n\nconst THIRD_PI = Math.PI / 3;\nconst ANGLES = [0, THIRD_PI, 2 * THIRD_PI, 3 * THIRD_PI, 4 * THIRD_PI, 5 * THIRD_PI];\n\n/** One bin of either shape (`hexbin`, `squarebin`): the points in it, plus its centre in the points' own units. */\nexport type BinOf<T> = T[] & { x: number; y: number };\n\n/**\n * Bin points into pointy-top hexagons of circumradius `radius`, in the points' OWN units (data space, not\n * pixels). Points with a NaN coordinate are skipped. Centres of `-0` are folded to `0` (blazing-the-nets\n * `main` `lib/data/aggregate.ts:101`), so equal hexes share one key. Throws `InputError` unless `radius` is a finite\n * number greater than 0.\n *\n * Each point goes to the centre upstream d3-hexbin picks, which is close to but not exactly the nearest one: it\n * compares two candidates' squared distances in lattice units (x over √3·radius, y over 1.5·radius; `:43`), so\n * about 1-2% of points sit just outside the hexagon drawn for their bin (on the real BKN fixture, 22 of 2000 shots\n * at radius 10 and 37 of 2000 at radius 15). That is faithful to d3-hexbin and to blazing-the-nets, which draw the\n * same; do not \"fix\" it. Squares (`squarebin`) contain every point they bin.\n *\n * @example\n * ```ts\n * import { hexbin } from \"@sportsdataverse/sdvplot/bins\";\n *\n * // three Brooklyn shots of 2025-26 in tenths of a foot from the hoop (sportsdataverse-data nba_stats_shots): the\n * // two at the rim share a 1 ft hexagon, so this is 2\n * const shots = [\n * { x_legacy: 0, y_legacy: 0 },\n * { x_legacy: -1, y_legacy: 7 },\n * { x_legacy: -44, y_legacy: 252 },\n * ];\n * hexbin(shots, { radius: 10, x: (s) => s.x_legacy, y: (s) => s.y_legacy }).length;\n * ```\n */\nexport function hexbin<T>(\n points: readonly T[],\n o: { radius: number; x: (p: T) => number; y: (p: T) => number },\n): BinOf<T>[] {\n if (!(Number.isFinite(o.radius) && o.radius > 0))\n throw new InputError(`hexbin radius must be a finite number > 0, got ${String(o.radius)}`);\n const dx = o.radius * 2 * Math.sin(THIRD_PI);\n const dy = o.radius * 1.5;\n const byId = new Map<string, BinOf<T>>();\n const bins: BinOf<T>[] = [];\n for (const p of points) {\n let px = +o.x(p);\n let py = +o.y(p);\n if (Number.isNaN(px) || Number.isNaN(py)) continue;\n py = py / dy;\n let pj = Math.round(py);\n px = px / dx - (pj & 1) / 2;\n let pi = Math.round(px);\n const py1 = py - pj;\n if (Math.abs(py1) * 3 > 1) {\n const px1 = px - pi;\n const pi2 = pi + (px < pi ? -1 : 1) / 2;\n const pj2 = pj + (py < pj ? -1 : 1);\n const px2 = px - pi2;\n const py2 = py - pj2;\n if (px1 * px1 + py1 * py1 > px2 * px2 + py2 * py2) {\n pi = pi2 + (pj & 1 ? 1 : -1) / 2;\n pj = pj2;\n }\n }\n const id = `${pi}-${pj}`;\n let bin = byId.get(id);\n if (bin === undefined) {\n bin = Object.assign([] as T[], { x: (pi + (pj & 1) / 2) * dx || 0, y: pj * dy || 0 });\n byId.set(id, bin);\n bins.push(bin);\n }\n bin.push(p);\n }\n return bins;\n}\n\n/** The six vertices of a pointy-top hexagon of circumradius `r` around (0, 0), d3-hexbin's order. */\nexport function hexagonPoints(r: number): [number, number][] {\n return ANGLES.map((a): [number, number] => [Math.sin(a) * r, -Math.cos(a) * r]);\n}\n\n/**\n * An SVG path for a hexagon of circumradius `r` centred on the current point, byte-identical to d3-hexbin's\n * `hexbin.hexagon(r)`: `selection.attr(\"transform\", translate(cx, cy)).attr(\"d\", hexagonPath(r))`.\n *\n * @example\n * ```ts\n * import { hexagonPath } from \"@sportsdataverse/sdvplot/bins\";\n *\n * hexagonPath(10);\n * ```\n */\nexport function hexagonPath(r: number): string {\n let x0 = 0;\n let y0 = 0;\n const steps = ANGLES.map((a) => {\n const x1 = Math.sin(a) * r;\n const y1 = -Math.cos(a) * r;\n const step = `${x1 - x0},${y1 - y0}`;\n x0 = x1;\n y0 = y1;\n return step;\n });\n return `m${steps.join(\"l\")}z`;\n}\n","// squarebin() is the square twin of hexbin(): the same bin record (BinOf), first-seen order, NaN skipped, -0\n// folded. binner() picks either lattice from one plain options object, so everything downstream of the bins is\n// shape-agnostic (spec J38).\nimport { InputError } from \"../errors.js\";\nimport { type BinOf, hexagonPath, hexagonPoints, hexbin } from \"./hexbin.js\";\n\n/** The cell shape of a lattice. */\nexport type BinShape = \"hex\" | \"square\";\n\n/**\n * A cell lattice as plain JSON: pointy-top hexagons of circumradius `radius` (d3-hexbin's), squares of side `side`,\n * or (`equalArea: true`) the square with the same area as a hexagon of circumradius `radius`.\n */\nexport type BinnerOptions =\n | { readonly shape?: \"hex\"; readonly radius: number }\n | { readonly shape: \"square\"; readonly side: number }\n | { readonly shape: \"square\"; readonly radius: number; readonly equalArea: true };\n\n/** A lattice with `equalArea` resolved: what an index stores so a later binning reproduces the same cells. */\nexport type Lattice =\n | { readonly shape?: \"hex\"; readonly radius: number }\n | { readonly shape: \"square\"; readonly side: number };\n\n/** One lattice, either shape. */\nexport interface Binner {\n /** `\"hex\"` or `\"square\"`. */\n readonly shape: BinShape;\n /** The full cell size in the points' units: the hexagon's circumradius, or the square's side. */\n readonly size: number;\n /** The resolved options: `{ radius }` for hexagons, `{ shape: \"square\", side }` for squares. */\n readonly lattice: Lattice;\n /** Bin points (`hexbin` or `squarebin`). */\n bins<T>(points: readonly T[], o: { x: (p: T) => number; y: (p: T) => number }): BinOf<T>[];\n /** An SVG path for one cell of size `s` (default `size`) centred on the current point. */\n cell(s?: number): string;\n}\n\n/** The option the caller passed, if it is a positive finite number; otherwise an InputError naming it. */\nfunction positive(name: \"radius\" | \"side\", v: number): number {\n if (!(Number.isFinite(v) && v > 0))\n throw new InputError(`binner ${name} must be a finite number > 0, got ${String(v)}`);\n return v;\n}\n\n/** √(3√3/2) ≈ 1.6119: a hexagon of circumradius r has area (3√3/2)·r², so the equal-area square has side r·√(3√3/2). */\nconst EQUAL_AREA = Math.sqrt(1.5 * Math.sqrt(3));\n\n/**\n * Bin points into axis-aligned squares of side `side`, in the points' OWN units. As in d3-hexbin's lattice, a cell\n * is centred on the origin, so centres are `(i·side, j·side)`. Cells are half-open, `[c − side/2, c + side/2)` on\n * each axis: a point exactly on an edge goes to the higher cell, because the cell index is `Math.round(x / side)`,\n * the rounding d3-hexbin uses (`hexbin.js:33-34`). Bins come out in first-seen order; points with a NaN coordinate\n * are skipped; centres of `-0` fold to `0`. Throws `InputError` unless `side` is a finite number greater than 0.\n *\n * @example\n * ```ts\n * import { squarebin } from \"@sportsdataverse/sdvplot/bins\";\n *\n * // three Brooklyn shots of 2025-26 (sportsdataverse-data nba_stats_shots); x = 5 sits on a cell edge and goes to\n * // the higher cell: [[0, 0, 1], [0, 10, 1], [10, 90, 1]]\n * const shots = [\n * { x_legacy: 0, y_legacy: 0 },\n * { x_legacy: -1, y_legacy: 7 },\n * { x_legacy: 5, y_legacy: 91 },\n * ];\n * squarebin(shots, { side: 10, x: (s) => s.x_legacy, y: (s) => s.y_legacy }).map((b) => [b.x, b.y, b.length]);\n * ```\n */\nexport function squarebin<T>(\n points: readonly T[],\n o: { side: number; x: (p: T) => number; y: (p: T) => number },\n): BinOf<T>[] {\n const s = o.side;\n if (!(Number.isFinite(s) && s > 0))\n throw new InputError(`squarebin side must be a finite number > 0, got ${String(s)}`);\n const byId = new Map<string, BinOf<T>>();\n const bins: BinOf<T>[] = [];\n for (const p of points) {\n const px = +o.x(p);\n const py = +o.y(p);\n if (Number.isNaN(px) || Number.isNaN(py)) continue;\n const i = Math.round(px / s);\n const j = Math.round(py / s);\n const id = `${i}-${j}`;\n let bin = byId.get(id);\n if (bin === undefined) {\n bin = Object.assign([] as T[], { x: i * s || 0, y: j * s || 0 });\n byId.set(id, bin);\n bins.push(bin);\n }\n bin.push(p);\n }\n return bins;\n}\n\n/** The four corners of a square of side `s` around (0, 0), in the same turning order as `hexagonPoints`. */\nexport function squarePoints(s: number): [number, number][] {\n const h = s / 2;\n return [\n [-h, -h],\n [h, -h],\n [h, h],\n [-h, h],\n ];\n}\n\n/**\n * An SVG path for a square of side `s` centred on the current point, drawn the way `hexagonPath` is:\n * `selection.attr(\"transform\", translate(cx, cy)).attr(\"d\", squarePath(s))`.\n *\n * @example\n * ```ts\n * import { squarePath } from \"@sportsdataverse/sdvplot/bins\";\n *\n * squarePath(10); // \"m-5,-5h10v10h-10z\"\n * ```\n */\nexport function squarePath(s: number): string {\n return `m${-s / 2},${-s / 2}h${s}v${s}h${-s}z`;\n}\n\n/**\n * A cell's vertices around (0, 0), in the points' units: a hexagon of circumradius `size` (d3-hexbin's vertex\n * order) or a square of side `size`. Add a bin's centre to each to outline that cell as a polygon.\n *\n * @example\n * ```ts\n * import { cellPoints } from \"@sportsdataverse/sdvplot/bins\";\n *\n * cellPoints(\"square\", 10); // [[-5, -5], [5, -5], [5, 5], [-5, 5]]\n * ```\n */\nexport function cellPoints(shape: BinShape, size: number): [number, number][] {\n return shape === \"square\" ? squarePoints(size) : hexagonPoints(size);\n}\n/**\n * A cell's relative SVG path, `hexagonPath(size)` or `squarePath(size)`, drawn with a `translate` to each bin centre.\n *\n * @example\n * ```ts\n * import { cellPath } from \"@sportsdataverse/sdvplot/bins\";\n *\n * cellPath(\"square\", 10); // \"m-5,-5h10v10h-10z\"\n * ```\n */\nexport function cellPath(shape: BinShape, size: number): string {\n return shape === \"square\" ? squarePath(size) : hexagonPath(size);\n}\n\n/**\n * One binning API for both lattices: `binner({ radius: 15 })` is d3-hexbin's, `binner({ shape: \"square\", side: 15 })`\n * squares, and `binner({ shape: \"square\", radius: 15, equalArea: true })` squares of side 15·√(3√3/2) ≈ 24.18,\n * each the area of a radius-15 hexagon. Works on any x/y data, not just shots. Throws `InputError` on an unknown\n * `shape` or a size that is not a positive finite number, naming the option passed.\n *\n * @example\n * ```ts\n * import { binner } from \"@sportsdataverse/sdvplot/bins\";\n *\n * const b = binner({ shape: \"square\", radius: 10, equalArea: true }); // side 16.1185…: a radius-10 hexagon's area\n * // three Brooklyn shots of 2025-26 (sportsdataverse-data nba_stats_shots): the two at the rim share a square\n * const shots = [\n * { x_legacy: 0, y_legacy: 0 },\n * { x_legacy: -1, y_legacy: 7 },\n * { x_legacy: -44, y_legacy: 252 },\n * ];\n * b.bins(shots, { x: (s) => s.x_legacy, y: (s) => s.y_legacy }).map((c) => [c.x, c.y, c.length]);\n * ```\n */\nexport function binner(o: BinnerOptions): Binner {\n // Runtime checks for JS callers: the types already rule these out.\n const shape: unknown = o.shape;\n if (shape !== undefined && shape !== \"hex\" && shape !== \"square\")\n throw new InputError(`binner shape must be \"hex\" or \"square\", got ${String(shape)}`);\n if (o.shape !== \"square\") {\n const radius = positive(\"radius\", o.radius);\n return {\n shape: \"hex\",\n size: radius,\n lattice: { radius },\n bins: (points, a) => hexbin(points, { radius, x: a.x, y: a.y }),\n cell: (s = radius) => hexagonPath(s),\n };\n }\n if (!(\"side\" in o) && o.equalArea !== true)\n throw new InputError(\"binner square needs side, or radius with equalArea: true\");\n const side = \"side\" in o ? positive(\"side\", o.side) : positive(\"radius\", o.radius) * EQUAL_AREA;\n return {\n shape: \"square\",\n size: side,\n lattice: { shape: \"square\", side },\n bins: (points, a) => squarebin(points, { side, x: a.x, y: a.y }),\n cell: (s = side) => squarePath(s),\n };\n}\n"]}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { checkHeight, checkAlpha } from './chunk-7ULOCCFL.js';
|
|
2
|
+
import { resolveSync, getLeagueSync } from './chunk-ZAEQXFJM.js';
|
|
3
|
+
import { InputError, warn } from './chunk-E7G7VSLV.js';
|
|
4
|
+
|
|
5
|
+
// src/tiers.ts
|
|
6
|
+
var TIER_DESC = {
|
|
7
|
+
1: "Elite",
|
|
8
|
+
2: "Very Good",
|
|
9
|
+
3: "Medium",
|
|
10
|
+
4: "Bad",
|
|
11
|
+
5: "What are they doing?",
|
|
12
|
+
6: "",
|
|
13
|
+
7: ""
|
|
14
|
+
};
|
|
15
|
+
var TIERS_SUBTITLE = "created with the #sdvplot Tiermaker";
|
|
16
|
+
var TIER_THEMES = {
|
|
17
|
+
dark: { bg: "#1e1e1e", line: "#e0e0e0", text: "#ffffff", muted: "#8e8e93" },
|
|
18
|
+
light: { bg: "#ffffff", line: "#3a3a3c", text: "#1e1e1e", muted: "#636366" }
|
|
19
|
+
};
|
|
20
|
+
var AUTO_VARIANT = { dark: "dark", light: "default" };
|
|
21
|
+
var DEFAULT_HEIGHT = 0.1;
|
|
22
|
+
function wrapLabel(text) {
|
|
23
|
+
const out = [];
|
|
24
|
+
let line = "";
|
|
25
|
+
for (const w of text.split(/\s+/).filter(Boolean)) {
|
|
26
|
+
if (line && line.length + 1 + w.length > 14) {
|
|
27
|
+
out.push(line);
|
|
28
|
+
line = w;
|
|
29
|
+
} else line = line ? `${line} ${w}` : w;
|
|
30
|
+
}
|
|
31
|
+
if (line) out.push(line);
|
|
32
|
+
return out.join("\n");
|
|
33
|
+
}
|
|
34
|
+
var miss = (v) => v === null || v === void 0 || typeof v === "number" && Number.isNaN(v);
|
|
35
|
+
var real = (v) => typeof v === "number" && !Number.isNaN(v) || typeof v === "bigint";
|
|
36
|
+
function prepareTiers(rows, league, o = {}) {
|
|
37
|
+
const theme = o.theme ?? "dark";
|
|
38
|
+
if (!Object.hasOwn(TIER_THEMES, theme))
|
|
39
|
+
throw new InputError(`theme must be "dark" or "light", got ${String(theme)}`);
|
|
40
|
+
const height = o.height === void 0 ? DEFAULT_HEIGHT : checkHeight(o.height);
|
|
41
|
+
const alpha = checkAlpha(o.alpha ?? 0.8);
|
|
42
|
+
const tiers = rows.map((r) => r.tierNo ?? r.tier_no);
|
|
43
|
+
const teams = rows.map((r) => r.team);
|
|
44
|
+
const ranksIn = rows.map((r) => r.tierRank ?? r.tier_rank);
|
|
45
|
+
const given = !o.presort && ranksIn.some((v) => v !== void 0) ? ranksIn : void 0;
|
|
46
|
+
const all = rows.map((_, i) => i);
|
|
47
|
+
const absent = all.filter((i) => miss(tiers[i]) || given !== void 0 && miss(given[i]));
|
|
48
|
+
if (absent.length) {
|
|
49
|
+
const names = absent.map((i) => String(teams[i]));
|
|
50
|
+
const extra = names.length > 10 ? ` and ${names.length - 10} more` : "";
|
|
51
|
+
const shown = `${names.slice(0, 10).join(", ")}${extra}`;
|
|
52
|
+
warn(
|
|
53
|
+
`tiers:${league}:missing:${shown}`,
|
|
54
|
+
`skipped ${names.length} point(s) with a missing tier_no or tier_rank: ${shown}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
let idx = all.filter((i) => !absent.includes(i));
|
|
58
|
+
if (!idx.length) throw new InputError("data has no rows with a tier_no");
|
|
59
|
+
if (!idx.every((i) => real(tiers[i]) && (given === void 0 || real(given[i]))))
|
|
60
|
+
throw new InputError("tier_no and tier_rank must hold numbers (tier 1 is the top tier)");
|
|
61
|
+
const tierOf = (i) => Number(tiers[i]);
|
|
62
|
+
if (o.presort) {
|
|
63
|
+
const name = (i) => String(teams[i]);
|
|
64
|
+
const cp = (i) => Array.from(name(i), (c) => c.codePointAt(0));
|
|
65
|
+
const cmp = (a, b) => {
|
|
66
|
+
for (let k = 0; k < Math.min(a.length, b.length); k++)
|
|
67
|
+
if (a[k] !== b[k]) return a[k] - b[k];
|
|
68
|
+
return a.length - b.length;
|
|
69
|
+
};
|
|
70
|
+
idx = idx.sort(
|
|
71
|
+
(a, b) => tierOf(a) - tierOf(b) || Number(miss(teams[a])) - Number(miss(teams[b])) || cmp(cp(a), cp(b))
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const ranks = /* @__PURE__ */ new Map();
|
|
75
|
+
const seen = /* @__PURE__ */ new Map();
|
|
76
|
+
for (const i of idx) {
|
|
77
|
+
if (given) ranks.set(i, Number(given[i]));
|
|
78
|
+
else {
|
|
79
|
+
const t = tierOf(i);
|
|
80
|
+
const n = (seen.get(t) ?? 0) + 1;
|
|
81
|
+
seen.set(t, n);
|
|
82
|
+
ranks.set(i, n);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const ids = resolveSync(
|
|
86
|
+
idx.map((i) => teams[i]),
|
|
87
|
+
league
|
|
88
|
+
);
|
|
89
|
+
const kept = idx.flatMap((i, k) => {
|
|
90
|
+
const id = ids[k];
|
|
91
|
+
return id === void 0 ? [] : [[i, id]];
|
|
92
|
+
});
|
|
93
|
+
const abbr = new Map(getLeagueSync(league).teams.map((t) => [t.team_id, t.abbr]));
|
|
94
|
+
const levels = [...new Set(idx.map(tierOf))].sort((a, b) => a - b);
|
|
95
|
+
const skip = new Set(
|
|
96
|
+
o.noLineBelowTier === void 0 ? [] : typeof o.noLineBelowTier === "number" ? [o.noLineBelowTier] : o.noLineBelowTier
|
|
97
|
+
);
|
|
98
|
+
const desc = o.tierDesc ?? TIER_DESC;
|
|
99
|
+
const rs = [...ranks.values()];
|
|
100
|
+
const lo = Math.min(...rs);
|
|
101
|
+
const hi = Math.max(...rs);
|
|
102
|
+
const pad = lo === hi ? 0.5 : 0.05 * (hi - lo);
|
|
103
|
+
const first = levels[0];
|
|
104
|
+
const last = levels[levels.length - 1];
|
|
105
|
+
return {
|
|
106
|
+
x: kept.map(([i]) => ranks.get(i)),
|
|
107
|
+
y: kept.map(([i]) => tierOf(i)),
|
|
108
|
+
teamIds: kept.map(([, id]) => id),
|
|
109
|
+
labels: kept.map(([i, id]) => abbr.get(id) || String(teams[i])),
|
|
110
|
+
lines: [first - 0.5, ...levels.filter((t) => !skip.has(t)).map((t) => t + 0.5)],
|
|
111
|
+
breaks: levels,
|
|
112
|
+
breakLabels: levels.map((t) => wrapLabel(desc[t] ?? "")),
|
|
113
|
+
xlim: [lo - pad, hi + pad],
|
|
114
|
+
ylim: [first - 0.6, last + 0.6],
|
|
115
|
+
title: o.title === void 0 ? `${league.toUpperCase()} Team Tiers` : o.title ?? "",
|
|
116
|
+
subtitle: o.subtitle === void 0 ? TIERS_SUBTITLE : o.subtitle,
|
|
117
|
+
caption: o.caption ?? null,
|
|
118
|
+
height,
|
|
119
|
+
alpha,
|
|
120
|
+
variant: (o.variant ?? "auto") === "auto" ? AUTO_VARIANT[theme] : o.variant,
|
|
121
|
+
theme: TIER_THEMES[theme]
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { TIERS_SUBTITLE, TIER_DESC, TIER_THEMES, prepareTiers, wrapLabel };
|
|
126
|
+
//# sourceMappingURL=chunk-DYMUVE6K.js.map
|
|
127
|
+
//# sourceMappingURL=chunk-DYMUVE6K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tiers.ts"],"names":[],"mappings":"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","file":"chunk-DYMUVE6K.js","sourcesContent":["import { InputError, warn } from \"./errors.js\";\nimport { getLeagueSync } from \"./index-data.js\";\nimport { checkAlpha, checkHeight } from \"./placement.js\";\nimport { type Value, resolveSync } from \"./resolve.js\";\nimport type { League, TeamId, Variant } from \"./types.js\";\n\nexport const TIER_DESC: Readonly<Record<number, string>> = {\n 1: \"Elite\",\n 2: \"Very Good\",\n 3: \"Medium\",\n 4: \"Bad\",\n 5: \"What are they doing?\",\n 6: \"\",\n 7: \"\",\n};\nexport const TIERS_SUBTITLE = \"created with the #sdvplot Tiermaker\";\nexport const TIER_THEMES = {\n dark: { bg: \"#1e1e1e\", line: \"#e0e0e0\", text: \"#ffffff\", muted: \"#8e8e93\" },\n light: { bg: \"#ffffff\", line: \"#3a3a3c\", text: \"#1e1e1e\", muted: \"#636366\" },\n} as const;\nconst AUTO_VARIANT = { dark: \"dark\", light: \"default\" } as const;\n// ponytail: DEFAULT_HEIGHT is the Python one (a fraction of panel height for 32 logos in 5 tiers).\nconst DEFAULT_HEIGHT = 0.1;\n\nexport interface TierRow {\n tierNo?: Value;\n tier_no?: Value;\n team: Value;\n tierRank?: Value;\n tier_rank?: Value;\n}\nexport interface TiersOptions {\n /** `undefined` gives the league name plus \"Team Tiers\"; `null` or an empty string gives no title. */\n title?: string | null;\n /** `undefined` gives the Tiermaker line; `null` or an empty string gives none. */\n subtitle?: string | null;\n caption?: string | null;\n tierDesc?: Readonly<Record<number, string>>;\n presort?: boolean;\n alpha?: number;\n /** Fraction of the plot height in (0, 1]; default 0.1. */\n height?: number;\n noLineBelowTier?: number | readonly number[];\n theme?: \"dark\" | \"light\";\n variant?: Variant | \"auto\";\n}\nexport interface Tiers {\n x: number[];\n y: number[];\n teamIds: TeamId[];\n labels: string[];\n lines: number[];\n breaks: number[];\n breakLabels: string[];\n xlim: [number, number];\n ylim: [number, number];\n title: string;\n subtitle: string | null;\n caption: string | null;\n height: number;\n alpha: number;\n variant: Variant;\n theme: (typeof TIER_THEMES)[keyof typeof TIER_THEMES];\n}\n\n/** `textwrap.wrap(text, 14, break_long_words=False, break_on_hyphens=False)` joined by newlines: lines of at most 14 characters, a longer word kept whole on its own line. */\nexport function wrapLabel(text: string): string {\n const out: string[] = [];\n let line = \"\";\n for (const w of text.split(/\\s+/).filter(Boolean)) {\n if (line && line.length + 1 + w.length > 14) {\n out.push(line);\n line = w;\n } else line = line ? `${line} ${w}` : w;\n }\n if (line) out.push(line);\n return out.join(\"\\n\");\n}\n\nconst miss = (v: Value): boolean =>\n v === null || v === undefined || (typeof v === \"number\" && Number.isNaN(v));\nconst real = (v: Value): boolean => (typeof v === \"number\" && !Number.isNaN(v)) || typeof v === \"bigint\";\n\n/** Port of `sdvplot._tiers.prepare`: validate the rows and compute everything a tier plot draws. */\nexport function prepareTiers(rows: readonly TierRow[], league: League, o: TiersOptions = {}): Tiers {\n const theme = o.theme ?? \"dark\";\n if (!Object.hasOwn(TIER_THEMES, theme))\n throw new InputError(`theme must be \"dark\" or \"light\", got ${String(theme)}`);\n const height = o.height === undefined ? DEFAULT_HEIGHT : checkHeight(o.height);\n const alpha = checkAlpha(o.alpha ?? 0.8);\n const tiers = rows.map((r): Value => r.tierNo ?? r.tier_no);\n const teams = rows.map((r) => r.team);\n const ranksIn = rows.map((r): Value => r.tierRank ?? r.tier_rank);\n const given = !o.presort && ranksIn.some((v) => v !== undefined) ? ranksIn : undefined;\n\n const all = rows.map((_, i) => i);\n const absent = all.filter((i) => miss(tiers[i]) || (given !== undefined && miss(given[i])));\n if (absent.length) {\n const names = absent.map((i) => String(teams[i]));\n const extra = names.length > 10 ? ` and ${names.length - 10} more` : \"\";\n const shown = `${names.slice(0, 10).join(\", \")}${extra}`;\n warn(\n `tiers:${league}:missing:${shown}`,\n `skipped ${names.length} point(s) with a missing tier_no or tier_rank: ${shown}`,\n );\n }\n let idx = all.filter((i) => !absent.includes(i));\n if (!idx.length) throw new InputError(\"data has no rows with a tier_no\");\n if (!idx.every((i) => real(tiers[i]) && (given === undefined || real(given[i]))))\n throw new InputError(\"tier_no and tier_rank must hold numbers (tier 1 is the top tier)\");\n const tierOf = (i: number): number => Number(tiers[i]);\n if (o.presort) {\n // arrange(tier_no, team), then rank within the tier; a missing team last, as R's NA\n const name = (i: number): string => String(teams[i]);\n const cp = (i: number): number[] => Array.from(name(i), (c) => c.codePointAt(0) as number);\n const cmp = (a: number[], b: number[]): number => {\n for (let k = 0; k < Math.min(a.length, b.length); k++)\n if (a[k] !== b[k]) return (a[k] as number) - (b[k] as number);\n return a.length - b.length;\n };\n idx = idx.sort(\n (a, b) => tierOf(a) - tierOf(b) || Number(miss(teams[a])) - Number(miss(teams[b])) || cmp(cp(a), cp(b)),\n );\n }\n const ranks = new Map<number, number>();\n const seen = new Map<number, number>();\n for (const i of idx) {\n if (given) ranks.set(i, Number(given[i]));\n else {\n const t = tierOf(i);\n const n = (seen.get(t) ?? 0) + 1;\n seen.set(t, n);\n ranks.set(i, n);\n }\n }\n\n // Rank first, then resolve: an unknown team keeps its slot (and the x range) but draws nothing.\n const ids = resolveSync(\n idx.map((i) => teams[i]),\n league,\n );\n const kept = idx.flatMap((i, k) => {\n const id = ids[k];\n return id === undefined ? [] : [[i, id] as const];\n });\n const abbr = new Map(getLeagueSync(league).teams.map((t) => [t.team_id, t.abbr]));\n const levels = [...new Set(idx.map(tierOf))].sort((a, b) => a - b);\n const skip = new Set<number>(\n o.noLineBelowTier === undefined\n ? []\n : typeof o.noLineBelowTier === \"number\"\n ? [o.noLineBelowTier]\n : o.noLineBelowTier,\n );\n const desc = o.tierDesc ?? TIER_DESC;\n const rs = [...ranks.values()];\n const lo = Math.min(...rs);\n const hi = Math.max(...rs);\n const pad = lo === hi ? 0.5 : 0.05 * (hi - lo);\n const first = levels[0] as number;\n const last = levels[levels.length - 1] as number;\n return {\n x: kept.map(([i]) => ranks.get(i) as number),\n y: kept.map(([i]) => tierOf(i)),\n teamIds: kept.map(([, id]) => id),\n labels: kept.map(([i, id]) => abbr.get(id) || String(teams[i])),\n lines: [first - 0.5, ...levels.filter((t) => !skip.has(t)).map((t) => t + 0.5)],\n breaks: levels,\n breakLabels: levels.map((t) => wrapLabel(desc[t] ?? \"\")),\n xlim: [lo - pad, hi + pad],\n ylim: [first - 0.6, last + 0.6],\n title: o.title === undefined ? `${league.toUpperCase()} Team Tiers` : (o.title ?? \"\"),\n subtitle: o.subtitle === undefined ? TIERS_SUBTITLE : o.subtitle,\n caption: o.caption ?? null,\n height,\n alpha,\n variant: (o.variant ?? \"auto\") === \"auto\" ? AUTO_VARIANT[theme] : (o.variant as Variant),\n theme: TIER_THEMES[theme],\n };\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var SdvplotError = class extends Error {
|
|
3
|
+
constructor(message, options) {
|
|
4
|
+
super(message, options);
|
|
5
|
+
this.name = new.target.name;
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
var InputError = class extends SdvplotError {
|
|
9
|
+
};
|
|
10
|
+
var UnresolvedTeamError = class extends SdvplotError {
|
|
11
|
+
};
|
|
12
|
+
var UnsupportedTargetError = class extends SdvplotError {
|
|
13
|
+
};
|
|
14
|
+
var OptionalDependencyError = class extends SdvplotError {
|
|
15
|
+
};
|
|
16
|
+
var OfflineError = class extends SdvplotError {
|
|
17
|
+
};
|
|
18
|
+
var DownloadError = class extends OfflineError {
|
|
19
|
+
constructor(message, url, status) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.url = url;
|
|
22
|
+
this.status = status;
|
|
23
|
+
}
|
|
24
|
+
url;
|
|
25
|
+
status;
|
|
26
|
+
};
|
|
27
|
+
var handler = null;
|
|
28
|
+
var seen = /* @__PURE__ */ new Set();
|
|
29
|
+
function setWarningHandler(fn) {
|
|
30
|
+
handler = fn;
|
|
31
|
+
}
|
|
32
|
+
function resetWarnings() {
|
|
33
|
+
seen.clear();
|
|
34
|
+
}
|
|
35
|
+
function warn(key, message) {
|
|
36
|
+
if (seen.has(key)) return;
|
|
37
|
+
seen.add(key);
|
|
38
|
+
(handler ?? ((m) => console.warn(`[sdvplot] ${m}`)))(message);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { DownloadError, InputError, OfflineError, OptionalDependencyError, SdvplotError, UnresolvedTeamError, UnsupportedTargetError, resetWarnings, setWarningHandler, warn };
|
|
42
|
+
//# sourceMappingURL=chunk-E7G7VSLV.js.map
|
|
43
|
+
//# sourceMappingURL=chunk-E7G7VSLV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts"],"names":[],"mappings":";AAAO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAAM;AAAA,EACtC,WAAA,CAAY,SAAiB,OAAA,EAAwB;AACnD,IAAA,KAAA,CAAM,SAAS,OAAO,CAAA;AACtB,IAAA,IAAA,CAAK,OAAO,GAAA,CAAA,MAAA,CAAW,IAAA;AAAA,EACzB;AACF;AACO,IAAM,UAAA,GAAN,cAAyB,YAAA,CAAa;AAAC;AACvC,IAAM,mBAAA,GAAN,cAAkC,YAAA,CAAa;AAAC;AAChD,IAAM,sBAAA,GAAN,cAAqC,YAAA,CAAa;AAAC;AAEnD,IAAM,uBAAA,GAAN,cAAsC,YAAA,CAAa;AAAC;AACpD,IAAM,YAAA,GAAN,cAA2B,YAAA,CAAa;AAAC;AACzC,IAAM,aAAA,GAAN,cAA4B,YAAA,CAAa;AAAA,EAC9C,WAAA,CACE,OAAA,EACS,GAAA,EACA,MAAA,EACT;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AAHJ,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAGX;AAAA,EAJW,GAAA;AAAA,EACA,MAAA;AAIb;AAGA,IAAI,OAAA,GAAiC,IAAA;AACrC,IAAM,IAAA,uBAAW,GAAA,EAAY;AACtB,SAAS,kBAAkB,EAAA,EAAiC;AACjE,EAAA,OAAA,GAAU,EAAA;AACZ;AACO,SAAS,aAAA,GAAsB;AACpC,EAAA,IAAA,CAAK,KAAA,EAAM;AACb;AAEO,SAAS,IAAA,CAAK,KAAa,OAAA,EAAuB;AACvD,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,EAAG;AACnB,EAAA,IAAA,CAAK,IAAI,GAAG,CAAA;AACZ,EAAA,CAAC,OAAA,KAAY,CAAC,CAAA,KAAc,OAAA,CAAQ,KAAK,CAAA,UAAA,EAAa,CAAC,CAAA,CAAE,CAAA,CAAA,EAAI,OAAO,CAAA;AACtE","file":"chunk-E7G7VSLV.js","sourcesContent":["export class SdvplotError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n }\n}\nexport class InputError extends SdvplotError {}\nexport class UnresolvedTeamError extends SdvplotError {}\nexport class UnsupportedTargetError extends SdvplotError {}\n/** An optional peer dependency (resvg, playwright, react) is not installed. */\nexport class OptionalDependencyError extends SdvplotError {}\nexport class OfflineError extends SdvplotError {}\nexport class DownloadError extends OfflineError {\n constructor(\n message: string,\n readonly url: string,\n readonly status?: number,\n ) {\n super(message);\n }\n}\n\nexport type WarningHandler = (message: string) => void;\nlet handler: WarningHandler | null = null;\nconst seen = new Set<string>();\nexport function setWarningHandler(fn: WarningHandler | null): void {\n handler = fn;\n}\nexport function resetWarnings(): void {\n seen.clear();\n}\n/** One warning per `key` per process (spec §4.3): a 10k-row join must not print 10k lines. */\nexport function warn(key: string, message: string): void {\n if (seen.has(key)) return;\n seen.add(key);\n (handler ?? ((m: string) => console.warn(`[sdvplot] ${m}`)))(message);\n}\n"]}
|