@pascal-app/nodes 1.0.0-beta.5 → 1.0.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 (831) hide show
  1. package/LICENSE +21 -0
  2. package/dist/block/commands.d.ts +68 -0
  3. package/dist/block/commands.d.ts.map +1 -0
  4. package/dist/block/commands.js +1144 -0
  5. package/dist/block/contextual-help.d.ts +3 -0
  6. package/dist/block/contextual-help.d.ts.map +1 -0
  7. package/dist/block/contextual-help.js +38 -0
  8. package/dist/block/definition.d.ts +8 -0
  9. package/dist/block/definition.d.ts.map +1 -0
  10. package/dist/block/definition.js +126 -0
  11. package/dist/block/edit-session.d.ts +16 -0
  12. package/dist/block/edit-session.d.ts.map +1 -0
  13. package/dist/block/edit-session.js +32 -0
  14. package/dist/block/face-host.d.ts +3 -0
  15. package/dist/block/face-host.d.ts.map +1 -0
  16. package/dist/block/face-host.js +178 -0
  17. package/dist/block/floorplan.d.ts +3 -0
  18. package/dist/block/floorplan.d.ts.map +1 -0
  19. package/dist/block/floorplan.js +43 -0
  20. package/dist/block/geometry-snap.d.ts +18 -0
  21. package/dist/block/geometry-snap.d.ts.map +1 -0
  22. package/dist/block/geometry-snap.js +137 -0
  23. package/dist/block/geometry.d.ts +11 -0
  24. package/dist/block/geometry.d.ts.map +1 -0
  25. package/dist/block/geometry.js +146 -0
  26. package/dist/block/gesture-wheel.d.ts +8 -0
  27. package/dist/block/gesture-wheel.d.ts.map +1 -0
  28. package/dist/block/gesture-wheel.js +7 -0
  29. package/dist/block/interaction-sfx.d.ts +3 -0
  30. package/dist/block/interaction-sfx.d.ts.map +1 -0
  31. package/dist/block/interaction-sfx.js +16 -0
  32. package/dist/block/last-operation.d.ts +48 -0
  33. package/dist/block/last-operation.d.ts.map +1 -0
  34. package/dist/block/last-operation.js +120 -0
  35. package/dist/block/loop-cut-interaction.d.ts +5 -0
  36. package/dist/block/loop-cut-interaction.d.ts.map +1 -0
  37. package/dist/block/loop-cut-interaction.js +13 -0
  38. package/dist/block/material-slots.d.ts +61 -0
  39. package/dist/block/material-slots.d.ts.map +1 -0
  40. package/dist/block/material-slots.js +131 -0
  41. package/dist/block/modal-face-operation.d.ts +13 -0
  42. package/dist/block/modal-face-operation.d.ts.map +1 -0
  43. package/dist/block/modal-face-operation.js +40 -0
  44. package/dist/block/modal-session.d.ts +15 -0
  45. package/dist/block/modal-session.d.ts.map +1 -0
  46. package/dist/block/modal-session.js +40 -0
  47. package/dist/block/modal-transform.d.ts +30 -0
  48. package/dist/block/modal-transform.d.ts.map +1 -0
  49. package/dist/block/modal-transform.js +144 -0
  50. package/dist/block/paint.d.ts +3 -0
  51. package/dist/block/paint.d.ts.map +1 -0
  52. package/dist/block/paint.js +136 -0
  53. package/dist/block/panel.d.ts +2 -0
  54. package/dist/block/panel.d.ts.map +1 -0
  55. package/dist/block/panel.js +234 -0
  56. package/dist/block/parametrics.d.ts +4 -0
  57. package/dist/block/parametrics.d.ts.map +1 -0
  58. package/dist/block/parametrics.js +9 -0
  59. package/dist/block/preview.d.ts +6 -0
  60. package/dist/block/preview.d.ts.map +1 -0
  61. package/dist/block/preview.js +46 -0
  62. package/dist/block/rotation-drag.d.ts +5 -0
  63. package/dist/block/rotation-drag.d.ts.map +1 -0
  64. package/dist/block/rotation-drag.js +18 -0
  65. package/dist/block/schema.d.ts +2 -0
  66. package/dist/block/schema.d.ts.map +1 -0
  67. package/dist/block/schema.js +1 -0
  68. package/dist/block/selection-geometry.d.ts +9 -0
  69. package/dist/block/selection-geometry.d.ts.map +1 -0
  70. package/dist/block/selection-geometry.js +31 -0
  71. package/dist/block/selection-model.d.ts +17 -0
  72. package/dist/block/selection-model.d.ts.map +1 -0
  73. package/dist/block/selection-model.js +84 -0
  74. package/dist/block/selection.d.ts +4 -0
  75. package/dist/block/selection.d.ts.map +1 -0
  76. package/dist/block/selection.js +2570 -0
  77. package/dist/block/slots.d.ts +4 -0
  78. package/dist/block/slots.d.ts.map +1 -0
  79. package/dist/block/slots.js +17 -0
  80. package/dist/block/tool.d.ts +3 -0
  81. package/dist/block/tool.d.ts.map +1 -0
  82. package/dist/block/tool.js +174 -0
  83. package/dist/block/toolbar-state.d.ts +46 -0
  84. package/dist/block/toolbar-state.d.ts.map +1 -0
  85. package/dist/block/toolbar-state.js +67 -0
  86. package/dist/block/use-block-face-operation.d.ts +39 -0
  87. package/dist/block/use-block-face-operation.d.ts.map +1 -0
  88. package/dist/block/use-block-face-operation.js +218 -0
  89. package/dist/box-vent/definition.d.ts.map +1 -1
  90. package/dist/box-vent/definition.js +8 -5
  91. package/dist/box-vent/geometry.d.ts +4 -0
  92. package/dist/box-vent/geometry.d.ts.map +1 -1
  93. package/dist/box-vent/geometry.js +76 -44
  94. package/dist/box-vent/paint.d.ts +4 -0
  95. package/dist/box-vent/paint.d.ts.map +1 -0
  96. package/dist/box-vent/paint.js +40 -0
  97. package/dist/box-vent/panel.js +1 -1
  98. package/dist/box-vent/parametrics.js +3 -3
  99. package/dist/box-vent/renderer.d.ts.map +1 -1
  100. package/dist/box-vent/renderer.js +29 -10
  101. package/dist/cabinet/compartment-card.d.ts.map +1 -1
  102. package/dist/cabinet/compartment-card.js +11 -3
  103. package/dist/cabinet/continuous-placement.d.ts +3 -0
  104. package/dist/cabinet/continuous-placement.d.ts.map +1 -1
  105. package/dist/cabinet/continuous-placement.js +6 -0
  106. package/dist/cabinet/definition.d.ts.map +1 -1
  107. package/dist/cabinet/definition.js +400 -54
  108. package/dist/cabinet/floorplan-move.d.ts.map +1 -1
  109. package/dist/cabinet/floorplan-move.js +91 -22
  110. package/dist/cabinet/geometry/fridge.d.ts.map +1 -1
  111. package/dist/cabinet/geometry/fridge.js +25 -1
  112. package/dist/cabinet/geometry/fronts.d.ts +4 -0
  113. package/dist/cabinet/geometry/fronts.d.ts.map +1 -1
  114. package/dist/cabinet/geometry/fronts.js +1 -1
  115. package/dist/cabinet/geometry/run.d.ts.map +1 -1
  116. package/dist/cabinet/geometry/run.js +19 -1
  117. package/dist/cabinet/geometry.d.ts.map +1 -1
  118. package/dist/cabinet/geometry.js +63 -1
  119. package/dist/cabinet/index.d.ts +1 -0
  120. package/dist/cabinet/index.d.ts.map +1 -1
  121. package/dist/cabinet/index.js +1 -0
  122. package/dist/cabinet/insertion.d.ts +19 -0
  123. package/dist/cabinet/insertion.d.ts.map +1 -0
  124. package/dist/cabinet/insertion.js +49 -0
  125. package/dist/cabinet/move-frame.d.ts.map +1 -1
  126. package/dist/cabinet/move-frame.js +68 -2
  127. package/dist/cabinet/panel-context.d.ts +7 -0
  128. package/dist/cabinet/panel-context.d.ts.map +1 -0
  129. package/dist/cabinet/panel-context.js +16 -0
  130. package/dist/cabinet/panel-visibility.d.ts +9 -0
  131. package/dist/cabinet/panel-visibility.d.ts.map +1 -0
  132. package/dist/cabinet/panel-visibility.js +26 -0
  133. package/dist/cabinet/panel.d.ts.map +1 -1
  134. package/dist/cabinet/panel.js +221 -37
  135. package/dist/cabinet/placement-dimensions.d.ts +42 -0
  136. package/dist/cabinet/placement-dimensions.d.ts.map +1 -0
  137. package/dist/cabinet/placement-dimensions.js +227 -0
  138. package/dist/cabinet/placement-snap.d.ts +21 -1
  139. package/dist/cabinet/placement-snap.d.ts.map +1 -1
  140. package/dist/cabinet/placement-snap.js +48 -6
  141. package/dist/cabinet/preset-width-debt.d.ts +7 -0
  142. package/dist/cabinet/preset-width-debt.d.ts.map +1 -0
  143. package/dist/cabinet/preset-width-debt.js +45 -0
  144. package/dist/cabinet/presets.d.ts.map +1 -1
  145. package/dist/cabinet/presets.js +14 -13
  146. package/dist/cabinet/profiles.d.ts +14 -0
  147. package/dist/cabinet/profiles.d.ts.map +1 -0
  148. package/dist/cabinet/profiles.js +30 -0
  149. package/dist/cabinet/quick-actions.d.ts.map +1 -1
  150. package/dist/cabinet/quick-actions.js +8 -24
  151. package/dist/cabinet/reveals.d.ts +37 -0
  152. package/dist/cabinet/reveals.d.ts.map +1 -0
  153. package/dist/cabinet/reveals.js +13 -0
  154. package/dist/cabinet/run-layout.d.ts +90 -4
  155. package/dist/cabinet/run-layout.d.ts.map +1 -1
  156. package/dist/cabinet/run-layout.js +408 -23
  157. package/dist/cabinet/run-ops.d.ts +202 -10
  158. package/dist/cabinet/run-ops.d.ts.map +1 -1
  159. package/dist/cabinet/run-ops.js +520 -33
  160. package/dist/cabinet/run-panel.d.ts +6 -2
  161. package/dist/cabinet/run-panel.d.ts.map +1 -1
  162. package/dist/cabinet/run-panel.js +285 -108
  163. package/dist/cabinet/stack-transitions.d.ts.map +1 -1
  164. package/dist/cabinet/stack-transitions.js +35 -24
  165. package/dist/cabinet/stack.d.ts +169 -1
  166. package/dist/cabinet/stack.d.ts.map +1 -1
  167. package/dist/cabinet/stack.js +57 -18
  168. package/dist/cabinet/tool.d.ts.map +1 -1
  169. package/dist/cabinet/tool.js +615 -91
  170. package/dist/cabinet/validation.d.ts +27 -0
  171. package/dist/cabinet/validation.d.ts.map +1 -0
  172. package/dist/cabinet/validation.js +56 -0
  173. package/dist/cabinet/wall-height-presets.d.ts +12 -0
  174. package/dist/cabinet/wall-height-presets.d.ts.map +1 -0
  175. package/dist/cabinet/wall-height-presets.js +15 -0
  176. package/dist/cabinet/wall-snap.d.ts +27 -3
  177. package/dist/cabinet/wall-snap.d.ts.map +1 -1
  178. package/dist/cabinet/wall-snap.js +278 -62
  179. package/dist/cabinet/widths.d.ts +10 -0
  180. package/dist/cabinet/widths.d.ts.map +1 -0
  181. package/dist/cabinet/widths.js +13 -0
  182. package/dist/ceiling/definition.d.ts.map +1 -1
  183. package/dist/ceiling/definition.js +7 -1
  184. package/dist/ceiling/floorplan-affordances.d.ts +8 -8
  185. package/dist/ceiling/materials.d.ts.map +1 -1
  186. package/dist/ceiling/materials.js +0 -1
  187. package/dist/ceiling/panel.js +1 -1
  188. package/dist/ceiling/parametrics.js +1 -1
  189. package/dist/chimney/definition.js +1 -1
  190. package/dist/chimney/panel.js +1 -1
  191. package/dist/chimney/parametrics.js +3 -3
  192. package/dist/column/definition.d.ts.map +1 -1
  193. package/dist/column/definition.js +22 -4
  194. package/dist/column/panel.d.ts.map +1 -1
  195. package/dist/column/panel.js +37 -7
  196. package/dist/column/parametrics.d.ts.map +1 -1
  197. package/dist/column/parametrics.js +5 -3
  198. package/dist/column/tool.d.ts.map +1 -1
  199. package/dist/column/tool.js +50 -14
  200. package/dist/cupola/definition.d.ts.map +1 -1
  201. package/dist/cupola/definition.js +15 -6
  202. package/dist/cupola/geometry.d.ts +6 -0
  203. package/dist/cupola/geometry.d.ts.map +1 -1
  204. package/dist/cupola/geometry.js +60 -13
  205. package/dist/cupola/paint.d.ts +4 -0
  206. package/dist/cupola/paint.d.ts.map +1 -0
  207. package/dist/cupola/paint.js +39 -0
  208. package/dist/cupola/panel.js +1 -1
  209. package/dist/cupola/parametrics.js +3 -3
  210. package/dist/cupola/renderer.d.ts.map +1 -1
  211. package/dist/cupola/renderer.js +27 -8
  212. package/dist/door/floorplan-move.d.ts.map +1 -1
  213. package/dist/door/floorplan-move.js +1 -1
  214. package/dist/door/move-tool.d.ts.map +1 -1
  215. package/dist/door/move-tool.js +86 -32
  216. package/dist/door/panel.d.ts.map +1 -1
  217. package/dist/door/panel.js +1 -1
  218. package/dist/door/parametrics.js +2 -2
  219. package/dist/door/tool.d.ts.map +1 -1
  220. package/dist/door/tool.js +16 -4
  221. package/dist/dormer/csg-geometry.d.ts +6 -42
  222. package/dist/dormer/csg-geometry.d.ts.map +1 -1
  223. package/dist/dormer/csg-geometry.js +38 -209
  224. package/dist/dormer/definition.d.ts +4 -12
  225. package/dist/dormer/definition.d.ts.map +1 -1
  226. package/dist/dormer/definition.js +18 -193
  227. package/dist/dormer/floorplan.d.ts +3 -3
  228. package/dist/dormer/floorplan.d.ts.map +1 -1
  229. package/dist/dormer/floorplan.js +6 -17
  230. package/dist/dormer/geometry.d.ts +5 -24
  231. package/dist/dormer/geometry.d.ts.map +1 -1
  232. package/dist/dormer/geometry.js +64 -41
  233. package/dist/dormer/index.d.ts +1 -1
  234. package/dist/dormer/index.d.ts.map +1 -1
  235. package/dist/dormer/index.js +1 -1
  236. package/dist/dormer/move-tool.d.ts.map +1 -1
  237. package/dist/dormer/move-tool.js +31 -3
  238. package/dist/dormer/panel-windows-section.d.ts +9 -0
  239. package/dist/dormer/panel-windows-section.d.ts.map +1 -0
  240. package/dist/dormer/panel-windows-section.js +7 -0
  241. package/dist/dormer/panel.d.ts.map +1 -1
  242. package/dist/dormer/panel.js +100 -18
  243. package/dist/dormer/parametrics.d.ts.map +1 -1
  244. package/dist/dormer/parametrics.js +8 -77
  245. package/dist/dormer/preview.d.ts.map +1 -1
  246. package/dist/dormer/preview.js +9 -1
  247. package/dist/dormer/renderer.d.ts.map +1 -1
  248. package/dist/dormer/renderer.js +24 -26
  249. package/dist/dormer/tool.d.ts.map +1 -1
  250. package/dist/dormer/tool.js +3 -1
  251. package/dist/dormer/window-layout.d.ts +15 -0
  252. package/dist/dormer/window-layout.d.ts.map +1 -0
  253. package/dist/dormer/window-layout.js +59 -0
  254. package/dist/downspout/definition.d.ts.map +1 -1
  255. package/dist/downspout/definition.js +17 -4
  256. package/dist/downspout/geometry.d.ts.map +1 -1
  257. package/dist/downspout/geometry.js +10 -0
  258. package/dist/downspout/inspector-editors.d.ts.map +1 -1
  259. package/dist/downspout/inspector-editors.js +9 -1
  260. package/dist/downspout/parametrics.d.ts.map +1 -1
  261. package/dist/downspout/parametrics.js +2 -1
  262. package/dist/downspout/renderer.d.ts.map +1 -1
  263. package/dist/downspout/renderer.js +19 -3
  264. package/dist/downspout/system.d.ts +7 -0
  265. package/dist/downspout/system.d.ts.map +1 -0
  266. package/dist/downspout/system.js +114 -0
  267. package/dist/duct-fitting/accessory-geometry.d.ts +4 -0
  268. package/dist/duct-fitting/accessory-geometry.d.ts.map +1 -0
  269. package/dist/duct-fitting/accessory-geometry.js +47 -0
  270. package/dist/duct-fitting/definition.d.ts.map +1 -1
  271. package/dist/duct-fitting/definition.js +11 -1
  272. package/dist/duct-fitting/floorplan.d.ts.map +1 -1
  273. package/dist/duct-fitting/floorplan.js +8 -0
  274. package/dist/duct-fitting/geometry.d.ts.map +1 -1
  275. package/dist/duct-fitting/geometry.js +52 -38
  276. package/dist/duct-fitting/parametrics.d.ts.map +1 -1
  277. package/dist/duct-fitting/parametrics.js +78 -15
  278. package/dist/duct-fitting/ports.d.ts +1 -0
  279. package/dist/duct-fitting/ports.d.ts.map +1 -1
  280. package/dist/duct-fitting/ports.js +46 -0
  281. package/dist/duct-fitting/tool.d.ts +19 -0
  282. package/dist/duct-fitting/tool.d.ts.map +1 -1
  283. package/dist/duct-fitting/tool.js +126 -34
  284. package/dist/duct-segment/continuation.d.ts +21 -0
  285. package/dist/duct-segment/continuation.d.ts.map +1 -0
  286. package/dist/duct-segment/continuation.js +167 -0
  287. package/dist/duct-segment/definition.d.ts.map +1 -1
  288. package/dist/duct-segment/definition.js +19 -18
  289. package/dist/duct-segment/floorplan.d.ts.map +1 -1
  290. package/dist/duct-segment/floorplan.js +43 -0
  291. package/dist/duct-segment/geometry.d.ts.map +1 -1
  292. package/dist/duct-segment/geometry.js +3 -0
  293. package/dist/duct-segment/move-tool.d.ts.map +1 -1
  294. package/dist/duct-segment/move-tool.js +33 -6
  295. package/dist/duct-segment/parametrics.d.ts.map +1 -1
  296. package/dist/duct-segment/parametrics.js +45 -1
  297. package/dist/duct-segment/selection.d.ts +3 -4
  298. package/dist/duct-segment/selection.d.ts.map +1 -1
  299. package/dist/duct-segment/selection.js +186 -158
  300. package/dist/duct-segment/tool.d.ts +37 -0
  301. package/dist/duct-segment/tool.d.ts.map +1 -1
  302. package/dist/duct-segment/tool.js +305 -678
  303. package/dist/duct-terminal/parametrics.js +2 -2
  304. package/dist/duct-terminal/tool.d.ts.map +1 -1
  305. package/dist/duct-terminal/tool.js +37 -7
  306. package/dist/elevator/panel.d.ts.map +1 -1
  307. package/dist/elevator/panel.js +7 -7
  308. package/dist/elevator/system.d.ts.map +1 -1
  309. package/dist/elevator/system.js +10 -1
  310. package/dist/eyebrow-vent/definition.d.ts.map +1 -1
  311. package/dist/eyebrow-vent/definition.js +8 -5
  312. package/dist/eyebrow-vent/geometry.d.ts +4 -0
  313. package/dist/eyebrow-vent/geometry.d.ts.map +1 -1
  314. package/dist/eyebrow-vent/geometry.js +60 -15
  315. package/dist/eyebrow-vent/paint.d.ts +4 -0
  316. package/dist/eyebrow-vent/paint.d.ts.map +1 -0
  317. package/dist/eyebrow-vent/paint.js +33 -0
  318. package/dist/eyebrow-vent/panel.js +1 -1
  319. package/dist/eyebrow-vent/parametrics.js +3 -3
  320. package/dist/eyebrow-vent/renderer.d.ts.map +1 -1
  321. package/dist/eyebrow-vent/renderer.js +27 -8
  322. package/dist/fence/definition.d.ts.map +1 -1
  323. package/dist/fence/definition.js +2 -1
  324. package/dist/fence/floorplan-affordances.d.ts +1 -0
  325. package/dist/fence/floorplan-affordances.d.ts.map +1 -1
  326. package/dist/fence/floorplan-affordances.js +30 -1
  327. package/dist/fence/floorplan.d.ts.map +1 -1
  328. package/dist/fence/floorplan.js +14 -0
  329. package/dist/fence/parametrics.js +3 -3
  330. package/dist/fence/tool.d.ts.map +1 -1
  331. package/dist/fence/tool.js +47 -20
  332. package/dist/gutter/corner-mitre.d.ts.map +1 -1
  333. package/dist/gutter/corner-mitre.js +47 -7
  334. package/dist/gutter/definition.d.ts.map +1 -1
  335. package/dist/gutter/definition.js +23 -13
  336. package/dist/gutter/eave-align.d.ts.map +1 -1
  337. package/dist/gutter/eave-align.js +10 -0
  338. package/dist/gutter/eave-snap.d.ts +1 -1
  339. package/dist/gutter/eave-snap.d.ts.map +1 -1
  340. package/dist/gutter/eave-snap.js +10 -23
  341. package/dist/gutter/geometry.d.ts.map +1 -1
  342. package/dist/gutter/geometry.js +126 -8
  343. package/dist/gutter/outlet-lookup.d.ts.map +1 -1
  344. package/dist/gutter/outlet-lookup.js +22 -1
  345. package/dist/gutter/paint.d.ts +3 -0
  346. package/dist/gutter/paint.d.ts.map +1 -0
  347. package/dist/gutter/paint.js +6 -0
  348. package/dist/gutter/parametrics.d.ts.map +1 -1
  349. package/dist/gutter/parametrics.js +4 -1
  350. package/dist/gutter/renderer.d.ts.map +1 -1
  351. package/dist/gutter/renderer.js +63 -7
  352. package/dist/gutter/trim-clip.d.ts +3 -0
  353. package/dist/gutter/trim-clip.d.ts.map +1 -0
  354. package/dist/gutter/trim-clip.js +5 -0
  355. package/dist/hvac-equipment/parametrics.js +3 -3
  356. package/dist/hvac-equipment/tool.d.ts.map +1 -1
  357. package/dist/hvac-equipment/tool.js +12 -2
  358. package/dist/index.d.ts +5 -2
  359. package/dist/index.d.ts.map +1 -1
  360. package/dist/index.js +9 -2
  361. package/dist/item/definition.d.ts.map +1 -1
  362. package/dist/item/definition.js +10 -2
  363. package/dist/item/floorplan-move.d.ts.map +1 -1
  364. package/dist/item/floorplan-move.js +20 -1
  365. package/dist/item/floorplan.d.ts.map +1 -1
  366. package/dist/item/floorplan.js +22 -1
  367. package/dist/item/move-tool.d.ts +28 -1
  368. package/dist/item/move-tool.d.ts.map +1 -1
  369. package/dist/item/move-tool.js +14 -2
  370. package/dist/item/renderer.d.ts.map +1 -1
  371. package/dist/item/renderer.js +16 -6
  372. package/dist/item/system.d.ts +5 -0
  373. package/dist/item/system.d.ts.map +1 -1
  374. package/dist/item/system.js +7 -1
  375. package/dist/lean-to-extension/arc.d.ts +14 -0
  376. package/dist/lean-to-extension/arc.d.ts.map +1 -0
  377. package/dist/lean-to-extension/arc.js +56 -0
  378. package/dist/lean-to-extension/assembly.d.ts +61 -0
  379. package/dist/lean-to-extension/assembly.d.ts.map +1 -0
  380. package/dist/lean-to-extension/assembly.js +761 -0
  381. package/dist/lean-to-extension/canopy-joint.d.ts +35 -0
  382. package/dist/lean-to-extension/canopy-joint.d.ts.map +1 -0
  383. package/dist/lean-to-extension/canopy-joint.js +234 -0
  384. package/dist/lean-to-extension/conical-host.d.ts +15 -0
  385. package/dist/lean-to-extension/conical-host.d.ts.map +1 -0
  386. package/dist/lean-to-extension/conical-host.js +113 -0
  387. package/dist/lean-to-extension/corner-joint.d.ts +29 -0
  388. package/dist/lean-to-extension/corner-joint.d.ts.map +1 -0
  389. package/dist/lean-to-extension/corner-joint.js +1202 -0
  390. package/dist/lean-to-extension/definition.d.ts +4 -0
  391. package/dist/lean-to-extension/definition.d.ts.map +1 -0
  392. package/dist/lean-to-extension/definition.js +433 -0
  393. package/dist/lean-to-extension/floorplan-affordances.d.ts +4 -0
  394. package/dist/lean-to-extension/floorplan-affordances.d.ts.map +1 -0
  395. package/dist/lean-to-extension/floorplan-affordances.js +144 -0
  396. package/dist/lean-to-extension/floorplan-move.d.ts +3 -0
  397. package/dist/lean-to-extension/floorplan-move.d.ts.map +1 -0
  398. package/dist/lean-to-extension/floorplan-move.js +157 -0
  399. package/dist/lean-to-extension/floorplan-tool.d.ts +4 -0
  400. package/dist/lean-to-extension/floorplan-tool.d.ts.map +1 -0
  401. package/dist/lean-to-extension/floorplan-tool.js +385 -0
  402. package/dist/lean-to-extension/floorplan.d.ts +3 -0
  403. package/dist/lean-to-extension/floorplan.d.ts.map +1 -0
  404. package/dist/lean-to-extension/floorplan.js +373 -0
  405. package/dist/lean-to-extension/geometry.d.ts +7 -0
  406. package/dist/lean-to-extension/geometry.d.ts.map +1 -0
  407. package/dist/lean-to-extension/geometry.js +662 -0
  408. package/dist/lean-to-extension/index.d.ts +8 -0
  409. package/dist/lean-to-extension/index.d.ts.map +1 -0
  410. package/dist/lean-to-extension/index.js +7 -0
  411. package/dist/lean-to-extension/layout.d.ts +108 -0
  412. package/dist/lean-to-extension/layout.d.ts.map +1 -0
  413. package/dist/lean-to-extension/layout.js +477 -0
  414. package/dist/lean-to-extension/managed-preview.d.ts +3 -0
  415. package/dist/lean-to-extension/managed-preview.d.ts.map +1 -0
  416. package/dist/lean-to-extension/managed-preview.js +93 -0
  417. package/dist/lean-to-extension/move-tool.d.ts +8 -0
  418. package/dist/lean-to-extension/move-tool.d.ts.map +1 -0
  419. package/dist/lean-to-extension/move-tool.js +202 -0
  420. package/dist/lean-to-extension/paint.d.ts +2 -0
  421. package/dist/lean-to-extension/paint.d.ts.map +1 -0
  422. package/dist/lean-to-extension/paint.js +16 -0
  423. package/dist/lean-to-extension/parametrics.d.ts +4 -0
  424. package/dist/lean-to-extension/parametrics.d.ts.map +1 -0
  425. package/dist/lean-to-extension/parametrics.js +570 -0
  426. package/dist/lean-to-extension/placement-scope.d.ts +3 -0
  427. package/dist/lean-to-extension/placement-scope.d.ts.map +1 -0
  428. package/dist/lean-to-extension/placement-scope.js +4 -0
  429. package/dist/lean-to-extension/placement-validation.d.ts +4 -0
  430. package/dist/lean-to-extension/placement-validation.d.ts.map +1 -0
  431. package/dist/lean-to-extension/placement-validation.js +324 -0
  432. package/dist/lean-to-extension/placement.d.ts +59 -0
  433. package/dist/lean-to-extension/placement.d.ts.map +1 -0
  434. package/dist/lean-to-extension/placement.js +331 -0
  435. package/dist/lean-to-extension/plan-footprint.d.ts +10 -0
  436. package/dist/lean-to-extension/plan-footprint.d.ts.map +1 -0
  437. package/dist/lean-to-extension/plan-footprint.js +27 -0
  438. package/dist/lean-to-extension/preview-geometry.d.ts +7 -0
  439. package/dist/lean-to-extension/preview-geometry.d.ts.map +1 -0
  440. package/dist/lean-to-extension/preview-geometry.js +40 -0
  441. package/dist/lean-to-extension/preview.d.ts +7 -0
  442. package/dist/lean-to-extension/preview.d.ts.map +1 -0
  443. package/dist/lean-to-extension/preview.js +20 -0
  444. package/dist/lean-to-extension/renderer.d.ts +6 -0
  445. package/dist/lean-to-extension/renderer.d.ts.map +1 -0
  446. package/dist/lean-to-extension/renderer.js +34 -0
  447. package/dist/lean-to-extension/roof-attachment.d.ts +28 -0
  448. package/dist/lean-to-extension/roof-attachment.d.ts.map +1 -0
  449. package/dist/lean-to-extension/roof-attachment.js +376 -0
  450. package/dist/lean-to-extension/schema.d.ts +2 -0
  451. package/dist/lean-to-extension/schema.d.ts.map +1 -0
  452. package/dist/lean-to-extension/schema.js +1 -0
  453. package/dist/lean-to-extension/slots.d.ts +5 -0
  454. package/dist/lean-to-extension/slots.d.ts.map +1 -0
  455. package/dist/lean-to-extension/slots.js +15 -0
  456. package/dist/lean-to-extension/system.d.ts +7 -0
  457. package/dist/lean-to-extension/system.d.ts.map +1 -0
  458. package/dist/lean-to-extension/system.js +726 -0
  459. package/dist/lean-to-extension/tool.d.ts +3 -0
  460. package/dist/lean-to-extension/tool.d.ts.map +1 -0
  461. package/dist/lean-to-extension/tool.js +580 -0
  462. package/dist/lean-to-extension/wall-target.d.ts +10 -0
  463. package/dist/lean-to-extension/wall-target.d.ts.map +1 -0
  464. package/dist/lean-to-extension/wall-target.js +29 -0
  465. package/dist/lineset/definition.d.ts.map +1 -1
  466. package/dist/lineset/definition.js +1 -0
  467. package/dist/liquid-line/definition.d.ts.map +1 -1
  468. package/dist/liquid-line/definition.js +1 -0
  469. package/dist/pipe-fitting/definition.d.ts.map +1 -1
  470. package/dist/pipe-fitting/definition.js +16 -2
  471. package/dist/pipe-fitting/floorplan.d.ts.map +1 -1
  472. package/dist/pipe-fitting/floorplan.js +4 -0
  473. package/dist/pipe-fitting/geometry.d.ts +3 -5
  474. package/dist/pipe-fitting/geometry.d.ts.map +1 -1
  475. package/dist/pipe-fitting/geometry.js +154 -21
  476. package/dist/pipe-fitting/inline-insertion.d.ts +13 -0
  477. package/dist/pipe-fitting/inline-insertion.d.ts.map +1 -0
  478. package/dist/pipe-fitting/inline-insertion.js +96 -0
  479. package/dist/pipe-fitting/parametrics.d.ts.map +1 -1
  480. package/dist/pipe-fitting/parametrics.js +17 -3
  481. package/dist/pipe-fitting/ports.d.ts.map +1 -1
  482. package/dist/pipe-fitting/ports.js +8 -1
  483. package/dist/pipe-fitting/tool.d.ts +26 -0
  484. package/dist/pipe-fitting/tool.d.ts.map +1 -1
  485. package/dist/pipe-fitting/tool.js +182 -37
  486. package/dist/pipe-segment/continuation.d.ts +22 -0
  487. package/dist/pipe-segment/continuation.d.ts.map +1 -0
  488. package/dist/pipe-segment/continuation.js +146 -0
  489. package/dist/pipe-segment/definition.d.ts.map +1 -1
  490. package/dist/pipe-segment/definition.js +23 -7
  491. package/dist/pipe-segment/floorplan.d.ts.map +1 -1
  492. package/dist/pipe-segment/floorplan.js +42 -0
  493. package/dist/pipe-segment/geometry.d.ts +2 -1
  494. package/dist/pipe-segment/geometry.d.ts.map +1 -1
  495. package/dist/pipe-segment/geometry.js +4 -1
  496. package/dist/pipe-segment/move-tool.d.ts.map +1 -1
  497. package/dist/pipe-segment/move-tool.js +25 -5
  498. package/dist/pipe-segment/parametrics.d.ts.map +1 -1
  499. package/dist/pipe-segment/parametrics.js +45 -0
  500. package/dist/pipe-segment/selection.d.ts +3 -4
  501. package/dist/pipe-segment/selection.d.ts.map +1 -1
  502. package/dist/pipe-segment/selection.js +169 -105
  503. package/dist/pipe-segment/slope.d.ts +5 -0
  504. package/dist/pipe-segment/slope.d.ts.map +1 -0
  505. package/dist/pipe-segment/slope.js +10 -0
  506. package/dist/pipe-segment/tool.d.ts.map +1 -1
  507. package/dist/pipe-segment/tool.js +325 -545
  508. package/dist/pipe-trap/tool.d.ts.map +1 -1
  509. package/dist/pipe-trap/tool.js +15 -2
  510. package/dist/ridge-vent/definition.js +1 -1
  511. package/dist/ridge-vent/panel.d.ts.map +1 -1
  512. package/dist/ridge-vent/panel.js +2 -2
  513. package/dist/ridge-vent/parametrics.js +1 -1
  514. package/dist/roof/conical-roof-placement.d.ts +36 -0
  515. package/dist/roof/conical-roof-placement.d.ts.map +1 -0
  516. package/dist/roof/conical-roof-placement.js +134 -0
  517. package/dist/roof/conical-roof.d.ts +3 -0
  518. package/dist/roof/conical-roof.d.ts.map +1 -0
  519. package/dist/roof/conical-roof.js +53 -0
  520. package/dist/roof/definition.d.ts +3 -10
  521. package/dist/roof/definition.d.ts.map +1 -1
  522. package/dist/roof/definition.js +83 -13
  523. package/dist/roof/floorplan.d.ts +3 -10
  524. package/dist/roof/floorplan.d.ts.map +1 -1
  525. package/dist/roof/floorplan.js +139 -165
  526. package/dist/roof/index.d.ts +1 -0
  527. package/dist/roof/index.d.ts.map +1 -1
  528. package/dist/roof/index.js +1 -0
  529. package/dist/roof/panel.d.ts.map +1 -1
  530. package/dist/roof/panel.js +22 -23
  531. package/dist/roof/renderer.d.ts.map +1 -1
  532. package/dist/roof/roof-footprint-source.d.ts +16 -0
  533. package/dist/roof/roof-footprint-source.d.ts.map +1 -0
  534. package/dist/roof/roof-footprint-source.js +14 -0
  535. package/dist/roof/roof-footprint.d.ts +25 -0
  536. package/dist/roof/roof-footprint.d.ts.map +1 -0
  537. package/dist/roof/roof-footprint.js +94 -0
  538. package/dist/roof/roof-placement-mode.d.ts +18 -0
  539. package/dist/roof/roof-placement-mode.d.ts.map +1 -0
  540. package/dist/roof/roof-placement-mode.js +24 -0
  541. package/dist/roof/tool.d.ts +10 -0
  542. package/dist/roof/tool.d.ts.map +1 -0
  543. package/dist/roof/tool.js +922 -0
  544. package/dist/roof-segment/definition.d.ts.map +1 -1
  545. package/dist/roof-segment/definition.js +52 -8
  546. package/dist/roof-segment/floorplan-affordances.d.ts +5 -6
  547. package/dist/roof-segment/floorplan-affordances.d.ts.map +1 -1
  548. package/dist/roof-segment/floorplan-affordances.js +64 -18
  549. package/dist/roof-segment/floorplan.d.ts +3 -2
  550. package/dist/roof-segment/floorplan.d.ts.map +1 -1
  551. package/dist/roof-segment/floorplan.js +95 -46
  552. package/dist/roof-segment/panel.d.ts.map +1 -1
  553. package/dist/roof-segment/panel.js +122 -27
  554. package/dist/scan/definition.d.ts +2 -3
  555. package/dist/scan/definition.d.ts.map +1 -1
  556. package/dist/scan/definition.js +9 -7
  557. package/dist/scan/parametrics.d.ts.map +1 -1
  558. package/dist/scan/parametrics.js +13 -1
  559. package/dist/scan/renderer.d.ts.map +1 -1
  560. package/dist/scan/renderer.js +8 -2
  561. package/dist/shared/accessory-cursor.d.ts +7 -0
  562. package/dist/shared/accessory-cursor.d.ts.map +1 -0
  563. package/dist/shared/accessory-cursor.js +83 -0
  564. package/dist/shared/accessory-floorplan.d.ts +4 -0
  565. package/dist/shared/accessory-floorplan.d.ts.map +1 -0
  566. package/dist/shared/accessory-floorplan.js +51 -0
  567. package/dist/shared/accessory-geometry.d.ts +7 -0
  568. package/dist/shared/accessory-geometry.d.ts.map +1 -0
  569. package/dist/shared/accessory-geometry.js +79 -0
  570. package/dist/shared/accessory-placement.d.ts +10 -0
  571. package/dist/shared/accessory-placement.d.ts.map +1 -0
  572. package/dist/shared/accessory-placement.js +126 -0
  573. package/dist/shared/accessory-snapping.d.ts +5 -0
  574. package/dist/shared/accessory-snapping.d.ts.map +1 -0
  575. package/dist/shared/accessory-snapping.js +40 -0
  576. package/dist/shared/automatic-run-end-cap.d.ts +11 -0
  577. package/dist/shared/automatic-run-end-cap.d.ts.map +1 -0
  578. package/dist/shared/automatic-run-end-cap.js +159 -0
  579. package/dist/shared/block-face-host.d.ts +15 -0
  580. package/dist/shared/block-face-host.d.ts.map +1 -0
  581. package/dist/shared/block-face-host.js +36 -0
  582. package/dist/shared/connection-compatibility.d.ts +8 -0
  583. package/dist/shared/connection-compatibility.d.ts.map +1 -0
  584. package/dist/shared/connection-compatibility.js +28 -0
  585. package/dist/shared/connection-feedback.d.ts +10 -0
  586. package/dist/shared/connection-feedback.d.ts.map +1 -0
  587. package/dist/shared/connection-feedback.js +17 -0
  588. package/dist/shared/distribution-run-contract.d.ts +49 -0
  589. package/dist/shared/distribution-run-contract.d.ts.map +1 -0
  590. package/dist/shared/distribution-run-contract.js +30 -0
  591. package/dist/shared/distribution-run-tool.d.ts +107 -0
  592. package/dist/shared/distribution-run-tool.d.ts.map +1 -0
  593. package/dist/shared/distribution-run-tool.js +941 -0
  594. package/dist/shared/dormer-wall-opening-placement.d.ts +21 -0
  595. package/dist/shared/dormer-wall-opening-placement.d.ts.map +1 -0
  596. package/dist/shared/dormer-wall-opening-placement.js +101 -0
  597. package/dist/shared/duct-adapter.d.ts +10 -0
  598. package/dist/shared/duct-adapter.d.ts.map +1 -0
  599. package/dist/shared/duct-adapter.js +43 -0
  600. package/dist/shared/elbow-branch-continuation.d.ts +20 -0
  601. package/dist/shared/elbow-branch-continuation.d.ts.map +1 -0
  602. package/dist/shared/elbow-branch-continuation.js +200 -0
  603. package/dist/shared/fitting-clearance.d.ts +5 -0
  604. package/dist/shared/fitting-clearance.d.ts.map +1 -0
  605. package/dist/shared/fitting-clearance.js +11 -0
  606. package/dist/shared/fitting-deletion-cleanup.d.ts +14 -0
  607. package/dist/shared/fitting-deletion-cleanup.d.ts.map +1 -0
  608. package/dist/shared/fitting-deletion-cleanup.js +297 -0
  609. package/dist/shared/fitting-surface-support.d.ts +5 -0
  610. package/dist/shared/fitting-surface-support.d.ts.map +1 -0
  611. package/dist/shared/fitting-surface-support.js +42 -0
  612. package/dist/shared/fitting-tool-options.d.ts +4 -0
  613. package/dist/shared/fitting-tool-options.d.ts.map +1 -0
  614. package/dist/shared/fitting-tool-options.js +175 -0
  615. package/dist/shared/floor-placement.d.ts +1 -1
  616. package/dist/shared/floor-placement.d.ts.map +1 -1
  617. package/dist/shared/floor-placement.js +13 -33
  618. package/dist/shared/ghost-materials.d.ts.map +1 -1
  619. package/dist/shared/ghost-materials.js +2 -1
  620. package/dist/shared/lean-to-post-omissions.d.ts +8 -0
  621. package/dist/shared/lean-to-post-omissions.d.ts.map +1 -0
  622. package/dist/shared/lean-to-post-omissions.js +51 -0
  623. package/dist/shared/mep-fitting-actions.d.ts +5 -0
  624. package/dist/shared/mep-fitting-actions.d.ts.map +1 -0
  625. package/dist/shared/mep-fitting-actions.js +16 -0
  626. package/dist/shared/mep-presets.d.ts +11 -0
  627. package/dist/shared/mep-presets.d.ts.map +1 -0
  628. package/dist/shared/mep-presets.js +34 -0
  629. package/dist/shared/node-batch/candidates.d.ts +29 -0
  630. package/dist/shared/node-batch/candidates.d.ts.map +1 -0
  631. package/dist/shared/node-batch/candidates.js +260 -0
  632. package/dist/shared/node-batch/store.d.ts +26 -0
  633. package/dist/shared/node-batch/store.d.ts.map +1 -0
  634. package/dist/shared/node-batch/store.js +319 -0
  635. package/dist/shared/node-batch/system.d.ts +8 -0
  636. package/dist/shared/node-batch/system.d.ts.map +1 -0
  637. package/dist/shared/node-batch/system.js +569 -0
  638. package/dist/shared/node-batch/types.d.ts +99 -0
  639. package/dist/shared/node-batch/types.d.ts.map +1 -0
  640. package/dist/shared/node-batch/types.js +4 -0
  641. package/dist/shared/opening-move-history.d.ts +47 -0
  642. package/dist/shared/opening-move-history.d.ts.map +1 -0
  643. package/dist/shared/opening-move-history.js +18 -0
  644. package/dist/shared/opening-move-wall-gate.d.ts +55 -0
  645. package/dist/shared/opening-move-wall-gate.d.ts.map +1 -0
  646. package/dist/shared/opening-move-wall-gate.js +44 -0
  647. package/dist/shared/path-point-affordance.d.ts +2 -2
  648. package/dist/shared/path-point-affordance.d.ts.map +1 -1
  649. package/dist/shared/path-point-affordance.js +30 -6
  650. package/dist/shared/placeholder-geometry.d.ts.map +1 -1
  651. package/dist/shared/placeholder-geometry.js +4 -0
  652. package/dist/shared/ports.d.ts +20 -3
  653. package/dist/shared/ports.d.ts.map +1 -1
  654. package/dist/shared/ports.js +148 -7
  655. package/dist/shared/primitive-uv.d.ts +17 -0
  656. package/dist/shared/primitive-uv.d.ts.map +1 -0
  657. package/dist/shared/primitive-uv.js +107 -0
  658. package/dist/shared/reducer-size.d.ts +2 -0
  659. package/dist/shared/reducer-size.d.ts.map +1 -0
  660. package/dist/shared/reducer-size.js +12 -0
  661. package/dist/shared/ridge-snap.js +1 -1
  662. package/dist/shared/roof-surface.d.ts.map +1 -1
  663. package/dist/shared/roof-surface.js +17 -2
  664. package/dist/shared/run-cursor.d.ts +22 -0
  665. package/dist/shared/run-cursor.d.ts.map +1 -0
  666. package/dist/shared/run-cursor.js +19 -0
  667. package/dist/shared/run-direction-feedback.d.ts +20 -0
  668. package/dist/shared/run-direction-feedback.d.ts.map +1 -0
  669. package/dist/shared/run-direction-feedback.js +139 -0
  670. package/dist/shared/run-hanger-controls.d.ts +7 -0
  671. package/dist/shared/run-hanger-controls.d.ts.map +1 -0
  672. package/dist/shared/run-hanger-controls.js +44 -0
  673. package/dist/shared/run-hanger-inspector.d.ts +5 -0
  674. package/dist/shared/run-hanger-inspector.d.ts.map +1 -0
  675. package/dist/shared/run-hanger-inspector.js +43 -0
  676. package/dist/shared/run-hanger-mode.d.ts +11 -0
  677. package/dist/shared/run-hanger-mode.d.ts.map +1 -0
  678. package/dist/shared/run-hanger-mode.js +32 -0
  679. package/dist/shared/run-hanger-system.d.ts +3 -0
  680. package/dist/shared/run-hanger-system.d.ts.map +1 -0
  681. package/dist/shared/run-hanger-system.js +39 -0
  682. package/dist/shared/run-hangers.d.ts +25 -0
  683. package/dist/shared/run-hangers.d.ts.map +1 -0
  684. package/dist/shared/run-hangers.js +261 -0
  685. package/dist/shared/run-port-snap.d.ts +11 -0
  686. package/dist/shared/run-port-snap.d.ts.map +1 -0
  687. package/dist/shared/run-port-snap.js +19 -0
  688. package/dist/shared/run-screen-direction.d.ts +5 -0
  689. package/dist/shared/run-screen-direction.d.ts.map +1 -0
  690. package/dist/shared/run-screen-direction.js +24 -0
  691. package/dist/shared/selection-handles.d.ts +4 -0
  692. package/dist/shared/selection-handles.d.ts.map +1 -1
  693. package/dist/shared/selection-handles.js +10 -0
  694. package/dist/shared/slot-paint.d.ts +8 -1
  695. package/dist/shared/slot-paint.d.ts.map +1 -1
  696. package/dist/shared/slot-paint.js +120 -27
  697. package/dist/shared/surface-paint.d.ts +1 -2
  698. package/dist/shared/surface-paint.d.ts.map +1 -1
  699. package/dist/shared/surface-paint.js +9 -20
  700. package/dist/shared/system-check-panel.d.ts +6 -0
  701. package/dist/shared/system-check-panel.d.ts.map +1 -0
  702. package/dist/shared/system-check-panel.js +62 -0
  703. package/dist/shared/system-checks.d.ts +9 -0
  704. package/dist/shared/system-checks.d.ts.map +1 -0
  705. package/dist/shared/system-checks.js +131 -0
  706. package/dist/shared/wall-attach-target.d.ts +14 -2
  707. package/dist/shared/wall-attach-target.d.ts.map +1 -1
  708. package/dist/shared/wall-attach-target.js +108 -4
  709. package/dist/shared/wall-opening-clearance.d.ts +19 -0
  710. package/dist/shared/wall-opening-clearance.d.ts.map +1 -0
  711. package/dist/shared/wall-opening-clearance.js +28 -0
  712. package/dist/shared/wall-run-move.d.ts +17 -0
  713. package/dist/shared/wall-run-move.d.ts.map +1 -0
  714. package/dist/shared/wall-run-move.js +56 -0
  715. package/dist/shelf/parametrics.js +4 -4
  716. package/dist/site/renderer.d.ts.map +1 -1
  717. package/dist/site/renderer.js +40 -28
  718. package/dist/site/terrain-geometry.d.ts +1 -1
  719. package/dist/site/terrain-geometry.d.ts.map +1 -1
  720. package/dist/site/terrain-geometry.js +35 -9
  721. package/dist/site/terrain-mesh.d.ts +5 -0
  722. package/dist/site/terrain-mesh.d.ts.map +1 -1
  723. package/dist/site/terrain-mesh.js +63 -34
  724. package/dist/skylight/definition.js +1 -1
  725. package/dist/skylight/panel.js +1 -1
  726. package/dist/skylight/parametrics.js +2 -2
  727. package/dist/slab/definition.d.ts.map +1 -1
  728. package/dist/slab/definition.js +6 -1
  729. package/dist/slab/dependency-tracker.d.ts +3 -0
  730. package/dist/slab/dependency-tracker.d.ts.map +1 -0
  731. package/dist/slab/dependency-tracker.js +104 -0
  732. package/dist/slab/floorplan-affordances.d.ts +8 -8
  733. package/dist/slab/geometry.d.ts.map +1 -1
  734. package/dist/slab/geometry.js +16 -2
  735. package/dist/slab/panel.js +1 -1
  736. package/dist/slab/parametrics.js +1 -1
  737. package/dist/slab/system.d.ts.map +1 -1
  738. package/dist/slab/system.js +8 -64
  739. package/dist/solar-panel/definition.js +1 -1
  740. package/dist/solar-panel/panel.js +1 -1
  741. package/dist/solar-panel/parametrics.js +2 -2
  742. package/dist/stair/panel.d.ts.map +1 -1
  743. package/dist/stair/panel.js +18 -13
  744. package/dist/stair/stair-type.d.ts +18 -0
  745. package/dist/stair/stair-type.d.ts.map +1 -0
  746. package/dist/stair/stair-type.js +25 -0
  747. package/dist/stair-segment/panel.d.ts.map +1 -1
  748. package/dist/stair-segment/panel.js +44 -5
  749. package/dist/turbine-vent/definition.d.ts.map +1 -1
  750. package/dist/turbine-vent/definition.js +8 -5
  751. package/dist/turbine-vent/geometry.d.ts.map +1 -1
  752. package/dist/turbine-vent/geometry.js +48 -18
  753. package/dist/turbine-vent/paint.d.ts +4 -0
  754. package/dist/turbine-vent/paint.d.ts.map +1 -0
  755. package/dist/turbine-vent/paint.js +35 -0
  756. package/dist/turbine-vent/panel.js +1 -1
  757. package/dist/turbine-vent/parametrics.js +2 -2
  758. package/dist/turbine-vent/renderer.d.ts.map +1 -1
  759. package/dist/turbine-vent/renderer.js +31 -9
  760. package/dist/wall/curve-eligibility.d.ts +3 -0
  761. package/dist/wall/curve-eligibility.d.ts.map +1 -0
  762. package/dist/wall/curve-eligibility.js +10 -0
  763. package/dist/wall/curve-tool.d.ts.map +1 -1
  764. package/dist/wall/curve-tool.js +8 -11
  765. package/dist/wall/definition.d.ts +1 -1
  766. package/dist/wall/definition.d.ts.map +1 -1
  767. package/dist/wall/definition.js +36 -13
  768. package/dist/wall/floorplan-affordances.d.ts +1 -0
  769. package/dist/wall/floorplan-affordances.d.ts.map +1 -1
  770. package/dist/wall/floorplan-affordances.js +46 -7
  771. package/dist/wall/floorplan-move.d.ts +3 -4
  772. package/dist/wall/floorplan-move.d.ts.map +1 -1
  773. package/dist/wall/floorplan-move.js +122 -9
  774. package/dist/wall/floorplan.d.ts.map +1 -1
  775. package/dist/wall/floorplan.js +19 -23
  776. package/dist/wall/move-shared.d.ts.map +1 -1
  777. package/dist/wall/move-shared.js +10 -7
  778. package/dist/wall/paint.d.ts.map +1 -1
  779. package/dist/wall/paint.js +2 -0
  780. package/dist/wall/panel.d.ts.map +1 -1
  781. package/dist/wall/panel.js +10 -17
  782. package/dist/wall/parametrics.js +2 -2
  783. package/dist/wall/pointer-transparency.d.ts +128 -0
  784. package/dist/wall/pointer-transparency.d.ts.map +1 -0
  785. package/dist/wall/pointer-transparency.js +147 -0
  786. package/dist/wall/renderer.d.ts.map +1 -1
  787. package/dist/wall/renderer.js +65 -8
  788. package/dist/wall/selection-hit-owner.d.ts +53 -0
  789. package/dist/wall/selection-hit-owner.d.ts.map +1 -0
  790. package/dist/wall/selection-hit-owner.js +79 -0
  791. package/dist/wall/system.d.ts +2 -0
  792. package/dist/wall/system.d.ts.map +1 -1
  793. package/dist/wall/system.js +66 -25
  794. package/dist/wall/tool.d.ts.map +1 -1
  795. package/dist/wall/tool.js +5 -0
  796. package/dist/wall/treatment-level-data.d.ts +6 -1
  797. package/dist/wall/treatment-level-data.d.ts.map +1 -1
  798. package/dist/wall/treatment-level-data.js +82 -5
  799. package/dist/wall/treatments.d.ts +1 -0
  800. package/dist/wall/treatments.d.ts.map +1 -1
  801. package/dist/wall/treatments.js +5 -0
  802. package/dist/wall/wall-batch-system.d.ts +38 -6
  803. package/dist/wall/wall-batch-system.d.ts.map +1 -1
  804. package/dist/wall/wall-batch-system.js +122 -25
  805. package/dist/wall/wall-batch.js +2 -2
  806. package/dist/window/definition.d.ts +2 -1
  807. package/dist/window/definition.d.ts.map +1 -1
  808. package/dist/window/definition.js +51 -24
  809. package/dist/window/floorplan-move.d.ts.map +1 -1
  810. package/dist/window/floorplan-move.js +1 -1
  811. package/dist/window/move-tool.d.ts +7 -5
  812. package/dist/window/move-tool.d.ts.map +1 -1
  813. package/dist/window/move-tool.js +344 -58
  814. package/dist/window/panel.d.ts.map +1 -1
  815. package/dist/window/panel.js +2 -0
  816. package/dist/window/parametrics.js +2 -2
  817. package/dist/window/renderer.d.ts.map +1 -1
  818. package/dist/window/renderer.js +5 -7
  819. package/dist/window/tool.d.ts.map +1 -1
  820. package/dist/window/tool.js +227 -13
  821. package/dist/zone/floorplan-affordances.d.ts +12 -12
  822. package/package.json +11 -9
  823. package/dist/dormer/panel-window-section.d.ts +0 -14
  824. package/dist/dormer/panel-window-section.d.ts.map +0 -1
  825. package/dist/dormer/panel-window-section.js +0 -59
  826. package/dist/dormer/window-assembly.d.ts +0 -22
  827. package/dist/dormer/window-assembly.d.ts.map +0 -1
  828. package/dist/dormer/window-assembly.js +0 -111
  829. package/dist/dormer/window-frame.d.ts +0 -24
  830. package/dist/dormer/window-frame.d.ts.map +0 -1
  831. package/dist/dormer/window-frame.js +0 -100
@@ -0,0 +1,1144 @@
1
+ import { blockUndirectedEdgeKey, getBlockFaceCentroid, getBlockFaceNormal, inspectBlockTopology, } from '@pascal-app/core';
2
+ function normalize(point) {
3
+ const length = Math.hypot(point[0], point[1], point[2]);
4
+ if (length < 1e-8)
5
+ return null;
6
+ return [point[0] / length, point[1] / length, point[2] / length];
7
+ }
8
+ export function blockFaceNormal(topology, face) {
9
+ return getBlockFaceNormal(topology, face);
10
+ }
11
+ export function blockFaceCentroid(topology, face) {
12
+ return getBlockFaceCentroid(topology, face);
13
+ }
14
+ function nextNumericId(prefix, ids) {
15
+ const pattern = new RegExp(`^${prefix}(\\d+)$`);
16
+ let next = ids.reduce((highest, id) => {
17
+ const match = pattern.exec(id);
18
+ return match ? Math.max(highest, Number(match[1]) + 1) : highest;
19
+ }, 0);
20
+ const occupied = new Set(ids);
21
+ return () => {
22
+ let candidate = `${prefix}${next++}`;
23
+ while (occupied.has(candidate))
24
+ candidate = `${prefix}${next++}`;
25
+ occupied.add(candidate);
26
+ return candidate;
27
+ };
28
+ }
29
+ function oppositeOrientedEdgeVertices(face, orientedVertices) {
30
+ if (face.vertexIds.length !== 4)
31
+ return null;
32
+ const [from, to] = orientedVertices;
33
+ const index = face.vertexIds.indexOf(from);
34
+ if (index < 0)
35
+ return null;
36
+ if (face.vertexIds[(index + 1) % 4] === to) {
37
+ return [face.vertexIds[(index + 3) % 4], face.vertexIds[(index + 2) % 4]];
38
+ }
39
+ if (face.vertexIds[(index + 3) % 4] === to) {
40
+ return [face.vertexIds[(index + 1) % 4], face.vertexIds[(index + 2) % 4]];
41
+ }
42
+ return null;
43
+ }
44
+ function resolveLoopCutRing(topology, edgeId) {
45
+ const startEdge = topology.edges.find((edge) => edge.id === edgeId);
46
+ if (!startEdge)
47
+ return null;
48
+ const edgeByKey = new Map(topology.edges.map((edge) => [blockUndirectedEdgeKey(...edge.vertexIds), edge]));
49
+ const facesByEdgeId = new Map();
50
+ for (const face of topology.faces) {
51
+ for (let index = 0; index < face.vertexIds.length; index += 1) {
52
+ const edge = edgeByKey.get(blockUndirectedEdgeKey(face.vertexIds[index], face.vertexIds[(index + 1) % face.vertexIds.length]));
53
+ if (!edge)
54
+ return null;
55
+ const faces = facesByEdgeId.get(edge.id) ?? [];
56
+ faces.push(face);
57
+ facesByEdgeId.set(edge.id, faces);
58
+ }
59
+ }
60
+ const incidentStartFaces = facesByEdgeId.get(startEdge.id) ?? [];
61
+ if (incidentStartFaces.length === 0 || incidentStartFaces.length > 2)
62
+ return null;
63
+ const startFaces = incidentStartFaces.filter((face) => face.vertexIds.length === 4);
64
+ if (startFaces.length === 0)
65
+ return null;
66
+ const orientedEdgeVertices = new Map([
67
+ [startEdge.id, startEdge.vertexIds],
68
+ ]);
69
+ const queue = startFaces.map((face) => ({
70
+ edgeId: startEdge.id,
71
+ faceId: face.id,
72
+ }));
73
+ const visitedFaces = new Set();
74
+ const steps = [];
75
+ while (queue.length > 0) {
76
+ const current = queue.shift();
77
+ if (visitedFaces.has(current.faceId))
78
+ continue;
79
+ const face = topology.faces.find((entry) => entry.id === current.faceId);
80
+ const orientedVertices = orientedEdgeVertices.get(current.edgeId);
81
+ if (!(face && orientedVertices) || face.vertexIds.length !== 4)
82
+ return null;
83
+ const oppositeVertices = oppositeOrientedEdgeVertices(face, orientedVertices);
84
+ if (!oppositeVertices)
85
+ return null;
86
+ const oppositeEdge = edgeByKey.get(blockUndirectedEdgeKey(...oppositeVertices));
87
+ if (!oppositeEdge)
88
+ return null;
89
+ const existingOrientation = orientedEdgeVertices.get(oppositeEdge.id);
90
+ if (existingOrientation &&
91
+ (existingOrientation[0] !== oppositeVertices[0] ||
92
+ existingOrientation[1] !== oppositeVertices[1])) {
93
+ return null;
94
+ }
95
+ orientedEdgeVertices.set(oppositeEdge.id, oppositeVertices);
96
+ visitedFaces.add(face.id);
97
+ steps.push({
98
+ faceId: face.id,
99
+ fromEdgeId: current.edgeId,
100
+ toEdgeId: oppositeEdge.id,
101
+ });
102
+ const adjacentFaces = facesByEdgeId.get(oppositeEdge.id) ?? [];
103
+ if (adjacentFaces.length > 2)
104
+ return null;
105
+ for (const adjacentFace of adjacentFaces) {
106
+ if (adjacentFace.id === face.id || visitedFaces.has(adjacentFace.id))
107
+ continue;
108
+ if (adjacentFace.vertexIds.length !== 4)
109
+ continue;
110
+ queue.push({ edgeId: oppositeEdge.id, faceId: adjacentFace.id });
111
+ }
112
+ }
113
+ return steps.length > 0 ? { steps, orientedEdgeVertices } : null;
114
+ }
115
+ function interpolatePoint(from, to, factor) {
116
+ return [
117
+ from[0] + (to[0] - from[0]) * factor,
118
+ from[1] + (to[1] - from[1]) * factor,
119
+ from[2] + (to[2] - from[2]) * factor,
120
+ ];
121
+ }
122
+ export function blockLoopCutSegments(topology, edgeId, factor, cuts = 1) {
123
+ const ring = resolveLoopCutRing(topology, edgeId);
124
+ const fractions = loopCutFractions(factor, cuts);
125
+ if (!ring || !fractions)
126
+ return null;
127
+ const vertexById = new Map(topology.vertices.map((vertex) => [vertex.id, vertex.position]));
128
+ const pointByEdgeId = new Map();
129
+ for (const [ringEdgeId, [fromId, toId]] of ring.orientedEdgeVertices) {
130
+ const from = vertexById.get(fromId);
131
+ const to = vertexById.get(toId);
132
+ if (!(from && to))
133
+ return null;
134
+ pointByEdgeId.set(ringEdgeId, fractions.map((fraction) => interpolatePoint(from, to, fraction)));
135
+ }
136
+ return ring.steps.flatMap((step) => fractions.map((_, index) => [
137
+ pointByEdgeId.get(step.fromEdgeId)[index],
138
+ pointByEdgeId.get(step.toEdgeId)[index],
139
+ ]));
140
+ }
141
+ function loopCutFractions(factor, cuts) {
142
+ const count = Math.floor(cuts);
143
+ if (!Number.isFinite(factor) ||
144
+ factor <= 0 ||
145
+ factor >= 1 ||
146
+ !Number.isFinite(cuts) ||
147
+ count < 1 ||
148
+ count > 32)
149
+ return null;
150
+ if (count === 1)
151
+ return [factor];
152
+ const spacing = 1 / (count + 1);
153
+ return Array.from({ length: count }, (_, index) => (index + 1) * spacing);
154
+ }
155
+ function augmentFaceLoop(face, cutVerticesByEdgeKey) {
156
+ const augmented = [];
157
+ for (let index = 0; index < face.vertexIds.length; index += 1) {
158
+ const current = face.vertexIds[index];
159
+ const next = face.vertexIds[(index + 1) % face.vertexIds.length];
160
+ augmented.push(current);
161
+ const cuts = cutVerticesByEdgeKey.get(blockUndirectedEdgeKey(current, next));
162
+ if (!cuts)
163
+ continue;
164
+ augmented.push(...(cuts.edgeOrder[0] === current ? cuts.ids : [...cuts.ids].reverse()));
165
+ }
166
+ return augmented;
167
+ }
168
+ function splitLoopByChord(loop, firstCutId, secondCutId) {
169
+ const firstIndex = loop.indexOf(firstCutId);
170
+ const secondIndex = loop.indexOf(secondCutId);
171
+ if (firstIndex < 0 || secondIndex < 0)
172
+ return null;
173
+ const walk = (start, end) => {
174
+ const result = [];
175
+ for (let index = start;; index = (index + 1) % loop.length) {
176
+ result.push(loop[index]);
177
+ if (index === end)
178
+ return result;
179
+ }
180
+ };
181
+ const first = walk(firstIndex, secondIndex);
182
+ const second = walk(secondIndex, firstIndex);
183
+ return first.length >= 3 && second.length >= 3 ? [first, second] : null;
184
+ }
185
+ function loopCut(topology, command) {
186
+ const fractions = loopCutFractions(command.factor, command.cuts ?? 1);
187
+ if (!fractions)
188
+ return {
189
+ ok: false,
190
+ error: 'Loop cut requires 1–32 cuts and a factor between 0 and 1',
191
+ };
192
+ const ring = resolveLoopCutRing(topology, command.edgeId);
193
+ if (!ring)
194
+ return {
195
+ ok: false,
196
+ error: 'Loop cut requires a connected ring of quad faces',
197
+ };
198
+ const vertexById = new Map(topology.vertices.map((vertex) => [vertex.id, vertex]));
199
+ const edgeById = new Map(topology.edges.map((edge) => [edge.id, edge]));
200
+ const allocateVertexId = nextNumericId('v', topology.vertices.map((vertex) => vertex.id));
201
+ const allocateEdgeId = nextNumericId('e', topology.edges.map((edge) => edge.id));
202
+ const allocateFaceId = nextNumericId('f', topology.faces.map((face) => face.id));
203
+ const cutVerticesByEdgeId = new Map();
204
+ const cutVerticesByEdgeKey = new Map();
205
+ const newVertices = [];
206
+ for (const [ringEdgeId, [fromId, toId]] of ring.orientedEdgeVertices) {
207
+ const from = vertexById.get(fromId);
208
+ const to = vertexById.get(toId);
209
+ const edge = edgeById.get(ringEdgeId);
210
+ if (!(from && to && edge))
211
+ return { ok: false, error: 'Loop cut references missing topology' };
212
+ const ids = fractions.map(() => allocateVertexId());
213
+ cutVerticesByEdgeId.set(ringEdgeId, ids);
214
+ const edgeOrderIds = edge.vertexIds[0] === fromId ? ids : [...ids].reverse();
215
+ cutVerticesByEdgeKey.set(blockUndirectedEdgeKey(...edge.vertexIds), {
216
+ edgeOrder: edge.vertexIds,
217
+ ids: edgeOrderIds,
218
+ });
219
+ newVertices.push(...ids.map((id, index) => ({
220
+ id,
221
+ position: interpolatePoint(from.position, to.position, fractions[index]),
222
+ })));
223
+ }
224
+ const splitBoundaryEdges = topology.edges.flatMap((edge) => {
225
+ const cutIds = cutVerticesByEdgeKey.get(blockUndirectedEdgeKey(...edge.vertexIds))?.ids;
226
+ if (!cutIds)
227
+ return [edge];
228
+ const chain = [edge.vertexIds[0], ...cutIds, edge.vertexIds[1]];
229
+ return chain.slice(0, -1).map((vertexId, index) => ({
230
+ id: index === 0 ? edge.id : allocateEdgeId(),
231
+ vertexIds: [vertexId, chain[index + 1]],
232
+ }));
233
+ });
234
+ const stepByFaceId = new Map(ring.steps.map((step) => [step.faceId, step]));
235
+ const cutEdgeIds = [];
236
+ const cutEdges = [];
237
+ const faces = [];
238
+ for (const face of topology.faces) {
239
+ const step = stepByFaceId.get(face.id);
240
+ if (!step) {
241
+ const vertexIds = augmentFaceLoop(face, cutVerticesByEdgeKey);
242
+ faces.push(vertexIds.length === face.vertexIds.length ? face : { ...face, vertexIds });
243
+ continue;
244
+ }
245
+ const fromCutIds = cutVerticesByEdgeId.get(step.fromEdgeId);
246
+ const toCutIds = cutVerticesByEdgeId.get(step.toEdgeId);
247
+ if (!(fromCutIds && toCutIds))
248
+ return { ok: false, error: 'Loop cut references missing topology' };
249
+ let remaining = augmentFaceLoop(face, cutVerticesByEdgeKey);
250
+ const splitLoops = [];
251
+ for (let index = 0; index < fromCutIds.length; index += 1) {
252
+ const fromCutId = fromCutIds[index];
253
+ const toCutId = toCutIds[index];
254
+ const pair = splitLoopByChord(remaining, fromCutId, toCutId);
255
+ if (!pair)
256
+ return { ok: false, error: `Could not split quad face: ${face.id}` };
257
+ const cutEdgeId = allocateEdgeId();
258
+ cutEdgeIds.push(cutEdgeId);
259
+ cutEdges.push({ id: cutEdgeId, vertexIds: [fromCutId, toCutId] });
260
+ if (index === fromCutIds.length - 1) {
261
+ splitLoops.push(...pair);
262
+ }
263
+ else {
264
+ const nextFrom = fromCutIds[index + 1];
265
+ const nextTo = toCutIds[index + 1];
266
+ const remainingIndex = pair.findIndex((candidate) => candidate.includes(nextFrom) && candidate.includes(nextTo));
267
+ if (remainingIndex < 0)
268
+ return {
269
+ ok: false,
270
+ error: `Could not order cuts on quad face: ${face.id}`,
271
+ };
272
+ splitLoops.push(pair[1 - remainingIndex]);
273
+ remaining = pair[remainingIndex];
274
+ }
275
+ }
276
+ faces.push(...splitLoops.map((vertexIds, index) => ({
277
+ ...face,
278
+ id: index === 0 ? face.id : allocateFaceId(),
279
+ vertexIds,
280
+ })));
281
+ }
282
+ const nextTopology = {
283
+ vertices: [...topology.vertices, ...newVertices],
284
+ edges: [...splitBoundaryEdges, ...cutEdges],
285
+ faces,
286
+ };
287
+ const issues = inspectBlockTopology(nextTopology);
288
+ if (issues.length > 0)
289
+ return { ok: false, error: issues[0].message };
290
+ return {
291
+ ok: true,
292
+ topology: nextTopology,
293
+ selection: { mode: 'edge', ids: cutEdgeIds },
294
+ };
295
+ }
296
+ function bevelProfileFactor(value, profile) {
297
+ const exponent = 2 ** ((0.5 - profile) * 4);
298
+ const a = value ** exponent;
299
+ const b = (1 - value) ** exponent;
300
+ return a / (a + b);
301
+ }
302
+ function roundedBevelPoint(origin, start, end, factor) {
303
+ const startDirection = normalize([
304
+ start[0] - origin[0],
305
+ start[1] - origin[1],
306
+ start[2] - origin[2],
307
+ ]);
308
+ const endDirection = normalize([end[0] - origin[0], end[1] - origin[1], end[2] - origin[2]]);
309
+ if (!(startDirection && endDirection))
310
+ return interpolatePoint(start, end, factor);
311
+ const directionDot = Math.max(-1, Math.min(1, startDirection[0] * endDirection[0] +
312
+ startDirection[1] * endDirection[1] +
313
+ startDirection[2] * endDirection[2]));
314
+ if (directionDot < -0.999999)
315
+ return interpolatePoint(start, end, factor);
316
+ const width = Math.hypot(start[0] - origin[0], start[1] - origin[1], start[2] - origin[2]);
317
+ const centerScale = width / (1 + directionDot);
318
+ const center = [
319
+ origin[0] + (startDirection[0] + endDirection[0]) * centerScale,
320
+ origin[1] + (startDirection[1] + endDirection[1]) * centerScale,
321
+ origin[2] + (startDirection[2] + endDirection[2]) * centerScale,
322
+ ];
323
+ const startRadius = [start[0] - center[0], start[1] - center[1], start[2] - center[2]];
324
+ const endRadius = [end[0] - center[0], end[1] - center[1], end[2] - center[2]];
325
+ const radius = Math.hypot(...startRadius);
326
+ const endRadiusLength = Math.hypot(...endRadius);
327
+ if (radius < 1e-8 || endRadiusLength < 1e-8)
328
+ return interpolatePoint(start, end, factor);
329
+ const radiusDot = Math.max(-1, Math.min(1, (startRadius[0] * endRadius[0] +
330
+ startRadius[1] * endRadius[1] +
331
+ startRadius[2] * endRadius[2]) /
332
+ (radius * endRadiusLength)));
333
+ const angle = Math.acos(radiusDot);
334
+ const sine = Math.sin(angle);
335
+ if (Math.abs(sine) < 1e-8)
336
+ return interpolatePoint(start, end, factor);
337
+ const startWeight = Math.sin((1 - factor) * angle) / sine;
338
+ const endWeight = Math.sin(factor * angle) / sine;
339
+ return [
340
+ center[0] + startRadius[0] * startWeight + endRadius[0] * endWeight,
341
+ center[1] + startRadius[1] * startWeight + endRadius[1] * endWeight,
342
+ center[2] + startRadius[2] * startWeight + endRadius[2] * endWeight,
343
+ ];
344
+ }
345
+ function rebuildEdgesFromFaces(topology, faces, allocateEdgeId) {
346
+ const oldByKey = new Map(topology.edges.map((edge) => [blockUndirectedEdgeKey(...edge.vertexIds), edge]));
347
+ const seen = new Set();
348
+ const edges = [];
349
+ for (const face of faces) {
350
+ for (let index = 0; index < face.vertexIds.length; index += 1) {
351
+ const vertexIds = [
352
+ face.vertexIds[index],
353
+ face.vertexIds[(index + 1) % face.vertexIds.length],
354
+ ];
355
+ const key = blockUndirectedEdgeKey(...vertexIds);
356
+ if (seen.has(key))
357
+ continue;
358
+ seen.add(key);
359
+ const old = oldByKey.get(key);
360
+ edges.push(old ?? { id: allocateEdgeId(), vertexIds });
361
+ }
362
+ }
363
+ return edges;
364
+ }
365
+ function bevelOneEdge(topology, edgeId, command) {
366
+ const edge = topology.edges.find((entry) => entry.id === edgeId);
367
+ if (!edge)
368
+ return { ok: false, error: `Edge not found: ${edgeId}` };
369
+ const segments = Math.floor(command.segments);
370
+ if (!Number.isFinite(command.width) || command.width <= 0)
371
+ return { ok: false, error: 'Bevel width must be positive' };
372
+ if (!Number.isFinite(command.segments) || segments < 1 || segments > 12)
373
+ return { ok: false, error: 'Bevel segments must be between 1 and 12' };
374
+ if (!Number.isFinite(command.profile) || command.profile < 0 || command.profile > 1)
375
+ return { ok: false, error: 'Bevel profile must be between 0 and 1' };
376
+ const [aId, bId] = edge.vertexIds;
377
+ const adjacentFaces = topology.faces.filter((face) => faceContainsEdge(face, aId, bId));
378
+ if (adjacentFaces.length !== 2)
379
+ return {
380
+ ok: false,
381
+ error: 'Bevel requires an edge shared by exactly two faces',
382
+ };
383
+ const incidentAt = (id) => topology.faces.filter((face) => face.vertexIds.includes(id));
384
+ const caps = [aId, bId].map((id) => incidentAt(id).filter((face) => !adjacentFaces.some((adjacent) => adjacent.id === face.id)));
385
+ if (caps.some((faces) => faces.length !== 1))
386
+ return {
387
+ ok: false,
388
+ error: 'Bevel currently requires three-face corner endpoints',
389
+ };
390
+ const vertexById = new Map(topology.vertices.map((vertex) => [vertex.id, vertex]));
391
+ const vertexA = vertexById.get(aId);
392
+ const vertexB = vertexById.get(bId);
393
+ if (!(vertexA && vertexB))
394
+ return { ok: false, error: 'Bevel edge references missing vertices' };
395
+ const neighborInFace = (face, id, other) => {
396
+ const index = face.vertexIds.indexOf(id);
397
+ const previous = face.vertexIds[(index - 1 + face.vertexIds.length) % face.vertexIds.length];
398
+ const next = face.vertexIds[(index + 1) % face.vertexIds.length];
399
+ return previous === other ? next : next === other ? previous : null;
400
+ };
401
+ const neighbors = adjacentFaces.map((face) => ({
402
+ a: neighborInFace(face, aId, bId),
403
+ b: neighborInFace(face, bId, aId),
404
+ }));
405
+ if (neighbors.some((entry) => !(entry.a && entry.b)))
406
+ return { ok: false, error: 'Could not resolve bevel corner neighbors' };
407
+ const points = neighbors.flatMap((entry, faceIndex) => [
408
+ ['a', aId],
409
+ ['b', bId],
410
+ ].map(([endpoint, endpointId]) => {
411
+ const neighborId = entry[endpoint];
412
+ const origin = vertexById.get(endpointId).position;
413
+ const neighbor = vertexById.get(neighborId)?.position;
414
+ return neighbor ? { faceIndex, endpoint, origin, neighbor } : null;
415
+ }));
416
+ if (points.some((point) => !point))
417
+ return { ok: false, error: 'Bevel references missing vertices' };
418
+ const safeMaximum = Math.min(...points.map((point) => Math.hypot(point.neighbor[0] - point.origin[0], point.neighbor[1] - point.origin[1], point.neighbor[2] - point.origin[2]))) * 0.49;
419
+ const width = command.clampOverlap ? Math.min(command.width, safeMaximum) : command.width;
420
+ if (!command.clampOverlap && width >= safeMaximum * 2)
421
+ return {
422
+ ok: false,
423
+ error: 'Bevel width overlaps adjacent edges; enable Clamp',
424
+ };
425
+ const offset = (origin, neighbor) => {
426
+ const direction = normalize([
427
+ neighbor[0] - origin[0],
428
+ neighbor[1] - origin[1],
429
+ neighbor[2] - origin[2],
430
+ ]);
431
+ return [
432
+ origin[0] + direction[0] * width,
433
+ origin[1] + direction[1] * width,
434
+ origin[2] + direction[2] * width,
435
+ ];
436
+ };
437
+ const outerA = neighbors.map((entry) => offset(vertexA.position, vertexById.get(entry.a).position));
438
+ const outerB = neighbors.map((entry) => offset(vertexB.position, vertexById.get(entry.b).position));
439
+ const allocateVertexId = nextNumericId('v', topology.vertices.map((vertex) => vertex.id));
440
+ const allocateEdgeId = nextNumericId('e', topology.edges.map((entry) => entry.id));
441
+ const allocateFaceId = nextNumericId('f', topology.faces.map((face) => face.id));
442
+ const railsA = [];
443
+ const railsB = [];
444
+ const newVertices = [];
445
+ for (let index = 0; index <= segments; index += 1) {
446
+ const factor = bevelProfileFactor(index / segments, command.profile);
447
+ const aVertexId = allocateVertexId();
448
+ const bVertexId = allocateVertexId();
449
+ railsA.push(aVertexId);
450
+ railsB.push(bVertexId);
451
+ newVertices.push({
452
+ id: aVertexId,
453
+ position: roundedBevelPoint(vertexA.position, outerA[0], outerA[1], factor),
454
+ }, {
455
+ id: bVertexId,
456
+ position: roundedBevelPoint(vertexB.position, outerB[0], outerB[1], factor),
457
+ });
458
+ }
459
+ const replaceVertex = (loop, id, replacement) => loop.flatMap((vertexId) => (vertexId === id ? replacement : [vertexId]));
460
+ const faces = topology.faces
461
+ .filter((face) => !adjacentFaces.some((adjacent) => adjacent.id === face.id))
462
+ .map((face) => {
463
+ if (face.id === caps[0][0].id) {
464
+ const index = face.vertexIds.indexOf(aId);
465
+ const previous = face.vertexIds[(index - 1 + face.vertexIds.length) % face.vertexIds.length];
466
+ const replacement = previous === neighbors[0].a ? railsA : [...railsA].reverse();
467
+ return {
468
+ ...face,
469
+ vertexIds: replaceVertex(face.vertexIds, aId, replacement),
470
+ };
471
+ }
472
+ if (face.id === caps[1][0].id) {
473
+ const index = face.vertexIds.indexOf(bId);
474
+ const previous = face.vertexIds[(index - 1 + face.vertexIds.length) % face.vertexIds.length];
475
+ const replacement = previous === neighbors[0].b ? railsB : [...railsB].reverse();
476
+ return {
477
+ ...face,
478
+ vertexIds: replaceVertex(face.vertexIds, bId, replacement),
479
+ };
480
+ }
481
+ return face;
482
+ });
483
+ for (let faceIndex = 0; faceIndex < 2; faceIndex += 1) {
484
+ const source = adjacentFaces[faceIndex];
485
+ faces.push({
486
+ ...source,
487
+ vertexIds: source.vertexIds.map((id) => id === aId
488
+ ? railsA[faceIndex === 0 ? 0 : segments]
489
+ : id === bId
490
+ ? railsB[faceIndex === 0 ? 0 : segments]
491
+ : id),
492
+ });
493
+ }
494
+ const firstFaceForward = adjacentFaces[0].vertexIds.some((id, index) => id === aId &&
495
+ adjacentFaces[0].vertexIds[(index + 1) % adjacentFaces[0].vertexIds.length] === bId);
496
+ for (let index = 0; index < segments; index += 1) {
497
+ const vertexIds = firstFaceForward
498
+ ? [railsB[index], railsA[index], railsA[index + 1], railsB[index + 1]]
499
+ : [railsA[index], railsB[index], railsB[index + 1], railsA[index + 1]];
500
+ faces.push({
501
+ id: allocateFaceId(),
502
+ vertexIds,
503
+ materialSlot: adjacentFaces[0].materialSlot,
504
+ });
505
+ }
506
+ const nextTopology = {
507
+ vertices: [
508
+ ...topology.vertices.filter((vertex) => vertex.id !== aId && vertex.id !== bId),
509
+ ...newVertices,
510
+ ],
511
+ edges: rebuildEdgesFromFaces(topology, faces, allocateEdgeId),
512
+ faces,
513
+ };
514
+ const issues = inspectBlockTopology(nextTopology);
515
+ if (issues.length > 0)
516
+ return { ok: false, error: issues[0].message };
517
+ const selected = nextTopology.edges.filter((entry) => {
518
+ const aRail = railsA.includes(entry.vertexIds[0]) && railsB.includes(entry.vertexIds[1]);
519
+ const bRail = railsB.includes(entry.vertexIds[0]) && railsA.includes(entry.vertexIds[1]);
520
+ return aRail || bRail;
521
+ });
522
+ return {
523
+ ok: true,
524
+ topology: nextTopology,
525
+ selection: { mode: 'edge', ids: selected.map((entry) => entry.id) },
526
+ };
527
+ }
528
+ function bevelEdges(topology, command) {
529
+ const edgeIds = [...new Set(command.edgeIds)];
530
+ if (edgeIds.length === 0)
531
+ return { ok: false, error: 'Select an edge to bevel' };
532
+ const selectedEdges = edgeIds.map((id) => topology.edges.find((edge) => edge.id === id));
533
+ const missingIndex = selectedEdges.findIndex((edge) => !edge);
534
+ if (missingIndex >= 0)
535
+ return { ok: false, error: `Edge not found: ${edgeIds[missingIndex]}` };
536
+ const originalVertexById = new Map(topology.vertices.map((vertex) => [vertex.id, vertex.position]));
537
+ const originalSegments = new Map(selectedEdges.map((edge) => [
538
+ edge.id,
539
+ [
540
+ originalVertexById.get(edge.vertexIds[0]),
541
+ originalVertexById.get(edge.vertexIds[1]),
542
+ ],
543
+ ]));
544
+ let current = topology;
545
+ const selectedResultIds = [];
546
+ for (const edgeId of edgeIds) {
547
+ const originalSegment = originalSegments.get(edgeId);
548
+ const vertexById = new Map(current.vertices.map((vertex) => [vertex.id, vertex.position]));
549
+ const distance = (left, right) => Math.hypot(left[0] - right[0], left[1] - right[1], left[2] - right[2]);
550
+ const remappedEdge = current.edges.find((edge) => edge.id === edgeId) ??
551
+ current.edges.reduce((best, edge) => {
552
+ const start = vertexById.get(edge.vertexIds[0]);
553
+ const end = vertexById.get(edge.vertexIds[1]);
554
+ const score = Math.min(distance(start, originalSegment[0]) + distance(end, originalSegment[1]), distance(start, originalSegment[1]) + distance(end, originalSegment[0]));
555
+ return !best || score < best.score ? { edge, score } : best;
556
+ }, null)?.edge;
557
+ if (!remappedEdge)
558
+ return { ok: false, error: `Could not remap bevel edge: ${edgeId}` };
559
+ const result = bevelOneEdge(current, remappedEdge.id, command);
560
+ if (!result.ok)
561
+ return result;
562
+ current = result.topology;
563
+ selectedResultIds.push(...result.selection.ids);
564
+ }
565
+ const survivingIds = new Set(current.edges.map((edge) => edge.id));
566
+ return {
567
+ ok: true,
568
+ topology: current,
569
+ selection: {
570
+ mode: 'edge',
571
+ ids: [...new Set(selectedResultIds)].filter((id) => survivingIds.has(id)),
572
+ },
573
+ };
574
+ }
575
+ function extrudeFaces(topology, command) {
576
+ const selectedFaceIds = new Set(command.faceIds);
577
+ const selectedFaces = topology.faces.filter((face) => selectedFaceIds.has(face.id));
578
+ if (selectedFaces.length !== selectedFaceIds.size || selectedFaces.length === 0) {
579
+ const missing = command.faceIds.find((id) => !topology.faces.some((face) => face.id === id));
580
+ return { ok: false, error: missing ? `Face not found: ${missing}` : 'Select a face to extrude' };
581
+ }
582
+ if (!Number.isFinite(command.distance) || Math.abs(command.distance) < 1e-6) {
583
+ return {
584
+ ok: false,
585
+ error: 'Extrude distance must be a non-zero finite number',
586
+ };
587
+ }
588
+ const edgeKeysByFace = new Map(selectedFaces.map((face) => [
589
+ face.id,
590
+ face.vertexIds.map((id, index) => blockUndirectedEdgeKey(id, face.vertexIds[(index + 1) % face.vertexIds.length])),
591
+ ]));
592
+ const connected = new Set([selectedFaces[0].id]);
593
+ const queue = [selectedFaces[0].id];
594
+ while (queue.length > 0) {
595
+ const faceId = queue.shift();
596
+ const keys = new Set(edgeKeysByFace.get(faceId));
597
+ for (const candidate of selectedFaces) {
598
+ if (connected.has(candidate.id))
599
+ continue;
600
+ if (edgeKeysByFace.get(candidate.id).some((key) => keys.has(key))) {
601
+ connected.add(candidate.id);
602
+ queue.push(candidate.id);
603
+ }
604
+ }
605
+ }
606
+ if (connected.size !== selectedFaces.length) {
607
+ return { ok: false, error: 'Extrude Region requires connected faces' };
608
+ }
609
+ const normals = selectedFaces.map((face) => blockFaceNormal(topology, face));
610
+ if (normals.some((normal) => !normal)) {
611
+ const invalidFace = selectedFaces[normals.findIndex((normal) => !normal)];
612
+ return { ok: false, error: `Face has no usable normal: ${invalidFace.id}` };
613
+ }
614
+ const normal = command.axis
615
+ ? [
616
+ command.axis === 'x' ? 1 : 0,
617
+ command.axis === 'y' ? 1 : 0,
618
+ command.axis === 'z' ? 1 : 0,
619
+ ]
620
+ : normalize(normals.reduce((sum, value) => [sum[0] + value[0], sum[1] + value[1], sum[2] + value[2]], [0, 0, 0]));
621
+ if (!normal)
622
+ return { ok: false, error: 'Selected face normals cancel each other out' };
623
+ const verticesById = new Map(topology.vertices.map((vertex) => [vertex.id, vertex]));
624
+ const allocateVertexId = nextNumericId('v', topology.vertices.map((vertex) => vertex.id));
625
+ const allocateEdgeId = nextNumericId('e', topology.edges.map((edge) => edge.id));
626
+ const allocateFaceId = nextNumericId('f', topology.faces.map((entry) => entry.id));
627
+ const duplicateIds = new Map();
628
+ const newVertices = [];
629
+ const selectedVertexIds = new Set(selectedFaces.flatMap((face) => face.vertexIds));
630
+ for (const vertexId of selectedVertexIds) {
631
+ const vertex = verticesById.get(vertexId);
632
+ if (!vertex)
633
+ return {
634
+ ok: false,
635
+ error: `Selected face references missing vertex: ${vertexId}`,
636
+ };
637
+ const id = allocateVertexId();
638
+ duplicateIds.set(vertexId, id);
639
+ newVertices.push({
640
+ id,
641
+ position: [
642
+ vertex.position[0] + normal[0] * command.distance,
643
+ vertex.position[1] + normal[1] * command.distance,
644
+ vertex.position[2] + normal[2] * command.distance,
645
+ ],
646
+ });
647
+ }
648
+ const boundaryByKey = new Map();
649
+ for (const face of selectedFaces) {
650
+ for (let index = 0; index < face.vertexIds.length; index += 1) {
651
+ const a = face.vertexIds[index];
652
+ const b = face.vertexIds[(index + 1) % face.vertexIds.length];
653
+ const key = blockUndirectedEdgeKey(a, b);
654
+ const boundary = boundaryByKey.get(key);
655
+ if (boundary)
656
+ boundary.count += 1;
657
+ else
658
+ boundaryByKey.set(key, { count: 1, a, b, source: face });
659
+ }
660
+ }
661
+ const sideFaces = [];
662
+ for (const boundary of boundaryByKey.values()) {
663
+ if (boundary.count !== 1)
664
+ continue;
665
+ const { a, b, source } = boundary;
666
+ const newA = duplicateIds.get(a);
667
+ const newB = duplicateIds.get(b);
668
+ sideFaces.push({
669
+ id: allocateFaceId(),
670
+ vertexIds: [a, b, newB, newA],
671
+ materialSlot: source.materialSlot,
672
+ });
673
+ }
674
+ const faces = [
675
+ ...topology.faces.map((face) => selectedFaceIds.has(face.id)
676
+ ? { ...face, vertexIds: face.vertexIds.map((id) => duplicateIds.get(id)) }
677
+ : face),
678
+ ...sideFaces,
679
+ ];
680
+ const nextTopology = {
681
+ vertices: [...topology.vertices, ...newVertices],
682
+ edges: rebuildEdgesFromFaces(topology, faces, allocateEdgeId),
683
+ faces,
684
+ };
685
+ const issues = inspectBlockTopology(nextTopology);
686
+ if (issues.length > 0)
687
+ return { ok: false, error: issues[0].message };
688
+ return {
689
+ ok: true,
690
+ topology: nextTopology,
691
+ selection: { mode: 'face', ids: selectedFaces.map((face) => face.id) },
692
+ };
693
+ }
694
+ export function blockSelectionVertexIds(topology, selection) {
695
+ const selectedIds = new Set(selection.ids);
696
+ switch (selection.mode) {
697
+ case 'vertex':
698
+ return new Set(topology.vertices.filter((vertex) => selectedIds.has(vertex.id)).map((v) => v.id));
699
+ case 'edge': {
700
+ const vertices = new Set();
701
+ for (const edge of topology.edges) {
702
+ if (!selectedIds.has(edge.id))
703
+ continue;
704
+ vertices.add(edge.vertexIds[0]);
705
+ vertices.add(edge.vertexIds[1]);
706
+ }
707
+ return vertices;
708
+ }
709
+ case 'face': {
710
+ const vertices = new Set();
711
+ for (const face of topology.faces) {
712
+ if (!selectedIds.has(face.id))
713
+ continue;
714
+ for (const vertexId of face.vertexIds)
715
+ vertices.add(vertexId);
716
+ }
717
+ return vertices;
718
+ }
719
+ }
720
+ }
721
+ function translateComponents(topology, command) {
722
+ if (command.delta.some((value) => !Number.isFinite(value))) {
723
+ return {
724
+ ok: false,
725
+ error: 'Translation delta must contain finite numbers',
726
+ };
727
+ }
728
+ const vertexIds = blockSelectionVertexIds(topology, command.selection);
729
+ if (vertexIds.size === 0)
730
+ return { ok: false, error: 'Select a component to move' };
731
+ const nextTopology = {
732
+ ...topology,
733
+ vertices: topology.vertices.map((vertex) => vertexIds.has(vertex.id)
734
+ ? {
735
+ ...vertex,
736
+ position: [
737
+ vertex.position[0] + command.delta[0],
738
+ vertex.position[1] + command.delta[1],
739
+ vertex.position[2] + command.delta[2],
740
+ ],
741
+ }
742
+ : vertex),
743
+ };
744
+ const issues = inspectBlockTopology(nextTopology);
745
+ if (issues.length > 0)
746
+ return { ok: false, error: issues[0].message };
747
+ return { ok: true, topology: nextTopology, selection: command.selection };
748
+ }
749
+ function transformComponents(topology, selection, transform) {
750
+ const vertexIds = blockSelectionVertexIds(topology, selection);
751
+ if (vertexIds.size === 0)
752
+ return { ok: false, error: 'Select a component to transform' };
753
+ const nextTopology = {
754
+ ...topology,
755
+ vertices: topology.vertices.map((vertex) => vertexIds.has(vertex.id) ? { ...vertex, position: transform(vertex.position) } : vertex),
756
+ };
757
+ const issues = inspectBlockTopology(nextTopology);
758
+ if (issues.length > 0)
759
+ return { ok: false, error: issues[0].message };
760
+ return { ok: true, topology: nextTopology, selection };
761
+ }
762
+ function rotateComponents(topology, command) {
763
+ if (!Number.isFinite(command.angle) || command.pivot.some((value) => !Number.isFinite(value))) {
764
+ return { ok: false, error: 'Rotation requires a finite angle and pivot' };
765
+ }
766
+ const axis = normalize(command.axis);
767
+ if (!axis)
768
+ return { ok: false, error: 'Rotation axis must be non-zero' };
769
+ const cosine = Math.cos(command.angle);
770
+ const sine = Math.sin(command.angle);
771
+ return transformComponents(topology, command.selection, (position) => {
772
+ const x = position[0] - command.pivot[0];
773
+ const y = position[1] - command.pivot[1];
774
+ const z = position[2] - command.pivot[2];
775
+ const dot = axis[0] * x + axis[1] * y + axis[2] * z;
776
+ const cross = [
777
+ axis[1] * z - axis[2] * y,
778
+ axis[2] * x - axis[0] * z,
779
+ axis[0] * y - axis[1] * x,
780
+ ];
781
+ return [
782
+ command.pivot[0] + x * cosine + cross[0] * sine + axis[0] * dot * (1 - cosine),
783
+ command.pivot[1] + y * cosine + cross[1] * sine + axis[1] * dot * (1 - cosine),
784
+ command.pivot[2] + z * cosine + cross[2] * sine + axis[2] * dot * (1 - cosine),
785
+ ];
786
+ });
787
+ }
788
+ function scaleComponents(topology, command) {
789
+ if (command.pivot.some((value) => !Number.isFinite(value)) ||
790
+ command.factors.some((value) => !Number.isFinite(value) || Math.abs(value) < 1e-6)) {
791
+ return {
792
+ ok: false,
793
+ error: 'Scale requires finite, non-zero factors and a finite pivot',
794
+ };
795
+ }
796
+ return transformComponents(topology, command.selection, (position) => [
797
+ command.pivot[0] + (position[0] - command.pivot[0]) * command.factors[0],
798
+ command.pivot[1] + (position[1] - command.pivot[1]) * command.factors[1],
799
+ command.pivot[2] + (position[2] - command.pivot[2]) * command.factors[2],
800
+ ]);
801
+ }
802
+ function insetOneFace(topology, faceId, amount, depth) {
803
+ const faceIndex = topology.faces.findIndex((face) => face.id === faceId);
804
+ const face = topology.faces[faceIndex];
805
+ if (!face)
806
+ return { ok: false, error: `Face not found: ${faceId}` };
807
+ if (!Number.isFinite(amount) || amount <= 0 || amount >= 1) {
808
+ return {
809
+ ok: false,
810
+ error: 'Inset amount must be greater than 0 and less than 1',
811
+ };
812
+ }
813
+ if (!Number.isFinite(depth))
814
+ return { ok: false, error: 'Inset depth must be finite' };
815
+ const centroid = blockFaceCentroid(topology, face);
816
+ const normal = blockFaceNormal(topology, face);
817
+ if (!(centroid && normal))
818
+ return { ok: false, error: `Face cannot be inset: ${face.id}` };
819
+ const verticesById = new Map(topology.vertices.map((vertex) => [vertex.id, vertex]));
820
+ const allocateVertexId = nextNumericId('v', topology.vertices.map((vertex) => vertex.id));
821
+ const allocateEdgeId = nextNumericId('e', topology.edges.map((edge) => edge.id));
822
+ const allocateFaceId = nextNumericId('f', topology.faces.map((entry) => entry.id));
823
+ const insetIds = [];
824
+ const newVertices = [];
825
+ for (const vertexId of face.vertexIds) {
826
+ const vertex = verticesById.get(vertexId);
827
+ if (!vertex)
828
+ return {
829
+ ok: false,
830
+ error: `Face references missing vertex: ${vertexId}`,
831
+ };
832
+ const id = allocateVertexId();
833
+ insetIds.push(id);
834
+ newVertices.push({
835
+ id,
836
+ position: [
837
+ vertex.position[0] + (centroid[0] - vertex.position[0]) * amount + normal[0] * depth,
838
+ vertex.position[1] + (centroid[1] - vertex.position[1]) * amount + normal[1] * depth,
839
+ vertex.position[2] + (centroid[2] - vertex.position[2]) * amount + normal[2] * depth,
840
+ ],
841
+ });
842
+ }
843
+ const newEdges = [];
844
+ const ringFaces = [];
845
+ for (let index = 0; index < face.vertexIds.length; index += 1) {
846
+ const oldA = face.vertexIds[index];
847
+ const oldB = face.vertexIds[(index + 1) % face.vertexIds.length];
848
+ const insetA = insetIds[index];
849
+ const insetB = insetIds[(index + 1) % insetIds.length];
850
+ newEdges.push({ id: allocateEdgeId(), vertexIds: [insetA, insetB] });
851
+ newEdges.push({ id: allocateEdgeId(), vertexIds: [oldA, insetA] });
852
+ ringFaces.push({
853
+ id: allocateFaceId(),
854
+ vertexIds: [oldA, oldB, insetB, insetA],
855
+ materialSlot: face.materialSlot,
856
+ });
857
+ }
858
+ const faces = topology.faces.slice();
859
+ faces[faceIndex] = { ...face, vertexIds: insetIds };
860
+ const nextTopology = {
861
+ vertices: [...topology.vertices, ...newVertices],
862
+ edges: [...topology.edges, ...newEdges],
863
+ faces: [...faces, ...ringFaces],
864
+ };
865
+ const issues = inspectBlockTopology(nextTopology);
866
+ if (issues.length > 0)
867
+ return { ok: false, error: issues[0].message };
868
+ return {
869
+ ok: true,
870
+ topology: nextTopology,
871
+ selection: { mode: 'face', ids: [face.id] },
872
+ };
873
+ }
874
+ function insetFaces(topology, command) {
875
+ const faceIds = [...new Set(command.faceIds)];
876
+ if (faceIds.length === 0)
877
+ return { ok: false, error: 'Select a face to inset' };
878
+ const missing = faceIds.find((id) => !topology.faces.some((face) => face.id === id));
879
+ if (missing)
880
+ return { ok: false, error: `Face not found: ${missing}` };
881
+ let current = topology;
882
+ for (const faceId of faceIds) {
883
+ const result = insetOneFace(current, faceId, command.amount, command.depth);
884
+ if (!result.ok)
885
+ return result;
886
+ current = result.topology;
887
+ }
888
+ return { ok: true, topology: current, selection: { mode: 'face', ids: faceIds } };
889
+ }
890
+ function deleteComponents(topology, command) {
891
+ const selected = new Set(command.selection.ids);
892
+ if (selected.size === 0)
893
+ return { ok: false, error: 'Select a component to delete' };
894
+ let vertices = topology.vertices;
895
+ let edges = topology.edges;
896
+ let faces = topology.faces;
897
+ if (command.selection.mode === 'face') {
898
+ faces = faces.filter((face) => !selected.has(face.id));
899
+ }
900
+ else if (command.selection.mode === 'edge') {
901
+ const removedKeys = new Set(edges
902
+ .filter((edge) => selected.has(edge.id))
903
+ .map((edge) => blockUndirectedEdgeKey(...edge.vertexIds)));
904
+ edges = edges.filter((edge) => !selected.has(edge.id));
905
+ faces = faces.filter((face) => face.vertexIds.every((vertexId, index) => {
906
+ const next = face.vertexIds[(index + 1) % face.vertexIds.length];
907
+ const key = blockUndirectedEdgeKey(vertexId, next);
908
+ return !removedKeys.has(key);
909
+ }));
910
+ }
911
+ else {
912
+ vertices = vertices.filter((vertex) => !selected.has(vertex.id));
913
+ edges = edges.filter((edge) => !selected.has(edge.vertexIds[0]) && !selected.has(edge.vertexIds[1]));
914
+ faces = faces.filter((face) => face.vertexIds.every((vertexId) => !selected.has(vertexId)));
915
+ }
916
+ const nextTopology = { vertices, edges, faces };
917
+ const issues = inspectBlockTopology(nextTopology);
918
+ if (issues.length > 0)
919
+ return { ok: false, error: issues[0].message };
920
+ return {
921
+ ok: true,
922
+ topology: nextTopology,
923
+ selection: { mode: command.selection.mode, ids: [] },
924
+ };
925
+ }
926
+ function mergeVertices(topology, command) {
927
+ const selected = new Set(command.vertexIds);
928
+ const selectedVertices = topology.vertices.filter((vertex) => selected.has(vertex.id));
929
+ if (selectedVertices.length < 2)
930
+ return { ok: false, error: 'Select at least two vertices to merge' };
931
+ const keepId = [...command.vertexIds]
932
+ .reverse()
933
+ .find((id) => selectedVertices.some((v) => v.id === id));
934
+ const center = selectedVertices.reduce((sum, vertex) => [
935
+ sum[0] + vertex.position[0],
936
+ sum[1] + vertex.position[1],
937
+ sum[2] + vertex.position[2],
938
+ ], [0, 0, 0]);
939
+ center[0] /= selectedVertices.length;
940
+ center[1] /= selectedVertices.length;
941
+ center[2] /= selectedVertices.length;
942
+ const mapVertexId = (id) => (selected.has(id) ? keepId : id);
943
+ const edgeKeys = new Set();
944
+ const edges = topology.edges.flatMap((edge) => {
945
+ const a = mapVertexId(edge.vertexIds[0]);
946
+ const b = mapVertexId(edge.vertexIds[1]);
947
+ if (a === b)
948
+ return [];
949
+ const key = blockUndirectedEdgeKey(a, b);
950
+ if (edgeKeys.has(key))
951
+ return [];
952
+ edgeKeys.add(key);
953
+ return [{ ...edge, vertexIds: [a, b] }];
954
+ });
955
+ const faces = [];
956
+ for (const face of topology.faces) {
957
+ const mapped = face.vertexIds.map(mapVertexId);
958
+ const loop = [];
959
+ for (const id of mapped) {
960
+ if (loop.at(-1) !== id)
961
+ loop.push(id);
962
+ }
963
+ if (loop.length > 1 && loop[0] === loop.at(-1))
964
+ loop.pop();
965
+ if (loop.length < 3 || new Set(loop).size < 3)
966
+ continue;
967
+ if (new Set(loop).size !== loop.length) {
968
+ return {
969
+ ok: false,
970
+ error: 'The selected vertices would create a repeated face vertex',
971
+ };
972
+ }
973
+ faces.push({ ...face, vertexIds: loop });
974
+ }
975
+ const nextTopology = {
976
+ vertices: topology.vertices
977
+ .filter((vertex) => vertex.id === keepId || !selected.has(vertex.id))
978
+ .map((vertex) => (vertex.id === keepId ? { ...vertex, position: center } : vertex)),
979
+ edges,
980
+ faces,
981
+ };
982
+ const issues = inspectBlockTopology(nextTopology);
983
+ if (issues.length > 0)
984
+ return { ok: false, error: issues[0].message };
985
+ return {
986
+ ok: true,
987
+ topology: nextTopology,
988
+ selection: { mode: 'vertex', ids: [keepId] },
989
+ };
990
+ }
991
+ function faceContainsEdge(face, a, b) {
992
+ return face.vertexIds.some((vertexId, index) => {
993
+ const next = face.vertexIds[(index + 1) % face.vertexIds.length];
994
+ return (vertexId === a && next === b) || (vertexId === b && next === a);
995
+ });
996
+ }
997
+ function longFacePath(face, start, end) {
998
+ const startIndex = face.vertexIds.indexOf(start);
999
+ if (startIndex < 0)
1000
+ return null;
1001
+ const forward = [start];
1002
+ for (let offset = 1; offset <= face.vertexIds.length; offset += 1) {
1003
+ const id = face.vertexIds[(startIndex + offset) % face.vertexIds.length];
1004
+ forward.push(id);
1005
+ if (id === end)
1006
+ break;
1007
+ }
1008
+ if (forward.at(-1) !== end)
1009
+ return null;
1010
+ if (forward.length > 2)
1011
+ return forward;
1012
+ const backward = [start];
1013
+ for (let offset = 1; offset <= face.vertexIds.length; offset += 1) {
1014
+ const index = (startIndex - offset + face.vertexIds.length) % face.vertexIds.length;
1015
+ const id = face.vertexIds[index];
1016
+ backward.push(id);
1017
+ if (id === end)
1018
+ break;
1019
+ }
1020
+ return backward.at(-1) === end && backward.length > 2 ? backward : null;
1021
+ }
1022
+ function dissolveOneEdge(topology, edgeId) {
1023
+ const edge = topology.edges.find((entry) => entry.id === edgeId);
1024
+ if (!edge)
1025
+ return { ok: false, error: `Edge not found: ${edgeId}` };
1026
+ const [a, b] = edge.vertexIds;
1027
+ const adjacentFaces = topology.faces.filter((face) => faceContainsEdge(face, a, b));
1028
+ if (adjacentFaces.length !== 2) {
1029
+ return {
1030
+ ok: false,
1031
+ error: 'Dissolve requires an edge shared by exactly two faces',
1032
+ };
1033
+ }
1034
+ const firstPath = longFacePath(adjacentFaces[0], a, b);
1035
+ const secondPath = longFacePath(adjacentFaces[1], b, a);
1036
+ if (!(firstPath && secondPath))
1037
+ return { ok: false, error: 'Could not resolve adjacent face loops' };
1038
+ const mergedLoop = [...firstPath, ...secondPath.slice(1, -1)];
1039
+ if (new Set(mergedLoop).size !== mergedLoop.length) {
1040
+ return {
1041
+ ok: false,
1042
+ error: 'Dissolving this edge would create a repeated face vertex',
1043
+ };
1044
+ }
1045
+ const removedFaceId = adjacentFaces[1].id;
1046
+ const nextTopology = {
1047
+ vertices: topology.vertices,
1048
+ edges: topology.edges.filter((entry) => entry.id !== edge.id),
1049
+ faces: topology.faces
1050
+ .filter((face) => face.id !== removedFaceId)
1051
+ .map((face) => face.id === adjacentFaces[0].id ? { ...face, vertexIds: mergedLoop } : face),
1052
+ };
1053
+ const issues = inspectBlockTopology(nextTopology);
1054
+ if (issues.length > 0)
1055
+ return { ok: false, error: issues[0].message };
1056
+ return {
1057
+ ok: true,
1058
+ topology: nextTopology,
1059
+ selection: { mode: 'face', ids: [adjacentFaces[0].id] },
1060
+ };
1061
+ }
1062
+ function dissolveEdges(topology, command) {
1063
+ const edgeIds = [...new Set(command.edgeIds)];
1064
+ if (edgeIds.length === 0)
1065
+ return { ok: false, error: 'Select an edge to dissolve' };
1066
+ const missing = edgeIds.find((id) => !topology.edges.some((edge) => edge.id === id));
1067
+ if (missing)
1068
+ return { ok: false, error: `Edge not found: ${missing}` };
1069
+ let current = topology;
1070
+ const resultFaceIds = [];
1071
+ for (const edgeId of edgeIds) {
1072
+ const result = dissolveOneEdge(current, edgeId);
1073
+ if (!result.ok)
1074
+ return result;
1075
+ current = result.topology;
1076
+ resultFaceIds.push(...result.selection.ids);
1077
+ }
1078
+ const survivingFaceIds = new Set(current.faces.map((face) => face.id));
1079
+ return {
1080
+ ok: true,
1081
+ topology: current,
1082
+ selection: {
1083
+ mode: 'face',
1084
+ ids: [...new Set(resultFaceIds)].filter((id) => survivingFaceIds.has(id)),
1085
+ },
1086
+ };
1087
+ }
1088
+ function dissolveFaces(topology, command) {
1089
+ const faceIds = new Set(command.faceIds);
1090
+ if (faceIds.size < 2)
1091
+ return { ok: false, error: 'Select at least two faces to dissolve' };
1092
+ const missing = [...faceIds].find((id) => !topology.faces.some((face) => face.id === id));
1093
+ if (missing)
1094
+ return { ok: false, error: `Face not found: ${missing}` };
1095
+ const internalEdgeIds = topology.edges
1096
+ .filter((edge) => {
1097
+ const incidentSelectedFaces = topology.faces.filter((face) => faceIds.has(face.id) && faceContainsEdge(face, ...edge.vertexIds));
1098
+ return incidentSelectedFaces.length === 2;
1099
+ })
1100
+ .map((edge) => edge.id);
1101
+ if (internalEdgeIds.length === 0) {
1102
+ return { ok: false, error: 'Selected faces do not share a dissolvable boundary' };
1103
+ }
1104
+ const result = dissolveEdges(topology, { type: 'dissolve-edges', edgeIds: internalEdgeIds });
1105
+ if (!result.ok)
1106
+ return result;
1107
+ const survivingSelectedIds = result.topology.faces
1108
+ .filter((face) => faceIds.has(face.id))
1109
+ .map((face) => face.id);
1110
+ return {
1111
+ ok: true,
1112
+ topology: result.topology,
1113
+ selection: { mode: 'face', ids: survivingSelectedIds },
1114
+ };
1115
+ }
1116
+ export function applyBlockCommand(topology, command) {
1117
+ const issues = inspectBlockTopology(topology);
1118
+ if (issues.length > 0)
1119
+ return { ok: false, error: issues[0].message };
1120
+ switch (command.type) {
1121
+ case 'extrude-faces':
1122
+ return extrudeFaces(topology, command);
1123
+ case 'translate-components':
1124
+ return translateComponents(topology, command);
1125
+ case 'rotate-components':
1126
+ return rotateComponents(topology, command);
1127
+ case 'scale-components':
1128
+ return scaleComponents(topology, command);
1129
+ case 'inset-faces':
1130
+ return insetFaces(topology, command);
1131
+ case 'delete-components':
1132
+ return deleteComponents(topology, command);
1133
+ case 'merge-vertices':
1134
+ return mergeVertices(topology, command);
1135
+ case 'dissolve-edges':
1136
+ return dissolveEdges(topology, command);
1137
+ case 'dissolve-faces':
1138
+ return dissolveFaces(topology, command);
1139
+ case 'loop-cut':
1140
+ return loopCut(topology, command);
1141
+ case 'bevel-edges':
1142
+ return bevelEdges(topology, command);
1143
+ }
1144
+ }