@pascal-app/core 0.9.2 → 1.0.0-beta.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 (339) hide show
  1. package/README.md +17 -10
  2. package/dist/events/bus.d.ts +21 -2
  3. package/dist/events/bus.d.ts.map +1 -1
  4. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +17 -1
  5. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -1
  6. package/dist/hooks/spatial-grid/floor-placed-elevation.js +61 -8
  7. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +116 -66
  8. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  9. package/dist/hooks/spatial-grid/spatial-grid-manager.js +319 -441
  10. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +46 -1
  11. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  12. package/dist/hooks/spatial-grid/spatial-grid-sync.js +204 -12
  13. package/dist/hooks/spatial-grid/support-host-id.d.ts +3 -0
  14. package/dist/hooks/spatial-grid/support-host-id.d.ts.map +1 -0
  15. package/dist/hooks/spatial-grid/support-host-id.js +2 -0
  16. package/dist/hooks/spatial-grid/support-host-patch.d.ts +51 -0
  17. package/dist/hooks/spatial-grid/support-host-patch.d.ts.map +1 -0
  18. package/dist/hooks/spatial-grid/support-host-patch.js +164 -0
  19. package/dist/index.d.ts +23 -11
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +22 -10
  22. package/dist/lib/measurement-geometry.d.ts +4 -0
  23. package/dist/lib/measurement-geometry.d.ts.map +1 -1
  24. package/dist/lib/measurement-geometry.js +27 -10
  25. package/dist/lib/slab-polygon.d.ts.map +1 -1
  26. package/dist/lib/slab-polygon.js +33 -1
  27. package/dist/lib/space-detection.d.ts +15 -4
  28. package/dist/lib/space-detection.d.ts.map +1 -1
  29. package/dist/lib/space-detection.js +232 -91
  30. package/dist/lib/terrain-brush.d.ts +163 -0
  31. package/dist/lib/terrain-brush.d.ts.map +1 -0
  32. package/dist/lib/terrain-brush.js +359 -0
  33. package/dist/lib/terrain-codec.d.ts +51 -0
  34. package/dist/lib/terrain-codec.d.ts.map +1 -0
  35. package/dist/lib/terrain-codec.js +167 -0
  36. package/dist/lib/terrain-field.d.ts +150 -0
  37. package/dist/lib/terrain-field.d.ts.map +1 -0
  38. package/dist/lib/terrain-field.js +248 -0
  39. package/dist/lib/terrain-raycast.d.ts +44 -0
  40. package/dist/lib/terrain-raycast.d.ts.map +1 -0
  41. package/dist/lib/terrain-raycast.js +188 -0
  42. package/dist/lib/terrain-source.d.ts +60 -0
  43. package/dist/lib/terrain-source.d.ts.map +1 -0
  44. package/dist/lib/terrain-source.js +90 -0
  45. package/dist/lib/terrain-support.d.ts +85 -0
  46. package/dist/lib/terrain-support.d.ts.map +1 -0
  47. package/dist/lib/terrain-support.js +218 -0
  48. package/dist/lib/zone-quantities.d.ts +1 -1
  49. package/dist/lib/zone-quantities.d.ts.map +1 -1
  50. package/dist/lib/zone-quantities.js +311 -66
  51. package/dist/material-library.d.ts +9 -1
  52. package/dist/material-library.d.ts.map +1 -1
  53. package/dist/material-library.js +56 -4
  54. package/dist/registry/handles.d.ts +16 -1
  55. package/dist/registry/handles.d.ts.map +1 -1
  56. package/dist/registry/index.d.ts +1 -1
  57. package/dist/registry/index.d.ts.map +1 -1
  58. package/dist/registry/subtree.d.ts.map +1 -1
  59. package/dist/registry/subtree.js +5 -2
  60. package/dist/registry/types.d.ts +83 -1
  61. package/dist/registry/types.d.ts.map +1 -1
  62. package/dist/schema/index.d.ts +6 -3
  63. package/dist/schema/index.d.ts.map +1 -1
  64. package/dist/schema/index.js +6 -3
  65. package/dist/schema/nodes/cabinet.d.ts +2 -0
  66. package/dist/schema/nodes/cabinet.d.ts.map +1 -1
  67. package/dist/schema/nodes/cabinet.js +4 -2
  68. package/dist/schema/nodes/ceiling.d.ts +1 -1
  69. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  70. package/dist/schema/nodes/ceiling.js +7 -1
  71. package/dist/schema/nodes/column.d.ts +1 -0
  72. package/dist/schema/nodes/column.d.ts.map +1 -1
  73. package/dist/schema/nodes/column.js +2 -0
  74. package/dist/schema/nodes/construction-dimension.d.ts +195 -0
  75. package/dist/schema/nodes/construction-dimension.d.ts.map +1 -0
  76. package/dist/schema/nodes/construction-dimension.js +151 -0
  77. package/dist/schema/nodes/door.d.ts +29 -0
  78. package/dist/schema/nodes/door.d.ts.map +1 -1
  79. package/dist/schema/nodes/door.js +20 -0
  80. package/dist/schema/nodes/duct-terminal.d.ts +1 -0
  81. package/dist/schema/nodes/duct-terminal.d.ts.map +1 -1
  82. package/dist/schema/nodes/duct-terminal.js +2 -0
  83. package/dist/schema/nodes/fence.d.ts +2 -0
  84. package/dist/schema/nodes/fence.d.ts.map +1 -1
  85. package/dist/schema/nodes/fence.js +8 -0
  86. package/dist/schema/nodes/hvac-equipment.d.ts +1 -0
  87. package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -1
  88. package/dist/schema/nodes/hvac-equipment.js +2 -0
  89. package/dist/schema/nodes/item.d.ts +1 -0
  90. package/dist/schema/nodes/item.d.ts.map +1 -1
  91. package/dist/schema/nodes/item.js +13 -0
  92. package/dist/schema/nodes/level.d.ts +2 -1
  93. package/dist/schema/nodes/level.d.ts.map +1 -1
  94. package/dist/schema/nodes/level.js +12 -33
  95. package/dist/schema/nodes/shelf.d.ts +1 -0
  96. package/dist/schema/nodes/shelf.d.ts.map +1 -1
  97. package/dist/schema/nodes/shelf.js +2 -0
  98. package/dist/schema/nodes/site.d.ts +9 -0
  99. package/dist/schema/nodes/site.d.ts.map +1 -1
  100. package/dist/schema/nodes/site.js +8 -6
  101. package/dist/schema/nodes/slab.d.ts +5 -0
  102. package/dist/schema/nodes/slab.d.ts.map +1 -1
  103. package/dist/schema/nodes/slab.js +14 -2
  104. package/dist/schema/nodes/spawn.d.ts +1 -0
  105. package/dist/schema/nodes/spawn.d.ts.map +1 -1
  106. package/dist/schema/nodes/spawn.js +2 -0
  107. package/dist/schema/nodes/stair.d.ts +3 -1
  108. package/dist/schema/nodes/stair.d.ts.map +1 -1
  109. package/dist/schema/nodes/stair.js +8 -1
  110. package/dist/schema/nodes/structural-grid.d.ts +27 -0
  111. package/dist/schema/nodes/structural-grid.d.ts.map +1 -0
  112. package/dist/schema/nodes/structural-grid.js +17 -0
  113. package/dist/schema/nodes/wall.d.ts +5 -2
  114. package/dist/schema/nodes/wall.d.ts.map +1 -1
  115. package/dist/schema/nodes/wall.js +13 -4
  116. package/dist/schema/nodes/window.d.ts +29 -0
  117. package/dist/schema/nodes/window.d.ts.map +1 -1
  118. package/dist/schema/nodes/window.js +18 -0
  119. package/dist/schema/nodes/zone.d.ts +20 -0
  120. package/dist/schema/nodes/zone.d.ts.map +1 -1
  121. package/dist/schema/nodes/zone.js +15 -0
  122. package/dist/schema/terrain.d.ts +28 -0
  123. package/dist/schema/terrain.d.ts.map +1 -0
  124. package/dist/schema/terrain.js +30 -0
  125. package/dist/schema/types.d.ts +211 -3
  126. package/dist/schema/types.d.ts.map +1 -1
  127. package/dist/schema/types.js +4 -0
  128. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts +80 -0
  129. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts.map +1 -0
  130. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.js +89 -0
  131. package/dist/services/index.d.ts +2 -1
  132. package/dist/services/index.d.ts.map +1 -1
  133. package/dist/services/index.js +2 -1
  134. package/dist/services/level-height.d.ts +8 -9
  135. package/dist/services/level-height.d.ts.map +1 -1
  136. package/dist/services/level-height.js +48 -16
  137. package/dist/services/storey.d.ts +103 -0
  138. package/dist/services/storey.d.ts.map +1 -0
  139. package/dist/services/storey.js +296 -0
  140. package/dist/store/actions/node-actions.d.ts.map +1 -1
  141. package/dist/store/actions/node-actions.js +47 -3
  142. package/dist/store/history-control.d.ts +20 -0
  143. package/dist/store/history-control.d.ts.map +1 -1
  144. package/dist/store/history-control.js +110 -13
  145. package/dist/store/use-live-terrain.d.ts +65 -0
  146. package/dist/store/use-live-terrain.d.ts.map +1 -0
  147. package/dist/store/use-live-terrain.js +31 -0
  148. package/dist/store/use-live-transforms.d.ts +8 -0
  149. package/dist/store/use-live-transforms.d.ts.map +1 -1
  150. package/dist/store/use-scene.d.ts +31 -2
  151. package/dist/store/use-scene.d.ts.map +1 -1
  152. package/dist/store/use-scene.js +638 -8
  153. package/dist/systems/elevator/elevator-opening-system.d.ts +1 -0
  154. package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -1
  155. package/dist/systems/elevator/elevator-opening-system.js +27 -21
  156. package/dist/systems/elevator/elevator-service.d.ts +0 -2
  157. package/dist/systems/elevator/elevator-service.d.ts.map +1 -1
  158. package/dist/systems/elevator/elevator-service.js +2 -24
  159. package/dist/systems/slab/slab-placement.d.ts +11 -0
  160. package/dist/systems/slab/slab-placement.d.ts.map +1 -0
  161. package/dist/systems/slab/slab-placement.js +14 -0
  162. package/dist/systems/slab/slab-support.d.ts +150 -0
  163. package/dist/systems/slab/slab-support.d.ts.map +1 -0
  164. package/dist/systems/slab/slab-support.js +575 -0
  165. package/dist/systems/stair/stair-opening-preview.d.ts +1060 -935
  166. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
  167. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  168. package/dist/systems/stair/stair-opening-sync.js +18 -15
  169. package/dist/systems/stair/stair-opening-system.d.ts.map +1 -1
  170. package/dist/systems/stair/stair-opening-system.js +32 -4
  171. package/dist/systems/stair/stair-rise.d.ts +22 -0
  172. package/dist/systems/stair/stair-rise.d.ts.map +1 -0
  173. package/dist/systems/stair/stair-rise.js +79 -0
  174. package/dist/systems/wall/wall-curve.d.ts +10 -0
  175. package/dist/systems/wall/wall-curve.d.ts.map +1 -1
  176. package/dist/systems/wall/wall-curve.js +1 -1
  177. package/dist/systems/wall/wall-mitering.js +6 -6
  178. package/dist/systems/wall/wall-top.d.ts +40 -0
  179. package/dist/systems/wall/wall-top.d.ts.map +1 -0
  180. package/dist/systems/wall/wall-top.js +46 -0
  181. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  182. package/dist/utils/clone-scene-graph.js +34 -3
  183. package/dist/utils/heal-scene-graph.d.ts +1 -1
  184. package/dist/utils/heal-scene-graph.d.ts.map +1 -1
  185. package/dist/utils/heal-scene-graph.js +25 -9
  186. package/dist/validation/validate-build-json.d.ts +2 -0
  187. package/dist/validation/validate-build-json.d.ts.map +1 -1
  188. package/dist/validation/validate-build-json.js +59 -3
  189. package/package.json +2 -2
  190. package/dist/hooks/scene-registry/scene-registry.test.d.ts +0 -2
  191. package/dist/hooks/scene-registry/scene-registry.test.d.ts.map +0 -1
  192. package/dist/hooks/scene-registry/scene-registry.test.js +0 -26
  193. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +0 -2
  194. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +0 -1
  195. package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +0 -366
  196. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts +0 -2
  197. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts.map +0 -1
  198. package/dist/hooks/spatial-grid/wall-slab-overlap.test.js +0 -363
  199. package/dist/lib/measurement-geometry.test.d.ts +0 -2
  200. package/dist/lib/measurement-geometry.test.d.ts.map +0 -1
  201. package/dist/lib/measurement-geometry.test.js +0 -102
  202. package/dist/lib/slab-polygon.test.d.ts +0 -2
  203. package/dist/lib/slab-polygon.test.d.ts.map +0 -1
  204. package/dist/lib/slab-polygon.test.js +0 -635
  205. package/dist/lib/space-detection-pause.test.d.ts +0 -2
  206. package/dist/lib/space-detection-pause.test.d.ts.map +0 -1
  207. package/dist/lib/space-detection-pause.test.js +0 -42
  208. package/dist/lib/space-detection.test.d.ts +0 -2
  209. package/dist/lib/space-detection.test.d.ts.map +0 -1
  210. package/dist/lib/space-detection.test.js +0 -376
  211. package/dist/lib/zone-quantities.test.d.ts +0 -2
  212. package/dist/lib/zone-quantities.test.d.ts.map +0 -1
  213. package/dist/lib/zone-quantities.test.js +0 -82
  214. package/dist/registry/registry.test.d.ts +0 -2
  215. package/dist/registry/registry.test.d.ts.map +0 -1
  216. package/dist/registry/registry.test.js +0 -196
  217. package/dist/registry/relations-resolver.test.d.ts +0 -2
  218. package/dist/registry/relations-resolver.test.d.ts.map +0 -1
  219. package/dist/registry/relations-resolver.test.js +0 -183
  220. package/dist/registry/scene-api.test.d.ts +0 -2
  221. package/dist/registry/scene-api.test.d.ts.map +0 -1
  222. package/dist/registry/scene-api.test.js +0 -183
  223. package/dist/registry/subtree.test.d.ts +0 -2
  224. package/dist/registry/subtree.test.d.ts.map +0 -1
  225. package/dist/registry/subtree.test.js +0 -163
  226. package/dist/schema/asset-url.test.d.ts +0 -2
  227. package/dist/schema/asset-url.test.d.ts.map +0 -1
  228. package/dist/schema/asset-url.test.js +0 -134
  229. package/dist/schema/material.test.d.ts +0 -2
  230. package/dist/schema/material.test.d.ts.map +0 -1
  231. package/dist/schema/material.test.js +0 -43
  232. package/dist/schema/nodes/measurement.test.d.ts +0 -2
  233. package/dist/schema/nodes/measurement.test.d.ts.map +0 -1
  234. package/dist/schema/nodes/measurement.test.js +0 -139
  235. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts +0 -2
  236. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts.map +0 -1
  237. package/dist/schema/nodes/ridge-vent-defaults.test.js +0 -205
  238. package/dist/schema/nodes/roof-segment-shape.test.d.ts +0 -2
  239. package/dist/schema/nodes/roof-segment-shape.test.d.ts.map +0 -1
  240. package/dist/schema/nodes/roof-segment-shape.test.js +0 -175
  241. package/dist/schema/nodes/roof-segment-surface.test.d.ts +0 -2
  242. package/dist/schema/nodes/roof-segment-surface.test.d.ts.map +0 -1
  243. package/dist/schema/nodes/roof-segment-surface.test.js +0 -48
  244. package/dist/schema/nodes/roof-segment-trim.test.d.ts +0 -2
  245. package/dist/schema/nodes/roof-segment-trim.test.d.ts.map +0 -1
  246. package/dist/schema/nodes/roof-segment-trim.test.js +0 -107
  247. package/dist/schema/nodes/roof-segment-walls.test.d.ts +0 -2
  248. package/dist/schema/nodes/roof-segment-walls.test.d.ts.map +0 -1
  249. package/dist/schema/nodes/roof-segment-walls.test.js +0 -92
  250. package/dist/schema/nodes/wall.test.d.ts +0 -2
  251. package/dist/schema/nodes/wall.test.d.ts.map +0 -1
  252. package/dist/schema/nodes/wall.test.js +0 -208
  253. package/dist/services/alignment-anchors.test.d.ts +0 -2
  254. package/dist/services/alignment-anchors.test.d.ts.map +0 -1
  255. package/dist/services/alignment-anchors.test.js +0 -383
  256. package/dist/services/alignment.test.d.ts +0 -2
  257. package/dist/services/alignment.test.d.ts.map +0 -1
  258. package/dist/services/alignment.test.js +0 -92
  259. package/dist/services/drag-session.test.d.ts +0 -2
  260. package/dist/services/drag-session.test.d.ts.map +0 -1
  261. package/dist/services/drag-session.test.js +0 -219
  262. package/dist/services/hosting.test.d.ts +0 -2
  263. package/dist/services/hosting.test.d.ts.map +0 -1
  264. package/dist/services/hosting.test.js +0 -230
  265. package/dist/services/movement.test.d.ts +0 -2
  266. package/dist/services/movement.test.d.ts.map +0 -1
  267. package/dist/services/movement.test.js +0 -133
  268. package/dist/services/opening-guides.test.d.ts +0 -2
  269. package/dist/services/opening-guides.test.d.ts.map +0 -1
  270. package/dist/services/opening-guides.test.js +0 -208
  271. package/dist/services/port-connectivity.test.d.ts +0 -2
  272. package/dist/services/port-connectivity.test.d.ts.map +0 -1
  273. package/dist/services/port-connectivity.test.js +0 -297
  274. package/dist/services/riser-diagram.test.d.ts +0 -2
  275. package/dist/services/riser-diagram.test.d.ts.map +0 -1
  276. package/dist/services/riser-diagram.test.js +0 -66
  277. package/dist/services/single-undo-dance.test.d.ts +0 -2
  278. package/dist/services/single-undo-dance.test.d.ts.map +0 -1
  279. package/dist/services/single-undo-dance.test.js +0 -210
  280. package/dist/services/snap.test.d.ts +0 -2
  281. package/dist/services/snap.test.d.ts.map +0 -1
  282. package/dist/services/snap.test.js +0 -137
  283. package/dist/services/system-graph.test.d.ts +0 -2
  284. package/dist/services/system-graph.test.d.ts.map +0 -1
  285. package/dist/services/system-graph.test.js +0 -138
  286. package/dist/services/validate-dwv.test.d.ts +0 -2
  287. package/dist/services/validate-dwv.test.d.ts.map +0 -1
  288. package/dist/services/validate-dwv.test.js +0 -109
  289. package/dist/store/actions/node-mutation-sanitize.test.d.ts +0 -2
  290. package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +0 -1
  291. package/dist/store/actions/node-mutation-sanitize.test.js +0 -147
  292. package/dist/store/actions/reparent.test.d.ts +0 -2
  293. package/dist/store/actions/reparent.test.d.ts.map +0 -1
  294. package/dist/store/actions/reparent.test.js +0 -212
  295. package/dist/store/actions/ridge-vent-update.test.d.ts +0 -2
  296. package/dist/store/actions/ridge-vent-update.test.d.ts.map +0 -1
  297. package/dist/store/actions/ridge-vent-update.test.js +0 -264
  298. package/dist/store/use-scene-dirty-tracking.test.d.ts +0 -2
  299. package/dist/store/use-scene-dirty-tracking.test.d.ts.map +0 -1
  300. package/dist/store/use-scene-dirty-tracking.test.js +0 -68
  301. package/dist/store/use-scene-elevator-migration.test.d.ts +0 -2
  302. package/dist/store/use-scene-elevator-migration.test.d.ts.map +0 -1
  303. package/dist/store/use-scene-elevator-migration.test.js +0 -109
  304. package/dist/store/use-scene-measurement-roundtrip.test.d.ts +0 -2
  305. package/dist/store/use-scene-measurement-roundtrip.test.d.ts.map +0 -1
  306. package/dist/store/use-scene-measurement-roundtrip.test.js +0 -97
  307. package/dist/store/use-scene-plugins.test.d.ts +0 -2
  308. package/dist/store/use-scene-plugins.test.d.ts.map +0 -1
  309. package/dist/store/use-scene-plugins.test.js +0 -51
  310. package/dist/store/use-scene-wall-slot-migration.test.d.ts +0 -2
  311. package/dist/store/use-scene-wall-slot-migration.test.d.ts.map +0 -1
  312. package/dist/store/use-scene-wall-slot-migration.test.js +0 -210
  313. package/dist/store/use-scene-window-migration.test.d.ts +0 -2
  314. package/dist/store/use-scene-window-migration.test.d.ts.map +0 -1
  315. package/dist/store/use-scene-window-migration.test.js +0 -85
  316. package/dist/systems/elevator/elevator-opening-sync.test.d.ts +0 -2
  317. package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +0 -1
  318. package/dist/systems/elevator/elevator-opening-sync.test.js +0 -146
  319. package/dist/systems/elevator/elevator-runtime.test.d.ts +0 -2
  320. package/dist/systems/elevator/elevator-runtime.test.d.ts.map +0 -1
  321. package/dist/systems/elevator/elevator-runtime.test.js +0 -64
  322. package/dist/systems/fence/fence-spline.test.d.ts +0 -2
  323. package/dist/systems/fence/fence-spline.test.d.ts.map +0 -1
  324. package/dist/systems/fence/fence-spline.test.js +0 -136
  325. package/dist/systems/stair/stair-opening-preview.test.d.ts +0 -2
  326. package/dist/systems/stair/stair-opening-preview.test.d.ts.map +0 -1
  327. package/dist/systems/stair/stair-opening-preview.test.js +0 -80
  328. package/dist/systems/stair/stair-opening-sync.test.d.ts +0 -2
  329. package/dist/systems/stair/stair-opening-sync.test.d.ts.map +0 -1
  330. package/dist/systems/stair/stair-opening-sync.test.js +0 -485
  331. package/dist/systems/wall/wall-mitering.test.d.ts +0 -2
  332. package/dist/systems/wall/wall-mitering.test.d.ts.map +0 -1
  333. package/dist/systems/wall/wall-mitering.test.js +0 -63
  334. package/dist/utils/clone-scene-graph.test.d.ts +0 -2
  335. package/dist/utils/clone-scene-graph.test.d.ts.map +0 -1
  336. package/dist/utils/clone-scene-graph.test.js +0 -62
  337. package/dist/utils/heal-scene-graph.test.d.ts +0 -2
  338. package/dist/utils/heal-scene-graph.test.d.ts.map +0 -1
  339. package/dist/utils/heal-scene-graph.test.js +0 -88
@@ -2,16 +2,15 @@ import type { CeilingNode } from '../schema';
2
2
  import type { AnyNode, AnyNodeId } from '../schema/types';
3
3
  export declare const DEFAULT_LEVEL_HEIGHT = 2.5;
4
4
  /**
5
- * Optional resolver for a wall's rendered base Y (mesh elevation).
6
- *
7
- * `packages/core` is pure domain logic and must not read viewer/Three.js
8
- * state (see AGENTS.md “Layer Boundaries”). Callers that legitimately have
9
- * registry access (viewer systems, node tools) may pass a resolver so the
10
- * mesh elevation is factored in; pure/headless callers (MCP, tests, server)
11
- * omit it and get a deterministic result from serialized node data alone.
5
+ * Effective ceiling height in level-local meters. An explicit stored
6
+ * `height` wins; absent height means the ceiling follows the level top —
7
+ * the same bound its write-clamp uses: min(storey plane, lowest
8
+ * covering-slab underside over its polygon) CEILING_CLAMP_MARGIN (see
9
+ * {@link getCeilingClampBound}). Falls back to the default plane minus
10
+ * the same margin when the owning level is unresolvable.
12
11
  */
13
- export type WallBaseYResolver = (wallId: AnyNodeId) => number | undefined;
14
- export declare function getLevelHeight(levelId: string, nodes: Record<AnyNodeId, AnyNode>, resolveWallBaseY?: WallBaseYResolver): number;
12
+ export declare function resolveCeilingHeight(ceiling: Pick<CeilingNode, 'height' | 'parentId' | 'polygon'>, nodes: Record<AnyNodeId, AnyNode>): number;
13
+ export declare function deriveLegacyLevelHeight(levelId: string, nodes: Record<AnyNodeId, AnyNode>): number;
15
14
  /**
16
15
  * The ceiling covering level-local point `[x, z]`, or `null` when none
17
16
  * sits over it. Points inside a ceiling's hole are treated as uncovered.
@@ -1 +1 @@
1
- {"version":3,"file":"level-height.d.ts","sourceRoot":"","sources":["../../src/services/level-height.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,WAAW,CAAA;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEzD,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,MAAM,GAAG,SAAS,CAAA;AAEzE,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,gBAAgB,CAAC,EAAE,iBAAiB,GACnC,MAAM,CAqBR;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,WAAW,GAAG,IAAI,CAepB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,MAAM,GAAG,IAAI,CAGf"}
1
+ {"version":3,"file":"level-height.d.ts","sourceRoot":"","sources":["../../src/services/level-height.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAiC,MAAM,WAAW,CAAA;AAC3E,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAOzD,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAEvC;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC,EAC7D,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAChC,MAAM,CAOR;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAChC,MAAM,CAqCR;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,WAAW,GAAG,IAAI,CAmBpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,MAAM,GAAG,IAAI,CAGf"}
@@ -1,24 +1,53 @@
1
- import { pointInPolygon } from '../hooks/spatial-grid/spatial-grid-manager';
1
+ import { computeWallSlabSupport, pointInPolygon } from '../systems/slab/slab-support';
2
+ import { resolveWallTop } from '../systems/wall/wall-top';
3
+ // Cycle with ./storey (it imports DEFAULT_LEVEL_HEIGHT from here) is safe:
4
+ // both sides only reference the other inside function bodies.
5
+ import { CEILING_CLAMP_MARGIN, getCeilingClampBound } from './storey';
2
6
  export const DEFAULT_LEVEL_HEIGHT = 2.5;
3
- export function getLevelHeight(levelId, nodes, resolveWallBaseY) {
7
+ /**
8
+ * Effective ceiling height in level-local meters. An explicit stored
9
+ * `height` wins; absent height means the ceiling follows the level top —
10
+ * the same bound its write-clamp uses: min(storey plane, lowest
11
+ * covering-slab underside over its polygon) − CEILING_CLAMP_MARGIN (see
12
+ * {@link getCeilingClampBound}). Falls back to the default plane minus
13
+ * the same margin when the owning level is unresolvable.
14
+ */
15
+ export function resolveCeilingHeight(ceiling, nodes) {
16
+ if (ceiling.height != null)
17
+ return ceiling.height;
18
+ const bound = typeof ceiling.parentId === 'string'
19
+ ? getCeilingClampBound(ceiling.parentId, nodes, ceiling.polygon)
20
+ : Number.POSITIVE_INFINITY;
21
+ return Number.isFinite(bound) ? bound : DEFAULT_LEVEL_HEIGHT - CEILING_CLAMP_MARGIN;
22
+ }
23
+ export function deriveLegacyLevelHeight(levelId, nodes) {
4
24
  const level = nodes[levelId];
5
25
  if (!level)
6
26
  return DEFAULT_LEVEL_HEIGHT;
27
+ const levelChildren = level.children
28
+ .map((childId) => nodes[childId])
29
+ .filter((child) => child !== undefined);
30
+ const slabs = levelChildren.filter((child) => child.type === 'slab');
31
+ const walls = levelChildren.filter((child) => child.type === 'wall');
7
32
  let maxTop = 0;
8
- for (const childId of level.children) {
9
- const child = nodes[childId];
10
- if (!child)
11
- continue;
33
+ for (const child of levelChildren) {
12
34
  if (child.type === 'ceiling') {
13
- const ch = child.height ?? DEFAULT_LEVEL_HEIGHT;
14
- if (ch > maxTop)
15
- maxTop = ch;
35
+ // Absence here is the PRE-migration legacy schema default (2.5), not
36
+ // follows-mode this derivation runs before the level has a height
37
+ // for a follows-mode bound to track.
38
+ const height = child.height ?? DEFAULT_LEVEL_HEIGHT;
39
+ if (height > maxTop)
40
+ maxTop = height;
16
41
  }
17
42
  else if (child.type === 'wall') {
18
- let baseY = resolveWallBaseY?.(childId) ?? 0;
19
- if (baseY < 0)
20
- baseY = 0;
21
- const top = baseY + (child.height ?? DEFAULT_LEVEL_HEIGHT);
43
+ const wall = child;
44
+ const electedElevation = computeWallSlabSupport({
45
+ start: wall.start,
46
+ end: wall.end,
47
+ curveOffset: wall.curveOffset,
48
+ thickness: wall.thickness,
49
+ }, slabs, walls).elevation;
50
+ const top = resolveWallTop(wall, level.height ?? DEFAULT_LEVEL_HEIGHT, electedElevation);
22
51
  if (top > maxTop)
23
52
  maxTop = top;
24
53
  }
@@ -36,6 +65,7 @@ export function getCeilingAt(levelId, nodes, x, z) {
36
65
  if (!level)
37
66
  return null;
38
67
  let best = null;
68
+ let bestHeight = Number.POSITIVE_INFINITY;
39
69
  for (const childId of level.children) {
40
70
  const child = nodes[childId];
41
71
  if (child?.type !== 'ceiling')
@@ -45,9 +75,11 @@ export function getCeilingAt(levelId, nodes, x, z) {
45
75
  continue;
46
76
  if (ceiling.holes.some((hole) => hole.length >= 3 && pointInPolygon(x, z, hole)))
47
77
  continue;
48
- const h = ceiling.height ?? DEFAULT_LEVEL_HEIGHT;
49
- if (best === null || h < (best.height ?? DEFAULT_LEVEL_HEIGHT))
78
+ const h = resolveCeilingHeight(ceiling, nodes);
79
+ if (best === null || h < bestHeight) {
50
80
  best = ceiling;
81
+ bestHeight = h;
82
+ }
51
83
  }
52
84
  return best;
53
85
  }
@@ -58,5 +90,5 @@ export function getCeilingAt(levelId, nodes, x, z) {
58
90
  */
59
91
  export function getCeilingHeightAt(levelId, nodes, x, z) {
60
92
  const ceiling = getCeilingAt(levelId, nodes, x, z);
61
- return ceiling ? (ceiling.height ?? DEFAULT_LEVEL_HEIGHT) : null;
93
+ return ceiling ? resolveCeilingHeight(ceiling, nodes) : null;
62
94
  }
@@ -0,0 +1,103 @@
1
+ import type { LevelNode, WallNode } from '../schema';
2
+ import type { AnyNode, AnyNodeId } from '../schema/types';
3
+ /**
4
+ * Gap kept between a ceiling's stored height and its clamp bound (storey
5
+ * plane or covering-slab underside), so the ceiling surface never
6
+ * coincides with the solid above it.
7
+ */
8
+ export declare const CEILING_CLAMP_MARGIN = 0.01;
9
+ /**
10
+ * Stored storey height in meters (floor-to-floor). Falls back to
11
+ * {@link DEFAULT_LEVEL_HEIGHT} for unmigrated legacy levels whose `height`
12
+ * field is absent.
13
+ */
14
+ export declare function getStoredLevelHeight(level: Pick<LevelNode, 'height'>): number;
15
+ export type LevelElevation = {
16
+ /** World Y of the level's floor: prefix sum of the storey heights below it. */
17
+ baseY: number;
18
+ /** Stored storey height of this level (fallback applied). */
19
+ height: number;
20
+ buildingId: string | null;
21
+ ordinal: number;
22
+ };
23
+ /**
24
+ * Per-building stacked elevations from stored storey heights: levels are
25
+ * sorted by ordinal ascending within each building, the lowest level's floor
26
+ * sits at 0, and each next floor sits on top of the previous storey height.
27
+ * Levels with no resolvable building share one legacy stack from 0.
28
+ *
29
+ * Pure — operates on the serialized nodes record only.
30
+ */
31
+ export declare function getLevelElevations(nodes: Record<AnyNodeId, AnyNode>): Map<string, LevelElevation>;
32
+ /**
33
+ * The id of the level directly above `levelId` in its own stack (same
34
+ * resolved building, or the shared legacy stack for building-less levels):
35
+ * the level with the lowest ordinal strictly greater than the queried
36
+ * level's. `null` when the level is topmost or unresolvable.
37
+ */
38
+ export declare function findLevelAboveId(levelId: string, elevations: Map<string, LevelElevation>): string | null;
39
+ /**
40
+ * The level directly above `levelId` — see {@link findLevelAboveId}.
41
+ * `null` when topmost or unresolvable. Pure.
42
+ */
43
+ export declare function getLevelAbove(levelId: string, nodes: Record<AnyNodeId, AnyNode>): LevelNode | null;
44
+ /**
45
+ * The id of the level directly below `levelId` in its own stack — mirror of
46
+ * {@link findLevelAboveId}: the level with the highest ordinal strictly less
47
+ * than the queried level's. `null` when the level is lowest or unresolvable.
48
+ */
49
+ export declare function findLevelBelowId(levelId: string, elevations: Map<string, LevelElevation>): string | null;
50
+ /**
51
+ * The level directly below `levelId` — see {@link findLevelBelowId}.
52
+ * `null` when lowest or unresolvable. Pure.
53
+ */
54
+ export declare function getLevelBelow(levelId: string, nodes: Record<AnyNodeId, AnyNode>): LevelNode | null;
55
+ /**
56
+ * Underside of the LOWEST slab from the level above that covers
57
+ * level-local point `[x, z]`, expressed in the queried level's local Y:
58
+ * `storeyHeight + (slab.elevation - slab.thickness)`. `recessed` slabs
59
+ * (pools) never cover. `null` when no covering slab (or no level above).
60
+ *
61
+ * Coordinate spaces: levels stack in Y only (`LevelNode` carries no XZ
62
+ * transform and the viewer's LevelSystem writes only `position.y`), so a
63
+ * level-local `[x, z]` is valid in every level of the stack unchanged.
64
+ */
65
+ export declare function getCoveringSlabUndersideAt(levelId: string, nodes: Record<AnyNodeId, AnyNode>, x: number, z: number): number | null;
66
+ /**
67
+ * Top plane for a plane-bound wall on `levelId`, in level-local Y:
68
+ * `min(stored storey height, lowest covering-slab underside over the wall's
69
+ * span)` — a thick or flush slab on the level above SHORTENS the walls below
70
+ * instead of colliding with them (Revit-style automatic attach).
71
+ *
72
+ * Coverage: the wall's thickness band (centerline + face lines, arc-aware)
73
+ * is clipped against each covering slab's stored polygon minus holes via
74
+ * {@link wallOverlapsSlabFootprint} — the same overlap machinery as the
75
+ * support election. Point sampling is deliberately avoided: auto-slab
76
+ * polygons derive from wall CENTERLINES, so perimeter walls sit exactly ON
77
+ * the polygon boundary, where ray-cast point-in-polygon flips with the
78
+ * edge's orientation (one wall clamped, its neighbor didn't). Boundary
79
+ * contact counts as covered on every side of the slab.
80
+ *
81
+ * This is THE plane for a plane-bound wall (`height` absent). Explicit-height
82
+ * walls ignore the value (`resolveWallTop` returns their stored height), so
83
+ * passing it wherever a raw storey height feeds `resolveWallTop` /
84
+ * `resolveWallEffectiveHeight` is always safe. Falls back to
85
+ * {@link DEFAULT_LEVEL_HEIGHT} when `levelId` doesn't resolve to a level.
86
+ */
87
+ export declare function getWallPlaneTop(wall: Pick<WallNode, 'start' | 'end'> & Partial<Pick<WallNode, 'thickness' | 'curveOffset'>>, levelId: string, nodes: Record<AnyNodeId, AnyNode>): number;
88
+ /**
89
+ * Upper bound for a ceiling's stored height over `polygon` on `levelId`:
90
+ * `min(storey plane, lowest covering-slab underside) - CEILING_CLAMP_MARGIN`.
91
+ * The covering underside is sampled at every polygon vertex plus the
92
+ * centroid — cheap, and a slab overlapping a convex-ish ceiling almost
93
+ * always covers one of those points; exact polygon-vs-polygon overlap is
94
+ * not worth its cost for a clamp bound. Ceiling outlines share footprint
95
+ * edges with the slabs above them the same way walls do, so vertices
96
+ * sitting exactly on a slab's boundary count as covered on every side
97
+ * (see `slabCoversPoint`) instead of flipping with the edge orientation.
98
+ *
99
+ * Returns `Infinity` when `levelId` doesn't resolve, so callers clamp
100
+ * against nothing rather than a garbage plane.
101
+ */
102
+ export declare function getCeilingClampBound(levelId: string, nodes: Record<AnyNodeId, AnyNode>, polygon: ReadonlyArray<[number, number]>): number;
103
+ //# sourceMappingURL=storey.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storey.d.ts","sourceRoot":"","sources":["../../src/services/storey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,SAAS,EAAY,QAAQ,EAAE,MAAM,WAAW,CAAA;AAC5E,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAQzD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAA;AAExC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,MAAM,CAE7E;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAA;IACb,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAkBD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAgCjG;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GACtC,MAAM,GAAG,IAAI,CAef;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAChC,SAAS,GAAG,IAAI,CAKlB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GACtC,MAAM,GAAG,IAAI,CAef;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAChC,SAAS,GAAG,IAAI,CAKlB;AAuFD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,MAAM,GAAG,IAAI,CAIf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,aAAa,CAAC,CAAC,EAC5F,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAChC,MAAM,CAYR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,OAAO,EAAE,aAAa,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACvC,MAAM,CAuBR"}
@@ -0,0 +1,296 @@
1
+ import { pointInPolygon, pointOnPolygonBoundary, wallOverlapsSlabFootprint, } from '../systems/slab/slab-support';
2
+ import { DEFAULT_LEVEL_HEIGHT } from './level-height';
3
+ /**
4
+ * Gap kept between a ceiling's stored height and its clamp bound (storey
5
+ * plane or covering-slab underside), so the ceiling surface never
6
+ * coincides with the solid above it.
7
+ */
8
+ export const CEILING_CLAMP_MARGIN = 0.01;
9
+ /**
10
+ * Stored storey height in meters (floor-to-floor). Falls back to
11
+ * {@link DEFAULT_LEVEL_HEIGHT} for unmigrated legacy levels whose `height`
12
+ * field is absent.
13
+ */
14
+ export function getStoredLevelHeight(level) {
15
+ return level.height ?? DEFAULT_LEVEL_HEIGHT;
16
+ }
17
+ /**
18
+ * Resolves the owning building: explicit `parentId` pointing at a building
19
+ * wins; legacy levels that only appear in a building's `children` array
20
+ * resolve through that membership.
21
+ */
22
+ function resolveLevelBuildingId(levelId, parentId, buildings) {
23
+ const directParent = parentId ? buildings.find((building) => building.id === parentId) : undefined;
24
+ if (directParent)
25
+ return directParent.id;
26
+ return buildings.find((building) => building.children.includes(levelId))?.id ?? null;
27
+ }
28
+ /**
29
+ * Per-building stacked elevations from stored storey heights: levels are
30
+ * sorted by ordinal ascending within each building, the lowest level's floor
31
+ * sits at 0, and each next floor sits on top of the previous storey height.
32
+ * Levels with no resolvable building share one legacy stack from 0.
33
+ *
34
+ * Pure — operates on the serialized nodes record only.
35
+ */
36
+ export function getLevelElevations(nodes) {
37
+ const buildings = Object.values(nodes).filter((node) => node?.type === 'building');
38
+ const entries = [];
39
+ for (const node of Object.values(nodes)) {
40
+ if (node?.type !== 'level')
41
+ continue;
42
+ const level = node;
43
+ entries.push({
44
+ levelId: level.id,
45
+ baseY: 0,
46
+ height: getStoredLevelHeight(level),
47
+ buildingId: resolveLevelBuildingId(level.id, level.parentId, buildings),
48
+ ordinal: level.level,
49
+ });
50
+ }
51
+ const elevations = new Map();
52
+ const cumulativeYByBuilding = new Map();
53
+ for (const entry of entries.sort((a, b) => a.ordinal - b.ordinal)) {
54
+ const baseY = cumulativeYByBuilding.get(entry.buildingId) ?? 0;
55
+ elevations.set(entry.levelId, {
56
+ baseY,
57
+ height: entry.height,
58
+ buildingId: entry.buildingId,
59
+ ordinal: entry.ordinal,
60
+ });
61
+ cumulativeYByBuilding.set(entry.buildingId, baseY + entry.height);
62
+ }
63
+ return elevations;
64
+ }
65
+ /**
66
+ * The id of the level directly above `levelId` in its own stack (same
67
+ * resolved building, or the shared legacy stack for building-less levels):
68
+ * the level with the lowest ordinal strictly greater than the queried
69
+ * level's. `null` when the level is topmost or unresolvable.
70
+ */
71
+ export function findLevelAboveId(levelId, elevations) {
72
+ const entry = elevations.get(levelId);
73
+ if (!entry)
74
+ return null;
75
+ let aboveId = null;
76
+ let aboveOrdinal = Number.POSITIVE_INFINITY;
77
+ for (const [candidateId, candidate] of elevations) {
78
+ if (candidateId === levelId)
79
+ continue;
80
+ if (candidate.buildingId !== entry.buildingId)
81
+ continue;
82
+ if (candidate.ordinal > entry.ordinal && candidate.ordinal < aboveOrdinal) {
83
+ aboveOrdinal = candidate.ordinal;
84
+ aboveId = candidateId;
85
+ }
86
+ }
87
+ return aboveId;
88
+ }
89
+ /**
90
+ * The level directly above `levelId` — see {@link findLevelAboveId}.
91
+ * `null` when topmost or unresolvable. Pure.
92
+ */
93
+ export function getLevelAbove(levelId, nodes) {
94
+ const aboveId = findLevelAboveId(levelId, getLevelElevations(nodes));
95
+ if (!aboveId)
96
+ return null;
97
+ const above = nodes[aboveId];
98
+ return above?.type === 'level' ? above : null;
99
+ }
100
+ /**
101
+ * The id of the level directly below `levelId` in its own stack — mirror of
102
+ * {@link findLevelAboveId}: the level with the highest ordinal strictly less
103
+ * than the queried level's. `null` when the level is lowest or unresolvable.
104
+ */
105
+ export function findLevelBelowId(levelId, elevations) {
106
+ const entry = elevations.get(levelId);
107
+ if (!entry)
108
+ return null;
109
+ let belowId = null;
110
+ let belowOrdinal = Number.NEGATIVE_INFINITY;
111
+ for (const [candidateId, candidate] of elevations) {
112
+ if (candidateId === levelId)
113
+ continue;
114
+ if (candidate.buildingId !== entry.buildingId)
115
+ continue;
116
+ if (candidate.ordinal < entry.ordinal && candidate.ordinal > belowOrdinal) {
117
+ belowOrdinal = candidate.ordinal;
118
+ belowId = candidateId;
119
+ }
120
+ }
121
+ return belowId;
122
+ }
123
+ /**
124
+ * The level directly below `levelId` — see {@link findLevelBelowId}.
125
+ * `null` when lowest or unresolvable. Pure.
126
+ */
127
+ export function getLevelBelow(levelId, nodes) {
128
+ const belowId = findLevelBelowId(levelId, getLevelElevations(nodes));
129
+ if (!belowId)
130
+ return null;
131
+ const below = nodes[belowId];
132
+ return below?.type === 'level' ? below : null;
133
+ }
134
+ /**
135
+ * Storey height of the queried level plus the level-above's covering
136
+ * (non-recessed) slabs. `null` when `levelId` doesn't resolve to a level.
137
+ * A missing level above yields an empty slab list, not `null` — the
138
+ * storey height is still meaningful for the clamp bound.
139
+ */
140
+ function resolveCoveringSlabContext(levelId, nodes) {
141
+ const level = nodes[levelId];
142
+ if (level?.type !== 'level')
143
+ return null;
144
+ const above = getLevelAbove(levelId, nodes);
145
+ const slabs = [];
146
+ for (const childId of above?.children ?? []) {
147
+ const child = nodes[childId];
148
+ if (child?.type !== 'slab')
149
+ continue;
150
+ const slab = child;
151
+ // Recessed slabs (pools) are open shells, not covering solids.
152
+ if (slab.recessed === true)
153
+ continue;
154
+ if (slab.polygon.length < 3)
155
+ continue;
156
+ slabs.push(slab);
157
+ }
158
+ return { storeyHeight: getStoredLevelHeight(level), slabs };
159
+ }
160
+ /**
161
+ * Underside of `slab`'s solid in the QUERIED level's local Y. The solid
162
+ * occupies `[elevation - thickness, elevation]` in ITS level's local Y,
163
+ * which sits `storeyHeight` above the queried level's floor.
164
+ */
165
+ function coveringUndersideY(storeyHeight, slab) {
166
+ return storeyHeight + ((slab.elevation ?? 0.05) - (slab.thickness ?? 0.05));
167
+ }
168
+ /**
169
+ * Whether `slab`'s stored footprint (polygon minus holes) covers `[x, z]`.
170
+ * Ray-cast pointInPolygon flips arbitrarily for points exactly ON the
171
+ * boundary (min-side edges read inside, max-side edges outside), so
172
+ * boundary contact counts as covered explicitly — the same convention as
173
+ * the slab-support interval classification. A point on a hole's rim keeps
174
+ * coverage (mirrors the support election's hole handling).
175
+ *
176
+ * Raw stored polygon + holes on purpose (mirrors getCeilingAt): the
177
+ * clamp bound doesn't need the rendered footprint's junction trims,
178
+ * and staying off the render path keeps this query cheap and pure.
179
+ */
180
+ function slabCoversPoint(slab, x, z) {
181
+ if (!pointInPolygon(x, z, slab.polygon) && !pointOnPolygonBoundary(x, z, slab.polygon)) {
182
+ return false;
183
+ }
184
+ for (const hole of slab.holes ?? []) {
185
+ if (hole.length < 3)
186
+ continue;
187
+ if (pointInPolygon(x, z, hole) && !pointOnPolygonBoundary(x, z, hole))
188
+ return false;
189
+ }
190
+ return true;
191
+ }
192
+ /**
193
+ * Lowest underside among `slabs` covering `[x, z]`, in the queried
194
+ * level's local Y, or `null` when none covers the point.
195
+ */
196
+ function lowestCoveringUndersideAt(context, x, z) {
197
+ let lowest = null;
198
+ for (const slab of context.slabs) {
199
+ if (!slabCoversPoint(slab, x, z))
200
+ continue;
201
+ const underside = coveringUndersideY(context.storeyHeight, slab);
202
+ if (lowest === null || underside < lowest)
203
+ lowest = underside;
204
+ }
205
+ return lowest;
206
+ }
207
+ /**
208
+ * Underside of the LOWEST slab from the level above that covers
209
+ * level-local point `[x, z]`, expressed in the queried level's local Y:
210
+ * `storeyHeight + (slab.elevation - slab.thickness)`. `recessed` slabs
211
+ * (pools) never cover. `null` when no covering slab (or no level above).
212
+ *
213
+ * Coordinate spaces: levels stack in Y only (`LevelNode` carries no XZ
214
+ * transform and the viewer's LevelSystem writes only `position.y`), so a
215
+ * level-local `[x, z]` is valid in every level of the stack unchanged.
216
+ */
217
+ export function getCoveringSlabUndersideAt(levelId, nodes, x, z) {
218
+ const context = resolveCoveringSlabContext(levelId, nodes);
219
+ if (!context)
220
+ return null;
221
+ return lowestCoveringUndersideAt(context, x, z);
222
+ }
223
+ /**
224
+ * Top plane for a plane-bound wall on `levelId`, in level-local Y:
225
+ * `min(stored storey height, lowest covering-slab underside over the wall's
226
+ * span)` — a thick or flush slab on the level above SHORTENS the walls below
227
+ * instead of colliding with them (Revit-style automatic attach).
228
+ *
229
+ * Coverage: the wall's thickness band (centerline + face lines, arc-aware)
230
+ * is clipped against each covering slab's stored polygon minus holes via
231
+ * {@link wallOverlapsSlabFootprint} — the same overlap machinery as the
232
+ * support election. Point sampling is deliberately avoided: auto-slab
233
+ * polygons derive from wall CENTERLINES, so perimeter walls sit exactly ON
234
+ * the polygon boundary, where ray-cast point-in-polygon flips with the
235
+ * edge's orientation (one wall clamped, its neighbor didn't). Boundary
236
+ * contact counts as covered on every side of the slab.
237
+ *
238
+ * This is THE plane for a plane-bound wall (`height` absent). Explicit-height
239
+ * walls ignore the value (`resolveWallTop` returns their stored height), so
240
+ * passing it wherever a raw storey height feeds `resolveWallTop` /
241
+ * `resolveWallEffectiveHeight` is always safe. Falls back to
242
+ * {@link DEFAULT_LEVEL_HEIGHT} when `levelId` doesn't resolve to a level.
243
+ */
244
+ export function getWallPlaneTop(wall, levelId, nodes) {
245
+ const context = resolveCoveringSlabContext(levelId, nodes);
246
+ if (!context)
247
+ return DEFAULT_LEVEL_HEIGHT;
248
+ let plane = context.storeyHeight;
249
+ for (const slab of context.slabs) {
250
+ const underside = coveringUndersideY(context.storeyHeight, slab);
251
+ if (underside >= plane)
252
+ continue;
253
+ if (!wallOverlapsSlabFootprint(wall, slab.polygon, slab.holes))
254
+ continue;
255
+ plane = underside;
256
+ }
257
+ return plane;
258
+ }
259
+ /**
260
+ * Upper bound for a ceiling's stored height over `polygon` on `levelId`:
261
+ * `min(storey plane, lowest covering-slab underside) - CEILING_CLAMP_MARGIN`.
262
+ * The covering underside is sampled at every polygon vertex plus the
263
+ * centroid — cheap, and a slab overlapping a convex-ish ceiling almost
264
+ * always covers one of those points; exact polygon-vs-polygon overlap is
265
+ * not worth its cost for a clamp bound. Ceiling outlines share footprint
266
+ * edges with the slabs above them the same way walls do, so vertices
267
+ * sitting exactly on a slab's boundary count as covered on every side
268
+ * (see `slabCoversPoint`) instead of flipping with the edge orientation.
269
+ *
270
+ * Returns `Infinity` when `levelId` doesn't resolve, so callers clamp
271
+ * against nothing rather than a garbage plane.
272
+ */
273
+ export function getCeilingClampBound(levelId, nodes, polygon) {
274
+ const context = resolveCoveringSlabContext(levelId, nodes);
275
+ if (!context)
276
+ return Number.POSITIVE_INFINITY;
277
+ let bound = context.storeyHeight;
278
+ if (polygon.length > 0) {
279
+ let cx = 0;
280
+ let cz = 0;
281
+ for (const [x, z] of polygon) {
282
+ cx += x;
283
+ cz += z;
284
+ }
285
+ const samples = [
286
+ ...polygon,
287
+ [cx / polygon.length, cz / polygon.length],
288
+ ];
289
+ for (const [x, z] of samples) {
290
+ const underside = lowestCoveringUndersideAt(context, x, z);
291
+ if (underside !== null && underside < bound)
292
+ bound = underside;
293
+ }
294
+ }
295
+ return bound - CEILING_CLAMP_MARGIN;
296
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"node-actions.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-actions.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EASf,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAG9C,KAAK,YAAY,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,CAAA;AAC3D,KAAK,YAAY,GAAG;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAA;AAC7D,KAAK,YAAY,GAAG,SAAS,CAAA;AA0uB7B,eAAO,MAAM,iBAAiB,GAC5B,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,KAAK,YAAY,EAAE,SAkDpB,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,SAAS;IAAE,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;CAAE,SAyIvF,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC5B,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,SAAS;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,EAAE,SAoFrD,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC5B,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,KAAK,SAAS,EAAE,SAqIjB,CAAA"}
1
+ {"version":3,"file":"node-actions.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-actions.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EASf,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAG9C,KAAK,YAAY,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,CAAA;AAC3D,KAAK,YAAY,GAAG;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAA;AAC7D,KAAK,YAAY,GAAG,SAAS,CAAA;AA0vB7B,eAAO,MAAM,iBAAiB,GAC5B,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,KAAK,YAAY,EAAE,SAkDpB,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,SAAS;IAAE,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;CAAE,SAyIvF,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC5B,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,SAAS;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,EAAE,SAoFrD,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC5B,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,KAAK,SAAS,EAAE,SA8JjB,CAAA"}
@@ -343,8 +343,21 @@ function parseCreatedNode(node, parentId) {
343
343
  warnSanitizedNodeMutation('create', node.id, sanitized.issues);
344
344
  return sanitized.value;
345
345
  }
346
+ // An explicit `key: undefined` in update data REMOVES the key: optional
347
+ // fields like wall.height encode a mode by their absence (absent =
348
+ // plane-bound top), and zod's safeParse echoes explicit-undefined keys, so
349
+ // a plain spread would leave a lingering own key that breaks `'height' in
350
+ // node` checks.
351
+ function mergeNodeUpdate(currentNode, patch) {
352
+ const merged = { ...currentNode, ...patch };
353
+ for (const key of Object.keys(patch)) {
354
+ if (patch[key] === undefined)
355
+ delete merged[key];
356
+ }
357
+ return merged;
358
+ }
346
359
  function parseUpdatedNode(currentNode, data) {
347
- const candidate = { ...currentNode, ...data };
360
+ const candidate = mergeNodeUpdate(currentNode, data);
348
361
  const parsed = AnyNodeSchema.safeParse(candidate);
349
362
  if (parsed.success)
350
363
  return parsed.data;
@@ -354,7 +367,7 @@ function parseUpdatedNode(currentNode, data) {
354
367
  return candidate;
355
368
  }
356
369
  warnSanitizedNodeMutation('update', currentNode.id, sanitized.issues);
357
- return { ...currentNode, ...sanitized.value };
370
+ return mergeNodeUpdate(currentNode, sanitized.value);
358
371
  }
359
372
  function shouldRefreshDefaultRidgeVents(data) {
360
373
  return Object.keys(data).some((key) => DEFAULT_RIDGE_VENT_REFRESH_FIELDS.has(key));
@@ -413,7 +426,10 @@ function areWallStylesCompatible(a, b) {
413
426
  return ((a.parentId ?? null) === (b.parentId ?? null) &&
414
427
  Math.abs((a.curveOffset ?? 0) - (b.curveOffset ?? 0)) <= 1e-6 &&
415
428
  Math.abs((a.thickness ?? 0.2) - (b.thickness ?? 0.2)) <= 1e-6 &&
416
- Math.abs((a.height ?? 2.5) - (b.height ?? 2.5)) <= 1e-6 &&
429
+ // Absent height means plane-bound (follows the storey), which must never
430
+ // merge with an explicit height — even one that currently matches the plane.
431
+ (a.height == null) === (b.height == null) &&
432
+ Math.abs((a.height ?? 0) - (b.height ?? 0)) <= 1e-6 &&
417
433
  aInterior === bInterior &&
418
434
  aExterior === bExterior &&
419
435
  a.frontSide === b.frontSide &&
@@ -861,6 +877,34 @@ export const deleteNodesAction = (set, get, ids) => {
861
877
  nodesToMarkDirty.add(update.id);
862
878
  }
863
879
  }
880
+ // Deleting a slab strips `supportSlabId` / `deckSlabId` references from
881
+ // surviving nodes in the same undo commit (mirrors the collectionIds
882
+ // cleanup below), so those nodes re-elect their support / re-derive
883
+ // their rise. Deletion is the ONLY writer — a host merely reshaped away
884
+ // keeps the field and the read path falls back, letting hosting resume
885
+ // if the slab returns.
886
+ const deletedSlabIds = new Set();
887
+ for (const id of allIds) {
888
+ if (nextNodes[id]?.type === 'slab')
889
+ deletedSlabIds.add(id);
890
+ }
891
+ if (deletedSlabIds.size > 0) {
892
+ for (const [nodeId, node] of Object.entries(nextNodes)) {
893
+ if (allIds.has(nodeId))
894
+ continue;
895
+ const patch = {};
896
+ const hostId = node.supportSlabId;
897
+ if (hostId && deletedSlabIds.has(hostId))
898
+ patch.supportSlabId = undefined;
899
+ const deckId = node.deckSlabId;
900
+ if (deckId && deletedSlabIds.has(deckId))
901
+ patch.deckSlabId = undefined;
902
+ if (Object.keys(patch).length > 0) {
903
+ nextNodes[nodeId] = { ...node, ...patch };
904
+ nodesToMarkDirty.add(nodeId);
905
+ }
906
+ }
907
+ }
864
908
  for (const id of allIds) {
865
909
  const node = nextNodes[id];
866
910
  if (!node)
@@ -1,3 +1,20 @@
1
+ import type { Collection, CollectionId } from '../schema/collections';
2
+ import type { SceneMaterial, SceneMaterialId } from '../schema/scene-material';
3
+ import type { AnyNode, AnyNodeId } from '../schema/types';
4
+ export type SceneSnapshot = {
5
+ nodes: Record<AnyNodeId, AnyNode>;
6
+ rootNodeIds: AnyNodeId[];
7
+ collections: Record<CollectionId, Collection>;
8
+ materials: Record<SceneMaterialId, SceneMaterial>;
9
+ installedPlugins: string[];
10
+ };
11
+ export type SceneCommitOrigin = 'local' | 'load' | 'host';
12
+ export type SceneCommit = {
13
+ origin: SceneCommitOrigin;
14
+ before: SceneSnapshot;
15
+ current: SceneSnapshot;
16
+ };
17
+ export type SceneCommitListener = (commit: SceneCommit) => void;
1
18
  type TemporalStoreLike = {
2
19
  temporal: {
3
20
  getState(): {
@@ -16,6 +33,9 @@ type TemporalHistoryStoreLike<TPastState> = {
16
33
  }): void;
17
34
  };
18
35
  };
36
+ export declare function areSceneSnapshotsEqual(left: SceneSnapshot, right: SceneSnapshot): boolean;
37
+ export declare function subscribeSceneCommits(listener: SceneCommitListener): () => void;
38
+ export declare function notifySceneCommit(commit: SceneCommit): void;
19
39
  export declare function pauseSceneHistory(sceneStore: TemporalStoreLike): void;
20
40
  export declare function resumeSceneHistory(sceneStore: TemporalStoreLike): void;
21
41
  export declare function getSceneHistoryPauseDepth(): number;
@@ -1 +1 @@
1
- {"version":3,"file":"history-control.d.ts","sourceRoot":"","sources":["../../src/store/history-control.ts"],"names":[],"mappings":"AAEA,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE;QACR,QAAQ,IAAI;YACV,KAAK,IAAI,IAAI,CAAA;YACb,MAAM,IAAI,IAAI,CAAA;SACf,CAAA;KACF,CAAA;CACF,CAAA;AAED,KAAK,wBAAwB,CAAC,UAAU,IAAI;IAC1C,QAAQ,EAAE;QACR,QAAQ,IAAI;YACV,UAAU,EAAE,UAAU,EAAE,CAAA;SACzB,CAAA;QACD,QAAQ,CAAC,KAAK,EAAE;YAAE,UAAU,EAAE,UAAU,EAAE,CAAA;SAAE,GAAG,IAAI,CAAA;KACpD,CAAA;CACF,CAAA;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAKrE;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAStE;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAkBD,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,OAAO,EAC7D,UAAU,EAAE,wBAAwB,CAAC,UAAU,CAAC,EAChD,GAAG,EAAE,MAAM,OAAO,GACjB,OAAO,CAeT"}
1
+ {"version":3,"file":"history-control.d.ts","sourceRoot":"","sources":["../../src/store/history-control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC9E,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAIzD,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IACjC,WAAW,EAAE,SAAS,EAAE,CAAA;IACxB,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;IAC7C,SAAS,EAAE,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,CAAA;IACjD,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;AAEzD,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,iBAAiB,CAAA;IACzB,MAAM,EAAE,aAAa,CAAA;IACrB,OAAO,EAAE,aAAa,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAA;AAE/D,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE;QACR,QAAQ,IAAI;YACV,KAAK,IAAI,IAAI,CAAA;YACb,MAAM,IAAI,IAAI,CAAA;SACf,CAAA;KACF,CAAA;CACF,CAAA;AAED,KAAK,wBAAwB,CAAC,UAAU,IAAI;IAC1C,QAAQ,EAAE;QACR,QAAQ,IAAI;YACV,UAAU,EAAE,UAAU,EAAE,CAAA;SACzB,CAAA;QACD,QAAQ,CAAC,KAAK,EAAE;YAAE,UAAU,EAAE,UAAU,EAAE,CAAA;SAAE,GAAG,IAAI,CAAA;KACpD,CAAA;CACF,CAAA;AA+BD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAQzF;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,IAAI,CAK/E;AAYD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAiB3D;AAyBD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAKrE;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAStE;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAkBD,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,OAAO,EAC7D,UAAU,EAAE,wBAAwB,CAAC,UAAU,CAAC,EAChD,GAAG,EAAE,MAAM,OAAO,GACjB,OAAO,CAuBT"}