@pascal-app/nodes 1.0.6 → 1.0.7

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 (566) hide show
  1. package/README.md +1 -0
  2. package/dist/beam/actions/move-endpoint.d.ts +24 -0
  3. package/dist/beam/actions/move-endpoint.d.ts.map +1 -0
  4. package/dist/beam/actions/move-endpoint.js +54 -0
  5. package/dist/beam/definition.d.ts +14 -0
  6. package/dist/beam/definition.d.ts.map +1 -0
  7. package/dist/beam/definition.js +173 -0
  8. package/dist/beam/draft-settings.d.ts +17 -0
  9. package/dist/beam/draft-settings.d.ts.map +1 -0
  10. package/dist/beam/draft-settings.js +81 -0
  11. package/dist/beam/drafting.d.ts +35 -0
  12. package/dist/beam/drafting.d.ts.map +1 -0
  13. package/dist/beam/drafting.js +103 -0
  14. package/dist/beam/floorplan-affordances.d.ts +8 -0
  15. package/dist/beam/floorplan-affordances.d.ts.map +1 -0
  16. package/dist/beam/floorplan-affordances.js +40 -0
  17. package/dist/beam/floorplan-move.d.ts +11 -0
  18. package/dist/beam/floorplan-move.d.ts.map +1 -0
  19. package/dist/beam/floorplan-move.js +54 -0
  20. package/dist/beam/floorplan.d.ts +11 -0
  21. package/dist/beam/floorplan.d.ts.map +1 -0
  22. package/dist/beam/floorplan.js +91 -0
  23. package/dist/beam/geometry.d.ts +19 -0
  24. package/dist/beam/geometry.d.ts.map +1 -0
  25. package/dist/beam/geometry.js +63 -0
  26. package/dist/beam/index.d.ts +3 -0
  27. package/dist/beam/index.d.ts.map +1 -0
  28. package/dist/beam/index.js +2 -0
  29. package/dist/beam/move-endpoint-tool.d.ts +21 -0
  30. package/dist/beam/move-endpoint-tool.d.ts.map +1 -0
  31. package/dist/beam/move-endpoint-tool.js +49 -0
  32. package/dist/beam/move-tool.d.ts +12 -0
  33. package/dist/beam/move-tool.d.ts.map +1 -0
  34. package/dist/beam/move-tool.js +114 -0
  35. package/dist/beam/panel-model.d.ts +28 -0
  36. package/dist/beam/panel-model.d.ts.map +1 -0
  37. package/dist/beam/panel-model.js +123 -0
  38. package/dist/beam/panel.d.ts +13 -0
  39. package/dist/beam/panel.d.ts.map +1 -0
  40. package/dist/beam/panel.js +107 -0
  41. package/dist/beam/parametrics.d.ts +3 -0
  42. package/dist/beam/parametrics.d.ts.map +1 -0
  43. package/dist/beam/parametrics.js +4 -0
  44. package/dist/beam/renderer.d.ts +13 -0
  45. package/dist/beam/renderer.d.ts.map +1 -0
  46. package/dist/beam/renderer.js +36 -0
  47. package/dist/beam/schema.d.ts +2 -0
  48. package/dist/beam/schema.d.ts.map +1 -0
  49. package/dist/beam/schema.js +1 -0
  50. package/dist/beam/tool.d.ts +13 -0
  51. package/dist/beam/tool.d.ts.map +1 -0
  52. package/dist/beam/tool.js +152 -0
  53. package/dist/box-vent/panel.d.ts.map +1 -1
  54. package/dist/box-vent/panel.js +8 -7
  55. package/dist/cabinet/batchable.d.ts +10 -0
  56. package/dist/cabinet/batchable.d.ts.map +1 -0
  57. package/dist/cabinet/batchable.js +12 -0
  58. package/dist/cabinet/compartment-card.d.ts.map +1 -1
  59. package/dist/cabinet/compartment-card.js +142 -46
  60. package/dist/cabinet/countertop-layout.d.ts +5 -0
  61. package/dist/cabinet/countertop-layout.d.ts.map +1 -1
  62. package/dist/cabinet/definition.d.ts.map +1 -1
  63. package/dist/cabinet/definition.js +3 -0
  64. package/dist/cabinet/dimension-input.d.ts +6 -0
  65. package/dist/cabinet/dimension-input.d.ts.map +1 -0
  66. package/dist/cabinet/dimension-input.js +8 -0
  67. package/dist/cabinet/essential-controls.d.ts.map +1 -1
  68. package/dist/cabinet/essential-controls.js +82 -17
  69. package/dist/cabinet/index.d.ts +1 -1
  70. package/dist/cabinet/index.d.ts.map +1 -1
  71. package/dist/cabinet/index.js +1 -1
  72. package/dist/cabinet/kitchen-catalog.d.ts +17 -4
  73. package/dist/cabinet/kitchen-catalog.d.ts.map +1 -1
  74. package/dist/cabinet/kitchen-catalog.js +45 -7
  75. package/dist/cabinet/kitchen-collection.d.ts.map +1 -1
  76. package/dist/cabinet/kitchen-collection.js +21 -5
  77. package/dist/cabinet/kitchen-module-actions.d.ts.map +1 -1
  78. package/dist/cabinet/kitchen-module-actions.js +107 -14
  79. package/dist/cabinet/neighbor-placement.d.ts +10 -0
  80. package/dist/cabinet/neighbor-placement.d.ts.map +1 -1
  81. package/dist/cabinet/panel.d.ts.map +1 -1
  82. package/dist/cabinet/panel.js +127 -50
  83. package/dist/cabinet/run-finishing.d.ts +5 -0
  84. package/dist/cabinet/run-finishing.d.ts.map +1 -1
  85. package/dist/cabinet/run-ops.d.ts +5 -0
  86. package/dist/cabinet/run-ops.d.ts.map +1 -1
  87. package/dist/cabinet/run-panel.d.ts.map +1 -1
  88. package/dist/cabinet/run-panel.js +203 -52
  89. package/dist/cabinet/tool.d.ts.map +1 -1
  90. package/dist/cabinet/tool.js +8 -5
  91. package/dist/ceiling/build-up-section.d.ts +7 -0
  92. package/dist/ceiling/build-up-section.d.ts.map +1 -0
  93. package/dist/ceiling/build-up-section.js +57 -0
  94. package/dist/ceiling/definition.d.ts.map +1 -1
  95. package/dist/ceiling/definition.js +3 -1
  96. package/dist/ceiling/floorplan-affordances.d.ts +112 -0
  97. package/dist/ceiling/floorplan-affordances.d.ts.map +1 -1
  98. package/dist/ceiling/materials.d.ts +5 -0
  99. package/dist/ceiling/materials.d.ts.map +1 -1
  100. package/dist/ceiling/materials.js +17 -0
  101. package/dist/ceiling/panel-model.d.ts +13 -0
  102. package/dist/ceiling/panel-model.d.ts.map +1 -0
  103. package/dist/ceiling/panel-model.js +49 -0
  104. package/dist/ceiling/panel.d.ts.map +1 -1
  105. package/dist/ceiling/panel.js +82 -18
  106. package/dist/ceiling/renderer.d.ts.map +1 -1
  107. package/dist/ceiling/renderer.js +10 -4
  108. package/dist/ceiling/tool.d.ts.map +1 -1
  109. package/dist/ceiling/tool.js +24 -4
  110. package/dist/chimney/panel.d.ts.map +1 -1
  111. package/dist/chimney/panel.js +113 -41
  112. package/dist/column/definition.d.ts.map +1 -1
  113. package/dist/column/definition.js +3 -0
  114. package/dist/column/floorplan.d.ts.map +1 -1
  115. package/dist/column/floorplan.js +17 -0
  116. package/dist/column/hosted-resize.d.ts +28 -1
  117. package/dist/column/hosted-resize.d.ts.map +1 -1
  118. package/dist/column/panel.d.ts.map +1 -1
  119. package/dist/column/panel.js +125 -37
  120. package/dist/column/post-options.d.ts +10 -0
  121. package/dist/column/post-options.d.ts.map +1 -0
  122. package/dist/column/post-options.js +71 -0
  123. package/dist/column/post-placement.d.ts +78 -0
  124. package/dist/column/post-placement.d.ts.map +1 -0
  125. package/dist/column/post-placement.js +157 -0
  126. package/dist/column/post-section.d.ts +17 -0
  127. package/dist/column/post-section.d.ts.map +1 -0
  128. package/dist/column/post-section.js +95 -0
  129. package/dist/column/post-tool.d.ts +11 -0
  130. package/dist/column/post-tool.d.ts.map +1 -0
  131. package/dist/column/post-tool.js +94 -0
  132. package/dist/column/tool.d.ts +3 -10
  133. package/dist/column/tool.d.ts.map +1 -1
  134. package/dist/column/tool.js +13 -1
  135. package/dist/cupola/panel.d.ts.map +1 -1
  136. package/dist/cupola/panel.js +14 -7
  137. package/dist/door/actions.d.ts +7 -0
  138. package/dist/door/actions.d.ts.map +1 -0
  139. package/dist/door/actions.js +32 -0
  140. package/dist/door/contextual-dimensions.d.ts.map +1 -1
  141. package/dist/door/contextual-dimensions.js +11 -1
  142. package/dist/door/definition.d.ts.map +1 -1
  143. package/dist/door/definition.js +34 -7
  144. package/dist/door/floorplan-affordances.d.ts.map +1 -1
  145. package/dist/door/floorplan-affordances.js +5 -2
  146. package/dist/door/floorplan-move.d.ts.map +1 -1
  147. package/dist/door/floorplan-move.js +28 -6
  148. package/dist/door/floorplan.d.ts +2 -0
  149. package/dist/door/floorplan.d.ts.map +1 -1
  150. package/dist/door/floorplan.js +89 -16
  151. package/dist/door/move-tool.d.ts.map +1 -1
  152. package/dist/door/move-tool.js +60 -16
  153. package/dist/door/paint.d.ts +2 -7
  154. package/dist/door/paint.d.ts.map +1 -1
  155. package/dist/door/paint.js +26 -2
  156. package/dist/door/panel.d.ts.map +1 -1
  157. package/dist/door/panel.js +242 -90
  158. package/dist/door/parametrics.js +1 -1
  159. package/dist/door/placement.d.ts.map +1 -1
  160. package/dist/door/placement.js +9 -2
  161. package/dist/door/swing.d.ts +22 -0
  162. package/dist/door/swing.d.ts.map +1 -0
  163. package/dist/door/swing.js +36 -0
  164. package/dist/door/tool.d.ts.map +1 -1
  165. package/dist/door/tool.js +76 -29
  166. package/dist/dormer/panel-actions-section.d.ts.map +1 -1
  167. package/dist/dormer/panel-actions-section.js +3 -1
  168. package/dist/dormer/panel-position-section.d.ts.map +1 -1
  169. package/dist/dormer/panel-position-section.js +4 -3
  170. package/dist/dormer/panel-windows-section.d.ts.map +1 -1
  171. package/dist/dormer/panel-windows-section.js +38 -1
  172. package/dist/dormer/panel.d.ts.map +1 -1
  173. package/dist/dormer/panel.js +24 -18
  174. package/dist/downspout/inspector-editors.d.ts.map +1 -1
  175. package/dist/downspout/inspector-editors.js +3 -2
  176. package/dist/duct-fitting/inspector-editors.d.ts.map +1 -1
  177. package/dist/duct-fitting/inspector-editors.js +6 -1
  178. package/dist/duct-fitting/tool.d.ts.map +1 -1
  179. package/dist/duct-fitting/tool.js +11 -2
  180. package/dist/duct-terminal/tool.d.ts.map +1 -1
  181. package/dist/duct-terminal/tool.js +11 -2
  182. package/dist/elevator/panel.d.ts.map +1 -1
  183. package/dist/elevator/panel.js +41 -18
  184. package/dist/eyebrow-vent/panel.d.ts.map +1 -1
  185. package/dist/eyebrow-vent/panel.js +16 -6
  186. package/dist/fence/drafting.d.ts +2 -3
  187. package/dist/fence/drafting.d.ts.map +1 -1
  188. package/dist/fence/drafting.js +2 -3
  189. package/dist/fence/inspector-editors.d.ts.map +1 -1
  190. package/dist/fence/inspector-editors.js +64 -21
  191. package/dist/fence/move-endpoint-tool.d.ts.map +1 -1
  192. package/dist/fence/move-endpoint-tool.js +11 -2
  193. package/dist/fence/system.d.ts.map +1 -1
  194. package/dist/fence/system.js +13 -0
  195. package/dist/fence-feature/actions.d.ts.map +1 -1
  196. package/dist/fence-feature/actions.js +5 -2
  197. package/dist/fence-feature/inspector.d.ts.map +1 -1
  198. package/dist/fence-feature/inspector.js +150 -34
  199. package/dist/fence-feature/move-session.d.ts +1 -1
  200. package/dist/floor-opening/floorplan.d.ts +29 -6
  201. package/dist/floor-opening/floorplan.d.ts.map +1 -1
  202. package/dist/floor-opening/floorplan.js +172 -14
  203. package/dist/gutter/downspouts-panel.d.ts.map +1 -1
  204. package/dist/gutter/downspouts-panel.js +14 -2
  205. package/dist/hvac-equipment/tool.d.ts.map +1 -1
  206. package/dist/hvac-equipment/tool.js +3 -2
  207. package/dist/i18n/definition-messages.d.ts +5 -0
  208. package/dist/i18n/definition-messages.d.ts.map +1 -0
  209. package/dist/i18n/definition-messages.js +771 -0
  210. package/dist/index.d.ts +4 -1
  211. package/dist/index.d.ts.map +1 -1
  212. package/dist/index.js +6 -1
  213. package/dist/item/authored-params.d.ts.map +1 -1
  214. package/dist/item/authored-params.js +19 -4
  215. package/dist/item/floorplan.d.ts +1 -1
  216. package/dist/item/floorplan.d.ts.map +1 -1
  217. package/dist/item/panel.d.ts.map +1 -1
  218. package/dist/item/panel.js +9 -7
  219. package/dist/liquid-line/tool.d.ts.map +1 -1
  220. package/dist/liquid-line/tool.js +10 -3
  221. package/dist/measurement/tool.d.ts +1 -1
  222. package/dist/measurement/tool.d.ts.map +1 -1
  223. package/dist/measurement/tool.js +8 -8
  224. package/dist/pipe-fitting/tool.d.ts.map +1 -1
  225. package/dist/pipe-fitting/tool.js +3 -2
  226. package/dist/pipe-trap/tool.d.ts.map +1 -1
  227. package/dist/pipe-trap/tool.js +7 -2
  228. package/dist/procedural-item/renderer.d.ts.map +1 -1
  229. package/dist/procedural-item/renderer.js +8 -1
  230. package/dist/ridge-vent/panel.d.ts.map +1 -1
  231. package/dist/ridge-vent/panel.js +22 -9
  232. package/dist/roof/barrel-panel.d.ts.map +1 -1
  233. package/dist/roof/barrel-panel.js +7 -3
  234. package/dist/roof/definition.d.ts.map +1 -1
  235. package/dist/roof/definition.js +33 -3
  236. package/dist/roof/floorplan-overrides.d.ts +22 -0
  237. package/dist/roof/floorplan-overrides.d.ts.map +1 -0
  238. package/dist/roof/floorplan-overrides.js +46 -0
  239. package/dist/roof/floorplan-tool.d.ts +9 -0
  240. package/dist/roof/floorplan-tool.d.ts.map +1 -0
  241. package/dist/roof/floorplan-tool.js +168 -0
  242. package/dist/roof/floorplan.d.ts.map +1 -1
  243. package/dist/roof/floorplan.js +131 -41
  244. package/dist/roof/glazing-controls.d.ts +2 -1
  245. package/dist/roof/glazing-controls.d.ts.map +1 -1
  246. package/dist/roof/glazing-controls.js +51 -13
  247. package/dist/roof/glazing-panel-editor.d.ts.map +1 -1
  248. package/dist/roof/glazing-panel-editor.js +40 -13
  249. package/dist/roof/glazing-panel.d.ts.map +1 -1
  250. package/dist/roof/glazing-panel.js +73 -9
  251. package/dist/roof/paint.d.ts +19 -0
  252. package/dist/roof/paint.d.ts.map +1 -0
  253. package/dist/roof/paint.js +158 -0
  254. package/dist/roof/panel.d.ts.map +1 -1
  255. package/dist/roof/panel.js +90 -22
  256. package/dist/roof/renderer.d.ts.map +1 -1
  257. package/dist/roof/renderer.js +13 -2
  258. package/dist/roof/roof-footprint-source.d.ts +17 -0
  259. package/dist/roof/roof-footprint-source.d.ts.map +1 -1
  260. package/dist/roof/roof-footprint-source.js +14 -0
  261. package/dist/roof/roof-footprint.d.ts +36 -2
  262. package/dist/roof/roof-footprint.d.ts.map +1 -1
  263. package/dist/roof/roof-footprint.js +124 -2
  264. package/dist/roof/roof-materials.d.ts.map +1 -1
  265. package/dist/roof/roof-materials.js +10 -1
  266. package/dist/roof/roof-placement-mode.d.ts +0 -2
  267. package/dist/roof/roof-placement-mode.d.ts.map +1 -1
  268. package/dist/roof/roof-placement-mode.js +8 -6
  269. package/dist/roof/roof-plan-creases.d.ts +50 -0
  270. package/dist/roof/roof-plan-creases.d.ts.map +1 -0
  271. package/dist/roof/roof-plan-creases.js +194 -0
  272. package/dist/roof/roof-ridge-mode.d.ts +20 -0
  273. package/dist/roof/roof-ridge-mode.d.ts.map +1 -0
  274. package/dist/roof/roof-ridge-mode.js +16 -0
  275. package/dist/roof/tool.d.ts +17 -4
  276. package/dist/roof/tool.d.ts.map +1 -1
  277. package/dist/roof/tool.js +211 -113
  278. package/dist/roof/wall-catch.d.ts +106 -0
  279. package/dist/roof/wall-catch.d.ts.map +1 -0
  280. package/dist/roof/wall-catch.js +267 -0
  281. package/dist/roof-segment/build-up-section.d.ts +9 -0
  282. package/dist/roof-segment/build-up-section.d.ts.map +1 -0
  283. package/dist/roof-segment/build-up-section.js +191 -0
  284. package/dist/roof-segment/definition.d.ts.map +1 -1
  285. package/dist/roof-segment/definition.js +50 -5
  286. package/dist/roof-segment/floorplan-affordances.d.ts.map +1 -1
  287. package/dist/roof-segment/floorplan-affordances.js +68 -19
  288. package/dist/roof-segment/floorplan.d.ts.map +1 -1
  289. package/dist/roof-segment/floorplan.js +16 -12
  290. package/dist/roof-segment/paint.d.ts +3 -1
  291. package/dist/roof-segment/paint.d.ts.map +1 -1
  292. package/dist/roof-segment/paint.js +43 -2
  293. package/dist/roof-segment/panel-model.d.ts +23 -0
  294. package/dist/roof-segment/panel-model.d.ts.map +1 -0
  295. package/dist/roof-segment/panel-model.js +64 -0
  296. package/dist/roof-segment/panel.d.ts.map +1 -1
  297. package/dist/roof-segment/panel.js +96 -14
  298. package/dist/roof-segment/parametrics.d.ts.map +1 -1
  299. package/dist/roof-segment/parametrics.js +4 -0
  300. package/dist/roof-segment/renderer.d.ts.map +1 -1
  301. package/dist/roof-segment/renderer.js +26 -5
  302. package/dist/separator/definition.d.ts.map +1 -1
  303. package/dist/separator/definition.js +10 -12
  304. package/dist/separator/floorplan-affordances.d.ts +9 -0
  305. package/dist/separator/floorplan-affordances.d.ts.map +1 -0
  306. package/dist/separator/floorplan-affordances.js +23 -0
  307. package/dist/separator/floorplan.d.ts +9 -0
  308. package/dist/separator/floorplan.d.ts.map +1 -0
  309. package/dist/separator/floorplan.js +54 -0
  310. package/dist/separator/renderer.d.ts +4 -0
  311. package/dist/separator/renderer.d.ts.map +1 -1
  312. package/dist/separator/renderer.js +21 -6
  313. package/dist/separator/selection.d.ts +10 -0
  314. package/dist/separator/selection.d.ts.map +1 -0
  315. package/dist/separator/selection.js +35 -0
  316. package/dist/shared/build-up-controls.d.ts +88 -0
  317. package/dist/shared/build-up-controls.d.ts.map +1 -0
  318. package/dist/shared/build-up-controls.js +139 -0
  319. package/dist/shared/curtain-opening-limits.d.ts.map +1 -1
  320. package/dist/shared/curtain-opening-limits.js +44 -14
  321. package/dist/shared/item-interaction-actions.d.ts.map +1 -1
  322. package/dist/shared/item-interaction-actions.js +11 -2
  323. package/dist/shared/layer-thickness.d.ts +2 -0
  324. package/dist/shared/layer-thickness.d.ts.map +1 -0
  325. package/dist/shared/layer-thickness.js +3 -0
  326. package/dist/shared/move-roof-tool.d.ts.map +1 -1
  327. package/dist/shared/move-roof-tool.js +100 -5
  328. package/dist/shared/node-batch/batchable.d.ts +2 -0
  329. package/dist/shared/node-batch/batchable.d.ts.map +1 -1
  330. package/dist/shared/node-batch/batchable.js +4 -0
  331. package/dist/shared/node-batch/candidates.d.ts +21 -5
  332. package/dist/shared/node-batch/candidates.d.ts.map +1 -1
  333. package/dist/shared/node-batch/candidates.js +85 -50
  334. package/dist/shared/node-batch/system.d.ts.map +1 -1
  335. package/dist/shared/node-batch/system.js +24 -15
  336. package/dist/shared/opening-casing-move.d.ts +16 -0
  337. package/dist/shared/opening-casing-move.d.ts.map +1 -0
  338. package/dist/shared/opening-casing-move.js +33 -0
  339. package/dist/shared/opening-casing-section.d.ts +15 -0
  340. package/dist/shared/opening-casing-section.d.ts.map +1 -0
  341. package/dist/shared/opening-casing-section.js +38 -0
  342. package/dist/shared/opening-casing.d.ts +26 -0
  343. package/dist/shared/opening-casing.d.ts.map +1 -0
  344. package/dist/shared/opening-casing.js +80 -0
  345. package/dist/shared/opening-depth-position.d.ts +13 -0
  346. package/dist/shared/opening-depth-position.d.ts.map +1 -0
  347. package/dist/shared/opening-depth-position.js +34 -0
  348. package/dist/shared/opening-documentation.d.ts.map +1 -1
  349. package/dist/shared/opening-documentation.js +17 -8
  350. package/dist/shared/opening-guides-runtime.d.ts +2 -1
  351. package/dist/shared/opening-guides-runtime.d.ts.map +1 -1
  352. package/dist/shared/opening-guides-runtime.js +11 -7
  353. package/dist/shared/opening-labels.d.ts +11 -0
  354. package/dist/shared/opening-labels.d.ts.map +1 -0
  355. package/dist/shared/opening-labels.js +46 -0
  356. package/dist/shared/opening-placement-dimensions.d.ts.map +1 -1
  357. package/dist/shared/opening-placement-dimensions.js +11 -9
  358. package/dist/shared/opening-plane-offset.d.ts +4 -3
  359. package/dist/shared/opening-plane-offset.d.ts.map +1 -1
  360. package/dist/shared/opening-plane-offset.js +7 -4
  361. package/dist/shared/opening-swap.d.ts +8 -0
  362. package/dist/shared/opening-swap.d.ts.map +1 -0
  363. package/dist/shared/opening-swap.js +18 -0
  364. package/dist/shared/polygon-vertex-affordance.d.ts +19 -0
  365. package/dist/shared/polygon-vertex-affordance.d.ts.map +1 -1
  366. package/dist/shared/polygon-vertex-affordance.js +65 -6
  367. package/dist/shared/roof-opening-host.d.ts +2 -1
  368. package/dist/shared/roof-opening-host.d.ts.map +1 -1
  369. package/dist/shared/roof-opening-host.js +14 -5
  370. package/dist/shared/roof-wall-opening-cut.d.ts.map +1 -1
  371. package/dist/shared/roof-wall-opening-cut.js +14 -6
  372. package/dist/shared/roof-wall-opening-placement.d.ts +2 -1
  373. package/dist/shared/roof-wall-opening-placement.d.ts.map +1 -1
  374. package/dist/shared/roof-wall-opening-placement.js +6 -4
  375. package/dist/shared/run-hanger-inspector.d.ts.map +1 -1
  376. package/dist/shared/run-hanger-inspector.js +39 -5
  377. package/dist/shared/system-check-panel.d.ts.map +1 -1
  378. package/dist/shared/system-check-panel.js +94 -15
  379. package/dist/shared/wall-attach-target.d.ts.map +1 -1
  380. package/dist/shared/wall-attach-target.js +6 -3
  381. package/dist/shelf/definition.d.ts.map +1 -1
  382. package/dist/shelf/definition.js +2 -0
  383. package/dist/site/ground-hole.d.ts +3 -3
  384. package/dist/site/ground-hole.js +3 -3
  385. package/dist/site/ground-veil.d.ts +5 -12
  386. package/dist/site/ground-veil.d.ts.map +1 -1
  387. package/dist/site/ground-veil.js +9 -33
  388. package/dist/site/panel.d.ts.map +1 -1
  389. package/dist/site/panel.js +52 -12
  390. package/dist/site/renderer.d.ts.map +1 -1
  391. package/dist/site/renderer.js +52 -23
  392. package/dist/skylight/panel.d.ts.map +1 -1
  393. package/dist/skylight/panel.js +49 -19
  394. package/dist/slab/deck-geometry.d.ts +28 -0
  395. package/dist/slab/deck-geometry.d.ts.map +1 -0
  396. package/dist/slab/deck-geometry.js +320 -0
  397. package/dist/slab/definition.d.ts +2 -1
  398. package/dist/slab/definition.d.ts.map +1 -1
  399. package/dist/slab/definition.js +6 -1
  400. package/dist/slab/dependency-tracker.d.ts.map +1 -1
  401. package/dist/slab/dependency-tracker.js +3 -0
  402. package/dist/slab/floorplan-affordances.d.ts +500 -0
  403. package/dist/slab/floorplan-affordances.d.ts.map +1 -1
  404. package/dist/slab/geometry.d.ts.map +1 -1
  405. package/dist/slab/geometry.js +46 -9
  406. package/dist/slab/panel.d.ts.map +1 -1
  407. package/dist/slab/panel.js +97 -21
  408. package/dist/slab/renderer.d.ts +11 -0
  409. package/dist/slab/renderer.d.ts.map +1 -0
  410. package/dist/slab/renderer.js +19 -0
  411. package/dist/slab/system.d.ts.map +1 -1
  412. package/dist/slab/system.js +12 -1
  413. package/dist/solar-panel/panel.d.ts.map +1 -1
  414. package/dist/solar-panel/panel.js +49 -14
  415. package/dist/stair/construction-controls.d.ts +3 -1
  416. package/dist/stair/construction-controls.d.ts.map +1 -1
  417. package/dist/stair/construction-controls.js +75 -12
  418. package/dist/stair/paint.d.ts +6 -0
  419. package/dist/stair/paint.d.ts.map +1 -1
  420. package/dist/stair/panel.d.ts.map +1 -1
  421. package/dist/stair/panel.js +243 -51
  422. package/dist/stair/parametrics.d.ts.map +1 -1
  423. package/dist/stair/parametrics.js +7 -0
  424. package/dist/stair/placement-defaults.d.ts +18 -0
  425. package/dist/stair/placement-defaults.d.ts.map +1 -0
  426. package/dist/stair/placement-defaults.js +27 -0
  427. package/dist/stair/railing-controls.d.ts.map +1 -1
  428. package/dist/stair/railing-controls.js +78 -18
  429. package/dist/stair/renderer.d.ts.map +1 -1
  430. package/dist/stair/renderer.js +16 -10
  431. package/dist/stair/structure-controls.d.ts +18 -0
  432. package/dist/stair/structure-controls.d.ts.map +1 -0
  433. package/dist/stair/structure-controls.js +275 -0
  434. package/dist/stair/tool.d.ts.map +1 -1
  435. package/dist/stair/tool.js +13 -9
  436. package/dist/stair/use-stair-render-data.d.ts +41 -0
  437. package/dist/stair/use-stair-render-data.d.ts.map +1 -1
  438. package/dist/stair-segment/panel.d.ts.map +1 -1
  439. package/dist/stair-segment/panel.js +65 -18
  440. package/dist/turbine-vent/panel.d.ts.map +1 -1
  441. package/dist/turbine-vent/panel.js +14 -5
  442. package/dist/unit/unit-panel.d.ts.map +1 -1
  443. package/dist/unit/unit-panel.js +28 -12
  444. package/dist/wall/actions.d.ts +1 -1
  445. package/dist/wall/actions.d.ts.map +1 -1
  446. package/dist/wall/actions.js +17 -13
  447. package/dist/wall/construction-dimensions.d.ts +6 -0
  448. package/dist/wall/construction-dimensions.d.ts.map +1 -1
  449. package/dist/wall/construction-dimensions.js +8 -0
  450. package/dist/wall/contextual-dimensions.d.ts +6 -0
  451. package/dist/wall/contextual-dimensions.d.ts.map +1 -1
  452. package/dist/wall/contextual-dimensions.js +104 -17
  453. package/dist/wall/curtain-opening-frame.d.ts.map +1 -1
  454. package/dist/wall/curtain-opening-frame.js +6 -4
  455. package/dist/wall/curtain-wall-layout.d.ts +1 -1
  456. package/dist/wall/curtain-wall-layout.d.ts.map +1 -1
  457. package/dist/wall/curtain-wall-layout.js +6 -4
  458. package/dist/wall/curtain-wall-materials.d.ts.map +1 -1
  459. package/dist/wall/curtain-wall-materials.js +8 -3
  460. package/dist/wall/curtain-wall-panel.d.ts.map +1 -1
  461. package/dist/wall/curtain-wall-panel.js +241 -37
  462. package/dist/wall/curve-tool.d.ts.map +1 -1
  463. package/dist/wall/curve-tool.js +2 -3
  464. package/dist/wall/definition.d.ts.map +1 -1
  465. package/dist/wall/definition.js +20 -5
  466. package/dist/wall/floorplan-affordances.d.ts.map +1 -1
  467. package/dist/wall/floorplan-affordances.js +9 -6
  468. package/dist/wall/floorplan-move.d.ts.map +1 -1
  469. package/dist/wall/floorplan-move.js +66 -16
  470. package/dist/wall/floorplan.d.ts +4 -5
  471. package/dist/wall/floorplan.d.ts.map +1 -1
  472. package/dist/wall/floorplan.js +62 -46
  473. package/dist/wall/move-endpoint-tool.d.ts.map +1 -1
  474. package/dist/wall/move-endpoint-tool.js +1 -0
  475. package/dist/wall/move-tool.d.ts.map +1 -1
  476. package/dist/wall/move-tool.js +77 -12
  477. package/dist/wall/paint.d.ts +2 -2
  478. package/dist/wall/paint.d.ts.map +1 -1
  479. package/dist/wall/paint.js +11 -4
  480. package/dist/wall/panel-model.d.ts +17 -2
  481. package/dist/wall/panel-model.d.ts.map +1 -1
  482. package/dist/wall/panel-model.js +158 -18
  483. package/dist/wall/panel.d.ts +8 -0
  484. package/dist/wall/panel.d.ts.map +1 -1
  485. package/dist/wall/panel.js +228 -156
  486. package/dist/wall/parametrics.d.ts +1 -1
  487. package/dist/wall/parametrics.d.ts.map +1 -1
  488. package/dist/wall/parametrics.js +14 -1
  489. package/dist/wall/railing-wall-adapter.d.ts +7 -0
  490. package/dist/wall/railing-wall-adapter.d.ts.map +1 -0
  491. package/dist/wall/railing-wall-adapter.js +38 -0
  492. package/dist/wall/railing-wall-floorplan.d.ts +8 -0
  493. package/dist/wall/railing-wall-floorplan.d.ts.map +1 -0
  494. package/dist/wall/railing-wall-floorplan.js +27 -0
  495. package/dist/wall/railing-wall-geometry.d.ts +12 -0
  496. package/dist/wall/railing-wall-geometry.d.ts.map +1 -0
  497. package/dist/wall/railing-wall-geometry.js +51 -0
  498. package/dist/wall/railing-wall-guard.d.ts +81 -0
  499. package/dist/wall/railing-wall-guard.d.ts.map +1 -0
  500. package/dist/wall/railing-wall-guard.js +435 -0
  501. package/dist/wall/railing-wall-materials.d.ts +11 -0
  502. package/dist/wall/railing-wall-materials.d.ts.map +1 -0
  503. package/dist/wall/railing-wall-materials.js +31 -0
  504. package/dist/wall/railing-wall-panel-model.d.ts +30 -0
  505. package/dist/wall/railing-wall-panel-model.d.ts.map +1 -0
  506. package/dist/wall/railing-wall-panel-model.js +117 -0
  507. package/dist/wall/railing-wall-panel.d.ts +17 -0
  508. package/dist/wall/railing-wall-panel.d.ts.map +1 -0
  509. package/dist/wall/railing-wall-panel.js +42 -0
  510. package/dist/wall/rectangle-command.d.ts +79 -1
  511. package/dist/wall/rectangle-command.d.ts.map +1 -1
  512. package/dist/wall/rectangle-tool.d.ts.map +1 -1
  513. package/dist/wall/rectangle-tool.js +2 -0
  514. package/dist/wall/renderer.d.ts.map +1 -1
  515. package/dist/wall/renderer.js +3 -1
  516. package/dist/wall/skirting-floor-gaps.d.ts +8 -0
  517. package/dist/wall/skirting-floor-gaps.d.ts.map +1 -0
  518. package/dist/wall/skirting-floor-gaps.js +36 -0
  519. package/dist/wall/system.d.ts +0 -14
  520. package/dist/wall/system.d.ts.map +1 -1
  521. package/dist/wall/system.js +6 -1
  522. package/dist/wall/tool.d.ts +16 -0
  523. package/dist/wall/tool.d.ts.map +1 -1
  524. package/dist/wall/tool.js +160 -47
  525. package/dist/wall/treatments.d.ts +3 -0
  526. package/dist/wall/treatments.d.ts.map +1 -1
  527. package/dist/wall/treatments.js +30 -7
  528. package/dist/window/actions.d.ts +7 -0
  529. package/dist/window/actions.d.ts.map +1 -0
  530. package/dist/window/actions.js +29 -0
  531. package/dist/window/definition.d.ts.map +1 -1
  532. package/dist/window/definition.js +11 -2
  533. package/dist/window/floorplan-move.d.ts.map +1 -1
  534. package/dist/window/floorplan-move.js +5 -0
  535. package/dist/window/floorplan.d.ts +2 -0
  536. package/dist/window/floorplan.d.ts.map +1 -1
  537. package/dist/window/floorplan.js +58 -1
  538. package/dist/window/mechanism.d.ts.map +1 -1
  539. package/dist/window/mechanism.js +21 -1
  540. package/dist/window/move-tool.d.ts.map +1 -1
  541. package/dist/window/move-tool.js +10 -3
  542. package/dist/window/paint.d.ts +2 -7
  543. package/dist/window/paint.d.ts.map +1 -1
  544. package/dist/window/paint.js +31 -3
  545. package/dist/window/panel.d.ts.map +1 -1
  546. package/dist/window/panel.js +311 -69
  547. package/dist/window/placement.d.ts.map +1 -1
  548. package/dist/window/placement.js +5 -1
  549. package/dist/window/swing.d.ts +13 -0
  550. package/dist/window/swing.d.ts.map +1 -0
  551. package/dist/window/swing.js +16 -0
  552. package/dist/window/tool.d.ts.map +1 -1
  553. package/dist/window/tool.js +19 -14
  554. package/dist/zone/floorplan-affordances.d.ts +136 -0
  555. package/dist/zone/floorplan-affordances.d.ts.map +1 -1
  556. package/dist/zone/quantities-panel.d.ts.map +1 -1
  557. package/dist/zone/quantities-panel.js +34 -9
  558. package/dist/zone/renderer.d.ts.map +1 -1
  559. package/dist/zone/renderer.js +9 -5
  560. package/package.json +14 -14
  561. package/dist/site/generic-foundation-geometry.d.ts +0 -16
  562. package/dist/site/generic-foundation-geometry.d.ts.map +0 -1
  563. package/dist/site/generic-foundation-geometry.js +0 -75
  564. package/dist/site/generic-foundation.d.ts +0 -4
  565. package/dist/site/generic-foundation.d.ts.map +0 -1
  566. package/dist/site/generic-foundation.js +0 -91
@@ -59,8 +59,8 @@ export const wallCurveAffordance = {
59
59
  affectedIds: [node.id],
60
60
  apply({ planPoint }) {
61
61
  const snapStep = getSegmentGridStep();
62
- // World-grid snap so a rotated building doesn't drag the curve
63
- // handle off the visible grid.
62
+ // Building-grid snap, the lattice the plan draws (it turns with the
63
+ // building).
64
64
  const [x, y] = snapBuildingLocalToWorldGrid([planPoint[0], planPoint[1]], snapStep);
65
65
  // Signed projection of (snappedPoint - chord midpoint) onto the
66
66
  // chord normal. Legacy negates because the SVG y-axis flips
@@ -167,16 +167,19 @@ export const wallMoveEndpointAffordance = {
167
167
  // is 0 outside grid mode), so `'lines' / 'angles' / 'off'` no longer
168
168
  // force a grid snap the mode chip says is inactive. In `'angles'` mode
169
169
  // the endpoint angle-locks off the fixed corner (free length), matching
170
- // the draft tool — the angle path ignores the `gridSnap` override.
171
- const angleLocked = isAngleSnapActive();
170
+ // the draft tool — the angle path ignores the `gridSnap` override. The
171
+ // fixed corner is the segment's start in every mode, as in the 3D tool:
172
+ // the body capture slides the end along it, so a wall whose fixed end
173
+ // is T-joined on another wall can't fold onto that wall's line.
172
174
  const snapped = snapWallDraftPoint({
173
175
  point: planPoint,
174
176
  walls,
175
177
  ignoreWallIds: staleWallIds,
176
- start: angleLocked ? fixedPoint : undefined,
177
- angleSnap: angleLocked,
178
+ start: fixedPoint,
179
+ angleSnap: isAngleSnapActive(),
178
180
  magnetic: isMagneticSnapActive(),
179
181
  gridSnap: (p) => snapBuildingLocalToWorldGrid(p, getSegmentGridStep()),
182
+ captureWallBodies: true,
180
183
  });
181
184
  // Figma-style alignment on the dragged corner — snaps it onto another
182
185
  // object's edge / wall face and publishes a guide. The guide is
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan-move.d.ts","sourceRoot":"","sources":["../../src/wall/floorplan-move.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,mBAAmB,EAQxB,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AAoBzB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAAmB,CAAC,QAAQ,CAgVjE,CAAA"}
1
+ {"version":3,"file":"floorplan-move.d.ts","sourceRoot":"","sources":["../../src/wall/floorplan-move.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,mBAAmB,EAYxB,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AAyBzB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAAmB,CAAC,QAAQ,CAqajE,CAAA"}
@@ -1,5 +1,5 @@
1
- import { createLevelStructurePreview, getPerpendicularWallMoveAxis, getPlannedLinkedWallUpdates, planWallEndRejoins, planWallMoveJunctions, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
2
- import { getFloorplanWallThickness, getSegmentGridStep, isSegmentLongEnough, snapScalarToGrid, useWallMoveGhosts, WALL_CONNECT_SNAP_RADIUS, } from '@pascal-app/editor';
1
+ import { createLevelStructurePreview, finishWallEdit, generateId, getPerpendicularWallMoveAxis, getPlannedLinkedWallUpdates, planAttachedWallChanges, planAttachedWallUpdates, planWallEndRejoins, planWallMoveJunctions, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
2
+ import { getFloorplanWallThickness, getSegmentGridStep, isSegmentLongEnough, isWallRunFree, nearestWallParallelTarget, publishWallParallelGuide, snapScalarToGrid, useWallMoveGhosts, WALL_CONNECT_SNAP_RADIUS, wallParallelTargets, } from '@pascal-app/editor';
3
3
  import { useViewer } from '@pascal-app/viewer';
4
4
  import { buildBridgeWallCreates, buildBridgeWallPreviews, getLinkedWallSnapshots, getWallsAfterUpdates, stripWallIsNewMetadata, } from './move-shared.js';
5
5
  /**
@@ -78,7 +78,22 @@ export const wallFloorplanMoveTarget = ({ node }) => {
78
78
  const dragNodes = Object.fromEntries(Object.values(useScene.getState().nodes)
79
79
  .filter((node) => node.parentId === levelId)
80
80
  .map((node) => [node.id, node]));
81
- const affectedIds = [wallId, ...linkedOriginals.map((wall) => wall.id)];
81
+ // Lines up with the parallel walls on the level: an outside wall's faces
82
+ // with the rest of the outside, any other wall's with every wall.
83
+ const parallelTargets = moveAxis
84
+ ? wallParallelTargets(node, moveAxis, Object.values(dragNodes).filter((entry) => entry.type === 'wall'))
85
+ : [];
86
+ let lastLineUp = null;
87
+ // Walls T'd into this one stay on it, stretched along their own line.
88
+ const linkedIds = new Set(linkedOriginals.map((wall) => wall.id));
89
+ const attachedWalls = isNew
90
+ ? []
91
+ : planAttachedWallUpdates(Object.values(dragNodes).filter((entry) => entry.type === 'wall' && entry.id !== wallId && !linkedIds.has(entry.id)), originalStart, originalEnd, originalStart, originalEnd).map(({ id }) => dragNodes[id]);
92
+ const affectedIds = [
93
+ wallId,
94
+ ...linkedOriginals.map((wall) => wall.id),
95
+ ...attachedWalls.map((wall) => wall.id),
96
+ ];
82
97
  const affectedIdSet = new Set(affectedIds);
83
98
  const touchedSurfaceIds = new Set();
84
99
  const publishSurfacePreviews = (walls) => {
@@ -121,25 +136,30 @@ export const wallFloorplanMoveTarget = ({ node }) => {
121
136
  };
122
137
  const session = {
123
138
  affectedIds,
124
- apply({ planPoint }) {
139
+ apply({ planPoint, modifiers }) {
125
140
  if (!rawAnchor) {
126
141
  rawAnchor = [planPoint[0], planPoint[1]];
127
142
  return;
128
143
  }
129
144
  const rawDx = planPoint[0] - rawAnchor[0];
130
145
  const rawDz = planPoint[1] - rawAnchor[1];
131
- const step = getSegmentGridStep();
146
+ // Alt (or snapping off) moves the wall exactly with the pointer.
147
+ const free = isWallRunFree(modifiers);
148
+ const step = free ? 0 : getSegmentGridStep();
132
149
  // Axis-lock the move to the wall's perpendicular (normal). Project
133
150
  // the original centre + raw cursor delta onto the axis, snap the
134
- // absolute projection to a grid multiple, then translate the wall
135
- // by `axis * perpDelta`. Matches `MoveWallTool` so 2D and 3D drag
136
- // produce identical wall topology.
151
+ // absolute projection to a parallel wall it lines up with, else to a
152
+ // grid multiple, then translate the wall by `axis * perpDelta`.
153
+ // Matches `MoveWallTool` so 2D and 3D drag produce identical wall
154
+ // topology.
137
155
  let dx;
138
156
  let dz;
157
+ let lineUp = null;
139
158
  if (moveAxis) {
140
159
  const originalProj = originalCenter[0] * moveAxis[0] + originalCenter[1] * moveAxis[1];
141
160
  const rawProj = originalProj + rawDx * moveAxis[0] + rawDz * moveAxis[1];
142
- const snappedProj = snapScalarToGrid(rawProj, step);
161
+ lineUp = free ? null : nearestWallParallelTarget(parallelTargets, rawProj);
162
+ const snappedProj = lineUp ? lineUp.position : snapScalarToGrid(rawProj, step);
143
163
  const perpDelta = snappedProj - originalProj;
144
164
  dx = moveAxis[0] * perpDelta;
145
165
  dz = moveAxis[1] * perpDelta;
@@ -148,6 +168,10 @@ export const wallFloorplanMoveTarget = ({ node }) => {
148
168
  dx = snapScalarToGrid(rawDx, step);
149
169
  dz = snapScalarToGrid(rawDz, step);
150
170
  }
171
+ if (lineUp !== lastLineUp) {
172
+ lastLineUp = lineUp;
173
+ publishWallParallelGuide(lineUp);
174
+ }
151
175
  if (dx === lastDelta[0] && dz === lastDelta[1])
152
176
  return;
153
177
  lastDelta = [dx, dz];
@@ -185,11 +209,14 @@ export const wallFloorplanMoveTarget = ({ node }) => {
185
209
  // until the user commits. Batched into a single zustand
186
210
  // notification — otherwise each per-wall `.set` would re-render
187
211
  // every override subscriber once per linked wall per tick.
212
+ const attachedById = new Map(planAttachedWallUpdates(attachedWalls, originalStart, originalEnd, nextStart, nextEnd).map((update) => [update.id, update]));
213
+ // A wall the move carries off this one's ends lets go: back where it was.
214
+ const attachedUpdates = attachedWalls.map((wall) => attachedById.get(wall.id) ?? { id: wall.id, start: wall.start, end: wall.end });
188
215
  useLiveNodeOverrides
189
216
  .getState()
190
217
  .setMany([
191
218
  [wallId, { start: nextStart, end: nextEnd }],
192
- ...linkedUpdates.map((upd) => [upd.id, { start: upd.start, end: upd.end }]),
219
+ ...[...linkedUpdates, ...attachedUpdates].map((upd) => [upd.id, { start: upd.start, end: upd.end }]),
193
220
  ]);
194
221
  // Surface bridge-wall previews so the floor-plan SVG layer can
195
222
  // render dashed outlines of what `commit()` will insert. Mirrors
@@ -199,7 +226,10 @@ export const wallFloorplanMoveTarget = ({ node }) => {
199
226
  // overrides we just published (zustand is still on baseline).
200
227
  const previewSceneWalls = getWallsAfterUpdates(dragNodes, [
201
228
  { id: wallId, data: { start: nextStart, end: nextEnd } },
202
- ...linkedUpdates.map((u) => ({ id: u.id, data: { start: u.start, end: u.end } })),
229
+ ...[...linkedUpdates, ...attachedUpdates].map((u) => ({
230
+ id: u.id,
231
+ data: { start: u.start, end: u.end },
232
+ })),
203
233
  ]).filter((wall) => !collapsedIds.has(wall.id));
204
234
  const bridgePreviews = buildBridgeWallPreviews({
205
235
  bridgePlans: plan.bridgePlans,
@@ -223,7 +253,7 @@ export const wallFloorplanMoveTarget = ({ node }) => {
223
253
  // (override-merged) so the mesh follows the cursor.
224
254
  const sceneState = useScene.getState();
225
255
  sceneState.markDirty(wallId);
226
- for (const upd of linkedUpdates) {
256
+ for (const upd of [...linkedUpdates, ...attachedUpdates]) {
227
257
  sceneState.markDirty(upd.id);
228
258
  }
229
259
  },
@@ -235,14 +265,16 @@ export const wallFloorplanMoveTarget = ({ node }) => {
235
265
  return dx !== 0 || dz !== 0;
236
266
  },
237
267
  commit() {
268
+ publishWallParallelGuide(null);
238
269
  const sceneState = useScene.getState();
239
270
  const liveWall = sceneState.nodes[wallId];
240
271
  if (liveWall?.type !== 'wall') {
241
272
  // Bail without leaving stale overrides behind.
242
273
  const overrides = useLiveNodeOverrides.getState();
243
274
  overrides.clear(wallId);
244
- for (const wall of linkedOriginals)
275
+ for (const wall of [...linkedOriginals, ...attachedWalls]) {
245
276
  overrides.clear(wall.id);
277
+ }
246
278
  clearSurfacePreviews();
247
279
  return;
248
280
  }
@@ -281,10 +313,27 @@ export const wallFloorplanMoveTarget = ({ node }) => {
281
313
  existingWalls,
282
314
  wallCount: Object.values(sceneState.nodes).filter((entry) => entry?.type === 'wall').length,
283
315
  });
284
- sceneState.applyNodeChanges(planWallEndRejoins(sceneState.nodes, commitUpdates.map(({ id }) => id), WALL_CONNECT_SNAP_RADIUS, {
285
- update: commitUpdates,
316
+ // Walls T'd into this one follow it, what hangs on them staying put.
317
+ const attachedChanges = planAttachedWallChanges(sceneState.nodes, wallId, originalStart, originalEnd, lastNextStart, lastNextEnd, new Set([...linkedIds, ...collapsedLinkedWallIds]));
318
+ const attachedWallIds = attachedChanges.flatMap(({ id }) => sceneState.nodes[id]?.type === 'wall' ? [id] : []);
319
+ // The moved and stretched walls and the jogs take over any stretch of a
320
+ // parallel wall they now run along; jogs on the outline are outside walls.
321
+ sceneState.applyNodeChanges(finishWallEdit(sceneState.nodes, planWallEndRejoins(sceneState.nodes, commitUpdates.map(({ id }) => id), WALL_CONNECT_SNAP_RADIUS, {
322
+ update: [
323
+ ...commitUpdates,
324
+ ...attachedChanges,
325
+ ],
286
326
  create: bridgeCreates,
287
327
  delete: Array.from(collapsedLinkedWallIds),
328
+ }), {
329
+ winnerIds: [
330
+ ...commitUpdates.map(({ id }) => id),
331
+ ...attachedWallIds,
332
+ ...bridgeCreates.map(({ node }) => node.id),
333
+ ],
334
+ movedWallId: wallId,
335
+ bridgeIds: bridgeCreates.map(({ node }) => node.id),
336
+ mintId: () => generateId('wall'),
288
337
  }));
289
338
  // Drop the live overrides now that the committed scene state
290
339
  // matches them — leaving them around would keep the system
@@ -293,8 +342,9 @@ export const wallFloorplanMoveTarget = ({ node }) => {
293
342
  // next frame reads the new scene state directly.
294
343
  const overrides = useLiveNodeOverrides.getState();
295
344
  overrides.clear(wallId);
296
- for (const wall of linkedOriginals)
345
+ for (const wall of [...linkedOriginals, ...attachedWalls]) {
297
346
  overrides.clear(wall.id);
347
+ }
298
348
  clearSurfacePreviews();
299
349
  // Swap ghosts → real walls: the bridges we just created render
300
350
  // through the registry layer, so the dashed previews aren't
@@ -1,13 +1,12 @@
1
1
  import { type AnyNode, type FloorplanGeometry, type GeometryContext, type WallLayerMiterData, type WallMiterData, type WallNode } from '@pascal-app/core';
2
2
  import { type WallConstructionDimensionPlan } from './construction-dimensions.js';
3
- declare const WALL_DIMENSION_REFERENCES: readonly ['finished-faces', 'centerline', 'stud-faces'];
4
- type WallDimensionReference = (typeof WALL_DIMENSION_REFERENCES)[number];
5
3
  export type WallFloorplanLevelData = {
6
4
  miters: WallMiterData;
7
5
  documentMiters: WallMiterData;
8
6
  layerMiters: WallLayerMiterData;
9
7
  documentLayerMiters: WallLayerMiterData;
10
- constructionDimensionsByReference: Record<WallDimensionReference, WallConstructionDimensionPlan>;
8
+ /** Built on first read: a level whose plan shows no automatic dimensions never pays for it. */
9
+ readonly constructionDimensions: WallConstructionDimensionPlan;
11
10
  };
12
11
  export declare function computeWallFloorplanLevelData({ siblings, nodes, }: {
13
12
  siblings: ReadonlyArray<WallNode>;
@@ -22,7 +21,8 @@ export declare function computeWallFloorplanLevelData({ siblings, nodes, }: {
22
21
  * 3. A transparent hit-line on the centerline so the user can grab the
23
22
  * wall body easily.
24
23
  * 4. Two endpoint handles (start + end) when selected — the registry
25
- * layer hosts the 5-circle stack + hover transitions + 2D drag.
24
+ * layer hosts the 5-circle stack + hover transitions + 2D drag — and,
25
+ * just past each, a "pull to continue" grip that draws the next wall.
26
26
  * 5. Exterior facade strings plus interior wall spans and hosted-opening widths.
27
27
  *
28
28
  * `ctx.levelData` provides the shared level miter graph when the floor-plan
@@ -30,5 +30,4 @@ export declare function computeWallFloorplanLevelData({ siblings, nodes, }: {
30
30
  * direct builder callers.
31
31
  */
32
32
  export declare function buildWallFloorplan(node: WallNode, ctx: GeometryContext): FloorplanGeometry | null;
33
- export {};
34
33
  //# sourceMappingURL=floorplan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/wall/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAGZ,KAAK,iBAAiB,EAEtB,KAAK,eAAe,EAYpB,KAAK,kBAAkB,EAEvB,KAAK,aAAa,EAClB,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AAQzB,OAAO,EAKL,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAA;AAalC,QAAA,MAAM,yBAAyB,YAAI,gBAAgB,EAAE,YAAY,EAAE,YAAY,CAAU,CAAA;AAEzF,KAAK,sBAAsB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAA;AAsBxE,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,aAAa,CAAA;IACrB,cAAc,EAAE,aAAa,CAAA;IAC7B,WAAW,EAAE,kBAAkB,CAAA;IAC/B,mBAAmB,EAAE,kBAAkB,CAAA;IACvC,iCAAiC,EAAE,MAAM,CAAC,sBAAsB,EAAE,6BAA6B,CAAC,CAAA;CACjG,CAAA;AAqJD,wBAAgB,6BAA6B,CAAC,EAC5C,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAA;IACjC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B,GAAG,sBAAsB,CA0CzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAG,iBAAiB,GAAG,IAAI,CAgPjG"}
1
+ {"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/wall/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAGZ,KAAK,iBAAiB,EAEtB,KAAK,eAAe,EAapB,KAAK,kBAAkB,EAEvB,KAAK,aAAa,EAClB,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AAazB,OAAO,EAML,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAA;AA4ClC,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,aAAa,CAAA;IACrB,cAAc,EAAE,aAAa,CAAA;IAC7B,WAAW,EAAE,kBAAkB,CAAA;IAC/B,mBAAmB,EAAE,kBAAkB,CAAA;IACvC,+FAA+F;IAC/F,QAAQ,CAAC,sBAAsB,EAAE,6BAA6B,CAAA;CAC/D,CAAA;AAqJD,wBAAgB,6BAA6B,CAAC,EAC5C,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAA;IACjC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B,GAAG,sBAAsB,CAoBzB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAG,iBAAiB,GAAG,IAAI,CAoRjG"}
@@ -1,9 +1,10 @@
1
- import { calculateLevelLayerMiters, calculateLevelMiters, getWallBodyCenterOffset, getWallCurveFrameAt, getWallCurveLength, getWallEffectiveHeightForNodes, getWallFaceOffsets, getWallLayerPolylines, getWallLocalFaceZ, getWallMidpointHandlePoint, getWallPlanFootprint, getWallThickness, isCurvedWall, wallLayerBoundaryOffsets, } from '@pascal-app/core';
2
- import { floorplanGeometryMetadata, readFloorplanContext, WALL_PUSH_AFFORDANCE, } from '@pascal-app/editor';
1
+ import { calculateLevelLayerMiters, calculateLevelMiters, getWallBodyCenterOffset, getWallCurveFrameAt, getWallCurveLength, getWallEffectiveHeightForNodes, getWallFaceOffsets, getWallLayerPolylines, getWallLocalFaceZ, getWallMidpointHandlePoint, getWallPlanFootprint, getWallThickness, isCurvedWall, isRailingWall, wallLayerBoundaryOffsets, } from '@pascal-app/core';
2
+ import { floorplanGeometryMetadata, readFloorplanContext, WALL_PULL_AFFORDANCE, WALL_PULL_GRIP_TITLE, WALL_PUSH_AFFORDANCE, wallPullSeed, } from '@pascal-app/editor';
3
3
  import { constructionDimensionStandard } from '../shared/construction-dimension-standards.js';
4
- import { buildCurvedWallConstructionDimensions, buildLevelWallConstructionDimensionPlan, buildWallConstructionDimensions, renderPlannedConstructionDimensions, } from './construction-dimensions.js';
4
+ import { buildCurvedWallConstructionDimensions, buildLevelWallConstructionDimensionPlan, buildWallConstructionDimensions, constructionDimensionFace, renderPlannedConstructionDimensions, } from './construction-dimensions.js';
5
5
  import { buildCurtainWallFloorplan } from './curtain-wall-floorplan.js';
6
6
  import { hasWallCurveBlockingChildren } from './curve-eligibility.js';
7
+ import { buildRailingWallFloorplan } from './railing-wall-floorplan.js';
7
8
  // Same constants the legacy `getFloorplanWall` uses (editor/lib/floorplan/walls.ts).
8
9
  // Slightly exaggerates thin walls so the 2D plan stays legible without
9
10
  // drifting from BIM data. Inlined to keep nodes/wall self-contained.
@@ -12,10 +13,19 @@ const FLOORPLAN_MIN_VISIBLE_WALL_THICKNESS = 0.13;
12
13
  const FLOORPLAN_MAX_EXTRA_THICKNESS = 0.035;
13
14
  const FLOORPLAN_SELECTION_HATCH_SPACING = 0.12;
14
15
  const FLOORPLAN_SELECTED_WALL_STROKE_WIDTH = 0.03;
16
+ const FLOORPLAN_RAILING_STROKE_WIDTH = 0.01;
15
17
  const FLOORPLAN_SELECTION_HATCH_STROKE_WIDTH = 0.02;
16
- const WALL_DIMENSION_REFERENCES = ['finished-faces', 'centerline', 'stud-faces'];
18
+ // The automatic strings measure finished faces; a partition meeting a facade
19
+ // is dimensioned to both of its faces.
20
+ const FINISHED_FACE_DIMENSIONS = {
21
+ datumPolicy: 'wall-face',
22
+ intersectionReferencePolicy: 'both-faces',
23
+ };
17
24
  function floorplanWallThickness(wall) {
18
25
  const baseThickness = getWallThickness(wall);
26
+ // A railing draws as thin as its posts.
27
+ if (isRailingWall(wall))
28
+ return baseThickness;
19
29
  const scaledThickness = baseThickness * FLOORPLAN_WALL_THICKNESS_SCALE;
20
30
  return Math.min(baseThickness + FLOORPLAN_MAX_EXTRA_THICKNESS, Math.max(baseThickness, scaledThickness, FLOORPLAN_MIN_VISIBLE_WALL_THICKNESS));
21
31
  }
@@ -166,38 +176,19 @@ function buildWallAssemblyLayers(wall, source, layerMiters, children, documentMo
166
176
  }
167
177
  export function computeWallFloorplanLevelData({ siblings, nodes, }) {
168
178
  const walls = siblings.map(exaggerateWallThickness);
169
- const constructionDimensionsByReference = {};
170
- for (const reference of WALL_DIMENSION_REFERENCES) {
171
- let cached;
172
- Object.defineProperty(constructionDimensionsByReference, reference, {
173
- enumerable: true,
174
- get: () => {
175
- if (cached)
176
- return cached;
177
- const datumPolicy = reference === 'finished-faces'
178
- ? 'wall-face'
179
- : reference === 'stud-faces'
180
- ? 'structural-face'
181
- : 'centerline';
182
- cached = buildLevelWallConstructionDimensionPlan(siblings, nodes, constructionDimensionStandard({
183
- datumPolicy,
184
- ...(reference === 'finished-faces'
185
- ? { intersectionReferencePolicy: 'both-faces' }
186
- : {}),
187
- }));
188
- return cached;
189
- },
190
- });
191
- }
192
179
  const documentWalls = [...siblings];
193
180
  const miters = calculateLevelMiters(walls);
194
181
  const documentMiters = calculateLevelMiters(documentWalls);
182
+ let constructionDimensions;
195
183
  return {
196
184
  miters,
197
185
  documentMiters,
198
186
  layerMiters: calculateLevelLayerMiters(walls, miters, wallLayerOffsets),
199
187
  documentLayerMiters: calculateLevelLayerMiters(documentWalls, documentMiters, wallLayerOffsets),
200
- constructionDimensionsByReference,
188
+ get constructionDimensions() {
189
+ constructionDimensions ??= buildLevelWallConstructionDimensionPlan(siblings, nodes, constructionDimensionStandard(FINISHED_FACE_DIMENSIONS));
190
+ return constructionDimensions;
191
+ },
201
192
  };
202
193
  }
203
194
  /**
@@ -209,7 +200,8 @@ export function computeWallFloorplanLevelData({ siblings, nodes, }) {
209
200
  * 3. A transparent hit-line on the centerline so the user can grab the
210
201
  * wall body easily.
211
202
  * 4. Two endpoint handles (start + end) when selected — the registry
212
- * layer hosts the 5-circle stack + hover transitions + 2D drag.
203
+ * layer hosts the 5-circle stack + hover transitions + 2D drag — and,
204
+ * just past each, a "pull to continue" grip that draws the next wall.
213
205
  * 5. Exterior facade strings plus interior wall spans and hosted-opening widths.
214
206
  *
215
207
  * `ctx.levelData` provides the shared level miter graph when the floor-plan
@@ -217,7 +209,7 @@ export function computeWallFloorplanLevelData({ siblings, nodes, }) {
217
209
  * direct builder callers.
218
210
  */
219
211
  export function buildWallFloorplan(node, ctx) {
220
- const { automaticDimensions, metricNotation, purpose, wallDimensionReference } = readFloorplanContext(ctx);
212
+ const { automaticDimensions, metricNotation, purpose, wallDimensionFaces } = readFloorplanContext(ctx);
221
213
  const documentMode = purpose === 'document';
222
214
  const wallForPurpose = (wall) => (documentMode ? wall : exaggerateWallThickness(wall));
223
215
  const self = wallForPurpose(node);
@@ -258,14 +250,24 @@ export function buildWallFloorplan(node, ctx) {
258
250
  : isHovered && palette
259
251
  ? palette.wallHoverStroke
260
252
  : '#1f2937';
261
- const fill = node.wallType === 'curtain' ? 'transparent' : showSelectedChrome ? '#ffffff' : '#374151';
253
+ // A railing is an outline round its posts, with no poché.
254
+ const railing = isRailingWall(node);
255
+ const fill = node.wallType === 'curtain' || railing
256
+ ? 'transparent'
257
+ : showSelectedChrome
258
+ ? '#ffffff'
259
+ : '#374151';
262
260
  const children = [
263
261
  {
264
262
  kind: 'polygon',
265
263
  points,
266
264
  fill,
267
265
  stroke,
268
- strokeWidth: showSelectedChrome ? FLOORPLAN_SELECTED_WALL_STROKE_WIDTH : 0.02,
266
+ strokeWidth: showSelectedChrome
267
+ ? FLOORPLAN_SELECTED_WALL_STROKE_WIDTH
268
+ : railing
269
+ ? FLOORPLAN_RAILING_STROKE_WIDTH
270
+ : 0.02,
269
271
  opacity: 0.92,
270
272
  metadata: floorplanGeometryMetadata({ annotationObstacle: 'outline' }),
271
273
  // Once the wall is selected, the body keeps catching the pointer
@@ -279,6 +281,9 @@ export function buildWallFloorplan(node, ctx) {
279
281
  if (node.wallType === 'curtain') {
280
282
  children.push(...buildCurtainWallFloorplan(node, getWallEffectiveHeightForNodes(node, ctx.sceneNodes ?? {}), stroke, ctx.children));
281
283
  }
284
+ else if (railing) {
285
+ children.push(...buildRailingWallFloorplan(node, ctx.children, ctx.siblings.filter((sibling) => sibling.type === 'wall'), stroke));
286
+ }
282
287
  else if (node.assembly) {
283
288
  // Assembly layer lines + framing poché, drawn inside the footprint.
284
289
  children.push(...buildWallAssemblyLayers(self, node, getLayerMiters(), ctx.children, documentMode));
@@ -286,7 +291,7 @@ export function buildWallFloorplan(node, ctx) {
286
291
  if (automaticDimensions) {
287
292
  const dimensionStroke = isSelected && palette ? palette.selectedStroke : (palette?.measurementStroke ?? '#334155');
288
293
  const dimensionStandard = constructionDimensionStandard({
289
- datumPolicy: wallDimensionDatumPolicy(wallDimensionReference),
294
+ ...FINISHED_FACE_DIMENSIONS,
290
295
  metricNotation,
291
296
  });
292
297
  const exteriorCornerDimensionStandard = constructionDimensionStandard({
@@ -303,11 +308,12 @@ export function buildWallFloorplan(node, ctx) {
303
308
  }));
304
309
  }
305
310
  else {
306
- const planned = levelData?.constructionDimensionsByReference[wallDimensionReference].get(node.id);
311
+ const planned = levelData?.constructionDimensions.get(node.id);
307
312
  if (planned) {
308
- children.push(...renderPlannedConstructionDimensions(planned, view?.unit ?? 'metric', dimensionStroke, documentMode ? 'document' : 'editor', dimensionStandard));
313
+ children.push(...renderPlannedConstructionDimensions(planned.filter((entry) => measuresFace(wallDimensionFaces, constructionDimensionFace(entry.tier))), view?.unit ?? 'metric', dimensionStroke, documentMode ? 'document' : 'editor', dimensionStandard));
309
314
  }
310
- else if (!levelData) {
315
+ else if (!levelData && measuresFace(wallDimensionFaces, 'exterior')) {
316
+ // Without the level plan, only the exterior strings are drawn.
311
317
  children.push(...buildWallConstructionDimensions(self, ctx, {
312
318
  unit: view?.unit ?? 'metric',
313
319
  stroke: dimensionStroke,
@@ -353,8 +359,25 @@ export function buildWallFloorplan(node, ctx) {
353
359
  affordance: 'move-endpoint',
354
360
  payload: { wallId: node.id, endpoint: 'end' },
355
361
  });
362
+ // Pull grips — an arrow just past each end, pointing out along the wall:
363
+ // drag one to draw the next wall from that end (`startWallPull`).
364
+ for (const endpoint of ['start', 'end']) {
365
+ const seed = wallPullSeed(node, endpoint);
366
+ if (!seed)
367
+ continue;
368
+ children.push({
369
+ kind: 'move-arrow',
370
+ variant: 'pull',
371
+ point: seed.start,
372
+ angle: Math.atan2(seed.reference[1], seed.reference[0]),
373
+ affordance: WALL_PULL_AFFORDANCE,
374
+ payload: { wallId: node.id, endpoint },
375
+ title: WALL_PULL_GRIP_TITLE,
376
+ });
377
+ }
356
378
  const thicknessFrame = getWallCurveFrameAt(self, 0.5);
357
- for (const side of [1, -1]) {
379
+ // A railing's posts set its thickness (the panel's Post size): it has no thickness handles.
380
+ for (const side of railing ? [] : [1, -1]) {
358
381
  const halfVisibleThickness = getWallLocalFaceZ(self, side > 0 ? 'a' : 'b') * side;
359
382
  children.push({
360
383
  kind: 'endpoint-handle',
@@ -442,13 +465,6 @@ function buildSelectedWallHatchLines(wall, stroke) {
442
465
  }
443
466
  return lines;
444
467
  }
445
- function wallDimensionDatumPolicy(reference) {
446
- switch (reference) {
447
- case 'centerline':
448
- return 'centerline';
449
- case 'stud-faces':
450
- return 'structural-face';
451
- case 'finished-faces':
452
- return 'wall-face';
453
- }
468
+ function measuresFace(faces, face) {
469
+ return faces === 'both' || faces === face;
454
470
  }
@@ -1 +1 @@
1
- {"version":3,"file":"move-endpoint-tool.d.ts","sourceRoot":"","sources":["../../src/wall/move-endpoint-tool.tsx"],"names":[],"mappings":"AAEA,OAAO,EAcL,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAA;AA4BzB;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,QAAQ,CAAA;IACd,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAA;CAC1B,CAAA;AA6HD,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,MAAM,EAAE,kBAAkB,CAAA;CAAE,CA6hBzE,CAAA;eAkBc,oBAAoB"}
1
+ {"version":3,"file":"move-endpoint-tool.d.ts","sourceRoot":"","sources":["../../src/wall/move-endpoint-tool.tsx"],"names":[],"mappings":"AAEA,OAAO,EAcL,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAA;AA4BzB;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,QAAQ,CAAA;IACd,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAA;CAC1B,CAAA;AA6HD,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,MAAM,EAAE,kBAAkB,CAAA;CAAE,CA8hBzE,CAAA;eAkBc,oBAAoB"}
@@ -260,6 +260,7 @@ export const MoveWallEndpointTool = ({ target }) => {
260
260
  start: fixedPoint,
261
261
  angleSnap: isAngleSnapActive(),
262
262
  magnetic: isMagneticSnapActive(),
263
+ captureWallBodies: true,
263
264
  });
264
265
  const snappedPoint = snapResult.point;
265
266
  // Figma-style alignment: nudge the dragged endpoint onto another wall /
@@ -1 +1 @@
1
- {"version":3,"file":"move-tool.d.ts","sourceRoot":"","sources":["../../src/wall/move-tool.tsx"],"names":[],"mappings":"AAEA,OAAO,EAgBL,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAA;AA2HzB,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CA6hBrD,CAAA;eAEc,YAAY"}
1
+ {"version":3,"file":"move-tool.d.ts","sourceRoot":"","sources":["../../src/wall/move-tool.tsx"],"names":[],"mappings":"AAEA,OAAO,EAoBL,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAA;AAgIzB,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAunBrD,CAAA;eAEc,YAAY"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { beginSceneHistoryPauseSession, createLevelStructurePreview, emitter, getPerpendicularWallMoveAxis, getPlannedLinkedWallUpdates, planWallEndRejoins, planWallMoveJunctions, resolveMovedWallSupportSlabPatch, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
4
- import { CursorSphere, EDITOR_LAYER, getSegmentGridStep, isSegmentLongEnough, isTypingTarget, markToolCancelConsumed, snapBuildingLocalToWorldGrid, snapScalarToGrid, triggerSFX, useEditor, WALL_CONNECT_SNAP_RADIUS, } from '@pascal-app/editor';
3
+ import { beginSceneHistoryPauseSession, createLevelStructurePreview, emitter, finishWallEdit, generateId, getPerpendicularWallMoveAxis, getPlannedLinkedWallUpdates, planAttachedWallChanges, planAttachedWallUpdates, planWallEndRejoins, planWallMoveJunctions, resolveMovedWallSupportSlabPatch, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
4
+ import { CursorSphere, EDITOR_LAYER, getSegmentGridStep, isSegmentLongEnough, isTypingTarget, isWallRunFree, markToolCancelConsumed, nearestWallParallelTarget, publishWallParallelGuide, snapBuildingLocalToWorldGrid, snapScalarToGrid, triggerSFX, useEditor, WALL_CONNECT_SNAP_RADIUS, wallParallelTargets, } from '@pascal-app/editor';
5
5
  import { useViewer } from '@pascal-app/viewer';
6
6
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
7
  import { BufferGeometry, DoubleSide, Float32BufferAttribute } from 'three';
@@ -233,11 +233,43 @@ export const MoveWallTool = ({ node }) => {
233
233
  const nextEnd = [center[0] + rotatedHalf[0], center[1] + rotatedHalf[1]];
234
234
  return { start: nextStart, end: nextEnd };
235
235
  };
236
+ // The level's other walls, which the moving wall lines up with when parallel
237
+ // (`wallParallelTargets`). Recomputed when R / T turns the wall.
238
+ const levelWalls = Object.values(useScene.getState().nodes).filter((entry) => entry?.type === 'wall' && (entry.parentId ?? null) === levelId);
239
+ const movingWall = levelWalls.find((entry) => entry.id === nodeId);
240
+ let parallelTargets = null;
241
+ const getParallelTargets = (axis) => {
242
+ const rotation = pendingRotationRef.current;
243
+ if (parallelTargets?.rotation !== rotation) {
244
+ const turned = buildWallFromCenter(originalCenter);
245
+ parallelTargets = {
246
+ rotation,
247
+ targets: movingWall
248
+ ? wallParallelTargets({ ...movingWall, ...turned }, axis, levelWalls)
249
+ : [],
250
+ };
251
+ }
252
+ return parallelTargets.targets;
253
+ };
254
+ let lastLineUp = null;
255
+ // Alt moves the wall exactly with the pointer, as in the floor plan.
256
+ let altHeld = false;
257
+ const onPointerMove = (event) => {
258
+ altHeld = event.altKey;
259
+ };
236
260
  const getMovePlan = (nextStart, nextEnd) => planWallMoveJunctions(linkedOriginalsRef.current, originalStart, originalEnd, nextStart, nextEnd);
237
261
  const getLinkedPreviewUpdates = (plan, nextStart, nextEnd) => {
238
262
  const movedUpdates = getPlannedLinkedWallUpdates(plan, originalStart, originalEnd, nextStart, nextEnd);
239
263
  const movedById = new Map(movedUpdates.map((entry) => [entry.id, entry]));
240
- return linkedOriginalsRef.current.map((wall) => movedById.get(wall.id) ?? { id: wall.id, start: wall.start, end: wall.end });
264
+ const linkedIds = new Set(linkedOriginalsRef.current.map((wall) => wall.id));
265
+ // Walls T'd into this one stay on it, stretched along their own line.
266
+ const attached = isNew
267
+ ? []
268
+ : planAttachedWallUpdates(Object.values(dragNodes).filter((entry) => entry.type === 'wall' && entry.id !== nodeId && !linkedIds.has(entry.id)), originalStart, originalEnd, nextStart, nextEnd);
269
+ return [
270
+ ...linkedOriginalsRef.current.map((wall) => movedById.get(wall.id) ?? { id: wall.id, start: wall.start, end: wall.end }),
271
+ ...attached,
272
+ ];
241
273
  };
242
274
  const applyPreview = (nextStart, nextEnd) => {
243
275
  const previous = previewRef.current ?? { start: originalStart, end: originalEnd };
@@ -309,6 +341,7 @@ export const MoveWallTool = ({ node }) => {
309
341
  const rawDeltaZ = rawZ - anchor[1];
310
342
  if (!hasDraggedRef.current && Math.hypot(rawDeltaX, rawDeltaZ) < 1e-6)
311
343
  return;
344
+ const free = isWallRunFree({ altKey: altHeld });
312
345
  // When the move is axis-locked (side-handle drag), snap the wall
313
346
  // center's absolute perpendicular offset to a multiple of
314
347
  // `snapStep`. For axis-aligned walls this puts the wall on grid
@@ -320,31 +353,41 @@ export const MoveWallTool = ({ node }) => {
320
353
  // the displacement alone preserves any pre-existing off-grid
321
354
  // offset, so the wall could only ever sit at `originalCentre ±
322
355
  // k·snapStep` — never on actual grid lines. Snapping the wall's
323
- // own perpendicular coordinate fixes that.
356
+ // own perpendicular coordinate fixes that. A parallel wall the wall
357
+ // lines up with wins over the grid.
324
358
  const axis = moveAxisRef.current;
325
359
  let deltaX;
326
360
  let deltaZ;
361
+ let lineUp = null;
327
362
  if (axis) {
328
363
  const originalProj = originalCenter[0] * axis[0] + originalCenter[1] * axis[1];
329
364
  const rawProj = originalProj + rawDeltaX * axis[0] + rawDeltaZ * axis[1];
330
- const snappedProj = snapScalarToGrid(rawProj, snapStep);
365
+ lineUp = free ? null : nearestWallParallelTarget(getParallelTargets(axis), rawProj);
366
+ const snappedProj = lineUp
367
+ ? lineUp.position
368
+ : snapScalarToGrid(rawProj, free ? 0 : snapStep);
331
369
  const perpDelta = snappedProj - originalProj;
332
370
  deltaX = axis[0] * perpDelta;
333
371
  deltaZ = axis[1] * perpDelta;
334
372
  }
335
373
  else {
336
- // Snap the resulting wall center to the WORLD XZ grid (projected
337
- // back into building-local), then express the result as a delta
338
- // from the original centre. Without this, a rotated building
339
- // dragged the wall along its local axes instead of world ones.
374
+ // Snap the resulting wall center to the building's own grid lattice
375
+ // (the one the wall tool drafts on, square to a turned house), then
376
+ // express the result as a delta from the original centre.
340
377
  const targetLocal = [
341
378
  originalCenter[0] + rawDeltaX,
342
379
  originalCenter[1] + rawDeltaZ,
343
380
  ];
344
- const snappedLocal = snapBuildingLocalToWorldGrid(targetLocal, snapStep);
381
+ const snappedLocal = free
382
+ ? targetLocal
383
+ : snapBuildingLocalToWorldGrid(targetLocal, snapStep);
345
384
  deltaX = snappedLocal[0] - originalCenter[0];
346
385
  deltaZ = snappedLocal[1] - originalCenter[1];
347
386
  }
387
+ if (lineUp !== lastLineUp) {
388
+ lastLineUp = lineUp;
389
+ publishWallParallelGuide(lineUp);
390
+ }
348
391
  const constrainedGridPos = [anchor[0] + deltaX, anchor[1] + deltaZ];
349
392
  if (previousGridPosRef.current &&
350
393
  (constrainedGridPos[0] !== previousGridPosRef.current[0] ||
@@ -422,10 +465,28 @@ export const MoveWallTool = ({ node }) => {
422
465
  // The drag writes nothing to the store (the preview is live overrides), so history is
423
466
  // paused only for this drop. Keyed by the moving wall: in split view the 2D move overlay
424
467
  // co-owns the gesture, and commitStep lifts its pause too.
425
- const changes = planWallEndRejoins(sceneState.nodes, commitUpdates.map(({ id }) => id), WALL_CONNECT_SNAP_RADIUS, {
426
- update: wallUpdates,
468
+ // The moved and stretched walls and the jogs take over any stretch of a
469
+ // parallel wall they now run along; jogs on the outline are outside walls.
470
+ // Walls T'd into this one follow it, what hangs on them staying put.
471
+ const attachedChanges = isNew
472
+ ? []
473
+ : planAttachedWallChanges(sceneState.nodes, nodeId, originalStart, originalEnd, preview.start, preview.end, new Set([
474
+ ...linkedOriginalsRef.current.map((wall) => wall.id),
475
+ ...collapsedLinkedWallIds,
476
+ ]));
477
+ const changes = finishWallEdit(sceneState.nodes, planWallEndRejoins(sceneState.nodes, commitUpdates.map(({ id }) => id), WALL_CONNECT_SNAP_RADIUS, {
478
+ update: [...wallUpdates, ...attachedChanges],
427
479
  create: wallCreates,
428
480
  delete: Array.from(collapsedLinkedWallIds),
481
+ }), {
482
+ winnerIds: [
483
+ ...commitUpdates.map(({ id }) => id),
484
+ ...attachedChanges.flatMap(({ id }) => sceneState.nodes[id]?.type === 'wall' ? [id] : []),
485
+ ...wallCreates.map(({ node }) => node.id),
486
+ ],
487
+ movedWallId: nodeId,
488
+ bridgeIds: wallCreates.map(({ node }) => node.id),
489
+ mintId: () => generateId('wall'),
429
490
  });
430
491
  const drop = beginSceneHistoryPauseSession(useScene, { gesture: nodeId });
431
492
  try {
@@ -500,9 +561,12 @@ export const MoveWallTool = ({ node }) => {
500
561
  };
501
562
  emitter.on('grid:move', onGridMove);
502
563
  emitter.on('tool:cancel', onCancel);
564
+ window.addEventListener('pointermove', onPointerMove);
503
565
  window.addEventListener('pointerup', onPointerUp);
504
566
  window.addEventListener('keydown', onKeyDown);
505
567
  return () => {
568
+ if (lastLineUp)
569
+ publishWallParallelGuide(null);
506
570
  if (shouldRestoreOnCleanup) {
507
571
  // `shouldRestoreOnCleanup` is only true if neither `onPointerUp`
508
572
  // (commit branch) nor `onCancel` ran — i.e., the unmount came
@@ -517,6 +581,7 @@ export const MoveWallTool = ({ node }) => {
517
581
  }
518
582
  emitter.off('grid:move', onGridMove);
519
583
  emitter.off('tool:cancel', onCancel);
584
+ window.removeEventListener('pointermove', onPointerMove);
520
585
  window.removeEventListener('pointerup', onPointerUp);
521
586
  window.removeEventListener('keydown', onKeyDown);
522
587
  };