@kerros/viewer 0.0.0-stage → 0.1.0-rc.1

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 +52 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elevators.js","sources":["../../../../src/map/elevators.ts"],"sourcesContent":["import type { Point, ProjectDocument, SiteObject } from '../model/types';\nimport { add, objectPosition, objectRotation, rectangle, rotate } from '../model/geometry';\nimport { servedFloors } from '../model/vertical';\n\nexport const cabinDimensions = (o: SiteObject) => ({\n width: Math.max(0.7, o.width - 0.28),\n depth: Math.max(0.7, o.depth - 0.28),\n});\n/** The car supplies the floor inside its footprint, even at the shaft's lowest landing. */\nexport function cabinFloorVoids(project: ProjectDocument, floorId: string, primary: Set<string>) {\n return project.objects\n .filter(o => o.kind === 'elevator' && primary.has(o.id) && servedFloors(project, o).some(f => f.id === floorId))\n .map(o => {\n const { width, depth } = cabinDimensions(o);\n return rectangle(objectPosition(project, o), width, depth, objectRotation(project, o));\n });\n}\n\nexport const elevatorDoorWidth = (o: SiteObject, turn = 0) =>\n Math.max(0.7, Math.min(1.6, (turn % 180 ? o.depth : o.width) - 0.5));\nexport const elevatorFacing = (o: SiteObject) =>\n o.rotation + { front: 0, right: 90, back: 180, left: 270 }[o.doorSides?.[0] ?? 'front'];\nexport const elevatorLanding = (o: SiteObject): Point => {\n const turn = elevatorFacing(o) - o.rotation;\n return add(o.position, rotate([0, (turn % 180 ? o.width : o.depth) / 2 + 0.8], elevatorFacing(o)));\n};\nexport function insideElevator(o: SiteObject, at: Point) {\n const local = rotate([at[0] - o.position[0], at[1] - o.position[1]], -o.rotation);\n return Math.abs(local[0]) < o.width / 2 - 0.2 && Math.abs(local[1]) < o.depth / 2 - 0.2;\n}\n/** Shaft faces and their jambs, shared by collision and the visible cabin. */\nexport function elevatorWalls(o: SiteObject, open: boolean) {\n const rings: Point[][] = [];\n for (const [face, turn] of [\n ['front', 0],\n ['right', 90],\n ['back', 180],\n ['left', 270],\n ] as const) {\n const across = turn % 180 ? o.depth : o.width,\n depth = turn % 180 ? o.width : o.depth;\n const gap = elevatorDoorWidth(o, turn);\n const doorway = open && (o.doorSides ?? ['front']).includes(face);\n const strip = (x: number, width: number) =>\n rings.push(\n rectangle(add(o.position, rotate([x, depth / 2 - 0.1], o.rotation + turn)), width, 0.2, o.rotation + turn),\n );\n if (!doorway) strip(0, across);\n else for (const sign of [-1, 1]) strip((sign * (across + gap)) / 4, (across - gap) / 2);\n }\n return rings;\n}\n\n/** Prefer the back wall for a mirror, and a side wall for a through-car lift. */\nexport const elevatorMirrorFace = (o: SiteObject) =>\n (['back', 'left', 'right', 'front'] as const).find(face => !(o.doorSides ?? ['front']).includes(face));\nexport function elevatorMirrorPoint(o: SiteObject): Point {\n const turn = { front: 0, right: 90, back: 180, left: 270 }[elevatorMirrorFace(o) ?? 'back'];\n return add(o.position, rotate([0, (turn % 180 ? o.width : o.depth) / 2], o.rotation + turn));\n}\n"],"names":["cabinDimensions","o","cabinFloorVoids","project","floorId","primary","servedFloors","f","width","depth","rectangle","objectPosition","objectRotation","elevatorDoorWidth","turn","elevatorFacing","_a","elevatorLanding","add","rotate","insideElevator","at","local","elevatorWalls","open","rings","face","across","gap","doorway","strip","x","sign","elevatorMirrorFace","elevatorMirrorPoint"],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import k from "polygon-clipping";
2
+ import { closeRing as h, openRing as x, ringArea as O, pointInRing as q } from "../model/geometry.js";
3
+ import { floorOutline as z } from "../model/walls.js";
4
+ import { floorIndex as B } from "./underground.js";
5
+ function J(i, r, e = 1 / 0) {
6
+ const o = B(i), a = r.filter((s) => s.elevation < 0 && s.elevation <= e + 0.01), c = [];
7
+ for (const s of a) {
8
+ const t = [];
9
+ for (const n of z(i, s.id)) t.push([h(n)]);
10
+ for (const n of o.objects.get(s.id) ?? []) n.slope && n.rings && t.push([h(n.rings[0])]);
11
+ for (const n of A(t)) c.push([h(n)]);
12
+ }
13
+ for (const s of r.filter((t) => t.elevation > e))
14
+ for (const t of o.objects.get(s.id) ?? []) {
15
+ if (!t.slope || !t.rings || t.slope.low > e || t.slope.high <= t.slope.low) continue;
16
+ const { axis: n, high: w, low: C } = t.slope, d = n[1][0] - n[0][0], m = n[1][1] - n[0][1], v = d * d + m * m;
17
+ if (!v) continue;
18
+ const j = (w - e) / (w - C), y = (f) => ((f[0] - n[0][0]) * d + (f[1] - n[0][1]) * m) / v - j, l = x(t.rings[0]), u = [];
19
+ for (let f = 0; f < l.length; f++) {
20
+ const p = l[f], b = l[(f + 1) % l.length], g = y(p), R = y(b);
21
+ if (g >= 0 && u.push(p), g < 0 != R < 0) {
22
+ const I = g / (g - R);
23
+ u.push([p[0] + (b[0] - p[0]) * I, p[1] + (b[1] - p[1]) * I]);
24
+ }
25
+ }
26
+ u.length >= 3 && c.push([h(u)]);
27
+ }
28
+ return c.length ? A(c) : [];
29
+ }
30
+ function A(i) {
31
+ if (!i.length) return [];
32
+ let r;
33
+ try {
34
+ r = k.union(i[0], ...i.slice(1)).map(
35
+ (e) => x(e[0])
36
+ );
37
+ } catch {
38
+ r = i.map((e) => x(e[0]));
39
+ }
40
+ return D(r);
41
+ }
42
+ function D(i) {
43
+ const r = i.map((o) => ({ ring: o, area: O(o) })).filter((o) => o.area > 1e-6).sort((o, a) => a.area - o.area), e = [];
44
+ for (const o of r)
45
+ e.some((a) => o.ring.every((c) => q(c, a.ring))) || e.push(o);
46
+ return e.map((o) => o.ring);
47
+ }
48
+ export {
49
+ D as dropContained,
50
+ J as excavationRings
51
+ };
52
+ //# sourceMappingURL=excavation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"excavation.js","sources":["../../../../src/map/excavation.ts"],"sourcesContent":["import polygonClipping from 'polygon-clipping';\nimport type { Floor, Point, ProjectDocument, Ring } from '../model/types';\nimport { closeRing, openRing, pointInRing, ringArea } from '../model/geometry';\nimport { floorOutline } from '../model/walls';\nimport { floorIndex } from './underground';\n\n/** The excavated volume's plan outline(s). A below-grade complex is rarely just the tower footprint:\n * parking decks sprawl past it and driveway ramps reach out to the street. Scoping the pit to a single\n * floor plate left everything beyond it hanging in open air, so union the plates of every below-grade\n * level with the footprints of any ramps (which are few, and are what actually reach the surface).\n * Returns one outline per disjoint excavation; empty when there is nothing below ground. */\nexport function excavationRings(project: ProjectDocument, levels: Floor[], ceiling = Infinity): Point[][] {\n const index = floorIndex(project);\n // In a cutaway, a shallow tunnel must not become a shaft extending down past the deepest\n // garage. A full stack still requests the entire excavation by leaving ceiling unbounded.\n const below = levels.filter(f => f.elevation < 0 && f.elevation <= ceiling + 0.01);\n const polygons: Ring[][] = [];\n for (const f of below) {\n // The floor's whole footprint, not its biggest room. `outlines` picks the single largest area on\n // a level, which is the floor plate on a document drawn as one zone per storey — and is one room\n // among several on an imported one. The pit was then dug under part of the basement and stopped\n // in the middle of it, leaving the rest of the storey hanging in the air with a cut edge running\n // through the building.\n //\n // Merged per level first. A garage deck is one plate and three hundred bays drawn as zones on\n // it, and one union of every ring on every level is the input polygon-clipping's sweep line\n // gives up on — after which the old fallback dug a thousand pits, one per bay, and stacked them\n // up through every buried storey as a forest of translucent walls. A level's own rings are\n // few enough to merge, and anything left inside a bigger ring is dropped either way.\n const own: Ring[][] = [];\n for (const ring of floorOutline(project, f.id)) own.push([closeRing(ring)]);\n for (const o of index.objects.get(f.id) ?? []) if (o.slope && o.rings) own.push([closeRing(o.rings[0])]);\n for (const ring of merge(own)) polygons.push([closeRing(ring)]);\n }\n // Include the lower part of a ramp arriving from a floor above the selected cut. Clip its\n // footprint at the requested elevation rather than projecting the street mouth downwards.\n for (const f of levels.filter(f => f.elevation > ceiling)) {\n for (const o of index.objects.get(f.id) ?? []) {\n if (!o.slope || !o.rings || o.slope.low > ceiling || o.slope.high <= o.slope.low) continue;\n const { axis, high, low } = o.slope;\n const dx = axis[1][0] - axis[0][0],\n dy = axis[1][1] - axis[0][1];\n const length2 = dx * dx + dy * dy;\n if (!length2) continue;\n const cut = (high - ceiling) / (high - low);\n const signed = (p: Point) => ((p[0] - axis[0][0]) * dx + (p[1] - axis[0][1]) * dy) / length2 - cut;\n const input = openRing(o.rings[0]),\n clipped: Point[] = [];\n for (let i = 0; i < input.length; i++) {\n const a = input[i],\n b = input[(i + 1) % input.length],\n da = signed(a),\n db = signed(b);\n if (da >= 0) clipped.push(a);\n if (da < 0 !== db < 0) {\n const t = da / (da - db);\n clipped.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);\n }\n }\n if (clipped.length >= 3) polygons.push([closeRing(clipped)]);\n }\n }\n if (!polygons.length) return [];\n return merge(polygons);\n}\n\n/** Union polygons into their outer rings — a courtyard void in a plate is still excavated ground\n * around the shaft — and, when the clipper cannot, keep only the rings that are not inside another. */\nfunction merge(polygons: Ring[][]): Point[][] {\n if (!polygons.length) return [];\n let outer: Point[][];\n try {\n outer = (polygonClipping.union(polygons[0], ...polygons.slice(1)) as unknown as Ring[][]).map(pg =>\n openRing(pg[0]),\n );\n } catch {\n outer = polygons.map(pg => openRing(pg[0]));\n }\n return dropContained(outer);\n}\n\n/** Rings that lie wholly inside another ring in the list add nothing to an excavation outline. */\nexport function dropContained(rings: Point[][]): Point[][] {\n const byArea = rings\n .map(ring => ({ ring, area: ringArea(ring) }))\n .filter(r => r.area > 1e-6)\n .sort((a, b) => b.area - a.area);\n const kept: { ring: Point[]; area: number }[] = [];\n for (const candidate of byArea) {\n if (kept.some(k => candidate.ring.every(pt => pointInRing(pt, k.ring)))) continue;\n kept.push(candidate);\n }\n return kept.map(k => k.ring);\n}\n"],"names":["excavationRings","project","levels","ceiling","index","floorIndex","below","f","polygons","own","ring","floorOutline","closeRing","o","merge","axis","high","low","dx","dy","length2","cut","signed","p","input","openRing","clipped","i","a","da","db","t","outer","polygonClipping","pg","dropContained","rings","byArea","ringArea","r","b","kept","candidate","k","pt","pointInRing"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { barrierEnds as k, segmentProjection as w } from "../model/geometry.js";
2
+ import { floorOutline as x } from "../model/walls.js";
3
+ const c = /* @__PURE__ */ new WeakMap();
4
+ function M(e) {
5
+ const s = c.get(e);
6
+ if (s) return s;
7
+ const n = /* @__PURE__ */ new Set();
8
+ for (const i of e.floors) {
9
+ const l = x(e, i.id);
10
+ for (const r of e.barriers.filter((o) => o.floorId === i.id && o.kind === "wall")) {
11
+ const [o, a] = k(e, r), d = Math.max(0.12, r.thickness * 0.75);
12
+ l.some(
13
+ (t) => [0.15, 0.5, 0.85].every((f) => {
14
+ const m = [o[0] + (a[0] - o[0]) * f, o[1] + (a[1] - o[1]) * f];
15
+ return t.some((u, h) => w(m, u, t[(h + 1) % t.length]).distance <= d);
16
+ })
17
+ ) && n.add(r.id);
18
+ }
19
+ }
20
+ return c.set(e, n), n;
21
+ }
22
+ export {
23
+ M as exteriorWalls
24
+ };
25
+ //# sourceMappingURL=exteriors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exteriors.js","sources":["../../../../src/map/exteriors.ts"],"sourcesContent":["import type { ProjectDocument } from '../model/types';\nimport { barrierEnds, segmentProjection } from '../model/geometry';\nimport { floorOutline } from '../model/walls';\n\nconst cache = new WeakMap<ProjectDocument, Set<string>>();\n\n/** Follow each floor's footprint, including stepped upper floors and courtyard edges. */\nexport function exteriorWalls(project: ProjectDocument): Set<string> {\n const cached = cache.get(project);\n if (cached) return cached;\n const result = new Set<string>();\n for (const floor of project.floors) {\n const rings = floorOutline(project, floor.id);\n for (const b of project.barriers.filter(b => b.floorId === floor.id && b.kind === 'wall')) {\n const [a, c] = barrierEnds(project, b),\n tolerance = Math.max(0.12, b.thickness * 0.75);\n if (\n rings.some(ring =>\n [0.15, 0.5, 0.85].every(t => {\n const point: [number, number] = [a[0] + (c[0] - a[0]) * t, a[1] + (c[1] - a[1]) * t];\n return ring.some((p, i) => segmentProjection(point, p, ring[(i + 1) % ring.length]).distance <= tolerance);\n }),\n )\n )\n result.add(b.id);\n }\n }\n cache.set(project, result);\n return result;\n}\n"],"names":["cache","exteriorWalls","project","cached","result","floor","rings","floorOutline","b","a","c","barrierEnds","tolerance","t","point","ring","p","i","segmentProjection"],"mappings":";;AAIA,MAAMA,wBAAY,QAAA;AAGX,SAASC,EAAcC,GAAuC;AACnE,QAAMC,IAASH,EAAM,IAAIE,CAAO;AAChC,MAAIC,EAAQ,QAAOA;AACnB,QAAMC,wBAAa,IAAA;AACnB,aAAWC,KAASH,EAAQ,QAAQ;AAClC,UAAMI,IAAQC,EAAaL,GAASG,EAAM,EAAE;AAC5C,eAAWG,KAAKN,EAAQ,SAAS,OAAO,CAAAM,MAAKA,EAAE,YAAYH,EAAM,MAAMG,EAAE,SAAS,MAAM,GAAG;AACzF,YAAM,CAACC,GAAGC,CAAC,IAAIC,EAAYT,GAASM,CAAC,GACnCI,IAAY,KAAK,IAAI,MAAMJ,EAAE,YAAY,IAAI;AAC/C,MACEF,EAAM;AAAA,QAAK,OACT,CAAC,MAAM,KAAK,IAAI,EAAE,MAAM,CAAAO,MAAK;AAC3B,gBAAMC,IAA0B,CAACL,EAAE,CAAC,KAAKC,EAAE,CAAC,IAAID,EAAE,CAAC,KAAKI,GAAGJ,EAAE,CAAC,KAAKC,EAAE,CAAC,IAAID,EAAE,CAAC,KAAKI,CAAC;AACnF,iBAAOE,EAAK,KAAK,CAACC,GAAGC,MAAMC,EAAkBJ,GAAOE,GAAGD,GAAME,IAAI,KAAKF,EAAK,MAAM,CAAC,EAAE,YAAYH,CAAS;AAAA,QAC3G,CAAC;AAAA,MAAA,KAGHR,EAAO,IAAII,EAAE,EAAE;AAAA,IACnB;AAAA,EACF;AACA,SAAAR,EAAM,IAAIE,GAASE,CAAM,GAClBA;AACT;"}
@@ -0,0 +1,218 @@
1
+ import { doorSymbol as G } from "../model/doors.js";
2
+ import { wallFootprints as T, wallSpan as V } from "./wallJoins.js";
3
+ import { statusTone as q } from "../adapters/status.js";
4
+ import { undergroundView as z } from "./underground.js";
5
+ import { isVertical as E, reaches as W } from "../model/vertical.js";
6
+ import { derivedGraph as B } from "../model/topology.js";
7
+ import { rectangle as A, toLngLat as M, add as F, rotate as L, closeRing as C, objectPosition as D, objectRotation as H, distance as N, footprint as J, openRing as K, ringArea as Q } from "../model/geometry.js";
8
+ const O = {
9
+ wall: "#9298a2",
10
+ fence: "#868c98",
11
+ door: "#6a63d8",
12
+ gate: "#5d6390",
13
+ window: "#ccd4dd",
14
+ zone: "#e0e3e6",
15
+ room: "#f4f5f6",
16
+ parcel: "#e4e6e3",
17
+ building: "#dfe1e5",
18
+ office: "#d3d9e2",
19
+ container: "#bcc3cd",
20
+ storage: "#e0e2d8",
21
+ evacuation: "#cfe6c6",
22
+ sensor: "#8fb7d4",
23
+ light: "#eee6b4",
24
+ alarm: "#e0524e",
25
+ equipment: "#c3bda8"
26
+ }, R = (i, s) => i.floorId === null || s !== null && i.floorId === s, U = (i, s, f) => R(s, f) || E(s.kind) && W(i, s, f), y = J;
27
+ function X(i) {
28
+ var c;
29
+ const s = (c = i.rings) == null ? void 0 : c[0];
30
+ if (!s) return Math.min(i.width, i.depth);
31
+ const f = K(s);
32
+ let w = 0;
33
+ for (let t = 0; t < f.length; t++) w += N(f[t], f[(t + 1) % f.length]);
34
+ return w > 0 ? 4 * Q(s) / w : Math.min(i.width, i.depth);
35
+ }
36
+ function Y(i, s, f = !1) {
37
+ const w = [], c = new Map(i.junctions.map((n) => [n.id, n.position])), t = new Map(i.floors.map((n) => [n.id, n.elevation])), h = /* @__PURE__ */ new Map();
38
+ for (const n of i.objects)
39
+ if (n.barrierId && ["door", "window", "gate"].includes(n.kind)) {
40
+ const e = h.get(n.barrierId) ?? [];
41
+ e.push(n), h.set(n.barrierId, e);
42
+ }
43
+ const l = i.barriers.filter((n) => f || R(n, s)), b = T({ ...i, barriers: l });
44
+ for (const n of l) {
45
+ const e = c.get(n.startId), o = c.get(n.endId);
46
+ if (!e || !o) continue;
47
+ const r = N(e, o);
48
+ if (r < 0.01) continue;
49
+ const d = b.get(n.id);
50
+ if (!d) continue;
51
+ const p = [(o[0] - e[0]) / r, (o[1] - e[1]) / r], k = f ? t.get(n.floorId ?? "") ?? 0 : 0, v = (h.get(n.id) ?? []).sort((a, g) => (a.offset ?? 0) - (g.offset ?? 0)), u = (a, g, I, P) => {
52
+ if (g - a < 5e-3 || P <= I) return;
53
+ const x = V(d, e, p, a <= 0 ? null : a, g >= r ? null : g);
54
+ x.length < 3 || w.push({
55
+ ring: x,
56
+ base: k + I,
57
+ height: k + P,
58
+ id: n.id,
59
+ color: n.color ?? O[n.kind],
60
+ floorId: n.floorId,
61
+ material: n.material
62
+ });
63
+ };
64
+ let m = 0;
65
+ for (const a of v) {
66
+ const g = Math.max(0, (a.offset ?? 0) - a.width / 2), I = Math.min(r, (a.offset ?? 0) + a.width / 2);
67
+ u(m, g, 0, n.height), a.kind === "window" ? (u(g, I, 0, 0.85), u(g, I, Math.min(0.85 + a.height, n.height), n.height)) : u(g, I, Math.min(a.height, n.height), n.height), m = I;
68
+ }
69
+ u(m, r, 0, n.height);
70
+ }
71
+ return w;
72
+ }
73
+ function eo(i, s, f, w, c, t) {
74
+ var e;
75
+ const h = [], l = (o, r) => h.push({
76
+ type: "Feature",
77
+ properties: r,
78
+ geometry: { type: "Polygon", coordinates: o.map((d) => C(d).map((p) => M(p, i.origin))) }
79
+ }), b = (o, r) => h.push({
80
+ type: "Feature",
81
+ properties: r,
82
+ geometry: { type: "LineString", coordinates: o.map((d) => M(d, i.origin)) }
83
+ }), n = z(i, s, !1).buried;
84
+ for (const o of i.objects.filter((r) => U(i, r, s) && !(n && r.floorId === null))) {
85
+ const r = {
86
+ id: o.id,
87
+ kind: o.kind,
88
+ floorId: o.floorId,
89
+ category: o.category,
90
+ selected: o.id === f,
91
+ // Narrow things (parking bays, parked cars, lockers, racks) are a few pixels across at building
92
+ // zoom, where drawing every one turns a dense plan into a hatch of noise. Flagged here so the
93
+ // plan can hold them back until you are close enough for them to mean something.
94
+ small: X(o) < 3.4,
95
+ color: ((e = t == null ? void 0 : t.objectColor) == null ? void 0 : e.call(t, o)) ?? o.color ?? (o.kind === "room" ? t == null ? void 0 : t.room : void 0) ?? O[o.kind] ?? "#ccd8dc"
96
+ };
97
+ if (o.rings) l(o.rings, r);
98
+ else if (["office", "container", "storage", "elevator", "stairs", "turnstile"].includes(o.kind))
99
+ l(y(o), r);
100
+ else if (o.kind === "fixture" || o.kind === "light") l(y(o), { ...r, kind: "furniture" });
101
+ else if (o.kind === "landscape") {
102
+ const d = [];
103
+ for (let p = 0; p <= 360; p += 30) d.push(F(o.position, L([o.width / 2, 0], p)));
104
+ l([C(d)], { ...r, kind: "furniture" });
105
+ }
106
+ if (["door", "window", "gate"].includes(o.kind)) {
107
+ const d = D(i, o), p = H(i, o), k = o.feedId ? c == null ? void 0 : c.get(o.feedId) : void 0, v = k ? q(k) : void 0, u = t == null ? void 0 : t.statusTones, m = v === "critical" ? (u == null ? void 0 : u.critical) ?? "#e0524e" : v === "warning" ? (u == null ? void 0 : u.warning) ?? "#e2a13d" : void 0;
108
+ if (l(
109
+ [A(d, o.width, o.kind === "window" ? 0.18 : 0.16, p)],
110
+ m ? { ...r, color: m } : r
111
+ ), v === "critical") {
112
+ const a = [];
113
+ for (let g = 0; g <= 360; g += 20) a.push(F(d, L([Math.max(1.1, o.width * 0.9), 0], g)));
114
+ l([C(a)], { id: o.id, kind: "status-halo", color: "#e0524e" });
115
+ }
116
+ if (o.kind === "door")
117
+ for (const a of G(i, o, k == null ? void 0 : k.open))
118
+ b(a, { ...r, color: m ?? r.color, decoration: !0 });
119
+ }
120
+ if (o.kind === "stairs") {
121
+ for (let d = 0; d < 10; d++)
122
+ b(
123
+ [
124
+ F(o.position, L([-o.width / 2, -o.depth / 2 + d * o.depth / 10], o.rotation)),
125
+ F(o.position, L([o.width / 2, -o.depth / 2 + d * o.depth / 10], o.rotation))
126
+ ],
127
+ { color: "#9ba0ab", decoration: !0 }
128
+ );
129
+ if (o.stairModel === "escalator") {
130
+ const d = c == null ? void 0 : c.get(o.feedId ?? ""), p = (d == null ? void 0 : d.running) ?? !0, k = ((d == null ? void 0 : d.travel) ?? o.travel ?? "up") === "down" ? -1 : 1, v = (g, I = 0) => F(o.position, L([I, g * k], o.rotation)), u = o.depth / 2 - 0.25, m = Math.min(0.9, o.depth / 5), a = { color: p ? "#4d5f8f" : "#a2a7b0", decoration: !0, escalator: !0 };
131
+ b([v(-u), v(u)], a);
132
+ for (const g of [-1, 1]) b([v(u - m, g * Math.min(o.width, m) / 2.2), v(u)], a);
133
+ }
134
+ }
135
+ if ((w || o.id === f) && o.kind === "camera") {
136
+ const d = [o.position];
137
+ for (let p = -(o.coverageAngle ?? 70) / 2; p <= (o.coverageAngle ?? 70) / 2; p += 2)
138
+ d.push(F(o.position, L([o.coverageRange ?? 12, 0], o.rotation + p)));
139
+ l([C(d)], { id: o.id, kind: "coverage", color: "#7b74dd" });
140
+ }
141
+ }
142
+ for (const o of Y(i, s).filter((r) => r.base === 0))
143
+ l([o.ring], {
144
+ id: o.id,
145
+ kind: "wall",
146
+ floorId: o.floorId,
147
+ color: (t == null ? void 0 : t.wall) ?? o.color,
148
+ selected: f === o.id
149
+ });
150
+ return { type: "FeatureCollection", features: h };
151
+ }
152
+ function no(i, s, f) {
153
+ const w = [], c = !i.navNodes, t = c ? B(i) : { nodes: i.navNodes ?? [], edges: i.navEdges ?? [] }, h = t.nodes, l = t.edges, b = new Map(h.map((e) => [e.id, e]));
154
+ for (const e of l) {
155
+ const o = b.get(e.aId), r = b.get(e.bId);
156
+ !o || !r || !R(o, s) && !R(r, s) || w.push({
157
+ type: "Feature",
158
+ properties: {
159
+ derived: c,
160
+ id: e.id,
161
+ kind: e.kind,
162
+ vertical: e.kind === "stairs" || e.kind === "elevator",
163
+ selected: e.id === f
164
+ },
165
+ geometry: {
166
+ type: "LineString",
167
+ coordinates: [M(o.position, i.origin), M(r.position, i.origin)]
168
+ }
169
+ });
170
+ }
171
+ const n = new Set(
172
+ l.filter((e) => e.kind === "stairs" || e.kind === "elevator").flatMap((e) => [e.aId, e.bId])
173
+ );
174
+ for (const e of h)
175
+ R(e, s) && w.push({
176
+ type: "Feature",
177
+ properties: {
178
+ derived: c,
179
+ id: e.id,
180
+ bound: !!e.objectId,
181
+ vertical: n.has(e.id),
182
+ selected: e.id === f
183
+ },
184
+ geometry: { type: "Point", coordinates: M(e.position, i.origin) }
185
+ });
186
+ return { type: "FeatureCollection", features: w };
187
+ }
188
+ function to(i, s, f, w) {
189
+ const c = f ? [...s, f] : s;
190
+ let t;
191
+ if (w === "rectangle" && c.length >= 2) {
192
+ const [h, l] = [c[0], c.at(-1)];
193
+ t = {
194
+ type: "Polygon",
195
+ coordinates: [
196
+ A([(h[0] + l[0]) / 2, (h[1] + l[1]) / 2], Math.abs(h[0] - l[0]), Math.abs(h[1] - l[1])).map(
197
+ (b) => M(b, i.origin)
198
+ )
199
+ ]
200
+ };
201
+ } else t = { type: "LineString", coordinates: c.map((h) => M(h, i.origin)) };
202
+ return {
203
+ type: "FeatureCollection",
204
+ features: c.length >= 2 ? [{ type: "Feature", properties: {}, geometry: t }] : []
205
+ };
206
+ }
207
+ export {
208
+ O as COLORS,
209
+ to as draftFeatures,
210
+ eo as makeFeatures,
211
+ no as navGraphFeatures,
212
+ y as objectRings,
213
+ U as onFloor,
214
+ X as planWidth,
215
+ R as visibleOnFloor,
216
+ Y as wallPieces
217
+ };
218
+ //# sourceMappingURL=features.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features.js","sources":["../../../../src/map/features.ts"],"sourcesContent":["import { doorSymbol } from '../model/doors';\nimport { wallFootprints, wallSpan } from './wallJoins';\nimport type { Feature, FeatureCollection, Geometry } from 'geojson';\nimport type { MaterialKind, Point, ProjectDocument, Ring, SiteObject } from '../model/types';\nimport type { StatusReading } from '../model/live';\nimport { statusTone } from '../adapters/status';\nimport { undergroundView } from './underground';\nimport { isVertical, reaches } from '../model/vertical';\nimport { derivedGraph } from '../model/topology';\nimport type { MapStyleOptions } from '../theme';\nimport {\n add,\n closeRing,\n distance,\n footprint,\n openRing,\n ringArea,\n objectPosition,\n objectRotation,\n rectangle,\n rotate,\n toLngLat,\n} from '../model/geometry';\n\nexport const COLORS: Record<string, string> = {\n wall: '#9298a2',\n fence: '#868c98',\n door: '#6a63d8',\n gate: '#5d6390',\n window: '#ccd4dd',\n zone: '#e0e3e6',\n room: '#f4f5f6',\n parcel: '#e4e6e3',\n building: '#dfe1e5',\n office: '#d3d9e2',\n container: '#bcc3cd',\n storage: '#e0e2d8',\n evacuation: '#cfe6c6',\n sensor: '#8fb7d4',\n light: '#eee6b4',\n alarm: '#e0524e',\n equipment: '#c3bda8',\n};\nexport const visibleOnFloor = (object: { floorId: string | null }, floorId: string | null) =>\n object.floorId === null || (floorId !== null && object.floorId === floorId);\n/** The same question for objects that span levels. A shaft is filed under the lowest level it serves\n * and reaches the rest, so asking only where it is filed hides it from every storey it arrives at —\n * on the plan as much as in the model. Takes the project because the answer is in `servedFloorIds`. */\nexport const onFloor = (project: ProjectDocument, object: SiteObject, floorId: string | null) =>\n visibleOnFloor(object, floorId) || (isVertical(object.kind) && reaches(project, object, floorId));\nexport const objectRings = footprint;\n/** Roughly how wide a shape is on the plan, independent of its rotation. `width`/`depth` are an\n * axis-aligned bounding box, which badly overstates anything drawn on a rotated site grid — a 2.5 m\n * parking bay turned 55° measures nearly 5 m that way. 4·area/perimeter is the harmonic mean of a\n * rectangle's sides (2wh/(w+h)), so it tracks the narrow dimension and does not care about angle. */\nexport function planWidth(o: SiteObject): number {\n const outer = o.rings?.[0];\n if (!outer) return Math.min(o.width, o.depth);\n const ring = openRing(outer);\n let perimeter = 0;\n for (let i = 0; i < ring.length; i++) perimeter += distance(ring[i], ring[(i + 1) % ring.length]);\n return perimeter > 0 ? (4 * ringArea(outer)) / perimeter : Math.min(o.width, o.depth);\n}\nexport interface WallPiece {\n ring: Ring;\n base: number;\n height: number;\n id: string;\n color: string;\n floorId: string | null;\n material?: MaterialKind;\n}\nexport function wallPieces(project: ProjectDocument, floorId: string | null, stack = false): WallPiece[] {\n const pieces: WallPiece[] = [];\n const junctions = new Map(project.junctions.map(j => [j.id, j.position]));\n const elevations = new Map(project.floors.map(f => [f.id, f.elevation]));\n const attached = new Map<string, SiteObject[]>();\n for (const o of project.objects)\n if (o.barrierId && ['door', 'window', 'gate'].includes(o.kind)) {\n const items = attached.get(o.barrierId) ?? [];\n items.push(o);\n attached.set(o.barrierId, items);\n }\n const visible = project.barriers.filter(b => stack || visibleOnFloor(b, floorId));\n const footprints = wallFootprints({ ...project, barriers: visible });\n for (const barrier of visible) {\n const a = junctions.get(barrier.startId),\n b = junctions.get(barrier.endId);\n if (!a || !b) continue;\n const length = distance(a, b);\n if (length < 0.01) continue;\n const footprint = footprints.get(barrier.id);\n if (!footprint) continue;\n const direction: Point = [(b[0] - a[0]) / length, (b[1] - a[1]) / length];\n const elevation = stack ? (elevations.get(barrier.floorId ?? '') ?? 0) : 0;\n const openings = (attached.get(barrier.id) ?? []).sort((a, b) => (a.offset ?? 0) - (b.offset ?? 0));\n const piece = (start: number, end: number, base: number, height: number) => {\n if (end - start < 0.005 || height <= base) return;\n const ring = wallSpan(footprint, a, direction, start <= 0 ? null : start, end >= length ? null : end);\n if (ring.length < 3) return;\n pieces.push({\n ring,\n base: elevation + base,\n height: elevation + height,\n id: barrier.id,\n color: barrier.color ?? COLORS[barrier.kind],\n floorId: barrier.floorId,\n material: barrier.material,\n });\n };\n // Openings retain their centreline offsets; only terminal pieces keep a joined end.\n let cursor = 0;\n for (const opening of openings) {\n const start = Math.max(0, (opening.offset ?? 0) - opening.width / 2),\n end = Math.min(length, (opening.offset ?? 0) + opening.width / 2);\n piece(cursor, start, 0, barrier.height);\n if (opening.kind === 'window') {\n piece(start, end, 0, 0.85);\n piece(start, end, Math.min(0.85 + opening.height, barrier.height), barrier.height);\n } else piece(start, end, Math.min(opening.height, barrier.height), barrier.height);\n cursor = end;\n }\n piece(cursor, length, 0, barrier.height);\n }\n return pieces;\n}\nexport function makeFeatures(\n project: ProjectDocument,\n floorId: string | null,\n selected: string | null,\n coverage: boolean,\n statuses?: Map<string, StatusReading>,\n mapStyle?: MapStyleOptions,\n): FeatureCollection {\n const features: Feature[] = [];\n const poly = (rings: Ring[], props: Record<string, unknown>) =>\n features.push({\n type: 'Feature',\n properties: props,\n geometry: { type: 'Polygon', coordinates: rings.map(r => closeRing(r).map(p => toLngLat(p, project.origin))) },\n });\n const line = (points: Point[], props: Record<string, unknown>) =>\n features.push({\n type: 'Feature',\n properties: props,\n geometry: { type: 'LineString', coordinates: points.map(p => toLngLat(p, project.origin)) },\n });\n // Below ground, outdoor geometry (the parcel, landscaping, site POIs) sits far above your head and\n // has nothing to do with the level you are standing on. The 3D scene already drops it when buried;\n // the plan has to agree, or a basement is drawn on top of the site plate.\n const buried = undergroundView(project, floorId, false).buried;\n for (const o of project.objects.filter(o => onFloor(project, o, floorId) && !(buried && o.floorId === null))) {\n const props = {\n id: o.id,\n kind: o.kind,\n floorId: o.floorId,\n category: o.category,\n selected: o.id === selected,\n // Narrow things (parking bays, parked cars, lockers, racks) are a few pixels across at building\n // zoom, where drawing every one turns a dense plan into a hatch of noise. Flagged here so the\n // plan can hold them back until you are close enough for them to mean something.\n small: planWidth(o) < 3.4,\n color:\n mapStyle?.objectColor?.(o) ??\n o.color ??\n (o.kind === 'room' ? mapStyle?.room : undefined) ??\n COLORS[o.kind] ??\n '#ccd8dc',\n };\n if (o.rings) poly(o.rings, props);\n else if (['office', 'container', 'storage', 'elevator', 'stairs', 'turnstile'].includes(o.kind))\n poly(objectRings(o), props);\n else if (o.kind === 'fixture' || o.kind === 'light') poly(objectRings(o), { ...props, kind: 'furniture' });\n else if (o.kind === 'landscape') {\n const circle: Point[] = [];\n for (let a = 0; a <= 360; a += 30) circle.push(add(o.position, rotate([o.width / 2, 0], a)));\n poly([closeRing(circle)], { ...props, kind: 'furniture' });\n }\n if (['door', 'window', 'gate'].includes(o.kind)) {\n const position = objectPosition(project, o),\n rotation = objectRotation(project, o);\n // Live status tints the leaf by tone: critical red, warning amber (incl. stale). Richer host\n // state (lock/contact/…) is not part of the contract and is not rendered here.\n const status = o.feedId ? statuses?.get(o.feedId) : undefined;\n const t = status ? statusTone(status) : undefined;\n const st = mapStyle?.statusTones;\n const tone =\n t === 'critical' ? (st?.critical ?? '#e0524e') : t === 'warning' ? (st?.warning ?? '#e2a13d') : undefined;\n poly(\n [rectangle(position, o.width, o.kind === 'window' ? 0.18 : 0.16, rotation)],\n tone ? { ...props, color: tone } : props,\n );\n if (t === 'critical') {\n const halo: Point[] = [];\n for (let a = 0; a <= 360; a += 20) halo.push(add(position, rotate([Math.max(1.1, o.width * 0.9), 0], a)));\n poly([closeRing(halo)], { id: o.id, kind: 'status-halo', color: '#e0524e' });\n }\n if (o.kind === 'door') {\n for (const points of doorSymbol(project, o, status?.open))\n line(points, { ...props, color: tone ?? props.color, decoration: true });\n }\n }\n if (o.kind === 'stairs') {\n for (let i = 0; i < 10; i++)\n line(\n [\n add(o.position, rotate([-o.width / 2, -o.depth / 2 + (i * o.depth) / 10], o.rotation)),\n add(o.position, rotate([o.width / 2, -o.depth / 2 + (i * o.depth) / 10], o.rotation)),\n ],\n { color: '#9ba0ab', decoration: true },\n );\n // An escalator is a stair that goes one way, and a plan that does not say which way is missing\n // the first thing anyone needs from it. The arrow runs the way it carries you — the feed's\n // direction if it has been reversed, the object's otherwise — and greys out when it stops,\n // because a stopped escalator carries nobody anywhere.\n if (o.stairModel === 'escalator') {\n const status = statuses?.get(o.feedId ?? '');\n const running = status?.running ?? true;\n const way = (status?.travel ?? o.travel ?? 'up') === 'down' ? -1 : 1;\n // Along the run, which is the depth axis — the same axis the 3D flight climbs.\n const at = (t: number, across = 0) => add(o.position, rotate([across, t * way], o.rotation));\n const reach = o.depth / 2 - 0.25;\n const head = Math.min(0.9, o.depth / 5);\n const arrow = { color: running ? '#4d5f8f' : '#a2a7b0', decoration: true, escalator: true };\n line([at(-reach), at(reach)], arrow);\n for (const side of [-1, 1]) line([at(reach - head, (side * Math.min(o.width, head)) / 2.2), at(reach)], arrow);\n }\n }\n // The coverage toggle shows every camera's field of view; selecting a camera always shows its own.\n if ((coverage || o.id === selected) && o.kind === 'camera') {\n const points: Point[] = [o.position];\n for (let i = -(o.coverageAngle ?? 70) / 2; i <= (o.coverageAngle ?? 70) / 2; i += 2)\n points.push(add(o.position, rotate([o.coverageRange ?? 12, 0], o.rotation + i)));\n poly([closeRing(points)], { id: o.id, kind: 'coverage', color: '#7b74dd' });\n }\n }\n // Top-down walls omit lintels so the opening stays visible.\n for (const piece of wallPieces(project, floorId).filter(p => p.base === 0))\n poly([piece.ring], {\n id: piece.id,\n kind: 'wall',\n floorId: piece.floorId,\n color: mapStyle?.wall ?? piece.color,\n selected: selected === piece.id,\n });\n return { type: 'FeatureCollection', features };\n}\n/** Edit-mode nav-graph overlay data (kerros-navgraph): nodes as points, edges as lines. Vertical edges\n * join twin shaft positions so they render as a marker-sized stub rather than a long line. */\nexport function navGraphFeatures(\n project: ProjectDocument,\n floorId: string | null,\n selected: string | null,\n): FeatureCollection {\n const features: Feature[] = [];\n // The same graph routing uses. A document that has never had a route authored on it has no stored\n // nodes at all, and routing quietly falls back to the one derived from spaces and portals — so the\n // graph was there and working and simply could not be seen, which is the worst of both: you cannot\n // tell a plan that routes from one that does not. Drawn either way, and marked which it is.\n const derived = !project.navNodes;\n const graph = derived ? derivedGraph(project) : { nodes: project.navNodes ?? [], edges: project.navEdges ?? [] };\n const nodes = graph.nodes,\n edges = graph.edges,\n byId = new Map(nodes.map(n => [n.id, n]));\n for (const e of edges) {\n const a = byId.get(e.aId),\n b = byId.get(e.bId);\n if (!a || !b) continue;\n if (!visibleOnFloor(a, floorId) && !visibleOnFloor(b, floorId)) continue;\n features.push({\n type: 'Feature',\n properties: {\n derived,\n id: e.id,\n kind: e.kind,\n vertical: e.kind === 'stairs' || e.kind === 'elevator',\n selected: e.id === selected,\n },\n geometry: {\n type: 'LineString',\n coordinates: [toLngLat(a.position, project.origin), toLngLat(b.position, project.origin)],\n },\n });\n }\n const verticalNodes = new Set(\n edges.filter(e => e.kind === 'stairs' || e.kind === 'elevator').flatMap(e => [e.aId, e.bId]),\n );\n for (const n of nodes)\n if (visibleOnFloor(n, floorId))\n features.push({\n type: 'Feature',\n properties: {\n derived,\n id: n.id,\n bound: !!n.objectId,\n vertical: verticalNodes.has(n.id),\n selected: n.id === selected,\n },\n geometry: { type: 'Point', coordinates: toLngLat(n.position, project.origin) },\n });\n return { type: 'FeatureCollection', features };\n}\nexport function draftFeatures(\n project: ProjectDocument,\n points: Point[],\n hover: Point | null,\n tool: string,\n): FeatureCollection {\n const coords = hover ? [...points, hover] : points;\n let geometry: Geometry;\n if (tool === 'rectangle' && coords.length >= 2) {\n const [a, b] = [coords[0], coords.at(-1)!];\n geometry = {\n type: 'Polygon',\n coordinates: [\n rectangle([(a[0] + b[0]) / 2, (a[1] + b[1]) / 2], Math.abs(a[0] - b[0]), Math.abs(a[1] - b[1])).map(p =>\n toLngLat(p, project.origin),\n ),\n ],\n };\n } else geometry = { type: 'LineString', coordinates: coords.map(p => toLngLat(p, project.origin)) };\n return {\n type: 'FeatureCollection',\n features: coords.length >= 2 ? [{ type: 'Feature', properties: {}, geometry }] : [],\n };\n}\n"],"names":["COLORS","visibleOnFloor","object","floorId","onFloor","project","isVertical","reaches","objectRings","footprint","planWidth","o","outer","_a","ring","openRing","perimeter","i","distance","ringArea","wallPieces","stack","pieces","junctions","j","elevations","f","attached","items","visible","b","footprints","wallFootprints","barrier","a","length","direction","elevation","openings","piece","start","end","base","height","wallSpan","cursor","opening","makeFeatures","selected","coverage","statuses","mapStyle","features","poly","rings","props","r","closeRing","toLngLat","line","points","p","buried","undergroundView","circle","add","rotate","position","objectPosition","rotation","objectRotation","status","t","statusTone","st","tone","rectangle","halo","doorSymbol","running","way","at","across","reach","head","arrow","side","navGraphFeatures","derived","graph","derivedGraph","nodes","edges","byId","n","verticalNodes","draftFeatures","hover","tool","coords","geometry"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import * as c from "three";
2
+ const d = new c.BoxGeometry(1, 1, 1).toNonIndexed();
3
+ function I(e, f, E, Z, n, A) {
4
+ const b = Math.hypot(f[0] - e[0], f[1] - e[1]), u = (f[0] - e[0]) / b, a = (f[1] - e[1]) / b, w = E.map((s) => (s[0] - e[0]) * u + (s[1] - e[1]) * a), l = Math.max(0, Math.min(...w)), B = Math.min(b, Math.max(...w)), h = [], v = [], X = [], M = d.getAttribute("position"), i = d.getAttribute("normal"), Y = d.getAttribute("uv"), y = (s, g, o, z, H) => {
5
+ for (let t = 0; t < M.count; t++) {
6
+ const F = M.getX(t) * o + s, G = M.getY(t) * z;
7
+ h.push(e[0] + F * u - G * a, e[1] + F * a + G * u, Z + g + M.getZ(t) * H), v.push(
8
+ i.getX(t) * u - i.getY(t) * a,
9
+ i.getX(t) * a + i.getY(t) * u,
10
+ i.getZ(t)
11
+ ), X.push(Y.getX(t), Y.getY(t));
12
+ }
13
+ }, r = B - l, m = Math.min(A, 0.065, r / 2), p = Math.min(0.05, n / 4);
14
+ if (r > 0 && n > 0) {
15
+ for (const o of [p / 2 + Math.min(0.08, n / 4), n - p / 2])
16
+ y((l + B) / 2, o, r, A, p);
17
+ const s = Math.max(1, Math.ceil(r / 1.2));
18
+ for (let o = 0; o <= s; o++) y(l + m / 2 + (r - m) * o / s, n / 2, m, m, n);
19
+ const g = Math.max(1, Math.ceil(r / 0.13));
20
+ for (let o = 1; o < g; o++)
21
+ y(l + r * o / g, n / 2, Math.min(0.018, m), Math.min(0.025, A), n - p);
22
+ }
23
+ const x = new c.BufferGeometry();
24
+ return x.setAttribute("position", new c.Float32BufferAttribute(h, 3)), x.setAttribute("normal", new c.Float32BufferAttribute(v, 3)), x.setAttribute("uv", new c.Float32BufferAttribute(X, 2)), x;
25
+ }
26
+ export {
27
+ I as fenceGeometry
28
+ };
29
+ //# sourceMappingURL=fences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fences.js","sources":["../../../../src/map/fences.ts"],"sourcesContent":["import * as THREE from 'three';\nimport type { Point, Ring } from '../model/types';\n\n// One non-indexed box template avoids allocating an extrusion for every picket.\nconst box = new THREE.BoxGeometry(1, 1, 1).toNonIndexed();\n\n/** Open railing geometry for one wall piece; the piece interval already excludes gates. */\nexport function fenceGeometry(a: Point, b: Point, ring: Ring, base: number, height: number, thickness: number) {\n const length = Math.hypot(b[0] - a[0], b[1] - a[1]);\n const ux = (b[0] - a[0]) / length,\n uy = (b[1] - a[1]) / length;\n const along = ring.map(p => (p[0] - a[0]) * ux + (p[1] - a[1]) * uy);\n const start = Math.max(0, Math.min(...along)),\n end = Math.min(length, Math.max(...along));\n const positions: number[] = [],\n normals: number[] = [],\n uvs: number[] = [];\n const source = box.getAttribute('position'),\n normal = box.getAttribute('normal'),\n uv = box.getAttribute('uv');\n const add = (x: number, z: number, w: number, d: number, h: number) => {\n for (let i = 0; i < source.count; i++) {\n const sx = source.getX(i) * w + x,\n sy = source.getY(i) * d;\n positions.push(a[0] + sx * ux - sy * uy, a[1] + sx * uy + sy * ux, base + z + source.getZ(i) * h);\n normals.push(\n normal.getX(i) * ux - normal.getY(i) * uy,\n normal.getX(i) * uy + normal.getY(i) * ux,\n normal.getZ(i),\n );\n uvs.push(uv.getX(i), uv.getY(i));\n }\n };\n const width = end - start,\n post = Math.min(thickness, 0.065, width / 2),\n rail = Math.min(0.05, height / 4);\n if (width > 0 && height > 0) {\n for (const z of [rail / 2 + Math.min(0.08, height / 4), height - rail / 2])\n add((start + end) / 2, z, width, thickness, rail);\n const bays = Math.max(1, Math.ceil(width / 1.2));\n for (let i = 0; i <= bays; i++) add(start + post / 2 + ((width - post) * i) / bays, height / 2, post, post, height);\n const pickets = Math.max(1, Math.ceil(width / 0.13));\n for (let i = 1; i < pickets; i++)\n add(start + (width * i) / pickets, height / 2, Math.min(0.018, post), Math.min(0.025, thickness), height - rail);\n }\n const geometry = new THREE.BufferGeometry();\n geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));\n geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));\n geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));\n return geometry;\n}\n"],"names":["box","THREE","fenceGeometry","a","b","ring","base","height","thickness","length","ux","uy","along","p","start","end","positions","normals","uvs","source","normal","uv","add","x","z","w","d","h","i","sx","sy","width","post","rail","bays","pickets","geometry"],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ var y = Object.defineProperty;
2
+ var I = (a, e, t) => e in a ? y(a, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[e] = t;
3
+ var h = (a, e, t) => I(a, typeof e != "symbol" ? e + "" : e, t);
4
+ import { toLngLat as M, distance as E } from "../model/geometry.js";
5
+ import { legStepIndices as b, pathHeading as v } from "./route.js";
6
+ import { aimCenter as _ } from "./cameraAim.js";
7
+ import { floorAim as q } from "./cameraAim.js";
8
+ const F = 4, G = 180, k = 1400, C = 4;
9
+ function L(a) {
10
+ const e = a.filter((t, s) => !s || E(t, a[s - 1]) > 0.01);
11
+ for (let t = 1; t < e.length - 1; ) {
12
+ const s = Math.abs(
13
+ Math.atan2(e[t + 1][1] - e[t][1], e[t + 1][0] - e[t][0]) - Math.atan2(e[t][1] - e[t - 1][1], e[t][0] - e[t - 1][0])
14
+ ) * 180 / Math.PI;
15
+ Math.min(s, 360 - s) < C ? e.splice(t, 1) : t++;
16
+ }
17
+ return e;
18
+ }
19
+ class T {
20
+ constructor(e, t) {
21
+ h(this, "cancelled", !1);
22
+ h(this, "ended", !1);
23
+ h(this, "_playing", !1);
24
+ h(this, "origin");
25
+ h(this, "onGesture", () => this.stop());
26
+ this.map = e, this.cb = t;
27
+ }
28
+ get playing() {
29
+ return this._playing;
30
+ }
31
+ play(e, t) {
32
+ if (this._playing || this.ended) return;
33
+ this.origin = t.origin, this._playing = !0;
34
+ const s = this.map.getCanvasContainer();
35
+ s.addEventListener("pointerdown", this.onGesture), s.addEventListener("wheel", this.onGesture), this.run(e, t.startFloorId).finally(() => this.end());
36
+ }
37
+ /** Cancel: abort the current ease, detach listeners, fire onEnd exactly once. Never resets the
38
+ * camera itself — the host re-runs fit if it wants to. */
39
+ stop() {
40
+ this.ended || (this.cancelled = !0, this.map.stop(), this.end());
41
+ }
42
+ end() {
43
+ if (this.ended) return;
44
+ this.ended = !0, this.cancelled = !0, this._playing = !1;
45
+ const e = this.map.getCanvasContainer();
46
+ e.removeEventListener("pointerdown", this.onGesture), e.removeEventListener("wheel", this.onGesture), this.cb.onEnd();
47
+ }
48
+ ease(e, t, s, o, c) {
49
+ if (this.cancelled) return Promise.resolve();
50
+ const l = M(e, this.origin), r = _([l[0], l[1]], this.cb.focusElevation(t), this.map.getPitch(), s);
51
+ return new Promise((i) => {
52
+ const n = () => {
53
+ clearTimeout(d), this.map.off("moveend", n), i();
54
+ }, d = setTimeout(n, o + 600);
55
+ this.map.on("moveend", n), this.map.easeTo({
56
+ center: r,
57
+ bearing: s,
58
+ duration: o,
59
+ easing: (f) => f,
60
+ essential: !0,
61
+ ...c === void 0 ? {} : { zoom: c }
62
+ });
63
+ });
64
+ }
65
+ async switchFloor(e, t) {
66
+ return e === t || this.cancelled ? t : (this.cb.requestFloor(e), await this.cb.waitForFloor(e), e);
67
+ }
68
+ async run(e, t) {
69
+ const s = b(e);
70
+ let o = t, c = -1;
71
+ const l = (i) => {
72
+ i !== c && !this.cancelled && (c = i, this.cb.onStep(i));
73
+ };
74
+ l(0);
75
+ const r = e.nodes[0];
76
+ if (r) {
77
+ if (o = await this.switchFloor(r.floorId, o), this.cancelled) return;
78
+ const i = e.nodes.length > 1 ? v(r.position, e.nodes[1].position, this.origin) : this.map.getBearing();
79
+ await this.ease(r.position, r.floorId, i, 750, Math.max(this.map.getZoom(), 18.5));
80
+ }
81
+ for (let i = 0; i < e.legs.length && !this.cancelled; i++) {
82
+ const n = e.legs[i];
83
+ if (n.edge.kind === "stairs" || n.edge.kind === "elevator") {
84
+ if (l(s[i]), o = await this.switchFloor(n.to.floorId, o), this.cancelled) break;
85
+ await this.ease(n.to.position, n.to.floorId, this.map.getBearing(), k);
86
+ continue;
87
+ }
88
+ const d = s[i], f = [n.from.position, n.to.position];
89
+ let p = n.to.floorId ?? n.from.floorId;
90
+ for (; i + 1 < e.legs.length && s[i + 1] === d && e.legs[i + 1].edge.kind !== "stairs" && e.legs[i + 1].edge.kind !== "elevator"; )
91
+ i++, f.push(e.legs[i].to.position), p = e.legs[i].to.floorId ?? p;
92
+ if (l(d), p !== null && (o = await this.switchFloor(p, o)), this.cancelled) break;
93
+ const m = L(f);
94
+ for (let g = 1; g < m.length && !this.cancelled; g++) {
95
+ const w = m[g - 1], u = m[g];
96
+ await this.ease(
97
+ u,
98
+ o,
99
+ v(w, u, this.origin),
100
+ Math.max(G, E(w, u) / F * 1e3)
101
+ );
102
+ }
103
+ }
104
+ this.cancelled || l(e.steps.length - 1);
105
+ }
106
+ }
107
+ export {
108
+ T as JourneyPlayer,
109
+ _ as aimCenter,
110
+ q as floorAim
111
+ };
112
+ //# sourceMappingURL=journey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey.js","sources":["../../../../src/map/journey.ts"],"sourcesContent":["// Journey animator: drives the MapLibre camera along a routed path with chained linear easeTo per\n// edge (bearing follows travel), asks the host to switch floors at vertical transitions, and\n// cancels on any user gesture. Owned and instantiated by MapCanvas; the host controls it through\n// the MapCanvas prop contract (playing / onJourneyStep / onJourneyEnd / onRequestFloor).\nimport type { Map as GLMap } from 'maplibre-gl';\nimport type { Origin, Point } from '../model/types';\nimport { distance, toLngLat } from '../model/geometry';\nimport type { Route } from '../model/navigation';\nimport { legStepIndices, pathHeading } from './route';\nimport { aimCenter } from './cameraAim';\nexport { aimCenter, floorAim } from './cameraAim';\n\nexport interface JourneyCallbacks {\n /** Ask the app to switch floors; the map rebuild follows via React. */\n requestFloor(floorId: string | null): void;\n /** Resolves once the app shows floorId and the view has rebuilt (implemented by MapCanvas). */\n waitForFloor(floorId: string | null): Promise<void>;\n /** Presented elevation of a floor: undergroundView(...).focusElevation in 3D, 0 in 2D. */\n focusElevation(floorId: string | null): number;\n onStep(index: number): void;\n onEnd(): void;\n}\n\nconst SPEED = 4; // m/s along walk legs — presentation pace, not literal walking speed.\nconst MIN_EDGE_MS = 180,\n RIDE_MS = 1400,\n MERGE_DEG = 4;\n\n/** Merge near-collinear vertices so consecutive edges read as one continuous ease. */\nfunction mergeCollinear(points: Point[]): Point[] {\n const out = points.filter((p, i) => !i || distance(p, points[i - 1]) > 0.01);\n for (let i = 1; i < out.length - 1; ) {\n const turn =\n (Math.abs(\n Math.atan2(out[i + 1][1] - out[i][1], out[i + 1][0] - out[i][0]) -\n Math.atan2(out[i][1] - out[i - 1][1], out[i][0] - out[i - 1][0]),\n ) *\n 180) /\n Math.PI;\n if (Math.min(turn, 360 - turn) < MERGE_DEG) out.splice(i, 1);\n else i++;\n }\n return out;\n}\n\nexport class JourneyPlayer {\n private cancelled = false;\n private ended = false;\n private _playing = false;\n private origin!: Origin;\n constructor(\n private map: GLMap,\n private cb: JourneyCallbacks,\n ) {}\n get playing() {\n return this._playing;\n }\n private onGesture = () => this.stop();\n play(route: Route, ctx: { origin: Origin; startFloorId: string | null }) {\n if (this._playing || this.ended) return;\n this.origin = ctx.origin;\n this._playing = true;\n const el = this.map.getCanvasContainer();\n el.addEventListener('pointerdown', this.onGesture);\n el.addEventListener('wheel', this.onGesture);\n void this.run(route, ctx.startFloorId).finally(() => this.end());\n }\n /** Cancel: abort the current ease, detach listeners, fire onEnd exactly once. Never resets the\n * camera itself — the host re-runs fit if it wants to. */\n stop() {\n if (this.ended) return;\n this.cancelled = true;\n this.map.stop();\n this.end();\n }\n private end() {\n if (this.ended) return;\n this.ended = true;\n this.cancelled = true;\n this._playing = false;\n const el = this.map.getCanvasContainer();\n el.removeEventListener('pointerdown', this.onGesture);\n el.removeEventListener('wheel', this.onGesture);\n this.cb.onEnd();\n }\n private ease(point: Point, floorId: string | null, bearing: number, duration: number, zoom?: number): Promise<void> {\n if (this.cancelled) return Promise.resolve();\n const ll = toLngLat(point, this.origin);\n const center = aimCenter([ll[0], ll[1]], this.cb.focusElevation(floorId), this.map.getPitch(), bearing);\n return new Promise(resolve => {\n const done = () => {\n clearTimeout(timer);\n this.map.off('moveend', done);\n resolve();\n };\n const timer = setTimeout(done, duration + 600); // easeTo may short-circuit without moveend\n this.map.on('moveend', done);\n this.map.easeTo({\n center,\n bearing,\n duration,\n easing: t => t,\n essential: true,\n ...(zoom === undefined ? {} : { zoom }),\n });\n });\n }\n private async switchFloor(target: string | null, current: string | null): Promise<string | null> {\n if (target === current || this.cancelled) return current;\n this.cb.requestFloor(target);\n await this.cb.waitForFloor(target);\n return target;\n }\n private async run(route: Route, startFloor: string | null) {\n const stepOf = legStepIndices(route);\n let floor = startFloor,\n lastStep = -1;\n const fire = (i: number) => {\n if (i !== lastStep && !this.cancelled) {\n lastStep = i;\n this.cb.onStep(i);\n }\n };\n fire(0); // depart\n const first = route.nodes[0];\n if (first) {\n // Always align the view with the departure floor — an outdoor-origin route (egress reversed)\n // starts on the outdoor site, not on whatever floor the user happened to be browsing.\n floor = await this.switchFloor(first.floorId, floor);\n if (this.cancelled) return;\n const bearing =\n route.nodes.length > 1\n ? pathHeading(first.position, route.nodes[1].position, this.origin)\n : this.map.getBearing();\n await this.ease(first.position, first.floorId, bearing, 750, Math.max(this.map.getZoom(), 18.5));\n }\n for (let i = 0; i < route.legs.length && !this.cancelled; i++) {\n const leg = route.legs[i];\n if (leg.edge.kind === 'stairs' || leg.edge.kind === 'elevator') {\n fire(stepOf[i]);\n floor = await this.switchFloor(leg.to.floorId, floor);\n if (this.cancelled) break;\n await this.ease(leg.to.position, leg.to.floorId, this.map.getBearing(), RIDE_MS);\n continue;\n }\n // Chain the run of same-step walk/door legs (doors are their own steps, so a chain never\n // crosses a step boundary) and ease each merged edge with linear timing.\n const step = stepOf[i];\n const points: Point[] = [leg.from.position, leg.to.position];\n let fid = leg.to.floorId ?? leg.from.floorId;\n while (\n i + 1 < route.legs.length &&\n stepOf[i + 1] === step &&\n route.legs[i + 1].edge.kind !== 'stairs' &&\n route.legs[i + 1].edge.kind !== 'elevator'\n ) {\n i++;\n points.push(route.legs[i].to.position);\n fid = route.legs[i].to.floorId ?? fid;\n }\n fire(step);\n if (fid !== null) floor = await this.switchFloor(fid, floor); // entering through a door switches inside; stepping outside keeps the floor (outdoor legs read from any floor)\n if (this.cancelled) break;\n const merged = mergeCollinear(points);\n for (let k = 1; k < merged.length && !this.cancelled; k++) {\n const a = merged[k - 1],\n b = merged[k];\n await this.ease(\n b,\n floor,\n pathHeading(a, b, this.origin),\n Math.max(MIN_EDGE_MS, (distance(a, b) / SPEED) * 1000),\n );\n }\n }\n if (!this.cancelled) fire(route.steps.length - 1); // arrive\n }\n}\n"],"names":["SPEED","MIN_EDGE_MS","RIDE_MS","MERGE_DEG","mergeCollinear","points","out","p","i","distance","turn","JourneyPlayer","map","cb","__publicField","route","ctx","el","point","floorId","bearing","duration","zoom","ll","toLngLat","center","aimCenter","resolve","done","timer","t","target","current","startFloor","stepOf","legStepIndices","floor","lastStep","fire","first","pathHeading","leg","step","fid","merged","k","a","b"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ const o = 0.15, t = 0.18;
2
+ export {
3
+ o as LIFT,
4
+ t as SLAB
5
+ };
6
+ //# sourceMappingURL=levels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"levels.js","sources":["../../../../src/map/levels.ts"],"sourcesContent":["// Where a storey's surfaces sit above its datum.\n//\n// Apart from SceneLayer because they are the one thing the flat plan needs from the 3D renderer: walk\n// mode solves its eye height from them, and importing them used to drag three.js and every material,\n// texture and geometry behind it into any bundle that touched MapCanvas.\n/** Clearance between the floor's datum and the underside of its slab. */\nexport const LIFT = 0.15;\n/** The slab itself. */\nexport const SLAB = 0.18;\n"],"names":["LIFT","SLAB"],"mappings":"AAMO,MAAMA,IAAO,MAEPC,IAAO;"}