@johnmorrisdotca/kyogijo-stadiums 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/NOTICE.md +179 -3
- package/README.md +43 -11
- package/dist/arenas-entry.d.ts +4 -0
- package/dist/arenas-entry.js +3 -0
- package/dist/arenas.d.ts +125 -0
- package/dist/arenas.data.d.ts +2 -0
- package/dist/arenas.data.js +3763 -0
- package/dist/arenas.js +72 -0
- package/dist/atlas-entry.d.ts +3 -1
- package/dist/atlas-entry.js +3 -2
- package/dist/atlas.d.ts +37 -19
- package/dist/atlas.js +92 -7
- package/dist/background.js +2 -1
- package/dist/budo/describe.d.ts +13 -0
- package/dist/budo/describe.js +120 -0
- package/dist/budo/dojo.api.d.ts +64 -0
- package/dist/budo/dojo.api.js +46 -0
- package/dist/budo/dojo.d.ts +72 -0
- package/dist/budo/dojo.js +300 -0
- package/dist/budo/judo.d.ts +61 -0
- package/dist/budo/judo.js +109 -0
- package/dist/budo/karate.d.ts +29 -0
- package/dist/budo/karate.js +80 -0
- package/dist/budo/kendo.d.ts +18 -0
- package/dist/budo/kendo.js +44 -0
- package/dist/budo/kimarite.d.ts +19 -0
- package/dist/budo/kimarite.js +33 -0
- package/dist/budo/kit.d.ts +16 -0
- package/dist/budo/kit.js +24 -0
- package/dist/budo/kyudo.d.ts +28 -0
- package/dist/budo/kyudo.js +66 -0
- package/dist/budo/looks.d.ts +28 -0
- package/dist/budo/looks.js +58 -0
- package/dist/budo/parts.d.ts +32 -0
- package/dist/budo/parts.js +10 -0
- package/dist/budo/plan.d.ts +64 -0
- package/dist/budo/plan.js +139 -0
- package/dist/budo/room.d.ts +133 -0
- package/dist/budo/room.js +111 -0
- package/dist/budo/sumo.d.ts +31 -0
- package/dist/budo/sumo.js +71 -0
- package/dist/budo/tatami.d.ts +55 -0
- package/dist/budo/tatami.js +156 -0
- package/dist/budo/types.d.ts +192 -0
- package/dist/budo/types.js +2 -0
- package/dist/budo-entry.d.ts +22 -0
- package/dist/budo-entry.js +13 -0
- package/dist/budo.board.d.ts +28 -0
- package/dist/budo.board.js +81 -0
- package/dist/budo.score.d.ts +18 -0
- package/dist/budo.score.js +52 -0
- package/dist/budo.style.d.ts +6 -0
- package/dist/budo.style.js +67 -0
- package/dist/charts.baseball.js +1 -1
- package/dist/charts.js +4 -4
- package/dist/charts.soccer.js +3 -3
- package/dist/charts.types.d.ts +10 -0
- package/dist/colour.d.ts +4 -4
- package/dist/colour.js +4 -4
- package/dist/crowd.d.ts +11 -1
- package/dist/crowd.draw.js +15 -9
- package/dist/crowd.js +17 -2
- package/dist/draw.js +156 -31
- package/dist/element-entry.d.ts +2 -0
- package/dist/element-entry.js +1 -0
- package/dist/element.d.ts +2 -2
- package/dist/element.js +2 -1
- package/dist/emblems-source.d.ts +50 -0
- package/dist/emblems-source.js +56 -0
- package/dist/eras.d.ts +3 -1
- package/dist/eras.js +8 -3
- package/dist/features.js +73 -0
- package/dist/field.data.d.ts +16 -0
- package/dist/field.data.js +50 -0
- package/dist/fields.kit.d.ts +2 -2
- package/dist/fields.types.d.ts +9 -3
- package/dist/formations.js +18 -7
- package/dist/formations.sports.d.ts +8 -0
- package/dist/formations.sports.js +251 -0
- package/dist/frames.providers.js +9 -1
- package/dist/halls.d.ts +65 -0
- package/dist/halls.js +122 -0
- package/dist/heat.js +17 -10
- package/dist/heat.zones.d.ts +4 -2
- package/dist/heat.zones.js +55 -3
- package/dist/homeside.js +7 -0
- package/dist/index.d.ts +15 -3
- package/dist/index.js +8 -1
- package/dist/layers-entry.d.ts +2 -1
- package/dist/layers-entry.js +1 -0
- package/dist/layers.d.ts +2 -1
- package/dist/layers.env.d.ts +10 -1
- package/dist/layers.env.js +33 -3
- package/dist/layers.js +30 -11
- package/dist/layers.types.d.ts +9 -2
- package/dist/leagues/budo.d.ts +196 -0
- package/dist/leagues/budo.js +105 -0
- package/dist/leagues/hockey.js +41 -19
- package/dist/leagues/sources.js +21 -0
- package/dist/leagues/types.d.ts +1 -1
- package/dist/leagues.d.ts +4 -1
- package/dist/leagues.js +102 -2
- package/dist/lineup.codes.d.ts +23 -0
- package/dist/lineup.codes.js +122 -0
- package/dist/logo.d.ts +25 -0
- package/dist/logo.js +47 -0
- package/dist/outline.d.ts +1 -1
- package/dist/outline.js +17 -3
- package/dist/pageant.js +7 -3
- package/dist/pointer-entry.d.ts +2 -0
- package/dist/pointer-entry.js +2 -0
- package/dist/pointer.d.ts +113 -0
- package/dist/pointer.js +151 -0
- package/dist/pool.d.ts +7 -7
- package/dist/pool.figures.d.ts +57 -55
- package/dist/pool.figures.js +59 -55
- package/dist/pool.js +7 -7
- package/dist/pool.labels.js +2 -1
- package/dist/ranges.names.js +6 -0
- package/dist/road.draw.js +3 -2
- package/dist/road.js +2 -1
- package/dist/road.types.d.ts +3 -0
- package/dist/rules.d.ts +138 -0
- package/dist/rules.js +511 -0
- package/dist/rules.types.d.ts +4 -0
- package/dist/rules.types.js +64 -0
- package/dist/score.d.ts +3 -0
- package/dist/score.js +23 -1
- package/dist/situation.d.ts +135 -0
- package/dist/situation.draw.d.ts +34 -0
- package/dist/situation.draw.js +202 -0
- package/dist/situation.js +337 -0
- package/dist/situation.style.d.ts +2 -0
- package/dist/situation.style.js +45 -0
- package/dist/situation.types.d.ts +77 -0
- package/dist/situation.types.js +3 -0
- package/dist/situation.widgets.d.ts +39 -0
- package/dist/situation.widgets.js +129 -0
- package/dist/strings.d.ts +175 -1
- package/dist/strings.js +364 -20
- package/dist/style.js +9 -2
- package/dist/surfaces/artistic.d.ts +3 -3
- package/dist/surfaces/artistic.js +3 -3
- package/dist/surfaces/baseball.js +2 -0
- package/dist/surfaces/basketball.js +1 -1
- package/dist/surfaces/budo.d.ts +6 -0
- package/dist/surfaces/budo.js +104 -0
- package/dist/surfaces/dive.d.ts +3 -3
- package/dist/surfaces/dive.js +3 -3
- package/dist/surfaces/football.js +7 -0
- package/dist/surfaces/hockey.js +3 -2
- package/dist/surfaces/polo.d.ts +4 -4
- package/dist/surfaces/polo.js +5 -5
- package/dist/surfaces/pool.d.ts +1 -1
- package/dist/surfaces/pool.js +3 -3
- package/dist/surfaces/surface.d.ts +32 -0
- package/dist/surfaces/venue.d.ts +1 -1
- package/dist/surfaces/venue.js +1 -1
- package/dist/target-entry.d.ts +2 -0
- package/dist/target-entry.js +2 -0
- package/dist/target.d.ts +200 -0
- package/dist/target.js +525 -0
- package/dist/team.common.d.ts +1 -1
- package/dist/team.common.js +3 -3
- package/dist/team.formation.d.ts +3 -1
- package/dist/team.formation.js +3 -3
- package/dist/team.layers.d.ts +2 -2
- package/dist/team.layers.js +4 -1
- package/dist/team.lineup.d.ts +27 -0
- package/dist/team.lineup.js +207 -0
- package/dist/team.network.js +6 -5
- package/dist/team.shape.js +5 -5
- package/dist/team.types.d.ts +40 -1
- package/dist/tenants.data.js +848 -0
- package/dist/turn-entry.d.ts +2 -2
- package/dist/turn-entry.js +1 -1
- package/dist/turn.camera.d.ts +16 -3
- package/dist/turn.camera.js +53 -7
- package/dist/turn.dom.js +11 -9
- package/dist/turn.kit.d.ts +2 -2
- package/dist/turn.style.js +4 -3
- package/dist/types.d.ts +43 -7
- package/dist/units.js +7 -0
- package/dist/uprights.budo.d.ts +17 -0
- package/dist/uprights.budo.js +87 -0
- package/dist/uprights.js +10 -1
- package/dist/uprights.pools.js +4 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/zones-entry.d.ts +2 -0
- package/dist/zones-entry.js +2 -0
- package/package.json +39 -3
package/dist/charts.baseball.js
CHANGED
|
@@ -76,7 +76,7 @@ export function mlbSprayChart(balls, options = {}) {
|
|
|
76
76
|
if (at !== undefined)
|
|
77
77
|
[x, y] = toFrame === undefined ? at : toFrame([at])[0];
|
|
78
78
|
}
|
|
79
|
-
return clean({ x: x ?? null, y: y ?? null, outcome: ballType(b.type), value: b.value, team: b.team, title: b.title });
|
|
79
|
+
return clean({ id: b.id, x: x ?? null, y: y ?? null, outcome: ballType(b.type), value: b.value, team: b.team, title: b.title });
|
|
80
80
|
});
|
|
81
81
|
const { counts, placed: count, skipped } = countOutcomes(rows);
|
|
82
82
|
// The fence of the park in that season, and the balls that came down beyond it: each ball's distance and its angle from dead centre, from the surface's own feet.
|
package/dist/charts.js
CHANGED
|
@@ -218,7 +218,7 @@ export function nbaShotChart(shots, options = {}) {
|
|
|
218
218
|
const end = options.end === "left" ? "left" : "right";
|
|
219
219
|
// A shot chart puts every shot at the basket it was taken at; a frame fixed to the court says where each one is.
|
|
220
220
|
const shooter = frame.ends === "shooter";
|
|
221
|
-
const data = shots.map((s) => clean({ x: s.x, y: s.y, outcome: s.made === undefined ? s.outcome : s.made ? "made" : "missed", value: s.value, team: s.team, title: s.title }));
|
|
221
|
+
const data = shots.map((s) => clean({ id: s.id, x: s.x, y: s.y, outcome: s.made === undefined ? s.outcome : s.made ? "made" : "missed", value: s.value, team: s.team, title: s.title }));
|
|
222
222
|
const { counts, placed: count, skipped } = countOutcomes(data);
|
|
223
223
|
const entries = [{ label: w.made, outcome: "made" }, { label: w.missed, outcome: "missed" }];
|
|
224
224
|
const o = drawingOptions("nba", options, w.titleNba);
|
|
@@ -330,7 +330,7 @@ export function nhlShotMap(shots, options = {}) {
|
|
|
330
330
|
const frame = frameOf(options.from, "nhl-api");
|
|
331
331
|
const o = drawingOptions("nhl", options, w.titleNhl);
|
|
332
332
|
const folded = options.fold === true;
|
|
333
|
-
const given = shots.map((s) => clean({ x: s.x, y: s.y, outcome: hockeyOutcome(s), value: s.xg, team: s.team, title: s.title }));
|
|
333
|
+
const given = shots.map((s) => clean({ id: s.id, x: s.x, y: s.y, outcome: hockeyOutcome(s), value: s.xg, team: s.team, title: s.title }));
|
|
334
334
|
const data = folded ? foldToRight(given, frame, o) : given;
|
|
335
335
|
const { counts, placed: count, skipped } = countOutcomes(data);
|
|
336
336
|
const hasXg = data.some((d) => isNum(d.value));
|
|
@@ -441,7 +441,7 @@ export function nflPassChart(passes, options = {}) {
|
|
|
441
441
|
const data = rows.map(({ p, outcome }, i) => {
|
|
442
442
|
const place_ = at[i];
|
|
443
443
|
const air = isNum(p.airYards) ? p.airYards : place_ === undefined ? undefined : round(inYards(place_[0] - losFeet), 1);
|
|
444
|
-
return clean({ x: p.x, y: p.y, outcome, value: air, team: p.team, title: p.title });
|
|
444
|
+
return clean({ id: p.id, x: p.x, y: p.y, outcome, value: air, team: p.team, title: p.title });
|
|
445
445
|
});
|
|
446
446
|
const { counts, placed: count, skipped } = countOutcomes(data);
|
|
447
447
|
const entries = [["complete", w.complete], ["incomplete", w.incomplete], ["touchdown", w.touchdown], ["interception", w.interception]].filter(([k]) => counts.has(k) || counts.size === 0 || ["complete", "incomplete"].includes(k)).map(([outcome, label]) => ({ label, outcome }));
|
|
@@ -480,7 +480,7 @@ export function nflPassChart(passes, options = {}) {
|
|
|
480
480
|
layers.push(line);
|
|
481
481
|
}
|
|
482
482
|
if (options.arrows === true) {
|
|
483
|
-
const arrows = rows.filter(({ p }) => isNum(p.fromX) && isNum(p.fromY) && isNum(p.x) && isNum(p.y)).map(({ p, outcome }) => clean({ x: p.fromX, y: p.fromY, x2: p.x, y2: p.y, outcome, team: p.team, title: p.title }));
|
|
483
|
+
const arrows = rows.filter(({ p }) => isNum(p.fromX) && isNum(p.fromY) && isNum(p.x) && isNum(p.y)).map(({ p, outcome }) => clean({ id: p.id, x: p.fromX, y: p.fromY, x2: p.x, y2: p.y, outcome, team: p.team, title: p.title }));
|
|
484
484
|
layers.push(clean({ kind: "arrows", id: "arrows", name: w.passes, ...where, data: arrows, style: { width: 0.7, opacity: 0.7, curve: 0.08, outcomes: { touchdown: {}, interception: {}, complete: {}, incomplete: { hollow: true } } } }));
|
|
485
485
|
}
|
|
486
486
|
const values = data.map((d) => d.value).filter(isNum);
|
package/dist/charts.soccer.js
CHANGED
|
@@ -65,7 +65,7 @@ export function soccerShotMap(shots, options = {}) {
|
|
|
65
65
|
});
|
|
66
66
|
const attacking = options.attacking === "left" ? "left" : undefined;
|
|
67
67
|
const where = clean({ from: fromOf(typeof provider === "object" ? provider : undefined, frame), attacking });
|
|
68
|
-
const data = shots.map((s) => clean({ x: s.x, y: s.y, outcome: outcomeOf(s), value: s.xg, team: s.team, title: s.title }));
|
|
68
|
+
const data = shots.map((s) => clean({ id: s.id, x: s.x, y: s.y, outcome: outcomeOf(s), value: s.xg, team: s.team, title: s.title }));
|
|
69
69
|
const { counts, placed: count, skipped } = countOutcomes(data);
|
|
70
70
|
const hasXg = data.some((d) => isNum(d.value));
|
|
71
71
|
const sized = options.xg === false ? false : hasXg;
|
|
@@ -140,7 +140,7 @@ export function soccerGoalMouth(shots, options = {}) {
|
|
|
140
140
|
};
|
|
141
141
|
const id = options.id === undefined ? undefined : options.id;
|
|
142
142
|
const surface = { sport: "soccer", pitch: { length: viewW, width: viewH, unit: "m" }, units: "m", language, ...(id === undefined ? {} : { id }), margin: 0 };
|
|
143
|
-
const rows = shots.map((s) => clean({ x: isNum(s.goalY) ? round(s.goalY * k, 3) : null, y: isNum(s.goalZ) ? round(s.goalZ * k, 3) : null, outcome: outcomeOf(s), value: s.xg, team: s.team, title: s.title }));
|
|
143
|
+
const rows = shots.map((s) => clean({ id: s.id, x: isNum(s.goalY) ? round(s.goalY * k, 3) : null, y: isNum(s.goalZ) ? round(s.goalZ * k, 3) : null, outcome: outcomeOf(s), value: s.xg, team: s.team, title: s.title }));
|
|
144
144
|
const { counts, placed: placedCount, skipped } = countOutcomes(rows);
|
|
145
145
|
const hasXg = rows.some((r) => isNum(r.value));
|
|
146
146
|
const sized = options.xg === false ? false : options.xg === true || hasXg;
|
|
@@ -150,7 +150,7 @@ export function soccerGoalMouth(shots, options = {}) {
|
|
|
150
150
|
const style = { outcomes: LOOKS, opacity: 0.92, ...(sized ? { size: { domain: [0, xgMax], range: [0.25, 0.8] } } : { size: 0.45 }) };
|
|
151
151
|
const layer = clean({ kind: "points", id: "goal-shots", name: w.shots, description: describeCounts(entries, counts, all), from: frame, units: "m", data: rows, style, clip: "none" });
|
|
152
152
|
const layers = [layer];
|
|
153
|
-
const rendered = renderLayers(surface, layers, false);
|
|
153
|
+
const rendered = renderLayers(surface, layers, false, undefined, undefined, () => undefined);
|
|
154
154
|
const F = FEET_PER_METRE;
|
|
155
155
|
const x0 = ((viewW - goalW) / 2) * F;
|
|
156
156
|
const x1 = x0 + goalW * F;
|
package/dist/charts.types.d.ts
CHANGED
|
@@ -48,6 +48,8 @@ export interface ChartOptions {
|
|
|
48
48
|
}
|
|
49
49
|
/** A shot as stats.nba.com's shot chart gives it: `LOC_X` and `LOC_Y` and whether it went in. */
|
|
50
50
|
export interface NbaShot {
|
|
51
|
+
/** Your id for the shot, written on its mark as `data-mark-id`. */
|
|
52
|
+
id?: string | number;
|
|
51
53
|
x: number | null;
|
|
52
54
|
y: number | null;
|
|
53
55
|
/** True for a shot that went in. Or give `outcome`. */
|
|
@@ -98,6 +100,8 @@ export interface NbaTotals {
|
|
|
98
100
|
}
|
|
99
101
|
/** A shot as the NHL's play-by-play gives it. */
|
|
100
102
|
export interface NhlShot {
|
|
103
|
+
/** Your id for the shot, written on its mark as `data-mark-id`. */
|
|
104
|
+
id?: string | number;
|
|
101
105
|
x: number | null;
|
|
102
106
|
y: number | null;
|
|
103
107
|
/** `goal`, `saved` (a shot on goal), `missed`, `blocked`, or `shot` for an attempt of no known result. */
|
|
@@ -152,6 +156,8 @@ export interface NhlTotals {
|
|
|
152
156
|
}
|
|
153
157
|
/** A ball in play. Give `x` and `y` in the frame, or for Retrosheet a `zone` (the location code, `8D`). */
|
|
154
158
|
export interface Ball {
|
|
159
|
+
/** Your id for the shot, written on its mark as `data-mark-id`. */
|
|
160
|
+
id?: string | number;
|
|
155
161
|
x?: number | null;
|
|
156
162
|
y?: number | null;
|
|
157
163
|
/** A Retrosheet hit-location code in place of `x` and `y`. */
|
|
@@ -197,6 +203,8 @@ export interface MlbTotals {
|
|
|
197
203
|
}
|
|
198
204
|
/** A pass. The destination is `x` and `y`; the passer's place is `fromX` and `fromY` when you have it. */
|
|
199
205
|
export interface NflPass {
|
|
206
|
+
/** Your id for the shot, written on its mark as `data-mark-id`. */
|
|
207
|
+
id?: string | number;
|
|
200
208
|
/** Where the ball came down or was caught, in the frame. */
|
|
201
209
|
x: number | null;
|
|
202
210
|
y: number | null;
|
|
@@ -246,6 +254,8 @@ export interface NflTotals {
|
|
|
246
254
|
}
|
|
247
255
|
/** A shot. The place is in the provider's frame; `goalY` and `goalZ` say where it crossed the goal line. */
|
|
248
256
|
export interface SoccerShot {
|
|
257
|
+
/** Your id for the shot, written on its mark as `data-mark-id`. */
|
|
258
|
+
id?: string | number;
|
|
249
259
|
x: number | null;
|
|
250
260
|
y: number | null;
|
|
251
261
|
/** `goal`, `saved`, `blocked`, `missed` or `post`. */
|
package/dist/colour.d.ts
CHANGED
|
@@ -17,13 +17,13 @@ export declare function luminance(rgb: [number, number, number]): number;
|
|
|
17
17
|
/** White or near-black, whichever reads better on the colour. White when the colour cannot be read. */
|
|
18
18
|
export declare function inkOn(color: string | undefined): string;
|
|
19
19
|
/**
|
|
20
|
-
* A
|
|
20
|
+
* A picture's address as it may be written into an image's `href`, or `undefined` (what `safeLogo` checks after it has looked for an emblem's id). An `https:` or `http:` address, a
|
|
21
21
|
* relative path, an image `data:` address, or an SVG document as text (written as an image, which cannot run scripts).
|
|
22
22
|
* @example
|
|
23
23
|
* ```ts
|
|
24
|
-
* import {
|
|
25
|
-
* console.log(
|
|
24
|
+
* import { safePicture } from "@johnmorrisdotca/kyogijo-stadiums";
|
|
25
|
+
* console.log(safePicture("https://example.com/gulls.svg"), safePicture("javascript:alert(1)"));
|
|
26
26
|
* // https://example.com/gulls.svg undefined
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
|
-
export declare function
|
|
29
|
+
export declare function safePicture(value: unknown): string | undefined;
|
package/dist/colour.js
CHANGED
|
@@ -64,16 +64,16 @@ export function inkOn(color) {
|
|
|
64
64
|
return luminance(rgb) > 0.4 ? "#161a18" : "#ffffff";
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
|
-
* A
|
|
67
|
+
* A picture's address as it may be written into an image's `href`, or `undefined` (what `safeLogo` checks after it has looked for an emblem's id). An `https:` or `http:` address, a
|
|
68
68
|
* relative path, an image `data:` address, or an SVG document as text (written as an image, which cannot run scripts).
|
|
69
69
|
* @example
|
|
70
70
|
* ```ts
|
|
71
|
-
* import {
|
|
72
|
-
* console.log(
|
|
71
|
+
* import { safePicture } from "@johnmorrisdotca/kyogijo-stadiums";
|
|
72
|
+
* console.log(safePicture("https://example.com/gulls.svg"), safePicture("javascript:alert(1)"));
|
|
73
73
|
* // https://example.com/gulls.svg undefined
|
|
74
74
|
* ```
|
|
75
75
|
*/
|
|
76
|
-
export function
|
|
76
|
+
export function safePicture(value) {
|
|
77
77
|
if (typeof value !== "string")
|
|
78
78
|
return undefined;
|
|
79
79
|
const text = value.trim();
|
package/dist/crowd.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export type Outline = {
|
|
|
28
28
|
export interface StandRing {
|
|
29
29
|
/** `lower`, `upper`, `courtside`, `grandstand` or `bleachers`, with the tier's number where a ring has parts. */
|
|
30
30
|
id: string;
|
|
31
|
-
tier: "courtside" | "lower" | "upper" | "grandstand" | "bleachers" | "wall";
|
|
31
|
+
tier: "courtside" | "gallery" | "lower" | "upper" | "grandstand" | "bleachers" | "wall";
|
|
32
32
|
/** The band is `region` less `hole` (nothing for a polygon that is the band itself). */
|
|
33
33
|
region: Outline;
|
|
34
34
|
/** Further pieces of the band that are apart from `region` (a ballpark's bleachers either side of the batter's eye). */
|
|
@@ -90,6 +90,8 @@ export interface CrowdStands {
|
|
|
90
90
|
};
|
|
91
91
|
/** The seats of all the stands, from the model: what a density is a share of when no capacity is given. */
|
|
92
92
|
seats: number;
|
|
93
|
+
/** The stands are a hall's gallery (a dojo's balcony): a thin band close to the walls, plain seats, a few neutral people and no sides. */
|
|
94
|
+
gallery?: boolean;
|
|
93
95
|
}
|
|
94
96
|
/** The crowd options read: every part has its value. */
|
|
95
97
|
export interface Crowd {
|
|
@@ -145,6 +147,14 @@ export interface LayoutInput {
|
|
|
145
147
|
awaySide: Side;
|
|
146
148
|
/** A ground's key, for the stadium a soccer pitch stands in. */
|
|
147
149
|
ground?: string;
|
|
150
|
+
/** The surface lies on an arena floor that fills most of the hall: the sport's closer stands, where it has them. */
|
|
151
|
+
arena?: boolean;
|
|
152
|
+
/** A hall's gallery instead of the sport's stands (a dojo's balcony, from `dojoRoom`): the distance of its first row from the surface, its depth and the heights of its first and last rows, in feet. */
|
|
153
|
+
gallery?: {
|
|
154
|
+
offset: number;
|
|
155
|
+
depth: number;
|
|
156
|
+
height: [number, number];
|
|
157
|
+
};
|
|
148
158
|
/** The ballpark's fence as points on the field, for a ballpark (see `parkLayout`). */
|
|
149
159
|
fence?: readonly Pt[];
|
|
150
160
|
/** The foul lines' length at the poles and the backstop's distance, for a ballpark. */
|
package/dist/crowd.draw.js
CHANGED
|
@@ -14,7 +14,7 @@ const n1 = (v) => {
|
|
|
14
14
|
return t === "-0" ? "0" : t.replace(/^0\./, ".");
|
|
15
15
|
};
|
|
16
16
|
/** One tile of people: the seats of a block 18 feet square, each taken with the probability `density`, in the colour of a team with the probability `share` and else in a neutral. */
|
|
17
|
-
function tile(seed, density, share, team) {
|
|
17
|
+
function tile(seed, density, share, team, neutral = false) {
|
|
18
18
|
const next = generator(seed);
|
|
19
19
|
const shirts = new Map();
|
|
20
20
|
const heads = new Map();
|
|
@@ -42,16 +42,16 @@ function tile(seed, density, share, team) {
|
|
|
42
42
|
}
|
|
43
43
|
let out = "";
|
|
44
44
|
if (empty.length > 0)
|
|
45
|
-
out += el("path", { class: "ky-cr-seat", d: empty.join("") });
|
|
45
|
+
out += el("path", { class: neutral ? "ky-cr-seat ky-bud-gseat" : "ky-cr-seat", d: empty.join("") });
|
|
46
46
|
for (const [key, list] of shirts)
|
|
47
47
|
out += key.startsWith("ky-") ? el("path", { class: key, d: list.join("") }) : el("path", { stroke: key, d: list.join("") });
|
|
48
48
|
const headPaths = [...heads].map(([colour, list]) => el("path", { stroke: colour, "stroke-width": 1.15, d: list.join("") })).join("");
|
|
49
49
|
return el("g", { fill: "none", "stroke-linecap": "round", "stroke-width": 2.3 }, out + headPaths);
|
|
50
50
|
}
|
|
51
51
|
/** The tiles a crowd needs: the home tile, the home tile of the upper tiers (emptier) and the visitors', and the display tiles. */
|
|
52
|
-
function patterns(id, crowd, density, tifo) {
|
|
52
|
+
function patterns(id, crowd, density, tifo, neutral = false) {
|
|
53
53
|
const make = (suffix, body, size = TILE) => el("pattern", { id: `${id}-cr-${suffix}`, width: size, height: size, patternUnits: "userSpaceOnUse" }, body);
|
|
54
|
-
let out = make("h", tile(crowd.seed * 7 + 1, density, 0.88, "ky-cr-h")) + make("a", tile(crowd.seed * 7 + 3, Math.min(1, density * 1.04), 0.94, "ky-cr-a"));
|
|
54
|
+
let out = make("h", tile(crowd.seed * 7 + 1, density, neutral ? 0 : 0.88, "ky-cr-h", neutral)) + make("a", tile(crowd.seed * 7 + 3, Math.min(1, density * 1.04), 0.94, "ky-cr-a"));
|
|
55
55
|
if (tifo !== undefined) {
|
|
56
56
|
const stripes = (cls) => el("rect", { class: cls, width: 3, height: 6 }) + el("rect", { class: "ky-cr-tw", x: 3, width: 3, height: 6 });
|
|
57
57
|
const checks = (cls) => el("rect", { class: cls, width: 3, height: 3 }) + el("rect", { class: cls, x: 3, y: 3, width: 3, height: 3 }) + el("rect", { class: "ky-cr-tw", x: 3, width: 3, height: 3 }) + el("rect", { class: "ky-cr-tw", y: 3, width: 3, height: 3 });
|
|
@@ -133,10 +133,11 @@ export function drawCrowd(stands, o) {
|
|
|
133
133
|
for (const ring of stands.rings) {
|
|
134
134
|
// The upper tiers are a little emptier: their paint is the same tile with the empty seats' bed showing through a veil.
|
|
135
135
|
// The bed of seats under the tile, so that no seam of the pattern shows light.
|
|
136
|
+
const bed = stands.gallery === true ? "ky-cr-bed ky-bud-gbed" : "ky-cr-bed";
|
|
136
137
|
if (fine)
|
|
137
|
-
body += el("path", { class:
|
|
138
|
+
body += el("path", { class: bed, d: path(ring) }) + el("path", { class: "ky-cr-people", d: path(ring), fill: `url(#${id}-cr-h)` });
|
|
138
139
|
else
|
|
139
|
-
body += el("path", { class:
|
|
140
|
+
body += el("path", { class: bed, d: path(ring) }) + el("path", { class: stands.gallery === true ? "ky-cr-blk ky-bud-gblk" : "ky-cr-blk", d: path(ring), "fill-opacity": num(0.28 + 0.6 * density * ring.fill) });
|
|
140
141
|
if (fine && ring.fill < 0.95)
|
|
141
142
|
body += el("path", { class: "ky-cr-bed", d: path(ring), "fill-opacity": num(Math.min(0.5, (1 - ring.fill) * 1.6)) });
|
|
142
143
|
}
|
|
@@ -144,9 +145,14 @@ export function drawCrowd(stands, o) {
|
|
|
144
145
|
body += sector(s, id, fine, density);
|
|
145
146
|
if (fine)
|
|
146
147
|
for (const ring of stands.rings)
|
|
147
|
-
body += rows(ring) + aisles(ring);
|
|
148
|
+
body += rows(ring) + (stands.gallery === true ? "" : aisles(ring));
|
|
148
149
|
// Depth: the front wall of a tier throws a shade on its first rows (and the ground in front of it), the roof's edge on the last rows of the upper tier.
|
|
149
|
-
|
|
150
|
+
// A gallery's railing: a line along its front edge, over the seats.
|
|
151
|
+
if (stands.gallery === true)
|
|
152
|
+
for (const ring of stands.rings)
|
|
153
|
+
if (ring.hole !== null)
|
|
154
|
+
body += el("path", { class: "ky-bud-rail", d: outlinePath(ring.hole), fill: "none" });
|
|
155
|
+
if (fine && stands.gallery !== true) {
|
|
150
156
|
for (const ring of stands.rings) {
|
|
151
157
|
if (ring.hole !== null)
|
|
152
158
|
body += el("path", { d: outlinePath(ring.hole), fill: "none", stroke: "#000", "stroke-opacity": 0.2, "stroke-width": 7 });
|
|
@@ -160,7 +166,7 @@ export function drawCrowd(stands, o) {
|
|
|
160
166
|
const [x0, y0, x1, y1] = g.box;
|
|
161
167
|
body += el("rect", { class: `ky-cr-${g.kind}`, x: n1(x0), y: n1(y0), width: n1(x1 - x0), height: n1(y1 - y0), rx: g.kind === "bench" ? 1 : undefined });
|
|
162
168
|
}
|
|
163
|
-
return { defs: (fine ? patterns(id, crowd, density, tifo.home || tifo.away ? tifo : undefined) : ""), markup: el("g", { class: "ky-crowd" }, body), over };
|
|
169
|
+
return { defs: (fine ? patterns(id, crowd, density, tifo.home || tifo.away ? tifo : undefined, stands.gallery === true) : ""), markup: el("g", { class: "ky-crowd" }, body), over };
|
|
164
170
|
}
|
|
165
171
|
/** A block of visitors or a card display, a polygon over the band it is in. */
|
|
166
172
|
function sector(s, id, fine, density) {
|
package/dist/crowd.js
CHANGED
|
@@ -91,6 +91,15 @@ const PROFILES = {
|
|
|
91
91
|
cricket: { shape: "bowl", side: 30, end: 30, bench: 0, corner: 0, tiers: [lower(66), upper(73, 125)], oval: true, away: "end" },
|
|
92
92
|
fieldhockey: { shape: "bowl", side: 20, end: 26, bench: 5, corner: 30, tiers: [lower(46, 3, 24), upper(53, 87, 32, 54)], away: "end" },
|
|
93
93
|
handball: { shape: "bowl", side: 14, end: 18, bench: 4, corner: 16, tiers: [courtside(6), { id: "lower", from: 10, to: 46, height: [3, 22], fill: 1 }, upper(53, 97, 30, 56)], away: "corner" },
|
|
94
|
+
// The budō (ours: no rule gives the stands). The competition areas of judo, karate and kendo stand in an arena's bowl, as the Nippon Budokan's does round its octagonal floor (the Budokan's leaflet: 2,946 temporary
|
|
95
|
+
// seats on the arena, 2,959 to 3,199 on the first floor, 7,876 on the second and third); sumo's dohyō stands in the middle of a hall of box seats and a gallery (the Ryōgoku Kokugikan's); a kyūdōjō's
|
|
96
|
+
// spectators stand along the yamichi; a dojo's gallery looks down on the mats from above, as the Kodokan's does from the eighth floor.
|
|
97
|
+
judo: { shape: "bowl", side: 36, end: 36, bench: 6, corner: 50, tiers: [lower(60, 3, 24), upper(67, 120, 32, 60)], away: "corner", arena: { side: 8, end: 8, bench: 0, corner: 56, tiers: [lower(34, 3, 24), upper(40, 74, 32, 62)] } },
|
|
98
|
+
karate: { shape: "bowl", side: 36, end: 36, bench: 6, corner: 50, tiers: [lower(60, 3, 24), upper(67, 120, 32, 60)], away: "corner", arena: { side: 8, end: 8, bench: 0, corner: 56, tiers: [lower(34, 3, 24), upper(40, 74, 32, 62)] } },
|
|
99
|
+
kendo: { shape: "bowl", side: 36, end: 36, bench: 6, corner: 50, tiers: [lower(60, 3, 24), upper(67, 120, 32, 60)], away: "corner", arena: { side: 8, end: 8, bench: 0, corner: 56, tiers: [lower(34, 3, 24), upper(40, 74, 32, 62)] } },
|
|
100
|
+
sumo: { shape: "bowl", side: 58, end: 58, bench: 0, corner: 56, tiers: [{ id: "lower", from: 0, to: 54, height: [1, 8], fill: 1 }, upper(62, 128, 26, 62)], away: "corner" },
|
|
101
|
+
kyudo: { shape: "sides", side: 10, end: 8, bench: 0, corner: 6, tiers: [lower(24, 2, 8), upper(30, 52, 12, 22)], away: "corner" },
|
|
102
|
+
dojo: { shape: "bowl", side: 5, end: 5, bench: 0, corner: 8, tiers: [lower(26, 2, 14), upper(32, 58, 20, 36)], away: "corner" },
|
|
94
103
|
};
|
|
95
104
|
/** The grounds of the package that are the home of an NFL or a CFL club, where a soccer pitch stands inside a gridiron's stands. From each ground's article. */
|
|
96
105
|
export const GRIDIRON_GROUNDS = Object.freeze({
|
|
@@ -297,8 +306,12 @@ function offsets(p, box, stadium) {
|
|
|
297
306
|
}
|
|
298
307
|
/** The layout of a sport's stands round its surface: rings, covers, sectors and gaps. For a ballpark see `parkLayout`. */
|
|
299
308
|
export function layoutOf(input) {
|
|
300
|
-
const { sport, box
|
|
301
|
-
const
|
|
309
|
+
const { sport, box } = input;
|
|
310
|
+
const gal = input.gallery;
|
|
311
|
+
// A gallery has no home and no visitors, no card display and no tunnel, camera or benches.
|
|
312
|
+
const crowd = gal === undefined ? input.crowd : { ...input.crowd, awayEnd: "none", tifo: undefined, gaps: false, shape: input.crowd.shape === "sides" ? "sides" : "bowl" };
|
|
313
|
+
const base0 = gal === undefined ? PROFILES[sport] : { shape: "bowl", side: gal.offset, end: gal.offset, bench: 0, corner: 0, tiers: [{ id: "gallery", from: 0, to: gal.depth, height: gal.height, fill: 1 }], away: "corner" };
|
|
314
|
+
const base = input.arena === true && base0.arena !== undefined ? { ...base0, ...base0.arena, tiers: base0.arena.tiers ?? base0.tiers } : base0;
|
|
302
315
|
const host = sport === "soccer" ? (crowd.stadium === "nfl" || crowd.stadium === "cfl" ? crowd.stadium : crowd.stadium === "auto" && input.ground !== undefined ? GRIDIRON_GROUNDS[input.ground] : undefined) : undefined;
|
|
303
316
|
const stadium = sport === "soccer" ? host ?? "purpose" : undefined;
|
|
304
317
|
const p = stadium === "nfl" || stadium === "cfl" ? { ...PROFILES[stadium], shape: base.shape, away: base.away } : base;
|
|
@@ -360,6 +373,8 @@ export function layoutOf(input) {
|
|
|
360
373
|
const stands = { sport, shape, box, rings, covers, concourses, sectors: [], gaps: [], reach: reachOf, seats: rings.reduce((n, r) => n + r.seats, 0) };
|
|
361
374
|
if (stadium !== undefined)
|
|
362
375
|
stands.stadium = stadium;
|
|
376
|
+
if (gal !== undefined)
|
|
377
|
+
stands.gallery = true;
|
|
363
378
|
if (stadium === "nfl" || stadium === "cfl") {
|
|
364
379
|
const f = gridiron(stadium);
|
|
365
380
|
const field = { x: cx - f.length / 2, y: cy - f.width / 2, w: f.length, h: f.width };
|