@kerros/viewer 0.0.0-stage → 0.1.0-rc.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.
Files changed (264) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -2
  3. package/dist/adapters/basemap.js +11 -0
  4. package/dist/adapters/basemap.js.map +1 -0
  5. package/dist/adapters/persistence.js +121 -0
  6. package/dist/adapters/persistence.js.map +1 -0
  7. package/dist/adapters/status.js +18 -0
  8. package/dist/adapters/status.js.map +1 -0
  9. package/dist/components/ElevatorPanel.js +120 -0
  10. package/dist/components/ElevatorPanel.js.map +1 -0
  11. package/dist/components/Icons.js +57 -0
  12. package/dist/components/Icons.js.map +1 -0
  13. package/dist/components/Inspector.js +1184 -0
  14. package/dist/components/Inspector.js.map +1 -0
  15. package/dist/components/NavigatePanel.js +301 -0
  16. package/dist/components/NavigatePanel.js.map +1 -0
  17. package/dist/components/NavigationGraph.js +234 -0
  18. package/dist/components/NavigationGraph.js.map +1 -0
  19. package/dist/components/StructureExplorer.js +510 -0
  20. package/dist/components/StructureExplorer.js.map +1 -0
  21. package/dist/components/StructureView.js +286 -0
  22. package/dist/components/StructureView.js.map +1 -0
  23. package/dist/components/controls.js +170 -0
  24. package/dist/components/controls.js.map +1 -0
  25. package/dist/components/graphLayout.js +27 -0
  26. package/dist/components/graphLayout.js.map +1 -0
  27. package/dist/host.js +169 -0
  28. package/dist/host.js.map +1 -0
  29. package/dist/i18n.js +88 -0
  30. package/dist/i18n.js.map +1 -0
  31. package/dist/index.js +171 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/map/CabinMaterials.js +81 -0
  34. package/dist/map/CabinMaterials.js.map +1 -0
  35. package/dist/map/CabinMirror.js +43 -0
  36. package/dist/map/CabinMirror.js.map +1 -0
  37. package/dist/map/Doors.js +80 -0
  38. package/dist/map/Doors.js.map +1 -0
  39. package/dist/map/ElevatorCabin.js +93 -0
  40. package/dist/map/ElevatorCabin.js.map +1 -0
  41. package/dist/map/FixtureLights.js +117 -0
  42. package/dist/map/FixtureLights.js.map +1 -0
  43. package/dist/map/MapCanvas.js +2161 -0
  44. package/dist/map/MapCanvas.js.map +1 -0
  45. package/dist/map/Passenger.js +69 -0
  46. package/dist/map/Passenger.js.map +1 -0
  47. package/dist/map/SceneLayer.js +1534 -0
  48. package/dist/map/SceneLayer.js.map +1 -0
  49. package/dist/map/UndergroundContext.js +153 -0
  50. package/dist/map/UndergroundContext.js.map +1 -0
  51. package/dist/map/ambience.js +194 -0
  52. package/dist/map/ambience.js.map +1 -0
  53. package/dist/map/architecture.js +305 -0
  54. package/dist/map/architecture.js.map +1 -0
  55. package/dist/map/bathMusic.js +92 -0
  56. package/dist/map/bathMusic.js.map +1 -0
  57. package/dist/map/cameraAim.js +33 -0
  58. package/dist/map/cameraAim.js.map +1 -0
  59. package/dist/map/elevators.js +56 -0
  60. package/dist/map/elevators.js.map +1 -0
  61. package/dist/map/excavation.js +52 -0
  62. package/dist/map/excavation.js.map +1 -0
  63. package/dist/map/exteriors.js +25 -0
  64. package/dist/map/exteriors.js.map +1 -0
  65. package/dist/map/features.js +218 -0
  66. package/dist/map/features.js.map +1 -0
  67. package/dist/map/fences.js +29 -0
  68. package/dist/map/fences.js.map +1 -0
  69. package/dist/map/journey.js +112 -0
  70. package/dist/map/journey.js.map +1 -0
  71. package/dist/map/levels.js +6 -0
  72. package/dist/map/levels.js.map +1 -0
  73. package/dist/map/lighting.js +142 -0
  74. package/dist/map/lighting.js.map +1 -0
  75. package/dist/map/loadBasemap.js +26 -0
  76. package/dist/map/loadBasemap.js.map +1 -0
  77. package/dist/map/materials.js +183 -0
  78. package/dist/map/materials.js.map +1 -0
  79. package/dist/map/poolLighting.js +39 -0
  80. package/dist/map/poolLighting.js.map +1 -0
  81. package/dist/map/projection.js +24 -0
  82. package/dist/map/projection.js.map +1 -0
  83. package/dist/map/rampGeometry.js +38 -0
  84. package/dist/map/rampGeometry.js.map +1 -0
  85. package/dist/map/route.js +107 -0
  86. package/dist/map/route.js.map +1 -0
  87. package/dist/map/stairSurfaces.js +115 -0
  88. package/dist/map/stairSurfaces.js.map +1 -0
  89. package/dist/map/surfaces.js +136 -0
  90. package/dist/map/surfaces.js.map +1 -0
  91. package/dist/map/textures.js +95 -0
  92. package/dist/map/textures.js.map +1 -0
  93. package/dist/map/underground.js +34 -0
  94. package/dist/map/underground.js.map +1 -0
  95. package/dist/map/walk.js +433 -0
  96. package/dist/map/walk.js.map +1 -0
  97. package/dist/map/walkContext.js +46 -0
  98. package/dist/map/walkContext.js.map +1 -0
  99. package/dist/map/walkDimensions.js +7 -0
  100. package/dist/map/walkDimensions.js.map +1 -0
  101. package/dist/map/walkJourney.js +85 -0
  102. package/dist/map/walkJourney.js.map +1 -0
  103. package/dist/map/walkSurfaces.js +106 -0
  104. package/dist/map/walkSurfaces.js.map +1 -0
  105. package/dist/map/wallJoins.js +70 -0
  106. package/dist/map/wallJoins.js.map +1 -0
  107. package/dist/map/water.js +274 -0
  108. package/dist/map/water.js.map +1 -0
  109. package/dist/model/authoring.js +80 -0
  110. package/dist/model/authoring.js.map +1 -0
  111. package/dist/model/boundaries.js +548 -0
  112. package/dist/model/boundaries.js.map +1 -0
  113. package/dist/model/coverage.js +30 -0
  114. package/dist/model/coverage.js.map +1 -0
  115. package/dist/model/document.js +52 -0
  116. package/dist/model/document.js.map +1 -0
  117. package/dist/model/doors.js +47 -0
  118. package/dist/model/doors.js.map +1 -0
  119. package/dist/model/factory.js +56 -0
  120. package/dist/model/factory.js.map +1 -0
  121. package/dist/model/geometry.js +394 -0
  122. package/dist/model/geometry.js.map +1 -0
  123. package/dist/model/inference.js +182 -0
  124. package/dist/model/inference.js.map +1 -0
  125. package/dist/model/materials.js +11 -0
  126. package/dist/model/materials.js.map +1 -0
  127. package/dist/model/mutations.js +157 -0
  128. package/dist/model/mutations.js.map +1 -0
  129. package/dist/model/navigation.js +275 -0
  130. package/dist/model/navigation.js.map +1 -0
  131. package/dist/model/ontology.js +109 -0
  132. package/dist/model/ontology.js.map +1 -0
  133. package/dist/model/passages.js +5 -0
  134. package/dist/model/passages.js.map +1 -0
  135. package/dist/model/portals.js +42 -0
  136. package/dist/model/portals.js.map +1 -0
  137. package/dist/model/precision.js +11 -0
  138. package/dist/model/precision.js.map +1 -0
  139. package/dist/model/project.js +17 -0
  140. package/dist/model/project.js.map +1 -0
  141. package/dist/model/ramps.js +10 -0
  142. package/dist/model/ramps.js.map +1 -0
  143. package/dist/model/roomNavigation.js +87 -0
  144. package/dist/model/roomNavigation.js.map +1 -0
  145. package/dist/model/routeClearance.js +72 -0
  146. package/dist/model/routeClearance.js.map +1 -0
  147. package/dist/model/spaces.js +104 -0
  148. package/dist/model/spaces.js.map +1 -0
  149. package/dist/model/stairGeometry.js +40 -0
  150. package/dist/model/stairGeometry.js.map +1 -0
  151. package/dist/model/stairTraversal.js +48 -0
  152. package/dist/model/stairTraversal.js.map +1 -0
  153. package/dist/model/topology.js +63 -0
  154. package/dist/model/topology.js.map +1 -0
  155. package/dist/model/types.js +35 -0
  156. package/dist/model/types.js.map +1 -0
  157. package/dist/model/validate.js +240 -0
  158. package/dist/model/validate.js.map +1 -0
  159. package/dist/model/vertical.js +129 -0
  160. package/dist/model/vertical.js.map +1 -0
  161. package/dist/model/walls.js +71 -0
  162. package/dist/model/walls.js.map +1 -0
  163. package/dist/styles.css +4931 -0
  164. package/dist/theme/index.js +34 -0
  165. package/dist/theme/index.js.map +1 -0
  166. package/dist/types/adapters/basemap.d.ts +2 -0
  167. package/dist/types/adapters/persistence.d.ts +29 -0
  168. package/dist/types/adapters/status.d.ts +4 -0
  169. package/dist/types/components/ElevatorPanel.d.ts +18 -0
  170. package/dist/types/components/Icons.d.ts +8 -0
  171. package/dist/types/components/Inspector.d.ts +38 -0
  172. package/dist/types/components/NavigatePanel.d.ts +27 -0
  173. package/dist/types/components/NavigationGraph.d.ts +10 -0
  174. package/dist/types/components/StructureExplorer.d.ts +8 -0
  175. package/dist/types/components/StructureView.d.ts +22 -0
  176. package/dist/types/components/controls.d.ts +52 -0
  177. package/dist/types/components/graphLayout.d.ts +9 -0
  178. package/dist/types/i18n.d.ts +85 -0
  179. package/dist/types/import/analysisPreview.d.ts +27 -0
  180. package/dist/types/import/brief.d.ts +22 -0
  181. package/dist/types/import/checkpoint.d.ts +49 -0
  182. package/dist/types/import/instructions.d.ts +7 -0
  183. package/dist/types/import/usage.d.ts +10 -0
  184. package/dist/types/map/CabinMaterials.d.ts +18 -0
  185. package/dist/types/map/CabinMirror.d.ts +13 -0
  186. package/dist/types/map/Doors.d.ts +11 -0
  187. package/dist/types/map/ElevatorCabin.d.ts +20 -0
  188. package/dist/types/map/FixtureLights.d.ts +35 -0
  189. package/dist/types/map/MapCanvas.d.ts +87 -0
  190. package/dist/types/map/Passenger.d.ts +18 -0
  191. package/dist/types/map/SceneLayer.d.ts +306 -0
  192. package/dist/types/map/UndergroundContext.d.ts +18 -0
  193. package/dist/types/map/ambience.d.ts +29 -0
  194. package/dist/types/map/architecture.d.ts +8 -0
  195. package/dist/types/map/bathMusic.d.ts +14 -0
  196. package/dist/types/map/cameraAim.d.ts +20 -0
  197. package/dist/types/map/elevators.d.ts +16 -0
  198. package/dist/types/map/excavation.d.ts +9 -0
  199. package/dist/types/map/exteriors.d.ts +3 -0
  200. package/dist/types/map/features.d.ts +33 -0
  201. package/dist/types/map/fences.d.ts +4 -0
  202. package/dist/types/map/journey.d.ts +36 -0
  203. package/dist/types/map/levels.d.ts +4 -0
  204. package/dist/types/map/lighting.d.ts +103 -0
  205. package/dist/types/map/loadBasemap.d.ts +4 -0
  206. package/dist/types/map/materials.d.ts +38 -0
  207. package/dist/types/map/poolLighting.d.ts +11 -0
  208. package/dist/types/map/projection.d.ts +9 -0
  209. package/dist/types/map/rampGeometry.d.ts +8 -0
  210. package/dist/types/map/route.d.ts +59 -0
  211. package/dist/types/map/stairSurfaces.d.ts +29 -0
  212. package/dist/types/map/surfaces.d.ts +36 -0
  213. package/dist/types/map/textures.d.ts +8 -0
  214. package/dist/types/map/underground.d.ts +30 -0
  215. package/dist/types/map/walk.d.ts +232 -0
  216. package/dist/types/map/walkContext.d.ts +11 -0
  217. package/dist/types/map/walkDimensions.d.ts +5 -0
  218. package/dist/types/map/walkJourney.d.ts +33 -0
  219. package/dist/types/map/walkSurfaces.d.ts +27 -0
  220. package/dist/types/map/wallJoins.d.ts +6 -0
  221. package/dist/types/map/water.d.ts +12 -0
  222. package/dist/types/model/authoring.d.ts +38 -0
  223. package/dist/types/model/boundaries.d.ts +41 -0
  224. package/dist/types/model/coverage.d.ts +8 -0
  225. package/dist/types/model/document.d.ts +9 -0
  226. package/dist/types/model/doors.d.ts +19 -0
  227. package/dist/types/model/factory.d.ts +5 -0
  228. package/dist/types/model/geometry.d.ts +99 -0
  229. package/dist/types/model/host.d.ts +154 -0
  230. package/dist/types/model/inference.d.ts +46 -0
  231. package/dist/types/model/live.d.ts +60 -0
  232. package/dist/types/model/materials.d.ts +28 -0
  233. package/dist/types/model/mutations.d.ts +178 -0
  234. package/dist/types/model/navigation.d.ts +70 -0
  235. package/dist/types/model/ontology.d.ts +43 -0
  236. package/dist/types/model/passages.d.ts +2 -0
  237. package/dist/types/model/portals.d.ts +9 -0
  238. package/dist/types/model/precision.d.ts +11 -0
  239. package/dist/types/model/project.d.ts +8 -0
  240. package/dist/types/model/ramps.d.ts +7 -0
  241. package/dist/types/model/roomNavigation.d.ts +15 -0
  242. package/dist/types/model/routeClearance.d.ts +11 -0
  243. package/dist/types/model/spaces.d.ts +46 -0
  244. package/dist/types/model/stairGeometry.d.ts +27 -0
  245. package/dist/types/model/stairTraversal.d.ts +8 -0
  246. package/dist/types/model/topology.d.ts +14 -0
  247. package/dist/types/model/types.d.ts +369 -0
  248. package/dist/types/model/validate.d.ts +38 -0
  249. package/dist/types/model/vertical.d.ts +145 -0
  250. package/dist/types/model/walls.d.ts +71 -0
  251. package/dist/types/schema/index.d.ts +25 -0
  252. package/dist/types/theme/index.d.ts +55 -0
  253. package/dist/types/viewer/FloorViewer.d.ts +76 -0
  254. package/dist/types/viewer/ViewerWorkspace.d.ts +2 -0
  255. package/dist/types/viewer/host.d.ts +11 -0
  256. package/dist/types/viewer/index.d.ts +2 -0
  257. package/dist/types/viewer/panels.d.ts +12 -0
  258. package/dist/viewer/FloorViewer.js +104 -0
  259. package/dist/viewer/FloorViewer.js.map +1 -0
  260. package/dist/viewer/ViewerWorkspace.js +370 -0
  261. package/dist/viewer/ViewerWorkspace.js.map +1 -0
  262. package/dist/viewer/panels.js +35 -0
  263. package/dist/viewer/panels.js.map +1 -0
  264. package/package.json +58 -4
@@ -0,0 +1,107 @@
1
+ import { toLngLat as g } from "../model/geometry.js";
2
+ const v = "#16a34a", w = "#6fc98d", m = "#ffffff", I = (s, l) => {
3
+ var o;
4
+ return l === null ? 0 : ((o = s.floors.find((e) => e.id === l)) == null ? void 0 : o.elevation) ?? 0;
5
+ };
6
+ function C(s) {
7
+ const l = [];
8
+ let o = 0;
9
+ for (let e = 0; e < s.steps.length; e++) {
10
+ const r = s.steps[e];
11
+ if (!(r.kind === "depart" || r.kind === "arrive"))
12
+ for (let t = 1; t < r.nodeIds.length && o < s.legs.length; t++) l[o++] = e;
13
+ }
14
+ for (; o < s.legs.length; ) l[o++] = Math.max(0, s.steps.length - 2);
15
+ return l;
16
+ }
17
+ function F(s, l, o, e) {
18
+ const r = [], t = C(l), i = (c) => c === null || c === o;
19
+ let n = null;
20
+ const d = () => {
21
+ n && n.points.length > 1 && r.push({
22
+ type: "Feature",
23
+ properties: {
24
+ step: n.step,
25
+ // Nothing being played means nothing is "later": the whole path is current, and dimming all
26
+ // of it to the not-your-step tone leaves a route drawn in the colour of an afterthought.
27
+ active: e === null || n.step === e,
28
+ onFloor: i(n.floorId),
29
+ vertical: !1
30
+ },
31
+ geometry: { type: "LineString", coordinates: n.points.map((c) => g(c, s.origin)) }
32
+ }), n = null;
33
+ };
34
+ let f = null;
35
+ const p = () => {
36
+ f && f.points.length > 1 && i(f.floorId) && r.push({
37
+ type: "Feature",
38
+ properties: { flow: !0, onFloor: !0, vertical: !1 },
39
+ geometry: { type: "LineString", coordinates: f.points.map((c) => g(c, s.origin)) }
40
+ }), f = null;
41
+ };
42
+ for (let c = 0; c < l.legs.length; c++) {
43
+ const a = l.legs[c], u = t[c];
44
+ if (a.edge.kind === "stairs" || a.edge.kind === "elevator") {
45
+ d(), p();
46
+ const y = I(s, a.to.floorId) >= I(s, a.from.floorId) ? "up" : "down";
47
+ r.push({
48
+ type: "Feature",
49
+ properties: {
50
+ step: u,
51
+ active: e === null || u === e,
52
+ onFloor: i(a.from.floorId) || i(a.to.floorId),
53
+ vertical: !0,
54
+ dir: y
55
+ },
56
+ geometry: { type: "Point", coordinates: g(a.from.position, s.origin) }
57
+ });
58
+ continue;
59
+ }
60
+ const h = a.from.floorId === a.to.floorId ? a.from.floorId : a.from.floorId ?? a.to.floorId;
61
+ (!n || n.step !== u || n.floorId !== h) && (d(), n = { step: u, floorId: h, points: [a.from.position] }), n.points.push(a.to.position), (!f || f.floorId !== h) && (p(), f = { floorId: h, points: [a.from.position] }), f.points.push(a.to.position);
62
+ }
63
+ return d(), p(), { type: "FeatureCollection", features: r };
64
+ }
65
+ function O(s, l = m, o = 26) {
66
+ const e = ["interpolate", ["linear"], ["line-progress"]], [r, t, i] = [1, 3, 5].map((n) => Number.parseInt(l.slice(n, n + 2), 16));
67
+ for (let n = 0; n < o; n++) {
68
+ const d = n / (o - 1), f = Math.abs(d - s), p = Math.max(0, 1 - Math.min(f, 1 - f) / 0.17);
69
+ e.push(d, `rgba(${r},${t},${i},${(0.45 * p ** 2).toFixed(3)})`);
70
+ }
71
+ return e;
72
+ }
73
+ function k(s = v) {
74
+ const e = (typeof OffscreenCanvas < "u" ? new OffscreenCanvas(32, 32) : Object.assign(document.createElement("canvas"), { width: 32, height: 32 })).getContext("2d");
75
+ e.clearRect(0, 0, 32, 32), e.lineCap = "round", e.lineJoin = "round";
76
+ const r = (t, i) => {
77
+ e.strokeStyle = i, e.lineWidth = t, e.beginPath(), e.moveTo(10, 7), e.lineTo(22.5, 16), e.lineTo(10, 25), e.stroke();
78
+ };
79
+ return r(9.5, s), r(5, m), e.getImageData(0, 0, 32, 32);
80
+ }
81
+ const R = 2.6, T = [256, 128];
82
+ function S(s = v, l = !0) {
83
+ const [o, e] = T, t = (typeof OffscreenCanvas < "u" ? new OffscreenCanvas(o, e) : Object.assign(document.createElement("canvas"), { width: o, height: e })).getContext("2d");
84
+ t.fillStyle = s, t.fillRect(0, 0, o, e);
85
+ const i = Math.round(e * 0.13);
86
+ t.fillStyle = m, t.fillRect(0, 0, o, i), t.fillRect(0, e - i, o, i);
87
+ const n = t.createLinearGradient(0, 0, o, 0);
88
+ return n.addColorStop(0, "rgba(255,255,255,0)"), n.addColorStop(0.55, "rgba(255,255,255,0.34)"), n.addColorStop(1, "rgba(255,255,255,0)"), t.fillStyle = n, t.fillRect(0, i, o, e - 2 * i), t.strokeStyle = m, t.lineWidth = e * 0.16, t.lineCap = "round", t.lineJoin = "round", t.globalAlpha = l ? 0.96 : 0.7, t.beginPath(), t.moveTo(o * 0.4, e * 0.28), t.lineTo(o * 0.62, e * 0.5), t.lineTo(o * 0.4, e * 0.72), t.stroke(), t.getImageData(0, 0, o, e);
89
+ }
90
+ function z(s, l, o) {
91
+ const [e, r] = g(s, o), [t, i] = g(l, o);
92
+ return (Math.atan2((t - e) * Math.cos((r + i) / 2 * Math.PI / 180), i - r) * 180 / Math.PI + 360) % 360;
93
+ }
94
+ export {
95
+ v as ROUTE_COLOR,
96
+ m as ROUTE_EDGE,
97
+ w as ROUTE_SOFT,
98
+ T as TRACK_PIXELS,
99
+ R as TRACK_TILE,
100
+ C as legStepIndices,
101
+ z as pathHeading,
102
+ k as routeArrowImage,
103
+ F as routeFeatures,
104
+ O as routeFlowGradient,
105
+ S as routeTrackImage
106
+ };
107
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sources":["../../../../src/map/route.ts"],"sourcesContent":["// Thin display adapter between the routing model (src/model/navigation.ts) and the map: GeoJSON\n// for the 2D route layers, the chevron sprite for the line-placed arrow symbols, and geographic\n// headings for the journey camera. No second display model — everything reads the model Route.\nimport type { Feature, FeatureCollection } from 'geojson';\nimport type { Origin, Point, ProjectDocument } from '../model/types';\nimport type { Route } from '../model/navigation';\nimport { toLngLat } from '../model/geometry';\n\n/** The route's own colours. A wayfinding line is not a plan colour — it is a thing laid ON the plan\n * for someone to follow, so it wants to be the one saturated element in a scene of pale rooms, and\n * it wants a white edge so it reads over a dark floor as well as a light one. Green because green\n * is what \"this way\" looks like on every floor of every building anyone has walked through. */\nexport const ROUTE_COLOR = '#16a34a';\n/** Steps either side of the one being walked: the same green, not a different colour — the path is\n * one path, and only emphasis separates the part you are on from the rest of it. */\nexport const ROUTE_SOFT = '#6fc98d';\nexport const ROUTE_EDGE = '#ffffff';\n\nconst elev = (p: ProjectDocument, id: string | null) =>\n id === null ? 0 : (p.floors.find(f => f.id === id)?.elevation ?? 0);\n\n/** Step index owning each traversal leg, in leg order. Every walk/door/vertical step carries\n * nodeIds for the nodes it visits (one more than its legs); depart/arrive own no legs. */\nexport function legStepIndices(route: Route): number[] {\n const out: number[] = [];\n let leg = 0;\n for (let s = 0; s < route.steps.length; s++) {\n const step = route.steps[s];\n if (step.kind === 'depart' || step.kind === 'arrive') continue;\n for (let k = 1; k < step.nodeIds.length && leg < route.legs.length; k++) out[leg++] = s;\n }\n while (leg < route.legs.length) out[leg++] = Math.max(0, route.steps.length - 2);\n return out;\n}\n\n/** GeoJSON for the 2D route layers. Walk/door legs chain into per-step polylines (so dashes and\n * chevrons flow continuously); vertical legs become Point features at the shaft. Every feature\n * carries { step, active, onFloor, vertical, dir }. Outdoor legs count as on-floor everywhere,\n * mirroring visibleOnFloor — the outdoor tail of an egress route must stay readable. */\nexport function routeFeatures(\n project: ProjectDocument,\n route: Route,\n floorId: string | null,\n activeStep: number | null,\n): FeatureCollection {\n const features: Feature[] = [];\n const steps = legStepIndices(route);\n const onFloor = (fid: string | null) => fid === null || fid === floorId;\n let run: { step: number; floorId: string | null; points: Point[] } | null = null;\n const flush = () => {\n if (run && run.points.length > 1)\n features.push({\n type: 'Feature',\n properties: {\n step: run.step,\n // Nothing being played means nothing is \"later\": the whole path is current, and dimming all\n // of it to the not-your-step tone leaves a route drawn in the colour of an afterthought.\n active: activeStep === null || run.step === activeStep,\n onFloor: onFloor(run.floorId),\n vertical: false,\n },\n geometry: { type: 'LineString', coordinates: run.points.map(pt => toLngLat(pt, project.origin)) },\n });\n run = null;\n };\n // A second, coarser tracing of the same path: one feature per unbroken stretch on one floor,\n // chained straight through the instruction boundaries the per-step features stop at. The travelling\n // highlight is a line-gradient over `line-progress`, and line-progress runs 0→1 within a single\n // feature — split the path per step and the band restarts at every \"turn left\", which reads as a\n // stutter rather than as motion towards somewhere.\n let flow: { floorId: string | null; points: Point[] } | null = null;\n const flushFlow = () => {\n if (flow && flow.points.length > 1 && onFloor(flow.floorId))\n features.push({\n type: 'Feature',\n properties: { flow: true, onFloor: true, vertical: false },\n geometry: { type: 'LineString', coordinates: flow.points.map(pt => toLngLat(pt, project.origin)) },\n });\n flow = null;\n };\n for (let i = 0; i < route.legs.length; i++) {\n const leg = route.legs[i],\n step = steps[i];\n if (leg.edge.kind === 'stairs' || leg.edge.kind === 'elevator') {\n flush();\n flushFlow(); // a flight is a break in the walk, not a place for the band to slide through\n const dir = elev(project, leg.to.floorId) >= elev(project, leg.from.floorId) ? 'up' : 'down';\n features.push({\n type: 'Feature',\n properties: {\n step,\n active: activeStep === null || step === activeStep,\n onFloor: onFloor(leg.from.floorId) || onFloor(leg.to.floorId),\n vertical: true,\n dir,\n },\n geometry: { type: 'Point', coordinates: toLngLat(leg.from.position, project.origin) },\n });\n continue;\n }\n // A door edge stepping outside draws on its indoor floor; outdoor-only legs keep floorId null.\n const fid = leg.from.floorId === leg.to.floorId ? leg.from.floorId : (leg.from.floorId ?? leg.to.floorId);\n if (!run || run.step !== step || run.floorId !== fid) {\n flush();\n run = { step, floorId: fid, points: [leg.from.position] };\n }\n run.points.push(leg.to.position);\n if (!flow || flow.floorId !== fid) {\n flushFlow();\n flow = { floorId: fid, points: [leg.from.position] };\n }\n flow.points.push(leg.to.position);\n }\n flush();\n flushFlow();\n return { type: 'FeatureCollection', features };\n}\n\n/** The route as something to be walked: its contiguous same-floor stretches, in travel order, in\n * plan metres. A flight is a break rather than a stretch — you do not walk up a lift shaft — so each\n * run ends where the route leaves the floor, and the floor it ends on is the one to switch to.\n *\n * This is the same decomposition the flow features use, kept apart from GeoJSON because walking a\n * route wants plan coordinates and a map layer wants degrees. */\nexport function routeWalks(route: Route): { floorId: string | null; points: Point[] }[] {\n const runs: { floorId: string | null; points: Point[] }[] = [];\n let run: { floorId: string | null; points: Point[] } | null = null;\n for (const leg of route.legs) {\n if (leg.edge.kind === 'stairs' || leg.edge.kind === 'elevator') {\n run = null;\n continue;\n }\n const fid = leg.from.floorId === leg.to.floorId ? leg.from.floorId : (leg.from.floorId ?? leg.to.floorId);\n if (!run || run.floorId !== fid) {\n run = { floorId: fid, points: [leg.from.position] };\n runs.push(run);\n }\n run.points.push(leg.to.position);\n }\n return runs.filter(r => r.points.length > 1);\n}\n\n/** A band of light travelling along the path, as a line-gradient over `line-progress`.\n *\n * Built from stops at fixed, strictly ascending positions with the band expressed as an alpha\n * falling off either side of `phase` — rather than by moving the stops, which is the obvious way\n * and the wrong one: a moving window has to be clamped as it wraps past 0 and 1, and interpolate\n * refuses stops that are not strictly ascending. This way there is nothing to clamp. */\nexport function routeFlowGradient(phase: number, color = ROUTE_EDGE, stops = 26): unknown[] {\n const out: unknown[] = ['interpolate', ['linear'], ['line-progress']];\n const [r, g, b] = [1, 3, 5].map(i => Number.parseInt(color.slice(i, i + 2), 16));\n for (let i = 0; i < stops; i++) {\n const at = i / (stops - 1);\n const gap = Math.abs(at - phase);\n // Wrapped distance: the head of the path is the tail's neighbour, so the band leaves one end and\n // arrives at the other without a seam.\n const reach = Math.max(0, 1 - Math.min(gap, 1 - gap) / 0.17);\n out.push(at, `rgba(${r},${g},${b},${(0.45 * reach ** 2).toFixed(3)})`);\n }\n return out;\n}\n\n/** 32×32 chevron sprite for the symbol-placement:'line' arrow layer (points +x; MapLibre rotates it\n * along the travel direction). White on a halo of the path's own colour: the arrow sits ON the green\n * track, so it has to be the light thing, and the halo is what keeps it legible where the track\n * passes over a pale floor. Drawn on a canvas — no glyphs, no external assets. */\nexport function routeArrowImage(color = ROUTE_COLOR): ImageData {\n const size = 32;\n const canvas = (\n typeof OffscreenCanvas !== 'undefined'\n ? new OffscreenCanvas(size, size)\n : Object.assign(document.createElement('canvas'), { width: size, height: size })\n ) as HTMLCanvasElement;\n const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;\n ctx.clearRect(0, 0, size, size);\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n const chevron = (width: number, stroke: string) => {\n ctx.strokeStyle = stroke;\n ctx.lineWidth = width;\n ctx.beginPath();\n ctx.moveTo(10, 7);\n ctx.lineTo(22.5, 16);\n ctx.lineTo(10, 25);\n ctx.stroke();\n };\n chevron(9.5, color);\n chevron(5, ROUTE_EDGE);\n return ctx.getImageData(0, 0, size, size);\n}\n\n/** The track's repeating tile, for the 3D ribbon: a green band with white edges and a white chevron\n * pointing along it, drawn once and tiled every `TRACK_TILE` metres of path.\n *\n * One texture answers all of it. The ribbon is a constant width, so the tile's top and bottom edges\n * ARE the track's edges — mitres and corners included, which is the part a separate outline geometry\n * gets wrong. The chevron rides in the same tile, so the arrows are spaced in metres of walking\n * rather than in pixels of screen. And scrolling the tile along the path is the animation: the\n * arrows march towards the destination instead of sitting still and merely pointing at it. */\nexport const TRACK_TILE = 2.6;\n/** The tile's own size in pixels, described in one place. Powers of two so it can carry mipmaps:\n * at eye level most of the track is seen at a grazing angle, and that is exactly where an\n * unmipmapped texture turns its chevrons into glitter. */\nexport const TRACK_PIXELS: [number, number] = [256, 128];\nexport function routeTrackImage(color = ROUTE_COLOR, active = true): ImageData {\n // Big enough to stay crisp with your nose a metre from it in walk mode, which is where a 128-wide\n // tile went soft — at that range one texel of the tile is a centimetre of floor.\n const [w, h] = TRACK_PIXELS;\n const canvas = (\n typeof OffscreenCanvas !== 'undefined'\n ? new OffscreenCanvas(w, h)\n : Object.assign(document.createElement('canvas'), { width: w, height: h })\n ) as HTMLCanvasElement;\n const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, w, h);\n // The white kerb, top and bottom. Thin enough to read as an edge rather than as a second stripe.\n const kerb = Math.round(h * 0.13);\n ctx.fillStyle = ROUTE_EDGE;\n ctx.fillRect(0, 0, w, kerb);\n ctx.fillRect(0, h - kerb, w, kerb);\n // A soft brightening across the tile, so the scroll reads as light travelling along the track and\n // not only as arrows sliding. Drawn before the chevron, which stays flat white.\n const sweep = ctx.createLinearGradient(0, 0, w, 0);\n sweep.addColorStop(0, 'rgba(255,255,255,0)');\n sweep.addColorStop(0.55, 'rgba(255,255,255,0.34)');\n sweep.addColorStop(1, 'rgba(255,255,255,0)');\n ctx.fillStyle = sweep;\n ctx.fillRect(0, kerb, w, h - 2 * kerb);\n ctx.strokeStyle = ROUTE_EDGE;\n ctx.lineWidth = h * 0.16;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.globalAlpha = active ? 0.96 : 0.7;\n ctx.beginPath();\n ctx.moveTo(w * 0.4, h * 0.28);\n ctx.lineTo(w * 0.62, h * 0.5);\n ctx.lineTo(w * 0.4, h * 0.72);\n ctx.stroke();\n return ctx.getImageData(0, 0, w, h);\n}\n\n/** Heading in degrees from true north between two local points, computed in geographic space so a\n * rotated Origin yields the bearing the map camera actually needs. */\nexport function pathHeading(a: Point, b: Point, origin: Origin): number {\n const [lng1, lat1] = toLngLat(a, origin),\n [lng2, lat2] = toLngLat(b, origin);\n const heading =\n (Math.atan2((lng2 - lng1) * Math.cos((((lat1 + lat2) / 2) * Math.PI) / 180), lat2 - lat1) * 180) / Math.PI;\n return (heading + 360) % 360;\n}\n"],"names":["ROUTE_COLOR","ROUTE_SOFT","ROUTE_EDGE","elev","p","id","_a","f","legStepIndices","route","out","leg","s","step","k","routeFeatures","project","floorId","activeStep","features","steps","onFloor","fid","run","flush","pt","toLngLat","flow","flushFlow","i","dir","routeFlowGradient","phase","color","stops","g","b","at","gap","reach","routeArrowImage","ctx","chevron","width","stroke","TRACK_TILE","TRACK_PIXELS","routeTrackImage","active","w","h","kerb","sweep","pathHeading","a","origin","lng1","lat1","lng2","lat2"],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ var x = Object.defineProperty;
2
+ var G = (f, e, i) => e in f ? x(f, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : f[e] = i;
3
+ var v = (f, e, i) => G(f, typeof e != "symbol" ? e + "" : e, i);
4
+ import { BODY as T } from "./walkDimensions.js";
5
+ import { slopeElevation as P, rectangle as M, rotate as R, segmentProjection as S, add as $, pointInRing as u, distance as B } from "../model/geometry.js";
6
+ import { rampFloors as D } from "../model/ramps.js";
7
+ import { primaryShafts as E, stairModel as O, flights as W, flightRun as Y } from "../model/vertical.js";
8
+ import { spiralGeometry as q, stairGeometry as z } from "../model/stairGeometry.js";
9
+ import { supportedStep as A } from "./walkSurfaces.js";
10
+ function U(f, e) {
11
+ var c;
12
+ const i = E(f), g = [];
13
+ for (const t of f.objects) {
14
+ if (t.slope && ((c = t.rings) != null && c.length)) {
15
+ const { low: n, high: l } = D(f, t);
16
+ n && l && g.push({
17
+ flightId: t.id,
18
+ fromFloor: n.id,
19
+ toFloor: l.id,
20
+ low: t.slope.low,
21
+ high: t.slope.high,
22
+ ring: t.rings[0],
23
+ holes: t.rings.slice(1),
24
+ height: (h) => P(t.slope, h)
25
+ });
26
+ }
27
+ if (t.kind !== "stairs" || !i.has(t.id)) continue;
28
+ const d = O(f, t);
29
+ for (const [n, l] of W(f, t).entries()) {
30
+ const h = Y(f, t, l.rise, d, n), a = {
31
+ flightId: `${t.id}:${n}`,
32
+ fromFloor: l.from.id,
33
+ toFloor: l.to.id,
34
+ low: l.from.elevation,
35
+ high: l.to.elevation
36
+ }, o = (r, s) => g.push({ ...a, ring: r, height: () => a.low + s }), p = (r, s, m, w, F, b) => {
37
+ const I = B(r, s), y = Math.atan2(s[1] - r[1], s[0] - r[0]) * 180 / Math.PI - 90;
38
+ g.push({
39
+ ...a,
40
+ ring: M([(s[0] + r[0]) / 2, (s[1] + r[1]) / 2], m, I, y),
41
+ height: (k) => a.low + w + S(k, r, s).t * F,
42
+ drive: b
43
+ });
44
+ };
45
+ if (d === "spiral")
46
+ for (const r of q(t, t.position, l.rise).wedges) o(r.ring, r.height);
47
+ else if (d === "escalator") {
48
+ const r = h.at(h.footT), s = h.at(h.topT), m = e == null ? void 0 : e.get(t.feedId ?? ""), w = ((m == null ? void 0 : m.travel) ?? t.travel ?? "up") === "up" ? 1 : -1, F = (m == null ? void 0 : m.running) ?? !0 ? 0.5 / Math.hypot(h.incline, l.rise) : 0;
49
+ p(r, s, t.width - 0.24, 0, l.rise, [
50
+ (s[0] - r[0]) * F * w,
51
+ (s[1] - r[1]) * F * w
52
+ ]), o(M(h.at((h.footT + h.half) / 2), t.width - 0.24, h.half - h.footT, h.rotation), 0), o(M(h.at(h.topT - h.pad / 2), t.width - 0.24, h.pad, h.rotation), l.rise);
53
+ for (const b of [-1, 1]) {
54
+ const I = R([b * (t.width / 2 - 0.06), 0], h.rotation);
55
+ g.push({
56
+ ...a,
57
+ rail: !0,
58
+ ring: M(
59
+ $(h.at((h.footT + h.topT) / 2), I),
60
+ 0.1 + T * 2,
61
+ h.incline + T * 2,
62
+ h.rotation
63
+ ),
64
+ height: (y) => a.low + S(y, r, s).t * l.rise
65
+ });
66
+ }
67
+ } else {
68
+ const r = z(t, h, l.rise, d);
69
+ for (const s of r.lanes) p(s.foot, s.head, s.width, s.base, s.rise);
70
+ for (const s of r.landings) o(s.ring, s.height);
71
+ }
72
+ }
73
+ }
74
+ return g;
75
+ }
76
+ class V {
77
+ constructor() {
78
+ v(this, "height", 0);
79
+ v(this, "active");
80
+ v(this, "pending");
81
+ v(this, "floor", null);
82
+ }
83
+ sync(e, i) {
84
+ e !== this.floor ? (e !== this.pending && this.reset(i), this.floor = e, this.pending = void 0) : this.active || (this.height = i);
85
+ }
86
+ refresh(e, i) {
87
+ this.active && (this.active = e.filter((g) => !g.rail && g.flightId === this.active.flightId && u(i, g.ring)).filter((g) => {
88
+ var c;
89
+ return !((c = g.holes) != null && c.some((t) => u(i, t)));
90
+ }).sort((g, c) => Math.abs(g.height(i) - this.height) - Math.abs(c.height(i) - this.height))[0]);
91
+ }
92
+ reset(e) {
93
+ this.active = void 0, this.pending = void 0, this.height = e;
94
+ }
95
+ step(e, i, g, c, t, d) {
96
+ const n = this.height;
97
+ if (g.some(
98
+ (o) => o.rail && u(i, o.ring) && o.height(i) < n + 1.7 && o.height(i) + 1 > n + 0.15
99
+ ))
100
+ return e;
101
+ const l = g.filter((o) => {
102
+ var p;
103
+ return !o.rail && u(i, o.ring) && !((p = o.holes) != null && p.some((r) => u(i, r)));
104
+ }), h = l.filter((o) => Math.abs(o.height(i) - n) <= 0.3).sort((o, p) => Math.abs(o.height(i) - n) - Math.abs(p.height(i) - n)), a = h.find((o) => {
105
+ var p;
106
+ return o.flightId === ((p = this.active) == null ? void 0 : p.flightId);
107
+ }) ?? h[0];
108
+ return a ? (this.active = a, this.height = a.height(i), this.height < n - 1e-6 && c !== a.fromFloor ? this.pending = a.fromFloor : this.height >= a.high - 5e-3 && c !== a.toFloor && this.height >= n && (this.pending = a.toFloor), i) : this.active && this.active.high - n <= 0.3 && c !== this.active.toFloor ? d && !d(i, this.active.toFloor) ? e : (this.height = this.active.high, this.pending = this.active.toFloor, i) : this.active && Math.abs(n - t) > 0.3 || l.some((o) => o.height(i) > n + 0.3 && o.height(i) < n + 1.9) ? e : (this.reset(t), A(e, i, (o) => (d == null ? void 0 : d(o, c)) ?? !0));
109
+ }
110
+ }
111
+ export {
112
+ V as StairWalker,
113
+ U as stairSurfaces
114
+ };
115
+ //# sourceMappingURL=stairSurfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stairSurfaces.js","sources":["../../../../src/map/stairSurfaces.ts"],"sourcesContent":["import { BODY } from './walkDimensions';\nimport type { Point, ProjectDocument, Ring } from '../model/types';\nimport type { StatusReading } from '../model/live';\nimport { add, distance, pointInRing, rectangle, rotate, segmentProjection, slopeElevation } from '../model/geometry';\nimport { rampFloors } from '../model/ramps';\nimport { flights, flightRun, primaryShafts, stairModel } from '../model/vertical';\nimport { spiralGeometry, stairGeometry } from '../model/stairGeometry';\nimport { supportedStep } from './walkSurfaces';\n\nexport interface StairSurface {\n flightId: string;\n fromFloor: string;\n toFloor: string;\n low: number;\n high: number;\n ring: Ring;\n holes?: Ring[];\n height(at: Point): number;\n drive?: Point;\n rail?: true;\n}\n\n/** Build once per document/status/floor change, not once per walking frame. Heights are absolute\n * floor elevations so switching the scene's floor datum cannot change the passenger's support. */\nexport function stairSurfaces(project: ProjectDocument, statuses?: Map<string, StatusReading>): StairSurface[] {\n const primary = primaryShafts(project),\n surfaces: StairSurface[] = [];\n for (const o of project.objects) {\n if (o.slope && o.rings?.length) {\n const { low, high } = rampFloors(project, o);\n if (low && high)\n surfaces.push({\n flightId: o.id,\n fromFloor: low.id,\n toFloor: high.id,\n low: o.slope.low,\n high: o.slope.high,\n ring: o.rings[0],\n holes: o.rings.slice(1),\n height: at => slopeElevation(o.slope!, at),\n });\n }\n if (o.kind !== 'stairs' || !primary.has(o.id)) continue;\n const model = stairModel(project, o);\n for (const [index, flight] of flights(project, o).entries()) {\n const run = flightRun(project, o, flight.rise, model, index);\n const common = {\n flightId: `${o.id}:${index}`,\n fromFloor: flight.from.id,\n toFloor: flight.to.id,\n low: flight.from.elevation,\n high: flight.to.elevation,\n };\n const flat = (ring: Ring, height: number) =>\n surfaces.push({ ...common, ring, height: () => common.low + height });\n const slope = (foot: Point, head: Point, width: number, base: number, rise: number, drive?: Point) => {\n const length = distance(foot, head);\n const rotation = (Math.atan2(head[1] - foot[1], head[0] - foot[0]) * 180) / Math.PI - 90;\n surfaces.push({\n ...common,\n ring: rectangle([(head[0] + foot[0]) / 2, (head[1] + foot[1]) / 2], width, length, rotation),\n height: at => common.low + base + segmentProjection(at, foot, head).t * rise,\n drive,\n });\n };\n if (model === 'spiral') {\n for (const step of spiralGeometry(o, o.position, flight.rise).wedges) flat(step.ring, step.height);\n } else if (model === 'escalator') {\n const foot = run.at(run.footT),\n head = run.at(run.topT);\n const reading = statuses?.get(o.feedId ?? '');\n const sign = (reading?.travel ?? o.travel ?? 'up') === 'up' ? 1 : -1;\n const speed = (reading?.running ?? true) ? 0.5 / Math.hypot(run.incline, flight.rise) : 0;\n slope(foot, head, o.width - 0.24, 0, flight.rise, [\n (head[0] - foot[0]) * speed * sign,\n (head[1] - foot[1]) * speed * sign,\n ]);\n flat(rectangle(run.at((run.footT + run.half) / 2), o.width - 0.24, run.half - run.footT, run.rotation), 0);\n flat(rectangle(run.at(run.topT - run.pad / 2), o.width - 0.24, run.pad, run.rotation), flight.rise);\n for (const sign of [-1, 1]) {\n const offset = rotate([sign * (o.width / 2 - 0.06), 0], run.rotation);\n surfaces.push({\n ...common,\n rail: true,\n ring: rectangle(\n add(run.at((run.footT + run.topT) / 2), offset),\n 0.1 + BODY * 2,\n run.incline + BODY * 2,\n run.rotation,\n ),\n height: at => common.low + segmentProjection(at, foot, head).t * flight.rise,\n });\n }\n } else {\n const geometry = stairGeometry(o, run, flight.rise, model);\n for (const lane of geometry.lanes) slope(lane.foot, lane.head, lane.width, lane.base, lane.rise);\n for (const landing of geometry.landings) flat(landing.ring, landing.height);\n }\n }\n }\n return surfaces;\n}\n\n/** Continuous walking support on the shared flights. The ramp envelope smooths the small tread\n * risers; high side entries are solid, and leaving a flight sideways cannot step into mid-air. */\nexport class StairWalker {\n height = 0;\n active?: StairSurface;\n pending?: string;\n private floor: string | null = null;\n sync(floor: string | null, elevation: number) {\n if (floor !== this.floor) {\n if (floor !== this.pending) this.reset(elevation);\n this.floor = floor;\n this.pending = undefined;\n } else if (!this.active) this.height = elevation;\n }\n refresh(surfaces: StairSurface[], at: Point) {\n if (!this.active) return;\n this.active = surfaces\n .filter(s => !s.rail && s.flightId === this.active!.flightId && pointInRing(at, s.ring))\n .filter(s => !s.holes?.some(h => pointInRing(at, h)))\n .sort((a, b) => Math.abs(a.height(at) - this.height) - Math.abs(b.height(at) - this.height))[0];\n }\n reset(elevation: number) {\n this.active = undefined;\n this.pending = undefined;\n this.height = elevation;\n }\n step(\n from: Point,\n to: Point,\n surfaces: StairSurface[],\n floor: string | null,\n elevation: number,\n supports?: (at: Point, floorId: string | null) => boolean,\n ): Point {\n const previous = this.height;\n if (\n surfaces.some(\n s => s.rail && pointInRing(to, s.ring) && s.height(to) < previous + 1.7 && s.height(to) + 1 > previous + 0.15,\n )\n )\n return from;\n const hits = surfaces.filter(s => !s.rail && pointInRing(to, s.ring) && !s.holes?.some(h => pointInRing(to, h)));\n const reachable = hits\n .filter(s => Math.abs(s.height(to) - previous) <= 0.3)\n .sort((a, b) => Math.abs(a.height(to) - previous) - Math.abs(b.height(to) - previous));\n const support = reachable.find(s => s.flightId === this.active?.flightId) ?? reachable[0];\n if (!support) {\n // The final riser may be crossed between frames (turning stairs have no top pad).\n if (this.active && this.active.high - previous <= 0.3 && floor !== this.active.toFloor) {\n if (supports && !supports(to, this.active.toFloor)) return from;\n this.height = this.active.high;\n this.pending = this.active.toFloor;\n return to;\n }\n // Side of a raised tread/truss, or a side exit from mid-flight: stay against its edge.\n if (\n (this.active && Math.abs(previous - elevation) > 0.3) ||\n hits.some(s => s.height(to) > previous + 0.3 && s.height(to) < previous + 1.9)\n )\n return from;\n this.reset(elevation);\n return supportedStep(from, to, at => supports?.(at, floor) ?? true);\n }\n this.active = support;\n this.height = support.height(to);\n if (this.height < previous - 1e-6 && floor !== support.fromFloor) this.pending = support.fromFloor;\n else if (this.height >= support.high - 0.005 && floor !== support.toFloor && this.height >= previous)\n this.pending = support.toFloor;\n return to;\n }\n}\n"],"names":["stairSurfaces","project","statuses","primary","primaryShafts","surfaces","o","_a","low","high","rampFloors","at","slopeElevation","model","stairModel","index","flight","flights","run","flightRun","common","flat","ring","height","slope","foot","head","width","base","rise","drive","length","distance","rotation","rectangle","segmentProjection","step","spiralGeometry","reading","sign","speed","offset","rotate","add","BODY","geometry","stairGeometry","lane","landing","StairWalker","__publicField","floor","elevation","s","pointInRing","h","a","b","from","to","supports","previous","hits","reachable","support","supportedStep"],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ var m = Object.defineProperty;
2
+ var b = (r, n, e) => n in r ? m(r, n, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[n] = e;
3
+ var d = (r, n, e) => b(r, typeof n != "symbol" ? n + "" : n, e);
4
+ import * as p from "three";
5
+ import { mergeGeometries as x } from "three/addons/utils/BufferGeometryUtils.js";
6
+ const y = 1;
7
+ class A {
8
+ constructor() {
9
+ d(this, "pending", /* @__PURE__ */ new Map());
10
+ d(this, "meshes", []);
11
+ /** Side of the square cells the batch is cut into, in scene metres; 0 batches a whole material
12
+ * into one mesh. Looking down on a floor from above, every part of it is in the frame and one
13
+ * mesh per material is the cheapest way to draw it. Standing on it, most of it is behind you or
14
+ * round a corner, and a lamp's shadow reaches twelve metres — but a mesh is culled whole or not
15
+ * at all, and one floor-sized mesh is never culled. Cut into cells, the frustum and every
16
+ * shadow pass keep only the cells they can see. */
17
+ d(this, "chunk", 0);
18
+ }
19
+ /** `outdoor` puts the surface on the OUTSIDE layer, out of reach of the interior lighting. It is
20
+ * part of the bucket key as well as a property of the mesh: two surfaces that share a material
21
+ * but not a side of the wall cannot be merged into one mesh, because a mesh sits on one layer. */
22
+ add(n, e, o, s = !1) {
23
+ let t = "";
24
+ if (this.chunk > 0) {
25
+ n.computeBoundingBox();
26
+ const u = n.boundingBox;
27
+ t = `|${Math.floor((u.min.x + u.max.x) / 2 / this.chunk)},${Math.floor((u.min.y + u.max.y) / 2 / this.chunk)}`;
28
+ }
29
+ const i = `${e.uuid}|${s ? "out" : "in"}${t}`, a = this.pending.get(i) ?? { material: e, outdoor: s, entries: [] };
30
+ a.entries.push({ geometry: n, id: o }), this.pending.set(i, a);
31
+ }
32
+ /** Fold a built object tree (a furniture group, a roof) into the batch, baking each mesh's world
33
+ * transform into its geometry. makeFixture emits one mesh per box, so a demo with a few hundred
34
+ * cars and columns was contributing thousands of draw calls and as many buffer uploads; merged by
35
+ * material they cost a handful. Instanced children are left alone — they are already batched. */
36
+ addObject(n, e, o = !1) {
37
+ n.updateMatrixWorld(!0);
38
+ const s = [];
39
+ return n.traverse((t) => {
40
+ if (t instanceof p.Light) {
41
+ const u = t.clone();
42
+ t.parent && u.applyMatrix4(t.parent.matrixWorld), s.push(u);
43
+ return;
44
+ }
45
+ if (!(t instanceof p.Mesh)) return;
46
+ if (t.isInstancedMesh) {
47
+ s.push(t);
48
+ return;
49
+ }
50
+ const i = Array.isArray(t.material) ? t.material[0] : t.material;
51
+ if (!i) return;
52
+ const a = t.geometry.clone();
53
+ a.applyMatrix4(t.matrixWorld), this.add(a, i, e, o);
54
+ }), s;
55
+ }
56
+ finish(n) {
57
+ for (const { material: e, outdoor: o, entries: s } of this.pending.values()) {
58
+ const t = s.map(({ geometry: l }) => l.index ? l.toNonIndexed() : l), i = [];
59
+ let a = 0;
60
+ s.forEach(({ id: l }, h) => {
61
+ const f = t[h].getAttribute("position").count;
62
+ i.push({ id: l, start: a, count: f }), a += f;
63
+ });
64
+ const u = x(t), c = new p.Mesh(u, e);
65
+ o && c.layers.set(y), c.userData.spans = i, c.castShadow = c.receiveShadow = !e.transparent, e.transparent && !e.depthWrite && (c.raycast = () => {
66
+ }), n.add(c), this.meshes.push(c), t.forEach((l) => l.dispose()), s.forEach(({ geometry: l }) => {
67
+ l.index && l.dispose();
68
+ });
69
+ }
70
+ this.pending.clear();
71
+ }
72
+ outline(n, e) {
73
+ const o = [];
74
+ for (const i of this.meshes) {
75
+ const a = i.geometry.getAttribute("position");
76
+ for (const u of i.userData.spans)
77
+ if (u.id === n)
78
+ for (let c = u.start; c < u.start + u.count; c++)
79
+ o.push(a.getX(c), a.getY(c), a.getZ(c));
80
+ }
81
+ if (!o.length) return null;
82
+ const s = new p.BufferGeometry().setAttribute("position", new p.Float32BufferAttribute(o, 3)), t = new p.LineSegments(
83
+ new p.EdgesGeometry(s, 35),
84
+ new p.LineBasicMaterial({ color: e, depthTest: !0, transparent: !0, opacity: 0.95 })
85
+ );
86
+ return s.dispose(), t;
87
+ }
88
+ clear() {
89
+ this.pending.clear(), this.meshes = [];
90
+ }
91
+ }
92
+ function v(r) {
93
+ if (r.instanceId !== void 0 && r.object.userData.entityIds)
94
+ return r.object.userData.entityIds[r.instanceId] ?? null;
95
+ if (r.object.userData.entityId) return r.object.userData.entityId;
96
+ const n = (r.faceIndex ?? -1) * 3, e = r.object.userData.spans;
97
+ if (e) {
98
+ let o = 0, s = e.length - 1;
99
+ for (; o <= s; ) {
100
+ const t = o + s >>> 1, i = e[t];
101
+ if (n < i.start) s = t - 1;
102
+ else if (n >= i.start + i.count) o = t + 1;
103
+ else return i.id;
104
+ }
105
+ }
106
+ return null;
107
+ }
108
+ function B(r, n = [1, 0]) {
109
+ const e = r.getAttribute("position"), o = r.getAttribute("normal"), s = new Float32Array(e.count * 2);
110
+ for (let t = 0; t < e.count; t++)
111
+ if (Math.abs(o.getZ(t)) > 0.5)
112
+ s[t * 2] = e.getX(t) * n[0] + e.getY(t) * n[1], s[t * 2 + 1] = -e.getX(t) * n[1] + e.getY(t) * n[0];
113
+ else {
114
+ let i = -o.getY(t), a = o.getX(t);
115
+ (i < -1e-3 || Math.abs(i) < 1e-3 && a < 0) && (i = -i, a = -a), s[t * 2] = e.getX(t) * i + e.getY(t) * a, s[t * 2 + 1] = e.getZ(t);
116
+ }
117
+ r.setAttribute("uv", new p.BufferAttribute(s, 2));
118
+ }
119
+ function D(r, n) {
120
+ const [e, o] = n, s = o[0] - e[0], t = o[1] - e[1], i = Math.hypot(s, t);
121
+ if (!i) return null;
122
+ const a = r.getAttribute("position"), u = r.getAttribute("normal"), c = [], l = [];
123
+ for (let f = 0; f < a.count; f += 3)
124
+ (Math.abs(u.getZ(f)) < 0.01 && [f, f + 1, f + 2].every((g) => Math.abs((a.getX(g) - e[0]) * t - (a.getY(g) - e[1]) * s) / i < 1e-4) ? c : l).push(f, f + 1, f + 2);
125
+ if (!c.length) return null;
126
+ const h = r.clone().setIndex(c);
127
+ return r.setIndex(l), h;
128
+ }
129
+ export {
130
+ y as OUTSIDE,
131
+ A as SurfaceBatch,
132
+ v as hitEntity,
133
+ B as metricUVs,
134
+ D as splitWallFace
135
+ };
136
+ //# sourceMappingURL=surfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surfaces.js","sources":["../../../../src/map/surfaces.ts"],"sourcesContent":["import * as THREE from 'three';\nimport { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';\n\n/** The scene renders on two layers. Everything is on OUTSIDE-or-INSIDE: the sun and the sky light\n * both, but the building's own ceiling lighting is on INSIDE alone, so turning the lamps up to read\n * a shop floor does not also floodlight the pavement at midnight. */\nexport const INSIDE = 0,\n OUTSIDE = 1;\n\ninterface Span {\n id: string;\n start: number;\n count: number;\n}\ninterface Entry {\n geometry: THREE.BufferGeometry;\n id: string;\n}\n\n/** Batch static surfaces by material, retaining triangle ownership for picking/highlights. */\nexport class SurfaceBatch {\n private pending = new Map<string, { material: THREE.Material; outdoor: boolean; entries: Entry[] }>();\n private meshes: THREE.Mesh[] = [];\n /** Side of the square cells the batch is cut into, in scene metres; 0 batches a whole material\n * into one mesh. Looking down on a floor from above, every part of it is in the frame and one\n * mesh per material is the cheapest way to draw it. Standing on it, most of it is behind you or\n * round a corner, and a lamp's shadow reaches twelve metres — but a mesh is culled whole or not\n * at all, and one floor-sized mesh is never culled. Cut into cells, the frustum and every\n * shadow pass keep only the cells they can see. */\n chunk = 0;\n\n /** `outdoor` puts the surface on the OUTSIDE layer, out of reach of the interior lighting. It is\n * part of the bucket key as well as a property of the mesh: two surfaces that share a material\n * but not a side of the wall cannot be merged into one mesh, because a mesh sits on one layer. */\n add(geometry: THREE.BufferGeometry, material: THREE.Material, id: string, outdoor = false) {\n let cell = '';\n if (this.chunk > 0) {\n geometry.computeBoundingBox();\n const box = geometry.boundingBox!;\n cell = `|${Math.floor((box.min.x + box.max.x) / 2 / this.chunk)},${Math.floor((box.min.y + box.max.y) / 2 / this.chunk)}`;\n }\n const key = `${material.uuid}|${outdoor ? 'out' : 'in'}${cell}`;\n const bucket = this.pending.get(key) ?? { material, outdoor, entries: [] };\n bucket.entries.push({ geometry, id });\n this.pending.set(key, bucket);\n }\n\n /** Fold a built object tree (a furniture group, a roof) into the batch, baking each mesh's world\n * transform into its geometry. makeFixture emits one mesh per box, so a demo with a few hundred\n * cars and columns was contributing thousands of draw calls and as many buffer uploads; merged by\n * material they cost a handful. Instanced children are left alone — they are already batched. */\n addObject(root: THREE.Object3D, id: string, outdoor = false) {\n root.updateMatrixWorld(true);\n const leftovers: THREE.Object3D[] = [];\n root.traverse(o => {\n if (o instanceof THREE.Light) {\n const light = o.clone();\n if (o.parent) light.applyMatrix4(o.parent.matrixWorld);\n leftovers.push(light);\n return;\n }\n if (!(o instanceof THREE.Mesh)) return;\n if ((o as THREE.InstancedMesh).isInstancedMesh) {\n leftovers.push(o);\n return;\n }\n const material = Array.isArray(o.material) ? o.material[0] : o.material;\n if (!material) return;\n const geometry = o.geometry.clone();\n geometry.applyMatrix4(o.matrixWorld);\n this.add(geometry, material, id, outdoor);\n });\n return leftovers;\n }\n\n finish(scene: THREE.Scene) {\n for (const { material, outdoor, entries } of this.pending.values()) {\n const geometries = entries.map(({ geometry }) => (geometry.index ? geometry.toNonIndexed() : geometry));\n const spans: Span[] = [];\n let start = 0;\n entries.forEach(({ id }, i) => {\n const count = geometries[i].getAttribute('position').count;\n spans.push({ id, start, count });\n start += count;\n });\n const geometry = mergeGeometries(geometries)!;\n const mesh = new THREE.Mesh(geometry, material);\n if (outdoor) mesh.layers.set(OUTSIDE);\n mesh.userData.spans = spans;\n mesh.castShadow = mesh.receiveShadow = !material.transparent;\n if (material.transparent && !material.depthWrite) mesh.raycast = () => {};\n scene.add(mesh);\n this.meshes.push(mesh);\n geometries.forEach(g => g.dispose());\n entries.forEach(({ geometry }) => {\n if (geometry.index) geometry.dispose();\n });\n }\n this.pending.clear();\n }\n\n outline(id: string, color: string): THREE.LineSegments | null {\n const points: number[] = [];\n for (const mesh of this.meshes) {\n const position = mesh.geometry.getAttribute('position');\n for (const span of mesh.userData.spans as Span[])\n if (span.id === id) {\n for (let i = span.start; i < span.start + span.count; i++)\n points.push(position.getX(i), position.getY(i), position.getZ(i));\n }\n }\n if (!points.length) return null;\n const geometry = new THREE.BufferGeometry().setAttribute('position', new THREE.Float32BufferAttribute(points, 3));\n const outline = new THREE.LineSegments(\n new THREE.EdgesGeometry(geometry, 35),\n new THREE.LineBasicMaterial({ color, depthTest: true, transparent: true, opacity: 0.95 }),\n );\n geometry.dispose();\n return outline;\n }\n\n clear() {\n this.pending.clear();\n this.meshes = [];\n }\n}\n\nexport function hitEntity(hit: THREE.Intersection): string | null {\n if (hit.instanceId !== undefined && hit.object.userData.entityIds)\n return hit.object.userData.entityIds[hit.instanceId] ?? null;\n if (hit.object.userData.entityId) return hit.object.userData.entityId;\n const vertex = (hit.faceIndex ?? -1) * 3;\n const spans = hit.object.userData.spans as Span[] | undefined;\n // Ranges are ordered; a binary search avoids scanning every wall on every mouse move.\n if (spans) {\n let lo = 0,\n hi = spans.length - 1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1,\n span = spans[mid];\n if (vertex < span.start) hi = mid - 1;\n else if (vertex >= span.start + span.count) lo = mid + 1;\n else return span.id;\n }\n }\n return null;\n}\n\n/** Use distances along each face, not a world-axis projection that squashes diagonal façades. */\nexport function metricUVs(geometry: THREE.BufferGeometry, horizontalAxis: readonly [number, number] = [1, 0]) {\n const p = geometry.getAttribute('position'),\n n = geometry.getAttribute('normal');\n const uv = new Float32Array(p.count * 2);\n for (let i = 0; i < p.count; i++) {\n if (Math.abs(n.getZ(i)) > 0.5) {\n uv[i * 2] = p.getX(i) * horizontalAxis[0] + p.getY(i) * horizontalAxis[1];\n uv[i * 2 + 1] = -p.getX(i) * horizontalAxis[1] + p.getY(i) * horizontalAxis[0];\n } else {\n let tx = -n.getY(i),\n ty = n.getX(i);\n if (tx < -0.001 || (Math.abs(tx) < 0.001 && ty < 0)) {\n tx = -tx;\n ty = -ty;\n }\n uv[i * 2] = p.getX(i) * tx + p.getY(i) * ty;\n uv[i * 2 + 1] = p.getZ(i);\n }\n }\n geometry.setAttribute('uv', new THREE.BufferAttribute(uv, 2));\n}\n\n/** Transfer an extrusion's vertical face to a separate material, without overlapping triangles.\n * The supplied edge is in scene coordinates. Caps and the other sides remain in `geometry`.\n * ExtrudeGeometry is non-indexed; retaining indices here preserves its UVs and contact shading. */\nexport function splitWallFace(geometry: THREE.BufferGeometry, edge: [[number, number], [number, number]]) {\n const [a, b] = edge,\n dx = b[0] - a[0],\n dy = b[1] - a[1],\n length = Math.hypot(dx, dy);\n if (!length) return null;\n const p = geometry.getAttribute('position'),\n n = geometry.getAttribute('normal');\n const inside: number[] = [],\n outside: number[] = [];\n for (let i = 0; i < p.count; i += 3) {\n const onFace =\n Math.abs(n.getZ(i)) < 0.01 &&\n [i, i + 1, i + 2].every(j => Math.abs((p.getX(j) - a[0]) * dy - (p.getY(j) - a[1]) * dx) / length < 0.0001);\n (onFace ? inside : outside).push(i, i + 1, i + 2);\n }\n if (!inside.length) return null;\n const face = geometry.clone().setIndex(inside);\n geometry.setIndex(outside);\n return face;\n}\n"],"names":["OUTSIDE","SurfaceBatch","__publicField","geometry","material","id","outdoor","cell","box","key","bucket","root","leftovers","o","THREE","light","scene","entries","geometries","spans","start","i","count","mergeGeometries","mesh","g","color","points","position","span","outline","hitEntity","hit","vertex","lo","hi","mid","metricUVs","horizontalAxis","p","n","uv","tx","ty","splitWallFace","edge","a","b","dx","dy","length","inside","outside","j","face"],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import * as N from "three";
2
+ const H = {
3
+ brick: [1.04, 0.64],
4
+ stone: [2.4, 1.2],
5
+ plaster: [3, 3],
6
+ timber: [2.4, 1.44],
7
+ veneer: [1, 2.2],
8
+ oak: [2.4, 1.44],
9
+ // Four columns and six rows of 150 mm square swimming-hall / bathroom tiles.
10
+ tile: [0.6, 0.9],
11
+ terrazzo: [2.4, 2.4],
12
+ grass: [3.2, 3.2],
13
+ paving: [1.6, 1.6],
14
+ roof: [2.4, 3.6],
15
+ // Four 600 mm modules across, which is the grid every suspended ceiling in every office is set out
16
+ // on. Getting this wrong is immediately obvious from underneath: the tiles are the one thing in a
17
+ // room whose real size everybody already knows.
18
+ ceiling: [2.4, 2.4],
19
+ carpet: [3, 3],
20
+ wallpaper: [1.2, 2.4]
21
+ }, g = (t) => t - Math.floor(t), y = (t, a) => g(Math.sin(t * 127.1 + a * 311.7 + 74.7) * 43758.5453), S = (t) => t * t * (3 - 2 * t), L = /* @__PURE__ */ new Map();
22
+ function U(t) {
23
+ let a = L.get(t);
24
+ return a || (a = Float64Array.from({ length: t * t }, (c, l) => y(l % t, Math.floor(l / t))), L.set(t, a)), a;
25
+ }
26
+ function E(t, a, c) {
27
+ const l = t * c, x = a * c, z = Math.floor(l), s = Math.floor(x), h = S(g(l)), e = S(g(x)), i = U(c), f = (M, R) => i[(s + R) % c * c + (z + M) % c];
28
+ return (f(0, 0) * (1 - h) + f(1, 0) * h) * (1 - e) + (f(0, 1) * (1 - h) + f(1, 1) * h) * e;
29
+ }
30
+ const u = 512;
31
+ let B;
32
+ function V() {
33
+ if (!B) {
34
+ const t = new Float64Array(u * u), a = new Float64Array(u * u);
35
+ for (let c = 0; c < u; c++)
36
+ for (let l = 0; l < u; l++) {
37
+ const x = c * u + l;
38
+ t[x] = y(l, c), a[x] = E(l / u, c / u, 4) * 0.6 + E(l / u, c / u, 16) * 0.4;
39
+ }
40
+ B = { grain: t, weather: a };
41
+ }
42
+ return B;
43
+ }
44
+ function W(t) {
45
+ const a = u, c = V(), l = Array.from({ length: 3 }, () => {
46
+ const s = document.createElement("canvas");
47
+ return s.width = s.height = a, s;
48
+ }), x = l.map((s) => s.getContext("2d")), z = x.map((s) => s.createImageData(a, a));
49
+ for (let s = 0; s < a; s++)
50
+ for (let h = 0; h < a; h++) {
51
+ const e = h / a, i = s / a, f = c.grain[s * a + h], M = c.weather[s * a + h], R = t === "plaster" ? 0.025 : 0.065;
52
+ let m = (t === "plaster" ? 0.985 : 0.91) + (M - 0.5) * R + (f - 0.5) * 0.045, v = 0.55 + (f - 0.5) * (t === "plaster" ? 0.08 : 0.15), w = 0.88;
53
+ if (t === "brick" || t === "stone" || t === "tile" || t === "paving") {
54
+ const o = t === "brick" ? 8 : 6, r = 4, n = Math.floor(i * o), p = t === "tile" ? 0 : n % 2 * 0.5, b = e * r + p, P = g(b), C = g(i * o), I = t === "brick" ? 0.022 : t === "tile" ? 0.012 : 8e-3, D = t === "brick" ? 0.065 : t === "tile" ? 0.012 : 0.015, T = Math.min(Math.min(P, 1 - P) / I, Math.min(C, 1 - C) / D), F = S(Math.min(1, T)), G = y(Math.floor(b) % r, n) - 0.5;
55
+ m *= 1 + G * (t === "brick" ? 0.12 : 0.06), t === "tile" && (m = 0.98 + G * 0.02 + (f - 0.5) * 5e-3), m = m * F + (t === "tile" ? 0.42 : t === "brick" ? 0.86 : 0.88) * (1 - F), v = 0.18 + F * (0.56 + (f - 0.5) * (t === "tile" ? 0.015 : 0.12)), w = t === "tile" ? 0.35 + M * 0.16 + (1 - F) * 0.4 : 0.84 + f * 0.12;
56
+ } else if (t === "terrazzo") {
57
+ const o = g(e * 4), r = g(i * 4), n = Math.min(o, 1 - o, r, 1 - r), p = S(Math.min(1, n / 6e-3)), b = y(Math.floor(e * 4), Math.floor(i * 4)), P = f > 0.95 ? -0.16 : f < 0.04 ? 0.035 : 0;
58
+ m = (0.92 + b * 0.055 + (M - 0.5) * 0.025 + P) * p + 0.5 * (1 - p), v = 0.3 + p * 0.35, w = 0.38 + M * 0.12 + (1 - p) * 0.42;
59
+ } else if (t === "veneer") {
60
+ const o = Math.sin(e * Math.PI * 2 * 110 + Math.sin(i * Math.PI * 2) * 1.5 + E(e, i, 8) * 2);
61
+ m = 0.97 + o * 0.022 + (M - 0.5) * 0.025, v = 0.55 + o * 0.018, w = 0.43 + M * 0.08;
62
+ } else if (t === "oak" || t === "timber") {
63
+ const o = Math.floor(i * 8), r = g(i * 8), n = g(e + o % 4 * 0.25), p = Math.min(r, 1 - r) < 0.012 || Math.min(n, 1 - n) < 2e-3, b = Math.sin(i * Math.PI * 2 * 180 + Math.sin(e * Math.PI * 4) * 3 + E(e, i, 8) * 8);
64
+ m *= 0.94 + y(0, o) * 0.14 + b * 0.035, p && (m *= 0.63), v = p ? 0.22 : 0.6 + b * 0.065, w = t === "oak" ? 0.44 + M * 0.18 : 0.72 + M * 0.17;
65
+ } else if (t === "carpet") {
66
+ const o = y(h, Math.floor(s / 2));
67
+ m = 0.84 + (M - 0.5) * 0.2 + (o - 0.5) * 0.23, v = 0.45 + o * 0.3, w = 0.98;
68
+ } else if (t === "wallpaper") {
69
+ const o = Math.cos(i * Math.PI * 16) * 0.2, r = Math.abs(Math.sin((e * 8 + o) * Math.PI)), n = Math.max(0, 1 - r / 0.22), p = Math.min(e, 1 - e) < 4e-3;
70
+ m = 0.95 - n * 0.13 - (p ? 0.07 : 0) + (M - 0.5) * 0.08, v = 0.5 + n * 0.03 + (f - 0.5) * 0.04, w = 0.92;
71
+ } else if (t === "grass")
72
+ m *= 0.78 + M * 0.32 + (f - 0.5) * 0.18, v = 0.3 + f * 0.5, w = 1;
73
+ else if (t === "ceiling") {
74
+ const r = e * 4, n = i * 4, p = g(r), b = g(n), C = Math.min(Math.min(p, 1 - p), Math.min(b, 1 - b)) / 0.022, I = S(Math.min(1, C)), D = y(Math.floor(r), Math.floor(n)), T = E(e, i, 40) * 0.55 + f * 0.45;
75
+ m = (0.9 * I + 1 * (1 - I)) * (1 + (D - 0.5) * 0.05) + (T - 0.5) * 0.06, v = I * (0.42 + (T - 0.5) * 0.3) + (1 - I) * 0.86, w = 0.93 - (1 - I) * 0.25;
76
+ } else if (t === "roof") {
77
+ const o = Math.min(g(e * 4), 1 - g(e * 4)), r = Math.max(0, 1 - o / 0.016);
78
+ m *= 0.94 + r * 0.16 + Math.sin(i * Math.PI * 24) * 0.012, v = 0.35 + r * 0.6, w = 0.5 + M * 0.22;
79
+ }
80
+ const A = (s * a + h) * 4;
81
+ [m, v, w].forEach((o, r) => {
82
+ const n = z[r].data;
83
+ n[A] = n[A + 1] = n[A + 2] = Math.round(Math.max(0, Math.min(1, o)) * 255), n[A + 3] = 255;
84
+ });
85
+ }
86
+ return l.map((s, h) => {
87
+ x[h].putImageData(z[h], 0, 0);
88
+ const e = new N.CanvasTexture(s);
89
+ return e.wrapS = e.wrapT = N.RepeatWrapping, e.colorSpace = h === 0 ? N.SRGBColorSpace : N.NoColorSpace, e.anisotropy = 8, e.repeat.set(1 / H[t][0], 1 / H[t][1]), e;
90
+ });
91
+ }
92
+ export {
93
+ W as surfaceTextures
94
+ };
95
+ //# sourceMappingURL=textures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textures.js","sources":["../../../../src/map/textures.ts"],"sourcesContent":["import * as THREE from 'three';\nimport type { MaterialKind } from '../model/types';\n\n/** `roof` and `ceiling` are finishes with no authorable material behind them: nobody writes \"this\n * wall is made of roof\". They exist because the renderer draws two surfaces the document never\n * names — the cap over a building, and the soffit walk mode puts over the storey you are in. */\nexport type SurfaceFinish = MaterialKind | 'roof' | 'ceiling' | 'veneer';\n\n// One repeat in real metres. Shared across colours, so an entire site needs only one set per finish.\nconst SCALE: Record<SurfaceFinish, [number, number]> = {\n brick: [1.04, 0.64],\n stone: [2.4, 1.2],\n plaster: [3, 3],\n timber: [2.4, 1.44],\n veneer: [1, 2.2],\n oak: [2.4, 1.44],\n // Four columns and six rows of 150 mm square swimming-hall / bathroom tiles.\n tile: [0.6, 0.9],\n terrazzo: [2.4, 2.4],\n grass: [3.2, 3.2],\n paving: [1.6, 1.6],\n roof: [2.4, 3.6],\n // Four 600 mm modules across, which is the grid every suspended ceiling in every office is set out\n // on. Getting this wrong is immediately obvious from underneath: the tiles are the one thing in a\n // room whose real size everybody already knows.\n ceiling: [2.4, 2.4],\n carpet: [3, 3],\n wallpaper: [1.2, 2.4],\n};\nconst fract = (x: number) => x - Math.floor(x);\nconst hash = (x: number, y: number) => fract(Math.sin(x * 127.1 + y * 311.7 + 74.7) * 43758.5453);\nconst smooth = (x: number) => x * x * (3 - 2 * x);\n\nconst lattices = new Map<number, Float64Array>();\nfunction lattice(cells: number) {\n let values = lattices.get(cells);\n if (!values) {\n values = Float64Array.from({ length: cells * cells }, (_, i) => hash(i % cells, Math.floor(i / cells)));\n lattices.set(cells, values);\n }\n return values;\n}\n\n/** Periodic noise: both value and slope meet at the tile edges, including broad weathering. */\nfunction noise(u: number, v: number, cells: number) {\n const x = u * cells,\n y = v * cells,\n ix = Math.floor(x),\n iy = Math.floor(y);\n const a = smooth(fract(x)),\n b = smooth(fract(y));\n const values = lattice(cells);\n const at = (dx: number, dy: number) => values[((iy + dy) % cells) * cells + ((ix + dx) % cells)];\n return (at(0, 0) * (1 - a) + at(1, 0) * a) * (1 - b) + (at(0, 1) * (1 - a) + at(1, 1) * a) * b;\n}\n\nconst SIZE = 512;\nlet sharedNoise: { grain: Float64Array; weather: Float64Array } | undefined;\nfunction textureNoise() {\n if (!sharedNoise) {\n const grain = new Float64Array(SIZE * SIZE),\n weather = new Float64Array(SIZE * SIZE);\n for (let y = 0; y < SIZE; y++)\n for (let x = 0; x < SIZE; x++) {\n const i = y * SIZE + x;\n grain[i] = hash(x, y);\n weather[i] = noise(x / SIZE, y / SIZE, 4) * 0.6 + noise(x / SIZE, y / SIZE, 16) * 0.4;\n }\n sharedNoise = { grain, weather };\n }\n return sharedNoise;\n}\n\n/** Separate colour, relief and roughness maps: a dark stain must not become a dent in the wall. */\nexport function surfaceTextures(kind: SurfaceFinish) {\n const size = SIZE;\n // All finishes sample the same deterministic field. Keep CPU data across scene lifetimes;\n // each caller still owns fresh canvas/GPU textures and can dispose them independently.\n const field = textureNoise();\n const canvases = Array.from({ length: 3 }, () => {\n const canvas = document.createElement('canvas');\n canvas.width = canvas.height = size;\n return canvas;\n });\n const contexts = canvases.map(c => c.getContext('2d')!);\n const images = contexts.map(c => c.createImageData(size, size));\n for (let y = 0; y < size; y++)\n for (let x = 0; x < size; x++) {\n const u = x / size,\n v = y / size,\n grain = field.grain[y * size + x];\n const weather = field.weather[y * size + x];\n // Plaster covers the floor plates — most of any interior frame. Its map must average out\n // near WHITE: the texture multiplies the authored room colour, so any headroom it keeps for\n // itself darkens every interior. At the earlier 0.91 ± 0.065 swing a whole storey read as\n // weathered concrete and the room colour-coding disappeared into the blotches.\n // Masonry used to swing 13% either side of its own colour, which on a whole facade reads as\n // weathering rather than as material — a wall that looks rained on rather than built. Half\n // that keeps the grain legible up close without dirtying the elevation.\n const mottle = kind === 'plaster' ? 0.025 : 0.065;\n let tone = (kind === 'plaster' ? 0.985 : 0.91) + (weather - 0.5) * mottle + (grain - 0.5) * 0.045;\n let relief = 0.55 + (grain - 0.5) * (kind === 'plaster' ? 0.08 : 0.15),\n roughness = 0.88;\n if (kind === 'brick' || kind === 'stone' || kind === 'tile' || kind === 'paving') {\n // Finer courses. Two stones across a tile made every block the size of a window, which is\n // what gave a wall its cyclopean, dated look; real ashlar runs far smaller than its openings.\n const rows = kind === 'brick' ? 8 : 6;\n const columns = kind === 'stone' ? 4 : 4;\n const row = Math.floor(v * rows);\n const stagger = kind === 'tile' ? 0 : (row % 2) * 0.5;\n const cell = u * columns + stagger;\n const fu = fract(cell),\n fv = fract(v * rows);\n const gapU = kind === 'brick' ? 0.022 : kind === 'tile' ? 0.012 : 0.008;\n const gapV = kind === 'brick' ? 0.065 : kind === 'tile' ? 0.012 : 0.015;\n const edge = Math.min(Math.min(fu, 1 - fu) / gapU, Math.min(fv, 1 - fv) / gapV);\n const face = smooth(Math.min(1, edge));\n const variation = hash(Math.floor(cell) % columns, row) - 0.5;\n tone *= 1 + variation * (kind === 'brick' ? 0.12 : 0.06);\n // Mortar sat a third darker than the stone, so every joint drew a hard black line and the\n // wall read as a grid before it read as a surface. Mortar is paler than brick in life.\n if (kind === 'tile') tone = 0.98 + variation * 0.02 + (grain - 0.5) * 0.005;\n tone = tone * face + (kind === 'tile' ? 0.42 : kind === 'brick' ? 0.86 : 0.88) * (1 - face);\n relief = 0.18 + face * (0.56 + (grain - 0.5) * (kind === 'tile' ? 0.015 : 0.12));\n roughness = kind === 'tile' ? 0.35 + weather * 0.16 + (1 - face) * 0.4 : 0.84 + grain * 0.12;\n } else if (kind === 'terrazzo') {\n // Four 600 mm polished stone tiles each way, with 3 mm grout and aggregate flecks.\n const fu = fract(u * 4),\n fv = fract(v * 4);\n const edge = Math.min(fu, 1 - fu, fv, 1 - fv);\n const face = smooth(Math.min(1, edge / 0.006));\n const stone = hash(Math.floor(u * 4), Math.floor(v * 4));\n const chip = grain > 0.95 ? -0.16 : grain < 0.04 ? 0.035 : 0;\n tone = (0.92 + stone * 0.055 + (weather - 0.5) * 0.025 + chip) * face + 0.5 * (1 - face);\n relief = 0.3 + face * 0.35;\n roughness = 0.38 + weather * 0.12 + (1 - face) * 0.42;\n } else if (kind === 'veneer') {\n // Continuous vertical door grain, without the board joints used by timber cladding.\n const fibre = Math.sin(u * Math.PI * 2 * 110 + Math.sin(v * Math.PI * 2) * 1.5 + noise(u, v, 8) * 2);\n tone = 0.97 + fibre * 0.022 + (weather - 0.5) * 0.025;\n relief = 0.55 + fibre * 0.018;\n roughness = 0.43 + weather * 0.08;\n } else if (kind === 'oak' || kind === 'timber') {\n const row = Math.floor(v * 8),\n across = fract(v * 8);\n const along = fract(u + (row % 4) * 0.25);\n const seam = Math.min(across, 1 - across) < 0.012 || Math.min(along, 1 - along) < 0.002;\n const fibre = Math.sin(v * Math.PI * 2 * 180 + Math.sin(u * Math.PI * 4) * 3 + noise(u, v, 8) * 8);\n tone *= 0.94 + hash(0, row) * 0.14 + fibre * 0.035;\n if (seam) tone *= 0.63;\n relief = seam ? 0.22 : 0.6 + fibre * 0.065;\n roughness = kind === 'oak' ? 0.44 + weather * 0.18 : 0.72 + weather * 0.17;\n } else if (kind === 'carpet') {\n // Dense loop pile with broad wear; stains affect colour, not the height of the fibres.\n const fibre = hash(x, Math.floor(y / 2));\n tone = 0.84 + (weather - 0.5) * 0.2 + (fibre - 0.5) * 0.23;\n relief = 0.45 + fibre * 0.3;\n roughness = 0.98;\n } else if (kind === 'wallpaper') {\n // Small repeated ogees and vertical paper seams, all periodic at the metre-scaled tile edge.\n const curve = Math.cos(v * Math.PI * 16) * 0.2;\n const pattern = Math.abs(Math.sin((u * 8 + curve) * Math.PI));\n const ink = Math.max(0, 1 - pattern / 0.22);\n const seam = Math.min(u, 1 - u) < 0.004;\n tone = 0.95 - ink * 0.13 - (seam ? 0.07 : 0) + (weather - 0.5) * 0.08;\n relief = 0.5 + ink * 0.03 + (grain - 0.5) * 0.04;\n roughness = 0.92;\n } else if (kind === 'grass') {\n tone *= 0.78 + weather * 0.32 + (grain - 0.5) * 0.18;\n relief = 0.3 + grain * 0.5;\n roughness = 1;\n } else if (kind === 'ceiling') {\n // Lay-in mineral fibre on an exposed T-bar grid. Three things make it read as a ceiling\n // rather than as tiling: the grid is PALER than the tile (aluminium against fibre, the\n // opposite way round from mortar and brick), the face is fissured rather than smooth, and\n // the odd tile sits a shade off its neighbours because somebody lifted it and put it back.\n // That last one is most of what makes a corridor of them look like somewhere real.\n const cells = 4;\n const cu = u * cells,\n cv = v * cells;\n const fu = fract(cu),\n fv = fract(cv);\n // 13 mm of tee across a 600 mm module.\n const gap = 0.022;\n const edge = Math.min(Math.min(fu, 1 - fu), Math.min(fv, 1 - fv)) / gap;\n const face = smooth(Math.min(1, edge));\n const lifted = hash(Math.floor(cu), Math.floor(cv));\n const fissure = noise(u, v, 40) * 0.55 + grain * 0.45;\n // The grid has to carry in COLOUR, not only in relief: a ceiling is lit from inside itself,\n // and an emissive surface has almost no shading for a bump map to show up in. A tenth is\n // enough to read as a grid from underneath without reading as tiling.\n tone = (0.9 * face + 1.0 * (1 - face)) * (1 + (lifted - 0.5) * 0.05) + (fissure - 0.5) * 0.06;\n // The tile face sits a few millimetres below the tee it rests in, so the grid is the high\n // ground — which is what catches the light and draws the lines without any colour doing it.\n relief = face * (0.42 + (fissure - 0.5) * 0.3) + (1 - face) * 0.86;\n roughness = 0.93 - (1 - face) * 0.25;\n } else if (kind === 'roof') {\n const seam = Math.min(fract(u * 4), 1 - fract(u * 4));\n const raised = Math.max(0, 1 - seam / 0.016);\n tone *= 0.94 + raised * 0.16 + Math.sin(v * Math.PI * 24) * 0.012;\n relief = 0.35 + raised * 0.6;\n roughness = 0.5 + weather * 0.22;\n }\n const index = (y * size + x) * 4;\n [tone, relief, roughness].forEach((value, channel) => {\n const pixels = images[channel].data;\n pixels[index] = pixels[index + 1] = pixels[index + 2] = Math.round(Math.max(0, Math.min(1, value)) * 255);\n pixels[index + 3] = 255;\n });\n }\n return canvases.map((canvas, i) => {\n contexts[i].putImageData(images[i], 0, 0);\n const texture = new THREE.CanvasTexture(canvas);\n texture.wrapS = texture.wrapT = THREE.RepeatWrapping;\n texture.colorSpace = i === 0 ? THREE.SRGBColorSpace : THREE.NoColorSpace;\n texture.anisotropy = 8;\n texture.repeat.set(1 / SCALE[kind][0], 1 / SCALE[kind][1]);\n return texture;\n });\n}\n"],"names":["SCALE","fract","x","hash","y","smooth","lattices","lattice","cells","values","_","i","noise","u","v","ix","iy","a","b","at","dx","dy","SIZE","sharedNoise","textureNoise","grain","weather","surfaceTextures","kind","size","field","canvases","canvas","contexts","c","images","mottle","tone","relief","roughness","rows","columns","row","stagger","cell","fu","fv","gapU","gapV","edge","face","variation","stone","chip","fibre","across","along","seam","curve","pattern","ink","cu","cv","lifted","fissure","raised","index","value","channel","pixels","texture","THREE"],"mappings":"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"}