@johnmorrisdotca/kyogijo-stadiums 0.0.0-stage → 0.4.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 +129 -0
- package/LICENSE +21 -0
- package/NOTICE.md +293 -0
- package/README.md +600 -2
- package/dist/annotate.d.ts +47 -0
- package/dist/annotate.js +349 -0
- package/dist/atlas-entry.d.ts +4 -0
- package/dist/atlas-entry.js +3 -0
- package/dist/atlas.d.ts +131 -0
- package/dist/atlas.js +149 -0
- package/dist/background.d.ts +56 -0
- package/dist/background.js +127 -0
- package/dist/ballpark.d.ts +48 -0
- package/dist/ballpark.js +129 -0
- package/dist/canvas-entry.d.ts +2 -0
- package/dist/canvas-entry.js +2 -0
- package/dist/canvas.d.ts +85 -0
- package/dist/canvas.js +279 -0
- package/dist/charts-entry.d.ts +10 -0
- package/dist/charts-entry.js +8 -0
- package/dist/charts.baseball.d.ts +30 -0
- package/dist/charts.baseball.js +152 -0
- package/dist/charts.curling.d.ts +52 -0
- package/dist/charts.curling.js +196 -0
- package/dist/charts.d.ts +172 -0
- package/dist/charts.js +508 -0
- package/dist/charts.network.d.ts +66 -0
- package/dist/charts.network.js +64 -0
- package/dist/charts.soccer.d.ts +43 -0
- package/dist/charts.soccer.js +191 -0
- package/dist/charts.types.d.ts +396 -0
- package/dist/charts.types.js +1 -0
- package/dist/colour.d.ts +29 -0
- package/dist/colour.js +97 -0
- package/dist/common.d.ts +14 -0
- package/dist/common.js +32 -0
- package/dist/court.d.ts +89 -0
- package/dist/court.js +248 -0
- package/dist/courts.style.d.ts +20 -0
- package/dist/courts.style.js +54 -0
- package/dist/cricket.board.d.ts +18 -0
- package/dist/cricket.board.js +67 -0
- package/dist/curling.board.d.ts +24 -0
- package/dist/curling.board.js +147 -0
- package/dist/d3-entry.d.ts +3 -0
- package/dist/d3-entry.js +2 -0
- package/dist/d3.d.ts +45 -0
- package/dist/d3.js +56 -0
- package/dist/draw.d.ts +52 -0
- package/dist/draw.js +705 -0
- package/dist/element-define.d.ts +2 -0
- package/dist/element-define.js +4 -0
- package/dist/element-entry.d.ts +1 -0
- package/dist/element-entry.js +2 -0
- package/dist/element.d.ts +39 -0
- package/dist/element.js +321 -0
- package/dist/eras-entry.d.ts +2 -0
- package/dist/eras-entry.js +2 -0
- package/dist/eras.d.ts +204 -0
- package/dist/eras.js +396 -0
- package/dist/events-entry.d.ts +3 -0
- package/dist/events-entry.js +3 -0
- package/dist/events.d.ts +54 -0
- package/dist/events.data.d.ts +59 -0
- package/dist/events.data.js +297 -0
- package/dist/events.js +127 -0
- package/dist/features.d.ts +31 -0
- package/dist/features.js +284 -0
- package/dist/field.score.d.ts +5 -0
- package/dist/field.score.js +33 -0
- package/dist/fields/afl.d.ts +11 -0
- package/dist/fields/afl.js +103 -0
- package/dist/fields/cricket.d.ts +9 -0
- package/dist/fields/cricket.js +81 -0
- package/dist/fields/fieldhockey.d.ts +6 -0
- package/dist/fields/fieldhockey.js +81 -0
- package/dist/fields/gaelic.d.ts +10 -0
- package/dist/fields/gaelic.js +86 -0
- package/dist/fields/handball.d.ts +6 -0
- package/dist/fields/handball.js +85 -0
- package/dist/fields/lacrosse.d.ts +17 -0
- package/dist/fields/lacrosse.js +169 -0
- package/dist/fields/rugby.d.ts +10 -0
- package/dist/fields/rugby.js +130 -0
- package/dist/fields.d.ts +12 -0
- package/dist/fields.js +44 -0
- package/dist/fields.kit.d.ts +63 -0
- package/dist/fields.kit.js +108 -0
- package/dist/fields.style.d.ts +4 -0
- package/dist/fields.style.js +19 -0
- package/dist/fields.types.d.ts +155 -0
- package/dist/fields.types.js +1 -0
- package/dist/format.d.ts +10 -0
- package/dist/format.js +28 -0
- package/dist/formations.d.ts +18 -0
- package/dist/formations.js +211 -0
- package/dist/frames-entry.d.ts +8 -0
- package/dist/frames-entry.js +7 -0
- package/dist/frames.d.ts +199 -0
- package/dist/frames.js +271 -0
- package/dist/frames.providers.d.ts +16 -0
- package/dist/frames.providers.js +302 -0
- package/dist/geojson-entry.d.ts +2 -0
- package/dist/geojson-entry.js +2 -0
- package/dist/geojson.d.ts +79 -0
- package/dist/geojson.js +147 -0
- package/dist/geometry-entry.d.ts +7 -0
- package/dist/geometry-entry.js +5 -0
- package/dist/geometry.d.ts +295 -0
- package/dist/geometry.js +361 -0
- package/dist/grounds-entry.d.ts +4 -0
- package/dist/grounds-entry.js +3 -0
- package/dist/grounds.d.ts +134 -0
- package/dist/grounds.data.d.ts +2 -0
- package/dist/grounds.data.js +1849 -0
- package/dist/grounds.js +74 -0
- package/dist/heat.colour.d.ts +37 -0
- package/dist/heat.colour.js +54 -0
- package/dist/heat.d.ts +43 -0
- package/dist/heat.js +614 -0
- package/dist/heat.stats.d.ts +123 -0
- package/dist/heat.stats.js +413 -0
- package/dist/heat.types.d.ts +145 -0
- package/dist/heat.types.js +1 -0
- package/dist/heat.zones.d.ts +54 -0
- package/dist/heat.zones.js +424 -0
- package/dist/homeside.d.ts +16 -0
- package/dist/homeside.js +43 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +29 -0
- package/dist/layers-entry.d.ts +19 -0
- package/dist/layers-entry.js +11 -0
- package/dist/layers.d.ts +41 -0
- package/dist/layers.env.d.ts +72 -0
- package/dist/layers.env.js +79 -0
- package/dist/layers.js +533 -0
- package/dist/layers.marks.d.ts +45 -0
- package/dist/layers.marks.js +168 -0
- package/dist/layers.types.d.ts +226 -0
- package/dist/layers.types.js +1 -0
- package/dist/layout.d.ts +97 -0
- package/dist/layout.js +381 -0
- package/dist/layout.style.d.ts +6 -0
- package/dist/layout.style.js +17 -0
- package/dist/layouts-entry.d.ts +5 -0
- package/dist/layouts-entry.js +4 -0
- package/dist/leagues/aussie.d.ts +57 -0
- package/dist/leagues/aussie.js +36 -0
- package/dist/leagues/baseball.d.ts +58 -0
- package/dist/leagues/baseball.js +171 -0
- package/dist/leagues/basketball.d.ts +67 -0
- package/dist/leagues/basketball.js +128 -0
- package/dist/leagues/courts.d.ts +212 -0
- package/dist/leagues/courts.js +114 -0
- package/dist/leagues/cricket.d.ts +45 -0
- package/dist/leagues/cricket.js +34 -0
- package/dist/leagues/curling.d.ts +64 -0
- package/dist/leagues/curling.js +53 -0
- package/dist/leagues/fieldhockey.d.ts +48 -0
- package/dist/leagues/fieldhockey.js +29 -0
- package/dist/leagues/fields.d.ts +10 -0
- package/dist/leagues/fields.js +6 -0
- package/dist/leagues/football.d.ts +8 -0
- package/dist/leagues/football.js +82 -0
- package/dist/leagues/gaelic.d.ts +50 -0
- package/dist/leagues/gaelic.js +29 -0
- package/dist/leagues/handball.d.ts +45 -0
- package/dist/leagues/handball.js +32 -0
- package/dist/leagues/hockey.d.ts +63 -0
- package/dist/leagues/hockey.js +166 -0
- package/dist/leagues/lacrosse.d.ts +103 -0
- package/dist/leagues/lacrosse.js +175 -0
- package/dist/leagues/rugby.d.ts +59 -0
- package/dist/leagues/rugby.js +72 -0
- package/dist/leagues/sources.d.ts +18 -0
- package/dist/leagues/sources.js +95 -0
- package/dist/leagues/types.d.ts +43 -0
- package/dist/leagues/types.js +5 -0
- package/dist/leagues-entry.d.ts +23 -0
- package/dist/leagues-entry.js +15 -0
- package/dist/leagues.d.ts +172 -0
- package/dist/leagues.js +462 -0
- package/dist/look.d.ts +50 -0
- package/dist/look.js +186 -0
- package/dist/mark.d.ts +35 -0
- package/dist/mark.js +59 -0
- package/dist/mount-entry.d.ts +2 -0
- package/dist/mount-entry.js +2 -0
- package/dist/mount.d.ts +63 -0
- package/dist/mount.js +167 -0
- package/dist/mow.d.ts +36 -0
- package/dist/mow.js +111 -0
- package/dist/names.d.ts +77 -0
- package/dist/names.js +97 -0
- package/dist/outline.d.ts +25 -0
- package/dist/outline.js +112 -0
- package/dist/parks-entry.d.ts +4 -0
- package/dist/parks-entry.js +3 -0
- package/dist/parks.d.ts +200 -0
- package/dist/parks.data.d.ts +2 -0
- package/dist/parks.data.js +4489 -0
- package/dist/parks.js +92 -0
- package/dist/plot-entry.d.ts +2 -0
- package/dist/plot-entry.js +2 -0
- package/dist/plot.d.ts +74 -0
- package/dist/plot.js +68 -0
- package/dist/png-entry.d.ts +2 -0
- package/dist/png-entry.js +2 -0
- package/dist/png.d.ts +26 -0
- package/dist/png.js +46 -0
- package/dist/ranges.d.ts +33 -0
- package/dist/ranges.js +178 -0
- package/dist/ranges.names.d.ts +31 -0
- package/dist/ranges.names.js +65 -0
- package/dist/react-entry.d.ts +2 -0
- package/dist/react-entry.js +2 -0
- package/dist/react.d.ts +53 -0
- package/dist/react.js +58 -0
- package/dist/replay-entry.d.ts +5 -0
- package/dist/replay-entry.js +4 -0
- package/dist/replay.control.d.ts +47 -0
- package/dist/replay.control.js +184 -0
- package/dist/replay.d.ts +170 -0
- package/dist/replay.dom.d.ts +21 -0
- package/dist/replay.dom.js +388 -0
- package/dist/replay.js +579 -0
- package/dist/replay.types.d.ts +152 -0
- package/dist/replay.types.js +1 -0
- package/dist/restyle.d.ts +21 -0
- package/dist/restyle.js +70 -0
- package/dist/road.d.ts +26 -0
- package/dist/road.draw.d.ts +6 -0
- package/dist/road.draw.js +269 -0
- package/dist/road.js +59 -0
- package/dist/road.model.d.ts +63 -0
- package/dist/road.model.js +215 -0
- package/dist/road.style.d.ts +7 -0
- package/dist/road.style.js +26 -0
- package/dist/road.types.d.ts +122 -0
- package/dist/road.types.js +1 -0
- package/dist/scene-entry.d.ts +7 -0
- package/dist/scene-entry.js +5 -0
- package/dist/scene.css.d.ts +44 -0
- package/dist/scene.css.js +296 -0
- package/dist/scene.d.ts +27 -0
- package/dist/scene.fit.d.ts +12 -0
- package/dist/scene.fit.js +18 -0
- package/dist/scene.flat.d.ts +38 -0
- package/dist/scene.flat.js +167 -0
- package/dist/scene.js +420 -0
- package/dist/scene.path.d.ts +40 -0
- package/dist/scene.path.js +357 -0
- package/dist/scene.types.d.ts +172 -0
- package/dist/scene.types.js +1 -0
- package/dist/scene.xml.d.ts +31 -0
- package/dist/scene.xml.js +81 -0
- package/dist/score.d.ts +70 -0
- package/dist/score.js +212 -0
- package/dist/sets.board.d.ts +22 -0
- package/dist/sets.board.js +89 -0
- package/dist/sheet.d.ts +18 -0
- package/dist/sheet.js +38 -0
- package/dist/special.d.ts +44 -0
- package/dist/special.js +204 -0
- package/dist/special.style.d.ts +8 -0
- package/dist/special.style.js +36 -0
- package/dist/strings.d.ts +581 -0
- package/dist/strings.js +411 -0
- package/dist/style.d.ts +35 -0
- package/dist/style.js +177 -0
- package/dist/surfaces/baseball.d.ts +33 -0
- package/dist/surfaces/baseball.js +208 -0
- package/dist/surfaces/baseball.walls.d.ts +16 -0
- package/dist/surfaces/baseball.walls.js +150 -0
- package/dist/surfaces/basketball.d.ts +8 -0
- package/dist/surfaces/basketball.js +199 -0
- package/dist/surfaces/curling.d.ts +23 -0
- package/dist/surfaces/curling.js +128 -0
- package/dist/surfaces/field.d.ts +12 -0
- package/dist/surfaces/field.js +163 -0
- package/dist/surfaces/football.d.ts +12 -0
- package/dist/surfaces/football.js +208 -0
- package/dist/surfaces/hockey.d.ts +10 -0
- package/dist/surfaces/hockey.js +166 -0
- package/dist/surfaces/net.d.ts +7 -0
- package/dist/surfaces/net.js +151 -0
- package/dist/surfaces/soccer-providers.d.ts +124 -0
- package/dist/surfaces/soccer-providers.js +334 -0
- package/dist/surfaces/soccer-spec.d.ts +138 -0
- package/dist/surfaces/soccer-spec.js +175 -0
- package/dist/surfaces/soccer.d.ts +25 -0
- package/dist/surfaces/soccer.js +270 -0
- package/dist/surfaces/surface.d.ts +180 -0
- package/dist/surfaces/surface.js +49 -0
- package/dist/team.common.d.ts +75 -0
- package/dist/team.common.js +97 -0
- package/dist/team.formation.d.ts +24 -0
- package/dist/team.formation.js +121 -0
- package/dist/team.geometry.d.ts +66 -0
- package/dist/team.geometry.js +304 -0
- package/dist/team.layers.d.ts +16 -0
- package/dist/team.layers.js +32 -0
- package/dist/team.motion.d.ts +9 -0
- package/dist/team.motion.js +325 -0
- package/dist/team.network.d.ts +43 -0
- package/dist/team.network.js +215 -0
- package/dist/team.shape.d.ts +12 -0
- package/dist/team.shape.js +217 -0
- package/dist/team.types.d.ts +189 -0
- package/dist/team.types.js +1 -0
- package/dist/text.d.ts +31 -0
- package/dist/text.js +88 -0
- package/dist/themes.d.ts +14 -0
- package/dist/themes.js +24 -0
- package/dist/types.d.ts +565 -0
- package/dist/types.js +1 -0
- package/dist/units.d.ts +45 -0
- package/dist/units.js +90 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/package.json +234 -4
package/dist/atlas.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { GROUNDS, GROUND_IDS } from "./grounds.js";
|
|
2
|
+
import { nameContainsAll, placesNamed, plainName } from "./names.js";
|
|
3
|
+
import { PARKS, PARK_IDS } from "./parks.js";
|
|
4
|
+
const leaguesOfGround = (ground) => {
|
|
5
|
+
const leagues = [];
|
|
6
|
+
if (ground.tournaments.some((t) => t.competition === "world-cup"))
|
|
7
|
+
leagues.push("FIFA World Cup");
|
|
8
|
+
if (ground.tournaments.some((t) => t.competition === "womens-world-cup"))
|
|
9
|
+
leagues.push("FIFA Women's World Cup");
|
|
10
|
+
return leagues;
|
|
11
|
+
};
|
|
12
|
+
const fromPark = (park) => {
|
|
13
|
+
const from = park.from ?? park.opened;
|
|
14
|
+
const to = park.to ?? park.closed;
|
|
15
|
+
return {
|
|
16
|
+
id: park.id,
|
|
17
|
+
kind: "park",
|
|
18
|
+
name: park.name,
|
|
19
|
+
nameJa: park.nameJa,
|
|
20
|
+
...(park.readingJa === undefined ? {} : { readingJa: park.readingJa }),
|
|
21
|
+
...(park.localName === undefined ? {} : { localName: park.localName, localLanguage: park.localLanguage }),
|
|
22
|
+
aliases: park.aliases ?? [],
|
|
23
|
+
sport: "baseball",
|
|
24
|
+
leagues: [park.league],
|
|
25
|
+
country: park.country,
|
|
26
|
+
city: park.city,
|
|
27
|
+
team: park.team,
|
|
28
|
+
lat: park.lat,
|
|
29
|
+
lon: park.lon,
|
|
30
|
+
// A ground a club also uses (`secondary`) is in use today.
|
|
31
|
+
status: park.status === "past" ? "past" : "current",
|
|
32
|
+
...(from === undefined ? {} : { from }),
|
|
33
|
+
...(park.status === "past" && to !== undefined ? { to } : {}),
|
|
34
|
+
wikidata: park.wikidata,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const fromGround = (ground) => ({
|
|
38
|
+
id: ground.id,
|
|
39
|
+
kind: "ground",
|
|
40
|
+
name: ground.name,
|
|
41
|
+
nameJa: ground.nameJa,
|
|
42
|
+
...(ground.readingJa === undefined ? {} : { readingJa: ground.readingJa }),
|
|
43
|
+
...(ground.localName === undefined ? {} : { localName: ground.localName, localLanguage: ground.localLanguage }),
|
|
44
|
+
aliases: ground.aliases ?? [],
|
|
45
|
+
...(ground.names === undefined ? {} : { names: ground.names }),
|
|
46
|
+
sport: "soccer",
|
|
47
|
+
leagues: leaguesOfGround(ground),
|
|
48
|
+
country: ground.country,
|
|
49
|
+
city: ground.city,
|
|
50
|
+
lat: ground.lat,
|
|
51
|
+
lon: ground.lon,
|
|
52
|
+
status: ground.status,
|
|
53
|
+
...(ground.from === undefined ? {} : { from: ground.from }),
|
|
54
|
+
...(ground.status === "past" && ground.to !== undefined ? { to: ground.to } : {}),
|
|
55
|
+
wikidata: ground.wikidata,
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* Every place the package draws, parks first and then grounds, each in the order of its own list: the ballparks of
|
|
59
|
+
* `PARKS` and the soccer grounds of `GROUNDS`, with where each stands.
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { PLACES } from "@johnmorrisdotca/kyogijo-stadiums/atlas";
|
|
63
|
+
* const fenway = PLACES.find((place) => place.id === "fenway-park");
|
|
64
|
+
* console.log(fenway?.name, fenway?.lat, fenway?.lon, fenway?.country);
|
|
65
|
+
* // Fenway Park 42.34639 -71.0975 US
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export const PLACES = Object.freeze([...PARK_IDS.map((id) => fromPark(PARKS[id])), ...GROUND_IDS.map((id) => fromGround(GROUNDS[id]))]);
|
|
69
|
+
const BY_ID = new Map(PLACES.map((place) => [place.id, place]));
|
|
70
|
+
/** The place with a key, or `undefined`. */
|
|
71
|
+
export const placeById = (id) => (id === undefined ? undefined : BY_ID.get(id));
|
|
72
|
+
/**
|
|
73
|
+
* The places that answer to a name, best match first: the everyday names, then the official and cited names and the sponsors', then the former names and the nicknames. The comparison
|
|
74
|
+
* ignores case, accents, spaces and punctuation, width, and in Japanese hiragana or katakana and the long vowel mark.
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* import { placeByName, placesByName } from "@johnmorrisdotca/kyogijo-stadiums/atlas";
|
|
78
|
+
* console.log(placeByName("Koshien Stadium")?.id, placeByName("サン・シーロ")?.id, placeByName("Estadio Jornalista Mario Filho")?.id, placesByName("wrigley field").map((place) => place.id).join());
|
|
79
|
+
* // hanshin-koshien-stadium san-siro maracana wrigley-field,wrigley-field-los-angeles
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export const placesByName = (name) => placesNamed(PLACES, name);
|
|
83
|
+
/** The place a name belongs to (see `placesByName`; the best match when several answer to it), or `undefined`. */
|
|
84
|
+
export const placeByName = (name) => placesByName(name)[0];
|
|
85
|
+
const plain = plainName;
|
|
86
|
+
const many = (value) => (value === undefined ? undefined : Array.isArray(value) ? value : [value]);
|
|
87
|
+
/**
|
|
88
|
+
* The places that match every field of the filter, in the order of `PLACES`.
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* import { placesBy } from "@johnmorrisdotca/kyogijo-stadiums/atlas";
|
|
92
|
+
* const japan = placesBy({ sport: "baseball", country: "JP", status: "current" });
|
|
93
|
+
* const in1950 = placesBy({ year: 1950, country: "US" });
|
|
94
|
+
* console.log(japan.length, in1950.some((place) => place.id === "ebbets-field"));
|
|
95
|
+
* // 14 true
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export function placesBy(filter = {}) {
|
|
99
|
+
const sports = many(filter.sport);
|
|
100
|
+
const kinds = many(filter.kind);
|
|
101
|
+
const leagues = many(filter.league);
|
|
102
|
+
const countries = many(filter.country);
|
|
103
|
+
const statuses = many(filter.status);
|
|
104
|
+
const year = filter.year !== undefined && Number.isFinite(filter.year) ? filter.year : undefined;
|
|
105
|
+
const words = filter.text === undefined ? [] : filter.text.split(/\s+/).map(plain).filter(Boolean);
|
|
106
|
+
return PLACES.filter((place) => {
|
|
107
|
+
if (sports !== undefined && !sports.includes(place.sport))
|
|
108
|
+
return false;
|
|
109
|
+
if (kinds !== undefined && !kinds.includes(place.kind))
|
|
110
|
+
return false;
|
|
111
|
+
if (leagues !== undefined && !place.leagues.some((league) => leagues.includes(league)))
|
|
112
|
+
return false;
|
|
113
|
+
if (countries !== undefined && !countries.includes(place.country))
|
|
114
|
+
return false;
|
|
115
|
+
if (statuses !== undefined && !statuses.includes(place.status))
|
|
116
|
+
return false;
|
|
117
|
+
if (year !== undefined && ((place.from !== undefined && year < place.from) || (place.to !== undefined && year > place.to)))
|
|
118
|
+
return false;
|
|
119
|
+
if (words.length > 0) {
|
|
120
|
+
const haystack = plain([place.city, place.team, place.country].filter(Boolean).join(" "));
|
|
121
|
+
if (!words.every((word) => haystack.includes(word) || nameContainsAll(place, [word])))
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The sports, leagues and countries of the places, and the span of years they give.
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* import { atlasFacets } from "@johnmorrisdotca/kyogijo-stadiums/atlas";
|
|
132
|
+
* const { sports, leagues, countries, years } = atlasFacets();
|
|
133
|
+
* console.log(sports.join(), leagues.join(), countries.length, years.min);
|
|
134
|
+
* // baseball,soccer FIFA Women's World Cup,FIFA World Cup,MLB,NPB 23 1877
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
export function atlasFacets() {
|
|
138
|
+
const years = PLACES.flatMap((place) => [place.from, place.to]).filter((year) => year !== undefined);
|
|
139
|
+
return {
|
|
140
|
+
sports: [...new Set(PLACES.map((place) => place.sport))].sort(),
|
|
141
|
+
leagues: [...new Set(PLACES.flatMap((place) => place.leagues))].sort(),
|
|
142
|
+
countries: [...new Set(PLACES.map((place) => place.country))].sort(),
|
|
143
|
+
years: { min: Math.min(...years), max: Math.max(...years) },
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** The options `drawKyogijo` takes to draw a place's surface: a park's fence or a ground's pitch at its own size. */
|
|
147
|
+
export function surfaceOf(place) {
|
|
148
|
+
return place.kind === "park" ? { sport: "mlb", park: place.id } : { sport: "soccer", ground: place.id };
|
|
149
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** The patterns: a tile of shapes repeated over the ground. */
|
|
2
|
+
export type BackgroundPattern = "felt" | "grass" | "wood" | "ice" | "paper" | "grid" | "dots";
|
|
3
|
+
/** How a photograph fills the ground: `cover` fills it and cuts what overhangs, `contain` shows all of it, `stretch` fills it out of shape. */
|
|
4
|
+
export type BackgroundFit = "cover" | "contain" | "stretch";
|
|
5
|
+
/** A ground in full. Every part is optional; with none, there is no ground. */
|
|
6
|
+
export interface BackgroundOptions {
|
|
7
|
+
/** A pattern drawn over the ground, in a low opacity. */
|
|
8
|
+
pattern?: BackgroundPattern;
|
|
9
|
+
/** A colour under the pattern and the photograph (a hex colour, `rgb()`, `hsl()`, a name or `var(--name)`). Without it the ground keeps its own colour. */
|
|
10
|
+
color?: string;
|
|
11
|
+
/** The opacity of the pattern, 0 to 1. Each pattern has its own subtle one. */
|
|
12
|
+
opacity?: number;
|
|
13
|
+
/** The size of the pattern's tile, as a multiple of its usual one (a fortyeighth of the drawing's longer side). 1 by default; 0.25 to 8 are kept. */
|
|
14
|
+
scale?: number;
|
|
15
|
+
/** A photograph behind the surface: an address or `data:` image, held to the rules of a team's `logo` (`safeLogo`). Drawn over the ground's colour and under the pattern. */
|
|
16
|
+
image?: string;
|
|
17
|
+
/** The opacity of the photograph, 0 to 1. 0.35 by default. */
|
|
18
|
+
imageOpacity?: number;
|
|
19
|
+
/** How the photograph fills the ground. `cover` by default. */
|
|
20
|
+
imageFit?: BackgroundFit;
|
|
21
|
+
}
|
|
22
|
+
/** A ground: a pattern's name, `"none"`, or the options in full. */
|
|
23
|
+
export type Background = BackgroundPattern | "none" | BackgroundOptions;
|
|
24
|
+
/** The patterns, in the order a menu lists them. */
|
|
25
|
+
export declare const BACKGROUND_PATTERNS: readonly BackgroundPattern[];
|
|
26
|
+
/** The opacity each pattern is drawn at when `opacity` says nothing. */
|
|
27
|
+
export declare const BACKGROUND_OPACITY: Readonly<Record<BackgroundPattern, number>>;
|
|
28
|
+
/** The rules of a ground, written into a drawing that has one (and in `KYOGIJO_STYLE`, for a page that supplies the stylesheet): the pattern's ink and light. */
|
|
29
|
+
export declare const KYOGIJO_BACKGROUND_STYLE: string;
|
|
30
|
+
/** The options of a ground, read: a name becomes a pattern, `"none"`, anything that is not a ground and a ground with nothing in it are `undefined`. */
|
|
31
|
+
export declare function resolveBackground(value: unknown): BackgroundOptions | undefined;
|
|
32
|
+
/** What a ground is made of: the pieces to write in the drawing's `<defs>` and the shapes to lay over the apron. */
|
|
33
|
+
export interface BackgroundMarkup {
|
|
34
|
+
defs: string;
|
|
35
|
+
shapes: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The ground as SVG: a rectangle `box` in the drawing's own numbers, filled with the colour, the photograph and the pattern, in that order, and the definitions they need
|
|
39
|
+
* (a pattern and, for a photograph that overhangs, a clip), with ids that begin with `id`. The tile is a fortyeighth of the box's longer side, times `scale`, so that a ground
|
|
40
|
+
* looks the same on a rink in feet and on a card in pixels. An empty string for each when there is no ground.
|
|
41
|
+
*/
|
|
42
|
+
export declare function backgroundMarkup(background: Background | undefined, box: {
|
|
43
|
+
x: number;
|
|
44
|
+
y: number;
|
|
45
|
+
w: number;
|
|
46
|
+
h: number;
|
|
47
|
+
}, id: string): BackgroundMarkup;
|
|
48
|
+
/**
|
|
49
|
+
* A drawing's `base` with a ground laid on its apron: the base's first shape is the apron's rectangle, and the ground is written right after it, over exactly the same
|
|
50
|
+
* rectangle, so that it lies under the surface and over the apron's colour. `defs` is what the ground needs in the drawing's `<defs>`. A base that does not begin with a
|
|
51
|
+
* rectangle, or a ground with nothing in it, leaves the base as it is.
|
|
52
|
+
*/
|
|
53
|
+
export declare function laidOn(base: string, background: Background | undefined, id: string): {
|
|
54
|
+
base: string;
|
|
55
|
+
defs: string;
|
|
56
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// The ground behind a drawing: a subtle pattern drawn in SVG (felt, grass, wood, ice, paper, a grid, dots) and, when the caller has one, a photograph at an opacity. It is
|
|
2
|
+
// written once here for the drawing (`background` in the options, laid on the apron under the surface) and for a grid of drawings (`drawGrid`, laid under all of them).
|
|
3
|
+
// Every pattern is a few shapes in a tile, with no random number, in the ink or the line colour of the drawing at a low opacity, so that it follows light and dark.
|
|
4
|
+
import { safeColour, safeLogo } from "./colour.js";
|
|
5
|
+
import { el, num } from "./format.js";
|
|
6
|
+
/** The patterns, in the order a menu lists them. */
|
|
7
|
+
export const BACKGROUND_PATTERNS = ["felt", "grass", "wood", "ice", "paper", "grid", "dots"];
|
|
8
|
+
/** The opacity each pattern is drawn at when `opacity` says nothing. */
|
|
9
|
+
export const BACKGROUND_OPACITY = { felt: 0.8, grass: 0.7, wood: 0.8, ice: 0.9, paper: 0.75, grid: 0.55, dots: 0.7 };
|
|
10
|
+
/** The rules of a ground, written into a drawing that has one (and in `KYOGIJO_STYLE`, for a page that supplies the stylesheet): the pattern's ink and light. */
|
|
11
|
+
export const KYOGIJO_BACKGROUND_STYLE = `
|
|
12
|
+
.kyogijo .ky-bg-i{stroke:var(--ky-ink);fill:none}
|
|
13
|
+
.kyogijo .ky-bg-id{fill:var(--ky-ink);stroke:none}
|
|
14
|
+
.kyogijo .ky-bg-l{stroke:var(--ky-line);fill:none}
|
|
15
|
+
.kyogijo .ky-bg-ld{fill:var(--ky-line);stroke:none}`.trim();
|
|
16
|
+
const has = (name) => typeof name === "string" && BACKGROUND_PATTERNS.includes(name);
|
|
17
|
+
const unit = (value, fallback) => (typeof value === "number" && Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : fallback);
|
|
18
|
+
/** The options of a ground, read: a name becomes a pattern, `"none"`, anything that is not a ground and a ground with nothing in it are `undefined`. */
|
|
19
|
+
export function resolveBackground(value) {
|
|
20
|
+
if (value === undefined || value === null || value === "none")
|
|
21
|
+
return undefined;
|
|
22
|
+
if (typeof value === "string")
|
|
23
|
+
return has(value) ? { pattern: value } : undefined;
|
|
24
|
+
if (typeof value !== "object")
|
|
25
|
+
return undefined;
|
|
26
|
+
const o = value;
|
|
27
|
+
const out = {};
|
|
28
|
+
if (has(o.pattern))
|
|
29
|
+
out.pattern = o.pattern;
|
|
30
|
+
const color = safeColour(o.color);
|
|
31
|
+
if (color !== undefined)
|
|
32
|
+
out.color = color;
|
|
33
|
+
if (typeof o.opacity === "number" && Number.isFinite(o.opacity))
|
|
34
|
+
out.opacity = unit(o.opacity, BACKGROUND_OPACITY[out.pattern ?? "felt"]);
|
|
35
|
+
if (typeof o.scale === "number" && Number.isFinite(o.scale))
|
|
36
|
+
out.scale = Math.min(8, Math.max(0.25, o.scale));
|
|
37
|
+
const image = safeLogo(o.image);
|
|
38
|
+
if (image !== undefined)
|
|
39
|
+
out.image = image;
|
|
40
|
+
if (typeof o.imageOpacity === "number" && Number.isFinite(o.imageOpacity))
|
|
41
|
+
out.imageOpacity = unit(o.imageOpacity, 0.35);
|
|
42
|
+
if (o.imageFit === "cover" || o.imageFit === "contain" || o.imageFit === "stretch")
|
|
43
|
+
out.imageFit = o.imageFit;
|
|
44
|
+
return out.pattern === undefined && out.color === undefined && out.image === undefined ? undefined : out;
|
|
45
|
+
}
|
|
46
|
+
/** The tile of a pattern: its width and height and the shapes in it, for a tile of side `t`. */
|
|
47
|
+
function tile(pattern, t) {
|
|
48
|
+
const n = (v) => num(Math.round(v * 1000) / 1000);
|
|
49
|
+
const line = (cls, x1, y1, x2, y2, width) => el("line", { class: cls, x1: n(x1), y1: n(y1), x2: n(x2), y2: n(y2), "stroke-width": n(width), "stroke-linecap": "round" });
|
|
50
|
+
const dot = (cls, x, y, r) => el("circle", { class: cls, cx: n(x), cy: n(y), r: n(r) });
|
|
51
|
+
switch (pattern) {
|
|
52
|
+
case "felt":
|
|
53
|
+
return {
|
|
54
|
+
w: t,
|
|
55
|
+
h: t,
|
|
56
|
+
body: [dot("ky-bg-ld", 0.12 * t, 0.2 * t, 0.045 * t), dot("ky-bg-id", 0.58 * t, 0.12 * t, 0.04 * t), dot("ky-bg-ld", 0.82 * t, 0.46 * t, 0.05 * t), dot("ky-bg-id", 0.3 * t, 0.55 * t, 0.045 * t), dot("ky-bg-ld", 0.66 * t, 0.84 * t, 0.04 * t), dot("ky-bg-id", 0.08 * t, 0.9 * t, 0.05 * t), dot("ky-bg-ld", 0.45 * t, 0.36 * t, 0.03 * t)].join(""),
|
|
57
|
+
};
|
|
58
|
+
case "grass":
|
|
59
|
+
return {
|
|
60
|
+
w: t,
|
|
61
|
+
h: t,
|
|
62
|
+
body: [line("ky-bg-l", 0.1 * t, 0.46 * t, 0.2 * t, 0.2 * t, 0.05 * t), line("ky-bg-l", 0.4 * t, 0.9 * t, 0.46 * t, 0.58 * t, 0.05 * t), line("ky-bg-l", 0.64 * t, 0.4 * t, 0.58 * t, 0.12 * t, 0.05 * t), line("ky-bg-l", 0.86 * t, 0.96 * t, 0.94 * t, 0.7 * t, 0.05 * t), line("ky-bg-i", 0.26 * t, 0.98 * t, 0.2 * t, 0.78 * t, 0.04 * t), line("ky-bg-i", 0.78 * t, 0.5 * t, 0.84 * t, 0.28 * t, 0.04 * t)].join(""),
|
|
63
|
+
};
|
|
64
|
+
case "wood": {
|
|
65
|
+
const w = 2 * t;
|
|
66
|
+
const grain = (y, bend) => el("path", { class: "ky-bg-i", d: `M0 ${n(y)}Q${n(0.5 * t)} ${n(y - bend)} ${n(t)} ${n(y)}T${n(w)} ${n(y)}`, "stroke-width": n(0.025 * t) });
|
|
67
|
+
return { w, h: t, body: [grain(0.22 * t, 0.12 * t), grain(0.48 * t, -0.1 * t), grain(0.72 * t, 0.08 * t), line("ky-bg-i", 0, t, w, t, 0.05 * t), line("ky-bg-i", 0.7 * t, 0, 0.7 * t, 0.5 * t, 0.04 * t), line("ky-bg-i", 1.4 * t, 0.5 * t, 1.4 * t, t, 0.04 * t)].join("") };
|
|
68
|
+
}
|
|
69
|
+
case "ice":
|
|
70
|
+
return { w: 2 * t, h: 2 * t, body: [line("ky-bg-l", 0.2 * t, 0.4 * t, 0.9 * t, 0.15 * t, 0.022 * t), line("ky-bg-l", 1.1 * t, 1.5 * t, 1.9 * t, 1.1 * t, 0.022 * t), line("ky-bg-l", 0.3 * t, 1.7 * t, 0.7 * t, 1.9 * t, 0.022 * t), line("ky-bg-l", 1.3 * t, 0.3 * t, 1.6 * t, 0.7 * t, 0.022 * t), line("ky-bg-i", 0.9 * t, 1.0 * t, 1.2 * t, 0.9 * t, 0.014 * t)].join("") };
|
|
71
|
+
case "paper":
|
|
72
|
+
return { w: 2 * t, h: 2 * t, body: [line("ky-bg-i", 0.3 * t, 0.3 * t, 0.42 * t, 0.34 * t, 0.022 * t), line("ky-bg-i", 1.2 * t, 0.2 * t, 1.1 * t, 0.3 * t, 0.022 * t), line("ky-bg-i", 0.8 * t, 0.9 * t, 0.95 * t, 0.88 * t, 0.022 * t), line("ky-bg-i", 1.6 * t, 1.1 * t, 1.72 * t, 1.18 * t, 0.022 * t), line("ky-bg-i", 0.2 * t, 1.5 * t, 0.3 * t, 1.42 * t, 0.022 * t), line("ky-bg-i", 1.0 * t, 1.7 * t, 1.14 * t, 1.76 * t, 0.022 * t), dot("ky-bg-id", 0.6 * t, 0.6 * t, 0.022 * t), dot("ky-bg-id", 1.5 * t, 1.6 * t, 0.022 * t)].join("") };
|
|
73
|
+
case "grid":
|
|
74
|
+
return { w: t, h: t, body: el("path", { class: "ky-bg-i", d: `M0 0H${n(t)}M0 0V${n(t)}`, "stroke-width": n(0.035 * t) }) };
|
|
75
|
+
default:
|
|
76
|
+
return { w: t, h: t, body: dot("ky-bg-id", t / 2, t / 2, 0.05 * t) };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The ground as SVG: a rectangle `box` in the drawing's own numbers, filled with the colour, the photograph and the pattern, in that order, and the definitions they need
|
|
81
|
+
* (a pattern and, for a photograph that overhangs, a clip), with ids that begin with `id`. The tile is a fortyeighth of the box's longer side, times `scale`, so that a ground
|
|
82
|
+
* looks the same on a rink in feet and on a card in pixels. An empty string for each when there is no ground.
|
|
83
|
+
*/
|
|
84
|
+
export function backgroundMarkup(background, box, id) {
|
|
85
|
+
const b = resolveBackground(background);
|
|
86
|
+
if (b === undefined)
|
|
87
|
+
return { defs: "", shapes: "" };
|
|
88
|
+
const at = { x: num(box.x), y: num(box.y), width: num(box.w), height: num(box.h) };
|
|
89
|
+
let defs = "";
|
|
90
|
+
let shapes = "";
|
|
91
|
+
if (b.color !== undefined)
|
|
92
|
+
shapes += el("rect", { class: "ky-bg", ...at, fill: b.color });
|
|
93
|
+
if (b.image !== undefined) {
|
|
94
|
+
const fit = b.imageFit ?? "cover";
|
|
95
|
+
const clip = `${id}-bgc`;
|
|
96
|
+
// A photograph that fills the ground may overhang it, and a canvas has no `slice`: the clip cuts it, as the SVG's own rule would.
|
|
97
|
+
if (fit === "cover")
|
|
98
|
+
defs += el("clipPath", { id: clip }, el("rect", at));
|
|
99
|
+
const picture = el("image", { class: "ky-bg-img", href: b.image, x: at.x, y: at.y, width: at.width, height: at.height, preserveAspectRatio: fit === "cover" ? "xMidYMid slice" : fit === "contain" ? "xMidYMid meet" : "none", opacity: b.imageOpacity ?? 0.35 });
|
|
100
|
+
shapes += fit === "cover" ? el("g", { "clip-path": `url(#${clip})` }, picture) : picture;
|
|
101
|
+
}
|
|
102
|
+
if (b.pattern !== undefined) {
|
|
103
|
+
const side = (Math.max(box.w, box.h) / 48) * (b.scale ?? 1);
|
|
104
|
+
const { w, h, body } = tile(b.pattern, side);
|
|
105
|
+
const key = `${id}-bg`;
|
|
106
|
+
defs += el("pattern", { id: key, width: num(w), height: num(h), patternUnits: "userSpaceOnUse" }, el("g", { opacity: b.opacity ?? BACKGROUND_OPACITY[b.pattern] }, body));
|
|
107
|
+
shapes += el("rect", { class: "ky-bg-pattern", ...at, fill: `url(#${key})` });
|
|
108
|
+
}
|
|
109
|
+
return { defs, shapes };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A drawing's `base` with a ground laid on its apron: the base's first shape is the apron's rectangle, and the ground is written right after it, over exactly the same
|
|
113
|
+
* rectangle, so that it lies under the surface and over the apron's colour. `defs` is what the ground needs in the drawing's `<defs>`. A base that does not begin with a
|
|
114
|
+
* rectangle, or a ground with nothing in it, leaves the base as it is.
|
|
115
|
+
*/
|
|
116
|
+
export function laidOn(base, background, id) {
|
|
117
|
+
if (resolveBackground(background) === undefined || !base.startsWith("<rect "))
|
|
118
|
+
return { base, defs: "" };
|
|
119
|
+
const end = base.indexOf("/>") + 2;
|
|
120
|
+
const tag = base.slice(0, end);
|
|
121
|
+
const read = (name) => Number(new RegExp(`\\s${name}="(-?[\\d.]+)"`).exec(tag)?.[1] ?? "NaN");
|
|
122
|
+
const box = { x: read("x"), y: read("y"), w: read("width"), h: read("height") };
|
|
123
|
+
if (![box.x, box.y, box.w, box.h].every(Number.isFinite))
|
|
124
|
+
return { base, defs: "" };
|
|
125
|
+
const made = backgroundMarkup(background, box, id);
|
|
126
|
+
return { base: `${tag}${made.shapes}${base.slice(end)}`, defs: made.defs };
|
|
127
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type ParkDugouts, type ParkQuirk, type ParkWall } from "./parks.ts";
|
|
2
|
+
import { type KyogijoWords } from "./strings.ts";
|
|
3
|
+
import type { Language, SurfaceOptions } from "./types.ts";
|
|
4
|
+
/** The unit the heights and distances are written in: feet, or metres for a drawing in metres, centimetres or millimetres. */
|
|
5
|
+
export type BallparkUnit = "ft" | "m";
|
|
6
|
+
/** What a ballpark drawing has been asked to add to the plain field. */
|
|
7
|
+
export interface Ballpark {
|
|
8
|
+
/** The warning track's width in feet where the park's article gives it. */
|
|
9
|
+
track?: number;
|
|
10
|
+
/** The stretches of wall to draw, with their heights in feet; none unless `walls` is asked for. */
|
|
11
|
+
walls: readonly ParkWall[];
|
|
12
|
+
legend: boolean;
|
|
13
|
+
wallLabels: boolean;
|
|
14
|
+
/** The quirks to draw. */
|
|
15
|
+
quirks: readonly ParkQuirk[];
|
|
16
|
+
/** The distance to the backstop in feet, when `quirks` asks for it and the article gives it. */
|
|
17
|
+
backstop?: number;
|
|
18
|
+
dugouts?: ParkDugouts;
|
|
19
|
+
quirkLabels: boolean;
|
|
20
|
+
unit: BallparkUnit;
|
|
21
|
+
language: Language;
|
|
22
|
+
words: KyogijoWords["ballpark"];
|
|
23
|
+
}
|
|
24
|
+
/** True when a drawing has something of a ballpark to add (its stylesheet is then written in). */
|
|
25
|
+
export declare const ballparkDrawn: (b: Ballpark | undefined) => boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The ballpark of a drawing: the park the options name, in the season they name, with the walls and quirks they ask for. `undefined` for a drawing with no park.
|
|
28
|
+
* A request that cannot be read is left out, as the other options' are.
|
|
29
|
+
*/
|
|
30
|
+
export declare function ballparkFor(o: SurfaceOptions): Ballpark | undefined;
|
|
31
|
+
/** A length in feet as it is written: in feet, or in metres, to a tenth. */
|
|
32
|
+
export declare function lengthText(feet: number, unit: BallparkUnit): string;
|
|
33
|
+
/** Where a stretch of fence stands, in words: the whole outfield, or the part of the field its middle falls in. */
|
|
34
|
+
export declare function sectionName(from: number, to: number, words: KyogijoWords["ballpark"]): string;
|
|
35
|
+
/** The name of a wall: the article's, else where the article puts it, else where its middle falls in the field. */
|
|
36
|
+
export declare const wallName: (wall: ParkWall, words: KyogijoWords["ballpark"]) => string;
|
|
37
|
+
/** The wall as one phrase: its name and its height. */
|
|
38
|
+
export declare function wallText(wall: ParkWall, b: Pick<Ballpark, "unit" | "words">): string;
|
|
39
|
+
/** What the tiers of the legend say, from the thresholds of 10, 20 and 30 feet: `under 10 ft`, `10 to 20 ft`, `20 to 30 ft`, `30 ft and over`. */
|
|
40
|
+
export declare function tierTexts(b: Pick<Ballpark, "unit" | "words">): string[];
|
|
41
|
+
/** The tier a height falls in, 1 to 4. */
|
|
42
|
+
export declare const tierOf: (feet: number) => 1 | 2 | 3 | 4;
|
|
43
|
+
/** What a quirk is called: the article's name for it, else its kind's. */
|
|
44
|
+
export declare function quirkName(q: ParkQuirk, words: KyogijoWords["ballpark"]): string;
|
|
45
|
+
/** A quirk in a phrase, with its figures. */
|
|
46
|
+
export declare function quirkText(q: ParkQuirk, b: Pick<Ballpark, "unit" | "words">): string;
|
|
47
|
+
/** The sentences a ballpark adds to a drawing's description: its walls, then its quirks. */
|
|
48
|
+
export declare function ballparkSentences(b: Ballpark): string[];
|
package/dist/ballpark.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// What a ballpark's article gives beyond the fence's distances, turned into what a drawing is asked for: the walls and their heights (`walls`), the quirks
|
|
2
|
+
// (`quirks`), the warning track's width, and the words that name them. The figures are in parks.ts; this file reads the options and says what to draw and what to say.
|
|
3
|
+
import { lengthUnit } from "./common.js";
|
|
4
|
+
import { parkById, parkQuirksIn, parkWallsIn } from "./parks.js";
|
|
5
|
+
import { KYOGIJO_STRINGS, fill } from "./strings.js";
|
|
6
|
+
/** True when a drawing has something of a ballpark to add (its stylesheet is then written in). */
|
|
7
|
+
export const ballparkDrawn = (b) => b !== undefined && (b.walls.length > 0 || b.quirks.length > 0 || b.backstop !== undefined || b.dugouts !== undefined);
|
|
8
|
+
const QUIRK_NAMES = ["hill", "overhang", "pole", "flagpole", "bullpen", "ivy", "basket", "backstop", "dugouts"];
|
|
9
|
+
/**
|
|
10
|
+
* The ballpark of a drawing: the park the options name, in the season they name, with the walls and quirks they ask for. `undefined` for a drawing with no park.
|
|
11
|
+
* A request that cannot be read is left out, as the other options' are.
|
|
12
|
+
*/
|
|
13
|
+
export function ballparkFor(o) {
|
|
14
|
+
const park = parkById(o.park);
|
|
15
|
+
if (park === undefined)
|
|
16
|
+
return undefined;
|
|
17
|
+
const language = o.language === "ja" ? "ja" : "en";
|
|
18
|
+
const unit = ["m", "cm", "mm"].includes(lengthUnit(o)) ? "m" : "ft";
|
|
19
|
+
const asked = o.walls === true || (typeof o.walls === "object" && o.walls !== null);
|
|
20
|
+
const wall = typeof o.walls === "object" && o.walls !== null ? o.walls : {};
|
|
21
|
+
const quirked = o.quirks === true || (typeof o.quirks === "object" && o.quirks !== null);
|
|
22
|
+
const quirk = typeof o.quirks === "object" && o.quirks !== null ? o.quirks : {};
|
|
23
|
+
const only = Array.isArray(quirk.only) ? new Set(quirk.only.filter((name) => QUIRK_NAMES.includes(name))) : undefined;
|
|
24
|
+
const wants = (name) => quirked && (only === undefined || only.has(name));
|
|
25
|
+
const walls = asked ? parkWallsIn(park, o.year) : [];
|
|
26
|
+
return {
|
|
27
|
+
track: park.track_ft,
|
|
28
|
+
walls,
|
|
29
|
+
legend: walls.length > 0 && wall.legend === true,
|
|
30
|
+
wallLabels: wall.labels === true,
|
|
31
|
+
quirks: parkQuirksIn(park, o.year).filter((q) => wants(q.kind)),
|
|
32
|
+
backstop: wants("backstop") ? park.backstop_ft : undefined,
|
|
33
|
+
dugouts: wants("dugouts") ? park.dugouts : undefined,
|
|
34
|
+
quirkLabels: quirk.labels === true,
|
|
35
|
+
unit,
|
|
36
|
+
language,
|
|
37
|
+
words: KYOGIJO_STRINGS[language].ballpark,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** A length in feet as it is written: in feet, or in metres, to a tenth. */
|
|
41
|
+
export function lengthText(feet, unit) {
|
|
42
|
+
const value = unit === "m" ? feet * 0.3048 : feet;
|
|
43
|
+
return String(Math.round(value * 10) / 10);
|
|
44
|
+
}
|
|
45
|
+
/** Where a stretch of fence stands, in words: the whole outfield, or the part of the field its middle falls in. */
|
|
46
|
+
export function sectionName(from, to, words) {
|
|
47
|
+
if (to - from >= 80)
|
|
48
|
+
return words.sections.outfield;
|
|
49
|
+
const mid = (from + to) / 2;
|
|
50
|
+
const { left, leftCenter, center, rightCenter, right } = words.sections;
|
|
51
|
+
return mid < -27 ? left : mid < -9 ? leftCenter : mid <= 9 ? center : mid <= 27 ? rightCenter : right;
|
|
52
|
+
}
|
|
53
|
+
/** The name of a wall: the article's, else where the article puts it, else where its middle falls in the field. */
|
|
54
|
+
export const wallName = (wall, words) => wall.label ?? (wall.place === undefined ? sectionName(wall.from, wall.to, words) : words.sections[wall.place]);
|
|
55
|
+
/** The wall as one phrase: its name and its height. */
|
|
56
|
+
export function wallText(wall, b) {
|
|
57
|
+
return fill(b.words.height, { name: wallName(wall, b.words), height: lengthText(wall.height_ft, b.unit), unit: b.words.unit[b.unit] });
|
|
58
|
+
}
|
|
59
|
+
/** What the tiers of the legend say, from the thresholds of 10, 20 and 30 feet: `under 10 ft`, `10 to 20 ft`, `20 to 30 ft`, `30 ft and over`. */
|
|
60
|
+
export function tierTexts(b) {
|
|
61
|
+
const unit = b.words.unit[b.unit];
|
|
62
|
+
const at = (feet) => lengthText(feet, b.unit);
|
|
63
|
+
const { under, between, over } = b.words.tiers;
|
|
64
|
+
return [fill(under, { a: at(10), unit }), fill(between, { a: at(10), b: at(20), unit }), fill(between, { a: at(20), b: at(30), unit }), fill(over, { a: at(30), unit })];
|
|
65
|
+
}
|
|
66
|
+
/** The tier a height falls in, 1 to 4. */
|
|
67
|
+
export const tierOf = (feet) => (feet < 10 ? 1 : feet < 20 ? 2 : feet < 30 ? 3 : 4);
|
|
68
|
+
/** What a quirk is called: the article's name for it, else its kind's. */
|
|
69
|
+
export function quirkName(q, words) {
|
|
70
|
+
if (q.label !== undefined)
|
|
71
|
+
return q.label;
|
|
72
|
+
const k = words.kinds;
|
|
73
|
+
switch (q.kind) {
|
|
74
|
+
case "hill":
|
|
75
|
+
return k.hillName;
|
|
76
|
+
case "overhang":
|
|
77
|
+
return k.overhangName;
|
|
78
|
+
case "pole":
|
|
79
|
+
return k.poleName;
|
|
80
|
+
case "flagpole":
|
|
81
|
+
return k.flagpoleName;
|
|
82
|
+
case "bullpen":
|
|
83
|
+
return q.side === "home" ? k.bullpenHome : q.side === "visitors" ? k.bullpenVisitors : k.bullpen;
|
|
84
|
+
case "ivy":
|
|
85
|
+
return k.ivy;
|
|
86
|
+
case "basket":
|
|
87
|
+
return k.basket;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** A quirk in a phrase, with its figures. */
|
|
91
|
+
export function quirkText(q, b) {
|
|
92
|
+
const k = b.words.kinds;
|
|
93
|
+
const unit = b.words.unit[b.unit];
|
|
94
|
+
const name = quirkName(q, b.words);
|
|
95
|
+
const at = (feet) => lengthText(feet, b.unit);
|
|
96
|
+
switch (q.kind) {
|
|
97
|
+
case "hill":
|
|
98
|
+
return fill(q.slope_deg === undefined ? k.hill : k.hillSlope, { name, width: at(q.width_ft), unit, slope: q.slope_deg ?? 0 });
|
|
99
|
+
case "overhang":
|
|
100
|
+
return fill(k.overhang, { name, depth: at(q.depth_ft), unit });
|
|
101
|
+
case "pole":
|
|
102
|
+
case "flagpole":
|
|
103
|
+
return fill(k.pole, { name, feet: at(q.feet), unit });
|
|
104
|
+
case "bullpen":
|
|
105
|
+
return fill(k.bullpenItem, { name, feet: at(q.feet), unit });
|
|
106
|
+
case "ivy":
|
|
107
|
+
case "basket":
|
|
108
|
+
return name;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** The sentences a ballpark adds to a drawing's description: its walls, then its quirks. */
|
|
112
|
+
export function ballparkSentences(b) {
|
|
113
|
+
const out = [];
|
|
114
|
+
const sep = KYOGIJO_STRINGS[b.language].layers.listSep;
|
|
115
|
+
if (b.walls.length > 0)
|
|
116
|
+
out.push(fill(b.words.walls, { list: b.walls.map((wall) => wallText(wall, b)).join(sep) }));
|
|
117
|
+
const items = b.quirks.map((q) => quirkText(q, b));
|
|
118
|
+
const k = b.words.kinds;
|
|
119
|
+
const unit = b.words.unit[b.unit];
|
|
120
|
+
if (b.backstop !== undefined)
|
|
121
|
+
items.push(fill(k.backstop, { feet: lengthText(b.backstop, b.unit), unit }));
|
|
122
|
+
if (b.dugouts?.home !== undefined)
|
|
123
|
+
items.push(fill(k.dugoutHome, { line: k[b.dugouts.home] }));
|
|
124
|
+
if (b.dugouts?.visitors !== undefined)
|
|
125
|
+
items.push(fill(k.dugoutVisitors, { line: k[b.dugouts.visitors] }));
|
|
126
|
+
if (items.length > 0)
|
|
127
|
+
out.push(fill(b.words.quirks, { list: items.join(sep) }));
|
|
128
|
+
return out;
|
|
129
|
+
}
|
package/dist/canvas.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { SceneOptions, SceneSource } from "./scene.types.ts";
|
|
2
|
+
/** A gradient the context makes: only `addColorStop` is called on it. */
|
|
3
|
+
export interface CanvasGradientLike {
|
|
4
|
+
addColorStop(offset: number, color: string): void;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The members of a 2D canvas context that are used, with the types of the standard ones. A `CanvasRenderingContext2D`, an `OffscreenCanvasRenderingContext2D`
|
|
8
|
+
* and the contexts of the Node canvas libraries all have them. Members marked optional are used only when the context has them.
|
|
9
|
+
*/
|
|
10
|
+
export interface CanvasContext {
|
|
11
|
+
save(): void;
|
|
12
|
+
restore(): void;
|
|
13
|
+
setTransform(a: number, b: number, c: number, d: number, e: number, f: number): void;
|
|
14
|
+
beginPath(): void;
|
|
15
|
+
moveTo(x: number, y: number): void;
|
|
16
|
+
lineTo(x: number, y: number): void;
|
|
17
|
+
bezierCurveTo(c1x: number, c1y: number, c2x: number, c2y: number, x: number, y: number): void;
|
|
18
|
+
quadraticCurveTo(cx: number, cy: number, x: number, y: number): void;
|
|
19
|
+
closePath(): void;
|
|
20
|
+
fill(rule?: "nonzero" | "evenodd"): void;
|
|
21
|
+
stroke(): void;
|
|
22
|
+
clip(rule?: "nonzero" | "evenodd"): void;
|
|
23
|
+
fillText(text: string, x: number, y: number): void;
|
|
24
|
+
strokeText(text: string, x: number, y: number): void;
|
|
25
|
+
fillRect?(x: number, y: number, w: number, h: number): void;
|
|
26
|
+
clearRect?(x: number, y: number, w: number, h: number): void;
|
|
27
|
+
setLineDash(segments: number[]): void;
|
|
28
|
+
createLinearGradient?(x0: number, y0: number, x1: number, y1: number): CanvasGradientLike;
|
|
29
|
+
drawImage?(image: never, x: number, y: number, w: number, h: number): void;
|
|
30
|
+
/** A colour, or a gradient (or, on a real context, a pattern, which is never written here). */
|
|
31
|
+
fillStyle: string | CanvasGradientLike | object;
|
|
32
|
+
strokeStyle: string | CanvasGradientLike | object;
|
|
33
|
+
lineWidth: number;
|
|
34
|
+
lineJoin: string;
|
|
35
|
+
lineCap: string;
|
|
36
|
+
globalAlpha: number;
|
|
37
|
+
font: string;
|
|
38
|
+
textAlign: string;
|
|
39
|
+
textBaseline: string;
|
|
40
|
+
/** CSS filters, where the context has them (a browser's). */
|
|
41
|
+
filter?: string;
|
|
42
|
+
letterSpacing?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Where and how large a scene is drawn, and what is not in the scene. */
|
|
45
|
+
export interface CanvasOptions extends SceneOptions {
|
|
46
|
+
/** The width the surface is drawn at, in the context's units (pixels). With a `height` too, the drawing is fitted inside and centred. */
|
|
47
|
+
width?: number;
|
|
48
|
+
height?: number;
|
|
49
|
+
/** Pixels for one unit of the drawing (a foot, for most). Instead of a width or a height. 1 when none of the three is given. */
|
|
50
|
+
scale?: number;
|
|
51
|
+
/** Where the drawing's top left corner goes. 0, 0. */
|
|
52
|
+
x?: number;
|
|
53
|
+
y?: number;
|
|
54
|
+
/** Fill the picture's box with this colour first. */
|
|
55
|
+
background?: string;
|
|
56
|
+
/** Clear the picture's box first (where the context can). */
|
|
57
|
+
clear?: boolean;
|
|
58
|
+
/** The picture for a team's logo: given the address in the drawing, it returns something the context's `drawImage` takes (a loaded `Image`, an `ImageBitmap`, a canvas), or nothing, and then that logo is left out. */
|
|
59
|
+
images?: (href: string) => unknown;
|
|
60
|
+
}
|
|
61
|
+
/** What `drawToCanvas` drew. */
|
|
62
|
+
export interface Drawn {
|
|
63
|
+
/** The box the drawing took, in the context's units. */
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
/** Pixels for one unit of the drawing. */
|
|
67
|
+
scale: number;
|
|
68
|
+
}
|
|
69
|
+
/** The colour with an alpha: `rgb(...)` of a hex or rgb colour, or the colour itself when it cannot be read (and the alpha is 1). */
|
|
70
|
+
export declare function withAlpha(color: string, alpha: number): string;
|
|
71
|
+
/**
|
|
72
|
+
* Draw a scene (or the options of `drawKyogijo`, or an SVG the package wrote) onto a canvas context. Give a `width` or a `height` in pixels, or a `scale`; the
|
|
73
|
+
* drawing keeps its shape. Light or dark by `theme`. A team's logo is drawn only if `images` returns a picture for its address.
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { drawToCanvas, type CanvasContext } from "@johnmorrisdotca/kyogijo-stadiums/canvas";
|
|
77
|
+
* // Any object with the context's members will do: this one only counts what is drawn.
|
|
78
|
+
* const counts: Record<string, number> = {};
|
|
79
|
+
* const ctx = new Proxy({} as CanvasContext, { get: (_, name: string) => () => void (counts[name] = (counts[name] ?? 0) + 1), set: () => true });
|
|
80
|
+
* const drawn = drawToCanvas(ctx, { sport: "nhl", id: "rink" }, { width: 800 });
|
|
81
|
+
* console.log(drawn.width, Math.round(drawn.height), counts.fill > 10);
|
|
82
|
+
* // 800 374 true
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare function drawToCanvas(ctx: CanvasContext, source: SceneSource, options?: CanvasOptions): Drawn;
|