@pascal-app/core 0.9.1 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/README.md +17 -10
  2. package/dist/events/bus.d.ts +49 -2
  3. package/dist/events/bus.d.ts.map +1 -1
  4. package/dist/hooks/scene-registry/item-clip-registry.d.ts +18 -0
  5. package/dist/hooks/scene-registry/item-clip-registry.d.ts.map +1 -0
  6. package/dist/hooks/scene-registry/item-clip-registry.js +10 -0
  7. package/dist/hooks/scene-registry/scene-registry.d.ts +8 -1
  8. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
  9. package/dist/hooks/scene-registry/scene-registry.js +27 -1
  10. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +17 -1
  11. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -1
  12. package/dist/hooks/spatial-grid/floor-placed-elevation.js +61 -8
  13. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +135 -28
  14. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  15. package/dist/hooks/spatial-grid/spatial-grid-manager.js +427 -271
  16. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +46 -1
  17. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  18. package/dist/hooks/spatial-grid/spatial-grid-sync.js +208 -9
  19. package/dist/hooks/spatial-grid/support-host-id.d.ts +3 -0
  20. package/dist/hooks/spatial-grid/support-host-id.d.ts.map +1 -0
  21. package/dist/hooks/spatial-grid/support-host-id.js +2 -0
  22. package/dist/hooks/spatial-grid/support-host-patch.d.ts +51 -0
  23. package/dist/hooks/spatial-grid/support-host-patch.d.ts.map +1 -0
  24. package/dist/hooks/spatial-grid/support-host-patch.js +164 -0
  25. package/dist/index.d.ts +33 -11
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +32 -10
  28. package/dist/lib/level-name.d.ts +4 -0
  29. package/dist/lib/level-name.d.ts.map +1 -0
  30. package/dist/lib/level-name.js +10 -0
  31. package/dist/lib/measurement-geometry.d.ts +23 -0
  32. package/dist/lib/measurement-geometry.d.ts.map +1 -0
  33. package/dist/lib/measurement-geometry.js +249 -0
  34. package/dist/lib/polygon-geometry.d.ts +0 -1
  35. package/dist/lib/polygon-geometry.d.ts.map +1 -1
  36. package/dist/lib/polygon-geometry.js +0 -18
  37. package/dist/lib/selection-proxy.d.ts +4 -0
  38. package/dist/lib/selection-proxy.d.ts.map +1 -0
  39. package/dist/lib/selection-proxy.js +12 -0
  40. package/dist/lib/slab-polygon.d.ts +31 -2
  41. package/dist/lib/slab-polygon.d.ts.map +1 -1
  42. package/dist/lib/slab-polygon.js +557 -31
  43. package/dist/lib/slots.d.ts +14 -0
  44. package/dist/lib/slots.d.ts.map +1 -0
  45. package/dist/lib/slots.js +26 -0
  46. package/dist/lib/space-detection.d.ts +40 -6
  47. package/dist/lib/space-detection.d.ts.map +1 -1
  48. package/dist/lib/space-detection.js +574 -192
  49. package/dist/lib/terrain-brush.d.ts +163 -0
  50. package/dist/lib/terrain-brush.d.ts.map +1 -0
  51. package/dist/lib/terrain-brush.js +359 -0
  52. package/dist/lib/terrain-codec.d.ts +51 -0
  53. package/dist/lib/terrain-codec.d.ts.map +1 -0
  54. package/dist/lib/terrain-codec.js +167 -0
  55. package/dist/lib/terrain-field.d.ts +150 -0
  56. package/dist/lib/terrain-field.d.ts.map +1 -0
  57. package/dist/lib/terrain-field.js +248 -0
  58. package/dist/lib/terrain-raycast.d.ts +44 -0
  59. package/dist/lib/terrain-raycast.d.ts.map +1 -0
  60. package/dist/lib/terrain-raycast.js +188 -0
  61. package/dist/lib/terrain-source.d.ts +60 -0
  62. package/dist/lib/terrain-source.d.ts.map +1 -0
  63. package/dist/lib/terrain-source.js +90 -0
  64. package/dist/lib/terrain-support.d.ts +60 -0
  65. package/dist/lib/terrain-support.d.ts.map +1 -0
  66. package/dist/lib/terrain-support.js +150 -0
  67. package/dist/lib/wall-distance.d.ts +60 -0
  68. package/dist/lib/wall-distance.d.ts.map +1 -0
  69. package/dist/lib/wall-distance.js +85 -0
  70. package/dist/lib/zone-quantities.d.ts +21 -0
  71. package/dist/lib/zone-quantities.d.ts.map +1 -0
  72. package/dist/lib/zone-quantities.js +428 -0
  73. package/dist/material-library.d.ts +28 -1
  74. package/dist/material-library.d.ts.map +1 -1
  75. package/dist/material-library.js +2233 -441
  76. package/dist/registry/handles.d.ts +91 -3
  77. package/dist/registry/handles.d.ts.map +1 -1
  78. package/dist/registry/index.d.ts +3 -3
  79. package/dist/registry/index.d.ts.map +1 -1
  80. package/dist/registry/index.js +1 -1
  81. package/dist/registry/registry.d.ts +46 -3
  82. package/dist/registry/registry.d.ts.map +1 -1
  83. package/dist/registry/registry.js +82 -1
  84. package/dist/registry/subtree.d.ts.map +1 -1
  85. package/dist/registry/subtree.js +8 -1
  86. package/dist/registry/types.d.ts +781 -39
  87. package/dist/registry/types.d.ts.map +1 -1
  88. package/dist/schema/index.d.ts +27 -8
  89. package/dist/schema/index.d.ts.map +1 -1
  90. package/dist/schema/index.js +23 -6
  91. package/dist/schema/material.d.ts +3 -2
  92. package/dist/schema/material.d.ts.map +1 -1
  93. package/dist/schema/material.js +7 -5
  94. package/dist/schema/nodes/box-vent.d.ts +2 -2
  95. package/dist/schema/nodes/cabinet.d.ts +536 -0
  96. package/dist/schema/nodes/cabinet.d.ts.map +1 -0
  97. package/dist/schema/nodes/cabinet.js +139 -0
  98. package/dist/schema/nodes/ceiling.d.ts +4 -3
  99. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  100. package/dist/schema/nodes/ceiling.js +11 -1
  101. package/dist/schema/nodes/chimney.d.ts +4 -4
  102. package/dist/schema/nodes/column.d.ts +4 -2
  103. package/dist/schema/nodes/column.d.ts.map +1 -1
  104. package/dist/schema/nodes/column.js +5 -0
  105. package/dist/schema/nodes/construction-dimension.d.ts +195 -0
  106. package/dist/schema/nodes/construction-dimension.d.ts.map +1 -0
  107. package/dist/schema/nodes/construction-dimension.js +151 -0
  108. package/dist/schema/nodes/cupola.d.ts +2 -2
  109. package/dist/schema/nodes/door.d.ts +42 -5
  110. package/dist/schema/nodes/door.d.ts.map +1 -1
  111. package/dist/schema/nodes/door.js +32 -0
  112. package/dist/schema/nodes/dormer.d.ts +8 -8
  113. package/dist/schema/nodes/dormer.js +1 -1
  114. package/dist/schema/nodes/downspout.d.ts +2 -2
  115. package/dist/schema/nodes/duct-fitting.d.ts +91 -0
  116. package/dist/schema/nodes/duct-fitting.d.ts.map +1 -0
  117. package/dist/schema/nodes/duct-fitting.js +92 -0
  118. package/dist/schema/nodes/duct-segment.d.ts +61 -0
  119. package/dist/schema/nodes/duct-segment.d.ts.map +1 -0
  120. package/dist/schema/nodes/duct-segment.js +73 -0
  121. package/dist/schema/nodes/duct-terminal.d.ts +58 -0
  122. package/dist/schema/nodes/duct-terminal.d.ts.map +1 -0
  123. package/dist/schema/nodes/duct-terminal.js +53 -0
  124. package/dist/schema/nodes/elevator.d.ts +3 -2
  125. package/dist/schema/nodes/elevator.d.ts.map +1 -1
  126. package/dist/schema/nodes/elevator.js +3 -0
  127. package/dist/schema/nodes/eyebrow-vent.d.ts +2 -2
  128. package/dist/schema/nodes/fence.d.ts +20 -2
  129. package/dist/schema/nodes/fence.d.ts.map +1 -1
  130. package/dist/schema/nodes/fence.js +33 -2
  131. package/dist/schema/nodes/gutter.d.ts +2 -2
  132. package/dist/schema/nodes/hvac-equipment.d.ts +64 -0
  133. package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -0
  134. package/dist/schema/nodes/hvac-equipment.js +57 -0
  135. package/dist/schema/nodes/item.d.ts +9 -0
  136. package/dist/schema/nodes/item.d.ts.map +1 -1
  137. package/dist/schema/nodes/item.js +24 -0
  138. package/dist/schema/nodes/level.d.ts +2 -1
  139. package/dist/schema/nodes/level.d.ts.map +1 -1
  140. package/dist/schema/nodes/level.js +12 -31
  141. package/dist/schema/nodes/lineset.d.ts +42 -0
  142. package/dist/schema/nodes/lineset.d.ts.map +1 -0
  143. package/dist/schema/nodes/lineset.js +38 -0
  144. package/dist/schema/nodes/liquid-line.d.ts +34 -0
  145. package/dist/schema/nodes/liquid-line.d.ts.map +1 -0
  146. package/dist/schema/nodes/liquid-line.js +24 -0
  147. package/dist/schema/nodes/measurement.d.ts +306 -0
  148. package/dist/schema/nodes/measurement.d.ts.map +1 -0
  149. package/dist/schema/nodes/measurement.js +87 -0
  150. package/dist/schema/nodes/pipe-fitting.d.ts +56 -0
  151. package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -0
  152. package/dist/schema/nodes/pipe-fitting.js +45 -0
  153. package/dist/schema/nodes/pipe-segment.d.ts +48 -0
  154. package/dist/schema/nodes/pipe-segment.d.ts.map +1 -0
  155. package/dist/schema/nodes/pipe-segment.js +36 -0
  156. package/dist/schema/nodes/pipe-trap.d.ts +43 -0
  157. package/dist/schema/nodes/pipe-trap.d.ts.map +1 -0
  158. package/dist/schema/nodes/pipe-trap.js +36 -0
  159. package/dist/schema/nodes/ridge-vent.d.ts +18 -3
  160. package/dist/schema/nodes/ridge-vent.d.ts.map +1 -1
  161. package/dist/schema/nodes/ridge-vent.js +221 -3
  162. package/dist/schema/nodes/roof-segment-shape.d.ts +90 -0
  163. package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -0
  164. package/dist/schema/nodes/roof-segment-shape.js +318 -0
  165. package/dist/schema/nodes/roof-segment-walls.d.ts +101 -0
  166. package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -0
  167. package/dist/schema/nodes/roof-segment-walls.js +330 -0
  168. package/dist/schema/nodes/roof-segment.d.ts +84 -8
  169. package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
  170. package/dist/schema/nodes/roof-segment.js +290 -5
  171. package/dist/schema/nodes/roof.d.ts +8 -8
  172. package/dist/schema/nodes/shelf.d.ts +4 -2
  173. package/dist/schema/nodes/shelf.d.ts.map +1 -1
  174. package/dist/schema/nodes/shelf.js +8 -0
  175. package/dist/schema/nodes/site.d.ts +9 -0
  176. package/dist/schema/nodes/site.d.ts.map +1 -1
  177. package/dist/schema/nodes/site.js +8 -6
  178. package/dist/schema/nodes/skylight.d.ts +10 -10
  179. package/dist/schema/nodes/slab.d.ts +8 -2
  180. package/dist/schema/nodes/slab.d.ts.map +1 -1
  181. package/dist/schema/nodes/slab.js +18 -2
  182. package/dist/schema/nodes/solar-panel.d.ts +4 -4
  183. package/dist/schema/nodes/spawn.d.ts +1 -0
  184. package/dist/schema/nodes/spawn.d.ts.map +1 -1
  185. package/dist/schema/nodes/spawn.js +2 -0
  186. package/dist/schema/nodes/stair-segment.d.ts +4 -4
  187. package/dist/schema/nodes/stair.d.ts +16 -13
  188. package/dist/schema/nodes/stair.d.ts.map +1 -1
  189. package/dist/schema/nodes/stair.js +11 -1
  190. package/dist/schema/nodes/structural-grid.d.ts +27 -0
  191. package/dist/schema/nodes/structural-grid.d.ts.map +1 -0
  192. package/dist/schema/nodes/structural-grid.js +17 -0
  193. package/dist/schema/nodes/turbine-vent.d.ts +2 -2
  194. package/dist/schema/nodes/wall.d.ts +248 -6
  195. package/dist/schema/nodes/wall.d.ts.map +1 -1
  196. package/dist/schema/nodes/wall.js +266 -0
  197. package/dist/schema/nodes/window.d.ts +40 -3
  198. package/dist/schema/nodes/window.d.ts.map +1 -1
  199. package/dist/schema/nodes/window.js +30 -0
  200. package/dist/schema/nodes/zone.d.ts +22 -0
  201. package/dist/schema/nodes/zone.d.ts.map +1 -1
  202. package/dist/schema/nodes/zone.js +21 -0
  203. package/dist/schema/scene-material.d.ts +41 -0
  204. package/dist/schema/scene-material.d.ts.map +1 -0
  205. package/dist/schema/scene-material.js +9 -0
  206. package/dist/schema/terrain.d.ts +28 -0
  207. package/dist/schema/terrain.d.ts.map +1 -0
  208. package/dist/schema/terrain.js +30 -0
  209. package/dist/schema/types.d.ts +1300 -97
  210. package/dist/schema/types.d.ts.map +1 -1
  211. package/dist/schema/types.js +27 -0
  212. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts +80 -0
  213. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts.map +1 -0
  214. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.js +89 -0
  215. package/dist/services/alignment-anchors.d.ts.map +1 -1
  216. package/dist/services/alignment-anchors.js +30 -1
  217. package/dist/services/alignment.d.ts +5 -0
  218. package/dist/services/alignment.d.ts.map +1 -1
  219. package/dist/services/alignment.js +2 -0
  220. package/dist/services/hosting.d.ts +8 -0
  221. package/dist/services/hosting.d.ts.map +1 -1
  222. package/dist/services/hosting.js +13 -4
  223. package/dist/services/index.d.ts +9 -2
  224. package/dist/services/index.d.ts.map +1 -1
  225. package/dist/services/index.js +9 -2
  226. package/dist/services/level-height.d.ts +27 -0
  227. package/dist/services/level-height.d.ts.map +1 -0
  228. package/dist/services/level-height.js +94 -0
  229. package/dist/services/opening-guides.d.ts +141 -0
  230. package/dist/services/opening-guides.d.ts.map +1 -0
  231. package/dist/services/opening-guides.js +267 -0
  232. package/dist/services/port-connectivity.d.ts +115 -0
  233. package/dist/services/port-connectivity.d.ts.map +1 -0
  234. package/dist/services/port-connectivity.js +300 -0
  235. package/dist/services/riser-diagram.d.ts +40 -0
  236. package/dist/services/riser-diagram.d.ts.map +1 -0
  237. package/dist/services/riser-diagram.js +110 -0
  238. package/dist/services/snap.d.ts +9 -1
  239. package/dist/services/snap.d.ts.map +1 -1
  240. package/dist/services/snap.js +21 -1
  241. package/dist/services/storey.d.ts +103 -0
  242. package/dist/services/storey.d.ts.map +1 -0
  243. package/dist/services/storey.js +296 -0
  244. package/dist/services/system-graph.d.ts +30 -0
  245. package/dist/services/system-graph.d.ts.map +1 -0
  246. package/dist/services/system-graph.js +164 -0
  247. package/dist/services/validate-dwv.d.ts +32 -0
  248. package/dist/services/validate-dwv.d.ts.map +1 -0
  249. package/dist/services/validate-dwv.js +118 -0
  250. package/dist/store/actions/node-actions.d.ts.map +1 -1
  251. package/dist/store/actions/node-actions.js +141 -4
  252. package/dist/store/history-control.d.ts +31 -0
  253. package/dist/store/history-control.d.ts.map +1 -1
  254. package/dist/store/history-control.js +130 -0
  255. package/dist/store/use-live-terrain.d.ts +65 -0
  256. package/dist/store/use-live-terrain.d.ts.map +1 -0
  257. package/dist/store/use-live-terrain.js +31 -0
  258. package/dist/store/use-live-transforms.d.ts +8 -0
  259. package/dist/store/use-live-transforms.d.ts.map +1 -1
  260. package/dist/store/use-scene.d.ts +47 -3
  261. package/dist/store/use-scene.d.ts.map +1 -1
  262. package/dist/store/use-scene.js +992 -55
  263. package/dist/systems/elevator/elevator-opening-system.d.ts +1 -0
  264. package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -1
  265. package/dist/systems/elevator/elevator-opening-system.js +27 -21
  266. package/dist/systems/elevator/elevator-service.d.ts +0 -2
  267. package/dist/systems/elevator/elevator-service.d.ts.map +1 -1
  268. package/dist/systems/elevator/elevator-service.js +2 -24
  269. package/dist/systems/fence/fence-centerline.d.ts +12 -0
  270. package/dist/systems/fence/fence-centerline.d.ts.map +1 -0
  271. package/dist/systems/fence/fence-centerline.js +37 -0
  272. package/dist/systems/fence/fence-spline.d.ts +39 -0
  273. package/dist/systems/fence/fence-spline.d.ts.map +1 -0
  274. package/dist/systems/fence/fence-spline.js +196 -0
  275. package/dist/systems/slab/slab-placement.d.ts +11 -0
  276. package/dist/systems/slab/slab-placement.d.ts.map +1 -0
  277. package/dist/systems/slab/slab-placement.js +14 -0
  278. package/dist/systems/slab/slab-support.d.ts +137 -0
  279. package/dist/systems/slab/slab-support.d.ts.map +1 -0
  280. package/dist/systems/slab/slab-support.js +557 -0
  281. package/dist/systems/stair/stair-footprint.d.ts.map +1 -1
  282. package/dist/systems/stair/stair-footprint.js +21 -11
  283. package/dist/systems/stair/stair-opening-preview.d.ts +1300 -505
  284. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
  285. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  286. package/dist/systems/stair/stair-opening-sync.js +19 -33
  287. package/dist/systems/stair/stair-opening-system.d.ts.map +1 -1
  288. package/dist/systems/stair/stair-opening-system.js +32 -4
  289. package/dist/systems/stair/stair-rise.d.ts +22 -0
  290. package/dist/systems/stair/stair-rise.d.ts.map +1 -0
  291. package/dist/systems/stair/stair-rise.js +79 -0
  292. package/dist/systems/wall/wall-curve.d.ts +10 -0
  293. package/dist/systems/wall/wall-curve.d.ts.map +1 -1
  294. package/dist/systems/wall/wall-curve.js +1 -1
  295. package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
  296. package/dist/systems/wall/wall-mitering.js +44 -8
  297. package/dist/systems/wall/wall-top.d.ts +40 -0
  298. package/dist/systems/wall/wall-top.d.ts.map +1 -0
  299. package/dist/systems/wall/wall-top.js +46 -0
  300. package/dist/utils/clone-scene-graph.d.ts +1 -0
  301. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  302. package/dist/utils/clone-scene-graph.js +56 -5
  303. package/dist/utils/heal-scene-graph.d.ts +18 -0
  304. package/dist/utils/heal-scene-graph.d.ts.map +1 -0
  305. package/dist/utils/heal-scene-graph.js +106 -0
  306. package/dist/validation/validate-build-json.d.ts +3 -0
  307. package/dist/validation/validate-build-json.d.ts.map +1 -1
  308. package/dist/validation/validate-build-json.js +90 -5
  309. package/package.json +4 -4
  310. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +0 -2
  311. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +0 -1
  312. package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +0 -273
  313. package/dist/lib/space-detection-pause.test.d.ts +0 -2
  314. package/dist/lib/space-detection-pause.test.d.ts.map +0 -1
  315. package/dist/lib/space-detection-pause.test.js +0 -42
  316. package/dist/lib/space-detection.test.d.ts +0 -2
  317. package/dist/lib/space-detection.test.d.ts.map +0 -1
  318. package/dist/lib/space-detection.test.js +0 -77
  319. package/dist/registry/registry.test.d.ts +0 -2
  320. package/dist/registry/registry.test.d.ts.map +0 -1
  321. package/dist/registry/registry.test.js +0 -183
  322. package/dist/registry/relations-resolver.test.d.ts +0 -2
  323. package/dist/registry/relations-resolver.test.d.ts.map +0 -1
  324. package/dist/registry/relations-resolver.test.js +0 -183
  325. package/dist/registry/scene-api.test.d.ts +0 -2
  326. package/dist/registry/scene-api.test.d.ts.map +0 -1
  327. package/dist/registry/scene-api.test.js +0 -183
  328. package/dist/registry/subtree.test.d.ts +0 -2
  329. package/dist/registry/subtree.test.d.ts.map +0 -1
  330. package/dist/registry/subtree.test.js +0 -133
  331. package/dist/schema/asset-url.test.d.ts +0 -2
  332. package/dist/schema/asset-url.test.d.ts.map +0 -1
  333. package/dist/schema/asset-url.test.js +0 -134
  334. package/dist/services/alignment-anchors.test.d.ts +0 -2
  335. package/dist/services/alignment-anchors.test.d.ts.map +0 -1
  336. package/dist/services/alignment-anchors.test.js +0 -383
  337. package/dist/services/alignment.test.d.ts +0 -2
  338. package/dist/services/alignment.test.d.ts.map +0 -1
  339. package/dist/services/alignment.test.js +0 -92
  340. package/dist/services/drag-session.test.d.ts +0 -2
  341. package/dist/services/drag-session.test.d.ts.map +0 -1
  342. package/dist/services/drag-session.test.js +0 -219
  343. package/dist/services/hosting.test.d.ts +0 -2
  344. package/dist/services/hosting.test.d.ts.map +0 -1
  345. package/dist/services/hosting.test.js +0 -199
  346. package/dist/services/movement.test.d.ts +0 -2
  347. package/dist/services/movement.test.d.ts.map +0 -1
  348. package/dist/services/movement.test.js +0 -133
  349. package/dist/services/single-undo-dance.test.d.ts +0 -2
  350. package/dist/services/single-undo-dance.test.d.ts.map +0 -1
  351. package/dist/services/single-undo-dance.test.js +0 -210
  352. package/dist/services/snap.test.d.ts +0 -2
  353. package/dist/services/snap.test.d.ts.map +0 -1
  354. package/dist/services/snap.test.js +0 -95
  355. package/dist/store/actions/node-mutation-sanitize.test.d.ts +0 -2
  356. package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +0 -1
  357. package/dist/store/actions/node-mutation-sanitize.test.js +0 -147
  358. package/dist/store/actions/reparent.test.d.ts +0 -2
  359. package/dist/store/actions/reparent.test.d.ts.map +0 -1
  360. package/dist/store/actions/reparent.test.js +0 -212
  361. package/dist/store/use-scene-elevator-migration.test.d.ts +0 -2
  362. package/dist/store/use-scene-elevator-migration.test.d.ts.map +0 -1
  363. package/dist/store/use-scene-elevator-migration.test.js +0 -109
  364. package/dist/systems/elevator/elevator-opening-sync.test.d.ts +0 -2
  365. package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +0 -1
  366. package/dist/systems/elevator/elevator-opening-sync.test.js +0 -146
  367. package/dist/systems/elevator/elevator-runtime.test.d.ts +0 -2
  368. package/dist/systems/elevator/elevator-runtime.test.d.ts.map +0 -1
  369. package/dist/systems/elevator/elevator-runtime.test.js +0 -64
  370. package/dist/systems/stair/stair-opening-preview.test.d.ts +0 -2
  371. package/dist/systems/stair/stair-opening-preview.test.d.ts.map +0 -1
  372. package/dist/systems/stair/stair-opening-preview.test.js +0 -80
  373. package/dist/systems/stair/stair-opening-sync.test.d.ts +0 -2
  374. package/dist/systems/stair/stair-opening-sync.test.d.ts.map +0 -1
  375. package/dist/systems/stair/stair-opening-sync.test.js +0 -446
  376. package/dist/utils/clone-scene-graph.test.d.ts +0 -2
  377. package/dist/utils/clone-scene-graph.test.d.ts.map +0 -1
  378. package/dist/utils/clone-scene-graph.test.js +0 -59
@@ -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;AA8rBrB,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;
@@ -329,24 +331,11 @@ function getSpiralOpeningPolygon(stair, offset = 0) {
329
331
  return toWorldPlanPoint(stair, Math.cos(angle) * radius, Math.sin(angle) * radius);
330
332
  });
331
333
  }
332
- function getSpiralLandingPolygon(stair, offset = 0) {
333
- const width = Math.max(stair.width ?? 1, 0.4);
334
- const outerRadius = Math.max(0.05, (stair.innerRadius ?? 0.9) + width);
335
- const depth = Math.max(stair.topLandingDepth ?? 0.9, 0.1);
336
- const halfWidth = width / 2;
337
- const localPoints = [
338
- [outerRadius - offset, -halfWidth - offset],
339
- [outerRadius + depth + offset, -halfWidth - offset],
340
- [outerRadius + depth + offset, halfWidth + offset],
341
- [outerRadius - offset, halfWidth + offset],
342
- ];
343
- return localPoints.map(([x, z]) => toWorldPlanPoint(stair, x, z));
344
- }
345
334
  function getStraightOpeningPolygonsForSurface(stair, nodes, targetElevation, openingOffsetOverride) {
346
335
  const layouts = getStraightStairLayouts(stair, nodes);
347
336
  if (layouts.length === 0)
348
337
  return [];
349
- const riserHeight = (stair.totalRise ?? 2.5) / Math.max(stair.stepCount ?? 10, 1);
338
+ const riserHeight = resolveStairTotalRise(stair, nodes) / Math.max(stair.stepCount ?? 10, 1);
350
339
  const targetThreshold = Math.max(riserHeight * 2, STRAIGHT_STAIR_TARGET_THRESHOLD_MIN);
351
340
  const openingOffset = Math.max(openingOffsetOverride ?? stair.openingOffset ?? 0, 0);
352
341
  const openingRects = [];
@@ -413,11 +402,7 @@ function getStairOpeningPolygons(stair, nodes, targetElevation, openingOffsetOve
413
402
  }
414
403
  if (stair.stairType === 'spiral') {
415
404
  const offset = Math.max(openingOffset - STAIR_SLAB_OPENING_TIGHTENING, 0);
416
- const polygons = [getSpiralOpeningPolygon(stair, offset)];
417
- if (stair.topLandingMode === 'integrated') {
418
- polygons.push(getSpiralLandingPolygon(stair, offset));
419
- }
420
- return polygons;
405
+ return [getSpiralOpeningPolygon(stair, offset)];
421
406
  }
422
407
  if (typeof targetElevation === 'number') {
423
408
  return getStraightOpeningPolygonsForSurface(stair, nodes, targetElevation, openingOffset);
@@ -439,25 +424,26 @@ function getApplicableStairOpeningPolygons(stair, nodes, targetElevation, surfac
439
424
  }
440
425
  function getTargetSlabElevationForStair(stair, slab, slabLevelId, nodes) {
441
426
  const { fromLevelId } = getResolvedStairLevelIds(stair, nodes);
442
- const fromLevel = getLevelNumber(fromLevelId, nodes);
443
- const slabLevel = getLevelNumber(slabLevelId, nodes);
444
- 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) {
445
431
  return slab.elevation ?? 0.05;
446
432
  }
447
- return ((slabLevel - fromLevel) * DEFAULT_WALL_HEIGHT +
448
- (slab.elevation ?? 0.05) -
449
- (stair.position[1] ?? 0));
433
+ return (slabElevation.baseY - fromElevation.baseY + (slab.elevation ?? 0.05) - (stair.position[1] ?? 0));
450
434
  }
451
435
  function getTargetCeilingElevationForStair(stair, ceiling, ceilingLevelId, nodes) {
452
436
  const { fromLevelId } = getResolvedStairLevelIds(stair, nodes);
453
- const fromLevel = getLevelNumber(fromLevelId, nodes);
454
- const ceilingLevel = getLevelNumber(ceilingLevelId, nodes);
455
- if (fromLevel === undefined || ceilingLevel === undefined) {
456
- 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;
457
445
  }
458
- return ((ceilingLevel - fromLevel) * DEFAULT_WALL_HEIGHT +
459
- (ceiling.height ?? DEFAULT_WALL_HEIGHT) -
460
- (stair.position[1] ?? 0));
446
+ return ceilingElevation.baseY - fromElevation.baseY + ceilingHeight - (stair.position[1] ?? 0);
461
447
  }
462
448
  function shouldApplyStairToSlab(stair, slabLevelId, nodes) {
463
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) {
@@ -1 +1 @@
1
- {"version":3,"file":"wall-mitering.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-mitering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAO5C,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;CAClB;AASD,KAAK,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAA;AAGzE,KAAK,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;AAQlD,iBAAS,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,SAAY,GAAG,MAAM,CAG7D;AA8CD,UAAU,QAAQ;IAChB,YAAY,EAAE,OAAO,CAAA;IACrB,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,CAAA;KAAE,CAAC,CAAA;CACpF;AAoND,MAAM,WAAW,aAAa;IAE5B,YAAY,EAAE,YAAY,CAAA;IAE1B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;CACjC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,aAAa,CAWrE;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,aAAa,GACvB,uBAAuB,GAAG,IAAI,CA0BhC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CA8C/F;AAGD,OAAO,EAAE,UAAU,EAAE,CAAA"}
1
+ {"version":3,"file":"wall-mitering.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-mitering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAO5C,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;CAClB;AASD,KAAK,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAA;AAGzE,KAAK,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;AAmBlD,iBAAS,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,SAAY,GAAG,MAAM,CAG7D;AA8CD,UAAU,QAAQ;IAChB,YAAY,EAAE,OAAO,CAAA;IACrB,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,CAAA;KAAE,CAAC,CAAA;CACpF;AAkPD,MAAM,WAAW,aAAa;IAE5B,YAAY,EAAE,YAAY,CAAA;IAE1B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;CACjC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,aAAa,CAWrE;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,aAAa,GACvB,uBAAuB,GAAG,IAAI,CA0BhC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CA8C/F;AAGD,OAAO,EAAE,UAAU,EAAE,CAAA"}
@@ -3,6 +3,16 @@ import { getWallCurveFrameAt, isCurvedWall } from './wall-curve';
3
3
  // UTILITY FUNCTIONS
4
4
  // ============================================================================
5
5
  const TOLERANCE = 0.001;
6
+ // Miter joints are line-line intersections, so the joint point sits a distance
7
+ // ≈ halfThickness / sin(θ) from the junction, where θ is the angle between the
8
+ // two walls. As θ → 0 (two walls nearly collinear — e.g. a room-preset preview
9
+ // dragged on top of an existing wall, or a freshly-drawn wall almost parallel
10
+ // to its neighbour) that distance runs away to infinity and the wall renders as
11
+ // an infinite spike. Cap the joint at this multiple of the wall half-thickness;
12
+ // beyond it we fall back to a square (butt) joint, exactly like the existing
13
+ // parallel-walls guard. 10× preserves every realistic corner (a 0.1 m wall keeps
14
+ // mitering down to ~11°) while bounding the pathological near-collinear case.
15
+ const MITER_LIMIT = 10;
6
16
  function pointToKey(p, tolerance = TOLERANCE) {
7
17
  const snap = 1 / tolerance;
8
18
  return `${Math.round(p.x * snap)},${Math.round(p.y * snap)}`;
@@ -107,10 +117,8 @@ function getWallBoundaryFrame(wall, endType) {
107
117
  const point = endType === 'start'
108
118
  ? { x: wall.start[0], y: wall.start[1] }
109
119
  : { x: wall.end[0], y: wall.end[1] };
110
- const vector = endType === 'start'
111
- ? { x: wall.end[0] - wall.start[0], y: wall.end[1] - wall.start[1] }
112
- : { x: wall.start[0] - wall.end[0], y: wall.start[1] - wall.end[1] };
113
- 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);
114
122
  if (length < 1e-9) {
115
123
  return {
116
124
  point,
@@ -120,8 +128,10 @@ function getWallBoundaryFrame(wall, endType) {
120
128
  }
121
129
  return {
122
130
  point,
123
- tangent: { x: vector.x / length, y: vector.y / length },
124
- 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 },
125
135
  };
126
136
  }
127
137
  function calculateJunctionIntersections(junction, getThickness) {
@@ -144,7 +154,14 @@ function calculateJunctionIntersections(junction, getThickness) {
144
154
  const edgeA = createLineFromPointAndVector(pA, v);
145
155
  const edgeB = createLineFromPointAndVector(pB, v);
146
156
  const angle = Math.atan2(v.y, v.x);
147
- processedWalls.push({ wallId: wall.id, angle, edgeA, edgeB, isPassthrough: true });
157
+ processedWalls.push({
158
+ wallId: wall.id,
159
+ angle,
160
+ edgeA,
161
+ edgeB,
162
+ isPassthrough: true,
163
+ halfThickness: halfT,
164
+ });
148
165
  }
149
166
  }
150
167
  else {
@@ -159,7 +176,14 @@ function calculateJunctionIntersections(junction, getThickness) {
159
176
  const edgeA = createLineFromPointAndVector(pA, v);
160
177
  const edgeB = createLineFromPointAndVector(pB, v);
161
178
  const angle = Math.atan2(v.y, v.x);
162
- processedWalls.push({ wallId: wall.id, angle, edgeA, edgeB, isPassthrough: false });
179
+ processedWalls.push({
180
+ wallId: wall.id,
181
+ angle,
182
+ edgeA,
183
+ edgeB,
184
+ isPassthrough: false,
185
+ halfThickness: halfT,
186
+ });
163
187
  }
164
188
  }
165
189
  // Sort by outgoing angle
@@ -182,6 +206,18 @@ function calculateJunctionIntersections(junction, getThickness) {
182
206
  x: (wall1.edgeA.b * wall2.edgeB.c - wall2.edgeB.b * wall1.edgeA.c) / det,
183
207
  y: (wall2.edgeB.a * wall1.edgeA.c - wall1.edgeA.a * wall2.edgeB.c) / det,
184
208
  };
209
+ // Miter limit: `det` only catches walls that are *exactly* parallel. Two
210
+ // walls meeting at a shallow angle have a small-but-nonzero `det`, so `p`
211
+ // lands far from the junction (∝ 1/sin θ) and the wall renders as an
212
+ // infinite spike. Reject any joint farther than MITER_LIMIT half-thicknesses
213
+ // from the meeting point — those walls fall back to a square joint.
214
+ const maxMiter = MITER_LIMIT * Math.max(wall1.halfThickness, wall2.halfThickness);
215
+ const dx = p.x - meetingPoint.x;
216
+ const dy = p.y - meetingPoint.y;
217
+ if (!(Number.isFinite(p.x) && Number.isFinite(p.y)) ||
218
+ dx * dx + dy * dy > maxMiter * maxMiter) {
219
+ continue;
220
+ }
185
221
  // Only assign intersection to non-passthrough walls
186
222
  // Passthrough walls don't receive junction data (their geometry doesn't change)
187
223
  if (!wall1.isPassthrough) {
@@ -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
+ }
@@ -4,6 +4,7 @@ export type SceneGraph = {
4
4
  nodes: Record<AnyNodeId, AnyNode>;
5
5
  rootNodeIds: AnyNodeId[];
6
6
  collections?: Record<CollectionId, Collection>;
7
+ installedPlugins?: string[];
7
8
  };
8
9
  /**
9
10
  * Deep clones a scene graph with all node IDs regenerated while preserving
@@ -1 +1 @@
1
- {"version":3,"file":"clone-scene-graph.d.ts","sourceRoot":"","sources":["../../src/utils/clone-scene-graph.ts"],"names":[],"mappings":"AAAA,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;CAC/C,CAAA;AAUD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAuGlE;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,CA8E/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,CAoEZ"}
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"}