@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
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { DirectionOptions, Language, Side, Sport } from "./types.ts";
|
|
2
|
+
import type { DriveResult, Situation, SituationTeam } from "./situation.types.ts";
|
|
3
|
+
export type { BaseballSituation, DriveLeg, DriveResult, FootballSituation, HockeySituation, ShootoutAttempt, ShootoutRound, Situation, SituationTeam } from "./situation.types.ts";
|
|
4
|
+
/** The most rounds of a shootout that are drawn. */
|
|
5
|
+
export declare const SHOOTOUT_MAX = 12;
|
|
6
|
+
/** The rounds that are regular: the rounds after them are sudden death. */
|
|
7
|
+
export declare const SHOOTOUT_REGULAR = 3;
|
|
8
|
+
/** The most drives drawn in a drive chart. */
|
|
9
|
+
export declare const DRIVES_MAX = 24;
|
|
10
|
+
/** The red zone: the yards before the goal line the offence attacks. */
|
|
11
|
+
export declare const RED_ZONE_YARDS = 20;
|
|
12
|
+
/** The yards to a first down. */
|
|
13
|
+
export declare const FIRST_DOWN_YARDS = 10;
|
|
14
|
+
/** The short code each drive result is written with at the head of its arrow. */
|
|
15
|
+
export declare const DRIVE_CODES: Readonly<Record<DriveResult, string>>;
|
|
16
|
+
/** What a reading needs to know of the drawing it is for. */
|
|
17
|
+
export interface SituationContext {
|
|
18
|
+
/** The sport as drawn (`chooseLeague(o).sport`): `nhl`, `mlb`, `nfl`, `cfl` or `ifaf`; a situation is for these only. */
|
|
19
|
+
sport: Sport;
|
|
20
|
+
/** The side the home team's banner is on. */
|
|
21
|
+
homeSide: Side;
|
|
22
|
+
/** The attack arrows' options, which say which end each team attacks. */
|
|
23
|
+
direction?: DirectionOptions;
|
|
24
|
+
/** The football field: the yards between the goal lines and the downs a team has. */
|
|
25
|
+
football?: {
|
|
26
|
+
yards: number;
|
|
27
|
+
downs: number;
|
|
28
|
+
};
|
|
29
|
+
/** The teams' names, when the drawing has teams. */
|
|
30
|
+
names?: {
|
|
31
|
+
home: string;
|
|
32
|
+
away: string;
|
|
33
|
+
};
|
|
34
|
+
/** The league's id as the options name it (`iihf`, `olympic`), when they name one. */
|
|
35
|
+
league?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Hockey, read. */
|
|
38
|
+
export interface HockeyRead {
|
|
39
|
+
kind: "hockey";
|
|
40
|
+
/** The skaters, when known. */
|
|
41
|
+
away?: number;
|
|
42
|
+
home?: number;
|
|
43
|
+
/** The team that is attacking (more skaters, or the goalkeeper pulled, or the other team of a delayed penalty), and the end it heads for. */
|
|
44
|
+
attacker?: SituationTeam;
|
|
45
|
+
toward?: "left" | "right";
|
|
46
|
+
/** The attacker's skaters and the other team's, in that order, when both are known. */
|
|
47
|
+
strength?: [number, number];
|
|
48
|
+
/** More skaters than the other team. */
|
|
49
|
+
powerPlay: boolean;
|
|
50
|
+
emptyNet?: SituationTeam;
|
|
51
|
+
delayed?: SituationTeam;
|
|
52
|
+
clock?: string;
|
|
53
|
+
/** The shootout is called the game winning shots (GWS) in this league. */
|
|
54
|
+
gws: boolean;
|
|
55
|
+
shootout?: {
|
|
56
|
+
rounds: [boolean | null, boolean | null][];
|
|
57
|
+
goals: [number, number];
|
|
58
|
+
sudden: number;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Baseball, read. */
|
|
62
|
+
export interface BaseballRead {
|
|
63
|
+
kind: "baseball";
|
|
64
|
+
bases?: [boolean, boolean, boolean];
|
|
65
|
+
outs?: number;
|
|
66
|
+
inning?: number;
|
|
67
|
+
half?: "top" | "bottom";
|
|
68
|
+
balls?: number;
|
|
69
|
+
strikes?: number;
|
|
70
|
+
}
|
|
71
|
+
/** A drive, read: in yards from the team's own goal line, and the end it heads for. */
|
|
72
|
+
export interface DriveRead {
|
|
73
|
+
team: SituationTeam;
|
|
74
|
+
from: number;
|
|
75
|
+
to: number;
|
|
76
|
+
result?: DriveResult;
|
|
77
|
+
toward: "left" | "right";
|
|
78
|
+
}
|
|
79
|
+
/** Football, read. */
|
|
80
|
+
export interface FootballRead {
|
|
81
|
+
kind: "football";
|
|
82
|
+
yards: number;
|
|
83
|
+
downs: number;
|
|
84
|
+
ballOn?: number;
|
|
85
|
+
toGo?: number | "goal";
|
|
86
|
+
down?: number;
|
|
87
|
+
possession?: SituationTeam;
|
|
88
|
+
/** The end zone the offence heads for. */
|
|
89
|
+
toward: "left" | "right";
|
|
90
|
+
redZone: boolean;
|
|
91
|
+
drive: DriveRead[];
|
|
92
|
+
}
|
|
93
|
+
/** A situation, read. */
|
|
94
|
+
export type SituationRead = HockeyRead | BaseballRead | FootballRead;
|
|
95
|
+
/** `m:ss`, `m:ss.s`, or a whole number of seconds written as `m:ss`; anything else is left out. */
|
|
96
|
+
export declare function clockText(value: unknown): string | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* The situation a drawing's options ask for, decided: which team attacks and which end, what is lit. `undefined` when the options have none, when the sport has no situation (only
|
|
99
|
+
* hockey, baseball and football do), or when nothing in it is a value the sport can have.
|
|
100
|
+
*/
|
|
101
|
+
export declare function readSituation(situation: unknown, ctx: SituationContext): SituationRead | undefined;
|
|
102
|
+
/** A yard line as the tag writes it: `OWN 35`, `OPP 12`, or the middle of the field. */
|
|
103
|
+
export declare function yardLineText(ballOn: number, yards: number, language: Language): string;
|
|
104
|
+
/** The down and distance as the tag writes it (`3rd & 7`, `1st & Goal`), or `undefined` when there is no down. */
|
|
105
|
+
export declare function downTag(read: FootballRead, language: Language): string | undefined;
|
|
106
|
+
/** The strength of a hockey situation on the pill: `5v4 PP`, `6v5 EN`, `4v4`, `DEL PEN`, with the clock after it; `undefined` when there is nothing to say (5 against 5). */
|
|
107
|
+
export declare function hockeyPill(read: HockeyRead, language: Language): string | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* The short label of a situation as the drawing writes it: hockey's `5v4 PP 1:42`, baseball's `Top 7 · 2 out · 3-2`, football's `3rd & 7`. `undefined` when there is none to write.
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* import { situationText } from "@johnmorrisdotca/kyogijo-stadiums";
|
|
113
|
+
* console.log(situationText({ sport: "nhl", situation: { awaySkaters: 5, homeSkaters: 4, clock: "1:42" } }));
|
|
114
|
+
* console.log(situationText({ sport: "mlb", situation: { inning: 7, half: "top", outs: 2, balls: 3, strikes: 2 } }));
|
|
115
|
+
* console.log(situationText({ sport: "nfl", situation: { ballOn: 88, down: 3, toGo: 7 } }));
|
|
116
|
+
* // 5v4 PP 1:42
|
|
117
|
+
* // Top 7 · 2 out · 3-2
|
|
118
|
+
* // 3rd & 7 · OPP 12
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export declare function situationText(options: {
|
|
122
|
+
sport: Sport;
|
|
123
|
+
league?: string;
|
|
124
|
+
homeSide?: Side;
|
|
125
|
+
direction?: DirectionOptions;
|
|
126
|
+
language?: Language;
|
|
127
|
+
situation?: Situation;
|
|
128
|
+
}, language?: Language): string | undefined;
|
|
129
|
+
/** The short label of a reading, in a language. */
|
|
130
|
+
export declare function tagOf(read: SituationRead, language: Language): string | undefined;
|
|
131
|
+
/** The sentences a description adds for a situation, each beginning with a capital. */
|
|
132
|
+
export declare function situationSentences(read: SituationRead, language: Language, names?: {
|
|
133
|
+
home: string;
|
|
134
|
+
away: string;
|
|
135
|
+
}): string[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type SituationRead } from "./situation.ts";
|
|
2
|
+
import { type Surface } from "./surfaces/surface.ts";
|
|
3
|
+
import type { Language, Side } from "./types.ts";
|
|
4
|
+
/** What a situation is drawn with. */
|
|
5
|
+
export interface SituationEnv {
|
|
6
|
+
id: string;
|
|
7
|
+
language: Language;
|
|
8
|
+
/** The side the home team is on. */
|
|
9
|
+
homeSide: Side;
|
|
10
|
+
/** The two teams' short names, when the drawing has teams. */
|
|
11
|
+
abbrs?: {
|
|
12
|
+
home: string;
|
|
13
|
+
away: string;
|
|
14
|
+
};
|
|
15
|
+
/** The score's panel stands in the middle of the surface. */
|
|
16
|
+
plate: boolean;
|
|
17
|
+
/** The strip under the drawing shows the situation (the pill, the scorebug or the tag), so the surface does not: a lineup's players stand where they would be. */
|
|
18
|
+
strip?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** What a situation adds to a drawing. */
|
|
21
|
+
export interface SituationDrawn {
|
|
22
|
+
/** Over the surface's ground and under its lines. */
|
|
23
|
+
under: string;
|
|
24
|
+
/** Over the lines and the data, under the score's panel. */
|
|
25
|
+
over: string;
|
|
26
|
+
defs: string;
|
|
27
|
+
}
|
|
28
|
+
/** A situation drawn on a surface, or `undefined` when nothing is to be drawn. */
|
|
29
|
+
export declare function drawSituation(read: SituationRead, surface: Surface, env: SituationEnv): SituationDrawn | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* A situation as the badge (and the compact card's strip) shows it in place of the picture of the surface: the markup centred on the origin of a box 76 wide, and `undefined` when there is
|
|
32
|
+
* nothing to show. Hockey's pill, baseball's scorebug and football's tag, each shrunk to fit.
|
|
33
|
+
*/
|
|
34
|
+
export declare function badgeSituation(read: SituationRead, language: Language): string | undefined;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// A situation drawn on a surface: hockey's pill and tinted attacking zone, its empty net and shootout strip; baseball's lit bases and scorebug; football's line of scrimmage, first-down line,
|
|
2
|
+
// red zone, tag and drive chart. What is under the surface's lines (a tint) is `under`, what is over them is `over`; both are in the surface's wide frame, in feet, with the widgets set
|
|
3
|
+
// upright (turned back when the surface is turned a quarter).
|
|
4
|
+
import { wideView } from "./annotate.js";
|
|
5
|
+
import { el, num } from "./format.js";
|
|
6
|
+
import { DRIVE_CODES, downTag, hockeyPill, yardLineText } from "./situation.js";
|
|
7
|
+
import { pillWidget, scorebugWidget, shootoutWidget, tagWidget } from "./situation.widgets.js";
|
|
8
|
+
import { KYOGIJO_STRINGS } from "./strings.js";
|
|
9
|
+
import { roundedRect, textOn } from "./surfaces/surface.js";
|
|
10
|
+
/** A widget set where the eye wants it, upright however the surface lies. */
|
|
11
|
+
function put(widget, cx, cy, turned) {
|
|
12
|
+
return el("g", { class: "ky-sit-w", transform: `translate(${num(cx)} ${num(cy)})${turned ? " rotate(-90)" : ""}` }, widget.markup);
|
|
13
|
+
}
|
|
14
|
+
/** A value held between `lo` and `hi`; the middle of them when they cross. */
|
|
15
|
+
const within = (value, lo, hi) => (hi < lo ? (lo + hi) / 2 : Math.min(hi, Math.max(lo, value)));
|
|
16
|
+
/**
|
|
17
|
+
* A widget's centre held inside a box with a little to spare. A surface turned a quarter turns the widget back, so its width then lies along the wide frame's y and its height along x.
|
|
18
|
+
*/
|
|
19
|
+
function inside(widget, turned, box, cx, cy) {
|
|
20
|
+
const hx = (turned ? widget.h : widget.w) / 2 + 1;
|
|
21
|
+
const hy = (turned ? widget.w : widget.h) / 2 + 1;
|
|
22
|
+
return [within(cx, box.x + hx, box.x + box.w - hx), within(cy, box.y + hy, box.y + box.h - hy)];
|
|
23
|
+
}
|
|
24
|
+
const sideOf = (team, homeSide) => ((team === "home") === (homeSide === "left") ? "left" : "right");
|
|
25
|
+
/** The rotation, on the eye's page, of an arrow that points to the `left` or `right` end of a surface drawn wide (turned a quarter, `right` is down). */
|
|
26
|
+
const headAngle = (toward, turned) => (turned ? (toward === "right" ? 90 : -90) : toward === "right" ? 0 : 180);
|
|
27
|
+
function drawHockey(read, surface, env) {
|
|
28
|
+
const a = surface.anchors?.kind === "hockey" ? surface.anchors : undefined;
|
|
29
|
+
const out = { under: "", over: "", defs: "" };
|
|
30
|
+
if (a === undefined)
|
|
31
|
+
return out;
|
|
32
|
+
const vb = surface.box ?? { x: 0, y: 0, w: a.length, h: a.width };
|
|
33
|
+
const size = 5.2;
|
|
34
|
+
const words = KYOGIJO_STRINGS[env.language].situation;
|
|
35
|
+
if (read.shootout !== undefined) {
|
|
36
|
+
const labels = [env.abbrs?.away ?? words.awayShort, env.abbrs?.home ?? words.homeShort];
|
|
37
|
+
const strip = shootoutWidget({ rounds: read.shootout.rounds, goals: read.shootout.goals, sudden: read.shootout.sudden, labels, sides: [sideOf("away", env.homeSide), sideOf("home", env.homeSide)], suddenWord: words.sudden }, size * 0.56, true);
|
|
38
|
+
const cx = vb.x + vb.w / 2;
|
|
39
|
+
// Under the score's panel when it stands in the middle, else across the middle of what is shown.
|
|
40
|
+
const below = env.plate ? surface.plate.cy + surface.plate.h / 2 + 2 + strip.h / 2 : vb.y + vb.h / 2;
|
|
41
|
+
out.over = put(strip, ...inside(strip, surface.turned, vb, cx, below), surface.turned);
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
const label = hockeyPill(read, env.language);
|
|
45
|
+
if (read.attacker !== undefined && read.toward !== undefined) {
|
|
46
|
+
// The attacking zone, from the blue line to the end boards, in the attacking team's colour, cut to the ice.
|
|
47
|
+
const x0 = read.toward === "right" ? a.length - a.blueLine : 0;
|
|
48
|
+
out.defs = el("clipPath", { id: `${env.id}-sit-ice` }, el("path", { d: roundedRect(0, 0, a.length, a.width, a.cornerRadius) }));
|
|
49
|
+
out.under = el("g", { class: "ky-sit-zone-g", "clip-path": `url(#${env.id}-sit-ice)` }, el("rect", { class: `ky-sit-zone ky-sit-zone-${sideOf(read.attacker, env.homeSide)}`, x: x0, y: 0, width: a.blueLine, height: a.width }));
|
|
50
|
+
}
|
|
51
|
+
if (read.emptyNet !== undefined && read.toward !== undefined) {
|
|
52
|
+
// The empty net is at the end the attackers are leaving: ringed.
|
|
53
|
+
const gx = read.toward === "right" ? a.goalLine : a.length - a.goalLine;
|
|
54
|
+
const dir = read.toward === "right" ? -1 : 1;
|
|
55
|
+
const pad = 1.3;
|
|
56
|
+
out.over += el("rect", { class: "ky-sit-net", x: dir < 0 ? gx - a.net.depth - pad : gx - pad, y: a.width / 2 - a.net.width / 2 - pad, width: a.net.depth + 2 * pad, height: a.net.width + 2 * pad, rx: 1.2 });
|
|
57
|
+
}
|
|
58
|
+
if (label !== undefined && env.strip !== true) {
|
|
59
|
+
const pill = pillWidget(label, size, read.attacker === undefined ? undefined : sideOf(read.attacker, env.homeSide));
|
|
60
|
+
// Wide, at the top of the ice in the middle; tall, just inside the top blue line, in the middle across.
|
|
61
|
+
const at = surface.turned
|
|
62
|
+
? inside(pill, true, vb, (vb.x <= a.blueLine ? a.blueLine : vb.x) + pill.h / 2 + 3, vb.y + vb.h / 2)
|
|
63
|
+
: inside(pill, false, vb, vb.x + vb.w / 2, vb.y + pill.h / 2 + 3);
|
|
64
|
+
out.over += put(pill, ...at, surface.turned);
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
function drawBaseball(read, surface, env) {
|
|
69
|
+
const a = surface.anchors?.kind === "baseball" ? surface.anchors : undefined;
|
|
70
|
+
const out = { under: "", over: "", defs: "" };
|
|
71
|
+
if (a === undefined)
|
|
72
|
+
return out;
|
|
73
|
+
// The occupied bases, lit larger than they are drawn so that they read from a distance.
|
|
74
|
+
if (read.bases !== undefined) {
|
|
75
|
+
const k = a.half * 2.6;
|
|
76
|
+
read.bases.forEach((on, i) => {
|
|
77
|
+
if (!on)
|
|
78
|
+
return;
|
|
79
|
+
const [u, v] = a.bases[i];
|
|
80
|
+
out.over += el("path", { class: "ky-sit-lit", d: `M${num(u)} ${num(v - k)}L${num(u + k)} ${num(v)}L${num(u)} ${num(v + k)}L${num(u - k)} ${num(v)}Z` });
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// The scorebug at the foot of the field, behind home plate, held inside what is shown.
|
|
84
|
+
const view = wideView(surface);
|
|
85
|
+
const bug = { bases: read.bases, outs: read.outs, inning: read.inning, half: read.half, balls: read.balls, strikes: read.strikes };
|
|
86
|
+
if (env.strip !== true && Object.values(bug).some((value) => value !== undefined)) {
|
|
87
|
+
let u = Math.min(11, view.h * 0.2, view.w * 0.1);
|
|
88
|
+
let widget = scorebugWidget(bug, u, true);
|
|
89
|
+
// A drawing cut very small gets a bug that fits it.
|
|
90
|
+
if (widget.w > view.w * 0.9) {
|
|
91
|
+
u *= (view.w * 0.9) / widget.w;
|
|
92
|
+
widget = scorebugWidget(bug, u, true);
|
|
93
|
+
}
|
|
94
|
+
out.over += put(widget, within(0, view.x + widget.w / 2 + 1, view.x + view.w - widget.w / 2 - 1), view.y + view.h - widget.h / 2 - 3, false);
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
function drawFootball(read, surface, env) {
|
|
99
|
+
const a = surface.anchors?.kind === "football" ? surface.anchors : undefined;
|
|
100
|
+
const out = { under: "", over: "", defs: "" };
|
|
101
|
+
if (a === undefined)
|
|
102
|
+
return out;
|
|
103
|
+
const e = a.endZone;
|
|
104
|
+
const G = a.length;
|
|
105
|
+
const W = a.width;
|
|
106
|
+
const dir = read.toward === "right" ? 1 : -1;
|
|
107
|
+
// A distance in yards from a team's own goal line, as a place along the field, for a team heading for `toward`.
|
|
108
|
+
const at = (yards, toward) => (toward === "right" ? e + yards * 3 : e + G - yards * 3);
|
|
109
|
+
const vb = surface.box ?? { x: 0, y: 0, w: G + 2 * e, h: W };
|
|
110
|
+
// The red zone: the last 20 yards before the goal line the offence is attacking.
|
|
111
|
+
if (read.redZone) {
|
|
112
|
+
const goal = read.toward === "right" ? e + G : e;
|
|
113
|
+
out.under = el("rect", { class: "ky-sit-red", x: Math.min(goal, goal - dir * 60), y: 0, width: 60, height: W });
|
|
114
|
+
}
|
|
115
|
+
let x;
|
|
116
|
+
if (read.ballOn !== undefined) {
|
|
117
|
+
x = at(read.ballOn, read.toward);
|
|
118
|
+
out.over += el("path", { class: "ky-sit-los-halo", d: `M${num(x)} 0V${num(W)}`, fill: "none" });
|
|
119
|
+
// The first-down line, ten yards on or where the options put it, unless the goal line is nearer.
|
|
120
|
+
if (typeof read.toGo === "number" && read.ballOn + read.toGo < read.yards) {
|
|
121
|
+
const fd = at(read.ballOn + read.toGo, read.toward);
|
|
122
|
+
out.over += el("path", { class: "ky-sit-fd-halo", d: `M${num(fd)} 0V${num(W)}`, fill: "none" });
|
|
123
|
+
out.over += el("path", { class: "ky-sit-fd", d: `M${num(fd)} 0V${num(W)}`, fill: "none" });
|
|
124
|
+
}
|
|
125
|
+
out.over += el("path", { class: "ky-sit-los", d: `M${num(x)} 0V${num(W)}`, fill: "none" });
|
|
126
|
+
}
|
|
127
|
+
// The drive chart: an arrow for each drive, in a lane of its own across the field.
|
|
128
|
+
const n = read.drive.length;
|
|
129
|
+
if (n > 0) {
|
|
130
|
+
// The lanes lie across the field; the score's panel stands over the middle of it, so they go in the band below it.
|
|
131
|
+
const y0 = env.plate ? surface.plate.cy + surface.plate.h / 2 + 3 : 10;
|
|
132
|
+
const y1 = env.plate ? W - 6 : W - 10;
|
|
133
|
+
const step = Math.min(10, Math.max(2.4, (y1 - y0) / n));
|
|
134
|
+
const top = y0 + (y1 - y0 - step * n) / 2;
|
|
135
|
+
read.drive.forEach((leg, i) => {
|
|
136
|
+
const y = top + step * (i + 0.5);
|
|
137
|
+
const x0 = at(leg.from, leg.toward);
|
|
138
|
+
const x1 = at(leg.to, leg.toward);
|
|
139
|
+
const d = Math.sign(x1 - x0) || (leg.toward === "right" ? 1 : -1);
|
|
140
|
+
const head = Math.min(3.4, Math.abs(x1 - x0));
|
|
141
|
+
const cls = `ky-sit-d-${leg.result ?? "none"}`;
|
|
142
|
+
out.over += el("path", { class: `ky-sit-dr-halo`, d: `M${num(x0)} ${num(y)}H${num(x1 - d * head * 0.6)}`, fill: "none" });
|
|
143
|
+
out.over += el("path", { class: `ky-sit-dr ${cls}`, d: `M${num(x0)} ${num(y)}H${num(x1 - d * head * 0.6)}`, fill: "none" });
|
|
144
|
+
out.over += el("circle", { class: `ky-sit-dot-s ${cls}`, cx: x0, cy: y, r: 1.2 });
|
|
145
|
+
if (head > 0.5)
|
|
146
|
+
out.over += el("path", { class: `ky-sit-dh ${cls}`, d: `M${num(x1)} ${num(y)}L${num(x1 - d * head)} ${num(y - head * 0.62)}V${num(y + head * 0.62)}Z` });
|
|
147
|
+
if (leg.result !== undefined)
|
|
148
|
+
out.over += textOn(surface.turned, x1 + d * 1.8, y + 3.6 * 0.36, DRIVE_CODES[leg.result], { size: 3.6, anchor: d > 0 ? "start" : "end", cls: "ky-sit-dl", weight: 800 });
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// The tag: the down and distance, with the yard line, behind the line of scrimmage near the far sideline.
|
|
152
|
+
const main = downTag(read, env.language);
|
|
153
|
+
const where = read.ballOn === undefined ? undefined : yardLineText(read.ballOn, read.yards, env.language);
|
|
154
|
+
if (env.strip !== true && (main !== undefined || where !== undefined)) {
|
|
155
|
+
const tag = tagWidget(main ?? where ?? "", main === undefined ? undefined : where, 5.6, headAngle(read.toward, surface.turned));
|
|
156
|
+
const along = (surface.turned ? tag.h : tag.w) / 2;
|
|
157
|
+
const across = (surface.turned ? tag.w : tag.h) / 2;
|
|
158
|
+
const cy = vb.y + across + 5;
|
|
159
|
+
// Behind the line of scrimmage, 4 feet off it; nudged along the field, to the line first and then away from it, until it covers none of the yard numbers and the arrows beside them that point to a goal line.
|
|
160
|
+
// Where there is no such room it keeps off the arrows at least.
|
|
161
|
+
const clear = (cx, hide) => {
|
|
162
|
+
const [px, py] = inside(tag, surface.turned, vb, cx, cy);
|
|
163
|
+
return !hide.some(([ox, oy, hx, hy]) => Math.abs(ox - px) < along + hx + 0.8 && Math.abs(oy - py) < across + hy + 0.8);
|
|
164
|
+
};
|
|
165
|
+
const gaps = [4, 3, 2, 1, 0.5, ...Array.from({ length: 36 }, (_, i) => 5 + i)];
|
|
166
|
+
const gap = x === undefined ? undefined : (gaps.find((g) => clear(x - dir * (along + g), [...a.arrows, ...a.numerals])) ?? gaps.find((g) => clear(x - dir * (along + g), a.arrows)) ?? 4);
|
|
167
|
+
const beside = x === undefined || gap === undefined ? vb.x + vb.w / 2 : x - dir * (along + gap);
|
|
168
|
+
out.over += put(tag, ...inside(tag, surface.turned, vb, beside, cy), surface.turned);
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
/** A situation drawn on a surface, or `undefined` when nothing is to be drawn. */
|
|
173
|
+
export function drawSituation(read, surface, env) {
|
|
174
|
+
const drawn = read.kind === "hockey" ? drawHockey(read, surface, env) : read.kind === "baseball" ? drawBaseball(read, surface, env) : drawFootball(read, surface, env);
|
|
175
|
+
return drawn.under === "" && drawn.over === "" ? undefined : drawn;
|
|
176
|
+
}
|
|
177
|
+
/** The widest a badge's middle holds, in the badge's units. */
|
|
178
|
+
const BADGE_ROOM = 74;
|
|
179
|
+
/**
|
|
180
|
+
* A situation as the badge (and the compact card's strip) shows it in place of the picture of the surface: the markup centred on the origin of a box 76 wide, and `undefined` when there is
|
|
181
|
+
* nothing to show. Hockey's pill, baseball's scorebug and football's tag, each shrunk to fit.
|
|
182
|
+
*/
|
|
183
|
+
export function badgeSituation(read, language) {
|
|
184
|
+
let widget;
|
|
185
|
+
if (read.kind === "hockey") {
|
|
186
|
+
const label = hockeyPill(read, language);
|
|
187
|
+
widget = label === undefined ? undefined : pillWidget(label, 9);
|
|
188
|
+
}
|
|
189
|
+
else if (read.kind === "baseball") {
|
|
190
|
+
const bug = { bases: read.bases, outs: read.outs, inning: read.inning, half: read.half, balls: read.balls, strikes: read.strikes };
|
|
191
|
+
widget = Object.values(bug).some((value) => value !== undefined) ? scorebugWidget(bug, 8.4, false) : undefined;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
const main = downTag(read, language);
|
|
195
|
+
const where = read.ballOn === undefined ? undefined : yardLineText(read.ballOn, read.yards, language);
|
|
196
|
+
widget = main === undefined && where === undefined ? undefined : tagWidget(main ?? where ?? "", main === undefined ? undefined : where, 8.2);
|
|
197
|
+
}
|
|
198
|
+
if (widget === undefined)
|
|
199
|
+
return undefined;
|
|
200
|
+
const k = Math.min(1, BADGE_ROOM / widget.w);
|
|
201
|
+
return k === 1 ? widget.markup : el("g", { transform: `scale(${k.toFixed(3)})` }, widget.markup);
|
|
202
|
+
}
|