@johnmorrisdotca/kyogijo-stadiums 0.24.0 → 0.26.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/NOTICE.md +17 -0
  3. package/README.md +58 -40
  4. package/dist/crowd.d.ts +51 -2
  5. package/dist/crowd.draw.d.ts +3 -1
  6. package/dist/crowd.draw.js +27 -3
  7. package/dist/crowd.js +17 -9
  8. package/dist/draw.js +13 -2
  9. package/dist/flythrough-entry.d.ts +8 -0
  10. package/dist/flythrough-entry.js +6 -0
  11. package/dist/flythrough.dom.d.ts +103 -0
  12. package/dist/flythrough.dom.js +517 -0
  13. package/dist/flythrough.markup.d.ts +48 -0
  14. package/dist/flythrough.markup.js +86 -0
  15. package/dist/flythrough.path.d.ts +145 -0
  16. package/dist/flythrough.path.js +253 -0
  17. package/dist/flythrough.style.d.ts +2 -0
  18. package/dist/flythrough.style.js +40 -0
  19. package/dist/flythrough.words.d.ts +33 -0
  20. package/dist/flythrough.words.js +44 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +1 -0
  23. package/dist/strings.d.ts +6 -0
  24. package/dist/strings.js +2 -2
  25. package/dist/turn.model.js +3 -1
  26. package/dist/turn.style.js +1 -0
  27. package/dist/types.d.ts +8 -0
  28. package/dist/venue.js +7 -4
  29. package/dist/venue.real.d.ts +16 -0
  30. package/dist/venue.real.js +103 -0
  31. package/dist/venues-entry.d.ts +2 -0
  32. package/dist/venues-entry.js +2 -0
  33. package/dist/venues.d.ts +153 -0
  34. package/dist/venues.data.d.ts +2 -0
  35. package/dist/venues.data.js +74 -0
  36. package/dist/venues.geo.d.ts +60 -0
  37. package/dist/venues.geo.js +211 -0
  38. package/dist/venues.js +213 -0
  39. package/dist/venues.osm.data.d.ts +4 -0
  40. package/dist/venues.osm.data.js +363 -0
  41. package/dist/version.d.ts +1 -1
  42. package/dist/version.js +1 -1
  43. package/package.json +13 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,32 @@ All notable changes to this project are written here. The format follows
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.26.0] - 2026-10-20
11
+
12
+ ### Added
13
+
14
+ - **The fly-through** (`mountFlythrough`, [the fly-through](./docs/flythrough.md), entry `@johnmorrisdotca/kyogijo-stadiums/flythrough`): one smooth camera from a map to a real arena. It starts on a map drawn with chizu (handed over as `start: { map, draw, project }`, so the package keeps no dependency; a plain graticule without one), falls to the city and to the building of the arena as OpenStreetMap maps it, and the building rises into the 3D arena, which tips to the sport's main camera and turns. `mountFlythrough(host, { arena, start?, stages?, language?, theme? }, settings?)` gives back `play`, `pause`, `toggle`, `next`, `back`, `goTo(stage)`, `seek(seconds)`, `camera()` and `destroy`. This first release ends on the turning arena; into the bowl, down to the rink and a game on it are the next ones.
15
+ - **One path, no jumps.** The camera is ten numbers (how far in, where the arena stands on the screen, how far the map is turned, how much of the map, the plan and the arena shows, the 3D view's turn, tip and zoom), each running through the path's keyframes as a monotone cubic: smooth at every keyframe, still at the start, never overshooting a layer's share. The map's viewBox is true to the ground round the arena and turned to the venue's own orientation as it comes in; the plan hands over to the 3D arena at the same size and place, measured in the stage as it is. `flightPlan`, `cameraAt` and `stageAt` are the path as plain numbers, pure and testable in Node.
16
+ - **Controls and reduced motion.** Back, Play or Pause (the clock eases to a stop), Next and a button for each stage; Space, the arrows, the number keys and Home; a skip glides along the path and does not cut. A reader who prefers reduced motion gets each stage as a still (the map, the plan, the arena at the main camera), Next, Back and the stage buttons show the next still at once, and Play steps through them every five seconds.
17
+ - **The demo's fly-through page** (`flythrough.html?arena=honda-center`): each of the eight venues, English and Japanese, light and dark, desk and phone, with the OpenStreetMap credit under the stage. No word is stretched (the words are HTML over the layers) and no team's or league's mark is drawn.
18
+ - Tests that walk the path at 60 frames a second for every list of stages (no step, no jump of speed), check that every stage is reachable, that the stills show one layer whole, and, in a real browser, that the pin, the plan and the 3D floor never jump on the screen at 30 frames a second and are the same size where they hand over.
19
+
20
+ ### Fixed
21
+
22
+ - The shared background module in `scripts/` (tooling for the demo and the README pictures; the package ships none of it) is now the family's version 1.0.1: the `id` it is given is made safe (letters, digits, `_` and `-`; `bg-` in front of one that does not start with a letter) before it is written into element ids and `url(#...)`. `test/family-backgrounds.test.ts` draws every preset with hostile ids.
23
+
24
+ ## [0.25.0] - 2026-10-20
25
+
26
+ ### Added
27
+
28
+ - **The real venues** (`crowd: { real: true }`, [the real venues](./docs/venues.md), entry `@johnmorrisdotca/kyogijo-stadiums/venues`): an NHL arena drawn in the outline of its building, as OpenStreetMap maps it, with the rink lying in it and a plausible bowl of the seats, the seating tiers and the roof the sources give. The first step is the eight arenas of the Pacific Division: the Honda Center, the Scotiabank Saddledome, Rogers Place, the Crypto.com Arena, the SAP Center, the Climate Pledge Arena, Rogers Arena and the T-Mobile Arena. An arena with no venue is drawn with the usual stands, as before; so is any other sport, and a drawing with a `league` of its own.
29
+ - **Level one, the building.** The outline is the closed way OpenStreetMap tags with the arena's Wikidata item, read with the Overpass API on 2026-10-20 and simplified to a metre. The drawing is turned so that the rink lies along the page where the building says how it lies, and a small arrow marked N says where north is; the rink's orientation is inferred from the building's main hall (the largest rectangle that fits its outline) for five of the eight, and not known for the three whose building has no direction (drawn east-west with north at the top and no arrow); OpenStreetMap maps the ice in none of them.
30
+ - **Level two, the bowl.** The venue's seats for ice hockey, in its number of tiers, between the first row and the building's wall: each tier's outer edge is placed so that its band holds its share of the seats (a lower bowl of 56 percent, or the share a source gives: two thirds in the Crypto.com Arena), with a walk between the tiers and rows no steeper than 36 degrees, and a bowl that fits holds exactly the venue's seats. A building too small for them gets a steeper bowl (the Rogers Arena's), and `fit` says `tight` where even that is not enough. The 3D view raises the tiers as solid raked planes, and with `venue: { roof: true }` the building's walls and its roof: a lid, or the Saddledome's saddle (high at the two ends of the rink, low at the two sides). A neutral crowd, no visitors' block and no mark of a team or a league; never a seat map.
31
+ - **Facts with their sources.** `VENUES` holds, for each venue, its seats (Wikidata's figure for ice hockey where its item has one, else the English article's), its number of tiers and its kind of roof, each with whether a source says it (`documented`) or it is ours (`plausible`) and what was read, with the article and revision. Wikidata has no tiers and no roof for any of them; the articles say the Saddledome's roof, the Climate Pledge Arena's, and the tiers of three. `planVenue`, `venueBowl`, `venueOrientation`, `bowlInside` and `venueById` are the rest of the entry.
32
+ - **ODbL data kept apart.** What OpenStreetMap gave (outline, height, the axis worked out from it) is ODbL 1.0 data, "(c) OpenStreetMap contributors": it is only in `src/venues.osm.data.ts` and `data-sources/venues/`, offered under the ODbL with the credit `VENUE_ATTRIBUTION`, and the description of a drawing made from it carries the credit. The facts are in `src/venues.data.ts`, CC0 and facts of English Wikipedia articles, and share nothing with it. The raw answers are kept as they were returned, dated (`pnpm venues:fetch` fetches them politely, one request at a time; `pnpm venues` makes the two data files and the table of the docs from them).
33
+ - **The demo's venues page** (`venues.html`): each of the eight in 2D and in 3D, the four viewpoints, the roof and walls, light and dark, English and Japanese, with what the drawing is made from listed under it. **The docs** have [a page](./docs/venues.md) and [a gallery](https://johnmorrisdotca.github.io/kyogijo-stadiums/docs/gallery-venues.html) of the eight.
34
+ - `CrowdOptions.stands: false` draws no seats (of a real venue only its building and the arrow): what the 3D view's floor needs under the stands it raises.
35
+
10
36
  ## [0.24.0] - 2026-10-20
11
37
 
12
38
  ### Added
package/NOTICE.md CHANGED
@@ -43,6 +43,8 @@ picture or code of the sources is copied; each source is named here so that the
43
43
  | `grounds`: the World Cups each ground staged matches of, and where each final was played | The venue section of the English Wikipedia article of each men's and Women's World Cup, at the revision cited below; FIFA's own pages are not used | Facts, CC BY-SA 4.0 articles as above |
44
44
  | `grounds`: the Wikidata item and the coordinates of each ground (`wikidata`, `lat`, `lon`) | The item that each cited article is linked to, its coordinates (P625) and its country (P17), read with the Wikidata API and checked against the item's labels by `scripts/check-grounds.mjs` | Wikidata's data is CC0 (https://creativecommons.org/publicdomain/zero/1.0/) |
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
+ | `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 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
+ | `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) |
46
48
  | `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") |
47
49
  | `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 |
48
50
  | `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/) |
@@ -405,6 +407,21 @@ The Japanese Wikipedia articles read for the names, with their revisions, are li
405
407
  | Verdun Auditorium | Q2870805 | Verdun Auditorium, 1372712513 |
406
408
  | Rhode Island Auditorium | Q7320897 | Rhode Island Auditorium, 1373848518 |
407
409
 
410
+ ## The venues' sources
411
+
412
+ The eight arenas of the NHL's Pacific Division, read on 2026-10-20 (`pnpm venues:fetch`; the files are in `data-sources/venues/2026-10-20/`: `osm-<arena>.json` as Overpass returned them, `wikidata.json` and `wikipedia.json`). The outline of a building is OpenStreetMap's, ODbL 1.0, (c) OpenStreetMap contributors, and lives in `src/venues.osm.data.ts` alone. The seats, the tiers and the roof are in `src/venues.data.ts`, with what each cites. No seat map of a team, a ticket seller or a venue is read or copied, and no mark of a team or a league is drawn.
413
+
414
+ | Arena | Wikidata item | OpenStreetMap way | English article and revision |
415
+ | --- | --- | --- | --- |
416
+ | Honda Center | Q774138 | way/120623151 | Honda Center, 1380000927 |
417
+ | Scotiabank Saddledome | Q1140083 | way/4397328 | Scotiabank Saddledome, 1377341466 |
418
+ | Rogers Place | Q5339036 | way/448626254 | Rogers Place, 1380110483 |
419
+ | Crypto.com Arena | Q186080 | way/428021739 | Crypto.com Arena, 1380458624 |
420
+ | SAP Center | Q261863 | way/24169210 | SAP Center, 1380804230 |
421
+ | Climate Pledge Arena | Q977529 | way/25123756 | Climate Pledge Arena, 1380851727 |
422
+ | Rogers Arena | Q517318 | way/24705911 | Rogers Arena, 1379918849 |
423
+ | T-Mobile Arena | Q3338973 | way/364489406 | T-Mobile Arena, 1379155307 |
424
+
408
425
  ## The World Cups' articles
409
426
 
410
427
  The grounds each World Cup used, and where its final was played, were read from the venue section of the tournament's English Wikipedia article at the revision named (32 tournaments), by the same address. FIFA's own pages are not used.
package/README.md CHANGED
@@ -72,46 +72,45 @@ console.log(svg.startsWith("<svg"), svg.includes("USA 3, Canada 2"));
72
72
 
73
73
  ## Features
74
74
 
75
- - **Forty-three surfaces, to scale.** The NFL, CFL and IFAF fields, the NHL rink, the NBA court, the soccer pitch and futsal court, the baseball field, the curling sheet, twelve net and racket sports, the pools, the athletics stadium, the skating rinks, seven field sports, the budō, the MMA cage, the rings and the wrestling mats. Every figure is cited in [sources](./docs/sources.md). [The surfaces](./docs/surfaces.md).
75
+ - **Forty-three surfaces, to scale.** The NFL, CFL and IFAF fields, the NHL rink, the NBA court, soccer, baseball, curling, twelve net and racket sports, the pools, athletics, skating, seven field sports, the budō, the MMA cage, the rings and the mats. Every figure is cited in [sources](./docs/sources.md). [The surfaces](./docs/surfaces.md).
76
76
  - **Each league's rulebook.** `league: "fiba"` draws a court as FIBA marks it: 48 variants, from the WNBA and the IIHF to Little League, softball, rugby league and box lacrosse, each cited. [League variants](./docs/variants.md).
77
77
  - **A realistic basketball floor.** `court: { hardwood: true }` draws maple boards that run the length of the court with staggered ends, grain and stained keys, the baskets with soft shadows, and benches. The clean floor stays the default. [Hardwood](./docs/hardwood.md).
78
- - **The stands and the crowd.** `crowd: true` draws the seating round any surface that has spectators: a bowl, two sides, a horseshoe, courtside rows or a ballpark's grandstands, with the people as seeded heads and shoulders in the home colours and the empty seats showing (`density`, or `attendance` over `capacity`). [The crowd](./docs/crowd.md).
79
- - **Curling, from the rule books.** A sheet from World Curling's and Curling Canada's rules, tall by default, with the board and the hammer in a score, `curlingStones` for the stones of an end, `show: "folded"` for the whole sheet with both houses enlarged beside it. [Curling](./docs/curling.md).
80
- - **Net, racket and court sports, from their rulebooks.** The ITF's, FIVB's (indoor, beach, sitting), BWF's, USA Pickleball's and ITTF's courts and table, the padel, squash, racquetball, sepak takraw, teqball, roundnet and netball courts, each in its look, with a board of sets and games. [Net, racket and court sports](./docs/courts.md).
78
+ - **The stands and the crowd.** `crowd: true` draws the seating round any surface that has spectators: a bowl, two sides, a horseshoe, courtside rows or a ballpark's grandstands, with the people as seeded heads in the home colours and the empty seats showing. `crowd: { real: true }` puts an NHL arena in its building's outline (OpenStreetMap) with a plausible bowl of its seats, tiers and roof: eight so far, never a seat map. [The crowd](./docs/crowd.md), [the real venues](./docs/venues.md).
79
+ - **Curling, from the rule books.** A sheet from World Curling's and Curling Canada's rules, tall by default, with the board and the hammer in a score, `curlingStones` for an end's stones, `show: "folded"` for both houses enlarged. [Curling](./docs/curling.md).
80
+ - **Net, racket and court sports, from their rulebooks.** The ITF's, FIVB's, BWF's, USA Pickleball's and ITTF's courts and table, and padel, squash, racquetball, sepak takraw, teqball, roundnet and netball, each in its look, with a board of sets and games. [Net, racket and court sports](./docs/courts.md).
81
81
  - **Swimming, water polo, diving and artistic swimming, from World Aquatics' regulations.** The 50 or 25 m pool with its lanes, ropes, blocks and touch panels; the water polo field; the diving well; the artistic swimming area. [Pools](./docs/pools.md).
82
82
  - **Lacrosse, rugby, Australian football, Gaelic games, cricket, field hockey and handball, from their rulebooks.** The fields and floors of World Lacrosse, World Rugby, the NRL, the AFL, the GAA, the FIH and the IHF, and a cricket ground. [Field sports](./docs/field-sports.md).
83
- - **The athletics stadium, from World Athletics' rules.** The 400 m track with its lanes and staggers worked out from the running lines, every race start, hurdles, the steeplechase and every field event, with lane draws, results and a race replay. [The athletics stadium](./docs/athletics.md).
84
- - **The skating rinks, from the ISU's rules.** The 400 m speed skating track with its lanes, blocks and the lines of every race worked out lane by lane; the short track's oval; the figure skating rink; and results, a race replay and the elements of a programme. [The skating rinks](./docs/skating.md).
85
- - **The budō, from the federations' rules.** The IJF's and the All Japan Judo Federation's judo areas, the WKF's karate areas, the kendo shiai-jo, the sumo dohyō, the kyūdōjō, and **a dojo builder**: a floor of tatami laid as real units, in jō or metres, with its walls, doors, windows and shrine. Scores are judo's ippon, waza-ari and shido, karate's and kendo's points and sumo's kimarite. [The budō](./docs/budo.md).
83
+ - **The athletics stadium, from World Athletics' rules.** The 400 m track with its lanes and staggers worked out from the running lines, every race start, hurdles, the steeplechase and the field events, with lane draws, results and a race replay. [The athletics stadium](./docs/athletics.md).
84
+ - **The skating rinks, from the ISU's rules.** The 400 m speed skating track with each race's lines worked out lane by lane; the short track's oval; the figure skating rink; and results, a race replay and a programme's elements. [The skating rinks](./docs/skating.md).
85
+ - **The budō, from the federations' rules.** The IJF's and the All Japan Judo Federation's judo areas, the WKF's karate areas, the kendo shiai-jo, the sumo dohyō, the kyūdōjō, and **a dojo builder**: tatami laid as real units, with its walls, doors, windows and shrine. Scores are judo's ippon, waza-ari and shido, points and sumo's kimarite. [The budō](./docs/budo.md).
86
86
  - **Soccer, to the Laws.** A pitch from Law 1 of the IFAB Laws of the Game of any size, with presets from international to 5-a-side, half pitches, five mowing patterns and four goal styles. [Soccer](./docs/soccer.md).
87
- - **Every ballpark.** The 30 current MLB parks and 44 that MLB clubs have left, and the 12 home parks of Nippon Professional Baseball with eight other NPB grounds, each with its fence drawn from the distances published for it, as it stood in a season. [Ballparks](./docs/ballparks.md).
88
- - **Soccer grounds, arenas and an atlas.** 64 soccer grounds at their published sizes, 167 NHL, NBA, WNBA and PWHL arenas (`drawKyogijo({ sport: "nhl", arena: "boston-garden" })`), and every park, ground, arena and budō hall (330 places) on a map, found by any of its names. [The grounds](./docs/grounds.md), [the arenas](./docs/arenas.md), [the atlas](./docs/atlas.md), [names](./docs/ballparks.md#names).
89
- - **Walls and quirks.** How high the fence stands, stretch by stretch and season by season (`walls`), and a park's hills, ivy, poles, bullpens and dugouts (`quirks`), each from the park's article, none guessed: [ballparks](./docs/ballparks.md).
90
- - **Special games.** A surface set inside another, to scale: an NHL rink on a ballpark's infield or a football stadium's field or a baseball field laid over a London stadium's pitch. 18 games, each cited. [Special games](./docs/special-games.md).
87
+ - **Every ballpark.** The 30 current MLB parks and 44 that MLB clubs have left, and the 12 home parks of Nippon Professional Baseball with eight other NPB grounds, each with its fence drawn from the distances published for it, as it stood in a season. With the fence's heights and a park's quirks (`walls`, `quirks`), none guessed. [Ballparks](./docs/ballparks.md).
88
+ - **Soccer grounds, arenas and an atlas.** 64 soccer grounds at their published sizes, 167 NHL, NBA, WNBA and PWHL arenas (`drawKyogijo({ sport: "nhl", arena: "boston-garden" })`), and 330 places on a map, found by any of their names. [The grounds](./docs/grounds.md), [the arenas](./docs/arenas.md), [the atlas](./docs/atlas.md), [names](./docs/ballparks.md#names).
89
+ - **Special games.** A surface set inside another, to scale: an NHL rink on a ballpark's infield, a baseball field over a London stadium's pitch. 18 games, each cited. [Special games](./docs/special-games.md).
91
90
  - **Surfaces through the years.** `year: 1975` draws a surface as its league's rules marked it that season, each change cited. [Surfaces through the years](./docs/eras.md).
92
- - **Score mode.** Each team's name and logo on its side (the end zones, the ends of the ice, the halves of the court, the dugouts) and the score large in the middle, with the competition, the status and the date, and the winner emphasised. [Score and layout](./docs/score.md#round-marks).
91
+ - **Score mode.** Each team's name and logo on its side (end zones, ends of the ice, halves of the court, dugouts) and the score large in the middle, with the competition, status and date, and the winner emphasised. [Score and layout](./docs/score.md#round-marks).
93
92
  - **The situation of a game.** `situation` draws what is happening: hockey's power play and shootout, baseball's runners and count, football's line of scrimmage, first-down line, red zone and down and distance, with a drive chart. [The situation](./docs/situation.md).
94
- - **Lineups.** A `lineup` layer places a team's players by position code (`GK`, `CB`, `HK`, `C`) with no coordinates: it reads where each stands from a formation (36 of them, in 13 sports), draws a disc or a jersey with the shirt number and the name under it, a captain's C and a flag. [Lineups](./docs/team-shape.md#lineups).
95
- - **A pageant.** Each team's flag or crest laid large over its half of the surface: `pageant: "carpet"` (a flag at its own ratio with a border) or `"stage"` (a crest on a round disc with a gold ring), an emblem or a trophy in the centre circle, the score in the middle or in the strip. [The pageant](./docs/pageant.md).
96
- - **Trophies, medals, balls and crests, by id.** `useEmblems(renderEmblem)` plugs in the sister package [Dougu Sports](https://johnmorrisdotca.github.io/dougu-sports/) in one line, and an id goes wherever a logo, a flag or a mark is taken: `logo: "crest:shield"`, `pageant: { emblem: "trophy:hockey-cup" }`, `"ball:puck"`. [Emblems](./docs/emblems.md).
93
+ - **Lineups.** A `lineup` layer places a team's players by position code (`GK`, `CB`, `HK`, `C`) with no coordinates, from a formation (36, in 13 sports), as a disc or a jersey with the number, the name, a captain's C and a flag. [Lineups](./docs/team-shape.md#lineups).
94
+ - **A pageant.** Each team's flag or crest laid large over its half of the surface: `pageant: "carpet"` (a flag at its own ratio) or `"stage"` (a crest on a round disc), an emblem or a trophy in the centre circle, the score in the middle or in the strip. [The pageant](./docs/pageant.md).
95
+ - **Trophies, medals, balls and crests, by id.** `useEmblems(renderEmblem)` plugs in the sister package [Dougu Sports](https://johnmorrisdotca.github.io/dougu-sports/), and an id goes wherever a logo, a flag or a mark is taken: `logo: "crest:shield"`, `"ball:puck"`. [Emblems](./docs/emblems.md).
97
96
  - **Country flags, by code.** `useFlags(render)` plugs in the sister package [Hata](https://github.com/johnmorrisdotca/hata) through a small adapter, and a team's `flag: "CA"` is its flag: round in a round mark, at its own proportions on a banner, a badge or a carpet. [Flags](./docs/flags.md).
98
- - **Zoom to a part of a surface.** `range: "offensive-zone"` crops a drawing to a rink's zone, a half court, the paint, the red zone, a penalty area, the infield, a curling house, or a box of your own, with its data and score following. [Display ranges](./docs/ranges.md).
99
- - **Wide, tall or fit the box.** Every surface lies wide or tall: tall turns it a quarter, keeps every word upright and puts the teams top and bottom. With `orientation: "auto"` the browser helpers follow the box.
100
- - **Where a point lies, by the rules, and a click as a place.** `zoneAt("nhl", [69, 22], { frame: "nhl-api" })` names the zone from the surface's own figures, and `clientPointToField(svg, x, y, { provider })` is a pointer's place in any of the frames, with the zone. [Zones](./docs/zones.md), [pointing](./docs/pointer.md).
97
+ - **Zoom to a part of a surface.** `range: "offensive-zone"` crops a drawing to a rink's zone, a half court, the paint, the red zone, a penalty area, the infield, a house, or a box of your own. [Display ranges](./docs/ranges.md).
98
+ - **Wide, tall or fit the box.** Every surface lies wide or tall: tall turns it a quarter, keeps every word upright and puts the teams top and bottom; `orientation: "auto"` follows the box.
99
+ - **Where a point lies, by the rules, and a click as a place.** `zoneAt("nhl", [69, 22], { frame: "nhl-api" })` names the zone from the surface's own figures; `clientPointToField` is a pointer's place in any frame. [Zones](./docs/zones.md), [pointing](./docs/pointer.md).
101
100
  - **Goals, the strike zone and the stumps, end-on.** A goal mouth for six sports with the shots laid on it, the catcher's view of the strike zone with its pitches numbered, and the wicket from the bowler's end with its zones. [Targets](./docs/targets.md).
102
101
  - **Data on the surface.** Points, lines, arrows, text, heat maps, hexagons and density contours over any surface, in its own feet or a provider's frame; ready-made shot and pass charts. [Data layers](./docs/layers.md), [charts](./docs/charts.md).
103
- - **Small multiples and backgrounds.** `drawGrid` puts many drawings in one SVG with a title, a shared key and a credit line, in a size for a card or a phone; `background` lays a quiet pattern or a photograph behind a drawing or a grid. [Layouts and backgrounds](./docs/layouts.md).
104
- - **Tournament layouts.** Several surfaces laid out together for one large event, to scale, on one floor, as one SVG: thirteen presets, each with its rule and sources, and a stadium round it. [Tournament layouts](./docs/multi.md).
102
+ - **Small multiples and backgrounds.** `drawGrid` puts many drawings in one SVG with a title, a shared key and a credit line, for a card or a phone; `background` lays a pattern or a photograph behind them. [Layouts and backgrounds](./docs/layouts.md).
103
+ - **Tournament layouts.** Several surfaces laid out for one event, to scale, as one SVG: thirteen presets, each with its rule and sources, and a stadium round it. [Tournament layouts](./docs/multi.md).
105
104
  - **Judging assignments.** Who judges where. [Judging](./docs/judging.md). **Boards over a surface.** [Boards](./docs/boards.md).
106
- - **Replay.** Tracking data played on any surface: the players, the ball and its trail, and the passes, shots and goals as they happen, with controls, the keyboard and a live region. [Replay](./docs/replay.md).
107
- - **A 3D view.** Any surface as a floor that turns by mouse, touch, wheel or keys, with four viewpoints (overhead, broadcast, end-on, low corner), and what the rulebook stands on it at its height (nets, baskets, goals, a cage's mesh, a ring's ropes) and the venue round it (raked stands, roofs, a dojo's walls). [The 3D view](./docs/turn.md).
108
- - **Renderers.** The same drawing as plain data (a scene), on a canvas (a browser's or a Node canvas), in a d3 selection, as a mark of an Observable Plot, as GeoJSON and as a PNG in Node, each an entry of its own. [Renderers](./docs/renderers.md).
109
- - **Units and frames.** Each surface in its rulebook's unit and any other, and 48 coordinate frames (ten soccer providers, the NHL API, NBA stats, NFL tracking, MLBAM and more) to move a provider's points onto a surface. [Units](./docs/units.md), [coordinate frames](./docs/frames.md).
105
+ - **Replay.** Tracking data played on any surface: the players, the ball and its trail, and the passes, shots and goals as they happen, with controls and the keyboard. [Replay](./docs/replay.md).
106
+ - **A 3D view.** Any surface as a floor that turns by mouse, touch, wheel or keys, with four viewpoints (overhead, broadcast, end-on, low corner), and what stands on it at its height (nets, baskets, goals, a cage's mesh) and the venue round it (raked stands, roofs, walls). [The 3D view](./docs/turn.md).
107
+ - **Renderers.** The same drawing as plain data (a scene), on a canvas, in a d3 selection, as an Observable Plot mark, as GeoJSON and as a PNG in Node, each an entry of its own. [Renderers](./docs/renderers.md).
108
+ - **Units and frames.** Each surface in its rulebook's unit and any other, and 48 coordinate frames (ten soccer providers, the NHL API, NBA stats, NFL tracking, MLBAM and more) for a provider's points. [Units](./docs/units.md), [coordinate frames](./docs/frames.md).
110
109
  - **A compact badge and a compact card.** One strip for a list, or the surface with that strip under it.
111
- - **Light and dark**, by the device or the page; every colour a custom property; eight named looks, colours by feature, line widths and dashes, and arrows, axes and labels for teaching. [Theming](./docs/theming.md), [annotations](./docs/annotations.md).
110
+ - **Light and dark**, by the device or the page; every colour a custom property; eight named looks, colours by feature, line widths and dashes, arrows, axes and labels. [Theming](./docs/theming.md), [annotations](./docs/annotations.md).
112
111
  - **Accessible.** Each drawing is an `img` with a title and a description of the surface and the result, in English or Japanese. [Accessibility](./docs/accessibility.md).
113
112
  - **Four ways in.** A function that returns SVG text; a helper that draws into a container; a custom element; a frame for pages with no script; a React helper that imports no React.
114
- - **No dependencies**, no network requests, nothing stored. Text, colours and logo addresses that come from outside are made safe before they are drawn.
113
+ - **No dependencies**, no network requests, nothing stored. Text, colours and logo addresses from outside are made safe.
115
114
  - **A docs site.** Guides, a search, a gallery that draws every picture from the code beside it, and [how it compares](./docs/compare.md): [the documentation](./docs/README.md).
116
115
 
117
116
  ### What's in it
@@ -123,17 +122,17 @@ console.log(svg.startsWith("<svg"), svg.includes("USA 3, Canada 2"));
123
122
  | `SOCCER_PITCHES`, `SOCCER_PROVIDERS`, `rescale`, `soccerSpec` | The soccer pitches, the data providers' coordinate systems and the map between them. |
124
123
  | `GROUNDS`, `groundById` | The 64 soccer grounds, each pitch at its own size. |
125
124
  | `EVENTS`, `eventOptions` | The 18 special games: a rink in a ballpark, a court on a carrier's deck. |
126
- | `./zones`, `./pointer`, `./target` | Where a point lies by a sport's own rules, and the yes-or-no questions; a click on a drawing as a place in a provider's numbers; a goal, the strike zone or the stumps seen end-on. |
127
- | `./geometry`, `./frames`, `./layers`, `./charts`, `./replay` | The dimensions in any unit; the providers' frames; data layers; ready-made charts; tracking data played on a surface. |
128
- | `./athletics` | The athletics stadium: the plan, the track's geometry, the races and field events, and the lane draw, results, race replay and field marks charts. |
129
- | `./budo` | The budō: the judo and karate areas, the kendo shiai-jo, the sumo dohyō, the kyūdōjō, the dojo builder (`dojo`, `drawDojo`), the 82 kimarite and the five halls. |
130
- | `./skating` | The skating rinks: the plan, the long track's geometry, the races, and the pairs, starting positions, results, race replay and programme elements charts. |
131
- | `./parks`, `./grounds`, `./arenas`, `./atlas`, `./eras`, `./leagues`, `./events` | The ballparks, the grounds, the arenas, every place, the markings of a season, each league's rulebook, the special games. |
125
+ | `./zones`, `./pointer`, `./target` | Where a point lies by a sport's own rules; a click as a place in a provider's numbers; a goal, the strike zone or the stumps end-on. |
126
+ | `./geometry`, `./frames`, `./layers`, `./charts`, `./replay` | The dimensions in any unit; the providers' frames; data layers; charts; tracking data played on a surface. |
127
+ | `./athletics` | The athletics stadium: the plan, the races and field events, and the lane draw, results, replay and field marks charts. |
128
+ | `./budo` | The budō: the areas, the dohyō, the kyūdōjō, the dojo builder (`dojo`, `drawDojo`), the 82 kimarite and the five halls. |
129
+ | `./skating` | The skating rinks: the plan, the races, and the pairs, starting positions, results, replay and programme charts. |
130
+ | `./parks`, `./grounds`, `./arenas`, `./venues`, `./atlas`, `./eras`, `./leagues`, `./events` | The ballparks, grounds, arenas and real venues, every place, a season's markings, each league's rulebook, the special games. |
132
131
  | `./turn` | A surface as a 3D view that turns, with the net, basket, goal or posts standing on it. |
133
- | `./scene`, `./canvas`, `./d3`, `./plot`, `./geojson`, `./png` | The drawing as data; on a canvas; in a d3 selection; as an Observable Plot mark; as GeoJSON; as a PNG (the optional peer `@resvg/resvg-js`). |
134
- | `./multi` | `drawMulti`, `multiFit`: several surfaces laid out for one event, in a hall ([docs](./docs/multi.md), [demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/halls.html)). |
135
- | `./judging` | `drawJudging`, `judgingBoard`, `mountJudgingBroadcast`, `judgingPrint`: who judges where, as a plan, on a broadcast board or on paper ([docs](./docs/judging.md)). |
136
- | `./layouts` | `drawGrid` and `GRID_PRESETS`: small multiples with a title, a subtitle, a key and a credit line; the grounds behind a drawing or a grid. |
132
+ | `./scene`, `./canvas`, `./d3`, `./plot`, `./geojson`, `./png` | The drawing as data, on a canvas, in d3, as an Observable Plot mark, as GeoJSON, as a PNG (the optional peer `@resvg/resvg-js`). |
133
+ | `./multi` | `drawMulti`, `multiFit`: several surfaces laid out for one event ([docs](./docs/multi.md), [demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/halls.html)). |
134
+ | `./judging` | `drawJudging`, `judgingBoard`, `mountJudgingBroadcast`, `judgingPrint`: who judges where, on a plan, a board or paper ([docs](./docs/judging.md)). |
135
+ | `./layouts` | `drawGrid` and `GRID_PRESETS`: small multiples with a title, a key and a credit line; backgrounds. |
137
136
  | `./mount`, `./react`, `./element`, `./element/define` | Draw into a container; React props; the `<kyogijo-field>` element and its registration. |
138
137
 
139
138
  The full list of entry points is in [entry points](./docs/entry-points.md); every export is in the [API reference](https://johnmorrisdotca.github.io/kyogijo-stadiums/api.html).
@@ -496,7 +495,7 @@ console.log(svg.includes('viewBox="0 0 1200 675"'), svg.includes("Three ballpark
496
495
 
497
496
  ### Several surfaces on one floor
498
497
 
499
- `drawMulti` lays the package's own surfaces out together for one large event, to scale, on one floor, with the officials' chairs and boards, and places for the event's words and marks (`sample: true` fills them with an invented event; [tournament layouts](./docs/multi.md)). Thirteen presets; `multiFit` says whether a layout fits a hall, `stadium: true` sets it inside real stands, and `mountMultiTurn` turns it in 3D.
498
+ `drawMulti` lays the package's own surfaces out for one large event, to scale, on one floor, with the officials' chairs and boards and places for the event's words (`sample: true` fills them with an invented event; [tournament layouts](./docs/multi.md)). `multiFit` says whether a layout fits a hall, `stadium: true` sets it inside stands, and `mountMultiTurn` turns it in 3D.
500
499
 
501
500
  ```ts
502
501
  import { drawMulti, multiLayout } from "@johnmorrisdotca/kyogijo-stadiums/multi";
@@ -541,8 +540,6 @@ console.log(layout.needs.length, layout.needs.width);
541
540
  <br><em>Without `sample`: the empty places an organiser fills.</em>
542
541
  </p>
543
542
 
544
- The [demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/halls.html) turns it in 3D.
545
-
546
543
  ### Boards over a surface
547
544
 
548
545
  `board` lays a results board, a bout presentation or a medal table, any SVG, over a surface in 2D and 3D, off its middle and never stretched ([boards](./docs/boards.md)).
@@ -571,6 +568,26 @@ The [demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/halls.html) turns
571
568
  <br><em>A medal table in a corner.</em>
572
569
  </p>
573
570
 
571
+ ### The real venues
572
+
573
+ `crowd: { real: true }` draws an NHL arena in its building ([the real venues](./docs/venues.md)).
574
+
575
+ <p align="center">
576
+ <picture>
577
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/venues-honda-desk-dark.webp">
578
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/venues-honda-desk-light.webp" alt="The Honda Center's rink in its building's outline: two tiers of seats and a north arrow." width="640">
579
+ </picture>
580
+ <br><em>The Honda Center.</em>
581
+ </p>
582
+
583
+ <p align="center">
584
+ <picture>
585
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/venues-saddledome-3d-desk-dark.webp">
586
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/venues-saddledome-3d-desk-light.webp" alt="The Saddledome in 3D: two tiers of seats under its roof." width="640">
587
+ </picture>
588
+ <br><em>The Saddledome, in 3D.</em>
589
+ </p>
590
+
574
591
  ### Who judges where
575
592
 
576
593
  `drawJudging` puts each judo mat's officials by seat in the picture and in a chart ([judging](./docs/judging.md)).
@@ -780,7 +797,8 @@ English and Japanese: the title, the description and the standard statuses of a
780
797
 
781
798
  Not here yet, each welcome as an [issue](https://github.com/johnmorrisdotca/kyogijo-stadiums/issues):
782
799
 
783
- - **More surfaces**: the FIBA and college courts, the international rink, rugby pitches, a stadium's seating, soccer's beach and street formats.
800
+ - **More surfaces**: the FIBA and college courts, the international rink, rugby pitches, soccer's beach and street formats.
801
+ - **More real venues**: the other divisions' arenas, then the NBA's, NFL's and MLB's, each in its building's outline.
784
802
  - **More parks of the past**: the 19th-century grounds, and the parks whose articles give no distances yet (the Kingdome, Joe Robbie Stadium).
785
803
  - **More charts on data layers**: a formation chart, a pass sonar chart and a Voronoi chart of their own (the layers exist; see [team shape](./docs/team-shape.md)).
786
804
  - **Football in a different standard**: the college and high-school hash marks.
package/dist/crowd.d.ts CHANGED
@@ -26,9 +26,9 @@ export type Outline = {
26
26
  };
27
27
  /** A tier of the stands: a band of seats between two outlines. */
28
28
  export interface StandRing {
29
- /** `lower`, `upper`, `courtside`, `grandstand` or `bleachers`, with the tier's number where a ring has parts. */
29
+ /** `lower`, `middle`, `upper`, `courtside`, `grandstand` or `bleachers`, with the tier's number where a ring has parts. */
30
30
  id: string;
31
- tier: "courtside" | "gallery" | "lower" | "upper" | "grandstand" | "bleachers" | "wall";
31
+ tier: "courtside" | "gallery" | "lower" | "middle" | "upper" | "grandstand" | "bleachers" | "wall";
32
32
  /** The band is `region` less `hole` (nothing for a polygon that is the band itself). */
33
33
  region: Outline;
34
34
  /** Further pieces of the band that are apart from `region` (a ballpark's bleachers either side of the batter's eye). */
@@ -94,6 +94,45 @@ export interface CrowdStands {
94
94
  seats: number;
95
95
  /** The stands are a hall's gallery (a dojo's balcony): a thin band close to the walls, plain seats, a few neutral people and no sides. */
96
96
  gallery?: boolean;
97
+ /** The stands are a real venue's (see `src/venues.ts`): the outline of its building, the way north lies on the page and the roof. */
98
+ real?: RealShell;
99
+ }
100
+ /** The room above a real venue's building that the arrow saying where north is takes, in feet, and how far above the building's box the middle of the arrow stands. */
101
+ export declare const NORTH_ROOM = 60;
102
+ export declare const NORTH_AT = 32;
103
+ /** What a real venue adds to the stands round a surface: the building they stand in, in feet in the wide frame, and the roof over them. */
104
+ export interface RealShell {
105
+ /** The outline of the building, a polygon (OpenStreetMap's, turned so that the rink lies along x). */
106
+ footprint: Pt[];
107
+ /** The hall the stands are in: the bowl's outer wall, a rounded rectangle inside the building, and what the 3D view raises walls and a roof on (the building's annexes, which the outline has, are left as ground). */
108
+ hall: Extract<Outline, {
109
+ kind: "rrect";
110
+ }>;
111
+ /** The way north points on the page, in degrees clockwise from the top; absent where the rink's real orientation is not known. */
112
+ north?: number;
113
+ /** `fixed` is a lid; `saddle` a roof that rises at the two ends and falls at the two sides. */
114
+ roof: "fixed" | "saddle";
115
+ /** The height of the roof's lowest edge above the surface, in feet. */
116
+ edge: number;
117
+ }
118
+ /** The stands of a real venue, worked out from its building (`venueBowl`): what `layoutOf` takes in place of a sport's usual tiers. */
119
+ export interface RealBowl {
120
+ rings: StandRing[];
121
+ /** How far the first row stands from the surface's edge at each side, in feet (the bench side has more). */
122
+ off: {
123
+ left: number;
124
+ right: number;
125
+ top: number;
126
+ bottom: number;
127
+ };
128
+ /** The ground the venue needs beyond the surface on each side, in feet: the building's, and room for the arrow that says where north is. */
129
+ reach: {
130
+ left: number;
131
+ right: number;
132
+ top: number;
133
+ bottom: number;
134
+ };
135
+ shell: RealShell;
97
136
  }
98
137
  /** The crowd options read: every part has its value. */
99
138
  export interface Crowd {
@@ -112,6 +151,10 @@ export interface Crowd {
112
151
  gaps: boolean;
113
152
  roof: boolean;
114
153
  wallSeats: boolean | undefined;
154
+ /** The stands of the arena's real venue, where the package has one for the `arena` asked for. */
155
+ real: boolean;
156
+ /** Whether the seats are drawn (a real venue's building is, without them, when this is false). */
157
+ stands: boolean;
115
158
  detail: "auto" | "fine" | "plain";
116
159
  }
117
160
  /** The crowd the options ask for, or `undefined` for none. */
@@ -130,6 +173,8 @@ export declare function rayAt(o: Outline, cx: number, cy: number, a: number): Pt
130
173
  export declare function outlinePath(o: Outline, reverse?: boolean): string;
131
174
  /** An outline as points, for a view that raises the stands: arcs are cut into steps of about 7.5 degrees. */
132
175
  export declare function outlinePoints(o: Outline): Pt[];
176
+ /** The area of an outline, in square feet. */
177
+ export declare const areaOfOutline: (o: Outline) => number;
133
178
  /** The seats of a band of the stands: its area at a seat's 2 by 2.3 feet. */
134
179
  export declare const seatsIn: (region: Outline, hole: Outline | null) => number;
135
180
  /** A point of a rounded rectangle and the outward normal there, by its parameter `u` (any real number, taken modulo 8): the four straight sides are the even whole numbers (the top from left to right, the right from top to bottom and so on round) and the four corners the odd ones. Concentric outlines (the same corners' centres) have the same parameter at the points an aisle joins. */
@@ -139,6 +184,8 @@ export declare function rrectAt(o: Extract<Outline, {
139
184
  p: Pt;
140
185
  n: Pt;
141
186
  };
187
+ /** The aisles of a band as lines from its inner edge to its outer. */
188
+ export declare function aislesOf(region: Outline, hole: Outline | null): [Pt, Pt][];
142
189
  /** What the layout needs of the options and the surface. */
143
190
  export interface LayoutInput {
144
191
  sport: Sport;
@@ -159,6 +206,8 @@ export interface LayoutInput {
159
206
  rows?: number;
160
207
  aisles?: readonly number[];
161
208
  };
209
+ /** A real venue's stands in place of the sport's usual tiers (see `venueBowl`). */
210
+ real?: RealBowl;
162
211
  /** The ballpark's fence as points on the field, for a ballpark (see `parkLayout`). */
163
212
  fence?: readonly Pt[];
164
213
  /** The foul lines' length at the poles and the backstop's distance, for a ballpark. */
@@ -1,4 +1,4 @@
1
- import { type Crowd, type CrowdStands } from "./crowd.ts";
1
+ import { type Crowd, type CrowdStands, type RealShell } from "./crowd.ts";
2
2
  /** The tiles a crowd needs: the home tile, the home tile of the upper tiers (emptier) and the visitors', and the display tiles. */
3
3
  export declare function patterns(id: string, crowd: Crowd, density: number, tifo: {
4
4
  home: boolean;
@@ -12,6 +12,8 @@ export interface DrawInput {
12
12
  fine: boolean;
13
13
  ground: string;
14
14
  }
15
+ /** The building of a real venue and, where its rink's orientation is known, a small arrow with an N that says where north lies. The N is a path of three strokes, so that no font is needed. */
16
+ export declare function realMarkup(real: RealShell): string;
15
17
  /** The stands and the crowd as markup and the defs it uses. The markup is laid over the ground and under the surface. */
16
18
  export declare function drawCrowd(stands: CrowdStands, o: DrawInput): {
17
19
  defs: string;
@@ -3,7 +3,7 @@
3
3
  // All of it is patterns, paths and rectangles, which every renderer of the package reads. The tiles are made from a seed with a generator of our own, so the same options give
4
4
  // the same crowd. Written for this package; no photograph or other drawing was copied.
5
5
  import { el, num } from "./format.js";
6
- import { generator, outlinePath } from "./crowd.js";
6
+ import { NORTH_AT, generator, outlinePath } from "./crowd.js";
7
7
  /** The width and the height of a tile of the crowd, in feet, and the seats' pitch. */
8
8
  const TILE = 14.4;
9
9
  const PITCH = 2.4;
@@ -132,11 +132,32 @@ function galleryRows(ring) {
132
132
  (gaps === "" ? "" : el("path", { class: "ky-bud-gaisle", d: gaps, fill: "none", "stroke-width": 3.4 })));
133
133
  }
134
134
  const poly = (points) => `M${points.map(([x, y]) => `${Math.round(x)} ${Math.round(y)}`).join("L")}Z`;
135
+ /** The building of a real venue and, where its rink's orientation is known, a small arrow with an N that says where north lies. The N is a path of three strokes, so that no font is needed. */
136
+ export function realMarkup(real) {
137
+ const fp = real.footprint;
138
+ let svg = el("path", { class: "ky-cr-shell", d: `M${fp.map(([x, y]) => `${n1(x)} ${n1(y)}`).join("L")}Z` });
139
+ if (real.north !== undefined) {
140
+ const top = Math.min(...fp.map((p) => p[1]));
141
+ const right = Math.max(...fp.map((p) => p[0]));
142
+ const [cx, cy] = [right - 34, top - NORTH_AT];
143
+ const a = (real.north * Math.PI) / 180;
144
+ const [dx, dy] = [Math.sin(a), -Math.cos(a)];
145
+ const at = (along, across = 0) => `${n1(cx + dx * along - dy * across)} ${n1(cy + dy * along + dx * across)}`;
146
+ svg += el("path", { class: "ky-cr-north", d: `M${at(-13)}L${at(14)}M${at(14)}L${at(6, 4.5)}L${at(6, -4.5)}Z` });
147
+ const [gx, gy] = [cx + dx * 24, cy + dy * 24];
148
+ svg += el("path", { class: "ky-cr-north-n", d: `M${n1(gx - 3.5)} ${n1(gy + 4.5)}V${n1(gy - 4.5)}L${n1(gx + 3.5)} ${n1(gy + 4.5)}V${n1(gy - 4.5)}` });
149
+ }
150
+ return el("g", { class: "ky-cr-real" }, svg);
151
+ }
135
152
  /** The stands and the crowd as markup and the defs it uses. The markup is laid over the ground and under the surface. */
136
153
  export function drawCrowd(stands, o) {
137
154
  const { id, crowd, density, fine } = o;
138
155
  const tifo = { home: stands.sectors.some((s) => s.kind === "tifo-home"), away: stands.sectors.some((s) => s.kind === "tifo-away") };
139
- let body = "";
156
+ // A real venue's building lies under its stands; with `stands: false` it is all there is.
157
+ const building = stands.real === undefined ? "" : realMarkup(stands.real);
158
+ if (!crowd.stands && stands.real !== undefined)
159
+ return { defs: "", markup: el("g", { class: "ky-crowd" }, building), over: "" };
160
+ let body = building;
140
161
  // A ballpark's stands stand on a plaza of their own, not on the grass of the foul ground.
141
162
  if (stands.sport === "mlb")
142
163
  for (const ring of stands.rings)
@@ -195,7 +216,7 @@ export function drawCrowd(stands, o) {
195
216
  const [x0, y0, x1, y1] = g.box;
196
217
  body += el("rect", { class: `ky-cr-${g.kind}`, x: n1(x0), y: n1(y0), width: n1(x1 - x0), height: n1(y1 - y0), rx: g.kind === "bench" ? 1 : undefined });
197
218
  }
198
- return { defs: (fine && (stands.gallery !== true || density > 0) ? patterns(id, crowd, density, tifo.home || tifo.away ? tifo : undefined, stands.gallery === true) : ""), markup: el("g", { class: "ky-crowd" }, body), over };
219
+ return { defs: (fine && (stands.gallery !== true || density > 0) ? patterns(id, crowd, density, tifo.home || tifo.away ? tifo : undefined, stands.gallery === true || stands.real !== undefined) : ""), markup: el("g", { class: "ky-crowd" }, body), over };
199
220
  }
200
221
  /** A block of visitors or a card display, a polygon over the band it is in. */
201
222
  function sector(s, id, fine, density) {
@@ -228,6 +249,9 @@ export const KYOGIJO_CROWD_STYLE = `
228
249
  .kyogijo .ky-cr-bench{fill:#1c2127;fill-opacity:.55}
229
250
  .kyogijo .ky-cr-media{fill:#1c2127;fill-opacity:.4}
230
251
  .kyogijo .ky-cr-camera{fill:#1c2127;fill-opacity:.75}
252
+ .kyogijo .ky-cr-shell{fill:var(--ky-cr-conc);fill-opacity:.55;stroke:var(--ky-cr-rim);stroke-width:1.6;stroke-linejoin:round}
253
+ .kyogijo .ky-cr-north{fill:var(--ky-cr-rim);stroke:var(--ky-cr-rim);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
254
+ .kyogijo .ky-cr-north-n{fill:none;stroke:var(--ky-cr-rim);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
231
255
  .kyogijo .ky-cr-plaza{fill:var(--ky-apron);stroke:var(--ky-apron);stroke-width:8;stroke-linejoin:round}
232
256
  .kyogijo .ky-cr-ez{fill:var(--ky-line);fill-opacity:.16}
233
257
  .kyogijo .ky-cr-yard{stroke:var(--ky-line);stroke-opacity:.5;fill:none}`.trim();
package/dist/crowd.js CHANGED
@@ -3,6 +3,9 @@
3
3
  // profile of the distances a venue of its kind keeps between the surface and its first row and how deep its tiers are; they are conventions for a drawing, not a rulebook's
4
4
  // figures (no rulebook gives them), except the footprint of the gridiron a soccer pitch can stand inside, which is the field's own.
5
5
  import { FOOTBALL } from "./geometry.js";
6
+ /** The room above a real venue's building that the arrow saying where north is takes, in feet, and how far above the building's box the middle of the arrow stands. */
7
+ export const NORTH_ROOM = 60;
8
+ export const NORTH_AT = 32;
6
9
  const finite = (v) => typeof v === "number" && Number.isFinite(v);
7
10
  const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
8
11
  /** The crowd the options ask for, or `undefined` for none. */
@@ -28,6 +31,8 @@ export function resolveCrowd(value) {
28
31
  gaps: o.gaps !== false,
29
32
  roof: o.roof !== false,
30
33
  wallSeats: typeof o.wallSeats === "boolean" ? o.wallSeats : undefined,
34
+ real: o.real === true,
35
+ stands: o.stands !== false,
31
36
  detail: o.detail === "fine" || o.detail === "plain" ? o.detail : "auto",
32
37
  };
33
38
  }
@@ -216,7 +221,8 @@ export function outlinePoints(o) {
216
221
  }
217
222
  return out;
218
223
  }
219
- const area = (o) => {
224
+ /** The area of an outline, in square feet. */
225
+ export const areaOfOutline = (o) => {
220
226
  if (o.kind === "ellipse")
221
227
  return Math.PI * o.rx * o.ry;
222
228
  if (o.kind === "rrect") {
@@ -231,7 +237,7 @@ const area = (o) => {
231
237
  return Math.abs(s) / 2;
232
238
  };
233
239
  /** The seats of a band of the stands: its area at a seat's 2 by 2.3 feet. */
234
- export const seatsIn = (region, hole) => Math.round((area(region) - (hole === null ? 0 : area(hole))) / 4.6);
240
+ export const seatsIn = (region, hole) => Math.round((areaOfOutline(region) - (hole === null ? 0 : areaOfOutline(hole))) / 4.6);
235
241
  /** A point of a rounded rectangle and the outward normal there, by its parameter `u` (any real number, taken modulo 8): the four straight sides are the even whole numbers (the top from left to right, the right from top to bottom and so on round) and the four corners the odd ones. Concentric outlines (the same corners' centres) have the same parameter at the points an aisle joins. */
236
242
  export function rrectAt(o, u) {
237
243
  const m = ((u % 8) + 8) % 8;
@@ -281,7 +287,7 @@ function aisleParameters(hole) {
281
287
  return { us, nh, nv };
282
288
  }
283
289
  /** The aisles of a band as lines from its inner edge to its outer. */
284
- function aislesOf(region, hole) {
290
+ export function aislesOf(region, hole) {
285
291
  if (region.kind === "rrect" && hole !== null && hole.kind === "rrect")
286
292
  return aisleParameters(hole).us.map((u) => [rrectAt(hole, u).p, rrectAt(region, u).p]);
287
293
  if (region.kind === "ellipse" && hole !== null && hole.kind === "ellipse") {
@@ -327,13 +333,13 @@ export function layoutOf(input) {
327
333
  const host = sport === "soccer" ? (crowd.stadium === "nfl" || crowd.stadium === "cfl" ? crowd.stadium : crowd.stadium === "auto" && input.ground !== undefined ? GRIDIRON_GROUNDS[input.ground] : undefined) : undefined;
328
334
  const stadium = sport === "soccer" ? host ?? "purpose" : undefined;
329
335
  const p = stadium === "nfl" || stadium === "cfl" ? { ...PROFILES[stadium], shape: base.shape, away: base.away } : base;
330
- const shape = crowd.shape ?? p.shape;
331
- const off = offsets(base, box, stadium ?? "purpose");
336
+ const shape = input.real !== undefined ? "bowl" : crowd.shape ?? p.shape;
337
+ const off = input.real?.off ?? offsets(base, box, stadium ?? "purpose");
332
338
  const cx = box.x + box.w / 2;
333
339
  const cy = box.y + box.h / 2;
334
- const rings = [];
335
- const reachOf = { left: 0, right: 0, top: 0, bottom: 0 };
336
- for (const t of p.tiers) {
340
+ const rings = input.real === undefined ? [] : input.real.rings.map((ring) => ({ ...ring }));
341
+ const reachOf = input.real === undefined ? { left: 0, right: 0, top: 0, bottom: 0 } : { ...input.real.reach };
342
+ for (const t of input.real === undefined ? p.tiers : []) {
337
343
  const at = (k) => {
338
344
  if (base.oval === true)
339
345
  return { kind: "ellipse", cx, cy, rx: box.w / 2 + (off.left + off.right) / 2 + k, ry: box.h / 2 + (off.top + off.bottom) / 2 + k };
@@ -384,6 +390,8 @@ export function layoutOf(input) {
384
390
  concourses.push({ region: ring.hole, hole: before.region });
385
391
  });
386
392
  const stands = { sport, shape, box, rings, covers, concourses, sectors: [], gaps: [], reach: reachOf, seats: rings.reduce((n, r) => n + r.seats, 0) };
393
+ if (input.real !== undefined)
394
+ stands.real = input.real.shell;
387
395
  if (stadium !== undefined)
388
396
  stands.stadium = stadium;
389
397
  if (gal !== undefined)
@@ -404,7 +412,7 @@ export function layoutOf(input) {
404
412
  const side = crowd.awayEnd === "left" || crowd.awayEnd === "right" ? crowd.awayEnd : input.awaySide;
405
413
  const sign = side === "right" ? 1 : -1;
406
414
  const mode = crowd.awayEnd === "auto" ? (p.away === "end" ? "end" : "corner") : crowd.awayEnd === "corner" ? "corner" : "end";
407
- const tiers = rings.filter((r) => r.tier === "lower" || r.tier === "upper");
415
+ const tiers = rings.filter((r) => r.tier === "lower" || r.tier === "middle" || r.tier === "upper");
408
416
  const hole = lowerRing.hole;
409
417
  if (lowerRing.region.kind === "rrect" && hole !== null && hole.kind === "rrect") {
410
418
  // Round a rectangle a block is whole sections: it starts and ends on aisles, so that it is a section of the stands and not a wedge.
package/dist/draw.js CHANGED
@@ -10,6 +10,7 @@ import { ERA_SPORTS, footballIn, rulesFor, seasonLabel } from "./eras.js";
10
10
  import { FOOTBALL } from "./geometry.js";
11
11
  import { renderLayers } from "./layers.js";
12
12
  import { arenaById, arenaCourtIn, arenaNameIn, arenaOfficialName, arenaRinkIn } from "./arenas.js";
13
+ import { venueById, venueBowl } from "./venues.js";
13
14
  import { groundById, groundNameIn, groundOfficialName } from "./grounds.js";
14
15
  import { BOARD, courtFor, hasSetsBoard, isNetLeague, isNetSport } from "./court.js";
15
16
  import { cricketBoardMarkup } from "./cricket.board.js";
@@ -428,10 +429,16 @@ function standsFor(o, surface, crowd) {
428
429
  const walls = park === undefined || crowd.wallSeats === false ? [] : parkWallsIn(park, o.year).filter((w) => (crowd.wallSeats === true ? w.height_ft >= 25 : w.label !== undefined && WALL_SEATS[park.id] === w.label));
429
430
  return parkLayout({ fence: fenceOutline(points), backstop, crowd, seatsOnWalls: walls.map((w) => ({ from: w.from, to: w.to, height: w.height_ft })) });
430
431
  }
432
+ // An arena's real venue: the building it stands in and a bowl fitted to it, where the package has one for the arena and no league of the caller's own sets the surface.
433
+ const box = surface.whole ?? surface.box ?? { x: 0, y: 0, w: 100, h: 100 };
434
+ const venue = crowd.real && sport === "nhl" && chooseLeague(o).league === undefined ? venueById(o.arena) : undefined;
435
+ // (No visitors' block: no source says where a real arena puts them.)
436
+ if (venue !== undefined)
437
+ return crowdLayout({ sport, box, crowd: crowd.awayEnd === "auto" ? { ...crowd, awayEnd: "none" } : crowd, awaySide, real: venueBowl(venue, box) });
431
438
  // A dojo's crowd sits in its gallery, from the room's balcony data (in feet here); the arenas (the Budokan, the Kokugikan) keep their bowls.
432
439
  const hall = sport === "dojo" ? dojoGallery(o) : undefined;
433
440
  const gallery = hall === undefined ? undefined : { offset: hall.offset / 0.3048, depth: hall.depth / 0.3048, height: [hall.height[0] / 0.3048, hall.height[1] / 0.3048], rows: hall.rows, aisles: hall.aisles };
434
- return crowdLayout({ sport, box: surface.whole ?? surface.box ?? { x: 0, y: 0, w: 100, h: 100 }, crowd, awaySide, ground: o.ground, arena: o.budo?.hall === "budokan", ...(gallery === undefined ? {} : { gallery }) });
441
+ return crowdLayout({ sport, box, crowd, awaySide, ground: o.ground, arena: o.budo?.hall === "budokan", ...(gallery === undefined ? {} : { gallery }) });
435
442
  }
436
443
  /** True when the drawing is asked to be small: `detail: "simple"` or a `width` under 480 pixels. */
437
444
  const smallDrawing = (o) => {
@@ -716,7 +723,11 @@ export function describe(o) {
716
723
  const special = specialText(o, language, surfaceName);
717
724
  // What the arena says about the size: that it is the one its sources publish, or that its rink is said to have been shorter and no size is given.
718
725
  const arenaLine = arenaName === undefined ? "" : `${arenaSize !== undefined ? `${gap}${fill(words.said.arenaSize, { arena: arenaName })}` : sport === "nhl" && arena?.rinkUnknown === true ? `${gap}${fill(words.said.arenaUnknown, { arena: arenaName })}` : ""}${officially(arena === undefined ? undefined : arenaOfficialName(arena, language, year))}`;
719
- const parkLine = (parkName === undefined ? `${groundLine}${halfLine}${arenaLine}` : `${gap}${fill(words.said.park, { park: parkName })}${officially(parkOfficial)}${ballpark === undefined ? "" : ballparkSentences(ballpark).map((line) => `${gap}${line}`).join("")}`) + (special === undefined ? "" : `${gap}${special.text}`);
726
+ // The stands of a real venue say what they are: a plausible bowl, and how the rink lies in the building.
727
+ const realCrowd = o.crowd === undefined || o.crowd === false ? undefined : resolveCrowd(o.crowd);
728
+ const venue = sport === "nhl" && league === undefined && realCrowd?.real === true ? venueById(o.arena) : undefined;
729
+ const realLine = venue === undefined || arenaName === undefined ? "" : `${gap}${fill(words.said.real, { arena: arenaName, seats: String(venue.seats.hockey), tiers: String(venue.tiers.count) })}${gap}${words.said[venue.osm.axis.basis === "mapped" ? "realMapped" : venue.osm.axis.basis === "building" ? "realBuilding" : "realNone"]}`;
730
+ const parkLine = (parkName === undefined ? `${groundLine}${halfLine}${arenaLine}${realLine}` : `${gap}${fill(words.said.park, { park: parkName })}${officially(parkOfficial)}${ballpark === undefined ? "" : ballparkSentences(ballpark).map((line) => `${gap}${line}`).join("")}`) + (special === undefined ? "" : `${gap}${special.text}`);
720
731
  // A drawing of a past season says which: the markings are those of that season.
721
732
  const dated = typeof o.year === "number" && Number.isFinite(o.year);
722
733
  const eraLine = dated && league !== undefined
@@ -0,0 +1,8 @@
1
+ export { mountFlythrough } from "./flythrough.dom.ts";
2
+ export type { Flythrough, FlythroughOptions, FlythroughSettings, FlythroughStart } from "./flythrough.dom.ts";
3
+ export { CHANNELS, PACE, RAMP, STAGE_IDS, cameraAt, flightPlan, foldTime, glide, glideSeconds, mainView, mapBox, mapFrame, monotoneSlopes, nextRate, stageAt, startOf, stagesOf } from "./flythrough.path.ts";
4
+ export type { Camera, Channel, Flight, FlightInput, Keyframe, MapFrame, StageId } from "./flythrough.path.ts";
5
+ export { flythroughMarkup, graticuleMarkup, mapMarkup } from "./flythrough.markup.ts";
6
+ export { FLYTHROUGH_STYLE } from "./flythrough.style.ts";
7
+ export { FLYTHROUGH_WORDS } from "./flythrough.words.ts";
8
+ export type { FlythroughWords } from "./flythrough.words.ts";
@@ -0,0 +1,6 @@
1
+ // The fly-through: one smooth camera from a map to the real arena, over the map (chizu's, handed over), the building's site plan and the 3D arena.
2
+ export { mountFlythrough } from "./flythrough.dom.js";
3
+ export { CHANNELS, PACE, RAMP, STAGE_IDS, cameraAt, flightPlan, foldTime, glide, glideSeconds, mainView, mapBox, mapFrame, monotoneSlopes, nextRate, stageAt, startOf, stagesOf } from "./flythrough.path.js";
4
+ export { flythroughMarkup, graticuleMarkup, mapMarkup } from "./flythrough.markup.js";
5
+ export { FLYTHROUGH_STYLE } from "./flythrough.style.js";
6
+ export { FLYTHROUGH_WORDS } from "./flythrough.words.js";