@johnmorrisdotca/kyogijo-stadiums 0.37.0 → 0.38.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 +10 -0
- package/README.md +1 -1
- package/dist/crowd.d.ts +3 -1
- package/dist/crowd.draw.js +25 -7
- package/dist/crowd.js +1 -0
- package/dist/district.turn.js +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/multi.draw.js +1 -1
- package/dist/multi.turn.js +2 -1
- package/dist/multi.types.d.ts +2 -1
- package/dist/turn-entry.d.ts +1 -1
- package/dist/turn.dom.js +5 -0
- package/dist/turn.markup.js +1 -1
- package/dist/turn.model.d.ts +9 -1
- package/dist/turn.model.js +7 -0
- package/dist/turn.style.js +10 -0
- package/dist/types.d.ts +10 -0
- package/dist/venue.boards.d.ts +6 -2
- package/dist/venue.boards.js +12 -6
- package/dist/venue.d.ts +6 -1
- package/dist/venue.js +10 -3
- package/dist/venue.lighting.d.ts +18 -0
- package/dist/venue.lighting.js +26 -0
- 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,16 @@ All notable changes to this project are written here. The format follows
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.38.0] - 2026-10-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Night lighting** (`lighting: "night"`, [night lighting](./docs/venues.md#night-lighting), `src/venue.lighting.ts`): a venue lit as at night. The house goes down (a veil over the seats, the venue's colours set dark in the light theme and in the dark one), the playing surface stays lit with a pool of light round it that is clear over the markings, small lights show along the aisles and stairs, the exits (every fourth aisle, the vomitories) are green, the ribbon boards and the scoreboard of [the LED boards](./docs/venues.md#led-boards) glow with a halo under every panel, and the windows, the coping and the lanes of the concourse are softly lit. `venue: { lighting }` in the 3D view, `crowd: { lighting }` in the plan (and read by the 3D view where `venue.lighting` says nothing), `stadium: { lighting }` round a tournament hall; `"night"`, `true` and `{ preset: "night" }` are the same and `false` is none. It is the base later lightings are added to: the option is a preset name, and `TurnModel.lighting` and the view's `data-lighting` say which. Off by default, and a drawing without it is exactly what it was. Nothing is worked out again as the view turns: the lights are custom properties (`--ky-night-aisle`, `--ky-night-exit`, `--ky-night-rim`) and a few static shapes, one added plane (`night-pool`) to a venue; `turn.update(options, { venue: { lighting } })` changes it while the view turns. The lights are at least seven to one against the seats in both themes (a test holds it). New exports: `LightingOptions` and `LightingPreset`. About 3 KB of source in the one new module.
|
|
15
|
+
- **A Night switch on the demo** (kept in the address as `night=1`): a button in the bar over the venues page's view and the home page's featured arena, which lights the plan and the 3D view and, in 3D, changes it without stopping or sending back the turn; a checkbox on the stadiums, tournament layouts and district pages.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **The plan's ribbon boards are outside the crowd's group** (`<g class="ky-cr-ribbon">` follows `<g class="ky-crowd">` and no longer sits inside it), so that the crowd's dimming and the night veil do not reach them.
|
|
10
20
|
## [0.37.0] - 2026-10-20
|
|
11
21
|
|
|
12
22
|
### Added
|
package/README.md
CHANGED
|
@@ -581,7 +581,7 @@ console.log(layout.needs.length, layout.needs.width);
|
|
|
581
581
|
|
|
582
582
|
### The real venues
|
|
583
583
|
|
|
584
|
-
`crowd: { real: true }` draws an NHL arena in its building: the outline from OpenStreetMap, the rink lying along the building where it says so, and a plausible bowl of the seats, tiers and roof the sources give. All thirty-two arenas of the league, division by division ([the real venues](./docs/venues.md)). `boards: true` adds LED ribbon boards and a hung scoreboard, generic content ([LED boards](./docs/venues.md#led-boards)). In 3D the demo's [venues page](https://johnmorrisdotca.github.io/kyogijo-stadiums/venues.html) turns while the pointer is over it, with the stands, walls, roof, LED boards, building, markings and labels switched on the view while it turns.
|
|
584
|
+
`crowd: { real: true }` draws an NHL arena in its building: the outline from OpenStreetMap, the rink lying along the building where it says so, and a plausible bowl of the seats, tiers and roof the sources give. All thirty-two arenas of the league, division by division ([the real venues](./docs/venues.md)). `boards: true` adds LED ribbon boards and a hung scoreboard, generic content ([LED boards](./docs/venues.md#led-boards)). In 3D the demo's [venues page](https://johnmorrisdotca.github.io/kyogijo-stadiums/venues.html) turns while the pointer is over it, with the stands, walls, roof, LED boards, night, building, markings and labels switched on the view while it turns.
|
|
585
585
|
|
|
586
586
|
```ts
|
|
587
587
|
import { drawKyogijo } from "@johnmorrisdotca/kyogijo-stadiums";
|
package/dist/crowd.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CrowdShape, LedBoardOptions, Side, Sport, SurfaceOptions } from "./types.ts";
|
|
1
|
+
import type { CrowdShape, LedBoardOptions, LightingOptions, Side, Sport, SurfaceOptions } from "./types.ts";
|
|
2
2
|
export type Pt = [number, number];
|
|
3
3
|
export type Box = {
|
|
4
4
|
x: number;
|
|
@@ -190,6 +190,8 @@ export interface Crowd {
|
|
|
190
190
|
detail: "auto" | "fine" | "plain";
|
|
191
191
|
/** The LED boards as asked (`resolveBoards` reads them), or `undefined` for none. */
|
|
192
192
|
boards: boolean | LedBoardOptions | undefined;
|
|
193
|
+
/** The lighting as asked (`resolveLighting` reads it), or `undefined`. */
|
|
194
|
+
lighting: LightingOptions | undefined;
|
|
193
195
|
}
|
|
194
196
|
/** The crowd the options ask for, or `undefined` for none. */
|
|
195
197
|
export declare function resolveCrowd(value: SurfaceOptions["crowd"]): Crowd | undefined;
|
package/dist/crowd.draw.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// the same crowd. Written for this package; no photograph or other drawing was copied.
|
|
5
5
|
import { el, num } from "./format.js";
|
|
6
6
|
import { hangsOver, planRibbons, planScoreboard, resolveBoards } from "./venue.boards.js";
|
|
7
|
+
import { POOL, resolveLighting } from "./venue.lighting.js";
|
|
7
8
|
import { NORTH_AT, generator, outlinePath } from "./crowd.js";
|
|
8
9
|
/** The width and the height of a tile of the crowd, in feet, and the seats' pitch. */
|
|
9
10
|
const TILE = 14.4;
|
|
@@ -68,11 +69,11 @@ export function patterns(id, crowd, density, tifo, neutral = false, rows = false
|
|
|
68
69
|
}
|
|
69
70
|
const path = (ring) => [ring.region, ...(ring.more ?? [])].map((o) => outlinePath(o)).join("") + (ring.hole === null ? "" : outlinePath(ring.hole, true));
|
|
70
71
|
/** The aisles of a band as one path: the lines the stands data gives, every fourth a vomitory, drawn wider. */
|
|
71
|
-
function aisles(ring) {
|
|
72
|
+
function aisles(ring, night = false) {
|
|
72
73
|
const line = ([[x0, y0], [x1, y1]]) => `M${Math.round(x0)} ${Math.round(y0)}L${Math.round(x1)} ${Math.round(y1)}`;
|
|
73
74
|
const plain = ring.aisles.filter((_, i) => i % 4 !== 0).map(line).join("");
|
|
74
75
|
const vomitories = ring.aisles.filter((_, i) => i % 4 === 0).map(line).join("");
|
|
75
|
-
return (plain === "" ? "" : el("path", { class: "ky-cr-aisle", d: plain, "stroke-width": 1.2 })) + (vomitories === "" ? "" : el("path", { class: "ky-cr-aisle", d: vomitories, "stroke-width": 2.6 }));
|
|
76
|
+
return (plain === "" ? "" : el("path", { class: "ky-cr-aisle", d: plain, "stroke-width": 1.2 })) + (vomitories === "" ? "" : el("path", { class: night ? "ky-cr-aisle ky-cr-vom" : "ky-cr-aisle", d: vomitories, "stroke-width": 2.6 }));
|
|
76
77
|
}
|
|
77
78
|
/** The pitch of the row lines, in feet: every second row of seats. */
|
|
78
79
|
const ROW = 5;
|
|
@@ -194,9 +195,14 @@ export function drawCrowd(stands, o) {
|
|
|
194
195
|
}
|
|
195
196
|
for (const s of stands.sectors)
|
|
196
197
|
body += sector(s, id, fine, density);
|
|
198
|
+
// The house is down: a dark veil over the seats, under the aisles' lights.
|
|
199
|
+
const night = resolveLighting(crowd.lighting) === "night" && stands.gallery !== true;
|
|
200
|
+
if (night)
|
|
201
|
+
for (const ring of stands.rings)
|
|
202
|
+
body += el("path", { class: "ky-cr-veil", d: path(ring) });
|
|
197
203
|
if (fine)
|
|
198
204
|
for (const ring of stands.rings)
|
|
199
|
-
body += stands.gallery === true ? "" : rows(ring) + aisles(ring);
|
|
205
|
+
body += stands.gallery === true ? "" : rows(ring) + aisles(ring, night);
|
|
200
206
|
// Depth: the front wall of a tier throws a shade on its first rows (and the ground in front of it), the roof's edge on the last rows of the upper tier.
|
|
201
207
|
// A gallery's railing: a line along its front edge, over the seats.
|
|
202
208
|
if (stands.gallery === true)
|
|
@@ -212,18 +218,25 @@ export function drawCrowd(stands, o) {
|
|
|
212
218
|
}
|
|
213
219
|
}
|
|
214
220
|
// The LED boards: a lit line along the front of the upper tiers, and the scoreboard over the middle of the surface (over it, in `over`).
|
|
215
|
-
const boards = resolveBoards(crowd.boards, { home: crowd.home, away: crowd.away });
|
|
216
|
-
|
|
217
|
-
body += planRibbons(stands, boards);
|
|
221
|
+
const boards = resolveBoards(crowd.boards, { home: crowd.home, away: crowd.away }, night);
|
|
222
|
+
const lit = boards?.ribbon === true ? planRibbons(stands, boards) : "";
|
|
218
223
|
if (boards?.scoreboard === true && hangsOver(stands.sport))
|
|
219
224
|
over += planScoreboard(stands.box, boards);
|
|
225
|
+
// The surface is lit: a pool of light round it, clear over the markings.
|
|
226
|
+
let pool = "";
|
|
227
|
+
if (night) {
|
|
228
|
+
const b = stands.box;
|
|
229
|
+
const [w, h] = [b.w + 2 * POOL.reach, b.h + 2 * POOL.reach];
|
|
230
|
+
pool = el("radialGradient", { id: `${id}-pool`, cx: 0.5, cy: 0.5, r: 0.5 }, [[0, 0], [POOL.edge - 0.12, 0], [POOL.edge, POOL.strength], [1, 0]].map(([offset, opacity]) => el("stop", { offset, "stop-color": "#fff4dc", "stop-opacity": opacity })).join(""));
|
|
231
|
+
over += el("rect", { class: "ky-cr-pool", x: n1(b.x - POOL.reach), y: n1(b.y - POOL.reach), width: n1(w), height: n1(h), fill: `url(#${id}-pool)` });
|
|
232
|
+
}
|
|
220
233
|
for (const [x0, y0, x1, y1] of stands.covers)
|
|
221
234
|
body += el("rect", { class: o.ground, x: n1(x0), y: n1(y0), width: n1(x1 - x0), height: n1(y1 - y0) });
|
|
222
235
|
for (const g of stands.gaps) {
|
|
223
236
|
const [x0, y0, x1, y1] = g.box;
|
|
224
237
|
body += el("rect", { class: `ky-cr-${g.kind}`, x: n1(x0), y: n1(y0), width: n1(x1 - x0), height: n1(y1 - y0), rx: g.kind === "bench" ? 1 : undefined });
|
|
225
238
|
}
|
|
226
|
-
return { defs: (fine && (stands.gallery !== true || density > 0) ? patterns(id, crowd, density, tifo.home || tifo.away ? tifo : undefined, stands.gallery === true || stands.real !== undefined) : ""), markup: el("g", { class: "ky-crowd" }, body), over };
|
|
239
|
+
return { defs: (fine && (stands.gallery !== true || density > 0) ? patterns(id, crowd, density, tifo.home || tifo.away ? tifo : undefined, stands.gallery === true || stands.real !== undefined) : "") + pool, markup: el("g", { class: night ? "ky-crowd ky-night" : "ky-crowd" }, body) + lit, over };
|
|
227
240
|
}
|
|
228
241
|
/** A block of visitors or a card display, a polygon over the band it is in. */
|
|
229
242
|
function sector(s, id, fine, density) {
|
|
@@ -263,6 +276,11 @@ export const KYOGIJO_CROWD_STYLE = `
|
|
|
263
276
|
.kyogijo .ky-cr-plaza{fill:var(--ky-apron);stroke:var(--ky-apron);stroke-width:8;stroke-linejoin:round}
|
|
264
277
|
.kyogijo .ky-cr-ez{fill:var(--ky-line);fill-opacity:.16}
|
|
265
278
|
.kyogijo .ky-cr-yard{stroke:var(--ky-line);stroke-opacity:.5;fill:none}
|
|
279
|
+
.kyogijo .ky-night{--ky-cr-bed:#11151a;--ky-cr-empty:#2a313a;--ky-cr-gap:#020304;--ky-cr-conc:#171c22;--ky-cr-rim:#07090c}
|
|
280
|
+
.kyogijo .ky-cr-veil{fill:#04060a;fill-opacity:.6}
|
|
281
|
+
.kyogijo .ky-night .ky-cr-aisle{stroke:#ffd9a3;stroke-opacity:.95;stroke-dasharray:1.1 3.4;stroke-linecap:round}
|
|
282
|
+
.kyogijo .ky-night .ky-cr-vom{stroke:#7dffb0;stroke-opacity:.6;stroke-dasharray:none}
|
|
283
|
+
.kyogijo .ky-night .ky-cr-shell{fill:#171c22}
|
|
266
284
|
.kyogijo .ky-cr-led-bed{fill:#0b0f14}
|
|
267
285
|
.kyogijo .ky-cr-led-line{fill:none;stroke:#0b0f14;stroke-linejoin:round}
|
|
268
286
|
.kyogijo .ky-cr-led{stroke-linecap:butt}`.trim();
|
package/dist/crowd.js
CHANGED
|
@@ -35,6 +35,7 @@ export function resolveCrowd(value) {
|
|
|
35
35
|
stands: o.stands !== false,
|
|
36
36
|
detail: o.detail === "fine" || o.detail === "plain" ? o.detail : "auto",
|
|
37
37
|
boards: o.boards === true || (typeof o.boards === "object" && o.boards !== null) ? o.boards : undefined,
|
|
38
|
+
lighting: o.lighting,
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
/** How full the stands are: a density, else attendance over capacity (the ground's, else the model's seats), else 0.85. */
|
package/dist/district.turn.js
CHANGED
|
@@ -7,7 +7,7 @@ import { n, plane } from "./turn.kit.js";
|
|
|
7
7
|
import { TURN_PRESETS, wrap } from "./turn.camera.js";
|
|
8
8
|
import { mountTurn } from "./turn.dom.js";
|
|
9
9
|
import { turnMarkupOf } from "./turn.markup.js";
|
|
10
|
-
import { buildTurn, openingView, sceneBox, WORLD_PX } from "./turn.model.js";
|
|
10
|
+
import { buildTurn, lightingOf, openingView, sceneBox, WORLD_PX } from "./turn.model.js";
|
|
11
11
|
import { planesOf } from "./uprights.js";
|
|
12
12
|
import { FEET_PER_METRE } from "./geometry.js";
|
|
13
13
|
import { hullOf } from "./venues.geo.js";
|
|
@@ -124,6 +124,7 @@ export function buildDistrictTurn(options, settings = {}) {
|
|
|
124
124
|
defs: defs.join(""),
|
|
125
125
|
crowdStyle,
|
|
126
126
|
venue: true,
|
|
127
|
+
...lightingOf(typeof settings.venue === "object" ? settings.venue : {}, undefined),
|
|
127
128
|
look: undefined,
|
|
128
129
|
planes,
|
|
129
130
|
box,
|
package/dist/index.d.ts
CHANGED
|
@@ -76,4 +76,4 @@ export type { SoccerPitchClass, SoccerPitchId, SoccerSize, SoccerSpec } from "./
|
|
|
76
76
|
export { SOCCER_PROVIDER_IDS, SOCCER_PROVIDERS, frameMatrix, rescale, soccerFrame, soccerLandmarks } from "./surfaces/soccer-providers.ts";
|
|
77
77
|
export type { Rescaler, SoccerFrame, SoccerProvider, SoccerProviderId } from "./surfaces/soccer-providers.ts";
|
|
78
78
|
export { VERSION } from "./version.ts";
|
|
79
|
-
export type { AxesOptions, Box, Colors, CourtStyle, CrowdOptions, LedBoardOptions, LedBoardPattern, CrowdShape, Detail, DirectionOptions, BudoSport, FieldSport, FieldStyle, Fence, GoalInfo, GoalStyle, HardwoodOptions, Language, NetStyle, LineDash, LineWidth, LookName, Orientation, PoolSport, PoolStyle, QuirkName, QuirkOptions, RangeBox, PageantOptions, PageantStyle, RangeName, RangeNamed, ScoreOptions, Side, Size, Sport, StatusWord, StripePattern, SurfaceOptions, Team, TeamMark, Theme, WallOptions, } from "./types.ts";
|
|
79
|
+
export type { AxesOptions, Box, Colors, CourtStyle, CrowdOptions, LedBoardOptions, LedBoardPattern, LightingOptions, LightingPreset, CrowdShape, Detail, DirectionOptions, BudoSport, FieldSport, FieldStyle, Fence, GoalInfo, GoalStyle, HardwoodOptions, Language, NetStyle, LineDash, LineWidth, LookName, Orientation, PoolSport, PoolStyle, QuirkName, QuirkOptions, RangeBox, PageantOptions, PageantStyle, RangeName, RangeNamed, ScoreOptions, Side, Size, Sport, StatusWord, StripePattern, SurfaceOptions, Team, TeamMark, Theme, WallOptions, } from "./types.ts";
|
package/dist/multi.draw.js
CHANGED
|
@@ -268,7 +268,7 @@ export function multiDrawing(options, raise, uprights = true) {
|
|
|
268
268
|
}
|
|
269
269
|
if (stadium !== undefined) {
|
|
270
270
|
const standsLike = venue?.standsLike ?? spec.sport;
|
|
271
|
-
const crowd = resolveCrowd({ gaps: false, awayEnd: "none", roof: stadium.roof, shape: stadium.shape, density: stadium.density, detail: stadium.detail === "plain" ? "plain" : "fine", boards: hallBoards(stadium.boards) });
|
|
271
|
+
const crowd = resolveCrowd({ gaps: false, awayEnd: "none", roof: stadium.roof, shape: stadium.shape, density: stadium.density, detail: stadium.detail === "plain" ? "plain" : "fine", boards: hallBoards(stadium.boards), lighting: stadium.lighting });
|
|
272
272
|
const stands = layoutOf({ sport: standsLike, box: wide, crowd, awaySide: "right", arena: venue?.shape === "octagon" });
|
|
273
273
|
const wall = venue?.shape === "octagon" ? el("path", { class: "ky-mu-wall", d: octagonD(hall.w / 2, hall.w / 2, hall.w / 2) }) : el("rect", { class: "ky-mu-wall", x: 0, y: 0, width: num(hall.w), height: num(hall.h), rx: 0.5 });
|
|
274
274
|
if (raise)
|
package/dist/multi.turn.js
CHANGED
|
@@ -10,7 +10,7 @@ import { TURN_PRESETS, NET_PRESETS, wrap } from "./turn.camera.js";
|
|
|
10
10
|
import { box, flat, n, plane, rect, shy, thinnest, wall } from "./turn.kit.js";
|
|
11
11
|
import { mountTurn } from "./turn.dom.js";
|
|
12
12
|
import { turnMarkupOf } from "./turn.markup.js";
|
|
13
|
-
import { openingView, sceneBox } from "./turn.model.js";
|
|
13
|
+
import { lightingOf, openingView, sceneBox } from "./turn.model.js";
|
|
14
14
|
import { describeUprights, planesOf, uprightsFor } from "./uprights.js";
|
|
15
15
|
import { standsVenueOf } from "./venue.js";
|
|
16
16
|
import { isNetSport } from "./court.js";
|
|
@@ -207,6 +207,7 @@ export function buildMultiTurn(options, settings = {}) {
|
|
|
207
207
|
defs,
|
|
208
208
|
crowdStyle,
|
|
209
209
|
venue: drawn.stands !== undefined,
|
|
210
|
+
...(drawn.stands === undefined ? {} : lightingOf({ lighting: typeof settings.venue === "object" && settings.venue.lighting !== undefined ? settings.venue.lighting : drawn.stands.crowd.lighting }, undefined)),
|
|
210
211
|
look: { sport: spec.sport, look: look ?? "plain" },
|
|
211
212
|
planes,
|
|
212
213
|
box: box3,
|
package/dist/multi.types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LedBoardOptions, Sport, SurfaceOptions } from "./types.ts";
|
|
1
|
+
import type { LedBoardOptions, LightingOptions, Sport, SurfaceOptions } from "./types.ts";
|
|
2
2
|
/** The unit a layout's lengths are written in: the one its rulebook prints. */
|
|
3
3
|
export type MultiUnit = "m" | "ft";
|
|
4
4
|
/** A length along the layout (`x`, left to right) and across it (`y`, top to bottom). */
|
|
@@ -248,6 +248,7 @@ export interface MultiOptions {
|
|
|
248
248
|
shape?: "bowl" | "sides";
|
|
249
249
|
detail?: "fine" | "plain";
|
|
250
250
|
boards?: boolean | LedBoardOptions;
|
|
251
|
+
lighting?: LightingOptions;
|
|
251
252
|
};
|
|
252
253
|
/** The empty places for the event's words and marks, and the boards: on by default; `false` draws neither. */
|
|
253
254
|
slots?: boolean;
|
package/dist/turn-entry.d.ts
CHANGED
|
@@ -13,5 +13,5 @@ export { describeUprights, heightUnit, planesOf, uprightsFor } from "./uprights.
|
|
|
13
13
|
export type { UprightOptions, Uprights } from "./uprights.ts";
|
|
14
14
|
export { ROOF } from "./venue.ts";
|
|
15
15
|
export type { Venue, VenueBuild, VenueSettings } from "./venue.ts";
|
|
16
|
-
export type { LedBoardOptions, LedBoardPattern } from "./types.ts";
|
|
16
|
+
export type { LedBoardOptions, LedBoardPattern, LightingOptions, LightingPreset } from "./types.ts";
|
|
17
17
|
export type { Plane, PartKind, Upright, UprightKind, V3 } from "./turn.kit.ts";
|
package/dist/turn.dom.js
CHANGED
|
@@ -483,6 +483,11 @@ export function mountTurn(host, options, settings, build) {
|
|
|
483
483
|
stage.insertAdjacentHTML("beforeend", turnBoardMarkup(model.board));
|
|
484
484
|
stage.setAttribute("aria-label", model.title);
|
|
485
485
|
root.querySelector(".ky-turn-help").textContent = `${turnWords(model).keys}${model.standing === "" ? "" : ` ${model.standing}`}`;
|
|
486
|
+
// The lighting is an attribute of the root, which the stylesheet reads: it changes with the scene, the view's turn is not touched.
|
|
487
|
+
if (model.lighting === undefined)
|
|
488
|
+
root.removeAttribute("data-lighting");
|
|
489
|
+
else
|
|
490
|
+
root.setAttribute("data-lighting", model.lighting);
|
|
486
491
|
const theme = model.options.theme;
|
|
487
492
|
if (theme === "light" || theme === "dark")
|
|
488
493
|
root.dataset.theme = theme;
|
package/dist/turn.markup.js
CHANGED
|
@@ -154,7 +154,7 @@ export function turnMarkupOf(model, settings = {}) {
|
|
|
154
154
|
const sheet = model.options.style === "none" ? "" : `<style>${KYOGIJO_TURN_STYLE}</style>`;
|
|
155
155
|
const theme = model.options.theme;
|
|
156
156
|
const sport = model.options.sport;
|
|
157
|
-
return (`<div class="ky-turn" id="${esc(model.id)}" data-sport="${esc(String(sport))}" data-mode="3d" data-touch="${settings.touch === "turn" ? "turn" : "scroll"}" data-preset="${presetOf(view, model.presets ?? sport) ?? "custom"}"${theme === "light" || theme === "dark" ? ` data-theme="${theme}"` : ""} lang="${model.language}">` +
|
|
157
|
+
return (`<div class="ky-turn" id="${esc(model.id)}" data-sport="${esc(String(sport))}" data-mode="3d" data-touch="${settings.touch === "turn" ? "turn" : "scroll"}" data-preset="${presetOf(view, model.presets ?? sport) ?? "custom"}"${model.lighting === undefined ? "" : ` data-lighting="${model.lighting}"`}${theme === "light" || theme === "dark" ? ` data-theme="${theme}"` : ""} lang="${model.language}">` +
|
|
158
158
|
sheet +
|
|
159
159
|
`<div class="ky-turn-view" tabindex="0" role="group" aria-roledescription="${esc(words.region)}" aria-label="${esc(model.title)}" aria-describedby="${esc(model.id)}-help" aria-keyshortcuts="ArrowLeft ArrowRight ArrowUp ArrowDown + - 0 1 2 3 4 Home Space"><div ${worldAttributes(model)}>${worldMarkup(model, layout)}</div>${model.board === undefined ? "" : turnBoardMarkup(model.board)}</div>` +
|
|
160
160
|
`<p class="ky-turn-help" id="${esc(model.id)}-help">${esc(keys)}</p>` +
|
package/dist/turn.model.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { type UprightOptions, type Uprights } from "./uprights.ts";
|
|
|
5
5
|
import { type ResolvedBoard } from "./overlay.ts";
|
|
6
6
|
import type { VenueSettings } from "./venue.ts";
|
|
7
7
|
import type { Box3 } from "./turn.camera.ts";
|
|
8
|
-
import type { Language, ScoreOptions, SurfaceOptions } from "./types.ts";
|
|
8
|
+
import type { Language, LightingOptions, LightingPreset, ScoreOptions, SurfaceOptions } from "./types.ts";
|
|
9
9
|
/** What can be asked of a 3D view. */
|
|
10
10
|
export interface TurnSettings {
|
|
11
11
|
/** The viewpoint it opens on: `overhead`, `broadcast`, `end` or `corner` (the default). */
|
|
@@ -85,6 +85,8 @@ export interface TurnModel {
|
|
|
85
85
|
crowdStyle: string;
|
|
86
86
|
/** Whether the venue is raised. */
|
|
87
87
|
venue: boolean;
|
|
88
|
+
/** The lighting of the venue (`data-lighting` on the view's root, which the stylesheet reads), or `undefined`. */
|
|
89
|
+
lighting?: LightingPreset;
|
|
88
90
|
/** The sport and look the floor is painted in, when it has one (a budō surface's mats): the planes of its room are painted in the same look. */
|
|
89
91
|
look: {
|
|
90
92
|
sport: string;
|
|
@@ -111,6 +113,12 @@ export interface TurnModel {
|
|
|
111
113
|
/** The board the options ask for (`board`), laid over the view: a layer of its own, never a plane of the scene. */
|
|
112
114
|
board?: ResolvedBoard;
|
|
113
115
|
}
|
|
116
|
+
/** The lighting a venue is built with, as a model's field: the venue's own setting, else the crowd's, and none where there is no venue. */
|
|
117
|
+
export declare const lightingOf: (venue: {
|
|
118
|
+
lighting?: LightingOptions;
|
|
119
|
+
} | undefined, crowd: SurfaceOptions["crowd"]) => {
|
|
120
|
+
lighting?: LightingPreset;
|
|
121
|
+
};
|
|
114
122
|
/** The options with what makes the floor of a 3D view: the whole surface, lying wide, at its full size. */
|
|
115
123
|
export declare function floorOptions(options: SurfaceOptions | ScoreOptions): SurfaceOptions | ScoreOptions;
|
|
116
124
|
/** The view a set of settings opens on. */
|
package/dist/turn.model.js
CHANGED
|
@@ -9,8 +9,14 @@ import { TURN_DEFAULT, clampView, presetView } from "./turn.camera.js";
|
|
|
9
9
|
import { describeUprights, planesOf, uprightsFor } from "./uprights.js";
|
|
10
10
|
import { resolveCrowd } from "./crowd.js";
|
|
11
11
|
import { resolveBoard } from "./overlay.js";
|
|
12
|
+
import { resolveLighting } from "./venue.lighting.js";
|
|
12
13
|
/** The pixels in one foot of the scene at a zoom of 1: the floor is drawn this big, and scaled down to fit. A larger floor is sharper. */
|
|
13
14
|
export const WORLD_PX = 1400;
|
|
15
|
+
/** The lighting a venue is built with, as a model's field: the venue's own setting, else the crowd's, and none where there is no venue. */
|
|
16
|
+
export const lightingOf = (venue, crowd) => {
|
|
17
|
+
const lighting = venue === undefined ? undefined : resolveLighting(venue.lighting !== undefined ? venue.lighting : resolveCrowd(crowd)?.lighting);
|
|
18
|
+
return lighting === undefined ? {} : { lighting };
|
|
19
|
+
};
|
|
14
20
|
/** The options with what makes the floor of a 3D view: the whole surface, lying wide, at its full size. */
|
|
15
21
|
export function floorOptions(options) {
|
|
16
22
|
const rest = { ...options, orientation: "landscape" };
|
|
@@ -122,6 +128,7 @@ export function buildTurn(options, settings = {}) {
|
|
|
122
128
|
// The colours the crowd has in the flat drawing (which the floor no longer carries) are read from a small drawing of the same options.
|
|
123
129
|
crowdStyle: uprights.defs === undefined ? "" : crowdVars(drawKyogijo({ ...asked, size: "badge", style: "none" })),
|
|
124
130
|
venue: venue !== undefined,
|
|
131
|
+
...lightingOf(venue, asked.crowd),
|
|
125
132
|
look: lookOf(floor),
|
|
126
133
|
planes,
|
|
127
134
|
box,
|
package/dist/turn.style.js
CHANGED
|
@@ -89,6 +89,16 @@ export const KYOGIJO_TURN_STYLE = `
|
|
|
89
89
|
/* LED boards: the housing and the screen are dark; the lit parts (class u-led) carry their own colours, and are what a lighting layer makes glow. */
|
|
90
90
|
.ky-turn .u-led-bed{fill:#0b0f14}
|
|
91
91
|
.ky-turn .u-led-screen{fill:#06121d}
|
|
92
|
+
/* Night: the house is down. The venue's own colours go dark in either theme (the page's theme is the page's), the seats under a veil, small lights show along the aisles, the exits are green, and the windows, the coping and the lanes of the concourse are softly lit. */
|
|
93
|
+
.ky-turn[data-lighting=night]{--ky-cr-bed:#11151a;--ky-cr-empty:#2a313a;--ky-cr-gap:#020304;--ky-cr-conc:#171c22;--ky-cr-dim:1;--ky-up-fascia:#272c29;--ky-up-ground:#1d211e;--ky-night-aisle:#ffd9a3;--ky-night-exit:#7dffb0;--ky-night-rim:#ffe0b0}
|
|
94
|
+
.ky-turn .u-veil{fill:#04060a;fill-opacity:.62}
|
|
95
|
+
.ky-turn[data-lighting=night] .ky-cr-aisle{stroke:var(--ky-night-aisle);stroke-opacity:.95;stroke-dasharray:1.1 3.4;stroke-linecap:round}
|
|
96
|
+
.ky-turn[data-lighting=night] .ky-cr-vom{stroke:var(--ky-night-exit);stroke-opacity:.6;stroke-dasharray:none}
|
|
97
|
+
.ky-turn[data-lighting=night] .u-suite-win{fill:color-mix(in srgb,var(--ky-night-aisle) 50%,#2a2218)}
|
|
98
|
+
.ky-turn[data-lighting=night] .u-coping{fill:color-mix(in srgb,var(--ky-night-rim) 55%,var(--ky-up-fascia))}
|
|
99
|
+
.ky-turn[data-lighting=night] .u-suite-ledge{fill:color-mix(in srgb,var(--ky-night-rim) 45%,var(--ky-up-fascia))}
|
|
100
|
+
.ky-turn[data-lighting=night] .u-clane{stroke:var(--ky-night-rim);stroke-opacity:.4}
|
|
101
|
+
.ky-turn[data-lighting=night] .u-apron{fill:#30363e}
|
|
92
102
|
.ky-turn .u-back{fill:color-mix(in srgb,var(--ky-up-fascia) 66%,var(--ky-cr-bed))}
|
|
93
103
|
.ky-turn .u-backrib{stroke:var(--ky-cr-bed);stroke-width:.7;stroke-opacity:.7}
|
|
94
104
|
.ky-turn .u-coping{fill:color-mix(in srgb,var(--ky-up-fascia) 70%,var(--ky-cr-conc))}
|
package/dist/types.d.ts
CHANGED
|
@@ -274,6 +274,14 @@ export type SheetShow = "sheet" | "half" | "house" | "folded";
|
|
|
274
274
|
export type SheetEnd = Side | "top" | "bottom";
|
|
275
275
|
/** The shape of a venue's stands round the playing surface. */
|
|
276
276
|
export type CrowdShape = "bowl" | "sides" | "horseshoe" | "grandstand";
|
|
277
|
+
/**
|
|
278
|
+
* The lighting of a venue (`crowd.lighting` in the plan, `venue.lighting` in the 3D view): `"night"` (or `true`, or `{ preset: "night" }`) dims the house, lights the playing surface and shows small lights along the aisles and stairs, the ribbon
|
|
279
|
+
* boards and the scoreboard glowing, the exits and the concourse rim softly lit. The named presets are the base other lightings are added to; `false` is no lighting. Off by default.
|
|
280
|
+
*/
|
|
281
|
+
export type LightingPreset = "night";
|
|
282
|
+
export type LightingOptions = LightingPreset | boolean | {
|
|
283
|
+
preset?: LightingPreset;
|
|
284
|
+
};
|
|
277
285
|
/** The patterns a ribbon board shows: `bands` (two bands of colour, in panels), `blocks` (a block of colour to a panel), `chevrons` (arrows running along it) or `waves`. */
|
|
278
286
|
export type LedBoardPattern = "bands" | "blocks" | "chevrons" | "waves";
|
|
279
287
|
/**
|
|
@@ -347,6 +355,8 @@ export interface CrowdOptions {
|
|
|
347
355
|
* middle of the surface. `true` for both in the package's colours, or `{ ribbon, scoreboard, colors, pattern, score }`. Generic content only. Off by default. See `docs/boards.md`.
|
|
348
356
|
*/
|
|
349
357
|
boards?: boolean | LedBoardOptions;
|
|
358
|
+
/** The lighting of the venue: `"night"` dims the house and lights the surface, the aisles, the boards and the exits. Off by default. See `docs/venues.md`. */
|
|
359
|
+
lighting?: LightingOptions;
|
|
350
360
|
}
|
|
351
361
|
/**
|
|
352
362
|
* The parts of a surface a drawing can be cropped to (`range`). Football: `red-zone`, `between-the-20s`, `end-zone`, `field-of-play`, `own-half`, `opponent-half`.
|
package/dist/venue.boards.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface Boards {
|
|
|
9
9
|
colors: [string, string, string];
|
|
10
10
|
pattern: LedBoardPattern;
|
|
11
11
|
score: [number, number];
|
|
12
|
+
/** Each lit panel has a halo under it (the venue is lit as at night). */
|
|
13
|
+
glow: boolean;
|
|
12
14
|
}
|
|
13
15
|
/** The package's own colours of light: amber, blue and a white with a little blue in it. */
|
|
14
16
|
export declare const BOARD_COLORS: readonly [string, string, string];
|
|
@@ -42,12 +44,14 @@ export declare const safeColor: (value: unknown) => string | undefined;
|
|
|
42
44
|
export declare function resolveBoards(ask: boolean | LedBoardOptions | undefined | null, team?: {
|
|
43
45
|
home?: string;
|
|
44
46
|
away?: string;
|
|
45
|
-
}): Boards | undefined;
|
|
47
|
+
}, glow?: boolean): Boards | undefined;
|
|
46
48
|
/**
|
|
47
49
|
* A ribbon on a quad `[a, b, c, d]` of a plane (a and b along its lower edge, d and c along its upper): a dark housing with panels of light in it, in the pattern asked. `run` is how far along the ribbon this quad starts, in feet,
|
|
48
50
|
* so that the panels carry on in the colour they were from one quad to the next. A quad that is not a quad is the housing alone.
|
|
49
51
|
*/
|
|
50
|
-
export declare function ledStrip(corners: readonly Pt[], look: Pick<Boards, "colors" | "pattern"
|
|
52
|
+
export declare function ledStrip(corners: readonly Pt[], look: Pick<Boards, "colors" | "pattern"> & {
|
|
53
|
+
glow?: boolean;
|
|
54
|
+
}, run?: number): string;
|
|
51
55
|
/** The ribbon on a wall, `length` feet long and `height` feet high: the same strip, on a rectangle (its lower edge is the wall's lower edge). */
|
|
52
56
|
export declare const ledWall: (length: number, height: number, look: Pick<Boards, "colors" | "pattern">, run?: number) => string;
|
|
53
57
|
/** A walk between two tiers: the tier below it, the one above and the walk's own outlines. */
|
package/dist/venue.boards.js
CHANGED
|
@@ -25,7 +25,7 @@ export const BOARD = {
|
|
|
25
25
|
const COLOR = /^(#[0-9a-f]{3,8}|(?:rgb|hsl)a?\([\d\s.,%/-]{3,40}\)|[a-z]{3,24})$/i;
|
|
26
26
|
export const safeColor = (value) => (typeof value === "string" && COLOR.test(value.trim()) ? value.trim() : undefined);
|
|
27
27
|
/** The boards a `boards` option asks for (the crowd's `home` and `away` are the colours when it names none), or `undefined` for none. */
|
|
28
|
-
export function resolveBoards(ask, team = {}) {
|
|
28
|
+
export function resolveBoards(ask, team = {}, glow = false) {
|
|
29
29
|
if (ask === undefined || ask === null || ask === false)
|
|
30
30
|
return undefined;
|
|
31
31
|
const o = ask === true ? {} : ask;
|
|
@@ -38,6 +38,7 @@ export function resolveBoards(ask, team = {}) {
|
|
|
38
38
|
colors,
|
|
39
39
|
pattern: BOARD_PATTERNS.includes(o.pattern) ? o.pattern : "bands",
|
|
40
40
|
score: [whole(o.score?.[0]), whole(o.score?.[1])],
|
|
41
|
+
glow,
|
|
41
42
|
};
|
|
42
43
|
return boards.ribbon || boards.scoreboard ? boards : undefined;
|
|
43
44
|
}
|
|
@@ -76,7 +77,7 @@ export function ledStrip(corners, look, run = 0) {
|
|
|
76
77
|
});
|
|
77
78
|
paint(color, `M${pts.map(at).join("L")}`);
|
|
78
79
|
});
|
|
79
|
-
const waves = [...lit].map(([color, shapes]) => `<path class="u-led" fill="none" stroke="${color}" stroke-width="${n(BOARD.ribbon * 0.2)}" stroke-linecap="round" stroke-linejoin="round" d="${shapes.join("")}"/>`);
|
|
80
|
+
const waves = [...lit].map(([color, shapes]) => (look.glow === true ? `<path class="u-led-glow" fill="none" stroke="${color}" stroke-opacity=".3" stroke-width="${n(BOARD.ribbon * 0.7)}" stroke-linecap="round" d="${shapes.join("")}"/>` : "") + `<path class="u-led" fill="none" stroke="${color}" stroke-width="${n(BOARD.ribbon * 0.2)}" stroke-linecap="round" stroke-linejoin="round" d="${shapes.join("")}"/>`);
|
|
80
81
|
return bed + waves.join("");
|
|
81
82
|
}
|
|
82
83
|
for (let i = 0; i < panels; i += 1) {
|
|
@@ -99,7 +100,9 @@ export function ledStrip(corners, look, run = 0) {
|
|
|
99
100
|
paint(other, loop([across(s0, mid + 0.03), across(s1, mid + 0.03), across(s1, hi), across(s0, hi)]));
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
|
-
|
|
103
|
+
// A halo under each colour's panels: the same shapes, stroked wide and faint, so that the light seems to spill over the housing and the wall beside it.
|
|
104
|
+
const halo = (color, shapes) => (look.glow === true ? `<path class="u-led-glow" fill="${color}" fill-opacity=".3" stroke="${color}" stroke-opacity=".3" stroke-width="${n(BOARD.ribbon * 0.6)}" stroke-linejoin="round" d="${shapes.join("")}"/>` : "");
|
|
105
|
+
return bed + [...lit].map(([color, shapes]) => `${halo(color, shapes)}<path class="u-led" fill="${color}" d="${shapes.join("")}"/>`).join("");
|
|
103
106
|
}
|
|
104
107
|
/** The ribbon on a wall, `length` feet long and `height` feet high: the same strip, on a rectangle (its lower edge is the wall's lower edge). */
|
|
105
108
|
export const ledWall = (length, height, look, run = 0) => ledStrip([[0, height], [length, height], [length, 0], [0, 0]], look, run);
|
|
@@ -162,7 +165,8 @@ function boardFace(w, h, look) {
|
|
|
162
165
|
const [c0, c1, c2] = look.colors;
|
|
163
166
|
const edge = Math.min(0.5, h * 0.06);
|
|
164
167
|
const band = h * 0.1;
|
|
165
|
-
|
|
168
|
+
const halo = look.glow ? `<rect class="u-led-glow" x="${n(-0.8)}" y="${n(-0.8)}" width="${n(w + 1.6)}" height="${n(h + 1.6)}" rx="1" fill="${c2}" fill-opacity=".2"/>` : "";
|
|
169
|
+
let svg = `${halo}<rect class="u-led-bed" width="${n(w)}" height="${n(h)}"/><rect class="u-led-screen" x="${n(edge)}" y="${n(edge)}" width="${n(w - 2 * edge)}" height="${n(h - 2 * edge)}"/>`;
|
|
166
170
|
svg += `<path class="u-led" fill="${c2}" d="M${n(edge)} ${n(edge)}h${n(w - 2 * edge)}v${n(band)}h${n(-(w - 2 * edge))}zM${n(edge)} ${n(h - edge - band)}h${n(w - 2 * edge)}v${n(band)}h${n(-(w - 2 * edge))}z"/>`;
|
|
167
171
|
const block = Math.min(w * 0.2, h * 0.5);
|
|
168
172
|
const [yb, hb] = [h * 0.28, h * 0.44];
|
|
@@ -240,7 +244,8 @@ export function planRibbons(stands, look) {
|
|
|
240
244
|
return "";
|
|
241
245
|
const cell = BOARD.panel;
|
|
242
246
|
const lit = look.colors.map((color, k) => el("path", { class: "ky-cr-led", d: paths, fill: "none", stroke: color, "stroke-width": 2.2, "stroke-dasharray": `${cell} ${cell * 2}`, "stroke-dashoffset": -cell * k }));
|
|
243
|
-
|
|
247
|
+
const glow = look.glow ? look.colors.map((color, k) => el("path", { class: "ky-cr-led-glow", d: paths, fill: "none", stroke: color, "stroke-opacity": 0.3, "stroke-width": 7, "stroke-dasharray": `${cell} ${cell * 2}`, "stroke-dashoffset": -cell * k })).join("") : "";
|
|
248
|
+
return el("g", { class: "ky-cr-ribbon" }, el("path", { class: "ky-cr-led-line", d: paths, "stroke-width": 3.4 }) + glow + lit.join(""));
|
|
244
249
|
}
|
|
245
250
|
/** The scoreboard in the flat plan, as the board hangs over the surface seen from above: its dark cap with a line of light round it and its four edges' colours. */
|
|
246
251
|
export function planScoreboard(box, look) {
|
|
@@ -250,7 +255,8 @@ export function planScoreboard(box, look) {
|
|
|
250
255
|
const inner = { x: b.x + rim * 2, y: b.y + rim * 2, w: b.w - rim * 4, d: b.d - rim * 4 };
|
|
251
256
|
const rect = (cls, r, extra = {}) => el("rect", { class: cls, x: n(r.x), y: n(r.y), width: n(r.w), height: n(r.d), rx: n(rim), ...extra });
|
|
252
257
|
const edge = (x, y, w, h, color) => el("rect", { class: "ky-cr-led", x: n(x), y: n(y), width: n(w), height: n(h), fill: color });
|
|
253
|
-
return el("g", { class: "ky-cr-board" }, rect("ky-cr-led-
|
|
258
|
+
return el("g", { class: "ky-cr-board" }, (look.glow ? rect("ky-cr-led-glow", { x: b.x - rim * 2, y: b.y - rim * 2, w: b.w + rim * 4, d: b.d + rim * 4 }, { fill: c2, "fill-opacity": 0.22 }) : "") +
|
|
259
|
+
rect("ky-cr-led-bed", b) +
|
|
254
260
|
edge(inner.x, inner.y, inner.w, rim, c0) +
|
|
255
261
|
edge(inner.x, inner.y + inner.d - rim, inner.w, rim, c1) +
|
|
256
262
|
edge(inner.x, inner.y, rim, inner.d, c2) +
|
package/dist/venue.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Crowd, type CrowdStands } from "./crowd.ts";
|
|
2
2
|
import { type Cut } from "./venue.geom.ts";
|
|
3
3
|
import { type Upright } from "./turn.kit.ts";
|
|
4
|
-
import type { LedBoardOptions, SurfaceOptions } from "./types.ts";
|
|
4
|
+
import type { LedBoardOptions, LightingOptions, SurfaceOptions } from "./types.ts";
|
|
5
5
|
/** What can be asked of the venue. */
|
|
6
6
|
export interface VenueSettings {
|
|
7
7
|
/** Roofs and canopies over the top tier of the stands: a canopy over the back of the top tier in the open, a lid over a hall. They give way to the eye as the walls do. Off by default. */
|
|
@@ -17,6 +17,11 @@ export interface VenueSettings {
|
|
|
17
17
|
* colours, or `{ ribbon, scoreboard, colors, pattern, score }`. Generic content, never a logo, a sponsor or a real board's text. Off by default; it wins over the crowd's own `boards`, and `false` turns that one off. See `docs/boards.md`.
|
|
18
18
|
*/
|
|
19
19
|
boards?: boolean | LedBoardOptions;
|
|
20
|
+
/**
|
|
21
|
+
* The lighting: `"night"` (or `true`, or `{ preset: "night" }`) dims the seats, lights the surface with a pool of light round it, shows small lights along the aisles and the exits, makes the boards glow and lights the rim of the concourse.
|
|
22
|
+
* Off by default; it wins over the crowd's own `lighting`, and `false` turns that one off. See `docs/venues.md`.
|
|
23
|
+
*/
|
|
24
|
+
lighting?: LightingOptions;
|
|
20
25
|
/** How finely the curves are cut: `low` a facet to each corner, `auto` (the default) three, `high` six; an oval is 12, 28 or 56 facets round. */
|
|
21
26
|
detail?: Cut;
|
|
22
27
|
}
|
package/dist/venue.js
CHANGED
|
@@ -7,8 +7,9 @@ import { patterns } from "./crowd.draw.js";
|
|
|
7
7
|
import { crowdStands } from "./draw.js";
|
|
8
8
|
import { groundById } from "./grounds.js";
|
|
9
9
|
import { ARC_STEPS, clipAround, clipConvex, clipSegment, areaOf, pointList, raked, slopeOf, stripsOf, tidy } from "./venue.geom.js";
|
|
10
|
-
import { apronPlanes, backEdges, backWall, concourseFloor, rimAt, suiteWall } from "./venue.fill.js";
|
|
10
|
+
import { APRON_RISE, apronPlanes, backEdges, backWall, concourseFloor, rimAt, suiteWall } from "./venue.fill.js";
|
|
11
11
|
import { firstHit, realBuilding } from "./venue.real.js";
|
|
12
|
+
import { lightPool, resolveLighting } from "./venue.lighting.js";
|
|
12
13
|
import { centreBoard, fasciaRibbon, hangsOver, ledStrip, resolveBoards, ribbonHigh, ribbonShare, tierLinks } from "./venue.boards.js";
|
|
13
14
|
import { n, shy, wall } from "./turn.kit.js";
|
|
14
15
|
export const NO_VENUE = { items: [], defs: "", reach: 0 };
|
|
@@ -73,6 +74,8 @@ export function standsVenueOf(stands, crowd, o, v, lift) {
|
|
|
73
74
|
else
|
|
74
75
|
svg += fine ? `<polygon fill="url(#${pid}-cr-${s.kind === "tifo-home" ? "th" : "ta"})" points="${points}"/>` : `<polygon class="${s.kind === "tifo-home" ? "u-th" : "u-ta"}" points="${points}"/>`;
|
|
75
76
|
}
|
|
77
|
+
if (night)
|
|
78
|
+
svg += `<polygon class="u-veil" points="${pointList(corners)}"/>`;
|
|
76
79
|
if (fine) {
|
|
77
80
|
let plain = "";
|
|
78
81
|
let vomitory = "";
|
|
@@ -90,7 +93,7 @@ export function standsVenueOf(stands, crowd, o, v, lift) {
|
|
|
90
93
|
if (plain !== "")
|
|
91
94
|
svg += `<path class="ky-cr-aisle" fill="none" stroke-width="1.1" d="${plain}"/>`;
|
|
92
95
|
if (vomitory !== "")
|
|
93
|
-
svg += `<path class="ky-cr-aisle" fill="none" stroke-width="2.4" d="${vomitory}"/>`;
|
|
96
|
+
svg += `<path class="${night ? "ky-cr-aisle ky-cr-vom" : "ky-cr-aisle"}" fill="none" stroke-width="2.4" d="${vomitory}"/>`;
|
|
94
97
|
}
|
|
95
98
|
return svg;
|
|
96
99
|
};
|
|
@@ -99,7 +102,8 @@ export function standsVenueOf(stands, crowd, o, v, lift) {
|
|
|
99
102
|
const links = tierLinks(stands);
|
|
100
103
|
for (const l of links)
|
|
101
104
|
chained.add(l.after);
|
|
102
|
-
const
|
|
105
|
+
const night = resolveLighting(v.lighting !== undefined ? v.lighting : crowd.lighting) === "night";
|
|
106
|
+
const boards = resolveBoards(v.boards !== undefined ? v.boards : crowd.boards, { home: crowd.home, away: crowd.away }, night);
|
|
103
107
|
const ribbons = [];
|
|
104
108
|
/** How far along the ribbon of one level the next piece of it starts, in feet, so that its panels carry on. */
|
|
105
109
|
let run = 0;
|
|
@@ -187,6 +191,9 @@ export function standsVenueOf(stands, crowd, o, v, lift) {
|
|
|
187
191
|
});
|
|
188
192
|
}
|
|
189
193
|
}
|
|
194
|
+
// The surface is lit: a pool of light on the floor round it, under the seats.
|
|
195
|
+
if (night)
|
|
196
|
+
seatPlanes.push(lightPool("night-pool", stands.box, lift + APRON_RISE + 0.03));
|
|
190
197
|
const items = [];
|
|
191
198
|
if (seatPlanes.length > 0)
|
|
192
199
|
items.push({ id: "stands", kind: "stands", at: centre, height: topOf(seatPlanes), planes: seatPlanes });
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Plane } from "./turn.kit.ts";
|
|
2
|
+
import type { LightingOptions, LightingPreset } from "./types.ts";
|
|
3
|
+
export declare const LIGHTING_PRESETS: readonly LightingPreset[];
|
|
4
|
+
/** The lighting an option asks for: a named preset, `true` for the first (night), `{ preset }`, or `undefined` for none (false, nothing, an unknown name). */
|
|
5
|
+
export declare function resolveLighting(ask: LightingOptions | undefined | null): LightingPreset | undefined;
|
|
6
|
+
/** How far the pool of light round the surface reaches beyond its box, in feet, and the share of the pool that is bright (the surface's own edge). The surface itself stays as drawn: the pool is brightest just outside its markings. */
|
|
7
|
+
export declare const POOL: {
|
|
8
|
+
readonly reach: 20;
|
|
9
|
+
readonly edge: 0.7;
|
|
10
|
+
readonly strength: 0.34;
|
|
11
|
+
};
|
|
12
|
+
/** The pool of light on the floor round the playing surface: a flat plane under the seats with a radial gradient that is clear over the middle (the markings stay as they are) and glows round its edge. */
|
|
13
|
+
export declare function lightPool(id: string, box: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
w: number;
|
|
17
|
+
h: number;
|
|
18
|
+
}, z: number): Plane;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// The lighting of a venue. `night` is the first preset: the house goes down, the playing surface stays lit, small lights show along the aisles and stairs, the ribbon boards and the scoreboard glow and the exits and the concourse rim are
|
|
2
|
+
// softly lit. It is made of what the venue already has: the colours are custom properties a stylesheet sets on the 3D view's root (`data-lighting="night"`, see turn.style.ts) and on the plan's crowd (`ky-night`), a veil over each
|
|
3
|
+
// plane of seats, a pool of light on the floor round the surface and a halo under each lit board panel; nothing is worked out again as the view turns. Later presets (a show's colours and chases) are added to the same option.
|
|
4
|
+
// Pure: data in, planes out.
|
|
5
|
+
import { flat, n } from "./turn.kit.js";
|
|
6
|
+
export const LIGHTING_PRESETS = ["night"];
|
|
7
|
+
/** The lighting an option asks for: a named preset, `true` for the first (night), `{ preset }`, or `undefined` for none (false, nothing, an unknown name). */
|
|
8
|
+
export function resolveLighting(ask) {
|
|
9
|
+
if (ask === undefined || ask === null || ask === false)
|
|
10
|
+
return undefined;
|
|
11
|
+
if (ask === true)
|
|
12
|
+
return "night";
|
|
13
|
+
const name = typeof ask === "string" ? ask : ask.preset ?? "night";
|
|
14
|
+
return LIGHTING_PRESETS.includes(name) ? name : undefined;
|
|
15
|
+
}
|
|
16
|
+
/** How far the pool of light round the surface reaches beyond its box, in feet, and the share of the pool that is bright (the surface's own edge). The surface itself stays as drawn: the pool is brightest just outside its markings. */
|
|
17
|
+
export const POOL = { reach: 20, edge: 0.7, strength: 0.34 };
|
|
18
|
+
/** The pool of light on the floor round the playing surface: a flat plane under the seats with a radial gradient that is clear over the middle (the markings stay as they are) and glows round its edge. */
|
|
19
|
+
export function lightPool(id, box, z) {
|
|
20
|
+
const [w, h] = [box.w + 2 * POOL.reach, box.h + 2 * POOL.reach];
|
|
21
|
+
const gid = `${id}-g`;
|
|
22
|
+
const svg = `<defs><radialGradient id="${gid}" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#fff4dc" stop-opacity="0"/><stop offset="${POOL.edge - 0.12}" stop-color="#fff4dc" stop-opacity="0"/>` +
|
|
23
|
+
`<stop offset="${POOL.edge}" stop-color="#fff4dc" stop-opacity="${POOL.strength}"/><stop offset="1" stop-color="#fff4dc" stop-opacity="0"/></radialGradient></defs>` +
|
|
24
|
+
`<rect class="u-pool" width="${n(w)}" height="${n(h)}" fill="url(#${gid})"/>`;
|
|
25
|
+
return flat(id, "apron", box.x - POOL.reach, box.y - POOL.reach, box.x + box.w + POOL.reach, box.y + box.h + POOL.reach, z, svg);
|
|
26
|
+
}
|
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.38.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.38.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnmorrisdotca/kyogijo-stadiums",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.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",
|