@johnmorrisdotca/kyogijo-stadiums 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +75 -0
- package/NOTICE.md +4 -0
- package/README.md +71 -16
- package/dist/annotate.js +1 -1
- package/dist/athletics/describe.d.ts +8 -0
- package/dist/athletics/describe.js +41 -0
- package/dist/athletics/events.d.ts +17 -0
- package/dist/athletics/events.js +48 -0
- package/dist/athletics/facilities.d.ts +80 -0
- package/dist/athletics/facilities.js +176 -0
- package/dist/athletics/infield.d.ts +79 -0
- package/dist/athletics/infield.js +166 -0
- package/dist/athletics/layout.d.ts +13 -0
- package/dist/athletics/layout.js +98 -0
- package/dist/athletics/oval.d.ts +57 -0
- package/dist/athletics/oval.js +116 -0
- package/dist/athletics/plan.d.ts +97 -0
- package/dist/athletics/plan.js +355 -0
- package/dist/athletics/races.d.ts +102 -0
- package/dist/athletics/races.js +355 -0
- package/dist/athletics/steeplechase.d.ts +72 -0
- package/dist/athletics/steeplechase.js +196 -0
- package/dist/athletics/stencil.d.ts +13 -0
- package/dist/athletics/stencil.js +85 -0
- package/dist/athletics/track.d.ts +54 -0
- package/dist/athletics/track.js +105 -0
- package/dist/athletics/types.d.ts +77 -0
- package/dist/athletics/types.js +1 -0
- package/dist/athletics-entry.d.ts +17 -0
- package/dist/athletics-entry.js +11 -0
- package/dist/athletics.style.d.ts +16 -0
- package/dist/athletics.style.js +51 -0
- package/dist/atlas-entry.d.ts +4 -2
- package/dist/atlas-entry.js +3 -2
- package/dist/atlas.d.ts +83 -7
- package/dist/atlas.js +108 -8
- package/dist/charts-entry.d.ts +6 -1
- package/dist/charts-entry.js +3 -0
- package/dist/charts.athletics.d.ts +183 -0
- package/dist/charts.athletics.js +455 -0
- package/dist/charts.curling.heat.d.ts +24 -0
- package/dist/charts.curling.heat.js +116 -0
- package/dist/charts.curling.js +14 -3
- package/dist/charts.pool.d.ts +67 -0
- package/dist/charts.pool.js +76 -0
- package/dist/charts.skating.d.ts +204 -0
- package/dist/charts.skating.js +426 -0
- package/dist/charts.types.d.ts +53 -1
- package/dist/court.d.ts +85 -2
- package/dist/court.js +102 -7
- package/dist/court.more.d.ts +46 -0
- package/dist/court.more.js +219 -0
- package/dist/courts.style.d.ts +14 -2
- package/dist/courts.style.js +51 -4
- package/dist/crowd.d.ts +154 -0
- package/dist/crowd.draw.d.ts +17 -0
- package/dist/crowd.draw.js +198 -0
- package/dist/crowd.js +465 -0
- package/dist/crowd.park.d.ts +19 -0
- package/dist/crowd.park.js +153 -0
- package/dist/curling.stats.d.ts +271 -0
- package/dist/curling.stats.js +301 -0
- package/dist/draw.d.ts +16 -0
- package/dist/draw.js +318 -35
- package/dist/element-entry.d.ts +1 -1
- package/dist/element-entry.js +1 -1
- package/dist/element.d.ts +13 -2
- package/dist/element.js +77 -3
- package/dist/features.js +131 -0
- package/dist/fields.kit.d.ts +2 -2
- package/dist/fields.types.d.ts +18 -4
- package/dist/frames.providers.js +63 -8
- package/dist/geojson.js +3 -0
- package/dist/geometry-entry.d.ts +2 -1
- package/dist/geometry-entry.js +2 -1
- package/dist/geometry.d.ts +20 -0
- package/dist/geometry.js +78 -1
- package/dist/grounds.d.ts +4 -0
- package/dist/grounds.data.js +12 -0
- package/dist/hardwood.d.ts +31 -0
- package/dist/hardwood.js +157 -0
- package/dist/heat.js +4 -3
- package/dist/heat.types.d.ts +5 -0
- package/dist/heat.zones.d.ts +2 -2
- package/dist/heat.zones.js +154 -6
- package/dist/homeside.js +17 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.js +10 -1
- package/dist/layers.d.ts +1 -1
- package/dist/layers.env.d.ts +3 -1
- package/dist/layers.env.js +4 -2
- package/dist/layers.js +4 -4
- package/dist/leagues/athletics.d.ts +63 -0
- package/dist/leagues/athletics.js +87 -0
- package/dist/leagues/courts.d.ts +269 -4
- package/dist/leagues/courts.js +155 -1
- package/dist/leagues/skating.d.ts +145 -0
- package/dist/leagues/skating.js +73 -0
- package/dist/leagues/sources.js +20 -0
- package/dist/leagues/types.d.ts +1 -1
- package/dist/leagues-entry.d.ts +4 -2
- package/dist/leagues-entry.js +2 -1
- package/dist/leagues.d.ts +14 -4
- package/dist/leagues.js +146 -5
- package/dist/look.d.ts +3 -3
- package/dist/look.js +39 -6
- package/dist/outline.d.ts +1 -1
- package/dist/outline.js +30 -2
- package/dist/pageant.d.ts +120 -0
- package/dist/pageant.draw.d.ts +21 -0
- package/dist/pageant.draw.js +171 -0
- package/dist/pageant.js +207 -0
- package/dist/pageant.style.d.ts +8 -0
- package/dist/pageant.style.js +42 -0
- package/dist/png.d.ts +8 -1
- package/dist/png.js +21 -1
- package/dist/pool.d.ts +176 -0
- package/dist/pool.figures.d.ts +304 -0
- package/dist/pool.figures.js +144 -0
- package/dist/pool.js +304 -0
- package/dist/pool.kit.d.ts +48 -0
- package/dist/pool.kit.js +141 -0
- package/dist/pool.labels.d.ts +20 -0
- package/dist/pool.labels.js +257 -0
- package/dist/pool.race.d.ts +65 -0
- package/dist/pool.race.js +140 -0
- package/dist/pool.replay.d.ts +46 -0
- package/dist/pool.replay.js +127 -0
- package/dist/pool.section.d.ts +13 -0
- package/dist/pool.section.js +83 -0
- package/dist/pool.style.d.ts +20 -0
- package/dist/pool.style.js +169 -0
- package/dist/pool.types.d.ts +145 -0
- package/dist/pool.types.js +3 -0
- package/dist/pool.words.d.ts +13 -0
- package/dist/pool.words.js +101 -0
- package/dist/ranges.names.js +47 -3
- package/dist/replay-entry.d.ts +3 -0
- package/dist/replay-entry.js +2 -0
- package/dist/restyle.js +1 -1
- package/dist/score.js +70 -5
- package/dist/sheet.d.ts +3 -0
- package/dist/sheet.js +7 -3
- package/dist/skating/describe.d.ts +7 -0
- package/dist/skating/describe.js +44 -0
- package/dist/skating/events.d.ts +24 -0
- package/dist/skating/events.js +47 -0
- package/dist/skating/figure.d.ts +24 -0
- package/dist/skating/figure.js +37 -0
- package/dist/skating/long.d.ts +54 -0
- package/dist/skating/long.js +111 -0
- package/dist/skating/parts.d.ts +43 -0
- package/dist/skating/parts.js +40 -0
- package/dist/skating/plan.d.ts +55 -0
- package/dist/skating/plan.js +197 -0
- package/dist/skating/short.d.ts +47 -0
- package/dist/skating/short.js +89 -0
- package/dist/skating/track.d.ts +66 -0
- package/dist/skating/track.js +94 -0
- package/dist/skating/types.d.ts +57 -0
- package/dist/skating/types.js +3 -0
- package/dist/skating-entry.d.ts +17 -0
- package/dist/skating-entry.js +11 -0
- package/dist/skating.style.d.ts +4 -0
- package/dist/skating.style.js +24 -0
- package/dist/strings.d.ts +296 -0
- package/dist/strings.js +328 -11
- package/dist/style.d.ts +2 -0
- package/dist/style.js +11 -1
- package/dist/surfaces/artistic.d.ts +9 -0
- package/dist/surfaces/artistic.js +94 -0
- package/dist/surfaces/athletics.d.ts +16 -0
- package/dist/surfaces/athletics.js +132 -0
- package/dist/surfaces/baseball.js +15 -0
- package/dist/surfaces/basketball.floor.d.ts +27 -0
- package/dist/surfaces/basketball.floor.js +83 -0
- package/dist/surfaces/basketball.gear.d.ts +27 -0
- package/dist/surfaces/basketball.gear.js +90 -0
- package/dist/surfaces/basketball.js +43 -21
- package/dist/surfaces/curling.d.ts +16 -0
- package/dist/surfaces/curling.fold.d.ts +67 -0
- package/dist/surfaces/curling.fold.js +107 -0
- package/dist/surfaces/curling.js +37 -3
- package/dist/surfaces/dive.d.ts +9 -0
- package/dist/surfaces/dive.js +128 -0
- package/dist/surfaces/field.d.ts +7 -2
- package/dist/surfaces/field.js +15 -9
- package/dist/surfaces/hockey.js +1 -0
- package/dist/surfaces/net.d.ts +2 -1
- package/dist/surfaces/net.js +160 -15
- package/dist/surfaces/polo.d.ts +12 -0
- package/dist/surfaces/polo.js +159 -0
- package/dist/surfaces/pool.d.ts +27 -0
- package/dist/surfaces/pool.js +198 -0
- package/dist/surfaces/skating.d.ts +6 -0
- package/dist/surfaces/skating.js +62 -0
- package/dist/surfaces/soccer.js +2 -1
- package/dist/surfaces/surface.d.ts +44 -0
- package/dist/surfaces/venue.d.ts +15 -0
- package/dist/surfaces/venue.js +95 -0
- package/dist/tenants.d.ts +77 -0
- package/dist/tenants.data.d.ts +8 -0
- package/dist/tenants.data.js +945 -0
- package/dist/tenants.js +46 -0
- package/dist/turn-entry.d.ts +12 -0
- package/dist/turn-entry.js +7 -0
- package/dist/turn.camera.d.ts +105 -0
- package/dist/turn.camera.js +168 -0
- package/dist/turn.dom.d.ts +40 -0
- package/dist/turn.dom.js +351 -0
- package/dist/turn.kit.d.ts +86 -0
- package/dist/turn.kit.js +110 -0
- package/dist/turn.markup.d.ts +48 -0
- package/dist/turn.markup.js +108 -0
- package/dist/turn.model.d.ts +78 -0
- package/dist/turn.model.js +91 -0
- package/dist/turn.style.d.ts +5 -0
- package/dist/turn.style.js +55 -0
- package/dist/types.d.ts +183 -18
- package/dist/units.d.ts +1 -1
- package/dist/units.js +15 -1
- package/dist/uprights.board.d.ts +17 -0
- package/dist/uprights.board.js +59 -0
- package/dist/uprights.d.ts +37 -0
- package/dist/uprights.fields.d.ts +32 -0
- package/dist/uprights.fields.js +172 -0
- package/dist/uprights.goal.d.ts +49 -0
- package/dist/uprights.goal.js +69 -0
- package/dist/uprights.js +160 -0
- package/dist/uprights.more.d.ts +7 -0
- package/dist/uprights.more.js +183 -0
- package/dist/uprights.net.d.ts +12 -0
- package/dist/uprights.net.js +109 -0
- package/dist/uprights.netkit.d.ts +28 -0
- package/dist/uprights.netkit.js +67 -0
- package/dist/uprights.pools.d.ts +8 -0
- package/dist/uprights.pools.js +190 -0
- package/dist/uprights.rinks.d.ts +27 -0
- package/dist/uprights.rinks.js +106 -0
- package/dist/uprights.track.d.ts +50 -0
- package/dist/uprights.track.js +246 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +19 -2
package/dist/draw.js
CHANGED
|
@@ -6,17 +6,33 @@ import { ERA_SPORTS, footballIn, rulesFor, seasonLabel } from "./eras.js";
|
|
|
6
6
|
import { FOOTBALL } from "./geometry.js";
|
|
7
7
|
import { renderLayers } from "./layers.js";
|
|
8
8
|
import { groundById, groundNameIn, groundOfficialName } from "./grounds.js";
|
|
9
|
-
import { courtFor, isNetSport } from "./court.js";
|
|
9
|
+
import { BOARD, courtFor, hasSetsBoard, isNetLeague, isNetSport } from "./court.js";
|
|
10
10
|
import { cricketBoardMarkup } from "./cricket.board.js";
|
|
11
|
+
import { athleticsPlan } from "./athletics/plan.js";
|
|
12
|
+
import { skatingPlan } from "./skating/plan.js";
|
|
13
|
+
import { skatingAbout, skatingSentences } from "./skating/describe.js";
|
|
14
|
+
import { skatingStyle } from "./skating.style.js";
|
|
15
|
+
import { skatingSurface } from "./surfaces/skating.js";
|
|
16
|
+
import { athleticsAbout, athleticsSentences } from "./athletics/describe.js";
|
|
17
|
+
import { athleticsStyle, athleticsTrackRules } from "./athletics.style.js";
|
|
18
|
+
import { athleticsSurface, pitchGroup } from "./surfaces/athletics.js";
|
|
11
19
|
import { fieldPlan, isFieldSport } from "./fields.js";
|
|
12
20
|
import { fieldScoreText, fieldTotal } from "./field.score.js";
|
|
13
21
|
import { fieldsStyle } from "./fields.style.js";
|
|
14
22
|
import { resolveHomeSide } from "./homeside.js";
|
|
15
23
|
import { LEAGUES, chooseLeague, courtOf, diamondOf, fieldOf, hasHistory, leagueSeason, leagueSize, netOf, rinkOf, sheetOf } from "./leagues.js";
|
|
16
|
-
import { diamondFence } from "./leagues/baseball.js";
|
|
17
|
-
import { fenceFor, parkById, parkNameIn, parkOfficialName } from "./parks.js";
|
|
24
|
+
import { MLB_DIAMOND, diamondFence } from "./leagues/baseball.js";
|
|
25
|
+
import { fenceFor, parkById, parkNameIn, parkOfficialName, parkWallsIn } from "./parks.js";
|
|
18
26
|
import { boardMarkup, endText } from "./curling.board.js";
|
|
19
27
|
import { sheetFor } from "./sheet.js";
|
|
28
|
+
import { commonOf, isPoolSport, swimPlan, poloPlan, divePlan, artisticPlan } from "./pool.js";
|
|
29
|
+
import { natatorium } from "./surfaces/venue.js";
|
|
30
|
+
import { poolDescription } from "./pool.words.js";
|
|
31
|
+
import { KYOGIJO_POOL_STYLE, poolStyle } from "./pool.style.js";
|
|
32
|
+
import { swimming } from "./surfaces/pool.js";
|
|
33
|
+
import { waterPolo } from "./surfaces/polo.js";
|
|
34
|
+
import { diving } from "./surfaces/dive.js";
|
|
35
|
+
import { artistic } from "./surfaces/artistic.js";
|
|
20
36
|
import { cropToRange, describeRange, rangeLabel, shows } from "./ranges.js";
|
|
21
37
|
import { badgeMarkup, bannerMarkup, plateMarkup, stateOf } from "./score.js";
|
|
22
38
|
import { setsBoardMarkup } from "./sets.board.js";
|
|
@@ -24,13 +40,21 @@ import { KYOGIJO_STRINGS, fill } from "./strings.js";
|
|
|
24
40
|
import { courtStyle } from "./courts.style.js";
|
|
25
41
|
import { annotate, annotated, wideView } from "./annotate.js";
|
|
26
42
|
import { KYOGIJO_BACKGROUND_STYLE, laidOn } from "./background.js";
|
|
43
|
+
import { KYOGIJO_HARDWOOD_STYLE, resolveHardwood } from "./hardwood.js";
|
|
44
|
+
import { WALL_SEATS, densityOf, layoutOf as crowdLayout, resolveCrowd } from "./crowd.js";
|
|
45
|
+
import { drawCrowd, KYOGIJO_CROWD_STYLE } from "./crowd.draw.js";
|
|
46
|
+
import { parkLayout } from "./crowd.park.js";
|
|
27
47
|
import { featureColours, featureStyle } from "./features.js";
|
|
28
48
|
import { isLook, lookStyle } from "./look.js";
|
|
29
49
|
import { lineLook, restyleLines } from "./restyle.js";
|
|
30
50
|
import { KYOGIJO_MARK_STYLE } from "./mark.js";
|
|
31
|
-
import {
|
|
51
|
+
import { outlineOf } from "./outline.js";
|
|
52
|
+
import { autoScore, pageantLayout, pageantOf, ratioOf } from "./pageant.js";
|
|
53
|
+
import { emblemMarkup, picturesMarkup } from "./pageant.draw.js";
|
|
54
|
+
import { KYOGIJO_PAGEANT_MOTION, KYOGIJO_PAGEANT_STYLE } from "./pageant.style.js";
|
|
55
|
+
import { KYOGIJO_BASE_STYLE, KYOGIJO_CURLING_STYLE, KYOGIJO_FOLD_STYLE, KYOGIJO_NOTE_STYLE, KYOGIJO_WALLS_STYLE } from "./style.js";
|
|
32
56
|
import { convertLength } from "./units.js";
|
|
33
|
-
import { baseball } from "./surfaces/baseball.js";
|
|
57
|
+
import { baseball, fenceOutline } from "./surfaces/baseball.js";
|
|
34
58
|
import { curling } from "./surfaces/curling.js";
|
|
35
59
|
import { netCourt } from "./surfaces/net.js";
|
|
36
60
|
import { fieldSurface } from "./surfaces/field.js";
|
|
@@ -47,7 +71,7 @@ import { abbreviate, capitals } from "./text.js";
|
|
|
47
71
|
import { VERSION } from "./version.js";
|
|
48
72
|
export { isPortrait };
|
|
49
73
|
/** The sports that can be drawn, in the order a menu lists them. */
|
|
50
|
-
export const SPORTS = ["nfl", "cfl", "ifaf", "nhl", "nba", "mlb", "soccer", "curling", "tennis", "volleyball", "badminton", "pickleball", "tabletennis", "lacrosse", "rugby", "afl", "gaelic", "cricket", "fieldhockey", "handball"];
|
|
74
|
+
export const SPORTS = ["nfl", "cfl", "ifaf", "nhl", "nba", "mlb", "soccer", "curling", "tennis", "volleyball", "badminton", "pickleball", "tabletennis", "padel", "squash", "racquetball", "sepaktakraw", "teqball", "roundnet", "netball", "swimming", "waterpolo", "diving", "artisticswimming", "athletics", "skating", "lacrosse", "rugby", "afl", "gaelic", "cricket", "fieldhockey", "handball"];
|
|
51
75
|
/** The CSS custom property each colour of `Colors` sets. */
|
|
52
76
|
export const COLOR_PROPERTIES = {
|
|
53
77
|
apron: "--ky-apron",
|
|
@@ -90,7 +114,8 @@ const isScore = (o) => o.home !== undefined && o.away !== undefined;
|
|
|
90
114
|
function minSets(sport, league) {
|
|
91
115
|
if (sport === "volleyball")
|
|
92
116
|
return league === "fivb-beach" ? 3 : 5;
|
|
93
|
-
|
|
117
|
+
// Table tennis, squash (World Squash Singles Rules 2.3) and racquetball (IRF Rule 1.5) are played to the best of five games.
|
|
118
|
+
return sport === "tabletennis" || sport === "squash" || sport === "racquetball" ? 5 : 3;
|
|
94
119
|
}
|
|
95
120
|
/** The standard statuses, spelled out. */
|
|
96
121
|
const FINAL = /^(?:final|f\b|ft\b|試合終了|終了)/i;
|
|
@@ -177,7 +202,7 @@ const safeLength = (value) => {
|
|
|
177
202
|
const PATTERNS = ["bands", "wide", "narrow", "lengthwise", "checks", "diagonal", "circles", "rings", "fan", "none"];
|
|
178
203
|
const GOALS = ["box", "line", "strip", "none"];
|
|
179
204
|
/** The pitch the options ask for: a `pitch` of the caller's, else the size a `ground` publishes, else none (the international pitch). */
|
|
180
|
-
function pitchOf(o) {
|
|
205
|
+
export function pitchOf(o) {
|
|
181
206
|
if (o.pitch !== undefined)
|
|
182
207
|
return o.pitch;
|
|
183
208
|
const ground = groundById(o.ground);
|
|
@@ -199,10 +224,30 @@ function buildSoccer(o, common, scored, unit) {
|
|
|
199
224
|
frame: frame === undefined ? undefined : { id: frame.id, matrix: frameMatrix(frame, spec) },
|
|
200
225
|
}, common);
|
|
201
226
|
}
|
|
227
|
+
/** The athletics stadium: its plan, with the soccer pitch laid inside the track as the soccer builder draws it. */
|
|
228
|
+
function buildAthletics(o, common) {
|
|
229
|
+
const plan = athleticsPlan({ ...o, detail: common.detail });
|
|
230
|
+
let embedded;
|
|
231
|
+
if (plan.pitch !== undefined) {
|
|
232
|
+
const spec = plan.pitch.spec;
|
|
233
|
+
const goals = common.goals ?? "box";
|
|
234
|
+
const pitch = soccer({ spec, layout: layoutOf(spec), half: undefined, pattern: "bands", goals, flags: common.detail === "full", frame: undefined }, { ...common, portrait: false, margin: 0, score: false, landmarks: false, id: `${common.id}-pitch` });
|
|
235
|
+
embedded = { base: pitchGroup(pitch.base), lines: pitch.lines, defs: pitch.defs };
|
|
236
|
+
}
|
|
237
|
+
// A cropped view has less ground round it than the stadium whole.
|
|
238
|
+
return athleticsSurface(common, plan, embedded, common.margin ?? (o.range === undefined ? 20 : 5));
|
|
239
|
+
}
|
|
240
|
+
/** The skating rink: its plan, drawn as it lies. */
|
|
241
|
+
function buildSkating(o, common) {
|
|
242
|
+
const plan = skatingPlan({ ...o, detail: common.detail });
|
|
243
|
+
return skatingSurface(common, plan, common.margin ?? (o.range === undefined ? 20 : 5));
|
|
244
|
+
}
|
|
245
|
+
/** One text for both ends or sides, or a pair of them. */
|
|
246
|
+
const pair = (text) => (text === undefined ? undefined : typeof text === "string" ? [text, text] : text);
|
|
202
247
|
/** The surface the options ask for, whole: before it is cropped to a `range`. */
|
|
203
248
|
function buildWhole(o, portrait, id, score, scored, extra) {
|
|
204
249
|
const court = o.court;
|
|
205
|
-
const baselineText = court?.baselineText
|
|
250
|
+
const baselineText = pair(court?.baselineText);
|
|
206
251
|
const unit = lengthUnit(o);
|
|
207
252
|
const common = {
|
|
208
253
|
portrait,
|
|
@@ -212,12 +257,13 @@ function buildWhole(o, portrait, id, score, scored, extra) {
|
|
|
212
257
|
stripes: o.stripes !== false,
|
|
213
258
|
id,
|
|
214
259
|
score,
|
|
215
|
-
court: { baselineText, centreLogo: safeLogo(court?.centreLogo), paintThreePoint: court?.paintThreePoint },
|
|
260
|
+
court: { baselineText, centreLogo: safeLogo(court?.centreLogo), paintThreePoint: court?.paintThreePoint, hardwood: resolveHardwood(court, o.detail === "simple", o.width), sidelineText: pair(court?.sidelineText) },
|
|
216
261
|
features: extra.features,
|
|
217
262
|
mow: PATTERNS.includes(o.stripePattern) ? o.stripePattern : undefined,
|
|
218
263
|
goals: GOALS.includes(o.goals) ? o.goals : undefined,
|
|
219
264
|
goalMarkup: typeof o.goalMarkup === "function" ? o.goalMarkup : undefined,
|
|
220
265
|
landmarks: o.landmarks === true,
|
|
266
|
+
pageant: extra.pageant === true ? true : undefined,
|
|
221
267
|
};
|
|
222
268
|
// A league's rulebook (`league`), else the markings of the season asked for (`year`), today's without one.
|
|
223
269
|
const { sport, league } = chooseLeague(o);
|
|
@@ -248,14 +294,33 @@ function buildWhole(o, portrait, id, score, scored, extra) {
|
|
|
248
294
|
case "curling": {
|
|
249
295
|
const plan = sheetFor(o);
|
|
250
296
|
// A score always shows the whole sheet, for the board's sake.
|
|
251
|
-
|
|
297
|
+
const words = KYOGIJO_STRINGS[o.language === "ja" ? "ja" : "en"].ranges.folded;
|
|
298
|
+
const labels = portrait ? [words.top, words.bottom] : [words.left, words.right];
|
|
299
|
+
return curling(common, { pebble: plan.pebble, guardZone: plan.guardZone, show: scored ? "sheet" : plan.show, end: plan.end, figures: plan.spec, fold: { panels: plan.panels, fit: plan.fit, labels } });
|
|
252
300
|
}
|
|
253
301
|
case "tennis":
|
|
254
302
|
case "volleyball":
|
|
255
303
|
case "badminton":
|
|
256
304
|
case "pickleball":
|
|
257
305
|
case "tabletennis":
|
|
306
|
+
case "padel":
|
|
307
|
+
case "squash":
|
|
308
|
+
case "racquetball":
|
|
309
|
+
case "sepaktakraw":
|
|
310
|
+
case "teqball":
|
|
311
|
+
case "roundnet":
|
|
312
|
+
case "netball":
|
|
258
313
|
return netCourt(common, courtFor({ ...o, sport }));
|
|
314
|
+
case "swimming":
|
|
315
|
+
if (o.pool?.venue !== undefined)
|
|
316
|
+
return natatorium(common, o.pool, commonOf(o.pool), o.language === "ja" ? "ja" : "en");
|
|
317
|
+
return swimming(common, swimPlan({ ...o, sport }));
|
|
318
|
+
case "waterpolo":
|
|
319
|
+
return waterPolo(common, poloPlan(o));
|
|
320
|
+
case "diving":
|
|
321
|
+
return diving(common, divePlan(o));
|
|
322
|
+
case "artisticswimming":
|
|
323
|
+
return artistic(common, artisticPlan(o));
|
|
259
324
|
case "lacrosse":
|
|
260
325
|
case "rugby":
|
|
261
326
|
case "afl":
|
|
@@ -264,18 +329,91 @@ function buildWhole(o, portrait, id, score, scored, extra) {
|
|
|
264
329
|
case "fieldhockey":
|
|
265
330
|
case "handball":
|
|
266
331
|
return fieldSurface(common, fieldPlan({ ...o, sport, detail: common.detail }), { goals: common.goals });
|
|
332
|
+
case "athletics":
|
|
333
|
+
return buildAthletics({ ...o, sport }, common);
|
|
334
|
+
case "skating":
|
|
335
|
+
return buildSkating({ ...o, sport }, common);
|
|
267
336
|
default:
|
|
268
337
|
throw new Error(`Unknown sport "${String(o.sport)}": use one of ${SPORTS.join(", ")}`);
|
|
269
338
|
}
|
|
270
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* The crowd the options ask for, or `undefined` for none: a special game has none, and nor has a folded curling sheet (its houses are enlarged over where the stands would be, so the
|
|
342
|
+
* stands would show only as strips behind the panels). A score on a curling sheet shows the whole sheet, which has its stands.
|
|
343
|
+
*/
|
|
344
|
+
function crowdOf(o) {
|
|
345
|
+
if (o.host !== undefined)
|
|
346
|
+
return undefined;
|
|
347
|
+
if (chooseLeague(o).sport === "curling" && !isScore(o) && sheetFor(o).show === "folded")
|
|
348
|
+
return undefined;
|
|
349
|
+
return resolveCrowd(o.crowd);
|
|
350
|
+
}
|
|
271
351
|
/** The surface the options ask for, cropped to their `range` when they have one (a curling sheet's named ranges are its builder's own: `sheet.show`). */
|
|
272
352
|
function buildSurface(o, portrait, id, score, scored, extra) {
|
|
273
|
-
const
|
|
353
|
+
const crowd = crowdOf(o);
|
|
354
|
+
const surface = crowd === undefined ? buildWhole(o, portrait, id, score, scored, extra) : buildCrowded(o, portrait, id, score, scored, extra, crowd);
|
|
274
355
|
// A score on a curling sheet shows all of it, for the board's sake.
|
|
275
356
|
if (o.range === undefined || (scored && chooseLeague(o).sport === "curling"))
|
|
276
357
|
return surface;
|
|
358
|
+
// A close-up of a start is the athletics builder's own view, turned to the start's line.
|
|
359
|
+
if (chooseLeague(o).sport === "athletics" && rangeLabel(o) === "start")
|
|
360
|
+
return surface;
|
|
277
361
|
const given = typeof o.margin === "number" && Number.isFinite(o.margin) && o.margin >= 0 ? convertLength(o.margin, lengthUnit(o), "ft") : undefined;
|
|
278
|
-
return cropToRange(surface, o, { id, margin: extra.room ?? extra.margin ?? given, panel: score });
|
|
362
|
+
return cropToRange(surface, o, { id, margin: extra.room ?? extra.margin ?? given ?? surface.crowdMargin, panel: score });
|
|
363
|
+
}
|
|
364
|
+
/** The fence of the ballpark the options ask for, as the drawing has it, and how far behind home plate the backstop stands. */
|
|
365
|
+
function parkFence(o) {
|
|
366
|
+
const unit = lengthUnit(o);
|
|
367
|
+
const { league } = chooseLeague(o);
|
|
368
|
+
if (league?.family === "baseball") {
|
|
369
|
+
const diamond = diamondOf(league, o.year);
|
|
370
|
+
return { points: fenceFor(parkById(o.park), feetFence(o.fence, unit), o.year, diamondFence(diamond)), backstop: diamond.backstop };
|
|
371
|
+
}
|
|
372
|
+
return { points: fenceFor(parkById(o.park), feetFence(o.fence, unit), o.year), backstop: MLB_DIAMOND.backstop };
|
|
373
|
+
}
|
|
374
|
+
/** The stands round a surface, laid out from its box (or, for a ballpark, from its fence). */
|
|
375
|
+
function standsFor(o, surface, crowd) {
|
|
376
|
+
const { sport } = chooseLeague(o);
|
|
377
|
+
const awaySide = resolveHomeSide({ ...o, sport }) === "left" ? "right" : "left";
|
|
378
|
+
if (sport === "mlb") {
|
|
379
|
+
const { points, backstop } = parkFence(o);
|
|
380
|
+
// The walls that have seats on top of them: the ones the package knows (Fenway Park's Green Monster), or, asked for, every wall of 25 feet or more.
|
|
381
|
+
const park = parkById(o.park);
|
|
382
|
+
const walls = park === undefined || crowd.wallSeats === false ? [] : parkWallsIn(park, o.year).filter((w) => (crowd.wallSeats === true ? w.height_ft >= 25 : w.label !== undefined && WALL_SEATS[park.id] === w.label));
|
|
383
|
+
return parkLayout({ fence: fenceOutline(points), backstop, crowd, seatsOnWalls: walls.map((w) => ({ from: w.from, to: w.to, height: w.height_ft })) });
|
|
384
|
+
}
|
|
385
|
+
return crowdLayout({ sport, box: surface.whole ?? surface.box ?? { x: 0, y: 0, w: 100, h: 100 }, crowd, awaySide, ground: o.ground });
|
|
386
|
+
}
|
|
387
|
+
/** True when the drawing is asked to be small: `detail: "simple"` or a `width` under 480 pixels. */
|
|
388
|
+
const smallDrawing = (o) => {
|
|
389
|
+
const w = typeof o.width === "number" ? o.width : typeof o.width === "string" && /^\s*\d+(?:\.\d+)?(?:px)?\s*$/.test(o.width) ? parseFloat(o.width) : undefined;
|
|
390
|
+
return o.detail === "simple" || (w !== undefined && w < 480);
|
|
391
|
+
};
|
|
392
|
+
/** A surface with the stands and the crowd round it: the ground is made as wide as the stands need, then the stands are laid on it under the surface. */
|
|
393
|
+
function buildCrowded(o, portrait, id, score, scored, extra, crowd) {
|
|
394
|
+
let margin = extra.margin;
|
|
395
|
+
if (!(typeof o.margin === "number" && Number.isFinite(o.margin) && o.margin >= 0)) {
|
|
396
|
+
// How much ground the stands need beyond what the surface has with none, then the surface is made again with it.
|
|
397
|
+
const probe = buildWhole({ ...o, margin: 0 }, portrait, id, score, scored, { ...extra, margin: undefined });
|
|
398
|
+
const stands = standsFor(o, probe, crowd);
|
|
399
|
+
const view = wideView(probe);
|
|
400
|
+
const have = { left: stands.box.x - view.x, right: view.x + view.w - stands.box.x - stands.box.w, top: stands.box.y - view.y, bottom: view.y + view.h - stands.box.y - stands.box.h };
|
|
401
|
+
const need = Math.max(stands.reach.left - have.left, stands.reach.right - have.right, stands.reach.top - have.top, stands.reach.bottom - have.bottom);
|
|
402
|
+
margin = Math.max(margin ?? 0, need + 6);
|
|
403
|
+
}
|
|
404
|
+
const surface = buildWhole(o, portrait, id, score, scored, { ...extra, margin });
|
|
405
|
+
const stands = standsFor(o, surface, crowd);
|
|
406
|
+
const seats = stands.seats;
|
|
407
|
+
const ground = groundById(o.ground)?.capacity;
|
|
408
|
+
const fine = crowd.detail === "fine" || (crowd.detail === "auto" && !smallDrawing(o));
|
|
409
|
+
const first = /^<[^>]*\/>/.exec(surface.base)?.[0] ?? "";
|
|
410
|
+
const bg = /class="([^"]*)"/.exec(first)?.[1] ?? "ky-apron";
|
|
411
|
+
const drawn = drawCrowd(stands, { id, crowd, density: densityOf(crowd, seats, ground), fine, ground: bg });
|
|
412
|
+
surface.base = first + drawn.markup + surface.base.slice(first.length);
|
|
413
|
+
surface.defs += drawn.defs;
|
|
414
|
+
surface.lines += drawn.over;
|
|
415
|
+
surface.crowdMargin = margin;
|
|
416
|
+
return surface;
|
|
279
417
|
}
|
|
280
418
|
/** The custom properties a drawing sets on its root, from the options' colours. */
|
|
281
419
|
function colourStyle(o, teams, features) {
|
|
@@ -315,6 +453,23 @@ function colourStyle(o, teams, features) {
|
|
|
315
453
|
props.push(`--ky-left-ink:${leftInk}`);
|
|
316
454
|
if (rightInk !== undefined)
|
|
317
455
|
props.push(`--ky-right-ink:${rightInk}`);
|
|
456
|
+
// A hardwood floor's border behind each baseline: the court's colour for it, else the team's, else the border's.
|
|
457
|
+
if (o.court?.hardwood !== undefined && o.court.hardwood !== false) {
|
|
458
|
+
const ends = pair(o.court.baselineColor);
|
|
459
|
+
const endLeft = safeColour(ends?.[0]) ?? left;
|
|
460
|
+
const endRight = safeColour(ends?.[1]) ?? right;
|
|
461
|
+
if (endLeft !== undefined)
|
|
462
|
+
props.push(`--ky-hw-end-left:${endLeft}`);
|
|
463
|
+
if (endRight !== undefined)
|
|
464
|
+
props.push(`--ky-hw-end-right:${endRight}`);
|
|
465
|
+
}
|
|
466
|
+
// The crowd's colours: the home fans in the home team's colour and the visitors in the other's, or the colours the crowd names.
|
|
467
|
+
const crowd = crowdOf(o);
|
|
468
|
+
if (crowd !== undefined) {
|
|
469
|
+
const homeLeft = resolveHomeSide({ ...o, sport: chooseLeague(o).sport }) === "left";
|
|
470
|
+
props.push(`--ky-cr-home:${safeColour(crowd.home) ?? (homeLeft ? left : right) ?? `var(--ky-${homeLeft ? "left" : "right"})`}`);
|
|
471
|
+
props.push(`--ky-cr-away:${safeColour(crowd.away) ?? (homeLeft ? right : left) ?? `var(--ky-${homeLeft ? "right" : "left"})`}`);
|
|
472
|
+
}
|
|
318
473
|
return props.join(";");
|
|
319
474
|
}
|
|
320
475
|
/** The round mark of a team, when its own `mark` or the score's `markShape` asks for one: the fit, the colour of its disc and the ink of its initials. */
|
|
@@ -348,10 +503,46 @@ function teamView(team, side, role, winner, uppercase, sport, markShape) {
|
|
|
348
503
|
innings: sport === "cricket" && Array.isArray(team.innings) ? team.innings.map((value) => (typeof value === "number" ? (Number.isFinite(value) ? String(value) : "") : typeof value === "string" ? value.trim().slice(0, 7) : "")) : undefined,
|
|
349
504
|
};
|
|
350
505
|
}
|
|
506
|
+
/** A team as the pageant lays it out. */
|
|
507
|
+
function pageantTeam(view, team) {
|
|
508
|
+
return { side: view.side, role: view.role, name: view.name, abbr: view.abbr, state: view.state, href: view.logo, fit: team.markFit === "contain" ? "contain" : "cover", ratio: ratioOf(team), color: safeColour(team.color), ink: safeColour(team.textColor) };
|
|
509
|
+
}
|
|
510
|
+
/** True when the drawing is asked to be small: its `width` is under 320 pixels. The pageant then leaves out its fabric, hairlines and name pills. */
|
|
511
|
+
function tiny(width) {
|
|
512
|
+
const px = typeof width === "number" ? width : typeof width === "string" && /^\d+(?:\.\d+)?(?:px)?$/.test(width.trim()) ? Number.parseFloat(width) : undefined;
|
|
513
|
+
return px !== undefined && Number.isFinite(px) && px < 320;
|
|
514
|
+
}
|
|
515
|
+
/** The teams that fill the first and the second place of a surface: a diamond's outfield is the visitors' and its infield the home team's, whichever side each is on. */
|
|
516
|
+
function pageantPair(surface, t) {
|
|
517
|
+
const l = pageantTeam(t.left, t.leftTeam);
|
|
518
|
+
const r = pageantTeam(t.right, t.rightTeam);
|
|
519
|
+
return surface.pageant?.byRole === true && l.role === "home" ? [r, l] : [l, r];
|
|
520
|
+
}
|
|
521
|
+
function pageantParts(o, surface, p, t) {
|
|
522
|
+
const [first, second] = pageantPair(surface, t);
|
|
523
|
+
const layout = pageantLayout({ pageant: p, surface, outline: outlineOf(o), left: first, right: second, turned: surface.turned, sport: t.sport, plate: t.plate });
|
|
524
|
+
if (layout === undefined)
|
|
525
|
+
return undefined;
|
|
526
|
+
// Cropped to a range, an emblem that does not lie wholly in what is shown is left out.
|
|
527
|
+
const shown = surface.whole === undefined ? undefined : surface.box;
|
|
528
|
+
if (layout.emblem !== undefined && shown !== undefined) {
|
|
529
|
+
const e = layout.emblem;
|
|
530
|
+
if (e.cx - e.r < shown.x || e.cx + e.r > shown.x + shown.w || e.cy - e.r < shown.y || e.cy + e.r > shown.y + shown.h)
|
|
531
|
+
delete layout.emblem;
|
|
532
|
+
}
|
|
533
|
+
const draw = { id: t.id, turned: surface.turned, simple: o.detail === "simple" || tiny(o.width), parts: t.parts };
|
|
534
|
+
const pictures = picturesMarkup(layout, p, draw);
|
|
535
|
+
const emblem = emblemMarkup(layout, p, draw);
|
|
536
|
+
return { pictures: pictures.markup, names: pictures.names, emblem: emblem.markup, defs: pictures.defs + emblem.defs, plateScale: layout.plateScale };
|
|
537
|
+
}
|
|
351
538
|
const trimmed = (value) => String(Math.round(value * 100) / 100);
|
|
352
539
|
/** What a league's surface is, in a sentence fragment: its size or its bases, from the league's own figures in the unit its rulebook uses. */
|
|
353
540
|
function leagueAbout(league, year, language, sheet) {
|
|
354
541
|
const words = KYOGIJO_STRINGS[language].league;
|
|
542
|
+
if (league.family === "skating")
|
|
543
|
+
return skatingAbout({ sport: "skating", league: league.id }, language);
|
|
544
|
+
if (league.family === "athletics")
|
|
545
|
+
return athleticsAbout({ sport: "athletics", league: league.id, athletics: sheet === undefined ? undefined : undefined }, language);
|
|
355
546
|
// The field sports: their size is the surface drawn (the one the options ask for), in the unit their rulebook prints first.
|
|
356
547
|
if (league.family === "lacrosse" || league.family === "rugby" || league.family === "afl" || league.family === "gaelic" || league.family === "cricket" || league.family === "fieldhockey" || league.family === "handball") {
|
|
357
548
|
const sized = sheet ?? leagueSize(league.family, league.id);
|
|
@@ -370,7 +561,7 @@ function leagueAbout(league, year, language, sheet) {
|
|
|
370
561
|
? rinkOf(league, year)
|
|
371
562
|
: league.family === "curling"
|
|
372
563
|
? (sheet ?? sheetOf(league))
|
|
373
|
-
: league
|
|
564
|
+
: isNetLeague(league)
|
|
374
565
|
? (() => {
|
|
375
566
|
const f = netOf(league);
|
|
376
567
|
return { length: f.length, width: f.widthDoubles ?? f.width };
|
|
@@ -381,7 +572,7 @@ function leagueAbout(league, year, language, sheet) {
|
|
|
381
572
|
})();
|
|
382
573
|
const to = league.unit;
|
|
383
574
|
const text = fill(words.size, { length: trimmed(convertLength(size.length, "ft", to)), width: trimmed(convertLength(size.width, "ft", to)), unit: words.units[to === "m" || to === "yd" ? to : "ft"] });
|
|
384
|
-
return fill(league.family === "volleyball" && league.id === "fivb-beach" ? words.beachVolleyball : words[league.family], { size: text });
|
|
575
|
+
return fill(league.family === "volleyball" && league.id === "fivb-beach" ? words.beachVolleyball : league.family === "volleyball" && league.id === "wpv-sitting" ? words.sittingVolleyball : words[league.family], { size: text });
|
|
385
576
|
}
|
|
386
577
|
/** A drawing's title and description, in a language. */
|
|
387
578
|
export function describe(o) {
|
|
@@ -398,15 +589,19 @@ export function describe(o) {
|
|
|
398
589
|
const ground = pitch?.format === "11" ? groundById(o.ground) : undefined;
|
|
399
590
|
const groundName = ground === undefined ? undefined : groundNameIn(ground, o.year, language);
|
|
400
591
|
// A league other than the sport's own is named by its rulebook's, and its description is made from its figures.
|
|
401
|
-
const
|
|
592
|
+
const trackGround = sport === "athletics" ? groundById(o.ground) : undefined;
|
|
593
|
+
const trackGroundName = trackGround === undefined ? undefined : groundNameIn(trackGround, o.year, language);
|
|
594
|
+
const surfaceName = trackGroundName !== undefined ? fill(words.athletics.ground, { ground: trackGroundName }) : groundName !== undefined ? (language === "ja" ? `${groundName}のピッチ` : `${groundName}, soccer pitch`) : league !== undefined && parkName === undefined ? league.name[language] : parkName === undefined ? (pitch?.format === "futsal" ? words.soccer.futsal : words.surface[sport]) : language === "ja" ? `${parkName}のグラウンド` : `${parkName}, baseball field`;
|
|
402
595
|
const { stop, colon, gap } = words.said;
|
|
403
596
|
const sentence = (text) => (/[.。]$/.test(text) ? text : `${text}${stop}`);
|
|
404
597
|
const metres = (value) => String(Math.round(value * 10) / 10);
|
|
405
598
|
const plan = sport === "curling" ? sheetFor(o) : undefined;
|
|
406
599
|
// A field sport: the surface as drawn, at the size the options ask for.
|
|
407
600
|
const field = isFieldSport(sport) ? fieldPlan({ ...o, sport }) : undefined;
|
|
408
|
-
const
|
|
409
|
-
const
|
|
601
|
+
const pools = isPoolSport(sport) ? poolDescription({ sport, pool: o.pool }, language) : undefined;
|
|
602
|
+
const poolLine = pools === undefined ? "" : pools.after.map((line) => `${gap}${line}`).join("");
|
|
603
|
+
const aboutText = pools !== undefined ? pools.about : sport === "skating" ? skatingAbout(o, language) : sport === "athletics" ? athleticsAbout(o, language) : field !== undefined ? leagueAbout(league ?? LEAGUES[sport][0], o.year, language, { length: field.length, width: field.width }) : plan !== undefined ? leagueAbout(plan.league, o.year, language, plan.spec) : league !== undefined ? leagueAbout(league, o.year, language) : pitch === undefined ? words.about[sport] : fill(pitch.format === "futsal" ? words.soccer.aboutFutsal : words.about.soccer, { length: metres(pitch.length), width: metres(pitch.width) });
|
|
604
|
+
const about = `${words.said.on}${colon}${aboutText}${stop}${sport === "athletics" ? athleticsSentences(o, language) : sport === "skating" ? skatingSentences(o, language) : ""}`;
|
|
410
605
|
const halfLine = pitch !== undefined && !isScore(o) && (o.half === "left" || o.half === "right") ? `${gap}${words.soccer.half}` : "";
|
|
411
606
|
// What the ground says about the size: that it is the published one, or that the ground has none and the international pitch is drawn.
|
|
412
607
|
const year = typeof o.year === "number" && Number.isFinite(o.year) ? o.year : undefined;
|
|
@@ -428,10 +623,21 @@ export function describe(o) {
|
|
|
428
623
|
? `${gap}${fill(words.said.era, { season: seasonLabel(sport, o.year) })}`
|
|
429
624
|
: "";
|
|
430
625
|
// A range says what is shown; a curling score shows all of the sheet, whatever the range.
|
|
431
|
-
const
|
|
626
|
+
const hasData = o.layers !== undefined && o.layers.length > 0;
|
|
627
|
+
// A folded sheet says what is enlarged and where, in place of the range's own sentence.
|
|
628
|
+
const foldedWords = words.ranges.folded;
|
|
629
|
+
const rangeLine = isScore(o) && sport === "curling"
|
|
630
|
+
? ""
|
|
631
|
+
: plan?.show === "folded"
|
|
632
|
+
? `${gap}${[foldedWords[isPortrait(o) ? "tall" : "wide"], foldedWords[plan.panels], hasData ? foldedWords.data : ""].filter((part) => part !== "").join(gap)}`
|
|
633
|
+
: describeRange(o, language, hasData);
|
|
432
634
|
if (!isScore(o))
|
|
433
|
-
return { title: o.title ?? special?.title ?? surfaceName, description: o.description ?? `${about}${parkLine}${eraLine}${rangeLine}` };
|
|
635
|
+
return { title: o.title ?? special?.title ?? surfaceName, description: o.description ?? `${about}${parkLine}${eraLine}${rangeLine}${poolLine}` };
|
|
434
636
|
const winner = winnerOf({ ...o, sport });
|
|
637
|
+
// A pageant says what lies on each half, and the emblem in the middle.
|
|
638
|
+
const pageant = pageantOf(o);
|
|
639
|
+
const pg = KYOGIJO_STRINGS[language].pageant;
|
|
640
|
+
const pageantLine = pageant === undefined ? "" : `${gap}${pageant.style === "stage" ? pg.stage : pg.carpet}${pageant.emblem === undefined ? "" : `${gap}${pageant.emblem.name === undefined ? pg.emblem : fill(pg.emblemNamed, { name: pageant.emblem.name })}`}`;
|
|
435
641
|
const s = (t) => fieldScoreText(sport, t) ?? (t.score === undefined || String(t.score) === "" ? "–" : String(t.score));
|
|
436
642
|
// The sentence follows the drawing: the team on the left is first. The words' {away} and {home} are the first and the second team named.
|
|
437
643
|
const order = resolveHomeSide({ ...o, sport }) === "left" ? [o.home, o.away] : [o.away, o.home];
|
|
@@ -463,12 +669,12 @@ export function describe(o) {
|
|
|
463
669
|
const tb = endText(team.tiebreaks?.[i]);
|
|
464
670
|
return `${endText(value) || "–"}${tb === "" ? "" : ` (${tb})`}`;
|
|
465
671
|
}).join(words.layers.listSep);
|
|
466
|
-
const netLine = !
|
|
672
|
+
const netLine = !hasSetsBoard(sport)
|
|
467
673
|
? ""
|
|
468
674
|
: order
|
|
469
675
|
.flatMap((team) => [
|
|
470
676
|
team.serving === true ? fill(net.serving, { name: team.name }) : "",
|
|
471
|
-
Array.isArray(team.sets) && team.sets.length > 0 ? fill(sport === "
|
|
677
|
+
Array.isArray(team.sets) && team.sets.length > 0 ? fill(BOARD[sport] === "games" ? net.byGame : net.bySet, { name: team.name, list: setList(team) }) : "",
|
|
472
678
|
team.points !== undefined && team.points !== null && String(team.points).trim() !== "" ? fill(net.gamePoints, { name: team.name, points: String(team.points).trim() }) : "",
|
|
473
679
|
])
|
|
474
680
|
.filter((part) => part !== "")
|
|
@@ -493,7 +699,7 @@ export function describe(o) {
|
|
|
493
699
|
: "";
|
|
494
700
|
return {
|
|
495
701
|
title: o.title ?? (status === undefined ? line : `${line}${words.said.sep}${status}`),
|
|
496
|
-
description: o.description ?? `${extra === "" ? "" : `${sentence(extra)}${gap}`}${sentence(line)}${result}${tied}${curlingLine}${netLine}${fieldLine}${gap}${about}${parkLine}${eraLine}${rangeLine}`.trim(),
|
|
702
|
+
description: o.description ?? `${extra === "" ? "" : `${sentence(extra)}${gap}`}${sentence(line)}${result}${tied}${curlingLine}${netLine}${fieldLine}${gap}${about}${parkLine}${eraLine}${rangeLine}${pageantLine}${poolLine}`.trim(),
|
|
497
703
|
};
|
|
498
704
|
}
|
|
499
705
|
/**
|
|
@@ -515,6 +721,9 @@ export function describe(o) {
|
|
|
515
721
|
export function drawKyogijo(o) {
|
|
516
722
|
if (o.host !== undefined && o.range !== undefined)
|
|
517
723
|
return drawKyogijo({ ...o, range: undefined });
|
|
724
|
+
// A special game is a surface set inside another, drawn to scale with no stands.
|
|
725
|
+
if (o.host !== undefined && o.crowd !== undefined)
|
|
726
|
+
return drawKyogijo({ ...o, crowd: undefined });
|
|
518
727
|
const language = o.language === "ja" ? "ja" : "en";
|
|
519
728
|
// The sport as drawn: a league that is a sport of its own (`league: "cfl"` on an `nfl`) is that sport.
|
|
520
729
|
const { sport } = chooseLeague(o);
|
|
@@ -526,6 +735,14 @@ export function drawKyogijo(o) {
|
|
|
526
735
|
// A tall curling sheet is 30 feet wide on the page and a strip under it would be unreadable: its compact size is the board on the ice.
|
|
527
736
|
const asked = isScore(o) ? (o.size === "compact" || o.size === "badge" ? o.size : "full") : "full";
|
|
528
737
|
let sized = asked === "compact" && sport === "curling" && portrait ? "full" : asked;
|
|
738
|
+
// A pageant that sends the score to the strip makes the drawing compact; a tall curling sheet cannot have a strip, so its board stays in the middle and the emblem is left out.
|
|
739
|
+
let pageant = isScore(o) ? pageantOf(o) : undefined;
|
|
740
|
+
if (pageant !== undefined && pageant.score === "strip") {
|
|
741
|
+
if (sport === "curling" && portrait)
|
|
742
|
+
pageant = { ...pageant, score: "centre", emblem: undefined };
|
|
743
|
+
else if (sized === "full")
|
|
744
|
+
sized = "compact";
|
|
745
|
+
}
|
|
529
746
|
const id = drawingId(o);
|
|
530
747
|
const parts = o.parts === true;
|
|
531
748
|
const homeSide = isScore(o) ? resolveHomeSide({ ...o, sport }) : "right";
|
|
@@ -555,6 +772,8 @@ export function drawKyogijo(o) {
|
|
|
555
772
|
const featured = featureColours(sport, o.featureColors);
|
|
556
773
|
const features = featured.length === 0 ? undefined : new Set(featured.map(([name]) => name));
|
|
557
774
|
const walled = sport === "mlb" && ballparkDrawn(ballparkFor(o));
|
|
775
|
+
const hardwood = sport === "nba" && o.court?.hardwood !== undefined && o.court.hardwood !== false;
|
|
776
|
+
const crowded = crowdOf(o) !== undefined;
|
|
558
777
|
const style = colourStyle(o, { left: leftTeam, right: rightTeam, home: isScore(o) ? o.home : undefined, away: isScore(o) ? o.away : undefined }, featured);
|
|
559
778
|
const palette = isLook(o.look) ? o.look : undefined;
|
|
560
779
|
let notes;
|
|
@@ -563,6 +782,8 @@ export function drawKyogijo(o) {
|
|
|
563
782
|
let body;
|
|
564
783
|
let defs = "";
|
|
565
784
|
let grounded = false;
|
|
785
|
+
let foldedSheet = false;
|
|
786
|
+
let pg;
|
|
566
787
|
if (isScore(o) && left !== undefined && right !== undefined && sized === "badge") {
|
|
567
788
|
const badge = badgeMarkup(left, right, { sport, status, parts, id });
|
|
568
789
|
viewBox = `0 0 ${badge.w} ${badge.h}`;
|
|
@@ -576,13 +797,28 @@ export function drawKyogijo(o) {
|
|
|
576
797
|
const guest = buildSurface({ ...o, margin: 0 }, false, id, isScore(o) && sized === "full", isScore(o), more);
|
|
577
798
|
return composeSpecial(o, guest, (host, hostId) => buildSurface(host, false, hostId, false, false, { features: undefined }), id);
|
|
578
799
|
};
|
|
579
|
-
let surface = make({ features });
|
|
800
|
+
let surface = make({ features, pageant: pageant !== undefined });
|
|
801
|
+
// The score of a pageant with no emblem and no board: the plate stays in the middle if the pictures lose little to it, else it goes to the strip and the surface is built again.
|
|
802
|
+
if (pageant !== undefined && pageant.score === "auto") {
|
|
803
|
+
let score = "centre";
|
|
804
|
+
if (isScore(o) && left !== undefined && right !== undefined && leftTeam !== undefined && rightTeam !== undefined && sized === "full") {
|
|
805
|
+
const [first, second] = pageantPair(surface, { left, right, leftTeam, rightTeam });
|
|
806
|
+
score = autoScore({ pageant, surface, outline: outlineOf(o), left: first, right: second, turned: surface.turned, sport, plate: true });
|
|
807
|
+
}
|
|
808
|
+
pageant = { ...pageant, score };
|
|
809
|
+
if (score === "strip") {
|
|
810
|
+
sized = "compact";
|
|
811
|
+
surface = make({ features, pageant: true });
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
foldedSheet = surface.folded === true;
|
|
580
815
|
// A range too small to hold the score's panel: the score goes to the strip under the drawing, as a compact card does.
|
|
581
816
|
if (surface.plateFits === false) {
|
|
582
817
|
sized = "compact";
|
|
583
|
-
surface = buildSurface(o, portrait, id, false, true, { features });
|
|
818
|
+
surface = buildSurface(o, portrait, id, false, true, { features, pageant: pageant !== undefined });
|
|
584
819
|
}
|
|
585
|
-
|
|
820
|
+
// A folded sheet has no margin for the annotations to stand in: they are not drawn on it.
|
|
821
|
+
if (annotated(o) && o.host === undefined && surface.folded !== true) {
|
|
586
822
|
const teams = { home: isScore(o) ? o.home.name : undefined, away: isScore(o) ? o.away.name : undefined };
|
|
587
823
|
notes = annotate({ o, surface, turned: surface.turned, words: KYOGIJO_STRINGS[language], teams });
|
|
588
824
|
// The annotations need room in the margin: unless the options fix it, the surface is drawn again with a margin that holds them.
|
|
@@ -590,7 +826,7 @@ export function drawKyogijo(o) {
|
|
|
590
826
|
const have = surface.ground ?? (surface.box === undefined ? 0 : surface.box.y - wideView(surface).y);
|
|
591
827
|
const roomed = surface.room ?? have;
|
|
592
828
|
if (o.margin === undefined && (notes.need > have + 0.01 || notes.need > roomed + 0.01)) {
|
|
593
|
-
surface = make({ features, margin: notes.need > have + 0.01 ? notes.need : undefined, room: notes.need });
|
|
829
|
+
surface = make({ features, pageant: pageant !== undefined, margin: notes.need > have + 0.01 ? notes.need : undefined, room: notes.need });
|
|
594
830
|
notes = annotate({ o, surface, turned: surface.turned, words: KYOGIJO_STRINGS[language], teams });
|
|
595
831
|
}
|
|
596
832
|
}
|
|
@@ -604,10 +840,19 @@ export function drawKyogijo(o) {
|
|
|
604
840
|
defs = surface.defs + ground.defs;
|
|
605
841
|
let inner = ground.base;
|
|
606
842
|
let teams = "";
|
|
607
|
-
|
|
843
|
+
// A pageant lays each team's picture over its half, in place of the names painted at the ends.
|
|
844
|
+
if (pageant !== undefined && isScore(o) && left !== undefined && right !== undefined && leftTeam !== undefined && rightTeam !== undefined && (sized === "full" || sized === "compact")) {
|
|
845
|
+
pg = pageantParts(o, surface, pageant, { left, right, leftTeam, rightTeam, sport, id, plate: sized === "full", parts });
|
|
846
|
+
if (pg !== undefined) {
|
|
847
|
+
defs += pg.defs;
|
|
848
|
+
if (sized === "full" && pg.plateScale !== 1)
|
|
849
|
+
surface.plate = { ...surface.plate, w: surface.plate.w * pg.plateScale, h: surface.plate.h * pg.plateScale };
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
if (isScore(o) && left !== undefined && right !== undefined && sized === "full" && pg === undefined) {
|
|
608
853
|
const onColour = surface.onColour ?? (sport === "nfl" || sport === "cfl" || sport === "ifaf" || sport === "soccer");
|
|
609
854
|
// A curling sheet, the net sports' courts and a cricket ground have no place for the names: the board names the teams.
|
|
610
|
-
if (sport !== "curling" && !
|
|
855
|
+
if (sport !== "curling" && !hasSetsBoard(sport) && sport !== "cricket") {
|
|
611
856
|
// Cropped to a range, a team's name is written only where its place is in what is shown.
|
|
612
857
|
if (shows(surface, surface.left))
|
|
613
858
|
teams += bannerMarkup(surface.left, left, { turned: surface.turned, onColour, parts, id });
|
|
@@ -616,20 +861,38 @@ export function drawKyogijo(o) {
|
|
|
616
861
|
}
|
|
617
862
|
}
|
|
618
863
|
// The teams are painted under the lines, unless the surface asks for them over (a panel that must hide them).
|
|
619
|
-
|
|
864
|
+
if (pg !== undefined && pageant !== undefined) {
|
|
865
|
+
// A surface cropped with room for its annotations is clipped to the cut; the pictures are cut with it.
|
|
866
|
+
const cut = surface.defs.includes(`id="${id}-rg"`);
|
|
867
|
+
const laid = cut ? el("g", { "clip-path": `url(#${id}-rg)` }, pg.pictures) : pg.pictures;
|
|
868
|
+
inner += pageant.lines !== "over" ? surface.lines + laid + pg.names + pg.emblem : laid + surface.lines + pg.names + pg.emblem;
|
|
869
|
+
}
|
|
870
|
+
else
|
|
871
|
+
inner += surface.bannersOver === true ? surface.lines + teams : teams + surface.lines;
|
|
620
872
|
// Data goes over the markings and under the score's panel.
|
|
621
|
-
const layered = renderLayers(o, o.layers, surface.turned, surface.whole === undefined ? undefined : surface.box);
|
|
873
|
+
const layered = renderLayers(o, o.layers, surface.turned, surface.whole === undefined ? undefined : surface.box, surface.upright);
|
|
622
874
|
inner += surface.frame === undefined || layered.svg === "" ? layered.svg : el("g", { transform: surface.frame }, layered.svg);
|
|
875
|
+
// A surface drawn in parts shows the data again in each enlarged part, with its own ids, hidden from a reader of the drawing (the strip says it). A colour bar sized
|
|
876
|
+
// against the whole sheet falls outside the panels' part of it and is cut away there; one given a `region` inside a panel's part is drawn there, large.
|
|
877
|
+
if (surface.panels !== undefined && layered.svg !== "" && o.layers !== undefined) {
|
|
878
|
+
surface.panels.forEach((panel, i) => {
|
|
879
|
+
const copy = renderLayers({ ...o, id: `${id}-f${i}` }, o.layers, surface.turned);
|
|
880
|
+
if (copy.svg !== "")
|
|
881
|
+
inner += el("g", { class: "ky-fold-data", "aria-hidden": "true", transform: panel.transform, "clip-path": `url(#${panel.clip})` }, copy.svg);
|
|
882
|
+
});
|
|
883
|
+
}
|
|
623
884
|
if (layered.summaries.length > 0 && o.description === undefined) {
|
|
624
885
|
const { colon, gap } = KYOGIJO_STRINGS[language].said;
|
|
625
886
|
description = `${said}${gap}${KYOGIJO_STRINGS[language].layers.data}${colon}${layered.summaries.join(gap)}`;
|
|
626
887
|
}
|
|
888
|
+
if (surface.over !== undefined)
|
|
889
|
+
inner += surface.over;
|
|
627
890
|
if (notes !== undefined && notes.svg !== "") {
|
|
628
891
|
inner += notes.svg;
|
|
629
892
|
if (notes.summaries.length > 0 && o.description === undefined)
|
|
630
893
|
description = `${description}${KYOGIJO_STRINGS[language].said.gap}${notes.summaries.join(KYOGIJO_STRINGS[language].said.gap)}`;
|
|
631
894
|
}
|
|
632
|
-
if (isScore(o) && left !== undefined && right !== undefined && sized === "full") {
|
|
895
|
+
if (isScore(o) && left !== undefined && right !== undefined && sized === "full" && o.plate !== false) {
|
|
633
896
|
const foot = o.label !== undefined && o.label !== "" ? o.label : [status, o.date].filter((part) => part !== undefined && part !== "").join(" · ");
|
|
634
897
|
if (sport === "curling") {
|
|
635
898
|
const board = boardMarkup(surface.plate, left, right, { competition: o.competition, foot: foot === "" ? undefined : foot, total: KYOGIJO_STRINGS[language].said.total }, { turned: surface.turned, parts });
|
|
@@ -640,8 +903,8 @@ export function drawKyogijo(o) {
|
|
|
640
903
|
const board = cricketBoardMarkup(surface.plate, left, right, { competition: o.competition, foot: foot === "" ? undefined : foot, innings: words.innings, total: words.total, overs: words.overs }, { parts });
|
|
641
904
|
inner += el("g", { transform: `translate(${num(surface.plate.cx)} ${num(surface.plate.cy)})${surface.turned ? " rotate(-90)" : ""}` }, board);
|
|
642
905
|
}
|
|
643
|
-
else if (
|
|
644
|
-
const board = setsBoardMarkup(surface.plate, left, right, { competition: o.competition, foot: foot === "" ? undefined : foot, head: KYOGIJO_STRINGS[language].net[sport === "
|
|
906
|
+
else if (hasSetsBoard(sport)) {
|
|
907
|
+
const board = setsBoardMarkup(surface.plate, left, right, { competition: o.competition, foot: foot === "" ? undefined : foot, head: KYOGIJO_STRINGS[language].net[BOARD[sport] === "games" ? "games" : "sets"], points: KYOGIJO_STRINGS[language].net.points }, { parts, least: minSets(sport, chooseLeague(o).league?.id) });
|
|
645
908
|
inner += el("g", { transform: `translate(${num(surface.plate.cx)} ${num(surface.plate.cy)})${surface.turned ? " rotate(-90)" : ""}` }, board);
|
|
646
909
|
}
|
|
647
910
|
else
|
|
@@ -676,14 +939,16 @@ export function drawKyogijo(o) {
|
|
|
676
939
|
"data-orientation": portrait ? "portrait" : "landscape",
|
|
677
940
|
"data-theme": theme,
|
|
678
941
|
"data-palette": palette,
|
|
942
|
+
"data-track": sport === "athletics" ? o.athletics?.track : undefined,
|
|
679
943
|
"data-winner": isScore(o) ? winner : undefined,
|
|
944
|
+
"data-pageant": pg === undefined ? undefined : pageant?.style,
|
|
680
945
|
"data-version": VERSION,
|
|
681
946
|
width: safeLength(o.width),
|
|
682
947
|
height: safeLength(o.height),
|
|
683
948
|
part: parts ? "svg" : undefined,
|
|
684
949
|
style: style === "" ? undefined : style,
|
|
685
950
|
}, `<title id="${titleId}">${esc(title)}</title><desc id="${descId}">${esc(description)}</desc>` +
|
|
686
|
-
(o.style === "none" ? "" : `<style>${KYOGIJO_BASE_STYLE}${left?.round === true || right?.round === true ? `\n${KYOGIJO_MARK_STYLE}` : ""}${sport === "curling" ? `\n${KYOGIJO_CURLING_STYLE}` : ""}${plan === undefined ? "" : `\n${courtStyle(plan.sport, plan.look)}`}${fieldsStyle(sport) === "" ? "" : `\n${fieldsStyle(sport)}`}${palette === undefined ? "" : `\n${lookStyle(palette)}`}${walled ? `\n${KYOGIJO_WALLS_STYLE}` : ""}${features === undefined ? "" : `\n${featureStyle(sport, features)}`}${notes === undefined || notes.svg === "" ? "" : `\n${KYOGIJO_NOTE_STYLE}`}${grounded ? `\n${KYOGIJO_BACKGROUND_STYLE}` : ""}${o.host === undefined ? "" : `\n${specialStyle(hostSport(o.host))}`}</style>`) +
|
|
951
|
+
(o.style === "none" ? "" : `<style>${KYOGIJO_BASE_STYLE}${left?.round === true || right?.round === true || (pg !== undefined && pageant?.style === "stage") || (pg !== undefined && pageant?.emblem?.shape !== "plain" && pageant?.emblem !== undefined) ? `\n${KYOGIJO_MARK_STYLE}` : ""}${pg === undefined ? "" : `\n${KYOGIJO_PAGEANT_STYLE}${pageant?.animate === undefined || pageant.animate === "none" ? "" : `\n${KYOGIJO_PAGEANT_MOTION}`}`}${sport === "curling" ? `\n${KYOGIJO_CURLING_STYLE}` : ""}${foldedSheet ? `\n${KYOGIJO_FOLD_STYLE}` : ""}${isPoolSport(sport) ? `\n${o.pool?.venue !== undefined ? KYOGIJO_POOL_STYLE : poolStyle({ sport, ...commonOf(o.pool ?? {}), race: o.pool?.race !== undefined, section: o.pool?.section !== undefined && o.pool.section !== false })}` : ""}${plan === undefined ? "" : `\n${courtStyle(plan.sport, plan.look)}`}${fieldsStyle(sport) === "" ? "" : `\n${fieldsStyle(sport)}`}${athleticsStyle(sport) === "" ? "" : `\n${athleticsStyle(sport)}`}${skatingStyle(sport) === "" ? "" : `\n${skatingStyle(sport)}`}${palette === undefined ? "" : `\n${lookStyle(palette)}`}${athleticsTrackRules(sport, o.athletics?.track) === "" ? "" : `\n${athleticsTrackRules(sport, o.athletics?.track)}`}${walled ? `\n${KYOGIJO_WALLS_STYLE}` : ""}${hardwood ? `\n${KYOGIJO_HARDWOOD_STYLE}` : ""}${features === undefined ? "" : `\n${featureStyle(sport, features)}`}${notes === undefined || notes.svg === "" ? "" : `\n${KYOGIJO_NOTE_STYLE}`}${grounded ? `\n${KYOGIJO_BACKGROUND_STYLE}` : ""}${crowded ? `\n${KYOGIJO_CROWD_STYLE}` : ""}${o.host === undefined ? "" : `\n${specialStyle(hostSport(o.host))}`}</style>`) +
|
|
687
952
|
(defs === "" ? "" : `<defs>${defs}</defs>`) +
|
|
688
953
|
body);
|
|
689
954
|
return root;
|
|
@@ -703,3 +968,21 @@ export function drawScore(options) {
|
|
|
703
968
|
export function drawBadge(options) {
|
|
704
969
|
return drawKyogijo({ ...options, size: "badge" });
|
|
705
970
|
}
|
|
971
|
+
/**
|
|
972
|
+
* The stands round a surface as data: the rings of seats with their outlines, heights and rake, the blocks of visitors and the places left open, in feet in the surface's
|
|
973
|
+
* wide frame, so that another view (a 3D one) can raise them. `undefined` when the options ask for no crowd (or for a special game, which has none).
|
|
974
|
+
* @example
|
|
975
|
+
* ```ts
|
|
976
|
+
* import { crowdStands } from "@johnmorrisdotca/kyogijo-stadiums";
|
|
977
|
+
* const stands = crowdStands({ sport: "soccer", crowd: true });
|
|
978
|
+
* console.log(stands?.rings.map((ring) => ring.id).join(" "), stands?.shape, stands?.stadium);
|
|
979
|
+
* // lower upper bowl purpose
|
|
980
|
+
* ```
|
|
981
|
+
*/
|
|
982
|
+
export function crowdStands(o) {
|
|
983
|
+
const crowd = crowdOf(o);
|
|
984
|
+
if (crowd === undefined)
|
|
985
|
+
return undefined;
|
|
986
|
+
const probe = buildWhole({ ...o, margin: 0 }, false, drawingId(o), false, false, { features: undefined });
|
|
987
|
+
return standsFor(o, probe, crowd);
|
|
988
|
+
}
|