@johnmorrisdotca/kyogijo-stadiums 0.44.0 → 0.44.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import type { ContextArea } from "./context.ts";
2
+ /** Vancouver: water, green and trees round BC Place and Rogers Arena: 0 buildings, 0 street lines, 28 land cover outlines and 1370 trees within 450 metres of the middle, read 2026-10-22. */
3
+ export declare const VANCOUVER_NATURE: ContextArea;
@@ -0,0 +1,57 @@
1
+ /** Vancouver: water, green and trees round BC Place and Rogers Arena: 0 buildings, 0 street lines, 28 land cover outlines and 1370 trees within 450 metres of the middle, read 2026-10-22. */
2
+ export const VANCOUVER_NATURE = {
3
+ "key": "vancouver-nature",
4
+ "name": { "en": "Vancouver: water, green and trees round BC Place and Rogers Arena", "ja": "バンクーバー:BCプレイスとロジャース・アリーナ周辺の水面・緑地・樹木" },
5
+ "origin": [-123.11038, 49.277237],
6
+ "radius": 450,
7
+ "grid": 0.5,
8
+ "layers": {
9
+ "landCover": {
10
+ "source": "VoxCity 1.7.0 land cover from OpenStreetMap",
11
+ "read": "2026-10-22",
12
+ "licence": "ODbL 1.0 (OpenStreetMap); VoxCity is MIT",
13
+ "attribution": { "en": "Land cover made with VoxCity (MIT) from OpenStreetMap", "ja": "土地被覆:OpenStreetMap から VoxCity(MIT)で作成" },
14
+ /** Each outline: the class (0 water, 1 green, 2 wood), then the first point (east, north) and each step to the next, in grid steps from `origin`. A hole is an outline of its own (filled even-odd). */
15
+ "rows": [
16
+ [0, 1838, 1998, -80, -120, 80, -40],
17
+ [0, 1558, 1998, -479, -200, 479, 40],
18
+ [0, 1119, -1079, -1758, 40, 0, -280],
19
+ [0, -80, -1159, 120, 80, 0, -40],
20
+ [0, -679, -1358, -1319, 159, 0, -799],
21
+ [1, 799, 1878, -80, -120, 80, -40],
22
+ [1, 1079, 1758, -120, -120, 639, 160],
23
+ [1, -1319, 1358, -199, -39, 199, -40],
24
+ [1, -1478, 1239, -120, -240, 160, 160],
25
+ [1, 80, 1199, -160, -120, 160, -80],
26
+ [1, -1119, 440, -160, 0, 0, -80],
27
+ [1, 599, 479, -40, -279, 280, 200, 0, 40],
28
+ [1, 639, 400, 40, 40, 0, -40],
29
+ [1, 1998, 479, -40, -359, 40, 0],
30
+ [1, 1159, 519, -480, -359, 520, -40],
31
+ [1, 1918, 80, -120, -120, 160, -40],
32
+ [1, 599, -160, -40, -160, 120, 40],
33
+ [1, 1438, -320, -119, -159, 119, 0],
34
+ [1, 1119, -240, -120, -479, 160, -40],
35
+ [1, 1718, -519, -200, -400, 160, 0],
36
+ [1, -919, -959, 320, 0, -40, 120],
37
+ [1, 1159, -1159, -40, 80, -240, -80],
38
+ [1, -1598, -919, -200, -439, 240, 319],
39
+ [1, 959, -1279, -120, -239, 80, -40],
40
+ [1, 120, -1119, -240, -160, -40, -119, 280, -120],
41
+ [1, -40, -1358, 40, 39, 0, -39],
42
+ [1, -799, -1598, -160, -120, 200, 40],
43
+ [1, -1598, -1918, -200, -80, 240, 0],
44
+ ],
45
+ },
46
+ "trees": {
47
+ "source": "OpenStreetMap natural=tree and the City of Vancouver's Public trees: https://opendata.vancouver.ca/explore/dataset/public-trees/",
48
+ "read": "2026-10-22",
49
+ "licence": "ODbL 1.0 (OpenStreetMap) and the Open Government Licence - Vancouver",
50
+ "attribution": { "en": "Trees: City of Vancouver open data, Public trees, under the Open Government Licence - Vancouver; simplified and edited by Kyogijo Stadiums", "ja": "樹木:バンクーバー市オープンデータ「Public trees」(Open Government Licence - Vancouver);Kyogijo Stadiums が加工して作成" },
51
+ /** One row for all the trees: the first tree's east, north and crown radius, then each next tree's step in east and north from the one before and its crown radius, all in grid steps. */
52
+ "rows": [
53
+ [-343, -825, 5, 4, 3, 7, 5, 4, 6, 8, 6, 5, 27, 11, 4, -1, 1, 5, 7, 6, 5, -13, 1, 5, -131, 8, 5, 15, 2, 5, -16, 2, 4, 0, 8, 5, -4, 2, 5, 20, 1, 5, -22, 3, 5, 41, 8, 5, -17, 0, 5, 10, 0, 5, 11, 4, 5, 57, 1, 4, -2, 1, 5, -19, 0, 5, 76, 3, 5, -208, 1, 5, 314, 1, 5, -228, 1, 4, -13, 1, 5, -55, 1, 5, 75, 1, 5, 112, 2, 4, 118, 0, 5, -313, 1, 5, 25, 2, 5, 261, 3, 5, -221, 1, 5, 258, 1, 5, -249, 0, 5, -52, 0, 5, -7, 1, 4, -2, 1, 5, 131, 0, 5, -166, 2, 5, 321, 1, 5, 29, 0, 5, -168, 1, 4, -136, 2, 5, 280, 1, 5, -315, 2, 5, 320, 2, 5, -248, 0, 5, 5, 1, 5, -33, 1, 5, -33, 3, 5, 315, 0, 5, -274, 3, 5, 140, 0, 5, -12, 1, 5, -223, 1, 5, 65, 3, 5, -59, 8, 5, 106, 0, 5, -36, 0, 5, 42, 1, 4, 4, 5, 5, 5, 1, 4, 107, 0, 5, -147, 1, 5, -77, 1, 5, 213, 0, 5, 192, 3, 5, -281, 2, 5, 290, 3, 5, -324, 0, 5, 351, 1, 5, -340, 5, 5, 5, 2, 5, 119, 1, 5, -13, 1, 5, -101, 3, 5, -148, 1, 3, 152, 1, 4, -172, 0, 5, 4, 0, 3, 210, 2, 4, -1, 3, 5, 86, 2, 5, -76, 4, 5, -35, 0, 4, -2, 1, 5, -165, 1, 5, 293, 1, 5, -44, 0, 5, 11, 0, 5, 20, 3, 5, -67, 1, 5, 3, 1, 4, -43, 0, 5, 158, 0, 5, -151, 1, 4, 90, 3, 5, -209, 0, 5, 121, 1, 5, 40, 2, 5, -32, 1, 5, -1, 3, 5, -115, 2, 5, 174, 1, 5, -11, 0, 4, 23, 0, 5, -26, 1, 5, -31, 2, 5, -2, 1, 5, 71, 1, 5, -186, 3, 5, 129, 3, 5, -3, 1, 5, -113, 4, 5, 126, 2, 5, -1, 1, 5, 66, 4, 5, -178, 2, 5, -119, 6, 3, -6, 1, 5, 139, 1, 5, -206, 2, 5, 85, 4, 5, 135, 4, 5, 2, 0, 3, 87, 1, 5, -2, 1, 4, 30, 0, 5, -233, 0, 4, -3, 2, 5, 130, 2, 5, 68, 4, 5, -181, 3, 5, 128, 1, 5, 77, 2, 5, -73, 0, 3, 73, 1, 4, -186, 4, 5, 149, 1, 5, -26, 1, 5, 24, 1, 4, -21, 0, 3, 51, 1, 5, -161, 6, 5, 147, 8, 5, -126, 1, 4, 126, 0, 4, 448, 1, 5, -687, 0, 5, 109, 0, 5, -236, 0, 5, 58, 7, 5, 79, 2, 5, 127, 5, 4, -269, 1, 5, 264, 0, 5, 559, 6, 5, -429, 0, 5, -108, 4, 4, -4, 1, 5, -291, 0, 5, 74, 1, 5, 326, 2, 4, -177, 1, 5, 200, 4, 5, -12, 4, 4, 424, 3, 5, 84, 7, 5, -496, 2, 5, 450, 2, 5, -443, 3, 5, -80, 6, 5, 90, 2, 5, -469, 1, 4, 393, 6, 5, 3, 1, 4, 516, 1, 5, -13, 1, 5, -886, 6, 3, 395, 0, 5, 3, 1, 4, 82, 1, 5, -66, 8, 4, 451, 0, 5, -11, 0, 5, -443, 1, 5, -403, 0, 5, 486, 1, 5, 360, 5, 5, -448, 5, 5, 24, 1, 4, 436, 4, 5, -888, 0, 5, 878, 1, 5, -832, 0, 5, -41, 1, 7, 459, 3, 5, -18, 1, 5, 23, 1, 4, 15, 4, 4, -427, 4, 5, -38, 2, 7, 479, 3, 4, -62, 0, 5, 38, 0, 5, -452, 4, 5, 472, 0, 5, -5, 7, 5, 20, 3, 5, -477, 1, 5, 5, 3, 7, 467, 2, 5, -332, 3, 5, -131, 3, 5, 287, 3, 5, -157, 2, 5, 476, 1, 5, -14, 2, 5, -580, 2, 6, 338, 1, 5, -216, 2, 4, -5, 1, 5, 228, 4, 5, -14, 1, 5, -199, 2, 5, 182, 1, 5, 9, 0, 5, -204, 1, 5, -112, 1, 5, 108, 0, 5, 227, 1, 5, -327, 1, 6, 504, 2, 4, -189, 1, 5, -201, 0, 5, 491, 2, 5, -300, 1, 5, 196, 2, 5, -403, 0, 5, -94, 4, 5, 107, 1, 5, -12, 3, 4, 523, 2, 5, -527, 0, 5, 16, 2, 5, -89, 7, 7, 103, 1, 5, 425, 0, 4, 88, 3, 5, -89, 0, 5, -525, 2, 5, 97, 3, 5, -19, 3, 5, 140, 5, 5, 1283, 2, 5, -1296, 1, 5, 347, 0, 4, -478, 0, 5, 1419, 0, 5, -9, 2, 5, -935, 0, 5, 928, 0, 5, 33, 1, 5, -43, 0, 5, -26, 0, 5, -1334, 2, 5, 1306, 1, 5, -21, 0, 5, 112, 1, 5, -208, 0, 5, 107, 1, 5, -1156, 0, 5, -14, 2, 5, 329, 2, 5, 703, 1, 5, 239, 2, 5, -1279, 0, 5, -155, 0, 7, 1213, 3, 5, -711, 3, 5, -375, 0, 5, 918, 1, 5, 15, 0, 5, -1122, 5, 5, 72, 0, 4, 8, 0, 5, 1425, 2, 5, -854, 0, 5, -76, 0, 5, 685, 1, 5, -670, 5, 4, -396, 0, 5, -201, 3, 5, 192, 1, 5, 1316, 0, 5, -1506, 0, 4, 593, 0, 5, -506, 3, 5, 115, 0, 5, -380, 1, 5, 271, 0, 8, 1163, 1, 5, -649, 1, 4, -3, 3, 5, 115, 0, 5, -727, 2, 5, 200, 0, 5, 1317, 0, 5, -1414, 0, 7, 1198, 3, 5, -673, 0, 4, 54, 2, 5, 607, 0, 5, -1094, 0, 5, -354, 0, 5, 1423, 2, 5, -1155, 0, 5, -275, 2, 5, 208, 2, 3, -47, 1, 4, 44, 1, 5, -47, 0, 5, 631, 2, 5, -465, 1, 5, 488, 1, 4, -571, 1, 5, 3, 0, 4, 111, 1, 5, -332, 0, 5, 264, 1, 3, 582, 1, 5, -61, 2, 5, -795, 0, 5, 139, 1, 5, 49, 2, 5, 2, 2, 4, 1447, 1, 5, -1628, 3, 5, 122, 1, 5, 154, 1, 3, -40, 0, 3, -2, 1, 5, -272, 0, 5, 0, 2, 5, 856, 1, 4, -648, 6, 3, -57, 0, 5, 54, 1, 5, 118, 1, 5, 1123, 1, 5, -1113, 2, 5, 519, 1, 5, -565, 3, 5, -271, 1, 5, 47, 2, 5, 231, 1, 4, -155, 0, 5, 52, 0, 5, 4, 1, 3, 673, 2, 5, -838, 8, 5, 1427, 1, 5, -1327, 1, 4, -53, 0, 5, 104, 1, 5, -54, 1, 5, 758, 0, 5, -578, 1, 4, -1, 1, 5, -262, 4, 5, 41, 2, 5, -43, 0, 5, 127, 2, 4, -4, 2, 5, -108, 8, 5, -3, 1, 5, 1395, 1, 5, -1291, 1, 3, -4, 0, 5, 779, 2, 4, 66, 1, 5, -929, 6, 6, 860, 1, 5, -788, 0, 5, 807, 4, 4, 498, 3, 5, -503, 4, 5, -885, 3, 5, 907, 2, 4, -907, 6, 5, 903, 0, 5, -892, 3, 5, 913, 5, 4, -845, 2, 5, 772, 0, 5, -848, 2, 4, 919, 2, 5, -922, 0, 5, -10, 0, 5, 835, 2, 5, 48, 0, 5, -879, 5, 5, 941, 1, 5, -953, 0, 5, 10, 1, 5, 864, 6, 5, -883, 1, 5, 11, 1, 5, 242, 4, 5, -247, 1, 5, 1410, 0, 5, -1280, 0, 5, 767, 1, 5, -899, 1, 5, -8, 8, 5, -21, 2, 5, 19, 0, 5, 300, 1, 5, -143, 0, 3, 117, 0, 5, 630, 2, 5, 11, 1, 5, -23, 0, 5, 810, 4, 5, -1714, 3, 5, 181, 2, 3, 132, 2, 5, 610, 2, 5, 795, 0, 5, -810, 0, 5, -10, 1, 5, -920, 1, 5, 1726, 2, 5, -1387, 0, 5, -327, 1, 5, -3, 2, 5, -19, 5, 5, 958, 2, 5, 508, 0, 5, -1458, 4, 5, 1105, 2, 4, -1108, 1, 5, 1105, 1, 5, -870, 1, 3, 714, 2, 5, 179, 7, 4, -1141, 1, 5, -11, 0, 5, 344, 3, 5, -73, 0, 3, 897, 3, 4, -3, 1, 5, -827, 1, 5, 699, 7, 5, -709, 2, 5, -43, 0, 3, 632, 3, 5, -594, 2, 5, -5, 5, 5, 915, 0, 4, -5, 2, 5, -931, 3, 5, 730, 0, 5, -704, 1, 5, 926, 1, 5, -931, 4, 5, 699, 5, 5, -710, 1, 5, 623, 3, 5, -896, 1, 5, -1, 3, 5, 974, 9, 5, -676, 7, 5, -276, 1, 3, -4, 1, 5, -39, 0, 5, 3, 1, 3, 1451, 1, 5, -469, 2, 5, -34, 4, 5, -940, 2, 3, 1463, 0, 3, -1464, 0, 5, 40, 1, 5, 4, 1, 5, 903, 1, 5, 304, 0, 4, 197, 1, 3, -545, 1, 5, 50, 0, 5, 508, 2, 5, -213, 0, 5, -1234, 6, 5, 11, 11, 5, 38, 0, 5, -37, 1, 3, 1436, 0, 5, -16, 5, 5, -1371, 4, 5, 1177, 0, 4, -1179, 0, 5, 1175, 2, 5, 216, 1, 3, -16, 11, 5, 17, 3, 3, -213, 0, 4, -1194, 6, 5, 1191, 2, 5, -1181, 6, 5, 8, 8, 7, 1177, 3, 14, -856, 3, 5, -40, 1, 5, -22, 0, 5, -213, 1, 3, 216, 2, 7, -251, 2, 6, 278, 4, 5, -236, 0, 5, 2, 1, 5, 195, 3, 6, -227, 4, 6, 175, 1, 5, 103, 3, 4, 1, 1, 5, -52, 0, 5, -11, 3, 6, -4, 1, 5, -202, 1, 6, 322, 7, 5, 149, 0, 5, -266, 2, 5, -11, 0, 5, 1427, 2, 5, -1610, 0, 3, 479, 1, 5, 655, 2, 4, -1002, 3, 5, 1447, 1, 7, -1531, 1, 6, 1083, 0, 5, 466, 0, 5, -804, 1, 3, -788, 0, 5, 7, 2, 3, 1550, 0, 5, -938, 1, 5, -289, 2, 6, 1210, 1, 5, -999, 1, 5, 982, 2, 5, -1473, 0, 5, 449, 1, 5, -446, 0, 4, 58, 3, 5, 1499, 0, 5, -41, 0, 5, -64, 1, 5, 107, 0, 8, -39, 0, 7, 26, 0, 5, -1584, 2, 5, 322, 1, 5, -189, 1, 5, -2, 0, 4, 1415, 0, 6, -1219, 0, 3, 472, 1, 3, 316, 1, 4, 416, 1, 7, -73, 0, 5, -346, 3, 5, 327, 1, 5, 147, 1, 6, -42, 0, 5, -32, 2, 7, -1433, 0, 5, 1401, 0, 5, -1359, 1, 5, 1302, 0, 7, -3, 1, 5, 124, 0, 7, -66, 1, 7, -818, 1, 5, -15, 0, 5, 935, 0, 5, -764, 1, 3, 683, 0, 8, -93, 0, 7, -2, 1, 5, 63, 0, 7, -25, 1, 5, -1328, 0, 4, 0, 2, 5, 233, 4, 5, -41, 0, 6, 43, 0, 3, 1134, 1, 5, 6, 0, 6, -45, 1, 7, -667, 0, 5, -131, 1, 5, -616, 1, 3, 599, 2, 5, 832, 1, 5, -1148, 3, 5, 1149, 0, 6, 13, 3, 5, -682, 1, 5, 5, 0, 4, 612, 0, 5, 70, 1, 7, -845, 0, 5, 761, 0, 5, 18, 0, 7, -11, 1, 7, -1080, 1, 5, 292, 0, 5, 813, 0, 8, 19, 3, 7, -33, 6, 7, -99, 0, 7, 93, 0, 5, -1075, 1, 3, 482, 1, 4, -4, 0, 5, 577, 1, 5, -767, 0, 5, 947, 0, 7, -266, 1, 7, 220, 0, 7, -921, 1, 5, -223, 1, 5, 917, 0, 5, -1197, 1, 5, 1366, 0, 4, -1362, 0, 5, 1351, 1, 4, -27, 0, 8, -1399, 0, 5, 1463, 1, 5, -13, 0, 5, -41, 1, 7, -152, 0, 5, -1254, 1, 4, 1347, 0, 5, -89, 0, 9, 110, 1, 5, 159, 0, 9, -50, 1, 5, -74, 1, 5, -169, 0, 5, 220, 0, 6, -1099, 0, 5, 203, 2, 5, 241, 0, 5, -225, 0, 5, -256, 1, 3, 1059, 1, 5, 66, 1, 7, -12, 0, 5, -73, 1, 5, 162, 0, 5, -1439, 1, 5, 1404, 0, 5, -172, 0, 5, -1323, 1, 3, 1232, 0, 5, 296, 0, 7, -198, 2, 7, 46, 2, 5, 147, 0, 5, -967, 1, 5, 320, 0, 5, -307, 1, 5, -506, 1, 6, 763, 0, 5, 518, 1, 7, 113, 1, 5, -1075, 0, 5, 1028, 0, 5, -1420, 1, 3, 1228, 0, 5, -1114, 0, 5, 231, 1, 5, 1022, 0, 5, 27, 0, 5, -1354, 0, 6, 1331, 0, 7, -74, 1, 4, 218, 0, 5, -221, 1, 5, -1175, 0, 4, 779, 2, 5, 200, 0, 5, -198, 1, 4, 434, 0, 5, 69, 2, 5, 32, 0, 5, -215, 0, 4, -1231, 1, 5, 1394, 1, 5, -168, 0, 5, -1185, 1, 5, -41, 0, 8, 375, 0, 5, -9, 0, 5, 1030, 1, 7, -530, 0, 5, -767, 0, 6, 1325, 2, 5, -48, 1, 3, 83, 1, 5, 3, 0, 4, -1041, 0, 5, 799, 0, 4, -797, 1, 4, 974, 1, 6, -59, 1, 5, 96, 0, 5, 93, 1, 5, -312, 1, 5, -638, 0, 5, 833, 2, 5, -1383, 1, 5, 450, 0, 5, 966, 1, 5, -561, 0, 5, 530, 2, 7, -829, 0, 5, -428, 1, 3, 1051, 0, 4, -2, 1, 5, -649, 1, 5, 899, 1, 5, -129, 1, 6, 204, 1, 7, -1553, 1, 5, 614, 0, 5, -575, 1, 5, 450, 0, 5, -583, 1, 5, 1423, 0, 5, -743, 0, 5, -14, 1, 5, 644, 1, 5, -290, 1, 4, -2, 0, 5, -501, 0, 5, 197, 3, 5, 825, 2, 5, -1415, 0, 5, 563, 1, 5, -402, 0, 5, -39, 0, 5, 1378, 0, 5, -973, 1, 5, 649, 1, 5, 0, 0, 4, 135, 1, 6, 108, 1, 7, -1533, 0, 5, 1507, 0, 5, -794, 1, 5, 832, 0, 7, -34, 0, 5, -827, 1, 5, -679, 0, 6, 1618, 0, 5, -596, 1, 4, -744, 1, 5, 1346, 0, 4, -347, 1, 5, -649, 0, 5, -529, 0, 5, 616, 3, 5, 844, 0, 7, -1316, 1, 5, 1360, 0, 4, -1359, 0, 4, 1357, 1, 5, 28, 1, 5, -117, 1, 7, -5, 0, 5, 39, 1, 6, -926, 0, 5, 952, 0, 7, -160, 2, 6, 69, 0, 5, -759, 0, 5, -472, 0, 5, -237, 0, 4, 277, 1, 5, 1202, 1, 7, -1404, 0, 5, 732, 1, 5, 819, 1, 5, -244, 1, 6, 219, 0, 5, -20, 1, 5, -894, 0, 5, 966, 0, 7, -938, 1, 5, 851, 0, 5, -190, 0, 5, -1348, 0, 5, 1542, 1, 7, -29, 1, 4, -948, 0, 5, 887, 1, 5, -1470, 0, 6, 1459, 2, 5, 78, 0, 4, -751, 0, 5, -102, 1, 5, -631, 0, 5, 2, 1, 3, 658, 0, 5, 937, 1, 5, -1072, 0, 5, 1024, 0, 5, -155, 1, 5, 177, 0, 5, -231, 0, 4, 0, 2, 5, -703, 1, 5, -405, 0, 5, 530, 0, 5, -527, 0, 3, 215, 1, 3, -3, 0, 5, 877, 1, 5, -657, 1, 5, 852, 0, 5, -24, 0, 4, -177, 0, 5, -598, 2, 5, 679, 0, 5, 4, 0, 5, -1412, 1, 6, 1590, 0, 4, -459, 0, 5, 436, 0, 5, 21, 1, 5, -167, 1, 5, 127, 0, 7, 24, 0, 4, -27, 1, 5, -444, 1, 5, 269, 0, 5, -686, 1, 5, 74, 1, 5, 325, 0, 5, 268, 1, 5, 46, 1, 5, -5, 0, 5, -674, 2, 5, -179, 0, 5, 124, 1, 5, 888, 0, 7, -861, 0, 5, -250, 0, 5, 971, 1, 5, -1149, 1, 5, 1069, 0, 5, 247, 1, 5, -169, 0, 5, -46, 0, 5, -1141, 0, 5, 524, 3, 5, -32, 1, 5, -282, 1, 5, 264, 1, 5, 852, 0, 7, -145, 0, 5, -732, 1, 5, -165, 0, 5, 4, 2, 5, 828, 0, 5, 20, 1, 4, -23, 2, 5, 64, 0, 5, 189, 0, 5, -1086, 0, 5, 175, 1, 5, -253, 0, 5, 934, 1, 5, -622, 0, 5, -257, 1, 5, 1054, 0, 8, -162, 1, 4, -1094, 1, 5, -275, 0, 3, 240, 0, 4, -244, 0, 5, 1558, 1, 5, -1274, 0, 6, 1087, 0, 5, -854, 0, 5, 401, 1, 5, -677, 0, 5, 445, 1, 5, 502, 0, 5, 384, 0, 5, -1007, 1, 5, -78, 0, 5, -71, 1, 5, 771, 0, 5, 364, 1, 5, -171, 0, 5, -701, 0, 5, -209, 2, 5, 59, 1, 7, 865, 0, 4, -1139, 2, 4, 271, 1, 5, -274, 0, 5, 1227, 2, 5, -74, 1, 4, -58, 0, 6, -771, 0, 5, 909, 2, 8, -84, 0, 5, -926, 0, 5, 915, 0, 4, 163, 1, 6, -2, 0, 5, -821, 0, 5, -63, 1, 5, 235, 0, 5, -919, 1, 5, 1398, 0, 5, -35, 1, 5, -1321, 0, 3, 1374, 1, 4, 170, 1, 5, -1306, 0, 5, 135, 0, 5, 1, 3, 5, 995, 1, 5, -938, 0, 5, 200, 0, 5, 911, 1, 5, -1018, 1, 5, 192, 0, 5, -196, 0, 5, 46, 0, 5, 779, 1, 5, -1377, 0, 5, 1468, 0, 5, 93, 1, 5, -1282, 1, 5, 117, 1, 5, 1157, 0, 5, -172, 1, 7, -659, 0, 5, 829, 1, 5, 26, 0, 5, -131, 1, 5, 41, 1, 5, -832, 0, 5, 787, 0, 5, -550, 1, 5, 606, 1, 7, -1171, 0, 5, 432, 0, 5, 603, 1, 6, 110, 0, 5, -860, 0, 10, -2, 2, 5, -320, 0, 5, 1163, 1, 5, -722, 0, 5, -276, 0, 5, 1107, 0, 5, -1271, 0, 4, 93, 0, 5, 262, 3, 5, 738, 0, 5, 160, 0, 5, 4, 1, 5, -781, 0, 5, -714, 0, 5, 1383, 1, 5, -860, 0, 6, -128, 2, 5, 748, 0, 5, -859, 0, 5, 1024, 1, 5, 168, 0, 5, -1095, 0, 5, -58, 0, 6, 988, 1, 6, -789, 0, 5, 164, 0, 5, -301, 1, 5, 334, 0, 5, 744, 1, 5, -879, 1, 5, 878, 1, 5, 53, 1, 6, -1220, 0, 5, 1152, 0, 4, -4, 1, 5, -110, 1, 5, 2, 1, 5, -609, 0, 5, -432, 0, 5, -268, 0, 5, 667, 1, 5, 804, 0, 5, -84, 1, 5, 70, 2, 6, -63, 0, 7, -105, 0, 7, -1299, 1, 5, 1126, 1, 5, -1200, 0, 5, 1522, 0, 5, -1514, 1, 5, 751, 0, 5, -744, 1, 5, 778, 0, 5, 712, 1, 5, -1459, 2, 5, 372, 0, 5, -394, 1, 5, 1373, 1, 6, 89, 0, 8, -1390, 1, 5, 669, 1, 5, 713, 0, 5, -677, 0, 5, -378, 2, 5, 777, 0, 4, 178, 1, 7, -4, 0, 5, -1285, 1, 5, 1361, 0, 5, -250, 2, 5, 201, 1, 5, -628, 0, 5, -717, 1, 5, 350, 0, 5, -298, 0, 5, 35, 1, 5, 1325, 0, 5, -11, 1, 8, -839, 2, 5, 195, 0, 5, 640, 0, 5, -839, 0, 5, -203, 1, 5, -279, 1, 5, 1305, 0, 7, -19, 0, 5, -638, 2, 5, 643, 2, 5, -18, 1, 5, -1338, 0, 5, 1343, 1, 8, -1303, 1, 5, 1094, 0, 4, -412, 3, 5, 411, 2, 5, -870, 0, 6, 347, 0, 9, -600, 2, 5, 1449, 1, 6, -1397, 1, 5, 505, 2, 5, 162, 0, 5, 726, 0, 5, -1400, 4, 5, 564, 0, 3, 816, 0, 5, -301, 2, 4, -1114, 0, 5, 9, 0, 3, 222, 0, 5, 1, 1, 4, 485, 0, 5, -389, 1, 5, 1069, 0, 5, -582, 1, 3, 298, 1, 5, -556, 1, 5, -504, 0, 5, 1212, 2, 4, 115, 0, 5, -129, 3, 5, 111, 1, 5, -1352, 1, 5, 603, 0, 8, 626, 0, 5, -1186, 1, 3, 1061, 2, 5, -547, 0, 5, 776, 1, 5, -122, 2, 4, -876, 2, 5, -324, 1, 5, 188, 0, 5, 904, 1, 5, 89, 0, 4, -628, 1, 5, -500, 2, 5, 1113, 2, 5, -15, 3, 5, -585, 1, 7, -520, 0, 5, 1262, 1, 4, -1298, 0, 5, 603, 0, 9, -5, 1, 5, -43, 0, 5, 574, 1, 5, -800, 0, 5, 968, 0, 5, -1242, 0, 5, 1062, 1, 5, 115, 0, 3, -1068, 1, 5, -154, 5, 5, 38, 1, 5, -37, 0, 3, 39, 0, 3, 288, 1, 5, 869, 0, 5, -1142, 0, 5, 90, 2, 4, -3, 1, 5, 1031, 3, 5, -608, 1, 7, 662, 0, 5, -1178, 3, 5, 1103, 2, 5, -813, 0, 5, -208, 1, 5, 660, 0, 5, 504, 0, 5, -269, 1, 5, -891, 0, 4, 1124, 1, 5, -132, 1, 5, -500, 0, 10, 675, 1, 5, -1114, 0, 5, 817, 2, 5, -219, 0, 5, 487, 3, 3, -1149, 0, 3, -60, 1, 3, 57, 1, 5, 458, 0, 5, -517, 1, 5, 913, 1, 5, 293, 1, 5, -490, 1, 5, -491, 1, 5, 824, 1, 5, -434, 1, 3, -644, 2, 5, 558, 3, 5, -228, 1, 5, 231, 0, 5, -428, 2, 5, 130, 0, 5, 577, 3, 5, -12, 1, 5, 419, 1, 3, -266, 1, 5, -969, 1, 5, 1043, 1, 5, -89, 2, 5, -847, 3, 5, 1128, 1, 5, -296, 0, 5, 85, 1, 5, 30, 1, 5, 5, 1, 5, -31, 0, 5, -1017, 0, 3, 1253, 0, 4, -926, 0, 5, 698, 2, 5, -219, 1, 5, 9, 0, 5, 12, 2, 5, -742, 1, 5, 6, 0, 4, 173, 1, 5, 721, 4, 5, -166, 0, 5, -298, 1, 5, 11, 0, 5, 452, 1, 5, 274, 3, 4, -245, 0, 5, 218, 3, 5, -215, 0, 5, -241, 3, 5, -268, 4, 5, -220, 2, 5, 298, 5, 5, 427, 1, 5, -451, 0, 5, -4, 1, 5, 460, 0, 5, -1021, 1, 5, 1239, 2, 5, 3, 1, 3, -740, 3, 4, -10, 3, 5, -499, 0, 3, 612, 1, 5, -296, 0, 5, 71, 0, 5, 168, 0, 5, 59, 1, 5, -617, 0, 5, 998, 1, 5, -402, 2, 5, 404, 1, 4, -421, 2, 5, 452, 1, 5, -4, 0, 5, -1035, 1, 3, 337, 1, 5, 72, 0, 5, -413, 1, 5, 561, 2, 5, -66, 1, 5, 147, 0, 5, -61, 1, 5, 38, 2, 5, -118, 1, 5, 509, 0, 5, -589, 2, 5, 270, 0, 4, 321, 1, 4, -522, 1, 4, -341, 2, 9, 393, 1, 5, 111, 1, 5, -105, 0, 5, 22, 2, 5, 472, 3, 5, -554, 0, 4, -381, 1, 4, 371, 0, 5, 561, 1, 5, -936, 1, 5, 57, 1, 9, 371, 1, 5, 29, 1, 5, 109, 6, 5, -127, 1, 5, -428, 1, 5, 55, 0, 9, 873, 0, 5, -594, 1, 5, 591, 0, 5, -579, 1, 5, 10, 2, 4, 47, 2, 5, -393, 0, 4, 886, 4, 5, 1, 1, 5, -885, 0, 5, 380, 5, 5, 536, 1, 5, -3, 2, 5, -530, 0, 5, -368, 7, 4, 264, 4, 5, 90, 3, 5, 544, 0, 5, -548, 2, 5, -255, 2, 5, 472, 0, 5, 301, 5, 5, 2, 3, 5, -793, 5, 7, 504, 1, 5, -494, 9, 9, 552, 2, 3, -5, 0, 5, -39, 2, 5, -238, 2, 5, 5, 0, 7, -266, 3, 8, 558, 9, 3, -5, 0, 5, 214, 1, 5, -545, 1, 5, -14, 2, 5, 72, 0, 6, 239, 1, 6, -313, 1, 5, 599, 7, 5, -238, 2, 5, -332, 0, 5, -93, 0, 5, 660, 0, 5, -27, 1, 4, -482, 1, 5, -100, 2, 5, 580, 3, 5, -526, 3, 5, 291, 1, 5, -409, 2, 5, 452, 0, 5, -454, 2, 6, -88, 0, 7, 505, 1, 5, 262, 1, 5, -4, 1, 5, -624, 0, 5, -56, 2, 5, 191, 3, 6, -265, 2, 6, 549, 1, 5, -431, 3, 5, 391, 1, 5, -280, 1, 5, 281, 0, 5, -449, 2, 6, -51, 2, 6, 214, 0, 5, 501, 4, 4, -767, 0, 4, 325, 3, 5, -263, 3, 6, 224, 0, 5, -142, 1, 5, 134, 2, 5, -136, 0, 5, -76, 1, 5, 506, 2, 5, -501, 3, 7, 505, 1, 5, -271, 5, 4, -175, 0, 3, 170, 0, 5, 316, 1, 5, -271, 0, 5, -2, 3, 5, 248, 6, 5, -472, 1, 5, 474, 1, 6, -477, 0, 5, 517, 2, 3, -277, 2, 5, -251, 7, 5, 502, 4, 3, -479, 0, 5, 519, 1, 4, -5, 1, 5, -51, 2, 5, -219, 2, 5, -27, 9, 4, -2, 1, 5, 240, 0, 5, -192, 5, 6, -2, 1, 5, -33, 8, 5],
54
+ ],
55
+ },
56
+ },
57
+ };
package/dist/context.js CHANGED
@@ -299,7 +299,9 @@ export function blockPlanes(b, prefix) {
299
299
  const ghost = (p) => (b.ghost ? { ...p, shy: [0, 0], fadeAt: -10, fadeTo: 0.16 } : p);
300
300
  // A see-through building: each wall gives way as the eye comes round to its side and looks in, to a trace of itself, and the roof is always nearly clear.
301
301
  const xray = b.xray;
302
- const wall1 = (p) => (xray === undefined ? ghost(p) : { ...shy(p, xray), fadeTo: 0.06, fadeSide: true });
302
+ // A wall of a solid building, nothing showing through it, is hidden by the nearer walls and the roof while it faces away: `cull` says which side of the plane is the building's outside (a wall's `u` x `v` points to the left of its walk, which is in when the ring is wound clockwise).
303
+ const outside = b.ghost || xray !== undefined || wound === 0 ? undefined : wound > 0 ? -1 : 1;
304
+ const wall1 = (p) => (xray === undefined ? (outside === undefined ? ghost(p) : { ...p, cull: outside }) : { ...shy(p, xray), fadeTo: 0.06, fadeSide: true });
303
305
  const roof1 = (p) => (xray === undefined ? ghost(p) : { ...p, shy: [0, 0], fadeAt: -10, fadeTo: 0.1 });
304
306
  b.ring.forEach((p, i) => {
305
307
  const q = b.ring[(i + 1) % b.ring.length];
@@ -78,6 +78,11 @@ export declare function nudge(view: TurnView, toward: TurnToward, amount: number
78
78
  * yaw falls) and a drag down tips the top toward the eye (the pitch falls).
79
79
  */
80
80
  export declare function drag(view: TurnView, dx: number, dy: number, tilt?: boolean, degreesPerPixel?: number): TurnView;
81
+ /**
82
+ * The view after the wheel turns by `deltaY` (positive is down, as `WheelEvent.deltaY` says) while the view has focus: the wheel up tips the surface as a drag up does (the near edge goes up, the
83
+ * front is lifted: the pitch rises) and the wheel down as a drag down does.
84
+ */
85
+ export declare const wheelTip: (view: TurnView, deltaY: number) => TurnView;
81
86
  /** A box in the scene, in pixels from the scene's middle, the floor at `z0` and the highest thing at `z1`. */
82
87
  export interface Box3 {
83
88
  x0: number;
@@ -100,6 +100,11 @@ export function nudge(view, toward, amount) {
100
100
  export function drag(view, dx, dy, tilt = true, degreesPerPixel = 0.4) {
101
101
  return clampView({ ...view, yaw: view.yaw - dx * degreesPerPixel, pitch: tilt ? view.pitch - dy * degreesPerPixel * 0.75 : view.pitch });
102
102
  }
103
+ /**
104
+ * The view after the wheel turns by `deltaY` (positive is down, as `WheelEvent.deltaY` says) while the view has focus: the wheel up tips the surface as a drag up does (the near edge goes up, the
105
+ * front is lifted: the pitch rises) and the wheel down as a drag down does.
106
+ */
107
+ export const wheelTip = (view, deltaY) => clampView({ ...view, pitch: view.pitch - deltaY * 0.15 });
103
108
  const RAD = Math.PI / 180;
104
109
  /** Where a point of the scene falls on the screen, in pixels from the middle of the container, when the scene is placed as given and the eye is `distance` pixels away. */
105
110
  export function project(point, view, at, distance) {
@@ -176,11 +181,12 @@ export function fit(b, view, w, h, distance, pad = 0.03) {
176
181
  }
177
182
  /** Two matrices as one: `b` first, then `a`. */
178
183
  export function multiply(a, b) {
179
- const out = new Array(16).fill(0);
180
- for (let col = 0; col < 4; col += 1)
184
+ const out = new Array(16);
185
+ for (let col = 0; col < 16; col += 4) {
186
+ const [b0, b1, b2, b3] = [b[col], b[col + 1], b[col + 2], b[col + 3]];
181
187
  for (let row = 0; row < 4; row += 1)
182
- for (let k = 0; k < 4; k += 1)
183
- out[col * 4 + row] += a[k * 4 + row] * b[col * 4 + k];
188
+ out[col + row] = a[row] * b0 + a[4 + row] * b1 + a[8 + row] * b2 + a[12 + row] * b3;
189
+ }
184
190
  return out;
185
191
  }
186
192
  const translation = (x, y, z) => [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1];
@@ -208,11 +214,20 @@ export function cameraMatrix(view, at, distance) {
208
214
  * surface swelled and shrank in steps.
209
215
  */
210
216
  const num = (value) => {
211
- const text = value.toFixed(10).replace(/\.?0+$/, "");
212
- return text === "-0" || text === "" ? "0" : text;
217
+ if (value === 0)
218
+ return "0";
219
+ const text = value.toFixed(10);
220
+ // Without the zeros at the end, and the point if nothing is left after it: the same text as stripping them with a pattern, a good deal faster for the thousands of numbers a turn writes.
221
+ let end = text.length;
222
+ while (text.charCodeAt(end - 1) === 48)
223
+ end -= 1;
224
+ if (text.charCodeAt(end - 1) === 46)
225
+ end -= 1;
226
+ const out = end === text.length ? text : text.slice(0, end);
227
+ return out === "-0" ? "0" : out;
213
228
  };
214
229
  /** A matrix as the CSS `transform` that applies it. */
215
- export const matrix3d = (m) => `matrix3d(${m.map(num).join(",")})`;
230
+ export const matrix3d = (m) => `matrix3d(${num(m[0])},${num(m[1])},${num(m[2])},${num(m[3])},${num(m[4])},${num(m[5])},${num(m[6])},${num(m[7])},${num(m[8])},${num(m[9])},${num(m[10])},${num(m[11])},${num(m[12])},${num(m[13])},${num(m[14])},${num(m[15])})`;
216
231
  /** How far a point is from the eye's plane after the scene is turned and tipped, in pixels towards the eye: bigger is nearer. Planes are drawn far first. */
217
232
  export function depthOf(point, view, at) {
218
233
  const k = at.k * view.zoom;
package/dist/turn.dom.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // reduced motion gets jumps for glides, and where the browser cannot draw in 3D the flat drawing stays. Nothing is polled and nothing runs while the view is
4
4
  // still: the elements' `transform`s are written when the view changes, and a glide between viewpoints is a short run of animation frames.
5
5
  import { drawKyogijo } from "./draw.js";
6
- import { STEP, TURN_PRESET_NAMES, clampView, drag, nearest, nudge, presetOf, presetView, step, turnDirection } from "./turn.camera.js";
6
+ import { STEP, TURN_PRESET_NAMES, clampView, drag, nearest, nudge, presetOf, presetView, step, turnDirection, wheelTip } from "./turn.camera.js";
7
7
  import { buildTurn, floorOptions, openingView, turnWords } from "./turn.model.js";
8
8
  import { controlsMarkup, layoutOf, turnMarkupOf, worldMarkup } from "./turn.markup.js";
9
9
  import { KYOGIJO_TURN_STYLE } from "./turn.style.js";
@@ -103,11 +103,14 @@ export function mountTurn(host, options, settings, build) {
103
103
  let written = [];
104
104
  let stacked = [];
105
105
  let faded = [];
106
+ /** Which elements are left out of the view now (the faces of a solid that face away from the eye): they are not written to, and drawn at all, till the view turns them round. */
107
+ let gone = [];
106
108
  const gather = () => {
107
109
  elements = [...stage.querySelectorAll(".ky-turn-floor, .ky-up")];
108
110
  written = elements.map((one) => one.style.transform);
109
111
  stacked = elements.map((one) => one.style.zIndex);
110
112
  faded = elements.map((one) => one.style.opacity);
113
+ gone = elements.map(() => false);
111
114
  };
112
115
  gather();
113
116
  // The crowd outside, a canvas on the floor (the floor's matrix carries it), painted again only when its level, its detail or its colour changes.
@@ -135,9 +138,18 @@ export function mountTurn(host, options, settings, build) {
135
138
  const paint = (now) => {
136
139
  const width = stage.clientWidth || 960;
137
140
  const height = stage.clientHeight || 600;
138
- const layout = layoutOf(model, now, width, height, settings.lens);
141
+ const layout = layoutOf(model, now, width, height, settings.lens, true);
139
142
  outside?.zoom(now.zoom);
143
+ const culled = layout.culled;
140
144
  elements.forEach((one, i) => {
145
+ // A face turned away from the eye is hidden by the solid it belongs to: it is taken out of the page's drawing, not written to.
146
+ const away = culled?.[i] === true;
147
+ if (away !== gone[i]) {
148
+ one.style.display = away ? "none" : "";
149
+ gone[i] = away;
150
+ }
151
+ if (away)
152
+ return;
141
153
  if (written[i] !== layout.transforms[i])
142
154
  one.style.transform = written[i] = layout.transforms[i];
143
155
  const z = String(layout.stack[i]);
@@ -438,7 +450,7 @@ export function mountTurn(host, options, settings, build) {
438
450
  else if (doc.activeElement === stage) {
439
451
  event.preventDefault();
440
452
  grab();
441
- view = event.shiftKey || Math.abs(event.deltaX) > Math.abs(event.deltaY) ? clampView({ ...view, yaw: view.yaw + (event.deltaX || event.deltaY) * 0.25 }) : clampView({ ...view, pitch: view.pitch + event.deltaY * 0.15 });
453
+ view = event.shiftKey || Math.abs(event.deltaX) > Math.abs(event.deltaY) ? clampView({ ...view, yaw: view.yaw + (event.deltaX || event.deltaY) * 0.25 }) : wheelTip(view, event.deltaY);
442
454
  }
443
455
  else
444
456
  return;
@@ -40,6 +40,11 @@ export interface Plane {
40
40
  * (a raised room is seen through every wall between it and the eye, at a low angle and from straight above alike).
41
41
  */
42
42
  fadeSide?: boolean;
43
+ /**
44
+ * A face of a closed solid that nothing shows through (a building's wall, drawn opaque): the side of it that is the solid's outside, as the way its `u` x `v` points, 1 out of the solid and -1 into it. While the face is turned away
45
+ * from the eye it is hidden by the solid's own near faces, so a page that asks for it (`layoutOf`'s `cull`) leaves it out of the view until the view turns it round, and spends nothing on it.
46
+ */
47
+ cull?: 1 | -1;
43
48
  /** A plate the other planes of its building stand on (a floor of a building picked out): it is placed by its farthest corner, so that it is drawn under everything on it, whatever way the view turns. */
44
49
  under?: boolean;
45
50
  /**
@@ -17,15 +17,20 @@ export declare function groupRanks(groups: readonly {
17
17
  /** Where everything is for a view in a container: the placement, the matrix of each element and its place in the stack. */
18
18
  export interface Layout {
19
19
  at: Placement;
20
- /** The floor, then the planes in the model's order. */
20
+ /** The floor, then the planes in the model's order. A plane that `cull` says is turned away has `""` here. */
21
21
  transforms: string[];
22
22
  /** The stack of each: 0 for the ground of a hall, 1 for the floor, then the planes far to near from 2. */
23
23
  stack: number[];
24
24
  /** How much of each is left: 1 for all but the walls that are between the eye and the surface, which are cut away as the view is low and on their side (`fadeOf`). */
25
25
  fade: number[];
26
+ /** Which elements are not drawn at this view, by their place in `transforms`: the faces of a closed solid that are turned away from the eye (`Plane.cull`). Only when `layoutOf` was asked to leave them out. */
27
+ culled?: boolean[];
26
28
  }
27
- /** Work a view out in a container `width` by `height` pixels. */
28
- export declare function layoutOf(model: TurnModel, view: TurnView, width: number, height: number, lens?: number): Layout;
29
+ /**
30
+ * Work a view out in a container `width` by `height` pixels. With `cull`, the faces of a closed solid that are turned away from the eye (`Plane.cull`) are left out: they have no transform and no place in the stack, and `culled` says
31
+ * which, so that a page need not write them at all; without it every plane is laid out, as a still picture needs.
32
+ */
33
+ export declare function layoutOf(model: TurnModel, view: TurnView, width: number, height: number, lens?: number, cull?: boolean): Layout;
29
34
  /** One plane as an element: its box in pixels and the SVG drawn on it in feet. */
30
35
  export declare function planeMarkup(p: Plane, scale: number, transform: string, stack: number, fade?: number): string;
31
36
  /**
@@ -53,20 +53,78 @@ export function groupRanks(groups, yaw) {
53
53
  }));
54
54
  return rank;
55
55
  }
56
- /** Work a view out in a container `width` by `height` pixels. */
57
- export function layoutOf(model, view, width, height, lens) {
56
+ const RAD = Math.PI / 180;
57
+ const prepared = new WeakMap();
58
+ function prepare(model) {
59
+ const known = prepared.get(model.planes);
60
+ if (known !== undefined && known.scale === model.scale && known.centre[0] === model.centre[0] && known.centre[1] === model.centre[1])
61
+ return known;
62
+ const s = model.scale;
63
+ const centre = model.centre;
64
+ const base = [];
65
+ const mid = [];
66
+ for (const p of model.planes) {
67
+ if (p.billboard === true) {
68
+ base.push(undefined);
69
+ mid.push(undefined);
70
+ continue;
71
+ }
72
+ base.push(planeMatrix(p, s, centre));
73
+ if (p.under === true) {
74
+ mid.push(undefined);
75
+ continue;
76
+ }
77
+ const zs = [p.o[2], p.o[2] + p.u[2], p.o[2] + p.v[2], p.o[2] + p.u[2] + p.v[2]];
78
+ mid.push([(p.o[0] + (p.u[0] + p.v[0]) / 2 - centre[0]) * s, (p.o[1] + (p.u[1] + p.v[1]) / 2 - centre[1]) * s, (p.o[2] + (p.u[2] + p.v[2]) / 2) * s, Math.min(...zs) * s, Math.max(...zs) * s]);
79
+ }
80
+ const made = { planes: model.planes, scale: s, centre, base, mid };
81
+ prepared.set(model.planes, made);
82
+ return made;
83
+ }
84
+ /**
85
+ * Whether a plane is turned away from the eye, given its matrix: the sign of the determinant of the 3 by 3 map from the plane's own x and y to the screen says which side of it faces the eye (positive: the side that its `u` x `v`
86
+ * points out of), and `Plane.cull` says which side is the solid's outside. A plane edge-on to the eye has no area to draw.
87
+ */
88
+ const turnedAway = (m, side) => side * (m[0] * (m[5] * m[15] - m[13] * m[7]) - m[4] * (m[1] * m[15] - m[13] * m[3]) + m[12] * (m[1] * m[7] - m[5] * m[3])) <= 0;
89
+ /**
90
+ * Work a view out in a container `width` by `height` pixels. With `cull`, the faces of a closed solid that are turned away from the eye (`Plane.cull`) are left out: they have no transform and no place in the stack, and `culled` says
91
+ * which, so that a page need not write them at all; without it every plane is laid out, as a still picture needs.
92
+ */
93
+ export function layoutOf(model, view, width, height, lens, cull = false) {
58
94
  const distance = eyeDistance(width, height, lens ?? model.lens);
59
95
  const at = fit(model.box, { ...view, zoom: 1 }, width, height, distance);
60
96
  const camera = cameraMatrix(view, at, distance);
61
97
  const s = model.scale;
62
98
  const centre = model.centre;
99
+ const known = prepare(model);
63
100
  const floor = { id: "floor", kind: "ground", o: [model.viewBox.x, model.viewBox.y, model.uprights.lift], u: [model.viewBox.w, 0, 0], v: [0, model.viewBox.h, 0], svg: "" };
101
+ const count = model.planes.length;
102
+ const transforms = new Array(count + 1);
103
+ const culled = cull ? new Array(count + 1).fill(false) : undefined;
64
104
  // A billboard faces the eye at this view.
65
- const planes = model.planes.map((p) => facing(p, view.yaw, view.pitch));
66
- const transforms = [floor, ...planes].map((p) => matrix3d(multiply(camera, planeMatrix(p, s, centre))));
105
+ const planes = new Array(count);
106
+ for (let i = 0; i < count; i += 1)
107
+ planes[i] = facing(model.planes[i], view.yaw, view.pitch);
108
+ transforms[0] = matrix3d(multiply(camera, planeMatrix(floor, s, centre)));
109
+ for (let i = 0; i < count; i += 1) {
110
+ const m = multiply(camera, known.base[i] ?? planeMatrix(planes[i], s, centre));
111
+ const side = planes[i].cull;
112
+ if (culled !== undefined && side !== undefined && turnedAway(m, side)) {
113
+ culled[i + 1] = true;
114
+ transforms[i + 1] = "";
115
+ }
116
+ else
117
+ transforms[i + 1] = matrix3d(m);
118
+ }
67
119
  // Far to near: a plane's depth is that of its middle. The ground of a hall is under everything. The floor is next, unless it stands raised on something (a table tennis table's top, on its legs): then it is
68
120
  // sorted with the rest, by its middle, so that the legs and the skirt on the far side are drawn under it and the ones on the near side over it, and nothing under the top shows through it.
69
- const depth = (p) => {
121
+ const [sinYaw, cosYaw, sinPitch, cosPitch] = [Math.sin(view.yaw * RAD), Math.cos(view.yaw * RAD), Math.sin(view.pitch * RAD), Math.cos(view.pitch * RAD)];
122
+ const k = at.k * view.zoom;
123
+ const depthAt = (x, y, z) => (x * k * sinYaw + y * k * cosYaw) * sinPitch + z * k * cosPitch;
124
+ const depth = (p, i) => {
125
+ const pre = i < 0 ? undefined : known.mid[i];
126
+ if (pre !== undefined)
127
+ return depthAt(pre[0], pre[1], p.fadeSide === true ? (fadeOf(p, view.yaw, view.pitch) >= 1 ? pre[3] : pre[4]) : pre[2]);
70
128
  // A see-through building's wall is a tall plane round what stands in it: a far wall is behind all of it and a near one in front of all of it, however high its middle is, so a far wall is placed by its foot and a near one by its top.
71
129
  const zs = [p.o[2], p.o[2] + p.u[2], p.o[2] + p.v[2], p.o[2] + p.u[2] + p.v[2]];
72
130
  const z = p.fadeSide === true ? (fadeOf(p, view.yaw, view.pitch) >= 1 ? Math.min(...zs) : Math.max(...zs)) : (p.o[2] + (p.u[2] + p.v[2]) / 2);
@@ -75,7 +133,12 @@ export function layoutOf(model, view, width, height, lens) {
75
133
  return depthOf([(p.o[0] + (p.u[0] + p.v[0]) / 2 - centre[0]) * s, (p.o[1] + (p.u[1] + p.v[1]) / 2 - centre[1]) * s, z * s], view, at);
76
134
  };
77
135
  const raised = model.uprights.lift > 0;
78
- const order = [...planes.map((p, i) => ({ i: i + 1, ground: p.kind === "ground", depth: depth(p), group: p.group })), ...(raised ? [{ i: 0, ground: false, depth: depth(floor), group: undefined }] : [])].filter((one) => !one.ground);
136
+ const order = [];
137
+ for (let i = 0; i < count; i += 1)
138
+ if (planes[i].kind !== "ground" && culled?.[i + 1] !== true)
139
+ order.push({ i: i + 1, depth: depth(planes[i], i), group: planes[i].group });
140
+ if (raised)
141
+ order.push({ i: 0, depth: depth(floor, -1), group: undefined });
79
142
  // Buildings that stand apart are drawn one after another, the far one first; a plane of none (the floor) goes by its depth alone.
80
143
  const rank = model.groups === undefined ? undefined : groupRanks(model.groups, view.yaw);
81
144
  // The planes of the blocks that stand round the buildings (`APART`) are stacked by depth among themselves and then woven into the buildings' by depth, each list keeping its own order.
@@ -84,13 +147,16 @@ export function layoutOf(model, view, width, height, lens) {
84
147
  order.length = 0;
85
148
  for (let i = 0, j = 0; i < own.length || j < apart.length;)
86
149
  order.push(j >= apart.length || (i < own.length && own[i].depth <= apart[j].depth) ? own[i++] : apart[j++]);
87
- const stack = new Array(model.planes.length + 1).fill(0);
150
+ const stack = new Array(count + 1).fill(0);
88
151
  stack[0] = 1;
89
152
  order.forEach((one, rank) => {
90
153
  stack[one.i] = rank + 2;
91
154
  });
92
- const fade = [1, ...model.planes.map((p) => fadeOf(p, view.yaw, view.pitch))];
93
- return { at, transforms, stack, fade };
155
+ const fade = new Array(count + 1);
156
+ fade[0] = 1;
157
+ for (let i = 0; i < count; i += 1)
158
+ fade[i + 1] = fadeOf(model.planes[i], view.yaw, view.pitch);
159
+ return { at, transforms, stack, fade, ...(culled === undefined ? {} : { culled }) };
94
160
  }
95
161
  /** One plane as an element: its box in pixels and the SVG drawn on it in feet. */
96
162
  export function planeMarkup(p, scale, transform, stack, fade = 1) {
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The package's version: the same as package.json's, and a test holds them to each other. */
2
- export declare const VERSION = "0.44.0";
2
+ export declare const VERSION = "0.44.2";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The package's version: the same as package.json's, and a test holds them to each other. */
2
- export const VERSION = "0.44.0";
2
+ export const VERSION = "0.44.2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnmorrisdotca/kyogijo-stadiums",
3
- "version": "0.44.0",
3
+ "version": "0.44.2",
4
4
  "description": "Kyogijo Stadiums 競技場, 40 sports' surfaces as SVG, drawn to scale from the rulebooks: NFL, CFL and IFAF football fields, an NHL rink, an NBA court, MLB ballparks, soccer pitches, curling sheets, athletics stadiums, speed skating, short track and figure skating rinks, swimming pools, water polo fields, diving wells, artistic swimming areas, and the courts and fields of tennis, volleyball (indoor, beach and sitting), badminton, pickleball, table tennis, padel, squash, racquetball, sepak takraw, teqball, roundnet, netball, lacrosse, rugby, Australian football, Gaelic games, cricket, field hockey and handball, and the budo of judo, karate, kendo, sumo and kyudo with a dojo of tatami laid as real units, the MMA cage, with each team's name and logo on its side and the score in the middle, stands and a crowd, a pageant of flags, and a 3D view. Light and dark, accessible, a compact badge for lists, a helper for React and a custom element. Zero dependencies.",
5
5
  "keywords": [
6
6
  "sports",
@@ -153,6 +153,11 @@
153
153
  "import": "./dist/cityctx-entry.js",
154
154
  "default": "./dist/cityctx-entry.js"
155
155
  },
156
+ "./areas": {
157
+ "types": "./dist/areas-entry.d.ts",
158
+ "import": "./dist/areas-entry.js",
159
+ "default": "./dist/areas-entry.js"
160
+ },
156
161
  "./osm": {
157
162
  "types": "./dist/osm-entry.d.ts",
158
163
  "import": "./dist/osm-entry.js",