@pascal-app/nodes 1.0.7 → 1.1.0

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 (915) hide show
  1. package/README.md +42 -8
  2. package/dist/agent-operations/index.d.ts +5 -0
  3. package/dist/agent-operations/index.d.ts.map +1 -1
  4. package/dist/agent-operations/index.js +10 -0
  5. package/dist/agent-tools/index.d.ts +195 -0
  6. package/dist/agent-tools/index.d.ts.map +1 -1
  7. package/dist/agent-tools/index.js +82 -2
  8. package/dist/beam/definition.d.ts +64 -13
  9. package/dist/beam/definition.d.ts.map +1 -1
  10. package/dist/beam/definition.edit.d.ts +4 -0
  11. package/dist/beam/definition.edit.d.ts.map +1 -0
  12. package/dist/beam/definition.edit.js +133 -0
  13. package/dist/beam/definition.js +4 -173
  14. package/dist/beam/definition.render.d.ts +6 -0
  15. package/dist/beam/definition.render.d.ts.map +1 -0
  16. package/dist/beam/definition.render.js +32 -0
  17. package/dist/beam/floorplan.d.ts.map +1 -1
  18. package/dist/beam/floorplan.js +2 -2
  19. package/dist/beam/panel.d.ts.map +1 -1
  20. package/dist/beam/panel.js +2 -2
  21. package/dist/beam/tool.d.ts.map +1 -1
  22. package/dist/beam/tool.js +2 -2
  23. package/dist/block/definition.d.ts +67 -7
  24. package/dist/block/definition.d.ts.map +1 -1
  25. package/dist/block/definition.edit.d.ts +4 -0
  26. package/dist/block/definition.edit.d.ts.map +1 -0
  27. package/dist/block/definition.edit.js +28 -0
  28. package/dist/block/definition.js +5 -144
  29. package/dist/block/definition.render.d.ts +11 -0
  30. package/dist/block/definition.render.d.ts.map +1 -0
  31. package/dist/block/definition.render.js +120 -0
  32. package/dist/box-vent/definition.d.ts +103 -29
  33. package/dist/box-vent/definition.d.ts.map +1 -1
  34. package/dist/box-vent/definition.edit.d.ts +4 -0
  35. package/dist/box-vent/definition.edit.d.ts.map +1 -0
  36. package/dist/box-vent/definition.edit.js +151 -0
  37. package/dist/box-vent/definition.js +4 -212
  38. package/dist/box-vent/definition.render.d.ts +6 -0
  39. package/dist/box-vent/definition.render.d.ts.map +1 -0
  40. package/dist/box-vent/definition.render.js +40 -0
  41. package/dist/box-vent/move-tool.d.ts.map +1 -1
  42. package/dist/box-vent/move-tool.js +6 -5
  43. package/dist/building/definition.d.ts +49 -11
  44. package/dist/building/definition.d.ts.map +1 -1
  45. package/dist/building/definition.edit.d.ts +4 -0
  46. package/dist/building/definition.edit.d.ts.map +1 -0
  47. package/dist/building/definition.edit.js +10 -0
  48. package/dist/building/definition.js +4 -54
  49. package/dist/building/definition.render.d.ts +6 -0
  50. package/dist/building/definition.render.d.ts.map +1 -0
  51. package/dist/building/definition.render.js +38 -0
  52. package/dist/builtin-definitions.d.ts +9784 -0
  53. package/dist/builtin-definitions.d.ts.map +1 -0
  54. package/dist/builtin-definitions.js +113 -0
  55. package/dist/builtin-edit-contributions.d.ts +9608 -0
  56. package/dist/builtin-edit-contributions.d.ts.map +1 -0
  57. package/dist/builtin-edit-contributions.js +112 -0
  58. package/dist/builtin-plugin-metadata.d.ts +5 -0
  59. package/dist/builtin-plugin-metadata.d.ts.map +1 -0
  60. package/dist/builtin-plugin-metadata.js +1 -0
  61. package/dist/cabinet/definition.d.ts +497 -25
  62. package/dist/cabinet/definition.d.ts.map +1 -1
  63. package/dist/cabinet/definition.edit.d.ts +5 -0
  64. package/dist/cabinet/definition.edit.d.ts.map +1 -0
  65. package/dist/cabinet/definition.edit.js +1375 -0
  66. package/dist/cabinet/definition.js +6 -2115
  67. package/dist/cabinet/definition.render.d.ts +66 -0
  68. package/dist/cabinet/definition.render.d.ts.map +1 -0
  69. package/dist/cabinet/definition.render.js +751 -0
  70. package/dist/cabinet/kitchen-module-actions.d.ts.map +1 -1
  71. package/dist/cabinet/kitchen-module-actions.js +2 -2
  72. package/dist/cabinet/placement-dimensions.d.ts +36 -2
  73. package/dist/cabinet/placement-dimensions.d.ts.map +1 -1
  74. package/dist/cabinet/placement-dimensions.js +68 -2
  75. package/dist/cabinet/system.js +1 -1
  76. package/dist/cabinet/tool.d.ts.map +1 -1
  77. package/dist/cabinet/tool.js +268 -15
  78. package/dist/cabinet/wall-snap.d.ts +5 -1
  79. package/dist/cabinet/wall-snap.d.ts.map +1 -1
  80. package/dist/cabinet/wall-snap.js +2 -2
  81. package/dist/ceiling/definition.d.ts +152 -19
  82. package/dist/ceiling/definition.d.ts.map +1 -1
  83. package/dist/ceiling/definition.edit.d.ts +4 -0
  84. package/dist/ceiling/definition.edit.d.ts.map +1 -0
  85. package/dist/ceiling/definition.edit.js +125 -0
  86. package/dist/ceiling/definition.js +4 -207
  87. package/dist/ceiling/definition.render.d.ts +6 -0
  88. package/dist/ceiling/definition.render.d.ts.map +1 -0
  89. package/dist/ceiling/definition.render.js +71 -0
  90. package/dist/ceiling/panel.js +2 -2
  91. package/dist/chimney/definition.d.ts +186 -17
  92. package/dist/chimney/definition.d.ts.map +1 -1
  93. package/dist/chimney/definition.edit.d.ts +4 -0
  94. package/dist/chimney/definition.edit.d.ts.map +1 -0
  95. package/dist/chimney/definition.edit.js +219 -0
  96. package/dist/chimney/definition.js +4 -390
  97. package/dist/chimney/definition.render.d.ts +6 -0
  98. package/dist/chimney/definition.render.d.ts.map +1 -0
  99. package/dist/chimney/definition.render.js +59 -0
  100. package/dist/chimney/move-tool.d.ts.map +1 -1
  101. package/dist/chimney/move-tool.js +6 -4
  102. package/dist/column/__bench__/geometry.d.ts +2 -0
  103. package/dist/column/__bench__/geometry.d.ts.map +1 -0
  104. package/dist/column/__bench__/geometry.js +36 -0
  105. package/dist/column/bounds.d.ts +7 -0
  106. package/dist/column/bounds.d.ts.map +1 -0
  107. package/dist/column/bounds.js +127 -0
  108. package/dist/column/definition.d.ts +372 -20
  109. package/dist/column/definition.d.ts.map +1 -1
  110. package/dist/column/definition.edit.d.ts +4 -0
  111. package/dist/column/definition.edit.d.ts.map +1 -0
  112. package/dist/column/definition.edit.js +323 -0
  113. package/dist/column/definition.js +4 -439
  114. package/dist/column/definition.render.d.ts +6 -0
  115. package/dist/column/definition.render.d.ts.map +1 -0
  116. package/dist/column/definition.render.js +81 -0
  117. package/dist/column/edit-policy.d.ts +5 -0
  118. package/dist/column/edit-policy.d.ts.map +1 -0
  119. package/dist/column/edit-policy.js +43 -0
  120. package/dist/column/floorplan-affordances.d.ts.map +1 -1
  121. package/dist/column/floorplan-affordances.js +2 -0
  122. package/dist/column/floorplan-move.d.ts.map +1 -1
  123. package/dist/column/floorplan-move.js +5 -1
  124. package/dist/column/floorplan.d.ts.map +1 -1
  125. package/dist/column/floorplan.js +33 -91
  126. package/dist/column/geometry.d.ts +13 -0
  127. package/dist/column/geometry.d.ts.map +1 -0
  128. package/dist/column/geometry.js +294 -0
  129. package/dist/column/hosted-resize.d.ts +3 -2
  130. package/dist/column/hosted-resize.d.ts.map +1 -1
  131. package/dist/column/hosted-resize.js +7 -1
  132. package/dist/column/inspector-highlight.d.ts +29 -0
  133. package/dist/column/inspector-highlight.d.ts.map +1 -0
  134. package/dist/column/inspector-highlight.js +166 -0
  135. package/dist/column/paint.d.ts.map +1 -1
  136. package/dist/column/paint.js +12 -2
  137. package/dist/column/panel.d.ts.map +1 -1
  138. package/dist/column/panel.js +451 -363
  139. package/dist/column/parametrics.js +1 -1
  140. package/dist/column/post-section.d.ts.map +1 -1
  141. package/dist/column/post-section.js +2 -2
  142. package/dist/column/preset-gallery.d.ts +8 -0
  143. package/dist/column/preset-gallery.d.ts.map +1 -0
  144. package/dist/column/preset-gallery.js +98 -0
  145. package/dist/column/presets.d.ts +16 -0
  146. package/dist/column/presets.d.ts.map +1 -0
  147. package/dist/column/presets.js +96 -0
  148. package/dist/column/property-preview.d.ts +8 -0
  149. package/dist/column/property-preview.d.ts.map +1 -0
  150. package/dist/column/property-preview.js +50 -0
  151. package/dist/column/renderer.d.ts +13 -1
  152. package/dist/column/renderer.d.ts.map +1 -1
  153. package/dist/column/renderer.js +174 -198
  154. package/dist/column/shape.d.ts +4 -4
  155. package/dist/column/shape.d.ts.map +1 -1
  156. package/dist/column/shape.js +1 -1
  157. package/dist/column/surface.d.ts.map +1 -1
  158. package/dist/column/surface.js +15 -8
  159. package/dist/column/tool.d.ts.map +1 -1
  160. package/dist/column/tool.js +21 -10
  161. package/dist/construction-dimension/definition.d.ts +129 -3
  162. package/dist/construction-dimension/definition.d.ts.map +1 -1
  163. package/dist/construction-dimension/definition.edit.d.ts +4 -0
  164. package/dist/construction-dimension/definition.edit.d.ts.map +1 -0
  165. package/dist/construction-dimension/definition.edit.js +32 -0
  166. package/dist/construction-dimension/definition.js +4 -83
  167. package/dist/construction-dimension/definition.render.d.ts +6 -0
  168. package/dist/construction-dimension/definition.render.d.ts.map +1 -0
  169. package/dist/construction-dimension/definition.render.js +53 -0
  170. package/dist/construction-dimension/floorplan-tool.d.ts.map +1 -1
  171. package/dist/construction-dimension/floorplan-tool.js +2 -2
  172. package/dist/cupola/definition.d.ts +95 -10
  173. package/dist/cupola/definition.d.ts.map +1 -1
  174. package/dist/cupola/definition.edit.d.ts +4 -0
  175. package/dist/cupola/definition.edit.d.ts.map +1 -0
  176. package/dist/cupola/definition.edit.js +103 -0
  177. package/dist/cupola/definition.js +4 -148
  178. package/dist/cupola/definition.render.d.ts +6 -0
  179. package/dist/cupola/definition.render.d.ts.map +1 -0
  180. package/dist/cupola/definition.render.js +43 -0
  181. package/dist/cupola/move-tool.d.ts.map +1 -1
  182. package/dist/cupola/move-tool.js +6 -5
  183. package/dist/definition-contributions.d.ts +14 -0
  184. package/dist/definition-contributions.d.ts.map +1 -0
  185. package/dist/definition-contributions.js +8 -0
  186. package/dist/door/definition.d.ts +350 -22
  187. package/dist/door/definition.d.ts.map +1 -1
  188. package/dist/door/definition.edit.d.ts +4 -0
  189. package/dist/door/definition.edit.d.ts.map +1 -0
  190. package/dist/door/definition.edit.js +246 -0
  191. package/dist/door/definition.js +4 -341
  192. package/dist/door/definition.render.d.ts +6 -0
  193. package/dist/door/definition.render.d.ts.map +1 -0
  194. package/dist/door/definition.render.js +88 -0
  195. package/dist/door/floorplan-move.d.ts.map +1 -1
  196. package/dist/door/floorplan-move.js +11 -8
  197. package/dist/door/mechanism.d.ts.map +1 -1
  198. package/dist/door/mechanism.js +1 -2
  199. package/dist/door/move-tool.d.ts.map +1 -1
  200. package/dist/door/move-tool.js +25 -23
  201. package/dist/door/tool.d.ts.map +1 -1
  202. package/dist/door/tool.js +2 -1
  203. package/dist/dormer/definition.d.ts +240 -11
  204. package/dist/dormer/definition.d.ts.map +1 -1
  205. package/dist/dormer/definition.edit.d.ts +4 -0
  206. package/dist/dormer/definition.edit.d.ts.map +1 -0
  207. package/dist/dormer/definition.edit.js +214 -0
  208. package/dist/dormer/definition.js +4 -319
  209. package/dist/dormer/definition.render.d.ts +6 -0
  210. package/dist/dormer/definition.render.d.ts.map +1 -0
  211. package/dist/dormer/definition.render.js +52 -0
  212. package/dist/downspout/definition.d.ts +108 -16
  213. package/dist/downspout/definition.d.ts.map +1 -1
  214. package/dist/downspout/definition.edit.d.ts +4 -0
  215. package/dist/downspout/definition.edit.d.ts.map +1 -0
  216. package/dist/downspout/definition.edit.js +134 -0
  217. package/dist/downspout/definition.js +4 -188
  218. package/dist/downspout/definition.render.d.ts +6 -0
  219. package/dist/downspout/definition.render.d.ts.map +1 -0
  220. package/dist/downspout/definition.render.js +46 -0
  221. package/dist/duct-fitting/definition.d.ts +99 -11
  222. package/dist/duct-fitting/definition.d.ts.map +1 -1
  223. package/dist/duct-fitting/definition.edit.d.ts +4 -0
  224. package/dist/duct-fitting/definition.edit.d.ts.map +1 -0
  225. package/dist/duct-fitting/definition.edit.js +49 -0
  226. package/dist/duct-fitting/definition.js +4 -135
  227. package/dist/duct-fitting/definition.render.d.ts +6 -0
  228. package/dist/duct-fitting/definition.render.d.ts.map +1 -0
  229. package/dist/duct-fitting/definition.render.js +82 -0
  230. package/dist/duct-fitting/floorplan.js +1 -1
  231. package/dist/duct-fitting/geometry.js +2 -1
  232. package/dist/duct-fitting/move-tool.d.ts.map +1 -1
  233. package/dist/duct-fitting/move-tool.js +19 -13
  234. package/dist/duct-fitting/selection.d.ts.map +1 -1
  235. package/dist/duct-fitting/selection.js +92 -80
  236. package/dist/duct-segment/continuation.d.ts +2 -2
  237. package/dist/duct-segment/continuation.d.ts.map +1 -1
  238. package/dist/duct-segment/continuation.js +53 -2
  239. package/dist/duct-segment/definition.d.ts +91 -3
  240. package/dist/duct-segment/definition.d.ts.map +1 -1
  241. package/dist/duct-segment/definition.edit.d.ts +4 -0
  242. package/dist/duct-segment/definition.edit.d.ts.map +1 -0
  243. package/dist/duct-segment/definition.edit.js +100 -0
  244. package/dist/duct-segment/definition.js +4 -181
  245. package/dist/duct-segment/definition.render.d.ts +6 -0
  246. package/dist/duct-segment/definition.render.d.ts.map +1 -0
  247. package/dist/duct-segment/definition.render.js +89 -0
  248. package/dist/duct-segment/edit-inspector.d.ts +5 -0
  249. package/dist/duct-segment/edit-inspector.d.ts.map +1 -0
  250. package/dist/duct-segment/edit-inspector.js +163 -0
  251. package/dist/duct-segment/edit-operation.d.ts +17 -0
  252. package/dist/duct-segment/edit-operation.d.ts.map +1 -0
  253. package/dist/duct-segment/edit-operation.js +283 -0
  254. package/dist/duct-segment/floorplan.js +1 -1
  255. package/dist/duct-segment/geometry.d.ts +0 -40
  256. package/dist/duct-segment/geometry.d.ts.map +1 -1
  257. package/dist/duct-segment/geometry.js +2 -77
  258. package/dist/duct-segment/move-tool.d.ts.map +1 -1
  259. package/dist/duct-segment/move-tool.js +34 -35
  260. package/dist/duct-segment/parametrics.d.ts.map +1 -1
  261. package/dist/duct-segment/parametrics.js +2 -35
  262. package/dist/duct-segment/profile.d.ts +42 -0
  263. package/dist/duct-segment/profile.d.ts.map +1 -0
  264. package/dist/duct-segment/profile.js +78 -0
  265. package/dist/duct-segment/selection.d.ts.map +1 -1
  266. package/dist/duct-segment/selection.js +112 -53
  267. package/dist/duct-segment/tool.d.ts.map +1 -1
  268. package/dist/duct-segment/tool.js +103 -27
  269. package/dist/duct-terminal/definition.d.ts +68 -12
  270. package/dist/duct-terminal/definition.d.ts.map +1 -1
  271. package/dist/duct-terminal/definition.edit.d.ts +4 -0
  272. package/dist/duct-terminal/definition.edit.d.ts.map +1 -0
  273. package/dist/duct-terminal/definition.edit.js +14 -0
  274. package/dist/duct-terminal/definition.js +4 -87
  275. package/dist/duct-terminal/definition.render.d.ts +6 -0
  276. package/dist/duct-terminal/definition.render.d.ts.map +1 -0
  277. package/dist/duct-terminal/definition.render.js +66 -0
  278. package/dist/duct-terminal/geometry.d.ts.map +1 -1
  279. package/dist/duct-terminal/geometry.js +2 -1
  280. package/dist/duct-terminal/parametrics.d.ts.map +1 -1
  281. package/dist/duct-terminal/parametrics.js +1 -0
  282. package/dist/duct-terminal/ports.js +1 -1
  283. package/dist/elevator/__bench__/runtime.d.ts +2 -0
  284. package/dist/elevator/__bench__/runtime.d.ts.map +1 -0
  285. package/dist/elevator/__bench__/runtime.js +38 -0
  286. package/dist/elevator/definition.d.ts +117 -10
  287. package/dist/elevator/definition.d.ts.map +1 -1
  288. package/dist/elevator/definition.edit.d.ts +4 -0
  289. package/dist/elevator/definition.edit.d.ts.map +1 -0
  290. package/dist/elevator/definition.edit.js +177 -0
  291. package/dist/elevator/definition.js +4 -258
  292. package/dist/elevator/definition.render.d.ts +11 -0
  293. package/dist/elevator/definition.render.d.ts.map +1 -0
  294. package/dist/elevator/definition.render.js +96 -0
  295. package/dist/elevator/floorplan-affordances.d.ts.map +1 -1
  296. package/dist/elevator/floorplan-affordances.js +11 -9
  297. package/dist/elevator/floorplan.d.ts +19 -0
  298. package/dist/elevator/floorplan.d.ts.map +1 -1
  299. package/dist/elevator/floorplan.js +3 -9
  300. package/dist/elevator/inspection.d.ts +17 -0
  301. package/dist/elevator/inspection.d.ts.map +1 -0
  302. package/dist/elevator/inspection.js +78 -0
  303. package/dist/elevator/panel.d.ts.map +1 -1
  304. package/dist/elevator/panel.js +152 -100
  305. package/dist/elevator/parametrics.d.ts +1 -1
  306. package/dist/elevator/parametrics.d.ts.map +1 -1
  307. package/dist/elevator/parametrics.js +7 -10
  308. package/dist/elevator/presets.d.ts +17 -0
  309. package/dist/elevator/presets.d.ts.map +1 -0
  310. package/dist/elevator/presets.js +52 -0
  311. package/dist/elevator/renderer.d.ts +2 -1
  312. package/dist/elevator/renderer.d.ts.map +1 -1
  313. package/dist/elevator/renderer.edit.d.ts +5 -0
  314. package/dist/elevator/renderer.edit.d.ts.map +1 -0
  315. package/dist/elevator/renderer.edit.js +8 -0
  316. package/dist/elevator/renderer.js +159 -60
  317. package/dist/elevator/tool.d.ts +2 -0
  318. package/dist/elevator/tool.d.ts.map +1 -0
  319. package/dist/elevator/tool.js +250 -0
  320. package/dist/eyebrow-vent/definition.d.ts +97 -11
  321. package/dist/eyebrow-vent/definition.d.ts.map +1 -1
  322. package/dist/eyebrow-vent/definition.edit.d.ts +4 -0
  323. package/dist/eyebrow-vent/definition.edit.d.ts.map +1 -0
  324. package/dist/eyebrow-vent/definition.edit.js +106 -0
  325. package/dist/eyebrow-vent/definition.js +4 -152
  326. package/dist/eyebrow-vent/definition.render.d.ts +6 -0
  327. package/dist/eyebrow-vent/definition.render.d.ts.map +1 -0
  328. package/dist/eyebrow-vent/definition.render.js +43 -0
  329. package/dist/eyebrow-vent/move-tool.d.ts.map +1 -1
  330. package/dist/eyebrow-vent/move-tool.js +6 -5
  331. package/dist/fence/definition.d.ts +186 -17
  332. package/dist/fence/definition.d.ts.map +1 -1
  333. package/dist/fence/definition.edit.d.ts +4 -0
  334. package/dist/fence/definition.edit.d.ts.map +1 -0
  335. package/dist/fence/definition.edit.js +282 -0
  336. package/dist/fence/definition.js +5 -413
  337. package/dist/fence/definition.render.d.ts +8 -0
  338. package/dist/fence/definition.render.d.ts.map +1 -0
  339. package/dist/fence/definition.render.js +124 -0
  340. package/dist/fence/move-endpoint-tool.d.ts.map +1 -1
  341. package/dist/fence/move-endpoint-tool.js +2 -2
  342. package/dist/fence/tool.js +2 -2
  343. package/dist/fence-feature/definition.d.ts +270 -3
  344. package/dist/fence-feature/definition.d.ts.map +1 -1
  345. package/dist/fence-feature/definition.edit.d.ts +5 -0
  346. package/dist/fence-feature/definition.edit.d.ts.map +1 -0
  347. package/dist/fence-feature/definition.edit.js +69 -0
  348. package/dist/fence-feature/definition.js +5 -116
  349. package/dist/fence-feature/definition.render.d.ts +9 -0
  350. package/dist/fence-feature/definition.render.d.ts.map +1 -0
  351. package/dist/fence-feature/definition.render.js +69 -0
  352. package/dist/floor-opening/definition.d.ts +58 -2
  353. package/dist/floor-opening/definition.d.ts.map +1 -1
  354. package/dist/floor-opening/definition.edit.d.ts +4 -0
  355. package/dist/floor-opening/definition.edit.d.ts.map +1 -0
  356. package/dist/floor-opening/definition.edit.js +11 -0
  357. package/dist/floor-opening/definition.js +4 -41
  358. package/dist/floor-opening/definition.render.d.ts +6 -0
  359. package/dist/floor-opening/definition.render.d.ts.map +1 -0
  360. package/dist/floor-opening/definition.render.js +32 -0
  361. package/dist/guide/definition.d.ts +65 -9
  362. package/dist/guide/definition.d.ts.map +1 -1
  363. package/dist/guide/definition.edit.d.ts +4 -0
  364. package/dist/guide/definition.edit.d.ts.map +1 -0
  365. package/dist/guide/definition.edit.js +4 -0
  366. package/dist/guide/definition.js +4 -53
  367. package/dist/guide/definition.render.d.ts +6 -0
  368. package/dist/guide/definition.render.d.ts.map +1 -0
  369. package/dist/guide/definition.render.js +45 -0
  370. package/dist/gutter/definition.d.ts +113 -18
  371. package/dist/gutter/definition.d.ts.map +1 -1
  372. package/dist/gutter/definition.edit.d.ts +4 -0
  373. package/dist/gutter/definition.edit.d.ts.map +1 -0
  374. package/dist/gutter/definition.edit.js +121 -0
  375. package/dist/gutter/definition.js +4 -176
  376. package/dist/gutter/definition.render.d.ts +6 -0
  377. package/dist/gutter/definition.render.d.ts.map +1 -0
  378. package/dist/gutter/definition.render.js +44 -0
  379. package/dist/gutter/move-tool.d.ts.map +1 -1
  380. package/dist/gutter/move-tool.js +5 -4
  381. package/dist/hvac-equipment/definition.d.ts +72 -12
  382. package/dist/hvac-equipment/definition.d.ts.map +1 -1
  383. package/dist/hvac-equipment/definition.edit.d.ts +4 -0
  384. package/dist/hvac-equipment/definition.edit.d.ts.map +1 -0
  385. package/dist/hvac-equipment/definition.edit.js +13 -0
  386. package/dist/hvac-equipment/definition.js +4 -92
  387. package/dist/hvac-equipment/definition.render.d.ts +6 -0
  388. package/dist/hvac-equipment/definition.render.d.ts.map +1 -0
  389. package/dist/hvac-equipment/definition.render.js +72 -0
  390. package/dist/hvac-equipment/floorplan.js +1 -1
  391. package/dist/hvac-equipment/geometry.d.ts.map +1 -1
  392. package/dist/hvac-equipment/geometry.js +2 -1
  393. package/dist/hvac-equipment/parametrics.d.ts.map +1 -1
  394. package/dist/hvac-equipment/parametrics.js +1 -0
  395. package/dist/hvac-equipment/ports.js +1 -1
  396. package/dist/i18n/definition-messages.js +1 -1
  397. package/dist/imported-mesh/definition.d.ts +54 -4
  398. package/dist/imported-mesh/definition.d.ts.map +1 -1
  399. package/dist/imported-mesh/definition.edit.d.ts +4 -0
  400. package/dist/imported-mesh/definition.edit.d.ts.map +1 -0
  401. package/dist/imported-mesh/definition.edit.js +5 -0
  402. package/dist/imported-mesh/definition.js +4 -40
  403. package/dist/imported-mesh/definition.render.d.ts +6 -0
  404. package/dist/imported-mesh/definition.render.d.ts.map +1 -0
  405. package/dist/imported-mesh/definition.render.js +36 -0
  406. package/dist/index.d.ts +3 -16
  407. package/dist/index.d.ts.map +1 -1
  408. package/dist/index.js +9 -136
  409. package/dist/item/definition.d.ts +280 -31
  410. package/dist/item/definition.d.ts.map +1 -1
  411. package/dist/item/definition.edit.d.ts +4 -0
  412. package/dist/item/definition.edit.d.ts.map +1 -0
  413. package/dist/item/definition.edit.js +206 -0
  414. package/dist/item/definition.js +4 -356
  415. package/dist/item/definition.render.d.ts +6 -0
  416. package/dist/item/definition.render.d.ts.map +1 -0
  417. package/dist/item/definition.render.js +128 -0
  418. package/dist/item/floorplan.d.ts.map +1 -1
  419. package/dist/item/floorplan.js +2 -2
  420. package/dist/item/renderer.d.ts.map +1 -1
  421. package/dist/item/renderer.js +2 -2
  422. package/dist/lean-to-extension/definition.d.ts +177 -3
  423. package/dist/lean-to-extension/definition.d.ts.map +1 -1
  424. package/dist/lean-to-extension/definition.edit.d.ts +4 -0
  425. package/dist/lean-to-extension/definition.edit.d.ts.map +1 -0
  426. package/dist/lean-to-extension/definition.edit.js +392 -0
  427. package/dist/lean-to-extension/definition.js +4 -433
  428. package/dist/lean-to-extension/definition.render.d.ts +6 -0
  429. package/dist/lean-to-extension/definition.render.d.ts.map +1 -0
  430. package/dist/lean-to-extension/definition.render.js +46 -0
  431. package/dist/level/definition.d.ts +49 -8
  432. package/dist/level/definition.d.ts.map +1 -1
  433. package/dist/level/definition.edit.d.ts +4 -0
  434. package/dist/level/definition.edit.d.ts.map +1 -0
  435. package/dist/level/definition.edit.js +4 -0
  436. package/dist/level/definition.js +4 -60
  437. package/dist/level/definition.render.d.ts +6 -0
  438. package/dist/level/definition.render.d.ts.map +1 -0
  439. package/dist/level/definition.render.js +53 -0
  440. package/dist/lineset/definition.d.ts +49 -14
  441. package/dist/lineset/definition.d.ts.map +1 -1
  442. package/dist/lineset/definition.edit.d.ts +4 -0
  443. package/dist/lineset/definition.edit.d.ts.map +1 -0
  444. package/dist/lineset/definition.edit.js +38 -0
  445. package/dist/lineset/definition.js +4 -119
  446. package/dist/lineset/definition.render.d.ts +6 -0
  447. package/dist/lineset/definition.render.d.ts.map +1 -0
  448. package/dist/lineset/definition.render.js +72 -0
  449. package/dist/lineset/floorplan.js +1 -1
  450. package/dist/lineset/geometry.js +1 -1
  451. package/dist/lineset/move-tool.d.ts.map +1 -1
  452. package/dist/lineset/move-tool.js +14 -7
  453. package/dist/liquid-line/definition.d.ts +47 -15
  454. package/dist/liquid-line/definition.d.ts.map +1 -1
  455. package/dist/liquid-line/definition.edit.d.ts +4 -0
  456. package/dist/liquid-line/definition.edit.d.ts.map +1 -0
  457. package/dist/liquid-line/definition.edit.js +31 -0
  458. package/dist/liquid-line/definition.js +4 -111
  459. package/dist/liquid-line/definition.render.d.ts +6 -0
  460. package/dist/liquid-line/definition.render.d.ts.map +1 -0
  461. package/dist/liquid-line/definition.render.js +70 -0
  462. package/dist/liquid-line/floorplan.js +1 -1
  463. package/dist/liquid-line/geometry.js +1 -1
  464. package/dist/measurement/definition.d.ts +126 -3
  465. package/dist/measurement/definition.d.ts.map +1 -1
  466. package/dist/measurement/definition.edit.d.ts +4 -0
  467. package/dist/measurement/definition.edit.d.ts.map +1 -0
  468. package/dist/measurement/definition.edit.js +26 -0
  469. package/dist/measurement/definition.js +4 -67
  470. package/dist/measurement/definition.render.d.ts +6 -0
  471. package/dist/measurement/definition.render.d.ts.map +1 -0
  472. package/dist/measurement/definition.render.js +43 -0
  473. package/dist/measurement/floorplan.d.ts.map +1 -1
  474. package/dist/measurement/floorplan.js +3 -2
  475. package/dist/measurement/renderer.d.ts.map +1 -1
  476. package/dist/measurement/renderer.js +2 -3
  477. package/dist/measurement/selection.d.ts.map +1 -1
  478. package/dist/measurement/selection.js +2 -2
  479. package/dist/measurement/tool.d.ts +2 -2
  480. package/dist/measurement/tool.d.ts.map +1 -1
  481. package/dist/measurement/tool.js +3 -3
  482. package/dist/pipe-fitting/definition.d.ts +73 -9
  483. package/dist/pipe-fitting/definition.d.ts.map +1 -1
  484. package/dist/pipe-fitting/definition.edit.d.ts +4 -0
  485. package/dist/pipe-fitting/definition.edit.d.ts.map +1 -0
  486. package/dist/pipe-fitting/definition.edit.js +47 -0
  487. package/dist/pipe-fitting/definition.js +4 -105
  488. package/dist/pipe-fitting/definition.render.d.ts +6 -0
  489. package/dist/pipe-fitting/definition.render.d.ts.map +1 -0
  490. package/dist/pipe-fitting/definition.render.js +54 -0
  491. package/dist/pipe-fitting/floorplan.js +1 -1
  492. package/dist/pipe-fitting/geometry.js +1 -1
  493. package/dist/pipe-fitting/move-tool.d.ts +1 -1
  494. package/dist/pipe-fitting/move-tool.d.ts.map +1 -1
  495. package/dist/pipe-fitting/move-tool.js +28 -22
  496. package/dist/pipe-fitting/parametrics.d.ts.map +1 -1
  497. package/dist/pipe-fitting/parametrics.js +1 -0
  498. package/dist/pipe-fitting/ports.js +1 -1
  499. package/dist/pipe-fitting/selection.d.ts.map +1 -1
  500. package/dist/pipe-fitting/selection.js +73 -29
  501. package/dist/pipe-segment/collar-connection.d.ts +12 -0
  502. package/dist/pipe-segment/collar-connection.d.ts.map +1 -0
  503. package/dist/pipe-segment/collar-connection.js +58 -0
  504. package/dist/pipe-segment/connect-operation.d.ts +9 -0
  505. package/dist/pipe-segment/connect-operation.d.ts.map +1 -0
  506. package/dist/pipe-segment/connect-operation.js +44 -0
  507. package/dist/pipe-segment/continuation.d.ts +3 -2
  508. package/dist/pipe-segment/continuation.d.ts.map +1 -1
  509. package/dist/pipe-segment/continuation.js +60 -1
  510. package/dist/pipe-segment/definition.d.ts +78 -13
  511. package/dist/pipe-segment/definition.d.ts.map +1 -1
  512. package/dist/pipe-segment/definition.edit.d.ts +4 -0
  513. package/dist/pipe-segment/definition.edit.d.ts.map +1 -0
  514. package/dist/pipe-segment/definition.edit.js +48 -0
  515. package/dist/pipe-segment/definition.js +4 -132
  516. package/dist/pipe-segment/definition.render.d.ts +6 -0
  517. package/dist/pipe-segment/definition.render.d.ts.map +1 -0
  518. package/dist/pipe-segment/definition.render.js +78 -0
  519. package/dist/pipe-segment/floorplan.d.ts +1 -1
  520. package/dist/pipe-segment/floorplan.d.ts.map +1 -1
  521. package/dist/pipe-segment/floorplan.js +34 -1
  522. package/dist/pipe-segment/geometry.d.ts.map +1 -1
  523. package/dist/pipe-segment/geometry.js +2 -1
  524. package/dist/pipe-segment/grade-affordance.d.ts +8 -0
  525. package/dist/pipe-segment/grade-affordance.d.ts.map +1 -0
  526. package/dist/pipe-segment/grade-affordance.js +116 -0
  527. package/dist/pipe-segment/grade-inspector.d.ts +5 -0
  528. package/dist/pipe-segment/grade-inspector.d.ts.map +1 -0
  529. package/dist/pipe-segment/grade-inspector.js +91 -0
  530. package/dist/pipe-segment/grade-label.d.ts +7 -0
  531. package/dist/pipe-segment/grade-label.d.ts.map +1 -0
  532. package/dist/pipe-segment/grade-label.js +32 -0
  533. package/dist/pipe-segment/grade-operation.d.ts +22 -0
  534. package/dist/pipe-segment/grade-operation.d.ts.map +1 -0
  535. package/dist/pipe-segment/grade-operation.js +114 -0
  536. package/dist/pipe-segment/move-tool.d.ts.map +1 -1
  537. package/dist/pipe-segment/move-tool.js +38 -15
  538. package/dist/pipe-segment/offset-inspector.d.ts +5 -0
  539. package/dist/pipe-segment/offset-inspector.d.ts.map +1 -0
  540. package/dist/pipe-segment/offset-inspector.js +112 -0
  541. package/dist/pipe-segment/offset-operation.d.ts +9 -0
  542. package/dist/pipe-segment/offset-operation.d.ts.map +1 -0
  543. package/dist/pipe-segment/offset-operation.js +104 -0
  544. package/dist/pipe-segment/parametrics.d.ts.map +1 -1
  545. package/dist/pipe-segment/parametrics.js +1 -9
  546. package/dist/pipe-segment/resize-inspector.d.ts +5 -0
  547. package/dist/pipe-segment/resize-inspector.d.ts.map +1 -0
  548. package/dist/pipe-segment/resize-inspector.js +75 -0
  549. package/dist/pipe-segment/resize-operation.d.ts +8 -0
  550. package/dist/pipe-segment/resize-operation.d.ts.map +1 -0
  551. package/dist/pipe-segment/resize-operation.js +143 -0
  552. package/dist/pipe-segment/run-move.d.ts +65 -0
  553. package/dist/pipe-segment/run-move.d.ts.map +1 -0
  554. package/dist/pipe-segment/run-move.js +110 -0
  555. package/dist/pipe-segment/selection.d.ts.map +1 -1
  556. package/dist/pipe-segment/selection.js +122 -178
  557. package/dist/pipe-segment/slope.d.ts +3 -0
  558. package/dist/pipe-segment/slope.d.ts.map +1 -1
  559. package/dist/pipe-segment/slope.js +47 -0
  560. package/dist/pipe-segment/tool.d.ts.map +1 -1
  561. package/dist/pipe-segment/tool.js +123 -28
  562. package/dist/pipe-trap/definition.d.ts +54 -9
  563. package/dist/pipe-trap/definition.d.ts.map +1 -1
  564. package/dist/pipe-trap/definition.edit.d.ts +4 -0
  565. package/dist/pipe-trap/definition.edit.d.ts.map +1 -0
  566. package/dist/pipe-trap/definition.edit.js +13 -0
  567. package/dist/pipe-trap/definition.js +4 -59
  568. package/dist/pipe-trap/definition.render.d.ts +6 -0
  569. package/dist/pipe-trap/definition.render.d.ts.map +1 -0
  570. package/dist/pipe-trap/definition.render.js +43 -0
  571. package/dist/pipe-trap/geometry.d.ts.map +1 -1
  572. package/dist/pipe-trap/geometry.js +2 -1
  573. package/dist/pipe-trap/parametrics.d.ts.map +1 -1
  574. package/dist/pipe-trap/parametrics.js +1 -0
  575. package/dist/procedural-item/definition.d.ts +734 -3
  576. package/dist/procedural-item/definition.d.ts.map +1 -1
  577. package/dist/procedural-item/definition.edit.d.ts +4 -0
  578. package/dist/procedural-item/definition.edit.d.ts.map +1 -0
  579. package/dist/procedural-item/definition.edit.js +255 -0
  580. package/dist/procedural-item/definition.js +4 -325
  581. package/dist/procedural-item/definition.render.d.ts +6 -0
  582. package/dist/procedural-item/definition.render.d.ts.map +1 -0
  583. package/dist/procedural-item/definition.render.js +84 -0
  584. package/dist/procedural-item/renderer.d.ts.map +1 -1
  585. package/dist/procedural-item/renderer.edit.d.ts +5 -0
  586. package/dist/procedural-item/renderer.edit.d.ts.map +1 -0
  587. package/dist/procedural-item/renderer.edit.js +49 -0
  588. package/dist/procedural-item/renderer.js +4 -21
  589. package/dist/render.d.ts +4 -0
  590. package/dist/render.d.ts.map +1 -0
  591. package/dist/render.js +4 -0
  592. package/dist/ridge-vent/definition.d.ts +95 -15
  593. package/dist/ridge-vent/definition.d.ts.map +1 -1
  594. package/dist/ridge-vent/definition.edit.d.ts +4 -0
  595. package/dist/ridge-vent/definition.edit.d.ts.map +1 -0
  596. package/dist/ridge-vent/definition.edit.js +148 -0
  597. package/dist/ridge-vent/definition.js +4 -197
  598. package/dist/ridge-vent/definition.render.d.ts +6 -0
  599. package/dist/ridge-vent/definition.render.d.ts.map +1 -0
  600. package/dist/ridge-vent/definition.render.js +42 -0
  601. package/dist/ridge-vent/move-tool.d.ts.map +1 -1
  602. package/dist/ridge-vent/move-tool.js +5 -4
  603. package/dist/roof/barrel-panel.d.ts.map +1 -1
  604. package/dist/roof/barrel-panel.js +2 -2
  605. package/dist/roof/definition.d.ts +325 -8
  606. package/dist/roof/definition.d.ts.map +1 -1
  607. package/dist/roof/definition.edit.d.ts +4 -0
  608. package/dist/roof/definition.edit.d.ts.map +1 -0
  609. package/dist/roof/definition.edit.js +183 -0
  610. package/dist/roof/definition.js +4 -284
  611. package/dist/roof/definition.render.d.ts +7 -0
  612. package/dist/roof/definition.render.d.ts.map +1 -0
  613. package/dist/roof/definition.render.js +101 -0
  614. package/dist/roof/geometry-notice.d.ts +5 -0
  615. package/dist/roof/geometry-notice.d.ts.map +1 -0
  616. package/dist/roof/geometry-notice.js +27 -0
  617. package/dist/roof/glazing-controls.d.ts.map +1 -1
  618. package/dist/roof/glazing-controls.js +2 -2
  619. package/dist/roof/panel.d.ts.map +1 -1
  620. package/dist/roof/panel.js +4 -3
  621. package/dist/roof/tool.d.ts +3 -0
  622. package/dist/roof/tool.d.ts.map +1 -1
  623. package/dist/roof/tool.js +56 -170
  624. package/dist/roof/type-thumbnail-assets.d.ts +3 -0
  625. package/dist/roof/type-thumbnail-assets.d.ts.map +1 -0
  626. package/dist/roof/type-thumbnail-assets.js +12 -0
  627. package/dist/roof/type-thumbnail.d.ts +5 -0
  628. package/dist/roof/type-thumbnail.d.ts.map +1 -0
  629. package/dist/roof/type-thumbnail.js +5 -0
  630. package/dist/roof-segment/definition.d.ts +564 -9
  631. package/dist/roof-segment/definition.d.ts.map +1 -1
  632. package/dist/roof-segment/definition.edit.d.ts +4 -0
  633. package/dist/roof-segment/definition.edit.d.ts.map +1 -0
  634. package/dist/roof-segment/definition.edit.js +305 -0
  635. package/dist/roof-segment/definition.js +4 -364
  636. package/dist/roof-segment/definition.render.d.ts +6 -0
  637. package/dist/roof-segment/definition.render.d.ts.map +1 -0
  638. package/dist/roof-segment/definition.render.js +59 -0
  639. package/dist/roof-segment/floorplan-affordances.d.ts.map +1 -1
  640. package/dist/roof-segment/floorplan-affordances.js +2 -2
  641. package/dist/roof-segment/paint.d.ts +0 -1
  642. package/dist/roof-segment/paint.d.ts.map +1 -1
  643. package/dist/roof-segment/paint.js +0 -1
  644. package/dist/roof-segment/panel.d.ts.map +1 -1
  645. package/dist/roof-segment/panel.js +32 -34
  646. package/dist/roof-segment/parametrics.d.ts.map +1 -1
  647. package/dist/roof-segment/parametrics.js +13 -11
  648. package/dist/roof-segment/slots.d.ts +2 -0
  649. package/dist/roof-segment/slots.d.ts.map +1 -0
  650. package/dist/roof-segment/slots.js +1 -0
  651. package/dist/scan/definition.d.ts +57 -7
  652. package/dist/scan/definition.d.ts.map +1 -1
  653. package/dist/scan/definition.edit.d.ts +4 -0
  654. package/dist/scan/definition.edit.d.ts.map +1 -0
  655. package/dist/scan/definition.edit.js +4 -0
  656. package/dist/scan/definition.js +4 -52
  657. package/dist/scan/definition.render.d.ts +6 -0
  658. package/dist/scan/definition.render.d.ts.map +1 -0
  659. package/dist/scan/definition.render.js +46 -0
  660. package/dist/separator/definition.d.ts +47 -2
  661. package/dist/separator/definition.d.ts.map +1 -1
  662. package/dist/separator/definition.edit.d.ts +4 -0
  663. package/dist/separator/definition.edit.d.ts.map +1 -0
  664. package/dist/separator/definition.edit.js +13 -0
  665. package/dist/separator/definition.js +4 -37
  666. package/dist/separator/definition.render.d.ts +6 -0
  667. package/dist/separator/definition.render.d.ts.map +1 -0
  668. package/dist/separator/definition.render.js +27 -0
  669. package/dist/separator/renderer.d.ts +2 -1
  670. package/dist/separator/renderer.d.ts.map +1 -1
  671. package/dist/separator/renderer.edit.d.ts +5 -0
  672. package/dist/separator/renderer.edit.d.ts.map +1 -0
  673. package/dist/separator/renderer.edit.js +8 -0
  674. package/dist/separator/renderer.js +3 -5
  675. package/dist/separator/selection.d.ts.map +1 -1
  676. package/dist/separator/selection.js +2 -2
  677. package/dist/shared/accessory-placement.js +1 -1
  678. package/dist/shared/auto-fitting.js +1 -1
  679. package/dist/shared/auto-offset-tag.d.ts +1 -1
  680. package/dist/shared/auto-offset-tag.d.ts.map +1 -1
  681. package/dist/shared/auto-offset-tag.js +3 -1
  682. package/dist/shared/automatic-run-end-cap.d.ts +5 -0
  683. package/dist/shared/automatic-run-end-cap.d.ts.map +1 -1
  684. package/dist/shared/automatic-run-end-cap.js +17 -9
  685. package/dist/shared/connection-feedback.d.ts +6 -3
  686. package/dist/shared/connection-feedback.d.ts.map +1 -1
  687. package/dist/shared/connection-feedback.js +29 -6
  688. package/dist/shared/curtain-opening-limits.d.ts.map +1 -1
  689. package/dist/shared/curtain-opening-limits.js +2 -3
  690. package/dist/shared/draft-axis-guides.d.ts.map +1 -1
  691. package/dist/shared/draft-axis-guides.js +2 -1
  692. package/dist/shared/duct-adapter.js +1 -1
  693. package/dist/shared/duct-collar-inspector.d.ts +5 -0
  694. package/dist/shared/duct-collar-inspector.d.ts.map +1 -0
  695. package/dist/shared/duct-collar-inspector.js +36 -0
  696. package/dist/shared/follower-frames.d.ts +15 -0
  697. package/dist/shared/follower-frames.d.ts.map +1 -0
  698. package/dist/shared/follower-frames.js +36 -0
  699. package/dist/shared/handle-drag-scope.d.ts +14 -0
  700. package/dist/shared/handle-drag-scope.d.ts.map +1 -0
  701. package/dist/shared/handle-drag-scope.js +36 -0
  702. package/dist/shared/hosted-resize.d.ts +3 -1
  703. package/dist/shared/hosted-resize.d.ts.map +1 -1
  704. package/dist/shared/hosted-resize.js +10 -8
  705. package/dist/shared/live-preview.d.ts +16 -0
  706. package/dist/shared/live-preview.d.ts.map +1 -0
  707. package/dist/shared/live-preview.js +36 -0
  708. package/dist/shared/opening-guides-runtime.d.ts.map +1 -1
  709. package/dist/shared/opening-guides-runtime.js +2 -2
  710. package/dist/shared/opening-placement-dimensions.d.ts.map +1 -1
  711. package/dist/shared/opening-placement-dimensions.js +1 -1
  712. package/dist/shared/path-point-affordance.d.ts.map +1 -1
  713. package/dist/shared/path-point-affordance.js +19 -24
  714. package/dist/shared/path-segment-affordance.d.ts +2 -1
  715. package/dist/shared/path-segment-affordance.d.ts.map +1 -1
  716. package/dist/shared/path-segment-affordance.js +11 -8
  717. package/dist/shared/pipe-collar-inspector.d.ts +5 -0
  718. package/dist/shared/pipe-collar-inspector.d.ts.map +1 -0
  719. package/dist/shared/pipe-collar-inspector.js +27 -0
  720. package/dist/shared/refrigerant-line-selection.d.ts.map +1 -1
  721. package/dist/shared/refrigerant-line-selection.js +46 -29
  722. package/dist/shared/relative-roof-drag.d.ts.map +1 -1
  723. package/dist/shared/relative-roof-drag.js +5 -4
  724. package/dist/shared/roof-surface.d.ts.map +1 -1
  725. package/dist/shared/roof-surface.js +2 -0
  726. package/dist/shared/run-hangers.d.ts.map +1 -1
  727. package/dist/shared/run-hangers.js +2 -2
  728. package/dist/shared/run-move-connectivity.d.ts +15 -6
  729. package/dist/shared/run-move-connectivity.d.ts.map +1 -1
  730. package/dist/shared/run-move-connectivity.js +14 -5
  731. package/dist/shared/run-tool-options.d.ts +8 -0
  732. package/dist/shared/run-tool-options.d.ts.map +1 -0
  733. package/dist/shared/run-tool-options.js +166 -0
  734. package/dist/shared/system-checks.d.ts.map +1 -1
  735. package/dist/shared/system-checks.js +2 -1
  736. package/dist/shared/vertical-offset.js +1 -1
  737. package/dist/shared/wall-opening-ceiling.d.ts +6 -0
  738. package/dist/shared/wall-opening-ceiling.d.ts.map +1 -1
  739. package/dist/shared/wall-opening-ceiling.js +36 -7
  740. package/dist/shelf/definition.d.ts +108 -3
  741. package/dist/shelf/definition.d.ts.map +1 -1
  742. package/dist/shelf/definition.edit.d.ts +4 -0
  743. package/dist/shelf/definition.edit.d.ts.map +1 -0
  744. package/dist/shelf/definition.edit.js +155 -0
  745. package/dist/shelf/definition.js +4 -272
  746. package/dist/shelf/definition.render.d.ts +6 -0
  747. package/dist/shelf/definition.render.d.ts.map +1 -0
  748. package/dist/shelf/definition.render.js +122 -0
  749. package/dist/site/definition.d.ts +312 -9
  750. package/dist/site/definition.d.ts.map +1 -1
  751. package/dist/site/definition.edit.d.ts +4 -0
  752. package/dist/site/definition.edit.d.ts.map +1 -0
  753. package/dist/site/definition.edit.js +4 -0
  754. package/dist/site/definition.js +4 -55
  755. package/dist/site/definition.render.d.ts +6 -0
  756. package/dist/site/definition.render.d.ts.map +1 -0
  757. package/dist/site/definition.render.js +47 -0
  758. package/dist/skylight/definition.d.ts +158 -9
  759. package/dist/skylight/definition.d.ts.map +1 -1
  760. package/dist/skylight/definition.edit.d.ts +4 -0
  761. package/dist/skylight/definition.edit.d.ts.map +1 -0
  762. package/dist/skylight/definition.edit.js +218 -0
  763. package/dist/skylight/definition.js +4 -271
  764. package/dist/skylight/definition.render.d.ts +6 -0
  765. package/dist/skylight/definition.render.d.ts.map +1 -0
  766. package/dist/skylight/definition.render.js +50 -0
  767. package/dist/skylight/move-tool.d.ts.map +1 -1
  768. package/dist/skylight/move-tool.js +7 -5
  769. package/dist/slab/definition.d.ts +289 -20
  770. package/dist/slab/definition.d.ts.map +1 -1
  771. package/dist/slab/definition.edit.d.ts +4 -0
  772. package/dist/slab/definition.edit.d.ts.map +1 -0
  773. package/dist/slab/definition.edit.js +230 -0
  774. package/dist/slab/definition.js +4 -334
  775. package/dist/slab/definition.render.d.ts +6 -0
  776. package/dist/slab/definition.render.d.ts.map +1 -0
  777. package/dist/slab/definition.render.js +92 -0
  778. package/dist/slab/panel.js +2 -2
  779. package/dist/solar-panel/definition.d.ts +145 -14
  780. package/dist/solar-panel/definition.d.ts.map +1 -1
  781. package/dist/solar-panel/definition.edit.d.ts +4 -0
  782. package/dist/solar-panel/definition.edit.d.ts.map +1 -0
  783. package/dist/solar-panel/definition.edit.js +213 -0
  784. package/dist/solar-panel/definition.js +4 -263
  785. package/dist/solar-panel/definition.render.d.ts +6 -0
  786. package/dist/solar-panel/definition.render.d.ts.map +1 -0
  787. package/dist/solar-panel/definition.render.js +42 -0
  788. package/dist/solar-panel/move-tool.d.ts.map +1 -1
  789. package/dist/solar-panel/move-tool.js +7 -5
  790. package/dist/spawn/definition.d.ts +48 -3
  791. package/dist/spawn/definition.d.ts.map +1 -1
  792. package/dist/spawn/definition.edit.d.ts +4 -0
  793. package/dist/spawn/definition.edit.d.ts.map +1 -0
  794. package/dist/spawn/definition.edit.js +64 -0
  795. package/dist/spawn/definition.js +4 -109
  796. package/dist/spawn/definition.render.d.ts +6 -0
  797. package/dist/spawn/definition.render.d.ts.map +1 -0
  798. package/dist/spawn/definition.render.js +48 -0
  799. package/dist/stair/definition.d.ts +334 -8
  800. package/dist/stair/definition.d.ts.map +1 -1
  801. package/dist/stair/definition.edit.d.ts +4 -0
  802. package/dist/stair/definition.edit.d.ts.map +1 -0
  803. package/dist/stair/definition.edit.js +377 -0
  804. package/dist/stair/definition.js +4 -443
  805. package/dist/stair/definition.render.d.ts +6 -0
  806. package/dist/stair/definition.render.d.ts.map +1 -0
  807. package/dist/stair/definition.render.js +65 -0
  808. package/dist/stair/panel.js +2 -2
  809. package/dist/stair/structure-controls.d.ts.map +1 -1
  810. package/dist/stair/structure-controls.js +2 -2
  811. package/dist/stair-segment/definition.d.ts +130 -7
  812. package/dist/stair-segment/definition.d.ts.map +1 -1
  813. package/dist/stair-segment/definition.edit.d.ts +4 -0
  814. package/dist/stair-segment/definition.edit.d.ts.map +1 -0
  815. package/dist/stair-segment/definition.edit.js +135 -0
  816. package/dist/stair-segment/definition.js +4 -172
  817. package/dist/stair-segment/definition.render.d.ts +6 -0
  818. package/dist/stair-segment/definition.render.d.ts.map +1 -0
  819. package/dist/stair-segment/definition.render.js +38 -0
  820. package/dist/structural-grid/definition.d.ts +50 -3
  821. package/dist/structural-grid/definition.d.ts.map +1 -1
  822. package/dist/structural-grid/definition.edit.d.ts +4 -0
  823. package/dist/structural-grid/definition.edit.d.ts.map +1 -0
  824. package/dist/structural-grid/definition.edit.js +18 -0
  825. package/dist/structural-grid/definition.js +4 -51
  826. package/dist/structural-grid/definition.render.d.ts +6 -0
  827. package/dist/structural-grid/definition.render.d.ts.map +1 -0
  828. package/dist/structural-grid/definition.render.js +35 -0
  829. package/dist/turbine-vent/definition.d.ts +97 -23
  830. package/dist/turbine-vent/definition.d.ts.map +1 -1
  831. package/dist/turbine-vent/definition.edit.d.ts +4 -0
  832. package/dist/turbine-vent/definition.edit.d.ts.map +1 -0
  833. package/dist/turbine-vent/definition.edit.js +66 -0
  834. package/dist/turbine-vent/definition.js +4 -121
  835. package/dist/turbine-vent/definition.render.d.ts +6 -0
  836. package/dist/turbine-vent/definition.render.d.ts.map +1 -0
  837. package/dist/turbine-vent/definition.render.js +40 -0
  838. package/dist/turbine-vent/move-tool.d.ts.map +1 -1
  839. package/dist/turbine-vent/move-tool.js +6 -5
  840. package/dist/unit/definition.d.ts +53 -9
  841. package/dist/unit/definition.d.ts.map +1 -1
  842. package/dist/unit/definition.edit.d.ts +4 -0
  843. package/dist/unit/definition.edit.d.ts.map +1 -0
  844. package/dist/unit/definition.edit.js +4 -0
  845. package/dist/unit/definition.js +4 -38
  846. package/dist/unit/definition.render.d.ts +6 -0
  847. package/dist/unit/definition.render.d.ts.map +1 -0
  848. package/dist/unit/definition.render.js +30 -0
  849. package/dist/unit/unit-panel.d.ts.map +1 -1
  850. package/dist/unit/unit-panel.js +2 -2
  851. package/dist/wall/contextual-dimensions.d.ts.map +1 -1
  852. package/dist/wall/contextual-dimensions.js +2 -2
  853. package/dist/wall/curtain-wall-panel.d.ts.map +1 -1
  854. package/dist/wall/curtain-wall-panel.js +2 -2
  855. package/dist/wall/definition.d.ts +809 -3
  856. package/dist/wall/definition.d.ts.map +1 -1
  857. package/dist/wall/definition.edit.d.ts +4 -0
  858. package/dist/wall/definition.edit.d.ts.map +1 -0
  859. package/dist/wall/definition.edit.js +184 -0
  860. package/dist/wall/definition.js +4 -279
  861. package/dist/wall/definition.render.d.ts +6 -0
  862. package/dist/wall/definition.render.d.ts.map +1 -0
  863. package/dist/wall/definition.render.js +100 -0
  864. package/dist/wall/floorplan.js +1 -1
  865. package/dist/wall/measurement.d.ts.map +1 -1
  866. package/dist/wall/measurement.js +22 -5
  867. package/dist/wall/move-endpoint-tool.d.ts.map +1 -1
  868. package/dist/wall/move-endpoint-tool.js +3 -3
  869. package/dist/wall/move-shared.js +1 -1
  870. package/dist/wall/panel-model.d.ts.map +1 -1
  871. package/dist/wall/panel-model.js +4 -3
  872. package/dist/wall/panel.d.ts.map +1 -1
  873. package/dist/wall/panel.js +15 -6
  874. package/dist/wall/quick-measurement.js +1 -1
  875. package/dist/wall/rectangle-command.d.ts +1 -0
  876. package/dist/wall/rectangle-command.d.ts.map +1 -1
  877. package/dist/wall/rectangle-tool.js +2 -2
  878. package/dist/wall/split-floorplan-layer.d.ts.map +1 -1
  879. package/dist/wall/split-floorplan-layer.js +2 -2
  880. package/dist/wall/split-tool.d.ts.map +1 -1
  881. package/dist/wall/split-tool.js +2 -2
  882. package/dist/wall/tool.d.ts.map +1 -1
  883. package/dist/wall/tool.js +138 -36
  884. package/dist/wall/treatments.d.ts.map +1 -1
  885. package/dist/wall/treatments.js +36 -16
  886. package/dist/window/definition.d.ts +356 -15
  887. package/dist/window/definition.d.ts.map +1 -1
  888. package/dist/window/definition.edit.d.ts +4 -0
  889. package/dist/window/definition.edit.d.ts.map +1 -0
  890. package/dist/window/definition.edit.js +255 -0
  891. package/dist/window/definition.js +5 -338
  892. package/dist/window/definition.render.d.ts +8 -0
  893. package/dist/window/definition.render.d.ts.map +1 -0
  894. package/dist/window/definition.render.js +83 -0
  895. package/dist/window/floorplan-move.d.ts.map +1 -1
  896. package/dist/window/floorplan-move.js +7 -4
  897. package/dist/window/mechanism.js +1 -1
  898. package/dist/window/move-tool.d.ts.map +1 -1
  899. package/dist/window/move-tool.js +7 -11
  900. package/dist/window/tool.d.ts.map +1 -1
  901. package/dist/window/tool.js +5 -2
  902. package/dist/zone/contextual-dimensions.d.ts.map +1 -1
  903. package/dist/zone/contextual-dimensions.js +1 -2
  904. package/dist/zone/definition.d.ts +264 -9
  905. package/dist/zone/definition.d.ts.map +1 -1
  906. package/dist/zone/definition.edit.d.ts +4 -0
  907. package/dist/zone/definition.edit.d.ts.map +1 -0
  908. package/dist/zone/definition.edit.js +38 -0
  909. package/dist/zone/definition.js +4 -97
  910. package/dist/zone/definition.render.d.ts +6 -0
  911. package/dist/zone/definition.render.d.ts.map +1 -0
  912. package/dist/zone/definition.render.js +55 -0
  913. package/dist/zone/quantities-panel.d.ts.map +1 -1
  914. package/dist/zone/quantities-panel.js +2 -2
  915. package/package.json +21 -10
@@ -1,2115 +1,6 @@
1
- import { CABINET_METRIC_DEFAULTS, cabinetSlots, findLevelAncestorId, selectionProxyIdFromMetadata, } from '@pascal-app/core';
2
- import { findWallOpeningConflicts } from '../shared/wall-opening-clearance.js';
3
- import { bakeCabinetAnimationClip } from './animation.js';
4
- import { cabinetBatchable } from './batchable.js';
5
- import { buildCabinetFloorplan, buildCabinetModuleFloorplan } from './floorplan.js';
6
- import { cabinetModuleFloorplanMoveTarget } from './floorplan-move.js';
7
- import { cabinetFloorplanSiblingOverrides } from './floorplan-overrides.js';
8
- import { buildCabinetGeometry } from './geometry.js';
9
- import { withCabinetHostedChildren } from './hosted-resize.js';
10
- import { toggleCabinetOperationState } from './interaction.js';
11
- import { cabinetModuleParentFrame } from './move-frame.js';
12
- import { cabinetPaint } from './paint.js';
13
- import { cabinetModuleUsesFixedApplianceWidth } from './panel-visibility.js';
14
- import { cabinetModuleParametrics, cabinetParametrics } from './parametrics.js';
15
- import { cabinetPlacementNotice } from './placement-notice.js';
16
- import { resolveCabinetGridPosition } from './placement-snap.js';
17
- import useCabinetPlacementType from './placement-type.js';
18
- import { metadataForSelectedWidth, metadataWithPresetWidthDebt } from './preset-width-debt.js';
19
- import { cabinetQuickActions } from './quick-actions.js';
20
- import { cabinetConnectedDepthBounds, cabinetResizeUpperBound, MAX_CABINET_DEPTH, MAX_CABINET_WIDTH, MIN_CABINET_DEPTH, MIN_CABINET_WIDTH, } from './resize-limits.js';
21
- import { moduleSideOpen, reflowRunModules as reflowCabinetRunModules, runWallConstraints, sortRunModules, } from './run-layout.js';
22
- import { backAlignedRunDepthOverrides, backAlignZ, buildWallCornerDepthIndex, bumpCabinetRunLayoutRevision, cabinetMetadataRecord, cabinetModulesForRun, cabinetModuleTotalHeight, totalCabinetHeight as cabinetTotalHeight, cornerSourceWidthOverridesForDerivedDepth, nestedCornerRunPositionOverrides, previewCornerRunsFromRunSources, resolveCabinetType, runModuleBaseY, syncCornerRunsFromRunSources, syncCornerRunsFromSourceModule, wallChildOf, wallCornerWidthOverridesForDepthTargets, } from './run-ops.js';
23
- import { cabinetSceneAction } from './scene-action.js';
24
- import { CabinetModuleNode, CabinetNode } from './schema.js';
25
- import { backAnchoredModuleZ, isHoodCompartmentType, minCabinetCarcassHeightForStack, stackForCabinet, } from './stack.js';
26
- import { cabinetModuleSurfaceProvider, cabinetSurfaceProvider } from './surfaces.js';
27
- import { cabinetFloorplanAffectedIds, cabinetTreeChildIds, cabinetTreeHidden, cabinetTreeLabel, } from './tree-structure.js';
28
- import { findClosestCabinetWallInPlan, resolveCabinetModuleWallSnapLocal, resolveCabinetRunWallSnap, resolveCabinetWallFaceOffset, } from './wall-snap.js';
29
- function isCabinetDuplicableNode(node) {
30
- return ((node?.type === 'cabinet' || node?.type === 'cabinet-module') &&
31
- Array.isArray(node.position) &&
32
- typeof node.rotation === 'number');
33
- }
34
- function stripCabinetDuplicateMetadata(metadata) {
35
- const { cabinetCornerDerivedRun: _derived, cabinetCornerSourceLink: _source, nodeSelectionProxyId: _proxy, ...rest } = cabinetMetadataRecord(metadata);
36
- return rest;
37
- }
38
- function cleanCabinetDuplicateNode(node) {
39
- return {
40
- ...node,
41
- metadata: stripCabinetDuplicateMetadata(node.metadata),
42
- };
43
- }
44
- function composeCabinetDuplicatePose(parentPosition, parentRotation, childPosition, childRotation) {
45
- const cos = Math.cos(parentRotation);
46
- const sin = Math.sin(parentRotation);
47
- return {
48
- position: [
49
- parentPosition[0] + childPosition[0] * cos + childPosition[2] * sin,
50
- parentPosition[1] + childPosition[1],
51
- parentPosition[2] - childPosition[0] * sin + childPosition[2] * cos,
52
- ],
53
- rotation: parentRotation + childRotation,
54
- };
55
- }
56
- function cabinetDuplicateWorldPose(node, nodes) {
57
- if (!isCabinetDuplicableNode(node))
58
- return null;
59
- const parent = node.parentId ? nodes[node.parentId] : null;
60
- if (isCabinetDuplicableNode(parent)) {
61
- const parentPose = cabinetDuplicateWorldPose(parent, nodes);
62
- return parentPose
63
- ? composeCabinetDuplicatePose(parentPose.position, parentPose.rotation, node.position, node.rotation)
64
- : null;
65
- }
66
- return { position: [...node.position], rotation: node.rotation };
67
- }
68
- function prepareCabinetSubtreeClone(args) {
69
- const parent = args.root.parentId ? args.nodes[args.root.parentId] : null;
70
- const nestedRun = args.root.type === 'cabinet' && isCabinetDuplicableNode(parent);
71
- const worldPose = nestedRun ? cabinetDuplicateWorldPose(args.root, args.nodes) : null;
72
- const levelId = nestedRun ? findLevelAncestorId(args.rootId, args.nodes) : null;
73
- const root = {
74
- ...cleanCabinetDuplicateNode(args.root),
75
- ...(worldPose ? { position: worldPose.position, rotation: worldPose.rotation } : null),
76
- ...(levelId ? { parentId: levelId } : null),
77
- };
78
- return {
79
- root,
80
- descendants: args.descendants.map((node) => cleanCabinetDuplicateNode(node)),
81
- parentId: levelId ? levelId : undefined,
82
- };
83
- }
84
- function appendCabinetFloorPlacedFootprints(run, nodes, parentPosition, parentRotation, footprints) {
85
- const cos = Math.cos(parentRotation);
86
- const sin = Math.sin(parentRotation);
87
- const runPosition = [
88
- parentPosition[0] + run.position[0] * cos + run.position[2] * sin,
89
- parentPosition[1] + run.position[1],
90
- parentPosition[2] - run.position[0] * sin + run.position[2] * cos,
91
- ];
92
- const runRotation = parentRotation + run.rotation;
93
- const modules = cabinetModulesForRun(run, nodes);
94
- if (modules.length > 0) {
95
- const runCos = Math.cos(runRotation);
96
- const runSin = Math.sin(runRotation);
97
- for (const module of modules) {
98
- const modulePosition = [
99
- runPosition[0] + module.position[0] * runCos + module.position[2] * runSin,
100
- runPosition[1] + module.position[1],
101
- runPosition[2] - module.position[0] * runSin + module.position[2] * runCos,
102
- ];
103
- footprints.push({
104
- position: modulePosition,
105
- dimensions: [module.width, cabinetModuleTotalHeight(module), module.depth],
106
- rotation: [0, runRotation + module.rotation, 0],
107
- });
108
- }
109
- }
110
- else {
111
- footprints.push({
112
- position: runPosition,
113
- dimensions: [run.width, cabinetTotalHeight(run), run.depth],
114
- rotation: [0, runRotation, 0],
115
- });
116
- }
117
- for (const childId of run.children ?? []) {
118
- const child = nodes[childId];
119
- if (isCabinetRun(child)) {
120
- appendCabinetFloorPlacedFootprints(child, nodes, runPosition, runRotation, footprints);
121
- }
122
- }
123
- }
124
- export function cabinetFloorPlacedFootprints(node, nodes) {
125
- if (!nodes) {
126
- return [
127
- {
128
- position: [...node.position],
129
- dimensions: [node.width, cabinetTotalHeight(node), node.depth],
130
- rotation: [0, node.rotation, 0],
131
- },
132
- ];
133
- }
134
- const footprints = [];
135
- appendCabinetFloorPlacedFootprints(node, nodes, [0, 0, 0], 0, footprints);
136
- return footprints;
137
- }
138
- function cabinetRunOverlapsWallOpening({ levelId, node, nodes, position, rotation, }) {
139
- const parentLevelId = (levelId ??
140
- findLevelAncestorId(node.id, nodes));
141
- if (!parentLevelId)
142
- return false;
143
- const candidate = { ...node, position: [...position], rotation };
144
- return cabinetFloorPlacedFootprints(candidate, nodes).some((footprint) => {
145
- if (!footprint.position)
146
- return false;
147
- const hit = findClosestCabinetWallInPlan({
148
- excludeIds: [],
149
- nodes: nodes,
150
- parentLevelId,
151
- planPoint: [footprint.position[0], footprint.position[2]],
152
- yaw: footprint.rotation[1],
153
- });
154
- if (!hit)
155
- return false;
156
- const normalScale = hit.side === 'front' ? 1 : -1;
157
- const expectedPerp = resolveCabinetWallFaceOffset({
158
- hit,
159
- nodes: nodes,
160
- parentLevelId,
161
- }) +
162
- normalScale * (footprint.dimensions[2] / 2);
163
- if (Math.abs(hit.perpDistance - expectedPerp) > 0.12)
164
- return false;
165
- return (findWallOpeningConflicts({
166
- bottom: footprint.position[1],
167
- height: footprint.dimensions[1],
168
- localX: hit.localX,
169
- nodes,
170
- wall: hit.wall,
171
- width: footprint.dimensions[0],
172
- }).length > 0);
173
- });
174
- }
175
- const SIDE_HANDLE_OFFSET = 0.18;
176
- const HEIGHT_HANDLE_OFFSET = 0.22;
177
- const ROTATE_CORNER_OFFSET = 0.32;
178
- const ROTATE_RING_OFFSET = 0.04;
179
- const MIN_CABINET_CARCASS_HEIGHT = 0.4;
180
- const CABINET_ADJACENCY_EPSILON = 1e-4;
181
- const CABINET_DEPTH_SNAP_THRESHOLD = 0.02;
182
- const CABINET_WIDTH_SNAP_THRESHOLD = 0.08;
183
- function isCabinetModule(node) {
184
- return node?.type === 'cabinet-module';
185
- }
186
- function isCabinetRun(node) {
187
- return node?.type === 'cabinet';
188
- }
189
- function hasCabinetParentId(node) {
190
- const parentId = node.parentId;
191
- return (typeof parentId === 'string' &&
192
- (parentId.startsWith('cabinet_') || parentId.startsWith('cabinet-module_')));
193
- }
194
- function resolveCabinetGroupMoveSnap({ candidatePosition, candidateRotation, levelId, movingIds, node, nodes, }) {
195
- if (node.type !== 'cabinet' || !levelId)
196
- return null;
197
- return resolveCabinetRunWallSnap({
198
- cabinet: node,
199
- candidatePosition,
200
- candidateRotation,
201
- excludeIds: movingIds,
202
- gridStep: 0,
203
- nodes: nodes,
204
- parentLevelId: levelId,
205
- });
206
- }
207
- function resolveCabinetMoveGridSnap({ candidatePosition, candidateRotation, gridStep, node, nodes, }) {
208
- if (!isCabinetRun(node))
209
- return candidatePosition;
210
- const bounds = cabinetLocalBounds(node, nodes);
211
- const snapped = resolveCabinetGridPosition({
212
- raw: candidatePosition,
213
- dimensions: bounds.size,
214
- footprintOffset: [bounds.center[0], bounds.center[2]],
215
- yaw: candidateRotation,
216
- step: gridStep,
217
- });
218
- return [snapped[0], candidatePosition[1], snapped[2]];
219
- }
220
- /**
221
- * Wall snap for a single dragged module. `parentFrame` kinds exchange
222
- * `candidatePosition` in the run's LOCAL frame with the move tool (it
223
- * converts through `planToLocal` before and `localToPlan` after), so this
224
- * resolver works local-in / local-out.
225
- */
226
- function resolveCabinetModuleGroupMoveSnap({ candidatePosition, levelId, movingIds, node, nodes, }) {
227
- if (node.type !== 'cabinet-module' || !node.parentId)
228
- return null;
229
- const run = nodes[node.parentId];
230
- if (!isCabinetRun(run))
231
- return null;
232
- const parentLevelId = (levelId ?? run.parentId ?? null);
233
- if (!parentLevelId)
234
- return null;
235
- const position = resolveCabinetModuleWallSnapLocal({
236
- candidateLocal: candidatePosition,
237
- excludeIds: movingIds,
238
- module: node,
239
- nodes: nodes,
240
- parentLevelId,
241
- run,
242
- });
243
- return position ? { position } : null;
244
- }
245
- function cabinetLayoutRevision(metadata) {
246
- return cabinetMetadataRecord(metadata).cabinetLayoutRevision ?? null;
247
- }
248
- function cabinetAdjacencyRevision(metadata) {
249
- return cabinetMetadataRecord(metadata).cabinetAdjacencyRevision ?? null;
250
- }
251
- // Margin added to each run's reach when deciding whether two sibling runs can
252
- // influence each other's countertop join — generous relative to any plausible
253
- // `countertopOverhang` so a run sliding away still re-keys the neighbor it
254
- // just un-joined from.
255
- const CABINET_NEIGHBOR_JOIN_MARGIN = 0.5;
256
- export function cabinetRunFootprint(run, nodes) {
257
- const bounds = cabinetLocalBounds(run, nodes);
258
- return {
259
- parentId: (run.parentId ?? null),
260
- x: run.position[0],
261
- z: run.position[2],
262
- reach: Math.hypot(bounds.size[0], bounds.size[2]) / 2 +
263
- Math.hypot(bounds.center[0], bounds.center[2]) +
264
- CABINET_NEIGHBOR_JOIN_MARGIN,
265
- };
266
- }
267
- /**
268
- * Re-key sibling cabinet runs whose countertop join could be affected by a
269
- * run that moved / resized / re-flowed. A run's overhang trims against
270
- * adjacent sibling runs (`siblingCabinetSpansInRunLocal`), but a neighbor's
271
- * own `geometryKey` doesn't change when THIS run moves — marking it dirty
272
- * alone would be swallowed by the geometry system's key-skip cache. Bumping
273
- * `cabinetAdjacencyRevision` (folded into the run geometryKey) both dirties
274
- * and re-keys it. History is paused: the counter is derived presentation
275
- * state, and an undo of the triggering move re-fires the watcher anyway.
276
- */
277
- export function bumpCabinetRunsNear(sceneApi, footprints, moverIds) {
278
- const nodes = sceneApi.nodes();
279
- const targets = new Set();
280
- for (const footprint of footprints) {
281
- if (!footprint.parentId)
282
- continue;
283
- const parent = nodes[footprint.parentId];
284
- const childIds = parent?.children;
285
- if (!Array.isArray(childIds))
286
- continue;
287
- for (const childId of childIds) {
288
- if (moverIds.has(childId) || targets.has(childId))
289
- continue;
290
- const sibling = nodes[childId];
291
- if (!isCabinetRun(sibling))
292
- continue;
293
- const siblingFootprint = cabinetRunFootprint(sibling, nodes);
294
- const distance = Math.hypot(siblingFootprint.x - footprint.x, siblingFootprint.z - footprint.z);
295
- if (distance <= siblingFootprint.reach + footprint.reach)
296
- targets.add(childId);
297
- }
298
- }
299
- if (targets.size === 0)
300
- return;
301
- // A mover's own trim also depends on its offset to those neighbors, and
302
- // `position` is not in its geometryKey — re-key it alongside them. Movers
303
- // with no neighbors in range never reach here, keeping lone-cabinet moves
304
- // rebuild-free.
305
- for (const id of moverIds) {
306
- if (isCabinetRun(nodes[id]))
307
- targets.add(id);
308
- }
309
- sceneApi.pauseHistory();
310
- try {
311
- for (const id of targets) {
312
- const run = sceneApi.get(id);
313
- if (!isCabinetRun(run))
314
- continue;
315
- const metadataRecord = cabinetMetadataRecord(run.metadata);
316
- const currentRevision = typeof metadataRecord.cabinetAdjacencyRevision === 'number'
317
- ? metadataRecord.cabinetAdjacencyRevision
318
- : 0;
319
- sceneApi.update(id, {
320
- metadata: { ...metadataRecord, cabinetAdjacencyRevision: currentRevision + 1 },
321
- });
322
- sceneApi.markDirty(id);
323
- }
324
- }
325
- finally {
326
- sceneApi.resumeHistory();
327
- }
328
- }
329
- /** Inputs of a run that can change a NEIGHBOR's countertop join. */
330
- export function cabinetRunNeighborSignature(run) {
331
- return JSON.stringify([
332
- run.position[0],
333
- run.position[2],
334
- run.rotation,
335
- run.width,
336
- run.depth,
337
- run.carcassHeight,
338
- run.runTier,
339
- run.countertopOverhang,
340
- (run.children ?? []).filter((id) => id.startsWith('cabinet_') || id.startsWith('cabinet-module_')),
341
- cabinetLayoutRevision(run.metadata),
342
- ]);
343
- }
344
- function includeCabinetModuleBounds(module, nodes, origin, bounds) {
345
- const x = origin[0] + module.position[0];
346
- const y = origin[1] + module.position[1];
347
- const z = origin[2] + module.position[2];
348
- bounds.minX = Math.min(bounds.minX, x - module.width / 2);
349
- bounds.maxX = Math.max(bounds.maxX, x + module.width / 2);
350
- bounds.minY = Math.min(bounds.minY, y - (module.showPlinth ? module.plinthHeight : 0));
351
- bounds.maxY = Math.max(bounds.maxY, y + cabinetModuleTotalHeight(module));
352
- bounds.minZ = Math.min(bounds.minZ, z - module.depth / 2);
353
- bounds.maxZ = Math.max(bounds.maxZ, z + module.depth / 2);
354
- for (const childId of module.children ?? []) {
355
- const child = nodes[childId];
356
- if (isCabinetModule(child))
357
- includeCabinetModuleBounds(child, nodes, [x, y, z], bounds);
358
- }
359
- }
360
- /**
361
- * Fold a child cabinet run (an L-corner leg parented to this run) into the
362
- * parent's local bounds: take the child's own local bounds, rotate its XZ
363
- * corners by the child's local rotation, and offset by its local position.
364
- */
365
- function includeChildRunBounds(child, nodes, bounds) {
366
- const childBounds = cabinetLocalBounds(child, nodes);
367
- const cos = Math.cos(child.rotation);
368
- const sin = Math.sin(child.rotation);
369
- for (const [lx, lz] of [
370
- [childBounds.minX, childBounds.minZ],
371
- [childBounds.maxX, childBounds.minZ],
372
- [childBounds.maxX, childBounds.maxZ],
373
- [childBounds.minX, childBounds.maxZ],
374
- ]) {
375
- const x = child.position[0] + lx * cos + lz * sin;
376
- const z = child.position[2] - lx * sin + lz * cos;
377
- bounds.minX = Math.min(bounds.minX, x);
378
- bounds.maxX = Math.max(bounds.maxX, x);
379
- bounds.minZ = Math.min(bounds.minZ, z);
380
- bounds.maxZ = Math.max(bounds.maxZ, z);
381
- }
382
- bounds.minY = Math.min(bounds.minY, child.position[1] + childBounds.minY);
383
- bounds.maxY = Math.max(bounds.maxY, child.position[1] + childBounds.maxY);
384
- }
385
- function cabinetLocalBounds(node, nodes) {
386
- const bounds = {
387
- minX: -node.width / 2,
388
- maxX: node.width / 2,
389
- minY: 0,
390
- maxY: node.type === 'cabinet-module' ? cabinetModuleTotalHeight(node) : cabinetTotalHeight(node),
391
- minZ: -node.depth / 2,
392
- maxZ: node.depth / 2,
393
- };
394
- if (isCabinetRun(node) && nodes) {
395
- const modules = cabinetModulesForRun(node, nodes);
396
- if (modules.length > 0) {
397
- bounds.minX = Number.POSITIVE_INFINITY;
398
- bounds.maxX = Number.NEGATIVE_INFINITY;
399
- bounds.minY = 0;
400
- bounds.maxY = (node.showPlinth ? node.plinthHeight : 0) + node.carcassHeight;
401
- bounds.minZ = Number.POSITIVE_INFINITY;
402
- bounds.maxZ = Number.NEGATIVE_INFINITY;
403
- for (const module of modules) {
404
- includeCabinetModuleBounds(module, nodes, [0, 0, 0], bounds);
405
- }
406
- bounds.maxY = Math.max(bounds.maxY, cabinetTotalHeight(node));
407
- // A seating back overhang (unlike the small front/side overhang) is
408
- // deep enough to matter for selection and collision.
409
- if (node.withCountertop && node.barLedge?.edge !== 'back') {
410
- bounds.minZ -= node.countertopBackOverhang;
411
- }
412
- if (node.withFinishedBack)
413
- bounds.minZ -= node.boardThickness;
414
- if (node.barLedge) {
415
- const edge = node.barLedge.edge;
416
- if (edge === 'back')
417
- bounds.minZ -= node.barLedge.depth;
418
- if (edge === 'left')
419
- bounds.minX -= node.barLedge.depth;
420
- if (edge === 'right')
421
- bounds.maxX += node.barLedge.depth;
422
- bounds.maxY = Math.max(bounds.maxY, node.barLedge.height);
423
- }
424
- }
425
- // L-corner leg runs are cabinet children of the source run — fold them in
426
- // so the run's rotate ring / pivot / drag box covers the whole L.
427
- for (const childId of node.children ?? []) {
428
- const child = nodes[childId];
429
- if (isCabinetRun(child))
430
- includeChildRunBounds(child, nodes, bounds);
431
- }
432
- }
433
- if (isCabinetModule(node) && nodes) {
434
- for (const childId of node.children ?? []) {
435
- const child = nodes[childId];
436
- if (isCabinetModule(child)) {
437
- includeCabinetModuleBounds(child, nodes, [0, 0, 0], bounds);
438
- }
439
- else if (isCabinetRun(child)) {
440
- includeChildRunBounds(child, nodes, bounds);
441
- }
442
- }
443
- }
444
- const width = Math.max(0.01, bounds.maxX - bounds.minX);
445
- const height = Math.max(0.01, bounds.maxY - bounds.minY);
446
- const depth = Math.max(0.01, bounds.maxZ - bounds.minZ);
447
- return {
448
- ...bounds,
449
- size: [width, height, depth],
450
- center: [
451
- (bounds.minX + bounds.maxX) / 2,
452
- (bounds.minY + bounds.maxY) / 2,
453
- (bounds.minZ + bounds.maxZ) / 2,
454
- ],
455
- };
456
- }
457
- function cabinetPlanBoundsAabb(node, nodes) {
458
- const bounds = cabinetLocalBounds(node, nodes);
459
- const cos = Math.cos(node.rotation);
460
- const sin = Math.sin(node.rotation);
461
- let minX = Number.POSITIVE_INFINITY;
462
- let maxX = Number.NEGATIVE_INFINITY;
463
- let minZ = Number.POSITIVE_INFINITY;
464
- let maxZ = Number.NEGATIVE_INFINITY;
465
- for (const [lx, lz] of [
466
- [bounds.minX, bounds.minZ],
467
- [bounds.maxX, bounds.minZ],
468
- [bounds.maxX, bounds.maxZ],
469
- [bounds.minX, bounds.maxZ],
470
- ]) {
471
- const x = node.position[0] + lx * cos + lz * sin;
472
- const z = node.position[2] - lx * sin + lz * cos;
473
- minX = Math.min(minX, x);
474
- maxX = Math.max(maxX, x);
475
- minZ = Math.min(minZ, z);
476
- maxZ = Math.max(maxZ, z);
477
- }
478
- return { minX, maxX, minZ, maxZ };
479
- }
480
- function cabinetModuleSideOpen(module, side, sceneApi) {
481
- const parent = module.parentId ? sceneApi.get(module.parentId) : undefined;
482
- if (!isCabinetRun(parent))
483
- return true;
484
- return moduleSideOpen(cabinetModulesForRun(parent, sceneApi.nodes()), module.id, side, CABINET_ADJACENCY_EPSILON);
485
- }
486
- function cabinetModuleConnectedNeighbor(module, side, sceneApi) {
487
- const parent = module.parentId ? sceneApi.get(module.parentId) : undefined;
488
- if (!isCabinetRun(parent))
489
- return undefined;
490
- const modules = sortRunModules(cabinetModulesForRun(parent, sceneApi.nodes()));
491
- const index = modules.findIndex((entry) => entry.id === module.id);
492
- if (index < 0 || cabinetModuleSideOpen(module, side, sceneApi))
493
- return undefined;
494
- return side === 'left' ? modules[index - 1] : modules[index + 1];
495
- }
496
- function cabinetWidthConnectedNeighbor(module, side, sceneApi) {
497
- const parent = module.parentId ? sceneApi.get(module.parentId) : undefined;
498
- if (isCabinetRun(parent))
499
- return cabinetModuleConnectedNeighbor(module, side, sceneApi);
500
- if (!isCabinetModule(parent))
501
- return undefined;
502
- if (wallChildOf(parent, sceneApi.nodes())?.id !== module.id)
503
- return undefined;
504
- const connectedHost = cabinetModuleConnectedNeighbor(parent, side, sceneApi);
505
- if (!connectedHost || isCabinetWidthFiller(connectedHost))
506
- return undefined;
507
- return wallChildOf(connectedHost, sceneApi.nodes()) ?? undefined;
508
- }
509
- function cabinetModuleRunLocalCenterX(module, sceneApi) {
510
- const parent = module.parentId ? sceneApi.get(module.parentId) : undefined;
511
- if (isCabinetRun(parent))
512
- return module.position[0];
513
- if (!isCabinetModule(parent))
514
- return null;
515
- const run = parent.parentId ? sceneApi.get(parent.parentId) : undefined;
516
- return isCabinetRun(run) ? parent.position[0] + module.position[0] : null;
517
- }
518
- function cabinetWallWidthGap(module, side, sceneApi) {
519
- const parent = module.parentId ? sceneApi.get(module.parentId) : undefined;
520
- const isWallModule = (isCabinetRun(parent) && parent.runTier === 'wall') ||
521
- (isCabinetModule(parent) && wallChildOf(parent, sceneApi.nodes())?.id === module.id);
522
- if (!isWallModule)
523
- return 0;
524
- const connected = cabinetWidthConnectedNeighbor(module, side, sceneApi);
525
- if (!connected || isCabinetWidthFiller(connected))
526
- return 0;
527
- const moduleCenter = cabinetModuleRunLocalCenterX(module, sceneApi);
528
- const connectedCenter = cabinetModuleRunLocalCenterX(connected, sceneApi);
529
- if (moduleCenter === null || connectedCenter === null)
530
- return 0;
531
- const direction = side === 'right' ? 1 : -1;
532
- return Math.max(0, direction * (connectedCenter - moduleCenter) - (module.width + connected.width) / 2);
533
- }
534
- function isCabinetWidthFiller(module) {
535
- return (module.moduleKind !== 'standard' ||
536
- module.name === 'Corner Filler' ||
537
- module.name === 'Wall Bridge Filler' ||
538
- module.name === 'Corner Wall Filler');
539
- }
540
- function cabinetNodeAttachedToAncestor(node, ancestorId, nodes) {
541
- let current = node;
542
- const visited = new Set();
543
- while (current.parentId) {
544
- const currentId = current.id;
545
- if (visited.has(currentId))
546
- return false;
547
- visited.add(currentId);
548
- const parent = nodes[current.parentId];
549
- if (parent?.type !== 'cabinet' && parent?.type !== 'cabinet-module')
550
- return false;
551
- if (!(parent.children ?? []).includes(currentId))
552
- return false;
553
- if (parent.id === ancestorId)
554
- return true;
555
- current = parent;
556
- }
557
- return false;
558
- }
559
- function cabinetSubtreeHasNamedFiller(root, name, sceneApi) {
560
- const pending = [...(root.children ?? [])];
561
- const visited = new Set();
562
- while (pending.length > 0) {
563
- const id = pending.pop();
564
- if (visited.has(id))
565
- continue;
566
- visited.add(id);
567
- const node = sceneApi.get(id);
568
- if (!node)
569
- continue;
570
- if (node.type === 'cabinet-module' && isCabinetWidthFiller(node) && node.name === name) {
571
- return true;
572
- }
573
- if (node.type === 'cabinet' || node.type === 'cabinet-module') {
574
- pending.push(...(node.children ?? []));
575
- }
576
- }
577
- return false;
578
- }
579
- function wallCabinetSideHasFiller(hostModule, side, sceneApi) {
580
- const hostRun = hostModule.parentId
581
- ? sceneApi.get(hostModule.parentId)
582
- : undefined;
583
- if (!hostRun || !isCabinetRun(hostRun))
584
- return false;
585
- const connected = cabinetModuleConnectedNeighbor(hostModule, side, sceneApi);
586
- if (connected &&
587
- isCabinetWidthFiller(connected) &&
588
- cabinetSubtreeHasNamedFiller(connected, 'Corner Wall Filler', sceneApi)) {
589
- return true;
590
- }
591
- const nodes = sceneApi.nodes();
592
- for (const candidate of Object.values(nodes)) {
593
- if (candidate?.type !== 'cabinet')
594
- continue;
595
- const derived = cabinetMetadataRecord(candidate.metadata).cabinetCornerDerivedRun;
596
- if (!derived || typeof derived !== 'object' || Array.isArray(derived))
597
- continue;
598
- if (derived.role !== 'bridge' ||
599
- derived.side !== side ||
600
- derived.sourceModuleId !== hostModule.id ||
601
- derived.sourceRunId !== hostRun.id ||
602
- !cabinetNodeAttachedToAncestor(candidate, hostRun.id, nodes)) {
603
- continue;
604
- }
605
- if (cabinetModulesForRun(candidate, nodes).some((entry) => entry.name === 'Wall Bridge Filler')) {
606
- return true;
607
- }
608
- }
609
- return false;
610
- }
611
- function cabinetModuleSideHasCornerFiller(module, side, sceneApi) {
612
- const parent = module.parentId ? sceneApi.get(module.parentId) : undefined;
613
- if (isCabinetModule(parent)) {
614
- return wallCabinetSideHasFiller(parent, side, sceneApi);
615
- }
616
- if (!isCabinetRun(parent))
617
- return false;
618
- const connected = cabinetModuleConnectedNeighbor(module, side, sceneApi);
619
- if (connected && isCabinetWidthFiller(connected)) {
620
- return true;
621
- }
622
- for (const candidate of Object.values(sceneApi.nodes())) {
623
- if (candidate?.type !== 'cabinet')
624
- continue;
625
- const derived = cabinetMetadataRecord(candidate.metadata).cabinetCornerDerivedRun;
626
- if (!derived || typeof derived !== 'object' || Array.isArray(derived))
627
- continue;
628
- if (candidate.parentId !== parent.id ||
629
- !(parent.children ?? []).includes(candidate.id) ||
630
- derived.role !== 'base-leg' ||
631
- derived.side !== side ||
632
- derived.sourceModuleId !== module.id ||
633
- derived.sourceRunId !== parent.id) {
634
- continue;
635
- }
636
- if (cabinetModulesForRun(candidate, sceneApi.nodes()).some((entry) => entry.moduleKind === 'corner-filler')) {
637
- return true;
638
- }
639
- }
640
- return false;
641
- }
642
- function connectedCabinetWidthResize(module, side, delta, sceneApi) {
643
- const connected = cabinetWidthConnectedNeighbor(module, side, sceneApi);
644
- if (!connected || isCabinetWidthFiller(connected))
645
- return null;
646
- const direction = side === 'right' ? 1 : -1;
647
- return {
648
- module: connected,
649
- patch: {
650
- width: connected.width - delta,
651
- position: [
652
- connected.position[0] + (direction * delta) / 2,
653
- connected.position[1],
654
- connected.position[2],
655
- ],
656
- },
657
- };
658
- }
659
- function wallCabinetWidthOverride(module, width, sceneApi) {
660
- const parent = module.parentId
661
- ? sceneApi.get(module.parentId)
662
- : undefined;
663
- if (!parent || !isCabinetRun(parent) || resolveCabinetType(module, parent) !== 'base')
664
- return null;
665
- const wallChild = wallChildOf(module, sceneApi.nodes());
666
- return wallChild ? [wallChild.id, { width }] : null;
667
- }
668
- function parentRunGeometryPreviewOverride(node, sceneApi) {
669
- if (!isCabinetModule(node) || !node.parentId)
670
- return null;
671
- const parent = sceneApi.get(node.parentId);
672
- if (isCabinetRun(parent))
673
- return [parent.id, {}];
674
- if (!isCabinetModule(parent) || wallChildOf(parent, sceneApi.nodes())?.id !== node.id)
675
- return null;
676
- const run = parent.parentId ? sceneApi.get(parent.parentId) : undefined;
677
- return isCabinetRun(run) ? [run.id, {}] : null;
678
- }
679
- function sharedDepthBounds(referenceDepth, targets, nodes) {
680
- const depthOwners = targets.flatMap((target) => {
681
- if (!isCabinetRun(target))
682
- return [target];
683
- const modules = cabinetModulesForRun(target, nodes);
684
- return modules.length > 0 ? [target, ...modules] : [target];
685
- });
686
- const minDelta = Math.max(...depthOwners.map((target) => MIN_CABINET_DEPTH - target.depth));
687
- const maxDelta = Math.min(...depthOwners.map((target) => cabinetResizeUpperBound(target.depth, MAX_CABINET_DEPTH) - target.depth));
688
- return {
689
- min: referenceDepth + minDelta,
690
- max: referenceDepth + maxDelta,
691
- };
692
- }
693
- function depthDeltaRunOverrides(run, nodes, depthDelta) {
694
- const overrides = [];
695
- for (const module of cabinetModulesForRun(run, nodes)) {
696
- const depth = module.depth + depthDelta;
697
- const positionZ = backAnchoredModuleZ(module.position[2], module.depth, depth);
698
- const parentShiftZ = positionZ - module.position[2];
699
- overrides.push([
700
- module.id,
701
- {
702
- depth,
703
- position: [module.position[0], module.position[1], positionZ],
704
- },
705
- ]);
706
- for (const childId of module.children ?? []) {
707
- const child = nodes[childId];
708
- if (child?.type !== 'cabinet')
709
- continue;
710
- overrides.push([
711
- child.id,
712
- {
713
- position: [child.position[0], child.position[1], child.position[2] - parentShiftZ],
714
- },
715
- ]);
716
- }
717
- const wallChild = wallChildOf(module, nodes);
718
- if (wallChild) {
719
- overrides.push([
720
- wallChild.id,
721
- {
722
- position: [
723
- wallChild.position[0],
724
- wallChild.position[1],
725
- backAlignZ(depth, wallChild.depth),
726
- ],
727
- },
728
- ]);
729
- }
730
- }
731
- return overrides;
732
- }
733
- function commitRunResize(run, patch, sceneApi, options = {}) {
734
- sceneApi.update(run.id, patch);
735
- const nextRun = { ...run, ...patch };
736
- const syncDepth = typeof patch.depth === 'number';
737
- const syncHeight = typeof patch.carcassHeight === 'number';
738
- const syncPosition = patch.showPlinth !== undefined || typeof patch.plinthHeight === 'number';
739
- if (syncDepth || syncHeight || syncPosition) {
740
- const depthOverrides = new Map(syncDepth ? backAlignedRunDepthOverrides(run, sceneApi.nodes(), nextRun.depth) : []);
741
- for (const module of cabinetModulesForRun(run, sceneApi.nodes())) {
742
- const modulePatch = {};
743
- if (syncDepth) {
744
- Object.assign(modulePatch, depthOverrides.get(module.id));
745
- }
746
- if (syncHeight) {
747
- modulePatch.carcassHeight = Math.max(nextRun.carcassHeight, minCabinetCarcassHeightForStack(module));
748
- }
749
- if (syncPosition) {
750
- modulePatch.position = [
751
- modulePatch.position?.[0] ?? module.position[0],
752
- runModuleBaseY(nextRun),
753
- modulePatch.position?.[2] ?? module.position[2],
754
- ];
755
- }
756
- if (Object.keys(modulePatch).length > 0) {
757
- sceneApi.update(module.id, modulePatch);
758
- const wallChild = wallChildOf(module, sceneApi.nodes());
759
- if (wallChild && typeof modulePatch.depth === 'number') {
760
- sceneApi.update(wallChild.id, {
761
- position: [
762
- wallChild.position[0],
763
- wallChild.position[1],
764
- backAlignZ(modulePatch.depth, wallChild.depth),
765
- ],
766
- });
767
- }
768
- }
769
- }
770
- if (syncDepth) {
771
- for (const [id, depthPatch] of depthOverrides) {
772
- if (sceneApi.get(id)?.type !== 'cabinet')
773
- continue;
774
- sceneApi.update(id, depthPatch);
775
- }
776
- }
777
- }
778
- if (syncDepth || syncHeight || syncPosition) {
779
- bumpCabinetRunLayoutRevision(sceneApi, nextRun);
780
- syncCornerRunsFromRunSources({
781
- baseLayout: options.cornerSync ?? 'full',
782
- run: nextRun,
783
- sceneApi,
784
- });
785
- }
786
- }
787
- function commitRunDepthDelta(run, depth, sceneApi, options = {}) {
788
- const depthDelta = depth - run.depth;
789
- for (const [id, patch] of depthDeltaRunOverrides(run, sceneApi.nodes(), depthDelta)) {
790
- sceneApi.update(id, patch);
791
- }
792
- sceneApi.update(run.id, { depth });
793
- const nextRun = { ...run, depth };
794
- bumpCabinetRunLayoutRevision(sceneApi, nextRun);
795
- syncCornerRunsFromRunSources({
796
- baseLayout: options.cornerSync ?? 'full',
797
- run: nextRun,
798
- sceneApi,
799
- });
800
- }
801
- function commitModuleResize(module, patch, sceneApi) {
802
- const nodes = sceneApi.nodes();
803
- const parent = module.parentId ? nodes[module.parentId] : undefined;
804
- const parentRun = isCabinetRun(parent) ? parent : undefined;
805
- if (!parentRun) {
806
- sceneApi.update(module.id, patch);
807
- const parentModule = isCabinetModule(parent) ? parent : undefined;
808
- if (parentModule)
809
- sceneApi.markDirty(parentModule.id);
810
- return;
811
- }
812
- if (typeof patch.width === 'number') {
813
- const previousModule = module;
814
- sceneApi.update(module.id, patch);
815
- if (resolveCabinetType(module, parentRun) === 'base') {
816
- const wallChild = wallChildOf(module, sceneApi.nodes());
817
- if (wallChild) {
818
- sceneApi.update(wallChild.id, { width: patch.width });
819
- }
820
- }
821
- bumpCabinetRunLayoutRevision(sceneApi, parentRun);
822
- syncCornerRunsFromSourceModule({
823
- module: sceneApi.get(module.id) ?? module,
824
- previousModule,
825
- run: sceneApi.get(parentRun.id) ?? parentRun,
826
- sceneApi,
827
- });
828
- return;
829
- }
830
- const modulePatch = { ...patch };
831
- if (typeof patch.depth === 'number') {
832
- modulePatch.position = [
833
- patch.position?.[0] ?? module.position[0],
834
- patch.position?.[1] ?? module.position[1],
835
- backAnchoredModuleZ(module.position[2], module.depth, patch.depth),
836
- ];
837
- }
838
- sceneApi.update(module.id, modulePatch);
839
- if (resolveCabinetType(module, parentRun) === 'base') {
840
- const runPatch = {};
841
- if (typeof patch.carcassHeight === 'number') {
842
- runPatch.carcassHeight = patch.carcassHeight;
843
- }
844
- if (Object.keys(runPatch).length > 0) {
845
- commitRunResize(parentRun, runPatch, sceneApi);
846
- }
847
- else {
848
- bumpCabinetRunLayoutRevision(sceneApi, parentRun);
849
- }
850
- }
851
- else {
852
- bumpCabinetRunLayoutRevision(sceneApi, parentRun);
853
- }
854
- syncCornerRunsFromSourceModule({
855
- module: sceneApi.get(module.id) ?? module,
856
- previousModule: module,
857
- run: sceneApi.get(parentRun.id) ?? parentRun,
858
- sceneApi,
859
- });
860
- const wallChild = wallChildOf(module, sceneApi.nodes());
861
- if (wallChild && typeof modulePatch.depth === 'number') {
862
- sceneApi.update(wallChild.id, {
863
- position: [
864
- wallChild.position[0],
865
- wallChild.position[1],
866
- backAlignZ(modulePatch.depth, wallChild.depth),
867
- ],
868
- });
869
- }
870
- }
871
- function commitCabinetResize(node, patch, sceneApi) {
872
- const liveNode = sceneApi.get(node.id) ?? node;
873
- if (isCabinetRun(liveNode)) {
874
- commitRunResize(liveNode, patch, sceneApi);
875
- return;
876
- }
877
- if (isCabinetModule(liveNode)) {
878
- commitModuleResize(liveNode, patch, sceneApi);
879
- return;
880
- }
881
- sceneApi.update(node.id, patch);
882
- }
883
- function cabinetManualWidthContext(node, sceneApi) {
884
- const parent = node.parentId ? sceneApi.get(node.parentId) : undefined;
885
- if (isCabinetRun(parent)) {
886
- return { run: parent, selected: node, modules: cabinetModulesForRun(parent, sceneApi.nodes()) };
887
- }
888
- if (!isCabinetModule(parent))
889
- return null;
890
- const run = parent.parentId ? sceneApi.get(parent.parentId) : undefined;
891
- if (!isCabinetRun(run) || wallChildOf(parent, sceneApi.nodes())?.id !== node.id)
892
- return null;
893
- return { run, selected: parent, modules: cabinetModulesForRun(run, sceneApi.nodes()) };
894
- }
895
- function cabinetWidthIsNestedModule(node, sceneApi) {
896
- const context = cabinetManualWidthContext(node, sceneApi);
897
- return context !== null && context.selected.id !== node.id;
898
- }
899
- function snapCabinetWidth(node, width, side, sceneApi) {
900
- const context = cabinetManualWidthContext(node, sceneApi);
901
- if (!context)
902
- return width;
903
- const sorted = sortRunModules(context.modules);
904
- const selected = context.selected;
905
- const selectedIndex = sorted.findIndex((module) => module.id === selected.id);
906
- const neighborHost = sorted[side === 'right' ? selectedIndex + 1 : selectedIndex - 1];
907
- if (!neighborHost)
908
- return width;
909
- const nested = selected.id !== node.id;
910
- const snapTarget = nested ? wallChildOf(neighborHost, sceneApi.nodes()) : neighborHost;
911
- if (!snapTarget)
912
- return width;
913
- const selectedCenter = nested
914
- ? cabinetModuleRunLocalCenterX(node, sceneApi)
915
- : selected.position[0];
916
- const targetCenter = nested
917
- ? cabinetModuleRunLocalCenterX(snapTarget, sceneApi)
918
- : snapTarget.position[0];
919
- if (selectedCenter === null || targetCenter === null)
920
- return width;
921
- const selectedEdge = selectedCenter + (side === 'right' ? node.width / 2 : -node.width / 2);
922
- const neighborEdge = targetCenter + (side === 'right' ? -snapTarget.width / 2 : snapTarget.width / 2);
923
- const gap = side === 'right' ? neighborEdge - selectedEdge : selectedEdge - neighborEdge;
924
- if (gap <= CABINET_ADJACENCY_EPSILON)
925
- return width;
926
- const targetWidth = node.width + gap - (nested ? 0 : cabinetWallWidthGap(node, side, sceneApi));
927
- return targetWidth > node.width && Math.abs(width - targetWidth) <= CABINET_WIDTH_SNAP_THRESHOLD
928
- ? targetWidth
929
- : width;
930
- }
931
- function cabinetIndependentWidthPatch(node, width, side, sceneApi) {
932
- const sign = side === 'right' ? 1 : -1;
933
- const gap = cabinetWallWidthGap(node, side, sceneApi);
934
- const effectiveWidth = width + gap;
935
- return {
936
- width: effectiveWidth,
937
- position: [
938
- node.position[0] + (sign * (effectiveWidth - node.width)) / 2,
939
- node.position[1],
940
- node.position[2],
941
- ],
942
- };
943
- }
944
- function cabinetIndependentWidthPreviewOverrides(node, width, side, sceneApi) {
945
- const overrides = [];
946
- overrides.push([
947
- node.id,
948
- cabinetIndependentWidthPatch(node, width, side, sceneApi),
949
- ]);
950
- const parentRunOverride = parentRunGeometryPreviewOverride(node, sceneApi);
951
- if (parentRunOverride)
952
- overrides.push(parentRunOverride);
953
- const gap = cabinetWallWidthGap(node, side, sceneApi);
954
- const selectedWallOverride = wallCabinetWidthOverride(node, width + gap, sceneApi);
955
- if (selectedWallOverride)
956
- overrides.push(selectedWallOverride);
957
- return overrides;
958
- }
959
- function previewCabinetCornerWidthOverrides(node, initialOverrides, sceneApi) {
960
- const parent = node.parentId
961
- ? sceneApi.get(node.parentId)
962
- : undefined;
963
- if (!parent || !isCabinetRun(parent))
964
- return initialOverrides;
965
- return previewCornerRunsFromRunSources({
966
- baseLayout: 'width-only',
967
- initialOverrides,
968
- previousModules: cabinetModulesForRun(parent, sceneApi.nodes()),
969
- run: parent,
970
- sceneApi,
971
- });
972
- }
973
- function cabinetManualWidthReflow(node, width, side, sceneApi) {
974
- const context = cabinetManualWidthContext(node, sceneApi);
975
- if (!context)
976
- return null;
977
- const wallGap = cabinetWallWidthGap(node, side, sceneApi);
978
- const selectedWidth = width + wallGap;
979
- const runConstraints = runWallConstraints(context.run, context.modules, sceneApi.nodes(), { widthGrowth: Math.max(0, selectedWidth - context.selected.width) });
980
- const draggedEnd = side === 'right' ? runConstraints.right : runConstraints.left;
981
- const clampedSelectedWidth = selectedWidth > context.selected.width && draggedEnd.constrained
982
- ? Math.min(selectedWidth, context.selected.width + draggedEnd.slack)
983
- : selectedWidth;
984
- const reflowed = reflowCabinetRunModules(context.modules, context.selected.id, clampedSelectedWidth, {
985
- resizeSide: side,
986
- consumeAdjacentGap: true,
987
- eligibleDonorIds: new Set(),
988
- maximumWidth: MAX_CABINET_WIDTH,
989
- });
990
- return reflowed.length > 0 ? { ...context, reflowed, wallGap } : null;
991
- }
992
- function commitCabinetManualWidth(node, width, side, sceneApi) {
993
- const reflow = cabinetManualWidthReflow(node, width, side, sceneApi);
994
- if (!reflow)
995
- return;
996
- const reflowById = new Map(reflow.reflowed.map((entry) => [entry.id, entry]));
997
- for (const module of reflow.modules) {
998
- const next = reflowById.get(module.id);
999
- if (!next)
1000
- continue;
1001
- const isSelected = module.id === reflow.selected.id;
1002
- const modulePatch = {
1003
- width: next.width,
1004
- position: next.position,
1005
- };
1006
- if (isSelected) {
1007
- modulePatch.metadata = metadataForSelectedWidth(module, next.width);
1008
- }
1009
- else if (Math.abs(next.width - module.width) > 1e-4) {
1010
- modulePatch.metadata = metadataWithPresetWidthDebt(module, reflow.selected.id, next.width - module.width);
1011
- }
1012
- const nestedCornerOverrides = nestedCornerRunPositionOverrides(module, next.position, sceneApi.nodes());
1013
- sceneApi.update(module.id, modulePatch);
1014
- for (const [id, override] of nestedCornerOverrides) {
1015
- sceneApi.update(id, override);
1016
- }
1017
- const wallChild = wallChildOf(module, sceneApi.nodes());
1018
- if (wallChild) {
1019
- sceneApi.update(wallChild.id, {
1020
- width: next.width,
1021
- position: [0, wallChild.position[1], backAlignZ(module.depth, wallChild.depth)],
1022
- });
1023
- }
1024
- }
1025
- syncCornerRunsFromRunSources({
1026
- baseLayout: 'width-only',
1027
- previousModules: reflow.modules,
1028
- run: sceneApi.get(reflow.run.id) ?? reflow.run,
1029
- sceneApi,
1030
- });
1031
- bumpCabinetRunLayoutRevision(sceneApi, reflow.run);
1032
- }
1033
- function cabinetWidthHandle(side) {
1034
- const sign = side === 'right' ? 1 : -1;
1035
- return {
1036
- kind: 'linear-resize',
1037
- axis: 'x',
1038
- anchor: side === 'right' ? 'min' : 'max',
1039
- min: (node, sceneApi) => {
1040
- if (!isCabinetModule(node))
1041
- return MIN_CABINET_WIDTH;
1042
- const gap = cabinetWidthIsNestedModule(node, sceneApi)
1043
- ? 0
1044
- : cabinetWallWidthGap(node, side, sceneApi);
1045
- return MIN_CABINET_WIDTH - gap;
1046
- },
1047
- max: (node, sceneApi) => {
1048
- const ownMax = cabinetResizeUpperBound(node.width, MAX_CABINET_WIDTH);
1049
- if (!isCabinetModule(node))
1050
- return ownMax;
1051
- const gap = cabinetWidthIsNestedModule(node, sceneApi)
1052
- ? 0
1053
- : cabinetWallWidthGap(node, side, sceneApi);
1054
- return ownMax - gap;
1055
- },
1056
- magneticSnap: (node, width, sceneApi) => isCabinetModule(node) ? snapCabinetWidth(node, width, side, sceneApi) : width,
1057
- currentValue: (node) => node.width,
1058
- apply: (node, width, sceneApi, modifiers) => {
1059
- if (isCabinetModule(node) && modifiers?.altKey) {
1060
- return cabinetIndependentWidthPatch(node, width, side, sceneApi);
1061
- }
1062
- if (isCabinetModule(node) &&
1063
- cabinetManualWidthContext(node, sceneApi) &&
1064
- !cabinetWidthIsNestedModule(node, sceneApi)) {
1065
- const reflow = cabinetManualWidthReflow(node, width, side, sceneApi);
1066
- if (reflow) {
1067
- const selected = reflow.reflowed.find((entry) => entry.id === reflow.selected.id);
1068
- if (selected)
1069
- return { width: selected.width, position: selected.position };
1070
- }
1071
- return { width: node.width, position: node.position };
1072
- }
1073
- const gap = isCabinetModule(node) && !cabinetWidthIsNestedModule(node, sceneApi)
1074
- ? cabinetWallWidthGap(node, side, sceneApi)
1075
- : 0;
1076
- const effectiveWidth = width + gap;
1077
- return {
1078
- width: effectiveWidth,
1079
- position: [
1080
- node.position[0] + (sign * (effectiveWidth - node.width)) / 2,
1081
- node.position[1],
1082
- node.position[2],
1083
- ],
1084
- };
1085
- },
1086
- previewOverrides: (node, width, sceneApi, modifiers) => {
1087
- if (!isCabinetModule(node))
1088
- return [];
1089
- if (modifiers?.altKey) {
1090
- return previewCabinetCornerWidthOverrides(node, cabinetIndependentWidthPreviewOverrides(node, width, side, sceneApi), sceneApi);
1091
- }
1092
- if (cabinetWidthIsNestedModule(node, sceneApi)) {
1093
- const parentRunOverride = parentRunGeometryPreviewOverride(node, sceneApi);
1094
- return parentRunOverride ? [parentRunOverride] : [];
1095
- }
1096
- const reflow = cabinetManualWidthReflow(node, width, side, sceneApi);
1097
- if (reflow) {
1098
- const overrides = [];
1099
- const parentRunOverride = parentRunGeometryPreviewOverride(node, sceneApi);
1100
- if (parentRunOverride)
1101
- overrides.push(parentRunOverride);
1102
- for (const entry of reflow.reflowed) {
1103
- const module = reflow.modules.find((candidate) => candidate.id === entry.id);
1104
- if (!module)
1105
- continue;
1106
- overrides.push([
1107
- module.id,
1108
- { width: entry.width, position: entry.position },
1109
- ]);
1110
- overrides.push(...nestedCornerRunPositionOverrides(module, entry.position, sceneApi.nodes()));
1111
- const wallChild = wallChildOf(module, sceneApi.nodes());
1112
- if (wallChild) {
1113
- overrides.push([
1114
- wallChild.id,
1115
- {
1116
- width: entry.width,
1117
- position: [0, wallChild.position[1], backAlignZ(module.depth, wallChild.depth)],
1118
- },
1119
- ]);
1120
- }
1121
- }
1122
- return previewCabinetCornerWidthOverrides(node, overrides, sceneApi);
1123
- }
1124
- if (cabinetManualWidthContext(node, sceneApi)) {
1125
- const parentRunOverride = parentRunGeometryPreviewOverride(node, sceneApi);
1126
- return parentRunOverride ? [parentRunOverride] : [];
1127
- }
1128
- const overrides = [];
1129
- const parentRunOverride = parentRunGeometryPreviewOverride(node, sceneApi);
1130
- if (parentRunOverride)
1131
- overrides.push(parentRunOverride);
1132
- const gap = cabinetWallWidthGap(node, side, sceneApi);
1133
- const effectiveWidth = width + gap;
1134
- const selectedPosition = [
1135
- node.position[0] + (sign * (effectiveWidth - node.width)) / 2,
1136
- node.position[1],
1137
- node.position[2],
1138
- ];
1139
- overrides.push([
1140
- node.id,
1141
- {
1142
- width: effectiveWidth,
1143
- position: selectedPosition,
1144
- },
1145
- ]);
1146
- overrides.push(...nestedCornerRunPositionOverrides(node, selectedPosition, sceneApi.nodes()));
1147
- const selectedWallOverride = wallCabinetWidthOverride(node, width + gap, sceneApi);
1148
- if (selectedWallOverride)
1149
- overrides.push(selectedWallOverride);
1150
- const connectedResize = connectedCabinetWidthResize(node, side, width - node.width, sceneApi);
1151
- if (connectedResize) {
1152
- overrides.push([
1153
- connectedResize.module.id,
1154
- connectedResize.patch,
1155
- ]);
1156
- overrides.push(...nestedCornerRunPositionOverrides(connectedResize.module, connectedResize.patch.position, sceneApi.nodes()));
1157
- const connectedWallOverride = wallCabinetWidthOverride(connectedResize.module, connectedResize.patch.width, sceneApi);
1158
- if (connectedWallOverride)
1159
- overrides.push(connectedWallOverride);
1160
- }
1161
- return previewCabinetCornerWidthOverrides(node, overrides, sceneApi);
1162
- },
1163
- commit: (node, patch, sceneApi, modifiers) => {
1164
- if (isCabinetModule(node) && typeof patch.width === 'number' && modifiers?.altKey) {
1165
- commitCabinetResize(node, {
1166
- ...patch,
1167
- metadata: metadataForSelectedWidth(node, patch.width, patch.metadata),
1168
- }, sceneApi);
1169
- return;
1170
- }
1171
- if (isCabinetModule(node) && typeof patch.width === 'number') {
1172
- if (cabinetWidthIsNestedModule(node, sceneApi)) {
1173
- commitCabinetResize(node, patch, sceneApi);
1174
- return;
1175
- }
1176
- commitCabinetManualWidth(node, patch.width - cabinetWallWidthGap(node, side, sceneApi), side, sceneApi);
1177
- return;
1178
- }
1179
- const connectedResize = isCabinetModule(node) && typeof patch.width === 'number'
1180
- ? connectedCabinetWidthResize(node, side, patch.width - node.width - cabinetWallWidthGap(node, side, sceneApi), sceneApi)
1181
- : null;
1182
- const selectedPatch = isCabinetModule(node) && typeof patch.width === 'number'
1183
- ? {
1184
- ...patch,
1185
- metadata: metadataForSelectedWidth(node, patch.width, patch.metadata),
1186
- }
1187
- : patch;
1188
- commitCabinetResize(node, selectedPatch, sceneApi);
1189
- if (connectedResize) {
1190
- const widthDelta = connectedResize.patch.width - connectedResize.module.width;
1191
- commitCabinetResize(connectedResize.module, {
1192
- ...connectedResize.patch,
1193
- metadata: metadataWithPresetWidthDebt(connectedResize.module, node.id, widthDelta),
1194
- }, sceneApi);
1195
- }
1196
- },
1197
- visible: (node, sceneApi) => !isCabinetModule(node) || cabinetModuleSideOpen(node, side, sceneApi),
1198
- placement: {
1199
- position: (node) => [
1200
- sign * (node.width / 2 + SIDE_HANDLE_OFFSET),
1201
- cabinetTotalHeight(node) / 2,
1202
- 0,
1203
- ],
1204
- rotationY: () => (side === 'left' ? Math.PI : 0),
1205
- },
1206
- };
1207
- }
1208
- function cabinetDepthResizePatch(node, depth) {
1209
- return {
1210
- depth,
1211
- position: [node.position[0], node.position[1], node.position[2] + (depth - node.depth) / 2],
1212
- };
1213
- }
1214
- function snapCabinetDepth(node, requestedDepth, sceneApi) {
1215
- if (!isCabinetModule(node))
1216
- return requestedDepth;
1217
- let snappedDepth = requestedDepth;
1218
- let closestDistance = Number.POSITIVE_INFINITY;
1219
- for (const side of ['left', 'right']) {
1220
- const connected = cabinetWidthConnectedNeighbor(node, side, sceneApi);
1221
- if (!connected || isCabinetWidthFiller(connected))
1222
- continue;
1223
- const distance = Math.abs(requestedDepth - connected.depth);
1224
- if (distance <= CABINET_DEPTH_SNAP_THRESHOLD && distance < closestDistance) {
1225
- snappedDepth = connected.depth;
1226
- closestDistance = distance;
1227
- }
1228
- }
1229
- return snappedDepth;
1230
- }
1231
- function cabinetDepthHandle() {
1232
- return {
1233
- kind: 'linear-resize',
1234
- axis: 'z',
1235
- anchor: 'min',
1236
- min: MIN_CABINET_DEPTH,
1237
- max: (node) => cabinetResizeUpperBound(node.depth, MAX_CABINET_DEPTH),
1238
- currentValue: (node) => node.depth,
1239
- apply: cabinetDepthResizePatch,
1240
- magneticSnap: snapCabinetDepth,
1241
- previewOverrides: (node, _depth, sceneApi) => {
1242
- const parentRunOverride = parentRunGeometryPreviewOverride(node, sceneApi);
1243
- return parentRunOverride ? [parentRunOverride] : [];
1244
- },
1245
- commit: commitCabinetResize,
1246
- placement: {
1247
- position: (node) => [0, cabinetTotalHeight(node) / 2, node.depth / 2 + SIDE_HANDLE_OFFSET],
1248
- },
1249
- };
1250
- }
1251
- function cornerBaseSourceRunId(node) {
1252
- const value = cabinetMetadataRecord(node.metadata).cabinetCornerDerivedRun;
1253
- if (!value || typeof value !== 'object' || Array.isArray(value))
1254
- return null;
1255
- const role = value.role;
1256
- const sourceRunId = value.sourceRunId;
1257
- return role === 'base-leg' && typeof sourceRunId === 'string' ? sourceRunId : null;
1258
- }
1259
- function connectedBaseRuns(node, sceneApi) {
1260
- const runs = new Map();
1261
- for (const candidate of Object.values(sceneApi.nodes())) {
1262
- if (isCabinetRun(candidate) && candidate.runTier === 'base') {
1263
- runs.set(candidate.id, candidate);
1264
- }
1265
- }
1266
- runs.set(node.id, node);
1267
- const neighbors = new Map();
1268
- const connect = (a, b) => {
1269
- const aNeighbors = neighbors.get(a) ?? new Set();
1270
- const bNeighbors = neighbors.get(b) ?? new Set();
1271
- aNeighbors.add(b);
1272
- bNeighbors.add(a);
1273
- neighbors.set(a, aNeighbors);
1274
- neighbors.set(b, bNeighbors);
1275
- };
1276
- for (const run of runs.values()) {
1277
- const sourceRunId = cornerBaseSourceRunId(run);
1278
- if (sourceRunId && runs.has(sourceRunId))
1279
- connect(sourceRunId, run.id);
1280
- }
1281
- const connected = [];
1282
- const pending = [node.id];
1283
- const visited = new Set();
1284
- while (pending.length > 0) {
1285
- const id = pending.shift();
1286
- if (visited.has(id))
1287
- continue;
1288
- visited.add(id);
1289
- const run = runs.get(id);
1290
- if (run)
1291
- connected.push(run);
1292
- for (const neighbor of neighbors.get(id) ?? [])
1293
- pending.push(neighbor);
1294
- }
1295
- return connected;
1296
- }
1297
- function cabinetRunFrontCenter(run, sceneApi) {
1298
- const modules = cabinetModulesForRun(run, sceneApi.nodes());
1299
- if (modules.length === 0) {
1300
- return [0, cabinetTotalHeight(run) / 2, run.depth / 2 + SIDE_HANDLE_OFFSET];
1301
- }
1302
- const minX = Math.min(...modules.map((module) => module.position[0] - module.width / 2));
1303
- const maxX = Math.max(...modules.map((module) => module.position[0] + module.width / 2));
1304
- const maxZ = Math.max(...modules.map((module) => module.position[2] + module.depth / 2));
1305
- return [(minX + maxX) / 2, cabinetTotalHeight(run) / 2, maxZ + SIDE_HANDLE_OFFSET];
1306
- }
1307
- function cabinetRunPointInSelectedFrame(selected, target, point, sceneApi) {
1308
- const nodes = sceneApi.nodes();
1309
- const selectedWorld = cabinetDuplicateWorldPose(selected, nodes);
1310
- const targetWorld = cabinetDuplicateWorldPose(target, nodes);
1311
- if (!selectedWorld || !targetWorld)
1312
- return [point[0], point[1], point[2]];
1313
- const worldPoint = composeCabinetDuplicatePose(targetWorld.position, targetWorld.rotation, point, 0).position;
1314
- const dx = worldPoint[0] - selectedWorld.position[0];
1315
- const dz = worldPoint[2] - selectedWorld.position[2];
1316
- const cos = Math.cos(selectedWorld.rotation);
1317
- const sin = Math.sin(selectedWorld.rotation);
1318
- return [cos * dx - sin * dz, worldPoint[1] - selectedWorld.position[1], sin * dx + cos * dz];
1319
- }
1320
- function cabinetRootRun(node, sceneApi) {
1321
- let current = node;
1322
- let root = isCabinetRun(current) ? current : null;
1323
- const visited = new Set();
1324
- while (current.parentId && !visited.has(current.parentId)) {
1325
- visited.add(current.parentId);
1326
- const parent = sceneApi.get(current.parentId);
1327
- if (!isCabinetRun(parent) && !isCabinetModule(parent))
1328
- break;
1329
- current = parent;
1330
- if (isCabinetRun(current))
1331
- root = current;
1332
- }
1333
- return root;
1334
- }
1335
- function cabinetWallTargets(node, sceneApi) {
1336
- const root = cabinetRootRun(node, sceneApi);
1337
- if (!root)
1338
- return [];
1339
- const targets = [];
1340
- const visited = new Set();
1341
- const visit = (current) => {
1342
- if (visited.has(current.id))
1343
- return;
1344
- visited.add(current.id);
1345
- if (isCabinetRun(current) && current.runTier === 'wall') {
1346
- targets.push(current);
1347
- return;
1348
- }
1349
- const parent = current.parentId ? sceneApi.get(current.parentId) : undefined;
1350
- if (isCabinetModule(current) && isCabinetModule(parent)) {
1351
- if (!isHoodOnlyCabinet(current))
1352
- targets.push(current);
1353
- return;
1354
- }
1355
- for (const childId of current.children ?? []) {
1356
- const child = sceneApi.get(childId);
1357
- if (isCabinetRun(child) || isCabinetModule(child))
1358
- visit(child);
1359
- }
1360
- };
1361
- visit(root);
1362
- return targets;
1363
- }
1364
- function cabinetWallDepthPreview(targets, depth, sceneApi, adjustCornerWidths, widthMode, cornerIndex) {
1365
- const overrides = new Map();
1366
- const liveTargets = targets.map((target) => sceneApi.get(target.id) ?? target);
1367
- const depthDelta = depth - (liveTargets[0]?.depth ?? depth);
1368
- for (const live of liveTargets) {
1369
- const nextDepth = live.depth + depthDelta;
1370
- if (isCabinetRun(live)) {
1371
- overrides.set(live.id, { depth: nextDepth });
1372
- for (const [id, patch] of depthDeltaRunOverrides(live, sceneApi.nodes(), depthDelta)) {
1373
- overrides.set(id, { ...(overrides.get(id) ?? {}), ...patch });
1374
- }
1375
- continue;
1376
- }
1377
- overrides.set(live.id, cabinetDepthResizePatch(live, nextDepth));
1378
- }
1379
- if (adjustCornerWidths) {
1380
- for (const [id, patch] of wallCornerWidthOverridesForDepthTargets({
1381
- cornerIndex,
1382
- depth,
1383
- nodes: sceneApi.nodes(),
1384
- targets: liveTargets,
1385
- widthMode,
1386
- })) {
1387
- const existing = overrides.get(id);
1388
- const cornerPatch = patch;
1389
- const merged = { ...(existing ?? {}), ...cornerPatch };
1390
- if (existing?.position && cornerPatch.position) {
1391
- merged.position = [cornerPatch.position[0], cornerPatch.position[1], existing.position[2]];
1392
- }
1393
- overrides.set(id, merged);
1394
- }
1395
- }
1396
- return [...overrides];
1397
- }
1398
- function commitCabinetWallDepth(targets, depth, sceneApi, adjustCornerWidths, widthMode, cornerIndex) {
1399
- const preview = cabinetWallDepthPreview(targets, depth, sceneApi, adjustCornerWidths, widthMode, cornerIndex);
1400
- for (const [id, patch] of preview) {
1401
- sceneApi.update(id, patch);
1402
- }
1403
- const bumpedRuns = new Set();
1404
- for (const [id] of preview) {
1405
- const live = sceneApi.get(id);
1406
- if (isCabinetRun(live)) {
1407
- if (!bumpedRuns.has(live.id)) {
1408
- bumpedRuns.add(live.id);
1409
- bumpCabinetRunLayoutRevision(sceneApi, live);
1410
- }
1411
- continue;
1412
- }
1413
- const parent = live?.parentId ? sceneApi.get(live.parentId) : undefined;
1414
- if (isCabinetRun(parent) && !bumpedRuns.has(parent.id)) {
1415
- bumpedRuns.add(parent.id);
1416
- bumpCabinetRunLayoutRevision(sceneApi, parent);
1417
- }
1418
- else if (isCabinetModule(parent)) {
1419
- sceneApi.markDirty(parent.id);
1420
- }
1421
- }
1422
- }
1423
- function cabinetWallDepthBounds(targets, sceneApi, adjustCornerWidths, widthMode, cornerIndex) {
1424
- const liveTargets = targets.map((target) => sceneApi.get(target.id) ?? target);
1425
- const currentDepth = liveTargets[0]?.depth ?? MIN_CABINET_DEPTH;
1426
- const sharedBounds = sharedDepthBounds(currentDepth, liveTargets, sceneApi.nodes());
1427
- let min = sharedBounds.min;
1428
- let max = sharedBounds.max;
1429
- if (!adjustCornerWidths)
1430
- return { min, max };
1431
- const baselineAdjustments = new Map(wallCornerWidthOverridesForDepthTargets({
1432
- clampWidths: false,
1433
- cornerIndex,
1434
- depth: currentDepth,
1435
- nodes: sceneApi.nodes(),
1436
- targets: liveTargets,
1437
- widthMode,
1438
- }));
1439
- const unitAdjustments = wallCornerWidthOverridesForDepthTargets({
1440
- clampWidths: false,
1441
- cornerIndex,
1442
- depth: currentDepth + 1,
1443
- nodes: sceneApi.nodes(),
1444
- targets: liveTargets,
1445
- widthMode,
1446
- });
1447
- for (const [id, patch] of unitAdjustments) {
1448
- const cabinetPatch = patch;
1449
- if (typeof cabinetPatch.width !== 'number')
1450
- continue;
1451
- const node = sceneApi.get(id);
1452
- if (!isCabinetModule(node))
1453
- continue;
1454
- const baselinePatch = baselineAdjustments.get(id);
1455
- const baselineWidth = typeof baselinePatch?.width === 'number' ? baselinePatch.width : node.width;
1456
- const factor = cabinetPatch.width - baselineWidth;
1457
- if (Math.abs(factor) <= CABINET_ADJACENCY_EPSILON)
1458
- continue;
1459
- const minWidth = node.name === 'Wall Bridge Filler'
1460
- ? 0
1461
- : node.name?.includes('Filler')
1462
- ? 0.05
1463
- : MIN_CABINET_WIDTH;
1464
- const maxWidth = cabinetResizeUpperBound(baselineWidth, MAX_CABINET_WIDTH);
1465
- const firstDepth = currentDepth + (minWidth - baselineWidth) / factor;
1466
- const secondDepth = currentDepth + (maxWidth - baselineWidth) / factor;
1467
- min = Math.max(min, Math.min(firstDepth, secondDepth));
1468
- max = Math.min(max, Math.max(firstDepth, secondDepth));
1469
- }
1470
- return {
1471
- min: Math.min(currentDepth, min),
1472
- max: Math.max(currentDepth, max),
1473
- };
1474
- }
1475
- function cabinetWallGroupDepthHandles(selected, sceneApi) {
1476
- const targets = cabinetWallTargets(selected, sceneApi);
1477
- if (targets.length < 2)
1478
- return [];
1479
- const cornerIndex = buildWallCornerDepthIndex(sceneApi.nodes());
1480
- const groups = [];
1481
- for (const target of targets) {
1482
- const rotation = cabinetDuplicateWorldPose(target, sceneApi.nodes())?.rotation ?? target.rotation;
1483
- const group = groups.find((candidate) => Math.abs(Math.atan2(Math.sin(rotation - candidate.rotation), Math.cos(rotation - candidate.rotation))) < 1e-3);
1484
- if (group)
1485
- group.targets.push(target);
1486
- else
1487
- groups.push({ rotation, targets: [target] });
1488
- }
1489
- const selectedRotation = cabinetDuplicateWorldPose(selected, sceneApi.nodes())?.rotation ?? 0;
1490
- return groups.map((group) => {
1491
- const representative = group.targets[0];
1492
- const relativeRotation = group.rotation - selectedRotation;
1493
- const frontX = Math.sin(relativeRotation);
1494
- const frontZ = Math.cos(relativeRotation);
1495
- const axis = Math.abs(frontX) > Math.abs(frontZ) ? 'x' : 'z';
1496
- const positive = axis === 'x' ? frontX >= 0 : frontZ >= 0;
1497
- const adjustCornerWidths = true;
1498
- const widthMode = Math.abs(frontX) < 1e-3 && frontZ > 0 ? 'corner-pair' : 'bridge';
1499
- const depthBounds = () => cabinetWallDepthBounds(group.targets, sceneApi, adjustCornerWidths, widthMode, cornerIndex);
1500
- const clampedDepth = (requestedDepth, liveSceneApi) => {
1501
- const bounds = cabinetWallDepthBounds(group.targets, liveSceneApi, adjustCornerWidths, widthMode, cornerIndex);
1502
- return Math.min(bounds.max, Math.max(bounds.min, requestedDepth));
1503
- };
1504
- return {
1505
- kind: 'linear-resize',
1506
- axis,
1507
- anchor: positive ? 'min' : 'max',
1508
- min: () => depthBounds().min,
1509
- max: () => depthBounds().max,
1510
- currentValue: () => sceneApi.get(representative.id)?.depth ??
1511
- representative.depth,
1512
- overrideTarget: () => representative.id,
1513
- apply: (_node, depth) => ({ depth }),
1514
- previewOverrides: (_node, depth, liveSceneApi) => cabinetWallDepthPreview(group.targets, clampedDepth(depth, liveSceneApi), liveSceneApi, adjustCornerWidths, widthMode, cornerIndex),
1515
- commit: (_node, patch, liveSceneApi) => {
1516
- if (typeof patch.depth === 'number') {
1517
- commitCabinetWallDepth(group.targets, clampedDepth(patch.depth, liveSceneApi), liveSceneApi, adjustCornerWidths, widthMode, cornerIndex);
1518
- }
1519
- },
1520
- placement: {
1521
- position: (node, liveSceneApi) => {
1522
- const points = group.targets.map((target) => {
1523
- const liveTarget = liveSceneApi.get(target.id) ?? target;
1524
- const point = isCabinetRun(liveTarget)
1525
- ? cabinetRunFrontCenter(liveTarget, liveSceneApi)
1526
- : [
1527
- 0,
1528
- cabinetTotalHeight(liveTarget) / 2,
1529
- liveTarget.depth / 2 + SIDE_HANDLE_OFFSET,
1530
- ];
1531
- return cabinetRunPointInSelectedFrame(node, liveTarget, point, liveSceneApi);
1532
- });
1533
- return [
1534
- points.reduce((sum, point) => sum + point[0], 0) / points.length,
1535
- points.reduce((sum, point) => sum + point[1], 0) / points.length,
1536
- points.reduce((sum, point) => sum + point[2], 0) / points.length,
1537
- ];
1538
- },
1539
- rotationY: () => (axis === 'x' ? relativeRotation - Math.PI / 2 : relativeRotation),
1540
- },
1541
- };
1542
- });
1543
- }
1544
- function cabinetConnectedRunDepthHandle(selected, target, sceneApi) {
1545
- const nodes = sceneApi.nodes();
1546
- const selectedWorld = cabinetDuplicateWorldPose(selected, nodes);
1547
- const targetWorld = cabinetDuplicateWorldPose(target, nodes);
1548
- const relativeRotation = (targetWorld?.rotation ?? 0) - (selectedWorld?.rotation ?? 0);
1549
- const frontX = Math.sin(relativeRotation);
1550
- const frontZ = Math.cos(relativeRotation);
1551
- const axis = Math.abs(frontX) > Math.abs(frontZ) ? 'x' : 'z';
1552
- const positive = axis === 'x' ? frontX >= 0 : frontZ >= 0;
1553
- const targetDepthBounds = (liveSceneApi) => {
1554
- const liveTarget = liveSceneApi.get(target.id) ?? target;
1555
- const compensatedWidths = [];
1556
- const derived = cabinetMetadataRecord(liveTarget.metadata).cabinetCornerDerivedRun;
1557
- if (derived && typeof derived === 'object' && !Array.isArray(derived)) {
1558
- const role = derived.role;
1559
- const side = derived.side;
1560
- const turnSide = derived.turnSide;
1561
- const sourceModuleId = derived.sourceModuleId;
1562
- const sourceModule = role === 'base-leg' &&
1563
- (turnSide === side || (turnSide !== 'left' && turnSide !== 'right')) &&
1564
- typeof sourceModuleId === 'string'
1565
- ? liveSceneApi.get(sourceModuleId)
1566
- : undefined;
1567
- if (sourceModule?.type === 'cabinet-module') {
1568
- compensatedWidths.push(sourceModule.width);
1569
- }
1570
- }
1571
- for (const childId of liveTarget.children ?? []) {
1572
- const child = liveSceneApi.get(childId);
1573
- if (child?.type !== 'cabinet')
1574
- continue;
1575
- const childDerived = cabinetMetadataRecord(child.metadata).cabinetCornerDerivedRun;
1576
- if (!childDerived || typeof childDerived !== 'object' || Array.isArray(childDerived))
1577
- continue;
1578
- if (childDerived.role !== 'base-leg' ||
1579
- childDerived.sourceRunId !== target.id) {
1580
- continue;
1581
- }
1582
- const connectedModule = cabinetModulesForRun(child, liveSceneApi.nodes()).find((module) => module.name === 'Base Cabinet');
1583
- if (connectedModule)
1584
- compensatedWidths.push(connectedModule.width);
1585
- }
1586
- const connectedBounds = cabinetConnectedDepthBounds(liveTarget.depth, compensatedWidths);
1587
- const deltaBounds = sharedDepthBounds(liveTarget.depth, [liveTarget], liveSceneApi.nodes());
1588
- return {
1589
- min: Math.max(connectedBounds.min, deltaBounds.min),
1590
- max: Math.min(connectedBounds.max, deltaBounds.max),
1591
- };
1592
- };
1593
- const clampedDepth = (depth, liveSceneApi) => {
1594
- const bounds = targetDepthBounds(liveSceneApi);
1595
- return Math.min(bounds.max, Math.max(bounds.min, depth));
1596
- };
1597
- return {
1598
- kind: 'linear-resize',
1599
- axis,
1600
- anchor: positive ? 'min' : 'max',
1601
- min: (_node, liveSceneApi) => targetDepthBounds(liveSceneApi).min,
1602
- max: (_node, liveSceneApi) => targetDepthBounds(liveSceneApi).max,
1603
- currentValue: (node) => node.id === target.id
1604
- ? node.depth
1605
- : (sceneApi.get(target.id)?.depth ?? target.depth),
1606
- overrideTarget: () => target.id,
1607
- apply: (_node, depth) => ({ depth }),
1608
- previewOverrides: (_node, depth, liveSceneApi) => {
1609
- const liveTarget = liveSceneApi.get(target.id) ?? target;
1610
- const nextDepth = clampedDepth(depth, liveSceneApi);
1611
- const moduleOverrides = depthDeltaRunOverrides(liveTarget, liveSceneApi.nodes(), nextDepth - liveTarget.depth);
1612
- const sourceOverrides = cornerSourceWidthOverridesForDerivedDepth(liveTarget, liveSceneApi.nodes(), nextDepth);
1613
- return previewCornerRunsFromRunSources({
1614
- baseLayout: 'width-only',
1615
- initialOverrides: [...moduleOverrides, ...sourceOverrides],
1616
- run: { ...liveTarget, depth: nextDepth },
1617
- sceneApi: liveSceneApi,
1618
- });
1619
- },
1620
- commit: (_node, patch, liveSceneApi) => {
1621
- if (typeof patch.depth !== 'number')
1622
- return;
1623
- const liveTarget = liveSceneApi.get(target.id) ?? target;
1624
- const nextDepth = clampedDepth(patch.depth, liveSceneApi);
1625
- for (const [id, sourcePatch] of cornerSourceWidthOverridesForDerivedDepth(liveTarget, liveSceneApi.nodes(), nextDepth)) {
1626
- liveSceneApi.update(id, sourcePatch);
1627
- }
1628
- commitRunDepthDelta(liveTarget, nextDepth, liveSceneApi, {
1629
- cornerSync: 'width-only',
1630
- });
1631
- },
1632
- placement: {
1633
- position: (node, liveSceneApi) => {
1634
- const liveTarget = liveSceneApi.get(target.id) ?? target;
1635
- return cabinetRunPointInSelectedFrame(node, liveTarget, cabinetRunFrontCenter(liveTarget, liveSceneApi), liveSceneApi);
1636
- },
1637
- rotationY: () => (axis === 'x' ? relativeRotation - Math.PI / 2 : relativeRotation),
1638
- },
1639
- };
1640
- }
1641
- function cabinetHeightHandle() {
1642
- return {
1643
- kind: 'linear-resize',
1644
- axis: 'y',
1645
- anchor: 'min',
1646
- min: MIN_CABINET_CARCASS_HEIGHT,
1647
- currentValue: (node) => node.carcassHeight,
1648
- apply: (_node, carcassHeight) => ({ carcassHeight }),
1649
- commit: commitCabinetResize,
1650
- placement: {
1651
- position: (node, sceneApi) => [
1652
- 0,
1653
- cabinetLocalBounds(node, sceneApi.nodes()).maxY + HEIGHT_HANDLE_OFFSET,
1654
- 0,
1655
- ],
1656
- },
1657
- };
1658
- }
1659
- function cabinetRotateHandle() {
1660
- return {
1661
- kind: 'arc-resize',
1662
- axis: 'angular',
1663
- shape: 'rotate',
1664
- apply: (initial, delta, sceneApi) => {
1665
- const rotation = (initial.rotation ?? 0) - delta;
1666
- const bounds = cabinetLocalBounds(initial, sceneApi.nodes());
1667
- const [centerX, , centerZ] = bounds.center;
1668
- const previousRotation = initial.rotation ?? 0;
1669
- const previousCos = Math.cos(previousRotation);
1670
- const previousSin = Math.sin(previousRotation);
1671
- const nextCos = Math.cos(rotation);
1672
- const nextSin = Math.sin(rotation);
1673
- const pivotWorldX = initial.position[0] + centerX * previousCos + centerZ * previousSin;
1674
- const pivotWorldZ = initial.position[2] - centerX * previousSin + centerZ * previousCos;
1675
- return {
1676
- rotation,
1677
- position: [
1678
- pivotWorldX - centerX * nextCos - centerZ * nextSin,
1679
- initial.position[1],
1680
- pivotWorldZ + centerX * nextSin - centerZ * nextCos,
1681
- ],
1682
- };
1683
- },
1684
- placement: {
1685
- position: (node, sceneApi) => {
1686
- const bounds = cabinetLocalBounds(node, sceneApi.nodes());
1687
- return [bounds.maxX, bounds.center[1], bounds.maxZ + ROTATE_CORNER_OFFSET];
1688
- },
1689
- rotationY: () => -Math.PI / 4,
1690
- },
1691
- rotationCenter: (node, sceneApi) => cabinetLocalBounds(node, sceneApi.nodes()).center,
1692
- decoration: {
1693
- kind: 'ring',
1694
- radius: (node, sceneApi) => {
1695
- const bounds = cabinetLocalBounds(node, sceneApi.nodes());
1696
- return Math.hypot(bounds.size[0] / 2, bounds.size[2] / 2) + ROTATE_RING_OFFSET;
1697
- },
1698
- y: (node) => cabinetTotalHeight(node) / 2,
1699
- center: (node, sceneApi) => cabinetLocalBounds(node, sceneApi.nodes()).center,
1700
- },
1701
- };
1702
- }
1703
- function cabinetHandles(node, sceneApi) {
1704
- const structuralChildren = (node.children ?? []).filter((id) => id.startsWith('cabinet_') || id.startsWith('cabinet-module_'));
1705
- if (structuralChildren.length > 0) {
1706
- const connectedRuns = sceneApi && node.runTier === 'base' ? connectedBaseRuns(node, sceneApi) : [];
1707
- const depthHandles = sceneApi
1708
- ? connectedRuns.map((run) => cabinetConnectedRunDepthHandle(node, run, sceneApi))
1709
- : [];
1710
- const wallDepthHandles = sceneApi ? cabinetWallGroupDepthHandles(node, sceneApi) : [];
1711
- return [
1712
- ...depthHandles,
1713
- ...wallDepthHandles,
1714
- cabinetRotateHandle(),
1715
- ];
1716
- }
1717
- const handles = [
1718
- cabinetDepthHandle(),
1719
- cabinetHeightHandle(),
1720
- cabinetRotateHandle(),
1721
- ];
1722
- if (structuralChildren.length === 0) {
1723
- handles.unshift(cabinetWidthHandle('left'), cabinetWidthHandle('right'));
1724
- }
1725
- return handles;
1726
- }
1727
- function isHoodOnlyCabinet(node) {
1728
- const stack = stackForCabinet(node);
1729
- return stack.length > 0 && stack.every((compartment) => isHoodCompartmentType(compartment.type));
1730
- }
1731
- function cabinetModuleHeightHandleVisible(node, sceneApi) {
1732
- const parent = node.parentId ? sceneApi.get(node.parentId) : undefined;
1733
- if (isCabinetRun(parent)) {
1734
- return parent.runTier === 'wall' || resolveCabinetType(node, parent) === 'tall';
1735
- }
1736
- return isCabinetModule(parent) && wallChildOf(parent, sceneApi.nodes())?.id === node.id;
1737
- }
1738
- function cabinetModuleHandles() {
1739
- return [
1740
- {
1741
- ...cabinetWidthHandle('left'),
1742
- visible: (node, sceneApi) => !isCabinetWidthFiller(node) &&
1743
- !cabinetModuleUsesFixedApplianceWidth(node) &&
1744
- !cabinetModuleSideHasCornerFiller(node, 'left', sceneApi),
1745
- },
1746
- {
1747
- ...cabinetWidthHandle('right'),
1748
- visible: (node, sceneApi) => !isCabinetWidthFiller(node) &&
1749
- !cabinetModuleUsesFixedApplianceWidth(node) &&
1750
- !cabinetModuleSideHasCornerFiller(node, 'right', sceneApi),
1751
- },
1752
- {
1753
- ...cabinetDepthHandle(),
1754
- visible: (node) => !isCabinetWidthFiller(node),
1755
- },
1756
- {
1757
- ...cabinetHeightHandle(),
1758
- visible: cabinetModuleHeightHandleVisible,
1759
- },
1760
- ];
1761
- }
1762
- export const cabinetDefinition = {
1763
- kind: 'cabinet',
1764
- schemaVersion: 8,
1765
- schema: CabinetNode,
1766
- category: 'furnish',
1767
- surfaceRole: 'joinery',
1768
- snapProfile: 'item',
1769
- facingIndicator: true,
1770
- defaults: () => ({
1771
- object: 'node',
1772
- parentId: null,
1773
- visible: true,
1774
- metadata: {},
1775
- position: [0, 0, 0],
1776
- rotation: 0,
1777
- runTier: 'base',
1778
- children: [],
1779
- width: 0.5,
1780
- depth: CABINET_METRIC_DEFAULTS.depth,
1781
- carcassHeight: CABINET_METRIC_DEFAULTS.carcassHeight,
1782
- operationState: 0,
1783
- plinthHeight: CABINET_METRIC_DEFAULTS.plinthHeight,
1784
- toeKickDepth: 0.075,
1785
- boardThickness: 0.018,
1786
- countertopThickness: CABINET_METRIC_DEFAULTS.countertopThickness,
1787
- countertopOverhang: 0.02,
1788
- countertopBackOverhang: 0,
1789
- withFinishedBack: false,
1790
- withWaterfall: false,
1791
- withFinishedEnds: false,
1792
- frontThickness: 0.018,
1793
- frontGap: 0.003,
1794
- frontStyle: 'slab',
1795
- panelReady: false,
1796
- handleStyle: 'bar',
1797
- handlePosition: 'auto',
1798
- frontOverlay: 'full',
1799
- withBottomPanel: true,
1800
- showPlinth: true,
1801
- withCountertop: true,
1802
- // material / materialPreset left undefined — paint mode writes slot refs.
1803
- }),
1804
- capabilities: {
1805
- batchable: cabinetBatchable,
1806
- reveal: { phase: 'furnishing', style: 'drop', height: 0.3 },
1807
- revealOnEdit: ['width', 'depth', 'carcassHeight', 'plinthHeight'],
1808
- surfacePlacement: 'floor-only',
1809
- selectable: { hitVolume: 'bbox' },
1810
- movable: {
1811
- axes: ['x', 'z'],
1812
- directDrag: true,
1813
- gridSnap: true,
1814
- gridSnapPosition: resolveCabinetMoveGridSnap,
1815
- groupMoveSnapPose: resolveCabinetGroupMoveSnap,
1816
- isValidPosition: ({ node, position, rotation, levelId, nodes }) => node.type !== 'cabinet' ||
1817
- !cabinetRunOverlapsWallOpening({
1818
- levelId,
1819
- node: node,
1820
- nodes: nodes,
1821
- position,
1822
- rotation,
1823
- }),
1824
- override: ({ node }) => selectionProxyIdFromMetadata(node.metadata)
1825
- ? { axes: [], gridSnap: false }
1826
- : null,
1827
- },
1828
- rotatable: { axes: ['y'], snapAngles: [Math.PI / 4] },
1829
- duplicable: { subtree: true, prepareSubtreeClone: prepareCabinetSubtreeClone },
1830
- deletable: true,
1831
- surfaces: {
1832
- hosting: cabinetSurfaceProvider,
1833
- top: {
1834
- height: (node, context) => cabinetLocalBounds(node, context.nodes).maxY,
1835
- },
1836
- },
1837
- floorPlaced: {
1838
- applies: (node) => !hasCabinetParentId(node),
1839
- footprints: (node, ctx) => cabinetFloorPlacedFootprints(node, ctx?.nodes),
1840
- collides: true,
1841
- },
1842
- alignmentFootprint: (node, nodes) => {
1843
- const n = node;
1844
- return { shape: 'aabb', ...cabinetPlanBoundsAabb(n, nodes) };
1845
- },
1846
- dragBounds: (node, nodes) => {
1847
- const bounds = cabinetLocalBounds(node, nodes);
1848
- return { size: bounds.size, center: bounds.center };
1849
- },
1850
- paint: cabinetPaint,
1851
- sceneAction: cabinetSceneAction,
1852
- slots: () => cabinetSlots(),
1853
- },
1854
- // Dirty-cascade: a dirtied run re-marks its hosted modules so their
1855
- // composite geometry re-flows with the run (see `cascadeDirty`).
1856
- relations: {
1857
- hosts: ['cabinet', 'cabinet-module', 'item', 'procedural-item'],
1858
- },
1859
- parametrics: cabinetParametrics,
1860
- handles: (node, sceneApi) => cabinetHandles(node, sceneApi).map(withCabinetHostedChildren),
1861
- placementNotice: cabinetPlacementNotice,
1862
- geometry: buildCabinetGeometry,
1863
- exportAnimation: ({ node, object }) => bakeCabinetAnimationClip(node, object),
1864
- system: {
1865
- module: () => import('./system.js'),
1866
- priority: 2,
1867
- },
1868
- // `operationState` is deliberately absent — door/drawer poses are applied
1869
- // per-frame by the cabinet animation system, not by geometry rebuilds.
1870
- geometryChildTypes: ['cabinet', 'cabinet-module'],
1871
- geometryKey: (n) => JSON.stringify([
1872
- n.width,
1873
- n.depth,
1874
- n.carcassHeight,
1875
- n.runTier,
1876
- n.plinthHeight,
1877
- n.toeKickDepth,
1878
- n.boardThickness,
1879
- n.countertopThickness,
1880
- n.countertopOverhang,
1881
- n.countertopBackOverhang,
1882
- n.withFinishedBack,
1883
- n.withWaterfall,
1884
- n.withFinishedEnds,
1885
- JSON.stringify(n.barLedge ?? null),
1886
- n.frontThickness,
1887
- n.frontGap,
1888
- n.frontStyle,
1889
- n.panelReady,
1890
- n.handleStyle,
1891
- n.handlePosition,
1892
- n.frontOverlay,
1893
- n.withBottomPanel,
1894
- n.showPlinth,
1895
- n.withCountertop,
1896
- JSON.stringify(n.material ?? null),
1897
- JSON.stringify(n.materialPreset ?? null),
1898
- JSON.stringify(n.slots ?? null),
1899
- JSON.stringify(cabinetLayoutRevision(n.metadata)),
1900
- // Bumped when a sibling run moves/resizes nearby, so the countertop
1901
- // overhang re-trims against the neighbor's new spans (the neighbor's
1902
- // own fields never appear in this key).
1903
- JSON.stringify(cabinetAdjacencyRevision(n.metadata)),
1904
- JSON.stringify((n.children ?? []).filter((id) => id.startsWith('cabinet_') || id.startsWith('cabinet-module_'))),
1905
- JSON.stringify(n.stack ?? null),
1906
- ]),
1907
- floorplan: buildCabinetFloorplan,
1908
- floorplanSiblingOverrides: cabinetFloorplanSiblingOverrides,
1909
- floorplanAffectedIds: cabinetFloorplanAffectedIds,
1910
- quickActionNodeScope: 'level',
1911
- quickActions: cabinetQuickActions,
1912
- // Corner-derived leg runs hide their own tree rows; their modules are
1913
- // flattened into the source run's hierarchy.
1914
- tree: {
1915
- label: cabinetTreeLabel,
1916
- hidden: cabinetTreeHidden,
1917
- childIds: cabinetTreeChildIds,
1918
- },
1919
- // E operates the run: every child module's doors/drawers swing together.
1920
- keyboardActions: {
1921
- e: {
1922
- appliesTo: (node) => node.type === 'cabinet',
1923
- run: (node) => toggleCabinetOperationState(node.id),
1924
- },
1925
- },
1926
- tool: () => import('./tool.js'),
1927
- toolHints: [
1928
- { key: 'Click', label: 'Place cabinet' },
1929
- {
1930
- key: 'I',
1931
- label: 'Placement type',
1932
- chip: {
1933
- subscribe: (onChange) => useCabinetPlacementType.subscribe(onChange),
1934
- value: () => useCabinetPlacementType.getState().type,
1935
- cycle: () => void useCabinetPlacementType.getState().cycleType(),
1936
- labels: { cabinet: 'Type: Cabinet', island: 'Type: Island' },
1937
- icons: { cabinet: 'lucide:rectangle-horizontal', island: 'lucide:table-2' },
1938
- tooltip: 'Placement type — click or press I to toggle',
1939
- },
1940
- },
1941
- { key: 'Alt', label: 'Force place' },
1942
- { key: 'R / T', label: 'Rotate' },
1943
- { key: 'Esc', label: 'Cancel run / exit' },
1944
- ],
1945
- presentation: {
1946
- label: 'Modular Cabinet',
1947
- description: 'A configurable parametric base cabinet.',
1948
- icon: { kind: 'url', src: '/icons/item.webp' },
1949
- paletteSection: 'furnish',
1950
- paletteOrder: 34,
1951
- },
1952
- mcp: {
1953
- description: 'A configurable parametric base cabinet with plinth, carcass, front panels, optional countertop, and editable dimensions.',
1954
- },
1955
- };
1956
- export const cabinetModuleDefinition = {
1957
- kind: 'cabinet-module',
1958
- schemaVersion: 5,
1959
- schema: CabinetModuleNode,
1960
- category: 'furnish',
1961
- surfaceRole: 'joinery',
1962
- snapProfile: 'item',
1963
- facingIndicator: true,
1964
- defaults: () => ({
1965
- object: 'node',
1966
- parentId: null,
1967
- visible: true,
1968
- metadata: {},
1969
- position: [0, 0, 0],
1970
- rotation: 0,
1971
- children: [],
1972
- cabinetType: 'base',
1973
- width: 0.5,
1974
- depth: CABINET_METRIC_DEFAULTS.depth,
1975
- carcassHeight: CABINET_METRIC_DEFAULTS.carcassHeight,
1976
- operationState: 0,
1977
- plinthHeight: 0,
1978
- toeKickDepth: 0.075,
1979
- boardThickness: 0.018,
1980
- countertopThickness: 0,
1981
- countertopOverhang: 0.02,
1982
- countertopBackOverhang: 0,
1983
- withFinishedBack: false,
1984
- frontThickness: 0.018,
1985
- frontGap: 0.003,
1986
- moduleKind: 'standard',
1987
- openSide: undefined,
1988
- cornerShelf: false,
1989
- topFinish: 'none',
1990
- topFinishHeight: CabinetModuleNode.parse({}).topFinishHeight,
1991
- topFinishDepth: 0.32,
1992
- frontStyle: 'slab',
1993
- panelReady: false,
1994
- handleStyle: 'bar',
1995
- handlePosition: 'auto',
1996
- frontOverlay: 'full',
1997
- withBottomPanel: true,
1998
- showPlinth: false,
1999
- withCountertop: false,
2000
- // material / materialPreset left undefined — paint mode writes slot refs.
2001
- }),
2002
- capabilities: {
2003
- batchable: cabinetBatchable,
2004
- selectable: { hitVolume: 'bbox' },
2005
- surfaces: { hosting: cabinetModuleSurfaceProvider },
2006
- movable: {
2007
- axes: ['x', 'z'],
2008
- directDrag: true,
2009
- gridSnap: true,
2010
- parentFrame: cabinetModuleParentFrame,
2011
- groupMoveSnapPose: resolveCabinetModuleGroupMoveSnap,
2012
- override: ({ node }) => selectionProxyIdFromMetadata(node.metadata)
2013
- ? { axes: [], gridSnap: false }
2014
- : null,
2015
- },
2016
- rotatable: { axes: ['y'], snapAngles: [Math.PI / 4] },
2017
- duplicable: { subtree: true, prepareSubtreeClone: prepareCabinetSubtreeClone },
2018
- deletable: true,
2019
- floorPlaced: {
2020
- applies: (node) => !hasCabinetParentId(node),
2021
- footprint: (node) => {
2022
- const n = node;
2023
- return {
2024
- dimensions: [n.width, cabinetModuleTotalHeight(n), n.depth],
2025
- rotation: [0, n.rotation, 0],
2026
- };
2027
- },
2028
- collides: true,
2029
- },
2030
- dragBounds: (node, nodes) => {
2031
- const bounds = cabinetLocalBounds(node, nodes);
2032
- return { size: bounds.size, center: bounds.center };
2033
- },
2034
- paint: cabinetPaint,
2035
- sceneAction: cabinetSceneAction,
2036
- slots: () => cabinetSlots(),
2037
- },
2038
- relations: { hosts: ['cabinet', 'cabinet-module', 'item', 'procedural-item'] },
2039
- parametrics: cabinetModuleParametrics,
2040
- handles: () => cabinetModuleHandles().map(withCabinetHostedChildren),
2041
- placementNotice: cabinetPlacementNotice,
2042
- geometry: buildCabinetGeometry,
2043
- exportAnimation: ({ node, object }) => bakeCabinetAnimationClip(node, object),
2044
- // `operationState` is deliberately absent — see cabinetDefinition.geometryKey.
2045
- geometryChildTypes: ['cabinet', 'cabinet-module'],
2046
- geometryKey: (n) => JSON.stringify([
2047
- n.cabinetType,
2048
- n.moduleKind,
2049
- n.width,
2050
- n.depth,
2051
- n.carcassHeight,
2052
- n.plinthHeight,
2053
- n.toeKickDepth,
2054
- n.boardThickness,
2055
- n.countertopThickness,
2056
- n.countertopOverhang,
2057
- n.frontThickness,
2058
- n.frontGap,
2059
- n.frontStyle,
2060
- n.panelReady,
2061
- n.handleStyle,
2062
- n.handlePosition,
2063
- n.frontOverlay,
2064
- n.withBottomPanel,
2065
- n.showPlinth,
2066
- n.withCountertop,
2067
- n.openSide ?? null,
2068
- n.cornerShelf ?? false,
2069
- n.topFinish,
2070
- n.topFinishHeight,
2071
- n.topFinishDepth,
2072
- JSON.stringify(n.material ?? null),
2073
- JSON.stringify(n.materialPreset ?? null),
2074
- JSON.stringify(n.slots ?? null),
2075
- JSON.stringify((n.children ?? []).filter((id) => id.startsWith('cabinet_') || id.startsWith('cabinet-module_'))),
2076
- JSON.stringify(n.stack ?? null),
2077
- ]),
2078
- floorplan: buildCabinetModuleFloorplan,
2079
- floorplanSiblingOverrides: cabinetFloorplanSiblingOverrides,
2080
- floorplanAffectedIds: cabinetFloorplanAffectedIds,
2081
- // 2D ↔ 3D parity: module position is run-local, so the generic overlay's
2082
- // plan-space translate would corrupt it on any rotated / offset run.
2083
- floorplanMoveTarget: cabinetModuleFloorplanMoveTarget,
2084
- quickActionNodeScope: 'level',
2085
- quickActions: cabinetQuickActions,
2086
- tree: {
2087
- label: cabinetTreeLabel,
2088
- childIds: cabinetTreeChildIds,
2089
- },
2090
- // Corner-generated modules keep a proxy so grouped move/rotate
2091
- // affordances can still key off the run, but a direct body click should
2092
- // stay on the clicked module so added legs / wall cabinets remain
2093
- // individually selectable.
2094
- selectionProxy: {
2095
- bypassDirectPick: (node, proxyTarget) => node.type === 'cabinet-module' && proxyTarget.type === 'cabinet',
2096
- },
2097
- // E animates this module's doors/drawers open ↔ closed (hood-only
2098
- // modules have nothing to operate).
2099
- keyboardActions: {
2100
- e: {
2101
- appliesTo: (node) => node.type === 'cabinet-module' && !isHoodOnlyCabinet(node),
2102
- run: (node) => toggleCabinetOperationState(node.id),
2103
- },
2104
- },
2105
- presentation: {
2106
- label: 'Cabinet Module',
2107
- description: 'An editable module inside a modular cabinet run.',
2108
- icon: { kind: 'url', src: '/icons/item.webp' },
2109
- paletteSection: 'furnish',
2110
- paletteOrder: 35,
2111
- },
2112
- mcp: {
2113
- description: 'A single editable cabinet module inside a modular cabinet run.',
2114
- },
2115
- };
1
+ import { composeNodeDefinition } from '../definition-contributions.js';
2
+ import { cabinetEditContribution, cabinetModuleEditContribution } from './definition.edit.js';
3
+ import { cabinetModuleRenderDefinition, cabinetRenderDefinition } from './definition.render.js';
4
+ export const cabinetDefinition = composeNodeDefinition(cabinetRenderDefinition, cabinetEditContribution);
5
+ export const cabinetModuleDefinition = composeNodeDefinition(cabinetModuleRenderDefinition, cabinetModuleEditContribution);
6
+ export { bumpCabinetRunsNear, cabinetFloorPlacedFootprints, cabinetRunFootprint, cabinetRunNeighborSignature, } from './definition.render.js';