@sportsdataverse/sporty 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 +5 -0
- package/README.md +235 -2
- package/dist/canvas.d.ts +54 -0
- package/dist/canvas.js +66 -0
- package/dist/canvas.js.map +1 -0
- package/dist/chunk-23Y47RKN.js +19 -0
- package/dist/chunk-23Y47RKN.js.map +1 -0
- package/dist/chunk-3WYO7OMK.js +43 -0
- package/dist/chunk-3WYO7OMK.js.map +1 -0
- package/dist/chunk-DRH5NZI4.js +14 -0
- package/dist/chunk-DRH5NZI4.js.map +1 -0
- package/dist/d3.d.ts +17 -0
- package/dist/d3.js +28 -0
- package/dist/d3.js.map +1 -0
- package/dist/index.d.ts +449 -0
- package/dist/index.js +6345 -0
- package/dist/index.js.map +1 -0
- package/dist/plot.d.ts +68 -0
- package/dist/plot.js +88 -0
- package/dist/plot.js.map +1 -0
- package/dist/scene-D5Jc19yu.d.ts +56 -0
- package/dist/specs/index.d.ts +431 -0
- package/dist/specs/index.js +3 -0
- package/dist/specs/index.js.map +1 -0
- package/dist/svg.d.ts +31 -0
- package/dist/svg.js +150 -0
- package/dist/svg.js.map +1 -0
- package/package.json +97 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SportsDataverse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Notices
|
|
2
|
+
|
|
3
|
+
- `@sportsdataverse/sporty` ports geometry and the surface-dimensions data of **sportyR** (Ross Drucker) and
|
|
4
|
+
**sportypy**. Those packages are published under GPL-3; by agreement between the SportsDataverse maintainers
|
|
5
|
+
(recorded 2026-10-05, spec decision J3) the ported material is distributed here under MIT.
|
package/README.md
CHANGED
|
@@ -1,3 +1,236 @@
|
|
|
1
|
-
#
|
|
1
|
+
# **@sportsdataverse/sporty** <a href='https://plot.sportsdataverse.org/'><img src='https://raw.githubusercontent.com/sportsdataverse/sdvplot-js/main/docs/static/img/sdvplot-js-logo.png' align="right" width="25%" min-width="120px" alt="sdvplot-js hex logo" /></a>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<!-- badges: start -->
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@sportsdataverse/sporty)
|
|
6
|
+
[](https://www.npmjs.com/package/@sportsdataverse/sporty)
|
|
7
|
+
[](https://www.npmjs.com/package/@sportsdataverse/sporty)
|
|
8
|
+
[](https://nodejs.org/)
|
|
9
|
+
[](https://www.npmjs.com/package/@sportsdataverse/sporty?activeTab=code)
|
|
10
|
+
[](https://github.com/sportsdataverse/sdvplot-js/actions/workflows/ci.yml)
|
|
11
|
+
[](https://github.com/sportsdataverse/sdvplot-js/actions/workflows/release.yml)
|
|
12
|
+
[](https://plot.sportsdataverse.org)
|
|
13
|
+
[](https://lifecycle.r-lib.org/articles/stages.html#experimental)
|
|
14
|
+
[](https://github.com/sportsdataverse/sdvplot-js/blob/main/LICENSE)
|
|
15
|
+
[](https://github.com/sportsdataverse/sdvplot-js/graphs/contributors)
|
|
16
|
+
[](https://x.com/SportsDataverse)
|
|
17
|
+
|
|
18
|
+
<!-- badges: end -->
|
|
19
|
+
|
|
20
|
+
Sport surfaces (courts, rinks, fields) as plain geometry for SportsDataverse plots. TypeScript port of the R package [sportyR](https://github.com/sportsdataverse/sportyR) 2.4.0: every surface is a `Scene` of polygons and text, rendered by the `svg`, `canvas`, `plot` and `d3` subpaths.
|
|
21
|
+
|
|
22
|
+
Part of [sdvplot-js](https://github.com/sportsdataverse/sdvplot-js#readme) · [Documentation](https://plot.sportsdataverse.org) · [API reference](https://plot.sportsdataverse.org/api/sporty/) ·
|
|
23
|
+
[Gallery](https://plot.sportsdataverse.org/gallery/sporty/) · [Notebooks](https://plot.sportsdataverse.org/notebooks/) · [Source](https://github.com/sportsdataverse/sdvplot-js/tree/main/packages/sporty)
|
|
24
|
+
|
|
25
|
+
## **Installation**
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm install @sportsdataverse/sporty
|
|
29
|
+
# or
|
|
30
|
+
pnpm add @sportsdataverse/sporty
|
|
31
|
+
# or
|
|
32
|
+
yarn add @sportsdataverse/sporty
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
ESM only, Node >= 20.18.1. The core, `svg`, `specs` and `canvas` subpaths need nothing else. Optional peers, installed
|
|
36
|
+
only for the subpath you import: `@observablehq/plot` >= 0.6.16 for `/plot`, `d3` >= 7 for `/d3`, and
|
|
37
|
+
`@napi-rs/canvas` >= 0.1.50 to give `/canvas` a 2D context in Node (browsers have their own).
|
|
38
|
+
|
|
39
|
+
## **Quick start**
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { writeFileSync } from "node:fs";
|
|
43
|
+
import { basketballCourt, toSurfaceFrame } from "@sportsdataverse/sporty";
|
|
44
|
+
import { toSVG } from "@sportsdataverse/sporty/svg";
|
|
45
|
+
|
|
46
|
+
writeFileSync("nba.svg", toSVG(basketballCourt("nba")));
|
|
47
|
+
|
|
48
|
+
// Move source data into the surface frame (feet, surface origin), then plot it over the SVG. This shot is LeBron
|
|
49
|
+
// James's made three at Denver, 2023-10-24 (stats.nba.com shotchartdetail, game 0022300061, event 510).
|
|
50
|
+
const shots = toSurfaceFrame([{ x_legacy: -53, y_legacy: 285 }], { from: "nba-legacy" });
|
|
51
|
+
// [{ x_legacy: -53, y_legacy: 285, surface_x: -13.25, surface_y: -5.3 }] -> plot (surface_x, surface_y) in feet
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Draw a surface with Observable Plot or d3 (optional peers):
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
import * as Plot from "@observablehq/plot";
|
|
58
|
+
import { basketballCourt } from "@sportsdataverse/sporty";
|
|
59
|
+
import { surfaceMark, surfaceScales } from "@sportsdataverse/sporty/plot";
|
|
60
|
+
|
|
61
|
+
const scene = basketballCourt("nba");
|
|
62
|
+
Plot.plot({ ...surfaceScales(scene), marks: surfaceMark(scene) });
|
|
63
|
+
|
|
64
|
+
// d3: appendSurface(selection, scene, x, y) draws the same Scene through your scale functions.
|
|
65
|
+
import { appendSurface } from "@sportsdataverse/sporty/d3";
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`surfaceMark(scene, { ariaDescription })`, `toSVG(scene, { ariaDescription })` and
|
|
69
|
+
`appendSurface(selection, scene, x, y, { ariaDescription })` describe the surface for assistive technology, on the group
|
|
70
|
+
that holds it; the default is the scene's league and sport, such as "nba basketball surface". A canvas has no DOM to
|
|
71
|
+
carry one: describe the `<canvas>` element itself.
|
|
72
|
+
|
|
73
|
+
Paint onto a canvas (a browser `CanvasRenderingContext2D`, or `@napi-rs/canvas` in Node; no DOM types needed):
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { drawScene } from "@sportsdataverse/sporty/canvas";
|
|
77
|
+
|
|
78
|
+
const { width, height } = drawScene(ctx, basketballCourt("nba"), { width: 800 }); // or { width, height } to fit a box, or { scale: 8 } px per unit
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`toSVG(scene, { arcs: "svg" })` replaces each detected circle run with SVG `A` commands, one per quarter turn (`ceil(span / 90°)` per run, so a full circle is four), instead of emitting every sampled point as `L`; the default `"sampled"` keeps the Scene's points verbatim. Under `arcs: "svg"`, `arcResolution` no longer affects how circles look (it still sets how many points the Scene carries).
|
|
82
|
+
|
|
83
|
+
`surface(sport, league, opts)` dispatches by sport; `leagues`, `features`, `displayRanges` and `colorKeys` list what each sport accepts. Options mirror sportyR: `updates` (parameter overrides), `colorUpdates`, `rotation`, `xTrans`, `yTrans`, `units` (`ft`, `m`, `yd`, `in`, `cm`, `mm`, any case, or a full name such as `"feet"`; anything else throws `UnknownUnitError`), `displayRange`, `xlim`, `ylim`, `arcResolution` (points per arc, an integer >= 2, default 200). Options are typed per sport, so a misspelled option or key is a compile error.
|
|
84
|
+
|
|
85
|
+
## **Subpaths**
|
|
86
|
+
|
|
87
|
+
| Import | Contents |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `@sportsdataverse/sporty` | `surface`, `SPORTS`, `basketballCourt`, `hockeyRink`, `footballField`, `soccerPitch`, `baseballField`, `tennisCourt`, `volleyballCourt`, `curlingSheet`, `lacrosseField`, `softballField`, `Scene`, `toSurfaceFrame`, `FRAMES`, `basketballZoneOf`, `basketballZones`, `BASKETBALL_ZONES`, `BASKETBALL_ZONE_LABELS`, errors |
|
|
90
|
+
| `@sportsdataverse/sporty/svg` | `toSVG` (`width`, `height`, `background`, `precision`, `id`, `arcs: "sampled" \| "svg"`) |
|
|
91
|
+
| `@sportsdataverse/sporty/specs` | generated parameter specs for all sports |
|
|
92
|
+
| `@sportsdataverse/sporty/plot` | `surfaceMark`, `surfaceScales`, `sceneToGeoJSON` (optional peer `@observablehq/plot`) |
|
|
93
|
+
| `@sportsdataverse/sporty/canvas` | `drawScene`, `SceneCanvasContext` (DOM-free; takes a browser 2D context or an `@napi-rs/canvas` one in Node) |
|
|
94
|
+
| `@sportsdataverse/sporty/d3` | `appendSurface` (optional peer `d3`) |
|
|
95
|
+
|
|
96
|
+
## **Ported sports**
|
|
97
|
+
|
|
98
|
+
| Sport | Builder | Leagues | Notes |
|
|
99
|
+
| --- | --- | --- | --- |
|
|
100
|
+
| basketball | `basketballCourt` | `custom`, `fiba`, `nba`, `nba g league`, `ncaa`, `nfhs`, `wnba` | |
|
|
101
|
+
| hockey | `hockeyRink` | `ahl`, `custom`, `echl`, `iihf`, `ncaa`, `nhl`, `nwhl`, `ohl`, `phf`, `pwhl`, `qmjhl`, `ushl` | |
|
|
102
|
+
| football | `footballField` | `cfl`, `custom`, `ncaa`, `nfhs11`, `nfhs6`, `nfhs8`, `nfhs9`, `nfl` | |
|
|
103
|
+
| soccer | `soccerPitch` | `custom`, `epl`, `fifa`, `mls`, `ncaa`, `nwsl` | Native units `m` (epl, fifa) or `yd` (mls, ncaa, nwsl) |
|
|
104
|
+
| baseball | `baseballField` | `custom`, `little league`, `milb`, `mlb`, `ncaa`, `nfhs`, `pony` | `origin: "home_plate"`: the back tip of home plate, +y toward centre field, +x toward first base; `ft` |
|
|
105
|
+
| tennis | `tennisCourt` | `atp`, `custom`, `ita`, `itf`, `ncaa`, `usta`, `wta` | Chair-umpire view: the net runs along y at x = 0; `ft` |
|
|
106
|
+
| volleyball | `volleyballCourt` | `custom`, `fivb`, `ncaa`, `usa volleyball` | `m` |
|
|
107
|
+
| curling | `curlingSheet` | `curling canada`, `custom`, `wcf` | Drawn vertically: +y toward the top house, `sheet_width` along x; `ft` |
|
|
108
|
+
| lacrosse | `lacrosseField` | `custom`, `ncaam`, `ncaaw`, `nll`, `pll`, `usam`, `usaw`, `world lacrosse` | Mixed native units: `yd` (ncaam, pll, usam), `m` (ncaaw, usaw), `ft` (nll, world lacrosse); pass `units` to draw them alike |
|
|
109
|
+
| softball | `softballField` | `custom`, `ncaa` | `origin: "home_plate"` like baseball; `ft` |
|
|
110
|
+
|
|
111
|
+
## **Coordinate frames**
|
|
112
|
+
|
|
113
|
+
`FRAMES` holds the source frames; `toSurfaceFrame(rows, { from, x?, y?, out? })` adds `surface_x`/`surface_y` (feet, surface origin; `null` for missing input). These move the data; sporty's `xTrans`/`yTrans` move the surface.
|
|
114
|
+
|
|
115
|
+
| Name | Source | Formula |
|
|
116
|
+
| --- | --- | --- |
|
|
117
|
+
| `nba-legacy` | stats.nba.com shots (tenths of a foot, hoop origin; inputs default to `x_legacy`/`y_legacy`). Shots land on the -x half: pair with `displayRange: "defense"` | `surface_x = -47 + 5.25 + y/10`, `surface_y = x/10` |
|
|
118
|
+
| `nba-legacy-vertical` | The same shots with the hoop at the bottom, for a `rotation: 90` scene: points come out already in the rotated frame (do not rotate them again); x across with its sign kept, y toward half court | `surface_x = x/10`, `surface_y = -47 + 5.25 + y/10` |
|
|
119
|
+
| `hockeytech` | HockeyTech play-by-play 600x300 canvas, top-left origin, centre ice at (300, 150), every league (fastRhockey). The canvas is stylised rather than true to scale: end-zone faceoff dots convert to about ±67 ft and ±52 ft against regulation ±69 ft, so the feet are approximate | `x/3 - 100`, `42.5 - y*85/300` |
|
|
120
|
+
| `espn-football-0-100` | ESPN yardline 0-100 | `x - 50`, `y` unchanged |
|
|
121
|
+
|
|
122
|
+
Basketball shot zones: `basketballZoneOf(x, y, shotValue, { league, scale })` names the stats.nba.com zone of a basket-centred shot (`BASKETBALL_ZONES`, display names in `BASKETBALL_ZONE_LABELS`), and `basketballZones(league, { scale, top })` returns the six zones as fillable rings. Both are basket-centred, not in a surface frame: map the rings through the same frame as the shots (for legacy shots on the hoop-at-the-bottom chart, `scale: 10` and `FRAMES["nba-legacy-vertical"]`).
|
|
123
|
+
|
|
124
|
+
## **Provenance and parity**
|
|
125
|
+
|
|
126
|
+
Ported from sportyR 2.4.0 (vendored from sportyR main at b97443a, which carries the geometry fixes merged in sportyR PR #68) - see [NOTICE.md](https://github.com/sportsdataverse/sdvplot-js/blob/main/packages/sporty/NOTICE.md) (J3). The R package is the oracle: the test suite compares every feature of all 54 non-`custom` league surfaces point for point to 1e-9 against R output stored under `fixtures/sporty`, and a league without fixtures fails. Regenerate the fixtures with `pnpm oracle:sporty basketball hockey football soccer baseball tennis volleyball curling lacrosse softball` (needs R and sportyR; set `R_LIBS` to a private library holding the vendored sportyR, and `SPORTYR_REPO` to its checkout so `VERSION` records the sha); the oracle refuses to run unless the installed sportyR's `surface_dimensions` equals the vendored `data/surface-dimensions.json`, and records that file's sha256 in `fixtures/sporty/VERSION`. Parameter specs in `src/specs` are generated from the vendored JSON (`pnpm codegen`).
|
|
127
|
+
|
|
128
|
+
Drift gates in CI: `pnpm codegen --check` (specs match the vendored JSON) and `test/fixtures-version.test.ts` (the vendored JSON's sha256 and sportyR version/checkout match the ones the fixtures were generated from). `pnpm vendor --check` compares the vendored JSON with a sportyR checkout, so it only runs where one exists (`SPORTYR_REPO`); elsewhere it skips.
|
|
129
|
+
|
|
130
|
+
## **Differences from sportyR**
|
|
131
|
+
|
|
132
|
+
- An unknown `displayRange` throws `UnknownDisplayRangeError` (R silently falls back to `"full"`).
|
|
133
|
+
- Vector colours recycle per copy of a feature (`colorAt`), as R `data.frame()` does. Curling's `house_rings` recycles too, so a single colour fills every ring; R indexes it per ring and leaves rings 2 and 3 `NA`.
|
|
134
|
+
- A lane space mark takes its own set's colour; R reads row `i` (the lane index) of the set instead, which is `NA` once a court has more lanes than marks per set. Default leagues are unaffected.
|
|
135
|
+
- An `undefined`/`null` entry in `updates` or `colorUpdates` keeps the default.
|
|
136
|
+
- The `custom` league builds a surface from its all-zero defaults in every sport. sportyR 2.4.0 builds it for every sport but football, which errors in `seq()`.
|
|
137
|
+
- A `custom` league with empty native units (every sport except volleyball, whose `custom` is in metres) reads them as feet, so `units` converts it. sportyR 2.4.0 stops instead and asks for the units to be set first (e.g. `geom_curling("custom", sheet_units = "m")`).
|
|
138
|
+
- Tennis accepts R's misspelt receiving-half keys (`receivicehalf`, `receivice_half`, `receivice half`) and the correct spellings (`receivehalf`, `receive_half`, `receive half`); R has only the misspelling and draws the full court for the correct one.
|
|
139
|
+
- Lacrosse: the three parameters R reads but no league defines (`center_face_off_marker_radius`, `corner_face_off_marker_bar_width`, `corner_face_off_marker_bar_length`) are fixed at 0 as R draws them, and `updates` cannot set them.
|
|
140
|
+
- Text features carry a `fitBox` (the ggfittext box) that the SVG renderer fits by height only.
|
|
141
|
+
|
|
142
|
+
## **Performance**
|
|
143
|
+
|
|
144
|
+
Local baseline from `test/perf.test.ts` (median of 10 runs after 5 warm-ups; the test asserts build < 50 ms and `toSVG` < 20 ms and is opt-in: `SDV_PERF_TESTS=1 pnpm vitest run perf`, skipped otherwise because the budgets are absolute and shared runners are noisy — these numbers are documented, not gated). Measured 2026-10-07 on a 12th Gen Intel Core i9-12900K, Node v24.15.0:
|
|
145
|
+
|
|
146
|
+
| Operation | Median |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| `basketballCourt("nba", { arcResolution: 200 })` | 3.1 ms |
|
|
149
|
+
| `toSVG(scene)` | 7.6 ms |
|
|
150
|
+
| `toSVG(scene, { arcs: "svg" })` | 1.8 ms |
|
|
151
|
+
|
|
152
|
+
Across three runs the medians ranged 2.5–3.2 ms (build), 4.8–8.0 ms (`toSVG`) and 0.8–1.8 ms (`arcs: "svg"`).
|
|
153
|
+
|
|
154
|
+
Three test files are gated: `canvas.render.test.ts` runs wherever `@napi-rs/canvas` installs (CI included); `svg-arcs.render.test.ts` rasterizes both path variants through resvg and needs `SDV_RENDER_TESTS=1`; `perf.test.ts` needs `SDV_PERF_TESTS=1`.
|
|
155
|
+
|
|
156
|
+
## **Roadmap**
|
|
157
|
+
|
|
158
|
+
3D surfaces. The `elevation` and `height` hints a `Scene` would carry are designed in the
|
|
159
|
+
[3D contract](https://github.com/sportsdataverse/sdvplot-js/blob/main/docs/3d-contract.md), for a future
|
|
160
|
+
`@sportsdataverse/sporty-3d`; the renderers here ignore them and draw flat.
|
|
161
|
+
|
|
162
|
+
## **Documentation**
|
|
163
|
+
|
|
164
|
+
The [**sdvplot-js** documentation website](https://plot.sportsdataverse.org) has the
|
|
165
|
+
[sporty gallery](https://plot.sportsdataverse.org/gallery/sporty/), the [notebooks](https://plot.sportsdataverse.org/notebooks/) and the
|
|
166
|
+
[sporty API reference](https://plot.sportsdataverse.org/api/sporty/), one page per subpath, plus:
|
|
167
|
+
|
|
168
|
+
**Guides:**
|
|
169
|
+
[Playing surfaces](https://plot.sportsdataverse.org/guides/surfaces) ·
|
|
170
|
+
[Shot charts](https://plot.sportsdataverse.org/guides/shot-charts) ·
|
|
171
|
+
[D3](https://plot.sportsdataverse.org/guides/d3) ·
|
|
172
|
+
[Node and SSR](https://plot.sportsdataverse.org/guides/node-ssr) ·
|
|
173
|
+
[From sportyR](https://plot.sportsdataverse.org/guides/migrating)
|
|
174
|
+
|
|
175
|
+
**Surfaces by sport:**
|
|
176
|
+
[Basketball](https://plot.sportsdataverse.org/examples/basketball) ·
|
|
177
|
+
[Football](https://plot.sportsdataverse.org/examples/football) ·
|
|
178
|
+
[Hockey](https://plot.sportsdataverse.org/examples/hockey)
|
|
179
|
+
|
|
180
|
+
The [repository README](https://github.com/sportsdataverse/sdvplot-js#readme) covers all three packages and development.
|
|
181
|
+
|
|
182
|
+
## **Logos, trademarks and data**
|
|
183
|
+
|
|
184
|
+
The surface geometry and the surface-dimensions data are ported from
|
|
185
|
+
[sportyR](https://github.com/sportsdataverse/sportyR) 2.4.0 (Ross Drucker) and
|
|
186
|
+
[sportypy](https://sportypy.sportsdataverse.org/); by agreement between the SportsDataverse maintainers the ported
|
|
187
|
+
material is distributed here under MIT. League names identify the rule sets the dimensions follow; sporty draws no
|
|
188
|
+
league or team marks.
|
|
189
|
+
|
|
190
|
+
The [MIT license](https://github.com/sportsdataverse/sdvplot-js/blob/main/LICENSE) covers the code; team data belongs to its respective owners and sources.
|
|
191
|
+
[NOTICE.md](https://github.com/sportsdataverse/sdvplot-js/blob/main/packages/sporty/NOTICE.md) records the ported sportyR/sportypy material and its licences.
|
|
192
|
+
|
|
193
|
+
## **The SportsDataverse**
|
|
194
|
+
|
|
195
|
+
`@sportsdataverse/sporty` is one of the three [sdvplot-js](https://github.com/sportsdataverse/sdvplot-js#readme) packages, with [`@sportsdataverse/sdvplot`](https://github.com/sportsdataverse/sdvplot-js/tree/main/packages/sdvplot#readme) and [`@sportsdataverse/sdvtables`](https://github.com/sportsdataverse/sdvplot-js/tree/main/packages/sdvtables#readme).
|
|
196
|
+
|
|
197
|
+
| Package | Sport / Scope |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| [**sdvplot**](https://sdvplot.sportsdataverse.org/) | The Python package sdvplot-js ports |
|
|
200
|
+
| [**sdvplotR**](https://sdvplotR.sportsdataverse.org/) | The R package sdvplot mirrors |
|
|
201
|
+
| [**sportyR**](https://github.com/sportsdataverse/sportyR) · [**sportypy**](https://sportypy.sportsdataverse.org/) | Playing-surface plots for R and Python, which `@sportsdataverse/sporty` ports |
|
|
202
|
+
| [**sportsdataverse.js**](https://js.sportsdataverse.org/) | SportsDataverse data for Node.js and TypeScript |
|
|
203
|
+
| [**sportsdataverse-py**](https://py.sportsdataverse.org/) | SportsDataverse data for Python: NFL, CFB, NBA, WNBA, MBB, WBB, MLB, NHL, PWHL, soccer and more |
|
|
204
|
+
| [**sportsdataverse-R**](https://r.sportsdataverse.org/) | The R packages: hoopR, wehoop, cfbfastR, fastRhockey, baseballr and more |
|
|
205
|
+
|
|
206
|
+
See the full ecosystem at [sportsdataverse.org](https://sportsdataverse.org/).
|
|
207
|
+
|
|
208
|
+
## **Follow the [SportsDataverse](https://x.com/SportsDataverse) on X and star this repo**
|
|
209
|
+
|
|
210
|
+
[](https://x.com/SportsDataverse)
|
|
211
|
+
[](https://github.com/sportsdataverse/sdvplot-js/stargazers)
|
|
212
|
+
|
|
213
|
+
## **Our Authors**
|
|
214
|
+
|
|
215
|
+
- [Saiem Gilani](https://x.com/saiemgilani)
|
|
216
|
+
<a href="https://x.com/saiemgilani" target="blank"><img src="https://img.shields.io/twitter/follow/saiemgilani?color=blue&label=%40saiemgilani&logo=x&style=for-the-badge" alt="@saiemgilani" /></a>
|
|
217
|
+
<a href="https://github.com/saiemgilani" target="blank"><img src="https://img.shields.io/github/followers/saiemgilani?color=eee&logo=Github&style=for-the-badge" alt="@saiemgilani" /></a>
|
|
218
|
+
|
|
219
|
+
## **Citations**
|
|
220
|
+
|
|
221
|
+
To cite [**`@sportsdataverse/sporty`**](https://plot.sportsdataverse.org) in publications, cite sdvplot-js:
|
|
222
|
+
|
|
223
|
+
BibTeX Citation
|
|
224
|
+
|
|
225
|
+
```bibtex
|
|
226
|
+
@misc{gilani_2026_sdvplot_js,
|
|
227
|
+
author = {Gilani, Saiem},
|
|
228
|
+
title = {sdvplot-js: Team identity, colors, logos, surfaces and tables for JavaScript and TypeScript plots},
|
|
229
|
+
url = {https://plot.sportsdataverse.org},
|
|
230
|
+
year = {2026}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## **License**
|
|
235
|
+
|
|
236
|
+
MIT. See [NOTICE.md](https://github.com/sportsdataverse/sdvplot-js/blob/main/packages/sporty/NOTICE.md) for the ported sportyR/sportypy material.
|
package/dist/canvas.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { C as Color, S as Scene } from './scene-D5Jc19yu.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Browser `CanvasRenderingContext2D` or `@napi-rs/canvas` `SKRSContext2D`; only the members below are used.
|
|
5
|
+
* Structural and DOM-type-free (`fillStyle`/`strokeStyle` are `unknown`, `textAlign`/`textBaseline` are `string`),
|
|
6
|
+
* so neither consumer needs the DOM lib.
|
|
7
|
+
*/
|
|
8
|
+
interface SceneCanvasContext {
|
|
9
|
+
save(): void;
|
|
10
|
+
restore(): void;
|
|
11
|
+
transform(a: number, b: number, c: number, d: number, e: number, f: number): void;
|
|
12
|
+
beginPath(): void;
|
|
13
|
+
moveTo(x: number, y: number): void;
|
|
14
|
+
lineTo(x: number, y: number): void;
|
|
15
|
+
closePath(): void;
|
|
16
|
+
fill(): void;
|
|
17
|
+
stroke(): void;
|
|
18
|
+
fillRect(x: number, y: number, w: number, h: number): void;
|
|
19
|
+
fillText(text: string, x: number, y: number): void;
|
|
20
|
+
measureText(text: string): {
|
|
21
|
+
width: number;
|
|
22
|
+
};
|
|
23
|
+
translate(x: number, y: number): void;
|
|
24
|
+
rotate(rad: number): void;
|
|
25
|
+
scale(x: number, y: number): void;
|
|
26
|
+
fillStyle: unknown;
|
|
27
|
+
strokeStyle: unknown;
|
|
28
|
+
lineWidth: number;
|
|
29
|
+
font: string;
|
|
30
|
+
textAlign: string;
|
|
31
|
+
textBaseline: string;
|
|
32
|
+
}
|
|
33
|
+
interface DrawSceneOptions {
|
|
34
|
+
width?: number;
|
|
35
|
+
height?: number;
|
|
36
|
+
background?: Color;
|
|
37
|
+
/** Pixels per scene unit; when given, the box size follows from it. */
|
|
38
|
+
scale?: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Paints `scene` at the origin of `ctx` and returns the size drawn, in the caller's current units.
|
|
42
|
+
* Give `width` (default 800, like `toSVG`; the height follows the aspect), `width` and `height` (the scene
|
|
43
|
+
* is fitted into the box, like `toSVG`'s viewBox, and the returned size is the fitted one), or `scale`
|
|
44
|
+
* (px per unit; the box size follows). The scene transform composes with the caller's (a HiDPI
|
|
45
|
+
* `ctx.scale(dpr, dpr)` survives) and the context is restored afterwards.
|
|
46
|
+
* Throws `InputError` when the scene's bbox is empty (the `custom` leagues of some sports).
|
|
47
|
+
*/
|
|
48
|
+
declare function drawScene(ctx: SceneCanvasContext, scene: Scene, opts?: DrawSceneOptions): {
|
|
49
|
+
width: number;
|
|
50
|
+
height: number;
|
|
51
|
+
scale: number;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export { type DrawSceneOptions, type SceneCanvasContext, drawScene };
|
package/dist/canvas.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { InputError } from './chunk-23Y47RKN.js';
|
|
2
|
+
import { isVisiblePolygon, hidden, isVisibleText } from './chunk-DRH5NZI4.js';
|
|
3
|
+
|
|
4
|
+
// src/canvas.ts
|
|
5
|
+
function drawScene(ctx, scene, opts = {}) {
|
|
6
|
+
const [x0, y0, x1, y1] = scene.bbox;
|
|
7
|
+
const bw = x1 - x0;
|
|
8
|
+
const bh = y1 - y0;
|
|
9
|
+
if (!(bw > 0 && bh > 0 && Number.isFinite(bw + bh)))
|
|
10
|
+
throw new InputError(
|
|
11
|
+
`${scene.sport} "${scene.league}" has an empty bbox [${scene.bbox.join(", ")}]; nothing to draw`
|
|
12
|
+
);
|
|
13
|
+
const box = opts.width ?? 800;
|
|
14
|
+
const scale = opts.scale ?? (opts.height === void 0 ? box / bw : Math.min(box / bw, opts.height / bh));
|
|
15
|
+
const width = Math.round(bw * scale);
|
|
16
|
+
const height = Math.round(bh * scale);
|
|
17
|
+
ctx.save();
|
|
18
|
+
ctx.transform(scale, 0, 0, -scale, -x0 * scale, y1 * scale);
|
|
19
|
+
const bg = opts.background ?? scene.background;
|
|
20
|
+
if (bg !== void 0) {
|
|
21
|
+
ctx.fillStyle = bg;
|
|
22
|
+
ctx.fillRect(x0, y0, bw, bh);
|
|
23
|
+
}
|
|
24
|
+
const sorted = [...scene.features].sort((a, b) => a.zIndex - b.zIndex);
|
|
25
|
+
for (const f of sorted) {
|
|
26
|
+
if (f.kind === "polygon") {
|
|
27
|
+
if (!isVisiblePolygon(f) || f.points.length < 2) continue;
|
|
28
|
+
ctx.beginPath();
|
|
29
|
+
f.points.forEach(([x, y], i) => {
|
|
30
|
+
if (i === 0) ctx.moveTo(x, y);
|
|
31
|
+
else ctx.lineTo(x, y);
|
|
32
|
+
});
|
|
33
|
+
ctx.closePath();
|
|
34
|
+
if (!hidden(f.fill)) {
|
|
35
|
+
ctx.fillStyle = f.fill;
|
|
36
|
+
ctx.fill();
|
|
37
|
+
}
|
|
38
|
+
if (f.stroke !== void 0) {
|
|
39
|
+
ctx.strokeStyle = f.stroke;
|
|
40
|
+
ctx.lineWidth = 1 / scale;
|
|
41
|
+
ctx.stroke();
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
if (!isVisibleText(f)) continue;
|
|
45
|
+
const [fw, fh] = f.fitBox;
|
|
46
|
+
ctx.save();
|
|
47
|
+
ctx.translate(f.x, f.y);
|
|
48
|
+
ctx.rotate(f.rotation * Math.PI / 180);
|
|
49
|
+
ctx.scale(1, -1);
|
|
50
|
+
ctx.fillStyle = f.fill;
|
|
51
|
+
ctx.font = `${fh}px ${f.fontFamily}`;
|
|
52
|
+
const w = ctx.measureText(f.text).width;
|
|
53
|
+
if (w > fw) ctx.font = `${fh * fw / w}px ${f.fontFamily}`;
|
|
54
|
+
ctx.textAlign = "center";
|
|
55
|
+
ctx.textBaseline = "middle";
|
|
56
|
+
ctx.fillText(f.text, 0, 0);
|
|
57
|
+
ctx.restore();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
ctx.restore();
|
|
61
|
+
return { width, height, scale };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { drawScene };
|
|
65
|
+
//# sourceMappingURL=canvas.js.map
|
|
66
|
+
//# sourceMappingURL=canvas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/canvas.ts"],"names":[],"mappings":"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","file":"canvas.js","sourcesContent":["import { InputError } from \"./errors.js\";\nimport { type Color, type Feature, type Scene, hidden, isVisiblePolygon, isVisibleText } from \"./scene.js\";\n\n/**\n * Browser `CanvasRenderingContext2D` or `@napi-rs/canvas` `SKRSContext2D`; only the members below are used.\n * Structural and DOM-type-free (`fillStyle`/`strokeStyle` are `unknown`, `textAlign`/`textBaseline` are `string`),\n * so neither consumer needs the DOM lib.\n */\nexport interface SceneCanvasContext {\n save(): void;\n restore(): void;\n transform(a: number, b: number, c: number, d: number, e: number, f: number): void;\n beginPath(): void;\n moveTo(x: number, y: number): void;\n lineTo(x: number, y: number): void;\n closePath(): void;\n fill(): void;\n stroke(): void;\n fillRect(x: number, y: number, w: number, h: number): void;\n fillText(text: string, x: number, y: number): void;\n measureText(text: string): { width: number };\n translate(x: number, y: number): void;\n rotate(rad: number): void;\n scale(x: number, y: number): void;\n fillStyle: unknown;\n strokeStyle: unknown;\n lineWidth: number;\n font: string;\n textAlign: string;\n textBaseline: string;\n}\n\nexport interface DrawSceneOptions {\n width?: number;\n height?: number;\n background?: Color;\n /** Pixels per scene unit; when given, the box size follows from it. */\n scale?: number;\n}\n\n/**\n * Paints `scene` at the origin of `ctx` and returns the size drawn, in the caller's current units.\n * Give `width` (default 800, like `toSVG`; the height follows the aspect), `width` and `height` (the scene\n * is fitted into the box, like `toSVG`'s viewBox, and the returned size is the fitted one), or `scale`\n * (px per unit; the box size follows). The scene transform composes with the caller's (a HiDPI\n * `ctx.scale(dpr, dpr)` survives) and the context is restored afterwards.\n * Throws `InputError` when the scene's bbox is empty (the `custom` leagues of some sports).\n */\nexport function drawScene(\n ctx: SceneCanvasContext,\n scene: Scene,\n opts: DrawSceneOptions = {},\n): { width: number; height: number; scale: number } {\n const [x0, y0, x1, y1] = scene.bbox;\n const bw = x1 - x0;\n const bh = y1 - y0;\n if (!(bw > 0 && bh > 0 && Number.isFinite(bw + bh)))\n throw new InputError(\n `${scene.sport} \"${scene.league}\" has an empty bbox [${scene.bbox.join(\", \")}]; nothing to draw`,\n );\n const box = opts.width ?? 800;\n const scale = opts.scale ?? (opts.height === undefined ? box / bw : Math.min(box / bw, opts.height / bh));\n const width = Math.round(bw * scale);\n const height = Math.round(bh * scale);\n ctx.save();\n ctx.transform(scale, 0, 0, -scale, -x0 * scale, y1 * scale);\n const bg = opts.background ?? scene.background;\n if (bg !== undefined) {\n ctx.fillStyle = bg;\n ctx.fillRect(x0, y0, bw, bh);\n }\n const sorted: Feature[] = [...scene.features].sort((a, b) => a.zIndex - b.zIndex); // stable\n for (const f of sorted) {\n if (f.kind === \"polygon\") {\n if (!isVisiblePolygon(f) || f.points.length < 2) continue;\n ctx.beginPath();\n f.points.forEach(([x, y], i) => {\n if (i === 0) ctx.moveTo(x, y);\n else ctx.lineTo(x, y);\n });\n ctx.closePath();\n if (!hidden(f.fill)) {\n ctx.fillStyle = f.fill;\n ctx.fill();\n }\n if (f.stroke !== undefined) {\n ctx.strokeStyle = f.stroke;\n ctx.lineWidth = 1 / scale;\n ctx.stroke();\n }\n } else {\n if (!isVisibleText(f)) continue;\n const [fw, fh] = f.fitBox;\n ctx.save();\n ctx.translate(f.x, f.y);\n ctx.rotate((f.rotation * Math.PI) / 180);\n ctx.scale(1, -1);\n ctx.fillStyle = f.fill;\n ctx.font = `${fh}px ${f.fontFamily}`;\n const w = ctx.measureText(f.text).width;\n if (w > fw) ctx.font = `${(fh * fw) / w}px ${f.fontFamily}`;\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"middle\";\n ctx.fillText(f.text, 0, 0);\n ctx.restore();\n }\n }\n ctx.restore();\n return { width, height, scale };\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var SportyError = class extends Error {
|
|
3
|
+
constructor(message, options) {
|
|
4
|
+
super(message, options);
|
|
5
|
+
this.name = new.target.name;
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
var UnknownLeagueError = class extends SportyError {
|
|
9
|
+
};
|
|
10
|
+
var UnknownDisplayRangeError = class extends SportyError {
|
|
11
|
+
};
|
|
12
|
+
var UnknownUnitError = class extends SportyError {
|
|
13
|
+
};
|
|
14
|
+
var InputError = class extends SportyError {
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export { InputError, SportyError, UnknownDisplayRangeError, UnknownLeagueError, UnknownUnitError };
|
|
18
|
+
//# sourceMappingURL=chunk-23Y47RKN.js.map
|
|
19
|
+
//# sourceMappingURL=chunk-23Y47RKN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts"],"names":[],"mappings":";AAAO,IAAM,WAAA,GAAN,cAA0B,KAAA,CAAM;AAAA,EACrC,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,kBAAA,GAAN,cAAiC,WAAA,CAAY;AAAC;AAC9C,IAAM,wBAAA,GAAN,cAAuC,WAAA,CAAY;AAAC;AACpD,IAAM,gBAAA,GAAN,cAA+B,WAAA,CAAY;AAAC;AAE5C,IAAM,UAAA,GAAN,cAAyB,WAAA,CAAY;AAAC","file":"chunk-23Y47RKN.js","sourcesContent":["export class SportyError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n }\n}\nexport class UnknownLeagueError extends SportyError {}\nexport class UnknownDisplayRangeError extends SportyError {}\nexport class UnknownUnitError extends SportyError {}\n/** A scene or option that cannot be drawn (e.g. a `custom` league with an empty bbox). */\nexport class InputError extends SportyError {}\n"]}
|