@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,103 @@
1
+ import type { InteriorLight, Point } from '../model/types';
2
+ export type { InteriorLight } from '../model/types';
3
+ /** Where the sun stands, seen from a place on the earth at a moment. */
4
+ export interface SolarPosition {
5
+ /** Degrees above the horizon. Negative once the sun has set. */
6
+ altitude: number;
7
+ /** Degrees clockwise from due north: 90 is east, 180 south, 270 west. */
8
+ azimuth: number;
9
+ }
10
+ /** The sun's position over `lngLat` at `when`.
11
+ *
12
+ * The NOAA low-precision formulae: good to about a hundredth of a degree over a century either
13
+ * side of 2000, which is several orders of magnitude better than a picture of a building needs and
14
+ * costs a few lines of arithmetic. Time is read in UTC, so no timezone table is involved — the
15
+ * earth's rotation is the clock, and a longitude is a time of day.
16
+ *
17
+ * Atmospheric refraction is left out. It lifts a sun on the horizon by about half a degree, which
18
+ * matters to an almanac and not to which way a shadow falls. */
19
+ export declare function solarPosition(lngLat: Point, when?: Date | number): SolarPosition;
20
+ /** How the scene is lit at a moment: where the sun is, and whether it is low enough that the model
21
+ * should wear its dusk treatment. */
22
+ export interface Sun extends SolarPosition {
23
+ /** Dusk: lamps on, warm low light, dark sky. True below civil twilight. */
24
+ evening: boolean;
25
+ }
26
+ /** Civil twilight. Below this the sky is no longer doing the lighting and the lamps are what you
27
+ * see by — which at 60°N in June the sun only just reaches, for an hour either side of midnight,
28
+ * and that is the honest picture of a white night. */
29
+ export declare const CIVIL_TWILIGHT = -6;
30
+ /** The sun over a project's origin at a moment, ready to light a scene with. */
31
+ export declare function sunAt(lngLat: Point, when?: Date | number): Sun;
32
+ /** A sun pinned to a look rather than to a clock, for the manual override and for tests. Afternoon
33
+ * and dusk, at the angles the two fixed lightings used before this file knew where the sun was. */
34
+ export declare const FIXED: Record<'day' | 'evening', Sun>;
35
+ /** Mix two hex colours. Exported so the basemap's sky can follow the same sun the model does
36
+ * rather than snapping between a day palette and a night one. */
37
+ export declare const mixColor: (a: string, b: string, t: number) => string;
38
+ /** Scene axes are east, north, up. Both renderers use this world-anchored sun. */
39
+ export declare const sunlight: (sun: Sun) => {
40
+ position: [number, number, number];
41
+ color: string;
42
+ intensity: number;
43
+ mapPosition: [number, number, number];
44
+ };
45
+ /** The artificial light a level burns.
46
+ *
47
+ * A building is not lit by the sky. Offices, shop floors, garages and platforms run their own
48
+ * lighting through the working day and often through the night, and what colour that light is says
49
+ * as much about a space as its plan does: a 4000 K fluorescent office, a 2700 K hotel corridor and
50
+ * a 5000 K operating theatre are three different rooms before anything is drawn in them. So it is
51
+ * a property of the floor, authored like its name and its height, rather than a render setting. */
52
+ /** The lamps a floor is likely to be fitted with, warmest first. Fluorescent is the default because
53
+ * it is what is actually overhead in most of the buildings anyone draws: offices, shops, schools,
54
+ * hospitals, car parks. */
55
+ export declare const LAMPS: {
56
+ id: string;
57
+ label: string;
58
+ kelvin: number;
59
+ }[];
60
+ /** What a floor is lit by when it does not say: a fluorescent ceiling, fully on. Most floors are
61
+ * fully lit; dimming is the thing worth authoring. */
62
+ export declare const DEFAULT_LIGHT: InteriorLight;
63
+ /** The nearest named lamp to a colour temperature, for showing an authored number as a choice. */
64
+ export declare const lampFor: (kelvin: number) => {
65
+ id: string;
66
+ label: string;
67
+ kelvin: number;
68
+ };
69
+ /** A colour temperature as a light colour: relative to the adapted white above, and held at one
70
+ * brightness so that changing the temperature changes the TINT and nothing else. How bright a floor
71
+ * is lit is the level's job, and the two must not fight each other. */
72
+ export declare function kelvinColor(kelvin: number): string;
73
+ /** Everything in the picture that is not the sun.
74
+ *
75
+ * Two contributions, and they are different in kind. The SKY is daylight bouncing off the ground
76
+ * and the air, so it follows the sun down and goes out with it. The INTERIOR is the building's own
77
+ * lighting, and that does not follow anything: a shop floor is lit at four in the afternoon and at
78
+ * four in the morning, which is why a cutaway of a storey at midnight is legible rather than black.
79
+ * Kerros draws buildings from the inside, so this is most of what you are looking at. */
80
+ export declare function ambient(sun: Sun, light?: InteriorLight): {
81
+ /** Hemisphere sky and ground colours, and its strength: the outdoor half. */
82
+ sky: string;
83
+ ground: string;
84
+ hemisphere: number;
85
+ /** The image-based environment, for specular life on glass and metal. */
86
+ environment: number;
87
+ /** The building's own lights: colour and strength, both from the floor, neither following the
88
+ * sun. A level that says nothing is lit by a fluorescent ceiling, because most levels are.
89
+ *
90
+ * A ceiling, which is to say it comes from ABOVE. Drawn as light arriving equally from every
91
+ * direction it lit the underside of a landing as brightly as the floor under it, and to get a
92
+ * floor bright enough it had to be turned up until the walls glowed — so the floor takes the
93
+ * full value and a wall about half of it, which is what a room of downlights actually does. */
94
+ interior: string;
95
+ /** What comes back UP off the floor: the same lamps, much weaker, which is the half of the
96
+ * picture that keeps a soffit from going black. */
97
+ interiorBounce: string;
98
+ interiorLevel: number;
99
+ /** How far along the fade to night the scene is: 1 in full day, 0 after civil twilight. */
100
+ day: number;
101
+ /** What the sky alone puts on a level surface, before the ceiling makes up the difference. */
102
+ daylight: number;
103
+ };
@@ -0,0 +1,4 @@
1
+ import type { StyleSpecification } from 'maplibre-gl';
2
+ import type { BasemapConfig } from '../model/host';
3
+ /** Fetch the external style without replacing the working, locally rendered plan. */
4
+ export declare function loadBasemap(config: BasemapConfig, signal: AbortSignal): Promise<StyleSpecification>;
@@ -0,0 +1,38 @@
1
+ import * as THREE from 'three';
2
+ import { type SurfaceFinish } from './textures';
3
+ /** Small deterministic, metre-scaled textures keep exported projects independent of CDNs. */
4
+ export declare class MaterialLibrary {
5
+ private materials;
6
+ private textures;
7
+ solid(color: string, roughness?: number): THREE.MeshStandardMaterial;
8
+ /** The active storey's own plate, when the stack has storeys under it. Opaque enough to read as
9
+ * the floor you are standing on, sheer enough that the levels beneath show through it — without
10
+ * this the plate is a lid and "All floors" shows one. depthWrite stays on: the plate must still
11
+ * occlude its own walls' hidden faces, and the ghosts below carry depthWrite off already, so they
12
+ * blend rather than fight it. */
13
+ plate(color: string, roughness?: number): THREE.MeshStandardMaterial;
14
+ /** A lit ceiling. Its underside faces down, so every light in the scene misses it and a plain
15
+ * material renders it as the darkest thing in the room — which is the opposite of what a ceiling
16
+ * full of fittings looks like from beneath. Emissive is the honest fix: the ceiling is not lit,
17
+ * it is the thing doing the lighting, and `tint` is whatever the floor's lamps are. */
18
+ luminous(color: string, tint: string, strength: number, finish?: SurfaceFinish): THREE.MeshStandardMaterial;
19
+ /** A polished twin of a finish, for the floor you are standing on in walk mode. Seen from above a
20
+ * floor is a flat patch of its own colour and matte is right; seen along at eye level it is the
21
+ * one surface in the room that shows you where the light is, because a grazing angle is where
22
+ * reflectance lives. The roughness map still multiplies through, so the polish keeps the finish's
23
+ * own variation instead of turning the plate into a mirror. */
24
+ polished(base: THREE.MeshStandardMaterial, roughness?: number): THREE.MeshStandardMaterial;
25
+ /** A vertex-coloured twin of an existing material, so geometry can carry baked shading without
26
+ * doubling the palette. Cached per source material: everything drawn with the twin must supply a
27
+ * colour attribute, since a merge needs matching attributes across all its geometries. */
28
+ shaded(base: THREE.MeshStandardMaterial): THREE.MeshStandardMaterial;
29
+ /** Translucent variant for de-emphasised floors in the stacked view. */
30
+ ghost(color: string, opacity?: number): THREE.MeshStandardMaterial;
31
+ private finishes;
32
+ get(kind: SurfaceFinish, color: string): THREE.MeshStandardMaterial;
33
+ glass(evening?: boolean, lit?: boolean): THREE.MeshStandardMaterial;
34
+ metal(color?: string): THREE.MeshStandardMaterial;
35
+ /** One image per step; the UVs follow the machine's run, including rotated/reversed flights. */
36
+ escalator(): THREE.MeshStandardMaterial;
37
+ dispose(): void;
38
+ }
@@ -0,0 +1,11 @@
1
+ import type { ProjectDocument, SiteObject } from '../model/types';
2
+ export interface PoolBounce {
3
+ room: SiteObject;
4
+ pool?: SiteObject;
5
+ strength: number;
6
+ height: number;
7
+ }
8
+ /** A diffuse approximation for white tiled rooms. Pool chambers receive the submerged fixtures;
9
+ * connecting chambers receive a weaker bounce through open portals. No light crosses a sealed
10
+ * wall, and switching off the source removes its reflected light as well. */
11
+ export declare function poolBounces(project: ProjectDocument, floorId: string): PoolBounce[];
@@ -0,0 +1,9 @@
1
+ import type { Box3, Camera, Matrix4 } from 'three';
2
+ /** Split the map's combined matrix so PBR shading sees the same eye as projection and picking.
3
+ * Keeping the camera axes in world space avoids decomposing Mercator's reflected Y axis. */
4
+ export declare function syncLightingCamera(camera: Camera, worldToClip: Matrix4, clipToWorld: Matrix4): void;
5
+ /** Extend a Mercator scene's far plane without changing its screen position or near plane. */
6
+ export declare function includeSceneDepth(matrix: Matrix4, bounds: Box3, scaleZ: number, near: number, far: number): void;
7
+ /** MapLibre's near distance follows viewport pixels and can cut away a wall before the walker
8
+ * touches it. Give POV a 4 cm near plane in scene metres, preserving the eye, X/Y and far plane. */
9
+ export declare function walkNearPlane(matrix: Matrix4, near: number, far: number): number;
@@ -0,0 +1,8 @@
1
+ import type { ProjectDocument, Ring, Slope } from '../model/types';
2
+ /** Clip in the slope's local frame. Clipping, rather than moving existing corner vertices, inserts
3
+ * the break lines where an incline meets its flat aprons, including concave outlines and holes. */
4
+ export declare function rampBand(rings: Ring[], slope: Slope, from: number, to: number): Ring[][];
5
+ export declare const rampSections: (rings: Ring[], slope: Slope) => Ring[][];
6
+ /** A descending ramp replaces the upper floor. The ceiling below also needs an opening wherever
7
+ * the ramp comes within head height. Arrival aprons stay supported by their lower deck. */
8
+ export declare function rampVoids(project: ProjectDocument, floorId: string, through?: 'floor' | 'ceiling'): Ring[];
@@ -0,0 +1,59 @@
1
+ import type { FeatureCollection } from 'geojson';
2
+ import type { Origin, Point, ProjectDocument } from '../model/types';
3
+ import type { Route } from '../model/navigation';
4
+ /** The route's own colours. A wayfinding line is not a plan colour — it is a thing laid ON the plan
5
+ * for someone to follow, so it wants to be the one saturated element in a scene of pale rooms, and
6
+ * it wants a white edge so it reads over a dark floor as well as a light one. Green because green
7
+ * is what "this way" looks like on every floor of every building anyone has walked through. */
8
+ export declare const ROUTE_COLOR = "#16a34a";
9
+ /** Steps either side of the one being walked: the same green, not a different colour — the path is
10
+ * one path, and only emphasis separates the part you are on from the rest of it. */
11
+ export declare const ROUTE_SOFT = "#6fc98d";
12
+ export declare const ROUTE_EDGE = "#ffffff";
13
+ /** Step index owning each traversal leg, in leg order. Every walk/door/vertical step carries
14
+ * nodeIds for the nodes it visits (one more than its legs); depart/arrive own no legs. */
15
+ export declare function legStepIndices(route: Route): number[];
16
+ /** GeoJSON for the 2D route layers. Walk/door legs chain into per-step polylines (so dashes and
17
+ * chevrons flow continuously); vertical legs become Point features at the shaft. Every feature
18
+ * carries { step, active, onFloor, vertical, dir }. Outdoor legs count as on-floor everywhere,
19
+ * mirroring visibleOnFloor — the outdoor tail of an egress route must stay readable. */
20
+ export declare function routeFeatures(project: ProjectDocument, route: Route, floorId: string | null, activeStep: number | null): FeatureCollection;
21
+ /** The route as something to be walked: its contiguous same-floor stretches, in travel order, in
22
+ * plan metres. A flight is a break rather than a stretch — you do not walk up a lift shaft — so each
23
+ * run ends where the route leaves the floor, and the floor it ends on is the one to switch to.
24
+ *
25
+ * This is the same decomposition the flow features use, kept apart from GeoJSON because walking a
26
+ * route wants plan coordinates and a map layer wants degrees. */
27
+ export declare function routeWalks(route: Route): {
28
+ floorId: string | null;
29
+ points: Point[];
30
+ }[];
31
+ /** A band of light travelling along the path, as a line-gradient over `line-progress`.
32
+ *
33
+ * Built from stops at fixed, strictly ascending positions with the band expressed as an alpha
34
+ * falling off either side of `phase` — rather than by moving the stops, which is the obvious way
35
+ * and the wrong one: a moving window has to be clamped as it wraps past 0 and 1, and interpolate
36
+ * refuses stops that are not strictly ascending. This way there is nothing to clamp. */
37
+ export declare function routeFlowGradient(phase: number, color?: string, stops?: number): unknown[];
38
+ /** 32×32 chevron sprite for the symbol-placement:'line' arrow layer (points +x; MapLibre rotates it
39
+ * along the travel direction). White on a halo of the path's own colour: the arrow sits ON the green
40
+ * track, so it has to be the light thing, and the halo is what keeps it legible where the track
41
+ * passes over a pale floor. Drawn on a canvas — no glyphs, no external assets. */
42
+ export declare function routeArrowImage(color?: string): ImageData;
43
+ /** The track's repeating tile, for the 3D ribbon: a green band with white edges and a white chevron
44
+ * pointing along it, drawn once and tiled every `TRACK_TILE` metres of path.
45
+ *
46
+ * One texture answers all of it. The ribbon is a constant width, so the tile's top and bottom edges
47
+ * ARE the track's edges — mitres and corners included, which is the part a separate outline geometry
48
+ * gets wrong. The chevron rides in the same tile, so the arrows are spaced in metres of walking
49
+ * rather than in pixels of screen. And scrolling the tile along the path is the animation: the
50
+ * arrows march towards the destination instead of sitting still and merely pointing at it. */
51
+ export declare const TRACK_TILE = 2.6;
52
+ /** The tile's own size in pixels, described in one place. Powers of two so it can carry mipmaps:
53
+ * at eye level most of the track is seen at a grazing angle, and that is exactly where an
54
+ * unmipmapped texture turns its chevrons into glitter. */
55
+ export declare const TRACK_PIXELS: [number, number];
56
+ export declare function routeTrackImage(color?: string, active?: boolean): ImageData;
57
+ /** Heading in degrees from true north between two local points, computed in geographic space so a
58
+ * rotated Origin yields the bearing the map camera actually needs. */
59
+ export declare function pathHeading(a: Point, b: Point, origin: Origin): number;
@@ -0,0 +1,29 @@
1
+ import type { Point, ProjectDocument, Ring } from '../model/types';
2
+ import type { StatusReading } from '../model/live';
3
+ export interface StairSurface {
4
+ flightId: string;
5
+ fromFloor: string;
6
+ toFloor: string;
7
+ low: number;
8
+ high: number;
9
+ ring: Ring;
10
+ holes?: Ring[];
11
+ height(at: Point): number;
12
+ drive?: Point;
13
+ rail?: true;
14
+ }
15
+ /** Build once per document/status/floor change, not once per walking frame. Heights are absolute
16
+ * floor elevations so switching the scene's floor datum cannot change the passenger's support. */
17
+ export declare function stairSurfaces(project: ProjectDocument, statuses?: Map<string, StatusReading>): StairSurface[];
18
+ /** Continuous walking support on the shared flights. The ramp envelope smooths the small tread
19
+ * risers; high side entries are solid, and leaving a flight sideways cannot step into mid-air. */
20
+ export declare class StairWalker {
21
+ height: number;
22
+ active?: StairSurface;
23
+ pending?: string;
24
+ private floor;
25
+ sync(floor: string | null, elevation: number): void;
26
+ refresh(surfaces: StairSurface[], at: Point): void;
27
+ reset(elevation: number): void;
28
+ step(from: Point, to: Point, surfaces: StairSurface[], floor: string | null, elevation: number, supports?: (at: Point, floorId: string | null) => boolean): Point;
29
+ }
@@ -0,0 +1,36 @@
1
+ import * as THREE from 'three';
2
+ /** The scene renders on two layers. Everything is on OUTSIDE-or-INSIDE: the sun and the sky light
3
+ * both, but the building's own ceiling lighting is on INSIDE alone, so turning the lamps up to read
4
+ * a shop floor does not also floodlight the pavement at midnight. */
5
+ export declare const INSIDE = 0, OUTSIDE = 1;
6
+ /** Batch static surfaces by material, retaining triangle ownership for picking/highlights. */
7
+ export declare class SurfaceBatch {
8
+ private pending;
9
+ private meshes;
10
+ /** Side of the square cells the batch is cut into, in scene metres; 0 batches a whole material
11
+ * into one mesh. Looking down on a floor from above, every part of it is in the frame and one
12
+ * mesh per material is the cheapest way to draw it. Standing on it, most of it is behind you or
13
+ * round a corner, and a lamp's shadow reaches twelve metres — but a mesh is culled whole or not
14
+ * at all, and one floor-sized mesh is never culled. Cut into cells, the frustum and every
15
+ * shadow pass keep only the cells they can see. */
16
+ chunk: number;
17
+ /** `outdoor` puts the surface on the OUTSIDE layer, out of reach of the interior lighting. It is
18
+ * part of the bucket key as well as a property of the mesh: two surfaces that share a material
19
+ * but not a side of the wall cannot be merged into one mesh, because a mesh sits on one layer. */
20
+ add(geometry: THREE.BufferGeometry, material: THREE.Material, id: string, outdoor?: boolean): void;
21
+ /** Fold a built object tree (a furniture group, a roof) into the batch, baking each mesh's world
22
+ * transform into its geometry. makeFixture emits one mesh per box, so a demo with a few hundred
23
+ * cars and columns was contributing thousands of draw calls and as many buffer uploads; merged by
24
+ * material they cost a handful. Instanced children are left alone — they are already batched. */
25
+ addObject(root: THREE.Object3D, id: string, outdoor?: boolean): THREE.Object3D<THREE.Object3DEventMap>[];
26
+ finish(scene: THREE.Scene): void;
27
+ outline(id: string, color: string): THREE.LineSegments | null;
28
+ clear(): void;
29
+ }
30
+ export declare function hitEntity(hit: THREE.Intersection): string | null;
31
+ /** Use distances along each face, not a world-axis projection that squashes diagonal façades. */
32
+ export declare function metricUVs(geometry: THREE.BufferGeometry, horizontalAxis?: readonly [number, number]): void;
33
+ /** Transfer an extrusion's vertical face to a separate material, without overlapping triangles.
34
+ * The supplied edge is in scene coordinates. Caps and the other sides remain in `geometry`.
35
+ * ExtrudeGeometry is non-indexed; retaining indices here preserves its UVs and contact shading. */
36
+ export declare function splitWallFace(geometry: THREE.BufferGeometry, edge: [[number, number], [number, number]]): THREE.BufferGeometry<THREE.NormalBufferAttributes, THREE.BufferGeometryEventMap> | null;
@@ -0,0 +1,8 @@
1
+ import * as THREE from 'three';
2
+ import type { MaterialKind } from '../model/types';
3
+ /** `roof` and `ceiling` are finishes with no authorable material behind them: nobody writes "this
4
+ * wall is made of roof". They exist because the renderer draws two surfaces the document never
5
+ * names — the cap over a building, and the soffit walk mode puts over the storey you are in. */
6
+ export type SurfaceFinish = MaterialKind | 'roof' | 'ceiling' | 'veneer';
7
+ /** Separate colour, relief and roughness maps: a dark stain must not become a dent in the wall. */
8
+ export declare function surfaceTextures(kind: SurfaceFinish): THREE.CanvasTexture[];
@@ -0,0 +1,30 @@
1
+ import type { Floor, ProjectDocument, SiteObject } from '../model/types';
2
+ export declare const DEPTH_CAP = 96;
3
+ export declare const MAX_CAGE_LEVELS = 32;
4
+ /** The camera, model, overlays and depth label must use the same presentation transform. */
5
+ export declare function undergroundView(project: ProjectDocument, floorId: string | null, stack: boolean): {
6
+ active: Floor | undefined;
7
+ levels: Floor[];
8
+ buried: boolean;
9
+ depthScale: number;
10
+ elevation: (z: number) => number;
11
+ focusElevation: number;
12
+ compressed: boolean;
13
+ };
14
+ export type UndergroundView = ReturnType<typeof undergroundView>;
15
+ interface FloorIndex {
16
+ floors: Map<string, Floor>;
17
+ objects: Map<string | null, SiteObject[]>;
18
+ outlines: Map<string, SiteObject>;
19
+ /** Ids of the objects that stand for their shaft — see primaryShafts. Anything vertical NOT in
20
+ * here is a twin of one that is, and is left to it to draw. */
21
+ primary: Set<string>;
22
+ /** Shafts that reach a floor without being filed under it. A stair lives on one storey and climbs
23
+ * to another, and the storey it arrives at has to draw it or the flight appears to come from
24
+ * nowhere — which is exactly what it did. Kept apart from `objects` so that everything already
25
+ * counting a floor's own objects keeps counting the same ones. */
26
+ reaching: Map<string, SiteObject[]>;
27
+ }
28
+ /** Project documents are immutable between edits. Floor navigation reuses this index. */
29
+ export declare function floorIndex(project: ProjectDocument): FloorIndex;
30
+ export {};
@@ -0,0 +1,232 @@
1
+ import type { Map as GLMap } from 'maplibre-gl';
2
+ import type { Origin, Point, Ring } from '../model/types';
3
+ import { type StairSurface } from './stairSurfaces';
4
+ /** Eye above the slab it stands on. A constant, not the floor's height: a mezzanine and a hall are
5
+ * different rooms but the same visitor. */
6
+ export { EYE, BODY, HEAD_ROOM } from './walkDimensions';
7
+ /** Shoulder radius. Generous enough that you do not clip a corner, tight enough for a 0.9 m door. */
8
+ /** Faster than anyone walks a corridor, because on a screen the corridor is the size of a hand and
9
+ * a literal 1.4 m/s reads as wading. Games settle around 3–4 m/s for the same reason. */
10
+ export declare const WALK_SPEED = 3;
11
+ export declare const RUN_SPEED = 6;
12
+ /** Following a route on its own. A touch over walking pace, because nobody watches a demonstration
13
+ * dawdle, and short of the run — a tour is for looking about you, and at eye level 6 m/s is a
14
+ * sprint. */
15
+ export declare const TOUR_SPEED = 3.5;
16
+ /** How fast the head can swing while a route is driving. A corner taken at the full rate still
17
+ * reads as a turn rather than as a cut, and anything quicker is the sharp turn this replaced. */
18
+ export declare const TURN_RATE = 120;
19
+ /** How far up the path to look while following it. Aiming at a point ahead rather than at the
20
+ * segment you are on is what makes the camera begin its turn before the corner instead of at it —
21
+ * it is also what a person does, which is why it looks right. */
22
+ export declare const LOOK_AHEAD = 2.6;
23
+ /** Pitch is MapLibre's: 0 looks straight down at the ground, 90 is level with it. The head can drop
24
+ * to 55° below level — enough to read a floor marking at your feet — and rise to a degree short of
25
+ * level. Not level itself: at 90° the point the map looks at leaves the ground, and MapLibre then
26
+ * has to invent a target in the sky, which pulls its zoom (and every zoom-gated layer) down to
27
+ * what a satellite would use. At 89° the target is a hundred-odd metres up the corridor, the
28
+ * horizon is a degree above the middle of the frame, and nothing has to be invented. */
29
+ export declare const MIN_PITCH = 35, MAX_PITCH = 89;
30
+ /** Where the head rests: a few degrees below level, so the near floor is in frame and walking reads
31
+ * as walking, with room to look up from. */
32
+ export declare const REST_PITCH = 86;
33
+ /** The plan camera's zoom ceiling, and the one the walk raises it to while it has the map. MapLibre
34
+ * puts its camera at the eye by choosing a zoom for the ground point the eye looks at, and jumpTo
35
+ * clamps that zoom to the ceiling without a word — a ceiling the solve can hit is a ceiling that
36
+ * silently lifts the eye off the floor. A 2 m eye looking 55° down on a 4K display at the equator
37
+ * needs 26.6, so the walk's ceiling is 28. Kept here so MapCanvas and the solve agree. */
38
+ export declare const MAX_MAP_ZOOM = 26, WALK_ZOOM = 28;
39
+ /** The most device pixels per CSS pixel the walk will draw. A retina display asks for two, which
40
+ * is four times the pixels of one, and at eye level every one of them is lit by the sun, the sky
41
+ * and four lamps with shadows: a dense floor measured at 11 frames a second at two and 35 at one.
42
+ * A plan is read; a walk is watched, and a watched picture has to move. 1.25 keeps most of the
43
+ * crispness of a retina display for a third of its cost. */
44
+ export declare const WALK_PIXEL_RATIO = 1.25;
45
+ /** Horizontal field of view in degrees. Kept stable when side panels resize the viewport. */
46
+ export declare const DEFAULT_WALK_FOV = 100, MIN_WALK_FOV = 60, MAX_WALK_FOV = 120;
47
+ export declare function walkFieldOfView(value: number): number;
48
+ /** MapLibre takes a vertical angle; tan(h/2) = aspect · tan(v/2). */
49
+ export declare function verticalFieldOfView(horizontal: number, aspect: number): number;
50
+ /** Degrees the view turns per pixel while dragging to look around. */
51
+ export declare const DRAG_LOOK = 0.22;
52
+ export declare const TURN_SPEED = 120;
53
+ /** Where the walker is, kept apart from the camera: the camera is whatever mode you are in, the
54
+ * avatar is the person, and they persist across leaving the walk and coming back to it. */
55
+ export interface WalkAvatar {
56
+ floorId: string | null;
57
+ /** Plan metres. */
58
+ position: Point;
59
+ /** Clockwise from plan +Y, as WalkPose.heading. */
60
+ heading: number;
61
+ pitch?: number;
62
+ }
63
+ export interface WalkPose {
64
+ /** Where the walker stands, in plan metres. */
65
+ position: Point;
66
+ /** Clockwise from plan +Y, matching the plan's own grid rather than the compass. */
67
+ heading: number;
68
+ pitch: number;
69
+ /** True while the mouse is held down and dragged to aim the view. */
70
+ looking: boolean;
71
+ }
72
+ /** Push a walker of radius `r` out of every wall it overlaps.
73
+ *
74
+ * Resolving each wall independently and in sequence is what produces sliding for free: walking into
75
+ * a wall at an angle leaves the along-wall component of the move untouched and cancels only the
76
+ * component that would have gone through it. Two passes settle inside corners, where pushing out of
77
+ * one wall can push you into its neighbour. */
78
+ export declare function unstick(p: Point, walls: Ring[], r?: number): Point;
79
+ /** The MapLibre camera that stands at `eye`, `alt` metres up, facing `bearing` and tilted `pitch`:
80
+ * the ground point it looks at and the zoom that puts it that far away.
81
+ *
82
+ * This is MapLibre's calculateCenterFromCameraLngLatAlt() for a camera that looks at the ground —
83
+ * the ray from the eye meets the ground alt/cos(pitch) away, and the zoom is the one at which that
84
+ * distance fills cameraToCenterDistance pixels. MapLibre's own copy gives up within six degrees of
85
+ * level and invents a target ten kilometres off instead, which is fine for a plane and is a 700 m
86
+ * eye for a person; this one holds to the ground right up to MAX_PITCH. The mercator scale is
87
+ * taken at the target rather than the eye, iterated, because a metre is a different fraction of
88
+ * the world at each latitude and the target is the point the zoom is measured at. */
89
+ export declare function eyeCamera(view: {
90
+ cameraToCenterDistance: number;
91
+ tileSize: number;
92
+ }, eye: [number, number], alt: number, bearing: number, pitch: number): {
93
+ center: [number, number];
94
+ zoom: number;
95
+ };
96
+ /** What a mouse movement does to where you are looking. Right turns you right; up looks up — which
97
+ * in MapLibre's terms is a LARGER pitch, 90 being level and 0 the floor. The head stops at the
98
+ * limits rather than wrapping: nobody looks at the ceiling by looking too far at their feet. */
99
+ export declare function aim(heading: number, pitch: number, dx: number, dy: number, perPixel?: number): {
100
+ heading: number;
101
+ pitch: number;
102
+ };
103
+ /** One frame of walking: what the held keys do to a heading and a position over `dt` seconds.
104
+ *
105
+ * Pulled out of the controller because it is the part with an opinion in it — which key turns you
106
+ * and which key carries you — and that opinion is worth stating somewhere a test can read it.
107
+ * Returns the new heading and the step to take, in plan metres, before any wall gets a say. */
108
+ export declare function stride(keys: ReadonlySet<string>, heading: number, dt: number): {
109
+ heading: number;
110
+ step?: Point;
111
+ };
112
+ /** Where you are after walking `travelled` metres along a polyline, and which way the path is
113
+ * heading `ahead` metres further on. Clamped at both ends, and `done` once the path runs out. */
114
+ export declare function alongPath(points: Point[], travelled: number, ahead?: number): {
115
+ at: Point;
116
+ heading: number;
117
+ done: boolean;
118
+ left: number;
119
+ };
120
+ /** Turn `from` towards `to` by at most `step` degrees, the short way round. */
121
+ export declare function easeHeading(from: number, to: number, step: number): number;
122
+ export interface WalkTerrain {
123
+ /** Solid wall bodies on the floor being walked, in plan metres. */
124
+ walls: Ring[];
125
+ /** Eye height above the presented ground plane: EYE plus whatever lifts the slab. */
126
+ eye: number;
127
+ floorId?: string | null;
128
+ elevation?: number;
129
+ stairs?: StairSurface[];
130
+ /** Whether a storey's slab supports the point, or an intentional drop has a landing below. */
131
+ supports?: (at: Point, floorId: string | null) => boolean;
132
+ dropAt?: (at: Point) => {
133
+ floorId: string;
134
+ distance: number;
135
+ } | undefined;
136
+ }
137
+ export interface WalkCallbacks {
138
+ /** Rebase at a stair landing, or before descending below the active scene's datum. */
139
+ onFloor?(floorId: string): void;
140
+ /** A real opening has a landing below. Rebase the scene onto that floor before animating descent. */
141
+ onDrop?(floorId: string): void;
142
+ /** Fired when the position, heading, pitch or look gesture changes. */
143
+ onPose(pose: WalkPose): void;
144
+ /** The walker asked to change level (F, or Shift+F for down). The host decides whether there is
145
+ * anywhere to go from where they are standing. */
146
+ onUse(down: boolean): void;
147
+ /** Escape: the host leaves walk mode. */
148
+ onExit(): void;
149
+ /** M: the walker wants the sound on or off. */
150
+ onSound?(): void;
151
+ }
152
+ /** The keys, by physical position rather than by letter so the layout survives a French or a Dvorak
153
+ * keyboard. W and S walk, A and D turn — the way Doom bound the arrows, and what a person
154
+ * walking a floor plan reaches for: the point of A is to look down the other corridor, not to
155
+ * sidle towards it. Q and E sidestep for whoever wants it, the arrows mirror the letters, and Shift
156
+ * runs. */
157
+ export declare const KEYS: Record<string, string>;
158
+ export declare class WalkController {
159
+ private readonly on;
160
+ private map?;
161
+ private origin;
162
+ private terrain;
163
+ private keys;
164
+ private frame;
165
+ private last;
166
+ private handlers;
167
+ private maxPitch;
168
+ private maxZoom;
169
+ private pixelRatio;
170
+ private planFov;
171
+ private fieldOfView;
172
+ private running;
173
+ private dragPointer;
174
+ private drag;
175
+ private path;
176
+ private travelled;
177
+ private arrive?;
178
+ private followOptions?;
179
+ private fall?;
180
+ private stairs;
181
+ position: Point;
182
+ heading: number;
183
+ pitch: number;
184
+ constructor(on: WalkCallbacks);
185
+ attach(map: GLMap, origin: Origin, start: {
186
+ position: Point;
187
+ heading: number;
188
+ pitch?: number;
189
+ }): void;
190
+ detach(): void;
191
+ /** The floor under the walker changed, or its walls were edited. */
192
+ setTerrain(terrain: WalkTerrain): void;
193
+ /** Widen the lens without changing the person's position, heading or eye height. */
194
+ setFieldOfView(value: number): void;
195
+ private resize;
196
+ /** Walk a path on its own, as far as it goes on this floor, then call `onArrive`. Taking any
197
+ * control cancels it: a tour you cannot interrupt is a video. */
198
+ follow(points: Point[], onArrive: () => void, options?: {
199
+ speed?: number;
200
+ physical?: boolean;
201
+ onCancel?: () => void;
202
+ }): void;
203
+ cancelFollow(notify?: boolean): void;
204
+ get following(): boolean;
205
+ /** Put the walker somewhere — arriving at a new floor, or jumping to a route's start. */
206
+ place(position: Point, heading?: number): void;
207
+ /** Inspect something from the current position. Only the walker writes a POV camera: panning
208
+ * its ground target like a plan camera would move the eye far behind the selected object. */
209
+ lookAt(point: Point): void;
210
+ get pose(): WalkPose;
211
+ get onStairs(): boolean;
212
+ /** Drag to look. Capture only delivers the remainder of this gesture if it leaves the canvas;
213
+ * the cursor stays visible and is free to operate panels as soon as the button is released. */
214
+ private grab;
215
+ private drop;
216
+ private endDrag;
217
+ private release;
218
+ private look;
219
+ private down;
220
+ private up;
221
+ private tick;
222
+ /** Stop at the first substep entering a hole, even when running over a narrow opening. */
223
+ private beginFall;
224
+ /** One frame of being carried along a route. The heading is rate-limited towards a point further
225
+ * up the path rather than snapped to the segment underfoot, which is what turns a corner into a
226
+ * turn instead of a cut. */
227
+ private advance;
228
+ /** One camera write per frame. The eye is where the walker stands, `terrain.eye` metres up, facing
229
+ * `heading` and tilted `pitch`; eyeCamera() turns that into the ground point the camera looks at
230
+ * and how far away it is, which is all a MapLibre camera can be told. */
231
+ private apply;
232
+ }
@@ -0,0 +1,11 @@
1
+ import type { Floor, ProjectDocument, Ring } from '../model/types';
2
+ /** Build the structural surroundings of both landings before a passenger starts climbing.
3
+ * The selected floor remains the walking datum; these levels only supply visible context. */
4
+ export declare function walkConnectionContext(project: ProjectDocument, active: Floor, primary?: Set<string>): Floor[];
5
+ /** Structural storeys seen through an authored atrium. Stops at a plate that closes the opening;
6
+ * room fit-out is deliberately left on its own floor. A room nested in a zone adds no new void. */
7
+ export declare function walkVoidContext(project: ProjectDocument, active: Floor, ceilingFloor: Floor): {
8
+ levels: Floor[];
9
+ roof: Floor | undefined;
10
+ ceilingOpenings: Ring[];
11
+ };
@@ -0,0 +1,5 @@
1
+ /** Eye height and shoulder radius in scene metres. Shared by stair rails and planar collision. */
2
+ export declare const EYE = 1.7;
3
+ export declare const BODY = 0.28;
4
+ /** Walls starting above this band are lintels the walker can pass beneath. */
5
+ export declare const HEAD_ROOM = 1.25;
@@ -0,0 +1,33 @@
1
+ import type { ElevatorControls } from '../model/host';
2
+ import type { StatusReading } from '../model/live';
3
+ import type { Route } from '../model/navigation';
4
+ import type { Point, ProjectDocument } from '../model/types';
5
+ export interface WalkJourneyHost {
6
+ project: ProjectDocument;
7
+ floor(): string | null;
8
+ changeFloor(id: string | null): Promise<void>;
9
+ position(): Point;
10
+ walk(points: Point[], options?: {
11
+ speed?: number;
12
+ physical?: boolean;
13
+ }): Promise<void>;
14
+ controls(): ElevatorControls | undefined;
15
+ status(feedId: string): StatusReading | undefined;
16
+ ride(id: string | null): void;
17
+ message(text: string): void;
18
+ step(index: number): void;
19
+ }
20
+ /** Stateful playback uses the same lift commands/readings as a passenger. Vertical legs must not
21
+ * disappear when a route is split into floor runs: that used to teleport straight past the ride. */
22
+ export declare class WalkJourney {
23
+ private host;
24
+ private stopped;
25
+ private timer?;
26
+ private rejectWait?;
27
+ constructor(host: WalkJourneyHost);
28
+ stop(): void;
29
+ private waitUntil;
30
+ private object;
31
+ private entry;
32
+ play(route: Route): Promise<void>;
33
+ }