@kerros/editor 0.0.0-stage → 0.1.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +35 -2
  3. package/dist/SitePlanner.js +1189 -0
  4. package/dist/SitePlanner.js.map +1 -0
  5. package/dist/adapters/basemap.js +11 -0
  6. package/dist/adapters/basemap.js.map +1 -0
  7. package/dist/adapters/persistence.js +121 -0
  8. package/dist/adapters/persistence.js.map +1 -0
  9. package/dist/adapters/status.js +11 -0
  10. package/dist/adapters/status.js.map +1 -0
  11. package/dist/adapters/useProjectPersistence.js +56 -0
  12. package/dist/adapters/useProjectPersistence.js.map +1 -0
  13. package/dist/components/AiImportPanel.js +635 -0
  14. package/dist/components/AiImportPanel.js.map +1 -0
  15. package/dist/components/AnalysisPreview.js +179 -0
  16. package/dist/components/AnalysisPreview.js.map +1 -0
  17. package/dist/components/ElevatorPanel.js +120 -0
  18. package/dist/components/ElevatorPanel.js.map +1 -0
  19. package/dist/components/Icons.js +57 -0
  20. package/dist/components/Icons.js.map +1 -0
  21. package/dist/components/ImportDialog.js +467 -0
  22. package/dist/components/ImportDialog.js.map +1 -0
  23. package/dist/components/Inspector.js +1184 -0
  24. package/dist/components/Inspector.js.map +1 -0
  25. package/dist/components/NavigatePanel.js +301 -0
  26. package/dist/components/NavigatePanel.js.map +1 -0
  27. package/dist/components/NavigationGraph.js +234 -0
  28. package/dist/components/NavigationGraph.js.map +1 -0
  29. package/dist/components/StructureExplorer.js +510 -0
  30. package/dist/components/StructureExplorer.js.map +1 -0
  31. package/dist/components/StructureView.js +286 -0
  32. package/dist/components/StructureView.js.map +1 -0
  33. package/dist/components/controls.js +209 -0
  34. package/dist/components/controls.js.map +1 -0
  35. package/dist/components/graphLayout.js +27 -0
  36. package/dist/components/graphLayout.js.map +1 -0
  37. package/dist/host.js +175 -0
  38. package/dist/host.js.map +1 -0
  39. package/dist/i18n.js +88 -0
  40. package/dist/i18n.js.map +1 -0
  41. package/dist/import/analysisPreview.js +41 -0
  42. package/dist/import/analysisPreview.js.map +1 -0
  43. package/dist/import/brief.js +29 -0
  44. package/dist/import/brief.js.map +1 -0
  45. package/dist/import/checkpoint.js +50 -0
  46. package/dist/import/checkpoint.js.map +1 -0
  47. package/dist/import/detect.js +120 -0
  48. package/dist/import/detect.js.map +1 -0
  49. package/dist/import/documentSvg.js +49 -0
  50. package/dist/import/documentSvg.js.map +1 -0
  51. package/dist/import/instructions.js +20 -0
  52. package/dist/import/instructions.js.map +1 -0
  53. package/dist/import/openings.js +61 -0
  54. package/dist/import/openings.js.map +1 -0
  55. package/dist/import/planImport.js +233 -0
  56. package/dist/import/planImport.js.map +1 -0
  57. package/dist/import/register.js +47 -0
  58. package/dist/import/register.js.map +1 -0
  59. package/dist/import/runs.js +67 -0
  60. package/dist/import/runs.js.map +1 -0
  61. package/dist/import/session.js +23 -0
  62. package/dist/import/session.js.map +1 -0
  63. package/dist/import/types.js +13 -0
  64. package/dist/import/types.js.map +1 -0
  65. package/dist/import/usage.js +29 -0
  66. package/dist/import/usage.js.map +1 -0
  67. package/dist/import/walls.js +112 -0
  68. package/dist/import/walls.js.map +1 -0
  69. package/dist/index.js +178 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/map/CabinMaterials.js +81 -0
  72. package/dist/map/CabinMaterials.js.map +1 -0
  73. package/dist/map/CabinMirror.js +43 -0
  74. package/dist/map/CabinMirror.js.map +1 -0
  75. package/dist/map/Doors.js +80 -0
  76. package/dist/map/Doors.js.map +1 -0
  77. package/dist/map/ElevatorCabin.js +93 -0
  78. package/dist/map/ElevatorCabin.js.map +1 -0
  79. package/dist/map/FixtureLights.js +117 -0
  80. package/dist/map/FixtureLights.js.map +1 -0
  81. package/dist/map/MapCanvas.js +2161 -0
  82. package/dist/map/MapCanvas.js.map +1 -0
  83. package/dist/map/Passenger.js +69 -0
  84. package/dist/map/Passenger.js.map +1 -0
  85. package/dist/map/SceneLayer.js +1534 -0
  86. package/dist/map/SceneLayer.js.map +1 -0
  87. package/dist/map/UndergroundContext.js +153 -0
  88. package/dist/map/UndergroundContext.js.map +1 -0
  89. package/dist/map/ambience.js +194 -0
  90. package/dist/map/ambience.js.map +1 -0
  91. package/dist/map/architecture.js +305 -0
  92. package/dist/map/architecture.js.map +1 -0
  93. package/dist/map/bathMusic.js +92 -0
  94. package/dist/map/bathMusic.js.map +1 -0
  95. package/dist/map/cameraAim.js +33 -0
  96. package/dist/map/cameraAim.js.map +1 -0
  97. package/dist/map/elevators.js +56 -0
  98. package/dist/map/elevators.js.map +1 -0
  99. package/dist/map/excavation.js +52 -0
  100. package/dist/map/excavation.js.map +1 -0
  101. package/dist/map/exteriors.js +25 -0
  102. package/dist/map/exteriors.js.map +1 -0
  103. package/dist/map/features.js +218 -0
  104. package/dist/map/features.js.map +1 -0
  105. package/dist/map/fences.js +29 -0
  106. package/dist/map/fences.js.map +1 -0
  107. package/dist/map/journey.js +112 -0
  108. package/dist/map/journey.js.map +1 -0
  109. package/dist/map/levels.js +6 -0
  110. package/dist/map/levels.js.map +1 -0
  111. package/dist/map/lighting.js +143 -0
  112. package/dist/map/lighting.js.map +1 -0
  113. package/dist/map/loadBasemap.js +26 -0
  114. package/dist/map/loadBasemap.js.map +1 -0
  115. package/dist/map/materials.js +183 -0
  116. package/dist/map/materials.js.map +1 -0
  117. package/dist/map/poolLighting.js +39 -0
  118. package/dist/map/poolLighting.js.map +1 -0
  119. package/dist/map/projection.js +24 -0
  120. package/dist/map/projection.js.map +1 -0
  121. package/dist/map/rampGeometry.js +38 -0
  122. package/dist/map/rampGeometry.js.map +1 -0
  123. package/dist/map/route.js +107 -0
  124. package/dist/map/route.js.map +1 -0
  125. package/dist/map/stairSurfaces.js +115 -0
  126. package/dist/map/stairSurfaces.js.map +1 -0
  127. package/dist/map/surfaces.js +136 -0
  128. package/dist/map/surfaces.js.map +1 -0
  129. package/dist/map/textures.js +95 -0
  130. package/dist/map/textures.js.map +1 -0
  131. package/dist/map/underground.js +34 -0
  132. package/dist/map/underground.js.map +1 -0
  133. package/dist/map/walk.js +433 -0
  134. package/dist/map/walk.js.map +1 -0
  135. package/dist/map/walkContext.js +46 -0
  136. package/dist/map/walkContext.js.map +1 -0
  137. package/dist/map/walkDimensions.js +7 -0
  138. package/dist/map/walkDimensions.js.map +1 -0
  139. package/dist/map/walkJourney.js +85 -0
  140. package/dist/map/walkJourney.js.map +1 -0
  141. package/dist/map/walkSurfaces.js +106 -0
  142. package/dist/map/walkSurfaces.js.map +1 -0
  143. package/dist/map/wallJoins.js +70 -0
  144. package/dist/map/wallJoins.js.map +1 -0
  145. package/dist/map/water.js +274 -0
  146. package/dist/map/water.js.map +1 -0
  147. package/dist/model/authoring.js +99 -0
  148. package/dist/model/authoring.js.map +1 -0
  149. package/dist/model/boundaries.js +548 -0
  150. package/dist/model/boundaries.js.map +1 -0
  151. package/dist/model/coverage.js +30 -0
  152. package/dist/model/coverage.js.map +1 -0
  153. package/dist/model/document.js +52 -0
  154. package/dist/model/document.js.map +1 -0
  155. package/dist/model/doors.js +47 -0
  156. package/dist/model/doors.js.map +1 -0
  157. package/dist/model/factory.js +56 -0
  158. package/dist/model/factory.js.map +1 -0
  159. package/dist/model/geometry.js +454 -0
  160. package/dist/model/geometry.js.map +1 -0
  161. package/dist/model/history.js +29 -0
  162. package/dist/model/history.js.map +1 -0
  163. package/dist/model/imports.js +45 -0
  164. package/dist/model/imports.js.map +1 -0
  165. package/dist/model/inference.js +182 -0
  166. package/dist/model/inference.js.map +1 -0
  167. package/dist/model/materials.js +11 -0
  168. package/dist/model/materials.js.map +1 -0
  169. package/dist/model/mutations.js +157 -0
  170. package/dist/model/mutations.js.map +1 -0
  171. package/dist/model/navigation.js +275 -0
  172. package/dist/model/navigation.js.map +1 -0
  173. package/dist/model/ontology.js +109 -0
  174. package/dist/model/ontology.js.map +1 -0
  175. package/dist/model/passages.js +5 -0
  176. package/dist/model/passages.js.map +1 -0
  177. package/dist/model/portals.js +42 -0
  178. package/dist/model/portals.js.map +1 -0
  179. package/dist/model/precision.js +11 -0
  180. package/dist/model/precision.js.map +1 -0
  181. package/dist/model/project.js +31 -0
  182. package/dist/model/project.js.map +1 -0
  183. package/dist/model/ramps.js +10 -0
  184. package/dist/model/ramps.js.map +1 -0
  185. package/dist/model/roomNavigation.js +87 -0
  186. package/dist/model/roomNavigation.js.map +1 -0
  187. package/dist/model/routeClearance.js +72 -0
  188. package/dist/model/routeClearance.js.map +1 -0
  189. package/dist/model/spaces.js +104 -0
  190. package/dist/model/spaces.js.map +1 -0
  191. package/dist/model/stairGeometry.js +40 -0
  192. package/dist/model/stairGeometry.js.map +1 -0
  193. package/dist/model/stairTraversal.js +48 -0
  194. package/dist/model/stairTraversal.js.map +1 -0
  195. package/dist/model/topology.js +63 -0
  196. package/dist/model/topology.js.map +1 -0
  197. package/dist/model/types.js +35 -0
  198. package/dist/model/types.js.map +1 -0
  199. package/dist/model/validate.js +240 -0
  200. package/dist/model/validate.js.map +1 -0
  201. package/dist/model/vertical.js +129 -0
  202. package/dist/model/vertical.js.map +1 -0
  203. package/dist/model/walls.js +261 -0
  204. package/dist/model/walls.js.map +1 -0
  205. package/dist/planner/DrawingDock.js +231 -0
  206. package/dist/planner/DrawingDock.js.map +1 -0
  207. package/dist/planner/FloorControls.js +93 -0
  208. package/dist/planner/FloorControls.js.map +1 -0
  209. package/dist/planner/MapSettings.js +143 -0
  210. package/dist/planner/MapSettings.js.map +1 -0
  211. package/dist/planner/PlannerHelp.js +57 -0
  212. package/dist/planner/PlannerHelp.js.map +1 -0
  213. package/dist/planner/ProjectSidebar.js +246 -0
  214. package/dist/planner/ProjectSidebar.js.map +1 -0
  215. package/dist/planner/floors.js +18 -0
  216. package/dist/planner/floors.js.map +1 -0
  217. package/dist/planner/useDrawingTools.js +341 -0
  218. package/dist/planner/useDrawingTools.js.map +1 -0
  219. package/dist/planner/useMapDisplay.js +51 -0
  220. package/dist/planner/useMapDisplay.js.map +1 -0
  221. package/dist/planner/usePlannerKeyboard.js +138 -0
  222. package/dist/planner/usePlannerKeyboard.js.map +1 -0
  223. package/dist/planner/useReferenceAlignment.js +50 -0
  224. package/dist/planner/useReferenceAlignment.js.map +1 -0
  225. package/dist/styles.css +4931 -0
  226. package/dist/theme/index.js +34 -0
  227. package/dist/theme/index.js.map +1 -0
  228. package/dist/types/SitePlanner.d.ts +5 -0
  229. package/dist/types/adapters/basemap.d.ts +2 -0
  230. package/dist/types/adapters/persistence.d.ts +29 -0
  231. package/dist/types/adapters/status.d.ts +4 -0
  232. package/dist/types/adapters/useProjectPersistence.d.ts +13 -0
  233. package/dist/types/components/AiImportPanel.d.ts +9 -0
  234. package/dist/types/components/AnalysisPreview.d.ts +4 -0
  235. package/dist/types/components/ElevatorPanel.d.ts +18 -0
  236. package/dist/types/components/Icons.d.ts +8 -0
  237. package/dist/types/components/ImportDialog.d.ts +42 -0
  238. package/dist/types/components/Inspector.d.ts +38 -0
  239. package/dist/types/components/NavigatePanel.d.ts +27 -0
  240. package/dist/types/components/NavigationGraph.d.ts +10 -0
  241. package/dist/types/components/StructureExplorer.d.ts +8 -0
  242. package/dist/types/components/StructureView.d.ts +22 -0
  243. package/dist/types/components/controls.d.ts +52 -0
  244. package/dist/types/components/graphLayout.d.ts +9 -0
  245. package/dist/types/editor/host.d.ts +9 -0
  246. package/dist/types/editor/index.d.ts +2 -0
  247. package/dist/types/i18n.d.ts +85 -0
  248. package/dist/types/import/analysisPreview.d.ts +27 -0
  249. package/dist/types/import/brief.d.ts +22 -0
  250. package/dist/types/import/checkpoint.d.ts +49 -0
  251. package/dist/types/import/detect.d.ts +34 -0
  252. package/dist/types/import/documentSvg.d.ts +2 -0
  253. package/dist/types/import/index.d.ts +10 -0
  254. package/dist/types/import/instructions.d.ts +7 -0
  255. package/dist/types/import/openings.d.ts +22 -0
  256. package/dist/types/import/planImport.d.ts +22 -0
  257. package/dist/types/import/register.d.ts +25 -0
  258. package/dist/types/import/runs.d.ts +31 -0
  259. package/dist/types/import/session.d.ts +33 -0
  260. package/dist/types/import/types.d.ts +56 -0
  261. package/dist/types/import/usage.d.ts +10 -0
  262. package/dist/types/import/walls.d.ts +37 -0
  263. package/dist/types/map/CabinMaterials.d.ts +18 -0
  264. package/dist/types/map/CabinMirror.d.ts +13 -0
  265. package/dist/types/map/Doors.d.ts +11 -0
  266. package/dist/types/map/ElevatorCabin.d.ts +20 -0
  267. package/dist/types/map/FixtureLights.d.ts +35 -0
  268. package/dist/types/map/MapCanvas.d.ts +87 -0
  269. package/dist/types/map/Passenger.d.ts +18 -0
  270. package/dist/types/map/SceneLayer.d.ts +306 -0
  271. package/dist/types/map/UndergroundContext.d.ts +18 -0
  272. package/dist/types/map/ambience.d.ts +29 -0
  273. package/dist/types/map/architecture.d.ts +8 -0
  274. package/dist/types/map/bathMusic.d.ts +14 -0
  275. package/dist/types/map/cameraAim.d.ts +20 -0
  276. package/dist/types/map/elevators.d.ts +16 -0
  277. package/dist/types/map/excavation.d.ts +9 -0
  278. package/dist/types/map/exteriors.d.ts +3 -0
  279. package/dist/types/map/features.d.ts +33 -0
  280. package/dist/types/map/fences.d.ts +4 -0
  281. package/dist/types/map/journey.d.ts +36 -0
  282. package/dist/types/map/levels.d.ts +4 -0
  283. package/dist/types/map/lighting.d.ts +103 -0
  284. package/dist/types/map/loadBasemap.d.ts +4 -0
  285. package/dist/types/map/materials.d.ts +38 -0
  286. package/dist/types/map/poolLighting.d.ts +11 -0
  287. package/dist/types/map/projection.d.ts +9 -0
  288. package/dist/types/map/rampGeometry.d.ts +8 -0
  289. package/dist/types/map/route.d.ts +59 -0
  290. package/dist/types/map/stairSurfaces.d.ts +29 -0
  291. package/dist/types/map/surfaces.d.ts +36 -0
  292. package/dist/types/map/textures.d.ts +8 -0
  293. package/dist/types/map/underground.d.ts +30 -0
  294. package/dist/types/map/walk.d.ts +232 -0
  295. package/dist/types/map/walkContext.d.ts +11 -0
  296. package/dist/types/map/walkDimensions.d.ts +5 -0
  297. package/dist/types/map/walkJourney.d.ts +33 -0
  298. package/dist/types/map/walkSurfaces.d.ts +27 -0
  299. package/dist/types/map/wallJoins.d.ts +6 -0
  300. package/dist/types/map/water.d.ts +12 -0
  301. package/dist/types/model/authoring.d.ts +38 -0
  302. package/dist/types/model/boundaries.d.ts +41 -0
  303. package/dist/types/model/coverage.d.ts +8 -0
  304. package/dist/types/model/document.d.ts +9 -0
  305. package/dist/types/model/doors.d.ts +19 -0
  306. package/dist/types/model/factory.d.ts +5 -0
  307. package/dist/types/model/geometry.d.ts +99 -0
  308. package/dist/types/model/history.d.ts +10 -0
  309. package/dist/types/model/host.d.ts +154 -0
  310. package/dist/types/model/imports.d.ts +7 -0
  311. package/dist/types/model/inference.d.ts +46 -0
  312. package/dist/types/model/live.d.ts +60 -0
  313. package/dist/types/model/materials.d.ts +28 -0
  314. package/dist/types/model/mutations.d.ts +178 -0
  315. package/dist/types/model/navigation.d.ts +70 -0
  316. package/dist/types/model/ontology.d.ts +43 -0
  317. package/dist/types/model/passages.d.ts +2 -0
  318. package/dist/types/model/portals.d.ts +9 -0
  319. package/dist/types/model/precision.d.ts +11 -0
  320. package/dist/types/model/project.d.ts +8 -0
  321. package/dist/types/model/ramps.d.ts +7 -0
  322. package/dist/types/model/roomNavigation.d.ts +15 -0
  323. package/dist/types/model/routeClearance.d.ts +11 -0
  324. package/dist/types/model/spaces.d.ts +46 -0
  325. package/dist/types/model/stairGeometry.d.ts +27 -0
  326. package/dist/types/model/stairTraversal.d.ts +8 -0
  327. package/dist/types/model/topology.d.ts +14 -0
  328. package/dist/types/model/types.d.ts +369 -0
  329. package/dist/types/model/validate.d.ts +38 -0
  330. package/dist/types/model/vertical.d.ts +145 -0
  331. package/dist/types/model/walls.d.ts +71 -0
  332. package/dist/types/planner/DrawingDock.d.ts +16 -0
  333. package/dist/types/planner/FloorControls.d.ts +17 -0
  334. package/dist/types/planner/MapSettings.d.ts +16 -0
  335. package/dist/types/planner/PlannerHelp.d.ts +3 -0
  336. package/dist/types/planner/ProjectSidebar.d.ts +32 -0
  337. package/dist/types/planner/floors.d.ts +3 -0
  338. package/dist/types/planner/types.d.ts +3 -0
  339. package/dist/types/planner/useDrawingTools.d.ts +52 -0
  340. package/dist/types/planner/useMapDisplay.d.ts +29 -0
  341. package/dist/types/planner/usePlannerKeyboard.d.ts +18 -0
  342. package/dist/types/planner/useReferenceAlignment.d.ts +30 -0
  343. package/dist/types/schema/index.d.ts +25 -0
  344. package/dist/types/theme/index.d.ts +55 -0
  345. package/package.json +58 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walkContext.js","sources":["../../../../src/map/walkContext.ts"],"sourcesContent":["import { centroid, pointInRing, ringArea } from '../model/geometry';\nimport type { Floor, ProjectDocument, Ring, SiteObject } from '../model/types';\nimport { flights, primaryShafts } from '../model/vertical';\nimport { rampFloors } from '../model/ramps';\n\n/** Build the structural surroundings of both landings before a passenger starts climbing.\n * The selected floor remains the walking datum; these levels only supply visible context. */\nexport function walkConnectionContext(project: ProjectDocument, active: Floor, primary = primaryShafts(project)) {\n const levels = new Map<string, Floor>();\n for (const o of project.objects) {\n const pairs =\n o.kind === 'stairs' && primary.has(o.id)\n ? flights(project, o).map(f => [f.from, f.to])\n : o.slope\n ? [Object.values(rampFloors(project, o))]\n : [];\n for (const pair of pairs) {\n if (!pair.some(f => f?.id === active.id)) continue;\n for (const f of pair) if (f && f.id !== active.id) levels.set(f.id, f);\n }\n }\n return [...levels.values()];\n}\n\n/** Structural storeys seen through an authored atrium. Stops at a plate that closes the opening;\n * room fit-out is deliberately left on its own floor. A room nested in a zone adds no new void. */\nexport function walkVoidContext(project: ProjectDocument, active: Floor, ceilingFloor: Floor) {\n const levels = project.floors\n .filter(f => f.buildingId === active.buildingId)\n .sort((a, b) => a.elevation - b.elevation);\n const areas = new Map<string, SiteObject[]>();\n for (const floor of levels) {\n const own = project.objects.filter(o => o.floorId === floor.id && o.rings?.length && !o.slope);\n const zones = own.filter(o => o.kind === 'zone' && !o.parentId);\n areas.set(floor.id, zones.length ? zones : own.filter(o => o.kind === 'room'));\n }\n const covers = (floor: Floor, ring: Ring) => {\n const at = centroid(ring);\n return (areas.get(floor.id) ?? []).some(\n o => pointInRing(at, o.rings![0]) && !o.rings!.slice(1).some(h => pointInRing(at, h)),\n );\n };\n const holes = (floor: Floor) =>\n (areas.get(floor.id) ?? []).flatMap(o => o.rings!.slice(1)).filter(r => ringArea(r) > 0.01 && !covers(floor, r));\n const above = levels.filter(f => f.elevation >= ceilingFloor.elevation + ceilingFloor.height - 0.01);\n const ceilingOpenings = above.length ? holes(above[0]) : [];\n const ownOpenings = holes(active),\n upper = [...ownOpenings, ...ceilingOpenings];\n const context: Floor[] = [];\n let pending = upper;\n for (const floor of above) {\n if (!pending.length) break;\n context.push(floor);\n pending = pending.filter(r => !covers(floor, r));\n }\n const roof = pending.length ? context.at(-1) : undefined;\n pending = ownOpenings;\n for (const floor of levels.filter(f => f.elevation < active.elevation).reverse()) {\n if (!pending.length) break;\n context.push(floor);\n pending = pending.filter(r => !covers(floor, r));\n }\n return { levels: context, roof, ceilingOpenings };\n}\n"],"names":["walkConnectionContext","project","active","primary","primaryShafts","levels","o","pairs","flights","f","rampFloors","pair","walkVoidContext","ceilingFloor","a","b","areas","floor","own","_a","zones","covers","ring","at","centroid","pointInRing","holes","r","ringArea","above","ceilingOpenings","ownOpenings","upper","context","pending","roof"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ const o = 1.7, t = 0.28, c = 1.25;
2
+ export {
3
+ t as BODY,
4
+ o as EYE,
5
+ c as HEAD_ROOM
6
+ };
7
+ //# sourceMappingURL=walkDimensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walkDimensions.js","sources":["../../../../src/map/walkDimensions.ts"],"sourcesContent":["/** Eye height and shoulder radius in scene metres. Shared by stair rails and planar collision. */\nexport const EYE = 1.7;\nexport const BODY = 0.28;\n/** Walls starting above this band are lintels the walker can pass beneath. */\nexport const HEAD_ROOM = 1.25;\n"],"names":["EYE","BODY","HEAD_ROOM"],"mappings":"AACO,MAAMA,IAAM,KACNC,IAAO,MAEPC,IAAY;"}
@@ -0,0 +1,85 @@
1
+ var j = Object.defineProperty;
2
+ var u = (n, o, t) => o in n ? j(n, o, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[o] = t;
3
+ var g = (n, o, t) => u(n, typeof o != "symbol" ? o + "" : o, t);
4
+ import { distance as I } from "../model/geometry.js";
5
+ import { stairTraversal as v } from "../model/stairTraversal.js";
6
+ import { elevatorLanding as y } from "./elevators.js";
7
+ import { legStepIndices as b } from "./route.js";
8
+ class R {
9
+ constructor(o) {
10
+ g(this, "stopped", !1);
11
+ g(this, "timer");
12
+ g(this, "rejectWait");
13
+ this.host = o;
14
+ }
15
+ stop() {
16
+ var o;
17
+ this.stopped = !0, clearTimeout(this.timer), (o = this.rejectWait) == null || o.call(this, new Error("Route playback stopped."));
18
+ }
19
+ async waitUntil(o) {
20
+ const t = Date.now();
21
+ for (; !o(); ) {
22
+ if (this.stopped) throw new Error("Route playback stopped.");
23
+ if (Date.now() - t > 15e3)
24
+ throw new Error("The elevator has not opened. Route playback paused at the landing.");
25
+ await new Promise((l, d) => {
26
+ this.rejectWait = d, this.timer = setTimeout(l, 40);
27
+ }), this.rejectWait = void 0;
28
+ }
29
+ if (this.stopped) throw new Error("Route playback stopped.");
30
+ }
31
+ object(o) {
32
+ return this.host.project.objects.find((t) => t.id === o.edge.objectId && t.kind === o.edge.kind);
33
+ }
34
+ entry(o) {
35
+ var l;
36
+ const t = this.object(o);
37
+ if (!t) throw new Error("This route needs a connection to its physical lift or stair.");
38
+ return t.kind === "elevator" ? y(t) : ((l = v(this.host.project, t, o.from.floorId, o.to.floorId)) == null ? void 0 : l.points[0]) ?? o.from.position;
39
+ }
40
+ async play(o) {
41
+ var k;
42
+ const t = this.host, l = b(o);
43
+ o.nodes[0] && t.floor() !== o.nodes[0].floorId && await t.changeFloor(o.nodes[0].floorId);
44
+ let d;
45
+ for (let a = 0; a < o.legs.length && !this.stopped; a++) {
46
+ const r = o.legs[a];
47
+ if (t.step(l[a]), r.edge.kind === "elevator") {
48
+ const e = this.object(r), i = t.controls();
49
+ if (!(e != null && e.feedId) || !i) throw new Error("Connect elevator controls to play this lift journey.");
50
+ const s = e.feedId, w = r.from.floorId, p = r.to.floorId, f = y(e);
51
+ I(t.position(), f) > 0.01 && await t.walk([t.position(), f]), t.message(`Calling ${e.name}…`), await this.waitUntil(() => {
52
+ var h;
53
+ return !((h = t.status(s)) != null && h.moving);
54
+ });
55
+ const m = (h) => {
56
+ const c = t.status(s);
57
+ return !!(c != null && c.open) && !c.moving && !c.targetFloorId && c.carFloorId === h;
58
+ };
59
+ m(w) || (i.call(s, w), await this.waitUntil(() => m(w))), t.message(`Entering ${e.name}`), await t.walk([f, e.position], { physical: !0 }), t.ride(e.id), t.message(`Taking ${e.name} to ${((k = t.project.floors.find((h) => h.id === p)) == null ? void 0 : k.name) ?? p}`), i.call(s, p), await this.waitUntil(() => m(p)), t.floor() !== p && await t.changeFloor(p), t.message(`Exiting ${e.name}`), await t.walk([e.position, f], { physical: !0 }), t.ride(null), d = f;
60
+ } else if (r.edge.kind === "stairs") {
61
+ const e = this.object(r), i = e && v(t.project, e, r.from.floorId, r.to.floorId);
62
+ if (!e || !i) throw new Error("This stair route has no matching physical flight.");
63
+ const s = e.feedId ? t.status(e.feedId) : void 0;
64
+ if (i.escalator && (s == null ? void 0 : s.running) !== !1 && ((s == null ? void 0 : s.travel) ?? e.travel ?? "up") === "up" !== i.up)
65
+ throw new Error("The escalator is running the other way. Choose another route.");
66
+ I(t.position(), i.points[0]) > 0.01 && await t.walk([t.position(), i.points[0]]), t.message(`${i.escalator ? "Riding" : "Walking"} ${e.name}`), await t.walk(i.points, {
67
+ physical: !0,
68
+ speed: i.escalator && (s == null ? void 0 : s.running) !== !1 ? i.speed : void 0
69
+ }), t.floor() !== r.to.floorId && await t.changeFloor(r.to.floorId), d = i.points.at(-1);
70
+ } else {
71
+ t.message("");
72
+ const e = [d ?? r.from.position, r.to.position];
73
+ for (d = void 0; a + 1 < o.legs.length && o.legs[a + 1].edge.kind !== "elevator" && o.legs[a + 1].edge.kind !== "stairs" && o.legs[a + 1].from.floorId === r.from.floorId; )
74
+ a++, e.push(o.legs[a].to.position);
75
+ const i = o.legs[a + 1];
76
+ i && (i.edge.kind === "stairs" || i.edge.kind === "elevator") && (e[e.length - 1] = this.entry(i)), await t.walk(e);
77
+ }
78
+ }
79
+ this.stopped || (t.message("Arrived"), t.step(o.steps.length - 1));
80
+ }
81
+ }
82
+ export {
83
+ R as WalkJourney
84
+ };
85
+ //# sourceMappingURL=walkJourney.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walkJourney.js","sources":["../../../../src/map/walkJourney.ts"],"sourcesContent":["import type { ElevatorControls } from '../model/host';\nimport type { StatusReading } from '../model/live';\nimport type { Route, RouteLeg } from '../model/navigation';\nimport type { Point, ProjectDocument } from '../model/types';\nimport { distance } from '../model/geometry';\nimport { stairTraversal } from '../model/stairTraversal';\nimport { elevatorLanding } from './elevators';\nimport { legStepIndices } from './route';\n\nexport interface WalkJourneyHost {\n project: ProjectDocument;\n floor(): string | null;\n changeFloor(id: string | null): Promise<void>;\n position(): Point;\n walk(points: Point[], options?: { speed?: number; physical?: boolean }): Promise<void>;\n controls(): ElevatorControls | undefined;\n status(feedId: string): StatusReading | undefined;\n ride(id: string | null): void;\n message(text: string): void;\n step(index: number): void;\n}\n\n/** Stateful playback uses the same lift commands/readings as a passenger. Vertical legs must not\n * disappear when a route is split into floor runs: that used to teleport straight past the ride. */\nexport class WalkJourney {\n private stopped = false;\n private timer?: ReturnType<typeof setTimeout>;\n private rejectWait?: (error: Error) => void;\n constructor(private host: WalkJourneyHost) {}\n stop() {\n this.stopped = true;\n clearTimeout(this.timer);\n this.rejectWait?.(new Error('Route playback stopped.'));\n }\n private async waitUntil(test: () => boolean) {\n const started = Date.now();\n while (!test()) {\n if (this.stopped) throw new Error('Route playback stopped.');\n if (Date.now() - started > 15_000)\n throw new Error('The elevator has not opened. Route playback paused at the landing.');\n await new Promise<void>((resolve, reject) => {\n this.rejectWait = reject;\n this.timer = setTimeout(resolve, 40);\n });\n this.rejectWait = undefined;\n }\n if (this.stopped) throw new Error('Route playback stopped.');\n }\n private object(leg: RouteLeg) {\n return this.host.project.objects.find(o => o.id === leg.edge.objectId && o.kind === leg.edge.kind);\n }\n private entry(leg: RouteLeg): Point {\n const o = this.object(leg);\n if (!o) throw new Error('This route needs a connection to its physical lift or stair.');\n if (o.kind === 'elevator') return elevatorLanding(o);\n return stairTraversal(this.host.project, o, leg.from.floorId!, leg.to.floorId!)?.points[0] ?? leg.from.position;\n }\n async play(route: Route) {\n const h = this.host,\n steps = legStepIndices(route);\n if (route.nodes[0] && h.floor() !== route.nodes[0].floorId) await h.changeFloor(route.nodes[0].floorId);\n let arrival: Point | undefined;\n for (let i = 0; i < route.legs.length && !this.stopped; i++) {\n const leg = route.legs[i];\n h.step(steps[i]);\n if (leg.edge.kind === 'elevator') {\n const o = this.object(leg),\n controls = h.controls();\n if (!o?.feedId || !controls) throw new Error('Connect elevator controls to play this lift journey.');\n const feed = o.feedId,\n from = leg.from.floorId!,\n to = leg.to.floorId!,\n landing = elevatorLanding(o);\n if (distance(h.position(), landing) > 0.01) await h.walk([h.position(), landing]);\n h.message(`Calling ${o.name}…`);\n await this.waitUntil(() => !h.status(feed)?.moving);\n const openHere = (floor: string) => {\n const s = h.status(feed);\n return !!s?.open && !s.moving && !s.targetFloorId && s.carFloorId === floor;\n };\n if (!openHere(from)) {\n controls.call(feed, from);\n await this.waitUntil(() => openHere(from));\n }\n h.message(`Entering ${o.name}`);\n await h.walk([landing, o.position], { physical: true });\n h.ride(o.id);\n h.message(`Taking ${o.name} to ${h.project.floors.find(f => f.id === to)?.name ?? to}`);\n controls.call(feed, to);\n await this.waitUntil(() => openHere(to));\n if (h.floor() !== to) await h.changeFloor(to);\n h.message(`Exiting ${o.name}`);\n await h.walk([o.position, landing], { physical: true });\n h.ride(null);\n arrival = landing;\n } else if (leg.edge.kind === 'stairs') {\n const o = this.object(leg);\n const path = o && stairTraversal(h.project, o, leg.from.floorId!, leg.to.floorId!);\n if (!o || !path) throw new Error('This stair route has no matching physical flight.');\n const status = o.feedId ? h.status(o.feedId) : undefined;\n if (path.escalator && status?.running !== false && ((status?.travel ?? o.travel ?? 'up') === 'up') !== path.up)\n throw new Error('The escalator is running the other way. Choose another route.');\n if (distance(h.position(), path.points[0]) > 0.01) await h.walk([h.position(), path.points[0]]);\n h.message(`${path.escalator ? 'Riding' : 'Walking'} ${o.name}`);\n await h.walk(path.points, {\n physical: true,\n speed: path.escalator && status?.running !== false ? path.speed : undefined,\n });\n if (h.floor() !== leg.to.floorId) await h.changeFloor(leg.to.floorId);\n arrival = path.points.at(-1)!;\n } else {\n h.message('');\n const points = [arrival ?? leg.from.position, leg.to.position];\n arrival = undefined;\n while (\n i + 1 < route.legs.length &&\n route.legs[i + 1].edge.kind !== 'elevator' &&\n route.legs[i + 1].edge.kind !== 'stairs' &&\n route.legs[i + 1].from.floorId === leg.from.floorId\n ) {\n i++;\n points.push(route.legs[i].to.position);\n }\n const next = route.legs[i + 1];\n if (next && (next.edge.kind === 'stairs' || next.edge.kind === 'elevator'))\n points[points.length - 1] = this.entry(next);\n await h.walk(points);\n }\n }\n if (!this.stopped) {\n h.message('Arrived');\n h.step(route.steps.length - 1);\n }\n }\n}\n"],"names":["WalkJourney","host","__publicField","_a","test","started","resolve","reject","leg","o","elevatorLanding","stairTraversal","route","h","steps","legStepIndices","arrival","i","controls","feed","from","to","landing","distance","openHere","floor","s","f","path","status","points","next"],"mappings":";;;;;;;AAwBO,MAAMA,EAAY;AAAA,EAIvB,YAAoBC,GAAuB;AAHnC,IAAAC,EAAA,iBAAU;AACV,IAAAA,EAAA;AACA,IAAAA,EAAA;AACY,SAAA,OAAAD;AAAA,EAAwB;AAAA,EAC5C,OAAO;;AACL,SAAK,UAAU,IACf,aAAa,KAAK,KAAK,IACvBE,IAAA,KAAK,eAAL,QAAAA,EAAA,WAAkB,IAAI,MAAM,yBAAyB;AAAA,EACvD;AAAA,EACA,MAAc,UAAUC,GAAqB;AAC3C,UAAMC,IAAU,KAAK,IAAA;AACrB,WAAO,CAACD,OAAQ;AACd,UAAI,KAAK,QAAS,OAAM,IAAI,MAAM,yBAAyB;AAC3D,UAAI,KAAK,QAAQC,IAAU;AACzB,cAAM,IAAI,MAAM,oEAAoE;AACtF,YAAM,IAAI,QAAc,CAACC,GAASC,MAAW;AAC3C,aAAK,aAAaA,GAClB,KAAK,QAAQ,WAAWD,GAAS,EAAE;AAAA,MACrC,CAAC,GACD,KAAK,aAAa;AAAA,IACpB;AACA,QAAI,KAAK,QAAS,OAAM,IAAI,MAAM,yBAAyB;AAAA,EAC7D;AAAA,EACQ,OAAOE,GAAe;AAC5B,WAAO,KAAK,KAAK,QAAQ,QAAQ,KAAK,CAAAC,MAAKA,EAAE,OAAOD,EAAI,KAAK,YAAYC,EAAE,SAASD,EAAI,KAAK,IAAI;AAAA,EACnG;AAAA,EACQ,MAAMA,GAAsB;;AAClC,UAAMC,IAAI,KAAK,OAAOD,CAAG;AACzB,QAAI,CAACC,EAAG,OAAM,IAAI,MAAM,8DAA8D;AACtF,WAAIA,EAAE,SAAS,aAAmBC,EAAgBD,CAAC,MAC5CN,IAAAQ,EAAe,KAAK,KAAK,SAASF,GAAGD,EAAI,KAAK,SAAUA,EAAI,GAAG,OAAQ,MAAvE,gBAAAL,EAA0E,OAAO,OAAMK,EAAI,KAAK;AAAA,EACzG;AAAA,EACA,MAAM,KAAKI,GAAc;;AACvB,UAAMC,IAAI,KAAK,MACbC,IAAQC,EAAeH,CAAK;AAC9B,IAAIA,EAAM,MAAM,CAAC,KAAKC,EAAE,MAAA,MAAYD,EAAM,MAAM,CAAC,EAAE,iBAAeC,EAAE,YAAYD,EAAM,MAAM,CAAC,EAAE,OAAO;AACtG,QAAII;AACJ,aAASC,IAAI,GAAGA,IAAIL,EAAM,KAAK,UAAU,CAAC,KAAK,SAASK,KAAK;AAC3D,YAAMT,IAAMI,EAAM,KAAKK,CAAC;AAExB,UADAJ,EAAE,KAAKC,EAAMG,CAAC,CAAC,GACXT,EAAI,KAAK,SAAS,YAAY;AAChC,cAAMC,IAAI,KAAK,OAAOD,CAAG,GACvBU,IAAWL,EAAE,SAAA;AACf,YAAI,EAACJ,KAAA,QAAAA,EAAG,WAAU,CAACS,EAAU,OAAM,IAAI,MAAM,sDAAsD;AACnG,cAAMC,IAAOV,EAAE,QACbW,IAAOZ,EAAI,KAAK,SAChBa,IAAKb,EAAI,GAAG,SACZc,IAAUZ,EAAgBD,CAAC;AAC7B,QAAIc,EAASV,EAAE,SAAA,GAAYS,CAAO,IAAI,QAAM,MAAMT,EAAE,KAAK,CAACA,EAAE,SAAA,GAAYS,CAAO,CAAC,GAChFT,EAAE,QAAQ,WAAWJ,EAAE,IAAI,GAAG,GAC9B,MAAM,KAAK,UAAU,MAAA;;AAAM,oBAACN,IAAAU,EAAE,OAAOM,CAAI,MAAb,QAAAhB,EAAgB;AAAA,SAAM;AAClD,cAAMqB,IAAW,CAACC,MAAkB;AAClC,gBAAMC,IAAIb,EAAE,OAAOM,CAAI;AACvB,iBAAO,CAAC,EAACO,KAAA,QAAAA,EAAG,SAAQ,CAACA,EAAE,UAAU,CAACA,EAAE,iBAAiBA,EAAE,eAAeD;AAAA,QACxE;AACA,QAAKD,EAASJ,CAAI,MAChBF,EAAS,KAAKC,GAAMC,CAAI,GACxB,MAAM,KAAK,UAAU,MAAMI,EAASJ,CAAI,CAAC,IAE3CP,EAAE,QAAQ,YAAYJ,EAAE,IAAI,EAAE,GAC9B,MAAMI,EAAE,KAAK,CAACS,GAASb,EAAE,QAAQ,GAAG,EAAE,UAAU,IAAM,GACtDI,EAAE,KAAKJ,EAAE,EAAE,GACXI,EAAE,QAAQ,UAAUJ,EAAE,IAAI,SAAON,IAAAU,EAAE,QAAQ,OAAO,KAAK,CAAAc,MAAKA,EAAE,OAAON,CAAE,MAAtC,gBAAAlB,EAAyC,SAAQkB,CAAE,EAAE,GACtFH,EAAS,KAAKC,GAAME,CAAE,GACtB,MAAM,KAAK,UAAU,MAAMG,EAASH,CAAE,CAAC,GACnCR,EAAE,YAAYQ,KAAI,MAAMR,EAAE,YAAYQ,CAAE,GAC5CR,EAAE,QAAQ,WAAWJ,EAAE,IAAI,EAAE,GAC7B,MAAMI,EAAE,KAAK,CAACJ,EAAE,UAAUa,CAAO,GAAG,EAAE,UAAU,IAAM,GACtDT,EAAE,KAAK,IAAI,GACXG,IAAUM;AAAA,MACZ,WAAWd,EAAI,KAAK,SAAS,UAAU;AACrC,cAAMC,IAAI,KAAK,OAAOD,CAAG,GACnBoB,IAAOnB,KAAKE,EAAeE,EAAE,SAASJ,GAAGD,EAAI,KAAK,SAAUA,EAAI,GAAG,OAAQ;AACjF,YAAI,CAACC,KAAK,CAACmB,EAAM,OAAM,IAAI,MAAM,mDAAmD;AACpF,cAAMC,IAASpB,EAAE,SAASI,EAAE,OAAOJ,EAAE,MAAM,IAAI;AAC/C,YAAImB,EAAK,cAAaC,KAAA,gBAAAA,EAAQ,aAAY,QAAWA,KAAA,gBAAAA,EAAQ,WAAUpB,EAAE,UAAU,UAAU,SAAUmB,EAAK;AAC1G,gBAAM,IAAI,MAAM,+DAA+D;AACjF,QAAIL,EAASV,EAAE,SAAA,GAAYe,EAAK,OAAO,CAAC,CAAC,IAAI,cAAYf,EAAE,KAAK,CAACA,EAAE,SAAA,GAAYe,EAAK,OAAO,CAAC,CAAC,CAAC,GAC9Ff,EAAE,QAAQ,GAAGe,EAAK,YAAY,WAAW,SAAS,IAAInB,EAAE,IAAI,EAAE,GAC9D,MAAMI,EAAE,KAAKe,EAAK,QAAQ;AAAA,UACxB,UAAU;AAAA,UACV,OAAOA,EAAK,cAAaC,KAAA,gBAAAA,EAAQ,aAAY,KAAQD,EAAK,QAAQ;AAAA,QAAA,CACnE,GACGf,EAAE,YAAYL,EAAI,GAAG,WAAS,MAAMK,EAAE,YAAYL,EAAI,GAAG,OAAO,GACpEQ,IAAUY,EAAK,OAAO,GAAG,EAAE;AAAA,MAC7B,OAAO;AACL,QAAAf,EAAE,QAAQ,EAAE;AACZ,cAAMiB,IAAS,CAACd,KAAWR,EAAI,KAAK,UAAUA,EAAI,GAAG,QAAQ;AAE7D,aADAQ,IAAU,QAERC,IAAI,IAAIL,EAAM,KAAK,UACnBA,EAAM,KAAKK,IAAI,CAAC,EAAE,KAAK,SAAS,cAChCL,EAAM,KAAKK,IAAI,CAAC,EAAE,KAAK,SAAS,YAChCL,EAAM,KAAKK,IAAI,CAAC,EAAE,KAAK,YAAYT,EAAI,KAAK;AAE5C,UAAAS,KACAa,EAAO,KAAKlB,EAAM,KAAKK,CAAC,EAAE,GAAG,QAAQ;AAEvC,cAAMc,IAAOnB,EAAM,KAAKK,IAAI,CAAC;AAC7B,QAAIc,MAASA,EAAK,KAAK,SAAS,YAAYA,EAAK,KAAK,SAAS,gBAC7DD,EAAOA,EAAO,SAAS,CAAC,IAAI,KAAK,MAAMC,CAAI,IAC7C,MAAMlB,EAAE,KAAKiB,CAAM;AAAA,MACrB;AAAA,IACF;AACA,IAAK,KAAK,YACRjB,EAAE,QAAQ,SAAS,GACnBA,EAAE,KAAKD,EAAM,MAAM,SAAS,CAAC;AAAA,EAEjC;AACF;"}
@@ -0,0 +1,106 @@
1
+ import { objectArea as w, pointInRing as m, slopeElevation as M } from "../model/geometry.js";
2
+ import { rampFloors as k } from "../model/ramps.js";
3
+ import { spacePoint as H } from "../model/spaces.js";
4
+ import { wallPieces as A } from "./features.js";
5
+ import { wallFootprints as R } from "./wallJoins.js";
6
+ import { HEAD_ROOM as S } from "./walkDimensions.js";
7
+ const I = (o, l) => {
8
+ var i;
9
+ return !!((i = o.rings) != null && i.length) && m(l, o.rings[0]) && !o.rings.slice(1).some((g) => m(l, g));
10
+ };
11
+ function C(o, l) {
12
+ const i = o.floors.find((e) => e.id === l), g = new Map(o.floors.map((e) => [e.id, e])), a = (i == null ? void 0 : i.elevation) ?? 0;
13
+ return A(o, null, !0).filter((e) => {
14
+ const s = g.get(e.floorId ?? "");
15
+ return (i ? (s == null ? void 0 : s.buildingId) === i.buildingId || e.floorId === null && i.elevation >= 0 : e.floorId === null) && e.base < a + S && e.height > a + 0.01;
16
+ }).map((e) => e.ring);
17
+ }
18
+ function D(o) {
19
+ var b;
20
+ const l = new Map(o.floors.map((n) => [n.id, n])), i = /* @__PURE__ */ new Map(), g = o.objects.filter((n) => {
21
+ var t;
22
+ return n.slope && ((t = n.rings) == null ? void 0 : t.length);
23
+ }), a = new Map(
24
+ o.floors.map((n) => [n.id, g.filter((t) => {
25
+ var r;
26
+ return t.floorId === n.id && ((r = k(o, t).high) == null ? void 0 : r.id) === n.id;
27
+ })])
28
+ );
29
+ for (const n of o.objects) {
30
+ if (n.kind !== "room" && n.kind !== "zone" || !((b = n.rings) != null && b.length)) continue;
31
+ const t = i.get(n.floorId) ?? [];
32
+ t.push(n), i.set(n.floorId, t);
33
+ }
34
+ const e = /* @__PURE__ */ new Map(), s = R(o);
35
+ for (const n of o.barriers) {
36
+ const t = s.get(n.id);
37
+ if (!t) continue;
38
+ const r = e.get(n.floorId) ?? [];
39
+ r.push(t), e.set(n.floorId, r);
40
+ }
41
+ const d = (n, t) => (i.get(t) ?? []).some((r) => I(r, n)) || (e.get(t) ?? []).some((r) => m(n, r)), f = (n, t) => (i.get(t) ?? []).some((r) => !r.slope && r.rings.slice(1).some((c) => m(n, c))), u = new Map(
42
+ o.floors.map((n) => [
43
+ n.id,
44
+ o.floors.filter((t) => t.buildingId === n.buildingId && t.elevation < n.elevation - 0.01).sort((t, r) => r.elevation - t.elevation)
45
+ ])
46
+ ), v = (n, t) => {
47
+ var p;
48
+ const r = l.get(t ?? "");
49
+ if (!r || d(n, t) || !f(n, t)) return;
50
+ const c = (p = u.get(r.id)) == null ? void 0 : p.find((h) => d(n, h.id));
51
+ return c ? { floorId: c.id, distance: r.elevation - c.elevation } : void 0;
52
+ };
53
+ return { supports: (n, t) => {
54
+ var c, p;
55
+ const r = l.get(t ?? "");
56
+ return r && ((c = a.get(r.id)) != null && c.some((h) => m(n, h.rings[0]) && M(h.slope, n) < r.elevation - 0.05)) ? !1 : !r || !((p = i.get(t)) != null && p.length) || d(n, t) ? !0 : f(n, t) ? !!v(n, t) : Math.abs(r.elevation) < 0.01;
57
+ }, dropAt: v };
58
+ }
59
+ function N(o, l, i) {
60
+ if (i(l)) return l;
61
+ if (!i(o)) return o;
62
+ let g = 0, a = 1;
63
+ for (let e = 0; e < 16; e++) {
64
+ const s = (g + a) / 2, d = [o[0] + (l[0] - o[0]) * s, o[1] + (l[1] - o[1]) * s];
65
+ i(d) ? g = s : a = s;
66
+ }
67
+ return [o[0] + (l[0] - o[0]) * g, o[1] + (l[1] - o[1]) * g];
68
+ }
69
+ function W(o, l, i) {
70
+ var d;
71
+ if (!l) return i;
72
+ const g = o.objects.filter((f) => f.floorId === l && (f.kind === "room" || f.kind === "zone"));
73
+ if (g.some((f) => I(f, i))) return i;
74
+ const a = o.objects.filter((f) => f.floorId === l && f.water), e = (f) => g.some((u) => I(u, f)) && !a.some((u) => I(u, f)), s = (d = o.navNodes) == null ? void 0 : d.find((f) => f.floorId === l && e(f.position));
75
+ if (s) return s.position;
76
+ for (const f of g.sort((u, v) => w(v) - w(u))) {
77
+ const u = H(o, f);
78
+ if (e(u)) return u;
79
+ }
80
+ return i;
81
+ }
82
+ function _(o, l) {
83
+ const i = o.floors.find((e) => e.id === l);
84
+ if (!i) return [];
85
+ const g = new Map(o.floors.map((e) => [e.id, e])), a = o.objects.filter((e) => {
86
+ var d;
87
+ const s = g.get(e.floorId ?? "");
88
+ return (s == null ? void 0 : s.buildingId) === i.buildingId && ((d = e.rings) == null ? void 0 : d.length) && e.ceilingHeight && e.ceilingHeight > s.height + 0.01 && s.elevation <= i.elevation && s.elevation + e.ceilingHeight > i.elevation;
89
+ });
90
+ return o.objects.filter((e) => {
91
+ if (e.floorId === l) return !1;
92
+ const s = g.get(e.floorId ?? "");
93
+ return (s == null ? void 0 : s.buildingId) === i.buildingId && a.some((d) => {
94
+ const f = g.get(d.floorId).elevation;
95
+ return s.elevation >= f && s.elevation < f + d.ceilingHeight && m(e.position, d.rings[0]);
96
+ });
97
+ });
98
+ }
99
+ export {
100
+ W as floorArrival,
101
+ D as floorSupport,
102
+ N as supportedStep,
103
+ _ as tallSpaceContext,
104
+ C as walkingWallRings
105
+ };
106
+ //# sourceMappingURL=walkSurfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walkSurfaces.js","sources":["../../../../src/map/walkSurfaces.ts"],"sourcesContent":["import type { Point, ProjectDocument, Ring, SiteObject } from '../model/types';\nimport { objectArea, pointInRing, slopeElevation } from '../model/geometry';\nimport { rampFloors } from '../model/ramps';\nimport { spacePoint } from '../model/spaces';\nimport { wallPieces } from './features';\nimport { wallFootprints } from './wallJoins';\nimport { HEAD_ROOM } from './walkDimensions';\n\nconst contains = (o: SiteObject, at: Point) =>\n !!o.rings?.length && pointInRing(at, o.rings[0]) && !o.rings.slice(1).some(hole => pointInRing(at, hole));\n\n/** Evaluate wall solids in absolute building heights. A host wall may continue through a\n * mezzanine, while a low partition below it and a lintel overhead do not obstruct the walker. */\nexport function walkingWallRings(project: ProjectDocument, floorId: string | null): Ring[] {\n const active = project.floors.find(f => f.id === floorId);\n const floors = new Map(project.floors.map(f => [f.id, f]));\n const foot = active?.elevation ?? 0;\n return wallPieces(project, null, true)\n .filter(piece => {\n const floor = floors.get(piece.floorId ?? '');\n const relevant = active\n ? floor?.buildingId === active.buildingId || (piece.floorId === null && active.elevation >= 0)\n : piece.floorId === null;\n return relevant && piece.base < foot + HEAD_ROOM && piece.height > foot + 0.01;\n })\n .map(piece => piece.ring);\n}\n\n/** Build the support lookup when geometry changes, not for every walking frame. Open-air grade\n * and floors without authored plates stay traversable. Higher and buried plates have real edges. */\nexport function floorSupport(project: ProjectDocument) {\n const floors = new Map(project.floors.map(f => [f.id, f]));\n const areas = new Map<string | null, SiteObject[]>();\n const ramps = project.objects.filter(o => o.slope && o.rings?.length);\n const openings = new Map(\n project.floors.map(f => [f.id, ramps.filter(o => o.floorId === f.id && rampFloors(project, o).high?.id === f.id)]),\n );\n for (const o of project.objects) {\n if ((o.kind !== 'room' && o.kind !== 'zone') || !o.rings?.length) continue;\n const own = areas.get(o.floorId) ?? [];\n own.push(o);\n areas.set(o.floorId, own);\n }\n // Rooms may end at the inner wall face. The slab beneath a wall also supports its doorway;\n // otherwise a valid room-to-room threshold would look like a narrow unsupported gap.\n const thresholds = new Map<string | null, Ring[]>();\n const footprints = wallFootprints(project);\n for (const wall of project.barriers) {\n const ring = footprints.get(wall.id);\n if (!ring) continue;\n const own = thresholds.get(wall.floorId) ?? [];\n own.push(ring);\n thresholds.set(wall.floorId, own);\n }\n const solid = (at: Point, id: string | null) =>\n (areas.get(id) ?? []).some(o => contains(o, at)) || (thresholds.get(id) ?? []).some(ring => pointInRing(at, ring));\n const hole = (at: Point, id: string | null) =>\n (areas.get(id) ?? []).some(o => !o.slope && o.rings!.slice(1).some(ring => pointInRing(at, ring)));\n const lower = new Map(\n project.floors.map(f => [\n f.id,\n project.floors\n .filter(b => b.buildingId === f.buildingId && b.elevation < f.elevation - 0.01)\n .sort((a, b) => b.elevation - a.elevation),\n ]),\n );\n const dropAt = (at: Point, id: string | null) => {\n const floor = floors.get(id ?? '');\n if (!floor || solid(at, id) || !hole(at, id)) return;\n const below = lower.get(floor.id)?.find(f => solid(at, f.id));\n return below ? { floorId: below.id, distance: floor.elevation - below.elevation } : undefined;\n };\n const supports = (at: Point, id: string | null): boolean => {\n const floor = floors.get(id ?? '');\n // The ramp walker supplies support on the incline. The deck must not supply an invisible\n // horizontal floor above it when someone tries to enter the opening from the side.\n if (\n floor &&\n openings\n .get(floor.id)\n ?.some(o => pointInRing(at, o.rings![0]) && slopeElevation(o.slope!, at) < floor.elevation - 0.05)\n )\n return false;\n if (!floor || !areas.get(id)?.length || solid(at, id)) return true;\n if (hole(at, id)) return !!dropAt(at, id);\n // An exposed gallery edge is not an authored opening. At grade, the surrounding ground\n // supports an exit from the building; in the air or an excavation, keep the last supported point.\n return Math.abs(floor.elevation) < 0.01;\n };\n return { supports, dropAt };\n}\n\n/** Clip a small walking step at the last supported point; never snap it onto another island. */\nexport function supportedStep(from: Point, to: Point, supports: (at: Point) => boolean): Point {\n if (supports(to)) return to;\n if (!supports(from)) return from;\n let low = 0,\n high = 1;\n for (let i = 0; i < 16; i++) {\n const t = (low + high) / 2;\n const at: Point = [from[0] + (to[0] - from[0]) * t, from[1] + (to[1] - from[1]) * t];\n if (supports(at)) low = t;\n else high = t;\n }\n return [from[0] + (to[0] - from[0]) * low, from[1] + (to[1] - from[1]) * low];\n}\n\n/** Keep a valid position across storeys, including a fall's landing. A floor picker can instead\n * select a smaller footprint: enter it at a dry navigation point, not in the void outside it. */\nexport function floorArrival(project: ProjectDocument, floorId: string | null, at: Point): Point {\n if (!floorId) return at;\n const rooms = project.objects.filter(o => o.floorId === floorId && (o.kind === 'room' || o.kind === 'zone'));\n if (rooms.some(o => contains(o, at))) return at;\n const pools = project.objects.filter(o => o.floorId === floorId && o.water);\n const dry = (p: Point) => rooms.some(o => contains(o, p)) && !pools.some(o => contains(o, p));\n const node = project.navNodes?.find(n => n.floorId === floorId && dry(n.position));\n if (node) return node.position;\n for (const room of rooms.sort((a, b) => objectArea(b) - objectArea(a))) {\n const point = spacePoint(project, room);\n if (dry(point)) return point;\n }\n return at;\n}\n\n/** Only authored openings trigger a drop. Gaps outside the building, an undrawn floor, and\n * overlapping areas that still provide a slab are not floor holes. */\nexport function floorDropAt(project: ProjectDocument, floorId: string | null, at: Point) {\n return floorSupport(project).dropAt(at, floorId);\n}\n\n/** Rooms which span the active storey bring the geometry inside their footprint along with them.\n * The same context supplies the lower plate seen through an opening and the gallery seen from below. */\nexport function tallSpaceContext(project: ProjectDocument, floorId: string | null): SiteObject[] {\n const active = project.floors.find(f => f.id === floorId);\n if (!active) return [];\n const floors = new Map(project.floors.map(f => [f.id, f]));\n const halls = project.objects.filter(o => {\n const floor = floors.get(o.floorId ?? '');\n return (\n floor?.buildingId === active.buildingId &&\n o.rings?.length &&\n o.ceilingHeight &&\n o.ceilingHeight > floor.height + 0.01 &&\n floor.elevation <= active.elevation &&\n floor.elevation + o.ceilingHeight > active.elevation\n );\n });\n return project.objects.filter(o => {\n if (o.floorId === floorId) return false;\n const floor = floors.get(o.floorId ?? '');\n return (\n floor?.buildingId === active.buildingId &&\n halls.some(hall => {\n const base = floors.get(hall.floorId!)!.elevation;\n return (\n floor.elevation >= base &&\n floor.elevation < base + hall.ceilingHeight! &&\n pointInRing(o.position, hall.rings![0])\n );\n })\n );\n });\n}\n"],"names":["contains","at","_a","pointInRing","hole","walkingWallRings","project","floorId","active","f","floors","foot","wallPieces","piece","floor","HEAD_ROOM","floorSupport","areas","ramps","o","openings","rampFloors","own","thresholds","footprints","wallFootprints","wall","ring","solid","id","lower","b","a","dropAt","below","slopeElevation","_b","supportedStep","from","to","supports","low","high","i","t","floorArrival","rooms","pools","dry","p","node","n","room","objectArea","point","spacePoint","tallSpaceContext","halls","hall","base"],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { distance as k } from "../model/geometry.js";
2
+ const I = (s, c) => s[0] * c[1] - s[1] * c[0], p = (s, c) => [
3
+ -s.direction[1] * s.wall.thickness * c / 2,
4
+ s.direction[0] * s.wall.thickness * c / 2
5
+ ];
6
+ function F(s) {
7
+ const c = new Map(s.junctions.map((n) => [n.id, n.position])), d = /* @__PURE__ */ new Map();
8
+ for (const n of s.barriers)
9
+ for (const [o, a] of [
10
+ [n.startId, n.endId],
11
+ [n.endId, n.startId]
12
+ ]) {
13
+ const i = c.get(o), f = c.get(a);
14
+ if (!i || !f) continue;
15
+ const t = k(i, f);
16
+ if (t < 1e-9) continue;
17
+ const e = [(f[0] - i[0]) / t, (f[1] - i[1]) / t], l = d.get(o) ?? [];
18
+ l.push({ wall: n, direction: e, length: t, angle: Math.atan2(e[1], e[0]) }), d.set(o, l);
19
+ }
20
+ const g = /* @__PURE__ */ new Map();
21
+ for (const [n, o] of d) {
22
+ o.sort((t, e) => t.angle - e.angle);
23
+ const a = c.get(n), i = (t) => [a[0] + t[0], a[1] + t[1]], f = o.map((t, e) => {
24
+ const l = o[(e + 1) % o.length], r = p(t, 1), h = p(l, -1), u = I(t.direction, l.direction), m = [(r[0] + h[0]) / 2, (r[1] + h[1]) / 2];
25
+ if (Math.abs(u) < 1e-6) return i(m);
26
+ const M = I([h[0] - r[0], h[1] - r[1]], l.direction) / u, b = [r[0] + t.direction[0] * M, r[1] + t.direction[1] * M], $ = Math.min(
27
+ 4 * Math.max(t.wall.thickness, l.wall.thickness),
28
+ Math.min(t.length, l.length) * 0.45
29
+ );
30
+ return i(Math.hypot(...b) <= $ ? b : m);
31
+ });
32
+ for (let t = 0; t < o.length; t++) {
33
+ const e = o[t];
34
+ g.set(
35
+ `${n}:${e.wall.id}`,
36
+ o.length === 1 ? { left: i(p(e, 1)), right: i(p(e, -1)) } : { left: f[t], right: f[(t + o.length - 1) % o.length] }
37
+ );
38
+ }
39
+ }
40
+ const w = /* @__PURE__ */ new Map();
41
+ for (const n of s.barriers) {
42
+ const o = g.get(`${n.startId}:${n.id}`), a = g.get(`${n.endId}:${n.id}`);
43
+ o && a && w.set(n.id, [o.right, a.left, a.right, o.left]);
44
+ }
45
+ return w;
46
+ }
47
+ function S(s, c, d, g, w) {
48
+ let n = s;
49
+ for (const [o, a] of [
50
+ [g, 1],
51
+ [w, -1]
52
+ ]) {
53
+ if (o === null) continue;
54
+ const i = [], f = (t) => ((t[0] - c[0]) * d[0] + (t[1] - c[1]) * d[1] - o) * a;
55
+ for (let t = 0; t < n.length; t++) {
56
+ const e = n[t], l = n[(t + 1) % n.length], r = f(e), h = f(l);
57
+ if (r >= 0 && i.push(e), r >= 0 != h >= 0) {
58
+ const u = r / (r - h);
59
+ i.push([e[0] + (l[0] - e[0]) * u, e[1] + (l[1] - e[1]) * u]);
60
+ }
61
+ }
62
+ n = i;
63
+ }
64
+ return n;
65
+ }
66
+ export {
67
+ F as wallFootprints,
68
+ S as wallSpan
69
+ };
70
+ //# sourceMappingURL=wallJoins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallJoins.js","sources":["../../../../src/map/wallJoins.ts"],"sourcesContent":["import { distance } from '../model/geometry';\nimport type { Barrier, Point, ProjectDocument, Ring } from '../model/types';\n\ntype Ray = { wall: Barrier; direction: Point; length: number; angle: number };\nconst cross = (a: Point, b: Point) => a[0] * b[1] - a[1] * b[0];\nconst offset = (ray: Ray, side: number): Point => [\n (-ray.direction[1] * ray.wall.thickness * side) / 2,\n (ray.direction[0] * ray.wall.thickness * side) / 2,\n];\n\n/** Adjacent wall faces intersect once at each junction. Both walls use that same corner, so\n * oblique L, T and four-way joins close without rectangular end caps protruding through them. */\nexport function wallFootprints(project: ProjectDocument): Map<string, Ring> {\n const positions = new Map(project.junctions.map(j => [j.id, j.position]));\n const meeting = new Map<string, Ray[]>();\n for (const wall of project.barriers) {\n for (const [from, to] of [\n [wall.startId, wall.endId],\n [wall.endId, wall.startId],\n ]) {\n const a = positions.get(from),\n b = positions.get(to);\n if (!a || !b) continue;\n const length = distance(a, b);\n if (length < 1e-9) continue;\n const direction: Point = [(b[0] - a[0]) / length, (b[1] - a[1]) / length];\n const rays = meeting.get(from) ?? [];\n rays.push({ wall, direction, length, angle: Math.atan2(direction[1], direction[0]) });\n meeting.set(from, rays);\n }\n }\n const ends = new Map<string, { left: Point; right: Point }>();\n for (const [id, rays] of meeting) {\n rays.sort((a, b) => a.angle - b.angle);\n const origin = positions.get(id)!;\n const absolute = (p: Point): Point => [origin[0] + p[0], origin[1] + p[1]];\n // Each wedge has the left face of one ray and the right face of the next ray.\n const corners = rays.map((ray, i): Point => {\n const next = rays[(i + 1) % rays.length];\n const a = offset(ray, 1),\n b = offset(next, -1);\n const denominator = cross(ray.direction, next.direction);\n const halfway: Point = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];\n if (Math.abs(denominator) < 1e-6) return absolute(halfway);\n const t = cross([b[0] - a[0], b[1] - a[1]], next.direction) / denominator;\n const at: Point = [a[0] + ray.direction[0] * t, a[1] + ray.direction[1] * t];\n // Bound mitres at nearly parallel branches and on tiny returns. A shared bevel point\n // prevents spikes and inverted pieces while preserving one join for both participants.\n const limit = Math.min(\n 4 * Math.max(ray.wall.thickness, next.wall.thickness),\n Math.min(ray.length, next.length) * 0.45,\n );\n return absolute(Math.hypot(...at) <= limit ? at : halfway);\n });\n for (let i = 0; i < rays.length; i++) {\n const ray = rays[i];\n ends.set(\n `${id}:${ray.wall.id}`,\n rays.length === 1\n ? { left: absolute(offset(ray, 1)), right: absolute(offset(ray, -1)) }\n : { left: corners[i], right: corners[(i + rays.length - 1) % rays.length] },\n );\n }\n }\n const result = new Map<string, Ring>();\n for (const wall of project.barriers) {\n const a = ends.get(`${wall.startId}:${wall.id}`),\n b = ends.get(`${wall.endId}:${wall.id}`);\n if (a && b) result.set(wall.id, [a.right, b.left, b.right, a.left]);\n }\n return result;\n}\n\n/** Cut an opening across the joined footprint, retaining the mitres on the wall's outside ends. */\nexport function wallSpan(ring: Ring, origin: Point, direction: Point, start: number | null, end: number | null): Ring {\n let result = ring;\n for (const [bound, sign] of [\n [start, 1],\n [end, -1],\n ] as const) {\n if (bound === null) continue;\n const next: Ring = [];\n const signed = (p: Point) => ((p[0] - origin[0]) * direction[0] + (p[1] - origin[1]) * direction[1] - bound) * sign;\n for (let i = 0; i < result.length; i++) {\n const a = result[i],\n b = result[(i + 1) % result.length],\n da = signed(a),\n db = signed(b);\n if (da >= 0) next.push(a);\n if (da >= 0 !== db >= 0) {\n const t = da / (da - db);\n next.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);\n }\n }\n result = next;\n }\n return result;\n}\n"],"names":["cross","a","b","offset","ray","side","wallFootprints","project","positions","j","meeting","wall","from","to","length","distance","direction","rays","ends","id","origin","absolute","p","corners","i","next","denominator","halfway","t","at","limit","result","wallSpan","ring","start","end","bound","sign","signed","da","db"],"mappings":"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"}
@@ -0,0 +1,274 @@
1
+ import * as e from "three";
2
+ import { closeRing as E, rotate as _ } from "../model/geometry.js";
3
+ import { metricUVs as T } from "./surfaces.js";
4
+ import { poolBounces as B } from "./poolLighting.js";
5
+ const A = `
6
+ uniform float poolTime;
7
+ uniform float poolRipple;
8
+ varying vec2 poolXY;
9
+ vec3 poolWave(vec2 p) {
10
+ vec3 wave = vec3(0.0);
11
+ for (int i = 0; i < 4; i++) {
12
+ float n = float(i);
13
+ float angle = n * 2.399 + 0.35;
14
+ vec2 direction = vec2(cos(angle), sin(angle));
15
+ float k = 2.6 + n * 2.3;
16
+ float amplitude = poolRipple / (1.0 + n * 1.6);
17
+ float phase = k * dot(direction, p) - sqrt(9.81 * k) * poolTime * 0.45;
18
+ wave.x += amplitude * sin(phase);
19
+ wave.yz += amplitude * k * cos(phase) * direction;
20
+ }
21
+ return wave;
22
+ }
23
+ `;
24
+ function D(d) {
25
+ return `
26
+ uniform vec4 poolEdges[${d}];
27
+ uniform float poolRefractionGuard;
28
+ float poolClearance(vec2 p) {
29
+ bool inside = false;
30
+ float clearance = 1e6;
31
+ for (int i = 0; i < ${d}; i++) {
32
+ vec2 a = poolEdges[i].xy, b = poolEdges[i].zw, edge = b-a;
33
+ float t = clamp(dot(p-a, edge) / max(dot(edge, edge), 1e-8), 0.0, 1.0);
34
+ clearance = min(clearance, length(p-a-edge*t));
35
+ if ((a.y > p.y) != (b.y > p.y)) {
36
+ float x = a.x + (p.y-a.y) * edge.x / edge.y;
37
+ if (p.x < x) inside = !inside;
38
+ }
39
+ }
40
+ return inside ? clearance : -clearance;
41
+ }
42
+ `;
43
+ }
44
+ function F(d, y) {
45
+ const h = new e.ShapeGeometry(d).toNonIndexed(), w = h.getAttribute("position"), g = [], b = (s, n, r, o) => {
46
+ if (o < 6 && Math.max(s.distanceToSquared(n), n.distanceToSquared(r), r.distanceToSquared(s)) > 2.25) {
47
+ const l = s.clone().add(n).multiplyScalar(0.5), a = n.clone().add(r).multiplyScalar(0.5), f = r.clone().add(s).multiplyScalar(0.5);
48
+ b(s, l, f, o + 1), b(l, n, a, o + 1), b(f, a, r, o + 1), b(l, a, f, o + 1);
49
+ } else for (const l of [s, n, r]) g.push(l.x, l.y, y);
50
+ };
51
+ for (let s = 0; s < w.count; s += 3)
52
+ b(
53
+ new e.Vector3().fromBufferAttribute(w, s),
54
+ new e.Vector3().fromBufferAttribute(w, s + 1),
55
+ new e.Vector3().fromBufferAttribute(w, s + 2),
56
+ 0
57
+ );
58
+ h.dispose();
59
+ const c = new e.BufferGeometry();
60
+ return c.setAttribute("position", new e.Float32BufferAttribute(g, 3)), c.computeVertexNormals(), T(c), c;
61
+ }
62
+ function X(d, y, h, w, g, b = !0) {
63
+ const c = new e.Group(), s = d.rings, n = new e.Shape(s[0].map((t) => new e.Vector2(...y(t))));
64
+ n.holes = s.slice(1).map((t) => new e.Path(t.map((i) => new e.Vector2(...y(i)))));
65
+ const r = d.water.depth, o = (t, i) => {
66
+ const m = new e.Mesh(t, i);
67
+ return m.userData.entityId = d.id, m.receiveShadow = !i.transparent, c.add(m), m;
68
+ }, l = w.get("tile", "#f2f3ef"), a = new e.ShapeGeometry(n);
69
+ a.translate(0, 0, h - r), T(a), o(a, l);
70
+ for (const t of s)
71
+ for (let i = 1; i < t.length; i++) {
72
+ const m = y(t[i - 1]), P = y(t[i]), W = Math.hypot(P[0] - m[0], P[1] - m[1]), G = new e.BoxGeometry(W, 0.15, r + 0.08);
73
+ G.rotateZ(Math.atan2(P[1] - m[1], P[0] - m[0])), G.translate((m[0] + P[0]) / 2, (m[1] + P[1]) / 2, h - r / 2 + 0.04), T(G), o(G, l);
74
+ }
75
+ if (!b) {
76
+ const t = new e.ShapeGeometry(n);
77
+ t.translate(0, 0, h - 0.08);
78
+ const i = o(
79
+ t,
80
+ new e.MeshBasicMaterial({
81
+ color: "#72bcc9",
82
+ transparent: !0,
83
+ opacity: 0.65,
84
+ depthWrite: !1,
85
+ side: e.DoubleSide
86
+ })
87
+ );
88
+ return i.userData.water = !0, i.renderOrder = 2, c;
89
+ }
90
+ const f = new e.MeshPhysicalMaterial({
91
+ color: "#d4f3ef",
92
+ roughness: 0.12,
93
+ metalness: 0,
94
+ transmission: 0.94,
95
+ thickness: r,
96
+ ior: 1.333,
97
+ attenuationColor: "#63bec5",
98
+ attenuationDistance: 12,
99
+ transparent: !0,
100
+ depthWrite: !1,
101
+ // Water is an open surface viewed from above, not a solid glass volume. DoubleSide makes
102
+ // Three render its back face into the transmission target and resolve mipmaps a second time.
103
+ side: e.FrontSide,
104
+ forceSinglePass: !0,
105
+ envMapIntensity: 0.8
106
+ }), x = s.map(E).flatMap((t) => t.slice(0, -1).map((i, m) => new e.Vector4(...y(i), ...y(t[m + 1])))), R = { value: 1 };
107
+ f.userData.refractionGuard = R, f.onBeforeCompile = (t) => {
108
+ t.uniforms.poolTime = g, t.uniforms.poolRipple = { value: d.water.ripple ?? 0.015 }, t.uniforms.poolEdges = { value: x }, t.uniforms.poolRefractionGuard = R, t.vertexShader = A + t.vertexShader.replace(
109
+ "#include <begin_vertex>",
110
+ `#include <begin_vertex>
111
+ poolXY = position.xy; transformed.z += poolWave(position.xy).x;`
112
+ ), t.fragmentShader = A + D(x.length) + t.fragmentShader.replace(
113
+ "#include <normal_fragment_begin>",
114
+ `#include <normal_fragment_begin>
115
+ vec3 wave = poolWave(poolXY);
116
+ normal = normalize(mat3(viewMatrix) * vec3(-wave.y, -wave.z, 1.0)) * faceDirection;`
117
+ ).replace(
118
+ "#include <transmission_pars_fragment>",
119
+ e.ShaderChunk.transmission_pars_fragment.replaceAll(
120
+ "vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );",
121
+ `
122
+ vec3 sight = refractedRayExit - cameraPosition;
123
+ // Test at the rim, slightly above the waterline, to include the retaining face.
124
+ float crossing = (position.z + 0.12 - cameraPosition.z) / min(sight.z, -0.00001);
125
+ vec2 entry = cameraPosition.xy + sight.xy * crossing;
126
+ float safeRefraction = smoothstep(0.12, 0.42, poolClearance(entry));
127
+ refractedRayExit = mix(position, refractedRayExit, mix(1.0, safeRefraction, poolRefractionGuard));
128
+ vec4 ndcPos = projMatrix * viewMatrix * vec4(refractedRayExit, 1.0);
129
+ `
130
+ )
131
+ );
132
+ }, f.customProgramCacheKey = () => `pool-water-v2-${x.length}`;
133
+ const V = o(F(n, h - 0.08), f);
134
+ V.userData.water = !0, V.renderOrder = 2;
135
+ const S = y(s[0][0]), p = y(s[0][1]), u = new e.Vector2(p[0] - S[0], p[1] - S[1]).normalize(), z = new e.Vector2(-u.y, u.x), M = new e.Vector2(S[0], S[1]).lerp(new e.Vector2(p[0], p[1]), 0.22), v = (t, i, m) => new e.Vector3(
136
+ M.x + u.x * t + z.x * i,
137
+ M.y + u.y * t + z.y * i,
138
+ h + m
139
+ ), C = w.metal("#bfcfce");
140
+ for (const t of [-0.3, 0.3]) {
141
+ const i = new e.CatmullRomCurve3([
142
+ v(t, -0.5, 0.02),
143
+ v(t, -0.5, 0.7),
144
+ v(t, 0, 0.88),
145
+ v(t, 0.4, 0.6),
146
+ v(t, 0.4, -r + 0.18)
147
+ ]);
148
+ o(new e.TubeGeometry(i, 32, 0.035, 8, !1), C);
149
+ }
150
+ for (let t = -0.15; t > -r + 0.2; t -= 0.28) {
151
+ const i = v(-0.3, 0.4, t), m = v(0.3, 0.4, t), P = new e.CylinderGeometry(0.027, 0.027, i.distanceTo(m), 8);
152
+ P.applyQuaternion(
153
+ new e.Quaternion().setFromUnitVectors(new e.Vector3(0, 1, 0), m.clone().sub(i).normalize())
154
+ ), P.translate((i.x + m.x) / 2, (i.y + m.y) / 2, i.z), o(P, C);
155
+ }
156
+ return c;
157
+ }
158
+ function q(d, y, h, w) {
159
+ const g = d.slide, b = g.path.map(([p, u, z]) => {
160
+ const M = _([p, u], d.rotation), v = y([d.position[0] + M[0], d.position[1] + M[1]]);
161
+ return new e.Vector3(v[0], v[1], h + z + g.radius);
162
+ }), c = new e.CatmullRomCurve3(b, !1, "centripetal"), s = [], n = [], r = 96, o = 16;
163
+ for (let p = 0; p <= r; p++) {
164
+ const u = c.getPointAt(p / r), z = c.getTangentAt(p / r), M = new e.Vector3(-z.y, z.x, 0).normalize();
165
+ for (let v = 0; v <= o; v++) {
166
+ const C = Math.PI + Math.PI * v / o;
167
+ if (s.push(
168
+ u.x + M.x * g.radius * Math.cos(C),
169
+ u.y + M.y * g.radius * Math.cos(C),
170
+ u.z + g.radius * Math.sin(C)
171
+ ), p < r && v < o) {
172
+ const t = p * (o + 1) + v, i = t + o + 1;
173
+ n.push(t, i, t + 1, i, i + 1, t + 1);
174
+ }
175
+ }
176
+ }
177
+ const l = new e.BufferGeometry();
178
+ l.setAttribute("position", new e.Float32BufferAttribute(s, 3)), l.setIndex(n), l.computeVertexNormals(), T(l);
179
+ const a = new e.Group(), f = new e.Mesh(l, w.polished(w.get("tile", d.color ?? "#f2f3ef"), 0.22));
180
+ f.userData.entityId = d.id, f.castShadow = f.receiveShadow = !0, a.add(f);
181
+ const x = w.metal("#c6d5d6");
182
+ for (const p of [0, 0.25, 0.5, 0.75]) {
183
+ const u = c.getPointAt(p), z = u.z - h - g.radius;
184
+ if (z < 0.2) continue;
185
+ const M = new e.Mesh(new e.CylinderGeometry(0.1, 0.1, z, 8).rotateX(Math.PI / 2), x);
186
+ M.position.set(u.x, u.y, h + z / 2), a.add(M);
187
+ }
188
+ const R = b[0], V = R.z - h - g.radius, S = Math.ceil(V / 0.2);
189
+ for (let p = 1; p <= S; p++) {
190
+ const u = new e.Mesh(new e.BoxGeometry(1.1, 0.27, 0.12), w.get("tile", "#f2f3ef"));
191
+ u.position.set(R.x - g.radius - 0.7, R.y - (S - p) * 0.25, h + V * p / S), a.add(u);
192
+ }
193
+ return a;
194
+ }
195
+ function K(d, y, h, w, g, b) {
196
+ const c = B(y, h).map(({ room: n, pool: r, strength: o, height: l }) => {
197
+ const a = w(n.position), f = w([n.position[0] + 1, n.position[1]]), x = Math.hypot(f[0] - a[0], f[1] - a[1]);
198
+ return {
199
+ frame: new e.Vector4(a[0], a[1], (f[0] - a[0]) / x, (f[1] - a[1]) / x),
200
+ room: new e.Vector4(n.width / 2 + 0.25, n.depth / 2 + 0.25, g + l, o),
201
+ pool: r ? new e.Vector4(
202
+ r.position[0] - n.position[0],
203
+ r.position[1] - n.position[1],
204
+ r.width / 2,
205
+ r.depth / 2
206
+ ) : new e.Vector4(0, 0, -1, -1)
207
+ };
208
+ });
209
+ if (!c.length) return;
210
+ const s = /* @__PURE__ */ new Map();
211
+ d.traverse((n) => {
212
+ if (!(n instanceof e.Mesh)) return;
213
+ const r = (o) => {
214
+ if (!(o instanceof e.MeshStandardMaterial) || o.userData.finish !== "tile") return o;
215
+ if (s.has(o)) return s.get(o);
216
+ const l = o.clone();
217
+ return l.userData = { ...o.userData, shared: !1, poolCaustics: !0 }, l.onBeforeCompile = (a, f) => {
218
+ o.onBeforeCompile(a, f), a.uniforms.bathTime = b, a.uniforms.bathFrames = { value: c.map((x) => x.frame) }, a.uniforms.bathRooms = { value: c.map((x) => x.room) }, a.uniforms.bathPools = { value: c.map((x) => x.pool) }, a.vertexShader = `varying vec3 bathWorld;
219
+ ` + a.vertexShader.replace(
220
+ "#include <worldpos_vertex>",
221
+ `#include <worldpos_vertex>
222
+ bathWorld = (modelMatrix * vec4(transformed, 1.0)).xyz;`
223
+ ), a.fragmentShader = `varying vec3 bathWorld; uniform float bathTime;
224
+ uniform vec4 bathFrames[${c.length}]; uniform vec4 bathRooms[${c.length}];
225
+ uniform vec4 bathPools[${c.length}];
226
+ ` + a.fragmentShader.replace(
227
+ "#include <emissivemap_fragment>",
228
+ `
229
+ #include <emissivemap_fragment>
230
+ #ifdef USE_MAP
231
+ // Keep 15 cm tile joints legible at room scale. A 3 mm seam in a mipmapped image
232
+ // otherwise averages away several metres from the wall. Widen subpixel seams only
233
+ // while individual tiles remain resolvable, then fade to avoid distant moiré.
234
+ vec2 tileCell = vMapUv * vec2(4.0, 6.0);
235
+ vec2 tilePixel = max(fwidth(tileCell), vec2(0.0001));
236
+ vec2 tileEdge = min(fract(tileCell), 1.0 - fract(tileCell));
237
+ vec2 tileWidth = max(vec2(0.012), tilePixel * 0.35);
238
+ vec2 tileLine = 1.0 - smoothstep(tileWidth - tilePixel * 0.5, tileWidth + tilePixel * 0.5, tileEdge);
239
+ float grout = max(tileLine.x, tileLine.y) * (1.0 - smoothstep(0.35, 0.7, max(tilePixel.x, tilePixel.y)));
240
+ diffuseColor.rgb *= 1.0 - grout * 0.62;
241
+ #endif
242
+ float bathGlow = 0.0;
243
+ for (int i = 0; i < ${c.length}; i++) {
244
+ vec2 delta = bathWorld.xy - bathFrames[i].xy;
245
+ vec2 local = vec2(dot(delta, bathFrames[i].zw), dot(delta, vec2(-bathFrames[i].w, bathFrames[i].z)));
246
+ if (abs(local.x) > bathRooms[i].x || abs(local.y) > bathRooms[i].y) continue;
247
+ bool reflected = bathPools[i].z < 0.0;
248
+ vec2 outside = reflected ? vec2(0.0) : max(abs(local - bathPools[i].xy) - bathPools[i].zw, vec2(0.0));
249
+ float dz = bathWorld.z - bathRooms[i].z;
250
+ float falloff = 1.0 / (1.0 + 0.012 * (dot(outside, outside) + dz * dz));
251
+ // Two independent phases on vertical faces as well as horizontal ones: a flat XY
252
+ // projection stretches the caustics into neon stripes up every wall.
253
+ vec3 phase = vec3(local, bathWorld.z) * 7.0;
254
+ float a = sin(phase.x + phase.z * 0.91 + sin(phase.y * 0.81 + phase.z * 0.73 + bathTime * 0.6));
255
+ float b = sin(phase.y - phase.z * 1.13 - sin(phase.x * 0.93 + phase.z * 1.31 - bathTime * 0.47));
256
+ float focus = reflected ? 0.0 : pow(max(0.0, 1.0 - abs(a + b) * 0.75), 10.0);
257
+ // Diffuse reflected pool light carries the tiles; focused ripples modulate it.
258
+ float spread = 0.65 + 0.35 * sin(local.x * 0.27 + sin(local.y * 0.23) + bathTime * 0.18);
259
+ bathGlow += (0.65 + focus * 0.14 * spread / (1.0 + abs(dz) * 0.05)) * falloff * bathRooms[i].w;
260
+ }
261
+ totalEmissiveRadiance += vec3(0.40, 0.66, 1.0) * min(bathGlow, 1.8) * diffuseColor.rgb;
262
+ `
263
+ );
264
+ }, l.customProgramCacheKey = () => `${o.customProgramCacheKey()}/bath-caustics-${c.length}`, s.set(o, l), l;
265
+ };
266
+ n.material = Array.isArray(n.material) ? n.material.map(r) : r(n.material);
267
+ });
268
+ }
269
+ export {
270
+ K as applyPoolCaustics,
271
+ X as makePool,
272
+ q as makeWaterSlide
273
+ };
274
+ //# sourceMappingURL=water.js.map