@pascal-app/nodes 1.0.0-beta.5 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (929) 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 +4 -4
  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/appliance-layout.d.ts +26 -0
  102. package/dist/cabinet/appliance-layout.d.ts.map +1 -0
  103. package/dist/cabinet/appliance-layout.js +52 -0
  104. package/dist/cabinet/compartment-card.d.ts.map +1 -1
  105. package/dist/cabinet/compartment-card.js +11 -3
  106. package/dist/cabinet/continuous-placement.d.ts +3 -0
  107. package/dist/cabinet/continuous-placement.d.ts.map +1 -1
  108. package/dist/cabinet/continuous-placement.js +6 -0
  109. package/dist/cabinet/countertop-layout.d.ts +173 -0
  110. package/dist/cabinet/countertop-layout.d.ts.map +1 -0
  111. package/dist/cabinet/countertop-layout.js +80 -0
  112. package/dist/cabinet/definition.d.ts.map +1 -1
  113. package/dist/cabinet/definition.js +414 -61
  114. package/dist/cabinet/floorplan-move.d.ts.map +1 -1
  115. package/dist/cabinet/floorplan-move.js +91 -22
  116. package/dist/cabinet/geometry/cooktop.d.ts.map +1 -1
  117. package/dist/cabinet/geometry/cooktop.js +2 -2
  118. package/dist/cabinet/geometry/fridge.d.ts.map +1 -1
  119. package/dist/cabinet/geometry/fridge.js +25 -1
  120. package/dist/cabinet/geometry/fronts.d.ts +4 -0
  121. package/dist/cabinet/geometry/fronts.d.ts.map +1 -1
  122. package/dist/cabinet/geometry/fronts.js +1 -1
  123. package/dist/cabinet/geometry/run.d.ts.map +1 -1
  124. package/dist/cabinet/geometry/run.js +28 -71
  125. package/dist/cabinet/geometry/sink.d.ts +2 -12
  126. package/dist/cabinet/geometry/sink.d.ts.map +1 -1
  127. package/dist/cabinet/geometry/sink.js +8 -39
  128. package/dist/cabinet/geometry.d.ts.map +1 -1
  129. package/dist/cabinet/geometry.js +63 -1
  130. package/dist/cabinet/hosted-resize.d.ts +8 -0
  131. package/dist/cabinet/hosted-resize.d.ts.map +1 -0
  132. package/dist/cabinet/hosted-resize.js +158 -0
  133. package/dist/cabinet/index.d.ts +1 -0
  134. package/dist/cabinet/index.d.ts.map +1 -1
  135. package/dist/cabinet/index.js +1 -0
  136. package/dist/cabinet/insertion.d.ts +19 -0
  137. package/dist/cabinet/insertion.d.ts.map +1 -0
  138. package/dist/cabinet/insertion.js +49 -0
  139. package/dist/cabinet/move-frame.d.ts.map +1 -1
  140. package/dist/cabinet/move-frame.js +68 -2
  141. package/dist/cabinet/panel-context.d.ts +7 -0
  142. package/dist/cabinet/panel-context.d.ts.map +1 -0
  143. package/dist/cabinet/panel-context.js +16 -0
  144. package/dist/cabinet/panel-visibility.d.ts +9 -0
  145. package/dist/cabinet/panel-visibility.d.ts.map +1 -0
  146. package/dist/cabinet/panel-visibility.js +26 -0
  147. package/dist/cabinet/panel.d.ts.map +1 -1
  148. package/dist/cabinet/panel.js +221 -37
  149. package/dist/cabinet/placement-dimensions.d.ts +42 -0
  150. package/dist/cabinet/placement-dimensions.d.ts.map +1 -0
  151. package/dist/cabinet/placement-dimensions.js +227 -0
  152. package/dist/cabinet/placement-snap.d.ts +21 -1
  153. package/dist/cabinet/placement-snap.d.ts.map +1 -1
  154. package/dist/cabinet/placement-snap.js +48 -6
  155. package/dist/cabinet/preset-width-debt.d.ts +7 -0
  156. package/dist/cabinet/preset-width-debt.d.ts.map +1 -0
  157. package/dist/cabinet/preset-width-debt.js +45 -0
  158. package/dist/cabinet/presets.d.ts.map +1 -1
  159. package/dist/cabinet/presets.js +14 -13
  160. package/dist/cabinet/profiles.d.ts +14 -0
  161. package/dist/cabinet/profiles.d.ts.map +1 -0
  162. package/dist/cabinet/profiles.js +30 -0
  163. package/dist/cabinet/quick-actions.d.ts.map +1 -1
  164. package/dist/cabinet/quick-actions.js +8 -24
  165. package/dist/cabinet/reveals.d.ts +37 -0
  166. package/dist/cabinet/reveals.d.ts.map +1 -0
  167. package/dist/cabinet/reveals.js +13 -0
  168. package/dist/cabinet/run-layout.d.ts +98 -5
  169. package/dist/cabinet/run-layout.d.ts.map +1 -1
  170. package/dist/cabinet/run-layout.js +430 -37
  171. package/dist/cabinet/run-ops.d.ts +204 -12
  172. package/dist/cabinet/run-ops.d.ts.map +1 -1
  173. package/dist/cabinet/run-ops.js +523 -36
  174. package/dist/cabinet/run-panel.d.ts +6 -2
  175. package/dist/cabinet/run-panel.d.ts.map +1 -1
  176. package/dist/cabinet/run-panel.js +294 -108
  177. package/dist/cabinet/stack-transitions.d.ts.map +1 -1
  178. package/dist/cabinet/stack-transitions.js +35 -24
  179. package/dist/cabinet/stack.d.ts +169 -1
  180. package/dist/cabinet/stack.d.ts.map +1 -1
  181. package/dist/cabinet/stack.js +57 -18
  182. package/dist/cabinet/surfaces.d.ts +4 -0
  183. package/dist/cabinet/surfaces.d.ts.map +1 -0
  184. package/dist/cabinet/surfaces.js +101 -0
  185. package/dist/cabinet/tool.d.ts.map +1 -1
  186. package/dist/cabinet/tool.js +615 -91
  187. package/dist/cabinet/tree-structure.d.ts.map +1 -1
  188. package/dist/cabinet/tree-structure.js +10 -9
  189. package/dist/cabinet/validation.d.ts +27 -0
  190. package/dist/cabinet/validation.d.ts.map +1 -0
  191. package/dist/cabinet/validation.js +56 -0
  192. package/dist/cabinet/wall-height-presets.d.ts +12 -0
  193. package/dist/cabinet/wall-height-presets.d.ts.map +1 -0
  194. package/dist/cabinet/wall-height-presets.js +15 -0
  195. package/dist/cabinet/wall-snap.d.ts +27 -3
  196. package/dist/cabinet/wall-snap.d.ts.map +1 -1
  197. package/dist/cabinet/wall-snap.js +278 -62
  198. package/dist/cabinet/widths.d.ts +10 -0
  199. package/dist/cabinet/widths.d.ts.map +1 -0
  200. package/dist/cabinet/widths.js +13 -0
  201. package/dist/ceiling/definition.d.ts.map +1 -1
  202. package/dist/ceiling/definition.js +8 -2
  203. package/dist/ceiling/floorplan-affordances.d.ts +12 -12
  204. package/dist/ceiling/materials.d.ts.map +1 -1
  205. package/dist/ceiling/materials.js +0 -1
  206. package/dist/ceiling/panel.js +1 -1
  207. package/dist/ceiling/parametrics.js +1 -1
  208. package/dist/chimney/definition.js +1 -1
  209. package/dist/chimney/panel.js +9 -9
  210. package/dist/chimney/parametrics.js +3 -3
  211. package/dist/column/definition.d.ts.map +1 -1
  212. package/dist/column/definition.js +22 -4
  213. package/dist/column/panel.d.ts.map +1 -1
  214. package/dist/column/panel.js +37 -7
  215. package/dist/column/parametrics.d.ts.map +1 -1
  216. package/dist/column/parametrics.js +5 -3
  217. package/dist/column/tool.d.ts.map +1 -1
  218. package/dist/column/tool.js +50 -14
  219. package/dist/cupola/definition.d.ts.map +1 -1
  220. package/dist/cupola/definition.js +15 -6
  221. package/dist/cupola/geometry.d.ts +6 -0
  222. package/dist/cupola/geometry.d.ts.map +1 -1
  223. package/dist/cupola/geometry.js +60 -13
  224. package/dist/cupola/paint.d.ts +4 -0
  225. package/dist/cupola/paint.d.ts.map +1 -0
  226. package/dist/cupola/paint.js +39 -0
  227. package/dist/cupola/panel.js +1 -1
  228. package/dist/cupola/parametrics.js +3 -3
  229. package/dist/cupola/renderer.d.ts.map +1 -1
  230. package/dist/cupola/renderer.js +27 -8
  231. package/dist/door/floorplan-move.d.ts.map +1 -1
  232. package/dist/door/floorplan-move.js +1 -1
  233. package/dist/door/move-tool.d.ts.map +1 -1
  234. package/dist/door/move-tool.js +86 -32
  235. package/dist/door/panel.d.ts.map +1 -1
  236. package/dist/door/panel.js +12 -12
  237. package/dist/door/parametrics.js +2 -2
  238. package/dist/door/tool.d.ts.map +1 -1
  239. package/dist/door/tool.js +16 -4
  240. package/dist/dormer/csg-geometry.d.ts +6 -42
  241. package/dist/dormer/csg-geometry.d.ts.map +1 -1
  242. package/dist/dormer/csg-geometry.js +38 -209
  243. package/dist/dormer/definition.d.ts +4 -12
  244. package/dist/dormer/definition.d.ts.map +1 -1
  245. package/dist/dormer/definition.js +18 -193
  246. package/dist/dormer/floorplan.d.ts +3 -3
  247. package/dist/dormer/floorplan.d.ts.map +1 -1
  248. package/dist/dormer/floorplan.js +6 -17
  249. package/dist/dormer/geometry.d.ts +5 -24
  250. package/dist/dormer/geometry.d.ts.map +1 -1
  251. package/dist/dormer/geometry.js +64 -41
  252. package/dist/dormer/index.d.ts +1 -1
  253. package/dist/dormer/index.d.ts.map +1 -1
  254. package/dist/dormer/index.js +1 -1
  255. package/dist/dormer/move-tool.d.ts.map +1 -1
  256. package/dist/dormer/move-tool.js +31 -3
  257. package/dist/dormer/panel-position-section.js +2 -2
  258. package/dist/dormer/panel-windows-section.d.ts +9 -0
  259. package/dist/dormer/panel-windows-section.d.ts.map +1 -0
  260. package/dist/dormer/panel-windows-section.js +7 -0
  261. package/dist/dormer/panel.d.ts.map +1 -1
  262. package/dist/dormer/panel.js +100 -18
  263. package/dist/dormer/parametrics.d.ts.map +1 -1
  264. package/dist/dormer/parametrics.js +8 -77
  265. package/dist/dormer/preview.d.ts.map +1 -1
  266. package/dist/dormer/preview.js +9 -1
  267. package/dist/dormer/renderer.d.ts.map +1 -1
  268. package/dist/dormer/renderer.js +24 -26
  269. package/dist/dormer/tool.d.ts.map +1 -1
  270. package/dist/dormer/tool.js +3 -1
  271. package/dist/dormer/window-layout.d.ts +15 -0
  272. package/dist/dormer/window-layout.d.ts.map +1 -0
  273. package/dist/dormer/window-layout.js +59 -0
  274. package/dist/downspout/definition.d.ts.map +1 -1
  275. package/dist/downspout/definition.js +17 -4
  276. package/dist/downspout/geometry.d.ts.map +1 -1
  277. package/dist/downspout/geometry.js +10 -0
  278. package/dist/downspout/inspector-editors.d.ts.map +1 -1
  279. package/dist/downspout/inspector-editors.js +9 -1
  280. package/dist/downspout/parametrics.d.ts.map +1 -1
  281. package/dist/downspout/parametrics.js +2 -1
  282. package/dist/downspout/renderer.d.ts.map +1 -1
  283. package/dist/downspout/renderer.js +19 -3
  284. package/dist/downspout/system.d.ts +7 -0
  285. package/dist/downspout/system.d.ts.map +1 -0
  286. package/dist/downspout/system.js +114 -0
  287. package/dist/duct-fitting/accessory-geometry.d.ts +4 -0
  288. package/dist/duct-fitting/accessory-geometry.d.ts.map +1 -0
  289. package/dist/duct-fitting/accessory-geometry.js +47 -0
  290. package/dist/duct-fitting/definition.d.ts.map +1 -1
  291. package/dist/duct-fitting/definition.js +11 -1
  292. package/dist/duct-fitting/floorplan.d.ts.map +1 -1
  293. package/dist/duct-fitting/floorplan.js +8 -0
  294. package/dist/duct-fitting/geometry.d.ts.map +1 -1
  295. package/dist/duct-fitting/geometry.js +52 -38
  296. package/dist/duct-fitting/parametrics.d.ts.map +1 -1
  297. package/dist/duct-fitting/parametrics.js +78 -15
  298. package/dist/duct-fitting/ports.d.ts +1 -0
  299. package/dist/duct-fitting/ports.d.ts.map +1 -1
  300. package/dist/duct-fitting/ports.js +46 -0
  301. package/dist/duct-fitting/tool.d.ts +19 -0
  302. package/dist/duct-fitting/tool.d.ts.map +1 -1
  303. package/dist/duct-fitting/tool.js +126 -34
  304. package/dist/duct-segment/continuation.d.ts +21 -0
  305. package/dist/duct-segment/continuation.d.ts.map +1 -0
  306. package/dist/duct-segment/continuation.js +167 -0
  307. package/dist/duct-segment/definition.d.ts.map +1 -1
  308. package/dist/duct-segment/definition.js +19 -18
  309. package/dist/duct-segment/floorplan.d.ts.map +1 -1
  310. package/dist/duct-segment/floorplan.js +43 -0
  311. package/dist/duct-segment/geometry.d.ts.map +1 -1
  312. package/dist/duct-segment/geometry.js +3 -0
  313. package/dist/duct-segment/move-tool.d.ts.map +1 -1
  314. package/dist/duct-segment/move-tool.js +33 -6
  315. package/dist/duct-segment/parametrics.d.ts.map +1 -1
  316. package/dist/duct-segment/parametrics.js +49 -1
  317. package/dist/duct-segment/selection.d.ts +3 -4
  318. package/dist/duct-segment/selection.d.ts.map +1 -1
  319. package/dist/duct-segment/selection.js +186 -158
  320. package/dist/duct-segment/tool.d.ts +38 -0
  321. package/dist/duct-segment/tool.d.ts.map +1 -1
  322. package/dist/duct-segment/tool.js +306 -677
  323. package/dist/duct-terminal/parametrics.js +2 -2
  324. package/dist/duct-terminal/tool.d.ts.map +1 -1
  325. package/dist/duct-terminal/tool.js +37 -7
  326. package/dist/elevator/panel.d.ts.map +1 -1
  327. package/dist/elevator/panel.js +7 -7
  328. package/dist/elevator/system.d.ts.map +1 -1
  329. package/dist/elevator/system.js +10 -1
  330. package/dist/eyebrow-vent/definition.d.ts.map +1 -1
  331. package/dist/eyebrow-vent/definition.js +8 -5
  332. package/dist/eyebrow-vent/geometry.d.ts +4 -0
  333. package/dist/eyebrow-vent/geometry.d.ts.map +1 -1
  334. package/dist/eyebrow-vent/geometry.js +60 -15
  335. package/dist/eyebrow-vent/paint.d.ts +4 -0
  336. package/dist/eyebrow-vent/paint.d.ts.map +1 -0
  337. package/dist/eyebrow-vent/paint.js +33 -0
  338. package/dist/eyebrow-vent/panel.js +1 -1
  339. package/dist/eyebrow-vent/parametrics.js +3 -3
  340. package/dist/eyebrow-vent/renderer.d.ts.map +1 -1
  341. package/dist/eyebrow-vent/renderer.js +27 -8
  342. package/dist/fence/definition.d.ts.map +1 -1
  343. package/dist/fence/definition.js +2 -1
  344. package/dist/fence/floorplan-affordances.d.ts +1 -0
  345. package/dist/fence/floorplan-affordances.d.ts.map +1 -1
  346. package/dist/fence/floorplan-affordances.js +30 -1
  347. package/dist/fence/floorplan.d.ts.map +1 -1
  348. package/dist/fence/floorplan.js +14 -0
  349. package/dist/fence/inspector-editors.js +1 -1
  350. package/dist/fence/parametrics.js +3 -3
  351. package/dist/fence/tool.d.ts.map +1 -1
  352. package/dist/fence/tool.js +47 -20
  353. package/dist/gutter/corner-mitre.d.ts.map +1 -1
  354. package/dist/gutter/corner-mitre.js +47 -7
  355. package/dist/gutter/definition.d.ts.map +1 -1
  356. package/dist/gutter/definition.js +23 -13
  357. package/dist/gutter/eave-align.d.ts.map +1 -1
  358. package/dist/gutter/eave-align.js +10 -0
  359. package/dist/gutter/eave-snap.d.ts +1 -1
  360. package/dist/gutter/eave-snap.d.ts.map +1 -1
  361. package/dist/gutter/eave-snap.js +10 -23
  362. package/dist/gutter/geometry.d.ts.map +1 -1
  363. package/dist/gutter/geometry.js +126 -8
  364. package/dist/gutter/outlet-lookup.d.ts.map +1 -1
  365. package/dist/gutter/outlet-lookup.js +22 -1
  366. package/dist/gutter/paint.d.ts +3 -0
  367. package/dist/gutter/paint.d.ts.map +1 -0
  368. package/dist/gutter/paint.js +6 -0
  369. package/dist/gutter/parametrics.d.ts.map +1 -1
  370. package/dist/gutter/parametrics.js +4 -1
  371. package/dist/gutter/renderer.d.ts.map +1 -1
  372. package/dist/gutter/renderer.js +63 -7
  373. package/dist/gutter/trim-clip.d.ts +3 -0
  374. package/dist/gutter/trim-clip.d.ts.map +1 -0
  375. package/dist/gutter/trim-clip.js +5 -0
  376. package/dist/hvac-equipment/parametrics.js +3 -3
  377. package/dist/hvac-equipment/tool.d.ts.map +1 -1
  378. package/dist/hvac-equipment/tool.js +12 -2
  379. package/dist/imported-mesh/definition.d.ts +5 -0
  380. package/dist/imported-mesh/definition.d.ts.map +1 -0
  381. package/dist/imported-mesh/definition.js +38 -0
  382. package/dist/imported-mesh/floorplan.d.ts +4 -0
  383. package/dist/imported-mesh/floorplan.d.ts.map +1 -0
  384. package/dist/imported-mesh/floorplan.js +34 -0
  385. package/dist/imported-mesh/geometry.d.ts +5 -0
  386. package/dist/imported-mesh/geometry.d.ts.map +1 -0
  387. package/dist/imported-mesh/geometry.js +34 -0
  388. package/dist/imported-mesh/index.d.ts +4 -0
  389. package/dist/imported-mesh/index.d.ts.map +1 -0
  390. package/dist/imported-mesh/index.js +3 -0
  391. package/dist/imported-mesh/schema.d.ts +2 -0
  392. package/dist/imported-mesh/schema.d.ts.map +1 -0
  393. package/dist/imported-mesh/schema.js +1 -0
  394. package/dist/index.d.ts +7 -2
  395. package/dist/index.d.ts.map +1 -1
  396. package/dist/index.js +17 -2
  397. package/dist/item/definition.d.ts.map +1 -1
  398. package/dist/item/definition.js +10 -2
  399. package/dist/item/floorplan-move.d.ts +9 -1
  400. package/dist/item/floorplan-move.d.ts.map +1 -1
  401. package/dist/item/floorplan-move.js +74 -2
  402. package/dist/item/floorplan.d.ts.map +1 -1
  403. package/dist/item/floorplan.js +33 -1
  404. package/dist/item/move-tool.d.ts +28 -1
  405. package/dist/item/move-tool.d.ts.map +1 -1
  406. package/dist/item/move-tool.js +15 -3
  407. package/dist/item/panel.js +1 -1
  408. package/dist/item/renderer.d.ts.map +1 -1
  409. package/dist/item/renderer.js +16 -6
  410. package/dist/item/system.d.ts +5 -0
  411. package/dist/item/system.d.ts.map +1 -1
  412. package/dist/item/system.js +7 -1
  413. package/dist/lean-to-extension/arc.d.ts +14 -0
  414. package/dist/lean-to-extension/arc.d.ts.map +1 -0
  415. package/dist/lean-to-extension/arc.js +56 -0
  416. package/dist/lean-to-extension/assembly.d.ts +61 -0
  417. package/dist/lean-to-extension/assembly.d.ts.map +1 -0
  418. package/dist/lean-to-extension/assembly.js +761 -0
  419. package/dist/lean-to-extension/canopy-joint.d.ts +35 -0
  420. package/dist/lean-to-extension/canopy-joint.d.ts.map +1 -0
  421. package/dist/lean-to-extension/canopy-joint.js +234 -0
  422. package/dist/lean-to-extension/conical-host.d.ts +15 -0
  423. package/dist/lean-to-extension/conical-host.d.ts.map +1 -0
  424. package/dist/lean-to-extension/conical-host.js +113 -0
  425. package/dist/lean-to-extension/corner-joint.d.ts +29 -0
  426. package/dist/lean-to-extension/corner-joint.d.ts.map +1 -0
  427. package/dist/lean-to-extension/corner-joint.js +1202 -0
  428. package/dist/lean-to-extension/definition.d.ts +4 -0
  429. package/dist/lean-to-extension/definition.d.ts.map +1 -0
  430. package/dist/lean-to-extension/definition.js +433 -0
  431. package/dist/lean-to-extension/floorplan-affordances.d.ts +4 -0
  432. package/dist/lean-to-extension/floorplan-affordances.d.ts.map +1 -0
  433. package/dist/lean-to-extension/floorplan-affordances.js +144 -0
  434. package/dist/lean-to-extension/floorplan-move.d.ts +3 -0
  435. package/dist/lean-to-extension/floorplan-move.d.ts.map +1 -0
  436. package/dist/lean-to-extension/floorplan-move.js +157 -0
  437. package/dist/lean-to-extension/floorplan-tool.d.ts +4 -0
  438. package/dist/lean-to-extension/floorplan-tool.d.ts.map +1 -0
  439. package/dist/lean-to-extension/floorplan-tool.js +385 -0
  440. package/dist/lean-to-extension/floorplan.d.ts +3 -0
  441. package/dist/lean-to-extension/floorplan.d.ts.map +1 -0
  442. package/dist/lean-to-extension/floorplan.js +373 -0
  443. package/dist/lean-to-extension/geometry.d.ts +7 -0
  444. package/dist/lean-to-extension/geometry.d.ts.map +1 -0
  445. package/dist/lean-to-extension/geometry.js +662 -0
  446. package/dist/lean-to-extension/index.d.ts +8 -0
  447. package/dist/lean-to-extension/index.d.ts.map +1 -0
  448. package/dist/lean-to-extension/index.js +7 -0
  449. package/dist/lean-to-extension/layout.d.ts +108 -0
  450. package/dist/lean-to-extension/layout.d.ts.map +1 -0
  451. package/dist/lean-to-extension/layout.js +477 -0
  452. package/dist/lean-to-extension/managed-preview.d.ts +3 -0
  453. package/dist/lean-to-extension/managed-preview.d.ts.map +1 -0
  454. package/dist/lean-to-extension/managed-preview.js +93 -0
  455. package/dist/lean-to-extension/move-tool.d.ts +8 -0
  456. package/dist/lean-to-extension/move-tool.d.ts.map +1 -0
  457. package/dist/lean-to-extension/move-tool.js +202 -0
  458. package/dist/lean-to-extension/paint.d.ts +2 -0
  459. package/dist/lean-to-extension/paint.d.ts.map +1 -0
  460. package/dist/lean-to-extension/paint.js +16 -0
  461. package/dist/lean-to-extension/parametrics.d.ts +4 -0
  462. package/dist/lean-to-extension/parametrics.d.ts.map +1 -0
  463. package/dist/lean-to-extension/parametrics.js +570 -0
  464. package/dist/lean-to-extension/placement-scope.d.ts +3 -0
  465. package/dist/lean-to-extension/placement-scope.d.ts.map +1 -0
  466. package/dist/lean-to-extension/placement-scope.js +4 -0
  467. package/dist/lean-to-extension/placement-validation.d.ts +4 -0
  468. package/dist/lean-to-extension/placement-validation.d.ts.map +1 -0
  469. package/dist/lean-to-extension/placement-validation.js +324 -0
  470. package/dist/lean-to-extension/placement.d.ts +59 -0
  471. package/dist/lean-to-extension/placement.d.ts.map +1 -0
  472. package/dist/lean-to-extension/placement.js +331 -0
  473. package/dist/lean-to-extension/plan-footprint.d.ts +10 -0
  474. package/dist/lean-to-extension/plan-footprint.d.ts.map +1 -0
  475. package/dist/lean-to-extension/plan-footprint.js +27 -0
  476. package/dist/lean-to-extension/preview-geometry.d.ts +7 -0
  477. package/dist/lean-to-extension/preview-geometry.d.ts.map +1 -0
  478. package/dist/lean-to-extension/preview-geometry.js +40 -0
  479. package/dist/lean-to-extension/preview.d.ts +7 -0
  480. package/dist/lean-to-extension/preview.d.ts.map +1 -0
  481. package/dist/lean-to-extension/preview.js +20 -0
  482. package/dist/lean-to-extension/renderer.d.ts +6 -0
  483. package/dist/lean-to-extension/renderer.d.ts.map +1 -0
  484. package/dist/lean-to-extension/renderer.js +34 -0
  485. package/dist/lean-to-extension/roof-attachment.d.ts +28 -0
  486. package/dist/lean-to-extension/roof-attachment.d.ts.map +1 -0
  487. package/dist/lean-to-extension/roof-attachment.js +376 -0
  488. package/dist/lean-to-extension/schema.d.ts +2 -0
  489. package/dist/lean-to-extension/schema.d.ts.map +1 -0
  490. package/dist/lean-to-extension/schema.js +1 -0
  491. package/dist/lean-to-extension/slots.d.ts +5 -0
  492. package/dist/lean-to-extension/slots.d.ts.map +1 -0
  493. package/dist/lean-to-extension/slots.js +15 -0
  494. package/dist/lean-to-extension/system.d.ts +7 -0
  495. package/dist/lean-to-extension/system.d.ts.map +1 -0
  496. package/dist/lean-to-extension/system.js +726 -0
  497. package/dist/lean-to-extension/tool.d.ts +3 -0
  498. package/dist/lean-to-extension/tool.d.ts.map +1 -0
  499. package/dist/lean-to-extension/tool.js +580 -0
  500. package/dist/lean-to-extension/wall-target.d.ts +10 -0
  501. package/dist/lean-to-extension/wall-target.d.ts.map +1 -0
  502. package/dist/lean-to-extension/wall-target.js +29 -0
  503. package/dist/lineset/definition.d.ts.map +1 -1
  504. package/dist/lineset/definition.js +1 -0
  505. package/dist/liquid-line/definition.d.ts.map +1 -1
  506. package/dist/liquid-line/definition.js +1 -0
  507. package/dist/pipe-fitting/definition.d.ts.map +1 -1
  508. package/dist/pipe-fitting/definition.js +16 -2
  509. package/dist/pipe-fitting/floorplan.d.ts.map +1 -1
  510. package/dist/pipe-fitting/floorplan.js +4 -0
  511. package/dist/pipe-fitting/geometry.d.ts +3 -5
  512. package/dist/pipe-fitting/geometry.d.ts.map +1 -1
  513. package/dist/pipe-fitting/geometry.js +154 -21
  514. package/dist/pipe-fitting/inline-insertion.d.ts +13 -0
  515. package/dist/pipe-fitting/inline-insertion.d.ts.map +1 -0
  516. package/dist/pipe-fitting/inline-insertion.js +96 -0
  517. package/dist/pipe-fitting/parametrics.d.ts.map +1 -1
  518. package/dist/pipe-fitting/parametrics.js +17 -3
  519. package/dist/pipe-fitting/ports.d.ts.map +1 -1
  520. package/dist/pipe-fitting/ports.js +8 -1
  521. package/dist/pipe-fitting/tool.d.ts +26 -0
  522. package/dist/pipe-fitting/tool.d.ts.map +1 -1
  523. package/dist/pipe-fitting/tool.js +182 -37
  524. package/dist/pipe-segment/continuation.d.ts +22 -0
  525. package/dist/pipe-segment/continuation.d.ts.map +1 -0
  526. package/dist/pipe-segment/continuation.js +146 -0
  527. package/dist/pipe-segment/definition.d.ts.map +1 -1
  528. package/dist/pipe-segment/definition.js +23 -7
  529. package/dist/pipe-segment/floorplan.d.ts.map +1 -1
  530. package/dist/pipe-segment/floorplan.js +42 -0
  531. package/dist/pipe-segment/geometry.d.ts +2 -1
  532. package/dist/pipe-segment/geometry.d.ts.map +1 -1
  533. package/dist/pipe-segment/geometry.js +4 -1
  534. package/dist/pipe-segment/move-tool.d.ts.map +1 -1
  535. package/dist/pipe-segment/move-tool.js +25 -5
  536. package/dist/pipe-segment/parametrics.d.ts.map +1 -1
  537. package/dist/pipe-segment/parametrics.js +49 -0
  538. package/dist/pipe-segment/selection.d.ts +3 -4
  539. package/dist/pipe-segment/selection.d.ts.map +1 -1
  540. package/dist/pipe-segment/selection.js +169 -105
  541. package/dist/pipe-segment/slope.d.ts +5 -0
  542. package/dist/pipe-segment/slope.d.ts.map +1 -0
  543. package/dist/pipe-segment/slope.js +10 -0
  544. package/dist/pipe-segment/tool.d.ts.map +1 -1
  545. package/dist/pipe-segment/tool.js +325 -545
  546. package/dist/pipe-trap/tool.d.ts.map +1 -1
  547. package/dist/pipe-trap/tool.js +15 -2
  548. package/dist/procedural-item/definition.d.ts +4 -0
  549. package/dist/procedural-item/definition.d.ts.map +1 -0
  550. package/dist/procedural-item/definition.js +254 -0
  551. package/dist/procedural-item/geometry.d.ts +31 -0
  552. package/dist/procedural-item/geometry.d.ts.map +1 -0
  553. package/dist/procedural-item/geometry.js +88 -0
  554. package/dist/procedural-item/index.d.ts +3 -0
  555. package/dist/procedural-item/index.d.ts.map +1 -0
  556. package/dist/procedural-item/index.js +2 -0
  557. package/dist/procedural-item/move-session.d.ts +15 -0
  558. package/dist/procedural-item/move-session.d.ts.map +1 -0
  559. package/dist/procedural-item/move-session.js +197 -0
  560. package/dist/procedural-item/move-tool.d.ts +5 -0
  561. package/dist/procedural-item/move-tool.d.ts.map +1 -0
  562. package/dist/procedural-item/move-tool.js +193 -0
  563. package/dist/procedural-item/renderer.d.ts +5 -0
  564. package/dist/procedural-item/renderer.d.ts.map +1 -0
  565. package/dist/procedural-item/renderer.js +66 -0
  566. package/dist/ridge-vent/definition.js +1 -1
  567. package/dist/ridge-vent/panel.d.ts.map +1 -1
  568. package/dist/ridge-vent/panel.js +4 -4
  569. package/dist/ridge-vent/parametrics.js +1 -1
  570. package/dist/roof/conical-roof-placement.d.ts +36 -0
  571. package/dist/roof/conical-roof-placement.d.ts.map +1 -0
  572. package/dist/roof/conical-roof-placement.js +134 -0
  573. package/dist/roof/conical-roof.d.ts +3 -0
  574. package/dist/roof/conical-roof.d.ts.map +1 -0
  575. package/dist/roof/conical-roof.js +53 -0
  576. package/dist/roof/definition.d.ts +3 -10
  577. package/dist/roof/definition.d.ts.map +1 -1
  578. package/dist/roof/definition.js +83 -13
  579. package/dist/roof/floorplan.d.ts +3 -10
  580. package/dist/roof/floorplan.d.ts.map +1 -1
  581. package/dist/roof/floorplan.js +139 -165
  582. package/dist/roof/index.d.ts +1 -0
  583. package/dist/roof/index.d.ts.map +1 -1
  584. package/dist/roof/index.js +1 -0
  585. package/dist/roof/panel.d.ts.map +1 -1
  586. package/dist/roof/panel.js +26 -25
  587. package/dist/roof/renderer.d.ts.map +1 -1
  588. package/dist/roof/roof-footprint-source.d.ts +16 -0
  589. package/dist/roof/roof-footprint-source.d.ts.map +1 -0
  590. package/dist/roof/roof-footprint-source.js +14 -0
  591. package/dist/roof/roof-footprint.d.ts +25 -0
  592. package/dist/roof/roof-footprint.d.ts.map +1 -0
  593. package/dist/roof/roof-footprint.js +94 -0
  594. package/dist/roof/roof-placement-mode.d.ts +18 -0
  595. package/dist/roof/roof-placement-mode.d.ts.map +1 -0
  596. package/dist/roof/roof-placement-mode.js +24 -0
  597. package/dist/roof/tool.d.ts +10 -0
  598. package/dist/roof/tool.d.ts.map +1 -0
  599. package/dist/roof/tool.js +922 -0
  600. package/dist/roof-segment/definition.d.ts.map +1 -1
  601. package/dist/roof-segment/definition.js +52 -8
  602. package/dist/roof-segment/floorplan-affordances.d.ts +5 -6
  603. package/dist/roof-segment/floorplan-affordances.d.ts.map +1 -1
  604. package/dist/roof-segment/floorplan-affordances.js +64 -18
  605. package/dist/roof-segment/floorplan.d.ts +3 -2
  606. package/dist/roof-segment/floorplan.d.ts.map +1 -1
  607. package/dist/roof-segment/floorplan.js +95 -46
  608. package/dist/roof-segment/panel.d.ts.map +1 -1
  609. package/dist/roof-segment/panel.js +122 -27
  610. package/dist/scan/definition.d.ts +2 -3
  611. package/dist/scan/definition.d.ts.map +1 -1
  612. package/dist/scan/definition.js +9 -7
  613. package/dist/scan/parametrics.d.ts.map +1 -1
  614. package/dist/scan/parametrics.js +13 -1
  615. package/dist/scan/renderer.d.ts.map +1 -1
  616. package/dist/scan/renderer.js +8 -2
  617. package/dist/shared/accessory-cursor.d.ts +7 -0
  618. package/dist/shared/accessory-cursor.d.ts.map +1 -0
  619. package/dist/shared/accessory-cursor.js +83 -0
  620. package/dist/shared/accessory-floorplan.d.ts +4 -0
  621. package/dist/shared/accessory-floorplan.d.ts.map +1 -0
  622. package/dist/shared/accessory-floorplan.js +51 -0
  623. package/dist/shared/accessory-geometry.d.ts +7 -0
  624. package/dist/shared/accessory-geometry.d.ts.map +1 -0
  625. package/dist/shared/accessory-geometry.js +79 -0
  626. package/dist/shared/accessory-placement.d.ts +10 -0
  627. package/dist/shared/accessory-placement.d.ts.map +1 -0
  628. package/dist/shared/accessory-placement.js +126 -0
  629. package/dist/shared/accessory-snapping.d.ts +5 -0
  630. package/dist/shared/accessory-snapping.d.ts.map +1 -0
  631. package/dist/shared/accessory-snapping.js +40 -0
  632. package/dist/shared/automatic-run-end-cap.d.ts +12 -0
  633. package/dist/shared/automatic-run-end-cap.d.ts.map +1 -0
  634. package/dist/shared/automatic-run-end-cap.js +169 -0
  635. package/dist/shared/block-face-host.d.ts +15 -0
  636. package/dist/shared/block-face-host.d.ts.map +1 -0
  637. package/dist/shared/block-face-host.js +36 -0
  638. package/dist/shared/connection-compatibility.d.ts +8 -0
  639. package/dist/shared/connection-compatibility.d.ts.map +1 -0
  640. package/dist/shared/connection-compatibility.js +28 -0
  641. package/dist/shared/connection-feedback.d.ts +10 -0
  642. package/dist/shared/connection-feedback.d.ts.map +1 -0
  643. package/dist/shared/connection-feedback.js +17 -0
  644. package/dist/shared/distribution-run-contract.d.ts +49 -0
  645. package/dist/shared/distribution-run-contract.d.ts.map +1 -0
  646. package/dist/shared/distribution-run-contract.js +30 -0
  647. package/dist/shared/distribution-run-tool.d.ts +117 -0
  648. package/dist/shared/distribution-run-tool.d.ts.map +1 -0
  649. package/dist/shared/distribution-run-tool.js +986 -0
  650. package/dist/shared/dormer-wall-opening-placement.d.ts +21 -0
  651. package/dist/shared/dormer-wall-opening-placement.d.ts.map +1 -0
  652. package/dist/shared/dormer-wall-opening-placement.js +101 -0
  653. package/dist/shared/duct-adapter.d.ts +10 -0
  654. package/dist/shared/duct-adapter.d.ts.map +1 -0
  655. package/dist/shared/duct-adapter.js +43 -0
  656. package/dist/shared/elbow-branch-continuation.d.ts +20 -0
  657. package/dist/shared/elbow-branch-continuation.d.ts.map +1 -0
  658. package/dist/shared/elbow-branch-continuation.js +200 -0
  659. package/dist/shared/fitting-clearance.d.ts +5 -0
  660. package/dist/shared/fitting-clearance.d.ts.map +1 -0
  661. package/dist/shared/fitting-clearance.js +11 -0
  662. package/dist/shared/fitting-deletion-cleanup.d.ts +14 -0
  663. package/dist/shared/fitting-deletion-cleanup.d.ts.map +1 -0
  664. package/dist/shared/fitting-deletion-cleanup.js +297 -0
  665. package/dist/shared/fitting-surface-support.d.ts +5 -0
  666. package/dist/shared/fitting-surface-support.d.ts.map +1 -0
  667. package/dist/shared/fitting-surface-support.js +42 -0
  668. package/dist/shared/fitting-tool-options.d.ts +4 -0
  669. package/dist/shared/fitting-tool-options.d.ts.map +1 -0
  670. package/dist/shared/fitting-tool-options.js +175 -0
  671. package/dist/shared/floor-placement.d.ts +1 -1
  672. package/dist/shared/floor-placement.d.ts.map +1 -1
  673. package/dist/shared/floor-placement.js +13 -33
  674. package/dist/shared/ghost-materials.d.ts.map +1 -1
  675. package/dist/shared/ghost-materials.js +2 -1
  676. package/dist/shared/lean-to-post-omissions.d.ts +8 -0
  677. package/dist/shared/lean-to-post-omissions.d.ts.map +1 -0
  678. package/dist/shared/lean-to-post-omissions.js +51 -0
  679. package/dist/shared/mep-fitting-actions.d.ts +5 -0
  680. package/dist/shared/mep-fitting-actions.d.ts.map +1 -0
  681. package/dist/shared/mep-fitting-actions.js +16 -0
  682. package/dist/shared/mep-presets.d.ts +11 -0
  683. package/dist/shared/mep-presets.d.ts.map +1 -0
  684. package/dist/shared/mep-presets.js +34 -0
  685. package/dist/shared/node-batch/candidates.d.ts +29 -0
  686. package/dist/shared/node-batch/candidates.d.ts.map +1 -0
  687. package/dist/shared/node-batch/candidates.js +260 -0
  688. package/dist/shared/node-batch/store.d.ts +26 -0
  689. package/dist/shared/node-batch/store.d.ts.map +1 -0
  690. package/dist/shared/node-batch/store.js +319 -0
  691. package/dist/shared/node-batch/system.d.ts +8 -0
  692. package/dist/shared/node-batch/system.d.ts.map +1 -0
  693. package/dist/shared/node-batch/system.js +569 -0
  694. package/dist/shared/node-batch/types.d.ts +99 -0
  695. package/dist/shared/node-batch/types.d.ts.map +1 -0
  696. package/dist/shared/node-batch/types.js +4 -0
  697. package/dist/shared/opening-move-history.d.ts +47 -0
  698. package/dist/shared/opening-move-history.d.ts.map +1 -0
  699. package/dist/shared/opening-move-history.js +18 -0
  700. package/dist/shared/opening-move-wall-gate.d.ts +55 -0
  701. package/dist/shared/opening-move-wall-gate.d.ts.map +1 -0
  702. package/dist/shared/opening-move-wall-gate.js +44 -0
  703. package/dist/shared/panel-fields.d.ts +15 -0
  704. package/dist/shared/panel-fields.d.ts.map +1 -0
  705. package/dist/shared/panel-fields.js +31 -0
  706. package/dist/shared/path-point-affordance.d.ts +2 -2
  707. package/dist/shared/path-point-affordance.d.ts.map +1 -1
  708. package/dist/shared/path-point-affordance.js +30 -6
  709. package/dist/shared/placeholder-geometry.d.ts.map +1 -1
  710. package/dist/shared/placeholder-geometry.js +4 -0
  711. package/dist/shared/ports.d.ts +20 -3
  712. package/dist/shared/ports.d.ts.map +1 -1
  713. package/dist/shared/ports.js +148 -7
  714. package/dist/shared/primitive-uv.d.ts +17 -0
  715. package/dist/shared/primitive-uv.d.ts.map +1 -0
  716. package/dist/shared/primitive-uv.js +107 -0
  717. package/dist/shared/reducer-size.d.ts +2 -0
  718. package/dist/shared/reducer-size.d.ts.map +1 -0
  719. package/dist/shared/reducer-size.js +12 -0
  720. package/dist/shared/resting-surface-plan.d.ts +3 -0
  721. package/dist/shared/resting-surface-plan.d.ts.map +1 -0
  722. package/dist/shared/resting-surface-plan.js +28 -0
  723. package/dist/shared/ridge-snap.js +1 -1
  724. package/dist/shared/roof-surface.d.ts.map +1 -1
  725. package/dist/shared/roof-surface.js +17 -2
  726. package/dist/shared/run-cursor.d.ts +22 -0
  727. package/dist/shared/run-cursor.d.ts.map +1 -0
  728. package/dist/shared/run-cursor.js +19 -0
  729. package/dist/shared/run-direction-feedback.d.ts +20 -0
  730. package/dist/shared/run-direction-feedback.d.ts.map +1 -0
  731. package/dist/shared/run-direction-feedback.js +139 -0
  732. package/dist/shared/run-hanger-controls.d.ts +7 -0
  733. package/dist/shared/run-hanger-controls.d.ts.map +1 -0
  734. package/dist/shared/run-hanger-controls.js +44 -0
  735. package/dist/shared/run-hanger-inspector.d.ts +5 -0
  736. package/dist/shared/run-hanger-inspector.d.ts.map +1 -0
  737. package/dist/shared/run-hanger-inspector.js +43 -0
  738. package/dist/shared/run-hanger-mode.d.ts +11 -0
  739. package/dist/shared/run-hanger-mode.d.ts.map +1 -0
  740. package/dist/shared/run-hanger-mode.js +32 -0
  741. package/dist/shared/run-hanger-system.d.ts +3 -0
  742. package/dist/shared/run-hanger-system.d.ts.map +1 -0
  743. package/dist/shared/run-hanger-system.js +39 -0
  744. package/dist/shared/run-hangers.d.ts +25 -0
  745. package/dist/shared/run-hangers.d.ts.map +1 -0
  746. package/dist/shared/run-hangers.js +261 -0
  747. package/dist/shared/run-port-snap.d.ts +11 -0
  748. package/dist/shared/run-port-snap.d.ts.map +1 -0
  749. package/dist/shared/run-port-snap.js +19 -0
  750. package/dist/shared/run-screen-direction.d.ts +5 -0
  751. package/dist/shared/run-screen-direction.d.ts.map +1 -0
  752. package/dist/shared/run-screen-direction.js +24 -0
  753. package/dist/shared/selection-handles.d.ts +4 -0
  754. package/dist/shared/selection-handles.d.ts.map +1 -1
  755. package/dist/shared/selection-handles.js +10 -0
  756. package/dist/shared/slot-paint.d.ts +8 -1
  757. package/dist/shared/slot-paint.d.ts.map +1 -1
  758. package/dist/shared/slot-paint.js +120 -27
  759. package/dist/shared/surface-paint.d.ts +1 -2
  760. package/dist/shared/surface-paint.d.ts.map +1 -1
  761. package/dist/shared/surface-paint.js +9 -20
  762. package/dist/shared/system-check-panel.d.ts +6 -0
  763. package/dist/shared/system-check-panel.d.ts.map +1 -0
  764. package/dist/shared/system-check-panel.js +62 -0
  765. package/dist/shared/system-checks.d.ts +9 -0
  766. package/dist/shared/system-checks.d.ts.map +1 -0
  767. package/dist/shared/system-checks.js +131 -0
  768. package/dist/shared/wall-attach-target.d.ts +14 -2
  769. package/dist/shared/wall-attach-target.d.ts.map +1 -1
  770. package/dist/shared/wall-attach-target.js +108 -4
  771. package/dist/shared/wall-opening-clearance.d.ts +19 -0
  772. package/dist/shared/wall-opening-clearance.d.ts.map +1 -0
  773. package/dist/shared/wall-opening-clearance.js +28 -0
  774. package/dist/shared/wall-run-move.d.ts +17 -0
  775. package/dist/shared/wall-run-move.d.ts.map +1 -0
  776. package/dist/shared/wall-run-move.js +56 -0
  777. package/dist/shelf/definition.d.ts.map +1 -1
  778. package/dist/shelf/definition.js +2 -1
  779. package/dist/shelf/floorplan-affordances.d.ts.map +1 -1
  780. package/dist/shelf/floorplan-affordances.js +5 -3
  781. package/dist/shelf/floorplan-move.d.ts.map +1 -1
  782. package/dist/shelf/floorplan-move.js +3 -2
  783. package/dist/shelf/geometry.d.ts.map +1 -1
  784. package/dist/shelf/geometry.js +2 -10
  785. package/dist/shelf/parametrics.js +4 -4
  786. package/dist/site/renderer.d.ts.map +1 -1
  787. package/dist/site/renderer.js +40 -28
  788. package/dist/site/terrain-geometry.d.ts +1 -1
  789. package/dist/site/terrain-geometry.d.ts.map +1 -1
  790. package/dist/site/terrain-geometry.js +35 -9
  791. package/dist/site/terrain-mesh.d.ts +5 -0
  792. package/dist/site/terrain-mesh.d.ts.map +1 -1
  793. package/dist/site/terrain-mesh.js +63 -34
  794. package/dist/skylight/definition.js +1 -1
  795. package/dist/skylight/panel.js +6 -6
  796. package/dist/skylight/parametrics.js +2 -2
  797. package/dist/slab/definition.d.ts.map +1 -1
  798. package/dist/slab/definition.js +6 -1
  799. package/dist/slab/dependency-tracker.d.ts +3 -0
  800. package/dist/slab/dependency-tracker.d.ts.map +1 -0
  801. package/dist/slab/dependency-tracker.js +104 -0
  802. package/dist/slab/floorplan-affordances.d.ts +8 -8
  803. package/dist/slab/geometry.d.ts.map +1 -1
  804. package/dist/slab/geometry.js +16 -2
  805. package/dist/slab/panel.js +1 -1
  806. package/dist/slab/parametrics.js +1 -1
  807. package/dist/slab/system.d.ts.map +1 -1
  808. package/dist/slab/system.js +8 -64
  809. package/dist/solar-panel/definition.js +1 -1
  810. package/dist/solar-panel/panel.js +2 -2
  811. package/dist/solar-panel/parametrics.js +2 -2
  812. package/dist/spawn/panel.js +1 -1
  813. package/dist/stair/panel.d.ts.map +1 -1
  814. package/dist/stair/panel.js +23 -16
  815. package/dist/stair/stair-type.d.ts +18 -0
  816. package/dist/stair/stair-type.d.ts.map +1 -0
  817. package/dist/stair/stair-type.js +25 -0
  818. package/dist/stair-segment/panel.d.ts.map +1 -1
  819. package/dist/stair-segment/panel.js +45 -6
  820. package/dist/turbine-vent/definition.d.ts.map +1 -1
  821. package/dist/turbine-vent/definition.js +8 -5
  822. package/dist/turbine-vent/geometry.d.ts.map +1 -1
  823. package/dist/turbine-vent/geometry.js +48 -18
  824. package/dist/turbine-vent/paint.d.ts +4 -0
  825. package/dist/turbine-vent/paint.d.ts.map +1 -0
  826. package/dist/turbine-vent/paint.js +35 -0
  827. package/dist/turbine-vent/panel.js +1 -1
  828. package/dist/turbine-vent/parametrics.js +2 -2
  829. package/dist/turbine-vent/renderer.d.ts.map +1 -1
  830. package/dist/turbine-vent/renderer.js +31 -9
  831. package/dist/unit/definition.d.ts +10 -0
  832. package/dist/unit/definition.d.ts.map +1 -0
  833. package/dist/unit/definition.js +38 -0
  834. package/dist/unit/index.d.ts +2 -0
  835. package/dist/unit/index.d.ts.map +1 -0
  836. package/dist/unit/index.js +1 -0
  837. package/dist/unit/parametrics.d.ts +3 -0
  838. package/dist/unit/parametrics.d.ts.map +1 -0
  839. package/dist/unit/parametrics.js +4 -0
  840. package/dist/unit/schema.d.ts +2 -0
  841. package/dist/unit/schema.d.ts.map +1 -0
  842. package/dist/unit/schema.js +1 -0
  843. package/dist/unit/unit-panel.d.ts +5 -0
  844. package/dist/unit/unit-panel.d.ts.map +1 -0
  845. package/dist/unit/unit-panel.js +61 -0
  846. package/dist/wall/curve-eligibility.d.ts +3 -0
  847. package/dist/wall/curve-eligibility.d.ts.map +1 -0
  848. package/dist/wall/curve-eligibility.js +10 -0
  849. package/dist/wall/curve-tool.d.ts.map +1 -1
  850. package/dist/wall/curve-tool.js +8 -11
  851. package/dist/wall/definition.d.ts +1 -1
  852. package/dist/wall/definition.d.ts.map +1 -1
  853. package/dist/wall/definition.js +36 -13
  854. package/dist/wall/floorplan-affordances.d.ts +1 -0
  855. package/dist/wall/floorplan-affordances.d.ts.map +1 -1
  856. package/dist/wall/floorplan-affordances.js +46 -7
  857. package/dist/wall/floorplan-move.d.ts +3 -4
  858. package/dist/wall/floorplan-move.d.ts.map +1 -1
  859. package/dist/wall/floorplan-move.js +122 -9
  860. package/dist/wall/floorplan.d.ts.map +1 -1
  861. package/dist/wall/floorplan.js +19 -23
  862. package/dist/wall/move-shared.d.ts.map +1 -1
  863. package/dist/wall/move-shared.js +10 -7
  864. package/dist/wall/paint.d.ts.map +1 -1
  865. package/dist/wall/paint.js +2 -0
  866. package/dist/wall/panel.d.ts.map +1 -1
  867. package/dist/wall/panel.js +10 -17
  868. package/dist/wall/parametrics.js +2 -2
  869. package/dist/wall/pointer-transparency.d.ts +128 -0
  870. package/dist/wall/pointer-transparency.d.ts.map +1 -0
  871. package/dist/wall/pointer-transparency.js +147 -0
  872. package/dist/wall/renderer.d.ts.map +1 -1
  873. package/dist/wall/renderer.js +65 -8
  874. package/dist/wall/selection-hit-owner.d.ts +53 -0
  875. package/dist/wall/selection-hit-owner.d.ts.map +1 -0
  876. package/dist/wall/selection-hit-owner.js +79 -0
  877. package/dist/wall/system.d.ts +2 -0
  878. package/dist/wall/system.d.ts.map +1 -1
  879. package/dist/wall/system.js +66 -25
  880. package/dist/wall/tool.d.ts.map +1 -1
  881. package/dist/wall/tool.js +5 -0
  882. package/dist/wall/treatment-level-data.d.ts +6 -1
  883. package/dist/wall/treatment-level-data.d.ts.map +1 -1
  884. package/dist/wall/treatment-level-data.js +82 -5
  885. package/dist/wall/treatments.d.ts +1 -0
  886. package/dist/wall/treatments.d.ts.map +1 -1
  887. package/dist/wall/treatments.js +5 -0
  888. package/dist/wall/wall-batch-system.d.ts +38 -6
  889. package/dist/wall/wall-batch-system.d.ts.map +1 -1
  890. package/dist/wall/wall-batch-system.js +122 -25
  891. package/dist/wall/wall-batch.js +2 -2
  892. package/dist/window/definition.d.ts +2 -1
  893. package/dist/window/definition.d.ts.map +1 -1
  894. package/dist/window/definition.js +51 -24
  895. package/dist/window/floorplan-move.d.ts.map +1 -1
  896. package/dist/window/floorplan-move.js +1 -1
  897. package/dist/window/move-tool.d.ts +7 -5
  898. package/dist/window/move-tool.d.ts.map +1 -1
  899. package/dist/window/move-tool.js +344 -58
  900. package/dist/window/panel.d.ts.map +1 -1
  901. package/dist/window/panel.js +8 -6
  902. package/dist/window/parametrics.js +2 -2
  903. package/dist/window/renderer.d.ts.map +1 -1
  904. package/dist/window/renderer.js +5 -7
  905. package/dist/window/tool.d.ts.map +1 -1
  906. package/dist/window/tool.js +227 -13
  907. package/dist/zone/definition.d.ts.map +1 -1
  908. package/dist/zone/definition.js +7 -1
  909. package/dist/zone/floorplan-affordances.d.ts +12 -12
  910. package/dist/zone/floorplan.d.ts +1 -3
  911. package/dist/zone/floorplan.d.ts.map +1 -1
  912. package/dist/zone/floorplan.js +43 -28
  913. package/dist/zone/quantities-panel.d.ts.map +1 -1
  914. package/dist/zone/quantities-panel.js +33 -29
  915. package/dist/zone/renderer.d.ts.map +1 -1
  916. package/dist/zone/renderer.js +20 -6
  917. package/dist/zone/unit-membership.d.ts +12 -0
  918. package/dist/zone/unit-membership.d.ts.map +1 -0
  919. package/dist/zone/unit-membership.js +22 -0
  920. package/package.json +16 -8
  921. package/dist/dormer/panel-window-section.d.ts +0 -14
  922. package/dist/dormer/panel-window-section.d.ts.map +0 -1
  923. package/dist/dormer/panel-window-section.js +0 -59
  924. package/dist/dormer/window-assembly.d.ts +0 -22
  925. package/dist/dormer/window-assembly.d.ts.map +0 -1
  926. package/dist/dormer/window-assembly.js +0 -111
  927. package/dist/dormer/window-frame.d.ts +0 -24
  928. package/dist/dormer/window-frame.d.ts.map +0 -1
  929. 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
+ }