@johnmorrisdotca/kyogijo-stadiums 0.45.1 → 0.45.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 +39 -0
- package/dist/quality.d.ts +20 -2
- package/dist/quality.js +25 -4
- package/dist/turn.camera.d.ts +5 -0
- package/dist/turn.camera.js +8 -0
- package/dist/turn.dom.js +55 -5
- 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,45 @@ All notable changes to this project are written here. The format follows
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.45.2] - 2026-10-30
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **The 3D view of a district makes its layers as fine as the screen shows them, and no finer: a phone holds a sixteenth of the pixels.** The scene is made 1,400 pixels across and every plane was a layer as fine as that, whatever the screen: a phone shows the scene a sixth of that size, so its layers were five times finer than anything it could draw, and at a ratio of 3 the Kodokan's building alone was 713 megabytes of layers (each floor a layer of 1,114 by 1,186 pixels), which is what an iPhone's browser could not keep (SPX-699). The view now works out the share of its size a layer needs from the scale that fits the scene in its box (at any tip of the view), the zoom, and a margin of a quarter for the perspective, and rounds it up to 0.25, 0.4, 0.6 or 1 (`viewShare`, `viewScale`, `SHARE_LEVELS`; docs/quality.md, "The pixels of the layers"). It holds for every tier, `high` asked for by name too; the 3D view of a single surface is not changed. The Kodokan alone on a phone is made at 0.25 (a sixteenth of the pixels). The floor, which is one layer and holds the thinnest lines of the view, is made at 0.6 at least (`FLOOR_SHARE`), because a one-pixel line such as the north arrow shows steps in Chromium when its layer is made a quarter as fine.
|
|
15
|
+
- **The share follows the view.** A zoom that needs a finer share draws the layers again 160 milliseconds after it stops (a pinch is not drawn again at every step, and from the whole view to the nearest it is at most three times); a zoom out or a smaller box makes them coarser only after a second and a half at rest; a new size of the container, a new set of venues and a step of the tier work it out again. The camera, the auto-turn and a drag in the middle of being made are not touched, and `data-share` on `.ky-turn` says the share. A wide screen needs the whole share, so there nothing changes: its screenshots are the same byte for byte.
|
|
16
|
+
- **The flat layers were not moved onto a canvas, as SPX-772 first asked, because they are not where a step goes.** Streets, footprints, trees and land are a few paths in the floor, and the floor is one layer (17 elements for the Kodokan). Taking the floor out of the page altogether saves 4 to 6 percent of a turn step in Chromium with the CPU 4 times slower (Seattle 117 to 111 ms and 119 to 113 in two runs); taking every plane out saves three quarters of it (117 to 44). A step costs the planes that stand up (2,175 for Seattle), not what lies flat.
|
|
17
|
+
- Costs 5 KB (1 KB gzipped) of script on a district page (2,411 to 2,416 KB); the bundle budget holds.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Tests: `viewShare`, the floor's share and `rasterShares` with a ceiling (`src/quality.test.ts`); `e2e/quality.demo.mjs` checks on a phone that the Kodokan's layers are under 120 megabytes, that a zoom in makes them finer a moment after it stops and a zoom out makes them coarser again after a while at rest, and that the floor is where it was.
|
|
22
|
+
|
|
23
|
+
### Measured
|
|
24
|
+
|
|
25
|
+
Chromium and WebKit on a Mac Studio, a phone window of 390 by 844 at a ratio of 3, the CPU of Chromium slowed 4 times where said, 40 turn steps a row (`node scripts/perf-district.mjs --quality high,medium,low,auto`); the 0.45.1 site and this one were built beside each other and measured one after the other. The load average of the machine was 18 to 20 before each run (`uptime`) and 15 to 26 during them, so a frame that differs by under 15 percent in Chromium is not a difference. Layers are the boxes times the ratio squared, four bytes a pixel, as the script has always counted them.
|
|
26
|
+
|
|
27
|
+
| Preset (high) | Planes | Layers MB at ratio 3: before | after | WebKit phone ms a frame: before | after | frames a second: before | after | Chromium phone, 4x CPU, ms: before | after |
|
|
28
|
+
|---|---|---|---|---|---|---|---|---|---|
|
|
29
|
+
| Rogers Arena alone | 547 | 100 | 25 | 17 | 17 | 59 | 59 | 30 | 32 |
|
|
30
|
+
| BC Place and Rogers Arena | 1309 | 118 | 24 | 17 | 17 | 59 | 59 | 81 | 68 |
|
|
31
|
+
| BC Place (soccer) and Rogers Arena | 1305 | 118 | 25 | 17 | 17 | 59 | 59 | 64 | 66 |
|
|
32
|
+
| Seattle Center | 1088 | 98 | 21 | 105 | 21 | 10 | 48 | 41 | 45 |
|
|
33
|
+
| SoDo | 1346 | 86 | 18 | 124 | 26 | 8 | 38 | 66 | 71 |
|
|
34
|
+
| Seattle, three venues | 2175 | 50 | 15 | 60 | 31 | 17 | 32 | 119 | 141 |
|
|
35
|
+
| Tokyo Dome City | 2049 | 76 | 19 | 121 | 41 | 8 | 24 | 109 | 109 |
|
|
36
|
+
| Tokyo Dome City and the Kodokan | 2286 | 78 | 16 | 140 | 54 | 7 | 19 | 119 | 123 |
|
|
37
|
+
| The Kodokan alone | 152 | 713 | 64 | 28 | 17 | 36 | 59 | 17 | 17 |
|
|
38
|
+
|
|
39
|
+
| Kodokan alone, layers MB at ratio 3 | before | after |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| high | 713 | 64 |
|
|
42
|
+
| medium | 67 | 25 |
|
|
43
|
+
| low | 32 | 21 |
|
|
44
|
+
| auto (first draw) | 317 | 64 |
|
|
45
|
+
|
|
46
|
+
- **WebKit, the iPhone's engine, turns the heavy presets two to five times faster** (Seattle Center 105 to 21 ms a frame, SoDo 124 to 26, Tokyo Dome City 121 to 41, Tokyo Dome City and the Kodokan 140 to 54, the Kodokan alone 28 to 17): its frames follow the pixels of the layers. Chromium does not move beyond the noise (a desk window at 4 times slower CPU, built before and after run in turn twice, for BC Place (soccer) and Rogers Arena, Tokyo Dome City, and with the Kodokan: 65 and 60, 95 and 99, 111 and 111 ms, then 64 and 67, 97 and 91, 124 and 113; and a desk window is the same page to the byte). The lower tiers' layers fall too: the Kodokan at `medium` 67 to 25 megabytes, at `low` 32 to 21.
|
|
47
|
+
- **The pictures.** Every preset at every tier, in light and dark, in Chromium (a desk window and a phone window) and in WebKit (a phone window): 162 pairs of screenshots. The desk window is the same to the byte. At a ratio of 3 WebKit differs by at most 0.23 percent of the pixels by more than 8 levels of 255 (0.05 percent by more than 32) and Chromium by at most 1.8 and 0.13 percent. Side by side the colours and the places are the same; what differs is thin lines and patterns that alias: a football field's yard lines are clean where they were dotted, rails and the Kodokan's mats are crisper, and in Chromium a one-pixel arrow is a little rougher.
|
|
48
|
+
|
|
10
49
|
## [0.45.1] - 2026-10-30
|
|
11
50
|
|
|
12
51
|
### Changed
|
package/dist/quality.d.ts
CHANGED
|
@@ -62,11 +62,29 @@ export declare function rasterOf(quality: Quality, tier: QualityTier, dpr: numbe
|
|
|
62
62
|
* still (see `rasterOf`). `Infinity` at `high`, by name or in `auto`.
|
|
63
63
|
*/
|
|
64
64
|
export declare function layerBudget(tier: QualityTier, isHandheld: boolean): number;
|
|
65
|
+
/**
|
|
66
|
+
* The shares a layer is made at, from the smallest to a whole: the share a view needs (`viewShare`) is rounded up to one of these, so that a zoom draws the layers again at most three times from the whole view to the nearest and not at every step.
|
|
67
|
+
*/
|
|
68
|
+
export declare const SHARE_LEVELS: readonly number[];
|
|
69
|
+
/** How much finer than the screen shows it a layer is made: the perspective makes what is near the eye larger than the scale of the fit says, by a tenth at most with the lens of a district. */
|
|
70
|
+
export declare const VIEW_MARGIN = 1.25;
|
|
71
|
+
/**
|
|
72
|
+
* The share of its size a layer needs to be as fine as the screen shows it, whatever the device's pixel ratio: a layer holds `share` device pixels for each pixel of the scene (times the ratio) and the screen shows `scale * zoom`
|
|
73
|
+
* of them (times the ratio too, and a little more near the eye), so a share of `scale * zoom * VIEW_MARGIN` is as sharp as the view and no layer needs more. `scale` is the scale that fits the scene in its container (`viewScale`), `zoom` the
|
|
74
|
+
* view's. Rounded up to one of `SHARE_LEVELS`, so a view that is drawn at 0.15 of the scene's size, as a district on a phone is, has layers a quarter as wide and as high, a sixteenth of the pixels. 1 when the view is at least as large as the scene.
|
|
75
|
+
*/
|
|
76
|
+
export declare function viewShare(scale: number, zoom: number): number;
|
|
77
|
+
/**
|
|
78
|
+
* The least share the floor is made at, under what the view needs: the floor is one layer, and it holds the thinnest lines of the view (the north arrow, the scale, the grid, the streets and their edges), which a browser draws as a staircase
|
|
79
|
+
* when the layer is made much coarser than it shows (Chromium does; measured on the demo at a ratio of 3). One layer at 0.6 of its size is 24 megabytes on a phone at a ratio of 3, against 67 whole.
|
|
80
|
+
*/
|
|
81
|
+
export declare const FLOOR_SHARE = 0.6;
|
|
65
82
|
/**
|
|
66
83
|
* The share of its size each layer is made at, from the sizes the layers would have at full size (their boxes in CSS pixels), the share every layer is made at (`rasterOf`), the device's pixel ratio and the layer budget: a layer is
|
|
67
|
-
* made at the smaller of the share and the one that holds `budget` device pixels, never under a fifth. The floor comes first, then the planes, as `layoutOf` takes them
|
|
84
|
+
* made at the smaller of the share and the one that holds `budget` device pixels, and never at more than `ceiling` (what the view needs, `viewShare`), never under a fifth. The floor comes first, then the planes, as `layoutOf` takes them; its
|
|
85
|
+
* ceiling is at least `floorMin`.
|
|
68
86
|
*/
|
|
69
|
-
export declare function rasterShares(sizes: readonly (readonly [number, number])[], share: number, dpr: number, budget: number): number[];
|
|
87
|
+
export declare function rasterShares(sizes: readonly (readonly [number, number])[], share: number, dpr: number, budget: number, ceiling?: number, floorMin?: number): number[];
|
|
70
88
|
/** How the governor judges the frames. Times are in milliseconds. */
|
|
71
89
|
export interface GovernorTuning {
|
|
72
90
|
/** The frames in a window it keeps (the oldest are dropped past twice this), and the fewest it judges: a window is also at least `settleMs` long, so a view at 60 frames a second judges 42 frames and one at 6 frames a second judges 8. */
|
package/dist/quality.js
CHANGED
|
@@ -51,16 +51,37 @@ export function layerBudget(tier, isHandheld) {
|
|
|
51
51
|
return Infinity;
|
|
52
52
|
return { medium: isHandheld ? 2e6 : 4e6, low: isHandheld ? 1e6 : 2e6 }[tier];
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* The shares a layer is made at, from the smallest to a whole: the share a view needs (`viewShare`) is rounded up to one of these, so that a zoom draws the layers again at most three times from the whole view to the nearest and not at every step.
|
|
56
|
+
*/
|
|
57
|
+
export const SHARE_LEVELS = [0.25, 0.4, 0.6, 1];
|
|
58
|
+
/** How much finer than the screen shows it a layer is made: the perspective makes what is near the eye larger than the scale of the fit says, by a tenth at most with the lens of a district. */
|
|
59
|
+
export const VIEW_MARGIN = 1.25;
|
|
60
|
+
/**
|
|
61
|
+
* The share of its size a layer needs to be as fine as the screen shows it, whatever the device's pixel ratio: a layer holds `share` device pixels for each pixel of the scene (times the ratio) and the screen shows `scale * zoom`
|
|
62
|
+
* of them (times the ratio too, and a little more near the eye), so a share of `scale * zoom * VIEW_MARGIN` is as sharp as the view and no layer needs more. `scale` is the scale that fits the scene in its container (`viewScale`), `zoom` the
|
|
63
|
+
* view's. Rounded up to one of `SHARE_LEVELS`, so a view that is drawn at 0.15 of the scene's size, as a district on a phone is, has layers a quarter as wide and as high, a sixteenth of the pixels. 1 when the view is at least as large as the scene.
|
|
64
|
+
*/
|
|
65
|
+
export function viewShare(scale, zoom) {
|
|
66
|
+
const need = scale * zoom * VIEW_MARGIN;
|
|
67
|
+
return SHARE_LEVELS.find((level) => level >= need - 1e-9) ?? 1;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The least share the floor is made at, under what the view needs: the floor is one layer, and it holds the thinnest lines of the view (the north arrow, the scale, the grid, the streets and their edges), which a browser draws as a staircase
|
|
71
|
+
* when the layer is made much coarser than it shows (Chromium does; measured on the demo at a ratio of 3). One layer at 0.6 of its size is 24 megabytes on a phone at a ratio of 3, against 67 whole.
|
|
72
|
+
*/
|
|
73
|
+
export const FLOOR_SHARE = 0.6;
|
|
54
74
|
/**
|
|
55
75
|
* The share of its size each layer is made at, from the sizes the layers would have at full size (their boxes in CSS pixels), the share every layer is made at (`rasterOf`), the device's pixel ratio and the layer budget: a layer is
|
|
56
|
-
* made at the smaller of the share and the one that holds `budget` device pixels, never under a fifth. The floor comes first, then the planes, as `layoutOf` takes them
|
|
76
|
+
* made at the smaller of the share and the one that holds `budget` device pixels, and never at more than `ceiling` (what the view needs, `viewShare`), never under a fifth. The floor comes first, then the planes, as `layoutOf` takes them; its
|
|
77
|
+
* ceiling is at least `floorMin`.
|
|
57
78
|
*/
|
|
58
|
-
export function rasterShares(sizes, share, dpr, budget) {
|
|
79
|
+
export function rasterShares(sizes, share, dpr, budget, ceiling = 1, floorMin = 0) {
|
|
59
80
|
const ratio = dpr > 0 ? dpr : 1;
|
|
60
|
-
return sizes.map(([w, h]) => {
|
|
81
|
+
return sizes.map(([w, h], i) => {
|
|
61
82
|
const area = w * h * ratio * ratio;
|
|
62
83
|
const own = area > budget ? Math.sqrt(budget / area) : 1;
|
|
63
|
-
return Math.max(0.2, Math.round(Math.min(share, own) * 1000) / 1000);
|
|
84
|
+
return Math.max(0.2, Math.round(Math.min(share, own, i === 0 ? Math.max(ceiling, floorMin) : ceiling) * 1000) / 1000);
|
|
64
85
|
});
|
|
65
86
|
}
|
|
66
87
|
export const GOVERNOR_TUNING = { frames: 24, minFrames: 8, settleMs: 700, warmMs: 600, budgetMs: 21, steadyMs: 18.5, upAfterMs: 5000, maxUpAfterMs: 300000, quietMs: 600, pauseMs: 250 };
|
package/dist/turn.camera.d.ts
CHANGED
|
@@ -114,6 +114,11 @@ export declare function covered(b: Box3, view: TurnView, at: Placement, distance
|
|
|
114
114
|
* extent of the scene on the screen changes with the pitch. The perspective bends the sum, so it is found by a few rounds of scaling and re-centring; six is plenty.
|
|
115
115
|
*/
|
|
116
116
|
export declare function fit(b: Box3, view: TurnView, w: number, h: number, distance: number, pad?: number): Placement;
|
|
117
|
+
/**
|
|
118
|
+
* The scale (screen pixels for a pixel of the scene, at a zoom of 1) a scene's box is fitted at in a container, the largest any tip of the view makes it: a view tipped towards the horizon is fitted larger, and the layers are made
|
|
119
|
+
* for the largest so that tipping the view does not make them again.
|
|
120
|
+
*/
|
|
121
|
+
export declare function viewScale(b: Box3, w: number, h: number, lens?: number): number;
|
|
117
122
|
/** A 4 by 4 matrix, as CSS writes it in `matrix3d`: sixteen numbers, a column after a column. */
|
|
118
123
|
export type Mat4 = readonly number[];
|
|
119
124
|
/** Two matrices as one: `b` first, then `a`. */
|
package/dist/turn.camera.js
CHANGED
|
@@ -179,6 +179,14 @@ export function fit(b, view, w, h, distance, pad = 0.03) {
|
|
|
179
179
|
lastFit = { key, at: { ...at } };
|
|
180
180
|
return at;
|
|
181
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* The scale (screen pixels for a pixel of the scene, at a zoom of 1) a scene's box is fitted at in a container, the largest any tip of the view makes it: a view tipped towards the horizon is fitted larger, and the layers are made
|
|
184
|
+
* for the largest so that tipping the view does not make them again.
|
|
185
|
+
*/
|
|
186
|
+
export function viewScale(b, w, h, lens) {
|
|
187
|
+
const distance = eyeDistance(w, h, lens);
|
|
188
|
+
return Math.max(...[0, 30, 58, 70, PITCH_MAX].map((pitch) => fit(b, { yaw: 0, pitch, zoom: 1 }, w, h, distance).k));
|
|
189
|
+
}
|
|
182
190
|
/** Two matrices as one: `b` first, then `a`. */
|
|
183
191
|
export function multiply(a, b) {
|
|
184
192
|
const out = new Array(16);
|
package/dist/turn.dom.js
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
// reduced motion gets jumps for glides, and where the browser cannot draw in 3D the flat drawing stays. Nothing is polled and nothing runs while the view is
|
|
4
4
|
// still: the elements' `transform`s are written when the view changes, and a glide between viewpoints is a short run of animation frames.
|
|
5
5
|
import { drawKyogijo } from "./draw.js";
|
|
6
|
-
import { STEP, TURN_PRESET_NAMES, clampView, drag, nearest, nudge, presetOf, presetView, step, turnDirection, wheelTip } from "./turn.camera.js";
|
|
6
|
+
import { STEP, TURN_PRESET_NAMES, clampView, drag, nearest, nudge, presetOf, presetView, step, turnDirection, viewScale, wheelTip } from "./turn.camera.js";
|
|
7
7
|
import { buildTurn, floorOptions, openingView, turnWords } from "./turn.model.js";
|
|
8
8
|
import { controlsMarkup, layoutOf, turnMarkupOf, worldMarkup } from "./turn.markup.js";
|
|
9
9
|
import { KYOGIJO_TURN_STYLE } from "./turn.style.js";
|
|
10
10
|
import { esc } from "./format.js";
|
|
11
11
|
import { turnBoardMarkup } from "./overlay.js";
|
|
12
12
|
import { attachOutside3d } from "./outside.dom.js";
|
|
13
|
-
import { TIER_PROFILES, createGovernor, handheld, layerBudget, qualityOf, rasterOf, rasterShares, tierOf } from "./quality.js";
|
|
13
|
+
import { TIER_PROFILES, createGovernor, handheld, layerBudget, qualityOf, rasterOf, rasterShares, tierOf, viewShare, FLOOR_SHARE } from "./quality.js";
|
|
14
14
|
import { size } from "./turn.kit.js";
|
|
15
15
|
/** True where the page can draw a plane in 3D by one `matrix3d`: that is, nearly everywhere with a stylesheet. */
|
|
16
16
|
export function turnSupported(win = typeof window === "undefined" ? undefined : window) {
|
|
@@ -50,6 +50,12 @@ export function mountTurn(host, options, settings, build) {
|
|
|
50
50
|
let tier = tierOf(quality);
|
|
51
51
|
// (Set again below, once the model is built, for a view that has no tiers.)
|
|
52
52
|
const isHandheld = handheld(win);
|
|
53
|
+
/**
|
|
54
|
+
* The share of its size a layer needs to be as fine as this screen shows it (`viewShare`): a district on a phone is drawn at a sixth of the scene's own size, so its layers need a quarter as many pixels on a side. It is worked out from the container's
|
|
55
|
+
* size and the zoom, and held while they are as they are: `settleRaster` draws the layers again when they change enough to need another.
|
|
56
|
+
*/
|
|
57
|
+
let ceiling = 1;
|
|
58
|
+
const ceilingFor = (of, width, height, zoom) => (of.tiered === true ? viewShare(viewScale(of.box, width, height, settings.lens ?? of.lens), zoom) : 1);
|
|
53
59
|
/** The share of its size each layer is made at for a model, now: one number when they all are, one each when the biggest are held to a budget of pixels. */
|
|
54
60
|
const rasterFor = (of) => {
|
|
55
61
|
// (Only a model that has tiers is made smaller: a surface on its own is small, and nothing of it would be left out.)
|
|
@@ -58,16 +64,21 @@ export function mountTurn(host, options, settings, build) {
|
|
|
58
64
|
const dpr = win?.devicePixelRatio ?? 1;
|
|
59
65
|
const share = rasterOf(quality, tier, dpr, isHandheld);
|
|
60
66
|
const budget = layerBudget(tier, isHandheld);
|
|
61
|
-
if (share === 1 && budget === Infinity)
|
|
67
|
+
if (share === 1 && budget === Infinity && ceiling === 1)
|
|
62
68
|
return 1;
|
|
63
69
|
const boxes = [[of.viewBox.w * of.scale, of.viewBox.h * of.scale], ...of.planes.map((p) => { const [w, h] = size(p); return [w * of.scale, h * of.scale]; })];
|
|
64
|
-
return rasterShares(boxes, share, dpr, budget);
|
|
70
|
+
return rasterShares(boxes, share, dpr, budget, ceiling, FLOOR_SHARE);
|
|
65
71
|
};
|
|
66
72
|
const sameRaster = (a, b) => (typeof a === "number" || typeof b === "number" ? a === b : a.length === b.length && a.every((x, i) => x === b[i]));
|
|
67
73
|
let current = options;
|
|
68
74
|
let model = build(options, { ...settings, quality: tier });
|
|
69
75
|
if (model.tiered !== true)
|
|
70
76
|
tier = "high";
|
|
77
|
+
// (The container is not written yet: its width is the host's and its height the one the stylesheet gives it, 16 by 10 or 5 by 4 on a phone; `settleRaster` corrects it once the view is in the page.)
|
|
78
|
+
{
|
|
79
|
+
const width = host.clientWidth > 0 ? host.clientWidth : (settings.size?.width ?? 960);
|
|
80
|
+
ceiling = ceilingFor(model, width, Math.max(260, width * (width <= 560 ? 0.8 : 0.625)), model.view.zoom);
|
|
81
|
+
}
|
|
71
82
|
let raster = rasterFor(model);
|
|
72
83
|
/** The models built at each tier for the options now, so that a step back to a tier is not built again. */
|
|
73
84
|
let made = new Map([[tier, model]]);
|
|
@@ -155,6 +166,7 @@ export function mountTurn(host, options, settings, build) {
|
|
|
155
166
|
crowd();
|
|
156
167
|
root.dataset.quality = quality;
|
|
157
168
|
root.dataset.tier = tier;
|
|
169
|
+
root.dataset.share = String(ceiling);
|
|
158
170
|
// ---- Quality: in `auto`, the frames the view makes while it turns decide the tier. ----
|
|
159
171
|
let governor = quality === "auto" && win !== null && model.tiered === true ? createGovernor(tier, settings.qualityTuning) : undefined;
|
|
160
172
|
let meterFrame;
|
|
@@ -230,6 +242,7 @@ export function mountTurn(host, options, settings, build) {
|
|
|
230
242
|
const known = made.get(tier);
|
|
231
243
|
model = known ?? build(current, { ...settings, quality: tier, id: settings.id ?? baseId, venue, preset: undefined, view });
|
|
232
244
|
made.set(tier, model);
|
|
245
|
+
ceiling = ceilingFor(model, stage.clientWidth || 960, stage.clientHeight || 600, shown.zoom);
|
|
233
246
|
raster = rasterFor(model);
|
|
234
247
|
const layout = layoutOf(model, shown, stage.clientWidth || 960, stage.clientHeight || 600, settings.lens, false, raster);
|
|
235
248
|
const world = root.querySelector(".ky-turn-world");
|
|
@@ -249,6 +262,7 @@ export function mountTurn(host, options, settings, build) {
|
|
|
249
262
|
crowd();
|
|
250
263
|
root.dataset.quality = quality;
|
|
251
264
|
root.dataset.tier = tier;
|
|
265
|
+
root.dataset.share = String(ceiling);
|
|
252
266
|
};
|
|
253
267
|
/** The governor stepped: draw at the new tier. */
|
|
254
268
|
const retier = (next) => {
|
|
@@ -273,6 +287,30 @@ export function mountTurn(host, options, settings, build) {
|
|
|
273
287
|
if (glyph !== null)
|
|
274
288
|
glyph.textContent = playing ? "❚❚" : "▶";
|
|
275
289
|
};
|
|
290
|
+
// ---- The pixels of the layers follow the view: a zoom or a new size of the container may need a finer or allow a coarser share, and the layers are drawn again at it. ----
|
|
291
|
+
let seenZoom = shown.zoom;
|
|
292
|
+
let settling;
|
|
293
|
+
/** Draw the layers again if the view needs another share than they were made at. */
|
|
294
|
+
const settleRaster = () => {
|
|
295
|
+
settling = undefined;
|
|
296
|
+
if (!alive || model.tiered !== true)
|
|
297
|
+
return;
|
|
298
|
+
if (ceilingFor(model, stage.clientWidth || 960, stage.clientHeight || 600, shown.zoom) === ceiling)
|
|
299
|
+
return;
|
|
300
|
+
renderWorld();
|
|
301
|
+
paint(shown);
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* The view changed in a way that may change the share it needs. A finer share is drawn soon (a moment after the zoom has stopped, so that a pinch is not drawn again at every step), a coarser one only after a while at rest: until
|
|
305
|
+
* then the layers are finer than needed, which costs memory and nothing to the eye.
|
|
306
|
+
*/
|
|
307
|
+
const wantRaster = () => {
|
|
308
|
+
if (!alive || model.tiered !== true || win === null)
|
|
309
|
+
return;
|
|
310
|
+
clearTimeout(settling);
|
|
311
|
+
const next = ceilingFor(model, stage.clientWidth || 960, stage.clientHeight || 600, shown.zoom);
|
|
312
|
+
settling = next === ceiling ? undefined : win.setTimeout(settleRaster, next > ceiling ? 160 : 1500);
|
|
313
|
+
};
|
|
276
314
|
/** Put a view on the screen. */
|
|
277
315
|
const paint = (now) => {
|
|
278
316
|
const width = stage.clientWidth || 960;
|
|
@@ -298,6 +336,10 @@ export function mountTurn(host, options, settings, build) {
|
|
|
298
336
|
if (faded[i] !== o)
|
|
299
337
|
one.style.opacity = faded[i] = o;
|
|
300
338
|
});
|
|
339
|
+
if (now.zoom !== seenZoom) {
|
|
340
|
+
seenZoom = now.zoom;
|
|
341
|
+
wantRaster();
|
|
342
|
+
}
|
|
301
343
|
touched();
|
|
302
344
|
};
|
|
303
345
|
const label = () => {
|
|
@@ -308,6 +350,8 @@ export function mountTurn(host, options, settings, build) {
|
|
|
308
350
|
};
|
|
309
351
|
paint(shown);
|
|
310
352
|
label();
|
|
353
|
+
// The container is in the page now: the share its layers need, at its real size.
|
|
354
|
+
settleRaster();
|
|
311
355
|
const stop = () => {
|
|
312
356
|
if (flight !== undefined)
|
|
313
357
|
win?.cancelAnimationFrame(flight.frame);
|
|
@@ -602,7 +646,12 @@ export function mountTurn(host, options, settings, build) {
|
|
|
602
646
|
// ---- The size of the box. ----
|
|
603
647
|
let observer;
|
|
604
648
|
if (typeof ResizeObserver === "function") {
|
|
605
|
-
observer = new ResizeObserver(() =>
|
|
649
|
+
observer = new ResizeObserver(() => {
|
|
650
|
+
if (!alive)
|
|
651
|
+
return;
|
|
652
|
+
paint(shown);
|
|
653
|
+
wantRaster();
|
|
654
|
+
});
|
|
606
655
|
observer.observe(stage);
|
|
607
656
|
}
|
|
608
657
|
// Nothing moves until the reader asks, unless the page did: `autoPlay` starts it, but not for a reader who prefers reduced motion (`"force"` does).
|
|
@@ -719,6 +768,7 @@ export function mountTurn(host, options, settings, build) {
|
|
|
719
768
|
if (!alive)
|
|
720
769
|
return;
|
|
721
770
|
alive = false;
|
|
771
|
+
clearTimeout(settling);
|
|
722
772
|
outside?.destroy();
|
|
723
773
|
stop();
|
|
724
774
|
stopTick();
|
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.45.
|
|
2
|
+
export declare const VERSION = "0.45.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.45.
|
|
2
|
+
export const VERSION = "0.45.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnmorrisdotca/kyogijo-stadiums",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.2",
|
|
4
4
|
"description": "Kyogijo Stadiums 競技場, 40 sports' surfaces as SVG, drawn to scale from the rulebooks: NFL, CFL and IFAF football fields, an NHL rink, an NBA court, MLB ballparks, soccer pitches, curling sheets, athletics stadiums, speed skating, short track and figure skating rinks, swimming pools, water polo fields, diving wells, artistic swimming areas, and the courts and fields of tennis, volleyball (indoor, beach and sitting), badminton, pickleball, table tennis, padel, squash, racquetball, sepak takraw, teqball, roundnet, netball, lacrosse, rugby, Australian football, Gaelic games, cricket, field hockey and handball, and the budo of judo, karate, kendo, sumo and kyudo with a dojo of tatami laid as real units, the MMA cage, with each team's name and logo on its side and the score in the middle, stands and a crowd, a pageant of flags, and a 3D view. Light and dark, accessible, a compact badge for lists, a helper for React and a custom element. Zero dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sports",
|