@pascal-app/nodes 0.1.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 (408) hide show
  1. package/README.md +34 -0
  2. package/dist/building/definition.js +1 -1
  3. package/dist/cabinet/definition.d.ts.map +1 -1
  4. package/dist/cabinet/definition.js +809 -72
  5. package/dist/cabinet/geometry.d.ts.map +1 -1
  6. package/dist/cabinet/geometry.js +4 -0
  7. package/dist/cabinet/move-frame.js +3 -3
  8. package/dist/cabinet/panel.d.ts.map +1 -1
  9. package/dist/cabinet/panel.js +1 -0
  10. package/dist/cabinet/placement-snap.d.ts +8 -0
  11. package/dist/cabinet/placement-snap.d.ts.map +1 -0
  12. package/dist/cabinet/placement-snap.js +19 -0
  13. package/dist/cabinet/presets.js +8 -8
  14. package/dist/cabinet/quick-actions.d.ts.map +1 -1
  15. package/dist/cabinet/quick-actions.js +9 -3
  16. package/dist/cabinet/resize-limits.d.ts +11 -0
  17. package/dist/cabinet/resize-limits.d.ts.map +1 -0
  18. package/dist/cabinet/resize-limits.js +22 -0
  19. package/dist/cabinet/run-layout.d.ts +8 -2
  20. package/dist/cabinet/run-layout.d.ts.map +1 -1
  21. package/dist/cabinet/run-layout.js +59 -7
  22. package/dist/cabinet/run-ops.d.ts +44 -3
  23. package/dist/cabinet/run-ops.d.ts.map +1 -1
  24. package/dist/cabinet/run-ops.js +646 -110
  25. package/dist/cabinet/run-panel.d.ts +2 -1
  26. package/dist/cabinet/run-panel.d.ts.map +1 -1
  27. package/dist/cabinet/run-panel.js +38 -4
  28. package/dist/cabinet/stack-transitions.js +4 -4
  29. package/dist/cabinet/tool.d.ts.map +1 -1
  30. package/dist/cabinet/tool.js +95 -17
  31. package/dist/cabinet/wall-snap.d.ts.map +1 -1
  32. package/dist/cabinet/wall-snap.js +2 -6
  33. package/dist/ceiling/boundary-editor.d.ts.map +1 -1
  34. package/dist/ceiling/boundary-editor.js +2 -2
  35. package/dist/ceiling/definition.d.ts +1 -1
  36. package/dist/ceiling/definition.d.ts.map +1 -1
  37. package/dist/ceiling/definition.js +36 -5
  38. package/dist/ceiling/floorplan-affordances.d.ts +4 -4
  39. package/dist/ceiling/floorplan-move.d.ts +4 -4
  40. package/dist/ceiling/floorplan-move.d.ts.map +1 -1
  41. package/dist/ceiling/floorplan-move.js +5 -4
  42. package/dist/ceiling/hole-editor.d.ts.map +1 -1
  43. package/dist/ceiling/hole-editor.js +2 -2
  44. package/dist/ceiling/move-tool.d.ts.map +1 -1
  45. package/dist/ceiling/move-tool.js +4 -3
  46. package/dist/ceiling/panel.d.ts.map +1 -1
  47. package/dist/ceiling/panel.js +65 -3
  48. package/dist/ceiling/renderer.d.ts.map +1 -1
  49. package/dist/ceiling/renderer.js +6 -2
  50. package/dist/ceiling/tool.d.ts.map +1 -1
  51. package/dist/ceiling/tool.js +12 -1
  52. package/dist/chimney/definition.d.ts.map +1 -1
  53. package/dist/chimney/definition.js +1 -0
  54. package/dist/chimney/tool.d.ts.map +1 -1
  55. package/dist/chimney/tool.js +16 -1
  56. package/dist/column/definition.d.ts.map +1 -1
  57. package/dist/column/definition.js +14 -2
  58. package/dist/column/floorplan-affordances.d.ts.map +1 -1
  59. package/dist/column/floorplan-affordances.js +19 -14
  60. package/dist/column/floorplan-move.d.ts.map +1 -1
  61. package/dist/column/floorplan-move.js +7 -2
  62. package/dist/column/floorplan.d.ts +9 -1
  63. package/dist/column/floorplan.d.ts.map +1 -1
  64. package/dist/column/floorplan.js +59 -4
  65. package/dist/column/tool.d.ts.map +1 -1
  66. package/dist/column/tool.js +33 -7
  67. package/dist/construction-dimension/definition.d.ts +4 -0
  68. package/dist/construction-dimension/definition.d.ts.map +1 -0
  69. package/dist/construction-dimension/definition.js +83 -0
  70. package/dist/construction-dimension/drawing-coordination.d.ts +7 -0
  71. package/dist/construction-dimension/drawing-coordination.d.ts.map +1 -0
  72. package/dist/construction-dimension/drawing-coordination.js +54 -0
  73. package/dist/construction-dimension/floorplan-affordances.d.ts +4 -0
  74. package/dist/construction-dimension/floorplan-affordances.d.ts.map +1 -0
  75. package/dist/construction-dimension/floorplan-affordances.js +114 -0
  76. package/dist/construction-dimension/floorplan-tool.d.ts +21 -0
  77. package/dist/construction-dimension/floorplan-tool.d.ts.map +1 -0
  78. package/dist/construction-dimension/floorplan-tool.js +557 -0
  79. package/dist/construction-dimension/floorplan.d.ts +3 -0
  80. package/dist/construction-dimension/floorplan.d.ts.map +1 -0
  81. package/dist/construction-dimension/floorplan.js +457 -0
  82. package/dist/construction-dimension/geometry.d.ts +31 -0
  83. package/dist/construction-dimension/geometry.d.ts.map +1 -0
  84. package/dist/construction-dimension/geometry.js +144 -0
  85. package/dist/construction-dimension/index.d.ts +4 -0
  86. package/dist/construction-dimension/index.d.ts.map +1 -0
  87. package/dist/construction-dimension/index.js +3 -0
  88. package/dist/construction-dimension/panel.d.ts +2 -0
  89. package/dist/construction-dimension/panel.d.ts.map +1 -0
  90. package/dist/construction-dimension/panel.js +128 -0
  91. package/dist/construction-dimension/parametrics.d.ts +3 -0
  92. package/dist/construction-dimension/parametrics.d.ts.map +1 -0
  93. package/dist/construction-dimension/parametrics.js +4 -0
  94. package/dist/construction-dimension/schema.d.ts +2 -0
  95. package/dist/construction-dimension/schema.d.ts.map +1 -0
  96. package/dist/construction-dimension/schema.js +1 -0
  97. package/dist/door/contextual-dimensions.d.ts +3 -0
  98. package/dist/door/contextual-dimensions.d.ts.map +1 -0
  99. package/dist/door/contextual-dimensions.js +7 -0
  100. package/dist/door/definition.d.ts.map +1 -1
  101. package/dist/door/definition.js +13 -8
  102. package/dist/door/floorplan-affordances.d.ts +2 -5
  103. package/dist/door/floorplan-affordances.d.ts.map +1 -1
  104. package/dist/door/floorplan-affordances.js +9 -18
  105. package/dist/door/floorplan.d.ts +3 -4
  106. package/dist/door/floorplan.d.ts.map +1 -1
  107. package/dist/door/floorplan.js +88 -6
  108. package/dist/door/move-tool.d.ts.map +1 -1
  109. package/dist/door/move-tool.js +2 -2
  110. package/dist/door/panel.d.ts.map +1 -1
  111. package/dist/door/panel.js +3 -1
  112. package/dist/door/schema.d.ts +1 -1
  113. package/dist/door/schema.d.ts.map +1 -1
  114. package/dist/door/schema.js +1 -1
  115. package/dist/door/tool.d.ts.map +1 -1
  116. package/dist/door/tool.js +63 -6
  117. package/dist/dormer/definition.d.ts.map +1 -1
  118. package/dist/dormer/definition.js +1 -0
  119. package/dist/dormer/tool.d.ts.map +1 -1
  120. package/dist/dormer/tool.js +23 -1
  121. package/dist/duct-segment/tool.d.ts.map +1 -1
  122. package/dist/duct-segment/tool.js +20 -9
  123. package/dist/duct-terminal/tool.d.ts.map +1 -1
  124. package/dist/duct-terminal/tool.js +9 -6
  125. package/dist/elevator/floorplan-affordances.d.ts +2 -6
  126. package/dist/elevator/floorplan-affordances.d.ts.map +1 -1
  127. package/dist/elevator/floorplan-affordances.js +13 -12
  128. package/dist/fence/actions/move-endpoint.d.ts.map +1 -1
  129. package/dist/fence/actions/move-endpoint.js +29 -1
  130. package/dist/fence/definition.d.ts.map +1 -1
  131. package/dist/fence/definition.js +67 -14
  132. package/dist/fence/floorplan-affordances.d.ts.map +1 -1
  133. package/dist/fence/floorplan-affordances.js +48 -11
  134. package/dist/fence/floorplan-move.d.ts.map +1 -1
  135. package/dist/fence/floorplan-move.js +38 -3
  136. package/dist/fence/geometry.d.ts.map +1 -1
  137. package/dist/fence/geometry.js +9 -1
  138. package/dist/fence/lift.d.ts +15 -0
  139. package/dist/fence/lift.d.ts.map +1 -0
  140. package/dist/fence/lift.js +23 -0
  141. package/dist/fence/move-tool.d.ts.map +1 -1
  142. package/dist/fence/move-tool.js +25 -5
  143. package/dist/fence/system.d.ts +3 -0
  144. package/dist/fence/system.d.ts.map +1 -0
  145. package/dist/fence/system.js +41 -0
  146. package/dist/fence/tool.d.ts.map +1 -1
  147. package/dist/fence/tool.js +103 -32
  148. package/dist/hvac-equipment/tool.d.ts.map +1 -1
  149. package/dist/hvac-equipment/tool.js +8 -3
  150. package/dist/index.d.ts +2 -0
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/index.js +6 -0
  153. package/dist/item/definition.d.ts.map +1 -1
  154. package/dist/item/definition.js +7 -1
  155. package/dist/item/floorplan-move.d.ts.map +1 -1
  156. package/dist/item/floorplan-move.js +50 -43
  157. package/dist/item/floorplan.d.ts +1 -0
  158. package/dist/item/floorplan.d.ts.map +1 -1
  159. package/dist/item/floorplan.js +49 -0
  160. package/dist/item/renderer.d.ts +3 -0
  161. package/dist/item/renderer.d.ts.map +1 -1
  162. package/dist/item/renderer.js +96 -8
  163. package/dist/level/definition.d.ts.map +1 -1
  164. package/dist/level/definition.js +7 -3
  165. package/dist/lineset/tool.d.ts.map +1 -1
  166. package/dist/lineset/tool.js +5 -1
  167. package/dist/liquid-line/tool.d.ts.map +1 -1
  168. package/dist/liquid-line/tool.js +7 -1
  169. package/dist/measurement/definition.d.ts.map +1 -1
  170. package/dist/measurement/definition.js +5 -0
  171. package/dist/measurement/floorplan.d.ts.map +1 -1
  172. package/dist/measurement/floorplan.js +11 -10
  173. package/dist/measurement/index.d.ts +1 -1
  174. package/dist/measurement/index.d.ts.map +1 -1
  175. package/dist/measurement/index.js +1 -1
  176. package/dist/measurement/resolve.d.ts +5 -0
  177. package/dist/measurement/resolve.d.ts.map +1 -1
  178. package/dist/measurement/resolve.js +2 -2
  179. package/dist/measurement/surface-query.js +5 -5
  180. package/dist/measurement/tool.d.ts +6 -1
  181. package/dist/measurement/tool.d.ts.map +1 -1
  182. package/dist/measurement/tool.js +7 -11
  183. package/dist/pipe-segment/tool.d.ts.map +1 -1
  184. package/dist/pipe-segment/tool.js +22 -19
  185. package/dist/roof-segment/floorplan-affordances.d.ts.map +1 -1
  186. package/dist/roof-segment/floorplan-affordances.js +14 -8
  187. package/dist/shared/construction-dimension-standards.d.ts +34 -0
  188. package/dist/shared/construction-dimension-standards.d.ts.map +1 -0
  189. package/dist/shared/construction-dimension-standards.js +18 -0
  190. package/dist/shared/construction-length.d.ts +10 -0
  191. package/dist/shared/construction-length.d.ts.map +1 -0
  192. package/dist/shared/construction-length.js +57 -0
  193. package/dist/shared/dimension-string.d.ts +20 -0
  194. package/dist/shared/dimension-string.d.ts.map +1 -0
  195. package/dist/shared/dimension-string.js +19 -0
  196. package/dist/shared/draft-axis-guides.d.ts +36 -0
  197. package/dist/shared/draft-axis-guides.d.ts.map +1 -0
  198. package/dist/shared/draft-axis-guides.js +107 -0
  199. package/dist/shared/move-roof-tool.d.ts.map +1 -1
  200. package/dist/shared/move-roof-tool.js +17 -3
  201. package/dist/shared/opening-documentation-fields.d.ts +16 -0
  202. package/dist/shared/opening-documentation-fields.d.ts.map +1 -0
  203. package/dist/shared/opening-documentation-fields.js +54 -0
  204. package/dist/shared/opening-documentation.d.ts +47 -0
  205. package/dist/shared/opening-documentation.d.ts.map +1 -0
  206. package/dist/shared/opening-documentation.js +300 -0
  207. package/dist/shared/opening-guides-runtime.d.ts.map +1 -1
  208. package/dist/shared/opening-guides-runtime.js +3 -2
  209. package/dist/shared/opening-placement-dimensions.d.ts.map +1 -1
  210. package/dist/shared/opening-placement-dimensions.js +14 -5
  211. package/dist/shared/wall-opening-ceiling.d.ts +30 -0
  212. package/dist/shared/wall-opening-ceiling.d.ts.map +1 -0
  213. package/dist/shared/wall-opening-ceiling.js +29 -0
  214. package/dist/shelf/floorplan-affordances.d.ts.map +1 -1
  215. package/dist/shelf/floorplan-affordances.js +10 -5
  216. package/dist/shelf/floorplan-move.d.ts +2 -16
  217. package/dist/shelf/floorplan-move.d.ts.map +1 -1
  218. package/dist/shelf/floorplan-move.js +19 -40
  219. package/dist/shelf/tool.d.ts.map +1 -1
  220. package/dist/shelf/tool.js +8 -3
  221. package/dist/site/recessed-slab-ground-holes.d.ts.map +1 -1
  222. package/dist/site/recessed-slab-ground-holes.js +1 -4
  223. package/dist/site/renderer.d.ts.map +1 -1
  224. package/dist/site/renderer.js +118 -28
  225. package/dist/site/terrain-drape.d.ts +91 -0
  226. package/dist/site/terrain-drape.d.ts.map +1 -0
  227. package/dist/site/terrain-drape.js +205 -0
  228. package/dist/site/terrain-geometry.d.ts +131 -0
  229. package/dist/site/terrain-geometry.d.ts.map +1 -0
  230. package/dist/site/terrain-geometry.js +259 -0
  231. package/dist/site/terrain-mesh.d.ts +49 -0
  232. package/dist/site/terrain-mesh.d.ts.map +1 -0
  233. package/dist/site/terrain-mesh.js +122 -0
  234. package/dist/site/terrain-renderer.d.ts +28 -0
  235. package/dist/site/terrain-renderer.d.ts.map +1 -0
  236. package/dist/site/terrain-renderer.js +69 -0
  237. package/dist/skylight/definition.d.ts.map +1 -1
  238. package/dist/skylight/definition.js +1 -0
  239. package/dist/skylight/tool.d.ts.map +1 -1
  240. package/dist/skylight/tool.js +16 -1
  241. package/dist/slab/definition.d.ts.map +1 -1
  242. package/dist/slab/definition.js +87 -10
  243. package/dist/slab/elevation-limit.d.ts +43 -0
  244. package/dist/slab/elevation-limit.d.ts.map +1 -0
  245. package/dist/slab/elevation-limit.js +124 -0
  246. package/dist/slab/floorplan-affordances.d.ts +16 -0
  247. package/dist/slab/floorplan-affordances.d.ts.map +1 -1
  248. package/dist/slab/geometry.d.ts.map +1 -1
  249. package/dist/slab/geometry.js +98 -5
  250. package/dist/slab/panel.d.ts.map +1 -1
  251. package/dist/slab/panel.js +82 -4
  252. package/dist/slab/parametrics.d.ts +1 -1
  253. package/dist/slab/parametrics.d.ts.map +1 -1
  254. package/dist/slab/parametrics.js +13 -1
  255. package/dist/slab/placement-ownership.d.ts +3 -0
  256. package/dist/slab/placement-ownership.d.ts.map +1 -0
  257. package/dist/slab/placement-ownership.js +3 -0
  258. package/dist/slab/quick-measurement.d.ts.map +1 -1
  259. package/dist/slab/quick-measurement.js +3 -2
  260. package/dist/slab/system.d.ts.map +1 -1
  261. package/dist/slab/system.js +14 -4
  262. package/dist/slab/tool.d.ts.map +1 -1
  263. package/dist/slab/tool.js +81 -23
  264. package/dist/solar-panel/definition.d.ts.map +1 -1
  265. package/dist/solar-panel/definition.js +1 -0
  266. package/dist/solar-panel/tool.d.ts.map +1 -1
  267. package/dist/solar-panel/tool.js +17 -1
  268. package/dist/spawn/definition.d.ts.map +1 -1
  269. package/dist/spawn/definition.js +1 -0
  270. package/dist/spawn/floorplan-affordances.d.ts.map +1 -1
  271. package/dist/spawn/floorplan-affordances.js +7 -4
  272. package/dist/spawn/floorplan-move.d.ts.map +1 -1
  273. package/dist/spawn/floorplan-move.js +8 -6
  274. package/dist/spawn/renderer.d.ts +4 -0
  275. package/dist/spawn/renderer.d.ts.map +1 -1
  276. package/dist/spawn/renderer.js +32 -18
  277. package/dist/spawn/tool.d.ts +2 -2
  278. package/dist/spawn/tool.d.ts.map +1 -1
  279. package/dist/spawn/tool.js +78 -11
  280. package/dist/stair/definition.d.ts.map +1 -1
  281. package/dist/stair/definition.js +15 -7
  282. package/dist/stair/destination.d.ts +16 -0
  283. package/dist/stair/destination.d.ts.map +1 -0
  284. package/dist/stair/destination.js +24 -0
  285. package/dist/stair/documentation.d.ts +8 -0
  286. package/dist/stair/documentation.d.ts.map +1 -0
  287. package/dist/stair/documentation.js +245 -0
  288. package/dist/stair/floor-stack.d.ts.map +1 -1
  289. package/dist/stair/floor-stack.js +38 -0
  290. package/dist/stair/floorplan-affordances.d.ts.map +1 -1
  291. package/dist/stair/floorplan-affordances.js +28 -9
  292. package/dist/stair/floorplan-move.d.ts +3 -5
  293. package/dist/stair/floorplan-move.d.ts.map +1 -1
  294. package/dist/stair/floorplan-move.js +14 -16
  295. package/dist/stair/floorplan.d.ts.map +1 -1
  296. package/dist/stair/floorplan.js +27 -14
  297. package/dist/stair/panel.d.ts.map +1 -1
  298. package/dist/stair/panel.js +35 -2
  299. package/dist/stair/renderer.d.ts.map +1 -1
  300. package/dist/stair/renderer.js +4 -3
  301. package/dist/structural-grid/coordination.d.ts +16 -0
  302. package/dist/structural-grid/coordination.d.ts.map +1 -0
  303. package/dist/structural-grid/coordination.js +105 -0
  304. package/dist/structural-grid/definition.d.ts +4 -0
  305. package/dist/structural-grid/definition.d.ts.map +1 -0
  306. package/dist/structural-grid/definition.js +51 -0
  307. package/dist/structural-grid/floorplan-tool.d.ts +16 -0
  308. package/dist/structural-grid/floorplan-tool.d.ts.map +1 -0
  309. package/dist/structural-grid/floorplan-tool.js +183 -0
  310. package/dist/structural-grid/floorplan.d.ts +3 -0
  311. package/dist/structural-grid/floorplan.d.ts.map +1 -0
  312. package/dist/structural-grid/floorplan.js +61 -0
  313. package/dist/structural-grid/index.d.ts +2 -0
  314. package/dist/structural-grid/index.d.ts.map +1 -0
  315. package/dist/structural-grid/index.js +1 -0
  316. package/dist/structural-grid/schema.d.ts +2 -0
  317. package/dist/structural-grid/schema.d.ts.map +1 -0
  318. package/dist/structural-grid/schema.js +1 -0
  319. package/dist/wall/construction-dimensions.d.ts +32 -0
  320. package/dist/wall/construction-dimensions.d.ts.map +1 -0
  321. package/dist/wall/construction-dimensions.js +1031 -0
  322. package/dist/wall/contextual-dimensions.d.ts +13 -0
  323. package/dist/wall/contextual-dimensions.d.ts.map +1 -0
  324. package/dist/wall/contextual-dimensions.js +221 -0
  325. package/dist/wall/definition.d.ts.map +1 -1
  326. package/dist/wall/definition.js +20 -1
  327. package/dist/wall/floorplan-affordances.d.ts.map +1 -1
  328. package/dist/wall/floorplan-affordances.js +23 -8
  329. package/dist/wall/floorplan-overrides.d.ts +9 -6
  330. package/dist/wall/floorplan-overrides.d.ts.map +1 -1
  331. package/dist/wall/floorplan-overrides.js +20 -11
  332. package/dist/wall/floorplan.d.ts +12 -3
  333. package/dist/wall/floorplan.d.ts.map +1 -1
  334. package/dist/wall/floorplan.js +121 -79
  335. package/dist/wall/measurement.d.ts.map +1 -1
  336. package/dist/wall/measurement.js +17 -4
  337. package/dist/wall/move-endpoint-tool.d.ts.map +1 -1
  338. package/dist/wall/move-endpoint-tool.js +74 -27
  339. package/dist/wall/move-shared.d.ts.map +1 -1
  340. package/dist/wall/move-shared.js +3 -2
  341. package/dist/wall/move-tool.d.ts.map +1 -1
  342. package/dist/wall/move-tool.js +15 -3
  343. package/dist/wall/paint.d.ts.map +1 -1
  344. package/dist/wall/paint.js +10 -6
  345. package/dist/wall/panel.d.ts.map +1 -1
  346. package/dist/wall/panel.js +32 -5
  347. package/dist/wall/parametrics.d.ts.map +1 -1
  348. package/dist/wall/parametrics.js +2 -0
  349. package/dist/wall/preview.d.ts +8 -0
  350. package/dist/wall/preview.d.ts.map +1 -0
  351. package/dist/wall/preview.js +39 -0
  352. package/dist/wall/quick-measurement.d.ts.map +1 -1
  353. package/dist/wall/quick-measurement.js +3 -2
  354. package/dist/wall/renderer.d.ts.map +1 -1
  355. package/dist/wall/renderer.js +7 -5
  356. package/dist/wall/system.d.ts +1 -1
  357. package/dist/wall/system.d.ts.map +1 -1
  358. package/dist/wall/system.js +37 -2
  359. package/dist/wall/tool.d.ts.map +1 -1
  360. package/dist/wall/tool.js +148 -96
  361. package/dist/wall/treatment-level-data.d.ts +14 -0
  362. package/dist/wall/treatment-level-data.d.ts.map +1 -0
  363. package/dist/wall/treatment-level-data.js +31 -0
  364. package/dist/wall/treatments.d.ts +8 -2
  365. package/dist/wall/treatments.d.ts.map +1 -1
  366. package/dist/wall/treatments.js +58 -15
  367. package/dist/window/contextual-dimensions.d.ts +3 -0
  368. package/dist/window/contextual-dimensions.d.ts.map +1 -0
  369. package/dist/window/contextual-dimensions.js +7 -0
  370. package/dist/window/definition.d.ts.map +1 -1
  371. package/dist/window/definition.js +15 -10
  372. package/dist/window/floorplan-affordances.d.ts +2 -5
  373. package/dist/window/floorplan-affordances.d.ts.map +1 -1
  374. package/dist/window/floorplan-affordances.js +9 -15
  375. package/dist/window/floorplan-move.d.ts.map +1 -1
  376. package/dist/window/floorplan-move.js +1 -1
  377. package/dist/window/floorplan.d.ts.map +1 -1
  378. package/dist/window/floorplan.js +7 -1
  379. package/dist/window/move-tool.d.ts.map +1 -1
  380. package/dist/window/move-tool.js +3 -3
  381. package/dist/window/panel.d.ts.map +1 -1
  382. package/dist/window/panel.js +4 -1
  383. package/dist/window/schema.d.ts +1 -1
  384. package/dist/window/schema.d.ts.map +1 -1
  385. package/dist/window/schema.js +1 -1
  386. package/dist/window/tool.d.ts.map +1 -1
  387. package/dist/window/tool.js +64 -7
  388. package/dist/window/window-math.d.ts +7 -3
  389. package/dist/window/window-math.d.ts.map +1 -1
  390. package/dist/window/window-math.js +8 -3
  391. package/dist/zone/contextual-dimensions.d.ts +3 -0
  392. package/dist/zone/contextual-dimensions.d.ts.map +1 -0
  393. package/dist/zone/contextual-dimensions.js +43 -0
  394. package/dist/zone/definition.d.ts.map +1 -1
  395. package/dist/zone/definition.js +9 -1
  396. package/dist/zone/floorplan-affordances.d.ts +36 -0
  397. package/dist/zone/floorplan-affordances.d.ts.map +1 -1
  398. package/dist/zone/floorplan.d.ts.map +1 -1
  399. package/dist/zone/floorplan.js +59 -3
  400. package/dist/zone/quantities-panel.d.ts.map +1 -1
  401. package/dist/zone/quantities-panel.js +48 -4
  402. package/dist/zone/room-clear-dimensions.d.ts +3 -0
  403. package/dist/zone/room-clear-dimensions.d.ts.map +1 -0
  404. package/dist/zone/room-clear-dimensions.js +406 -0
  405. package/dist/zone/room-documentation.d.ts +11 -0
  406. package/dist/zone/room-documentation.d.ts.map +1 -0
  407. package/dist/zone/room-documentation.js +97 -0
  408. package/package.json +7 -7
@@ -1,9 +1,47 @@
1
1
  export function getStairFloorPlacedFootprints(stair, nodes) {
2
+ if ((stair.stairType ?? 'straight') !== 'straight') {
3
+ return getArcStairFloorPlacedFootprints(stair);
4
+ }
2
5
  const segments = (stair.children ?? [])
3
6
  .map((childId) => nodes[childId])
4
7
  .filter((node) => node?.type === 'stair-segment');
5
8
  return getStairSegmentFloorPlacedFootprints(stair, segments);
6
9
  }
10
+ const MAX_ARC_FOOTPRINT_SWEEP = Math.PI / 12;
11
+ function getArcStairFloorPlacedFootprints(stair) {
12
+ const isSpiral = stair.stairType === 'spiral';
13
+ const innerRadius = Math.max(isSpiral ? 0.05 : 0.2, stair.innerRadius ?? (isSpiral ? 0.2 : 0.9));
14
+ const outerRadius = innerRadius + Math.max(stair.width ?? 1, 0.4);
15
+ const sweepAngle = stair.sweepAngle ?? (isSpiral ? Math.PI * 2 : Math.PI / 2);
16
+ const stepCount = Math.max(2, Math.round(stair.stepCount ?? 10));
17
+ const sliceCount = Math.max(stepCount, Math.ceil(Math.abs(sweepAngle) / MAX_ARC_FOOTPRINT_SWEEP));
18
+ const footprints = [];
19
+ for (let index = 0; index < sliceCount; index += 1) {
20
+ const startAngle = -sweepAngle / 2 + (sweepAngle * index) / sliceCount;
21
+ const endAngle = -sweepAngle / 2 + (sweepAngle * (index + 1)) / sliceCount;
22
+ const midAngle = (startAngle + endAngle) / 2;
23
+ const halfSweep = Math.abs(endAngle - startAngle) / 2;
24
+ const projectedInnerRadius = innerRadius * Math.cos(halfSweep);
25
+ const radialSize = outerRadius - projectedInnerRadius;
26
+ const tangentialSize = 2 * outerRadius * Math.sin(halfSweep);
27
+ const centerRadius = (outerRadius + projectedInnerRadius) / 2;
28
+ const [offsetX, offsetZ] = rotateXZ(Math.cos(midAngle) * centerRadius, Math.sin(midAngle) * centerRadius, stair.rotation);
29
+ footprints.push({
30
+ position: [stair.position[0] + offsetX, stair.position[1], stair.position[2] + offsetZ],
31
+ dimensions: [Math.max(radialSize, 0.01), 0.01, Math.max(tangentialSize, 0.01)],
32
+ rotation: [0, midAngle - stair.rotation, 0],
33
+ });
34
+ }
35
+ if (isSpiral && (stair.showCenterColumn ?? true)) {
36
+ const columnRadius = Math.max(0.05, Math.min(innerRadius * 0.72, innerRadius - 0.03));
37
+ footprints.push({
38
+ position: stair.position,
39
+ dimensions: [columnRadius * 2, 0.01, columnRadius * 2],
40
+ rotation: [0, 0, 0],
41
+ });
42
+ }
43
+ return footprints;
44
+ }
7
45
  export function getStairSegmentFloorPlacedFootprints(stair, segments) {
8
46
  const transforms = computeStairSegmentFloorStackTransforms(segments);
9
47
  return segments.map((segment, index) => {
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan-affordances.d.ts","sourceRoot":"","sources":["../../src/stair/floorplan-affordances.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAExB,KAAK,SAAS,EAGf,MAAM,kBAAkB,CAAA;AAuCzB;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,CAAC,SAAS,CA+BjE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAAmB,CAAC,SAAS,CA8BlE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,mBAAmB,CAAC,SAAS,CA6BrE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,EAAE,mBAAmB,CAAC,SAAS,CA4C3E,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,EAAE,mBAAmB,CAAC,SAAS,CA8BhE,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,EAAE,mBAAmB,CAAC,SAAS,CA6CrE,CAAA"}
1
+ {"version":3,"file":"floorplan-affordances.d.ts","sourceRoot":"","sources":["../../src/stair/floorplan-affordances.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAExB,KAAK,SAAS,EAIf,MAAM,kBAAkB,CAAA;AAwCzB;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,CAAC,SAAS,CAiCjE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAAmB,CAAC,SAAS,CAgClE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,mBAAmB,CAAC,SAAS,CA+BrE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,EAAE,mBAAmB,CAAC,SAAS,CAiD3E,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,EAAE,mBAAmB,CAAC,SAAS,CAgChE,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,EAAE,mBAAmB,CAAC,SAAS,CAkDrE,CAAA"}
@@ -1,4 +1,5 @@
1
- import { useScene, } from '@pascal-app/core';
1
+ import { useLiveNodeOverrides, useScene, } from '@pascal-app/core';
2
+ import { isAngleSnapActive } from '@pascal-app/editor';
2
3
  import { rotateAffordanceDelta } from '../shared/rotate-affordance';
3
4
  // Minimums + max sweep mirror the 3D handles in
4
5
  // `packages/editor/src/components/editor/stair-segment-handles.tsx` so a 2D
@@ -47,12 +48,14 @@ export const segmentWidthAffordance = {
47
48
  const delta = sign * (currentProj - initialProj);
48
49
  const newWidth = Math.max(MIN_SEGMENT_WIDTH, initialWidth + delta);
49
50
  lastWidth = newWidth;
50
- useScene.getState().updateNode(segmentNodeId, { width: newWidth });
51
+ useLiveNodeOverrides.getState().set(segmentNodeId, { width: newWidth });
52
+ useScene.getState().markDirty(segmentNodeId);
51
53
  },
52
54
  canCommit() {
53
55
  return true;
54
56
  },
55
57
  commit() {
58
+ useLiveNodeOverrides.getState().clear(segmentNodeId);
56
59
  useScene.getState().updateNode(segmentNodeId, { width: lastWidth });
57
60
  },
58
61
  };
@@ -83,12 +86,14 @@ export const segmentLengthAffordance = {
83
86
  const delta = currentProj - initialProj;
84
87
  const newLength = Math.max(MIN_SEGMENT_LENGTH, initialLength + delta);
85
88
  lastLength = newLength;
86
- useScene.getState().updateNode(segmentNodeId, { length: newLength });
89
+ useLiveNodeOverrides.getState().set(segmentNodeId, { length: newLength });
90
+ useScene.getState().markDirty(segmentNodeId);
87
91
  },
88
92
  canCommit() {
89
93
  return true;
90
94
  },
91
95
  commit() {
96
+ useLiveNodeOverrides.getState().clear(segmentNodeId);
92
97
  useScene.getState().updateNode(segmentNodeId, { length: lastLength });
93
98
  },
94
99
  };
@@ -118,12 +123,14 @@ export const curvedStairWidthAffordance = {
118
123
  const currentRadial = (planPoint[0] - cx) * radialX + (planPoint[1] - cz) * radialZ;
119
124
  const newWidth = Math.max(MIN_CURVED_WIDTH, initialWidth + (currentRadial - initialRadial));
120
125
  lastWidth = newWidth;
121
- useScene.getState().updateNode(stairId, { width: newWidth });
126
+ useLiveNodeOverrides.getState().set(stairId, { width: newWidth });
127
+ useScene.getState().markDirty(stairId);
122
128
  },
123
129
  canCommit() {
124
130
  return true;
125
131
  },
126
132
  commit() {
133
+ useLiveNodeOverrides.getState().clear(stairId);
127
134
  useScene.getState().updateNode(stairId, { width: lastWidth });
128
135
  },
129
136
  };
@@ -162,12 +169,17 @@ export const curvedStairInnerRadiusAffordance = {
162
169
  const newWidth = initialOuterRadius - newInner;
163
170
  lastInner = newInner;
164
171
  lastWidth = newWidth;
165
- useScene.getState().updateNode(stairId, { innerRadius: newInner, width: newWidth });
172
+ useLiveNodeOverrides.getState().set(stairId, {
173
+ innerRadius: newInner,
174
+ width: newWidth,
175
+ });
176
+ useScene.getState().markDirty(stairId);
166
177
  },
167
178
  canCommit() {
168
179
  return true;
169
180
  },
170
181
  commit() {
182
+ useLiveNodeOverrides.getState().clear(stairId);
171
183
  useScene.getState().updateNode(stairId, { innerRadius: lastInner, width: lastWidth });
172
184
  },
173
185
  };
@@ -193,21 +205,23 @@ export const stairRotateAffordance = {
193
205
  let lastRotation = initialRotation;
194
206
  return {
195
207
  affectedIds: [stairId],
196
- apply({ planPoint, modifiers }) {
208
+ apply({ planPoint }) {
197
209
  const delta = rotateAffordanceDelta({
198
210
  center: [cx, cz],
199
211
  initialAngle,
200
212
  planPoint,
201
- free: modifiers.shiftKey,
213
+ free: !isAngleSnapActive(),
202
214
  });
203
215
  const newRotation = initialRotation - delta;
204
216
  lastRotation = newRotation;
205
- useScene.getState().updateNode(stairId, { rotation: newRotation });
217
+ useLiveNodeOverrides.getState().set(stairId, { rotation: newRotation });
218
+ useScene.getState().markDirty(stairId);
206
219
  },
207
220
  canCommit() {
208
221
  return true;
209
222
  },
210
223
  commit() {
224
+ useLiveNodeOverrides.getState().clear(stairId);
211
225
  useScene.getState().updateNode(stairId, { rotation: lastRotation });
212
226
  },
213
227
  };
@@ -253,12 +267,17 @@ export const curvedStairSweepAffordance = {
253
267
  const newRotation = initialRotation + rotationShift;
254
268
  lastSweep = newSweep;
255
269
  lastRotation = newRotation;
256
- useScene.getState().updateNode(stairId, { sweepAngle: newSweep, rotation: newRotation });
270
+ useLiveNodeOverrides.getState().set(stairId, {
271
+ sweepAngle: newSweep,
272
+ rotation: newRotation,
273
+ });
274
+ useScene.getState().markDirty(stairId);
257
275
  },
258
276
  canCommit() {
259
277
  return true;
260
278
  },
261
279
  commit() {
280
+ useLiveNodeOverrides.getState().clear(stairId);
262
281
  useScene.getState().updateNode(stairId, { sweepAngle: lastSweep, rotation: lastRotation });
263
282
  },
264
283
  };
@@ -5,13 +5,11 @@ import { type FloorplanMoveTarget, type StairNode } from '@pascal-app/core';
5
5
  * Existing stairs preserve the cursor grab offset, matching the 3D move
6
6
  * tools; fresh catalog placement follows the cursor absolutely.
7
7
  *
8
- * Figma alignment is layered on the stair footprint edges; Alt bypasses.
8
+ * Figma alignment is layered on the stair footprint edges.
9
9
  * Guides are cleared by `FloorplanRegistryMoveOverlay`'s Path 1 teardown.
10
10
  *
11
- * The position is written straight to scene each tick (the stair has a real
12
- * `position` field, unlike polygon kinds) and re-applied atomically via
13
- * `commit()` so the overlay's deterministic revert → resume → commit path
14
- * records a single undo step (same pattern door / window use).
11
+ * The position previews through the live override store and is written to
12
+ * scene once via `commit()`.
15
13
  */
16
14
  export declare const stairFloorplanMoveTarget: FloorplanMoveTarget<StairNode>;
17
15
  //# sourceMappingURL=floorplan-move.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan-move.d.ts","sourceRoot":"","sources":["../../src/stair/floorplan-move.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,mBAAmB,EAGxB,KAAK,SAAS,EAGf,MAAM,kBAAkB,CAAA;AAIzB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,EAAE,mBAAmB,CAAC,SAAS,CAoDnE,CAAA"}
1
+ {"version":3,"file":"floorplan-move.d.ts","sourceRoot":"","sources":["../../src/stair/floorplan-move.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,mBAAmB,EAGxB,KAAK,SAAS,EAIf,MAAM,kBAAkB,CAAA;AASzB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,EAAE,mBAAmB,CAAC,SAAS,CAoDnE,CAAA"}
@@ -1,5 +1,5 @@
1
- import { collectAlignmentAnchors, movingAlignmentAnchors, snapScalar, useScene, } from '@pascal-app/core';
2
- import { applyFloorplanAlignment, getSegmentGridStep } from '@pascal-app/editor';
1
+ import { collectAlignmentAnchors, movingAlignmentAnchors, snapScalar, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
2
+ import { applyFloorplanAlignment, getSegmentGridStep, isGridSnapActive, isMagneticSnapActive, } from '@pascal-app/editor';
3
3
  import { createFloorplanCursorResolver } from '../shared/floorplan-cursor';
4
4
  /**
5
5
  * 2D floor-plan move handler for stair.
@@ -7,13 +7,11 @@ import { createFloorplanCursorResolver } from '../shared/floorplan-cursor';
7
7
  * Existing stairs preserve the cursor grab offset, matching the 3D move
8
8
  * tools; fresh catalog placement follows the cursor absolutely.
9
9
  *
10
- * Figma alignment is layered on the stair footprint edges; Alt bypasses.
10
+ * Figma alignment is layered on the stair footprint edges.
11
11
  * Guides are cleared by `FloorplanRegistryMoveOverlay`'s Path 1 teardown.
12
12
  *
13
- * The position is written straight to scene each tick (the stair has a real
14
- * `position` field, unlike polygon kinds) and re-applied atomically via
15
- * `commit()` so the overlay's deterministic revert → resume → commit path
16
- * records a single undo step (same pattern door / window use).
13
+ * The position previews through the live override store and is written to
14
+ * scene once via `commit()`.
17
15
  */
18
16
  export const stairFloorplanMoveTarget = ({ node, nodes }) => {
19
17
  const startY = node.position[1];
@@ -26,24 +24,23 @@ export const stairFloorplanMoveTarget = ({ node, nodes }) => {
26
24
  let lastValid = null;
27
25
  const session = {
28
26
  affectedIds: [node.id],
29
- apply({ planPoint, modifiers }) {
30
- // Snap the origin to the editor's current grid step (driven by
31
- // `useEditor.gridSnapStep`). Shift bypasses the grid snap.
32
- const step = getSegmentGridStep();
33
- const snap = (value) => (modifiers.shiftKey ? value : snapScalar(value, step));
27
+ apply({ planPoint }) {
28
+ const step = isGridSnapActive() ? getSegmentGridStep() : 0;
29
+ const snap = (value) => (step > 0 ? snapScalar(value, step) : value);
34
30
  const [gx, gz] = resolveCursor(planPoint, { snap });
35
- // Figma alignment on the actual stair footprint (Alt bypasses alignment; Shift all snap),
36
- // matching the 3D move tool. Publishes guides via `useAlignmentGuides`.
31
+ // Figma alignment on the actual stair footprint, matching the 3D move
32
+ // tool. Publishes guides via `useAlignmentGuides`.
37
33
  const movingAnchors = movingAlignmentAnchors(node, nodes, gx, gz, node.rotation ?? 0);
38
34
  const { point: aligned } = applyFloorplanAlignment([gx, gz], movingAnchors.length > 0
39
35
  ? movingAnchors
40
- : [{ nodeId: node.id, kind: 'corner', x: gx, z: gz }], candidates, { bypass: modifiers.altKey || modifiers.shiftKey });
36
+ : [{ nodeId: node.id, kind: 'corner', x: gx, z: gz }], candidates, { applySnap: isMagneticSnapActive() });
41
37
  const sx = aligned[0];
42
38
  const sz = aligned[1];
43
39
  if (lastValid && lastValid.position[0] === sx && lastValid.position[2] === sz)
44
40
  return;
45
41
  lastValid = { position: [sx, startY, sz] };
46
- useScene.getState().updateNodes([{ id: node.id, data: lastValid }]);
42
+ useLiveNodeOverrides.getState().set(node.id, lastValid);
43
+ useScene.getState().markDirty(node.id);
47
44
  },
48
45
  canCommit() {
49
46
  // No overlap / placement rules for stairs in 2D — any pointer-up
@@ -57,6 +54,7 @@ export const stairFloorplanMoveTarget = ({ node, nodes }) => {
57
54
  // door / window use.
58
55
  if (!lastValid)
59
56
  return;
57
+ useLiveNodeOverrides.getState().clear(node.id);
60
58
  useScene.getState().updateNodes([{ id: node.id, data: lastValid }]);
61
59
  },
62
60
  };
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/stair/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAEjB,eAAe,EACf,SAAS,EAEV,MAAM,kBAAkB,CAAA;AAgBzB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,eAAe,GACnB,iBAAiB,GAAG,IAAI,CAib1B"}
1
+ {"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/stair/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAEjB,eAAe,EACf,SAAS,EAEV,MAAM,kBAAkB,CAAA;AAuBzB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,eAAe,GACnB,iBAAiB,GAAG,IAAI,CAoc1B"}
@@ -3,7 +3,8 @@
3
3
  // `definition.ts` so the 2D handle visually lines up with where the 3D
4
4
  // curved-arrow gizmo would sit at the matching world point.
5
5
  const STAIR_ROTATE_PLAN_OFFSET = 0.4;
6
- import { buildFloorplanStairEntry, buildSvgAnnularSectorPath, buildSvgArcPath, buildSvgArrowHeadPoints, getArcPlanPoint, } from '@pascal-app/editor';
6
+ import { buildFloorplanStairEntry, buildSvgAnnularSectorPath, buildSvgArcPath, buildSvgArrowHeadPoints, floorplanGeometryMetadata, getArcPlanPoint, } from '@pascal-app/editor';
7
+ import { buildStairDocumentation, resolveStairPlanDirection, resolveStraightStairDirectionArrow, stairPlanBreakStep, } from './documentation';
7
8
  /**
8
9
  * Stage C floor-plan emitter for stair. The stair is the parent; its
9
10
  * children are the `stair-segment`s whose transforms are *cumulative*
@@ -79,7 +80,11 @@ export function buildStairFloorplan(stair, ctx) {
79
80
  // Tread bars — one per visible step inside the segment.
80
81
  // `buildFloorplanStairEntry` already returns the thickened
81
82
  // polygons; we emit them as filled polygons.
82
- for (const treadBar of segmentEntry.treadBars) {
83
+ const breakStep = stairPlanBreakStep(segmentEntry.segment.stepCount);
84
+ for (let treadIndex = 0; treadIndex < segmentEntry.treadBars.length; treadIndex += 1) {
85
+ if (treadIndex + 1 >= breakStep)
86
+ continue;
87
+ const treadBar = segmentEntry.treadBars[treadIndex];
83
88
  children.push({
84
89
  kind: 'polygon',
85
90
  points: toFloorplanPoints(treadBar),
@@ -216,10 +221,10 @@ export function buildStairFloorplan(stair, ctx) {
216
221
  const stepBase = stairType === 'spiral' ? 6 : 4;
217
222
  const stepCount = Math.max(stepBase, Math.round(stair.stepCount ?? 10));
218
223
  const stepSweep = normalizedSweepAngle / stepCount;
219
- // For spirals only: the last ~32% of the sweep is dashed (matches
220
- // the legacy `dashedFromIndex = Math.floor(stepCount * 0.68)`).
221
- const dashedFromIndex = stairType === 'spiral' ? Math.floor(stepCount * 0.68) : Infinity;
224
+ const breakStep = stairPlanBreakStep(stepCount);
222
225
  for (let index = 0; index <= stepCount; index += 1) {
226
+ if (index >= breakStep && index !== stepCount)
227
+ continue;
223
228
  const angle = sectorStartAngle + stepSweep * index;
224
229
  const inner = getArcPlanPoint(stairCenter, innerRadius, angle);
225
230
  const outer = getArcPlanPoint(stairCenter, outerRadius, angle);
@@ -228,8 +233,7 @@ export function buildStairFloorplan(stair, ctx) {
228
233
  // Curved: regular stroke everywhere, but both the starting and the
229
234
  // ending step lines are bolded (matches the legacy
230
235
  // `<FloorplanStairLayer>` curved branch).
231
- // Spiral: only the last step is accented + bolded; intermediate
232
- // steps past `dashedFromIndex` are dashed.
236
+ // Spiral: only the last step is accented + bolded.
233
237
  const isEmphasised = stairType === 'spiral' ? isLast : isFirst || isLast;
234
238
  const stepWidth = stairType === 'spiral' ? (isEmphasised ? 1.8 : 1.15) : isEmphasised ? 1.5 : 1.1;
235
239
  children.push({
@@ -240,7 +244,6 @@ export function buildStairFloorplan(stair, ctx) {
240
244
  y2: outer.y,
241
245
  stroke: stairType === 'spiral' && isLast ? stairAccent : stairStroke,
242
246
  strokeWidth: stepWidth,
243
- strokeDasharray: index >= dashedFromIndex && !isLast ? '0.1 0.08' : undefined,
244
247
  vectorEffect: 'non-scaling-stroke',
245
248
  });
246
249
  }
@@ -273,9 +276,13 @@ export function buildStairFloorplan(stair, ctx) {
273
276
  });
274
277
  }
275
278
  // 6. Direction arrow — head only, at the upper end of the sweep.
276
- const arrowAngle = visualSectorEndAngle - stepSweep * 0.8;
279
+ const direction = resolveStairPlanDirection(stair, ctx.parent?.type === 'level' ? ctx.parent.id : stair.parentId);
280
+ const arrowAngle = direction === 'up'
281
+ ? visualSectorEndAngle - stepSweep * 0.8
282
+ : sectorStartAngle + stepSweep * 0.8;
277
283
  const arrowPoint = getArcPlanPoint(stairCenter, centerlineRadius, arrowAngle);
278
- const tangentAngle = arrowAngle + (normalizedSweepAngle >= 0 ? Math.PI / 2 : -Math.PI / 2);
284
+ const sweepDirection = normalizedSweepAngle >= 0 ? 1 : -1;
285
+ const tangentAngle = arrowAngle + sweepDirection * (direction === 'up' ? Math.PI / 2 : -Math.PI / 2);
279
286
  const arrowSize = clamp(stair.width * (stairType === 'spiral' ? 0.18 : 0.16), 0.1, 0.18);
280
287
  const headPts = buildSvgArrowHeadPoints(arrowPoint, tangentAngle, arrowSize);
281
288
  children.push({
@@ -283,6 +290,7 @@ export function buildStairFloorplan(stair, ctx) {
283
290
  points: headPts.map((p) => [p.x, p.y]),
284
291
  fill: stairAccent,
285
292
  stroke: 'none',
293
+ metadata: floorplanGeometryMetadata({ annotationRole: 'stair-annotation' }),
286
294
  });
287
295
  // 7. Resize arrows — mirror of the 3D `CurvedStairWidthArrow`,
288
296
  // `CurvedStairInnerRadiusArrow`, and two `CurvedStairSweepArrow`s.
@@ -344,28 +352,33 @@ export function buildStairFloorplan(stair, ctx) {
344
352
  // the stair-segment chain in straight space and produces a malformed
345
353
  // polyline once the chain is laid around an arc.
346
354
  if (stairType === 'straight' && entry.arrow) {
347
- if (entry.arrow.polyline.length >= 2) {
355
+ const direction = resolveStairPlanDirection(stair, ctx.parent?.type === 'level' ? ctx.parent.id : stair.parentId);
356
+ const directionArrow = resolveStraightStairDirectionArrow(entry, direction);
357
+ if (directionArrow && directionArrow.polyline.length >= 2) {
348
358
  children.push({
349
359
  kind: 'polyline',
350
- points: toFloorplanPoints(entry.arrow.polyline),
360
+ points: toFloorplanPoints(directionArrow.polyline),
351
361
  fill: 'none',
352
362
  stroke: stairAccent,
353
363
  strokeWidth: 0.02,
354
364
  strokeLinecap: 'round',
355
365
  strokeLinejoin: 'round',
356
366
  opacity: showSelectedChrome ? 0.92 : 0.72,
367
+ metadata: floorplanGeometryMetadata({ annotationRole: 'stair-annotation' }),
357
368
  });
358
369
  }
359
- if (entry.arrow.head.length >= 3) {
370
+ if (directionArrow && directionArrow.head.length >= 3) {
360
371
  children.push({
361
372
  kind: 'polygon',
362
- points: toFloorplanPoints(entry.arrow.head),
373
+ points: toFloorplanPoints(directionArrow.head),
363
374
  fill: stairAccent,
364
375
  stroke: 'none',
365
376
  opacity: showSelectedChrome ? 0.92 : 0.72,
377
+ metadata: floorplanGeometryMetadata({ annotationRole: 'stair-annotation' }),
366
378
  });
367
379
  }
368
380
  }
381
+ children.push(...buildStairDocumentation(stair, entry, ctx));
369
382
  // Whole-stair rotation handle — sister to the 3D `stairRotateHandle`
370
383
  // (arc-resize, curved-arrow). 2D doesn't have a dedicated curved-arrow
371
384
  // primitive, so we emit a `move-arrow` with the `'stair-rotate'`
@@ -1 +1 @@
1
- {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/stair/panel.tsx"],"names":[],"mappings":"AA6DA,MAAM,CAAC,OAAO,UAAU,UAAU,uCA6fjC"}
1
+ {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/stair/panel.tsx"],"names":[],"mappings":"AAoEA,MAAM,CAAC,OAAO,UAAU,UAAU,uCAskBjC"}
@@ -1,11 +1,12 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { StairSegmentNode as StairSegmentNodeSchema, useScene, } from '@pascal-app/core';
3
+ import { resolveStairTotalRise, StairSegmentNode as StairSegmentNodeSchema, useScene, } from '@pascal-app/core';
4
4
  import { ActionButton, ActionGroup, DEFAULT_SPIRAL_STAIR_SWEEP_ANGLE, duplicateStairSubtree, getStairLevelOptions, MetricControl, PanelSection, PanelWrapper, resolveStairDestinationLevel, resolveStairFromLevelId, resolveStairToLevelId, SegmentedControl, SliderControl, ToggleControl, triggerSFX, useEditor, } from '@pascal-app/editor';
5
5
  import { useViewer } from '@pascal-app/viewer';
6
6
  import { Copy, Move, Plus, Trash2 } from 'lucide-react';
7
7
  import { useCallback, useMemo } from 'react';
8
8
  import { useShallow } from 'zustand/react/shallow';
9
+ import { getStairDestinationUpdates } from './destination';
9
10
  const RAILING_MODE_OPTIONS = [
10
11
  { label: 'None', value: 'none' },
11
12
  { label: 'Left', value: 'left' },
@@ -25,6 +26,9 @@ const STAIR_SLAB_OPENING_OPTIONS = [
25
26
  { label: 'None', value: 'none' },
26
27
  { label: 'Destination', value: 'destination' },
27
28
  ];
29
+ // Slabs at least this high off the storey floor read as decks (mezzanines) —
30
+ // lower slabs are floor coverings, never useful stair destinations.
31
+ const DECK_DESTINATION_MIN_ELEVATION = 0.5;
28
32
  export default function StairPanel() {
29
33
  const selectedId = useViewer((s) => s.selection.selectedIds[0]);
30
34
  const selectedCount = useViewer((s) => s.selection.selectedIds.length);
@@ -35,6 +39,23 @@ export default function StairPanel() {
35
39
  const nodes = useScene((s) => s.nodes);
36
40
  const node = useScene((s) => selectedId ? s.nodes[selectedId] : undefined);
37
41
  const levels = useMemo(() => (node?.type === 'stair' ? getStairLevelOptions(nodes, node) : []), [node, nodes]);
42
+ const candidateDecks = useMemo(() => {
43
+ if (node?.type !== 'stair')
44
+ return [];
45
+ const level = node.parentId ? nodes[node.parentId] : undefined;
46
+ if (level?.type !== 'level')
47
+ return [];
48
+ const decks = [];
49
+ for (const childId of level.children) {
50
+ const child = nodes[childId];
51
+ if (child?.type !== 'slab')
52
+ continue;
53
+ if (child.id === node.deckSlabId || child.elevation >= DECK_DESTINATION_MIN_ELEVATION) {
54
+ decks.push(child);
55
+ }
56
+ }
57
+ return decks;
58
+ }, [node, nodes]);
38
59
  const segments = useScene(useShallow((s) => {
39
60
  if (!selectedId)
40
61
  return [];
@@ -83,6 +104,12 @@ export default function StairPanel() {
83
104
  toLevelId: plan?.toLevel.id ?? fromLevelId,
84
105
  });
85
106
  }, [handleUpdate]);
107
+ const handleDestinationChange = useCallback((value) => {
108
+ if (!node)
109
+ return;
110
+ const target = useScene.getState().nodes[value];
111
+ handleUpdate(getStairDestinationUpdates(node, target, value));
112
+ }, [node, handleUpdate]);
86
113
  const getLastSegmentFillDefaults = useCallback(() => {
87
114
  if (!node)
88
115
  return { fillToFloor: true };
@@ -166,13 +193,19 @@ export default function StairPanel() {
166
193
  return null;
167
194
  const resolvedFromLevelId = resolveStairFromLevelId(nodes, node, levels);
168
195
  const resolvedToLevelId = resolveStairToLevelId(nodes, node, resolvedFromLevelId, levels);
196
+ const deckNode = node.deckSlabId ? nodes[node.deckSlabId] : undefined;
197
+ const attachedDeck = deckNode?.type === 'slab' ? deckNode : undefined;
198
+ const resolvedRise = Math.round(resolveStairTotalRise(node, nodes) * 100) / 100;
169
199
  return (_jsxs(PanelWrapper, { icon: "/icons/stairs.webp", onClose: handleClose, title: node.name || 'Staircase', width: 300, children: [_jsx(PanelSection, { title: "Type", children: _jsx(SegmentedControl, { onChange: (value) => handleUpdate(value === 'spiral' && node.stairType !== 'spiral'
170
200
  ? {
171
201
  stairType: value,
172
202
  sweepAngle: DEFAULT_SPIRAL_STAIR_SWEEP_ANGLE,
173
203
  position: [node.position[0], 0, node.position[2]],
174
204
  }
175
- : { stairType: value }), options: STAIR_TYPE_OPTIONS, value: node.stairType ?? 'straight' }) }), _jsx(PanelSection, { title: "Opening", children: _jsxs("div", { className: "space-y-3", children: [_jsx(ToggleControl, { checked: (node.slabOpeningMode ?? 'none') === 'destination', label: "Auto Cutout", onChange: handleAutoCutoutChange }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "px-1 text-[11px] text-muted-foreground uppercase tracking-[0.14em]", children: "From Level" }), _jsx("select", { className: "h-9 w-full rounded-lg border border-border/50 bg-[#2C2C2E] px-3 text-foreground text-sm", onChange: (event) => handleFromLevelChange(event.target.value), value: resolvedFromLevelId ?? '', children: levels.map((level) => (_jsx("option", { value: level.id, children: level.name || `Level ${level.level + 1}` }, level.id))) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "px-1 text-[11px] text-muted-foreground uppercase tracking-[0.14em]", children: "To Level" }), _jsx("select", { className: "h-9 w-full rounded-lg border border-border/50 bg-[#2C2C2E] px-3 text-foreground text-sm", onChange: (event) => handleUpdate({ toLevelId: event.target.value }), value: resolvedToLevelId ?? '', children: levels.map((level) => (_jsx("option", { value: level.id, children: level.name || `Level ${level.level + 1}` }, level.id))) })] }), _jsx(SegmentedControl, { onChange: (value) => handleAutoCutoutChange(value === 'destination'), options: STAIR_SLAB_OPENING_OPTIONS, value: node.slabOpeningMode ?? 'none' }), (node.slabOpeningMode ?? 'none') === 'destination' ? (_jsx(MetricControl, { label: "Opening Offset", max: 0.5, min: 0, onChange: (value) => handleUpdate({ openingOffset: value }), precision: 2, step: 0.01, unit: "m", value: Math.round((node.openingOffset ?? 0) * 100) / 100 })) : null, node.stairType === 'spiral' && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "px-1 text-[11px] text-muted-foreground uppercase tracking-[0.14em]", children: "Landing" }), _jsx(SegmentedControl, { onChange: (value) => handleUpdate({ topLandingMode: value }), options: TOP_LANDING_MODE_OPTIONS, value: node.topLandingMode ?? 'none' })] }), (node.topLandingMode ?? 'none') === 'integrated' && (_jsx(MetricControl, { label: "Top Landing", max: 5, min: 0.3, onChange: (value) => handleUpdate({ topLandingDepth: value }), precision: 2, step: 0.05, unit: "m", value: Math.round((node.topLandingDepth ?? 0.9) * 100) / 100 }))] }))] }) }), node.stairType === 'straight' && (_jsxs(PanelSection, { title: "Segments", children: [_jsx("div", { className: "flex flex-col gap-1", children: segments.map((seg, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-[#3e3e3e]", onClick: () => handleSelectSegment(seg.id), type: "button", children: [_jsx("span", { className: "truncate", children: seg.name || `Segment ${i + 1}` }), _jsx("span", { className: "text-muted-foreground text-xs capitalize", children: seg.segmentType })] }, seg.id))) }), _jsxs("div", { className: "flex gap-1.5", children: [_jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add flight", onClick: handleAddFlight }), _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add landing", onClick: handleAddLanding })] })] })), (node.stairType === 'curved' || node.stairType === 'spiral') && (_jsxs(PanelSection, { title: "Geometry", children: [_jsx(MetricControl, { label: "Width", max: 10, min: 0.4, onChange: (value) => handleUpdate({ width: value }), precision: 2, step: 0.05, unit: "m", value: Math.round((node.width ?? 1) * 100) / 100 }), _jsx(MetricControl, { label: "Rise", max: 10, min: 0.2, onChange: (value) => handleUpdate({ totalRise: value }), precision: 2, step: 0.05, unit: "m", value: Math.round((node.totalRise ?? 2.5) * 100) / 100 }), _jsx(MetricControl, { label: "Steps", max: 32, min: 2, onChange: (value) => handleUpdate({ stepCount: Math.max(2, Math.round(value)) }), precision: 0, step: 1, unit: "", value: Math.max(2, Math.round(node.stepCount ?? 10)) }), node.stairType !== 'spiral' && (_jsx(ToggleControl, { checked: node.fillToFloor ?? true, label: "Fit To Floor", onChange: (checked) => handleUpdate({ fillToFloor: checked }) })), (node.stairType === 'spiral' || !(node.fillToFloor ?? true)) && (_jsx(MetricControl, { label: "Thickness", max: 1, min: 0.02, onChange: (value) => handleUpdate({ thickness: value }), precision: 2, step: 0.01, unit: "m", value: Math.round((node.thickness ?? 0.25) * 100) / 100 })), _jsx(MetricControl, { label: "Inner Radius", max: 10, min: node.stairType === 'spiral' ? 0.05 : 0.2, onChange: (value) => handleUpdate({ innerRadius: value }), precision: 2, step: 0.05, unit: "m", value: Math.round((node.innerRadius ?? 0.9) * 100) / 100 }), _jsx(SliderControl, { label: "Sweep", max: node.stairType === 'spiral' ? 720 : 270, min: node.stairType === 'spiral' ? -720 : -270, onChange: (degrees) => handleUpdate({ sweepAngle: (degrees * Math.PI) / 180 }), precision: 0, step: 1, unit: "\u00B0", value: Math.round(((node.sweepAngle ?? Math.PI / 2) * 180) / Math.PI) }), node.stairType === 'spiral' && (_jsxs(_Fragment, { children: [_jsx(ToggleControl, { checked: node.showCenterColumn ?? true, label: "Center Column", onChange: (checked) => handleUpdate({ showCenterColumn: checked }) }), _jsx(ToggleControl, { checked: node.showStepSupports ?? true, label: "Step Supports", onChange: (checked) => handleUpdate({ showStepSupports: checked }) })] }))] })), _jsxs(PanelSection, { title: "Position", children: [_jsx(SliderControl, { label: "X", max: 50, min: -50, onChange: (v) => {
205
+ : { stairType: value }), options: STAIR_TYPE_OPTIONS, value: node.stairType ?? 'straight' }) }), _jsx(PanelSection, { title: "Opening", children: _jsxs("div", { className: "space-y-3", children: [attachedDeck ? null : (_jsx(ToggleControl, { checked: (node.slabOpeningMode ?? 'none') === 'destination', label: "Auto Cutout", onChange: handleAutoCutoutChange })), _jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "px-1 text-[11px] text-muted-foreground uppercase tracking-[0.14em]", children: "From Level" }), _jsx("select", { className: "h-9 w-full rounded-lg border border-border/50 bg-[#2C2C2E] px-3 text-foreground text-sm", onChange: (event) => handleFromLevelChange(event.target.value), value: resolvedFromLevelId ?? '', children: levels.map((level) => (_jsx("option", { value: level.id, children: level.name || `Level ${level.level + 1}` }, level.id))) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "px-1 text-[11px] text-muted-foreground uppercase tracking-[0.14em]", children: "To" }), _jsxs("select", { className: "h-9 w-full rounded-lg border border-border/50 bg-[#2C2C2E] px-3 text-foreground text-sm", onChange: (event) => handleDestinationChange(event.target.value), value: attachedDeck ? attachedDeck.id : (resolvedToLevelId ?? ''), children: [levels.map((level) => (_jsx("option", { value: level.id, children: level.name || `Level ${level.level + 1}` }, level.id))), candidateDecks.map((deck) => (_jsx("option", { value: deck.id, children: deck.name || 'Deck' }, deck.id)))] })] }), attachedDeck ? (_jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "px-1 text-[11px] text-muted-foreground uppercase tracking-[0.14em]", children: "Rise" }), _jsx(SegmentedControl, { onChange: (value) => handleUpdate(value === 'custom' ? { totalRise: resolvedRise } : { totalRise: undefined }), options: [
206
+ { label: 'Follows deck', value: 'follows' },
207
+ { label: 'Custom rise', value: 'custom' },
208
+ ], value: node.totalRise == null ? 'follows' : 'custom' }), node.totalRise == null ? (_jsxs("div", { className: "px-1 text-[11px] text-muted-foreground", children: ["Currently ", resolvedRise, " m"] })) : (_jsx(MetricControl, { label: "Rise", max: 10, min: 0.2, onChange: (value) => handleUpdate({ totalRise: value }), precision: 2, step: 0.05, unit: "m", value: resolvedRise }))] })) : null, attachedDeck ? null : (_jsxs(_Fragment, { children: [_jsx(SegmentedControl, { onChange: (value) => handleAutoCutoutChange(value === 'destination'), options: STAIR_SLAB_OPENING_OPTIONS, value: node.slabOpeningMode ?? 'none' }), (node.slabOpeningMode ?? 'none') === 'destination' ? (_jsx(MetricControl, { label: "Opening Offset", max: 0.5, min: 0, onChange: (value) => handleUpdate({ openingOffset: value }), precision: 2, step: 0.01, unit: "m", value: Math.round((node.openingOffset ?? 0) * 100) / 100 })) : null] })), node.stairType === 'spiral' && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "px-1 text-[11px] text-muted-foreground uppercase tracking-[0.14em]", children: "Landing" }), _jsx(SegmentedControl, { onChange: (value) => handleUpdate({ topLandingMode: value }), options: TOP_LANDING_MODE_OPTIONS, value: node.topLandingMode ?? 'none' })] }), (node.topLandingMode ?? 'none') === 'integrated' && (_jsx(MetricControl, { label: "Top Landing", max: 5, min: 0.3, onChange: (value) => handleUpdate({ topLandingDepth: value }), precision: 2, step: 0.05, unit: "m", value: Math.round((node.topLandingDepth ?? 0.9) * 100) / 100 }))] }))] }) }), node.stairType === 'straight' && (_jsxs(PanelSection, { title: "Segments", children: [_jsx("div", { className: "flex flex-col gap-1", children: segments.map((seg, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-[#3e3e3e]", onClick: () => handleSelectSegment(seg.id), type: "button", children: [_jsx("span", { className: "truncate", children: seg.name || `Segment ${i + 1}` }), _jsx("span", { className: "text-muted-foreground text-xs capitalize", children: seg.segmentType })] }, seg.id))) }), _jsxs("div", { className: "flex gap-1.5", children: [_jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add flight", onClick: handleAddFlight }), _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add landing", onClick: handleAddLanding })] })] })), (node.stairType === 'curved' || node.stairType === 'spiral') && (_jsxs(PanelSection, { title: "Geometry", children: [_jsx(MetricControl, { label: "Width", max: 10, min: 0.4, onChange: (value) => handleUpdate({ width: value }), precision: 2, step: 0.05, unit: "m", value: Math.round((node.width ?? 1) * 100) / 100 }), _jsx(MetricControl, { label: "Rise", max: 10, min: 0.2, onChange: (value) => handleUpdate({ totalRise: value }), precision: 2, step: 0.05, unit: "m", value: Math.round(resolveStairTotalRise(node, nodes) * 100) / 100 }), _jsx(MetricControl, { label: "Steps", max: 32, min: 2, onChange: (value) => handleUpdate({ stepCount: Math.max(2, Math.round(value)) }), precision: 0, step: 1, unit: "", value: Math.max(2, Math.round(node.stepCount ?? 10)) }), node.stairType !== 'spiral' && (_jsx(ToggleControl, { checked: node.fillToFloor ?? true, label: "Fit To Floor", onChange: (checked) => handleUpdate({ fillToFloor: checked }) })), (node.stairType === 'spiral' || !(node.fillToFloor ?? true)) && (_jsx(MetricControl, { label: "Thickness", max: 1, min: 0.02, onChange: (value) => handleUpdate({ thickness: value }), precision: 2, step: 0.01, unit: "m", value: Math.round((node.thickness ?? 0.25) * 100) / 100 })), _jsx(MetricControl, { label: "Inner Radius", max: 10, min: node.stairType === 'spiral' ? 0.05 : 0.2, onChange: (value) => handleUpdate({ innerRadius: value }), precision: 2, step: 0.05, unit: "m", value: Math.round((node.innerRadius ?? 0.9) * 100) / 100 }), _jsx(SliderControl, { label: "Sweep", max: node.stairType === 'spiral' ? 720 : 270, min: node.stairType === 'spiral' ? -720 : -270, onChange: (degrees) => handleUpdate({ sweepAngle: (degrees * Math.PI) / 180 }), precision: 0, step: 1, unit: "\u00B0", value: Math.round(((node.sweepAngle ?? Math.PI / 2) * 180) / Math.PI) }), node.stairType === 'spiral' && (_jsxs(_Fragment, { children: [_jsx(ToggleControl, { checked: node.showCenterColumn ?? true, label: "Center Column", onChange: (checked) => handleUpdate({ showCenterColumn: checked }) }), _jsx(ToggleControl, { checked: node.showStepSupports ?? true, label: "Step Supports", onChange: (checked) => handleUpdate({ showStepSupports: checked }) })] }))] })), _jsxs(PanelSection, { title: "Position", children: [_jsx(SliderControl, { label: "X", max: 50, min: -50, onChange: (v) => {
176
209
  const pos = [...node.position];
177
210
  pos[0] = v;
178
211
  handleUpdate({ position: pos });
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/stair/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,SAAS,EAKf,MAAM,kBAAkB,CAAA;AAmDzB,eAAO,MAAM,aAAa,GAAI,mBAAmB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,gCAiHnE,CAAA;AAinCD,eAAe,aAAa,CAAA"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/stair/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,SAAS,EAKf,MAAM,kBAAkB,CAAA;AAmDzB,eAAO,MAAM,aAAa,GAAI,mBAAmB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,gCAiHnE,CAAA;AAknCD,eAAe,aAAa,CAAA"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useLiveNodeOverrides, useRegistry, useScene, } from '@pascal-app/core';
3
+ import { resolveStairTotalRise, useLiveNodeOverrides, useRegistry, useScene, } from '@pascal-app/core';
4
4
  import { getStairBodyMaterials, getStairRailingMaterial, NodeRenderer, resolveMaterialRef, resolveSlotDefaultMaterial, useNodeEvents, useViewer, } from '@pascal-app/viewer';
5
5
  import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
6
6
  import * as THREE from 'three';
@@ -69,7 +69,7 @@ function StairRailings({ stair, material }) {
69
69
  const stepCount = Math.max(2, Math.round(stair.stepCount ?? 10));
70
70
  const sweepAngle = stair.sweepAngle ?? (stair.stairType === 'spiral' ? Math.PI * 2 : Math.PI / 2);
71
71
  const stepSweep = sweepAngle / stepCount;
72
- const stepHeight = Math.max(stair.totalRise ?? 2.5, 0.1) / stepCount;
72
+ const stepHeight = Math.max(resolveStairTotalRise(stair, nodes), 0.1) / stepCount;
73
73
  const innerRadius = Math.max(stair.stairType === 'spiral' ? 0.05 : 0.2, stair.innerRadius ?? 0.9);
74
74
  const outerRadius = innerRadius + Math.max(stair.width ?? 1, 0.4);
75
75
  const leftRadius = sweepAngle >= 0 ? innerRadius + 0.04 : outerRadius - 0.04;
@@ -205,9 +205,10 @@ function RailSegment({ start, end, radius, material, }) {
205
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 }));
206
206
  }
207
207
  function CurvedStairBody({ stair, bodyMaterials, }) {
208
+ const nodes = useScene((state) => state.nodes);
208
209
  const sideMaterial = bodyMaterials[1];
209
210
  const stepCount = Math.max(2, Math.round(stair.stepCount ?? 10));
210
- const totalRise = Math.max(stair.totalRise ?? 2.5, 0.1);
211
+ const totalRise = Math.max(resolveStairTotalRise(stair, nodes), 0.1);
211
212
  const stepHeight = totalRise / stepCount;
212
213
  const isSpiral = stair.stairType === 'spiral';
213
214
  const innerRadius = Math.max(isSpiral ? 0.05 : 0.2, stair.innerRadius ?? 0.9);
@@ -0,0 +1,16 @@
1
+ import type { AnyNode, StructuralGridNode } from '@pascal-app/core';
2
+ export type StructuralGridPoint = readonly [x: number, z: number];
3
+ export type StructuralGridSnap = {
4
+ point: [number, number];
5
+ distance: number;
6
+ kind: 'intersection' | 'line';
7
+ axes: StructuralGridNode[];
8
+ reference: string;
9
+ };
10
+ export declare const STRUCTURAL_GRID_SNAP_DISTANCE_M = 0.25;
11
+ export declare const STRUCTURAL_GRID_REFERENCE_TOLERANCE_M = 0.02;
12
+ export declare function collectStructuralGridAxes(nodes: Readonly<Record<string, AnyNode>>, levelId: string | null | undefined): StructuralGridNode[];
13
+ export declare function formatStructuralGridReference(axes: readonly StructuralGridNode[]): string;
14
+ export declare function resolveStructuralGridSnap(point: StructuralGridPoint, axes: readonly StructuralGridNode[], maxDistance?: number): StructuralGridSnap | null;
15
+ export declare function resolveStructuralGridReference(point: StructuralGridPoint, axes: readonly StructuralGridNode[], tolerance?: number): string | null;
16
+ //# sourceMappingURL=coordination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordination.d.ts","sourceRoot":"","sources":["../../src/structural-grid/coordination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAEnE,MAAM,MAAM,mBAAmB,GAAG,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,CAAA;AAEjE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,cAAc,GAAG,MAAM,CAAA;IAC7B,IAAI,EAAE,kBAAkB,EAAE,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,eAAO,MAAM,+BAA+B,OAAO,CAAA;AACnD,eAAO,MAAM,qCAAqC,OAAO,CAAA;AAIzD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACjC,kBAAkB,EAAE,CAMtB;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,CASzF;AAED,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,SAAS,kBAAkB,EAAE,EACnC,WAAW,SAAkC,GAC5C,kBAAkB,GAAG,IAAI,CA4C3B;AAED,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,SAAS,kBAAkB,EAAE,EACnC,SAAS,SAAwC,GAChD,MAAM,GAAG,IAAI,CAMf"}