@johnmorrisdotca/kyogijo-stadiums 0.12.0 → 0.12.2
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 +21 -0
- package/README.md +41 -7
- package/dist/turn.camera.d.ts +6 -1
- package/dist/turn.camera.js +16 -3
- package/dist/turn.kit.d.ts +5 -0
- package/dist/turn.markup.d.ts +5 -0
- package/dist/turn.markup.js +23 -6
- package/dist/turn.model.d.ts +1 -1
- package/dist/turn.model.js +1 -1
- package/dist/turn.style.js +3 -0
- package/dist/uprights.budo.d.ts +2 -0
- package/dist/uprights.budo.js +14 -7
- package/dist/uprights.d.ts +2 -0
- package/dist/uprights.js +3 -1
- package/dist/uprights.net.js +12 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ All notable changes to this project are written here. The format follows
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.12.2] - 2026-10-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **The demo leads to the tournament layouts.** A "Tournament layouts" link in the demo's header; under the surface of a sport that has layouts (table tennis, pickleball, curling, judo, karate, badminton, tennis) a "See it as a tournament floor" link to `halls.html` with that sport's first layout chosen (and the page's language and theme); and a link back to the demo from `halls.html`. `halls.html` also takes `theme=light|dark` in its address.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **A dohyō in the 3D view is a solid block, not a hollow tray, and the floor round it is not cut off.** The whole floor was lifted to the mound's height and always drawn under the mound's four walls, so the walls on the far side showed over the clay and the clay looked sunk in a tray, with the grey hall floor raised level with it. Now the floor lies on the hall's floor, the mound is four sides (a tone darker than the top) and a flat top (the drawing's own clay and straw bales, cropped to the mound's square and lifted to its height), and the sides on the far side are drawn before the top and the ones on the near side after it at every turn. The view is fitted to the whole floor, so no part of it is cut off at any angle, and the stands of a crowd stand on the hall's floor.
|
|
19
|
+
- **A table tennis table's far legs no longer show through its top, and its legs stand on something.** The raised floor (the table's top) was always drawn under every plane, so the legs and skirt on the far side were drawn over it. A raised floor now takes its turn among the planes by its middle, like any other, and a soft shadow under the table on the hall's floor seats the legs.
|
|
20
|
+
- **The 3D view no longer swells and shrinks as it turns.** A surface going round, by hand or by the auto-turn, grew and shrank rhythmically, and in steps, instead of turning smoothly. There were two causes, both in the camera. The matrix written to each plane (`matrix3d`) rounded every number to four decimals, but its perspective terms are a few ten-thousandths and are multiplied by the pixels of a whole floor, so the rounding bent the perspective by tens of percent and by jumps as the angle changed (a floor's area on the screen jumped by up to a quarter between two views a few degrees apart); the numbers are now written to ten decimals. And the lens was short (1.7 times the box's longer side), so the nearer end of the floor swelled as it swung toward the eye (about a tenth of the area over a turn); the default is now 8 (`lens` still takes any value, and a smaller one brings the swelling back). The scale and the centre were already one for the whole turn (0.10.0); now the floor's area on the screen varies by about one percent over a turn, smoothly, for every sport and a tournament layout used as a floor, light and dark, desk and phone. New tests (`src/turn.size.test.ts`, and one in `e2e/turn.demo.mjs` in a real browser) sample the floor's area over a full turn, as the browser draws the written matrix, and fail on the old camera.
|
|
21
|
+
|
|
22
|
+
## [0.12.1] - 2026-10-19
|
|
23
|
+
|
|
24
|
+
The npm page shows the tournament layouts. No change to the package's code.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **README: a showcase for the tournament layouts of 0.12.0.** A new example section, "Several surfaces on one floor", with a short tested example and two pictures in a light and a dark twin (three judo areas, four table tennis areas), and a Features entry; the same pictures are on [the layouts page](./docs/multi.md). Made by `pnpm screenshots:readme` (`ONLY=<subject>` takes just one subject). Several long Features entries of the README (the budō, the 3D view, the surfaces, zones, pools, athletics) are shortened to stay under the README size budget; the detail is on the page each links to.
|
|
29
|
+
- **`CONTRIBUTING.md` has a release-checklist line for visual features:** new visual feature? update the README images and add a showcase section if the examples do not show it well.
|
|
30
|
+
|
|
10
31
|
## [0.12.0] - 2026-10-19
|
|
11
32
|
|
|
12
33
|
### Added
|
package/README.md
CHANGED
|
@@ -72,17 +72,17 @@ console.log(svg.startsWith("<svg"), svg.includes("USA 3, Canada 2"));
|
|
|
72
72
|
|
|
73
73
|
## Features
|
|
74
74
|
|
|
75
|
-
- **Thirty-nine surfaces, to scale.** The NFL
|
|
75
|
+
- **Thirty-nine 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, the courts of tennis, volleyball (beach and sitting), badminton, pickleball, table tennis, padel, squash, racquetball, sepak takraw, teqball, roundnet and netball, the pools, the athletics stadium, the skating rinks, the fields of lacrosse, rugby, Australian football, Gaelic games, cricket, field hockey and handball, and the budō, with the lines, circles, keys, boxes and goals of each. 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, the NCAA and the IIHF to Little League, PONY, softball, Curling Canada, FIVB's beach volleyball, rugby league and the NLL's 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 tone, stained keys, circle and border, a strip behind each baseline in the team's colour, the baskets and stanchions with soft shadows from above, a coaching box and benches, and a satin sheen, in every league and every renderer, with the detail dropping at small sizes. The clean floor stays the default. [Hardwood](./docs/hardwood.md).
|
|
78
78
|
- **The stands and the crowd.** `crowd: true` draws the seating round any surface that has spectators: a bowl, two sides, a horseshoe, an arena's courtside rows, or a ballpark's grandstands and bleachers laid behind the park's own fence, with the people as a seeded pattern of heads and shoulders in the home colours, the visitors at an end, the empty seats showing (`density`, or `attendance` over `capacity`), a card display on request, and a soccer pitch inside a gridiron stadium's footprint, to scale. A few kB of patterns, blocks at small sizes, every renderer, and the stands' rings, heights and rake as data. [The crowd](./docs/crowd.md).
|
|
79
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, scored by the rules, `show: "folded"` for the whole sheet with both houses enlarged beside it in a square, and a heat map and statistics (free guards, hammer, steals, shots) for many ends. [Curling](./docs/curling.md).
|
|
80
80
|
- **Net, racket and court sports, from their rulebooks.** The ITF's court, the FIVB's indoor and beach courts, World ParaVolley's sitting volleyball court, the BWF's, USA Pickleball's, the ITTF's table, the FIP's padel court, World Squash's courts (with the front wall laid flat beside the floor), the IRF's racquetball court, the ISTAF's sepak takraw court, the FITEQ's teqball court and table, the Roundnet Federation's court and World Netball's court, each in its look (hard court, clay, grass, wood, sand), with a board of sets and games and a ball beside the server. [Net, racket and court sports](./docs/courts.md).
|
|
81
|
-
- **Swimming, water polo, diving and artistic swimming, from World Aquatics' regulations.** The competition pool of 50 or 25 m
|
|
81
|
+
- **Swimming, water polo, diving and artistic swimming, from World Aquatics' regulations.** The competition pool of 50 or 25 m with its lanes, ropes, blocks, touch panels and flags; the water polo field; the diving well with its springboards and platforms; the artistic swimming area; several pools in a natatorium. The lanes of a race carry names, flags, places, times and splits, with a race replay by lane (`raceTimeline`) and a `splitsChart`. [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, the NCAA, the PLL and the NLL; World Rugby's union and the NRL's and IRL's league; the AFL's oval at the size of a named ground; the GAA's field for football and hurling; a cricket ground with a boundary of your own and the 30-yard circle; the FIH's pitch and the IHF's court. A score is goals and behinds (10.12 (72)), goals and points (2-14 (20)) or a board of runs, wickets and overs. [Field sports](./docs/field-sports.md).
|
|
83
|
-
- **The athletics stadium, from World Athletics' rules.** The 400 m track with its lanes
|
|
84
|
-
- **The skating rinks, from the ISU's rules.** The standard 400 m speed skating track with its
|
|
85
|
-
- **The budō, from the federations' rules.** The IJF's
|
|
83
|
+
- **The athletics stadium, from World Athletics' rules.** The 400 m track with its lanes and staggers worked out from the running lines, curved and relay starts, hurdles, the steeplechase, every field event, a close-up of each start, the covered 200 m track, and lane draws, results, a race replay and the marks of throws and jumps. [The athletics stadium](./docs/athletics.md).
|
|
84
|
+
- **The skating rinks, from the ISU's rules.** The standard 400 m speed skating track with its lanes, blocks and coaches' area and the starting and finishing lines of every race worked out lane by lane; the short track's oval of 111.12 m with its blocks and starting positions; the figure skating rink of 60 by 30 m with its judges' seats; and pairs, results, a race replay and the elements of a programme on the ice. [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 (no four corners meet), in jō or metres, with the walls, doors, windows, shrine and kamiza. Five famous halls are on the atlas, each with only what a source gives, and the scores are judo's ippon, waza-ari and shido, karate's points, kendo's 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 (international, UEFA, MLS, NCAA, the FA's 9-, 7- and 5-a-side, FIFA's futsal), half pitches, five mowing patterns, four goal styles, penalties and extra time in a score. [Soccer](./docs/soccer.md).
|
|
87
87
|
- **Every ballpark.** The 30 current MLB parks and 44 that MLB clubs have left (the Polo Grounds, Ebbets Field, the Astrodome and more), 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; a past park whose fence moved is drawn as it stood in a season. [Ballparks](./docs/ballparks.md).
|
|
88
88
|
- **Soccer grounds, arenas and an atlas.** 64 soccer grounds, each pitch at its published size, and 167 NHL, NBA, WNBA and PWHL arenas (`drawKyogijo({ sport: "nhl", arena: "boston-garden" })`), a rink or a court at the arena's own size where an article gives one, and every park, ground, arena and budō hall (330 places) on a map, by sport, league, country and year, each found by any name it has in English, Japanese or its own language (`Koshien Stadium` is also `甲子園`, `San Siro` is `サン・シーロ`). [The grounds](./docs/grounds.md), [the arenas](./docs/arenas.md), [the atlas](./docs/atlas.md), [names](./docs/ballparks.md#names).
|
|
@@ -97,12 +97,13 @@ console.log(svg.startsWith("<svg"), svg.includes("USA 3, Canada 2"));
|
|
|
97
97
|
- **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 pageant's carpet. The same adapter serves [Taisen Brackets](https://github.com/johnmorrisdotca/taisen-brackets). Kyogijo bundles no flag and keeps zero dependencies; without the plug-in a code is ignored. [Flags](./docs/flags.md).
|
|
98
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 soccer half, third or penalty area, the infield, a curling house, or a box of your own in any frame, with its data, heat, labels and score following. [Display ranges](./docs/ranges.md).
|
|
99
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 and the area in it from the rink's, court's, pitch's, park's or field's own figures
|
|
100
|
+
- **Where a point lies, by the rules, and a click as a place.** `zoneAt("nhl", [69, 22], { frame: "nhl-api" })` names the zone and the area in it from the rink's, court's, pitch's, park's or field's own figures, with `isThreePointer`, `inSlot`, `inPenaltyArea`, `isFair`, `overWall`, `isHomeRun` and the like; `clientPointToField(svg, x, y, { provider })` is a pointer's place in any of the frames, with the zone, and `fieldPointToClient` the way back. `marks: true` writes `data-mark-id`, `data-index` and `data-zone` on every mark of a layer, for one listener to link it to a table. [Zones](./docs/zones.md), [pointing](./docs/pointer.md).
|
|
101
101
|
- **Goals, the strike zone and the stumps, end-on.** A goal mouth for hockey (6 by 4 feet), soccer, handball, water polo, lacrosse (6 by 6) and field hockey with the shots laid on it and its zones (the top shelf, the five-hole, the corners), the catcher's view of the strike zone with its nine and thirteen zones and the pitches numbered, and the wicket from the bowler's end with its line and length zones. [Targets](./docs/targets.md).
|
|
102
102
|
- **Data on the surface.** Points, lines, arrows, text, heat maps, hexagons, zone charts and density contours over any surface, in its feet or a data provider's frame, cut to its outline, with a table for each layer; ready-made shot and pass charts (the road to a tournament's final moved to [`@johnmorrisdotca/taisen-brackets`](https://www.npmjs.com/package/@johnmorrisdotca/taisen-brackets), its `/road` entry). [Data layers](./docs/layers.md), [charts](./docs/charts.md).
|
|
103
103
|
- **Small multiples and backgrounds.** `drawGrid` puts many drawings in one SVG, under a title and a subtitle, over a credit line, with a key they share, in a size for a social card, a square post or a phone; `background` lays a quiet pattern (felt, grass, wood, ice, paper, a grid, dots) 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 from table tennis, pickleball, curling, judo, karate, badminton and tennis, each with its rule and sources, whether it fits a hall, and a real stadium round it. [Tournament layouts](./docs/multi.md).
|
|
104
105
|
- **Replay.** Tracking data played on any surface: the players with their numbers, the ball and its trail, and the passes, shots and goals as they happen, with controls, the keyboard and a live region, steps for a reader who prefers reduced motion, and a pause when the page is hidden. [Replay](./docs/replay.md).
|
|
105
|
-
- **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)
|
|
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 the rulebook stands on it at its height: nets, antennae, baskets, rink boards and glass, goals, posts, stumps, a ballpark's fence, the walls of padel and squash, a pool's blocks, a diving well's platforms, a jump's bar, hurdles; and the venue round it (the raked stands with the people on them, roofs on request, a dojo's walls and gallery). Plain DOM and CSS over the same SVG. [The 3D view](./docs/turn.md).
|
|
106
107
|
- **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 that imports nothing from outside the package. [Renderers](./docs/renderers.md).
|
|
107
108
|
- **Units and frames.** Each surface in its rulebook's unit and any other, and 48 coordinate frames (ten soccer providers, the NHL API, HockeyTech, NBA stats, NFL tracking, MLBAM, Retrosheet, a curling stone's place from the button, a place from the net in each net sport (from the centre of the court in squash, racquetball, roundnet and netball), the centre of each field sport's surface, the middle of the water of each pool, the middle of the athletics stadium and of each skating rink) to move a provider's points onto a surface. [Units](./docs/units.md), [coordinate frames](./docs/frames.md).
|
|
108
109
|
- **A compact badge and a compact card.** One strip for a list, or the surface with that strip under it.
|
|
@@ -491,6 +492,39 @@ console.log(svg.includes('viewBox="0 0 1200 675"'), svg.includes("Three ballpark
|
|
|
491
492
|
// true true
|
|
492
493
|
```
|
|
493
494
|
|
|
495
|
+
### Several surfaces on one floor
|
|
496
|
+
|
|
497
|
+
For a large event that runs many contests at once: `drawMulti` lays the package's own surfaces out together, to scale, on one floor, with the officials' chairs, the boards along the edges and empty places for the event's own words and marks ([tournament layouts](./docs/multi.md)). Thirteen presets, each with its rule and sources, from table tennis to judo and karate; `multiFit` says whether a layout fits a hall, and `stadium: true` sets it inside real stands.
|
|
498
|
+
|
|
499
|
+
```ts
|
|
500
|
+
import { drawMulti, multiLayout } from "@johnmorrisdotca/kyogijo-stadiums/multi";
|
|
501
|
+
|
|
502
|
+
const svg = drawMulti({ preset: "judo-1x3" });
|
|
503
|
+
const layout = multiLayout({ preset: "judo-1x3" });
|
|
504
|
+
console.log(svg.startsWith("<svg"), layout.cells.length);
|
|
505
|
+
// true 3
|
|
506
|
+
console.log(layout.needs.length, layout.needs.width);
|
|
507
|
+
// 47 19
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
<p align="center">
|
|
511
|
+
<picture>
|
|
512
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/multi-judo-desk-dark.webp">
|
|
513
|
+
<img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/multi-judo-desk-light.webp" alt="Three judo contest areas in a row on one red floor of mats, each numbered, with dashed empty places for the event's name and logos and a board along each edge." width="640">
|
|
514
|
+
</picture>
|
|
515
|
+
<br><em>Three judo contest areas on one floor of mats: 47 by 19 metres.</em>
|
|
516
|
+
</p>
|
|
517
|
+
|
|
518
|
+
<p align="center">
|
|
519
|
+
<picture>
|
|
520
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/multi-tabletennis-desk-dark.webp">
|
|
521
|
+
<img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/multi-tabletennis-desk-light.webp" alt="Four table tennis areas in two rows of two on one blue floor, numbered Table 1 to Table 4, with a scoreboard hung over the middle and boards along the edges." width="640">
|
|
522
|
+
</picture>
|
|
523
|
+
<br><em>Four table tennis areas under one scoreboard.</em>
|
|
524
|
+
</p>
|
|
525
|
+
|
|
526
|
+
Each surface is the one `drawKyogijo` draws, so nothing is drawn a second way; the [demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/halls.html) picks a preset, sets it in a hall and switches the stadium on and off.
|
|
527
|
+
|
|
494
528
|
### The element in a page
|
|
495
529
|
|
|
496
530
|
```html
|
package/dist/turn.camera.d.ts
CHANGED
|
@@ -114,5 +114,10 @@ export declare function cameraMatrix(view: TurnView, at: Placement, distance: nu
|
|
|
114
114
|
export declare const matrix3d: (m: Mat4) => string;
|
|
115
115
|
/** How far a point is from the eye's plane after the scene is turned and tipped, in pixels towards the eye: bigger is nearer. Planes are drawn far first. */
|
|
116
116
|
export declare function depthOf(point: readonly [number, number, number], view: TurnView, at: Placement): number;
|
|
117
|
-
/**
|
|
117
|
+
/** The lens of the eye, as a multiple of the container's longer side. */
|
|
118
|
+
export declare const LENS = 8;
|
|
119
|
+
/**
|
|
120
|
+
* How far the eye is from the screen, in pixels, for a container: a long lens, so that the floor keeps its shape and the heights lean a little. The lens is long enough that the
|
|
121
|
+
* nearer end of a floor is hardly larger than the farther one: with a short lens (it was 1.7) the floor swelled by up to a quarter as its near end swung toward the eye in a turn.
|
|
122
|
+
*/
|
|
118
123
|
export declare const eyeDistance: (w: number, h: number, lens?: number) => number;
|
package/dist/turn.camera.js
CHANGED
|
@@ -199,7 +199,15 @@ export function cameraMatrix(view, at, distance) {
|
|
|
199
199
|
m = multiply(m, rotateZ);
|
|
200
200
|
return multiply(m, scale);
|
|
201
201
|
}
|
|
202
|
-
|
|
202
|
+
/**
|
|
203
|
+
* A number of a matrix as CSS writes it: to ten decimals, with no exponent. The perspective terms of a camera matrix are tiny (a few ten-thousandths) and are multiplied by
|
|
204
|
+
* the pixels of a whole floor, so rounding them to four decimals, as every other term could be, bent the perspective by tens of percent and by jumps as the turn went on: the
|
|
205
|
+
* surface swelled and shrank in steps.
|
|
206
|
+
*/
|
|
207
|
+
const num = (value) => {
|
|
208
|
+
const text = value.toFixed(10).replace(/\.?0+$/, "");
|
|
209
|
+
return text === "-0" || text === "" ? "0" : text;
|
|
210
|
+
};
|
|
203
211
|
/** A matrix as the CSS `transform` that applies it. */
|
|
204
212
|
export const matrix3d = (m) => `matrix3d(${m.map(num).join(",")})`;
|
|
205
213
|
/** How far a point is from the eye's plane after the scene is turned and tipped, in pixels towards the eye: bigger is nearer. Planes are drawn far first. */
|
|
@@ -210,5 +218,10 @@ export function depthOf(point, view, at) {
|
|
|
210
218
|
const y2 = x * Math.sin(yaw) + y * Math.cos(yaw);
|
|
211
219
|
return y2 * Math.sin(view.pitch * RAD) + z * Math.cos(view.pitch * RAD);
|
|
212
220
|
}
|
|
213
|
-
/**
|
|
214
|
-
export const
|
|
221
|
+
/** The lens of the eye, as a multiple of the container's longer side. */
|
|
222
|
+
export const LENS = 8;
|
|
223
|
+
/**
|
|
224
|
+
* How far the eye is from the screen, in pixels, for a container: a long lens, so that the floor keeps its shape and the heights lean a little. The lens is long enough that the
|
|
225
|
+
* nearer end of a floor is hardly larger than the farther one: with a short lens (it was 1.7) the floor swelled by up to a quarter as its near end swung toward the eye in a turn.
|
|
226
|
+
*/
|
|
227
|
+
export const eyeDistance = (w, h, lens = LENS) => lens * Math.max(w, h);
|
package/dist/turn.kit.d.ts
CHANGED
|
@@ -21,6 +21,11 @@ export interface Plane {
|
|
|
21
21
|
* wall. The nearer the eye is to that side, and the lower it is, the more the wall is cut away (see `fadeOf`), so that the court is never hidden behind its own glass.
|
|
22
22
|
*/
|
|
23
23
|
shy?: readonly [number, number];
|
|
24
|
+
/**
|
|
25
|
+
* A plane that is the floor's own drawing seen through its rectangle, not a drawing of its own: the top of a raised platform (a dohyō's clay with its bales). The floor lies on the ground, so
|
|
26
|
+
* that the ground round the platform stays low, and this plane lifts the part of it that is the platform's top to the platform's height. Its `u` and `v` must be flat and along x and y.
|
|
27
|
+
*/
|
|
28
|
+
crop?: boolean;
|
|
24
29
|
/** How much of a `shy` plane is left when it is cut away altogether: 0.08 when missing. A plane that others lie over on the screen (the steps of a gallery) takes less, so that they do not add up to a veil. */
|
|
25
30
|
fadeTo?: number;
|
|
26
31
|
}
|
package/dist/turn.markup.d.ts
CHANGED
|
@@ -21,6 +21,11 @@ export interface Layout {
|
|
|
21
21
|
export declare function layoutOf(model: TurnModel, view: TurnView, width: number, height: number, lens?: number): Layout;
|
|
22
22
|
/** One plane as an element: its box in pixels and the SVG drawn on it in feet. */
|
|
23
23
|
export declare function planeMarkup(p: Plane, scale: number, transform: string, stack: number, fade?: number): string;
|
|
24
|
+
/**
|
|
25
|
+
* A plane that is the floor's own drawing seen through its rectangle (`Plane.crop`): the floor's SVG again, its viewBox cut down to the plane's rectangle, in the plane's own box. It is a picture, not a second
|
|
26
|
+
* description: it carries no role, title or description of its own.
|
|
27
|
+
*/
|
|
28
|
+
export declare function cropMarkup(model: TurnModel, p: Plane, transform: string, stack: number, fade?: number): string;
|
|
24
29
|
/** The floor as an element: the flat drawing, in the pixels of the scene. */
|
|
25
30
|
export declare function floorMarkup(model: TurnModel, transform: string, stack: number): string;
|
|
26
31
|
/** The patterns the venue's planes are drawn with, once, in a box of no size: every plane's SVG reads them by id, and the crowd's colours are on this element. */
|
package/dist/turn.markup.js
CHANGED
|
@@ -29,15 +29,16 @@ export function layoutOf(model, view, width, height, lens) {
|
|
|
29
29
|
// A billboard faces the eye at this view.
|
|
30
30
|
const planes = model.planes.map((p) => facing(p, view.yaw));
|
|
31
31
|
const transforms = [floor, ...planes].map((p) => matrix3d(multiply(camera, planeMatrix(p, s, centre))));
|
|
32
|
-
// Far to near: a plane's depth is that of its middle. The ground of a hall is under everything
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
// Far to near: a plane's depth is that of its middle. The ground of a hall is under everything. The floor is next, unless it stands raised on something (a table tennis table's top, on its legs): then it is
|
|
33
|
+
// sorted with the rest, by its middle, so that the legs and the skirt on the far side are drawn under it and the ones on the near side over it, and nothing under the top shows through it.
|
|
34
|
+
const depth = (p) => depthOf([(p.o[0] + (p.u[0] + p.v[0]) / 2 - centre[0]) * s, (p.o[1] + (p.u[1] + p.v[1]) / 2 - centre[1]) * s, (p.o[2] + (p.u[2] + p.v[2]) / 2) * s], view, at);
|
|
35
|
+
const raised = model.uprights.lift > 0;
|
|
36
|
+
const order = [...planes.map((p, i) => ({ i: i + 1, ground: p.kind === "ground", depth: depth(p) })), ...(raised ? [{ i: 0, ground: false, depth: depth(floor) }] : [])].filter((one) => !one.ground);
|
|
36
37
|
order.sort((a, b) => a.depth - b.depth);
|
|
37
38
|
const stack = new Array(model.planes.length + 1).fill(0);
|
|
38
39
|
stack[0] = 1;
|
|
39
40
|
order.forEach((one, rank) => {
|
|
40
|
-
stack[one.i
|
|
41
|
+
stack[one.i] = rank + 2;
|
|
41
42
|
});
|
|
42
43
|
const fade = [1, ...model.planes.map((p) => fadeOf(p, view.yaw, view.pitch))];
|
|
43
44
|
return { at, transforms, stack, fade };
|
|
@@ -47,6 +48,22 @@ export function planeMarkup(p, scale, transform, stack, fade = 1) {
|
|
|
47
48
|
const [w, h] = size(p);
|
|
48
49
|
return `<div class="ky-up ky-up-${p.kind}" data-part="${esc(p.id)}" style="width:${px(w * scale)}px;height:${px(h * scale)}px;transform:${transform};z-index:${stack}${fade < 1 ? `;opacity:${n(fade)}` : ""}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${n(w)} ${n(h)}" width="${px(w * scale)}" height="${px(h * scale)}" aria-hidden="true" focusable="false">${p.svg}</svg></div>`;
|
|
49
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* A plane that is the floor's own drawing seen through its rectangle (`Plane.crop`): the floor's SVG again, its viewBox cut down to the plane's rectangle, in the plane's own box. It is a picture, not a second
|
|
53
|
+
* description: it carries no role, title or description of its own.
|
|
54
|
+
*/
|
|
55
|
+
export function cropMarkup(model, p, transform, stack, fade = 1) {
|
|
56
|
+
const [w, h] = size(p);
|
|
57
|
+
const box = `${px(p.o[0])} ${px(p.o[1])} ${px(w)} ${px(h)}`;
|
|
58
|
+
const picture = model.floor
|
|
59
|
+
.replace(/^<svg([^>]*?)\sviewBox="[^"]*"/, `<svg$1 viewBox="${box}"`)
|
|
60
|
+
.replace(/^<svg([^>]*?)\swidth="[^"]*"/, "<svg$1")
|
|
61
|
+
.replace(/^<svg([^>]*?)\sheight="[^"]*"/, "<svg$1")
|
|
62
|
+
.replace(/^<svg([^>]*?)\srole="img"/, '<svg$1 aria-hidden="true"')
|
|
63
|
+
.replace(/^<svg([^>]*?)\saria-labelledby="[^"]*"/, "<svg$1")
|
|
64
|
+
.replace(/<title\b[^>]*>[\s\S]*?<\/title>|<desc\b[^>]*>[\s\S]*?<\/desc>/g, "");
|
|
65
|
+
return `<div class="ky-up ky-up-${p.kind}" data-part="${esc(p.id)}" data-crop="floor" style="width:${px(w * model.scale)}px;height:${px(h * model.scale)}px;transform:${transform};z-index:${stack}${fade < 1 ? `;opacity:${n(fade)}` : ""}">${picture}</div>`;
|
|
66
|
+
}
|
|
50
67
|
/** The floor as an element: the flat drawing, in the pixels of the scene. */
|
|
51
68
|
export function floorMarkup(model, transform, stack) {
|
|
52
69
|
const vb = model.viewBox;
|
|
@@ -66,7 +83,7 @@ export function worldAttributes(model) {
|
|
|
66
83
|
}
|
|
67
84
|
/** The floor and the planes of a model, laid out. */
|
|
68
85
|
export function worldMarkup(model, layout) {
|
|
69
|
-
return defsMarkup(model) + floorMarkup(model, layout.transforms[0], layout.stack[0]) + model.planes.map((p, i) => planeMarkup(p, model.scale, layout.transforms[i + 1], layout.stack[i + 1], layout.fade[i + 1])).join("");
|
|
86
|
+
return defsMarkup(model) + floorMarkup(model, layout.transforms[0], layout.stack[0]) + model.planes.map((p, i) => (p.crop === true ? cropMarkup(model, p, layout.transforms[i + 1], layout.stack[i + 1], layout.fade[i + 1]) : planeMarkup(p, model.scale, layout.transforms[i + 1], layout.stack[i + 1], layout.fade[i + 1]))).join("");
|
|
70
87
|
}
|
|
71
88
|
/** The buttons under the view. */
|
|
72
89
|
export function controlsMarkup(model, view, playing = false) {
|
package/dist/turn.model.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface TurnSettings {
|
|
|
29
29
|
motion?: "auto" | "smooth" | "none";
|
|
30
30
|
/** The drawing the floor is, in place of `drawKyogijo`: a chart's `draw`, for instance. It is given the options with the view's own changes (the whole surface, lying wide). */
|
|
31
31
|
floor?: (options: SurfaceOptions | ScoreOptions) => string;
|
|
32
|
-
/** How long a lens the eye has, as a multiple of the view's longer side:
|
|
32
|
+
/** How long a lens the eye has, as a multiple of the view's longer side: 8 by default (`LENS`), and the larger it is the flatter the picture; a short lens makes the near end of a turning floor swell. */
|
|
33
33
|
lens?: number;
|
|
34
34
|
/** The size the first view is worked out for, in pixels, when the markup is made away from a page. 960 by 600 by default. */
|
|
35
35
|
size?: {
|
package/dist/turn.model.js
CHANGED
|
@@ -66,7 +66,7 @@ export function buildTurn(options, settings = {}) {
|
|
|
66
66
|
const centre = [vx + vw / 2, vy + vh / 2];
|
|
67
67
|
// The scene's extent: the playing surface (the whole floor where it has no uprights), and whatever stands beyond it (a net behind a goal line). The ground
|
|
68
68
|
// round the surface is not counted, so that the view is fitted to what is played on and the ground runs out of the picture.
|
|
69
|
-
const area = uprights.area
|
|
69
|
+
const area = uprights.whole === true || uprights.area === undefined ? { x0: vx, y0: vy, x1: vx + vw, y1: vy + vh } : uprights.area;
|
|
70
70
|
let [x0, y0, x1, y1] = [area.x0, area.y0, area.x1, area.y1];
|
|
71
71
|
let top = uprights.lift;
|
|
72
72
|
for (const p of planes) {
|
package/dist/turn.style.js
CHANGED
|
@@ -15,6 +15,7 @@ export const KYOGIJO_TURN_STYLE = `
|
|
|
15
15
|
.ky-turn-floor,.ky-up{position:absolute;left:0;top:0;transform-origin:0 0;pointer-events:none}
|
|
16
16
|
.ky-turn-floor>svg{display:block;width:100%;height:auto}
|
|
17
17
|
.ky-up>svg{display:block;overflow:visible}
|
|
18
|
+
.ky-up[data-crop]>svg{width:100%;height:100%;overflow:hidden}
|
|
18
19
|
@media (max-width:560px){.ky-turn-view{--ky-turn-ratio:5/4}.ky-turn-controls button{padding:0 10px;font-size:.85rem}}
|
|
19
20
|
.ky-turn .u-sbframe{fill:var(--ky-up-sbframe);stroke:var(--ky-up-sbedge);stroke-width:.25}
|
|
20
21
|
.ky-turn .u-sbline{stroke:var(--ky-up-sbline)}
|
|
@@ -29,8 +30,10 @@ export const KYOGIJO_TURN_STYLE = `
|
|
|
29
30
|
.ky-turn .u-table{fill:var(--ky-up-table)}
|
|
30
31
|
.ky-turn .u-foot{fill:var(--ky-up-foot)}
|
|
31
32
|
.ky-turn .u-ground{fill:var(--ky-up-ground)}
|
|
33
|
+
.ky-turn .u-shadow{fill:#000;fill-opacity:.16}
|
|
32
34
|
.ky-turn .u-wall{fill:var(--ky-up-wall)}
|
|
33
35
|
.ky-turn .u-clay{fill:var(--ky-up-clay)}
|
|
36
|
+
.ky-turn .u-clay-side{fill:color-mix(in srgb,var(--ky-dirt,var(--ky-up-clay)) 74%,#000)}
|
|
34
37
|
.ky-turn .u-cap{fill:var(--ky-up-cap)}
|
|
35
38
|
.ky-turn .u-board{fill:var(--ky-up-board)}
|
|
36
39
|
.ky-turn .u-glass{fill:var(--ky-up-glass)}
|
package/dist/uprights.budo.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export interface BudoUprights {
|
|
|
10
10
|
x1: number;
|
|
11
11
|
y1: number;
|
|
12
12
|
};
|
|
13
|
+
/** The view is fitted to the whole floor (a platform stands in it), not to `area`. */
|
|
14
|
+
whole: boolean;
|
|
13
15
|
}
|
|
14
16
|
/** The walls of a dojo, 3 m high (ours). */
|
|
15
17
|
export declare const DOJO_WALL = 3;
|
package/dist/uprights.budo.js
CHANGED
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
// (a judo platform's height is not in the rules: "it depends on the sightlines"). The plans are in metres in the frame of their own origin, and the drawing's frame is feet from the corner of the plan's
|
|
4
4
|
// box, so every point goes through `at`. The mound's height is the option's; the azuchi's (1.5 m or 1.8 m) and a dojo wall's (3 m) are ours: no rule gives them.
|
|
5
5
|
import { budoPlan } from "./budo/plan.js";
|
|
6
|
-
import { n, plane, shy, wall } from "./turn.kit.js";
|
|
6
|
+
import { flat, n, plane, shy, wall } from "./turn.kit.js";
|
|
7
7
|
const K = 1 / 0.3048;
|
|
8
8
|
/** The walls of a dojo, 3 m high (ours). */
|
|
9
9
|
export const DOJO_WALL = 3;
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* The planes of the dohyō's clay mound, a solid block: its four sides, from the floor of the hall up to the clay, and its flat top, which is the drawing's own clay with the ring of straw bales on it, lifted to the
|
|
12
|
+
* mound's height (the floor itself lies on the hall's floor, so that the ground round the mound stays low). The sides are one tone darker than the top, so that top and sides are lit the same way; whichever way
|
|
13
|
+
* the view is turned, the sides on the far side are drawn first and the top over them (see `layoutOf`), so the mound never looks hollow.
|
|
14
|
+
*/
|
|
11
15
|
function moundPlanes(plan, at, height) {
|
|
12
16
|
const L = plan.sumo;
|
|
13
17
|
if (L === undefined)
|
|
@@ -17,8 +21,10 @@ function moundPlanes(plan, at, height) {
|
|
|
17
21
|
const planes = [];
|
|
18
22
|
corners.forEach((a, i) => {
|
|
19
23
|
const c = corners[(i + 1) % 4];
|
|
20
|
-
planes.push(wall(`mound-${i + 1}`, "wall", a[0], a[1], c[0], c[1], 0, height, `<rect width="${n(Math.hypot(c[0] - a[0], c[1] - a[1]))}" height="${n(height)}" class="u-clay"/>`));
|
|
24
|
+
planes.push(wall(`mound-${i + 1}`, "wall", a[0], a[1], c[0], c[1], 0, height, `<rect width="${n(Math.hypot(c[0] - a[0], c[1] - a[1]))}" height="${n(height)}" class="u-clay u-clay-side"/>`));
|
|
21
25
|
});
|
|
26
|
+
const [a, c] = [corners[0], corners[2]];
|
|
27
|
+
planes.push({ ...flat("mound-top", "platform", a[0], a[1], c[0], c[1], height, ""), crop: true });
|
|
22
28
|
return planes;
|
|
23
29
|
}
|
|
24
30
|
/** The azuchi's face, a slope from the foot of the mound up to its top, and the target of each shooter standing against it. */
|
|
@@ -54,10 +60,11 @@ export function budoUprights(o, thin) {
|
|
|
54
60
|
const at = (x, y) => [(x - ox) * K, (y - oy) * K];
|
|
55
61
|
const area = { x0: 0, y0: 0, x1: (plan.box.x1 - ox) * K, y1: (plan.box.y1 - oy) * K };
|
|
56
62
|
const items = [];
|
|
57
|
-
|
|
63
|
+
// The floor lies on the hall's floor (`lift` is 0): the mound is a block standing on it, with its own top.
|
|
64
|
+
const lift = 0;
|
|
58
65
|
if (plan.sumo !== undefined) {
|
|
59
|
-
|
|
60
|
-
items.push({ id: "mound", kind: "mound", at: at(plan.sumo.centre[0], plan.sumo.centre[1]), height
|
|
66
|
+
const height = plan.sumo.height * K;
|
|
67
|
+
items.push({ id: "mound", kind: "mound", at: at(plan.sumo.centre[0], plan.sumo.centre[1]), height, planes: moundPlanes(plan, at, height) });
|
|
61
68
|
}
|
|
62
69
|
if (plan.kyudo !== undefined) {
|
|
63
70
|
const built = azuchiPlanes(plan, at);
|
|
@@ -83,5 +90,5 @@ export function budoUprights(o, thin) {
|
|
|
83
90
|
items.push({ id: "walls", kind: "walls", at: centre, height: h, planes });
|
|
84
91
|
}
|
|
85
92
|
void thin;
|
|
86
|
-
return { items, lift, area };
|
|
93
|
+
return { items, lift, area, whole: plan.sumo !== undefined };
|
|
87
94
|
}
|
package/dist/uprights.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ export interface Uprights {
|
|
|
29
29
|
x1: number;
|
|
30
30
|
y1: number;
|
|
31
31
|
};
|
|
32
|
+
/** True when the view is fitted to the whole floor drawing, not to `area` (which is then only where the score board goes): a raised platform stands in a floor that must not be cut at any angle. */
|
|
33
|
+
whole?: boolean;
|
|
32
34
|
/** The patterns the venue's planes are drawn with (the contents of an SVG `defs`), when it has any. */
|
|
33
35
|
defs?: string;
|
|
34
36
|
/** How far the venue reaches beyond the surface, in feet, when it has one: the ground a floor needs for it to stand on. */
|
package/dist/uprights.js
CHANGED
|
@@ -41,6 +41,7 @@ export function uprightsFor(o, settings = {}) {
|
|
|
41
41
|
let lift = 0;
|
|
42
42
|
let thin = 0.2;
|
|
43
43
|
let area;
|
|
44
|
+
let whole = false;
|
|
44
45
|
if (sport === "nfl" || sport === "cfl" || sport === "ifaf") {
|
|
45
46
|
const spec = league?.family === "football" ? fieldOf(league, o.year) : footballIn(sport, o.year);
|
|
46
47
|
const shape = league?.family === "football" || sport === "ifaf" ? "slingshot" : rulesFor(sport, o.year).rules.postShape;
|
|
@@ -112,6 +113,7 @@ export function uprightsFor(o, settings = {}) {
|
|
|
112
113
|
thin = thinnest(area.x1, area.y1);
|
|
113
114
|
items = budoUprights({ ...o, sport }, thin).items;
|
|
114
115
|
lift = built.lift;
|
|
116
|
+
whole = built.whole;
|
|
115
117
|
}
|
|
116
118
|
else if (isFieldSport(sport)) {
|
|
117
119
|
const plan = fieldPlan({ ...o, sport });
|
|
@@ -145,7 +147,7 @@ export function uprightsFor(o, settings = {}) {
|
|
|
145
147
|
}
|
|
146
148
|
if (settings.only !== undefined)
|
|
147
149
|
items = items.filter((item) => settings.only.includes(item.kind));
|
|
148
|
-
return { items, lift, thin, area, ...(defs === "" ? {} : { defs }), ...(reach === 0 ? {} : { reach }) };
|
|
150
|
+
return { items, lift, thin, area, ...(whole ? { whole } : {}), ...(defs === "" ? {} : { defs }), ...(reach === 0 ? {} : { reach }) };
|
|
149
151
|
}
|
|
150
152
|
/** A length in feet as a rulebook would print it in `unit`: metres to the centimetre, feet to a tenth. */
|
|
151
153
|
function said(feet, unit, language) {
|
package/dist/uprights.net.js
CHANGED
|
@@ -100,7 +100,18 @@ export function netUprights(plan, geo, settings, thin) {
|
|
|
100
100
|
items.push({ id: "table", kind: "table-legs", at: [L / 2, W / 2], height: lift, planes: tableBody(0, 0, L, W, lift, thin, false) });
|
|
101
101
|
// The floor of the hall, under the table.
|
|
102
102
|
const room = Math.max(L, W) * 5;
|
|
103
|
-
|
|
103
|
+
// Under the table lies its shadow on that floor, so that the legs stand on something.
|
|
104
|
+
const grow = Math.max(convertLength(0.25, "m", "ft"), thin * 4);
|
|
105
|
+
items.push({
|
|
106
|
+
id: "ground",
|
|
107
|
+
kind: "table-legs",
|
|
108
|
+
at: [L / 2, W / 2],
|
|
109
|
+
height: 0,
|
|
110
|
+
planes: [
|
|
111
|
+
flat("ground", "ground", -room, -room, L + room, W + room, 0, `<rect width="${n(L + 2 * room)}" height="${n(W + 2 * room)}" class="u-ground"/>`),
|
|
112
|
+
flat("table-shadow", "ground", -grow / 2, -grow / 2, L + grow / 2, W + grow / 2, 0.005, `<rect width="${n(L + grow)}" height="${n(W + grow)}" rx="${n(grow)}" class="u-shadow"/>`),
|
|
113
|
+
],
|
|
114
|
+
});
|
|
104
115
|
return { items, lift };
|
|
105
116
|
}
|
|
106
117
|
default:
|
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.12.
|
|
2
|
+
export declare const VERSION = "0.12.2";
|
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.12.
|
|
2
|
+
export const VERSION = "0.12.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnmorrisdotca/kyogijo-stadiums",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"description": "Kyogijo Stadiums 競技場, 39 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, 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",
|