@pascal-app/nodes 0.1.1 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +74 -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 +15 -1
  138. package/dist/fence/lift.d.ts +38 -0
  139. package/dist/fence/lift.d.ts.map +1 -0
  140. package/dist/fence/lift.js +51 -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 +46 -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,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { resolveLevelId, useLiveNodeOverrides, useScene } from '@pascal-app/core';
3
+ import { resolveCeilingHeight, resolveLevelId, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
4
4
  import { PolygonEditor } from '@pascal-app/editor';
5
5
  import { useViewer } from '@pascal-app/viewer';
6
6
  import { useCallback, useEffect } from 'react';
@@ -40,6 +40,6 @@ export const CeilingHoleEditor = ({ ceilingId, holeIndex }) => {
40
40
  }, [ceilingId]);
41
41
  if (!(ceiling && hole) || hole.length < 3)
42
42
  return null;
43
- return (_jsx(PolygonEditor, { allowEdgeMove: true, allowPolygonMove: true, color: "#ef4444", levelId: resolveLevelId(ceiling, useScene.getState().nodes), minVertices: 3, onPolygonChange: handlePolygonChange, onPolygonPreview: handlePolygonPreview, polygon: hole, surfaceHeight: ceiling.height ?? 2.5 }));
43
+ return (_jsx(PolygonEditor, { allowEdgeMove: true, allowPolygonMove: true, color: "#ef4444", levelId: resolveLevelId(ceiling, useScene.getState().nodes), minVertices: 3, onPolygonChange: handlePolygonChange, onPolygonPreview: handlePolygonPreview, polygon: hole, surfaceHeight: resolveCeilingHeight(ceiling, useScene.getState().nodes) }));
44
44
  };
45
45
  export default CeilingHoleEditor;
@@ -1 +1 @@
1
- {"version":3,"file":"move-tool.d.ts","sourceRoot":"","sources":["../../src/ceiling/move-tool.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,WAAW,EAUjB,MAAM,kBAAkB,CAAA;AAgFzB,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAuK3D,CAAA;AAiHD,eAAe,eAAe,CAAA"}
1
+ {"version":3,"file":"move-tool.d.ts","sourceRoot":"","sources":["../../src/ceiling/move-tool.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,WAAW,EAWjB,MAAM,kBAAkB,CAAA;AAgFzB,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAwK3D,CAAA;AAiHD,eAAe,eAAe,CAAA"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { collectAlignmentAnchors, emitter, polygonAnchors, resolveAlignment, sceneRegistry, snapScalar, useLiveTransforms, useScene, } from '@pascal-app/core';
3
+ import { collectAlignmentAnchors, emitter, polygonAnchors, resolveAlignment, resolveCeilingHeight, sceneRegistry, snapScalar, useLiveTransforms, useScene, } from '@pascal-app/core';
4
4
  import { CursorSphere, consumePlacementDragRelease, isAlignmentGuideActive, isMagneticSnapActive, markToolCancelConsumed, triggerSFX, useAlignmentGuides, useEditor, } from '@pascal-app/editor';
5
5
  import { useViewer } from '@pascal-app/viewer';
6
6
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
@@ -65,7 +65,8 @@ export const MoveCeilingTool = ({ node }) => {
65
65
  const originalPolygonRef = useRef(node.polygon.map(([x, z]) => [x, z]));
66
66
  const originalHolesRef = useRef((node.holes ?? []).map((hole) => hole.map(([x, z]) => [x, z])));
67
67
  const originalCenterRef = useRef(getPolygonCenter(originalPolygonRef.current));
68
- const heightRef = useRef(node.height ?? 2.5);
68
+ // Resolved once at drag start — the ceiling plane can't change mid-move.
69
+ const heightRef = useRef(resolveCeilingHeight(node, useScene.getState().nodes));
69
70
  const dragAnchorRef = useRef(null);
70
71
  const previousGridPosRef = useRef(null);
71
72
  const deltaRef = useRef([0, 0]);
@@ -198,7 +199,7 @@ export const MoveCeilingTool = ({ node }) => {
198
199
  window.removeEventListener('pointerup', onPlacementDragPointerUp);
199
200
  };
200
201
  }, [exitMoveMode, node.id]);
201
- return (_jsx(CeilingMovePreview, { ceilingId: node.id, cursorLocalPos: cursorLocalPos, height: node.height ?? 2.5, originalHoles: originalHolesRef.current, originalPolygon: originalPolygonRef.current }));
202
+ return (_jsx(CeilingMovePreview, { ceilingId: node.id, cursorLocalPos: cursorLocalPos, height: heightRef.current, originalHoles: originalHolesRef.current, originalPolygon: originalPolygonRef.current }));
202
203
  };
203
204
  /**
204
205
  * Translucent fill + bright outline showing where the ceiling will land
@@ -1 +1 @@
1
- {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/ceiling/panel.tsx"],"names":[],"mappings":"AAmBA;;;;;;;GAOG;AACH,wBAAgB,YAAY,uCA+P3B;AAED,eAAe,YAAY,CAAA"}
1
+ {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/ceiling/panel.tsx"],"names":[],"mappings":"AA2BA;;;;;;;GAOG;AACH,wBAAgB,YAAY,uCA0V3B;AAED,eAAe,YAAY,CAAA"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useScene } from '@pascal-app/core';
4
- import { ActionButton, ActionGroup, holeEditScope, PanelSection, PanelWrapper, SliderControl, triggerSFX, useEditingHole, useEditor, useInteractionScope, } from '@pascal-app/editor';
3
+ import { getCeilingClampBound, resolveCeilingHeight, useScene, } from '@pascal-app/core';
4
+ import { ActionButton, ActionGroup, formatLinearMeasurement, holeEditScope, PanelSection, PanelWrapper, SegmentedControl, SliderControl, triggerSFX, useEditingHole, useEditor, useInteractionScope, } from '@pascal-app/editor';
5
5
  import { useViewer } from '@pascal-app/viewer';
6
6
  import { Edit, Move, Plus, Trash2 } from 'lucide-react';
7
7
  import { useCallback, useEffect, useRef } from 'react';
@@ -15,19 +15,64 @@ import { useCallback, useEffect, useRef } from 'react';
15
15
  */
16
16
  export function CeilingPanel() {
17
17
  const selectedId = useViewer((s) => s.selection.selectedIds[0]);
18
+ const unit = useViewer((s) => s.unit);
18
19
  const setSelection = useViewer((s) => s.setSelection);
19
20
  const editingHole = useEditingHole();
20
21
  const setMovingNode = useEditor((s) => s.setMovingNode);
21
22
  const node = useScene((s) => selectedId ? s.nodes[selectedId] : undefined);
23
+ // Ceilings no longer drive the storey height — the stored level height
24
+ // does — so height writes clamp under min(storey plane, lowest covering
25
+ // slab underside from the level above) − margin instead of poking into
26
+ // the level above or a deck hanging from it (clamp, never ask).
27
+ // Selector returns a primitive, so recomputing per store update is
28
+ // re-render-safe.
29
+ const maxHeight = useScene((s) => {
30
+ const parent = node?.parentId ? s.nodes[node.parentId] : undefined;
31
+ return parent?.type === 'level'
32
+ ? getCeilingClampBound(parent.id, s.nodes, node?.polygon ?? [])
33
+ : 6;
34
+ });
35
+ // Effective height: the stored custom height, or — for follows-mode
36
+ // ceilings (absent `height`) — the live level-top bound. Primitive
37
+ // selector so it tracks level-height / covering-slab edits.
38
+ const resolvedHeight = useScene((s) => {
39
+ const ceiling = selectedId
40
+ ? s.nodes[selectedId]
41
+ : undefined;
42
+ return ceiling?.type === 'ceiling' ? resolveCeilingHeight(ceiling, s.nodes) : 2.5;
43
+ });
22
44
  // Panel slider-drag fix recipe (plans/editor-node-registry.md): stable
23
45
  // handler refs so slider drags don't trigger Maximum update depth.
24
46
  const nodeRef = useRef(node);
25
47
  nodeRef.current = node;
48
+ const maxHeightRef = useRef(maxHeight);
49
+ maxHeightRef.current = maxHeight;
50
+ const resolvedHeightRef = useRef(resolvedHeight);
51
+ resolvedHeightRef.current = resolvedHeight;
26
52
  const handleUpdate = useCallback((updates) => {
27
53
  if (!selectedId)
28
54
  return;
29
55
  useScene.getState().updateNode(selectedId, updates);
30
56
  }, [selectedId]);
57
+ const handleHeightChange = useCallback((proposed) => {
58
+ handleUpdate({ height: Math.min(proposed, maxHeightRef.current) });
59
+ }, [handleUpdate]);
60
+ const handleTopModeChange = useCallback((mode) => {
61
+ const n = nodeRef.current;
62
+ if (!n)
63
+ return;
64
+ const isCustom = n.height != null;
65
+ if (mode === 'custom' && !isCustom) {
66
+ // Seed from the current resolved height so the surface doesn't
67
+ // jump at the moment of detaching from the level top.
68
+ handleUpdate({ height: Math.min(resolvedHeightRef.current, maxHeightRef.current) });
69
+ }
70
+ else if (mode === 'storey' && isCustom) {
71
+ // Absent `height` = follows the level top; the store strips
72
+ // undefined keys.
73
+ handleUpdate({ height: undefined });
74
+ }
75
+ }, [handleUpdate]);
31
76
  const handleClose = useCallback(() => {
32
77
  setSelection({ selectedIds: [] });
33
78
  useInteractionScope
@@ -122,7 +167,24 @@ export function CeilingPanel() {
122
167
  return Math.abs(area) / 2;
123
168
  };
124
169
  const area = calculateArea(node.polygon);
125
- return (_jsxs(PanelWrapper, { icon: "/icons/ceiling.webp", onClose: handleClose, title: node.name || 'Ceiling', width: 320, children: [_jsxs(PanelSection, { title: "Height", children: [_jsx(SliderControl, { label: "Height", max: 6, min: 0, onChange: (v) => handleUpdate({ height: v }), precision: 3, step: 0.01, unit: "m", value: Math.round(node.height * 1000) / 1000 }), _jsxs("div", { className: "mt-2 grid grid-cols-3 gap-1.5 px-1 pb-1", children: [_jsx(ActionButton, { label: "Low (2.4m)", onClick: () => handleUpdate({ height: 2.4 }) }), _jsx(ActionButton, { label: "Standard (2.5m)", onClick: () => handleUpdate({ height: 2.5 }) }), _jsx(ActionButton, { label: "High (3.0m)", onClick: () => handleUpdate({ height: 3.0 }) })] })] }), _jsx(PanelSection, { title: "Info", children: _jsxs("div", { className: "flex items-center justify-between px-2 py-1 text-muted-foreground text-sm", children: [_jsx("span", { children: "Area" }), _jsxs("span", { className: "font-mono text-white", children: [area.toFixed(2), " m\u00B2"] })] }) }), _jsxs(PanelSection, { title: "Holes", children: [node.holes && node.holes.length > 0 ? (_jsx("div", { className: "flex flex-col gap-1 pb-2", children: node.holes.map((hole, index) => {
170
+ const isFollows = node.height == null;
171
+ // Clean preset values per display system; imperial stores exact meters
172
+ // for 8'0" / 8'6" / 9'0" ceilings.
173
+ const heightPresets = unit === 'imperial'
174
+ ? [
175
+ { label: 'Low (8\'0")', height: 2.4384 },
176
+ { label: 'Standard (8\'6")', height: 2.5908 },
177
+ { label: 'High (9\'0")', height: 2.7432 },
178
+ ]
179
+ : [
180
+ { label: 'Low (2.4m)', height: 2.4 },
181
+ { label: 'Standard (2.5m)', height: 2.5 },
182
+ { label: 'High (3.0m)', height: 3.0 },
183
+ ];
184
+ return (_jsxs(PanelWrapper, { icon: "/icons/ceiling.webp", onClose: handleClose, title: node.name || 'Ceiling', width: 320, children: [_jsxs(PanelSection, { title: "Height", children: [_jsx(SegmentedControl, { onChange: handleTopModeChange, options: [
185
+ { label: 'Follows level', value: 'storey' },
186
+ { label: 'Custom height', value: 'custom' },
187
+ ], value: isFollows ? 'storey' : 'custom' }), isFollows ? (_jsxs("div", { className: "px-1 text-[11px] text-muted-foreground", children: ["Currently ", formatLinearMeasurement(resolvedHeight, unit)] })) : (_jsx(SliderControl, { label: "Height", max: Math.min(6, maxHeight), min: 0, onChange: handleHeightChange, precision: 3, step: 0.01, unit: "m", value: Math.round((node.height ?? resolvedHeight) * 1000) / 1000 })), _jsx("div", { className: "mt-2 grid grid-cols-3 gap-1.5 px-1 pb-1", children: heightPresets.map((preset) => (_jsx(ActionButton, { label: preset.label, onClick: () => handleHeightChange(preset.height) }, preset.label))) })] }), _jsx(PanelSection, { title: "Info", children: _jsxs("div", { className: "flex items-center justify-between px-2 py-1 text-muted-foreground text-sm", children: [_jsx("span", { children: "Area" }), _jsxs("span", { className: "font-mono text-white", children: [area.toFixed(2), " m\u00B2"] })] }) }), _jsxs(PanelSection, { title: "Holes", children: [node.holes && node.holes.length > 0 ? (_jsx("div", { className: "flex flex-col gap-1 pb-2", children: node.holes.map((hole, index) => {
126
188
  const holeArea = calculateArea(hole);
127
189
  const isEditing = editingHole?.nodeId === selectedId && editingHole?.holeIndex === index;
128
190
  const source = node.holeMetadata?.[index]?.source ?? 'manual';
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/ceiling/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,WAAW,EAMjB,MAAM,kBAAkB,CAAA;AAiBzB,eAAO,MAAM,eAAe,GAAI,UAAU;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,gCAoG9D,CAAA;AAED,eAAe,eAAe,CAAA"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/ceiling/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,WAAW,EAOjB,MAAM,kBAAkB,CAAA;AAiBzB,eAAO,MAAM,eAAe,GAAI,UAAU;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,gCAwG9D,CAAA;AAED,eAAe,eAAe,CAAA"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { getMaterialPresetByRef, resolveMaterial, useLiveTransforms, useRegistry, useScene, } from '@pascal-app/core';
3
+ import { getMaterialPresetByRef, resolveCeilingHeight, resolveMaterial, useLiveTransforms, useRegistry, useScene, } from '@pascal-app/core';
4
4
  import { createSurfaceRoleMaterial, NodeRenderer, resolveSurfaceColor, useViewer, } from '@pascal-app/viewer';
5
5
  import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
6
6
  import { BackSide } from 'three/webgpu';
@@ -31,7 +31,11 @@ export const CeilingRenderer = ({ node }) => {
31
31
  // ceiling slot references re-tints it live.
32
32
  const sceneMaterials = useScene((s) => s.materials);
33
33
  const liveTransform = useLiveTransforms((s) => s.get(node.id));
34
- const ceilingY = (node.height ?? 2.5) - 0.01 + (liveTransform?.position[1] ?? 0);
34
+ // Resolved height: explicit when stored, else the live level-top bound
35
+ // (primitive selector, so follows-mode ceilings track level-height edits
36
+ // and covering-slab changes without a node write).
37
+ const resolvedHeight = useScene((s) => resolveCeilingHeight(node, s.nodes));
38
+ const ceilingY = resolvedHeight - 0.01 + (liveTransform?.position[1] ?? 0);
35
39
  const position = [
36
40
  liveTransform?.position[0] ?? 0,
37
41
  ceilingY,
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/ceiling/tool.tsx"],"names":[],"mappings":"AAoDA,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAwV/B,CAAA;AAED,eAAe,WAAW,CAAA"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/ceiling/tool.tsx"],"names":[],"mappings":"AAqDA,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAuW/B,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { DEFAULT_ANGLE_STEP, emitter, snapPointAlongAngleRay, snapPointToGrid, useScene, } from '@pascal-app/core';
4
- import { CursorSphere, clearCeilingSnapFeedback, EDITOR_LAYER, isAngleSnapActive, isGridSnapActive, markToolCancelConsumed, resolveCeilingPlanPointSnap, triggerSFX, useEditor, } from '@pascal-app/editor';
4
+ import { CursorSphere, clearCeilingSnapFeedback, EDITOR_LAYER, isAngleSnapActive, isGridSnapActive, markToolCancelConsumed, resolveCeilingPlanPointSnap, triggerSFX, useEditor, useFloorplanDraftPreview, } from '@pascal-app/editor';
5
5
  import { useViewer } from '@pascal-app/viewer';
6
6
  import { useEffect, useMemo, useRef, useState } from 'react';
7
7
  import { BufferGeometry, DoubleSide, Shape, Vector3 } from 'three';
@@ -52,6 +52,16 @@ export const CeilingTool = () => {
52
52
  useEditor.getState().setDraftVertexCount(points.length);
53
53
  }, [points.length]);
54
54
  useEffect(() => () => useEditor.getState().setDraftVertexCount(0), []);
55
+ useEffect(() => {
56
+ useFloorplanDraftPreview.getState().setPolygonDraft('ceiling', points);
57
+ }, [points]);
58
+ useEffect(() => () => {
59
+ const draftPreview = useFloorplanDraftPreview.getState();
60
+ if (draftPreview.polygonDraftType === 'ceiling') {
61
+ draftPreview.setPolygonDraft(null, []);
62
+ }
63
+ draftPreview.setCursorPoint(null);
64
+ }, []);
55
65
  const verticalGeo = useMemo(() => new BufferGeometry().setFromPoints([
56
66
  new Vector3(0, 0, 0),
57
67
  new Vector3(0, CEILING_HEIGHT - GRID_OFFSET, 0),
@@ -81,6 +91,7 @@ export const CeilingTool = () => {
81
91
  fallbackPoint: orthoPoint,
82
92
  levelId: currentLevelId,
83
93
  }).point;
94
+ useFloorplanDraftPreview.getState().setCursorPoint(displayPoint);
84
95
  setSnappedCursorPosition(displayPoint);
85
96
  if (points.length > 0 &&
86
97
  previousSnappedPointRef.current &&
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/chimney/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAqUtC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,OAAO,WAAW,CAoEhE,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/chimney/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAqUtC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,OAAO,WAAW,CAqEhE,CAAA"}
@@ -369,6 +369,7 @@ export const chimneyDefinition = {
369
369
  kind: 'parametric',
370
370
  module: () => import('./renderer'),
371
371
  },
372
+ preview: () => import('./preview'),
372
373
  tool: () => import('./tool'),
373
374
  toolHints: [
374
375
  { key: 'Left click', label: 'Place chimney on roof' },
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/chimney/tool.tsx"],"names":[],"mappings":"AA4CA,QAAA,MAAM,WAAW,mCAyIhB,CAAA;AAED,eAAe,WAAW,CAAA"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/chimney/tool.tsx"],"names":[],"mappings":"AA4CA,QAAA,MAAM,WAAW,mCAwJhB,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { ChimneyNode, emitter, sceneRegistry, useScene, } from '@pascal-app/core';
4
- import { triggerSFX } from '@pascal-app/editor';
4
+ import { triggerSFX, usePlacementPreview } from '@pascal-app/editor';
5
5
  import { useViewer } from '@pascal-app/viewer';
6
6
  import { useEffect, useMemo, useRef, useState } from 'react';
7
7
  import * as THREE from 'three';
@@ -79,6 +79,12 @@ const ChimneyTool = () => {
79
79
  setSegmentXform(xform);
80
80
  setHitLocal([hit.localX, hit.localY, hit.localZ]);
81
81
  setPreviewSegment(hit.segment);
82
+ usePlacementPreview.getState().set(ChimneyNode.parse({
83
+ ...previewNode,
84
+ parentId: hit.segment.id,
85
+ position: [hit.localX, hit.localY, hit.localZ],
86
+ roofSegmentId: hit.segment.id,
87
+ }), hit.segment);
82
88
  publishRoofSurfacePlacementGuides({
83
89
  roof: event.node,
84
90
  segment: hit.segment,
@@ -103,6 +109,9 @@ const ChimneyTool = () => {
103
109
  state.dirtyNodes.add(hit.segment.id);
104
110
  setSelection({ selectedIds: [chimney.id] });
105
111
  triggerSFX('sfx:item-place');
112
+ const placementPreview = usePlacementPreview.getState();
113
+ if (placementPreview.node?.id === previewNode.id)
114
+ placementPreview.clear();
106
115
  clearRoofSurfacePlacementGuides();
107
116
  event.stopPropagation();
108
117
  };
@@ -113,6 +122,9 @@ const ChimneyTool = () => {
113
122
  emitter.off('roof:move', updatePreview);
114
123
  emitter.off('roof:enter', updatePreview);
115
124
  emitter.off('roof:click', onClick);
125
+ const placementPreview = usePlacementPreview.getState();
126
+ if (placementPreview.node?.id === previewNode.id)
127
+ placementPreview.clear();
116
128
  clearRoofSurfacePlacementGuides();
117
129
  };
118
130
  }, [activeBuildingId, setSelection, previewNode]);
@@ -120,6 +132,9 @@ const ChimneyTool = () => {
120
132
  setSegmentXform(null);
121
133
  setHitLocal(null);
122
134
  setPreviewSegment(null);
135
+ const placementPreview = usePlacementPreview.getState();
136
+ if (placementPreview.node?.id === previewNode.id)
137
+ placementPreview.clear();
123
138
  clearRoofSurfacePlacementGuides();
124
139
  } }), activeBuildingId && segmentXform && hitLocal && previewSegment && (_jsx("group", { position: segmentXform.position, quaternion: segmentXform.quaternion, children: _jsx("group", { position: [hitLocal[0], 0, hitLocal[2]], children: _jsx(ChimneyPreview, { node: previewNode, segment: previewSegment }) }) }))] }));
125
140
  };
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/column/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAA;AAMzB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AA8RrC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,OAAO,UAAU,CAyF9D,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/column/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAA;AAUzB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AA0SrC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,OAAO,UAAU,CAgG9D,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { ColumnNode as ColumnNodeSchema, } from '@pascal-app/core';
2
- import { buildColumnFloorplan } from './floorplan';
2
+ import { collectStructuralGridAxes, resolveStructuralGridSnap, } from '../structural-grid/coordination';
3
+ import { buildColumnFloorplan, computeColumnFloorplanLevelData } from './floorplan';
3
4
  import { columnResizeAffordance, columnRotateAffordance } from './floorplan-affordances';
4
5
  import { columnFloorplanMoveTarget } from './floorplan-move';
5
6
  import { columnPaint } from './paint';
@@ -272,6 +273,10 @@ function columnHandles(node) {
272
273
  handles.push(columnRotateHandle(), columnMoveHandle());
273
274
  return handles;
274
275
  }
276
+ function resolveColumnStructuralGridMoveSnap({ candidatePosition, nodes, levelId, }) {
277
+ const snap = resolveStructuralGridSnap([candidatePosition[0], candidatePosition[2]], collectStructuralGridAxes(nodes, levelId));
278
+ return snap ? [snap.point[0], candidatePosition[1], snap.point[1]] : null;
279
+ }
275
280
  /**
276
281
  * Column — Stage A registration. Wrap-export of the legacy
277
282
  * `ColumnRenderer` (no system — column geometry is computed inline in
@@ -309,7 +314,11 @@ export const columnDefinition = {
309
314
  // Generic 3D translate-on-XZ via `MoveRegistryNodeTool` (grid snap + the
310
315
  // mode-driven snapping the overhaul standardised). 2D move keeps using
311
316
  // `floorplanMoveTarget`, which wins the 2D move dispatch.
312
- movable: { axes: ['x', 'z'], gridSnap: true },
317
+ movable: {
318
+ axes: ['x', 'z'],
319
+ gridSnap: true,
320
+ groupMoveSnap: resolveColumnStructuralGridMoveSnap,
321
+ },
313
322
  slots: (node) => columnSlots(node),
314
323
  paint: columnPaint,
315
324
  // Slab elevation lift via the generic `<FloorElevationSystem>` + the
@@ -337,6 +346,7 @@ export const columnDefinition = {
337
346
  kind: 'parametric',
338
347
  module: () => import('./renderer'),
339
348
  },
349
+ preview: () => import('./renderer').then(({ ColumnPreview }) => ({ default: ColumnPreview })),
340
350
  // Registry-driven placement tool — renders a translucent `ColumnPreview`
341
351
  // ghost at the cursor (mirroring the shelf build tool) instead of the
342
352
  // bare sphere the legacy editor-side `ColumnTool` showed. `ToolManager`'s
@@ -346,6 +356,8 @@ export const columnDefinition = {
346
356
  { key: 'Left click', label: 'Place column' },
347
357
  { key: 'Esc', label: 'Cancel' },
348
358
  ],
359
+ computeFloorplanLevelData: computeColumnFloorplanLevelData,
360
+ floorplanDependsOnSiblings: true,
349
361
  floorplan: buildColumnFloorplan,
350
362
  // 2D body move routes through this kind-specific target so the column
351
363
  // aligns by its footprint *edges* (and snaps flush to wall faces) instead
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan-affordances.d.ts","sourceRoot":"","sources":["../../src/column/floorplan-affordances.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,mBAAmB,EAEzB,MAAM,kBAAkB,CAAA;AAYzB;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EACC,OAAO,GACP,OAAO,GACP,SAAS,GACT,QAAQ,GACR,aAAa,GACb,aAAa,GACb,qBAAqB,GACrB,kBAAkB,CAAA;IACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC3B,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,CAAC,UAAU,CAkFlE,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,CAAC,UAAU,CA8BlE,CAAA"}
1
+ {"version":3,"file":"floorplan-affordances.d.ts","sourceRoot":"","sources":["../../src/column/floorplan-affordances.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,mBAAmB,EAGzB,MAAM,kBAAkB,CAAA;AAazB;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EACC,OAAO,GACP,OAAO,GACP,SAAS,GACT,QAAQ,GACR,aAAa,GACb,aAAa,GACb,qBAAqB,GACrB,kBAAkB,CAAA;IACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC3B,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,CAAC,UAAU,CAoFlE,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,CAAC,UAAU,CAgClE,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
  // Floor minimums — mirror the 3D handles in `column/definition.ts` so a
4
5
  // drag can't push a value past what the renderer accepts.
@@ -22,9 +23,10 @@ export const columnResizeAffordance = {
22
23
  const initialBraceBottomSpread = node.braceBottomSpread ?? Math.max(node.width * 3, 1.2);
23
24
  const initialBraceTopSpread = node.braceTopSpread ?? 0.12;
24
25
  let lastPatch = {};
25
- const commitPatch = (patch) => {
26
+ const previewPatch = (patch) => {
26
27
  lastPatch = patch;
27
- useScene.getState().updateNode(columnId, patch);
28
+ useLiveNodeOverrides.getState().set(columnId, patch);
29
+ useScene.getState().markDirty(columnId);
28
30
  };
29
31
  return {
30
32
  affectedIds: [columnId],
@@ -33,42 +35,42 @@ export const columnResizeAffordance = {
33
35
  const projDelta = currentProj - initialProj;
34
36
  switch (dim) {
35
37
  case 'width':
36
- commitPatch({
38
+ previewPatch({
37
39
  width: Math.max(MIN_COLUMN_WIDTH, initialWidth + 2 * projDelta),
38
40
  });
39
41
  return;
40
42
  case 'depth':
41
- commitPatch({
43
+ previewPatch({
42
44
  depth: Math.max(MIN_COLUMN_DEPTH, initialDepth + 2 * projDelta),
43
45
  });
44
46
  return;
45
47
  case 'uniform': {
46
48
  const next = Math.max(MIN_COLUMN_WIDTH, initialWidth + 2 * projDelta);
47
- commitPatch({ width: next, depth: next });
49
+ previewPatch({ width: next, depth: next });
48
50
  return;
49
51
  }
50
52
  case 'radius':
51
- commitPatch({
53
+ previewPatch({
52
54
  radius: Math.max(MIN_COLUMN_RADIUS, initialRadius + projDelta),
53
55
  });
54
56
  return;
55
57
  case 'brace-width':
56
- commitPatch({
58
+ previewPatch({
57
59
  braceWidth: Math.max(MIN_BRACE_DIMENSION, initialBraceWidth + 2 * projDelta),
58
60
  });
59
61
  return;
60
62
  case 'brace-depth':
61
- commitPatch({
63
+ previewPatch({
62
64
  braceDepth: Math.max(MIN_BRACE_DIMENSION, initialBraceDepth + 2 * projDelta),
63
65
  });
64
66
  return;
65
67
  case 'brace-bottom-spread':
66
- commitPatch({
68
+ previewPatch({
67
69
  braceBottomSpread: Math.max(MIN_BRACE_BOTTOM_SPREAD, initialBraceBottomSpread + 2 * projDelta),
68
70
  });
69
71
  return;
70
72
  case 'brace-top-spread':
71
- commitPatch({
73
+ previewPatch({
72
74
  braceTopSpread: Math.max(MIN_BRACE_TOP_SPREAD, initialBraceTopSpread + 2 * projDelta),
73
75
  });
74
76
  return;
@@ -79,6 +81,7 @@ export const columnResizeAffordance = {
79
81
  },
80
82
  commit() {
81
83
  if (Object.keys(lastPatch).length > 0) {
84
+ useLiveNodeOverrides.getState().clear(columnId);
82
85
  useScene.getState().updateNode(columnId, lastPatch);
83
86
  }
84
87
  },
@@ -104,21 +107,23 @@ export const columnRotateAffordance = {
104
107
  let lastRotation = initialRotation;
105
108
  return {
106
109
  affectedIds: [columnId],
107
- apply({ planPoint, modifiers }) {
110
+ apply({ planPoint }) {
108
111
  const delta = rotateAffordanceDelta({
109
112
  center: [cx, cz],
110
113
  initialAngle,
111
114
  planPoint,
112
- free: modifiers.shiftKey,
115
+ free: !isAngleSnapActive(),
113
116
  });
114
117
  const newRotation = initialRotation - delta;
115
118
  lastRotation = newRotation;
116
- useScene.getState().updateNode(columnId, { rotation: newRotation });
119
+ useLiveNodeOverrides.getState().set(columnId, { rotation: newRotation });
120
+ useScene.getState().markDirty(columnId);
117
121
  },
118
122
  canCommit() {
119
123
  return true;
120
124
  },
121
125
  commit() {
126
+ useLiveNodeOverrides.getState().clear(columnId);
122
127
  useScene.getState().updateNode(columnId, { rotation: lastRotation });
123
128
  },
124
129
  };
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan-move.d.ts","sourceRoot":"","sources":["../../src/column/floorplan-move.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EAEf,KAAK,mBAAmB,EAMzB,MAAM,kBAAkB,CAAA;AAYzB;;;;;;;GAOG;AAEH,eAAO,MAAM,yBAAyB,EAAE,mBAAmB,CAAC,UAAU,CAiErE,CAAA"}
1
+ {"version":3,"file":"floorplan-move.d.ts","sourceRoot":"","sources":["../../src/column/floorplan-move.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EAEf,KAAK,mBAAmB,EAMzB,MAAM,kBAAkB,CAAA;AAgBzB;;;;;;;GAOG;AAEH,eAAO,MAAM,yBAAyB,EAAE,mBAAmB,CAAC,UAAU,CAsErE,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { collectAlignmentAnchors, movingFootprintAnchors, sceneRegistry, useLiveTransforms, useScene, } from '@pascal-app/core';
2
2
  import { applyFloorplanAlignment, getFloorStackPreviewPosition, isGridSnapActive, isMagneticSnapActive, triggerSFX, useEditor, } from '@pascal-app/editor';
3
3
  import { createFloorplanCursorResolver } from '../shared/floorplan-cursor';
4
+ import { collectStructuralGridAxes, resolveStructuralGridSnap, } from '../structural-grid/coordination';
4
5
  /**
5
6
  * 2D floor-plan move handler for column. Columns need the same footprint-edge
6
7
  * alignment as shelf / item, but they must preview through live transforms and
@@ -33,9 +34,13 @@ export const columnFloorplanMoveTarget = ({ node, nodes }) => {
33
34
  const gridSnapped = resolveCursor(planPoint, { snap });
34
35
  // Figma-style alignment layered on the active snap mode.
35
36
  const { point: snapped } = applyFloorplanAlignment(gridSnapped, movingFootprintAnchors(node, gridSnapped[0], gridSnapped[1], rotationY), candidates, { applySnap: isMagneticSnapActive() });
36
- const next = [snapped[0], originalPosition[1], snapped[1]];
37
+ const structuralSnap = isGridSnapActive() || isMagneticSnapActive()
38
+ ? resolveStructuralGridSnap(snapped, collectStructuralGridAxes(nodes, node.parentId))
39
+ : null;
40
+ const coordinated = structuralSnap?.point ?? snapped;
41
+ const next = [coordinated[0], originalPosition[1], coordinated[1]];
37
42
  lastPosition = next;
38
- const snapKey = `${snapped[0]},${snapped[1]}`;
43
+ const snapKey = `${coordinated[0]},${coordinated[1]}`;
39
44
  if (snapKey !== lastSnapKey) {
40
45
  triggerSFX('sfx:grid-snap');
41
46
  lastSnapKey = snapKey;
@@ -1,4 +1,11 @@
1
- import type { ColumnNode, FloorplanGeometry, GeometryContext } from '@pascal-app/core';
1
+ import type { AnyNode, ColumnNode, FloorplanGeometry, FloorplanPoint, GeometryContext, StructuralGridNode } from '@pascal-app/core';
2
+ export type ColumnFloorplanLevelData = {
3
+ structuralGrids: StructuralGridNode[];
4
+ };
5
+ export declare function computeColumnFloorplanLevelData({ siblings, nodes, }: {
6
+ siblings: readonly ColumnNode[];
7
+ nodes: Record<string, AnyNode>;
8
+ }): ColumnFloorplanLevelData;
2
9
  /**
3
10
  * Stage C floor-plan builder for column. Inlined from the legacy
4
11
  * `getColumnPlanFootprint` helper in `floorplan-panel.tsx`. The
@@ -12,4 +19,5 @@ import type { ColumnNode, FloorplanGeometry, GeometryContext } from '@pascal-app
12
19
  * dragging the body, and a rotate-arrow at the front-right corner.
13
20
  */
14
21
  export declare function buildColumnFloorplan(node: ColumnNode, ctx: GeometryContext): FloorplanGeometry | null;
22
+ export declare function getColumnFloorplanFootprint(node: ColumnNode): FloorplanPoint[];
15
23
  //# sourceMappingURL=floorplan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/column/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EAEjB,eAAe,EAChB,MAAM,kBAAkB,CAAA;AAezB;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,eAAe,GACnB,iBAAiB,GAAG,IAAI,CAgI1B"}
1
+ {"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/column/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,kBAAkB,EACnB,MAAM,kBAAkB,CAAA;AAsBzB,MAAM,MAAM,wBAAwB,GAAG;IACrC,eAAe,EAAE,kBAAkB,EAAE,CAAA;CACtC,CAAA;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,SAAS,UAAU,EAAE,CAAA;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B,GAAG,wBAAwB,CAI3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,eAAe,GACnB,iBAAiB,GAAG,IAAI,CAiL1B;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,EAAE,CAE9E"}
@@ -1,13 +1,22 @@
1
+ import { floorplanGeometryMetadata } from '@pascal-app/editor';
2
+ import { collectStructuralGridAxes, resolveStructuralGridReference, } from '../structural-grid/coordination';
1
3
  // Offsets for the floor-plan selection arrows. Resize chevrons hug the
2
4
  // footprint a hair off the rim; the rotate-arrow corner sits a bit
3
5
  // further out so it doesn't crowd the resize arrows.
4
6
  const RESIZE_ARROW_OFFSET = 0.12;
5
7
  const ROTATE_ARROW_CORNER_OFFSET = 0.22;
8
+ const GRID_REFERENCE_OFFSET = 0.16;
9
+ const GRID_REFERENCE_FONT_SIZE = 0.13;
6
10
  const ROUND_CROSS_SECTIONS = new Set([
7
11
  'round',
8
12
  'octagonal',
9
13
  'sixteen-sided',
10
14
  ]);
15
+ export function computeColumnFloorplanLevelData({ siblings, nodes, }) {
16
+ return {
17
+ structuralGrids: collectStructuralGridAxes(nodes, siblings[0]?.parentId),
18
+ };
19
+ }
11
20
  /**
12
21
  * Stage C floor-plan builder for column. Inlined from the legacy
13
22
  * `getColumnPlanFootprint` helper in `floorplan-panel.tsx`. The
@@ -21,8 +30,8 @@ const ROUND_CROSS_SECTIONS = new Set([
21
30
  * dragging the body, and a rotate-arrow at the front-right corner.
22
31
  */
23
32
  export function buildColumnFloorplan(node, ctx) {
24
- const polygon = getColumnPlanFootprint(node);
25
- if (polygon.length < 3)
33
+ const points = getColumnFloorplanFootprint(node);
34
+ if (points.length < 3)
26
35
  return null;
27
36
  const view = ctx.viewState;
28
37
  const palette = view?.palette;
@@ -31,7 +40,6 @@ export function buildColumnFloorplan(node, ctx) {
31
40
  const showSelectedChrome = isSelected || isHighlighted;
32
41
  const stroke = showSelectedChrome && palette ? palette.selectedStroke : '#374151';
33
42
  const fill = showSelectedChrome ? '#fed7aa' : '#9ca3af';
34
- const points = polygon.map((p) => [p.x, p.y]);
35
43
  const children = [
36
44
  {
37
45
  kind: 'polygon',
@@ -40,8 +48,53 @@ export function buildColumnFloorplan(node, ctx) {
40
48
  stroke,
41
49
  strokeWidth: showSelectedChrome ? 0.03 : 0.02,
42
50
  opacity: 0.92,
51
+ metadata: floorplanGeometryMetadata({ annotationObstacle: 'bounds' }),
43
52
  },
44
53
  ];
54
+ const { halfX, halfZ } = columnPlanHalfExtents(node);
55
+ const centerMarkHalf = Math.min(0.09, Math.max(0.035, Math.min(halfX, halfZ) * 0.45));
56
+ const centerX = node.position[0];
57
+ const centerZ = node.position[2];
58
+ children.push({
59
+ kind: 'line',
60
+ x1: centerX - centerMarkHalf,
61
+ y1: centerZ - centerMarkHalf,
62
+ x2: centerX + centerMarkHalf,
63
+ y2: centerZ + centerMarkHalf,
64
+ stroke,
65
+ strokeWidth: 0.9,
66
+ vectorEffect: 'non-scaling-stroke',
67
+ pointerEvents: 'none',
68
+ metadata: floorplanGeometryMetadata({ annotationRole: 'column-center' }),
69
+ }, {
70
+ kind: 'line',
71
+ x1: centerX - centerMarkHalf,
72
+ y1: centerZ + centerMarkHalf,
73
+ x2: centerX + centerMarkHalf,
74
+ y2: centerZ - centerMarkHalf,
75
+ stroke,
76
+ strokeWidth: 0.9,
77
+ vectorEffect: 'non-scaling-stroke',
78
+ pointerEvents: 'none',
79
+ metadata: floorplanGeometryMetadata({ annotationRole: 'column-center' }),
80
+ });
81
+ const levelData = ctx.levelData;
82
+ const gridReference = resolveStructuralGridReference([centerX, centerZ], levelData?.structuralGrids ?? []);
83
+ if (gridReference) {
84
+ children.push({
85
+ kind: 'text',
86
+ x: centerX,
87
+ y: centerZ + halfZ + GRID_REFERENCE_OFFSET,
88
+ text: gridReference,
89
+ fontSize: GRID_REFERENCE_FONT_SIZE,
90
+ fill: stroke,
91
+ fontWeight: 700,
92
+ textAnchor: 'middle',
93
+ dominantBaseline: 'middle',
94
+ upright: true,
95
+ metadata: floorplanGeometryMetadata({ annotationRole: 'column-center' }),
96
+ });
97
+ }
45
98
  // Hatch overlay on selected — same `<defs>` pattern as the wall.
46
99
  if (isSelected && palette) {
47
100
  children.push({
@@ -117,7 +170,6 @@ export function buildColumnFloorplan(node, ctx) {
117
170
  // Rotate-arrow at the +X / +Z corner — matches the 3D
118
171
  // `columnRotateHandle` corner placement so users see the rotation
119
172
  // affordance in the same quadrant across views.
120
- const { halfX, halfZ } = columnPlanHalfExtents(node);
121
173
  const cornerLocalX = halfX + ROTATE_ARROW_CORNER_OFFSET;
122
174
  const cornerLocalZ = halfZ + ROTATE_ARROW_CORNER_OFFSET;
123
175
  const [cornerWorldX, cornerWorldZ] = rotatePlanVector(cornerLocalX, cornerLocalZ, rot);
@@ -132,6 +184,9 @@ export function buildColumnFloorplan(node, ctx) {
132
184
  }
133
185
  return { kind: 'group', children };
134
186
  }
187
+ export function getColumnFloorplanFootprint(node) {
188
+ return getColumnPlanFootprint(node).map((point) => [point.x, point.y]);
189
+ }
135
190
  /**
136
191
  * XZ half-extents for the column's plan footprint. Mirrors the 3D
137
192
  * `columnFootprintHalf` so side-arrows + rotate-arrow ring the same
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/column/tool.tsx"],"names":[],"mappings":"AA+CA;;;;;;;;;;GAUG;AACH,QAAA,MAAM,UAAU,0CA2Hf,CAAA;AAED,eAAe,UAAU,CAAA"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/column/tool.tsx"],"names":[],"mappings":"AAoDA;;;;;;;;;;GAUG;AACH,QAAA,MAAM,UAAU,0CA0Jf,CAAA;AAED,eAAe,UAAU,CAAA"}