@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
@@ -1 +1 @@
1
- {"version":3,"file":"stair-opening-preview.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7E,OAA6B,EAAE,KAAK,iBAAiB,EAAE,MAAM,qCAAqC,CAAA;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAGpE,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,CAAA;CACtC,CAAA;AAoBD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,WAYvC;AAED,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBvC;AAED,wBAAgB,qCAAqC;;mBAUlC,oBAAoB,EAAE;;EAiCxC"}
1
+ {"version":3,"file":"stair-opening-preview.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7E,OAA6B,EAAE,KAAK,iBAAiB,EAAE,MAAM,qCAAqC,CAAA;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAGpE,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,CAAA;CACtC,CAAA;AAoBD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,WAYvC;AAED,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBvC;AAED,wBAAgB,qCAAqC;;mBAUlC,oBAAoB,EAAE;;EAiCxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"stair-opening-sync.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,WAAW,EACX,QAAQ,EAIT,MAAM,cAAc,CAAA;AA0qBrB,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;QAQvC,SAAS;UAAQ,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC;IAqG5E"}
1
+ {"version":3,"file":"stair-opening-sync.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,WAAW,EACX,QAAQ,EAIT,MAAM,cAAc,CAAA;AA8qBrB,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;QAQvC,SAAS;UAAQ,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC;IAqG5E"}
@@ -1,7 +1,9 @@
1
1
  import { resolveBuildingForLevel, resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync';
2
2
  import { polygonContainsPolygon, polygonsOverlap } from '../../lib/polygon-relations';
3
- import { DEFAULT_WALL_HEIGHT } from '../wall/wall-footprint';
3
+ import { resolveCeilingHeight } from '../../services/level-height';
4
+ import { getLevelElevations } from '../../services/storey';
4
5
  import { computeSegmentTransforms, rotateXZ } from './stair-footprint';
6
+ import { resolveStairTotalRise } from './stair-rise';
5
7
  const CURVED_STAIR_SLAB_OPENING_RATIO = 0.8;
6
8
  const STRAIGHT_STAIR_TARGET_THRESHOLD_MIN = 0.35;
7
9
  const STAIR_SLAB_OPENING_TIGHTENING = 0;
@@ -333,7 +335,7 @@ function getStraightOpeningPolygonsForSurface(stair, nodes, targetElevation, ope
333
335
  const layouts = getStraightStairLayouts(stair, nodes);
334
336
  if (layouts.length === 0)
335
337
  return [];
336
- const riserHeight = (stair.totalRise ?? 2.5) / Math.max(stair.stepCount ?? 10, 1);
338
+ const riserHeight = resolveStairTotalRise(stair, nodes) / Math.max(stair.stepCount ?? 10, 1);
337
339
  const targetThreshold = Math.max(riserHeight * 2, STRAIGHT_STAIR_TARGET_THRESHOLD_MIN);
338
340
  const openingOffset = Math.max(openingOffsetOverride ?? stair.openingOffset ?? 0, 0);
339
341
  const openingRects = [];
@@ -422,25 +424,26 @@ function getApplicableStairOpeningPolygons(stair, nodes, targetElevation, surfac
422
424
  }
423
425
  function getTargetSlabElevationForStair(stair, slab, slabLevelId, nodes) {
424
426
  const { fromLevelId } = getResolvedStairLevelIds(stair, nodes);
425
- const fromLevel = getLevelNumber(fromLevelId, nodes);
426
- const slabLevel = getLevelNumber(slabLevelId, nodes);
427
- if (fromLevel === undefined || slabLevel === undefined) {
427
+ const elevations = getLevelElevations(nodes);
428
+ const fromElevation = fromLevelId ? elevations.get(fromLevelId) : undefined;
429
+ const slabElevation = elevations.get(slabLevelId);
430
+ if (!fromElevation || !slabElevation || fromElevation.buildingId !== slabElevation.buildingId) {
428
431
  return slab.elevation ?? 0.05;
429
432
  }
430
- return ((slabLevel - fromLevel) * DEFAULT_WALL_HEIGHT +
431
- (slab.elevation ?? 0.05) -
432
- (stair.position[1] ?? 0));
433
+ return (slabElevation.baseY - fromElevation.baseY + (slab.elevation ?? 0.05) - (stair.position[1] ?? 0));
433
434
  }
434
435
  function getTargetCeilingElevationForStair(stair, ceiling, ceilingLevelId, nodes) {
435
436
  const { fromLevelId } = getResolvedStairLevelIds(stair, nodes);
436
- const fromLevel = getLevelNumber(fromLevelId, nodes);
437
- const ceilingLevel = getLevelNumber(ceilingLevelId, nodes);
438
- if (fromLevel === undefined || ceilingLevel === undefined) {
439
- return ceiling.height ?? DEFAULT_WALL_HEIGHT;
437
+ const elevations = getLevelElevations(nodes);
438
+ const fromElevation = fromLevelId ? elevations.get(fromLevelId) : undefined;
439
+ const ceilingElevation = elevations.get(ceilingLevelId);
440
+ const ceilingHeight = resolveCeilingHeight(ceiling, nodes);
441
+ if (!fromElevation ||
442
+ !ceilingElevation ||
443
+ fromElevation.buildingId !== ceilingElevation.buildingId) {
444
+ return ceilingHeight;
440
445
  }
441
- return ((ceilingLevel - fromLevel) * DEFAULT_WALL_HEIGHT +
442
- (ceiling.height ?? DEFAULT_WALL_HEIGHT) -
443
- (stair.position[1] ?? 0));
446
+ return ceilingElevation.baseY - fromElevation.baseY + ceilingHeight - (stair.position[1] ?? 0);
444
447
  }
445
448
  function shouldApplyStairToSlab(stair, slabLevelId, nodes) {
446
449
  const { fromLevelId, toLevelId } = getResolvedStairLevelIds(stair, nodes);
@@ -1 +1 @@
1
- {"version":3,"file":"stair-opening-system.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-system.tsx"],"names":[],"mappings":"AA2CA,eAAO,MAAM,kBAAkB,YAyF9B,CAAA"}
1
+ {"version":3,"file":"stair-opening-system.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-system.tsx"],"names":[],"mappings":"AA4CA,eAAO,MAAM,kBAAkB,YAoH9B,CAAA"}
@@ -6,6 +6,7 @@ import useLiveTransforms from '../../store/use-live-transforms';
6
6
  import useScene from '../../store/use-scene';
7
7
  import { createSurfaceOpeningPreviewController, getNodesWithLiveStairOpeningInputs, hasLiveStairOpeningInputs, } from './stair-opening-preview';
8
8
  import { syncAutoStairOpenings } from './stair-opening-sync';
9
+ import { syncStairRises } from './stair-rise';
9
10
  function isOpeningRelevantNode(node) {
10
11
  return (node?.type === 'building' ||
11
12
  node?.type === 'ceiling' ||
@@ -77,15 +78,41 @@ export const StairOpeningSystem = () => {
77
78
  }
78
79
  applyPreviewUpdates(syncAutoStairOpenings(getNodesWithLiveStairOpeningInputs(nodes, liveTransforms, liveOverrides, previewSurfaceIds)));
79
80
  };
80
- applyUpdates(syncAutoStairOpenings(useScene.getState().nodes));
81
- refreshLivePreview();
81
+ const runAutoSync = () => {
82
+ // Rise first: straight stairs converge their flight heights to the
83
+ // resolved rise (level height or deck elevation), and the opening pass
84
+ // reads those segment heights — so it must run against the post-rise
85
+ // nodes.
86
+ applyUpdates(syncStairRises(useScene.getState().nodes));
87
+ applyUpdates(syncAutoStairOpenings(useScene.getState().nodes));
88
+ };
89
+ let disposed = false;
90
+ let autoSyncQueued = false;
91
+ const scheduleAutoSync = () => {
92
+ if (autoSyncQueued)
93
+ return;
94
+ autoSyncQueued = true;
95
+ // One microtask later so every other scene-store listener for the
96
+ // triggering transition (and, at mount, the editor's spatial-grid
97
+ // init) runs first — the spatial-grid sync in particular. The
98
+ // deck-attached rise elects the stair's floor-stack base elevation
99
+ // through the spatial grid; syncing before the grid listener would
100
+ // rescale flights against the pre-transition slab state.
101
+ queueMicrotask(() => {
102
+ autoSyncQueued = false;
103
+ if (disposed)
104
+ return;
105
+ runAutoSync();
106
+ refreshLivePreview();
107
+ });
108
+ };
109
+ scheduleAutoSync();
82
110
  const unsubscribeScene = useScene.subscribe((state, prevState) => {
83
111
  if (syncingAutoOpeningsRef.current)
84
112
  return;
85
113
  if (!hasOpeningRelevantNodeChange(state.nodes, prevState.nodes))
86
114
  return;
87
- applyUpdates(syncAutoStairOpenings(state.nodes));
88
- refreshLivePreview();
115
+ scheduleAutoSync();
89
116
  });
90
117
  const unsubscribeLiveTransforms = useLiveTransforms.subscribe(() => {
91
118
  refreshLivePreview();
@@ -94,6 +121,7 @@ export const StairOpeningSystem = () => {
94
121
  refreshLivePreview();
95
122
  });
96
123
  return () => {
124
+ disposed = true;
97
125
  unsubscribeScene();
98
126
  unsubscribeLiveTransforms();
99
127
  unsubscribeLiveOverrides();
@@ -0,0 +1,22 @@
1
+ import type { AnyNode, AnyNodeId, StairNode } from '../../schema';
2
+ export declare function resolveStairTotalRise(stair: StairNode, nodes: Record<string, AnyNode>): number;
3
+ /**
4
+ * Keeps straight stairs' flight segments in step with the resolved rise.
5
+ * Straight-stair geometry derives from per-segment heights (not from
6
+ * `resolveStairTotalRise`), so level-height and deck-elevation changes must
7
+ * write through to the flight segments — curved/spiral stairs read the
8
+ * resolved rise directly and need no sync.
9
+ *
10
+ * Scope: stairs whose total the system owns — follows-mode stairs (absent
11
+ * `totalRise`, tracking their level or their deck) and deck-attached stairs
12
+ * (an explicit rise converges to the typed value). A detached stair with an
13
+ * explicit `totalRise` is the one place hand-edited segment chains are
14
+ * legitimate, so it is never touched. Flight heights scale proportionally
15
+ * (landings keep theirs); returns `updateNodes` patches, empty when every
16
+ * stair is already in step.
17
+ */
18
+ export declare function syncStairRises(nodes: Record<string, AnyNode>): Array<{
19
+ id: AnyNodeId;
20
+ data: Partial<AnyNode>;
21
+ }>;
22
+ //# sourceMappingURL=stair-rise.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stair-rise.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-rise.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAoB,MAAM,cAAc,CAAA;AAInF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CA+B9F;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,KAAK,CAAC;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAgClD"}
@@ -0,0 +1,79 @@
1
+ import { getFloorStackedPosition } from '../../hooks/spatial-grid/floor-placed-elevation';
2
+ import { DEFAULT_LEVEL_HEIGHT } from '../../services/level-height';
3
+ import { getStoredLevelHeight } from '../../services/storey';
4
+ export function resolveStairTotalRise(stair, nodes) {
5
+ if (stair.totalRise !== undefined)
6
+ return stair.totalRise;
7
+ const level = Object.values(nodes).find((node) => node.type === 'level' && node.children.includes(stair.id));
8
+ if (stair.deckSlabId) {
9
+ const deck = nodes[stair.deckSlabId];
10
+ // The deck's `elevation` IS its walking surface (level-local), but the
11
+ // stair's own base may be lifted onto a floor slab by the floor-stack
12
+ // (`FloorElevationSystem` / `syncStairGroupElevation` put the group at
13
+ // `position[1] + elected slab elevation`). The rise is measured from
14
+ // that base, so subtract it — electing the base exactly the way the
15
+ // visual systems do (persisted `supportSlabId` honored, uncapped
16
+ // election otherwise) keeps base + rise landing precisely on the deck's
17
+ // walking surface. A stale reference (deck gone) falls through to the
18
+ // level-derived rise.
19
+ if (deck?.type === 'slab') {
20
+ const baseElevation = getFloorStackedPosition({
21
+ node: stair,
22
+ nodes,
23
+ position: stair.position,
24
+ rotation: stair.rotation,
25
+ levelId: level?.id ?? null,
26
+ })[1];
27
+ return (deck.elevation ?? 0.05) - baseElevation;
28
+ }
29
+ }
30
+ return level?.type === 'level' ? getStoredLevelHeight(level) : DEFAULT_LEVEL_HEIGHT;
31
+ }
32
+ const RISE_SYNC_EPSILON = 1e-4;
33
+ /**
34
+ * Keeps straight stairs' flight segments in step with the resolved rise.
35
+ * Straight-stair geometry derives from per-segment heights (not from
36
+ * `resolveStairTotalRise`), so level-height and deck-elevation changes must
37
+ * write through to the flight segments — curved/spiral stairs read the
38
+ * resolved rise directly and need no sync.
39
+ *
40
+ * Scope: stairs whose total the system owns — follows-mode stairs (absent
41
+ * `totalRise`, tracking their level or their deck) and deck-attached stairs
42
+ * (an explicit rise converges to the typed value). A detached stair with an
43
+ * explicit `totalRise` is the one place hand-edited segment chains are
44
+ * legitimate, so it is never touched. Flight heights scale proportionally
45
+ * (landings keep theirs); returns `updateNodes` patches, empty when every
46
+ * stair is already in step.
47
+ */
48
+ export function syncStairRises(nodes) {
49
+ const updates = [];
50
+ for (const node of Object.values(nodes)) {
51
+ if (node.type !== 'stair' || node.stairType !== 'straight')
52
+ continue;
53
+ const deck = node.deckSlabId ? nodes[node.deckSlabId] : undefined;
54
+ if (node.totalRise !== undefined && deck?.type !== 'slab')
55
+ continue;
56
+ const segments = (node.children ?? [])
57
+ .map((childId) => nodes[childId])
58
+ .filter((child) => child?.type === 'stair-segment');
59
+ const flights = segments.filter((segment) => segment.segmentType === 'stair');
60
+ if (flights.length === 0)
61
+ continue;
62
+ const landingRise = segments
63
+ .filter((segment) => segment.segmentType !== 'stair')
64
+ .reduce((sum, segment) => sum + segment.height, 0);
65
+ const flightRise = flights.reduce((sum, segment) => sum + segment.height, 0);
66
+ const targetFlightRise = resolveStairTotalRise(node, nodes) - landingRise;
67
+ if (targetFlightRise <= 0)
68
+ continue;
69
+ if (Math.abs(flightRise - targetFlightRise) <= RISE_SYNC_EPSILON)
70
+ continue;
71
+ for (const flight of flights) {
72
+ const height = flightRise > RISE_SYNC_EPSILON
73
+ ? flight.height * (targetFlightRise / flightRise)
74
+ : targetFlightRise / flights.length;
75
+ updates.push({ id: flight.id, data: { height } });
76
+ }
77
+ }
78
+ return updates;
79
+ }
@@ -34,6 +34,16 @@ export declare function getWallChordFrame(wall: WallCurveLike): {
34
34
  };
35
35
  length: number;
36
36
  };
37
+ export declare function getWallArcData(wall: WallCurveLike): {
38
+ center: {
39
+ x: number;
40
+ y: number;
41
+ };
42
+ radius: number;
43
+ startAngle: number;
44
+ delta: number;
45
+ direction: number;
46
+ } | null;
37
47
  export declare function getWallCurveFrameAt(wall: WallCurveLike, t: number): CurveFrame;
38
48
  export declare function getWallMidpointHandlePoint(wall: WallCurveLike): Point2D;
39
49
  export declare function sampleWallCenterline(wall: WallCurveLike, segments?: number): Point2D[];
@@ -1 +1 @@
1
- {"version":3,"file":"wall-curve.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-curve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAK9C,KAAK,aAAa,GAAG,IAAI,CAAC,QAAQ,GAAG,SAAS,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,CAAC,CAAA;AAEhF,KAAK,UAAU,GAAG;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,KAAK,yBAAyB,GAAG;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAcD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAE9D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAE5D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,UAErD;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,aAAa,UAExD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,aAAa,UAE5D;AAWD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,UAG3E;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,aAAa,UAI5D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,WAE/C;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa;;;;;;;;;;;;;EA6BpD;AA+BD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAwC9E;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,aAAa,WAE7D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,SAA0B,aAM3F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,SAA0B,UASzF;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,IAAI,CAAC,QAAQ,GAAG,SAAS,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,GAAG,WAAW,CAAC,EAC/E,QAAQ,SAA0B,EAClC,cAAc,CAAC,EAAE,yBAAyB,aA2B3C"}
1
+ {"version":3,"file":"wall-curve.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-curve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAK9C,KAAK,aAAa,GAAG,IAAI,CAAC,QAAQ,GAAG,SAAS,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,CAAC,CAAA;AAEhF,KAAK,UAAU,GAAG;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,KAAK,yBAAyB,GAAG;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAcD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAE9D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAE5D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,UAErD;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,aAAa,UAExD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,aAAa,UAE5D;AAWD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,UAG3E;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,aAAa,UAI5D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,WAE/C;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa;;;;;;;;;;;;;EA6BpD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa;;;;;;;;;SA2BjD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAwC9E;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,aAAa,WAE7D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,SAA0B,aAM3F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,SAA0B,UASzF;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,IAAI,CAAC,QAAQ,GAAG,SAAS,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,GAAG,WAAW,CAAC,EAC/E,QAAQ,SAA0B,EAClC,cAAc,CAAC,EAAE,yBAAyB,aA2B3C"}
@@ -71,7 +71,7 @@ export function getWallChordFrame(wall) {
71
71
  length,
72
72
  };
73
73
  }
74
- function getWallArcData(wall) {
74
+ export function getWallArcData(wall) {
75
75
  const chord = getWallChordFrame(wall);
76
76
  const sagitta = getClampedWallCurveOffset(wall);
77
77
  if (Math.abs(sagitta) <= CURVE_EPSILON || chord.length < CURVE_EPSILON) {
@@ -117,10 +117,8 @@ function getWallBoundaryFrame(wall, endType) {
117
117
  const point = endType === 'start'
118
118
  ? { x: wall.start[0], y: wall.start[1] }
119
119
  : { x: wall.end[0], y: wall.end[1] };
120
- const vector = endType === 'start'
121
- ? { x: wall.end[0] - wall.start[0], y: wall.end[1] - wall.start[1] }
122
- : { x: wall.start[0] - wall.end[0], y: wall.start[1] - wall.end[1] };
123
- const length = Math.hypot(vector.x, vector.y);
120
+ const direction = { x: wall.end[0] - wall.start[0], y: wall.end[1] - wall.start[1] };
121
+ const length = Math.hypot(direction.x, direction.y);
124
122
  if (length < 1e-9) {
125
123
  return {
126
124
  point,
@@ -130,8 +128,10 @@ function getWallBoundaryFrame(wall, endType) {
130
128
  }
131
129
  return {
132
130
  point,
133
- tangent: { x: vector.x / length, y: vector.y / length },
134
- normal: { x: -vector.y / length, y: vector.x / length },
131
+ tangent: endType === 'start'
132
+ ? { x: direction.x / length, y: direction.y / length }
133
+ : { x: -direction.x / length, y: -direction.y / length },
134
+ normal: { x: -direction.y / length, y: direction.x / length },
135
135
  };
136
136
  }
137
137
  function calculateJunctionIntersections(junction, getThickness) {
@@ -0,0 +1,40 @@
1
+ import type { WallNode } from '../../schema/nodes/wall';
2
+ /**
3
+ * Minimum wall body height in meters. Governs both the wall height
4
+ * arrow's lower drag bound and the slab-elevation clamp: a slab may not
5
+ * rise past `storeyHeight - MIN_WALL_HEIGHT` while a plane-bound wall
6
+ * elects it as its base, or the wall's extrusion (plane minus base)
7
+ * would collapse below this minimum.
8
+ */
9
+ export declare const MIN_WALL_HEIGHT = 0.5;
10
+ /**
11
+ * Wall-top inversion (vertical building model): a wall with no stored
12
+ * `height` is plane-bound — its top sits at the storey plane (level-local
13
+ * Y = the level's stored height), so a slab lifting the wall's base makes
14
+ * the wall shorter, never taller, and no gap can open at the top of a
15
+ * level. A wall WITH `height` is an explicit exception (half wall,
16
+ * parapet) and keeps the legacy semantics: the top rides a raised elected
17
+ * base (`electedBase + height`), while a zero or sunken slab base leaves
18
+ * the top at `height` (the legacy negative-slab constraint). Explicit
19
+ * ground-hosted walls are the terrain exception: `height` is always body
20
+ * height, including below datum, so sculpting cannot stretch the wall.
21
+ *
22
+ * Returns the top in level-local Y (same frame as `electedBase`).
23
+ */
24
+ export declare function resolveWallTop(wall: Pick<WallNode, 'height' | 'supportSlabId'>, storeyHeight: number, electedBase: number): number;
25
+ /**
26
+ * Extruded height of the wall body: {@link resolveWallTop} minus the
27
+ * elected base. Base convention: the elected slab-support elevation itself
28
+ * — the viewer computes `effectiveBaseElevation = min(baseElevation,
29
+ * slabElevation)` and defaults `baseElevation` to the elected elevation,
30
+ * so with only the election in hand the two coincide. Fill-down below the
31
+ * elected base (`baseSegments`) is a geometry detail the extruder handles
32
+ * separately and never changes where the top sits.
33
+ *
34
+ * Equivalently: the wall-local Y of the wall's top, measured from the wall
35
+ * mesh origin (which sits at `electedBase`). May be non-positive when a
36
+ * slab reaches the storey plane; callers own the degenerate-geometry
37
+ * policy.
38
+ */
39
+ export declare function resolveWallEffectiveHeight(wall: Pick<WallNode, 'height' | 'supportSlabId'>, storeyHeight: number, electedBase: number): number;
40
+ //# sourceMappingURL=wall-top.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wall-top.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-top.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAEvD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,MAAM,CAAA;AAElC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,eAAe,CAAC,EAChD,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,MAAM,CAIR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,eAAe,CAAC,EAChD,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,MAAM,CAER"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Minimum wall body height in meters. Governs both the wall height
3
+ * arrow's lower drag bound and the slab-elevation clamp: a slab may not
4
+ * rise past `storeyHeight - MIN_WALL_HEIGHT` while a plane-bound wall
5
+ * elects it as its base, or the wall's extrusion (plane minus base)
6
+ * would collapse below this minimum.
7
+ */
8
+ export const MIN_WALL_HEIGHT = 0.5;
9
+ /**
10
+ * Wall-top inversion (vertical building model): a wall with no stored
11
+ * `height` is plane-bound — its top sits at the storey plane (level-local
12
+ * Y = the level's stored height), so a slab lifting the wall's base makes
13
+ * the wall shorter, never taller, and no gap can open at the top of a
14
+ * level. A wall WITH `height` is an explicit exception (half wall,
15
+ * parapet) and keeps the legacy semantics: the top rides a raised elected
16
+ * base (`electedBase + height`), while a zero or sunken slab base leaves
17
+ * the top at `height` (the legacy negative-slab constraint). Explicit
18
+ * ground-hosted walls are the terrain exception: `height` is always body
19
+ * height, including below datum, so sculpting cannot stretch the wall.
20
+ *
21
+ * Returns the top in level-local Y (same frame as `electedBase`).
22
+ */
23
+ export function resolveWallTop(wall, storeyHeight, electedBase) {
24
+ if (wall.height == null)
25
+ return storeyHeight;
26
+ if (wall.supportSlabId === 'ground')
27
+ return electedBase + wall.height;
28
+ return electedBase > 0 ? electedBase + wall.height : wall.height;
29
+ }
30
+ /**
31
+ * Extruded height of the wall body: {@link resolveWallTop} minus the
32
+ * elected base. Base convention: the elected slab-support elevation itself
33
+ * — the viewer computes `effectiveBaseElevation = min(baseElevation,
34
+ * slabElevation)` and defaults `baseElevation` to the elected elevation,
35
+ * so with only the election in hand the two coincide. Fill-down below the
36
+ * elected base (`baseSegments`) is a geometry detail the extruder handles
37
+ * separately and never changes where the top sits.
38
+ *
39
+ * Equivalently: the wall-local Y of the wall's top, measured from the wall
40
+ * mesh origin (which sits at `electedBase`). May be non-positive when a
41
+ * slab reaches the storey plane; callers own the degenerate-geometry
42
+ * policy.
43
+ */
44
+ export function resolveWallEffectiveHeight(wall, storeyHeight, electedBase) {
45
+ return resolveWallTop(wall, storeyHeight, electedBase) - electedBase;
46
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"clone-scene-graph.d.ts","sourceRoot":"","sources":["../../src/utils/clone-scene-graph.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAEnD,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAErE,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IACjC,WAAW,EAAE,SAAS,EAAE,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;IAC9C,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC5B,CAAA;AAUD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAmHlE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,OAAO,EAAE,SAAS,GACjB;IAAE,WAAW,EAAE,OAAO,EAAE,CAAC;IAAC,UAAU,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAwF/E;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,qBAA0B,GAClC,UAAU,CAqEZ"}
1
+ {"version":3,"file":"clone-scene-graph.d.ts","sourceRoot":"","sources":["../../src/utils/clone-scene-graph.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAEnD,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAErE,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IACjC,WAAW,EAAE,SAAS,EAAE,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;IAC9C,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC5B,CAAA;AAUD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAuIlE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,OAAO,EAAE,SAAS,GACjB;IAAE,WAAW,EAAE,OAAO,EAAE,CAAC;IAAC,UAAU,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAsG/E;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,qBAA0B,GAClC,UAAU,CAqEZ"}
@@ -1,4 +1,5 @@
1
- import { remapMeasurementReferences } from '../lib/measurement-geometry';
1
+ import { GROUND_SUPPORT_ID } from '../hooks/spatial-grid/floor-placed-elevation';
2
+ import { remapConstructionDimensionReferences, remapMeasurementReferences, } from '../lib/measurement-geometry';
2
3
  import { generateId } from '../schema/base';
3
4
  /**
4
5
  * Extracts the type prefix from a node ID (e.g., "wall_abc123" -> "wall")
@@ -29,7 +30,7 @@ export function cloneSceneGraph(sceneGraph) {
29
30
  const clonedNodes = {};
30
31
  for (const [oldId, node] of Object.entries(nodes)) {
31
32
  const newId = idMap.get(oldId);
32
- const clonedNode = structuredClone({ ...node, id: newId });
33
+ let clonedNode = structuredClone({ ...node, id: newId });
33
34
  // Remap parentId
34
35
  if (clonedNode.parentId && typeof clonedNode.parentId === 'string') {
35
36
  clonedNode.parentId = (idMap.get(clonedNode.parentId) ?? null);
@@ -63,9 +64,24 @@ export function cloneSceneGraph(sceneGraph) {
63
64
  ;
64
65
  clonedNode.roofSegmentId = idMap.get(clonedNode.roofSegmentId);
65
66
  }
67
+ // Remap supportSlabId (persisted slab-support hosts). The 'ground'
68
+ // sentinel is not a node id — keep it as-is.
69
+ if ('supportSlabId' in clonedNode &&
70
+ typeof clonedNode.supportSlabId === 'string' &&
71
+ clonedNode.supportSlabId !== GROUND_SUPPORT_ID) {
72
+ ;
73
+ clonedNode.supportSlabId = idMap.get(clonedNode.supportSlabId);
74
+ }
75
+ if ('deckSlabId' in clonedNode && typeof clonedNode.deckSlabId === 'string') {
76
+ ;
77
+ clonedNode.deckSlabId = idMap.get(clonedNode.deckSlabId);
78
+ }
66
79
  if (clonedNode.type === 'measurement') {
67
80
  clonedNode.measurement = remapMeasurementReferences(clonedNode.measurement, idMap);
68
81
  }
82
+ if (clonedNode.type === 'construction-dimension') {
83
+ clonedNode = remapConstructionDimensionReferences(clonedNode, idMap);
84
+ }
69
85
  clonedNodes[newId] = clonedNode;
70
86
  }
71
87
  // Remap root node IDs
@@ -166,7 +182,7 @@ export function cloneLevelSubtree(nodes, levelId) {
166
182
  continue;
167
183
  const newId = idMap.get(oldId);
168
184
  // JSON roundtrip: safely strips functions, Object3D, circular refs, etc.
169
- const cloned = JSON.parse(JSON.stringify(node));
185
+ let cloned = JSON.parse(JSON.stringify(node));
170
186
  cloned.id = newId;
171
187
  // Remap parentId — but only for descendants, not the level node itself
172
188
  // (the level's parentId points to the building, which is outside the subtree)
@@ -201,9 +217,24 @@ export function cloneLevelSubtree(nodes, levelId) {
201
217
  cloned.roofSegmentId =
202
218
  idMap.get(cloned.roofSegmentId) ?? cloned.roofSegmentId;
203
219
  }
220
+ // Remap supportSlabId when the host slab is inside the cloned subtree;
221
+ // preserve it otherwise (like wallId, the reference may point outside).
222
+ if ('supportSlabId' in cloned && typeof cloned.supportSlabId === 'string') {
223
+ ;
224
+ cloned.supportSlabId =
225
+ idMap.get(cloned.supportSlabId) ?? cloned.supportSlabId;
226
+ }
227
+ if ('deckSlabId' in cloned && typeof cloned.deckSlabId === 'string') {
228
+ ;
229
+ cloned.deckSlabId =
230
+ idMap.get(cloned.deckSlabId) ?? cloned.deckSlabId;
231
+ }
204
232
  if (cloned.type === 'measurement') {
205
233
  cloned.measurement = remapMeasurementReferences(cloned.measurement, idMap);
206
234
  }
235
+ if (cloned.type === 'construction-dimension') {
236
+ cloned = remapConstructionDimensionReferences(cloned, idMap);
237
+ }
207
238
  clonedNodes.push(cloned);
208
239
  }
209
240
  return { clonedNodes, newLevelId, idMap };
@@ -2,7 +2,7 @@ export interface HealSceneResult {
2
2
  nodes: Record<string, unknown>;
3
3
  /** Ids of zero-length walls that were dropped. */
4
4
  droppedWallIds: string[];
5
- /** Count of non-string (e.g. null) entries removed from `children` arrays. */
5
+ /** Count of invalid non-string (e.g. null) entries removed from `children` arrays. */
6
6
  strippedChildRefs: number;
7
7
  /**
8
8
  * Count of child references removed because the child's `parentId` points at
@@ -1 +1 @@
1
- {"version":3,"file":"heal-scene-graph.d.ts","sourceRoot":"","sources":["../../src/utils/heal-scene-graph.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,kDAAkD;IAClD,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,8EAA8E;IAC9E,iBAAiB,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,sBAAsB,EAAE,MAAM,CAAA;CAC/B;AAgBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,CA0D9E"}
1
+ {"version":3,"file":"heal-scene-graph.d.ts","sourceRoot":"","sources":["../../src/utils/heal-scene-graph.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,kDAAkD;IAClD,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,sFAAsF;IACtF,iBAAiB,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,sBAAsB,EAAE,MAAM,CAAA;CAC/B;AAgBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,CA2E9E"}
@@ -54,26 +54,42 @@ export function healSceneNodes(input) {
54
54
  const dropped = new Set(droppedWallIds);
55
55
  let strippedChildRefs = 0;
56
56
  let strippedStaleChildRefs = 0;
57
- // Pass 2: clean `children` arrays — drop non-string entries (the `[null]`
58
- // bug), references to walls we just removed, same-array duplicates, and
59
- // stale references whose child's `parentId` names a different parent.
57
+ // Pass 2: clean `children` arrays — drop invalid non-string entries (the
58
+ // `[null]` bug), references to walls we just removed, same-array duplicates,
59
+ // and stale references whose child's `parentId` names a different parent.
60
+ // Legacy sites embedded full child objects; keep those for migrateNodes to
61
+ // flatten after healing instead of disconnecting the entire building.
60
62
  const nodes = {};
61
63
  for (const [id, node] of Object.entries(kept)) {
62
64
  const children = node?.children;
63
65
  if (Array.isArray(children)) {
64
66
  const seen = new Set();
65
- const cleaned = children.filter((c) => {
66
- if (typeof c !== 'string' || dropped.has(c)) {
67
+ const cleaned = children.filter((child) => {
68
+ const embeddedSiteChildId = node.type === 'site' &&
69
+ child &&
70
+ typeof child === 'object' &&
71
+ typeof child.id === 'string'
72
+ ? child.id
73
+ : null;
74
+ if (embeddedSiteChildId) {
75
+ if (seen.has(embeddedSiteChildId)) {
76
+ strippedStaleChildRefs++;
77
+ return false;
78
+ }
79
+ seen.add(embeddedSiteChildId);
80
+ return true;
81
+ }
82
+ if (typeof child !== 'string' || dropped.has(child)) {
67
83
  strippedChildRefs++;
68
84
  return false;
69
85
  }
70
- if (seen.has(c)) {
86
+ if (seen.has(child)) {
71
87
  strippedStaleChildRefs++;
72
88
  return false;
73
89
  }
74
- seen.add(c);
75
- const child = kept[c];
76
- if (child && typeof child.parentId === 'string' && child.parentId !== id) {
90
+ seen.add(child);
91
+ const childNode = kept[child];
92
+ if (childNode && typeof childNode.parentId === 'string' && childNode.parentId !== id) {
77
93
  strippedStaleChildRefs++;
78
94
  return false;
79
95
  }
@@ -9,6 +9,8 @@ export type ValidationIssue = {
9
9
  export type BuildStats = {
10
10
  total: number;
11
11
  byType: Partial<Record<AnyNodeType, number>>;
12
+ /** Kinds outside the static schema union but registered at runtime (plugins). */
13
+ pluginTypes: Record<string, number>;
12
14
  unknownTypes: Record<string, number>;
13
15
  floorAreaM2: number;
14
16
  };
@@ -1 +1 @@
1
- {"version":3,"file":"validate-build-json.d.ts","sourceRoot":"","sources":["../../src/validation/validate-build-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG3D,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,SAAS,CAAA;AAEpD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAA;IAC5C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;IAC9B,KAAK,EAAE,UAAU,CAAA;IACjB,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAA;IAC3B,gBAAgB,EAAE,MAAM,CAAA;CACzB,CAAA;AA8BD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAwPzE"}
1
+ {"version":3,"file":"validate-build-json.d.ts","sourceRoot":"","sources":["../../src/validation/validate-build-json.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG3D,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,SAAS,CAAA;AAEpD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAA;IAC5C,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;IAC9B,KAAK,EAAE,UAAU,CAAA;IACjB,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAA;IAC3B,gBAAgB,EAAE,MAAM,CAAA;CACzB,CAAA;AA8BD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CA8SzE"}