@johnmorrisdotca/kyogijo-stadiums 0.33.0 → 0.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ All notable changes to this project are written here. The format follows
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.34.1] - 2026-10-20
11
+
12
+ ### Fixed
13
+
14
+ - **No gray empty areas in the 3D view of a real venue** (`src/venue.fill.ts`, [the venues](./docs/venues.md), [the stadiums](./docs/stadiums.md)): the 3D arenas showed flat gray nothing in three places. **The shelf between the bowls** is now the suite level: a facing wall with a band of windows, a pane to every 7 feet, and a ledge. **The apron between the surface and the first row** is a thin dark floor, with the players' benches, the penalty boxes and the timekeeper's table at the usual places on the bench side of an ice rink. **The slab between the back of the bowl and the building** is closed by a back wall with a coping (it gives way to the eye as the building's walls do, keeping a little of itself) and a darker concourse floor with its lanes and joints, out to the building's outline, the annexes included. Every arena of the NHL, every stadium and the hall, and a tournament layout's stadium (`VenueSettings.fill`) have the fill; the usual stands (`crowd: true`) are unchanged. New part kinds `apron`, `bench` and `floor`; a ballpark gets the suite wall and the back wall, and no apron or floor of its own.
15
+
16
+ ## [0.34.0] - 2026-10-20
17
+
18
+ ### Added
19
+
20
+ - **Koshien Stadium** (`park: "hanshin-koshien-stadium"`, `阪神甲子園球場`, 47,400 seats, open air): the way home plate faces is found by fitting the park's published fence to the grass of the field OpenStreetMap maps (`axis.basis` `pitch`, 172 degrees, the fence 2.9 metres from the mapped edge, `axis.rms`; good to about ten degrees, and the Japanese article puts the annex south, behind centre field). The ivy and the all-dirt infield are plain drawing choices the sources give (`looks.ivyWalls`, `looks.dirtInfield`): green walls in 2D and 3D, and no grass inside the bases. No roof over the field (`StadiumRoof` `open`); the Ginsan roof over the grandstand is not drawn.
21
+
10
22
  ## [0.33.0] - 2026-10-20
11
23
 
12
24
  ### Added
package/NOTICE.md CHANGED
@@ -45,8 +45,8 @@ picture or code of the sources is copied; each source is named here so that the
45
45
  | `arenas`: each arena's names, years, city, seats, rink sizes, Wikidata item and coordinates | The infobox and the text of the English Wikipedia article of each arena, at the revision cited below (the names with the years the article dates them, the year it opened, closed and was pulled down, the seats for ice hockey and for basketball, a rink's length and width where the article gives them in a sentence of its own); the name in Japanese from the title of its Japanese article or written for the package; the Wikidata item (CC0) that each article is linked to and its coordinates (P625) | Facts. The articles are CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0/); none of their text, tables or images is used |
46
46
  | `venues`: the outline of each NHL arena's building, how its rink lies in it and the building's height (`VENUE_OSM`, `src/venues.osm.data.ts`) | OpenStreetMap, through the Overpass API: the closed way (or the relation of a building drawn in two ways) tagged with the venue's Wikidata item (and the ice, where it is mapped), read on 2026-10-20, with the raw answers kept in `data-sources/venues/2026-10-20/`; the outline is simplified to a metre, and the way the rink lies is worked out from it (`coreOf`, `src/venues.geo.ts`) | ODbL 1.0, (c) OpenStreetMap contributors (https://www.openstreetmap.org/copyright): a derived database, kept in its own file and offered under the ODbL; a drawing made from it is a produced work and carries the credit (`VENUE_ATTRIBUTION`) |
47
47
  | `venues`: each venue's seats for ice hockey, number of seating tiers and kind of roof (`VENUE_DATA`, `src/venues.data.ts`) | The item of Wikidata (the capacity P1083 and the sport it applies to P641) and the English Wikipedia article of each arena, at the revision cited below, in `data-sources/venues/2026-10-20/` as read; the facts are numbers and words of our own, with what was read in a few words | Wikidata's data is CC0; the facts read from the articles are facts, with the articles credited here (CC BY-SA 4.0) |
48
- | `stadiums`: the outline of each stadium's building, the roof OpenStreetMap maps over it (its heights, the opening in it or its canopies), the pitches or the infield, and how the field lies, worked out from the opening, the pitches or the infield and the outline (`STADIUM_OSM`, `src/stadiums.osm.data.ts`) | OpenStreetMap, through the Overpass API: the closed way tagged with the stadium's Wikidata item and the multipolygon of its roof (BC Place), or the way or relation of the building, the roof parts, the pitches, the infield or the grass of the field named in `scripts/stadiums.data.json` (Lumen Field, T-Mobile Park, Tokyo Dome, Korakuen Hall), read on 2026-10-20, with the raw answers kept in `data-sources/stadiums/2026-10-20/`; the outline is simplified to a metre, and the way the field lies is worked out from the opening, the pitches or the infield (`smallestBox`, `src/venues.geo.ts`; a ballpark's way out to centre field is the one of four where its fence lies inside the outline, or, where nothing maps it, not known, and the heading is the one in which fence and stands fit the outline best (Tokyo Dome)) | ODbL 1.0, (c) OpenStreetMap contributors (https://www.openstreetmap.org/copyright): a derived database, kept in its own file and offered under the ODbL; a drawing made from it is a produced work and carries the credit (`VENUE_ATTRIBUTION`) |
49
- | `stadiums`: each stadium's seats for each configuration, number of seating bowls, kind of roof and the size of its opening (`STADIUM_DATA`, `src/stadiums.data.ts`) | The item of Wikidata (the capacity P1083, the coordinates P625 and the labels in English and Japanese that name a hall) and the English Wikipedia article of each stadium, at the revision cited below, and the Japanese article of the stadiums of Japan, at the revision cited in [NOTICE.ja.md](./NOTICE.ja.md#実在のスタジアム), in `data-sources/stadiums/2026-10-20/` as read; the facts are numbers and words of our own, with what was read in a few words (and, for a fact leaning on a sentence, the phrase the archive must hold) | Wikidata's data is CC0; the facts read from the articles are facts, with the articles credited here (CC BY-SA 4.0) |
48
+ | `stadiums`: the outline of each stadium's building, the roof OpenStreetMap maps over it (its heights, the opening in it or its canopies), the pitches or the infield, and how the field lies, worked out from the opening, the pitches or the infield and the outline (`STADIUM_OSM`, `src/stadiums.osm.data.ts`) | OpenStreetMap, through the Overpass API: the closed way tagged with the stadium's Wikidata item and the multipolygon of its roof (BC Place), or the way or relation of the building, the roof parts, the pitches, the infield or the grass of the field named in `scripts/stadiums.data.json` (Lumen Field, T-Mobile Park, Tokyo Dome, Korakuen Hall, Koshien Stadium), read on 2026-10-20, with the raw answers kept in `data-sources/stadiums/2026-10-20/`; the outline is simplified to a metre, and the way the field lies is worked out from the opening, the pitches or the infield (`smallestBox`, `src/venues.geo.ts`; a ballpark's way out to centre field is the one of four where its fence lies inside the outline, or the heading and home plate at which its published fence lies nearest the mapped grass of the field (Koshien Stadium), or, where nothing maps it, not known, and the heading is the one in which fence and stands fit the outline best (Tokyo Dome)) | ODbL 1.0, (c) OpenStreetMap contributors (https://www.openstreetmap.org/copyright): a derived database, kept in its own file and offered under the ODbL; a drawing made from it is a produced work and carries the credit (`VENUE_ATTRIBUTION`) |
49
+ | `stadiums`: each stadium's seats for each configuration, number of seating bowls, kind of roof and the size of its opening (`STADIUM_DATA`, `src/stadiums.data.ts`) | The item of Wikidata (the capacity P1083, the coordinates P625 and the labels in English and Japanese that name a hall) and the English Wikipedia article of each stadium, at the revision cited below, and the Japanese article of the three stadiums of Japan, at the revision cited in [NOTICE.ja.md](./NOTICE.ja.md#実在のスタジアム), in `data-sources/stadiums/2026-10-20/` as read; the facts are numbers and words of our own, with what was read in a few words (and, for a fact leaning on a sentence, the phrase the archive must hold) | Wikidata's data is CC0; the facts read from the articles are facts, with the articles credited here (CC BY-SA 4.0) |
50
50
  | `atlas`: the clubs that played in each park, ground and arena (`tenants`: the club, its Wikidata item, its league, the years) | The occupants (P466) and home venues (P115) Wikidata lists for each place, with the start (P580) and end (P582) times of each statement, and the clubs' labels in English and Japanese, sports (P641) and leagues (P118), read with the Wikidata API and query service by `scripts/fetch-tenants.mjs` into `scripts/tenants.wikidata.json`; and the `tenants` line of the infobox of each place's English Wikipedia article, at the revision that `source.wikipedia` names for each club (155 articles, read on 2026-10-13) | Facts. Wikidata's data is CC0 (https://creativecommons.org/publicdomain/zero/1.0/), and the articles are CC BY-SA 4.0 as above; the names of clubs and the years are facts, no text, table or image of an article is used. Which clubs are tenants, and the league each is filed under, are our reading, in docs/atlas.md ("Tenants") |
51
51
  | `parks`: the walls, quirks, backstop, warning track, dugouts and extra points of a ballpark (`walls`, `quirks`, `backstop_ft`, `track_ft`, `dugouts`, `labels`) | The infobox and the text of the same English article of each park, at the same revision as its fence (the Japanese article for the heights of the walls of the parks of Japan): the height of the wall along a stretch of the fence and the seasons it stood, the hill, overhang, poles, bullpens, ivy and basket the text describes with figures, the infobox's backstop distance, the warning track's width, the sides of the dugouts, and the further distances the text gives (Wrigley Field's wells, Sportsman's Park's and Milwaukee County Stadium's markers). The stretch each figure covers and the angle of each extra point are our reading of the article's words | Facts. The articles are CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0/); none of their text, tables or images is used |
52
52
  | `parks`: the coordinates of each ballpark (`lat`, `lon`) and the years it opened and closed (`opened`, `closed`) | The item's coordinate location (P625), its date of official opening or inception (P1619, P571) and of official closure or dissolution (P3999, P576), read with the Wikidata API by `scripts/fetch-park-coordinates.mjs` and checked by `scripts/check-parks.mjs` (ES CON Field's opening, 2023, is read from its article) | Wikidata's data is CC0 (https://creativecommons.org/publicdomain/zero/1.0/) |
@@ -452,7 +452,7 @@ The thirty-two arenas of the NHL, read on 2026-10-20 (`pnpm venues:fetch <date>
452
452
 
453
453
  ## The stadiums' sources
454
454
 
455
- BC Place, Lumen Field, T-Mobile Park, Tokyo Dome and Korakuen Hall, read on 2026-10-20 (`pnpm stadiums:fetch`; the files are in `data-sources/stadiums/2026-10-20/`: `osm-<stadium>.json` as Overpass returned it, `wikidata.json` and `wikipedia.json`). The outline of the building, its roof and the opening in it are OpenStreetMap's, ODbL 1.0, (c) OpenStreetMap contributors, and live in `src/stadiums.osm.data.ts` alone. The seats of each configuration, the bowls, the roof's kind and the size of its opening are in `src/stadiums.data.ts`, with what each cites. No seat map of a club, a ticket seller or a federation is read or copied, and no mark of a club, a league or a tournament is drawn.
455
+ BC Place, Lumen Field, T-Mobile Park, Tokyo Dome, Korakuen Hall and Koshien Stadium, read on 2026-10-20 (`pnpm stadiums:fetch`; the files are in `data-sources/stadiums/2026-10-20/`: `osm-<stadium>.json` as Overpass returned it, `wikidata.json` and `wikipedia.json`). The outline of the building, its roof and the opening in it are OpenStreetMap's, ODbL 1.0, (c) OpenStreetMap contributors, and live in `src/stadiums.osm.data.ts` alone. The seats of each configuration, the bowls, the roof's kind and the size of its opening are in `src/stadiums.data.ts`, with what each cites. No seat map of a club, a ticket seller or a federation is read or copied, and no mark of a club, a league or a tournament is drawn.
456
456
 
457
457
  | Stadium | Wikidata item | OpenStreetMap way | English article and revision |
458
458
  | --- | --- | --- | --- |
@@ -461,6 +461,7 @@ BC Place, Lumen Field, T-Mobile Park, Tokyo Dome and Korakuen Hall, read on 2026
461
461
  | T-Mobile Park | Q1193117 | relation/3008332 | T-Mobile Park, 1379419810 |
462
462
  | Tokyo Dome | Q733748 | way/20028328 | Tokyo Dome, 1379232526 |
463
463
  | Korakuen Hall | Q4389857 | way/45977418 | Korakuen Hall, 1372211318 |
464
+ | Koshien Stadium | Q959811 | relation/9845926 | Koshien Stadium, 1374217766 |
464
465
 
465
466
  ## The World Cups' articles
466
467
 
package/README.md CHANGED
@@ -604,7 +604,7 @@ console.log(svg.includes("ky-cr-shell"), planVenue(venueById("climate-pledge-are
604
604
 
605
605
  ### A real stadium, and a district
606
606
 
607
- `stadium: "bc-place"` draws BC Place for football or soccer under its cable roof, and Lumen Field, T-Mobile Park, Tokyo Dome (`park`) and Korakuen Hall (`sport: "boxing"`) likewise ([stadiums](./docs/stadiums.md)); `drawDistrict({ venues: ["bc-place", "rogers-arena"] })` puts venues at their real positions, tight as neighbours or spread as a city, to turn together in 3D ([districts](./docs/district.md)).
607
+ `stadium: "bc-place"` draws BC Place for football or soccer under its cable roof, and Lumen Field, T-Mobile Park, Tokyo Dome, Koshien Stadium (`park`) and Korakuen Hall (`sport: "boxing"`) likewise ([stadiums](./docs/stadiums.md)); `drawDistrict({ venues: ["bc-place", "rogers-arena"] })` puts venues at their real positions, tight as neighbours or spread as a city, to turn together in 3D ([districts](./docs/district.md)).
608
608
 
609
609
  <p align="center">
610
610
  <picture>
@@ -177,7 +177,7 @@ export function buildMultiTurn(options, settings = {}) {
177
177
  let crowdStyle = "";
178
178
  if (drawn.stands !== undefined && keep("stands")) {
179
179
  const roof = typeof settings.venue === "object" && settings.venue.roof !== undefined ? settings.venue.roof : typeof options.stadium === "object" && options.stadium.roof === true;
180
- const venue = standsVenueOf(drawn.stands.stands, drawn.stands.crowd, { sport: drawn.stands.standsLike }, { ...(typeof settings.venue === "object" ? settings.venue : {}), roof, id }, 0);
180
+ const venue = standsVenueOf(drawn.stands.stands, drawn.stands.crowd, { sport: drawn.stands.standsLike }, { fill: true, ...(typeof settings.venue === "object" ? settings.venue : {}), roof, id }, 0);
181
181
  items.push(...venue.items.filter((item) => item.kind !== "roof" || roof));
182
182
  defs = venue.defs;
183
183
  crowdStyle = "--ky-cr-home:#8b94a3;--ky-cr-away:#8b94a3";
@@ -58,4 +58,17 @@ export const STADIUM_DATA = [
58
58
  "tiers": { "count": 2, "basis": "documented", "lowerSeats": 1403, "read": "the Japanese article: the 1,403 seats are on the floor (780 fixed seats on the south side, movable seats of 109 east, 109 west and 405 north), and the balconies on the east and west sides, above, take standing places up to 2,005" },
59
59
  "roof": { "kind": "fixed", "basis": "plausible", "read": "the hall is on the fifth floor of a building (the Japanese article), under the building's own roof, drawn as a lid (ours)" },
60
60
  },
61
+ {
62
+ "id": "hanshin-koshien-stadium",
63
+ "kind": "park",
64
+ "wikidata": "Q959811",
65
+ "park": "hanshin-koshien-stadium",
66
+ "en": { "title": "Koshien Stadium", "revision": 1374217766 },
67
+ "ja": { "title": "阪神甲子園球場", "revision": 111237979 },
68
+ "looks": { "ivyWalls": "the Japanese article: ivy planted on the outer wall since 1924, taken off for the renovation and planted again; the English article calls the ivy a symbol of the stadium (drawn as a green wall, nothing more)", "dirtInfield": "the Japanese article: the infield is the black soil of the park and the outfield natural grass; the English article: the infield continues to be all dirt (drawn as dirt with no grass inside the bases)" },
69
+ "configs": { "mlb": { "sport": "mlb", "seats": 47400, "source": "wikipedia", "read": "the infobox's last capacity, 47,400 (it lists the earlier ones, 47,757 and 47,808 among them)" } },
70
+ "wikidataSeats": [60000],
71
+ "tiers": { "count": 1, "basis": "documented", "read": "the Japanese article: the stands are on one level, the largest of a one-level stand in Japan, the infield stand's 50 rows 14.3 metres high" },
72
+ "roof": { "kind": "open", "basis": "documented", "read": "the English article: the outfield is natural grass and open to the air, no roof over the grounds; the Ginsan roof over the grandstand behind home plate is not drawn" },
73
+ },
61
74
  ];
@@ -269,4 +269,56 @@ export const STADIUM_OSM = {
269
269
  [139.752282, 35.704525],
270
270
  ],
271
271
  },
272
+ "hanshin-koshien-stadium": {
273
+ "source": "relation/9845926",
274
+ "read": "2026-10-20",
275
+ "base": "2026-10-21T00:18:36Z",
276
+ "licence": "ODbL-1.0",
277
+ "attribution": "(c) OpenStreetMap contributors, ODbL 1.0, openstreetmap.org/copyright",
278
+ "axis": { "heading": 172, "basis": "pitch", "strength": 1.11, "elongation": 1, "centre": [135.361543, 34.721754], "rms": 2.9 },
279
+ "ring": [
280
+ [135.362626, 34.721658],
281
+ [135.362552, 34.721775],
282
+ [135.362618, 34.721805],
283
+ [135.362565, 34.721886],
284
+ [135.362495, 34.721855],
285
+ [135.362256, 34.722145],
286
+ [135.362131, 34.722246],
287
+ [135.362049, 34.72229],
288
+ [135.361969, 34.722318],
289
+ [135.361787, 34.722342],
290
+ [135.361787, 34.722375],
291
+ [135.361678, 34.722374],
292
+ [135.361678, 34.722335],
293
+ [135.361376, 34.722322],
294
+ [135.361375, 34.722349],
295
+ [135.361213, 34.722344],
296
+ [135.361212, 34.722301],
297
+ [135.361103, 34.722268],
298
+ [135.360939, 34.722177],
299
+ [135.360855, 34.722072],
300
+ [135.360679, 34.72174],
301
+ [135.360588, 34.721768],
302
+ [135.360532, 34.721652],
303
+ [135.360621, 34.721628],
304
+ [135.360411, 34.721003],
305
+ [135.360474, 34.721001],
306
+ [135.360531, 34.720839],
307
+ [135.360642, 34.720691],
308
+ [135.360816, 34.72055],
309
+ [135.360954, 34.720484],
310
+ [135.361226, 34.720409],
311
+ [135.361499, 34.720376],
312
+ [135.361802, 34.720379],
313
+ [135.361906, 34.720392],
314
+ [135.362228, 34.720484],
315
+ [135.362456, 34.720591],
316
+ [135.36266, 34.720748],
317
+ [135.362788, 34.720952],
318
+ [135.362826, 34.721047],
319
+ [135.362826, 34.721152],
320
+ [135.36287, 34.721163],
321
+ [135.362615, 34.721654],
322
+ ],
323
+ },
272
324
  };
@@ -1,7 +1,7 @@
1
1
  /** A point or a direction: x, y, z in feet. */
2
2
  export type V3 = readonly [number, number, number];
3
3
  /** What a plane is a part of: it is the class of its element (`ky-up-<kind>`) and what a test finds it by. */
4
- export type PartKind = "post" | "net" | "tape" | "antenna" | "board" | "glass" | "rim" | "stanchion" | "frame" | "goal" | "flag" | "stump" | "leg" | "table" | "pylon" | "wall" | "screen" | "pole" | "ground" | "target" | "seats" | "concourse" | "fascia" | "roof" | "banner" | "platform" | "rail" | "matedge" | "shrine" | "surround" | "chair" | "desk" | "card" | "sign";
4
+ export type PartKind = "post" | "net" | "tape" | "antenna" | "board" | "glass" | "rim" | "stanchion" | "frame" | "goal" | "flag" | "stump" | "leg" | "table" | "pylon" | "wall" | "screen" | "pole" | "ground" | "target" | "seats" | "concourse" | "fascia" | "roof" | "banner" | "platform" | "rail" | "matedge" | "shrine" | "surround" | "chair" | "desk" | "card" | "sign" | "apron" | "bench" | "floor";
5
5
  /** One flat rectangle. Its own x runs along `u` (its whole length) and its own y along `v`; `o` is the corner both start from. `svg` is what is drawn on it, in feet along `u` and `v`. */
6
6
  export interface Plane {
7
7
  /** A name that says what it is and where: `net`, `post-left`, `goal-right-back`. */
@@ -74,6 +74,24 @@ export const KYOGIJO_TURN_STYLE = `
74
74
  .ky-turn .u-cable{fill:none;stroke:var(--ky-up-roofline);stroke-width:1}
75
75
  .ky-turn .u-fabric{fill:var(--ky-up-roof);fill-opacity:.55;stroke:none}
76
76
  .ky-turn .u-pod{fill:var(--ky-up-fascia);stroke:var(--ky-up-roofline);stroke-width:.6}
77
+ /* A real venue's empty places, filled: the suite level between two bowls, the apron round the surface, the back of each bowl and the concourse floor behind it. */
78
+ .ky-turn .u-suite{fill:color-mix(in srgb,var(--ky-up-fascia) 78%,var(--ky-cr-bed))}
79
+ .ky-turn .u-suite-win{fill:color-mix(in srgb,var(--ky-cr-gap) 55%,#6f93b3)}
80
+ .ky-turn .u-suite-bar{stroke:var(--ky-up-fascia);stroke-width:.5}
81
+ .ky-turn .u-suite-ledge{fill:color-mix(in srgb,var(--ky-up-fascia) 60%,var(--ky-cr-conc))}
82
+ .ky-turn .u-apron{fill:color-mix(in srgb,var(--ky-cr-bed) 82%,#000)}
83
+ .ky-turn .u-timeb{fill:color-mix(in srgb,var(--ky-cr-conc) 70%,#3a3f46)}
84
+ .ky-turn .u-timeb-side{fill:color-mix(in srgb,var(--ky-cr-conc) 70%,#3a3f46);filter:brightness(.7)}
85
+ .ky-turn .u-penb{fill:color-mix(in srgb,var(--ky-up-glass,#bcd2e0) 80%,#5d7a8f)}
86
+ .ky-turn .u-penb-side{fill:color-mix(in srgb,var(--ky-up-glass,#bcd2e0) 80%,#5d7a8f);filter:brightness(.72)}
87
+ .ky-turn .u-teamb{fill:color-mix(in srgb,var(--ky-up-wood,#8a6a47) 80%,#2a2f36)}
88
+ .ky-turn .u-teamb-side{fill:color-mix(in srgb,var(--ky-up-wood,#8a6a47) 80%,#2a2f36);filter:brightness(.7)}
89
+ .ky-turn .u-back{fill:color-mix(in srgb,var(--ky-up-fascia) 66%,var(--ky-cr-bed))}
90
+ .ky-turn .u-backrib{stroke:var(--ky-cr-bed);stroke-width:.7;stroke-opacity:.7}
91
+ .ky-turn .u-coping{fill:color-mix(in srgb,var(--ky-up-fascia) 70%,var(--ky-cr-conc))}
92
+ .ky-turn .u-cfloor{fill:color-mix(in srgb,var(--ky-cr-bed) 60%,var(--ky-cr-conc))}
93
+ .ky-turn .u-clane{stroke:color-mix(in srgb,var(--ky-cr-conc) 55%,#fff);stroke-opacity:.6;stroke-width:1.1}
94
+ .ky-turn .kyogijo .ky-cr-shell:not(.ky-cr-ivy){fill:color-mix(in srgb,var(--ky-cr-bed) 70%,var(--ky-cr-conc));fill-opacity:.9}
77
95
  .ky-turn .ky-up-seats svg{opacity:var(--ky-cr-dim)}
78
96
  .ky-turn .ky-cr-seat{fill:none;stroke:var(--ky-cr-empty);stroke-width:1.3;stroke-opacity:.65}
79
97
  .ky-turn .ky-cr-h{stroke:var(--ky-cr-home)}
package/dist/venue.d.ts CHANGED
@@ -10,6 +10,8 @@ export interface VenueSettings {
10
10
  roofOpen?: boolean;
11
11
  /** The walls of a real venue's building (`crowd: { real: true }`), raised on its outline: they follow `roof` where left out (a roof and walls together, as before), and `walls: true` with `roof` off raises the walls alone, `roof: true` with `walls: false` the roof alone. */
12
12
  walls?: boolean;
13
+ /** Fill the empty places of the stands (the apron round the surface, the back of each bowl): on for a real venue's, off for the usual stands (where it is a tournament layout's hall that asks for it). */
14
+ fill?: boolean;
13
15
  /** How finely the curves are cut: `low` a facet to each corner, `auto` (the default) three, `high` six; an oval is 12, 28 or 56 facets round. */
14
16
  detail?: Cut;
15
17
  }
@@ -0,0 +1,40 @@
1
+ import { type CrowdStands, type Outline, type Pt, type StandRing } from "./crowd.ts";
2
+ import { type Plane } from "./turn.kit.ts";
3
+ import { type Cut } from "./venue.geom.ts";
4
+ /** The suite level's windows stand between these shares of the way across the facing wall (the bottom is a parapet, the top a ledge), and a pane is about this wide, in feet (ours). */
5
+ export declare const SUITE: {
6
+ readonly from: 0.3;
7
+ readonly to: 0.82;
8
+ readonly pane: 7;
9
+ };
10
+ /** The thin floor of the apron lies this far over the surface, in feet, so that it shows over the floor's own drawing. */
11
+ export declare const APRON_RISE = 0.06;
12
+ /** The bench side's furniture, in feet along the boards from the middle of the rink: the timekeeper's table, the penalty boxes either side of it, and each team's bench beyond (ours: the usual layout of a rink, no plan of a real one). */
13
+ export declare const BENCH_SIDE: {
14
+ readonly table: 6;
15
+ readonly penalty: readonly [8, 20];
16
+ readonly bench: readonly [24, 62];
17
+ readonly depth: 5;
18
+ readonly gap: 1.5;
19
+ readonly height: 3.4;
20
+ };
21
+ /** How much of a bowl's back wall is left where it is between the eye and the surface (the walls of the building take 0.02: the back of a bowl keeps a little of its body). */
22
+ export declare const BACK_FADE = 0.08;
23
+ /** A piece of the concourse floor smaller than this, in square feet, is a sliver a corner leaves (where the hall's rim meets the building's outline): it is left to the floor drawn under it, which has the same dark tone. */
24
+ export declare const SLIVER = 80;
25
+ /** The suite level seen square on: the facing wall of a quad `[inner a, inner b, outer b, outer a]` (the plane's own corners), with a band of windows and a pane to every `SUITE.pane` feet. */
26
+ export declare function suiteWall(corners: Pt[]): string;
27
+ /** The apron: the floor between the playing surface and the first row, a thin dark floor, and where the bowl is an ice rink's the benches, the penalty boxes and the timekeeper's table on the bench side. */
28
+ export declare function apronPlanes(stands: CrowdStands, first: StandRing, cut: Cut, lift: number): Plane[];
29
+ /** The plan of a ring's back: the outer edge of every strip of a tier that no walk leads on from, as the loop of points the wall is raised on. */
30
+ export declare const backEdges: (ring: StandRing, cut: Cut) => Pt[][];
31
+ /** The back wall of one bowl: from the floor up to the top row and a coping over it, on each stretch of the edge, painted with its ribs. It gives way to the eye as the walls of the building do, but keeps a little of itself. */
32
+ export declare function backWall(id: string, edge: readonly Pt[], top: number, lift: number, centre: readonly [number, number]): Plane[];
33
+ /**
34
+ * The concourse floor behind a bowl: flat quads, painted dark with two lanes, in rays from the middle: from the bowl's outer edge out to the rim of the hall (the nearer of the hall's rounded outline and the
35
+ * building's own), and from there on out to the building's outline, where the building has annexes beyond the hall (the walls and the roof stand on the hall only; the rest is floor too, not a bare sheet).
36
+ * The two rings are cut at the rim, so that a wall standing on it is drawn between them in the right order.
37
+ */
38
+ export declare function concourseFloor(id: string, edge: Outline, middle: Pt, rim: (angle: number) => Pt, far: (angle: number) => Pt | undefined, steps: number, lift: number): Plane[];
39
+ /** The rim of a real venue's hall along an angle from `middle`: the nearer of the hall's rounded outline and the building's own outline. */
40
+ export declare const rimAt: (hall: Outline, footprint: readonly Pt[], middle: Pt) => (angle: number) => Pt;
@@ -0,0 +1,174 @@
1
+ // What fills the empty places of a real venue's 3D view, so that no big flat sheet is left in it: the suite level between two bowls (a facing wall with a band of windows, not a bare shelf), the apron between the
2
+ // playing surface and the first row (a thin dark floor, and for ice hockey the players' benches, the penalty boxes and the timekeeper's table), the back of each bowl (a wall that closes it) and, between that wall and
3
+ // the walls of the hall, a darker concourse floor with its lanes. All of it is ours, plausible and no plan of a real building: where a source gives a figure for none of it, the sizes are the usual ones.
4
+ // Pure: data in, planes out.
5
+ import { rayAt } from "./crowd.js";
6
+ import { NHL } from "./geometry.js";
7
+ import { n, shy, wall, box } from "./turn.kit.js";
8
+ import { areaOf, pointList, raked, stripsOf, tidy } from "./venue.geom.js";
9
+ import { firstHit } from "./venue.real.js";
10
+ /** The suite level's windows stand between these shares of the way across the facing wall (the bottom is a parapet, the top a ledge), and a pane is about this wide, in feet (ours). */
11
+ export const SUITE = { from: 0.3, to: 0.82, pane: 7 };
12
+ /** The thin floor of the apron lies this far over the surface, in feet, so that it shows over the floor's own drawing. */
13
+ export const APRON_RISE = 0.06;
14
+ /** The bench side's furniture, in feet along the boards from the middle of the rink: the timekeeper's table, the penalty boxes either side of it, and each team's bench beyond (ours: the usual layout of a rink, no plan of a real one). */
15
+ export const BENCH_SIDE = { table: 6, penalty: [8, 20], bench: [24, 62], depth: 5, gap: 1.5, height: 3.4 };
16
+ /** How much of a bowl's back wall is left where it is between the eye and the surface (the walls of the building take 0.02: the back of a bowl keeps a little of its body). */
17
+ export const BACK_FADE = 0.08;
18
+ /** How high the coping stands over the top row on the back wall, in feet, and the walks of the concourse floor are this many feet from its two edges (ours). */
19
+ const COPING = 3;
20
+ /** The joints of the concourse floor: one about every this many feet along it, at most so many to a quad (ours). */
21
+ const JOINT = 22;
22
+ /** A piece of the concourse floor smaller than this, in square feet, is a sliver a corner leaves (where the hall's rim meets the building's outline): it is left to the floor drawn under it, which has the same dark tone. */
23
+ export const SLIVER = 80;
24
+ const JOINTS_MOST = 8;
25
+ const lerp = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
26
+ const flatAt = (z) => ({ x0: 0, y0: 0, z0: z, gx: 0, gy: 0 });
27
+ /** The suite level seen square on: the facing wall of a quad `[inner a, inner b, outer b, outer a]` (the plane's own corners), with a band of windows and a pane to every `SUITE.pane` feet. */
28
+ export function suiteWall(corners) {
29
+ const base = `<polygon class="u-suite" points="${pointList(corners)}"/>`;
30
+ if (corners.length !== 4)
31
+ return base;
32
+ const [a, b, c, d] = corners;
33
+ const across = (s, t) => lerp(lerp(a, b, s), lerp(d, c, s), t);
34
+ const panes = Math.max(1, Math.round(Math.hypot(b[0] - a[0], b[1] - a[1]) / SUITE.pane));
35
+ const band = `<polygon class="u-suite-win" points="${pointList([across(0, SUITE.from), across(1, SUITE.from), across(1, SUITE.to), across(0, SUITE.to)])}"/>`;
36
+ let bars = "";
37
+ for (let j = 1; j < panes; j += 1) {
38
+ const [p, q] = [across(j / panes, SUITE.from), across(j / panes, SUITE.to)];
39
+ bars += `M${n(p[0])} ${n(p[1])}L${n(q[0])} ${n(q[1])}`;
40
+ }
41
+ const ledge = `<polygon class="u-suite-ledge" points="${pointList([across(0, SUITE.to), across(1, SUITE.to), across(1, SUITE.to + 0.07), across(0, SUITE.to + 0.07)])}"/>`;
42
+ return `${base}${band}${bars === "" ? "" : `<path class="u-suite-bar" fill="none" d="${bars}"/>`}${ledge}`;
43
+ }
44
+ /** The floor strips of a ring of quads painted as a floor: a base and the walks along it, at the shares `lanes` of the way across. */
45
+ function floorOf(cls, lanes) {
46
+ return (corners) => {
47
+ const base = `<polygon class="${cls}" points="${pointList(corners)}"/>`;
48
+ if (corners.length === 3) {
49
+ // A triangle (where two rays meet at a corner of the building): its outline drawn again, smaller, round its middle.
50
+ const mid = [(corners[0][0] + corners[1][0] + corners[2][0]) / 3, (corners[0][1] + corners[1][1] + corners[2][1]) / 3];
51
+ const inner = lanes.map((t) => `M${corners.map((p) => `${n(mid[0] + (p[0] - mid[0]) * (1 - t))} ${n(mid[1] + (p[1] - mid[1]) * (1 - t))}`).join("L")}Z`);
52
+ return `${base}<path class="u-clane" fill="none" d="${inner.join("")}"/>`;
53
+ }
54
+ if (corners.length !== 4)
55
+ return base;
56
+ const [a, b, c, d] = corners;
57
+ const lines = lanes.map((t) => {
58
+ const [p, q] = [lerp(a, d, t), lerp(b, c, t)];
59
+ return `M${n(p[0])} ${n(p[1])}L${n(q[0])} ${n(q[1])}`;
60
+ });
61
+ // Joints across the floor, a paving's worth, so that a wide floor is not one flat sheet.
62
+ const joints = Math.min(JOINTS_MOST, Math.max(0, Math.round(Math.hypot(b[0] - a[0], b[1] - a[1]) / JOINT)));
63
+ for (let j = 1; j < joints; j += 1) {
64
+ const [p, q] = [lerp(a, b, j / joints), lerp(d, c, j / joints)];
65
+ lines.push(`M${n(p[0])} ${n(p[1])}L${n(q[0])} ${n(q[1])}`);
66
+ }
67
+ return `${base}<path class="u-clane" fill="none" d="${lines.join("")}"/>`;
68
+ };
69
+ }
70
+ /** The quads of a strip, as `[inner i, inner i + 1, outer i + 1, outer i]`, those with a width. */
71
+ function* quadsOf(strip) {
72
+ for (let i = 0; i + 1 < strip.inner.length; i += 1) {
73
+ const q = [strip.inner[i], strip.inner[i + 1], strip.outer[i + 1], strip.outer[i]];
74
+ if (Math.hypot(q[1][0] - q[0][0], q[1][1] - q[0][1]) < 1e-6 && Math.hypot(q[2][0] - q[3][0], q[2][1] - q[3][1]) < 1e-6)
75
+ continue;
76
+ yield q;
77
+ }
78
+ }
79
+ /** The direction along a quad's first edge (or its outer edge where the inner has shrunk to a point). */
80
+ const alongOf = (q) => {
81
+ const [from, to] = Math.hypot(q[1][0] - q[0][0], q[1][1] - q[0][1]) >= Math.hypot(q[2][0] - q[3][0], q[2][1] - q[3][1]) ? [q[0], q[1]] : [q[3], q[2]];
82
+ const long = Math.hypot(to[0] - from[0], to[1] - from[1]);
83
+ return long < 1e-9 ? [1, 0] : [(to[0] - from[0]) / long, (to[1] - from[1]) / long];
84
+ };
85
+ /** A ring of flat quads lying at `z`, between two outlines, painted as `paint` says, with the ids `${id}-${i}`. */
86
+ function floorRing(id, kind, strips, z, paint, least = 0.05) {
87
+ const planes = [];
88
+ let count = 0;
89
+ for (const strip of strips) {
90
+ for (const q of quadsOf(strip)) {
91
+ const poly = tidy(q);
92
+ if (poly.length < 3 || Math.abs(areaOf(poly)) < least)
93
+ continue;
94
+ count += 1;
95
+ const p = raked(`${id}-${count}`, kind, poly, flatAt(z), alongOf(q), (corners) => paint(corners));
96
+ if (p !== undefined)
97
+ planes.push(p);
98
+ }
99
+ }
100
+ return planes;
101
+ }
102
+ /** The apron: the floor between the playing surface and the first row, a thin dark floor, and where the bowl is an ice rink's the benches, the penalty boxes and the timekeeper's table on the bench side. */
103
+ export function apronPlanes(stands, first, cut, lift) {
104
+ const hole = first.hole;
105
+ if (hole === null || hole.kind !== "rrect")
106
+ return [];
107
+ const b = stands.box;
108
+ const rink = { kind: "rrect", x0: b.x, y0: b.y, x1: b.x + b.w, y1: b.y + b.h, r: stands.sport === "nhl" ? NHL.cornerRadius : 0 };
109
+ const planes = floorRing("apron", "apron", stripsOf(hole, rink, [], cut), lift + APRON_RISE, (corners) => `<polygon class="u-apron" points="${pointList(corners)}"/>`);
110
+ if (stands.sport !== "nhl")
111
+ return planes;
112
+ // The bench side is the one with the deeper apron (the NHL's first row stands further from that side); the furniture stands in the clear between the boards and the first row.
113
+ const bottom = hole.y1 - (b.y + b.h) >= b.y - hole.y0;
114
+ const depth = bottom ? hole.y1 - (b.y + b.h) : b.y - hole.y0;
115
+ const need = BENCH_SIDE.gap + BENCH_SIDE.depth;
116
+ if (depth < need + 1)
117
+ return planes;
118
+ const [y0, y1] = bottom ? [b.y + b.h + BENCH_SIDE.gap, b.y + b.h + need] : [b.y - need, b.y - BENCH_SIDE.gap];
119
+ const mid = b.x + b.w / 2;
120
+ const put = (id, from, to, cls) => box(id, "bench", { x0: mid + from, y0, x1: mid + to, y1 }, BENCH_SIDE.height, cls);
121
+ const furniture = [...put("timekeeper", -BENCH_SIDE.table, BENCH_SIDE.table, "u-timeb")];
122
+ for (const sign of [-1, 1]) {
123
+ const span = (lo, hi) => (sign < 0 ? [-hi, -lo] : [lo, hi]);
124
+ furniture.push(...put(`penalty-${sign < 0 ? "a" : "b"}`, ...span(BENCH_SIDE.penalty[0], BENCH_SIDE.penalty[1]), "u-penb"));
125
+ furniture.push(...put(`bench-${sign < 0 ? "a" : "b"}`, ...span(BENCH_SIDE.bench[0], BENCH_SIDE.bench[1]), "u-teamb"));
126
+ }
127
+ return [...planes, ...furniture];
128
+ }
129
+ /** The plan of a ring's back: the outer edge of every strip of a tier that no walk leads on from, as the loop of points the wall is raised on. */
130
+ export const backEdges = (ring, cut) => stripsOf(ring.region, ring.hole, ring.more ?? [], cut).map((strip) => strip.outer);
131
+ /** The back wall of one bowl: from the floor up to the top row and a coping over it, on each stretch of the edge, painted with its ribs. It gives way to the eye as the walls of the building do, but keeps a little of itself. */
132
+ export function backWall(id, edge, top, lift, centre) {
133
+ const planes = [];
134
+ for (let i = 0; i + 1 < edge.length; i += 1) {
135
+ const [a, b] = [edge[i], edge[i + 1]];
136
+ const long = Math.hypot(b[0] - a[0], b[1] - a[1]);
137
+ if (long < 0.5)
138
+ continue;
139
+ const high = lift + top;
140
+ const ribs = Math.max(1, Math.round(long / 14));
141
+ let lines = "";
142
+ for (let j = 1; j < ribs; j += 1)
143
+ lines += `M${n((long * j) / ribs)} ${n(COPING)}V${n(high)}`;
144
+ const svg = `<rect class="u-back" width="${n(long)}" height="${n(high)}"/>${lines === "" ? "" : `<path class="u-backrib" fill="none" d="${lines}"/>`}<rect class="u-coping" width="${n(long)}" height="${n(COPING)}"/>`;
145
+ planes.push({ ...shy(wall(`${id}-${i}`, "wall", a[0], a[1], b[0], b[1], 0, high, svg), centre), fadeTo: BACK_FADE });
146
+ }
147
+ return planes;
148
+ }
149
+ /**
150
+ * The concourse floor behind a bowl: flat quads, painted dark with two lanes, in rays from the middle: from the bowl's outer edge out to the rim of the hall (the nearer of the hall's rounded outline and the
151
+ * building's own), and from there on out to the building's outline, where the building has annexes beyond the hall (the walls and the roof stand on the hall only; the rest is floor too, not a bare sheet).
152
+ * The two rings are cut at the rim, so that a wall standing on it is drawn between them in the right order.
153
+ */
154
+ export function concourseFloor(id, edge, middle, rim, far, steps, lift) {
155
+ const dist = (p) => Math.hypot(p[0] - middle[0], p[1] - middle[1]);
156
+ const angles = Array.from({ length: steps + 1 }, (_, k) => (2 * Math.PI * k) / steps);
157
+ const inner = angles.map((a) => rayAt(edge, middle[0], middle[1], a));
158
+ const rims = angles.map((a, k) => {
159
+ const r = rim(a);
160
+ return dist(r) > dist(inner[k]) + 0.5 ? r : inner[k];
161
+ });
162
+ const outs = angles.map((a, k) => {
163
+ const f = far(a);
164
+ return f !== undefined && dist(f) > dist(rims[k]) + 0.5 ? f : rims[k];
165
+ });
166
+ const paint = floorOf("u-cfloor", [0.3, 0.62]);
167
+ return [...floorRing(`${id}-in`, "floor", [{ inner, outer: rims }], lift + APRON_RISE, paint, SLIVER), ...floorRing(`${id}-out`, "floor", [{ inner: rims, outer: outs }], lift + APRON_RISE, paint, SLIVER)];
168
+ }
169
+ /** The rim of a real venue's hall along an angle from `middle`: the nearer of the hall's rounded outline and the building's own outline. */
170
+ export const rimAt = (hall, footprint, middle) => (angle) => {
171
+ const cap = rayAt(hall, middle[0], middle[1], angle);
172
+ const out = firstHit(footprint, middle, angle);
173
+ return out !== undefined && Math.hypot(out[0] - middle[0], out[1] - middle[1]) < Math.hypot(cap[0] - middle[0], cap[1] - middle[1]) ? out : cap;
174
+ };
package/dist/venue.js CHANGED
@@ -7,7 +7,8 @@ import { patterns } from "./crowd.draw.js";
7
7
  import { crowdStands } from "./draw.js";
8
8
  import { groundById } from "./grounds.js";
9
9
  import { ARC_STEPS, clipAround, clipConvex, clipSegment, areaOf, pointList, raked, slopeOf, stripsOf, tidy } from "./venue.geom.js";
10
- import { realBuilding } from "./venue.real.js";
10
+ import { apronPlanes, backEdges, backWall, concourseFloor, rimAt, suiteWall } from "./venue.fill.js";
11
+ import { firstHit, realBuilding } from "./venue.real.js";
11
12
  import { n, shy, wall } from "./turn.kit.js";
12
13
  export const NO_VENUE = { items: [], defs: "", reach: 0 };
13
14
  /** The sports played under a roof: their stands have a lid and not a canopy. */
@@ -51,6 +52,7 @@ export function standsVenueOf(stands, crowd, o, v, lift) {
51
52
  const covers = stands.covers;
52
53
  const inCover = (p) => covers.some(([x0, y0, x1, y1]) => p[0] >= x0 && p[0] <= x1 && p[1] >= y0 && p[1] <= y1);
53
54
  const pieces = (poly) => (covers.length === 0 ? [poly] : clipAround(poly, covers));
55
+ const filled = v.fill ?? stands.real !== undefined;
54
56
  const seatPlanes = [];
55
57
  const wallSeats = [];
56
58
  let count = 0;
@@ -138,12 +140,31 @@ export function standsVenueOf(stands, crowd, o, v, lift) {
138
140
  continue;
139
141
  for (const piece of pieces(tidy(q))) {
140
142
  count += 1;
141
- const p = raked(`walk-${count}`, "concourse", piece, slope.slope, slope.along, (corners) => `<polygon class="u-conc" points="${pointList(corners)}"/>`);
143
+ // Where the empty places are filled, the walk between two tiers is the suite level: a facing wall with a band of windows, not a bare shelf.
144
+ const p = raked(`walk-${count}`, "concourse", piece, slope.slope, slope.along, (corners) => (filled ? suiteWall(corners) : `<polygon class="u-conc" points="${pointList(corners)}"/>`));
142
145
  if (p !== undefined)
143
146
  seatPlanes.push({ ...p, shy: slope.out });
144
147
  }
145
148
  }
146
149
  }
150
+ // The empty places are filled (see `venue.fill.ts`): the apron round the surface and the back of each bowl, and, for a real venue's building, the concourse floor behind the bowl.
151
+ if (filled) {
152
+ const first = stands.rings.find((r) => r.tier !== "wall" && r.tier !== "bleachers") ?? stands.rings[0];
153
+ if (first !== undefined)
154
+ seatPlanes.push(...apronPlanes(stands, first, cut, lift));
155
+ const real = stands.real;
156
+ const middle = [centre[0], centre[1]];
157
+ const behind = new Set(links.map((l) => l.before));
158
+ for (const ring of stands.rings) {
159
+ if (ring.tier === "wall" || behind.has(ring))
160
+ continue;
161
+ backEdges(ring, cut).forEach((edge, k) => {
162
+ seatPlanes.push(...backWall(`back-${ring.id}${k === 0 ? "" : `-s${k}`}`, edge, ring.height[1], lift, centre));
163
+ if (real !== undefined && ring.region.kind !== "poly" && k === 0)
164
+ seatPlanes.push(...concourseFloor(`floor-${ring.id}`, ring.region, middle, rimAt(real.hall, real.footprint, middle), (a) => firstHit(real.footprint, middle, a), ARC_STEPS[cut].oval, lift));
165
+ });
166
+ }
167
+ }
147
168
  const items = [];
148
169
  if (seatPlanes.length > 0)
149
170
  items.push({ id: "stands", kind: "stands", at: centre, height: topOf(seatPlanes), planes: seatPlanes });
@@ -7,6 +7,8 @@ export declare const SADDLE_RISE = 30;
7
7
  export declare function saddleAt(real: Pick<RealShell, "footprint">, [x, y]: Pt): number;
8
8
  /** The height of the roof over a point of the building's edge, above the surface, in feet. */
9
9
  export declare const roofAt: (real: RealShell, p: Pt) => number;
10
+ /** The point where a ray from `from` first meets a polygon, or `undefined`. */
11
+ export declare function firstHit(poly: readonly Pt[], from: Pt, angle: number): Pt | undefined;
10
12
  /**
11
13
  * The walls and the roof of a real venue (`parts` says which are made). The hall is the building's outline cut to the bowl's wall and a margin (`RealShell.hall`): its rim is where a ray from the middle meets the one or the other first, in `steps`
12
14
  * rays. A wall stands on each stretch of the rim, up to the roof's height over it; the roof is a band from the rim in to the inner edge of the top tier (the middle stays open, as the 3D view's roofs are).
@@ -21,7 +21,7 @@ export function saddleAt(real, [x, y]) {
21
21
  /** The height of the roof over a point of the building's edge, above the surface, in feet. */
22
22
  export const roofAt = (real, p) => real.edge + (real.roof === "saddle" ? SADDLE_RISE * saddleAt(real, p) : 0);
23
23
  /** The point where a ray from `from` first meets a polygon, or `undefined`. */
24
- function firstHit(poly, from, angle) {
24
+ export function firstHit(poly, from, angle) {
25
25
  const [dx, dy] = [Math.cos(angle), Math.sin(angle)];
26
26
  let best;
27
27
  poly.forEach(([ax, ay], i) => {
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The package's version: the same as package.json's, and a test holds them to each other. */
2
- export declare const VERSION = "0.33.0";
2
+ export declare const VERSION = "0.34.1";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The package's version: the same as package.json's, and a test holds them to each other. */
2
- export const VERSION = "0.33.0";
2
+ export const VERSION = "0.34.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnmorrisdotca/kyogijo-stadiums",
3
- "version": "0.33.0",
3
+ "version": "0.34.1",
4
4
  "description": "Kyogijo Stadiums 競技場, 40 sports' surfaces as SVG, drawn to scale from the rulebooks: NFL, CFL and IFAF football fields, an NHL rink, an NBA court, MLB ballparks, soccer pitches, curling sheets, athletics stadiums, speed skating, short track and figure skating rinks, swimming pools, water polo fields, diving wells, artistic swimming areas, and the courts and fields of tennis, volleyball (indoor, beach and sitting), badminton, pickleball, table tennis, padel, squash, racquetball, sepak takraw, teqball, roundnet, netball, lacrosse, rugby, Australian football, Gaelic games, cricket, field hockey and handball, and the budo of judo, karate, kendo, sumo and kyudo with a dojo of tatami laid as real units, the MMA cage, with each team's name and logo on its side and the score in the middle, stands and a crowd, a pageant of flags, and a 3D view. Light and dark, accessible, a compact badge for lists, a helper for React and a custom element. Zero dependencies.",
5
5
  "keywords": [
6
6
  "sports",