@johnmorrisdotca/kyogijo-stadiums 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/NOTICE.md +17 -0
- package/README.md +58 -40
- package/dist/crowd.d.ts +51 -2
- package/dist/crowd.draw.d.ts +3 -1
- package/dist/crowd.draw.js +27 -3
- package/dist/crowd.js +17 -9
- package/dist/draw.js +13 -2
- package/dist/flythrough-entry.d.ts +8 -0
- package/dist/flythrough-entry.js +6 -0
- package/dist/flythrough.dom.d.ts +103 -0
- package/dist/flythrough.dom.js +517 -0
- package/dist/flythrough.markup.d.ts +48 -0
- package/dist/flythrough.markup.js +86 -0
- package/dist/flythrough.path.d.ts +145 -0
- package/dist/flythrough.path.js +253 -0
- package/dist/flythrough.style.d.ts +2 -0
- package/dist/flythrough.style.js +40 -0
- package/dist/flythrough.words.d.ts +33 -0
- package/dist/flythrough.words.js +44 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/strings.d.ts +6 -0
- package/dist/strings.js +2 -2
- package/dist/turn.model.js +3 -1
- package/dist/turn.style.js +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/venue.js +7 -4
- package/dist/venue.real.d.ts +16 -0
- package/dist/venue.real.js +103 -0
- package/dist/venues-entry.d.ts +2 -0
- package/dist/venues-entry.js +2 -0
- package/dist/venues.d.ts +153 -0
- package/dist/venues.data.d.ts +2 -0
- package/dist/venues.data.js +74 -0
- package/dist/venues.geo.d.ts +60 -0
- package/dist/venues.geo.js +211 -0
- package/dist/venues.js +213 -0
- package/dist/venues.osm.data.d.ts +4 -0
- package/dist/venues.osm.data.js +363 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +13 -1
package/dist/strings.d.ts
CHANGED
|
@@ -504,6 +504,12 @@ export interface KyogijoWords {
|
|
|
504
504
|
arenaSize: string;
|
|
505
505
|
/** An arena whose rink is said to have been shorter than the NHL's, with no size given: {arena}. */
|
|
506
506
|
arenaUnknown: string;
|
|
507
|
+
/** The stands of an arena's real venue: {seats} seats in {tiers} tiers, fitted inside the outline of {arena}'s building from OpenStreetMap. */
|
|
508
|
+
real: string;
|
|
509
|
+
/** How the rink lies in it: along the building's long axis (an inference), as OpenStreetMap maps the ice, or not known. */
|
|
510
|
+
realBuilding: string;
|
|
511
|
+
realMapped: string;
|
|
512
|
+
realNone: string;
|
|
507
513
|
/** A game decided on penalties: {name}, and the shoot-out score {a}-{b}, winner first. */
|
|
508
514
|
wonPens: string;
|
|
509
515
|
/** The shoot-out, as it is written beside a status: {a} and {b}, in the order of the sides on the drawing. */
|
package/dist/strings.js
CHANGED
|
@@ -199,7 +199,7 @@ export const KYOGIJO_STRINGS = {
|
|
|
199
199
|
},
|
|
200
200
|
},
|
|
201
201
|
scoreTitle: "{away} {awayScore}, {home} {homeScore}",
|
|
202
|
-
said: { home: "home", away: "away", won: "{name} won.", tied: "The game is level.", park: "The outfield fence follows the figures published for {park}.", officially: "Its official name is {name}.", ground: "The pitch follows the size published for {ground}.", groundDefault: "No pitch size is published for {ground}, so the pitch has the international size of 105 by 68 metres.", arenaRink: "{arena}, hockey rink", arenaCourt: "{arena}, basketball court", arenaRinkAbout: "an ice hockey rink, {length} by {width} feet ({lengthM} by {widthM} metres) with rounded corners, with its red and blue lines, face-off circles, creases and goal nets", arenaCourtAbout: "a basketball court, {length} by {width} feet ({lengthM} by {widthM} metres), with its keys, three-point lines, free-throw circles and the centre circle", arenaSize: "The size is the one published for {arena}.", arenaUnknown: "The article says the rink of {arena} was shorter than the NHL's but gives no size, so the NHL's is drawn.", era: "The markings are those of the {season} season.", hammer: "{name} has the hammer.", ends: "By end, {name}: {list}.", total: "Total", wonPens: "{name} won {a}-{b} on penalties.", pens: "{a}-{b} pens", on: "Drawn as", sep: ", ", stop: ".", colon: ": ", gap: " " },
|
|
202
|
+
said: { home: "home", away: "away", won: "{name} won.", tied: "The game is level.", park: "The outfield fence follows the figures published for {park}.", officially: "Its official name is {name}.", ground: "The pitch follows the size published for {ground}.", groundDefault: "No pitch size is published for {ground}, so the pitch has the international size of 105 by 68 metres.", arenaRink: "{arena}, hockey rink", arenaCourt: "{arena}, basketball court", arenaRinkAbout: "an ice hockey rink, {length} by {width} feet ({lengthM} by {widthM} metres) with rounded corners, with its red and blue lines, face-off circles, creases and goal nets", arenaCourtAbout: "a basketball court, {length} by {width} feet ({lengthM} by {widthM} metres), with its keys, three-point lines, free-throw circles and the centre circle", arenaSize: "The size is the one published for {arena}.", arenaUnknown: "The article says the rink of {arena} was shorter than the NHL's but gives no size, so the NHL's is drawn.", real: "The stands are a plausible bowl of {seats} seats in {tiers} tiers, fitted inside the outline of the building of {arena} from OpenStreetMap ((c) OpenStreetMap contributors); it is not the arena's plan.", realBuilding: "The rink lies along the building's long axis, which is an inference, and the arrow points north.", realMapped: "The rink lies as OpenStreetMap maps the ice, and the arrow points north.", realNone: "The rink's orientation is not known, so it is drawn east-west with north at the top.", era: "The markings are those of the {season} season.", hammer: "{name} has the hammer.", ends: "By end, {name}: {list}.", total: "Total", wonPens: "{name} won {a}-{b} on penalties.", pens: "{a}-{b} pens", on: "Drawn as", sep: ", ", stop: ".", colon: ": ", gap: " " },
|
|
203
203
|
budo: {
|
|
204
204
|
sides: { north: "north", south: "south", east: "east", west: "west" },
|
|
205
205
|
judo: "a judo contest area of {contest} by {contest} metres in a safety area {safety} metres wide, laid with tatami of {tatami}",
|
|
@@ -758,7 +758,7 @@ export const KYOGIJO_STRINGS = {
|
|
|
758
758
|
},
|
|
759
759
|
},
|
|
760
760
|
scoreTitle: "{away} {awayScore}、{home} {homeScore}",
|
|
761
|
-
said: { home: "ホーム", away: "アウェイ", won: "{name}の勝ちです。", tied: "同点です。", park: "外野フェンスは、{park}の公表されている距離をもとに描かれています。", officially: "正式名称は{name}です。", ground: "ピッチは、{ground}の公表されている広さをもとに描かれています。", groundDefault: "{ground}のピッチの広さは公表されていないため、国際標準の105×68mで描かれています。", arenaRink: "{arena}のアイスホッケーリンク", arenaCourt: "{arena}のバスケットボールコート", arenaRinkAbout: "アイスホッケーのリンク。{length}フィート(約{lengthM}m)×{width}フィート(約{widthM}m)で角が丸く、レッドライン、ブルーライン、フェイスオフサークル、クリース、ゴールがあります", arenaCourtAbout: "バスケットボールのコート。{length}フィート(約{lengthM}m)×{width}フィート(約{widthM}m)で、ペイントエリア、スリーポイントライン、フリースローサークル、センターサークルがあります", arenaSize: "広さは、{arena}の公表されている数値をもとに描かれています。", arenaUnknown: "{arena}のリンクはNHLの規格より短かったとされていますが、広さが示されていないため、NHLの規格で描かれています。", era: "ラインやマークは{season}シーズンのものです。", hammer: "{name}がハンマー(最後の1投)を持っています。", ends: "{name}のエンドごとの得点:{list}。", total: "合計", wonPens: "{name}がPK戦を{a}-{b}で制しました。", pens: "PK戦 {a}-{b}", on: "図の内容", sep: "、", stop: "。", colon: ":", gap: "" },
|
|
761
|
+
said: { home: "ホーム", away: "アウェイ", won: "{name}の勝ちです。", tied: "同点です。", park: "外野フェンスは、{park}の公表されている距離をもとに描かれています。", officially: "正式名称は{name}です。", ground: "ピッチは、{ground}の公表されている広さをもとに描かれています。", groundDefault: "{ground}のピッチの広さは公表されていないため、国際標準の105×68mで描かれています。", arenaRink: "{arena}のアイスホッケーリンク", arenaCourt: "{arena}のバスケットボールコート", arenaRinkAbout: "アイスホッケーのリンク。{length}フィート(約{lengthM}m)×{width}フィート(約{widthM}m)で角が丸く、レッドライン、ブルーライン、フェイスオフサークル、クリース、ゴールがあります", arenaCourtAbout: "バスケットボールのコート。{length}フィート(約{lengthM}m)×{width}フィート(約{widthM}m)で、ペイントエリア、スリーポイントライン、フリースローサークル、センターサークルがあります", arenaSize: "広さは、{arena}の公表されている数値をもとに描かれています。", arenaUnknown: "{arena}のリンクはNHLの規格より短かったとされていますが、広さが示されていないため、NHLの規格で描かれています。", real: "スタンドは、OpenStreetMapの建物の輪郭((c) OpenStreetMap contributors)の内側に収まるように、{arena}の{seats}席・{tiers}層を推定して描いたもので、アリーナの設計図ではありません。", realBuilding: "リンクは建物の長い軸に沿って置いており(推定)、矢印は北を指します。", realMapped: "リンクの向きはOpenStreetMapに記録された氷面のとおりで、矢印は北を指します。", realNone: "リンクの向きは分かっていないため、東西に置き、北を上にしています。", era: "ラインやマークは{season}シーズンのものです。", hammer: "{name}がハンマー(最後の1投)を持っています。", ends: "{name}のエンドごとの得点:{list}。", total: "合計", wonPens: "{name}がPK戦を{a}-{b}で制しました。", pens: "PK戦 {a}-{b}", on: "図の内容", sep: "、", stop: "。", colon: ":", gap: "" },
|
|
762
762
|
budo: {
|
|
763
763
|
sides: { north: "北", south: "南", east: "東", west: "西" },
|
|
764
764
|
judo: "柔道の試合場。試合場内は{contest}m四方、幅{safety}mの安全地帯があり、{tatami}の畳を敷いています",
|
package/dist/turn.model.js
CHANGED
|
@@ -89,7 +89,9 @@ export function buildTurn(options, settings = {}) {
|
|
|
89
89
|
const reach = uprights.reach;
|
|
90
90
|
const margin = reach === undefined ? undefined : convertLength(reach, "ft", lengthUnit(asked));
|
|
91
91
|
const own = asked.margin;
|
|
92
|
-
|
|
92
|
+
// (A real venue's building stays on the floor, with the arrow that says where north is: the crowd drawn without its seats.)
|
|
93
|
+
const keep = resolveCrowd(crowdOpt)?.real === true ? { real: true, stands: false } : undefined;
|
|
94
|
+
const base = reach !== undefined && crowdOpt !== undefined && crowdOpt !== false ? { ...asked, crowd: keep, margin: Math.max(margin ?? 0, typeof own === "number" && Number.isFinite(own) ? own : 0) } : asked;
|
|
93
95
|
const o = boarded ? { ...base, plate: false, size: asked.size === "compact" ? "full" : asked.size } : base;
|
|
94
96
|
const floor = settings.floor === undefined ? drawKyogijo(o) : settings.floor(o);
|
|
95
97
|
const found = VIEWBOX.exec(floor);
|
package/dist/turn.style.js
CHANGED
|
@@ -66,6 +66,7 @@ export const KYOGIJO_TURN_STYLE = `
|
|
|
66
66
|
.ky-turn .u-th{fill:var(--ky-cr-home)}
|
|
67
67
|
.ky-turn .u-conc{fill:var(--ky-cr-conc)}
|
|
68
68
|
.ky-turn .u-fascia{fill:var(--ky-up-fascia)}
|
|
69
|
+
.ky-turn .u-shell{fill:var(--ky-up-fascia);stroke:var(--ky-up-roofline);stroke-width:.6}
|
|
69
70
|
.ky-turn .u-roof{fill:var(--ky-up-roof);stroke:var(--ky-up-roofline);stroke-width:.8;stroke-linejoin:round}
|
|
70
71
|
.ky-turn .ky-up-seats svg{opacity:var(--ky-cr-dim)}
|
|
71
72
|
.ky-turn .ky-cr-seat{fill:none;stroke:var(--ky-cr-empty);stroke-width:1.3;stroke-opacity:.65}
|
package/dist/types.d.ts
CHANGED
|
@@ -284,6 +284,14 @@ export interface CrowdOptions {
|
|
|
284
284
|
* ballpark's outfield with a gap at the batter's eye) or `grandstand` (a ballpark's infield stands only). Each sport has its usual one.
|
|
285
285
|
*/
|
|
286
286
|
shape?: CrowdShape;
|
|
287
|
+
/**
|
|
288
|
+
* The stands of the arena's real venue, for an NHL `arena` the package has one of (`VENUES`, `@johnmorrisdotca/kyogijo-stadiums/venues`): the outline of its building from OpenStreetMap, the rink lying along
|
|
289
|
+
* the building as it does in the world (a small arrow says where north is), and a bowl of the number of tiers and the seats the sources give, fitted inside the building, under its kind of roof. An arena with no venue
|
|
290
|
+
* is drawn with the usual stands. Off by default. The drawing needs the credit `VENUE_ATTRIBUTION`. See `docs/venues.md`.
|
|
291
|
+
*/
|
|
292
|
+
real?: boolean;
|
|
293
|
+
/** `false` draws no seats: of a real venue (`real`) only its building and the arrow that says where north is, which is what the floor of the 3D view needs under the stands it raises. On by default. */
|
|
294
|
+
stands?: boolean;
|
|
287
295
|
/** The end of a `horseshoe` that is open: `right` (the usual) or `left`. */
|
|
288
296
|
open?: "left" | "right";
|
|
289
297
|
/**
|
package/dist/venue.js
CHANGED
|
@@ -6,7 +6,8 @@ import { densityOf, resolveCrowd } from "./crowd.js";
|
|
|
6
6
|
import { patterns } from "./crowd.draw.js";
|
|
7
7
|
import { crowdStands } from "./draw.js";
|
|
8
8
|
import { groundById } from "./grounds.js";
|
|
9
|
-
import { clipAround, clipConvex, clipSegment, areaOf, pointList, raked, slopeOf, stripsOf, tidy } from "./venue.geom.js";
|
|
9
|
+
import { ARC_STEPS, clipAround, clipConvex, clipSegment, areaOf, pointList, raked, slopeOf, stripsOf, tidy } from "./venue.geom.js";
|
|
10
|
+
import { realBuilding } from "./venue.real.js";
|
|
10
11
|
import { n, shy, wall } from "./turn.kit.js";
|
|
11
12
|
export const NO_VENUE = { items: [], defs: "", reach: 0 };
|
|
12
13
|
/** The sports played under a roof: their stands have a lid and not a canopy. */
|
|
@@ -45,7 +46,7 @@ export function standsVenueOf(stands, crowd, o, v, lift) {
|
|
|
45
46
|
const density = densityOf(crowd, stands.seats, groundById(o.ground)?.capacity);
|
|
46
47
|
const tifo = { home: stands.sectors.some((s) => s.kind === "tifo-home"), away: stands.sectors.some((s) => s.kind === "tifo-away") };
|
|
47
48
|
const pid = `${v.id}-v`;
|
|
48
|
-
const defs = fine ? patterns(pid, crowd, density, tifo.home || tifo.away ? tifo : undefined,
|
|
49
|
+
const defs = fine ? patterns(pid, crowd, density, tifo.home || tifo.away ? tifo : undefined, stands.real !== undefined, true) : "";
|
|
49
50
|
const centre = [stands.box.x + stands.box.w / 2, stands.box.y + stands.box.h / 2];
|
|
50
51
|
const covers = stands.covers;
|
|
51
52
|
const inCover = (p) => covers.some(([x0, y0, x1, y1]) => p[0] >= x0 && p[0] <= x1 && p[1] >= y0 && p[1] <= y1);
|
|
@@ -148,8 +149,10 @@ export function standsVenueOf(stands, crowd, o, v, lift) {
|
|
|
148
149
|
items.push({ id: "stands", kind: "stands", at: centre, height: topOf(seatPlanes), planes: seatPlanes });
|
|
149
150
|
if (wallSeats.length > 0)
|
|
150
151
|
items.push({ id: "wall-seats", kind: "wall-seats", at: centre, height: topOf(wallSeats), planes: wallSeats });
|
|
151
|
-
// The roof: a canopy over the back of the top tier, or a lid over it in a hall.
|
|
152
|
-
|
|
152
|
+
// The roof: a canopy over the back of the top tier, or a lid over it in a hall. A real venue has its own: the walls of its building and a roof of its kind over them.
|
|
153
|
+
if (stands.real !== undefined && v.roof === true)
|
|
154
|
+
items.push(...realBuilding(stands.real, topRing(stands), centre, lift, ARC_STEPS[cut].oval * 2));
|
|
155
|
+
const top = v.roof === true && crowd.roof && stands.real === undefined ? topRing(stands) : undefined;
|
|
153
156
|
if (top !== undefined) {
|
|
154
157
|
const lid = INDOOR.includes(String(o.sport));
|
|
155
158
|
const reach = lid ? 1 : ROOF.canopyReach;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Pt, type RealShell, type StandRing } from "./crowd.ts";
|
|
2
|
+
import { type Upright } from "./turn.kit.ts";
|
|
3
|
+
/** How far a lid rises from its edge towards the middle, and how far a saddle's high ends stand over its low sides, in feet (ours: the sources give a roof's kind, not its height). */
|
|
4
|
+
export declare const LID_RISE = 22;
|
|
5
|
+
export declare const SADDLE_RISE = 30;
|
|
6
|
+
/** The saddle's share of its rise at a point (0 at the middle of a side, 1 at the middle of an end): half a saddle's way from one to the other, as a hyperbolic paraboloid (u squared less v squared) lies over the building's box. */
|
|
7
|
+
export declare function saddleAt(real: Pick<RealShell, "footprint">, [x, y]: Pt): number;
|
|
8
|
+
/** The height of the roof over a point of the building's edge, above the surface, in feet. */
|
|
9
|
+
export declare const roofAt: (real: RealShell, p: Pt) => number;
|
|
10
|
+
/**
|
|
11
|
+
* The walls and the roof of a real venue. The hall is the building's outline cut to the bowl's wall and a margin (`RealShell.hall`): its rim is where a ray from the middle meets the one or the other first, in `steps`
|
|
12
|
+
* rays. A wall stands on each stretch of the rim, up to the roof's height over it; the roof is a band from the rim in to the inner edge of the top tier (the middle stays open, as the 3D view's roofs are).
|
|
13
|
+
*/
|
|
14
|
+
export declare function realBuilding(real: RealShell, top: StandRing | undefined, centre: readonly [number, number], lift: number, steps: number): Items;
|
|
15
|
+
type Items = Upright[];
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// The building of a real venue, raised: its walls, standing on the outline OpenStreetMap gives, and its roof over the top tier, a lid that rises towards the middle or a saddle that rises at the two ends and falls at the two
|
|
2
|
+
// sides. Both are asked for with the roof (`venue: { roof: true }`) and give way to the eye as the other walls do. Pure: data in, planes out.
|
|
3
|
+
import { rayAt } from "./crowd.js";
|
|
4
|
+
import { n, shy, wall } from "./turn.kit.js";
|
|
5
|
+
import { pointList, raked } from "./venue.geom.js";
|
|
6
|
+
/** How far a lid rises from its edge towards the middle, and how far a saddle's high ends stand over its low sides, in feet (ours: the sources give a roof's kind, not its height). */
|
|
7
|
+
export const LID_RISE = 22;
|
|
8
|
+
export const SADDLE_RISE = 30;
|
|
9
|
+
/** The saddle's share of its rise at a point (0 at the middle of a side, 1 at the middle of an end): half a saddle's way from one to the other, as a hyperbolic paraboloid (u squared less v squared) lies over the building's box. */
|
|
10
|
+
export function saddleAt(real, [x, y]) {
|
|
11
|
+
const xs = real.footprint.map((p) => p[0]);
|
|
12
|
+
const ys = real.footprint.map((p) => p[1]);
|
|
13
|
+
const [cx, cy] = [(Math.min(...xs) + Math.max(...xs)) / 2, (Math.min(...ys) + Math.max(...ys)) / 2];
|
|
14
|
+
const [hx, hy] = [(Math.max(...xs) - Math.min(...xs)) / 2, (Math.max(...ys) - Math.min(...ys)) / 2];
|
|
15
|
+
const s = ((x - cx) / hx) ** 2 - ((y - cy) / hy) ** 2;
|
|
16
|
+
return (1 + Math.max(-1, Math.min(1, s))) / 2;
|
|
17
|
+
}
|
|
18
|
+
/** The height of the roof over a point of the building's edge, above the surface, in feet. */
|
|
19
|
+
export const roofAt = (real, p) => real.edge + (real.roof === "saddle" ? SADDLE_RISE * saddleAt(real, p) : 0);
|
|
20
|
+
/** The point where a ray from `from` first meets a polygon, or `undefined`. */
|
|
21
|
+
function firstHit(poly, from, angle) {
|
|
22
|
+
const [dx, dy] = [Math.cos(angle), Math.sin(angle)];
|
|
23
|
+
let best;
|
|
24
|
+
poly.forEach(([ax, ay], i) => {
|
|
25
|
+
const [bx, by] = poly[(i + 1) % poly.length];
|
|
26
|
+
const [ex, ey] = [bx - ax, by - ay];
|
|
27
|
+
const den = dx * ey - dy * ex;
|
|
28
|
+
if (Math.abs(den) < 1e-12)
|
|
29
|
+
return;
|
|
30
|
+
const t = ((ax - from[0]) * ey - (ay - from[1]) * ex) / den;
|
|
31
|
+
const u = ((ax - from[0]) * dy - (ay - from[1]) * dx) / den;
|
|
32
|
+
if (t > 1e-6 && u >= 0 && u <= 1 && (best === undefined || t < best.t))
|
|
33
|
+
best = { t, p: [from[0] + dx * t, from[1] + dy * t] };
|
|
34
|
+
});
|
|
35
|
+
return best?.p;
|
|
36
|
+
}
|
|
37
|
+
/** The plane a quad of points with heights lies nearest to, as a slope: least squares through its corners. */
|
|
38
|
+
function slopeThrough(points) {
|
|
39
|
+
const m = points.length;
|
|
40
|
+
const [mx, my, mz] = [points.reduce((s, p) => s + p[0], 0) / m, points.reduce((s, p) => s + p[1], 0) / m, points.reduce((s, p) => s + p[2], 0) / m];
|
|
41
|
+
let [sxx, sxy, syy, sxz, syz] = [0, 0, 0, 0, 0];
|
|
42
|
+
for (const [x, y, z] of points) {
|
|
43
|
+
sxx += (x - mx) ** 2;
|
|
44
|
+
sxy += (x - mx) * (y - my);
|
|
45
|
+
syy += (y - my) ** 2;
|
|
46
|
+
sxz += (x - mx) * (z - mz);
|
|
47
|
+
syz += (y - my) * (z - mz);
|
|
48
|
+
}
|
|
49
|
+
const det = sxx * syy - sxy * sxy;
|
|
50
|
+
const [gx, gy] = Math.abs(det) < 1e-9 ? [0, 0] : [(sxz * syy - syz * sxy) / det, (syz * sxx - sxz * sxy) / det];
|
|
51
|
+
return { x0: mx, y0: my, z0: mz, gx, gy };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The walls and the roof of a real venue. The hall is the building's outline cut to the bowl's wall and a margin (`RealShell.hall`): its rim is where a ray from the middle meets the one or the other first, in `steps`
|
|
55
|
+
* rays. A wall stands on each stretch of the rim, up to the roof's height over it; the roof is a band from the rim in to the inner edge of the top tier (the middle stays open, as the 3D view's roofs are).
|
|
56
|
+
*/
|
|
57
|
+
export function realBuilding(real, top, centre, lift, steps) {
|
|
58
|
+
const items = [];
|
|
59
|
+
const c = [centre[0], centre[1]];
|
|
60
|
+
const rim = Array.from({ length: steps }, (_, k) => {
|
|
61
|
+
const angle = (2 * Math.PI * k) / steps;
|
|
62
|
+
const cap = rayAt(real.hall, c[0], c[1], angle);
|
|
63
|
+
const out = firstHit(real.footprint, c, angle);
|
|
64
|
+
return out !== undefined && Math.hypot(out[0] - c[0], out[1] - c[1]) < Math.hypot(cap[0] - c[0], cap[1] - c[1]) ? out : cap;
|
|
65
|
+
});
|
|
66
|
+
const planes = [];
|
|
67
|
+
rim.forEach((a, i) => {
|
|
68
|
+
const b = rim[(i + 1) % steps];
|
|
69
|
+
const long = Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
70
|
+
if (long < 1)
|
|
71
|
+
return;
|
|
72
|
+
const high = lift + roofAt(real, [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2]);
|
|
73
|
+
planes.push({ ...shy(wall(`shell-${i}`, "wall", a[0], a[1], b[0], b[1], 0, high, `<rect class="u-shell" width="${n(long)}" height="${n(high)}"/>`), centre), fadeTo: 0.02 });
|
|
74
|
+
});
|
|
75
|
+
if (planes.length > 0)
|
|
76
|
+
items.push({ id: "shell", kind: "walls", at: centre, height: Math.max(...planes.map((p) => p.o[2])), planes });
|
|
77
|
+
if (top?.hole == null)
|
|
78
|
+
return items;
|
|
79
|
+
const roof = [];
|
|
80
|
+
const hole = top.hole;
|
|
81
|
+
const inner = rim.map((_, k) => rayAt(hole, c[0], c[1], (2 * Math.PI * k) / steps));
|
|
82
|
+
const rise = real.roof === "saddle" ? 0 : LID_RISE;
|
|
83
|
+
const zOf = (p, inward) => lift + roofAt(real, p) + (inward ? rise : 0);
|
|
84
|
+
for (let k = 0; k < steps; k += 1) {
|
|
85
|
+
const [a, b] = [rim[k], rim[(k + 1) % steps]];
|
|
86
|
+
const [ai, bi] = [inner[k], inner[(k + 1) % steps]];
|
|
87
|
+
const quad = [ai, bi, b, a];
|
|
88
|
+
const heights = [zOf(ai, true), zOf(bi, true), zOf(b, false), zOf(a, false)];
|
|
89
|
+
const slope = slopeThrough(quad.map((p, i) => [p[0], p[1], heights[i]]));
|
|
90
|
+
const [ex, ey] = [b[0] - a[0], b[1] - a[1]];
|
|
91
|
+
const el = Math.hypot(ex, ey);
|
|
92
|
+
if (el < 1e-6)
|
|
93
|
+
continue;
|
|
94
|
+
const mid = [(a[0] + b[0] + ai[0] + bi[0]) / 4, (a[1] + b[1] + ai[1] + bi[1]) / 4];
|
|
95
|
+
const away = Math.hypot(mid[0] - c[0], mid[1] - c[1]);
|
|
96
|
+
const piece = raked(`roof-${k}`, "roof", quad, slope, [ex / el, ey / el], (corners) => `<polygon class="u-roof" points="${pointList(corners)}"/>`);
|
|
97
|
+
if (piece !== undefined)
|
|
98
|
+
roof.push({ ...piece, shy: [(mid[0] - c[0]) / away, (mid[1] - c[1]) / away] });
|
|
99
|
+
}
|
|
100
|
+
if (roof.length > 0)
|
|
101
|
+
items.push({ id: "roof", kind: "roof", at: centre, height: Math.max(...roof.flatMap((p) => [p.o[2], p.o[2] + p.u[2], p.o[2] + p.v[2], p.o[2] + p.u[2] + p.v[2]])), planes: roof });
|
|
102
|
+
return items;
|
|
103
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { VENUE_ATTRIBUTION, VENUE_IDS, VENUES, arenaOfVenue, bowlInside, planVenue, venueBowl, venueById, venueByWikidata, venueOrientation, venueRink, venuesIn } from "./venues.ts";
|
|
2
|
+
export type { RealVenue, RealVenueData, VenueBasis, VenueDivision, VenueOrientation, VenueOsm, VenuePlan, VenueRoof } from "./venues.ts";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// The real venues of the NHL's arenas: the outline of each building and the way its rink lies (OpenStreetMap, ODbL 1.0) and a plausible bowl from the seats, tiers and roof the sources give (Wikidata, Wikipedia).
|
|
2
|
+
export { VENUE_ATTRIBUTION, VENUE_IDS, VENUES, arenaOfVenue, bowlInside, planVenue, venueBowl, venueById, venueByWikidata, venueOrientation, venueRink, venuesIn } from "./venues.js";
|
package/dist/venues.d.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { type Arena, type ArenaSource } from "./arenas.ts";
|
|
2
|
+
import { type Box, type Pt, type RealBowl } from "./crowd.ts";
|
|
3
|
+
import { VENUE_ATTRIBUTION } from "./venues.osm.data.ts";
|
|
4
|
+
export { VENUE_ATTRIBUTION };
|
|
5
|
+
/** How a fact is known: `documented` is what a source cited with it says; `plausible` is ours, for a bowl no source describes. */
|
|
6
|
+
export type VenueBasis = "documented" | "plausible";
|
|
7
|
+
/** The kinds of roof the venues have: `fixed` is a lid over the bowl, `saddle` a roof that rises at the two ends and falls at the two sides (the Scotiabank Saddledome's). */
|
|
8
|
+
export type VenueRoof = "fixed" | "saddle";
|
|
9
|
+
/** The NHL's divisions that have venues so far. */
|
|
10
|
+
export type VenueDivision = "pacific";
|
|
11
|
+
/** The facts of a venue: the seats and the number of seating levels and the kind of roof, each with what a source says. No text of an article is kept. */
|
|
12
|
+
export interface RealVenueData {
|
|
13
|
+
/** The key of the arena in `ARENAS`: `honda-center`. */
|
|
14
|
+
id: string;
|
|
15
|
+
/** The Wikidata item of the arena. */
|
|
16
|
+
wikidata: string;
|
|
17
|
+
division: VenueDivision;
|
|
18
|
+
/** The English Wikipedia article the facts that are not Wikidata's were read from, at a revision. */
|
|
19
|
+
en: ArenaSource;
|
|
20
|
+
seats: {
|
|
21
|
+
/** The seats for ice hockey: Wikidata's where its item gives a figure for ice hockey (`wikidata`), else the article's (`wikipedia`). */
|
|
22
|
+
hockey: number;
|
|
23
|
+
source: "wikidata" | "wikipedia";
|
|
24
|
+
/** Wikidata's own capacity (P1083), kept beside it: it is often for another event, or an older year. */
|
|
25
|
+
wikidata: number;
|
|
26
|
+
/** What was read, in a few words of ours. */
|
|
27
|
+
read: string;
|
|
28
|
+
};
|
|
29
|
+
tiers: {
|
|
30
|
+
/** The seating levels (bowls), with the suites and the clubs that lie between them counted as walks, not tiers. */
|
|
31
|
+
count: 1 | 2 | 3;
|
|
32
|
+
basis: VenueBasis;
|
|
33
|
+
/** The share of the seats that are in the lowest tier, where a source gives it. */
|
|
34
|
+
lowerShare?: number;
|
|
35
|
+
read: string;
|
|
36
|
+
};
|
|
37
|
+
roof: {
|
|
38
|
+
kind: VenueRoof;
|
|
39
|
+
basis: VenueBasis;
|
|
40
|
+
read: string;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** What OpenStreetMap gave of a venue: ODbL 1.0 data, "(c) OpenStreetMap contributors". */
|
|
44
|
+
export interface VenueOsm {
|
|
45
|
+
/** The element the outline is: `way/120623151`. */
|
|
46
|
+
source: string;
|
|
47
|
+
/** The day it was read and the time of the OpenStreetMap data the answer was made from. */
|
|
48
|
+
read: string;
|
|
49
|
+
base: string;
|
|
50
|
+
licence: "ODbL-1.0";
|
|
51
|
+
attribution: string;
|
|
52
|
+
/** The building's height in metres, where OpenStreetMap maps one. */
|
|
53
|
+
height?: number;
|
|
54
|
+
/** The building's outline as `[longitude, latitude]`, not repeating its first point. */
|
|
55
|
+
ring: [number, number][];
|
|
56
|
+
/** The ice, where OpenStreetMap maps it inside the building: the element and its outline. */
|
|
57
|
+
ice?: {
|
|
58
|
+
source: string;
|
|
59
|
+
ring: [number, number][];
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* How the rink lies, worked out when the data was made: the heading of its long side (degrees clockwise from north, 0 to under 180), how that is known (`mapped`: from the ice OpenStreetMap maps; `building`: from the long
|
|
63
|
+
* axis of the building's main hall, the largest rectangle that fits its outline, which a rink usually follows, an inference; `none`: the building has no direction, 90 is stood in), how much of a direction the building has
|
|
64
|
+
* (`strength`, 1 for a round one) and how much longer than wide its main hall is (`elongation`), and the middle of the ice or of the main hall as `[longitude, latitude]`.
|
|
65
|
+
*/
|
|
66
|
+
axis: {
|
|
67
|
+
heading: number;
|
|
68
|
+
basis: "mapped" | "building" | "none";
|
|
69
|
+
strength: number;
|
|
70
|
+
elongation: number;
|
|
71
|
+
centre: [number, number];
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** A venue: its facts and what OpenStreetMap gave. */
|
|
75
|
+
export interface RealVenue extends RealVenueData {
|
|
76
|
+
osm: VenueOsm;
|
|
77
|
+
}
|
|
78
|
+
/** How the rink lies in the building: its long side's heading (degrees clockwise from north, 0 to under 180), how that is known, and the way north points on the page (degrees clockwise from the top). */
|
|
79
|
+
export interface VenueOrientation {
|
|
80
|
+
heading: number;
|
|
81
|
+
/** `mapped`: from the ice OpenStreetMap maps in the building. `building`: from the building's long axis, which a rink usually follows (an inference). `none`: the building is near round, nothing is known, the plan has north at the top. */
|
|
82
|
+
basis: "mapped" | "building" | "none";
|
|
83
|
+
north: number;
|
|
84
|
+
}
|
|
85
|
+
/** The venues by key: every arena of the Pacific division of the NHL, with its facts and its outline. */
|
|
86
|
+
export declare const VENUES: Readonly<Record<string, RealVenue>>;
|
|
87
|
+
/** The keys of `VENUES`, in the order of the data. */
|
|
88
|
+
export declare const VENUE_IDS: readonly string[];
|
|
89
|
+
/** The venue of an arena (by its key in `ARENAS`), or `undefined`. */
|
|
90
|
+
export declare const venueById: (id: string | undefined) => RealVenue | undefined;
|
|
91
|
+
/** The venue whose arena is the Wikidata item `Q774138`, or `undefined`. */
|
|
92
|
+
export declare const venueByWikidata: (wikidata: string | undefined) => RealVenue | undefined;
|
|
93
|
+
/** The venues of a division of the NHL, in the order of the data. */
|
|
94
|
+
export declare const venuesIn: (division: VenueDivision) => RealVenue[];
|
|
95
|
+
/** The arena a venue is of. */
|
|
96
|
+
export declare const arenaOfVenue: (venue: RealVenue) => Arena;
|
|
97
|
+
/** How the rink lies in a venue's building (see `VenueOsm.axis`). */
|
|
98
|
+
export declare const venueOrientation: (venue: RealVenue) => VenueOrientation;
|
|
99
|
+
/** The rink's box as the drawing has it: the arena's own where its sources publish a size, else the NHL's. */
|
|
100
|
+
export declare function venueRink(venue: RealVenue): Box;
|
|
101
|
+
/** The floor a seat takes in plan, in square feet (ours): a bowl of today's arenas is steeper than the usual drawing's, whose seat is 2 by 2.3 feet (4.6), so that a seat takes less of the floor's area. Tried in turn: a building
|
|
102
|
+
* with room for the seats at the first has the first; one with less has a steeper bowl, to the last, which is the least a seat takes. */
|
|
103
|
+
export declare const SEAT_AREAS: readonly number[];
|
|
104
|
+
/** How far the bowl's outer edge keeps from the building's wall, in feet (ours: the wall and the walk round the bowl). */
|
|
105
|
+
export declare const WALL = 10;
|
|
106
|
+
/** How far the hall's wall stands beyond the bowl's outer edge, in feet, where the building's outline allows: the concourse and the wall, ours. */
|
|
107
|
+
export declare const HALL = 30;
|
|
108
|
+
/** The walk between two tiers, in feet (ours: a suite level or a club between two bowls). */
|
|
109
|
+
export declare const GAP = 10;
|
|
110
|
+
/** The height of the first row above the ice, in feet, the rise of a tier's rows by the number of tiers (what the lowest, a middle and the upper bowl rise, at most), the rise of the walk between two tiers (a suite level), and the steepest a bowl is raked, in degrees (ours: no source gives them). */
|
|
111
|
+
export declare const FIRST_HEIGHT = 4;
|
|
112
|
+
export declare const RISES: Readonly<Record<1 | 2 | 3, readonly number[]>>;
|
|
113
|
+
export declare const WALK_RISE = 16;
|
|
114
|
+
export declare const MAX_RAKE = 36;
|
|
115
|
+
/** The height a roof's edge stands over the top row, at the least, in feet. */
|
|
116
|
+
export declare const CLEARANCE = 22;
|
|
117
|
+
/** The plan of a venue: its building in the drawing's frame, the bowl fitted inside, and how it lies. */
|
|
118
|
+
export interface VenuePlan {
|
|
119
|
+
venue: RealVenue;
|
|
120
|
+
orientation: VenueOrientation;
|
|
121
|
+
/** The rink, in the drawing's frame. */
|
|
122
|
+
box: Box;
|
|
123
|
+
/** The building's outline in feet, in the drawing's frame (the rink along x, in the middle of the building's box). */
|
|
124
|
+
footprint: Pt[];
|
|
125
|
+
/** The stands for `layoutOf`: tiers, first row, ground, and the building and roof. */
|
|
126
|
+
bowl: RealBowl;
|
|
127
|
+
/** The seats the bowl's tiers hold, by tier, and in all: equal to the venue's seats where the building has room (`fit` is `fits`) and fewer where it has not (`tight`). */
|
|
128
|
+
seats: {
|
|
129
|
+
tier: string;
|
|
130
|
+
seats: number;
|
|
131
|
+
}[];
|
|
132
|
+
total: number;
|
|
133
|
+
/** The floor a seat takes in the plan, in square feet (one of `SEAT_AREAS`). */
|
|
134
|
+
seatArea: number;
|
|
135
|
+
fit: "fits" | "tight";
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The plan of a venue's bowl: the building's outline turned so that the rink lies along x, a first row round the rink as an NHL arena has it, the largest bowl-shaped outline that fits the building round the middle of its main
|
|
139
|
+
* hall, and between the two the tiers, the outer edge of each placed so that its band holds the share of the venue's seats the sources give it (at `SEAT_AREAS` square feet a seat), with a walk between the tiers. The
|
|
140
|
+
* bowl is as long and wide as the building lets it be and as round at the corners as a bowl is; the rest is the building's.
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* import { planVenue, venueById } from "@johnmorrisdotca/kyogijo-stadiums/venues";
|
|
144
|
+
* const plan = planVenue(venueById("honda-center")!);
|
|
145
|
+
* console.log(plan.orientation.basis, plan.seats.map((t) => t.tier).join(" "), plan.total);
|
|
146
|
+
* // building lower upper 17174
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export declare function planVenue(venue: RealVenue, rink?: Box): VenuePlan;
|
|
150
|
+
/** The stands of a venue for `layoutOf` and the 3D view, round a rink: `planVenue`'s bowl. */
|
|
151
|
+
export declare const venueBowl: (venue: RealVenue, rink?: Box) => RealBowl;
|
|
152
|
+
/** Whether the bowl lies inside the building: every corner of every tier's outer edge is inside the footprint. */
|
|
153
|
+
export declare function bowlInside(plan: VenuePlan): boolean;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const VENUE_DATA = [
|
|
2
|
+
{
|
|
3
|
+
"id": "honda-center",
|
|
4
|
+
"wikidata": "Q774138",
|
|
5
|
+
"division": "pacific",
|
|
6
|
+
"en": { "title": "Honda Center", "revision": 1380000927 },
|
|
7
|
+
"seats": { "hockey": 17174, "source": "wikipedia", "wikidata": 17174, "read": "the article's seating line for the Ducks, 17,174, which the infobox gives for ice hockey too; Wikidata's one capacity (P1083) is the same" },
|
|
8
|
+
"tiers": { "count": 2, "basis": "plausible", "read": "no source says how many seating levels there are: two bowls (a lower and an upper) are drawn, the usual for an arena of 17,000 seats; the article speaks of two full levels of ribbon displays, one on each bowl's fascia, which agrees" },
|
|
9
|
+
"roof": { "kind": "fixed", "basis": "plausible", "read": "an indoor arena: a fixed roof, as nothing says otherwise" },
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "scotiabank-saddledome",
|
|
13
|
+
"wikidata": "Q1140083",
|
|
14
|
+
"division": "pacific",
|
|
15
|
+
"en": { "title": "Scotiabank Saddledome", "revision": 1377341466 },
|
|
16
|
+
"seats": { "hockey": 19289, "source": "wikipedia", "wikidata": 16605, "read": "the infobox's present figure for hockey (19,289, from 1995), which the article's amenities line gives too; Wikidata's 16,605 is the 1983 figure and the first the infobox lists" },
|
|
17
|
+
"tiers": { "count": 2, "basis": "documented", "read": "the article speaks of the lower bowl, a second level and upper loges above it: two seating levels with suites at the top of the lower bowl" },
|
|
18
|
+
"roof": { "kind": "saddle", "basis": "documented", "read": "the article says the roof is a reverse hyperbolic paraboloid, shaped like a horse saddle; which way the saddle lies over the ice is not given: it is drawn high at the two ends" },
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "rogers-place",
|
|
22
|
+
"wikidata": "Q5339036",
|
|
23
|
+
"division": "pacific",
|
|
24
|
+
"en": { "title": "Rogers Place", "revision": 1380110483 },
|
|
25
|
+
"seats": { "hockey": 18641, "source": "wikidata", "wikidata": 18641, "read": "Wikidata's capacity for ice hockey (P1083 with the sport P641 ice hockey); the article's own line gives 18,347 for a hockey arena, 1.6 percent fewer, and OpenStreetMap has 18,641" },
|
|
26
|
+
"tiers": { "count": 2, "basis": "plausible", "read": "no source says how many seating levels there are: two bowls are drawn" },
|
|
27
|
+
"roof": { "kind": "fixed", "basis": "plausible", "read": "an indoor arena: a fixed roof, as nothing says otherwise" },
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "crypto-com-arena",
|
|
31
|
+
"wikidata": "Q186080",
|
|
32
|
+
"division": "pacific",
|
|
33
|
+
"en": { "title": "Crypto.com Arena", "revision": 1380458624 },
|
|
34
|
+
"seats": { "hockey": 18145, "source": "wikipedia", "wikidata": 19060, "read": "the article's figure for ice hockey, 18,145, which the infobox gives too; Wikidata's one capacity (19,060) is not for hockey" },
|
|
35
|
+
"tiers": { "count": 2, "basis": "documented", "lowerShare": 0.667, "read": "the article says two thirds of the seating, with the club seats, is in the lower bowl, and that three levels of suites lie between the lower and upper bowls" },
|
|
36
|
+
"roof": { "kind": "fixed", "basis": "plausible", "read": "an indoor arena: a fixed roof, as nothing says otherwise" },
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "sap-center",
|
|
40
|
+
"wikidata": "Q261863",
|
|
41
|
+
"division": "pacific",
|
|
42
|
+
"en": { "title": "SAP Center", "revision": 1380804230 },
|
|
43
|
+
"seats": { "hockey": 17435, "source": "wikipedia", "wikidata": 19190, "read": "the article says the hockey capacity changed from 17,562 to 17,435 when the new premium seating was finished (2023); Wikidata's one capacity (19,190) is the concert one" },
|
|
44
|
+
"tiers": { "count": 2, "basis": "plausible", "read": "no source says how many seating levels there are: two bowls are drawn; the article speaks of club levels and a penthouse, which are not seating bowls" },
|
|
45
|
+
"roof": { "kind": "fixed", "basis": "plausible", "read": "an indoor arena: a fixed roof, as nothing says otherwise" },
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "climate-pledge-arena",
|
|
49
|
+
"wikidata": "Q977529",
|
|
50
|
+
"division": "pacific",
|
|
51
|
+
"en": { "title": "Climate Pledge Arena", "revision": 1380851727 },
|
|
52
|
+
"seats": { "hockey": 17151, "source": "wikipedia", "wikidata": 18000, "read": "the article's seating capacity for ice hockey, 17,151, which the infobox gives too; Wikidata's one capacity (18,000) is near the basketball one (18,300)" },
|
|
53
|
+
"tiers": { "count": 2, "basis": "documented", "read": "the article tells of the upper level and the lower level, most of the lower level being below street level since the 2021 rebuild: two seating levels" },
|
|
54
|
+
"roof": { "kind": "fixed", "basis": "documented", "read": "the article says the original 1962 roof's shape was kept and carried on a fixed roof of steel trusses (the first, cable-hung, roof being taken down), and that the roof is landmarked" },
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": "rogers-arena",
|
|
58
|
+
"wikidata": "Q517318",
|
|
59
|
+
"division": "pacific",
|
|
60
|
+
"en": { "title": "Rogers Arena", "revision": 1379918849 },
|
|
61
|
+
"seats": { "hockey": 18871, "source": "wikipedia", "wikidata": 18422, "read": "the infobox's present figure for ice hockey (18,871, from 2018); Wikidata's 18,422 is the 1995 figure and the first the infobox lists" },
|
|
62
|
+
"tiers": { "count": 2, "basis": "plausible", "read": "no source says how many seating levels there are: two bowls are drawn; the article speaks of a lower bowl; OpenStreetMap maps four building levels, which are floors, not seating bowls" },
|
|
63
|
+
"roof": { "kind": "fixed", "basis": "plausible", "read": "an indoor arena: a fixed roof, as nothing says otherwise" },
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "t-mobile-arena",
|
|
67
|
+
"wikidata": "Q3338973",
|
|
68
|
+
"division": "pacific",
|
|
69
|
+
"en": { "title": "T-Mobile Arena", "revision": 1379155307 },
|
|
70
|
+
"seats": { "hockey": 17500, "source": "wikipedia", "wikidata": 20000, "read": "the infobox's figure for ice hockey, 17,500 (20,000 with standing room); Wikidata's one capacity (20,000) is the one with standing room" },
|
|
71
|
+
"tiers": { "count": 2, "basis": "plausible", "read": "no source says how many seating levels there are: two bowls are drawn" },
|
|
72
|
+
"roof": { "kind": "fixed", "basis": "plausible", "read": "an indoor arena: a fixed roof, as nothing says otherwise" },
|
|
73
|
+
},
|
|
74
|
+
];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Pt } from "./crowd.ts";
|
|
2
|
+
/** A point as `[longitude, latitude]`, in degrees, the order OpenStreetMap's GeoJSON gives. */
|
|
3
|
+
export type LonLat = readonly [number, number];
|
|
4
|
+
/** Points in degrees as `[east, north]` metres from `origin`, on a plane tangent to the earth there (good to a few centimetres over a building's size). */
|
|
5
|
+
export declare function metresFrom(origin: LonLat, ring: readonly LonLat[]): Pt[];
|
|
6
|
+
/** The inverse of `metresFrom`: a point given as east and north metres from `origin`, in degrees. */
|
|
7
|
+
export declare function lonLatFrom(origin: LonLat, [east, north]: Pt): LonLat;
|
|
8
|
+
/** The mean of a ring's longitudes and latitudes: a point to measure from. */
|
|
9
|
+
export declare const middleOf: (ring: readonly LonLat[]) => LonLat;
|
|
10
|
+
/** The area of a polygon, unsigned. */
|
|
11
|
+
export declare function areaOf(points: readonly Pt[]): number;
|
|
12
|
+
/** The centre of mass of a polygon's area. */
|
|
13
|
+
export declare function centroidOf(points: readonly Pt[]): Pt;
|
|
14
|
+
/** The convex hull of some points (Andrew's monotone chain), counter-clockwise. */
|
|
15
|
+
export declare function hullOf(points: readonly Pt[]): Pt[];
|
|
16
|
+
/** The heading, in degrees clockwise from north, of a direction given as east and north components, taken modulo 180 (an axis has no head): 0 to under 180. */
|
|
17
|
+
export declare const axisHeading: (east: number, north: number) => number;
|
|
18
|
+
/** The smallest rectangle (by area) that encloses some points: its long side's heading, its length and its width, in the units of the points. */
|
|
19
|
+
export declare function smallestBox(points: readonly Pt[]): {
|
|
20
|
+
heading: number;
|
|
21
|
+
length: number;
|
|
22
|
+
width: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* A point given as east and north metres, in the drawing's frame, for a rink whose long side runs along `heading` (degrees clockwise from north, 0 to under 180): x along the rink's long side, y across it and down the page,
|
|
26
|
+
* so that the picture is the map turned and not mirrored. Metres stay metres.
|
|
27
|
+
*/
|
|
28
|
+
export declare function turnedTo(heading: number, [east, north]: Pt): Pt;
|
|
29
|
+
/** The way north points in the drawing, in degrees clockwise from the top of the page, for a rink along `heading`. 90 less the heading: a rink that runs east-west has north at the top. */
|
|
30
|
+
export declare const northOn: (heading: number) => number;
|
|
31
|
+
/** A ring with points that lie within `tolerance` of the line between their neighbours taken out (Douglas and Peucker), the ends kept: a ring is a list of points not repeating its first. */
|
|
32
|
+
export declare function simplified(ring: readonly Pt[], tolerance: number): Pt[];
|
|
33
|
+
/** Whether a point is in a polygon (even-odd). */
|
|
34
|
+
export declare function inside(points: readonly Pt[], [x, y]: Pt): boolean;
|
|
35
|
+
/** The smallest box round some points. */
|
|
36
|
+
export declare function boxOf(points: readonly Pt[]): {
|
|
37
|
+
x0: number;
|
|
38
|
+
y0: number;
|
|
39
|
+
x1: number;
|
|
40
|
+
y1: number;
|
|
41
|
+
};
|
|
42
|
+
/** The distance from a point to the nearest edge of a polygon. */
|
|
43
|
+
export declare function edgeDistance(points: readonly Pt[], [x, y]: Pt): number;
|
|
44
|
+
/** What `coreOf` finds: the building's main hall, as the largest rectangle that lies inside its outline. */
|
|
45
|
+
export interface Core {
|
|
46
|
+
/** The heading of the rectangle's long side, 0 to under 180, degrees clockwise from north. */
|
|
47
|
+
heading: number;
|
|
48
|
+
/** The rectangle's long and short sides, in the units of the points. */
|
|
49
|
+
length: number;
|
|
50
|
+
width: number;
|
|
51
|
+
/** The middle of the rectangle, as east and north from the points' origin. */
|
|
52
|
+
centre: Pt;
|
|
53
|
+
/** How much the best direction beats the worst: the largest rectangle's area over the smallest, over every direction. A round building has 1; a building with a direction has more. */
|
|
54
|
+
strength: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The main hall of a building: the largest rectangle inside its outline over every direction (`STEP` degrees apart), which does not see a wing, a plaza or a ramp that a rectangle cannot fill. Its long side is the
|
|
58
|
+
* building's axis, its middle the middle of the hall, and `strength` how much of a direction the building has (a round one has none). Ties between a direction and the one at a right angle go to the longer rectangle.
|
|
59
|
+
*/
|
|
60
|
+
export declare function coreOf(points: readonly Pt[]): Core;
|