@johnmorrisdotca/kyogijo-stadiums 0.45.2 → 0.46.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 +71 -0
- package/NOTICE.md +9 -2
- package/dist/annotate.d.ts +1 -1
- package/dist/annotate.js +1 -1
- package/dist/areas.d.ts +4 -0
- package/dist/areas.js +33 -1
- package/dist/arenas-entry.d.ts +1 -0
- package/dist/arenas-entry.js +2 -0
- package/dist/athletics/describe.js +1 -1
- package/dist/athletics-entry.d.ts +1 -0
- package/dist/athletics-entry.js +2 -0
- package/dist/atlas-entry.d.ts +1 -0
- package/dist/atlas-entry.js +2 -0
- package/dist/ballpark.d.ts +1 -1
- package/dist/ballpark.js +1 -1
- package/dist/budo/describe.js +1 -1
- package/dist/budo-entry.d.ts +1 -0
- package/dist/budo-entry.js +2 -0
- package/dist/canvas-entry.d.ts +1 -0
- package/dist/canvas-entry.js +2 -0
- package/dist/charts-entry.d.ts +1 -0
- package/dist/charts-entry.js +2 -0
- package/dist/charts.athletics.js +1 -1
- package/dist/charts.baseball.js +1 -1
- package/dist/charts.curling.heat.js +1 -1
- package/dist/charts.curling.js +1 -1
- package/dist/charts.d.ts +1 -1
- package/dist/charts.js +1 -1
- package/dist/charts.network.js +1 -1
- package/dist/charts.pool.js +1 -1
- package/dist/charts.skating.js +1 -1
- package/dist/charts.soccer.js +1 -1
- package/dist/cityctx-entry.d.ts +1 -0
- package/dist/cityctx-entry.js +2 -0
- package/dist/cityctx.belmont-park.data.d.ts +3 -0
- package/dist/cityctx.belmont-park.data.js +1802 -0
- package/dist/cityctx.data.d.ts +10 -1
- package/dist/cityctx.data.js +11 -2
- package/dist/cityctx.js +19 -1
- package/dist/cityctx.meadowlands.data.d.ts +3 -0
- package/dist/cityctx.meadowlands.data.js +820 -0
- package/dist/cityctx.montreal-city.data.d.ts +3 -0
- package/dist/cityctx.montreal-city.data.js +2899 -0
- package/dist/cityctx.montreal-downtown.data.d.ts +3 -0
- package/dist/cityctx.montreal-downtown.data.js +3004 -0
- package/dist/cityctx.montreal-olympic.data.d.ts +3 -0
- package/dist/cityctx.montreal-olympic.data.js +2562 -0
- package/dist/cityctx.nyc-bronx.data.d.ts +3 -0
- package/dist/cityctx.nyc-bronx.data.js +1671 -0
- package/dist/cityctx.nyc-brooklyn.data.d.ts +3 -0
- package/dist/cityctx.nyc-brooklyn.data.js +3485 -0
- package/dist/cityctx.nyc-flushing.data.d.ts +3 -0
- package/dist/cityctx.nyc-flushing.data.js +865 -0
- package/dist/cityctx.nyc-midtown.data.d.ts +3 -0
- package/dist/cityctx.nyc-midtown.data.js +2438 -0
- package/dist/crowd.d.ts +22 -2
- package/dist/d3-entry.d.ts +1 -0
- package/dist/d3-entry.js +2 -0
- package/dist/district-entry.d.ts +2 -0
- package/dist/district-entry.js +4 -0
- package/dist/district-lean-entry.js +1 -1
- package/dist/district.js +2 -2
- package/dist/draw.js +3 -3
- package/dist/element-entry.d.ts +1 -0
- package/dist/element-entry.js +2 -0
- package/dist/eras-entry.d.ts +1 -0
- package/dist/eras-entry.js +2 -0
- package/dist/events.js +1 -1
- package/dist/flythrough-entry.d.ts +1 -0
- package/dist/flythrough-entry.js +2 -0
- package/dist/flythrough.dom.d.ts +2 -0
- package/dist/flythrough.dom.js +2 -2
- package/dist/frames-entry.d.ts +1 -0
- package/dist/frames-entry.js +2 -0
- package/dist/full.d.ts +1 -0
- package/dist/full.js +3 -1
- package/dist/geojson-entry.d.ts +1 -0
- package/dist/geojson-entry.js +2 -0
- package/dist/geometry-entry.d.ts +1 -0
- package/dist/geometry-entry.js +2 -0
- package/dist/grounds-entry.d.ts +1 -0
- package/dist/grounds-entry.js +2 -0
- package/dist/heat.js +1 -1
- package/dist/heat.zones.d.ts +1 -1
- package/dist/heat.zones.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/judging-entry.d.ts +1 -0
- package/dist/judging-entry.js +2 -0
- package/dist/judging.board.js +1 -1
- package/dist/judging.draw.js +1 -1
- package/dist/judging.js +1 -1
- package/dist/judging.text.js +1 -1
- package/dist/layers.env.d.ts +1 -1
- package/dist/layers.env.js +1 -1
- package/dist/layers.js +1 -1
- package/dist/layout.js +1 -1
- package/dist/layouts-entry.d.ts +1 -0
- package/dist/layouts-entry.js +2 -0
- package/dist/leagues-entry.d.ts +1 -0
- package/dist/leagues-entry.js +2 -0
- package/dist/mount-entry.d.ts +1 -0
- package/dist/mount-entry.js +2 -0
- package/dist/multi-entry.d.ts +1 -0
- package/dist/multi-entry.js +2 -0
- package/dist/multi.draw.js +1 -1
- package/dist/multi.js +1 -1
- package/dist/osm-entry.d.ts +1 -0
- package/dist/osm-entry.js +2 -0
- package/dist/outside.d.ts +2 -1
- package/dist/outside.js +7 -6
- package/dist/parks-entry.d.ts +1 -0
- package/dist/parks-entry.js +2 -0
- package/dist/plateau-entry.d.ts +1 -0
- package/dist/plateau-entry.js +2 -0
- package/dist/plot-entry.d.ts +1 -0
- package/dist/plot-entry.js +2 -0
- package/dist/png-entry.d.ts +1 -0
- package/dist/png-entry.js +2 -0
- package/dist/pool.replay.js +1 -1
- package/dist/pool.words.js +1 -1
- package/dist/ranges.js +1 -1
- package/dist/react-entry.d.ts +1 -0
- package/dist/react-entry.js +2 -0
- package/dist/replay-entry.d.ts +1 -0
- package/dist/replay-entry.js +2 -0
- package/dist/replay.d.ts +1 -1
- package/dist/replay.dom.js +1 -1
- package/dist/replay.js +1 -1
- package/dist/rules.js +1 -1
- package/dist/scene-entry.d.ts +1 -0
- package/dist/scene-entry.js +2 -0
- package/dist/situation.draw.js +1 -1
- package/dist/situation.js +1 -1
- package/dist/skating/describe.js +1 -1
- package/dist/skating-entry.d.ts +1 -0
- package/dist/skating-entry.js +2 -0
- package/dist/stadiums-entry.d.ts +1 -0
- package/dist/stadiums-entry.js +2 -0
- package/dist/stadiums.d.ts +35 -4
- package/dist/stadiums.data.js +60 -0
- package/dist/stadiums.js +31 -5
- package/dist/stadiums.osm.data.js +224 -2
- package/dist/strings.core.d.ts +1105 -0
- package/dist/strings.core.js +55 -0
- package/dist/strings.d.ts +4 -1084
- package/dist/strings.en.d.ts +2 -0
- package/dist/strings.en.js +537 -0
- package/dist/strings.ja.d.ts +2 -0
- package/dist/strings.ja.js +562 -0
- package/dist/strings.js +5 -1100
- package/dist/surfaces/venue.js +1 -1
- package/dist/target.js +1 -1
- package/dist/team.common.js +1 -1
- package/dist/team.formation.js +1 -1
- package/dist/team.lineup.js +1 -1
- package/dist/team.motion.js +1 -1
- package/dist/team.network.js +1 -1
- package/dist/team.shape.js +1 -1
- package/dist/turn-entry.d.ts +1 -0
- package/dist/turn-entry.js +2 -0
- package/dist/turn.kit.d.ts +14 -1
- package/dist/turn.kit.js +19 -0
- package/dist/turn.model.d.ts +1 -1
- package/dist/turn.model.js +1 -1
- package/dist/turn.style.js +16 -3
- package/dist/uprights.js +1 -1
- package/dist/venue-data-entry.d.ts +2 -0
- package/dist/venue-data-entry.js +5 -1
- package/dist/venue-data.d.ts +1 -1
- package/dist/venue-data.js +5 -2
- package/dist/venue.js +3 -1
- package/dist/venue.real.d.ts +45 -1
- package/dist/venue.real.js +225 -15
- package/dist/venues/amerant-bank-arena.data.d.ts +2 -0
- package/dist/venues/amerant-bank-arena.data.js +9 -0
- package/dist/venues/amerant-bank-arena.osm.data.d.ts +2 -0
- package/dist/venues/amerant-bank-arena.osm.data.js +47 -0
- package/dist/venues/american-airlines-center.data.d.ts +2 -0
- package/dist/venues/american-airlines-center.data.js +9 -0
- package/dist/venues/american-airlines-center.osm.data.d.ts +2 -0
- package/dist/venues/american-airlines-center.osm.data.js +57 -0
- package/dist/venues/ball-arena.data.d.ts +2 -0
- package/dist/venues/ball-arena.data.js +9 -0
- package/dist/venues/ball-arena.osm.data.d.ts +2 -0
- package/dist/venues/ball-arena.osm.data.js +63 -0
- package/dist/venues/bell-centre.data.d.ts +2 -0
- package/dist/venues/bell-centre.data.js +9 -0
- package/dist/venues/bell-centre.osm.data.d.ts +2 -0
- package/dist/venues/bell-centre.osm.data.js +24 -0
- package/dist/venues/benchmark-international-arena.data.d.ts +2 -0
- package/dist/venues/benchmark-international-arena.data.js +9 -0
- package/dist/venues/benchmark-international-arena.osm.data.d.ts +2 -0
- package/dist/venues/benchmark-international-arena.osm.data.js +57 -0
- package/dist/venues/bridgestone-arena.data.d.ts +2 -0
- package/dist/venues/bridgestone-arena.data.js +9 -0
- package/dist/venues/bridgestone-arena.osm.data.d.ts +2 -0
- package/dist/venues/bridgestone-arena.osm.data.js +43 -0
- package/dist/venues/canada-life-centre.data.d.ts +2 -0
- package/dist/venues/canada-life-centre.data.js +9 -0
- package/dist/venues/canada-life-centre.osm.data.d.ts +2 -0
- package/dist/venues/canada-life-centre.osm.data.js +35 -0
- package/dist/venues/canadian-tire-centre.data.d.ts +2 -0
- package/dist/venues/canadian-tire-centre.data.js +9 -0
- package/dist/venues/canadian-tire-centre.osm.data.d.ts +2 -0
- package/dist/venues/canadian-tire-centre.osm.data.js +45 -0
- package/dist/venues/capital-one-arena.data.d.ts +2 -0
- package/dist/venues/capital-one-arena.data.js +9 -0
- package/dist/venues/capital-one-arena.osm.data.d.ts +2 -0
- package/dist/venues/capital-one-arena.osm.data.js +22 -0
- package/dist/venues/climate-pledge-arena.data.d.ts +2 -0
- package/dist/venues/climate-pledge-arena.data.js +9 -0
- package/dist/venues/climate-pledge-arena.osm.data.d.ts +2 -0
- package/dist/venues/climate-pledge-arena.osm.data.js +32 -0
- package/dist/venues/crypto-com-arena.data.d.ts +2 -0
- package/dist/venues/crypto-com-arena.data.js +9 -0
- package/dist/venues/crypto-com-arena.osm.data.d.ts +2 -0
- package/dist/venues/crypto-com-arena.osm.data.js +64 -0
- package/dist/venues/delta-center.data.d.ts +2 -0
- package/dist/venues/delta-center.data.js +9 -0
- package/dist/venues/delta-center.osm.data.d.ts +2 -0
- package/dist/venues/delta-center.osm.data.js +40 -0
- package/dist/venues/enterprise-center.data.d.ts +2 -0
- package/dist/venues/enterprise-center.data.js +9 -0
- package/dist/venues/enterprise-center.osm.data.d.ts +2 -0
- package/dist/venues/enterprise-center.osm.data.js +35 -0
- package/dist/venues/grand-casino-arena.data.d.ts +2 -0
- package/dist/venues/grand-casino-arena.data.js +9 -0
- package/dist/venues/grand-casino-arena.osm.data.d.ts +2 -0
- package/dist/venues/grand-casino-arena.osm.data.js +37 -0
- package/dist/venues/honda-center.data.d.ts +2 -0
- package/dist/venues/honda-center.data.js +9 -0
- package/dist/venues/honda-center.osm.data.d.ts +2 -0
- package/dist/venues/honda-center.osm.data.js +46 -0
- package/dist/venues/keybank-center.data.d.ts +2 -0
- package/dist/venues/keybank-center.data.js +9 -0
- package/dist/venues/keybank-center.osm.data.d.ts +2 -0
- package/dist/venues/keybank-center.osm.data.js +34 -0
- package/dist/venues/lenovo-center.data.d.ts +2 -0
- package/dist/venues/lenovo-center.data.js +9 -0
- package/dist/venues/lenovo-center.osm.data.d.ts +2 -0
- package/dist/venues/lenovo-center.osm.data.js +51 -0
- package/dist/venues/little-caesars-arena.data.d.ts +2 -0
- package/dist/venues/little-caesars-arena.data.js +9 -0
- package/dist/venues/little-caesars-arena.osm.data.d.ts +2 -0
- package/dist/venues/little-caesars-arena.osm.data.js +64 -0
- package/dist/venues/madison-square-garden.data.d.ts +2 -0
- package/dist/venues/madison-square-garden.data.js +9 -0
- package/dist/venues/madison-square-garden.osm.data.d.ts +2 -0
- package/dist/venues/madison-square-garden.osm.data.js +47 -0
- package/dist/venues/nationwide-arena.data.d.ts +2 -0
- package/dist/venues/nationwide-arena.data.js +9 -0
- package/dist/venues/nationwide-arena.osm.data.d.ts +2 -0
- package/dist/venues/nationwide-arena.osm.data.js +50 -0
- package/dist/venues/ppg-paints-arena.data.d.ts +2 -0
- package/dist/venues/ppg-paints-arena.data.js +9 -0
- package/dist/venues/ppg-paints-arena.osm.data.d.ts +2 -0
- package/dist/venues/ppg-paints-arena.osm.data.js +31 -0
- package/dist/venues/prudential-center.data.d.ts +2 -0
- package/dist/venues/prudential-center.data.js +9 -0
- package/dist/venues/prudential-center.osm.data.d.ts +2 -0
- package/dist/venues/prudential-center.osm.data.js +41 -0
- package/dist/venues/rogers-arena.data.d.ts +2 -0
- package/dist/venues/rogers-arena.data.js +10 -0
- package/dist/venues/rogers-arena.osm.data.d.ts +2 -0
- package/dist/venues/rogers-arena.osm.data.js +27 -0
- package/dist/venues/rogers-place.data.d.ts +2 -0
- package/dist/venues/rogers-place.data.js +9 -0
- package/dist/venues/rogers-place.osm.data.d.ts +2 -0
- package/dist/venues/rogers-place.osm.data.js +40 -0
- package/dist/venues/sap-center.data.d.ts +2 -0
- package/dist/venues/sap-center.data.js +9 -0
- package/dist/venues/sap-center.osm.data.d.ts +2 -0
- package/dist/venues/sap-center.osm.data.js +54 -0
- package/dist/venues/scotiabank-arena.data.d.ts +2 -0
- package/dist/venues/scotiabank-arena.data.js +9 -0
- package/dist/venues/scotiabank-arena.osm.data.d.ts +2 -0
- package/dist/venues/scotiabank-arena.osm.data.js +45 -0
- package/dist/venues/scotiabank-saddledome.data.d.ts +2 -0
- package/dist/venues/scotiabank-saddledome.data.js +9 -0
- package/dist/venues/scotiabank-saddledome.osm.data.d.ts +2 -0
- package/dist/venues/scotiabank-saddledome.osm.data.js +46 -0
- package/dist/venues/t-mobile-arena.data.d.ts +2 -0
- package/dist/venues/t-mobile-arena.data.js +9 -0
- package/dist/venues/t-mobile-arena.osm.data.d.ts +2 -0
- package/dist/venues/t-mobile-arena.osm.data.js +50 -0
- package/dist/venues/td-garden.data.d.ts +2 -0
- package/dist/venues/td-garden.data.js +9 -0
- package/dist/venues/td-garden.osm.data.d.ts +2 -0
- package/dist/venues/td-garden.osm.data.js +24 -0
- package/dist/venues/ubs-arena.data.d.ts +2 -0
- package/dist/venues/ubs-arena.data.js +9 -0
- package/dist/venues/ubs-arena.osm.data.d.ts +2 -0
- package/dist/venues/ubs-arena.osm.data.js +33 -0
- package/dist/venues/united-center.data.d.ts +2 -0
- package/dist/venues/united-center.data.js +9 -0
- package/dist/venues/united-center.osm.data.d.ts +2 -0
- package/dist/venues/united-center.osm.data.js +56 -0
- package/dist/venues/xfinity-mobile-arena.data.d.ts +2 -0
- package/dist/venues/xfinity-mobile-arena.data.js +9 -0
- package/dist/venues/xfinity-mobile-arena.osm.data.d.ts +2 -0
- package/dist/venues/xfinity-mobile-arena.osm.data.js +47 -0
- package/dist/venues-entry.d.ts +1 -0
- package/dist/venues-entry.js +2 -0
- package/dist/venues.all.d.ts +1 -0
- package/dist/venues.all.js +6 -0
- package/dist/venues.d.ts +18 -6
- package/dist/venues.data.js +1 -1
- package/dist/venues.index.d.ts +8 -0
- package/dist/venues.index.js +34 -0
- package/dist/venues.js +45 -8
- package/dist/venues.osm.data.d.ts +0 -2
- package/dist/venues.osm.data.js +0 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/walk/vancouver.data.d.ts +2 -0
- package/dist/walk/vancouver.data.js +1291 -0
- package/dist/walk.d.ts +24 -1
- package/dist/walk.data.d.ts +2 -2
- package/dist/walk.data.js +13 -1293
- package/dist/walk.full.d.ts +1 -0
- package/dist/walk.full.js +5 -0
- package/dist/walk.index.d.ts +7 -0
- package/dist/walk.index.js +3 -0
- package/dist/walk.js +69 -7
- package/package.json +7 -2
package/dist/venue.real.js
CHANGED
|
@@ -4,11 +4,14 @@
|
|
|
4
4
|
// membrane from the rim of the walls to a crown, which never opens), or no roof at all (Koshien Stadium's). All are asked for with the roof (`venue: { roof: true }`) and give way to the eye as the other walls do.
|
|
5
5
|
// Pure: data in, planes out.
|
|
6
6
|
import { rayAt } from "./crowd.js";
|
|
7
|
-
import { n, shy, wall } from "./turn.kit.js";
|
|
7
|
+
import { bar, box, n, pole, shy, wall } from "./turn.kit.js";
|
|
8
8
|
import { pointList, raked } from "./venue.geom.js";
|
|
9
|
+
import { edgeDistance, inside } from "./venues.geo.js";
|
|
9
10
|
/** 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). */
|
|
10
11
|
export const LID_RISE = 22;
|
|
11
12
|
export const SADDLE_RISE = 30;
|
|
13
|
+
/** How far a barrel vault's crown stands over its springing at the long sides, in feet (ours: the article says the roof looks like a Quonset hut, not how high it rises). */
|
|
14
|
+
export const BARREL_RISE = 34;
|
|
12
15
|
/** 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. */
|
|
13
16
|
export function saddleAt(real, [x, y]) {
|
|
14
17
|
const xs = real.footprint.map((p) => p[0]);
|
|
@@ -18,8 +21,108 @@ export function saddleAt(real, [x, y]) {
|
|
|
18
21
|
const s = ((x - cx) / hx) ** 2 - ((y - cy) / hy) ** 2;
|
|
19
22
|
return (1 + Math.max(-1, Math.min(1, s))) / 2;
|
|
20
23
|
}
|
|
24
|
+
/** The barrel vault's share of its rise at a point (0 at the springing along each long side, 1 over the middle line): a round arch over the building's short axis, the quarter of a circle's rise at each step in from the side. */
|
|
25
|
+
export function barrelAt(real, [, y]) {
|
|
26
|
+
const ys = real.footprint.map((p) => p[1]);
|
|
27
|
+
const [cy, hy] = [(Math.min(...ys) + Math.max(...ys)) / 2, (Math.max(...ys) - Math.min(...ys)) / 2];
|
|
28
|
+
const v = Math.min(1, Math.abs((y - cy) / hy));
|
|
29
|
+
return Math.sqrt(1 - v * v);
|
|
30
|
+
}
|
|
21
31
|
/** The height of the roof over a point of the building's edge, above the surface, in feet. */
|
|
22
|
-
export const roofAt = (real, p) => real.edge + (real.roof === "saddle" ? SADDLE_RISE * saddleAt(real, p) : 0);
|
|
32
|
+
export const roofAt = (real, p) => real.edge + (real.roof === "saddle" ? SADDLE_RISE * saddleAt(real, p) : real.roof === "barrel" ? BARREL_RISE * barrelAt(real, p) : 0);
|
|
33
|
+
/** How near a wall must stand to a canopy's outline to be the wall under it, in feet. */
|
|
34
|
+
const CANOPY_REACH = 50;
|
|
35
|
+
/** The height of the wall of a building under canopies at a point of its rim: where a canopy is over it, the canopy's back edge, which the wall carries up to (the outside of Lumen Field's roofs is one curved face to the ground); elsewhere the rim's own height. */
|
|
36
|
+
export function wallAt(real, p) {
|
|
37
|
+
if (real.roof !== "canopy" || real.canopies === undefined)
|
|
38
|
+
return roofAt(real, p);
|
|
39
|
+
let top = roofAt(real, p);
|
|
40
|
+
for (const canopy of real.canopies)
|
|
41
|
+
if (inside(canopy.ring, p) || edgeDistance(canopy.ring, p) < CANOPY_REACH)
|
|
42
|
+
top = Math.max(top, canopy.low + canopy.rise);
|
|
43
|
+
return top;
|
|
44
|
+
}
|
|
45
|
+
/** How many rings the leaning tower's shaft is cut into, how far its foot stands wider than the shaft over it (a plinth), and the observatory's width over the shaft's top, its height and the share of it that is glass (ours). */
|
|
46
|
+
const TOWER_RINGS = 12;
|
|
47
|
+
const TOWER_PLINTH = { wide: 1.3, high: 26 };
|
|
48
|
+
const TOWER_CAP = { wide: 1.5, high: 40 };
|
|
49
|
+
/**
|
|
50
|
+
* A tower that leans and curves: a square shaft of horizontal sections that tapers from a broad foot on the ground to a narrow top, along a curve that runs from the foot to the top (the line the map gives of its funicular), its
|
|
51
|
+
* back curving up from a flatter foot to the inclination the article gives at the top; a plinth where it meets the ground, the dark slits down its sloped back, an observatory of dark and glass bands on its top and, with `cables`,
|
|
52
|
+
* thin lines from the observatory to the roof. Each face of a ring is a trapezoid on a plane through its corners, so that the faces meet along their edges. The foot and the top and their height are the sources'; the curve, the
|
|
53
|
+
* widths, the slits, the plinth and the observatory are ours.
|
|
54
|
+
*/
|
|
55
|
+
export function towerPlanes(tower, z, cables = []) {
|
|
56
|
+
const [bx, by] = tower.base;
|
|
57
|
+
const [tx, ty] = tower.top;
|
|
58
|
+
const run = Math.hypot(tx - bx, ty - by) || 1;
|
|
59
|
+
const d = [(tx - bx) / run, (ty - by) / run];
|
|
60
|
+
const a = [-d[1], d[0]];
|
|
61
|
+
const [w0, w1] = tower.width;
|
|
62
|
+
const H = tower.height - TOWER_CAP.high;
|
|
63
|
+
// The curve in the plane of the lean (run along the axis, height): a quadratic from the foot to the top whose tangent at the top rises at the article's inclination.
|
|
64
|
+
const lean = Math.tan((tower.angle * Math.PI) / 180);
|
|
65
|
+
const reach = Math.min(H * 0.12, run * 0.12);
|
|
66
|
+
const control = [run - reach / lean, H - reach];
|
|
67
|
+
const at = (t) => [2 * t * (1 - t) * control[0] + t * t * run, 2 * t * (1 - t) * control[1] + t * t * H];
|
|
68
|
+
const wide = (t) => w1 + (w0 - w1) * (1 - t) ** 1.15;
|
|
69
|
+
const offsets = [[-1, -1], [1, -1], [1, 1], [-1, 1]];
|
|
70
|
+
const ring = (centre, height, w, k) => [bx + d[0] * centre[0] + (a[0] * offsets[k][0] + d[0] * offsets[k][1]) * (w / 2), by + d[1] * centre[0] + (a[1] * offsets[k][0] + d[1] * offsets[k][1]) * (w / 2), z + height];
|
|
71
|
+
const planes = [];
|
|
72
|
+
const face = (id, o, u, v, svg) => {
|
|
73
|
+
// (A wall, not a mast: the lower tiers leave out the small parts, and a tower 165 metres high is not one.)
|
|
74
|
+
planes.push({ id, kind: "wall", o, u, v, svg });
|
|
75
|
+
};
|
|
76
|
+
for (let i = 0; i < TOWER_RINGS; i += 1) {
|
|
77
|
+
const [t0, t1] = [i / TOWER_RINGS, (i + 1) / TOWER_RINGS];
|
|
78
|
+
const [p0, p1] = [at(t0), at(t1)];
|
|
79
|
+
const [wa, wb] = [wide(t0), wide(t1)];
|
|
80
|
+
for (let k = 0; k < 4; k += 1) {
|
|
81
|
+
const o = ring([p0[0], 0], p0[1], wa, k);
|
|
82
|
+
const up = ring([p1[0], 0], p1[1], wb, k);
|
|
83
|
+
const next = ring([p0[0], 0], p0[1], wa, (k + 1) % 4);
|
|
84
|
+
const u = [up[0] - o[0], up[1] - o[1], up[2] - o[2]];
|
|
85
|
+
const v = [next[0] - o[0], next[1] - o[1], next[2] - o[2]];
|
|
86
|
+
const [lu, lv] = [Math.hypot(...u), Math.hypot(...v)];
|
|
87
|
+
const r = wb / wa;
|
|
88
|
+
// The sloped back (the face away from the lean) has the two long dark slits down it, a few rings clear of the foot and of the top.
|
|
89
|
+
const slit = (f0, f1) => `<polygon class="u-tower-slit" points="0 ${n(f0 * lv)} ${n(lu)} ${n(f0 * lv * r)} ${n(lu)} ${n(f1 * lv * r)} 0 ${n(f1 * lv)}"/>`;
|
|
90
|
+
const slits = k === 0 && i >= 2 && i < TOWER_RINGS - 1 ? `${slit(0.24, 0.31)}${slit(0.62, 0.69)}` : "";
|
|
91
|
+
face(`tower-${i}-${k}`, o, u, v, `<polygon class="u-tower" points="0 0 ${n(lu)} 0 ${n(lu)} ${n(lv * r)} 0 ${n(lv)}"/>${slits}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// The plinth where the foot meets the ground: a low block wider than the shaft, with its top.
|
|
95
|
+
{
|
|
96
|
+
const wp = w0 * TOWER_PLINTH.wide;
|
|
97
|
+
for (let k = 0; k < 4; k += 1) {
|
|
98
|
+
const o = ring([0, 0], 0, wp, k);
|
|
99
|
+
const next = ring([0, 0], 0, wp, (k + 1) % 4);
|
|
100
|
+
const v = [next[0] - o[0], next[1] - o[1], 0];
|
|
101
|
+
face(`tower-foot-${k}`, o, [0, 0, TOWER_PLINTH.high], v, `<rect class="u-tower-foot" width="${n(TOWER_PLINTH.high)}" height="${n(Math.hypot(v[0], v[1]))}"/>`);
|
|
102
|
+
}
|
|
103
|
+
const o = ring([0, 0], TOWER_PLINTH.high, wp, 0);
|
|
104
|
+
const [c1, c3] = [ring([0, 0], TOWER_PLINTH.high, wp, 1), ring([0, 0], TOWER_PLINTH.high, wp, 3)];
|
|
105
|
+
face("tower-foot-top", o, [c1[0] - o[0], c1[1] - o[1], 0], [c3[0] - o[0], c3[1] - o[1], 0], `<rect class="u-tower-foot" width="${n(wp)}" height="${n(wp)}"/>`);
|
|
106
|
+
}
|
|
107
|
+
// The observatory on the top: a block a little wider than the shaft there, its walls dark, with a band of glass round it, and a flat roof.
|
|
108
|
+
const [topAt, topW] = [at(1), wide(1) * TOWER_CAP.wide];
|
|
109
|
+
for (let k = 0; k < 4; k += 1) {
|
|
110
|
+
const o = ring([topAt[0], 0], topAt[1], topW, k);
|
|
111
|
+
const next = ring([topAt[0], 0], topAt[1], topW, (k + 1) % 4);
|
|
112
|
+
const v = [next[0] - o[0], next[1] - o[1], 0];
|
|
113
|
+
const lv = Math.hypot(v[0], v[1]);
|
|
114
|
+
face(`tower-cap-${k}`, o, [0, 0, TOWER_CAP.high], v, `<rect class="u-tower-cap" width="${n(TOWER_CAP.high)}" height="${n(lv)}"/><rect class="u-tower-glass" x="${n(TOWER_CAP.high * 0.3)}" width="${n(TOWER_CAP.high * 0.45)}" height="${n(lv)}"/>`);
|
|
115
|
+
}
|
|
116
|
+
{
|
|
117
|
+
const o = ring([topAt[0], 0], topAt[1] + TOWER_CAP.high, topW, 0);
|
|
118
|
+
const [c1, c3] = [ring([topAt[0], 0], topAt[1] + TOWER_CAP.high, topW, 1), ring([topAt[0], 0], topAt[1] + TOWER_CAP.high, topW, 3)];
|
|
119
|
+
face("tower-cap-top", o, [c1[0] - o[0], c1[1] - o[1], 0], [c3[0] - o[0], c3[1] - o[1], 0], `<rect class="u-tower-cap" width="${n(topW)}" height="${n(topW)}"/>`);
|
|
120
|
+
}
|
|
121
|
+
// The cables that held the roof, from the observatory to the roof: thin lines.
|
|
122
|
+
const from = ring([topAt[0], 0], topAt[1] + TOWER_CAP.high * 0.4, 0, 0);
|
|
123
|
+
cables.forEach((to, i) => planes.push(...bar(`tower-cable-${i}`, "mast", from, to, 1.2, "u-tower-cable")));
|
|
124
|
+
return planes;
|
|
125
|
+
}
|
|
23
126
|
/** The point where a ray from `from` first meets a polygon, or `undefined`. */
|
|
24
127
|
export function firstHit(poly, from, angle) {
|
|
25
128
|
const [dx, dy] = [Math.cos(angle), Math.sin(angle)];
|
|
@@ -107,11 +210,16 @@ export function realBuilding(real, top, centre, lift, steps, open = false, parts
|
|
|
107
210
|
const long = Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
108
211
|
if (long < 1)
|
|
109
212
|
return;
|
|
110
|
-
const high = lift + (real.wall ??
|
|
111
|
-
planes.push({ ...shy(wall(`shell-${i}`, "wall", a[0], a[1], b[0], b[1], 0, high, `<rect class="${real.ivy === true ? "u-shell u-ivy" : "u-shell"}" width="${n(long)}" height="${n(high)}"/>${real.podium === undefined ? "" : podiumBands(long, high, real.podium)}`), centre), fadeTo: 0.02 });
|
|
213
|
+
const high = lift + (real.wall ?? wallAt(real, [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2]));
|
|
214
|
+
planes.push({ ...shy(wall(`shell-${i}`, "wall", a[0], a[1], b[0], b[1], 0, high, `<rect class="${real.ivy === true ? "u-shell u-ivy" : real.brick === true ? "u-shell u-brick" : "u-shell"}" width="${n(long)}" height="${n(high)}"/>${real.podium === undefined ? "" : podiumBands(long, high, real.podium)}`), centre), fadeTo: 0.02 });
|
|
112
215
|
});
|
|
113
216
|
if (parts.walls && planes.length > 0)
|
|
114
217
|
items.push({ id: "shell", kind: "walls", at: centre, height: Math.max(...planes.map((p) => p.o[2])), planes });
|
|
218
|
+
if (parts.walls && real.towers !== undefined) {
|
|
219
|
+
const towers = real.towers.flatMap((tower, i) => endTowerPlanes(tower, i, lift, c));
|
|
220
|
+
if (towers.length > 0)
|
|
221
|
+
items.push({ id: "endtower", kind: "walls", at: centre, height: Math.max(...real.towers.map((t) => t.height)) + lift, planes: towers });
|
|
222
|
+
}
|
|
115
223
|
if (parts.roof && real.roof === "canopy" && real.canopies !== undefined) {
|
|
116
224
|
const planes = canopyRoof(real.canopies, c, lift, steps);
|
|
117
225
|
if (planes.length > 0)
|
|
@@ -119,7 +227,15 @@ export function realBuilding(real, top, centre, lift, steps, open = false, parts
|
|
|
119
227
|
return items;
|
|
120
228
|
}
|
|
121
229
|
if (parts.roof && real.roof === "dome" && real.dome !== undefined) {
|
|
122
|
-
|
|
230
|
+
// The cables run from the tower to the middle of the roof and a third of the way out each side of it.
|
|
231
|
+
const rim = real.dome.rim;
|
|
232
|
+
const mid = [rim.reduce((t, p) => t + p[0], 0) / rim.length, rim.reduce((t, p) => t + p[1], 0) / rim.length];
|
|
233
|
+
const reach = Math.max(...rim.map((p) => Math.hypot(p[0] - mid[0], p[1] - mid[1]))) / 3;
|
|
234
|
+
const across = real.tower === undefined ? [0, 0] : [-(real.tower.top[1] - real.tower.base[1]), real.tower.top[0] - real.tower.base[0]];
|
|
235
|
+
const long = Math.hypot(across[0], across[1]) || 1;
|
|
236
|
+
const high = lift + real.dome.low + (real.dome.crown - real.dome.low) * (1 - 0.1);
|
|
237
|
+
const cables = [[mid[0], mid[1], lift + real.dome.crown], ...[-1, 1].map((side) => [mid[0] + (across[0] / long) * reach * side, mid[1] + (across[1] / long) * reach * side, high])];
|
|
238
|
+
const planes = [...domeRoof(real.dome, lift, steps), ...(real.tower === undefined ? [] : towerPlanes(real.tower, lift, cables))];
|
|
123
239
|
if (planes.length > 0)
|
|
124
240
|
items.push({ id: "roof", kind: "roof", at: centre, height: heightOf(planes), planes });
|
|
125
241
|
return items;
|
|
@@ -142,7 +258,7 @@ export function realBuilding(real, top, centre, lift, steps, open = false, parts
|
|
|
142
258
|
const angle = (2 * Math.PI * k) / steps;
|
|
143
259
|
return crown === undefined ? rayAt(top.hole, c[0], c[1], angle) : firstHit(crown.opening, c, angle) ?? rayAt(top.hole, c[0], c[1], angle);
|
|
144
260
|
});
|
|
145
|
-
const rise = real.roof === "saddle" || real.roof === "cable" ? 0 : LID_RISE;
|
|
261
|
+
const rise = real.roof === "saddle" || real.roof === "barrel" || real.roof === "cable" ? 0 : LID_RISE;
|
|
146
262
|
const zOf = (p, inward) => lift + roofAt(real, p) + (inward ? (crown === undefined ? rise : crown.rise) : 0);
|
|
147
263
|
for (let k = 0; k < steps; k += 1) {
|
|
148
264
|
const [a, b] = [rim[k], rim[(k + 1) % steps]];
|
|
@@ -228,7 +344,7 @@ function span(poly, from, angle) {
|
|
|
228
344
|
* A piece of roof between two rays: the quad `[inner a, inner b, outer b, outer a]` at the heights given, as a plane that gives way to the eye. A quad whose edges are not parallel is not flat at those heights, so
|
|
229
345
|
* with `exact` it is two triangles, each a plane through its corners.
|
|
230
346
|
*/
|
|
231
|
-
function piece(id, quad, heights, c, cables, exact = false) {
|
|
347
|
+
function piece(id, quad, heights, c, cables, exact = false, cls = "u-roof") {
|
|
232
348
|
const [ai, bi, b, a] = quad;
|
|
233
349
|
const [ex, ey] = [b[0] - a[0], b[1] - a[1]];
|
|
234
350
|
const el = Math.hypot(ex, ey);
|
|
@@ -238,7 +354,7 @@ function piece(id, quad, heights, c, cables, exact = false) {
|
|
|
238
354
|
const slope = slopeThrough(poly.map((p, i) => [p[0], p[1], hs[i]]));
|
|
239
355
|
const mid = [poly.reduce((s, p) => s + p[0], 0) / poly.length, poly.reduce((s, p) => s + p[1], 0) / poly.length];
|
|
240
356
|
const away = Math.hypot(mid[0] - c[0], mid[1] - c[1]) || 1;
|
|
241
|
-
const paint = (corners) => `<polygon class="
|
|
357
|
+
const paint = (corners) => `<polygon class="${cls}" points="${pointList(corners)}"/>${cable ? `<path class="u-cable" d="M${n(corners[0][0])} ${n(corners[0][1])}L${n(corners[3][0])} ${n(corners[3][1])}"/>` : ""}`;
|
|
242
358
|
const plane = raked(name, "roof", poly, slope, [ex / el, ey / el], paint);
|
|
243
359
|
return plane === undefined ? [] : [{ ...plane, shy: [(mid[0] - c[0]) / away, (mid[1] - c[1]) / away] }];
|
|
244
360
|
};
|
|
@@ -248,24 +364,69 @@ function piece(id, quad, heights, c, cables, exact = false) {
|
|
|
248
364
|
}
|
|
249
365
|
/** How far a canopy's low edge, the field's side of it, stands under its high edge: the OpenStreetMap roof's `roof:height` is the rise, and the canopy rises to the back of the stands (ours: the sources do not say which way it slopes). */
|
|
250
366
|
export const CANOPY_NOTE = "low over the field, high over the back of the stands";
|
|
367
|
+
/** How many strips a canopy is cut into from the field's side to the back, so that a curved roof is a curve (ours: the drawing's pieces are flat). */
|
|
368
|
+
export const CANOPY_STRIPS = 3;
|
|
369
|
+
/** From this many rays round the middle on, a canopy is cut by every other one. */
|
|
370
|
+
export const CANOPY_FINE = 48;
|
|
371
|
+
/** The share of a canopy's rise at a share of the way from the field's side to the back: a straight slope, or for OpenStreetMap's `roof:shape=round` a convex arc, a quarter of a circle, steep at the field's edge and levelling over the back of the stands. */
|
|
372
|
+
export function canopyRiseAt(shape, t) {
|
|
373
|
+
return shape === "round" ? Math.sin((Math.max(0, Math.min(1, t)) * Math.PI) / 2) : t;
|
|
374
|
+
}
|
|
375
|
+
const between = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
|
|
251
376
|
/**
|
|
252
|
-
* Canopies over some of the stands (Lumen Field's two, one each side of the field): each OpenStreetMap roof outline is cut by the rays from the middle of the field into the strips between them,
|
|
253
|
-
* the field low
|
|
377
|
+
* Canopies over some of the stands (Lumen Field's two, one each side of the field): each OpenStreetMap roof outline is cut by the rays from the middle of the field into the strips between them, and each strip across, from
|
|
378
|
+
* the edge towards the field, low, to the edge towards the back of the stands, `rise` higher, into `CANOPY_STRIPS` pieces that follow the roof's shape: a straight slope, or a round arc where OpenStreetMap says `round`.
|
|
254
379
|
*/
|
|
255
380
|
function canopyRoof(canopies, c, lift, steps) {
|
|
256
381
|
const planes = [];
|
|
257
382
|
canopies.forEach((canopy, ci) => {
|
|
258
383
|
const spans = Array.from({ length: steps }, (_, k) => span(canopy.ring, c, (2 * Math.PI * k) / steps));
|
|
259
|
-
|
|
260
|
-
|
|
384
|
+
// Every other ray of a fine cut: a canopy is long and gently curved, and each ray is a piece across its three strips (the plane budget of a district counts them).
|
|
385
|
+
const stride = steps >= CANOPY_FINE ? 2 : 1;
|
|
386
|
+
for (let k = 0; k < steps; k += stride) {
|
|
387
|
+
const [s, t] = [spans[k], spans[(k + stride) % steps]];
|
|
261
388
|
if (s === undefined || t === undefined)
|
|
262
389
|
continue;
|
|
263
|
-
|
|
264
|
-
|
|
390
|
+
for (let j = 0; j < CANOPY_STRIPS; j += 1) {
|
|
391
|
+
const [u0, u1] = [j / CANOPY_STRIPS, (j + 1) / CANOPY_STRIPS];
|
|
392
|
+
const [z0, z1] = [lift + canopy.low + canopy.rise * canopyRiseAt(canopy.shape, u0), lift + canopy.low + canopy.rise * canopyRiseAt(canopy.shape, u1)];
|
|
393
|
+
planes.push(...piece(`roof-c${ci}-${k}-${j}`, [between(s[0], s[1], u0), between(t[0], t[1], u0), between(t[0], t[1], u1), between(s[0], s[1], u1)], [z0, z0, z1, z1], c, false, false, "u-roof u-canopy"));
|
|
394
|
+
}
|
|
265
395
|
}
|
|
266
396
|
});
|
|
267
397
|
return planes;
|
|
268
398
|
}
|
|
399
|
+
/** How wide the scoreboard on a tower's face is and how tall, as shares of the face, and how far it stands out from it, in feet (ours: the article says the scoreboard is vertical, not how large). */
|
|
400
|
+
export const TOWER_BOARD = { wide: 0.62, tall: 0.46, from: 0.55, out: 0.4 };
|
|
401
|
+
/**
|
|
402
|
+
* A tower standing apart from the bowl: a box on its outline, four faces and a flat top, that gives way to the eye as a wall does (a tall tower between the eye and the field is cut away, whole from above), with the scoreboard
|
|
403
|
+
* on the face towards the field, a dark panel high on it and divided into its rows of lamps.
|
|
404
|
+
*/
|
|
405
|
+
export function endTowerPlanes(tower, index, lift, centre) {
|
|
406
|
+
const xs = tower.ring.map((p) => p[0]);
|
|
407
|
+
const ys = tower.ring.map((p) => p[1]);
|
|
408
|
+
const [x0, x1, y0, y1] = [Math.min(...xs), Math.max(...xs), Math.min(...ys), Math.max(...ys)];
|
|
409
|
+
const top = lift + tower.height;
|
|
410
|
+
const parts = box(`endtower-${index}`, "wall", { x0, y0, x1, y1 }, top, "u-endtower");
|
|
411
|
+
const planes = parts.map((p) => (p.id.endsWith("-top") ? p : { ...shy(p, centre), fadeTo: 0.25 }));
|
|
412
|
+
// The scoreboard: on the face that looks at the field, a little proud of it.
|
|
413
|
+
const [fx, fy] = tower.face;
|
|
414
|
+
const [w, h] = fx !== 0 ? [y1 - y0, top] : [x1 - x0, top];
|
|
415
|
+
const [bw, bh] = [w * TOWER_BOARD.wide, h * TOWER_BOARD.tall];
|
|
416
|
+
const zTop = top * TOWER_BOARD.from + bh;
|
|
417
|
+
const svg = `<rect class="u-endtower-board" width="${n(bw)}" height="${n(bh)}"/><path class="u-endtower-rows" d="${Array.from({ length: 9 }, (_, k) => `M0 ${n(((k + 1) * bh) / 10)}H${n(bw)}`).join("")}"/>`;
|
|
418
|
+
if (fx !== 0) {
|
|
419
|
+
const x = fx > 0 ? x1 + TOWER_BOARD.out : x0 - TOWER_BOARD.out;
|
|
420
|
+
const mid = (y0 + y1) / 2;
|
|
421
|
+
planes.push({ ...shy(wall(`endtower-${index}-board`, "wall", x, mid - bw / 2, x, mid + bw / 2, zTop - bh, zTop, svg), centre), fadeTo: 0.25 });
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
const y = fy > 0 ? y1 + TOWER_BOARD.out : y0 - TOWER_BOARD.out;
|
|
425
|
+
const mid = (x0 + x1) / 2;
|
|
426
|
+
planes.push({ ...shy(wall(`endtower-${index}-board`, "wall", mid - bw / 2, y, mid + bw / 2, y, zTop - bh, zTop, svg), centre), fadeTo: 0.25 });
|
|
427
|
+
}
|
|
428
|
+
return planes;
|
|
429
|
+
}
|
|
269
430
|
/** How many rings of pieces an air-supported roof is drawn in, from the rim in to the crown (ours: the roof is smooth, the drawing is faceted). */
|
|
270
431
|
export const DOME_RINGS = 4;
|
|
271
432
|
/**
|
|
@@ -315,7 +476,7 @@ const PANEL_THICK = 7;
|
|
|
315
476
|
* building they park on, as slabs at the rim's height.
|
|
316
477
|
*/
|
|
317
478
|
function panelsRoof(panels, c, lift, steps, open) {
|
|
318
|
-
const planes = [];
|
|
479
|
+
const planes = [...panelRails(panels, c, lift)];
|
|
319
480
|
if (!open) {
|
|
320
481
|
const rim = Array.from({ length: steps }, (_, k) => firstHit(panels.cover, c, (2 * Math.PI * k) / steps) ?? c);
|
|
321
482
|
const inner = rim.map((p) => [c[0] + (p[0] - c[0]) * PANEL_CROWN, c[1] + (p[1] - c[1]) * PANEL_CROWN]);
|
|
@@ -344,6 +505,55 @@ function panelsRoof(panels, c, lift, steps, open) {
|
|
|
344
505
|
});
|
|
345
506
|
return planes;
|
|
346
507
|
}
|
|
508
|
+
/** How wide a rail girder is, how thick, how wide a column under it is, and how many columns stand under each rail, in feet (ours: the article says the roof runs on rails on the north and the south side, not what holds them up). */
|
|
509
|
+
export const RAIL = { wide: 12, thick: 16, column: 15, columns: 4, inset: 24 };
|
|
510
|
+
/** A prism over a four-cornered outline, from `z0` to `z1`: its sides and its top, in the roof's pod tone (a dark steel). */
|
|
511
|
+
function prism(id, ring, z0, z1, c) {
|
|
512
|
+
const planes = [];
|
|
513
|
+
ring.forEach((a, i) => {
|
|
514
|
+
const b = ring[(i + 1) % ring.length];
|
|
515
|
+
const long = Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
516
|
+
if (long < 0.5)
|
|
517
|
+
return;
|
|
518
|
+
planes.push({ ...shy(wall(`${id}-${i}`, "roof", a[0], a[1], b[0], b[1], z0, z1, `<rect class="u-rail" width="${n(long)}" height="${n(z1 - z0)}"/>`), c), fadeTo: 0.3 });
|
|
519
|
+
});
|
|
520
|
+
const top = raked(`${id}-top`, "roof", [...ring], { x0: ring[0][0], y0: ring[0][1], z0: z1, gx: 0, gy: 0 }, [1, 0], (corners) => `<polygon class="u-rail" points="${pointList(corners)}"/>`);
|
|
521
|
+
if (top !== undefined)
|
|
522
|
+
planes.push(top);
|
|
523
|
+
return planes;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* The rails a retractable ballpark roof runs on, along its north and south sides (the article: 128 wheels on rails there), as two girders at the roof's lowest height, each on a row of columns from the ground, and running
|
|
527
|
+
* on over the strip where the sections rest. The rails run the way the sections park, from the cover's middle to where they rest; a girder lies a little inside each side of the cover.
|
|
528
|
+
*/
|
|
529
|
+
export function panelRails(panels, c, lift) {
|
|
530
|
+
const mean = (ring) => [ring.reduce((s, p) => s + p[0], 0) / ring.length, ring.reduce((s, p) => s + p[1], 0) / ring.length];
|
|
531
|
+
const park = mean(panels.parked);
|
|
532
|
+
const length = Math.hypot(park[0] - c[0], park[1] - c[1]);
|
|
533
|
+
if (length < 1)
|
|
534
|
+
return [];
|
|
535
|
+
const e = [(park[0] - c[0]) / length, (park[1] - c[1]) / length];
|
|
536
|
+
const m = [-e[1], e[0]];
|
|
537
|
+
const along = (p) => (p[0] - c[0]) * e[0] + (p[1] - c[1]) * e[1];
|
|
538
|
+
const across = (p) => (p[0] - c[0]) * m[0] + (p[1] - c[1]) * m[1];
|
|
539
|
+
const [a0, a1] = [Math.min(...panels.cover.map(along)), Math.max(...panels.parked.map(along), ...panels.cover.map(along))];
|
|
540
|
+
const [m0, m1] = [Math.min(...panels.cover.map(across)) + RAIL.inset, Math.max(...panels.cover.map(across)) - RAIL.inset];
|
|
541
|
+
const at = (u, v) => [c[0] + e[0] * u + m[0] * v, c[1] + e[1] * u + m[1] * v];
|
|
542
|
+
const planes = [];
|
|
543
|
+
const z = lift + panels.bottom;
|
|
544
|
+
[m0, m1].forEach((v, side) => {
|
|
545
|
+
const w = RAIL.wide / 2;
|
|
546
|
+
planes.push(...prism(`rail-${side}`, [at(a0, v - w), at(a1, v - w), at(a1, v + w), at(a0, v + w)], z - RAIL.thick, z, c));
|
|
547
|
+
for (let k = 0; k < RAIL.columns; k += 1) {
|
|
548
|
+
const u = a0 + ((k + 0.5) * (a1 - a0)) / RAIL.columns;
|
|
549
|
+
const h = RAIL.column / 2;
|
|
550
|
+
// A column is two strips crossed, as a post is (`pole`): seen from every side with two planes and not five.
|
|
551
|
+
const [x, y] = at(u, v);
|
|
552
|
+
planes.push(...pole(`rail-${side}-leg-${k}`, "roof", x, y, lift, z - RAIL.thick, h * 2, "u-rail"));
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
return planes;
|
|
556
|
+
}
|
|
347
557
|
/** The pod in the middle of a cable roof's opening, as a share of the opening's size, and how thick it is, in feet (ours: the article says the fabric gathers into a pod above the video board, not how large it is). */
|
|
348
558
|
export const POD = { share: 0.24, thick: 9 };
|
|
349
559
|
/**
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const VENUE = {
|
|
2
|
+
"id": "amerant-bank-arena",
|
|
3
|
+
"wikidata": "Q795526",
|
|
4
|
+
"division": "atlantic",
|
|
5
|
+
"en": { "title": "Amerant Bank Arena", "revision": 1379821144 },
|
|
6
|
+
"seats": { "hockey": 19250, "source": "wikipedia", "wikidata": 20737, "read": "the infobox's figure for ice hockey, 19,250 (at least 20,000 with standing room); Wikidata's one capacity (20,737) has no sport and is the basketball one" },
|
|
7
|
+
"tiers": { "count": 2, "basis": "plausible", "read": "no source says how many seating levels there are: two bowls are drawn; the article speaks of 70 suites and 2,623 club seats, which are not seating bowls" },
|
|
8
|
+
"roof": { "kind": "fixed", "basis": "plausible", "read": "an indoor arena: a fixed roof, as nothing says otherwise" },
|
|
9
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const OSM = {
|
|
2
|
+
"source": "way/43903801",
|
|
3
|
+
"read": "2026-10-20",
|
|
4
|
+
"base": "2026-10-20T23:14:51Z",
|
|
5
|
+
"licence": "ODbL-1.0",
|
|
6
|
+
"attribution": "(c) OpenStreetMap contributors, ODbL 1.0, openstreetmap.org/copyright",
|
|
7
|
+
"axis": { "heading": 90, "basis": "none", "strength": 1.16, "elongation": 1.02, "centre": [-80.325443, 26.158421] },
|
|
8
|
+
"ring": [
|
|
9
|
+
[-80.324676, 26.158684],
|
|
10
|
+
[-80.324614, 26.158588],
|
|
11
|
+
[-80.324552, 26.158446],
|
|
12
|
+
[-80.324708, 26.158215],
|
|
13
|
+
[-80.324608, 26.158149],
|
|
14
|
+
[-80.324762, 26.15801],
|
|
15
|
+
[-80.324661, 26.157905],
|
|
16
|
+
[-80.325085, 26.157719],
|
|
17
|
+
[-80.325365, 26.157557],
|
|
18
|
+
[-80.3254, 26.157691],
|
|
19
|
+
[-80.325621, 26.157659],
|
|
20
|
+
[-80.325627, 26.157763],
|
|
21
|
+
[-80.325924, 26.157768],
|
|
22
|
+
[-80.326026, 26.157894],
|
|
23
|
+
[-80.326082, 26.157998],
|
|
24
|
+
[-80.326126, 26.157984],
|
|
25
|
+
[-80.326144, 26.158016],
|
|
26
|
+
[-80.32619, 26.157992],
|
|
27
|
+
[-80.326228, 26.15808],
|
|
28
|
+
[-80.326258, 26.158233],
|
|
29
|
+
[-80.326261, 26.158415],
|
|
30
|
+
[-80.326242, 26.158516],
|
|
31
|
+
[-80.326189, 26.158716],
|
|
32
|
+
[-80.326139, 26.158828],
|
|
33
|
+
[-80.326054, 26.158935],
|
|
34
|
+
[-80.326022, 26.158916],
|
|
35
|
+
[-80.325842, 26.159111],
|
|
36
|
+
[-80.325688, 26.159133],
|
|
37
|
+
[-80.325547, 26.159133],
|
|
38
|
+
[-80.325544, 26.15918],
|
|
39
|
+
[-80.325358, 26.159169],
|
|
40
|
+
[-80.325129, 26.159108],
|
|
41
|
+
[-80.324965, 26.159041],
|
|
42
|
+
[-80.324812, 26.158949],
|
|
43
|
+
[-80.324618, 26.158787],
|
|
44
|
+
[-80.324672, 26.15875],
|
|
45
|
+
[-80.324645, 26.158707],
|
|
46
|
+
],
|
|
47
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const VENUE = {
|
|
2
|
+
"id": "american-airlines-center",
|
|
3
|
+
"wikidata": "Q463345",
|
|
4
|
+
"division": "central",
|
|
5
|
+
"en": { "title": "American Airlines Center", "revision": 1379833787 },
|
|
6
|
+
"seats": { "hockey": 18532, "source": "wikipedia", "wikidata": 19200, "read": "the infobox's figure for ice hockey, 18,532 (19,323 with standing room); Wikidata's one capacity (19,200) is the basketball one" },
|
|
7
|
+
"tiers": { "count": 2, "basis": "plausible", "read": "no source says how many seating levels there are: two bowls are drawn; the article speaks of retractable seating, which the bowl does not draw" },
|
|
8
|
+
"roof": { "kind": "barrel", "basis": "documented", "read": "the article says the roof looks like a Quonset hut, which is a barrel vault: drawn as a round arch over the rink's short axis, highest over its middle line and falling to the walls at the two long sides; the height of the crown (34 feet over the springing) is ours (the article gives none)" },
|
|
9
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export const OSM = {
|
|
2
|
+
"source": "way/25312698",
|
|
3
|
+
"read": "2026-10-20",
|
|
4
|
+
"base": "2026-10-20T21:35:07Z",
|
|
5
|
+
"licence": "ODbL-1.0",
|
|
6
|
+
"attribution": "(c) OpenStreetMap contributors, ODbL 1.0, openstreetmap.org/copyright",
|
|
7
|
+
"axis": { "heading": 144, "basis": "building", "strength": 2.19, "elongation": 1.31, "centre": [-96.81028, 32.79049] },
|
|
8
|
+
"ring": [
|
|
9
|
+
[-96.810234, 32.791328],
|
|
10
|
+
[-96.810164, 32.791245],
|
|
11
|
+
[-96.810182, 32.791236],
|
|
12
|
+
[-96.810056, 32.791086],
|
|
13
|
+
[-96.810012, 32.791113],
|
|
14
|
+
[-96.809956, 32.791046],
|
|
15
|
+
[-96.809969, 32.791039],
|
|
16
|
+
[-96.809568, 32.790561],
|
|
17
|
+
[-96.809555, 32.790569],
|
|
18
|
+
[-96.8095, 32.790504],
|
|
19
|
+
[-96.809545, 32.790477],
|
|
20
|
+
[-96.80934, 32.790256],
|
|
21
|
+
[-96.809439, 32.790198],
|
|
22
|
+
[-96.809429, 32.790171],
|
|
23
|
+
[-96.80958, 32.790081],
|
|
24
|
+
[-96.809551, 32.790046],
|
|
25
|
+
[-96.809959, 32.789817],
|
|
26
|
+
[-96.809952, 32.789809],
|
|
27
|
+
[-96.81003, 32.789763],
|
|
28
|
+
[-96.810056, 32.789794],
|
|
29
|
+
[-96.810209, 32.789704],
|
|
30
|
+
[-96.81024, 32.789718],
|
|
31
|
+
[-96.810339, 32.789661],
|
|
32
|
+
[-96.810408, 32.789744],
|
|
33
|
+
[-96.810392, 32.789752],
|
|
34
|
+
[-96.81051, 32.789892],
|
|
35
|
+
[-96.810556, 32.789865],
|
|
36
|
+
[-96.810611, 32.789932],
|
|
37
|
+
[-96.810599, 32.789939],
|
|
38
|
+
[-96.810997, 32.790421],
|
|
39
|
+
[-96.81101, 32.790413],
|
|
40
|
+
[-96.811063, 32.790477],
|
|
41
|
+
[-96.811021, 32.790502],
|
|
42
|
+
[-96.811154, 32.790659],
|
|
43
|
+
[-96.811168, 32.790649],
|
|
44
|
+
[-96.811237, 32.790731],
|
|
45
|
+
[-96.811139, 32.79079],
|
|
46
|
+
[-96.81113, 32.790776],
|
|
47
|
+
[-96.810963, 32.790875],
|
|
48
|
+
[-96.811009, 32.790929],
|
|
49
|
+
[-96.810986, 32.790943],
|
|
50
|
+
[-96.810931, 32.790975],
|
|
51
|
+
[-96.810924, 32.790967],
|
|
52
|
+
[-96.81053, 32.791213],
|
|
53
|
+
[-96.810484, 32.791159],
|
|
54
|
+
[-96.810319, 32.791257],
|
|
55
|
+
[-96.810331, 32.791269],
|
|
56
|
+
],
|
|
57
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const VENUE = {
|
|
2
|
+
"id": "ball-arena",
|
|
3
|
+
"wikidata": "Q795470",
|
|
4
|
+
"division": "central",
|
|
5
|
+
"en": { "title": "Ball Arena", "revision": 1379560431 },
|
|
6
|
+
"seats": { "hockey": 17809, "source": "wikipedia", "wikidata": 19155, "read": "the infobox's figure for hockey (given with lacrosse), 17,809; Wikidata's capacity without a sport is 19,155 and its other (17,417) is for futsal" },
|
|
7
|
+
"tiers": { "count": 2, "basis": "plausible", "read": "no source says how many seating levels there are: two bowls are drawn" },
|
|
8
|
+
"roof": { "kind": "fixed", "basis": "plausible", "read": "an indoor arena: a fixed roof, as nothing says otherwise" },
|
|
9
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const OSM = {
|
|
2
|
+
"source": "way/25312645",
|
|
3
|
+
"read": "2026-10-20",
|
|
4
|
+
"base": "2026-10-20T21:33:18Z",
|
|
5
|
+
"licence": "ODbL-1.0",
|
|
6
|
+
"attribution": "(c) OpenStreetMap contributors, ODbL 1.0, openstreetmap.org/copyright",
|
|
7
|
+
"height": 22,
|
|
8
|
+
"axis": { "heading": 90, "basis": "none", "strength": 1.26, "elongation": 1.02, "centre": [-105.007573, 39.748633] },
|
|
9
|
+
"ring": [
|
|
10
|
+
[-105.008169, 39.749084],
|
|
11
|
+
[-105.008338, 39.748973],
|
|
12
|
+
[-105.008439, 39.748857],
|
|
13
|
+
[-105.008498, 39.748726],
|
|
14
|
+
[-105.008512, 39.748603],
|
|
15
|
+
[-105.008498, 39.748498],
|
|
16
|
+
[-105.008449, 39.748382],
|
|
17
|
+
[-105.00837, 39.748281],
|
|
18
|
+
[-105.008268, 39.7482],
|
|
19
|
+
[-105.008098, 39.748114],
|
|
20
|
+
[-105.008077, 39.748156],
|
|
21
|
+
[-105.007939, 39.748121],
|
|
22
|
+
[-105.00785, 39.748115],
|
|
23
|
+
[-105.007829, 39.748063],
|
|
24
|
+
[-105.007872, 39.74804],
|
|
25
|
+
[-105.007756, 39.747764],
|
|
26
|
+
[-105.007484, 39.74798],
|
|
27
|
+
[-105.007254, 39.748038],
|
|
28
|
+
[-105.007224, 39.747996],
|
|
29
|
+
[-105.00704, 39.74808],
|
|
30
|
+
[-105.006878, 39.748188],
|
|
31
|
+
[-105.00693, 39.748223],
|
|
32
|
+
[-105.00681, 39.748372],
|
|
33
|
+
[-105.006749, 39.748523],
|
|
34
|
+
[-105.006624, 39.74863],
|
|
35
|
+
[-105.006638, 39.748641],
|
|
36
|
+
[-105.006521, 39.748736],
|
|
37
|
+
[-105.006734, 39.748736],
|
|
38
|
+
[-105.006738, 39.748757],
|
|
39
|
+
[-105.006766, 39.748757],
|
|
40
|
+
[-105.006796, 39.748878],
|
|
41
|
+
[-105.00686, 39.748986],
|
|
42
|
+
[-105.00696, 39.749077],
|
|
43
|
+
[-105.006919, 39.749103],
|
|
44
|
+
[-105.007099, 39.749198],
|
|
45
|
+
[-105.007072, 39.74924],
|
|
46
|
+
[-105.006874, 39.749291],
|
|
47
|
+
[-105.006962, 39.749349],
|
|
48
|
+
[-105.00719, 39.749449],
|
|
49
|
+
[-105.007191, 39.749467],
|
|
50
|
+
[-105.00745, 39.749525],
|
|
51
|
+
[-105.007633, 39.749535],
|
|
52
|
+
[-105.007613, 39.749485],
|
|
53
|
+
[-105.007884, 39.749454],
|
|
54
|
+
[-105.007869, 39.749385],
|
|
55
|
+
[-105.007985, 39.749351],
|
|
56
|
+
[-105.007956, 39.749294],
|
|
57
|
+
[-105.008018, 39.749277],
|
|
58
|
+
[-105.007993, 39.749232],
|
|
59
|
+
[-105.008094, 39.749221],
|
|
60
|
+
[-105.008075, 39.749183],
|
|
61
|
+
[-105.008185, 39.749115],
|
|
62
|
+
],
|
|
63
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const VENUE = {
|
|
2
|
+
"id": "bell-centre",
|
|
3
|
+
"wikidata": "Q522282",
|
|
4
|
+
"division": "atlantic",
|
|
5
|
+
"en": { "title": "Bell Centre", "revision": 1380110375 },
|
|
6
|
+
"seats": { "hockey": 20962, "source": "wikipedia", "wikidata": 21273, "read": "the article's seating capacity for hockey, 20,962 (the infobox's present figure, from 2025); Wikidata's one capacity (21,273) is the first figure of 1996" },
|
|
7
|
+
"tiers": { "count": 3, "basis": "documented", "read": "the article says the Bell Centre is arranged in a three-tier layout: the lower 100 section, the 200 section between two levels of boxes, and the upper 300 section" },
|
|
8
|
+
"roof": { "kind": "fixed", "basis": "plausible", "read": "an indoor arena: a fixed roof, as nothing says otherwise" },
|
|
9
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const OSM = {
|
|
2
|
+
"source": "way/19911284",
|
|
3
|
+
"read": "2026-10-20",
|
|
4
|
+
"base": "2026-10-20T23:14:51Z",
|
|
5
|
+
"licence": "ODbL-1.0",
|
|
6
|
+
"attribution": "(c) OpenStreetMap contributors, ODbL 1.0, openstreetmap.org/copyright",
|
|
7
|
+
"axis": { "heading": 132, "basis": "building", "strength": 2.28, "elongation": 1.53, "centre": [-73.569281, 45.496067] },
|
|
8
|
+
"ring": [
|
|
9
|
+
[-73.570363, 45.496182],
|
|
10
|
+
[-73.569576, 45.496827],
|
|
11
|
+
[-73.569294, 45.496653],
|
|
12
|
+
[-73.569259, 45.496681],
|
|
13
|
+
[-73.568425, 45.496166],
|
|
14
|
+
[-73.568449, 45.496144],
|
|
15
|
+
[-73.568418, 45.496124],
|
|
16
|
+
[-73.568329, 45.496108],
|
|
17
|
+
[-73.5683, 45.496056],
|
|
18
|
+
[-73.568168, 45.495975],
|
|
19
|
+
[-73.56905, 45.495279],
|
|
20
|
+
[-73.569731, 45.49569],
|
|
21
|
+
[-73.570154, 45.495977],
|
|
22
|
+
[-73.570107, 45.496015],
|
|
23
|
+
],
|
|
24
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const VENUE = {
|
|
2
|
+
"id": "benchmark-international-arena",
|
|
3
|
+
"wikidata": "Q940199",
|
|
4
|
+
"division": "atlantic",
|
|
5
|
+
"en": { "title": "Benchmark International Arena", "revision": 1376541285 },
|
|
6
|
+
"seats": { "hockey": 19092, "source": "wikipedia", "wikidata": 19204, "read": "the infobox's figure for ice hockey, 19,092; Wikidata's one capacity (19,204) has no sport" },
|
|
7
|
+
"tiers": { "count": 2, "basis": "plausible", "read": "no source says how many seating levels there are: two bowls are drawn" },
|
|
8
|
+
"roof": { "kind": "fixed", "basis": "plausible", "read": "an indoor arena: a fixed roof, as nothing says otherwise; OpenStreetMap maps the building 43.47 metres high" },
|
|
9
|
+
};
|