@pascal-app/nodes 1.0.3 → 1.0.4

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 (708) hide show
  1. package/dist/block/definition.d.ts.map +1 -1
  2. package/dist/block/definition.js +18 -3
  3. package/dist/block/floorplan.d.ts +11 -1
  4. package/dist/block/floorplan.d.ts.map +1 -1
  5. package/dist/block/floorplan.js +81 -13
  6. package/dist/block/geometry.d.ts +2 -2
  7. package/dist/block/geometry.d.ts.map +1 -1
  8. package/dist/block/geometry.js +39 -24
  9. package/dist/block/hosted-edit.d.ts.map +1 -1
  10. package/dist/block/hosted-edit.js +25 -0
  11. package/dist/block/material-slots.d.ts +1 -3
  12. package/dist/block/material-slots.d.ts.map +1 -1
  13. package/dist/block/material-slots.js +1 -11
  14. package/dist/block/paint.d.ts.map +1 -1
  15. package/dist/block/paint.js +4 -10
  16. package/dist/block/panel.d.ts.map +1 -1
  17. package/dist/block/panel.js +8 -3
  18. package/dist/block/selection.d.ts.map +1 -1
  19. package/dist/block/selection.js +2 -2
  20. package/dist/box-vent/definition.d.ts.map +1 -1
  21. package/dist/box-vent/definition.js +2 -5
  22. package/dist/box-vent/panel.d.ts.map +1 -1
  23. package/dist/box-vent/panel.js +5 -28
  24. package/dist/cabinet/countertop-layout.d.ts +13 -2
  25. package/dist/cabinet/countertop-layout.d.ts.map +1 -1
  26. package/dist/cabinet/definition.d.ts.map +1 -1
  27. package/dist/cabinet/definition.js +1 -2
  28. package/dist/cabinet/floorplan.d.ts.map +1 -1
  29. package/dist/cabinet/floorplan.js +125 -28
  30. package/dist/cabinet/geometry/shared.d.ts +1 -2
  31. package/dist/cabinet/geometry/shared.d.ts.map +1 -1
  32. package/dist/cabinet/geometry/shared.js +1 -2
  33. package/dist/cabinet/run-layout.d.ts.map +1 -1
  34. package/dist/cabinet/run-layout.js +8 -5
  35. package/dist/cabinet/run-ops.d.ts +13 -2
  36. package/dist/cabinet/run-ops.d.ts.map +1 -1
  37. package/dist/cabinet/tool.d.ts.map +1 -1
  38. package/dist/cabinet/tool.js +22 -24
  39. package/dist/cabinet/wall-snap.d.ts.map +1 -1
  40. package/dist/cabinet/wall-snap.js +8 -8
  41. package/dist/ceiling/boundary-editor.d.ts.map +1 -1
  42. package/dist/ceiling/boundary-editor.js +5 -3
  43. package/dist/ceiling/definition.d.ts +4 -4
  44. package/dist/ceiling/definition.d.ts.map +1 -1
  45. package/dist/ceiling/definition.js +15 -15
  46. package/dist/ceiling/floorplan-affordances.d.ts +84 -4
  47. package/dist/ceiling/floorplan-affordances.d.ts.map +1 -1
  48. package/dist/ceiling/floorplan-affordances.js +0 -1
  49. package/dist/ceiling/floorplan-move.d.ts.map +1 -1
  50. package/dist/ceiling/floorplan-move.js +0 -1
  51. package/dist/ceiling/floorplan.d.ts +1 -1
  52. package/dist/ceiling/floorplan.d.ts.map +1 -1
  53. package/dist/ceiling/floorplan.js +2 -1
  54. package/dist/ceiling/height-bounds.d.ts +19 -0
  55. package/dist/ceiling/height-bounds.d.ts.map +1 -0
  56. package/dist/ceiling/height-bounds.js +24 -0
  57. package/dist/ceiling/hole-editor.d.ts.map +1 -1
  58. package/dist/ceiling/hole-editor.js +3 -4
  59. package/dist/ceiling/materials.d.ts +1 -1
  60. package/dist/ceiling/materials.d.ts.map +1 -1
  61. package/dist/ceiling/materials.js +10 -3
  62. package/dist/ceiling/paint.d.ts +2 -8
  63. package/dist/ceiling/paint.d.ts.map +1 -1
  64. package/dist/ceiling/paint.js +121 -24
  65. package/dist/ceiling/panel.d.ts.map +1 -1
  66. package/dist/ceiling/panel.js +40 -23
  67. package/dist/ceiling/renderer.d.ts.map +1 -1
  68. package/dist/ceiling/renderer.js +41 -5
  69. package/dist/chimney/move-tool.d.ts.map +1 -1
  70. package/dist/chimney/move-tool.js +8 -2
  71. package/dist/chimney/paint.d.ts.map +1 -1
  72. package/dist/chimney/paint.js +3 -10
  73. package/dist/column/definition.d.ts.map +1 -1
  74. package/dist/column/definition.js +24 -2
  75. package/dist/column/floorplan-affordances.d.ts.map +1 -1
  76. package/dist/column/floorplan-affordances.js +5 -0
  77. package/dist/column/floorplan.d.ts +1 -1
  78. package/dist/column/floorplan.d.ts.map +1 -1
  79. package/dist/column/floorplan.js +47 -2
  80. package/dist/column/hosted-resize.d.ts +101 -2
  81. package/dist/column/hosted-resize.d.ts.map +1 -1
  82. package/dist/column/panel.d.ts.map +1 -1
  83. package/dist/column/panel.js +147 -146
  84. package/dist/column/renderer.d.ts.map +1 -1
  85. package/dist/column/renderer.js +6 -4
  86. package/dist/column/surface.d.ts.map +1 -1
  87. package/dist/column/surface.js +7 -0
  88. package/dist/construction-dimension/floorplan.js +7 -3
  89. package/dist/cupola/definition.d.ts.map +1 -1
  90. package/dist/cupola/definition.js +2 -8
  91. package/dist/cupola/panel.d.ts.map +1 -1
  92. package/dist/cupola/panel.js +5 -23
  93. package/dist/door/definition.d.ts.map +1 -1
  94. package/dist/door/definition.js +12 -3
  95. package/dist/door/door-math.d.ts +5 -10
  96. package/dist/door/door-math.d.ts.map +1 -1
  97. package/dist/door/door-math.js +14 -15
  98. package/dist/door/floorplan-move.d.ts.map +1 -1
  99. package/dist/door/floorplan-move.js +3 -1
  100. package/dist/door/floorplan.d.ts.map +1 -1
  101. package/dist/door/floorplan.js +55 -21
  102. package/dist/door/mechanism.d.ts +4 -0
  103. package/dist/door/mechanism.d.ts.map +1 -0
  104. package/dist/door/mechanism.js +24 -0
  105. package/dist/door/move-tool.d.ts.map +1 -1
  106. package/dist/door/move-tool.js +72 -48
  107. package/dist/door/paint.d.ts.map +1 -1
  108. package/dist/door/paint.js +3 -0
  109. package/dist/door/panel.d.ts.map +1 -1
  110. package/dist/door/panel.js +34 -259
  111. package/dist/door/placement.d.ts +45 -0
  112. package/dist/door/placement.d.ts.map +1 -0
  113. package/dist/door/placement.js +51 -0
  114. package/dist/door/preview.d.ts.map +1 -1
  115. package/dist/door/preview.js +2 -0
  116. package/dist/door/renderer.d.ts.map +1 -1
  117. package/dist/door/renderer.js +3 -2
  118. package/dist/door/tool.d.ts.map +1 -1
  119. package/dist/door/tool.js +92 -21
  120. package/dist/dormer/csg-geometry.d.ts.map +1 -1
  121. package/dist/dormer/csg-geometry.js +2 -0
  122. package/dist/dormer/paint.d.ts.map +1 -1
  123. package/dist/dormer/paint.js +2 -5
  124. package/dist/dormer/panel.d.ts.map +1 -1
  125. package/dist/dormer/panel.js +4 -14
  126. package/dist/downspout/definition.d.ts.map +1 -1
  127. package/dist/downspout/definition.js +2 -2
  128. package/dist/duct-fitting/definition.d.ts.map +1 -1
  129. package/dist/duct-fitting/definition.js +2 -1
  130. package/dist/duct-fitting/geometry.d.ts.map +1 -1
  131. package/dist/duct-fitting/geometry.js +1 -1
  132. package/dist/duct-fitting/move-tool.d.ts.map +1 -1
  133. package/dist/duct-fitting/move-tool.js +8 -2
  134. package/dist/duct-segment/definition.d.ts.map +1 -1
  135. package/dist/duct-segment/definition.js +2 -2
  136. package/dist/duct-segment/geometry.d.ts.map +1 -1
  137. package/dist/duct-segment/geometry.js +1 -1
  138. package/dist/duct-segment/move-tool.d.ts.map +1 -1
  139. package/dist/duct-segment/move-tool.js +8 -2
  140. package/dist/elevator/definition.d.ts.map +1 -1
  141. package/dist/elevator/definition.js +1 -2
  142. package/dist/elevator/panel.d.ts.map +1 -1
  143. package/dist/elevator/panel.js +5 -27
  144. package/dist/elevator/parametrics.js +1 -1
  145. package/dist/elevator/renderer.d.ts.map +1 -1
  146. package/dist/elevator/renderer.js +3 -4
  147. package/dist/eyebrow-vent/definition.d.ts.map +1 -1
  148. package/dist/eyebrow-vent/definition.js +2 -5
  149. package/dist/eyebrow-vent/paint.d.ts.map +1 -1
  150. package/dist/eyebrow-vent/paint.js +3 -6
  151. package/dist/eyebrow-vent/panel.d.ts.map +1 -1
  152. package/dist/eyebrow-vent/panel.js +5 -23
  153. package/dist/fence/actions/move-endpoint.d.ts +1 -1
  154. package/dist/fence/actions/move-endpoint.d.ts.map +1 -1
  155. package/dist/fence/actions/move-endpoint.js +2 -1
  156. package/dist/fence/definition.d.ts +2 -1
  157. package/dist/fence/definition.d.ts.map +1 -1
  158. package/dist/fence/definition.js +83 -28
  159. package/dist/fence/drafting.d.ts +36 -0
  160. package/dist/fence/drafting.d.ts.map +1 -0
  161. package/dist/fence/drafting.js +210 -0
  162. package/dist/fence/feature-tool.d.ts +4 -0
  163. package/dist/fence/feature-tool.d.ts.map +1 -0
  164. package/dist/fence/feature-tool.js +140 -0
  165. package/dist/fence/features.d.ts +2 -0
  166. package/dist/fence/features.d.ts.map +1 -0
  167. package/dist/fence/features.js +12 -0
  168. package/dist/fence/floorplan-affordances.d.ts.map +1 -1
  169. package/dist/fence/floorplan-affordances.js +2 -1
  170. package/dist/fence/floorplan.d.ts +2 -3
  171. package/dist/fence/floorplan.d.ts.map +1 -1
  172. package/dist/fence/floorplan.js +131 -71
  173. package/dist/fence/geometry-parts.d.ts +50 -0
  174. package/dist/fence/geometry-parts.d.ts.map +1 -0
  175. package/dist/fence/geometry-parts.js +1144 -0
  176. package/dist/fence/geometry.d.ts +2 -3
  177. package/dist/fence/geometry.d.ts.map +1 -1
  178. package/dist/fence/geometry.js +84 -7
  179. package/dist/fence/index.d.ts +1 -0
  180. package/dist/fence/index.d.ts.map +1 -1
  181. package/dist/fence/index.js +1 -0
  182. package/dist/fence/inspector-editors.d.ts +15 -0
  183. package/dist/fence/inspector-editors.d.ts.map +1 -1
  184. package/dist/fence/inspector-editors.js +128 -3
  185. package/dist/fence/lift.d.ts.map +1 -1
  186. package/dist/fence/lift.js +5 -2
  187. package/dist/fence/move-tool.js +2 -1
  188. package/dist/fence/panel-model.d.ts +4 -0
  189. package/dist/fence/panel-model.d.ts.map +1 -0
  190. package/dist/fence/panel-model.js +12 -0
  191. package/dist/fence/parametrics.d.ts +0 -12
  192. package/dist/fence/parametrics.d.ts.map +1 -1
  193. package/dist/fence/parametrics.js +185 -18
  194. package/dist/fence/system.d.ts.map +1 -1
  195. package/dist/fence/system.js +141 -31
  196. package/dist/fence/tool.d.ts.map +1 -1
  197. package/dist/fence/tool.js +256 -47
  198. package/dist/fence-feature/actions.d.ts +7 -0
  199. package/dist/fence-feature/actions.d.ts.map +1 -0
  200. package/dist/fence-feature/actions.js +26 -0
  201. package/dist/fence-feature/definition.d.ts +4 -0
  202. package/dist/fence-feature/definition.d.ts.map +1 -0
  203. package/dist/fence-feature/definition.js +112 -0
  204. package/dist/fence-feature/floorplan-affordances.d.ts +3 -0
  205. package/dist/fence-feature/floorplan-affordances.d.ts.map +1 -0
  206. package/dist/fence-feature/floorplan-affordances.js +37 -0
  207. package/dist/fence-feature/floorplan.d.ts +4 -0
  208. package/dist/fence-feature/floorplan.d.ts.map +1 -0
  209. package/dist/fence-feature/floorplan.js +120 -0
  210. package/dist/fence-feature/geometry.d.ts +5 -0
  211. package/dist/fence-feature/geometry.d.ts.map +1 -0
  212. package/dist/fence-feature/geometry.js +43 -0
  213. package/dist/fence-feature/handles.d.ts +5 -0
  214. package/dist/fence-feature/handles.d.ts.map +1 -0
  215. package/dist/fence-feature/handles.js +84 -0
  216. package/dist/fence-feature/inspector.d.ts +6 -0
  217. package/dist/fence-feature/inspector.d.ts.map +1 -0
  218. package/dist/fence-feature/inspector.js +214 -0
  219. package/dist/fence-feature/interaction.d.ts +3 -0
  220. package/dist/fence-feature/interaction.d.ts.map +1 -0
  221. package/dist/fence-feature/interaction.js +41 -0
  222. package/dist/fence-feature/move-session.d.ts +9 -0
  223. package/dist/fence-feature/move-session.d.ts.map +1 -0
  224. package/dist/fence-feature/move-session.js +48 -0
  225. package/dist/fence-feature/move-tool.d.ts +5 -0
  226. package/dist/fence-feature/move-tool.d.ts.map +1 -0
  227. package/dist/fence-feature/move-tool.js +58 -0
  228. package/dist/fence-feature/pick-target.d.ts +7 -0
  229. package/dist/fence-feature/pick-target.d.ts.map +1 -0
  230. package/dist/fence-feature/pick-target.js +52 -0
  231. package/dist/floor-opening/definition.d.ts +3 -0
  232. package/dist/floor-opening/definition.d.ts.map +1 -0
  233. package/dist/floor-opening/definition.js +41 -0
  234. package/dist/floor-opening/floorplan.d.ts +156 -0
  235. package/dist/floor-opening/floorplan.d.ts.map +1 -0
  236. package/dist/floor-opening/floorplan.js +63 -0
  237. package/dist/gutter/definition.d.ts.map +1 -1
  238. package/dist/gutter/definition.js +2 -2
  239. package/dist/imported-mesh/definition.d.ts.map +1 -1
  240. package/dist/imported-mesh/definition.js +2 -0
  241. package/dist/imported-mesh/geometry.d.ts +1 -1
  242. package/dist/imported-mesh/geometry.d.ts.map +1 -1
  243. package/dist/imported-mesh/geometry.js +40 -12
  244. package/dist/index.d.ts +13 -2
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js +20 -2
  247. package/dist/item/authored-face-host.d.ts +10 -0
  248. package/dist/item/authored-face-host.d.ts.map +1 -0
  249. package/dist/item/authored-face-host.js +87 -0
  250. package/dist/item/authored-params.d.ts +15 -0
  251. package/dist/item/authored-params.d.ts.map +1 -0
  252. package/dist/item/authored-params.js +72 -0
  253. package/dist/item/definition.d.ts +2 -2
  254. package/dist/item/definition.d.ts.map +1 -1
  255. package/dist/item/definition.js +33 -4
  256. package/dist/item/floorplan.d.ts +1 -1
  257. package/dist/item/floorplan.d.ts.map +1 -1
  258. package/dist/item/floorplan.js +35 -5
  259. package/dist/item/index.d.ts +1 -0
  260. package/dist/item/index.d.ts.map +1 -1
  261. package/dist/item/index.js +1 -0
  262. package/dist/item/model-loader.d.ts +2 -0
  263. package/dist/item/model-loader.d.ts.map +1 -1
  264. package/dist/item/model-loader.js +55 -23
  265. package/dist/item/move-tool.js +4 -4
  266. package/dist/item/paint.d.ts.map +1 -1
  267. package/dist/item/paint.js +5 -24
  268. package/dist/item/panel.d.ts +1 -3
  269. package/dist/item/panel.d.ts.map +1 -1
  270. package/dist/item/panel.js +20 -24
  271. package/dist/item/placement-notice.d.ts +14 -0
  272. package/dist/item/placement-notice.d.ts.map +1 -0
  273. package/dist/item/placement-notice.js +38 -0
  274. package/dist/item/plan-symbols.d.ts +67 -0
  275. package/dist/item/plan-symbols.d.ts.map +1 -0
  276. package/dist/item/plan-symbols.js +407 -0
  277. package/dist/item/renderer.d.ts +10 -0
  278. package/dist/item/renderer.d.ts.map +1 -1
  279. package/dist/item/renderer.js +64 -38
  280. package/dist/item/script-param-defaults.d.ts +12 -0
  281. package/dist/item/script-param-defaults.d.ts.map +1 -0
  282. package/dist/item/script-param-defaults.js +34 -0
  283. package/dist/lean-to-extension/definition.d.ts.map +1 -1
  284. package/dist/lean-to-extension/definition.js +1 -2
  285. package/dist/lean-to-extension/geometry.d.ts.map +1 -1
  286. package/dist/lean-to-extension/geometry.js +1 -1
  287. package/dist/lean-to-extension/layout.d.ts.map +1 -1
  288. package/dist/lean-to-extension/layout.js +2 -3
  289. package/dist/lean-to-extension/placement-validation.d.ts.map +1 -1
  290. package/dist/lean-to-extension/placement-validation.js +5 -5
  291. package/dist/lean-to-extension/tool.d.ts.map +1 -1
  292. package/dist/lean-to-extension/tool.js +5 -5
  293. package/dist/lineset/move-tool.d.ts.map +1 -1
  294. package/dist/lineset/move-tool.js +8 -2
  295. package/dist/liquid-line/move-tool.d.ts.map +1 -1
  296. package/dist/liquid-line/move-tool.js +8 -2
  297. package/dist/measurement/definition.js +1 -1
  298. package/dist/pipe-fitting/move-tool.d.ts.map +1 -1
  299. package/dist/pipe-fitting/move-tool.js +8 -2
  300. package/dist/pipe-segment/move-tool.d.ts.map +1 -1
  301. package/dist/pipe-segment/move-tool.js +8 -2
  302. package/dist/procedural-item/animation.d.ts +8 -0
  303. package/dist/procedural-item/animation.d.ts.map +1 -0
  304. package/dist/procedural-item/animation.js +116 -0
  305. package/dist/procedural-item/definition.d.ts.map +1 -1
  306. package/dist/procedural-item/definition.js +74 -8
  307. package/dist/procedural-item/geometry.d.ts +5 -1
  308. package/dist/procedural-item/geometry.d.ts.map +1 -1
  309. package/dist/procedural-item/geometry.js +239 -15
  310. package/dist/procedural-item/renderer.d.ts.map +1 -1
  311. package/dist/procedural-item/renderer.js +272 -23
  312. package/dist/ridge-vent/panel.d.ts.map +1 -1
  313. package/dist/ridge-vent/panel.js +5 -23
  314. package/dist/ridge-vent/renderer.d.ts.map +1 -1
  315. package/dist/ridge-vent/renderer.js +9 -8
  316. package/dist/roof/definition.d.ts.map +1 -1
  317. package/dist/roof/definition.js +4 -1
  318. package/dist/roof/floorplan.d.ts +8 -0
  319. package/dist/roof/floorplan.d.ts.map +1 -1
  320. package/dist/roof/floorplan.js +194 -18
  321. package/dist/roof/panel-model.d.ts.map +1 -1
  322. package/dist/roof/panel-model.js +10 -6
  323. package/dist/roof/panel.d.ts.map +1 -1
  324. package/dist/roof/panel.js +3 -10
  325. package/dist/roof/renderer.d.ts.map +1 -1
  326. package/dist/roof/renderer.js +5 -2
  327. package/dist/roof-segment/panel.d.ts.map +1 -1
  328. package/dist/roof-segment/panel.js +5 -24
  329. package/dist/roof-segment/renderer.d.ts.map +1 -1
  330. package/dist/roof-segment/renderer.js +17 -8
  331. package/dist/separator/definition.d.ts +3 -0
  332. package/dist/separator/definition.d.ts.map +1 -0
  333. package/dist/separator/definition.js +39 -0
  334. package/dist/separator/renderer.d.ts +5 -0
  335. package/dist/separator/renderer.d.ts.map +1 -0
  336. package/dist/separator/renderer.js +36 -0
  337. package/dist/shared/duct-body-paint.d.ts +0 -4
  338. package/dist/shared/duct-body-paint.d.ts.map +1 -1
  339. package/dist/shared/duct-body-paint.js +1 -5
  340. package/dist/shared/item-interaction-actions.d.ts +6 -0
  341. package/dist/shared/item-interaction-actions.d.ts.map +1 -0
  342. package/dist/shared/item-interaction-actions.js +27 -0
  343. package/dist/shared/item-interactions.d.ts +9 -0
  344. package/dist/shared/item-interactions.d.ts.map +1 -0
  345. package/dist/shared/item-interactions.js +74 -0
  346. package/dist/shared/item-light-placement.d.ts +2 -0
  347. package/dist/shared/item-light-placement.d.ts.map +1 -0
  348. package/dist/shared/item-light-placement.js +3 -0
  349. package/dist/shared/move-roof-tool.js +2 -1
  350. package/dist/shared/node-batch/batchable.d.ts +12 -0
  351. package/dist/shared/node-batch/batchable.d.ts.map +1 -0
  352. package/dist/shared/node-batch/batchable.js +41 -0
  353. package/dist/shared/node-batch/candidates.d.ts +7 -5
  354. package/dist/shared/node-batch/candidates.d.ts.map +1 -1
  355. package/dist/shared/node-batch/candidates.js +43 -59
  356. package/dist/shared/node-batch/release.d.ts +8 -0
  357. package/dist/shared/node-batch/release.d.ts.map +1 -0
  358. package/dist/shared/node-batch/release.js +16 -0
  359. package/dist/shared/node-batch/system.d.ts.map +1 -1
  360. package/dist/shared/node-batch/system.js +17 -12
  361. package/dist/shared/node-batch/types.d.ts +4 -1
  362. package/dist/shared/node-batch/types.d.ts.map +1 -1
  363. package/dist/shared/opening-documentation.d.ts +13 -1
  364. package/dist/shared/opening-documentation.d.ts.map +1 -1
  365. package/dist/shared/opening-documentation.js +245 -59
  366. package/dist/shared/opening-guides-runtime.d.ts.map +1 -1
  367. package/dist/shared/opening-guides-runtime.js +17 -14
  368. package/dist/shared/opening-placement-dimensions.d.ts.map +1 -1
  369. package/dist/shared/opening-placement-dimensions.js +3 -3
  370. package/dist/shared/opening-plane-offset.d.ts +23 -0
  371. package/dist/shared/opening-plane-offset.d.ts.map +1 -0
  372. package/dist/shared/opening-plane-offset.js +24 -0
  373. package/dist/shared/polygon-centroid-move.d.ts.map +1 -1
  374. package/dist/shared/polygon-centroid-move.js +4 -1
  375. package/dist/shared/polygon-vertex-affordance.d.ts.map +1 -1
  376. package/dist/shared/polygon-vertex-affordance.js +19 -15
  377. package/dist/shared/run-hangers.d.ts.map +1 -1
  378. package/dist/shared/run-hangers.js +5 -4
  379. package/dist/shared/scripted-opening-handles.d.ts +18 -0
  380. package/dist/shared/scripted-opening-handles.d.ts.map +1 -0
  381. package/dist/shared/scripted-opening-handles.js +53 -0
  382. package/dist/shared/scripted-opening.d.ts +25 -0
  383. package/dist/shared/scripted-opening.d.ts.map +1 -0
  384. package/dist/shared/scripted-opening.js +72 -0
  385. package/dist/shared/slot-paint.d.ts +14 -2
  386. package/dist/shared/slot-paint.d.ts.map +1 -1
  387. package/dist/shared/slot-paint.js +29 -29
  388. package/dist/shared/surface-paint.d.ts.map +1 -1
  389. package/dist/shared/surface-paint.js +2 -5
  390. package/dist/shared/swap-preview-material.d.ts +11 -0
  391. package/dist/shared/swap-preview-material.d.ts.map +1 -0
  392. package/dist/shared/swap-preview-material.js +16 -0
  393. package/dist/shared/system-checks.d.ts.map +1 -1
  394. package/dist/shared/system-checks.js +5 -3
  395. package/dist/shared/wall-attach-target.d.ts +3 -10
  396. package/dist/shared/wall-attach-target.d.ts.map +1 -1
  397. package/dist/shared/wall-attach-target.js +3 -63
  398. package/dist/shared/wall-opening-ceiling.d.ts +5 -15
  399. package/dist/shared/wall-opening-ceiling.d.ts.map +1 -1
  400. package/dist/shared/wall-opening-ceiling.js +10 -18
  401. package/dist/shelf/definition.d.ts.map +1 -1
  402. package/dist/shelf/definition.js +1 -1
  403. package/dist/shelf/geometry.d.ts.map +1 -1
  404. package/dist/shelf/geometry.js +1 -2
  405. package/dist/shelf/paint.d.ts.map +1 -1
  406. package/dist/shelf/paint.js +4 -10
  407. package/dist/site/line-ribbon.d.ts +31 -0
  408. package/dist/site/line-ribbon.d.ts.map +1 -0
  409. package/dist/site/line-ribbon.js +117 -0
  410. package/dist/site/lot-mask.d.ts +18 -0
  411. package/dist/site/lot-mask.d.ts.map +1 -0
  412. package/dist/site/lot-mask.js +35 -0
  413. package/dist/site/panel.d.ts +10 -0
  414. package/dist/site/panel.d.ts.map +1 -0
  415. package/dist/site/panel.js +155 -0
  416. package/dist/site/parametrics.d.ts +7 -0
  417. package/dist/site/parametrics.d.ts.map +1 -1
  418. package/dist/site/parametrics.js +8 -0
  419. package/dist/site/recessed-slab-ground-holes.d.ts.map +1 -1
  420. package/dist/site/recessed-slab-ground-holes.js +5 -2
  421. package/dist/site/renderer.d.ts.map +1 -1
  422. package/dist/site/renderer.js +150 -19
  423. package/dist/site/setbacks.d.ts +33 -0
  424. package/dist/site/setbacks.d.ts.map +1 -0
  425. package/dist/site/setbacks.js +204 -0
  426. package/dist/site/terrain-geometry.d.ts +28 -1
  427. package/dist/site/terrain-geometry.d.ts.map +1 -1
  428. package/dist/site/terrain-geometry.js +78 -4
  429. package/dist/site/terrain-holes.d.ts +15 -0
  430. package/dist/site/terrain-holes.d.ts.map +1 -0
  431. package/dist/site/terrain-holes.js +74 -0
  432. package/dist/site/terrain-mesh.d.ts +9 -2
  433. package/dist/site/terrain-mesh.d.ts.map +1 -1
  434. package/dist/site/terrain-mesh.js +36 -9
  435. package/dist/site/terrain-renderer.d.ts +9 -2
  436. package/dist/site/terrain-renderer.d.ts.map +1 -1
  437. package/dist/site/terrain-renderer.js +14 -4
  438. package/dist/slab/boundary-editor.d.ts +1 -1
  439. package/dist/slab/boundary-editor.d.ts.map +1 -1
  440. package/dist/slab/boundary-editor.js +13 -13
  441. package/dist/slab/definition.d.ts.map +1 -1
  442. package/dist/slab/definition.js +19 -3
  443. package/dist/slab/dependency-tracker.d.ts.map +1 -1
  444. package/dist/slab/dependency-tracker.js +67 -7
  445. package/dist/slab/elevation-limit.d.ts.map +1 -1
  446. package/dist/slab/elevation-limit.js +2 -2
  447. package/dist/slab/floorplan-affordances.d.ts +176 -4
  448. package/dist/slab/floorplan-affordances.d.ts.map +1 -1
  449. package/dist/slab/floorplan-affordances.js +0 -1
  450. package/dist/slab/floorplan-move.d.ts.map +1 -1
  451. package/dist/slab/floorplan-move.js +3 -4
  452. package/dist/slab/floorplan.d.ts +7 -0
  453. package/dist/slab/floorplan.d.ts.map +1 -1
  454. package/dist/slab/floorplan.js +22 -5
  455. package/dist/slab/geometry.d.ts.map +1 -1
  456. package/dist/slab/geometry.js +186 -76
  457. package/dist/slab/move-tool.js +7 -6
  458. package/dist/slab/paint.d.ts +18 -5
  459. package/dist/slab/paint.d.ts.map +1 -1
  460. package/dist/slab/paint.js +458 -9
  461. package/dist/slab/panel.d.ts.map +1 -1
  462. package/dist/slab/panel.js +28 -9
  463. package/dist/slab/placement-ownership.d.ts +2 -1
  464. package/dist/slab/placement-ownership.d.ts.map +1 -1
  465. package/dist/slab/placement-ownership.js +2 -2
  466. package/dist/slab/surface-split.d.ts +24 -0
  467. package/dist/slab/surface-split.d.ts.map +1 -0
  468. package/dist/slab/surface-split.js +389 -0
  469. package/dist/slab/tool.d.ts.map +1 -1
  470. package/dist/slab/tool.js +3 -3
  471. package/dist/stair/baluster-geometry.d.ts +21 -0
  472. package/dist/stair/baluster-geometry.d.ts.map +1 -0
  473. package/dist/stair/baluster-geometry.js +74 -0
  474. package/dist/stair/baluster-guard.d.ts +50 -0
  475. package/dist/stair/baluster-guard.d.ts.map +1 -0
  476. package/dist/stair/baluster-guard.js +71 -0
  477. package/dist/stair/boards-guard.d.ts +37 -0
  478. package/dist/stair/boards-guard.d.ts.map +1 -0
  479. package/dist/stair/boards-guard.js +61 -0
  480. package/dist/stair/cable-guard.d.ts +39 -0
  481. package/dist/stair/cable-guard.d.ts.map +1 -0
  482. package/dist/stair/cable-guard.js +105 -0
  483. package/dist/stair/construction-controls.d.ts +15 -0
  484. package/dist/stair/construction-controls.d.ts.map +1 -0
  485. package/dist/stair/construction-controls.js +29 -0
  486. package/dist/stair/continuous-railings.d.ts +10 -0
  487. package/dist/stair/continuous-railings.d.ts.map +1 -0
  488. package/dist/stair/continuous-railings.js +159 -0
  489. package/dist/stair/definition.d.ts.map +1 -1
  490. package/dist/stair/definition.js +16 -37
  491. package/dist/stair/documentation.d.ts +1 -1
  492. package/dist/stair/documentation.d.ts.map +1 -1
  493. package/dist/stair/documentation.js +10 -17
  494. package/dist/stair/floorplan-affordances.d.ts.map +1 -1
  495. package/dist/stair/floorplan-affordances.js +9 -25
  496. package/dist/stair/floorplan.d.ts +1 -23
  497. package/dist/stair/floorplan.d.ts.map +1 -1
  498. package/dist/stair/floorplan.js +102 -86
  499. package/dist/stair/glass-guard.d.ts +72 -0
  500. package/dist/stair/glass-guard.d.ts.map +1 -0
  501. package/dist/stair/glass-guard.js +167 -0
  502. package/dist/stair/guard-path.d.ts +119 -0
  503. package/dist/stair/guard-path.d.ts.map +1 -0
  504. package/dist/stair/guard-path.js +208 -0
  505. package/dist/stair/materials.d.ts +11 -0
  506. package/dist/stair/materials.d.ts.map +1 -0
  507. package/dist/stair/materials.js +51 -0
  508. package/dist/stair/metal-guard.d.ts +38 -0
  509. package/dist/stair/metal-guard.d.ts.map +1 -0
  510. package/dist/stair/metal-guard.js +79 -0
  511. package/dist/stair/paint.d.ts +32 -0
  512. package/dist/stair/paint.d.ts.map +1 -1
  513. package/dist/stair/paint.js +75 -36
  514. package/dist/stair/panel.d.ts.map +1 -1
  515. package/dist/stair/panel.js +154 -17
  516. package/dist/stair/parametrics.d.ts +1 -1
  517. package/dist/stair/parametrics.d.ts.map +1 -1
  518. package/dist/stair/parametrics.js +32 -15
  519. package/dist/stair/plan-entry.d.ts +31 -0
  520. package/dist/stair/plan-entry.d.ts.map +1 -0
  521. package/dist/stair/plan-entry.js +325 -0
  522. package/dist/stair/post-and-rail-guard.d.ts +31 -0
  523. package/dist/stair/post-and-rail-guard.d.ts.map +1 -0
  524. package/dist/stair/post-and-rail-guard.js +63 -0
  525. package/dist/stair/railing-controls.d.ts +9 -0
  526. package/dist/stair/railing-controls.d.ts.map +1 -0
  527. package/dist/stair/railing-controls.js +49 -0
  528. package/dist/stair/renderer.d.ts.map +1 -1
  529. package/dist/stair/renderer.js +259 -259
  530. package/dist/stair/use-stair-render-data.d.ts +317 -0
  531. package/dist/stair/use-stair-render-data.d.ts.map +1 -0
  532. package/dist/stair/use-stair-render-data.js +73 -0
  533. package/dist/stair-segment/definition.d.ts.map +1 -1
  534. package/dist/stair-segment/definition.js +51 -10
  535. package/dist/stair-segment/panel.d.ts.map +1 -1
  536. package/dist/stair-segment/panel.js +36 -42
  537. package/dist/stair-segment/parametrics.d.ts +1 -1
  538. package/dist/stair-segment/parametrics.d.ts.map +1 -1
  539. package/dist/stair-segment/parametrics.js +21 -8
  540. package/dist/stair-segment/renderer.d.ts.map +1 -1
  541. package/dist/stair-segment/renderer.js +16 -28
  542. package/dist/turbine-vent/definition.d.ts.map +1 -1
  543. package/dist/turbine-vent/definition.js +2 -5
  544. package/dist/turbine-vent/paint.d.ts.map +1 -1
  545. package/dist/turbine-vent/paint.js +2 -5
  546. package/dist/turbine-vent/panel.d.ts.map +1 -1
  547. package/dist/turbine-vent/panel.js +5 -23
  548. package/dist/unit/unit-panel.d.ts.map +1 -1
  549. package/dist/unit/unit-panel.js +6 -1
  550. package/dist/wall/actions.d.ts.map +1 -1
  551. package/dist/wall/actions.js +7 -2
  552. package/dist/wall/construction-dimensions.js +8 -5
  553. package/dist/wall/contextual-dimensions.js +4 -4
  554. package/dist/wall/curtain-wall-materials.d.ts +1 -1
  555. package/dist/wall/curtain-wall-materials.d.ts.map +1 -1
  556. package/dist/wall/curtain-wall-materials.js +2 -2
  557. package/dist/wall/curve-tool.d.ts.map +1 -1
  558. package/dist/wall/curve-tool.js +4 -0
  559. package/dist/wall/definition.d.ts.map +1 -1
  560. package/dist/wall/definition.js +41 -17
  561. package/dist/wall/floorplan-move.d.ts +1 -1
  562. package/dist/wall/floorplan-move.d.ts.map +1 -1
  563. package/dist/wall/floorplan-move.js +20 -83
  564. package/dist/wall/floorplan-tool.d.ts.map +1 -1
  565. package/dist/wall/floorplan-tool.js +10 -11
  566. package/dist/wall/floorplan.d.ts +3 -1
  567. package/dist/wall/floorplan.d.ts.map +1 -1
  568. package/dist/wall/floorplan.js +184 -21
  569. package/dist/wall/measurement.d.ts.map +1 -1
  570. package/dist/wall/measurement.js +5 -5
  571. package/dist/wall/move-endpoint-tool.d.ts.map +1 -1
  572. package/dist/wall/move-endpoint-tool.js +20 -7
  573. package/dist/wall/move-shared.d.ts.map +1 -1
  574. package/dist/wall/move-shared.js +11 -7
  575. package/dist/wall/move-tool.d.ts.map +1 -1
  576. package/dist/wall/move-tool.js +118 -148
  577. package/dist/wall/paint.d.ts +26 -15
  578. package/dist/wall/paint.d.ts.map +1 -1
  579. package/dist/wall/paint.js +306 -117
  580. package/dist/wall/panel-model.d.ts +18 -0
  581. package/dist/wall/panel-model.d.ts.map +1 -1
  582. package/dist/wall/panel-model.js +77 -19
  583. package/dist/wall/panel.d.ts.map +1 -1
  584. package/dist/wall/panel.js +217 -41
  585. package/dist/wall/parametrics.d.ts.map +1 -1
  586. package/dist/wall/parametrics.js +3 -0
  587. package/dist/wall/rectangle-command.d.ts +111 -12
  588. package/dist/wall/rectangle-command.d.ts.map +1 -1
  589. package/dist/wall/rectangle-tool.d.ts.map +1 -1
  590. package/dist/wall/rectangle-tool.js +26 -8
  591. package/dist/wall/renderer.d.ts.map +1 -1
  592. package/dist/wall/renderer.js +4 -2
  593. package/dist/wall/split-floorplan-layer.d.ts.map +1 -1
  594. package/dist/wall/split-floorplan-layer.js +7 -7
  595. package/dist/wall/split-session.d.ts.map +1 -1
  596. package/dist/wall/split-session.js +7 -1
  597. package/dist/wall/system.d.ts.map +1 -1
  598. package/dist/wall/system.js +36 -4
  599. package/dist/wall/tool.d.ts.map +1 -1
  600. package/dist/wall/tool.js +105 -49
  601. package/dist/wall/treatment-level-data.d.ts +3 -2
  602. package/dist/wall/treatment-level-data.d.ts.map +1 -1
  603. package/dist/wall/treatment-level-data.js +17 -13
  604. package/dist/wall/treatments.d.ts +10 -10
  605. package/dist/wall/treatments.d.ts.map +1 -1
  606. package/dist/wall/treatments.js +67 -54
  607. package/dist/wall/wall-batch-system.d.ts +7 -0
  608. package/dist/wall/wall-batch-system.d.ts.map +1 -1
  609. package/dist/wall/wall-batch-system.js +35 -3
  610. package/dist/wall/wall-batch.d.ts +6 -0
  611. package/dist/wall/wall-batch.d.ts.map +1 -1
  612. package/dist/wall/wall-batch.js +7 -1
  613. package/dist/window/definition.d.ts.map +1 -1
  614. package/dist/window/definition.js +12 -4
  615. package/dist/window/floorplan-move.d.ts.map +1 -1
  616. package/dist/window/floorplan-move.js +3 -1
  617. package/dist/window/floorplan.d.ts.map +1 -1
  618. package/dist/window/floorplan.js +145 -60
  619. package/dist/window/mechanism.d.ts +4 -0
  620. package/dist/window/mechanism.d.ts.map +1 -0
  621. package/dist/window/mechanism.js +20 -0
  622. package/dist/window/move-tool.d.ts.map +1 -1
  623. package/dist/window/move-tool.js +79 -48
  624. package/dist/window/paint.d.ts.map +1 -1
  625. package/dist/window/paint.js +3 -0
  626. package/dist/window/panel.d.ts.map +1 -1
  627. package/dist/window/panel.js +32 -88
  628. package/dist/window/placement.d.ts +23 -0
  629. package/dist/window/placement.d.ts.map +1 -0
  630. package/dist/window/placement.js +66 -0
  631. package/dist/window/preview.d.ts.map +1 -1
  632. package/dist/window/preview.js +2 -0
  633. package/dist/window/renderer.d.ts.map +1 -1
  634. package/dist/window/renderer.js +3 -2
  635. package/dist/window/tool.d.ts.map +1 -1
  636. package/dist/window/tool.js +96 -24
  637. package/dist/window/window-math.d.ts +6 -14
  638. package/dist/window/window-math.d.ts.map +1 -1
  639. package/dist/window/window-math.js +14 -20
  640. package/dist/zone/contextual-dimensions.d.ts.map +1 -1
  641. package/dist/zone/contextual-dimensions.js +6 -28
  642. package/dist/zone/definition.d.ts.map +1 -1
  643. package/dist/zone/definition.js +3 -3
  644. package/dist/zone/floorplan-affordances.d.ts +200 -0
  645. package/dist/zone/floorplan-affordances.d.ts.map +1 -1
  646. package/dist/zone/floorplan.d.ts +1 -1
  647. package/dist/zone/floorplan.d.ts.map +1 -1
  648. package/dist/zone/floorplan.js +93 -53
  649. package/dist/zone/quantities-panel.d.ts.map +1 -1
  650. package/dist/zone/quantities-panel.js +3 -14
  651. package/dist/zone/quick-measurement.d.ts +2 -2
  652. package/dist/zone/quick-measurement.d.ts.map +1 -1
  653. package/dist/zone/quick-measurement.js +6 -5
  654. package/dist/zone/renderer.d.ts +1 -1
  655. package/dist/zone/renderer.d.ts.map +1 -1
  656. package/dist/zone/renderer.js +58 -146
  657. package/dist/zone/room-clear-dimensions.js +3 -3
  658. package/dist/zone/room-documentation.d.ts.map +1 -1
  659. package/dist/zone/room-documentation.js +2 -6
  660. package/package.json +11 -11
  661. package/dist/block/slots.d.ts +0 -4
  662. package/dist/block/slots.d.ts.map +0 -1
  663. package/dist/block/slots.js +0 -17
  664. package/dist/cabinet/slots.d.ts +0 -4
  665. package/dist/cabinet/slots.d.ts.map +0 -1
  666. package/dist/cabinet/slots.js +0 -24
  667. package/dist/ceiling/slots.d.ts +0 -6
  668. package/dist/ceiling/slots.d.ts.map +0 -1
  669. package/dist/ceiling/slots.js +0 -7
  670. package/dist/column/slots.d.ts +0 -9
  671. package/dist/column/slots.d.ts.map +0 -1
  672. package/dist/column/slots.js +0 -19
  673. package/dist/door/slots.d.ts +0 -9
  674. package/dist/door/slots.d.ts.map +0 -1
  675. package/dist/door/slots.js +0 -20
  676. package/dist/elevator/slots.d.ts +0 -8
  677. package/dist/elevator/slots.d.ts.map +0 -1
  678. package/dist/elevator/slots.js +0 -17
  679. package/dist/fence/slots.d.ts +0 -10
  680. package/dist/fence/slots.d.ts.map +0 -1
  681. package/dist/fence/slots.js +0 -23
  682. package/dist/item/__fixtures__/main-floorplan.d.ts +0 -26
  683. package/dist/item/__fixtures__/main-floorplan.d.ts.map +0 -1
  684. package/dist/item/__fixtures__/main-floorplan.js +0 -297
  685. package/dist/lean-to-extension/slots.d.ts +0 -5
  686. package/dist/lean-to-extension/slots.d.ts.map +0 -1
  687. package/dist/lean-to-extension/slots.js +0 -15
  688. package/dist/shelf/slots.d.ts +0 -9
  689. package/dist/shelf/slots.d.ts.map +0 -1
  690. package/dist/shelf/slots.js +0 -30
  691. package/dist/slab/slots.d.ts +0 -10
  692. package/dist/slab/slots.d.ts.map +0 -1
  693. package/dist/slab/slots.js +0 -20
  694. package/dist/stair/floor-stack.d.ts +0 -10
  695. package/dist/stair/floor-stack.d.ts.map +0 -1
  696. package/dist/stair/floor-stack.js +0 -115
  697. package/dist/stair/slots.d.ts +0 -7
  698. package/dist/stair/slots.d.ts.map +0 -1
  699. package/dist/stair/slots.js +0 -13
  700. package/dist/wall/drawing-mode.d.ts +0 -12
  701. package/dist/wall/drawing-mode.d.ts.map +0 -1
  702. package/dist/wall/drawing-mode.js +0 -38
  703. package/dist/wall/slots.d.ts +0 -10
  704. package/dist/wall/slots.d.ts.map +0 -1
  705. package/dist/wall/slots.js +0 -92
  706. package/dist/window/slots.d.ts +0 -5
  707. package/dist/window/slots.d.ts.map +0 -1
  708. package/dist/window/slots.js +0 -11
@@ -1,11 +1,19 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { resolveStairTotalRise, useLiveNodeOverrides, useRegistry, useScene, } from '@pascal-app/core';
4
- import { getStairBodyMaterials, getStairRailingMaterial, NodeRenderer, resolveMaterialRef, resolveSlotDefaultMaterial, useNodeEvents, useViewer, } from '@pascal-app/viewer';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { computeSegmentTransforms, measureStairDetail, resolveArcStairConstruction, resolveStairArcLayout, resolveStairRailPaths, resolveStairWalkingPaths, rotateXZ, STAIR_RAILING_SLOT_DEFAULT, stairArcSliceCount, useLiveNodeOverrides, useRegistry, useScene, } from '@pascal-app/core';
4
+ import { getStairBodyMaterials, getStairRailingMaterial, NodeRenderer, OVERLAY_LAYER, useNodeEvents, useViewer, } from '@pascal-app/viewer';
5
5
  import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
6
6
  import * as THREE from 'three';
7
7
  import { createPlaceholderGeometry } from '../shared/placeholder-geometry';
8
- import { STAIR_BODY_SLOT_DEFAULT, STAIR_RAILING_SLOT_DEFAULT, STAIR_TREADS_SLOT_DEFAULT, } from './slots';
8
+ import { mergeGuardBoxes } from './baluster-geometry';
9
+ import { balusterGuardRails, buildBalusterGuard } from './baluster-guard';
10
+ import { boardsGuardRails, buildBoardsGuard } from './boards-guard';
11
+ import { buildCableGuard, cableGuardRails } from './cable-guard';
12
+ import { ContinuousStairRailings } from './continuous-railings';
13
+ import { barBox, GUARD_PICKET_PITCH, GUARD_POST_SPACING, } from './guard-path';
14
+ import { resolveStairBodySlotMaterials, resolveStairSegmentMaterials, resolveStairSlotMaterial, } from './materials';
15
+ import { buildPostAndRailGuard, postAndRailGuardRails } from './post-and-rail-guard';
16
+ import { useStairRenderData } from './use-stair-render-data';
9
17
  export const StairRenderer = ({ node: rawNode }) => {
10
18
  const ref = useRef(null);
11
19
  // Merge any live drag override into the node so curved/spiral geometry
@@ -26,227 +34,255 @@ export const StairRenderer = ({ node: rawNode }) => {
26
34
  const colorPreset = useViewer((s) => s.colorPreset);
27
35
  const sceneMaterials = useScene((s) => s.materials);
28
36
  const baseBodyMaterials = useMemo(() => getStairBodyMaterials(node, shading, textures, colorPreset), [node, shading, textures, colorPreset]);
29
- const bodyMaterials = useMemo(() => [
30
- resolveStairSlotMaterial(node, 'treads', STAIR_TREADS_SLOT_DEFAULT, baseBodyMaterials[STAIR_TREAD_MATERIAL_INDEX], sceneMaterials, shading, textures),
31
- resolveStairSlotMaterial(node, 'body', STAIR_BODY_SLOT_DEFAULT, baseBodyMaterials[STAIR_SIDE_MATERIAL_INDEX], sceneMaterials, shading, textures),
32
- ], [baseBodyMaterials, node, sceneMaterials, shading, textures]);
37
+ const bodyMaterials = useMemo(() => resolveStairBodySlotMaterials(node, baseBodyMaterials, sceneMaterials, shading, textures), [baseBodyMaterials, node, sceneMaterials, shading, textures]);
38
+ const renderData = useStairRenderData(node);
39
+ const { segments, totalRise } = renderData;
40
+ useLayoutEffect(() => {
41
+ for (const id of node.children)
42
+ useScene.getState().markDirty(id);
43
+ }, [node.children, node.construction]);
44
+ const detail = useMemo(() => measureStairDetail(node, segments), [node, segments]);
45
+ const mergedMaterials = useMemo(() => segments.length
46
+ ? segments.flatMap((segment) => resolveStairSegmentMaterials(segment, node, bodyMaterials, sceneMaterials, shading, textures, colorPreset))
47
+ : bodyMaterials, [segments, node, bodyMaterials, sceneMaterials, shading, textures, colorPreset]);
48
+ const mergedSlotData = useMemo(() => ({
49
+ surfaceNodeIds: segments.flatMap((segment) => [segment.id, segment.id]),
50
+ segmentIds: segments.flatMap((segment) => [segment.id, segment.id]),
51
+ slotIds: Array.from({ length: Math.max(1, segments.length) }, () => [
52
+ 'treads',
53
+ 'body',
54
+ ]).flat(),
55
+ }), [segments]);
33
56
  const baseRailingMaterial = useMemo(() => getStairRailingMaterial(node, shading, textures, colorPreset), [node, shading, textures, colorPreset]);
34
57
  const railingMaterial = useMemo(() => resolveStairSlotMaterial(node, 'railing', STAIR_RAILING_SLOT_DEFAULT, baseRailingMaterial, sceneMaterials, shading, textures), [baseRailingMaterial, node, sceneMaterials, shading, textures]);
35
- // 2 groups map 1:1 to the stair body's 2-material array (treads + body).
58
+ // Each flight contributes tread and body material groups to the merged mesh.
36
59
  const straightPlaceholderGeometry = useMemo(() => createPlaceholderGeometry(2), []);
37
60
  useEffect(() => {
38
61
  return () => {
39
62
  straightPlaceholderGeometry.dispose();
40
63
  };
41
64
  }, [straightPlaceholderGeometry]);
42
- return (_jsxs("group", { "position-x": node.position[0], "position-z": node.position[2], ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers, children: [isSegmentBasedStair ? (_jsx("mesh", { castShadow: true, geometry: straightPlaceholderGeometry, material: bodyMaterials, name: "merged-stair", receiveShadow: true, userData: STAIR_BODY_SLOT_USER_DATA })) : null, isSegmentBasedStair ? null : _jsx(CurvedStairBody, { bodyMaterials: bodyMaterials, stair: node }), _jsx(StairRailings, { material: railingMaterial, stair: node }), isSegmentBasedStair ? (_jsx("group", { name: "segments-wrapper", visible: false, children: (node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) })) : null] }));
65
+ return (_jsxs("group", { "position-x": node.position[0], "position-z": node.position[2], ref: ref, "rotation-y": node.rotation, visible: node.visible, userData: { pascalExportRefusal: detail.error }, ...handlers, children: [isSegmentBasedStair ? (_jsx("mesh", { castShadow: true, geometry: straightPlaceholderGeometry, material: mergedMaterials, name: "merged-stair", receiveShadow: true, userData: mergedSlotData })) : null, isSegmentBasedStair || detail.error ? null : (_jsx(CurvedStairBody, { bodyMaterials: bodyMaterials, stair: node, rise: totalRise })), detail.error ? null : segments.some((segment) => segment.winder) ||
66
+ node.railingPath === 'continuous' ||
67
+ node.railingStyle === 'glass' ||
68
+ node.railingStyle === 'metal' ? (_jsx(ContinuousStairRailings, { material: railingMaterial, stair: node, renderData: renderData })) : (_jsxs(_Fragment, { children: [_jsx(StairRailings, { material: railingMaterial, stair: node, renderData: renderData }), node.handrail ? (_jsx(ContinuousStairRailings, { material: railingMaterial, stair: node, renderData: renderData, guard: false })) : null] })), _jsx(StairWalkingLine, { stair: node, segments: segments, totalRise: totalRise }), isSegmentBasedStair && !detail.error ? (_jsx("group", { name: "segments-wrapper", visible: false, children: (node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) })) : null] }));
43
69
  };
44
- function StairRailings({ stair, material }) {
45
- const nodes = useScene((state) => state.nodes);
46
- // Stair segments' width/length/height arrow handles publish drag values to
47
- // `useLiveNodeOverrides` and only commit to zustand on release. Subscribing
48
- // here and merging each child segment's override means the railing tracks
49
- // the drag in real time instead of freezing at the pre-drag values.
50
- const overrides = useLiveNodeOverrides((s) => s.overrides);
51
- const segments = useMemo(() => (stair.children ?? [])
52
- .map((childId) => {
53
- const base = nodes[childId];
54
- if (!base)
55
- return undefined;
56
- const override = overrides.get(childId);
57
- return (override ? { ...base, ...override } : base);
58
- })
59
- .filter((node) => node?.type === 'stair-segment' && node.visible !== false), [nodes, overrides, stair.children]);
60
- const railPaths = useMemo(() => buildStairRailPaths(segments, stair.railingMode ?? 'none'), [segments, stair.railingMode]);
70
+ function StairWalkingLine({ stair, segments, totalRise, }) {
71
+ const selected = useViewer((state) => state.selection.selectedIds.includes(stair.id));
72
+ const lines = useMemo(() => {
73
+ if (!selected)
74
+ return [];
75
+ return resolveStairWalkingPaths(stair, segments, totalRise).map((points) => {
76
+ const geometry = new THREE.BufferGeometry().setFromPoints(points.map(([x, y, z]) => new THREE.Vector3(x, y + 0.02, z)));
77
+ const line = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: '#2563eb', depthTest: false }));
78
+ line.name = 'stair-walking-line';
79
+ line.layers.set(OVERLAY_LAYER);
80
+ line.userData.pascalExport = 'strip';
81
+ line.raycast = () => { };
82
+ return line;
83
+ });
84
+ }, [selected, stair, segments, totalRise]);
85
+ useEffect(() => () => {
86
+ for (const line of lines) {
87
+ line.geometry.dispose();
88
+ line.material.dispose();
89
+ }
90
+ }, [lines]);
91
+ return (_jsx("group", { name: "stair-walking-lines", children: lines.map((line) => (_jsx("primitive", { object: line, dispose: null }, line.uuid))) }));
92
+ }
93
+ function StairRailings({ stair, material, renderData, }) {
94
+ const { stair: resolvedStair, segments, nodes } = renderData;
95
+ const railPaths = useMemo(() => buildStairRailPaths(segments, stair.railingMode ?? 'none')
96
+ .map((path, index) => ({
97
+ ...path,
98
+ connectFromPrevious: path.connectFromPrevious && segments[index - 1]?.visible !== false,
99
+ }))
100
+ .filter((path) => path.layout.segment.visible !== false), [segments, stair.railingMode]);
61
101
  const railHeight = stair.railingHeight ?? 0.92;
62
- const midRailHeight = Math.max(railHeight * 0.45, 0.35);
63
- const railRadius = 0.022;
64
- const balusterRadius = 0.018;
102
+ const style = stair.railingStyle ?? 'balusters';
103
+ // Every style — balusters, post-and-rail, cable and the board-infill deck
104
+ // guard — is a merged `GuardMesh` built on the shared path chassis, so each
105
+ // reads as one coherent guard across straight, chained, winder, curved and
106
+ // spiral layouts.
65
107
  if ((stair.railingMode ?? 'none') === 'none') {
66
108
  return null;
67
109
  }
68
110
  if (stair.stairType === 'curved' || stair.stairType === 'spiral') {
69
- const stepCount = Math.max(2, Math.round(stair.stepCount ?? 10));
70
- const sweepAngle = stair.sweepAngle ?? (stair.stairType === 'spiral' ? Math.PI * 2 : Math.PI / 2);
71
- const stepSweep = sweepAngle / stepCount;
72
- const stepHeight = Math.max(resolveStairTotalRise(stair, nodes), 0.1) / stepCount;
73
- const innerRadius = Math.max(stair.stairType === 'spiral' ? 0.05 : 0.2, stair.innerRadius ?? 0.9);
74
- const outerRadius = innerRadius + Math.max(stair.width ?? 1, 0.4);
75
- const leftRadius = sweepAngle >= 0 ? innerRadius + 0.04 : outerRadius - 0.04;
76
- const rightRadius = sweepAngle >= 0 ? outerRadius - 0.04 : innerRadius + 0.04;
77
- const radii = stair.railingMode === 'both'
78
- ? [leftRadius, rightRadius]
79
- : stair.railingMode === 'left'
80
- ? [leftRadius]
81
- : stair.railingMode === 'right'
82
- ? [rightRadius]
83
- : [];
84
- return (_jsx("group", { name: "stair-railing", children: radii.map((radius, sideIndex) => {
85
- const sidePoints = Array.from({ length: stepCount }).map((_, index) => {
86
- const angle = -sweepAngle / 2 + stepSweep * index + stepSweep / 2;
87
- return [
88
- Math.cos(angle) * radius,
89
- stair.stairType === 'spiral'
90
- ? stepHeight * index + Math.max(stair.thickness ?? 0.25, 0.02)
91
- : stepHeight * (index + 1),
92
- Math.sin(angle) * radius,
93
- ];
94
- });
95
- return (_jsxs("group", { children: [sidePoints.map((point, pointIndex) => (_jsx("mesh", { castShadow: true, geometry: BALUSTER_GEOMETRY, material: material, name: "stair-railing-baluster", position: [point[0], point[1] + railHeight / 2, point[2]], receiveShadow: true, scale: [balusterRadius, railHeight, balusterRadius], userData: STAIR_RAILING_SLOT_USER_DATA }, `${stair.id}-curved-baluster-${sideIndex}-${pointIndex}`))), sidePoints.slice(0, -1).map((point, pointIndex) => {
96
- const nextPoint = sidePoints[pointIndex + 1];
97
- if (!nextPoint)
98
- return null;
99
- return (_jsxs("group", { children: [_jsx(RailSegment, { end: [nextPoint[0], nextPoint[1] + railHeight, nextPoint[2]], material: material, radius: railRadius, start: [point[0], point[1] + railHeight, point[2]] }), _jsx(RailSegment, { end: [nextPoint[0], nextPoint[1] + midRailHeight, nextPoint[2]], material: material, radius: railRadius * 0.8, start: [point[0], point[1] + midRailHeight, point[2]] })] }, `${stair.id}-curved-rail-${sideIndex}-${pointIndex}`));
100
- })] }, `${stair.id}-curved-railing-${sideIndex}`));
101
- }) }));
111
+ // The arc rail follows the same analytic ≤5° path the continuous guard and
112
+ // the exporters read (`resolveStairRailPaths`): one shared source for the
113
+ // signed, possibly multi-turn sweep, the inner/outer radius per side and the
114
+ // integrated landing arrival — not a per-step polyline that facets the curve
115
+ // and drops the landing. `GuardMesh` then spaces pickets by run, so the
116
+ // density tracks the arc length instead of the step count — for the deck
117
+ // post-and-rail guard as well as the balusters.
118
+ const arcPaths = resolveStairRailPaths(resolvedStair, nodes, stair.railingMode ?? 'none');
119
+ if (arcPaths.length === 0)
120
+ return null;
121
+ return (_jsx("group", { name: "stair-railing", userData: { pascalIfcRole: 'railing' }, children: arcPaths.map((path, index) => (_jsx(GuardMesh, { material: material, points: path.points, postThrough: stair.railingPostThrough === true, railHeight: railHeight, reach: stair.railingTopReach ?? 0, style: style, topPost: stair.railingTopPost !== false }, `${stair.id}-arc-railing-${path.side}-${index}`))) }));
102
122
  }
103
123
  if (railPaths.length === 0) {
104
124
  return null;
105
125
  }
106
- return (_jsxs("group", { name: "stair-railing", children: [railPaths.map((segmentPath, index) => (_jsx("group", { position: [
126
+ // `topPost`/`reach` describe the top of the whole stair, so they apply only
127
+ // to the flight whose guard reaches the highest point; the rest keep their
128
+ // newel at the junction with the next flight.
129
+ const topY = Math.max(...railPaths.flatMap((segmentPath) => segmentPath.sidePaths.map((sidePath) => segmentPath.layout.elevation + Math.max(...sidePath.points.map((point) => point[1])))));
130
+ // The rails bridging consecutive flights, as metre-UV boxes in the stair's
131
+ // local frame, merged into one mesh (E-009) like the per-flight guards. The
132
+ // bridge carries the same rails as the guard style it joins.
133
+ const connectorRails = style === 'post-and-rail'
134
+ ? postAndRailGuardRails(railHeight)
135
+ : style === 'cable'
136
+ ? cableGuardRails(railHeight)
137
+ : style === 'boards'
138
+ ? boardsGuardRails(railHeight)
139
+ : balusterGuardRails(railHeight);
140
+ const connectorBoxes = railPaths.slice(1).flatMap((segmentPath, index) => {
141
+ const previousPath = railPaths[index];
142
+ if (!(previousPath && segmentPath.connectFromPrevious))
143
+ return [];
144
+ if (previousPath.layout.segment.segmentType === 'landing')
145
+ return [];
146
+ if (segmentPath.layout.segment.segmentType === 'landing')
147
+ return [];
148
+ return segmentPath.sidePaths.flatMap((sidePath) => {
149
+ const currentPoint = sidePath.points[0];
150
+ if (!currentPoint)
151
+ return [];
152
+ const currentWorldPoint = toWorldRailPoint(segmentPath.layout, currentPoint);
153
+ const previousSidePath = [...previousPath.sidePaths]
154
+ .map((entry) => {
155
+ const lastPoint = entry.points[entry.points.length - 1];
156
+ return {
157
+ entry,
158
+ distance: lastPoint
159
+ ? distance3(toWorldRailPoint(previousPath.layout, lastPoint), currentWorldPoint)
160
+ : Number.POSITIVE_INFINITY,
161
+ };
162
+ })
163
+ .sort((left, right) => left.distance - right.distance)[0]?.entry;
164
+ const previousPoint = previousSidePath?.points.length
165
+ ? previousSidePath.points[previousSidePath.points.length - 1]
166
+ : null;
167
+ if (!previousPoint)
168
+ return [];
169
+ const previousWorldPoint = toWorldRailPoint(previousPath.layout, previousPoint);
170
+ return connectorRails
171
+ .map((rail) => barBox([previousWorldPoint[0], previousWorldPoint[1] + rail.y, previousWorldPoint[2]], [currentWorldPoint[0], currentWorldPoint[1] + rail.y, currentWorldPoint[2]], rail.across, rail.vertical))
172
+ .filter((box) => box !== null);
173
+ });
174
+ });
175
+ return (_jsxs("group", { name: "stair-railing", userData: { pascalIfcRole: 'railing' }, children: [railPaths.map((segmentPath) => (_jsx("group", { position: [
107
176
  segmentPath.layout.center[0],
108
177
  segmentPath.layout.elevation,
109
178
  segmentPath.layout.center[1],
110
- ], "rotation-y": segmentPath.layout.rotation, children: segmentPath.sidePaths.map((sidePath, sideIndex) => (_jsxs("group", { children: [sidePath.points.map((point, pointIndex) => (_jsx("mesh", { castShadow: true, geometry: BALUSTER_GEOMETRY, material: material, name: "stair-railing-baluster", position: [point[2], point[1] + railHeight / 2, point[0]], receiveShadow: true, scale: [balusterRadius, railHeight, balusterRadius], userData: STAIR_RAILING_SLOT_USER_DATA }, `${segmentPath.layout.segment.id}-${sidePath.side}-baluster-${pointIndex}`))), sidePath.points.slice(0, -1).map((point, pointIndex) => {
111
- const nextPoint = sidePath.points[pointIndex + 1];
112
- if (!nextPoint)
113
- return null;
114
- return (_jsxs("group", { children: [_jsx(RailSegment, { end: [nextPoint[2], nextPoint[1] + railHeight, nextPoint[0]], material: material, radius: railRadius, start: [point[2], point[1] + railHeight, point[0]] }), _jsx(RailSegment, { end: [nextPoint[2], nextPoint[1] + midRailHeight, nextPoint[0]], material: material, radius: railRadius * 0.8, start: [point[2], point[1] + midRailHeight, point[0]] })] }, `${segmentPath.layout.segment.id}-${sidePath.side}-rail-${pointIndex}`));
115
- })] }, `${segmentPath.layout.segment.id}-${sidePath.side}-${sideIndex}`))) }, `${segmentPath.layout.segment.id}-railing`))), railPaths.slice(1).map((segmentPath, index) => {
116
- const previousPath = railPaths[index];
117
- if (!(previousPath && segmentPath.connectFromPrevious))
118
- return null;
119
- if (previousPath.layout.segment.segmentType === 'landing')
120
- return null;
121
- if (segmentPath.layout.segment.segmentType === 'landing')
122
- return null;
123
- return segmentPath.sidePaths.map((sidePath, sideIndex) => {
124
- const currentPoint = sidePath.points[0];
125
- if (!currentPoint)
126
- return null;
127
- const currentWorldPoint = toWorldRailPoint(segmentPath.layout, currentPoint);
128
- const previousSidePath = [...previousPath.sidePaths]
129
- .map((entry) => {
130
- const lastPoint = entry.points[entry.points.length - 1];
131
- return {
132
- entry,
133
- distance: lastPoint
134
- ? distance3(toWorldRailPoint(previousPath.layout, lastPoint), currentWorldPoint)
135
- : Number.POSITIVE_INFINITY,
136
- };
137
- })
138
- .sort((left, right) => left.distance - right.distance)[0]?.entry;
139
- const previousPoint = previousSidePath?.points.length
140
- ? previousSidePath.points[previousSidePath.points.length - 1]
141
- : null;
142
- if (!(previousPoint && currentPoint)) {
143
- return null;
144
- }
145
- const previousWorldPoint = toWorldRailPoint(previousPath.layout, previousPoint);
146
- return (_jsxs("group", { children: [_jsx(RailSegment, { end: [
147
- currentWorldPoint[0],
148
- currentWorldPoint[1] + railHeight,
149
- currentWorldPoint[2],
150
- ], material: material, radius: railRadius, start: [
151
- previousWorldPoint[0],
152
- previousWorldPoint[1] + railHeight,
153
- previousWorldPoint[2],
154
- ] }), _jsx(RailSegment, { end: [
155
- currentWorldPoint[0],
156
- currentWorldPoint[1] + midRailHeight,
157
- currentWorldPoint[2],
158
- ], material: material, radius: railRadius * 0.8, start: [
159
- previousWorldPoint[0],
160
- previousWorldPoint[1] + midRailHeight,
161
- previousWorldPoint[2],
162
- ] })] }, `${previousPath.layout.segment.id}-${segmentPath.layout.segment.id}-${sideIndex}`));
163
- });
164
- })] }));
179
+ ], "rotation-y": segmentPath.layout.rotation, children: segmentPath.sidePaths.map((sidePath, sideIndex) => {
180
+ const points = sidePath.points.map((p) => [p[2], p[1], p[0]]);
181
+ const guardTopY = segmentPath.layout.elevation + Math.max(...points.map((p) => p[1]));
182
+ const terminal = guardTopY >= topY - 1e-4;
183
+ // The builder treats the last point as the run's top, so a terminal
184
+ // flight whose points descend is reversed before `reach`/`topPost`.
185
+ const oriented = terminal && (points[0]?.[1] ?? 0) > (points.at(-1)?.[1] ?? 0)
186
+ ? [...points].reverse()
187
+ : points;
188
+ return (_jsx(GuardMesh, { material: material, points: oriented, postThrough: stair.railingPostThrough === true, railHeight: railHeight, reach: terminal ? (stair.railingTopReach ?? 0) : 0, style: style, topPost: terminal ? stair.railingTopPost !== false : true }, `${segmentPath.layout.segment.id}-${sidePath.side}-${sideIndex}`));
189
+ }) }, `${segmentPath.layout.segment.id}-railing`))), _jsx(ConnectorRails, { boxes: connectorBoxes, material: material })] }));
165
190
  }
166
- const BALUSTER_GEOMETRY = new THREE.CylinderGeometry(1, 1, 1, 8);
167
- const RAIL_GEOMETRY = new THREE.CylinderGeometry(1, 1, 1, 8);
191
+ // Shared, module-level geometries: every mesh that uses one carries
192
+ // `dispose={null}` — R3F disposes a mesh's geometry on unmount, and a
193
+ // regenerated house unmounts its stairs, after which the next stair drew a
194
+ // disposed buffer (WebGPU: "Vertex buffer slot 0 … was not set").
168
195
  const STAIR_TREAD_MATERIAL_INDEX = 0;
169
196
  const STAIR_SIDE_MATERIAL_INDEX = 1;
170
197
  const STAIR_BODY_SLOT_IDS = ['treads', 'body'];
171
198
  const STAIR_BODY_SLOT_USER_DATA = { slotIds: STAIR_BODY_SLOT_IDS };
172
199
  const STAIR_BODY_SINGLE_SLOT_USER_DATA = { slotId: 'body' };
173
200
  const STAIR_RAILING_SLOT_USER_DATA = { slotId: 'railing' };
174
- function hasMaterialSpec(material, materialPreset) {
175
- return material !== undefined || typeof materialPreset === 'string';
176
- }
177
- function hasLegacyStairSlotMaterial(node, slotId) {
178
- const hasWhole = hasMaterialSpec(node.material, node.materialPreset);
179
- const hasTread = hasMaterialSpec(node.treadMaterial, node.treadMaterialPreset);
180
- const hasSide = hasMaterialSpec(node.sideMaterial, node.sideMaterialPreset);
181
- const hasRailing = hasMaterialSpec(node.railingMaterial, node.railingMaterialPreset);
182
- if (slotId === 'treads')
183
- return hasTread || hasSide || hasWhole;
184
- if (slotId === 'body')
185
- return hasSide || hasTread || hasWhole;
186
- return hasRailing || hasTread || hasSide || hasWhole;
187
- }
188
- function resolveStairSlotMaterial(node, slotId, defaultRef, baseMaterial, sceneMaterials, shading, textures) {
189
- if (!textures)
190
- return baseMaterial;
191
- const slotMaterial = resolveMaterialRef(node.slots?.[slotId], sceneMaterials, shading);
192
- if (slotMaterial)
193
- return slotMaterial;
194
- if (hasLegacyStairSlotMaterial(node, slotId))
195
- return baseMaterial;
196
- return resolveSlotDefaultMaterial(defaultRef, shading);
201
+ /**
202
+ * A guard for one rail path — a run of posts, rails and infill spaced by run
203
+ * length (not one per path vertex), merged into a single metre-UV mesh (E-009)
204
+ * so a finish tiles the same as the continuous guard and the whole guard is one
205
+ * draw call. `style` picks the members: 'post-and-rail' and 'boards' build the
206
+ * DCA 6 deck guard (4x4 posts, a flat cap) with square pickets or a stack of
207
+ * flat board courses respectively, 'cable' spans chords between the posts, and
208
+ * every other style builds the balusters. Spacing by run keeps the infill
209
+ * density coherent whether the path is a straight flight's nosing line or a
210
+ * densely sampled arc. `topPost`/`reach` describe the run's terminal end
211
+ * and so are passed only on the flight that reaches the top of the stair; every
212
+ * other flight keeps its post where it meets the next.
213
+ */
214
+ function GuardMesh({ points, railHeight, postThrough, topPost = true, reach = 0, style, material, }) {
215
+ const geometry = useMemo(() => mergeGuardBoxes(style === 'post-and-rail'
216
+ ? buildPostAndRailGuard(points, {
217
+ railHeight,
218
+ postSpacing: GUARD_POST_SPACING,
219
+ postThrough,
220
+ topPost,
221
+ reach,
222
+ })
223
+ : style === 'cable'
224
+ ? buildCableGuard(points, {
225
+ railHeight,
226
+ postSpacing: GUARD_POST_SPACING,
227
+ postThrough,
228
+ topPost,
229
+ reach,
230
+ })
231
+ : style === 'boards'
232
+ ? buildBoardsGuard(points, {
233
+ railHeight,
234
+ postSpacing: GUARD_POST_SPACING,
235
+ postThrough,
236
+ topPost,
237
+ reach,
238
+ })
239
+ : buildBalusterGuard(points, {
240
+ railHeight,
241
+ pickets: GUARD_PICKET_PITCH,
242
+ postSpacing: GUARD_POST_SPACING,
243
+ postThrough,
244
+ topPost,
245
+ reach,
246
+ })), [points, railHeight, postThrough, topPost, reach, style]);
247
+ useEffect(() => () => geometry?.dispose(), [geometry]);
248
+ if (!geometry)
249
+ return null;
250
+ return (_jsx("mesh", { castShadow: true, dispose: null, geometry: geometry, material: material, name: style === 'post-and-rail'
251
+ ? 'stair-railing-post-and-rail'
252
+ : style === 'cable'
253
+ ? 'stair-railing-cable'
254
+ : style === 'boards'
255
+ ? 'stair-railing-boards'
256
+ : 'stair-railing-baluster', receiveShadow: true, userData: STAIR_RAILING_SLOT_USER_DATA }));
197
257
  }
198
- function RailSegment({ start, end, radius, material, }) {
199
- const startVector = useMemo(() => new THREE.Vector3(...start), [start]);
200
- const endVector = useMemo(() => new THREE.Vector3(...end), [end]);
201
- const direction = useMemo(() => endVector.clone().sub(startVector), [endVector, startVector]);
202
- const length = Math.max(direction.length(), 0.01);
203
- const quaternion = useMemo(() => new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction.clone().normalize()), [direction]);
204
- const midpoint = useMemo(() => startVector.clone().add(endVector).multiplyScalar(0.5), [endVector, startVector]);
205
- return (_jsx("mesh", { castShadow: true, geometry: RAIL_GEOMETRY, material: material, name: "stair-railing-rail", position: [midpoint.x, midpoint.y, midpoint.z], quaternion: quaternion, receiveShadow: true, scale: [Math.max(radius, 0.01), length, Math.max(radius, 0.01)], userData: STAIR_RAILING_SLOT_USER_DATA }));
258
+ /** The rails bridging consecutive flights, merged into one metre-UV mesh. */
259
+ function ConnectorRails({ boxes, material }) {
260
+ const geometry = useMemo(() => mergeGuardBoxes(boxes), [boxes]);
261
+ useEffect(() => () => geometry?.dispose(), [geometry]);
262
+ if (!geometry)
263
+ return null;
264
+ return (_jsx("mesh", { castShadow: true, dispose: null, geometry: geometry, material: material, name: "stair-railing-baluster", receiveShadow: true, userData: STAIR_RAILING_SLOT_USER_DATA }));
206
265
  }
207
- function CurvedStairBody({ stair, bodyMaterials, }) {
208
- const nodes = useScene((state) => state.nodes);
266
+ function CurvedStairBody({ stair, bodyMaterials, rise, }) {
209
267
  const sideMaterial = bodyMaterials[1];
210
- const stepCount = Math.max(2, Math.round(stair.stepCount ?? 10));
211
- const totalRise = Math.max(resolveStairTotalRise(stair, nodes), 0.1);
212
- const stepHeight = totalRise / stepCount;
268
+ const totalRise = Math.max(rise, 0.001);
213
269
  const isSpiral = stair.stairType === 'spiral';
214
- const innerRadius = Math.max(isSpiral ? 0.05 : 0.2, stair.innerRadius ?? 0.9);
215
- const outerRadius = innerRadius + Math.max(stair.width ?? 1, 0.4);
216
- const sweepAngle = stair.sweepAngle ?? (isSpiral ? Math.PI * 2 : Math.PI / 2);
217
- const stepSweep = sweepAngle / stepCount;
218
- const thickness = Math.max(stair.thickness ?? 0.25, 0.02);
219
- const fillToFloor = stair.fillToFloor ?? true;
220
- const spiralColumnRadius = Math.max(0.05, Math.min(innerRadius * 0.72, innerRadius - 0.03));
270
+ const layout = resolveStairArcLayout(stair, totalRise);
271
+ const { innerRadius, outerRadius, thickness } = layout;
272
+ const spiralColumnRadius = Math.min(innerRadius * 0.72, Math.max(innerRadius - 0.03, innerRadius * 0.5));
221
273
  const spiralColumnHeight = totalRise + thickness;
222
- const spiralLandingDepth = Math.max(0.3, stair.topLandingDepth ?? Math.max((stair.width ?? 1) * 0.9, 0.8));
223
- const spiralLandingSweep = isSpiral && (stair.topLandingMode ?? 'none') === 'integrated'
224
- ? Math.min(Math.PI * 0.75, spiralLandingDepth / Math.max(innerRadius + (stair.width ?? 1) / 2, 0.1)) * Math.sign(sweepAngle || 1)
225
- : 0;
226
- const spiralLastStepTop = stepHeight * Math.max(stepCount - 1, 0) + thickness;
227
- const spiralLandingThickness = isSpiral && (stair.topLandingMode ?? 'none') === 'integrated'
228
- ? Math.max(0.02, totalRise - spiralLastStepTop)
229
- : 0;
230
- return (_jsxs("group", { name: isSpiral ? 'spiral-stair' : 'curved-stair', children: [isSpiral && (stair.showCenterColumn ?? true) ? (_jsx(SpiralColumnMesh, { height: spiralColumnHeight, material: sideMaterial, radius: spiralColumnRadius })) : null, Array.from({ length: stepCount }).map((_, index) => {
231
- const currentHeight = stepHeight * (index + 1);
232
- const actualStepHeight = isSpiral
233
- ? thickness
234
- : fillToFloor
235
- ? Math.max(currentHeight, thickness)
236
- : thickness;
237
- const startAngle = -sweepAngle / 2 + stepSweep * index;
238
- const endAngle = startAngle + stepSweep;
239
- const stepY = isSpiral
240
- ? stepHeight * index
241
- : fillToFloor
242
- ? 0
243
- : Math.max(currentHeight - thickness, 0);
244
- const midAngle = startAngle + stepSweep / 2;
245
- return (_jsxs("group", { "position-y": stepY, children: [isSpiral && (stair.showStepSupports ?? true) ? (_jsx(SpiralStepSupportMesh, { innerRadius: innerRadius, material: sideMaterial, midAngle: midAngle, spiralColumnRadius: spiralColumnRadius, thickness: thickness })) : null, _jsx(CurvedStepMesh, { endAngle: endAngle, innerRadius: innerRadius, material: bodyMaterials, outerRadius: outerRadius, positionY: 0, startAngle: startAngle, stepHeight: actualStepHeight, thickness: thickness })] }, `${stair.id}-${isSpiral ? 'spiral' : 'curved'}-step-${index}`));
246
- }), isSpiral && (stair.topLandingMode ?? 'none') === 'integrated' ? (_jsx(CurvedStepMesh, { endAngle: sweepAngle / 2 + spiralLandingSweep, innerRadius: innerRadius, material: bodyMaterials, outerRadius: outerRadius, positionY: spiralLastStepTop, startAngle: sweepAngle / 2, stepHeight: spiralLandingThickness, thickness: spiralLandingThickness })) : null] }));
274
+ const explicitPieces = resolveArcStairConstruction(stair, totalRise);
275
+ return (_jsxs("group", { name: isSpiral ? 'spiral-stair' : 'curved-stair', children: [isSpiral && (stair.showCenterColumn ?? true) ? (_jsx(SpiralColumnMesh, { height: spiralColumnHeight, material: sideMaterial, radius: spiralColumnRadius })) : null, explicitPieces ? (_jsxs(_Fragment, { children: [explicitPieces.map((piece, index) => (_jsx(CurvedStepMesh, { innerRadius: piece.innerRadius, outerRadius: piece.outerRadius, startAngle: piece.startAngle, endAngle: piece.endAngle, positionY: piece.bottomStart, stepHeight: piece.top - piece.bottomStart, bottomDelta: piece.bottomEnd - piece.bottomStart, ifcRole: piece.index === layout.stepCount ? 'landing' : undefined, surfaceRole: piece.role, material: bodyMaterials }, `${stair.id}-construction-${index}`))), isSpiral && (stair.showStepSupports ?? true)
276
+ ? layout.steps.map((step, index) => (_jsx("group", { "position-y": step.top -
277
+ (stair.construction.finishThickness + stair.construction.treadThickness), children: _jsx(SpiralStepSupportMesh, { innerRadius: innerRadius, spiralColumnRadius: spiralColumnRadius, midAngle: (step.startAngle + step.endAngle) / 2, thickness: stair.construction.treadThickness, material: sideMaterial }) }, `${stair.id}-support-${index}`)))
278
+ : null] })) : (layout.steps.map((step, index) => {
279
+ const { startAngle, endAngle } = step;
280
+ const midAngle = (startAngle + endAngle) / 2;
281
+ return (_jsxs("group", { "position-y": step.bottom, children: [isSpiral && (stair.showStepSupports ?? true) ? (_jsx(SpiralStepSupportMesh, { innerRadius: innerRadius, material: sideMaterial, midAngle: midAngle, spiralColumnRadius: spiralColumnRadius, thickness: thickness })) : null, _jsx(CurvedStepMesh, { endAngle: endAngle, innerRadius: innerRadius, material: bodyMaterials, outerRadius: outerRadius, positionY: 0, startAngle: startAngle, stepHeight: step.top - step.bottom })] }, `${stair.id}-${isSpiral ? 'spiral' : 'curved'}-step-${index}`));
282
+ })), layout.landing && !explicitPieces ? (_jsx(CurvedStepMesh, { ifcRole: "landing", endAngle: layout.landing.endAngle, innerRadius: innerRadius, material: bodyMaterials, outerRadius: outerRadius, positionY: layout.landing.bottom, startAngle: layout.landing.startAngle, stepHeight: thickness })) : null] }));
247
283
  }
248
- function CurvedStepMesh({ innerRadius, outerRadius, startAngle, endAngle, stepHeight, thickness, positionY, material, }) {
249
- const geometry = useMemo(() => buildCurvedStepGeometry(innerRadius, outerRadius, startAngle, endAngle, Math.max(stepHeight, thickness)), [endAngle, innerRadius, outerRadius, startAngle, stepHeight, thickness]);
284
+ function CurvedStepMesh({ innerRadius, outerRadius, startAngle, endAngle, stepHeight, positionY, material, bottomDelta = 0, surfaceRole = 'mixed', ifcRole, }) {
285
+ const geometry = useMemo(() => buildCurvedStepGeometry(innerRadius, outerRadius, startAngle, endAngle, stepHeight, bottomDelta, surfaceRole), [endAngle, innerRadius, outerRadius, startAngle, stepHeight, bottomDelta, surfaceRole]);
250
286
  // Dispose the prior BufferGeometry as soon as a new one supersedes it.
251
287
  // Resize drags (in 2D or 3D) rebuild this geometry every pointer move;
252
288
  // without explicit disposal, WebGPU keeps a stale pipeline reference to
@@ -256,7 +292,7 @@ function CurvedStepMesh({ innerRadius, outerRadius, startAngle, endAngle, stepHe
256
292
  useEffect(() => () => {
257
293
  geometry.dispose();
258
294
  }, [geometry]);
259
- return (_jsx("mesh", { castShadow: true, geometry: geometry, material: material, "position-y": positionY, receiveShadow: true, userData: STAIR_BODY_SLOT_USER_DATA }));
295
+ return (_jsx("mesh", { castShadow: true, geometry: geometry, material: material, "position-y": positionY, receiveShadow: true, userData: { ...STAIR_BODY_SLOT_USER_DATA, ...(ifcRole ? { pascalIfcRole: ifcRole } : {}) } }));
260
296
  }
261
297
  /**
262
298
  * Spiral center column. The cylinder is rebuilt whenever
@@ -291,36 +327,34 @@ function SpiralStepSupportMesh({ innerRadius, spiralColumnRadius, midAngle, thic
291
327
  const radial = spiralColumnRadius + sizeX / 2 - 0.02;
292
328
  return (_jsx("mesh", { castShadow: true, geometry: geometry, material: material, name: "stair-side", position: [Math.cos(midAngle) * radial, sizeY / 2, Math.sin(midAngle) * radial], receiveShadow: true, "rotation-y": -midAngle, userData: STAIR_BODY_SINGLE_SLOT_USER_DATA }));
293
329
  }
294
- function buildCurvedStepGeometry(innerRadius, outerRadius, startAngle, endAngle, height) {
295
- const clampedHeight = Math.max(height, 0.02);
330
+ function buildCurvedStepGeometry(innerRadius, outerRadius, startAngle, authoredEndAngle, height, bottomDelta = 0, surfaceRole = 'mixed') {
331
+ const clampedHeight = Math.max(height, Number.EPSILON);
296
332
  const y0 = 0;
297
333
  const y1 = clampedHeight;
334
+ const endAngle = startAngle +
335
+ Math.sign(authoredEndAngle - startAngle || 1) *
336
+ Math.min(Math.abs(authoredEndAngle - startAngle), Math.PI * 2);
298
337
  const sweepAngle = endAngle - startAngle;
299
338
  const sweepDirection = Math.sign(sweepAngle) || 1;
300
- const segmentCount = Math.max(4, Math.min(24, Math.ceil(Math.abs(sweepAngle) / (Math.PI / 18) + Math.max(0, (outerRadius - innerRadius) * 3))));
339
+ const segmentCount = stairArcSliceCount(innerRadius, outerRadius, sweepAngle);
301
340
  const positions = [];
302
341
  const normals = [];
303
342
  const uvs = [];
304
343
  const triangleMaterialIndices = [];
305
344
  const pointOnArc = (radius, angle, y) => new THREE.Vector3(Math.cos(angle) * radius, y, Math.sin(angle) * radius);
306
- const pushUv = (point, normal, materialIndex) => {
307
- if (materialIndex === STAIR_TREAD_MATERIAL_INDEX) {
308
- const angle = Math.atan2(point.z, point.x);
309
- const arcOffset = (angle - startAngle) * Math.max((innerRadius + outerRadius) * 0.5, 0.01);
310
- uvs.push(arcOffset, Math.sqrt(point.x * point.x + point.z * point.z) - innerRadius);
311
- return;
312
- }
313
- const absX = Math.abs(normal.x);
314
- const absY = Math.abs(normal.y);
315
- const absZ = Math.abs(normal.z);
316
- if (absY >= absX && absY >= absZ) {
345
+ const pushUv = (point, normal) => {
346
+ if (normal.y > 0.999999) {
317
347
  uvs.push(point.x, point.z);
318
348
  }
319
- else if (absX >= absZ) {
320
- uvs.push(point.z, point.y);
349
+ else if (normal.y < -0.000001) {
350
+ // Each faceted underside triangle has its own physical plane.
351
+ const u = new THREE.Vector3(1, 0, 0).addScaledVector(normal, -normal.x).normalize();
352
+ const v = new THREE.Vector3().crossVectors(normal, u).normalize();
353
+ uvs.push(point.dot(u), point.dot(v));
321
354
  }
322
355
  else {
323
- uvs.push(point.x, point.y);
356
+ const u = new THREE.Vector3(normal.z, 0, -normal.x).normalize();
357
+ uvs.push(point.dot(u), point.y);
324
358
  }
325
359
  };
326
360
  const pushTriangle = (a, b, c, normal, materialIndex) => {
@@ -330,10 +364,17 @@ function buildCurvedStepGeometry(innerRadius, outerRadius, startAngle, endAngle,
330
364
  const ordered = faceNormal.dot(normal) >= 0 ? [a, b, c] : [a, c, b];
331
365
  for (const point of ordered) {
332
366
  positions.push(point.x, point.y, point.z);
333
- normals.push(normal.x, normal.y, normal.z);
334
- pushUv(point, normal, materialIndex);
367
+ const shadingNormal = normal.y < 0
368
+ ? faceNormal.normalize().multiplyScalar(faceNormal.dot(normal) < 0 ? -1 : 1)
369
+ : normal;
370
+ normals.push(shadingNormal.x, shadingNormal.y, shadingNormal.z);
371
+ pushUv(point, shadingNormal);
335
372
  }
336
- triangleMaterialIndices.push(materialIndex);
373
+ triangleMaterialIndices.push(surfaceRole === 'mixed'
374
+ ? materialIndex
375
+ : surfaceRole === 'tread'
376
+ ? STAIR_TREAD_MATERIAL_INDEX
377
+ : STAIR_SIDE_MATERIAL_INDEX);
337
378
  };
338
379
  const pushQuad = (a, b, c, d, normal, materialIndex) => {
339
380
  pushTriangle(a, b, c, normal, materialIndex);
@@ -347,10 +388,10 @@ function buildCurvedStepGeometry(innerRadius, outerRadius, startAngle, endAngle,
347
388
  const segStart = startAngle + sweepAngle * t0;
348
389
  const segEnd = startAngle + sweepAngle * t1;
349
390
  const midAngle = (segStart + segEnd) / 2;
350
- const innerStartBottom = pointOnArc(innerRadius, segStart, y0);
351
- const innerEndBottom = pointOnArc(innerRadius, segEnd, y0);
352
- const outerStartBottom = pointOnArc(outerRadius, segStart, y0);
353
- const outerEndBottom = pointOnArc(outerRadius, segEnd, y0);
391
+ const innerStartBottom = pointOnArc(innerRadius, segStart, y0 + bottomDelta * t0);
392
+ const innerEndBottom = pointOnArc(innerRadius, segEnd, y0 + bottomDelta * t1);
393
+ const outerStartBottom = pointOnArc(outerRadius, segStart, y0 + bottomDelta * t0);
394
+ const outerEndBottom = pointOnArc(outerRadius, segEnd, y0 + bottomDelta * t1);
354
395
  const innerStartTop = pointOnArc(innerRadius, segStart, y1);
355
396
  const innerEndTop = pointOnArc(innerRadius, segEnd, y1);
356
397
  const outerStartTop = pointOnArc(outerRadius, segStart, y1);
@@ -366,8 +407,8 @@ function buildCurvedStepGeometry(innerRadius, outerRadius, startAngle, endAngle,
366
407
  const startOuterBottom = pointOnArc(outerRadius, startAngle, y0);
367
408
  const startInnerTop = pointOnArc(innerRadius, startAngle, y1);
368
409
  const startOuterTop = pointOnArc(outerRadius, startAngle, y1);
369
- const endInnerBottom = pointOnArc(innerRadius, endAngle, y0);
370
- const endOuterBottom = pointOnArc(outerRadius, endAngle, y0);
410
+ const endInnerBottom = pointOnArc(innerRadius, endAngle, y0 + bottomDelta);
411
+ const endOuterBottom = pointOnArc(outerRadius, endAngle, y0 + bottomDelta);
371
412
  const endInnerTop = pointOnArc(innerRadius, endAngle, y1);
372
413
  const endOuterTop = pointOnArc(outerRadius, endAngle, y1);
373
414
  const startNormal = new THREE.Vector3(sweepDirection * Math.sin(startAngle), 0, -sweepDirection * Math.cos(startAngle)).normalize();
@@ -621,47 +662,6 @@ function toWorldRailPoint(layout, point) {
621
662
  const [offsetX, offsetZ] = rotateXZ(localZ, localX, layout.rotation);
622
663
  return [layout.center[0] + offsetX, layout.elevation + localY, layout.center[1] + offsetZ];
623
664
  }
624
- function computeSegmentTransforms(segments) {
625
- const transforms = [];
626
- let currentPos = new THREE.Vector3(0, 0, 0);
627
- let currentRot = 0;
628
- for (let i = 0; i < segments.length; i++) {
629
- const segment = segments[i];
630
- if (i === 0) {
631
- transforms.push({
632
- position: [currentPos.x, currentPos.y, currentPos.z],
633
- rotation: currentRot,
634
- });
635
- continue;
636
- }
637
- const prev = segments[i - 1];
638
- const localAttachPos = new THREE.Vector3();
639
- let rotChange = 0;
640
- switch (segment.attachmentSide) {
641
- case 'front':
642
- localAttachPos.set(0, prev.height, prev.length);
643
- break;
644
- case 'left':
645
- localAttachPos.set(prev.width / 2, prev.height, prev.length / 2);
646
- rotChange = Math.PI / 2;
647
- break;
648
- case 'right':
649
- localAttachPos.set(-prev.width / 2, prev.height, prev.length / 2);
650
- rotChange = -Math.PI / 2;
651
- break;
652
- }
653
- localAttachPos.applyAxisAngle(new THREE.Vector3(0, 1, 0), currentRot);
654
- currentPos = currentPos.clone().add(localAttachPos);
655
- currentRot += rotChange;
656
- transforms.push({ position: [currentPos.x, currentPos.y, currentPos.z], rotation: currentRot });
657
- }
658
- return transforms;
659
- }
660
- function rotateXZ(x, z, angle) {
661
- const cos = Math.cos(angle);
662
- const sin = Math.sin(angle);
663
- return [x * cos + z * sin, -x * sin + z * cos];
664
- }
665
665
  function distance3(a, b) {
666
666
  return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
667
667
  }