@johnmorrisdotca/kyogijo-stadiums 0.57.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/NOTICE.md +2 -0
- package/README.md +1 -1
- package/catalogue.json +1 -1
- package/dist/areas.d.ts +8 -3
- package/dist/areas.js +14 -5
- package/dist/athletics/plan.d.ts +2 -0
- package/dist/athletics/plan.js +20 -4
- package/dist/catalogue.data.js +1 -1
- package/dist/circuits-entry.d.ts +1 -1
- package/dist/circuits-entry.js +1 -1
- package/dist/circuits.draw.js +7 -4
- package/dist/circuits.held.d.ts +1 -0
- package/dist/circuits.held.js +13 -0
- package/dist/circuits.index.d.ts +15 -0
- package/dist/circuits.index.js +28 -0
- package/dist/circuits.race.dom.d.ts +25 -5
- package/dist/circuits.race.dom.js +22 -7
- package/dist/circuits.race.draw.d.ts +1 -1
- package/dist/circuits.race.draw.js +4 -4
- package/dist/circuits.turn.d.ts +8 -0
- package/dist/circuits.turn.js +29 -21
- package/dist/circuits.types.d.ts +2 -0
- package/dist/circuits.words.d.ts +4 -0
- package/dist/circuits.words.js +6 -0
- package/dist/cityctx-entry.d.ts +1 -1
- package/dist/cityctx-entry.js +1 -1
- package/dist/cityctx.d.ts +2 -2
- package/dist/cityctx.data.d.ts +2 -1
- package/dist/cityctx.data.js +3 -2
- package/dist/cityctx.js +9 -3
- package/dist/cityctx.las-vegas.data.d.ts +3 -0
- package/dist/cityctx.las-vegas.data.js +1387 -0
- package/dist/context.d.ts +11 -1
- package/dist/context.js +18 -4
- package/dist/crowd.d.ts +18 -0
- package/dist/crowd.js +88 -18
- package/dist/district-entry.d.ts +1 -0
- package/dist/district-entry.js +2 -0
- package/dist/district.api.d.ts +9 -2
- package/dist/district.api.js +7 -1
- package/dist/district.circuit.d.ts +34 -0
- package/dist/district.circuit.js +13 -0
- package/dist/district.d.ts +27 -3
- package/dist/district.js +85 -15
- package/dist/district.race.d.ts +40 -0
- package/dist/district.race.js +60 -0
- package/dist/district.turn.js +27 -1
- package/dist/draw.js +22 -7
- package/dist/landmarks.d.ts +105 -0
- package/dist/landmarks.js +160 -0
- package/dist/surfaces/artistic.js +3 -1
- package/dist/surfaces/athletics.js +2 -0
- package/dist/surfaces/hockey.js +3 -1
- package/dist/surfaces/surface.d.ts +7 -0
- package/dist/surfaces/surface.js +9 -0
- package/dist/turn.dom.js +5 -5
- package/dist/turn.markup.js +12 -1
- package/dist/turn.style.js +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { FEET_PER_METRE } from "./geometry.js";
|
|
2
|
+
import { bar, n, plane } from "./turn.kit.js";
|
|
3
|
+
import { hullOf, inside, metresFrom, smallestBox } from "./venues.geo.js";
|
|
4
|
+
export const LANDMARK_SHAPES = ["dome", "wheel", "fountains", "named"];
|
|
5
|
+
const RAD = Math.PI / 180;
|
|
6
|
+
const rotate = ([x, y], degrees) => [x * Math.cos(degrees * RAD) - y * Math.sin(degrees * RAD), x * Math.sin(degrees * RAD) + y * Math.cos(degrees * RAD)];
|
|
7
|
+
const middleOf = (ring) => [ring.reduce((s, p) => s + p[0], 0) / ring.length, ring.reduce((s, p) => s + p[1], 0) / ring.length];
|
|
8
|
+
/** The landmarks of some areas placed in a district: turned as the plan is and kept where they stand in the frame (`frame` is the plan's box in feet). */
|
|
9
|
+
export function placeLandmarks(areas, origin, turn, frame) {
|
|
10
|
+
const placed = [];
|
|
11
|
+
for (const area of areas) {
|
|
12
|
+
const [east, north] = metresFrom(origin, [area.origin])[0];
|
|
13
|
+
for (const l of area.landmarks ?? []) {
|
|
14
|
+
const ring = l.ring.map(([e, nn]) => rotate([(e + east) * FEET_PER_METRE, -(nn + north) * FEET_PER_METRE], turn));
|
|
15
|
+
const at = middleOf(ring);
|
|
16
|
+
if (at[0] < frame.x || at[0] > frame.x + frame.w || at[1] < frame.y || at[1] > frame.y + frame.h)
|
|
17
|
+
continue;
|
|
18
|
+
// The long way of the outline, on the page: `smallestBox` reads east and north, and the page's y runs down.
|
|
19
|
+
const heading = smallestBox(ring.map(([x, y]) => [x, -y])).heading;
|
|
20
|
+
const along = [Math.sin(heading * RAD), -Math.cos(heading * RAD)];
|
|
21
|
+
const label = l.top === undefined ? at : rotate([(l.top[0] + east) * FEET_PER_METRE, -(l.top[1] + north) * FEET_PER_METRE], turn);
|
|
22
|
+
placed.push({ key: l.key, name: l.name, shape: l.shape, ring, at, label, height: l.height * FEET_PER_METRE, ...(l.width === undefined ? {} : { width: l.width * FEET_PER_METRE }), along });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return placed;
|
|
26
|
+
}
|
|
27
|
+
/** Whether a building's outline is a landmark's own (most of it inside a dome's footprint): the dome draws it, and the block is left out. */
|
|
28
|
+
export const ownedByLandmark = (landmarks, ring) => landmarks.some((l) => l.shape === "dome" && (inside(hullOf([...l.ring]), middleOf(ring)) || ring.filter((p) => inside(l.ring, p)).length * 10 >= ring.length * 7));
|
|
29
|
+
/** Whether a building stands under a named landmark (its middle inside the landmark's outline): it is raised in the 3D view whatever the count of blocks. */
|
|
30
|
+
export const underLandmark = (landmarks, ring) => landmarks.some((l) => l.shape === "named" && inside(l.ring, middleOf(ring)));
|
|
31
|
+
const num = (value) => String(Math.round(value * 10) / 10);
|
|
32
|
+
/** The jets of fountains, in feet: where each stands along the long way of the water (from its middle) and how high it reaches, as a share of the most. Ours: the published figure is the most they reach, the row is a pattern. */
|
|
33
|
+
export const FOUNTAIN_JETS = [[-0.36, 0.3], [-0.27, 0.48], [-0.18, 0.66], [-0.09, 0.84], [0, 1], [0.09, 0.84], [0.18, 0.66], [0.27, 0.48], [0.36, 0.3]];
|
|
34
|
+
/** The extent of an outline along a unit direction from its middle: the least and the most. */
|
|
35
|
+
const extent = (ring, at, along) => {
|
|
36
|
+
const t = ring.map((p) => (p[0] - at[0]) * along[0] + (p[1] - at[1]) * along[1]);
|
|
37
|
+
return [Math.min(...t), Math.max(...t)];
|
|
38
|
+
};
|
|
39
|
+
/** What the floor has of the landmarks: a dome's footprint and a wheel's line on the ground, in the tone of the tallest buildings, and the jets' feet as dots on the water. Empty for none. */
|
|
40
|
+
export function landmarksFloor(landmarks) {
|
|
41
|
+
const parts = [];
|
|
42
|
+
for (const l of landmarks) {
|
|
43
|
+
if (l.shape === "dome")
|
|
44
|
+
parts.push(`<path class="ky-dist-block ky-dist-b3 ky-dist-landmark" data-landmark="${l.key}" d="M${l.ring.map(([x, y]) => `${num(x)} ${num(y)}`).join("L")}Z"/>`);
|
|
45
|
+
if (l.shape === "wheel") {
|
|
46
|
+
const r = (l.width ?? 0) / 2;
|
|
47
|
+
parts.push(`<path class="ky-dist-ink ky-dist-landmark" data-landmark="${l.key}" stroke-width="${num(Math.max(r * 0.06, 3))}" d="M${num(l.at[0] - l.along[0] * r)} ${num(l.at[1] - l.along[1] * r)}L${num(l.at[0] + l.along[0] * r)} ${num(l.at[1] + l.along[1] * r)}"/>`);
|
|
48
|
+
}
|
|
49
|
+
if (l.shape === "fountains") {
|
|
50
|
+
// The water the jets stand in, as the land cover draws water (an area's lake mapped as the fountains' own outline is not in its land cover).
|
|
51
|
+
parts.push(`<path class="ky-dist-land ky-dist-water" data-landmark="${l.key}" d="M${l.ring.map(([x, y]) => `${num(x)} ${num(y)}`).join("L")}Z"/>`);
|
|
52
|
+
const [t0, t1] = extent(l.ring, l.at, l.along);
|
|
53
|
+
const long = t1 - t0;
|
|
54
|
+
const d = FOUNTAIN_JETS.map(([s]) => `M${num(l.at[0] + l.along[0] * (t0 + long * (0.5 + s)))} ${num(l.at[1] + l.along[1] * (t0 + long * (0.5 + s)))}h0`).join("");
|
|
55
|
+
parts.push(`<path class="ky-dist-jet ky-dist-landmark" data-landmark="${l.key}" stroke-width="${num(Math.max(long * 0.02, 6))}" d="${d}"/>`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return parts.length === 0 ? "" : `<g class="ky-dist-landmarks" aria-hidden="true">${parts.join("")}</g>`;
|
|
59
|
+
}
|
|
60
|
+
/** A plane through three or more points of one flat polygon in 3D, painted with `cls`, and `cull` set so that its face away from `inward` is the one drawn (it is hidden from inside). */
|
|
61
|
+
function facet(id, points, cls, inward) {
|
|
62
|
+
const sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
63
|
+
const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
64
|
+
const cross = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
65
|
+
const unit = (a) => {
|
|
66
|
+
const l = Math.hypot(...a);
|
|
67
|
+
return l < 1e-9 ? [0, 0, 0] : [a[0] / l, a[1] / l, a[2] / l];
|
|
68
|
+
};
|
|
69
|
+
const [p0, p1] = points;
|
|
70
|
+
const u = unit(sub(p1, p0));
|
|
71
|
+
const normal = unit(cross(sub(p1, p0), sub(points[points.length - 1], p0)));
|
|
72
|
+
if (Math.hypot(...normal) < 0.5)
|
|
73
|
+
return undefined;
|
|
74
|
+
const v = unit(cross(normal, u));
|
|
75
|
+
const local = points.map((p) => [dot(sub(p, p0), u), dot(sub(p, p0), v)]);
|
|
76
|
+
const [a0, a1] = [Math.min(...local.map((p) => p[0])), Math.max(...local.map((p) => p[0]))];
|
|
77
|
+
const [b0, b1] = [Math.min(...local.map((p) => p[1])), Math.max(...local.map((p) => p[1]))];
|
|
78
|
+
const o = [p0[0] + u[0] * a0 + v[0] * b0, p0[1] + u[1] * a0 + v[1] * b0, p0[2] + u[2] * a0 + v[2] * b0];
|
|
79
|
+
const svg = `<polygon class="${cls}" points="${local.map(([a, b]) => `${n(a - a0)},${n(b - b0)}`).join(" ")}"/>`;
|
|
80
|
+
const out = sub(points[0], inward);
|
|
81
|
+
// `u` x `v` is the normal; the face that is drawn is the one the normal points out of.
|
|
82
|
+
return { ...plane(id, "block", o, [u[0] * (a1 - a0), u[1] * (a1 - a0), u[2] * (a1 - a0)], [v[0] * (b1 - b0), v[1] * (b1 - b0), v[2] * (b1 - b0)], svg), cull: dot(normal, out) >= 0 ? 1 : -1 };
|
|
83
|
+
}
|
|
84
|
+
/** The tone of a facet by the way it faces the light (from up the page, to the left, and above): three tones, as a building's walls have. */
|
|
85
|
+
const toneOf = (normal) => {
|
|
86
|
+
const lit = normal[0] * -0.4 + normal[1] * -0.5 + normal[2] * 0.75;
|
|
87
|
+
return lit > 0.45 ? "u-block-a" : lit > 0 ? "u-block-b" : "u-block-c";
|
|
88
|
+
};
|
|
89
|
+
/** How finely a dome is cut: sides round it and bands up it. Fewer in the lower tiers. */
|
|
90
|
+
export const DOME_CUT = Object.freeze({ high: { sides: 16, bands: 6 }, simple: { sides: 10, bands: 4 } });
|
|
91
|
+
/** The planes of a dome: a sphere `width` across whose top is `height` over the ground, cut by the ground, as bands of flat facets, each lit by the way it faces. */
|
|
92
|
+
export function domePlanes(prefix, at, height, width, cut) {
|
|
93
|
+
const r = width / 2;
|
|
94
|
+
const zc = height - r;
|
|
95
|
+
const low = Math.asin(Math.max(-1, Math.min(1, -zc / r)));
|
|
96
|
+
const centre = [at[0], at[1], zc];
|
|
97
|
+
const point = (side, band) => {
|
|
98
|
+
const lat = low + ((Math.PI / 2 - low) * band) / cut.bands;
|
|
99
|
+
const lon = (2 * Math.PI * side) / cut.sides;
|
|
100
|
+
return [at[0] + r * Math.cos(lat) * Math.cos(lon), at[1] + r * Math.cos(lat) * Math.sin(lon), zc + r * Math.sin(lat)];
|
|
101
|
+
};
|
|
102
|
+
const planes = [];
|
|
103
|
+
for (let band = 0; band < cut.bands; band += 1) {
|
|
104
|
+
for (let side = 0; side < cut.sides; side += 1) {
|
|
105
|
+
const top = band + 1 === cut.bands;
|
|
106
|
+
const pts = top ? [point(side, band), point(side + 1, band), point(side, band + 1)] : [point(side, band), point(side + 1, band), point(side + 1, band + 1), point(side, band + 1)];
|
|
107
|
+
const mid = [pts.reduce((s, p) => s + p[0], 0) / pts.length - centre[0], pts.reduce((s, p) => s + p[1], 0) / pts.length - centre[1], pts.reduce((s, p) => s + p[2], 0) / pts.length - centre[2]];
|
|
108
|
+
const l = Math.hypot(...mid);
|
|
109
|
+
const f = facet(`${prefix}d${band}-${side}`, pts, `u-block ${toneOf([mid[0] / l, mid[1] / l, mid[2] / l])}`, centre);
|
|
110
|
+
if (f !== undefined)
|
|
111
|
+
planes.push(f);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return planes;
|
|
115
|
+
}
|
|
116
|
+
/** How a wheel is drawn: segments round its rim and spokes to its hub. */
|
|
117
|
+
export const WHEEL_CUT = Object.freeze({ high: { rim: 24, spokes: 12 }, simple: { rim: 12, spokes: 6 } });
|
|
118
|
+
/** The planes of a big wheel standing upright along `along`: its rim, its spokes and the two legs from its hub to the ground, each a thin bar seen from every side. */
|
|
119
|
+
export function wheelPlanes(prefix, at, along, height, width, cut) {
|
|
120
|
+
const r = width / 2;
|
|
121
|
+
const hub = [at[0], at[1], height - r];
|
|
122
|
+
const on = (angle, radius = r) => [at[0] + along[0] * radius * Math.cos(angle), at[1] + along[1] * radius * Math.cos(angle), hub[2] + radius * Math.sin(angle)];
|
|
123
|
+
const thick = Math.max(width * 0.025, 2);
|
|
124
|
+
const planes = [];
|
|
125
|
+
for (let i = 0; i < cut.rim; i += 1)
|
|
126
|
+
planes.push(...bar(`${prefix}rim${i}`, "pole", on((2 * Math.PI * i) / cut.rim), on((2 * Math.PI * (i + 1)) / cut.rim), thick, "u-leg"));
|
|
127
|
+
for (let i = 0; i < cut.spokes; i += 1)
|
|
128
|
+
planes.push(...bar(`${prefix}spoke${i}`, "pole", hub, on((2 * Math.PI * i) / cut.spokes), thick * 0.35, "u-leg"));
|
|
129
|
+
// Two legs, leaning out across the wheel's plane to the ground.
|
|
130
|
+
const across = [-along[1], along[0]];
|
|
131
|
+
for (const [i, side] of [[0, 1], [1, -1]])
|
|
132
|
+
planes.push(...bar(`${prefix}leg${i}`, "pole", hub, [at[0] + across[0] * side * r * 0.3, at[1] + across[1] * side * r * 0.3, 0], thick * 1.4, "u-leg"));
|
|
133
|
+
return planes;
|
|
134
|
+
}
|
|
135
|
+
/** The planes of fountains: a row of jets along the long way of the water, each a thin upright strip reaching to its share of `height`. */
|
|
136
|
+
export function fountainPlanes(prefix, l) {
|
|
137
|
+
const [t0, t1] = extent(l.ring, l.at, l.along);
|
|
138
|
+
const long = t1 - t0;
|
|
139
|
+
const planes = [];
|
|
140
|
+
FOUNTAIN_JETS.forEach(([s, share], i) => {
|
|
141
|
+
const t = t0 + long * (0.5 + s);
|
|
142
|
+
const [x, y] = [l.at[0] + l.along[0] * t, l.at[1] + l.along[1] * t];
|
|
143
|
+
planes.push(...bar(`${prefix}jet${i}`, "pole", [x, y, 0], [x, y, l.height * share], Math.max(long * 0.012, 3), "u-jet"));
|
|
144
|
+
});
|
|
145
|
+
return planes;
|
|
146
|
+
}
|
|
147
|
+
/** The 3D view's landmarks: each one's shape standing where it is, cut more coarsely in a simple tier. A named landmark has no shape of its own: its buildings are the area's. */
|
|
148
|
+
export function landmarkUprights(landmarks, simple) {
|
|
149
|
+
const items = [];
|
|
150
|
+
for (const l of landmarks) {
|
|
151
|
+
const prefix = `lm-${l.key}-`;
|
|
152
|
+
if (l.shape === "dome" && l.width !== undefined)
|
|
153
|
+
items.push({ id: `lm-${l.key}`, kind: "blocks", at: l.at, height: l.height, planes: domePlanes(prefix, l.at, l.height, l.width, simple ? DOME_CUT.simple : DOME_CUT.high) });
|
|
154
|
+
if (l.shape === "wheel" && l.width !== undefined)
|
|
155
|
+
items.push({ id: `lm-${l.key}`, kind: "flags", at: l.at, height: l.height, planes: wheelPlanes(prefix, l.at, l.along, l.height, l.width, simple ? WHEEL_CUT.simple : WHEEL_CUT.high) });
|
|
156
|
+
if (l.shape === "fountains" && !simple)
|
|
157
|
+
items.push({ id: `lm-${l.key}`, kind: "flags", at: l.at, height: l.height, planes: fountainPlanes(prefix, l) });
|
|
158
|
+
}
|
|
159
|
+
return items;
|
|
160
|
+
}
|
|
@@ -59,7 +59,9 @@ export function artistic(o, plan, ctx = {}) {
|
|
|
59
59
|
}
|
|
60
60
|
const top = -jy + 0.4;
|
|
61
61
|
const bottom = sy + start.depth + 0.4 - W;
|
|
62
|
-
|
|
62
|
+
// The deck is narrower along the sides than at the ends; a margin that is asked for (the ground the stands of a crowd stand on) is the same all round.
|
|
63
|
+
const across = o.margin === undefined ? m * 0.4 : m;
|
|
64
|
+
const view0 = { x: -m, y: -feet(top) - across, w: feet(L) + 2 * m, h: feet(W) + feet(top + bottom) + 2 * across };
|
|
63
65
|
base = (ctx.bare === true ? "" : deckMarkup(o, ids, plan.deck, view0.x, view0.y, view0.w, view0.h)) + base + deck;
|
|
64
66
|
const Lf = feet(L);
|
|
65
67
|
const Wf = feet(W);
|
|
@@ -96,6 +96,8 @@ export function athleticsSurface(o, plan, embedded, margin) {
|
|
|
96
96
|
onColour: false,
|
|
97
97
|
};
|
|
98
98
|
surface.box = { x: 0, y: 0, w: W, h: H };
|
|
99
|
+
if (plan.outline !== undefined && sv === undefined)
|
|
100
|
+
surface.floor = plan.outline.map(([x, y]) => [(x - ox) * K, (y - oy) * K]);
|
|
99
101
|
surface.lineRef = { width: thick, feet: K };
|
|
100
102
|
const ranges = {};
|
|
101
103
|
for (const [name, r] of Object.entries(plan.ranges))
|
package/dist/surfaces/hockey.js
CHANGED
|
@@ -2,7 +2,7 @@ import { el, num } from "../format.js";
|
|
|
2
2
|
import { rulesFor } from "../eras.js";
|
|
3
3
|
import { NHL } from "../geometry.js";
|
|
4
4
|
import { NHL_RINK } from "../leagues/hockey.js";
|
|
5
|
-
import { atEnds, edges, ft, quarterTurn, roundedRect } from "./surface.js";
|
|
5
|
+
import { atEnds, edges, ft, quarterTurn, roundedOutline, roundedRect } from "./surface.js";
|
|
6
6
|
/** Where the boards' curve is at a distance x from the end: the y at which it meets a line across the rink. The corner's radius is the NHL's unless given. */
|
|
7
7
|
export function boardsY(x, radius = NHL.cornerRadius) {
|
|
8
8
|
const r = radius;
|
|
@@ -128,6 +128,8 @@ export function hockey(o, era, rink = NHL_RINK) {
|
|
|
128
128
|
};
|
|
129
129
|
surface.lineRef = { width: thin, feet: 1 };
|
|
130
130
|
surface.box = { x: 0, y: 0, w: L, h: W };
|
|
131
|
+
// The floor the stands of a crowd clear: the boards round the rounded corners, not the apron's square corners.
|
|
132
|
+
surface.floor = roundedOutline(-bw, -bw, L + 2 * bw, W + 2 * bw, r + bw);
|
|
131
133
|
surface.centreRadius = rink.circleRadius;
|
|
132
134
|
surface.anchors = { kind: "hockey", length: L, width: W, blueLine, goalLine: g, cornerRadius: r, net: net_ };
|
|
133
135
|
// The zones of the ice: from the end boards to a blue line, and between the blue lines.
|
|
@@ -153,6 +153,11 @@ export interface Surface {
|
|
|
153
153
|
width: number;
|
|
154
154
|
feet: number;
|
|
155
155
|
};
|
|
156
|
+
/**
|
|
157
|
+
* For a surface whose floor is not the rectangle of its view (an athletics track, its sprint straight and runways): the outline of everything it draws round its playing area, as points in the wide frame
|
|
158
|
+
* in feet. The stands of a crowd clear it.
|
|
159
|
+
*/
|
|
160
|
+
floor?: (readonly [number, number])[];
|
|
156
161
|
/** For a surface with a crowd: the margin it was drawn with, which holds the stands. */
|
|
157
162
|
crowdMargin?: number;
|
|
158
163
|
/** A real stadium of more than one configuration laid out in the venue's own frame: where the middle of its field is in the surface's frame (the surface is shifted to lie there), and the ground, building, stands and crowd under it, in the venue's frame. */
|
|
@@ -254,6 +259,8 @@ export declare function quarterTurn(view: {
|
|
|
254
259
|
};
|
|
255
260
|
/** A box from its edges: the left, the top, the right and the bottom. */
|
|
256
261
|
export declare const edges: (x0: number, y0: number, x1: number, y1: number) => Box;
|
|
262
|
+
/** A rounded rectangle's outline as points (each corner in 12 steps), for a surface's `floor`. */
|
|
263
|
+
export declare function roundedOutline(x: number, y: number, w: number, h: number, r: number): [number, number][];
|
|
257
264
|
/** A range that has an end: its box at the left end and at the right, made by a function of the end. */
|
|
258
265
|
export declare const atEnds: (box: (end: "left" | "right") => Box) => {
|
|
259
266
|
left: Box;
|
package/dist/surfaces/surface.js
CHANGED
|
@@ -45,5 +45,14 @@ export function quarterTurn(view) {
|
|
|
45
45
|
}
|
|
46
46
|
/** A box from its edges: the left, the top, the right and the bottom. */
|
|
47
47
|
export const edges = (x0, y0, x1, y1) => ({ x: Math.min(x0, x1), y: Math.min(y0, y1), w: Math.abs(x1 - x0), h: Math.abs(y1 - y0) });
|
|
48
|
+
/** A rounded rectangle's outline as points (each corner in 12 steps), for a surface's `floor`. */
|
|
49
|
+
export function roundedOutline(x, y, w, h, r) {
|
|
50
|
+
const rr = Math.max(0, Math.min(r, w / 2, h / 2));
|
|
51
|
+
const out = [];
|
|
52
|
+
for (const [cx, cy, a0] of [[x + w - rr, y + rr, -90], [x + w - rr, y + h - rr, 0], [x + rr, y + h - rr, 90], [x + rr, y + rr, 180]])
|
|
53
|
+
for (let i = 0; i <= 12; i += 1)
|
|
54
|
+
out.push([cx + rr * Math.cos(((a0 + i * 7.5) * Math.PI) / 180), cy + rr * Math.sin(((a0 + i * 7.5) * Math.PI) / 180)]);
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
48
57
|
/** A range that has an end: its box at the left end and at the right, made by a function of the end. */
|
|
49
58
|
export const atEnds = (box) => ({ left: box("left"), right: box("right") });
|
package/dist/turn.dom.js
CHANGED
|
@@ -212,6 +212,8 @@ export function mountTurn(host, options, settings, build, replayable = false, le
|
|
|
212
212
|
root.dataset.quality = quality;
|
|
213
213
|
root.dataset.tier = tier;
|
|
214
214
|
root.dataset.share = String(ceiling);
|
|
215
|
+
// ---- One frame request a frame: the auto-turn, a replay or a race on the view, and the quality's timing of the frames ask through this, and share one animation frame when they run together. ----
|
|
216
|
+
const frames = frameBatch(win);
|
|
215
217
|
// ---- Quality: in `auto`, the frames the view makes while it turns decide the tier. ----
|
|
216
218
|
let governor = quality === "auto" && win !== null && model.tiered === true ? createGovernor(tier, settings.qualityTuning) : undefined;
|
|
217
219
|
let meterFrame;
|
|
@@ -230,7 +232,7 @@ export function mountTurn(host, options, settings, build, replayable = false, le
|
|
|
230
232
|
};
|
|
231
233
|
const stopMeter = () => {
|
|
232
234
|
if (meterFrame !== undefined)
|
|
233
|
-
|
|
235
|
+
frames.cancel(meterFrame);
|
|
234
236
|
meterFrame = undefined;
|
|
235
237
|
lastStamp = undefined;
|
|
236
238
|
};
|
|
@@ -252,7 +254,7 @@ export function mountTurn(host, options, settings, build, replayable = false, le
|
|
|
252
254
|
if (next !== undefined)
|
|
253
255
|
retier(next);
|
|
254
256
|
if (meterFrame === undefined)
|
|
255
|
-
meterFrame =
|
|
257
|
+
meterFrame = frames.request(meterTick);
|
|
256
258
|
};
|
|
257
259
|
/** The view was painted: the frames are timed from now until it is still. */
|
|
258
260
|
const touched = () => {
|
|
@@ -260,7 +262,7 @@ export function mountTurn(host, options, settings, build, replayable = false, le
|
|
|
260
262
|
return;
|
|
261
263
|
lastPaint = win.performance.now();
|
|
262
264
|
if (meterFrame === undefined)
|
|
263
|
-
meterFrame =
|
|
265
|
+
meterFrame = frames.request(meterTick);
|
|
264
266
|
};
|
|
265
267
|
/** Draw the world again, from the model of the tier and the options now: the camera, the auto-turn and the pointer are not touched (the elements are the only thing replaced). */
|
|
266
268
|
const renderWorld = () => {
|
|
@@ -447,8 +449,6 @@ export function mountTurn(host, options, settings, build, replayable = false, le
|
|
|
447
449
|
go(opening, 560, "none");
|
|
448
450
|
say(turnWords(model).reset);
|
|
449
451
|
};
|
|
450
|
-
// ---- One frame request a frame: the auto-turn and a replay on the view ask through this, and share one animation frame when both run. ----
|
|
451
|
-
const frames = frameBatch(win);
|
|
452
452
|
const requestFrame = (callback) => frames.request(callback);
|
|
453
453
|
const cancelFrame = (id) => frames.cancel(id);
|
|
454
454
|
// ---- The auto-turn: a trophy case's slow turn. It runs only when asked, holds while the pointer holds the view, and does nothing while the page is hidden. ----
|
package/dist/turn.markup.js
CHANGED
|
@@ -56,6 +56,8 @@ export function groupRanks(groups, yaw) {
|
|
|
56
56
|
return rank;
|
|
57
57
|
}
|
|
58
58
|
const RAD = Math.PI / 180;
|
|
59
|
+
/** The things of a venue's bowl: they stand on the ground round the surface, never under a raised floor. */
|
|
60
|
+
const BOWL = new Set(["stands", "wall-seats", "gallery", "roof", "ribbon-boards", "hung-board"]);
|
|
59
61
|
const prepared = new WeakMap();
|
|
60
62
|
function prepare(model) {
|
|
61
63
|
const known = prepared.get(model.planes);
|
|
@@ -79,7 +81,8 @@ function prepare(model) {
|
|
|
79
81
|
const zs = [p.o[2], p.o[2] + p.u[2], p.o[2] + p.v[2], p.o[2] + p.u[2] + p.v[2]];
|
|
80
82
|
mid.push([(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, Math.min(...zs) * s, Math.max(...zs) * s]);
|
|
81
83
|
}
|
|
82
|
-
const
|
|
84
|
+
const ofBowl = new Set(model.uprights.items.filter((item) => BOWL.has(item.kind)).flatMap((item) => item.planes));
|
|
85
|
+
const made = { planes: model.planes, scale: s, centre, base, mid, bowl: model.planes.map((p) => ofBowl.has(p)) };
|
|
83
86
|
prepared.set(model.planes, made);
|
|
84
87
|
return made;
|
|
85
88
|
}
|
|
@@ -159,6 +162,14 @@ export function layoutOf(model, view, width, height, lens, cull = false, raster
|
|
|
159
162
|
order.length = 0;
|
|
160
163
|
for (let i = 0, j = 0; i < own.length || j < apart.length;)
|
|
161
164
|
order.push(j >= apart.length || (i < own.length && own[i].depth <= apart[j].depth) ? own[i++] : apart[j++]);
|
|
165
|
+
// A raised floor (a table's top) carries the ground the bowl stands on, so it is sorted by its middle among the surface's own things only: the parts of the bowl that are farther than its middle
|
|
166
|
+
// come after it, still in their own order, or the floor would be drawn over the far stands.
|
|
167
|
+
if (raised) {
|
|
168
|
+
const at = order.findIndex((one) => one.i === 0);
|
|
169
|
+
const far = order.slice(0, at).filter((one) => known.bowl[one.i - 1]);
|
|
170
|
+
if (far.length > 0)
|
|
171
|
+
order.splice(0, at + 1, ...order.slice(0, at).filter((one) => !known.bowl[one.i - 1]), order[at], ...far);
|
|
172
|
+
}
|
|
162
173
|
const stack = new Array(count + 1).fill(0);
|
|
163
174
|
stack[0] = 1;
|
|
164
175
|
order.forEach((one, rank) => {
|
package/dist/turn.style.js
CHANGED
|
@@ -28,6 +28,7 @@ export const KYOGIJO_TURN_STYLE = `
|
|
|
28
28
|
.ky-turn .u-orange{fill:var(--ky-up-orange)}
|
|
29
29
|
.ky-turn .u-wood{fill:var(--ky-up-wood)}
|
|
30
30
|
.ky-turn .u-leg{fill:var(--ky-up-leg)}
|
|
31
|
+
.ky-turn .u-jet{fill:var(--ky-up-jet,rgba(222,240,252,.82))}
|
|
31
32
|
.ky-turn .u-pad{fill:var(--ky-up-pad)}
|
|
32
33
|
.ky-turn .u-table{fill:var(--ky-up-table)}
|
|
33
34
|
.ky-turn .u-foot{fill:var(--ky-up-foot)}
|
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.
|
|
2
|
+
export declare const VERSION = "0.59.0";
|
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.
|
|
2
|
+
export const VERSION = "0.59.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnmorrisdotca/kyogijo-stadiums",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
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",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"./dist/grounds.js",
|
|
76
76
|
"./dist/venues.all.js",
|
|
77
77
|
"./dist/walk.full.js",
|
|
78
|
+
"./dist/circuits.held.js",
|
|
78
79
|
"./dist/strings.js",
|
|
79
80
|
"./dist/strings.en.js",
|
|
80
81
|
"./dist/strings.ja.js"
|