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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (831) hide show
  1. package/LICENSE +21 -0
  2. package/dist/block/commands.d.ts +68 -0
  3. package/dist/block/commands.d.ts.map +1 -0
  4. package/dist/block/commands.js +1144 -0
  5. package/dist/block/contextual-help.d.ts +3 -0
  6. package/dist/block/contextual-help.d.ts.map +1 -0
  7. package/dist/block/contextual-help.js +38 -0
  8. package/dist/block/definition.d.ts +8 -0
  9. package/dist/block/definition.d.ts.map +1 -0
  10. package/dist/block/definition.js +126 -0
  11. package/dist/block/edit-session.d.ts +16 -0
  12. package/dist/block/edit-session.d.ts.map +1 -0
  13. package/dist/block/edit-session.js +32 -0
  14. package/dist/block/face-host.d.ts +3 -0
  15. package/dist/block/face-host.d.ts.map +1 -0
  16. package/dist/block/face-host.js +178 -0
  17. package/dist/block/floorplan.d.ts +3 -0
  18. package/dist/block/floorplan.d.ts.map +1 -0
  19. package/dist/block/floorplan.js +43 -0
  20. package/dist/block/geometry-snap.d.ts +18 -0
  21. package/dist/block/geometry-snap.d.ts.map +1 -0
  22. package/dist/block/geometry-snap.js +137 -0
  23. package/dist/block/geometry.d.ts +11 -0
  24. package/dist/block/geometry.d.ts.map +1 -0
  25. package/dist/block/geometry.js +146 -0
  26. package/dist/block/gesture-wheel.d.ts +8 -0
  27. package/dist/block/gesture-wheel.d.ts.map +1 -0
  28. package/dist/block/gesture-wheel.js +7 -0
  29. package/dist/block/interaction-sfx.d.ts +3 -0
  30. package/dist/block/interaction-sfx.d.ts.map +1 -0
  31. package/dist/block/interaction-sfx.js +16 -0
  32. package/dist/block/last-operation.d.ts +48 -0
  33. package/dist/block/last-operation.d.ts.map +1 -0
  34. package/dist/block/last-operation.js +120 -0
  35. package/dist/block/loop-cut-interaction.d.ts +5 -0
  36. package/dist/block/loop-cut-interaction.d.ts.map +1 -0
  37. package/dist/block/loop-cut-interaction.js +13 -0
  38. package/dist/block/material-slots.d.ts +61 -0
  39. package/dist/block/material-slots.d.ts.map +1 -0
  40. package/dist/block/material-slots.js +131 -0
  41. package/dist/block/modal-face-operation.d.ts +13 -0
  42. package/dist/block/modal-face-operation.d.ts.map +1 -0
  43. package/dist/block/modal-face-operation.js +40 -0
  44. package/dist/block/modal-session.d.ts +15 -0
  45. package/dist/block/modal-session.d.ts.map +1 -0
  46. package/dist/block/modal-session.js +40 -0
  47. package/dist/block/modal-transform.d.ts +30 -0
  48. package/dist/block/modal-transform.d.ts.map +1 -0
  49. package/dist/block/modal-transform.js +144 -0
  50. package/dist/block/paint.d.ts +3 -0
  51. package/dist/block/paint.d.ts.map +1 -0
  52. package/dist/block/paint.js +136 -0
  53. package/dist/block/panel.d.ts +2 -0
  54. package/dist/block/panel.d.ts.map +1 -0
  55. package/dist/block/panel.js +234 -0
  56. package/dist/block/parametrics.d.ts +4 -0
  57. package/dist/block/parametrics.d.ts.map +1 -0
  58. package/dist/block/parametrics.js +9 -0
  59. package/dist/block/preview.d.ts +6 -0
  60. package/dist/block/preview.d.ts.map +1 -0
  61. package/dist/block/preview.js +46 -0
  62. package/dist/block/rotation-drag.d.ts +5 -0
  63. package/dist/block/rotation-drag.d.ts.map +1 -0
  64. package/dist/block/rotation-drag.js +18 -0
  65. package/dist/block/schema.d.ts +2 -0
  66. package/dist/block/schema.d.ts.map +1 -0
  67. package/dist/block/schema.js +1 -0
  68. package/dist/block/selection-geometry.d.ts +9 -0
  69. package/dist/block/selection-geometry.d.ts.map +1 -0
  70. package/dist/block/selection-geometry.js +31 -0
  71. package/dist/block/selection-model.d.ts +17 -0
  72. package/dist/block/selection-model.d.ts.map +1 -0
  73. package/dist/block/selection-model.js +84 -0
  74. package/dist/block/selection.d.ts +4 -0
  75. package/dist/block/selection.d.ts.map +1 -0
  76. package/dist/block/selection.js +2570 -0
  77. package/dist/block/slots.d.ts +4 -0
  78. package/dist/block/slots.d.ts.map +1 -0
  79. package/dist/block/slots.js +17 -0
  80. package/dist/block/tool.d.ts +3 -0
  81. package/dist/block/tool.d.ts.map +1 -0
  82. package/dist/block/tool.js +174 -0
  83. package/dist/block/toolbar-state.d.ts +46 -0
  84. package/dist/block/toolbar-state.d.ts.map +1 -0
  85. package/dist/block/toolbar-state.js +67 -0
  86. package/dist/block/use-block-face-operation.d.ts +39 -0
  87. package/dist/block/use-block-face-operation.d.ts.map +1 -0
  88. package/dist/block/use-block-face-operation.js +218 -0
  89. package/dist/box-vent/definition.d.ts.map +1 -1
  90. package/dist/box-vent/definition.js +8 -5
  91. package/dist/box-vent/geometry.d.ts +4 -0
  92. package/dist/box-vent/geometry.d.ts.map +1 -1
  93. package/dist/box-vent/geometry.js +76 -44
  94. package/dist/box-vent/paint.d.ts +4 -0
  95. package/dist/box-vent/paint.d.ts.map +1 -0
  96. package/dist/box-vent/paint.js +40 -0
  97. package/dist/box-vent/panel.js +1 -1
  98. package/dist/box-vent/parametrics.js +3 -3
  99. package/dist/box-vent/renderer.d.ts.map +1 -1
  100. package/dist/box-vent/renderer.js +29 -10
  101. package/dist/cabinet/compartment-card.d.ts.map +1 -1
  102. package/dist/cabinet/compartment-card.js +11 -3
  103. package/dist/cabinet/continuous-placement.d.ts +3 -0
  104. package/dist/cabinet/continuous-placement.d.ts.map +1 -1
  105. package/dist/cabinet/continuous-placement.js +6 -0
  106. package/dist/cabinet/definition.d.ts.map +1 -1
  107. package/dist/cabinet/definition.js +400 -54
  108. package/dist/cabinet/floorplan-move.d.ts.map +1 -1
  109. package/dist/cabinet/floorplan-move.js +91 -22
  110. package/dist/cabinet/geometry/fridge.d.ts.map +1 -1
  111. package/dist/cabinet/geometry/fridge.js +25 -1
  112. package/dist/cabinet/geometry/fronts.d.ts +4 -0
  113. package/dist/cabinet/geometry/fronts.d.ts.map +1 -1
  114. package/dist/cabinet/geometry/fronts.js +1 -1
  115. package/dist/cabinet/geometry/run.d.ts.map +1 -1
  116. package/dist/cabinet/geometry/run.js +19 -1
  117. package/dist/cabinet/geometry.d.ts.map +1 -1
  118. package/dist/cabinet/geometry.js +63 -1
  119. package/dist/cabinet/index.d.ts +1 -0
  120. package/dist/cabinet/index.d.ts.map +1 -1
  121. package/dist/cabinet/index.js +1 -0
  122. package/dist/cabinet/insertion.d.ts +19 -0
  123. package/dist/cabinet/insertion.d.ts.map +1 -0
  124. package/dist/cabinet/insertion.js +49 -0
  125. package/dist/cabinet/move-frame.d.ts.map +1 -1
  126. package/dist/cabinet/move-frame.js +68 -2
  127. package/dist/cabinet/panel-context.d.ts +7 -0
  128. package/dist/cabinet/panel-context.d.ts.map +1 -0
  129. package/dist/cabinet/panel-context.js +16 -0
  130. package/dist/cabinet/panel-visibility.d.ts +9 -0
  131. package/dist/cabinet/panel-visibility.d.ts.map +1 -0
  132. package/dist/cabinet/panel-visibility.js +26 -0
  133. package/dist/cabinet/panel.d.ts.map +1 -1
  134. package/dist/cabinet/panel.js +221 -37
  135. package/dist/cabinet/placement-dimensions.d.ts +42 -0
  136. package/dist/cabinet/placement-dimensions.d.ts.map +1 -0
  137. package/dist/cabinet/placement-dimensions.js +227 -0
  138. package/dist/cabinet/placement-snap.d.ts +21 -1
  139. package/dist/cabinet/placement-snap.d.ts.map +1 -1
  140. package/dist/cabinet/placement-snap.js +48 -6
  141. package/dist/cabinet/preset-width-debt.d.ts +7 -0
  142. package/dist/cabinet/preset-width-debt.d.ts.map +1 -0
  143. package/dist/cabinet/preset-width-debt.js +45 -0
  144. package/dist/cabinet/presets.d.ts.map +1 -1
  145. package/dist/cabinet/presets.js +14 -13
  146. package/dist/cabinet/profiles.d.ts +14 -0
  147. package/dist/cabinet/profiles.d.ts.map +1 -0
  148. package/dist/cabinet/profiles.js +30 -0
  149. package/dist/cabinet/quick-actions.d.ts.map +1 -1
  150. package/dist/cabinet/quick-actions.js +8 -24
  151. package/dist/cabinet/reveals.d.ts +37 -0
  152. package/dist/cabinet/reveals.d.ts.map +1 -0
  153. package/dist/cabinet/reveals.js +13 -0
  154. package/dist/cabinet/run-layout.d.ts +90 -4
  155. package/dist/cabinet/run-layout.d.ts.map +1 -1
  156. package/dist/cabinet/run-layout.js +408 -23
  157. package/dist/cabinet/run-ops.d.ts +202 -10
  158. package/dist/cabinet/run-ops.d.ts.map +1 -1
  159. package/dist/cabinet/run-ops.js +520 -33
  160. package/dist/cabinet/run-panel.d.ts +6 -2
  161. package/dist/cabinet/run-panel.d.ts.map +1 -1
  162. package/dist/cabinet/run-panel.js +285 -108
  163. package/dist/cabinet/stack-transitions.d.ts.map +1 -1
  164. package/dist/cabinet/stack-transitions.js +35 -24
  165. package/dist/cabinet/stack.d.ts +169 -1
  166. package/dist/cabinet/stack.d.ts.map +1 -1
  167. package/dist/cabinet/stack.js +57 -18
  168. package/dist/cabinet/tool.d.ts.map +1 -1
  169. package/dist/cabinet/tool.js +615 -91
  170. package/dist/cabinet/validation.d.ts +27 -0
  171. package/dist/cabinet/validation.d.ts.map +1 -0
  172. package/dist/cabinet/validation.js +56 -0
  173. package/dist/cabinet/wall-height-presets.d.ts +12 -0
  174. package/dist/cabinet/wall-height-presets.d.ts.map +1 -0
  175. package/dist/cabinet/wall-height-presets.js +15 -0
  176. package/dist/cabinet/wall-snap.d.ts +27 -3
  177. package/dist/cabinet/wall-snap.d.ts.map +1 -1
  178. package/dist/cabinet/wall-snap.js +278 -62
  179. package/dist/cabinet/widths.d.ts +10 -0
  180. package/dist/cabinet/widths.d.ts.map +1 -0
  181. package/dist/cabinet/widths.js +13 -0
  182. package/dist/ceiling/definition.d.ts.map +1 -1
  183. package/dist/ceiling/definition.js +7 -1
  184. package/dist/ceiling/floorplan-affordances.d.ts +8 -8
  185. package/dist/ceiling/materials.d.ts.map +1 -1
  186. package/dist/ceiling/materials.js +0 -1
  187. package/dist/ceiling/panel.js +1 -1
  188. package/dist/ceiling/parametrics.js +1 -1
  189. package/dist/chimney/definition.js +1 -1
  190. package/dist/chimney/panel.js +1 -1
  191. package/dist/chimney/parametrics.js +3 -3
  192. package/dist/column/definition.d.ts.map +1 -1
  193. package/dist/column/definition.js +22 -4
  194. package/dist/column/panel.d.ts.map +1 -1
  195. package/dist/column/panel.js +37 -7
  196. package/dist/column/parametrics.d.ts.map +1 -1
  197. package/dist/column/parametrics.js +5 -3
  198. package/dist/column/tool.d.ts.map +1 -1
  199. package/dist/column/tool.js +50 -14
  200. package/dist/cupola/definition.d.ts.map +1 -1
  201. package/dist/cupola/definition.js +15 -6
  202. package/dist/cupola/geometry.d.ts +6 -0
  203. package/dist/cupola/geometry.d.ts.map +1 -1
  204. package/dist/cupola/geometry.js +60 -13
  205. package/dist/cupola/paint.d.ts +4 -0
  206. package/dist/cupola/paint.d.ts.map +1 -0
  207. package/dist/cupola/paint.js +39 -0
  208. package/dist/cupola/panel.js +1 -1
  209. package/dist/cupola/parametrics.js +3 -3
  210. package/dist/cupola/renderer.d.ts.map +1 -1
  211. package/dist/cupola/renderer.js +27 -8
  212. package/dist/door/floorplan-move.d.ts.map +1 -1
  213. package/dist/door/floorplan-move.js +1 -1
  214. package/dist/door/move-tool.d.ts.map +1 -1
  215. package/dist/door/move-tool.js +86 -32
  216. package/dist/door/panel.d.ts.map +1 -1
  217. package/dist/door/panel.js +1 -1
  218. package/dist/door/parametrics.js +2 -2
  219. package/dist/door/tool.d.ts.map +1 -1
  220. package/dist/door/tool.js +16 -4
  221. package/dist/dormer/csg-geometry.d.ts +6 -42
  222. package/dist/dormer/csg-geometry.d.ts.map +1 -1
  223. package/dist/dormer/csg-geometry.js +38 -209
  224. package/dist/dormer/definition.d.ts +4 -12
  225. package/dist/dormer/definition.d.ts.map +1 -1
  226. package/dist/dormer/definition.js +18 -193
  227. package/dist/dormer/floorplan.d.ts +3 -3
  228. package/dist/dormer/floorplan.d.ts.map +1 -1
  229. package/dist/dormer/floorplan.js +6 -17
  230. package/dist/dormer/geometry.d.ts +5 -24
  231. package/dist/dormer/geometry.d.ts.map +1 -1
  232. package/dist/dormer/geometry.js +64 -41
  233. package/dist/dormer/index.d.ts +1 -1
  234. package/dist/dormer/index.d.ts.map +1 -1
  235. package/dist/dormer/index.js +1 -1
  236. package/dist/dormer/move-tool.d.ts.map +1 -1
  237. package/dist/dormer/move-tool.js +31 -3
  238. package/dist/dormer/panel-windows-section.d.ts +9 -0
  239. package/dist/dormer/panel-windows-section.d.ts.map +1 -0
  240. package/dist/dormer/panel-windows-section.js +7 -0
  241. package/dist/dormer/panel.d.ts.map +1 -1
  242. package/dist/dormer/panel.js +100 -18
  243. package/dist/dormer/parametrics.d.ts.map +1 -1
  244. package/dist/dormer/parametrics.js +8 -77
  245. package/dist/dormer/preview.d.ts.map +1 -1
  246. package/dist/dormer/preview.js +9 -1
  247. package/dist/dormer/renderer.d.ts.map +1 -1
  248. package/dist/dormer/renderer.js +24 -26
  249. package/dist/dormer/tool.d.ts.map +1 -1
  250. package/dist/dormer/tool.js +3 -1
  251. package/dist/dormer/window-layout.d.ts +15 -0
  252. package/dist/dormer/window-layout.d.ts.map +1 -0
  253. package/dist/dormer/window-layout.js +59 -0
  254. package/dist/downspout/definition.d.ts.map +1 -1
  255. package/dist/downspout/definition.js +17 -4
  256. package/dist/downspout/geometry.d.ts.map +1 -1
  257. package/dist/downspout/geometry.js +10 -0
  258. package/dist/downspout/inspector-editors.d.ts.map +1 -1
  259. package/dist/downspout/inspector-editors.js +9 -1
  260. package/dist/downspout/parametrics.d.ts.map +1 -1
  261. package/dist/downspout/parametrics.js +2 -1
  262. package/dist/downspout/renderer.d.ts.map +1 -1
  263. package/dist/downspout/renderer.js +19 -3
  264. package/dist/downspout/system.d.ts +7 -0
  265. package/dist/downspout/system.d.ts.map +1 -0
  266. package/dist/downspout/system.js +114 -0
  267. package/dist/duct-fitting/accessory-geometry.d.ts +4 -0
  268. package/dist/duct-fitting/accessory-geometry.d.ts.map +1 -0
  269. package/dist/duct-fitting/accessory-geometry.js +47 -0
  270. package/dist/duct-fitting/definition.d.ts.map +1 -1
  271. package/dist/duct-fitting/definition.js +11 -1
  272. package/dist/duct-fitting/floorplan.d.ts.map +1 -1
  273. package/dist/duct-fitting/floorplan.js +8 -0
  274. package/dist/duct-fitting/geometry.d.ts.map +1 -1
  275. package/dist/duct-fitting/geometry.js +52 -38
  276. package/dist/duct-fitting/parametrics.d.ts.map +1 -1
  277. package/dist/duct-fitting/parametrics.js +78 -15
  278. package/dist/duct-fitting/ports.d.ts +1 -0
  279. package/dist/duct-fitting/ports.d.ts.map +1 -1
  280. package/dist/duct-fitting/ports.js +46 -0
  281. package/dist/duct-fitting/tool.d.ts +19 -0
  282. package/dist/duct-fitting/tool.d.ts.map +1 -1
  283. package/dist/duct-fitting/tool.js +126 -34
  284. package/dist/duct-segment/continuation.d.ts +21 -0
  285. package/dist/duct-segment/continuation.d.ts.map +1 -0
  286. package/dist/duct-segment/continuation.js +167 -0
  287. package/dist/duct-segment/definition.d.ts.map +1 -1
  288. package/dist/duct-segment/definition.js +19 -18
  289. package/dist/duct-segment/floorplan.d.ts.map +1 -1
  290. package/dist/duct-segment/floorplan.js +43 -0
  291. package/dist/duct-segment/geometry.d.ts.map +1 -1
  292. package/dist/duct-segment/geometry.js +3 -0
  293. package/dist/duct-segment/move-tool.d.ts.map +1 -1
  294. package/dist/duct-segment/move-tool.js +33 -6
  295. package/dist/duct-segment/parametrics.d.ts.map +1 -1
  296. package/dist/duct-segment/parametrics.js +45 -1
  297. package/dist/duct-segment/selection.d.ts +3 -4
  298. package/dist/duct-segment/selection.d.ts.map +1 -1
  299. package/dist/duct-segment/selection.js +186 -158
  300. package/dist/duct-segment/tool.d.ts +37 -0
  301. package/dist/duct-segment/tool.d.ts.map +1 -1
  302. package/dist/duct-segment/tool.js +305 -678
  303. package/dist/duct-terminal/parametrics.js +2 -2
  304. package/dist/duct-terminal/tool.d.ts.map +1 -1
  305. package/dist/duct-terminal/tool.js +37 -7
  306. package/dist/elevator/panel.d.ts.map +1 -1
  307. package/dist/elevator/panel.js +7 -7
  308. package/dist/elevator/system.d.ts.map +1 -1
  309. package/dist/elevator/system.js +10 -1
  310. package/dist/eyebrow-vent/definition.d.ts.map +1 -1
  311. package/dist/eyebrow-vent/definition.js +8 -5
  312. package/dist/eyebrow-vent/geometry.d.ts +4 -0
  313. package/dist/eyebrow-vent/geometry.d.ts.map +1 -1
  314. package/dist/eyebrow-vent/geometry.js +60 -15
  315. package/dist/eyebrow-vent/paint.d.ts +4 -0
  316. package/dist/eyebrow-vent/paint.d.ts.map +1 -0
  317. package/dist/eyebrow-vent/paint.js +33 -0
  318. package/dist/eyebrow-vent/panel.js +1 -1
  319. package/dist/eyebrow-vent/parametrics.js +3 -3
  320. package/dist/eyebrow-vent/renderer.d.ts.map +1 -1
  321. package/dist/eyebrow-vent/renderer.js +27 -8
  322. package/dist/fence/definition.d.ts.map +1 -1
  323. package/dist/fence/definition.js +2 -1
  324. package/dist/fence/floorplan-affordances.d.ts +1 -0
  325. package/dist/fence/floorplan-affordances.d.ts.map +1 -1
  326. package/dist/fence/floorplan-affordances.js +30 -1
  327. package/dist/fence/floorplan.d.ts.map +1 -1
  328. package/dist/fence/floorplan.js +14 -0
  329. package/dist/fence/parametrics.js +3 -3
  330. package/dist/fence/tool.d.ts.map +1 -1
  331. package/dist/fence/tool.js +47 -20
  332. package/dist/gutter/corner-mitre.d.ts.map +1 -1
  333. package/dist/gutter/corner-mitre.js +47 -7
  334. package/dist/gutter/definition.d.ts.map +1 -1
  335. package/dist/gutter/definition.js +23 -13
  336. package/dist/gutter/eave-align.d.ts.map +1 -1
  337. package/dist/gutter/eave-align.js +10 -0
  338. package/dist/gutter/eave-snap.d.ts +1 -1
  339. package/dist/gutter/eave-snap.d.ts.map +1 -1
  340. package/dist/gutter/eave-snap.js +10 -23
  341. package/dist/gutter/geometry.d.ts.map +1 -1
  342. package/dist/gutter/geometry.js +126 -8
  343. package/dist/gutter/outlet-lookup.d.ts.map +1 -1
  344. package/dist/gutter/outlet-lookup.js +22 -1
  345. package/dist/gutter/paint.d.ts +3 -0
  346. package/dist/gutter/paint.d.ts.map +1 -0
  347. package/dist/gutter/paint.js +6 -0
  348. package/dist/gutter/parametrics.d.ts.map +1 -1
  349. package/dist/gutter/parametrics.js +4 -1
  350. package/dist/gutter/renderer.d.ts.map +1 -1
  351. package/dist/gutter/renderer.js +63 -7
  352. package/dist/gutter/trim-clip.d.ts +3 -0
  353. package/dist/gutter/trim-clip.d.ts.map +1 -0
  354. package/dist/gutter/trim-clip.js +5 -0
  355. package/dist/hvac-equipment/parametrics.js +3 -3
  356. package/dist/hvac-equipment/tool.d.ts.map +1 -1
  357. package/dist/hvac-equipment/tool.js +12 -2
  358. package/dist/index.d.ts +5 -2
  359. package/dist/index.d.ts.map +1 -1
  360. package/dist/index.js +9 -2
  361. package/dist/item/definition.d.ts.map +1 -1
  362. package/dist/item/definition.js +10 -2
  363. package/dist/item/floorplan-move.d.ts.map +1 -1
  364. package/dist/item/floorplan-move.js +20 -1
  365. package/dist/item/floorplan.d.ts.map +1 -1
  366. package/dist/item/floorplan.js +22 -1
  367. package/dist/item/move-tool.d.ts +28 -1
  368. package/dist/item/move-tool.d.ts.map +1 -1
  369. package/dist/item/move-tool.js +14 -2
  370. package/dist/item/renderer.d.ts.map +1 -1
  371. package/dist/item/renderer.js +16 -6
  372. package/dist/item/system.d.ts +5 -0
  373. package/dist/item/system.d.ts.map +1 -1
  374. package/dist/item/system.js +7 -1
  375. package/dist/lean-to-extension/arc.d.ts +14 -0
  376. package/dist/lean-to-extension/arc.d.ts.map +1 -0
  377. package/dist/lean-to-extension/arc.js +56 -0
  378. package/dist/lean-to-extension/assembly.d.ts +61 -0
  379. package/dist/lean-to-extension/assembly.d.ts.map +1 -0
  380. package/dist/lean-to-extension/assembly.js +761 -0
  381. package/dist/lean-to-extension/canopy-joint.d.ts +35 -0
  382. package/dist/lean-to-extension/canopy-joint.d.ts.map +1 -0
  383. package/dist/lean-to-extension/canopy-joint.js +234 -0
  384. package/dist/lean-to-extension/conical-host.d.ts +15 -0
  385. package/dist/lean-to-extension/conical-host.d.ts.map +1 -0
  386. package/dist/lean-to-extension/conical-host.js +113 -0
  387. package/dist/lean-to-extension/corner-joint.d.ts +29 -0
  388. package/dist/lean-to-extension/corner-joint.d.ts.map +1 -0
  389. package/dist/lean-to-extension/corner-joint.js +1202 -0
  390. package/dist/lean-to-extension/definition.d.ts +4 -0
  391. package/dist/lean-to-extension/definition.d.ts.map +1 -0
  392. package/dist/lean-to-extension/definition.js +433 -0
  393. package/dist/lean-to-extension/floorplan-affordances.d.ts +4 -0
  394. package/dist/lean-to-extension/floorplan-affordances.d.ts.map +1 -0
  395. package/dist/lean-to-extension/floorplan-affordances.js +144 -0
  396. package/dist/lean-to-extension/floorplan-move.d.ts +3 -0
  397. package/dist/lean-to-extension/floorplan-move.d.ts.map +1 -0
  398. package/dist/lean-to-extension/floorplan-move.js +157 -0
  399. package/dist/lean-to-extension/floorplan-tool.d.ts +4 -0
  400. package/dist/lean-to-extension/floorplan-tool.d.ts.map +1 -0
  401. package/dist/lean-to-extension/floorplan-tool.js +385 -0
  402. package/dist/lean-to-extension/floorplan.d.ts +3 -0
  403. package/dist/lean-to-extension/floorplan.d.ts.map +1 -0
  404. package/dist/lean-to-extension/floorplan.js +373 -0
  405. package/dist/lean-to-extension/geometry.d.ts +7 -0
  406. package/dist/lean-to-extension/geometry.d.ts.map +1 -0
  407. package/dist/lean-to-extension/geometry.js +662 -0
  408. package/dist/lean-to-extension/index.d.ts +8 -0
  409. package/dist/lean-to-extension/index.d.ts.map +1 -0
  410. package/dist/lean-to-extension/index.js +7 -0
  411. package/dist/lean-to-extension/layout.d.ts +108 -0
  412. package/dist/lean-to-extension/layout.d.ts.map +1 -0
  413. package/dist/lean-to-extension/layout.js +477 -0
  414. package/dist/lean-to-extension/managed-preview.d.ts +3 -0
  415. package/dist/lean-to-extension/managed-preview.d.ts.map +1 -0
  416. package/dist/lean-to-extension/managed-preview.js +93 -0
  417. package/dist/lean-to-extension/move-tool.d.ts +8 -0
  418. package/dist/lean-to-extension/move-tool.d.ts.map +1 -0
  419. package/dist/lean-to-extension/move-tool.js +202 -0
  420. package/dist/lean-to-extension/paint.d.ts +2 -0
  421. package/dist/lean-to-extension/paint.d.ts.map +1 -0
  422. package/dist/lean-to-extension/paint.js +16 -0
  423. package/dist/lean-to-extension/parametrics.d.ts +4 -0
  424. package/dist/lean-to-extension/parametrics.d.ts.map +1 -0
  425. package/dist/lean-to-extension/parametrics.js +570 -0
  426. package/dist/lean-to-extension/placement-scope.d.ts +3 -0
  427. package/dist/lean-to-extension/placement-scope.d.ts.map +1 -0
  428. package/dist/lean-to-extension/placement-scope.js +4 -0
  429. package/dist/lean-to-extension/placement-validation.d.ts +4 -0
  430. package/dist/lean-to-extension/placement-validation.d.ts.map +1 -0
  431. package/dist/lean-to-extension/placement-validation.js +324 -0
  432. package/dist/lean-to-extension/placement.d.ts +59 -0
  433. package/dist/lean-to-extension/placement.d.ts.map +1 -0
  434. package/dist/lean-to-extension/placement.js +331 -0
  435. package/dist/lean-to-extension/plan-footprint.d.ts +10 -0
  436. package/dist/lean-to-extension/plan-footprint.d.ts.map +1 -0
  437. package/dist/lean-to-extension/plan-footprint.js +27 -0
  438. package/dist/lean-to-extension/preview-geometry.d.ts +7 -0
  439. package/dist/lean-to-extension/preview-geometry.d.ts.map +1 -0
  440. package/dist/lean-to-extension/preview-geometry.js +40 -0
  441. package/dist/lean-to-extension/preview.d.ts +7 -0
  442. package/dist/lean-to-extension/preview.d.ts.map +1 -0
  443. package/dist/lean-to-extension/preview.js +20 -0
  444. package/dist/lean-to-extension/renderer.d.ts +6 -0
  445. package/dist/lean-to-extension/renderer.d.ts.map +1 -0
  446. package/dist/lean-to-extension/renderer.js +34 -0
  447. package/dist/lean-to-extension/roof-attachment.d.ts +28 -0
  448. package/dist/lean-to-extension/roof-attachment.d.ts.map +1 -0
  449. package/dist/lean-to-extension/roof-attachment.js +376 -0
  450. package/dist/lean-to-extension/schema.d.ts +2 -0
  451. package/dist/lean-to-extension/schema.d.ts.map +1 -0
  452. package/dist/lean-to-extension/schema.js +1 -0
  453. package/dist/lean-to-extension/slots.d.ts +5 -0
  454. package/dist/lean-to-extension/slots.d.ts.map +1 -0
  455. package/dist/lean-to-extension/slots.js +15 -0
  456. package/dist/lean-to-extension/system.d.ts +7 -0
  457. package/dist/lean-to-extension/system.d.ts.map +1 -0
  458. package/dist/lean-to-extension/system.js +726 -0
  459. package/dist/lean-to-extension/tool.d.ts +3 -0
  460. package/dist/lean-to-extension/tool.d.ts.map +1 -0
  461. package/dist/lean-to-extension/tool.js +580 -0
  462. package/dist/lean-to-extension/wall-target.d.ts +10 -0
  463. package/dist/lean-to-extension/wall-target.d.ts.map +1 -0
  464. package/dist/lean-to-extension/wall-target.js +29 -0
  465. package/dist/lineset/definition.d.ts.map +1 -1
  466. package/dist/lineset/definition.js +1 -0
  467. package/dist/liquid-line/definition.d.ts.map +1 -1
  468. package/dist/liquid-line/definition.js +1 -0
  469. package/dist/pipe-fitting/definition.d.ts.map +1 -1
  470. package/dist/pipe-fitting/definition.js +16 -2
  471. package/dist/pipe-fitting/floorplan.d.ts.map +1 -1
  472. package/dist/pipe-fitting/floorplan.js +4 -0
  473. package/dist/pipe-fitting/geometry.d.ts +3 -5
  474. package/dist/pipe-fitting/geometry.d.ts.map +1 -1
  475. package/dist/pipe-fitting/geometry.js +154 -21
  476. package/dist/pipe-fitting/inline-insertion.d.ts +13 -0
  477. package/dist/pipe-fitting/inline-insertion.d.ts.map +1 -0
  478. package/dist/pipe-fitting/inline-insertion.js +96 -0
  479. package/dist/pipe-fitting/parametrics.d.ts.map +1 -1
  480. package/dist/pipe-fitting/parametrics.js +17 -3
  481. package/dist/pipe-fitting/ports.d.ts.map +1 -1
  482. package/dist/pipe-fitting/ports.js +8 -1
  483. package/dist/pipe-fitting/tool.d.ts +26 -0
  484. package/dist/pipe-fitting/tool.d.ts.map +1 -1
  485. package/dist/pipe-fitting/tool.js +182 -37
  486. package/dist/pipe-segment/continuation.d.ts +22 -0
  487. package/dist/pipe-segment/continuation.d.ts.map +1 -0
  488. package/dist/pipe-segment/continuation.js +146 -0
  489. package/dist/pipe-segment/definition.d.ts.map +1 -1
  490. package/dist/pipe-segment/definition.js +23 -7
  491. package/dist/pipe-segment/floorplan.d.ts.map +1 -1
  492. package/dist/pipe-segment/floorplan.js +42 -0
  493. package/dist/pipe-segment/geometry.d.ts +2 -1
  494. package/dist/pipe-segment/geometry.d.ts.map +1 -1
  495. package/dist/pipe-segment/geometry.js +4 -1
  496. package/dist/pipe-segment/move-tool.d.ts.map +1 -1
  497. package/dist/pipe-segment/move-tool.js +25 -5
  498. package/dist/pipe-segment/parametrics.d.ts.map +1 -1
  499. package/dist/pipe-segment/parametrics.js +45 -0
  500. package/dist/pipe-segment/selection.d.ts +3 -4
  501. package/dist/pipe-segment/selection.d.ts.map +1 -1
  502. package/dist/pipe-segment/selection.js +169 -105
  503. package/dist/pipe-segment/slope.d.ts +5 -0
  504. package/dist/pipe-segment/slope.d.ts.map +1 -0
  505. package/dist/pipe-segment/slope.js +10 -0
  506. package/dist/pipe-segment/tool.d.ts.map +1 -1
  507. package/dist/pipe-segment/tool.js +325 -545
  508. package/dist/pipe-trap/tool.d.ts.map +1 -1
  509. package/dist/pipe-trap/tool.js +15 -2
  510. package/dist/ridge-vent/definition.js +1 -1
  511. package/dist/ridge-vent/panel.d.ts.map +1 -1
  512. package/dist/ridge-vent/panel.js +2 -2
  513. package/dist/ridge-vent/parametrics.js +1 -1
  514. package/dist/roof/conical-roof-placement.d.ts +36 -0
  515. package/dist/roof/conical-roof-placement.d.ts.map +1 -0
  516. package/dist/roof/conical-roof-placement.js +134 -0
  517. package/dist/roof/conical-roof.d.ts +3 -0
  518. package/dist/roof/conical-roof.d.ts.map +1 -0
  519. package/dist/roof/conical-roof.js +53 -0
  520. package/dist/roof/definition.d.ts +3 -10
  521. package/dist/roof/definition.d.ts.map +1 -1
  522. package/dist/roof/definition.js +83 -13
  523. package/dist/roof/floorplan.d.ts +3 -10
  524. package/dist/roof/floorplan.d.ts.map +1 -1
  525. package/dist/roof/floorplan.js +139 -165
  526. package/dist/roof/index.d.ts +1 -0
  527. package/dist/roof/index.d.ts.map +1 -1
  528. package/dist/roof/index.js +1 -0
  529. package/dist/roof/panel.d.ts.map +1 -1
  530. package/dist/roof/panel.js +22 -23
  531. package/dist/roof/renderer.d.ts.map +1 -1
  532. package/dist/roof/roof-footprint-source.d.ts +16 -0
  533. package/dist/roof/roof-footprint-source.d.ts.map +1 -0
  534. package/dist/roof/roof-footprint-source.js +14 -0
  535. package/dist/roof/roof-footprint.d.ts +25 -0
  536. package/dist/roof/roof-footprint.d.ts.map +1 -0
  537. package/dist/roof/roof-footprint.js +94 -0
  538. package/dist/roof/roof-placement-mode.d.ts +18 -0
  539. package/dist/roof/roof-placement-mode.d.ts.map +1 -0
  540. package/dist/roof/roof-placement-mode.js +24 -0
  541. package/dist/roof/tool.d.ts +10 -0
  542. package/dist/roof/tool.d.ts.map +1 -0
  543. package/dist/roof/tool.js +922 -0
  544. package/dist/roof-segment/definition.d.ts.map +1 -1
  545. package/dist/roof-segment/definition.js +52 -8
  546. package/dist/roof-segment/floorplan-affordances.d.ts +5 -6
  547. package/dist/roof-segment/floorplan-affordances.d.ts.map +1 -1
  548. package/dist/roof-segment/floorplan-affordances.js +64 -18
  549. package/dist/roof-segment/floorplan.d.ts +3 -2
  550. package/dist/roof-segment/floorplan.d.ts.map +1 -1
  551. package/dist/roof-segment/floorplan.js +95 -46
  552. package/dist/roof-segment/panel.d.ts.map +1 -1
  553. package/dist/roof-segment/panel.js +122 -27
  554. package/dist/scan/definition.d.ts +2 -3
  555. package/dist/scan/definition.d.ts.map +1 -1
  556. package/dist/scan/definition.js +9 -7
  557. package/dist/scan/parametrics.d.ts.map +1 -1
  558. package/dist/scan/parametrics.js +13 -1
  559. package/dist/scan/renderer.d.ts.map +1 -1
  560. package/dist/scan/renderer.js +8 -2
  561. package/dist/shared/accessory-cursor.d.ts +7 -0
  562. package/dist/shared/accessory-cursor.d.ts.map +1 -0
  563. package/dist/shared/accessory-cursor.js +83 -0
  564. package/dist/shared/accessory-floorplan.d.ts +4 -0
  565. package/dist/shared/accessory-floorplan.d.ts.map +1 -0
  566. package/dist/shared/accessory-floorplan.js +51 -0
  567. package/dist/shared/accessory-geometry.d.ts +7 -0
  568. package/dist/shared/accessory-geometry.d.ts.map +1 -0
  569. package/dist/shared/accessory-geometry.js +79 -0
  570. package/dist/shared/accessory-placement.d.ts +10 -0
  571. package/dist/shared/accessory-placement.d.ts.map +1 -0
  572. package/dist/shared/accessory-placement.js +126 -0
  573. package/dist/shared/accessory-snapping.d.ts +5 -0
  574. package/dist/shared/accessory-snapping.d.ts.map +1 -0
  575. package/dist/shared/accessory-snapping.js +40 -0
  576. package/dist/shared/automatic-run-end-cap.d.ts +11 -0
  577. package/dist/shared/automatic-run-end-cap.d.ts.map +1 -0
  578. package/dist/shared/automatic-run-end-cap.js +159 -0
  579. package/dist/shared/block-face-host.d.ts +15 -0
  580. package/dist/shared/block-face-host.d.ts.map +1 -0
  581. package/dist/shared/block-face-host.js +36 -0
  582. package/dist/shared/connection-compatibility.d.ts +8 -0
  583. package/dist/shared/connection-compatibility.d.ts.map +1 -0
  584. package/dist/shared/connection-compatibility.js +28 -0
  585. package/dist/shared/connection-feedback.d.ts +10 -0
  586. package/dist/shared/connection-feedback.d.ts.map +1 -0
  587. package/dist/shared/connection-feedback.js +17 -0
  588. package/dist/shared/distribution-run-contract.d.ts +49 -0
  589. package/dist/shared/distribution-run-contract.d.ts.map +1 -0
  590. package/dist/shared/distribution-run-contract.js +30 -0
  591. package/dist/shared/distribution-run-tool.d.ts +107 -0
  592. package/dist/shared/distribution-run-tool.d.ts.map +1 -0
  593. package/dist/shared/distribution-run-tool.js +941 -0
  594. package/dist/shared/dormer-wall-opening-placement.d.ts +21 -0
  595. package/dist/shared/dormer-wall-opening-placement.d.ts.map +1 -0
  596. package/dist/shared/dormer-wall-opening-placement.js +101 -0
  597. package/dist/shared/duct-adapter.d.ts +10 -0
  598. package/dist/shared/duct-adapter.d.ts.map +1 -0
  599. package/dist/shared/duct-adapter.js +43 -0
  600. package/dist/shared/elbow-branch-continuation.d.ts +20 -0
  601. package/dist/shared/elbow-branch-continuation.d.ts.map +1 -0
  602. package/dist/shared/elbow-branch-continuation.js +200 -0
  603. package/dist/shared/fitting-clearance.d.ts +5 -0
  604. package/dist/shared/fitting-clearance.d.ts.map +1 -0
  605. package/dist/shared/fitting-clearance.js +11 -0
  606. package/dist/shared/fitting-deletion-cleanup.d.ts +14 -0
  607. package/dist/shared/fitting-deletion-cleanup.d.ts.map +1 -0
  608. package/dist/shared/fitting-deletion-cleanup.js +297 -0
  609. package/dist/shared/fitting-surface-support.d.ts +5 -0
  610. package/dist/shared/fitting-surface-support.d.ts.map +1 -0
  611. package/dist/shared/fitting-surface-support.js +42 -0
  612. package/dist/shared/fitting-tool-options.d.ts +4 -0
  613. package/dist/shared/fitting-tool-options.d.ts.map +1 -0
  614. package/dist/shared/fitting-tool-options.js +175 -0
  615. package/dist/shared/floor-placement.d.ts +1 -1
  616. package/dist/shared/floor-placement.d.ts.map +1 -1
  617. package/dist/shared/floor-placement.js +13 -33
  618. package/dist/shared/ghost-materials.d.ts.map +1 -1
  619. package/dist/shared/ghost-materials.js +2 -1
  620. package/dist/shared/lean-to-post-omissions.d.ts +8 -0
  621. package/dist/shared/lean-to-post-omissions.d.ts.map +1 -0
  622. package/dist/shared/lean-to-post-omissions.js +51 -0
  623. package/dist/shared/mep-fitting-actions.d.ts +5 -0
  624. package/dist/shared/mep-fitting-actions.d.ts.map +1 -0
  625. package/dist/shared/mep-fitting-actions.js +16 -0
  626. package/dist/shared/mep-presets.d.ts +11 -0
  627. package/dist/shared/mep-presets.d.ts.map +1 -0
  628. package/dist/shared/mep-presets.js +34 -0
  629. package/dist/shared/node-batch/candidates.d.ts +29 -0
  630. package/dist/shared/node-batch/candidates.d.ts.map +1 -0
  631. package/dist/shared/node-batch/candidates.js +260 -0
  632. package/dist/shared/node-batch/store.d.ts +26 -0
  633. package/dist/shared/node-batch/store.d.ts.map +1 -0
  634. package/dist/shared/node-batch/store.js +319 -0
  635. package/dist/shared/node-batch/system.d.ts +8 -0
  636. package/dist/shared/node-batch/system.d.ts.map +1 -0
  637. package/dist/shared/node-batch/system.js +569 -0
  638. package/dist/shared/node-batch/types.d.ts +99 -0
  639. package/dist/shared/node-batch/types.d.ts.map +1 -0
  640. package/dist/shared/node-batch/types.js +4 -0
  641. package/dist/shared/opening-move-history.d.ts +47 -0
  642. package/dist/shared/opening-move-history.d.ts.map +1 -0
  643. package/dist/shared/opening-move-history.js +18 -0
  644. package/dist/shared/opening-move-wall-gate.d.ts +55 -0
  645. package/dist/shared/opening-move-wall-gate.d.ts.map +1 -0
  646. package/dist/shared/opening-move-wall-gate.js +44 -0
  647. package/dist/shared/path-point-affordance.d.ts +2 -2
  648. package/dist/shared/path-point-affordance.d.ts.map +1 -1
  649. package/dist/shared/path-point-affordance.js +30 -6
  650. package/dist/shared/placeholder-geometry.d.ts.map +1 -1
  651. package/dist/shared/placeholder-geometry.js +4 -0
  652. package/dist/shared/ports.d.ts +20 -3
  653. package/dist/shared/ports.d.ts.map +1 -1
  654. package/dist/shared/ports.js +148 -7
  655. package/dist/shared/primitive-uv.d.ts +17 -0
  656. package/dist/shared/primitive-uv.d.ts.map +1 -0
  657. package/dist/shared/primitive-uv.js +107 -0
  658. package/dist/shared/reducer-size.d.ts +2 -0
  659. package/dist/shared/reducer-size.d.ts.map +1 -0
  660. package/dist/shared/reducer-size.js +12 -0
  661. package/dist/shared/ridge-snap.js +1 -1
  662. package/dist/shared/roof-surface.d.ts.map +1 -1
  663. package/dist/shared/roof-surface.js +17 -2
  664. package/dist/shared/run-cursor.d.ts +22 -0
  665. package/dist/shared/run-cursor.d.ts.map +1 -0
  666. package/dist/shared/run-cursor.js +19 -0
  667. package/dist/shared/run-direction-feedback.d.ts +20 -0
  668. package/dist/shared/run-direction-feedback.d.ts.map +1 -0
  669. package/dist/shared/run-direction-feedback.js +139 -0
  670. package/dist/shared/run-hanger-controls.d.ts +7 -0
  671. package/dist/shared/run-hanger-controls.d.ts.map +1 -0
  672. package/dist/shared/run-hanger-controls.js +44 -0
  673. package/dist/shared/run-hanger-inspector.d.ts +5 -0
  674. package/dist/shared/run-hanger-inspector.d.ts.map +1 -0
  675. package/dist/shared/run-hanger-inspector.js +43 -0
  676. package/dist/shared/run-hanger-mode.d.ts +11 -0
  677. package/dist/shared/run-hanger-mode.d.ts.map +1 -0
  678. package/dist/shared/run-hanger-mode.js +32 -0
  679. package/dist/shared/run-hanger-system.d.ts +3 -0
  680. package/dist/shared/run-hanger-system.d.ts.map +1 -0
  681. package/dist/shared/run-hanger-system.js +39 -0
  682. package/dist/shared/run-hangers.d.ts +25 -0
  683. package/dist/shared/run-hangers.d.ts.map +1 -0
  684. package/dist/shared/run-hangers.js +261 -0
  685. package/dist/shared/run-port-snap.d.ts +11 -0
  686. package/dist/shared/run-port-snap.d.ts.map +1 -0
  687. package/dist/shared/run-port-snap.js +19 -0
  688. package/dist/shared/run-screen-direction.d.ts +5 -0
  689. package/dist/shared/run-screen-direction.d.ts.map +1 -0
  690. package/dist/shared/run-screen-direction.js +24 -0
  691. package/dist/shared/selection-handles.d.ts +4 -0
  692. package/dist/shared/selection-handles.d.ts.map +1 -1
  693. package/dist/shared/selection-handles.js +10 -0
  694. package/dist/shared/slot-paint.d.ts +8 -1
  695. package/dist/shared/slot-paint.d.ts.map +1 -1
  696. package/dist/shared/slot-paint.js +120 -27
  697. package/dist/shared/surface-paint.d.ts +1 -2
  698. package/dist/shared/surface-paint.d.ts.map +1 -1
  699. package/dist/shared/surface-paint.js +9 -20
  700. package/dist/shared/system-check-panel.d.ts +6 -0
  701. package/dist/shared/system-check-panel.d.ts.map +1 -0
  702. package/dist/shared/system-check-panel.js +62 -0
  703. package/dist/shared/system-checks.d.ts +9 -0
  704. package/dist/shared/system-checks.d.ts.map +1 -0
  705. package/dist/shared/system-checks.js +131 -0
  706. package/dist/shared/wall-attach-target.d.ts +14 -2
  707. package/dist/shared/wall-attach-target.d.ts.map +1 -1
  708. package/dist/shared/wall-attach-target.js +108 -4
  709. package/dist/shared/wall-opening-clearance.d.ts +19 -0
  710. package/dist/shared/wall-opening-clearance.d.ts.map +1 -0
  711. package/dist/shared/wall-opening-clearance.js +28 -0
  712. package/dist/shared/wall-run-move.d.ts +17 -0
  713. package/dist/shared/wall-run-move.d.ts.map +1 -0
  714. package/dist/shared/wall-run-move.js +56 -0
  715. package/dist/shelf/parametrics.js +4 -4
  716. package/dist/site/renderer.d.ts.map +1 -1
  717. package/dist/site/renderer.js +40 -28
  718. package/dist/site/terrain-geometry.d.ts +1 -1
  719. package/dist/site/terrain-geometry.d.ts.map +1 -1
  720. package/dist/site/terrain-geometry.js +35 -9
  721. package/dist/site/terrain-mesh.d.ts +5 -0
  722. package/dist/site/terrain-mesh.d.ts.map +1 -1
  723. package/dist/site/terrain-mesh.js +63 -34
  724. package/dist/skylight/definition.js +1 -1
  725. package/dist/skylight/panel.js +1 -1
  726. package/dist/skylight/parametrics.js +2 -2
  727. package/dist/slab/definition.d.ts.map +1 -1
  728. package/dist/slab/definition.js +6 -1
  729. package/dist/slab/dependency-tracker.d.ts +3 -0
  730. package/dist/slab/dependency-tracker.d.ts.map +1 -0
  731. package/dist/slab/dependency-tracker.js +104 -0
  732. package/dist/slab/floorplan-affordances.d.ts +8 -8
  733. package/dist/slab/geometry.d.ts.map +1 -1
  734. package/dist/slab/geometry.js +16 -2
  735. package/dist/slab/panel.js +1 -1
  736. package/dist/slab/parametrics.js +1 -1
  737. package/dist/slab/system.d.ts.map +1 -1
  738. package/dist/slab/system.js +8 -64
  739. package/dist/solar-panel/definition.js +1 -1
  740. package/dist/solar-panel/panel.js +1 -1
  741. package/dist/solar-panel/parametrics.js +2 -2
  742. package/dist/stair/panel.d.ts.map +1 -1
  743. package/dist/stair/panel.js +18 -13
  744. package/dist/stair/stair-type.d.ts +18 -0
  745. package/dist/stair/stair-type.d.ts.map +1 -0
  746. package/dist/stair/stair-type.js +25 -0
  747. package/dist/stair-segment/panel.d.ts.map +1 -1
  748. package/dist/stair-segment/panel.js +44 -5
  749. package/dist/turbine-vent/definition.d.ts.map +1 -1
  750. package/dist/turbine-vent/definition.js +8 -5
  751. package/dist/turbine-vent/geometry.d.ts.map +1 -1
  752. package/dist/turbine-vent/geometry.js +48 -18
  753. package/dist/turbine-vent/paint.d.ts +4 -0
  754. package/dist/turbine-vent/paint.d.ts.map +1 -0
  755. package/dist/turbine-vent/paint.js +35 -0
  756. package/dist/turbine-vent/panel.js +1 -1
  757. package/dist/turbine-vent/parametrics.js +2 -2
  758. package/dist/turbine-vent/renderer.d.ts.map +1 -1
  759. package/dist/turbine-vent/renderer.js +31 -9
  760. package/dist/wall/curve-eligibility.d.ts +3 -0
  761. package/dist/wall/curve-eligibility.d.ts.map +1 -0
  762. package/dist/wall/curve-eligibility.js +10 -0
  763. package/dist/wall/curve-tool.d.ts.map +1 -1
  764. package/dist/wall/curve-tool.js +8 -11
  765. package/dist/wall/definition.d.ts +1 -1
  766. package/dist/wall/definition.d.ts.map +1 -1
  767. package/dist/wall/definition.js +36 -13
  768. package/dist/wall/floorplan-affordances.d.ts +1 -0
  769. package/dist/wall/floorplan-affordances.d.ts.map +1 -1
  770. package/dist/wall/floorplan-affordances.js +46 -7
  771. package/dist/wall/floorplan-move.d.ts +3 -4
  772. package/dist/wall/floorplan-move.d.ts.map +1 -1
  773. package/dist/wall/floorplan-move.js +122 -9
  774. package/dist/wall/floorplan.d.ts.map +1 -1
  775. package/dist/wall/floorplan.js +19 -23
  776. package/dist/wall/move-shared.d.ts.map +1 -1
  777. package/dist/wall/move-shared.js +10 -7
  778. package/dist/wall/paint.d.ts.map +1 -1
  779. package/dist/wall/paint.js +2 -0
  780. package/dist/wall/panel.d.ts.map +1 -1
  781. package/dist/wall/panel.js +10 -17
  782. package/dist/wall/parametrics.js +2 -2
  783. package/dist/wall/pointer-transparency.d.ts +128 -0
  784. package/dist/wall/pointer-transparency.d.ts.map +1 -0
  785. package/dist/wall/pointer-transparency.js +147 -0
  786. package/dist/wall/renderer.d.ts.map +1 -1
  787. package/dist/wall/renderer.js +65 -8
  788. package/dist/wall/selection-hit-owner.d.ts +53 -0
  789. package/dist/wall/selection-hit-owner.d.ts.map +1 -0
  790. package/dist/wall/selection-hit-owner.js +79 -0
  791. package/dist/wall/system.d.ts +2 -0
  792. package/dist/wall/system.d.ts.map +1 -1
  793. package/dist/wall/system.js +66 -25
  794. package/dist/wall/tool.d.ts.map +1 -1
  795. package/dist/wall/tool.js +5 -0
  796. package/dist/wall/treatment-level-data.d.ts +6 -1
  797. package/dist/wall/treatment-level-data.d.ts.map +1 -1
  798. package/dist/wall/treatment-level-data.js +82 -5
  799. package/dist/wall/treatments.d.ts +1 -0
  800. package/dist/wall/treatments.d.ts.map +1 -1
  801. package/dist/wall/treatments.js +5 -0
  802. package/dist/wall/wall-batch-system.d.ts +38 -6
  803. package/dist/wall/wall-batch-system.d.ts.map +1 -1
  804. package/dist/wall/wall-batch-system.js +122 -25
  805. package/dist/wall/wall-batch.js +2 -2
  806. package/dist/window/definition.d.ts +2 -1
  807. package/dist/window/definition.d.ts.map +1 -1
  808. package/dist/window/definition.js +51 -24
  809. package/dist/window/floorplan-move.d.ts.map +1 -1
  810. package/dist/window/floorplan-move.js +1 -1
  811. package/dist/window/move-tool.d.ts +7 -5
  812. package/dist/window/move-tool.d.ts.map +1 -1
  813. package/dist/window/move-tool.js +344 -58
  814. package/dist/window/panel.d.ts.map +1 -1
  815. package/dist/window/panel.js +2 -0
  816. package/dist/window/parametrics.js +2 -2
  817. package/dist/window/renderer.d.ts.map +1 -1
  818. package/dist/window/renderer.js +5 -7
  819. package/dist/window/tool.d.ts.map +1 -1
  820. package/dist/window/tool.js +227 -13
  821. package/dist/zone/floorplan-affordances.d.ts +12 -12
  822. package/package.json +11 -9
  823. package/dist/dormer/panel-window-section.d.ts +0 -14
  824. package/dist/dormer/panel-window-section.d.ts.map +0 -1
  825. package/dist/dormer/panel-window-section.js +0 -59
  826. package/dist/dormer/window-assembly.d.ts +0 -22
  827. package/dist/dormer/window-assembly.d.ts.map +0 -1
  828. package/dist/dormer/window-assembly.js +0 -111
  829. package/dist/dormer/window-frame.d.ts +0 -24
  830. package/dist/dormer/window-frame.d.ts.map +0 -1
  831. package/dist/dormer/window-frame.js +0 -100
@@ -0,0 +1,1202 @@
1
+ import { unionPolygons, WallNode } from '@pascal-app/core';
2
+ import { bendLocalPoint, isCurvedLeanTo, leanToArcFrameAtLocalX } from './arc';
3
+ import { resolveFreestandingCanopyJoints } from './canopy-joint';
4
+ import { leanToWallLocalPose, resolveLeanToLayout } from './layout';
5
+ import { applyLeanToWallCornerSpan } from './roof-attachment';
6
+ export const LEAN_TO_CORNER_JOINTS_KEY = 'leanToCornerJoints';
7
+ const WALL_CONNECTION_OVERLAP = 0.02;
8
+ const WALL_CONNECTION_TRIM = 0.002;
9
+ const PLAN_TOLERANCE = 1e-6;
10
+ const MIN_NON_COLLINEAR_ANGLE = 1e-4;
11
+ const LINEAR_DIRECTION_TOLERANCE = 1e-6;
12
+ const LINEAR_JOIN_PLAN_TOLERANCE = 0.03;
13
+ const FREESTANDING_JOINT_WALL_THICKNESS = 0.1;
14
+ const LINEAR_JOIN_HEIGHT_TOLERANCE = 0.02;
15
+ function planDistance(a, b) {
16
+ return Math.hypot(a[0] - b[0], a[1] - b[1]);
17
+ }
18
+ function directionsFormSupportedCorner(away, candidateAway) {
19
+ if (!(away && candidateAway))
20
+ return false;
21
+ const dot = Math.max(-1, Math.min(1, away[0] * candidateAway[0] + away[1] * candidateAway[1]));
22
+ const angle = Math.acos(dot);
23
+ return angle > MIN_NON_COLLINEAR_ANGLE && angle < Math.PI - MIN_NON_COLLINEAR_ANGLE;
24
+ }
25
+ function wallFrame(wall) {
26
+ const dx = wall.end[0] - wall.start[0];
27
+ const dz = wall.end[1] - wall.start[1];
28
+ const length = Math.hypot(dx, dz);
29
+ if (length <= PLAN_TOLERANCE)
30
+ return null;
31
+ return {
32
+ along: [dx / length, dz / length],
33
+ perpendicular: [-dz / length, dx / length],
34
+ start: [wall.start[0], wall.start[1]],
35
+ };
36
+ }
37
+ function resolveLeanToJointFrame(leanTo, wall) {
38
+ if (wall)
39
+ return { kind: 'wall', leanTo, wall };
40
+ if (!(leanTo.hostKind === 'freestanding' && leanTo.canopyForm === 'mono' && leanTo.parentId)) {
41
+ return null;
42
+ }
43
+ const halfSpan = leanTo.span / 2;
44
+ const cos = Math.cos(leanTo.rotation[1]);
45
+ const sin = Math.sin(leanTo.rotation[1]);
46
+ const surfaceOffset = FREESTANDING_JOINT_WALL_THICKNESS / 2;
47
+ const syntheticWall = WallNode.parse({
48
+ name: 'Freestanding canopy run frame',
49
+ parentId: leanTo.parentId,
50
+ start: [
51
+ leanTo.position[0] - halfSpan * cos - surfaceOffset * sin,
52
+ leanTo.position[2] + halfSpan * sin - surfaceOffset * cos,
53
+ ],
54
+ end: [
55
+ leanTo.position[0] + halfSpan * cos - surfaceOffset * sin,
56
+ leanTo.position[2] - halfSpan * sin - surfaceOffset * cos,
57
+ ],
58
+ height: leanTo.highEdgeHeight,
59
+ thickness: FREESTANDING_JOINT_WALL_THICKNESS,
60
+ });
61
+ return {
62
+ kind: 'freestanding',
63
+ wall: syntheticWall,
64
+ leanTo: {
65
+ ...leanTo,
66
+ parentId: syntheticWall.id,
67
+ position: [halfSpan, leanTo.position[1], surfaceOffset],
68
+ rotation: [0, 0, 0],
69
+ spanArcCenterZ: undefined,
70
+ spanArcRadius: undefined,
71
+ },
72
+ };
73
+ }
74
+ function leanToOutwardDirection(wall, leanTo, side) {
75
+ const layout = resolveLeanToLayout(leanTo);
76
+ const x = layout.roofCenterX + (side === 'left' ? -layout.roofWidth / 2 : layout.roofWidth / 2);
77
+ const frame = leanToArcFrameAtLocalX(leanTo, x);
78
+ const pose = leanToWallLocalPose(wall, leanTo, 0);
79
+ const cos = Math.cos(pose.rotationY);
80
+ const sin = Math.sin(pose.rotationY);
81
+ return [frame.normal.x * cos + frame.normal.y * sin, -frame.normal.x * sin + frame.normal.y * cos];
82
+ }
83
+ function awayFromEndDirection(wall, leanTo, side) {
84
+ const layout = resolveLeanToLayout(leanTo);
85
+ const endpointX = layout.roofCenterX + (side === 'left' ? -layout.roofWidth / 2 : layout.roofWidth / 2);
86
+ const frame = leanToArcFrameAtLocalX(leanTo, endpointX);
87
+ const pose = leanToWallLocalPose(wall, leanTo, 0);
88
+ const cos = Math.cos(pose.rotationY);
89
+ const sin = Math.sin(pose.rotationY);
90
+ const inwardSign = side === 'left' ? 1 : -1;
91
+ return [
92
+ (frame.tangent.x * cos + frame.tangent.y * sin) * inwardSign,
93
+ (-frame.tangent.x * sin + frame.tangent.y * cos) * inwardSign,
94
+ ];
95
+ }
96
+ function awayFromEndChordDirection(wall, leanTo, side) {
97
+ const endpoint = endWorldPoint(wall, leanTo, side);
98
+ const opposite = endWorldPoint(wall, leanTo, side === 'left' ? 'right' : 'left');
99
+ if (!(endpoint && opposite))
100
+ return null;
101
+ const dx = opposite[0] - endpoint[0];
102
+ const dz = opposite[1] - endpoint[1];
103
+ const length = Math.hypot(dx, dz);
104
+ return length > PLAN_TOLERANCE ? [dx / length, dz / length] : null;
105
+ }
106
+ function cornerKindFromDirections(wall, leanTo, side, candidateWall, candidate, candidateSide) {
107
+ const outward = leanToOutwardDirection(wall, leanTo, side);
108
+ const candidateOutward = leanToOutwardDirection(candidateWall, candidate, candidateSide);
109
+ const away = awayFromEndDirection(wall, leanTo, side);
110
+ const candidateAway = awayFromEndDirection(candidateWall, candidate, candidateSide);
111
+ if (!(outward && candidateOutward && away && candidateAway))
112
+ return null;
113
+ const candidateAcrossOwn = outward[0] * candidateAway[0] + outward[1] * candidateAway[1];
114
+ const ownAcrossCandidate = candidateOutward[0] * away[0] + candidateOutward[1] * away[1];
115
+ const outwardDot = outward[0] * candidateOutward[0] + outward[1] * candidateOutward[1];
116
+ const awayDot = away[0] * candidateAway[0] + away[1] * candidateAway[1];
117
+ if (outwardDot >= 1 - LINEAR_DIRECTION_TOLERANCE && awayDot <= -1 + LINEAR_DIRECTION_TOLERANCE) {
118
+ return 'linear';
119
+ }
120
+ if (candidateAcrossOwn < -PLAN_TOLERANCE && ownAcrossCandidate < -PLAN_TOLERANCE) {
121
+ return 'convex';
122
+ }
123
+ if (candidateAcrossOwn > PLAN_TOLERANCE && ownAcrossCandidate > PLAN_TOLERANCE) {
124
+ return 'concave';
125
+ }
126
+ return null;
127
+ }
128
+ function roofEndWorldPoint(wall, leanTo, side) {
129
+ const layout = resolveLeanToLayout(leanTo);
130
+ const sign = side === 'left' ? -1 : 1;
131
+ return leanToPointToWorld(wall, leanTo, layout.roofCenterX + sign * (layout.roofWidth / 2), 0);
132
+ }
133
+ function candidateRoofSideAtPoint(wall, leanTo, point) {
134
+ const left = roofEndWorldPoint(wall, leanTo, 'left');
135
+ const right = roofEndWorldPoint(wall, leanTo, 'right');
136
+ if (left && planDistance(left, point) <= LINEAR_JOIN_PLAN_TOLERANCE)
137
+ return 'left';
138
+ if (right && planDistance(right, point) <= LINEAR_JOIN_PLAN_TOLERANCE)
139
+ return 'right';
140
+ return null;
141
+ }
142
+ function resolveLinearJoint(wall, leanTo, side, candidateWall, candidate, candidateSide) {
143
+ const layout = resolveLeanToLayout(leanTo);
144
+ const candidateLayout = resolveLeanToLayout(candidate);
145
+ const sign = side === 'left' ? -1 : 1;
146
+ const candidateSign = candidateSide === 'left' ? -1 : 1;
147
+ const sideX = layout.roofCenterX + sign * (layout.roofWidth / 2);
148
+ const candidateSideX = candidateLayout.roofCenterX + candidateSign * (candidateLayout.roofWidth / 2);
149
+ const edges = roofPlanEdges(leanTo);
150
+ const candidateEdges = roofPlanEdges(candidate);
151
+ const ownBack = leanToPointToWorld(wall, leanTo, sideX, edges.back);
152
+ const ownFront = leanToPointToWorld(wall, leanTo, sideX, edges.front);
153
+ const candidateBack = leanToPointToWorld(candidateWall, candidate, candidateSideX, candidateEdges.back);
154
+ const candidateFront = leanToPointToWorld(candidateWall, candidate, candidateSideX, candidateEdges.front);
155
+ if (!(ownBack && ownFront && candidateBack && candidateFront))
156
+ return null;
157
+ if (planDistance(ownBack, candidateBack) > LINEAR_JOIN_PLAN_TOLERANCE ||
158
+ planDistance(ownFront, candidateFront) > LINEAR_JOIN_PLAN_TOLERANCE) {
159
+ return null;
160
+ }
161
+ for (const point of [ownBack, ownFront]) {
162
+ const ownHeight = leanToTopHeightAtWorld(wall, leanTo, point);
163
+ const candidateHeight = leanToTopHeightAtWorld(candidateWall, candidate, point);
164
+ if (ownHeight === null ||
165
+ candidateHeight === null ||
166
+ Math.abs(ownHeight - candidateHeight) > LINEAR_JOIN_HEIGHT_TOLERANCE) {
167
+ return null;
168
+ }
169
+ }
170
+ const ownBeam = leanToPointToWorld(wall, leanTo, sideX, layout.beamZ);
171
+ const candidateBeam = leanToPointToWorld(candidateWall, candidate, candidateSideX, candidateLayout.beamZ);
172
+ if (!(ownBeam && candidateBeam))
173
+ return null;
174
+ if (planDistance(ownBeam, candidateBeam) > LINEAR_JOIN_PLAN_TOLERANCE)
175
+ return null;
176
+ const structuralSideX = sign * (layout.span / 2);
177
+ const beamExtension = Math.max(0, sign * (sideX - structuralSideX));
178
+ return {
179
+ roofPiece: [],
180
+ seam: [
181
+ [sideX, edges.back],
182
+ [sideX, edges.front],
183
+ ],
184
+ beamExtension,
185
+ sharedPostPosition: [sideX, 0, layout.beamZ],
186
+ };
187
+ }
188
+ function cornerInteriorAngle(wall, leanTo, side, candidateWall, candidate, candidateSide) {
189
+ const away = awayFromEndDirection(wall, leanTo, side);
190
+ const candidateAway = awayFromEndDirection(candidateWall, candidate, candidateSide);
191
+ if (!(away && candidateAway))
192
+ return null;
193
+ const dot = Math.max(-1, Math.min(1, away[0] * candidateAway[0] + away[1] * candidateAway[1]));
194
+ return Math.acos(dot);
195
+ }
196
+ function isSupportedHostCorner(wall, leanTo, side, candidateWall, candidate, candidateSide) {
197
+ if (directionsFormSupportedCorner(awayFromEndDirection(wall, leanTo, side), awayFromEndDirection(candidateWall, candidate, candidateSide))) {
198
+ return true;
199
+ }
200
+ return directionsFormSupportedCorner(awayFromEndChordDirection(wall, leanTo, side), awayFromEndChordDirection(candidateWall, candidate, candidateSide));
201
+ }
202
+ function leanToPointToWorld(wall, leanTo, localX, localZ) {
203
+ const pose = leanToWallLocalPose(wall, leanTo, 0);
204
+ const point = bendLocalPoint(leanTo, localX, localZ);
205
+ const cos = Math.cos(pose.rotationY);
206
+ const sin = Math.sin(pose.rotationY);
207
+ return [
208
+ pose.position[0] + point.x * cos + point.y * sin,
209
+ pose.position[2] - point.x * sin + point.y * cos,
210
+ ];
211
+ }
212
+ function worldPointToLeanTo(wall, leanTo, point) {
213
+ const pose = leanToWallLocalPose(wall, leanTo, 0);
214
+ const dx = point[0] - pose.position[0];
215
+ const dz = point[1] - pose.position[2];
216
+ const cos = Math.cos(pose.rotationY);
217
+ const sin = Math.sin(pose.rotationY);
218
+ const bentX = dx * cos - dz * sin;
219
+ const bentZ = dx * sin + dz * cos;
220
+ if (!isCurvedLeanTo(leanTo))
221
+ return [bentX, bentZ];
222
+ const centerZ = leanTo.spanArcCenterZ;
223
+ const radialSign = -(Math.sign(centerZ) || 1);
224
+ const radial = Math.hypot(bentX, bentZ - centerZ) * radialSign;
225
+ const phi = Math.atan2(-bentX * radialSign, (bentZ - centerZ) * radialSign);
226
+ const signedRadius = (Math.sign(centerZ) || 1) * leanTo.spanArcRadius;
227
+ return [phi * signedRadius, centerZ + radial];
228
+ }
229
+ function extensionToRunIntersection(wall, leanTo, side, ownSideX, ownZ, candidateWall, candidate, candidateZ) {
230
+ const ownOrigin = leanToPointToWorld(wall, leanTo, 0, ownZ);
231
+ const ownNext = leanToPointToWorld(wall, leanTo, 1, ownZ);
232
+ const ownBoundary = leanToPointToWorld(wall, leanTo, ownSideX, ownZ);
233
+ const candidateOrigin = leanToPointToWorld(candidateWall, candidate, 0, candidateZ);
234
+ const candidateNext = leanToPointToWorld(candidateWall, candidate, 1, candidateZ);
235
+ if (!(ownOrigin && ownNext && ownBoundary && candidateOrigin && candidateNext))
236
+ return null;
237
+ const ownDirection = [ownNext[0] - ownOrigin[0], ownNext[1] - ownOrigin[1]];
238
+ const sideSign = side === 'left' ? -1 : 1;
239
+ if (isCurvedLeanTo(leanTo) && !isCurvedLeanTo(candidate)) {
240
+ const candidateDirection = [
241
+ candidateNext[0] - candidateOrigin[0],
242
+ candidateNext[1] - candidateOrigin[1],
243
+ ];
244
+ const directionLength = Math.hypot(candidateDirection[0], candidateDirection[1]);
245
+ if (directionLength <= PLAN_TOLERANCE)
246
+ return null;
247
+ const direction = [
248
+ candidateDirection[0] / directionLength,
249
+ candidateDirection[1] / directionLength,
250
+ ];
251
+ const pose = leanToWallLocalPose(wall, leanTo, 0);
252
+ const centerZ = leanTo.spanArcCenterZ;
253
+ const cos = Math.cos(pose.rotationY);
254
+ const sin = Math.sin(pose.rotationY);
255
+ const center = [
256
+ pose.position[0] + centerZ * sin,
257
+ pose.position[2] + centerZ * cos,
258
+ ];
259
+ const offset = [candidateOrigin[0] - center[0], candidateOrigin[1] - center[1]];
260
+ const projection = offset[0] * direction[0] + offset[1] * direction[1];
261
+ const radius = Math.abs(ownZ - centerZ);
262
+ const discriminant = projection * projection - (offset[0] * offset[0] + offset[1] * offset[1] - radius * radius);
263
+ if (discriminant < -PLAN_TOLERANCE)
264
+ return null;
265
+ const root = Math.sqrt(Math.max(0, discriminant));
266
+ const extensions = [-projection - root, -projection + root].flatMap((distance) => {
267
+ const intersection = [
268
+ candidateOrigin[0] + direction[0] * distance,
269
+ candidateOrigin[1] + direction[1] * distance,
270
+ ];
271
+ const local = worldPointToLeanTo(wall, leanTo, intersection);
272
+ if (!local)
273
+ return [];
274
+ const extension = sideSign * (local[0] - ownSideX);
275
+ return extension >= -PLAN_TOLERANCE ? [Math.max(0, extension)] : [];
276
+ });
277
+ return extensions.length > 0 ? Math.min(...extensions) : null;
278
+ }
279
+ if (isCurvedLeanTo(candidate) && !isCurvedLeanTo(leanTo)) {
280
+ const directionLength = Math.hypot(ownDirection[0], ownDirection[1]);
281
+ if (directionLength <= PLAN_TOLERANCE)
282
+ return null;
283
+ const direction = [
284
+ (ownDirection[0] / directionLength) * sideSign,
285
+ (ownDirection[1] / directionLength) * sideSign,
286
+ ];
287
+ const pose = leanToWallLocalPose(candidateWall, candidate, 0);
288
+ const centerZ = candidate.spanArcCenterZ;
289
+ const cos = Math.cos(pose.rotationY);
290
+ const sin = Math.sin(pose.rotationY);
291
+ const center = [
292
+ pose.position[0] + centerZ * sin,
293
+ pose.position[2] + centerZ * cos,
294
+ ];
295
+ const offset = [ownBoundary[0] - center[0], ownBoundary[1] - center[1]];
296
+ const projection = offset[0] * direction[0] + offset[1] * direction[1];
297
+ const radius = Math.abs(candidateZ - centerZ);
298
+ const discriminant = projection * projection - (offset[0] * offset[0] + offset[1] * offset[1] - radius * radius);
299
+ if (discriminant < -PLAN_TOLERANCE)
300
+ return null;
301
+ const root = Math.sqrt(Math.max(0, discriminant));
302
+ const intersections = [-projection - root, -projection + root].filter((distance) => distance >= -PLAN_TOLERANCE);
303
+ return intersections.length > 0 ? Math.max(0, Math.min(...intersections)) : null;
304
+ }
305
+ const candidateDirection = [
306
+ candidateNext[0] - candidateOrigin[0],
307
+ candidateNext[1] - candidateOrigin[1],
308
+ ];
309
+ const cross = ownDirection[0] * candidateDirection[1] - ownDirection[1] * candidateDirection[0];
310
+ if (Math.abs(cross) <= PLAN_TOLERANCE)
311
+ return null;
312
+ const deltaX = candidateOrigin[0] - ownOrigin[0];
313
+ const deltaZ = candidateOrigin[1] - ownOrigin[1];
314
+ const alongOwn = (deltaX * candidateDirection[1] - deltaZ * candidateDirection[0]) / cross;
315
+ const intersection = [
316
+ ownOrigin[0] + ownDirection[0] * alongOwn,
317
+ ownOrigin[1] + ownDirection[1] * alongOwn,
318
+ ];
319
+ return (sideSign *
320
+ ((intersection[0] - ownBoundary[0]) * ownDirection[0] +
321
+ (intersection[1] - ownBoundary[1]) * ownDirection[1]));
322
+ }
323
+ function leanToTopHeightAtWorld(wall, leanTo, point) {
324
+ const local = worldPointToLeanTo(wall, leanTo, point);
325
+ if (!local)
326
+ return null;
327
+ const layout = resolveLeanToLayout(leanTo);
328
+ return leanTo.position[1] + layout.highEdgeHeight - local[1] * Math.tan(layout.pitchRadians);
329
+ }
330
+ function roofPlanEdges(leanTo) {
331
+ const layout = resolveLeanToLayout(leanTo);
332
+ const depth = layout.roofRun + WALL_CONNECTION_OVERLAP;
333
+ const centerZ = depth / 2 - Math.max(0, leanTo.highOverhang) - WALL_CONNECTION_TRIM - WALL_CONNECTION_OVERLAP;
334
+ return {
335
+ back: centerZ - depth / 2 + (leanTo.highOverhang > 0 ? 0 : WALL_CONNECTION_TRIM),
336
+ front: centerZ + depth / 2,
337
+ };
338
+ }
339
+ function gutterAwayFromJointDirection(wall, leanTo, side, extension) {
340
+ if (Math.abs(extension) <= PLAN_TOLERANCE)
341
+ return awayFromEndDirection(wall, leanTo, side);
342
+ const layout = resolveLeanToLayout(leanTo);
343
+ const sideSign = side === 'left' ? -1 : 1;
344
+ const baseX = layout.roofCenterX + sideSign * (layout.roofWidth / 2);
345
+ const front = roofPlanEdges(leanTo).front;
346
+ const base = leanToPointToWorld(wall, leanTo, baseX, front);
347
+ const end = leanToPointToWorld(wall, leanTo, baseX + sideSign * extension, front);
348
+ if (!(base && end))
349
+ return null;
350
+ const dx = base[0] - end[0];
351
+ const dz = base[1] - end[1];
352
+ const length = Math.hypot(dx, dz);
353
+ return length > PLAN_TOLERANCE ? [dx / length, dz / length] : null;
354
+ }
355
+ function endWorldPoint(wall, leanTo, side) {
356
+ const layout = resolveLeanToLayout(leanTo);
357
+ const x = side === 'left' ? -layout.span / 2 : layout.span / 2;
358
+ return leanToPointToWorld(wall, leanTo, x, 0);
359
+ }
360
+ function candidateSideAtPoint(wall, leanTo, point, tolerance) {
361
+ const left = endWorldPoint(wall, leanTo, 'left');
362
+ const right = endWorldPoint(wall, leanTo, 'right');
363
+ if (left && planDistance(left, point) <= tolerance)
364
+ return 'left';
365
+ if (right && planDistance(right, point) <= tolerance)
366
+ return 'right';
367
+ return null;
368
+ }
369
+ function clipToRetainedRoofSide(polygon, heightDelta, retainedSign) {
370
+ const clipped = [];
371
+ for (let index = 0; index < polygon.length; index++) {
372
+ const current = polygon[index];
373
+ const next = polygon[(index + 1) % polygon.length];
374
+ const currentDelta = heightDelta(current);
375
+ const nextDelta = heightDelta(next);
376
+ if (currentDelta === null || nextDelta === null)
377
+ return [];
378
+ const currentInside = currentDelta * retainedSign >= -PLAN_TOLERANCE;
379
+ const nextInside = nextDelta * retainedSign >= -PLAN_TOLERANCE;
380
+ if (currentInside)
381
+ clipped.push([current[0], current[1]]);
382
+ if (currentInside === nextInside)
383
+ continue;
384
+ const ratio = currentDelta / (currentDelta - nextDelta);
385
+ clipped.push([
386
+ current[0] + (next[0] - current[0]) * ratio,
387
+ current[1] + (next[1] - current[1]) * ratio,
388
+ ]);
389
+ }
390
+ return clipped.filter((point, index) => index === 0 || planDistance(point, clipped[index - 1]) > PLAN_TOLERANCE);
391
+ }
392
+ function roofExtensionBand(leanTo, side, extension) {
393
+ const layout = resolveLeanToLayout(leanTo);
394
+ const edges = roofPlanEdges(leanTo);
395
+ const sideSign = side === 'left' ? -1 : 1;
396
+ const originalSideX = layout.roofCenterX + sideSign * (layout.roofWidth / 2);
397
+ const extendedSideX = originalSideX + sideSign * extension;
398
+ return [
399
+ [originalSideX, edges.back],
400
+ [extendedSideX, edges.back],
401
+ [extendedSideX, edges.front],
402
+ [originalSideX, edges.front],
403
+ ];
404
+ }
405
+ function roofBasePolygon(leanTo) {
406
+ const layout = resolveLeanToLayout(leanTo);
407
+ const edges = roofPlanEdges(leanTo);
408
+ return [
409
+ [layout.roofCenterX - layout.roofWidth / 2, edges.back],
410
+ [layout.roofCenterX + layout.roofWidth / 2, edges.back],
411
+ [layout.roofCenterX + layout.roofWidth / 2, edges.front],
412
+ [layout.roofCenterX - layout.roofWidth / 2, edges.front],
413
+ ];
414
+ }
415
+ function polygonSignedArea(polygon) {
416
+ let area = 0;
417
+ for (let index = 0; index < polygon.length; index++) {
418
+ const current = polygon[index];
419
+ const next = polygon[(index + 1) % polygon.length];
420
+ area += current[0] * next[1] - next[0] * current[1];
421
+ }
422
+ return area / 2;
423
+ }
424
+ function pointInPlanPolygon(point, polygon) {
425
+ let inside = false;
426
+ for (let index = 0, previous = polygon.length - 1; index < polygon.length; previous = index++) {
427
+ const current = polygon[index];
428
+ const prior = polygon[previous];
429
+ const edgeX = current[0] - prior[0];
430
+ const edgeZ = current[1] - prior[1];
431
+ const cross = (point[0] - prior[0]) * edgeZ - (point[1] - prior[1]) * edgeX;
432
+ const dot = (point[0] - prior[0]) * (point[0] - current[0]) +
433
+ (point[1] - prior[1]) * (point[1] - current[1]);
434
+ if (Math.abs(cross) <= PLAN_TOLERANCE && dot <= PLAN_TOLERANCE)
435
+ return true;
436
+ if (current[1] > point[1] !== prior[1] > point[1] &&
437
+ point[0] <
438
+ ((prior[0] - current[0]) * (point[1] - current[1])) / (prior[1] - current[1]) + current[0]) {
439
+ inside = !inside;
440
+ }
441
+ }
442
+ return inside;
443
+ }
444
+ function resolveFramingRetainedSide(seam, pieces) {
445
+ if (!seam || pieces.length === 0)
446
+ return undefined;
447
+ const midpoint = [(seam[0][0] + seam[1][0]) / 2, (seam[0][1] + seam[1][1]) / 2];
448
+ const probeDistance = 0.01;
449
+ const contains = (point) => pieces.some((polygon) => pointInPlanPolygon(point, polygon));
450
+ const front = contains([midpoint[0], midpoint[1] + probeDistance]);
451
+ const back = contains([midpoint[0], midpoint[1] - probeDistance]);
452
+ if (front === back)
453
+ return undefined;
454
+ return front ? 'front' : 'back';
455
+ }
456
+ function connectedPlanPolygonComponent(polygons, probe) {
457
+ const connected = new Set();
458
+ const queue = polygons.flatMap((polygon, index) => pointInPlanPolygon(probe, polygon) ? [index] : []);
459
+ for (const index of queue)
460
+ connected.add(index);
461
+ while (queue.length > 0) {
462
+ const currentIndex = queue.shift();
463
+ const current = polygons[currentIndex];
464
+ for (let candidateIndex = 0; candidateIndex < polygons.length; candidateIndex++) {
465
+ if (connected.has(candidateIndex))
466
+ continue;
467
+ const candidate = polygons[candidateIndex];
468
+ const touches = current.some((point) => pointInPlanPolygon(point, candidate)) ||
469
+ candidate.some((point) => pointInPlanPolygon(point, current));
470
+ if (!touches)
471
+ continue;
472
+ connected.add(candidateIndex);
473
+ queue.push(candidateIndex);
474
+ }
475
+ }
476
+ return polygons.filter((_, index) => connected.has(index));
477
+ }
478
+ function planSegmentsShareLength(leftStart, leftEnd, rightStart, rightEnd) {
479
+ const leftX = leftEnd[0] - leftStart[0];
480
+ const leftZ = leftEnd[1] - leftStart[1];
481
+ const leftLength = Math.hypot(leftX, leftZ);
482
+ if (leftLength <= PLAN_TOLERANCE)
483
+ return false;
484
+ const cross = (x, z) => leftX * z - leftZ * x;
485
+ if (Math.abs(cross(rightStart[0] - leftStart[0], rightStart[1] - leftStart[1])) >
486
+ PLAN_TOLERANCE * leftLength ||
487
+ Math.abs(cross(rightEnd[0] - leftStart[0], rightEnd[1] - leftStart[1])) >
488
+ PLAN_TOLERANCE * leftLength) {
489
+ return false;
490
+ }
491
+ const project = (point) => ((point[0] - leftStart[0]) * leftX + (point[1] - leftStart[1]) * leftZ) / leftLength;
492
+ const rightStartDistance = project(rightStart);
493
+ const rightEndDistance = project(rightEnd);
494
+ const overlapStart = Math.max(0, Math.min(rightStartDistance, rightEndDistance));
495
+ const overlapEnd = Math.min(leftLength, Math.max(rightStartDistance, rightEndDistance));
496
+ return overlapEnd - overlapStart > PLAN_TOLERANCE;
497
+ }
498
+ function polygonsSharePlanEdge(left, right) {
499
+ return left.some((leftStart, leftIndex) => {
500
+ const leftEnd = left[(leftIndex + 1) % left.length];
501
+ return right.some((rightStart, rightIndex) => planSegmentsShareLength(leftStart, leftEnd, rightStart, right[(rightIndex + 1) % right.length]));
502
+ });
503
+ }
504
+ function edgeConnectedPlanPolygonComponent(polygons, anchor) {
505
+ return edgeConnectedPlanPolygonComponents(polygons, [anchor]);
506
+ }
507
+ function edgeConnectedPlanPolygonComponents(polygons, anchors) {
508
+ const connected = new Set();
509
+ const queue = polygons.flatMap((polygon, index) => anchors.some((anchor) => polygonsSharePlanEdge(anchor, polygon)) ? [index] : []);
510
+ for (const index of queue)
511
+ connected.add(index);
512
+ while (queue.length > 0) {
513
+ const currentIndex = queue.shift();
514
+ for (let candidateIndex = 0; candidateIndex < polygons.length; candidateIndex++) {
515
+ if (connected.has(candidateIndex) ||
516
+ !polygonsSharePlanEdge(polygons[currentIndex], polygons[candidateIndex])) {
517
+ continue;
518
+ }
519
+ connected.add(candidateIndex);
520
+ queue.push(candidateIndex);
521
+ }
522
+ }
523
+ return polygons.filter((_, index) => connected.has(index));
524
+ }
525
+ function intersectConvexPolygons(subject, clip) {
526
+ let result = subject.map((point) => [point[0], point[1]]);
527
+ const orientation = Math.sign(polygonSignedArea(clip)) || 1;
528
+ for (let clipIndex = 0; clipIndex < clip.length && result.length > 0; clipIndex++) {
529
+ const edgeStart = clip[clipIndex];
530
+ const edgeEnd = clip[(clipIndex + 1) % clip.length];
531
+ const input = result;
532
+ result = [];
533
+ const edgeSide = (point) => orientation *
534
+ ((edgeEnd[0] - edgeStart[0]) * (point[1] - edgeStart[1]) -
535
+ (edgeEnd[1] - edgeStart[1]) * (point[0] - edgeStart[0]));
536
+ for (let index = 0; index < input.length; index++) {
537
+ const current = input[index];
538
+ const next = input[(index + 1) % input.length];
539
+ const currentSide = edgeSide(current);
540
+ const nextSide = edgeSide(next);
541
+ const currentInside = currentSide >= -PLAN_TOLERANCE;
542
+ const nextInside = nextSide >= -PLAN_TOLERANCE;
543
+ if (currentInside)
544
+ result.push(current);
545
+ if (currentInside === nextInside)
546
+ continue;
547
+ const ratio = currentSide / (currentSide - nextSide);
548
+ result.push([
549
+ current[0] + (next[0] - current[0]) * ratio,
550
+ current[1] + (next[1] - current[1]) * ratio,
551
+ ]);
552
+ }
553
+ }
554
+ return result;
555
+ }
556
+ function clipPolygonToHalfPlane(polygon, edgeStart, edgeEnd, orientation, keepInside) {
557
+ const clipped = [];
558
+ const edgeSide = (point) => orientation *
559
+ ((edgeEnd[0] - edgeStart[0]) * (point[1] - edgeStart[1]) -
560
+ (edgeEnd[1] - edgeStart[1]) * (point[0] - edgeStart[0]));
561
+ for (let index = 0; index < polygon.length; index++) {
562
+ const current = polygon[index];
563
+ const next = polygon[(index + 1) % polygon.length];
564
+ const currentSide = edgeSide(current);
565
+ const nextSide = edgeSide(next);
566
+ const currentInside = keepInside
567
+ ? currentSide >= -PLAN_TOLERANCE
568
+ : currentSide <= PLAN_TOLERANCE;
569
+ const nextInside = keepInside ? nextSide >= -PLAN_TOLERANCE : nextSide <= PLAN_TOLERANCE;
570
+ if (currentInside)
571
+ clipped.push([current[0], current[1]]);
572
+ if (currentInside === nextInside)
573
+ continue;
574
+ const ratio = currentSide / (currentSide - nextSide);
575
+ clipped.push([
576
+ current[0] + (next[0] - current[0]) * ratio,
577
+ current[1] + (next[1] - current[1]) * ratio,
578
+ ]);
579
+ }
580
+ return clipped.filter((point, index) => index === 0 || planDistance(point, clipped[index - 1]) > PLAN_TOLERANCE);
581
+ }
582
+ function subtractConvexPolygon(subject, clip) {
583
+ if (subject.length < 3)
584
+ return [];
585
+ if (clip.length < 3)
586
+ return [subject.map((point) => [point[0], point[1]])];
587
+ const orientation = Math.sign(polygonSignedArea(clip)) || 1;
588
+ let remaining = subject.map((point) => [point[0], point[1]]);
589
+ const outside = [];
590
+ for (let index = 0; index < clip.length && remaining.length >= 3; index++) {
591
+ const edgeStart = clip[index];
592
+ const edgeEnd = clip[(index + 1) % clip.length];
593
+ const fragment = clipPolygonToHalfPlane(remaining, edgeStart, edgeEnd, orientation, false);
594
+ if (fragment.length >= 3 && Math.abs(polygonSignedArea(fragment)) > PLAN_TOLERANCE) {
595
+ outside.push(fragment);
596
+ }
597
+ remaining = clipPolygonToHalfPlane(remaining, edgeStart, edgeEnd, orientation, true);
598
+ }
599
+ return outside;
600
+ }
601
+ function roofWorldFacets(wall, leanTo) {
602
+ const layout = resolveLeanToLayout(leanTo);
603
+ const edges = roofPlanEdges(leanTo);
604
+ const facetCount = isCurvedLeanTo(leanTo)
605
+ ? Math.max(4, Math.min(32, Math.ceil(layout.roofWidth / 0.4)))
606
+ : 1;
607
+ const leftX = layout.roofCenterX - layout.roofWidth / 2;
608
+ const facetWidth = layout.roofWidth / facetCount;
609
+ return Array.from({ length: facetCount }, (_, index) => {
610
+ const minX = leftX + index * facetWidth;
611
+ const maxX = index === facetCount - 1 ? leftX + layout.roofWidth : minX + facetWidth;
612
+ return [
613
+ leanToPointToWorld(wall, leanTo, minX, edges.back),
614
+ leanToPointToWorld(wall, leanTo, maxX, edges.back),
615
+ leanToPointToWorld(wall, leanTo, maxX, edges.front),
616
+ leanToPointToWorld(wall, leanTo, minX, edges.front),
617
+ ].flatMap((point) => (point ? [point] : []));
618
+ }).filter((polygon) => polygon.length >= 3);
619
+ }
620
+ function sharedRoofSeam(wall, leanTo, side, extension, candidateWall, candidate, candidateSide, candidateExtension, kind) {
621
+ const ownPolygon = kind === 'convex' ? roofExtensionBand(leanTo, side, extension) : roofBasePolygon(leanTo);
622
+ const ownBand = ownPolygon.flatMap((point) => {
623
+ const world = leanToPointToWorld(wall, leanTo, point[0], point[1]);
624
+ return world ? [world] : [];
625
+ });
626
+ const candidatePolygon = kind === 'convex'
627
+ ? roofExtensionBand(candidate, candidateSide, candidateExtension)
628
+ : roofBasePolygon(candidate);
629
+ const candidateBand = candidatePolygon.flatMap((point) => {
630
+ const world = leanToPointToWorld(candidateWall, candidate, point[0], point[1]);
631
+ return world ? [world] : [];
632
+ });
633
+ if (ownBand.length < 3 || candidateBand.length < 3)
634
+ return null;
635
+ const overlap = intersectConvexPolygons(ownBand, candidateBand);
636
+ const seamWorld = [];
637
+ const heightDelta = (point) => {
638
+ const ownHeight = leanToTopHeightAtWorld(wall, leanTo, point);
639
+ const candidateHeight = leanToTopHeightAtWorld(candidateWall, candidate, point);
640
+ return ownHeight === null || candidateHeight === null ? null : ownHeight - candidateHeight;
641
+ };
642
+ for (let index = 0; index < overlap.length; index++) {
643
+ const current = overlap[index];
644
+ const next = overlap[(index + 1) % overlap.length];
645
+ const currentDelta = heightDelta(current);
646
+ const nextDelta = heightDelta(next);
647
+ if (currentDelta === null || nextDelta === null)
648
+ return null;
649
+ if (Math.abs(currentDelta) <= PLAN_TOLERANCE)
650
+ seamWorld.push(current);
651
+ if (currentDelta * nextDelta >= 0)
652
+ continue;
653
+ const ratio = currentDelta / (currentDelta - nextDelta);
654
+ seamWorld.push([
655
+ current[0] + (next[0] - current[0]) * ratio,
656
+ current[1] + (next[1] - current[1]) * ratio,
657
+ ]);
658
+ }
659
+ const unique = seamWorld.filter((point, index) => seamWorld.findIndex((candidatePoint) => planDistance(point, candidatePoint) <= PLAN_TOLERANCE) === index);
660
+ if (unique.length < 2)
661
+ return null;
662
+ let endpoints = [unique[0], unique[1]];
663
+ for (const first of unique) {
664
+ for (const second of unique) {
665
+ if (planDistance(first, second) > planDistance(endpoints[0], endpoints[1])) {
666
+ endpoints = [first, second];
667
+ }
668
+ }
669
+ }
670
+ const localized = endpoints.map((point) => worldPointToLeanTo(wall, leanTo, point));
671
+ return localized[0] && localized[1] ? [localized[0], localized[1]] : null;
672
+ }
673
+ function resolveConcaveRoofPiece(leanTo, wall, side, candidate, candidateWall) {
674
+ const layout = resolveLeanToLayout(leanTo);
675
+ const edges = roofPlanEdges(leanTo);
676
+ const sideSign = side === 'left' ? -1 : 1;
677
+ const originalSideX = layout.roofCenterX + sideSign * (layout.roofWidth / 2);
678
+ const heightDelta = (point) => {
679
+ const worldPoint = leanToPointToWorld(wall, leanTo, point[0], point[1]);
680
+ if (!worldPoint)
681
+ return null;
682
+ const ownHeight = leanToTopHeightAtWorld(wall, leanTo, worldPoint);
683
+ const candidateHeight = leanToTopHeightAtWorld(candidateWall, candidate, worldPoint);
684
+ return ownHeight === null || candidateHeight === null ? null : ownHeight - candidateHeight;
685
+ };
686
+ const probeDelta = heightDelta([
687
+ originalSideX - sideSign * Math.min(0.1, layout.roofWidth / 4),
688
+ edges.back,
689
+ ]);
690
+ if (probeDelta === null || Math.abs(probeDelta) <= PLAN_TOLERANCE) {
691
+ return { piece: [], seam: null };
692
+ }
693
+ const base = roofBasePolygon(leanTo);
694
+ const piece = clipToRetainedRoofSide(base, heightDelta, Math.sign(probeDelta));
695
+ const seamPoints = [];
696
+ for (let index = 0; index < base.length; index++) {
697
+ const current = base[index];
698
+ const next = base[(index + 1) % base.length];
699
+ const currentDelta = heightDelta(current);
700
+ const nextDelta = heightDelta(next);
701
+ if (currentDelta === null || nextDelta === null)
702
+ continue;
703
+ if (Math.abs(currentDelta) <= PLAN_TOLERANCE)
704
+ seamPoints.push(current);
705
+ if (currentDelta * nextDelta >= 0)
706
+ continue;
707
+ const ratio = currentDelta / (currentDelta - nextDelta);
708
+ seamPoints.push([
709
+ current[0] + (next[0] - current[0]) * ratio,
710
+ current[1] + (next[1] - current[1]) * ratio,
711
+ ]);
712
+ }
713
+ const uniqueSeam = seamPoints.filter((point, index) => seamPoints.findIndex((other) => planDistance(point, other) <= PLAN_TOLERANCE) === index);
714
+ return {
715
+ piece,
716
+ seam: uniqueSeam.length >= 2 ? [uniqueSeam[0], uniqueSeam[1]] : null,
717
+ };
718
+ }
719
+ function resolveCurvedStraightConcaveRoofPiece(leanTo, wall, side, candidate, candidateWall, candidateSide) {
720
+ const ownCurved = isCurvedLeanTo(leanTo);
721
+ const candidateCurved = isCurvedLeanTo(candidate);
722
+ if (ownCurved === candidateCurved)
723
+ return null;
724
+ const curved = ownCurved ? leanTo : candidate;
725
+ const curvedWall = ownCurved ? wall : candidateWall;
726
+ const curvedSide = ownCurved ? side : candidateSide;
727
+ const straight = ownCurved ? candidate : leanTo;
728
+ const straightWall = ownCurved ? candidateWall : wall;
729
+ const curvedLayout = resolveLeanToLayout(curved);
730
+ const curvedEdges = roofPlanEdges(curved);
731
+ const curvedSideSign = curvedSide === 'left' ? -1 : 1;
732
+ const curvedSideX = curvedLayout.roofCenterX + curvedSideSign * (curvedLayout.roofWidth / 2);
733
+ const probeWorld = leanToPointToWorld(curvedWall, curved, curvedSideX - curvedSideSign * Math.min(0.1, curvedLayout.roofWidth / 4), curvedEdges.back);
734
+ if (!probeWorld)
735
+ return null;
736
+ const worldHeightDelta = (point) => {
737
+ const curvedHeight = leanToTopHeightAtWorld(curvedWall, curved, point);
738
+ const straightHeight = leanToTopHeightAtWorld(straightWall, straight, point);
739
+ return curvedHeight === null || straightHeight === null ? null : curvedHeight - straightHeight;
740
+ };
741
+ const probeDelta = worldHeightDelta(probeWorld);
742
+ if (probeDelta === null || Math.abs(probeDelta) <= PLAN_TOLERANCE)
743
+ return null;
744
+ const curvedRetainedSign = Math.sign(probeDelta);
745
+ const straightSide = ownCurved ? candidateSide : side;
746
+ const straightLayout = resolveLeanToLayout(straight);
747
+ const straightEdges = roofPlanEdges(straight);
748
+ const straightSideSign = straightSide === 'left' ? -1 : 1;
749
+ const straightProbe = leanToPointToWorld(straightWall, straight, straightLayout.roofCenterX -
750
+ straightSideSign *
751
+ (straightLayout.roofWidth / 2 - Math.min(0.1, straightLayout.roofWidth / 4)), (straightEdges.back + straightEdges.front) / 2);
752
+ if (!straightProbe)
753
+ return null;
754
+ // The equal-height cut only divides the shared footprint. Applying it to the
755
+ // whole curved band removes roof area that the straight neighbor never covers.
756
+ const curvedFacets = roofWorldFacets(curvedWall, curved);
757
+ const straightBase = roofWorldFacets(straightWall, straight)[0];
758
+ if (!straightBase)
759
+ return null;
760
+ const overlaps = curvedFacets
761
+ .map((facet) => intersectConvexPolygons(facet, straightBase))
762
+ .filter((polygon) => polygon.length >= 3);
763
+ if (overlaps.length === 0)
764
+ return null;
765
+ let retainedWorld;
766
+ if (ownCurved) {
767
+ retainedWorld = curvedFacets.flatMap((facet) => {
768
+ const overlap = intersectConvexPolygons(facet, straightBase);
769
+ const exclusive = subtractConvexPolygon(facet, straightBase);
770
+ const retainedOverlap = clipToRetainedRoofSide(overlap, worldHeightDelta, curvedRetainedSign);
771
+ return [...exclusive, ...(retainedOverlap.length >= 3 ? [retainedOverlap] : [])];
772
+ });
773
+ }
774
+ else {
775
+ let exclusive = [straightBase];
776
+ for (const facet of curvedFacets) {
777
+ exclusive = exclusive.flatMap((polygon) => subtractConvexPolygon(polygon, facet));
778
+ }
779
+ const connectedExclusive = connectedPlanPolygonComponent(exclusive, straightProbe);
780
+ if (connectedExclusive.length > 0)
781
+ exclusive = connectedExclusive;
782
+ const retainedOverlap = overlaps.flatMap((overlap) => {
783
+ const piece = clipToRetainedRoofSide(overlap, worldHeightDelta, -curvedRetainedSign);
784
+ return piece.length >= 3 ? [piece] : [];
785
+ });
786
+ retainedWorld = [...exclusive, ...retainedOverlap];
787
+ }
788
+ const seamWorld = [];
789
+ for (const overlap of overlaps) {
790
+ for (let index = 0; index < overlap.length; index++) {
791
+ const current = overlap[index];
792
+ const next = overlap[(index + 1) % overlap.length];
793
+ const currentDelta = worldHeightDelta(current);
794
+ const nextDelta = worldHeightDelta(next);
795
+ if (currentDelta === null || nextDelta === null)
796
+ continue;
797
+ if (Math.abs(currentDelta) <= PLAN_TOLERANCE)
798
+ seamWorld.push(current);
799
+ if (currentDelta * nextDelta >= 0)
800
+ continue;
801
+ const ratio = currentDelta / (currentDelta - nextDelta);
802
+ seamWorld.push([
803
+ current[0] + (next[0] - current[0]) * ratio,
804
+ current[1] + (next[1] - current[1]) * ratio,
805
+ ]);
806
+ }
807
+ }
808
+ const uniqueSeam = seamWorld.filter((point, index) => seamWorld.findIndex((candidatePoint) => planDistance(point, candidatePoint) <= PLAN_TOLERANCE) === index);
809
+ let seamEndpoints = null;
810
+ for (const first of uniqueSeam) {
811
+ for (const second of uniqueSeam) {
812
+ if (!seamEndpoints || planDistance(first, second) > planDistance(...seamEndpoints)) {
813
+ seamEndpoints = [first, second];
814
+ }
815
+ }
816
+ }
817
+ const pieces = retainedWorld.flatMap((polygon) => {
818
+ const localized = polygon.map((point) => worldPointToLeanTo(wall, leanTo, point));
819
+ if (localized.some((point) => !point))
820
+ return [];
821
+ const piece = localized;
822
+ return piece.length >= 3 && Math.abs(polygonSignedArea(piece)) > PLAN_TOLERANCE ? [piece] : [];
823
+ });
824
+ const localizedSeam = seamEndpoints?.map((point) => worldPointToLeanTo(wall, leanTo, point));
825
+ const seam = localizedSeam?.[0] && localizedSeam[1]
826
+ ? [localizedSeam[0], localizedSeam[1]]
827
+ : null;
828
+ if (pieces.length === 0 || !seam)
829
+ return null;
830
+ return { piece: pieces[0], pieces, seam };
831
+ }
832
+ export function applyLeanToCornerRoofPieces(base, joints) {
833
+ let retained = [base];
834
+ const additions = [];
835
+ let shouldUnionPieces = false;
836
+ for (const side of ['left', 'right']) {
837
+ const joint = joints[side];
838
+ if (!joint || joint.roofPiece.length < 3)
839
+ continue;
840
+ if (joint.kind === 'concave') {
841
+ const clips = joint.roofPieces ?? [joint.roofPiece];
842
+ shouldUnionPieces ||= joint.mergeRoofPieces === true;
843
+ retained = retained.flatMap((subject) => clips.flatMap((clip) => {
844
+ const intersection = intersectConvexPolygons(subject, clip);
845
+ return intersection.length >= 3 &&
846
+ Math.abs(polygonSignedArea(intersection)) > PLAN_TOLERANCE
847
+ ? [intersection]
848
+ : [];
849
+ }));
850
+ }
851
+ else {
852
+ const roofClips = joint.roofPieces;
853
+ if (roofClips) {
854
+ shouldUnionPieces ||= joint.mergeRoofPieces === true;
855
+ retained = retained.flatMap((subject) => roofClips.flatMap((clip) => {
856
+ const intersection = intersectConvexPolygons(subject, clip);
857
+ return intersection.length >= 3 &&
858
+ Math.abs(polygonSignedArea(intersection)) > PLAN_TOLERANCE
859
+ ? [intersection]
860
+ : [];
861
+ }));
862
+ }
863
+ additions.push(...(joint.roofAdditionPieces ?? [joint.roofPiece]));
864
+ }
865
+ }
866
+ const pieces = [...retained, ...additions];
867
+ return shouldUnionPieces ? unionPolygons(pieces) : pieces;
868
+ }
869
+ function resolveRoofPiece(leanTo, wall, side, extension, candidate, candidateWall) {
870
+ const layout = resolveLeanToLayout(leanTo);
871
+ const edges = roofPlanEdges(leanTo);
872
+ const sideSign = side === 'left' ? -1 : 1;
873
+ const originalSideX = layout.roofCenterX + sideSign * (layout.roofWidth / 2);
874
+ const heightDelta = (point) => {
875
+ const worldPoint = leanToPointToWorld(wall, leanTo, point[0], point[1]);
876
+ if (!worldPoint)
877
+ return null;
878
+ const ownHeight = leanToTopHeightAtWorld(wall, leanTo, worldPoint);
879
+ const candidateHeight = leanToTopHeightAtWorld(candidateWall, candidate, worldPoint);
880
+ return ownHeight === null || candidateHeight === null ? null : ownHeight - candidateHeight;
881
+ };
882
+ const probeDelta = heightDelta([
883
+ originalSideX - sideSign * Math.min(0.1, layout.roofWidth / 4),
884
+ (edges.back + edges.front) / 2,
885
+ ]);
886
+ if (probeDelta === null || Math.abs(probeDelta) <= PLAN_TOLERANCE) {
887
+ return { piece: [], seam: null };
888
+ }
889
+ const band = roofExtensionBand(leanTo, side, extension);
890
+ const piece = clipToRetainedRoofSide(band, heightDelta, Math.sign(probeDelta));
891
+ const seamPoints = [];
892
+ for (let index = 0; index < band.length; index++) {
893
+ const current = band[index];
894
+ const next = band[(index + 1) % band.length];
895
+ const currentDelta = heightDelta(current);
896
+ const nextDelta = heightDelta(next);
897
+ if (currentDelta === null || nextDelta === null)
898
+ continue;
899
+ if (Math.abs(currentDelta) <= PLAN_TOLERANCE)
900
+ seamPoints.push(current);
901
+ if (currentDelta * nextDelta >= 0)
902
+ continue;
903
+ const ratio = currentDelta / (currentDelta - nextDelta);
904
+ seamPoints.push([
905
+ current[0] + (next[0] - current[0]) * ratio,
906
+ current[1] + (next[1] - current[1]) * ratio,
907
+ ]);
908
+ }
909
+ const uniqueSeam = seamPoints.filter((point, index) => seamPoints.findIndex((other) => planDistance(point, other) <= PLAN_TOLERANCE) === index);
910
+ return {
911
+ piece,
912
+ seam: uniqueSeam.length >= 2 ? [uniqueSeam[0], uniqueSeam[1]] : null,
913
+ };
914
+ }
915
+ function roofExtendedBasePolygon(leanTo, side, extension) {
916
+ const polygon = roofBasePolygon(leanTo);
917
+ const layout = resolveLeanToLayout(leanTo);
918
+ const sideSign = side === 'left' ? -1 : 1;
919
+ const extendedSideX = layout.roofCenterX + sideSign * (layout.roofWidth / 2 + extension);
920
+ const sideIndices = side === 'left' ? [0, 3] : [1, 2];
921
+ for (const index of sideIndices)
922
+ polygon[index][0] = extendedSideX;
923
+ return polygon;
924
+ }
925
+ function resolveFreestandingRoofPartition(leanTo, wall, side, kind, extension, candidate, candidateWall, candidateSide, candidateExtension, trimConcaveCross) {
926
+ const layout = resolveLeanToLayout(leanTo);
927
+ const edges = roofPlanEdges(leanTo);
928
+ const sideSign = side === 'left' ? -1 : 1;
929
+ const structuralSideX = sideSign * (layout.span / 2);
930
+ const originalSideX = layout.roofCenterX + sideSign * (layout.roofWidth / 2);
931
+ const heightDelta = (point) => {
932
+ const worldPoint = leanToPointToWorld(wall, leanTo, point[0], point[1]);
933
+ if (!worldPoint)
934
+ return null;
935
+ const ownHeight = leanToTopHeightAtWorld(wall, leanTo, worldPoint);
936
+ const candidateHeight = leanToTopHeightAtWorld(candidateWall, candidate, worldPoint);
937
+ return ownHeight === null || candidateHeight === null ? null : ownHeight - candidateHeight;
938
+ };
939
+ const structuralProbeDelta = heightDelta([
940
+ structuralSideX - sideSign * Math.min(0.1, layout.span / 4),
941
+ (edges.back + edges.front) / 2,
942
+ ]);
943
+ const probeDelta = structuralProbeDelta === null || Math.abs(structuralProbeDelta) <= PLAN_TOLERANCE
944
+ ? heightDelta([
945
+ originalSideX - sideSign * Math.min(0.1, layout.roofWidth / 4),
946
+ (edges.back + edges.front) / 2,
947
+ ])
948
+ : structuralProbeDelta;
949
+ if (probeDelta === null || Math.abs(probeDelta) <= PLAN_TOLERANCE)
950
+ return null;
951
+ const candidatePolygon = (kind === 'convex'
952
+ ? roofExtendedBasePolygon(candidate, candidateSide, candidateExtension)
953
+ : roofBasePolygon(candidate)).flatMap((point) => {
954
+ const worldPoint = leanToPointToWorld(candidateWall, candidate, point[0], point[1]);
955
+ const localized = worldPoint && worldPointToLeanTo(wall, leanTo, worldPoint);
956
+ return localized ? [localized] : [];
957
+ });
958
+ if (candidatePolygon.length < 3)
959
+ return null;
960
+ const partition = (polygon) => {
961
+ const overlap = intersectConvexPolygons(polygon, candidatePolygon);
962
+ const exclusive = subtractConvexPolygon(polygon, candidatePolygon);
963
+ const retainedOverlap = clipToRetainedRoofSide(overlap, heightDelta, (trimConcaveCross ? -1 : 1) * Math.sign(probeDelta));
964
+ const pieces = [...exclusive, retainedOverlap].filter((piece) => piece.length >= 3 && Math.abs(polygonSignedArea(piece)) > PLAN_TOLERANCE);
965
+ return trimConcaveCross ? edgeConnectedPlanPolygonComponent(pieces, retainedOverlap) : pieces;
966
+ };
967
+ const basePieces = partition(roofBasePolygon(leanTo));
968
+ if (basePieces.length === 0)
969
+ return null;
970
+ if (kind === 'concave')
971
+ return { basePieces };
972
+ const additionPieces = edgeConnectedPlanPolygonComponent(partition(roofExtensionBand(leanTo, side, extension)), roofBasePolygon(leanTo));
973
+ const connectedAdditions = edgeConnectedPlanPolygonComponents(additionPieces, basePieces);
974
+ return connectedAdditions.length > 0
975
+ ? { basePieces, additionPieces: connectedAdditions }
976
+ : { basePieces };
977
+ }
978
+ function resolveCurvedStraightRoofPiece(leanTo, wall, side, extension, candidate, candidateWall, candidateSide, candidateExtension) {
979
+ const ownCurved = isCurvedLeanTo(leanTo);
980
+ const candidateCurved = isCurvedLeanTo(candidate);
981
+ if (ownCurved === candidateCurved)
982
+ return null;
983
+ const layout = resolveLeanToLayout(leanTo);
984
+ const edges = roofPlanEdges(leanTo);
985
+ const sideSign = side === 'left' ? -1 : 1;
986
+ const sideX = layout.roofCenterX + sideSign * (layout.roofWidth / 2);
987
+ const curved = ownCurved ? leanTo : candidate;
988
+ const curvedWall = ownCurved ? wall : candidateWall;
989
+ const curvedSide = ownCurved ? side : candidateSide;
990
+ const curvedExtension = ownCurved ? extension : candidateExtension;
991
+ const curvedLayout = resolveLeanToLayout(curved);
992
+ const curvedEdges = roofPlanEdges(curved);
993
+ const curvedSideSign = curvedSide === 'left' ? -1 : 1;
994
+ const curvedSideX = curvedLayout.roofCenterX + curvedSideSign * (curvedLayout.roofWidth / 2);
995
+ const curvedLowX = curvedSideX + curvedSideSign * curvedExtension;
996
+ const seamWorld = [
997
+ leanToPointToWorld(curvedWall, curved, curvedSideX, curvedEdges.back),
998
+ leanToPointToWorld(curvedWall, curved, curvedLowX, curvedEdges.front),
999
+ ];
1000
+ if (seamWorld.some((point) => !point))
1001
+ return null;
1002
+ const localized = seamWorld.map((point) => worldPointToLeanTo(wall, leanTo, point));
1003
+ if (localized.some((point) => !point))
1004
+ return null;
1005
+ const seamPoints = localized;
1006
+ const seam = [seamPoints[0], seamPoints.at(-1)];
1007
+ return {
1008
+ piece: [...seamPoints, [sideX, edges.front]],
1009
+ seam,
1010
+ };
1011
+ }
1012
+ export function resolveLeanToCornerJoints(leanTo, wall, nodes) {
1013
+ if (!nodes)
1014
+ return {};
1015
+ const sourceLeanTo = leanTo;
1016
+ const freestandingJoints = sourceLeanTo.hostKind === 'freestanding'
1017
+ ? resolveFreestandingCanopyJoints(sourceLeanTo, nodes)
1018
+ : undefined;
1019
+ const ownFrame = resolveLeanToJointFrame(leanTo, wall);
1020
+ if (!ownFrame || !wallFrame(ownFrame.wall))
1021
+ return {};
1022
+ leanTo = ownFrame.leanTo;
1023
+ wall = ownFrame.wall;
1024
+ const cornerLeanTo = applyLeanToWallCornerSpan(leanTo, wall);
1025
+ const tolerance = Math.max(0.35, (wall.thickness ?? 0.1) + Math.max(leanTo.leftOverhang, leanTo.rightOverhang));
1026
+ const joints = {};
1027
+ for (const side of ['left', 'right']) {
1028
+ const endpoint = endWorldPoint(wall, cornerLeanTo, side);
1029
+ const roofEndpoint = roofEndWorldPoint(wall, cornerLeanTo, side);
1030
+ if (!(endpoint && roofEndpoint))
1031
+ continue;
1032
+ for (const candidate of Object.values(nodes)) {
1033
+ if (candidate.type !== 'lean-to-extension' || candidate.id === sourceLeanTo.id)
1034
+ continue;
1035
+ const storedCandidateWall = candidate.parentId ? nodes[candidate.parentId] : undefined;
1036
+ const candidateFrame = resolveLeanToJointFrame(candidate, storedCandidateWall?.type === 'wall' ? storedCandidateWall : undefined);
1037
+ if (!candidateFrame || candidateFrame.kind !== ownFrame.kind)
1038
+ continue;
1039
+ if (ownFrame.kind === 'wall'
1040
+ ? candidateFrame.wall.parentId !== wall.parentId
1041
+ : candidate.parentId !== sourceLeanTo.parentId) {
1042
+ continue;
1043
+ }
1044
+ const candidateWall = candidateFrame.wall;
1045
+ if (!wallFrame(candidateWall))
1046
+ continue;
1047
+ const cornerCandidate = applyLeanToWallCornerSpan(candidateFrame.leanTo, candidateWall);
1048
+ const linearNeighborSide = candidateRoofSideAtPoint(candidateWall, cornerCandidate, roofEndpoint);
1049
+ if (linearNeighborSide) {
1050
+ const linearKind = cornerKindFromDirections(wall, cornerLeanTo, side, candidateWall, cornerCandidate, linearNeighborSide);
1051
+ const linearJoint = linearKind === 'linear'
1052
+ ? resolveLinearJoint(wall, cornerLeanTo, side, candidateWall, cornerCandidate, linearNeighborSide)
1053
+ : null;
1054
+ if (linearJoint) {
1055
+ joints[side] = {
1056
+ side,
1057
+ kind: 'linear',
1058
+ neighborId: candidate.id,
1059
+ neighborSide: linearNeighborSide,
1060
+ roofExtension: 0,
1061
+ roofPiece: linearJoint.roofPiece,
1062
+ seam: linearJoint.seam,
1063
+ beamExtension: linearJoint.beamExtension,
1064
+ gutterMitre: 0,
1065
+ sharedPostOwner: String(cornerLeanTo.id) < String(candidate.id),
1066
+ sharedPostPosition: linearJoint.sharedPostPosition,
1067
+ };
1068
+ break;
1069
+ }
1070
+ }
1071
+ if (!leanTo.autoMiterCorners || !candidate.autoMiterCorners)
1072
+ continue;
1073
+ const expectedFreestandingJoint = freestandingJoints?.[side];
1074
+ if (ownFrame.kind === 'freestanding' &&
1075
+ expectedFreestandingJoint?.neighborId !== candidate.id) {
1076
+ continue;
1077
+ }
1078
+ const neighborSide = candidateSideAtPoint(candidateWall, cornerCandidate, endpoint, tolerance);
1079
+ if (!neighborSide)
1080
+ continue;
1081
+ if (expectedFreestandingJoint && expectedFreestandingJoint.neighborSide !== neighborSide) {
1082
+ continue;
1083
+ }
1084
+ const kind = cornerKindFromDirections(wall, cornerLeanTo, side, candidateWall, cornerCandidate, neighborSide);
1085
+ if (!kind || kind === 'linear')
1086
+ continue;
1087
+ if (!isSupportedHostCorner(wall, cornerLeanTo, side, candidateWall, cornerCandidate, neighborSide)) {
1088
+ continue;
1089
+ }
1090
+ const interiorAngle = cornerInteriorAngle(wall, cornerLeanTo, side, candidateWall, cornerCandidate, neighborSide);
1091
+ if (interiorAngle === null)
1092
+ continue;
1093
+ const candidateLayout = resolveLeanToLayout(cornerCandidate);
1094
+ const layout = resolveLeanToLayout(cornerLeanTo);
1095
+ // A curved concave join is trimmed at the shared roof seam. Extending
1096
+ // the run from a straight chord into the curved band is not a valid
1097
+ // construction: the line/circle intersection can select the distant
1098
+ // branch and create runaway beam and gutter lengths.
1099
+ const curvedConcaveJoint = kind === 'concave' && (isCurvedLeanTo(cornerLeanTo) || isCurvedLeanTo(cornerCandidate));
1100
+ const sideSign = side === 'left' ? -1 : 1;
1101
+ const ownEdges = roofPlanEdges(cornerLeanTo);
1102
+ const candidateEdges = roofPlanEdges(cornerCandidate);
1103
+ const roofSideX = layout.roofCenterX + sideSign * (layout.roofWidth / 2);
1104
+ const roofExtension = curvedConcaveJoint
1105
+ ? 0
1106
+ : (extensionToRunIntersection(wall, cornerLeanTo, side, roofSideX, ownEdges.front, candidateWall, cornerCandidate, candidateEdges.front) ?? 0);
1107
+ const candidateSideSign = neighborSide === 'left' ? -1 : 1;
1108
+ const candidateRoofSideX = candidateLayout.roofCenterX + candidateSideSign * (candidateLayout.roofWidth / 2);
1109
+ const candidateRoofExtension = curvedConcaveJoint
1110
+ ? 0
1111
+ : (extensionToRunIntersection(candidateWall, cornerCandidate, neighborSide, candidateRoofSideX, candidateEdges.front, wall, cornerLeanTo, ownEdges.front) ?? 0);
1112
+ const curvedStraightRoof = kind === 'convex'
1113
+ ? resolveCurvedStraightRoofPiece(cornerLeanTo, wall, side, roofExtension, cornerCandidate, candidateWall, neighborSide, candidateRoofExtension)
1114
+ : null;
1115
+ const trimFreestandingConcaveCross = kind === 'concave';
1116
+ const freestandingRoof = ownFrame.kind === 'freestanding'
1117
+ ? resolveFreestandingRoofPartition(cornerLeanTo, wall, side, kind, roofExtension, cornerCandidate, candidateWall, neighborSide, candidateRoofExtension, trimFreestandingConcaveCross)
1118
+ : null;
1119
+ const curvedStraightConcaveRoof = kind === 'concave'
1120
+ ? resolveCurvedStraightConcaveRoofPiece(cornerLeanTo, wall, side, cornerCandidate, candidateWall, neighborSide)
1121
+ : null;
1122
+ const roof = curvedStraightRoof ??
1123
+ curvedStraightConcaveRoof ??
1124
+ (kind === 'convex'
1125
+ ? resolveRoofPiece(cornerLeanTo, wall, side, roofExtension, cornerCandidate, candidateWall)
1126
+ : resolveConcaveRoofPiece(cornerLeanTo, wall, side, cornerCandidate, candidateWall));
1127
+ const seam = curvedStraightRoof || curvedStraightConcaveRoof
1128
+ ? roof.seam
1129
+ : sharedRoofSeam(wall, cornerLeanTo, side, roofExtension, candidateWall, cornerCandidate, neighborSide, candidateRoofExtension, kind);
1130
+ const resolvedSeam = seam ?? roof.seam;
1131
+ const resolvedRoofPieces = curvedStraightConcaveRoof?.pieces ??
1132
+ freestandingRoof?.basePieces ?? [roof.piece];
1133
+ const beamExtension = curvedConcaveJoint
1134
+ ? 0
1135
+ : (extensionToRunIntersection(wall, cornerLeanTo, side, sideSign * (layout.span / 2), layout.beamZ, candidateWall, cornerCandidate, candidateLayout.beamZ) ?? 0);
1136
+ const gutterAway = gutterAwayFromJointDirection(wall, cornerLeanTo, side, roofExtension);
1137
+ const candidateGutterAway = gutterAwayFromJointDirection(candidateWall, cornerCandidate, neighborSide, candidateRoofExtension);
1138
+ // A curved wall can meet a straight wall at a roof seam while their
1139
+ // low eave curves never intersect. Do not apply a nominal angle miter
1140
+ // in that case: skewing both gutter ends creates a floating, uneven
1141
+ // joint instead of a valid shared edge.
1142
+ const gutterIntersection = gutterAway && candidateGutterAway
1143
+ ? extensionToRunIntersection(wall, cornerLeanTo, side, layout.roofCenterX + sideSign * (layout.roofWidth / 2), ownEdges.front, candidateWall, cornerCandidate, candidateEdges.front) !== null ||
1144
+ extensionToRunIntersection(candidateWall, cornerCandidate, neighborSide, candidateLayout.roofCenterX + candidateSideSign * (candidateLayout.roofWidth / 2), candidateEdges.front, wall, cornerLeanTo, ownEdges.front) !== null
1145
+ : false;
1146
+ const gutterInteriorAngle = gutterAway && candidateGutterAway
1147
+ ? Math.acos(Math.max(-1, Math.min(1, gutterAway[0] * candidateGutterAway[0] + gutterAway[1] * candidateGutterAway[1])))
1148
+ : interiorAngle;
1149
+ joints[side] = {
1150
+ side,
1151
+ kind,
1152
+ neighborId: candidate.id,
1153
+ neighborSide,
1154
+ roofExtension,
1155
+ roofPiece: roof.piece,
1156
+ roofPieces: curvedStraightConcaveRoof?.pieces ?? freestandingRoof?.basePieces,
1157
+ roofAdditionPieces: freestandingRoof?.additionPieces,
1158
+ mergeRoofPieces: freestandingRoof !== null,
1159
+ seam: resolvedSeam,
1160
+ framingRetainedSide: kind === 'concave'
1161
+ ? resolveFramingRetainedSide(resolvedSeam, resolvedRoofPieces)
1162
+ : undefined,
1163
+ beamExtension,
1164
+ gutterMitre: gutterIntersection
1165
+ ? (kind === 'concave' ? -1 : 1) * ((Math.PI - gutterInteriorAngle) / 2)
1166
+ : 0,
1167
+ sharedPostOwner: String(cornerLeanTo.id) < String(candidate.id),
1168
+ sharedPostPosition: [
1169
+ (side === 'left' ? -layout.span / 2 : layout.span / 2) +
1170
+ (side === 'left' ? -beamExtension : beamExtension),
1171
+ 0,
1172
+ layout.beamZ,
1173
+ ],
1174
+ };
1175
+ break;
1176
+ }
1177
+ }
1178
+ return joints;
1179
+ }
1180
+ export function leanToCornerJointMetadata(joints) {
1181
+ return Object.fromEntries(Object.entries(joints).map(([side, joint]) => [
1182
+ side,
1183
+ joint
1184
+ ? {
1185
+ beamExtension: joint.beamExtension,
1186
+ gutterMitre: joint.gutterMitre,
1187
+ seam: joint.seam,
1188
+ framingRetainedSide: joint.framingRetainedSide,
1189
+ sharedPostOwner: joint.sharedPostOwner,
1190
+ }
1191
+ : undefined,
1192
+ ]));
1193
+ }
1194
+ export function readLeanToCornerJointMetadata(leanTo) {
1195
+ const metadata = leanTo.metadata;
1196
+ if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata))
1197
+ return {};
1198
+ const value = metadata[LEAN_TO_CORNER_JOINTS_KEY];
1199
+ return value && typeof value === 'object' && !Array.isArray(value)
1200
+ ? value
1201
+ : {};
1202
+ }