@pascal-app/nodes 1.0.1 → 1.0.4

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 (867) hide show
  1. package/dist/block/definition.d.ts.map +1 -1
  2. package/dist/block/definition.js +22 -4
  3. package/dist/block/face-host.d.ts.map +1 -1
  4. package/dist/block/face-host.js +19 -2
  5. package/dist/block/floorplan.d.ts +11 -1
  6. package/dist/block/floorplan.d.ts.map +1 -1
  7. package/dist/block/floorplan.js +81 -13
  8. package/dist/block/geometry.d.ts +2 -2
  9. package/dist/block/geometry.d.ts.map +1 -1
  10. package/dist/block/geometry.js +39 -24
  11. package/dist/block/hosted-edit.d.ts +13 -0
  12. package/dist/block/hosted-edit.d.ts.map +1 -0
  13. package/dist/block/hosted-edit.js +230 -0
  14. package/dist/block/last-operation.d.ts +1 -1
  15. package/dist/block/last-operation.d.ts.map +1 -1
  16. package/dist/block/last-operation.js +6 -4
  17. package/dist/block/material-slots.d.ts +1 -3
  18. package/dist/block/material-slots.d.ts.map +1 -1
  19. package/dist/block/material-slots.js +1 -11
  20. package/dist/block/modal-session.d.ts +2 -1
  21. package/dist/block/modal-session.d.ts.map +1 -1
  22. package/dist/block/modal-session.js +2 -2
  23. package/dist/block/paint.d.ts.map +1 -1
  24. package/dist/block/paint.js +4 -10
  25. package/dist/block/panel.d.ts.map +1 -1
  26. package/dist/block/panel.js +8 -3
  27. package/dist/block/selection.d.ts.map +1 -1
  28. package/dist/block/selection.js +83 -25
  29. package/dist/block/surface.d.ts +4 -0
  30. package/dist/block/surface.d.ts.map +1 -0
  31. package/dist/block/surface.js +54 -0
  32. package/dist/block/use-block-face-operation.d.ts +2 -2
  33. package/dist/block/use-block-face-operation.d.ts.map +1 -1
  34. package/dist/block/use-block-face-operation.js +25 -11
  35. package/dist/box-vent/definition.d.ts.map +1 -1
  36. package/dist/box-vent/definition.js +3 -5
  37. package/dist/box-vent/panel.d.ts.map +1 -1
  38. package/dist/box-vent/panel.js +5 -28
  39. package/dist/building/renderer.d.ts.map +1 -1
  40. package/dist/building/renderer.js +1 -1
  41. package/dist/cabinet/countertop-layout.d.ts +13 -2
  42. package/dist/cabinet/countertop-layout.d.ts.map +1 -1
  43. package/dist/cabinet/definition.d.ts.map +1 -1
  44. package/dist/cabinet/definition.js +4 -3
  45. package/dist/cabinet/floorplan.d.ts.map +1 -1
  46. package/dist/cabinet/floorplan.js +125 -28
  47. package/dist/cabinet/geometry/shared.d.ts +1 -2
  48. package/dist/cabinet/geometry/shared.d.ts.map +1 -1
  49. package/dist/cabinet/geometry/shared.js +1 -2
  50. package/dist/cabinet/hosted-resize.d.ts +3 -6
  51. package/dist/cabinet/hosted-resize.d.ts.map +1 -1
  52. package/dist/cabinet/hosted-resize.js +9 -153
  53. package/dist/cabinet/placement-snap.d.ts.map +1 -1
  54. package/dist/cabinet/placement-snap.js +4 -2
  55. package/dist/cabinet/run-layout.d.ts.map +1 -1
  56. package/dist/cabinet/run-layout.js +8 -5
  57. package/dist/cabinet/run-ops.d.ts +13 -2
  58. package/dist/cabinet/run-ops.d.ts.map +1 -1
  59. package/dist/cabinet/surfaces.d.ts +1 -0
  60. package/dist/cabinet/surfaces.d.ts.map +1 -1
  61. package/dist/cabinet/surfaces.js +13 -1
  62. package/dist/cabinet/tool.d.ts.map +1 -1
  63. package/dist/cabinet/tool.js +30 -28
  64. package/dist/cabinet/wall-snap.d.ts.map +1 -1
  65. package/dist/cabinet/wall-snap.js +8 -8
  66. package/dist/ceiling/boundary-editor.d.ts.map +1 -1
  67. package/dist/ceiling/boundary-editor.js +5 -3
  68. package/dist/ceiling/definition.d.ts +4 -4
  69. package/dist/ceiling/definition.d.ts.map +1 -1
  70. package/dist/ceiling/definition.js +20 -15
  71. package/dist/ceiling/floorplan-affordances.d.ts +84 -4
  72. package/dist/ceiling/floorplan-affordances.d.ts.map +1 -1
  73. package/dist/ceiling/floorplan-affordances.js +0 -1
  74. package/dist/ceiling/floorplan-move.d.ts.map +1 -1
  75. package/dist/ceiling/floorplan-move.js +0 -1
  76. package/dist/ceiling/floorplan.d.ts +1 -1
  77. package/dist/ceiling/floorplan.d.ts.map +1 -1
  78. package/dist/ceiling/floorplan.js +2 -1
  79. package/dist/ceiling/height-bounds.d.ts +19 -0
  80. package/dist/ceiling/height-bounds.d.ts.map +1 -0
  81. package/dist/ceiling/height-bounds.js +24 -0
  82. package/dist/ceiling/hole-editor.d.ts.map +1 -1
  83. package/dist/ceiling/hole-editor.js +3 -4
  84. package/dist/ceiling/materials.d.ts +1 -1
  85. package/dist/ceiling/materials.d.ts.map +1 -1
  86. package/dist/ceiling/materials.js +10 -3
  87. package/dist/ceiling/paint.d.ts +2 -8
  88. package/dist/ceiling/paint.d.ts.map +1 -1
  89. package/dist/ceiling/paint.js +121 -24
  90. package/dist/ceiling/panel.d.ts.map +1 -1
  91. package/dist/ceiling/panel.js +40 -23
  92. package/dist/ceiling/renderer.d.ts.map +1 -1
  93. package/dist/ceiling/renderer.js +43 -6
  94. package/dist/chimney/definition.d.ts.map +1 -1
  95. package/dist/chimney/definition.js +1 -0
  96. package/dist/chimney/move-tool.d.ts.map +1 -1
  97. package/dist/chimney/move-tool.js +8 -2
  98. package/dist/chimney/paint.d.ts.map +1 -1
  99. package/dist/chimney/paint.js +3 -10
  100. package/dist/column/definition.d.ts.map +1 -1
  101. package/dist/column/definition.js +34 -5
  102. package/dist/column/floorplan-affordances.d.ts.map +1 -1
  103. package/dist/column/floorplan-affordances.js +11 -1
  104. package/dist/column/floorplan.d.ts +1 -1
  105. package/dist/column/floorplan.d.ts.map +1 -1
  106. package/dist/column/floorplan.js +69 -12
  107. package/dist/column/hosted-resize.d.ts +215 -0
  108. package/dist/column/hosted-resize.d.ts.map +1 -0
  109. package/dist/column/hosted-resize.js +9 -0
  110. package/dist/column/panel.d.ts.map +1 -1
  111. package/dist/column/panel.js +153 -149
  112. package/dist/column/renderer.d.ts.map +1 -1
  113. package/dist/column/renderer.js +29 -105
  114. package/dist/column/shape.d.ts +22 -0
  115. package/dist/column/shape.d.ts.map +1 -0
  116. package/dist/column/shape.js +120 -0
  117. package/dist/column/surface.d.ts +4 -0
  118. package/dist/column/surface.d.ts.map +1 -0
  119. package/dist/column/surface.js +73 -0
  120. package/dist/construction-dimension/floorplan.js +7 -3
  121. package/dist/cupola/definition.d.ts.map +1 -1
  122. package/dist/cupola/definition.js +3 -8
  123. package/dist/cupola/panel.d.ts.map +1 -1
  124. package/dist/cupola/panel.js +5 -23
  125. package/dist/door/definition.d.ts.map +1 -1
  126. package/dist/door/definition.js +52 -4
  127. package/dist/door/door-math.d.ts +6 -11
  128. package/dist/door/door-math.d.ts.map +1 -1
  129. package/dist/door/door-math.js +13 -17
  130. package/dist/door/floorplan-affordances.d.ts.map +1 -1
  131. package/dist/door/floorplan-affordances.js +8 -12
  132. package/dist/door/floorplan-move.d.ts.map +1 -1
  133. package/dist/door/floorplan-move.js +3 -1
  134. package/dist/door/floorplan.d.ts.map +1 -1
  135. package/dist/door/floorplan.js +55 -21
  136. package/dist/door/mechanism.d.ts +4 -0
  137. package/dist/door/mechanism.d.ts.map +1 -0
  138. package/dist/door/mechanism.js +24 -0
  139. package/dist/door/move-tool.d.ts.map +1 -1
  140. package/dist/door/move-tool.js +75 -52
  141. package/dist/door/paint.d.ts.map +1 -1
  142. package/dist/door/paint.js +3 -0
  143. package/dist/door/panel.d.ts.map +1 -1
  144. package/dist/door/panel.js +54 -308
  145. package/dist/door/placement.d.ts +45 -0
  146. package/dist/door/placement.d.ts.map +1 -0
  147. package/dist/door/placement.js +51 -0
  148. package/dist/door/preview.d.ts.map +1 -1
  149. package/dist/door/preview.js +2 -0
  150. package/dist/door/renderer.d.ts.map +1 -1
  151. package/dist/door/renderer.js +3 -2
  152. package/dist/door/tool.d.ts.map +1 -1
  153. package/dist/door/tool.js +112 -23
  154. package/dist/dormer/csg-geometry.d.ts.map +1 -1
  155. package/dist/dormer/csg-geometry.js +2 -0
  156. package/dist/dormer/definition.d.ts.map +1 -1
  157. package/dist/dormer/definition.js +1 -0
  158. package/dist/dormer/paint.d.ts.map +1 -1
  159. package/dist/dormer/paint.js +2 -5
  160. package/dist/dormer/panel.d.ts.map +1 -1
  161. package/dist/dormer/panel.js +4 -14
  162. package/dist/downspout/definition.d.ts.map +1 -1
  163. package/dist/downspout/definition.js +3 -2
  164. package/dist/duct-fitting/definition.d.ts.map +1 -1
  165. package/dist/duct-fitting/definition.js +2 -1
  166. package/dist/duct-fitting/geometry.d.ts.map +1 -1
  167. package/dist/duct-fitting/geometry.js +1 -1
  168. package/dist/duct-fitting/move-tool.d.ts.map +1 -1
  169. package/dist/duct-fitting/move-tool.js +8 -2
  170. package/dist/duct-segment/definition.d.ts.map +1 -1
  171. package/dist/duct-segment/definition.js +2 -2
  172. package/dist/duct-segment/geometry.d.ts.map +1 -1
  173. package/dist/duct-segment/geometry.js +1 -1
  174. package/dist/duct-segment/move-tool.d.ts.map +1 -1
  175. package/dist/duct-segment/move-tool.js +8 -2
  176. package/dist/duct-segment/tool.d.ts.map +1 -1
  177. package/dist/duct-segment/tool.js +11 -0
  178. package/dist/duct-terminal/tool.d.ts.map +1 -1
  179. package/dist/duct-terminal/tool.js +77 -8
  180. package/dist/elevator/definition.d.ts.map +1 -1
  181. package/dist/elevator/definition.js +3 -2
  182. package/dist/elevator/panel.d.ts.map +1 -1
  183. package/dist/elevator/panel.js +5 -27
  184. package/dist/elevator/parametrics.d.ts.map +1 -1
  185. package/dist/elevator/parametrics.js +98 -1
  186. package/dist/elevator/renderer.d.ts.map +1 -1
  187. package/dist/elevator/renderer.js +3 -4
  188. package/dist/eyebrow-vent/definition.d.ts.map +1 -1
  189. package/dist/eyebrow-vent/definition.js +3 -5
  190. package/dist/eyebrow-vent/paint.d.ts.map +1 -1
  191. package/dist/eyebrow-vent/paint.js +3 -6
  192. package/dist/eyebrow-vent/panel.d.ts.map +1 -1
  193. package/dist/eyebrow-vent/panel.js +5 -23
  194. package/dist/fence/actions/move-endpoint.d.ts +1 -1
  195. package/dist/fence/actions/move-endpoint.d.ts.map +1 -1
  196. package/dist/fence/actions/move-endpoint.js +2 -1
  197. package/dist/fence/definition.d.ts +2 -1
  198. package/dist/fence/definition.d.ts.map +1 -1
  199. package/dist/fence/definition.js +84 -28
  200. package/dist/fence/drafting.d.ts +36 -0
  201. package/dist/fence/drafting.d.ts.map +1 -0
  202. package/dist/fence/drafting.js +210 -0
  203. package/dist/fence/feature-tool.d.ts +4 -0
  204. package/dist/fence/feature-tool.d.ts.map +1 -0
  205. package/dist/fence/feature-tool.js +140 -0
  206. package/dist/fence/features.d.ts +2 -0
  207. package/dist/fence/features.d.ts.map +1 -0
  208. package/dist/fence/features.js +12 -0
  209. package/dist/fence/floorplan-affordances.d.ts.map +1 -1
  210. package/dist/fence/floorplan-affordances.js +2 -1
  211. package/dist/fence/floorplan.d.ts +2 -3
  212. package/dist/fence/floorplan.d.ts.map +1 -1
  213. package/dist/fence/floorplan.js +131 -71
  214. package/dist/fence/geometry-parts.d.ts +50 -0
  215. package/dist/fence/geometry-parts.d.ts.map +1 -0
  216. package/dist/fence/geometry-parts.js +1144 -0
  217. package/dist/fence/geometry.d.ts +2 -3
  218. package/dist/fence/geometry.d.ts.map +1 -1
  219. package/dist/fence/geometry.js +84 -7
  220. package/dist/fence/index.d.ts +1 -0
  221. package/dist/fence/index.d.ts.map +1 -1
  222. package/dist/fence/index.js +1 -0
  223. package/dist/fence/inspector-editors.d.ts +15 -0
  224. package/dist/fence/inspector-editors.d.ts.map +1 -1
  225. package/dist/fence/inspector-editors.js +128 -3
  226. package/dist/fence/lift.d.ts.map +1 -1
  227. package/dist/fence/lift.js +5 -2
  228. package/dist/fence/move-tool.js +2 -1
  229. package/dist/fence/panel-model.d.ts +4 -0
  230. package/dist/fence/panel-model.d.ts.map +1 -0
  231. package/dist/fence/panel-model.js +12 -0
  232. package/dist/fence/parametrics.d.ts +0 -12
  233. package/dist/fence/parametrics.d.ts.map +1 -1
  234. package/dist/fence/parametrics.js +185 -18
  235. package/dist/fence/system.d.ts.map +1 -1
  236. package/dist/fence/system.js +141 -31
  237. package/dist/fence/tool.d.ts.map +1 -1
  238. package/dist/fence/tool.js +257 -48
  239. package/dist/fence-feature/actions.d.ts +7 -0
  240. package/dist/fence-feature/actions.d.ts.map +1 -0
  241. package/dist/fence-feature/actions.js +26 -0
  242. package/dist/fence-feature/definition.d.ts +4 -0
  243. package/dist/fence-feature/definition.d.ts.map +1 -0
  244. package/dist/fence-feature/definition.js +112 -0
  245. package/dist/fence-feature/floorplan-affordances.d.ts +3 -0
  246. package/dist/fence-feature/floorplan-affordances.d.ts.map +1 -0
  247. package/dist/fence-feature/floorplan-affordances.js +37 -0
  248. package/dist/fence-feature/floorplan.d.ts +4 -0
  249. package/dist/fence-feature/floorplan.d.ts.map +1 -0
  250. package/dist/fence-feature/floorplan.js +120 -0
  251. package/dist/fence-feature/geometry.d.ts +5 -0
  252. package/dist/fence-feature/geometry.d.ts.map +1 -0
  253. package/dist/fence-feature/geometry.js +43 -0
  254. package/dist/fence-feature/handles.d.ts +5 -0
  255. package/dist/fence-feature/handles.d.ts.map +1 -0
  256. package/dist/fence-feature/handles.js +84 -0
  257. package/dist/fence-feature/inspector.d.ts +6 -0
  258. package/dist/fence-feature/inspector.d.ts.map +1 -0
  259. package/dist/fence-feature/inspector.js +214 -0
  260. package/dist/fence-feature/interaction.d.ts +3 -0
  261. package/dist/fence-feature/interaction.d.ts.map +1 -0
  262. package/dist/fence-feature/interaction.js +41 -0
  263. package/dist/fence-feature/move-session.d.ts +9 -0
  264. package/dist/fence-feature/move-session.d.ts.map +1 -0
  265. package/dist/fence-feature/move-session.js +48 -0
  266. package/dist/fence-feature/move-tool.d.ts +5 -0
  267. package/dist/fence-feature/move-tool.d.ts.map +1 -0
  268. package/dist/fence-feature/move-tool.js +58 -0
  269. package/dist/fence-feature/pick-target.d.ts +7 -0
  270. package/dist/fence-feature/pick-target.d.ts.map +1 -0
  271. package/dist/fence-feature/pick-target.js +52 -0
  272. package/dist/floor-opening/definition.d.ts +3 -0
  273. package/dist/floor-opening/definition.d.ts.map +1 -0
  274. package/dist/floor-opening/definition.js +41 -0
  275. package/dist/floor-opening/floorplan.d.ts +156 -0
  276. package/dist/floor-opening/floorplan.d.ts.map +1 -0
  277. package/dist/floor-opening/floorplan.js +63 -0
  278. package/dist/guide/definition.d.ts.map +1 -1
  279. package/dist/guide/definition.js +1 -0
  280. package/dist/gutter/definition.d.ts.map +1 -1
  281. package/dist/gutter/definition.js +3 -2
  282. package/dist/hvac-equipment/tool.d.ts.map +1 -1
  283. package/dist/hvac-equipment/tool.js +8 -2
  284. package/dist/imported-mesh/definition.d.ts.map +1 -1
  285. package/dist/imported-mesh/definition.js +2 -0
  286. package/dist/imported-mesh/geometry.d.ts +1 -1
  287. package/dist/imported-mesh/geometry.d.ts.map +1 -1
  288. package/dist/imported-mesh/geometry.js +40 -12
  289. package/dist/index.d.ts +13 -2
  290. package/dist/index.d.ts.map +1 -1
  291. package/dist/index.js +20 -2
  292. package/dist/item/authored-face-host.d.ts +10 -0
  293. package/dist/item/authored-face-host.d.ts.map +1 -0
  294. package/dist/item/authored-face-host.js +87 -0
  295. package/dist/item/authored-params.d.ts +15 -0
  296. package/dist/item/authored-params.d.ts.map +1 -0
  297. package/dist/item/authored-params.js +72 -0
  298. package/dist/item/definition.d.ts +2 -2
  299. package/dist/item/definition.d.ts.map +1 -1
  300. package/dist/item/definition.js +35 -4
  301. package/dist/item/floorplan-move.d.ts.map +1 -1
  302. package/dist/item/floorplan-move.js +209 -130
  303. package/dist/item/floorplan.d.ts +22 -0
  304. package/dist/item/floorplan.d.ts.map +1 -1
  305. package/dist/item/floorplan.js +95 -6
  306. package/dist/item/index.d.ts +1 -0
  307. package/dist/item/index.d.ts.map +1 -1
  308. package/dist/item/index.js +1 -0
  309. package/dist/item/model-loader.d.ts +2 -0
  310. package/dist/item/model-loader.d.ts.map +1 -1
  311. package/dist/item/model-loader.js +55 -23
  312. package/dist/item/move-tool.d.ts +1 -1
  313. package/dist/item/move-tool.d.ts.map +1 -1
  314. package/dist/item/move-tool.js +19 -11
  315. package/dist/item/paint.d.ts.map +1 -1
  316. package/dist/item/paint.js +5 -24
  317. package/dist/item/panel.d.ts +1 -3
  318. package/dist/item/panel.d.ts.map +1 -1
  319. package/dist/item/panel.js +20 -24
  320. package/dist/item/placement-notice.d.ts +14 -0
  321. package/dist/item/placement-notice.d.ts.map +1 -0
  322. package/dist/item/placement-notice.js +38 -0
  323. package/dist/item/plan-symbols.d.ts +67 -0
  324. package/dist/item/plan-symbols.d.ts.map +1 -0
  325. package/dist/item/plan-symbols.js +407 -0
  326. package/dist/item/renderer.d.ts +10 -0
  327. package/dist/item/renderer.d.ts.map +1 -1
  328. package/dist/item/renderer.js +72 -40
  329. package/dist/item/script-param-defaults.d.ts +12 -0
  330. package/dist/item/script-param-defaults.d.ts.map +1 -0
  331. package/dist/item/script-param-defaults.js +34 -0
  332. package/dist/lean-to-extension/definition.d.ts.map +1 -1
  333. package/dist/lean-to-extension/definition.js +1 -2
  334. package/dist/lean-to-extension/geometry.d.ts.map +1 -1
  335. package/dist/lean-to-extension/geometry.js +1 -1
  336. package/dist/lean-to-extension/layout.d.ts.map +1 -1
  337. package/dist/lean-to-extension/layout.js +2 -3
  338. package/dist/lean-to-extension/placement-validation.d.ts.map +1 -1
  339. package/dist/lean-to-extension/placement-validation.js +5 -5
  340. package/dist/lean-to-extension/tool.d.ts.map +1 -1
  341. package/dist/lean-to-extension/tool.js +5 -5
  342. package/dist/level/renderer.js +1 -1
  343. package/dist/lineset/move-tool.d.ts.map +1 -1
  344. package/dist/lineset/move-tool.js +8 -2
  345. package/dist/lineset/tool.d.ts.map +1 -1
  346. package/dist/lineset/tool.js +1 -0
  347. package/dist/liquid-line/move-tool.d.ts.map +1 -1
  348. package/dist/liquid-line/move-tool.js +8 -2
  349. package/dist/liquid-line/tool.d.ts.map +1 -1
  350. package/dist/liquid-line/tool.js +5 -1
  351. package/dist/measurement/definition.d.ts.map +1 -1
  352. package/dist/measurement/definition.js +2 -1
  353. package/dist/pipe-fitting/move-tool.d.ts.map +1 -1
  354. package/dist/pipe-fitting/move-tool.js +8 -2
  355. package/dist/pipe-segment/move-tool.d.ts.map +1 -1
  356. package/dist/pipe-segment/move-tool.js +8 -2
  357. package/dist/pipe-segment/tool.d.ts.map +1 -1
  358. package/dist/pipe-segment/tool.js +14 -3
  359. package/dist/pipe-trap/tool.d.ts.map +1 -1
  360. package/dist/pipe-trap/tool.js +7 -8
  361. package/dist/procedural-item/animation.d.ts +8 -0
  362. package/dist/procedural-item/animation.d.ts.map +1 -0
  363. package/dist/procedural-item/animation.js +116 -0
  364. package/dist/procedural-item/definition.d.ts.map +1 -1
  365. package/dist/procedural-item/definition.js +77 -8
  366. package/dist/procedural-item/geometry.d.ts +5 -1
  367. package/dist/procedural-item/geometry.d.ts.map +1 -1
  368. package/dist/procedural-item/geometry.js +239 -15
  369. package/dist/procedural-item/renderer.d.ts.map +1 -1
  370. package/dist/procedural-item/renderer.js +272 -23
  371. package/dist/ridge-vent/definition.d.ts.map +1 -1
  372. package/dist/ridge-vent/definition.js +1 -0
  373. package/dist/ridge-vent/panel.d.ts.map +1 -1
  374. package/dist/ridge-vent/panel.js +5 -23
  375. package/dist/ridge-vent/renderer.d.ts.map +1 -1
  376. package/dist/ridge-vent/renderer.js +9 -8
  377. package/dist/roof/definition.d.ts.map +1 -1
  378. package/dist/roof/definition.js +8 -2
  379. package/dist/roof/floorplan.d.ts +8 -0
  380. package/dist/roof/floorplan.d.ts.map +1 -1
  381. package/dist/roof/floorplan.js +194 -18
  382. package/dist/roof/panel-model.d.ts +4 -0
  383. package/dist/roof/panel-model.d.ts.map +1 -0
  384. package/dist/roof/panel-model.js +155 -0
  385. package/dist/roof/panel.d.ts.map +1 -1
  386. package/dist/roof/panel.js +5 -13
  387. package/dist/roof/parametrics.d.ts.map +1 -1
  388. package/dist/roof/parametrics.js +16 -1
  389. package/dist/roof/renderer.d.ts.map +1 -1
  390. package/dist/roof/renderer.js +5 -2
  391. package/dist/roof-segment/definition.d.ts.map +1 -1
  392. package/dist/roof-segment/definition.js +1 -0
  393. package/dist/roof-segment/panel.d.ts.map +1 -1
  394. package/dist/roof-segment/panel.js +5 -24
  395. package/dist/roof-segment/parametrics.d.ts.map +1 -1
  396. package/dist/roof-segment/parametrics.js +176 -1
  397. package/dist/roof-segment/renderer.d.ts.map +1 -1
  398. package/dist/roof-segment/renderer.js +17 -8
  399. package/dist/scan/definition.d.ts.map +1 -1
  400. package/dist/scan/definition.js +1 -0
  401. package/dist/separator/definition.d.ts +3 -0
  402. package/dist/separator/definition.d.ts.map +1 -0
  403. package/dist/separator/definition.js +39 -0
  404. package/dist/separator/renderer.d.ts +5 -0
  405. package/dist/separator/renderer.d.ts.map +1 -0
  406. package/dist/separator/renderer.js +36 -0
  407. package/dist/shared/commit-opening-move.d.ts +4 -0
  408. package/dist/shared/commit-opening-move.d.ts.map +1 -0
  409. package/dist/shared/commit-opening-move.js +9 -0
  410. package/dist/shared/curtain-opening-limits.d.ts +14 -0
  411. package/dist/shared/curtain-opening-limits.d.ts.map +1 -0
  412. package/dist/shared/curtain-opening-limits.js +53 -0
  413. package/dist/shared/draft-axis-guides.d.ts +0 -6
  414. package/dist/shared/draft-axis-guides.d.ts.map +1 -1
  415. package/dist/shared/draft-axis-guides.js +1 -8
  416. package/dist/shared/duct-body-paint.d.ts +0 -4
  417. package/dist/shared/duct-body-paint.d.ts.map +1 -1
  418. package/dist/shared/duct-body-paint.js +1 -5
  419. package/dist/shared/hosted-resize.d.ts +12 -0
  420. package/dist/shared/hosted-resize.d.ts.map +1 -0
  421. package/dist/shared/hosted-resize.js +166 -0
  422. package/dist/shared/item-interaction-actions.d.ts +6 -0
  423. package/dist/shared/item-interaction-actions.d.ts.map +1 -0
  424. package/dist/shared/item-interaction-actions.js +27 -0
  425. package/dist/shared/item-interactions.d.ts +9 -0
  426. package/dist/shared/item-interactions.d.ts.map +1 -0
  427. package/dist/shared/item-interactions.js +74 -0
  428. package/dist/shared/item-light-placement.d.ts +2 -0
  429. package/dist/shared/item-light-placement.d.ts.map +1 -0
  430. package/dist/shared/item-light-placement.js +3 -0
  431. package/dist/shared/move-roof-tool.js +2 -1
  432. package/dist/shared/node-batch/batchable.d.ts +12 -0
  433. package/dist/shared/node-batch/batchable.d.ts.map +1 -0
  434. package/dist/shared/node-batch/batchable.js +41 -0
  435. package/dist/shared/node-batch/candidates.d.ts +7 -5
  436. package/dist/shared/node-batch/candidates.d.ts.map +1 -1
  437. package/dist/shared/node-batch/candidates.js +43 -59
  438. package/dist/shared/node-batch/release.d.ts +8 -0
  439. package/dist/shared/node-batch/release.d.ts.map +1 -0
  440. package/dist/shared/node-batch/release.js +16 -0
  441. package/dist/shared/node-batch/system.d.ts.map +1 -1
  442. package/dist/shared/node-batch/system.js +17 -12
  443. package/dist/shared/node-batch/types.d.ts +4 -1
  444. package/dist/shared/node-batch/types.d.ts.map +1 -1
  445. package/dist/shared/opening-documentation.d.ts +13 -1
  446. package/dist/shared/opening-documentation.d.ts.map +1 -1
  447. package/dist/shared/opening-documentation.js +245 -59
  448. package/dist/shared/opening-grid-follow.d.ts +8 -0
  449. package/dist/shared/opening-grid-follow.d.ts.map +1 -0
  450. package/dist/shared/opening-grid-follow.js +12 -0
  451. package/dist/shared/opening-guides-runtime.d.ts.map +1 -1
  452. package/dist/shared/opening-guides-runtime.js +17 -14
  453. package/dist/shared/opening-placement-dimensions.d.ts.map +1 -1
  454. package/dist/shared/opening-placement-dimensions.js +3 -3
  455. package/dist/shared/opening-plane-offset.d.ts +23 -0
  456. package/dist/shared/opening-plane-offset.d.ts.map +1 -0
  457. package/dist/shared/opening-plane-offset.js +24 -0
  458. package/dist/shared/opening-property-preview-host.d.ts +3 -0
  459. package/dist/shared/opening-property-preview-host.d.ts.map +1 -0
  460. package/dist/shared/opening-property-preview-host.js +21 -0
  461. package/dist/shared/opening-property-preview.d.ts +19 -0
  462. package/dist/shared/opening-property-preview.d.ts.map +1 -0
  463. package/dist/shared/opening-property-preview.js +99 -0
  464. package/dist/shared/polygon-centroid-move.d.ts.map +1 -1
  465. package/dist/shared/polygon-centroid-move.js +4 -1
  466. package/dist/shared/polygon-vertex-affordance.d.ts.map +1 -1
  467. package/dist/shared/polygon-vertex-affordance.js +19 -15
  468. package/dist/shared/resting-surface-plan.d.ts +2 -1
  469. package/dist/shared/resting-surface-plan.d.ts.map +1 -1
  470. package/dist/shared/resting-surface-plan.js +29 -4
  471. package/dist/shared/run-hangers.d.ts.map +1 -1
  472. package/dist/shared/run-hangers.js +5 -4
  473. package/dist/shared/scripted-opening-handles.d.ts +18 -0
  474. package/dist/shared/scripted-opening-handles.d.ts.map +1 -0
  475. package/dist/shared/scripted-opening-handles.js +53 -0
  476. package/dist/shared/scripted-opening.d.ts +25 -0
  477. package/dist/shared/scripted-opening.d.ts.map +1 -0
  478. package/dist/shared/scripted-opening.js +72 -0
  479. package/dist/shared/slot-paint.d.ts +14 -2
  480. package/dist/shared/slot-paint.d.ts.map +1 -1
  481. package/dist/shared/slot-paint.js +29 -29
  482. package/dist/shared/surface-counterparts.d.ts +12 -0
  483. package/dist/shared/surface-counterparts.d.ts.map +1 -0
  484. package/dist/shared/surface-counterparts.js +27 -0
  485. package/dist/shared/surface-paint.d.ts.map +1 -1
  486. package/dist/shared/surface-paint.js +2 -5
  487. package/dist/shared/swap-preview-material.d.ts +11 -0
  488. package/dist/shared/swap-preview-material.d.ts.map +1 -0
  489. package/dist/shared/swap-preview-material.js +16 -0
  490. package/dist/shared/system-checks.d.ts.map +1 -1
  491. package/dist/shared/system-checks.js +5 -3
  492. package/dist/shared/wall-attach-target.d.ts +4 -10
  493. package/dist/shared/wall-attach-target.d.ts.map +1 -1
  494. package/dist/shared/wall-attach-target.js +6 -63
  495. package/dist/shared/wall-event-from-grid.d.ts +5 -0
  496. package/dist/shared/wall-event-from-grid.d.ts.map +1 -0
  497. package/dist/shared/wall-event-from-grid.js +36 -0
  498. package/dist/shared/wall-opening-ceiling.d.ts +5 -15
  499. package/dist/shared/wall-opening-ceiling.d.ts.map +1 -1
  500. package/dist/shared/wall-opening-ceiling.js +10 -18
  501. package/dist/shelf/definition.d.ts.map +1 -1
  502. package/dist/shelf/definition.js +4 -2
  503. package/dist/shelf/floorplan.d.ts.map +1 -1
  504. package/dist/shelf/floorplan.js +9 -2
  505. package/dist/shelf/geometry.d.ts.map +1 -1
  506. package/dist/shelf/geometry.js +1 -2
  507. package/dist/shelf/paint.d.ts.map +1 -1
  508. package/dist/shelf/paint.js +4 -10
  509. package/dist/site/line-ribbon.d.ts +31 -0
  510. package/dist/site/line-ribbon.d.ts.map +1 -0
  511. package/dist/site/line-ribbon.js +117 -0
  512. package/dist/site/lot-mask.d.ts +18 -0
  513. package/dist/site/lot-mask.d.ts.map +1 -0
  514. package/dist/site/lot-mask.js +35 -0
  515. package/dist/site/panel.d.ts +10 -0
  516. package/dist/site/panel.d.ts.map +1 -0
  517. package/dist/site/panel.js +155 -0
  518. package/dist/site/parametrics.d.ts +7 -0
  519. package/dist/site/parametrics.d.ts.map +1 -1
  520. package/dist/site/parametrics.js +8 -0
  521. package/dist/site/recessed-slab-ground-holes.d.ts.map +1 -1
  522. package/dist/site/recessed-slab-ground-holes.js +5 -2
  523. package/dist/site/renderer.d.ts.map +1 -1
  524. package/dist/site/renderer.js +158 -20
  525. package/dist/site/setbacks.d.ts +33 -0
  526. package/dist/site/setbacks.d.ts.map +1 -0
  527. package/dist/site/setbacks.js +204 -0
  528. package/dist/site/terrain-geometry.d.ts +28 -1
  529. package/dist/site/terrain-geometry.d.ts.map +1 -1
  530. package/dist/site/terrain-geometry.js +78 -4
  531. package/dist/site/terrain-holes.d.ts +15 -0
  532. package/dist/site/terrain-holes.d.ts.map +1 -0
  533. package/dist/site/terrain-holes.js +74 -0
  534. package/dist/site/terrain-mesh.d.ts +9 -2
  535. package/dist/site/terrain-mesh.d.ts.map +1 -1
  536. package/dist/site/terrain-mesh.js +36 -9
  537. package/dist/site/terrain-renderer.d.ts +9 -2
  538. package/dist/site/terrain-renderer.d.ts.map +1 -1
  539. package/dist/site/terrain-renderer.js +14 -4
  540. package/dist/skylight/definition.d.ts.map +1 -1
  541. package/dist/skylight/definition.js +1 -0
  542. package/dist/slab/boundary-editor.d.ts +1 -1
  543. package/dist/slab/boundary-editor.d.ts.map +1 -1
  544. package/dist/slab/boundary-editor.js +13 -13
  545. package/dist/slab/definition.d.ts.map +1 -1
  546. package/dist/slab/definition.js +23 -3
  547. package/dist/slab/dependency-tracker.d.ts.map +1 -1
  548. package/dist/slab/dependency-tracker.js +67 -7
  549. package/dist/slab/elevation-limit.d.ts.map +1 -1
  550. package/dist/slab/elevation-limit.js +2 -2
  551. package/dist/slab/floorplan-affordances.d.ts +176 -4
  552. package/dist/slab/floorplan-affordances.d.ts.map +1 -1
  553. package/dist/slab/floorplan-affordances.js +0 -1
  554. package/dist/slab/floorplan-move.d.ts.map +1 -1
  555. package/dist/slab/floorplan-move.js +3 -4
  556. package/dist/slab/floorplan.d.ts +7 -0
  557. package/dist/slab/floorplan.d.ts.map +1 -1
  558. package/dist/slab/floorplan.js +22 -5
  559. package/dist/slab/geometry.d.ts.map +1 -1
  560. package/dist/slab/geometry.js +186 -76
  561. package/dist/slab/move-tool.js +7 -6
  562. package/dist/slab/paint.d.ts +18 -5
  563. package/dist/slab/paint.d.ts.map +1 -1
  564. package/dist/slab/paint.js +458 -9
  565. package/dist/slab/panel.d.ts.map +1 -1
  566. package/dist/slab/panel.js +28 -9
  567. package/dist/slab/placement-ownership.d.ts +2 -1
  568. package/dist/slab/placement-ownership.d.ts.map +1 -1
  569. package/dist/slab/placement-ownership.js +2 -2
  570. package/dist/slab/surface-split.d.ts +24 -0
  571. package/dist/slab/surface-split.d.ts.map +1 -0
  572. package/dist/slab/surface-split.js +389 -0
  573. package/dist/slab/tool.d.ts.map +1 -1
  574. package/dist/slab/tool.js +3 -3
  575. package/dist/solar-panel/definition.d.ts.map +1 -1
  576. package/dist/solar-panel/definition.js +1 -0
  577. package/dist/spawn/definition.d.ts.map +1 -1
  578. package/dist/spawn/definition.js +1 -0
  579. package/dist/stair/baluster-geometry.d.ts +21 -0
  580. package/dist/stair/baluster-geometry.d.ts.map +1 -0
  581. package/dist/stair/baluster-geometry.js +74 -0
  582. package/dist/stair/baluster-guard.d.ts +50 -0
  583. package/dist/stair/baluster-guard.d.ts.map +1 -0
  584. package/dist/stair/baluster-guard.js +71 -0
  585. package/dist/stair/boards-guard.d.ts +37 -0
  586. package/dist/stair/boards-guard.d.ts.map +1 -0
  587. package/dist/stair/boards-guard.js +61 -0
  588. package/dist/stair/cable-guard.d.ts +39 -0
  589. package/dist/stair/cable-guard.d.ts.map +1 -0
  590. package/dist/stair/cable-guard.js +105 -0
  591. package/dist/stair/construction-controls.d.ts +15 -0
  592. package/dist/stair/construction-controls.d.ts.map +1 -0
  593. package/dist/stair/construction-controls.js +29 -0
  594. package/dist/stair/continuous-railings.d.ts +10 -0
  595. package/dist/stair/continuous-railings.d.ts.map +1 -0
  596. package/dist/stair/continuous-railings.js +159 -0
  597. package/dist/stair/definition.d.ts.map +1 -1
  598. package/dist/stair/definition.js +18 -37
  599. package/dist/stair/documentation.d.ts +1 -1
  600. package/dist/stair/documentation.d.ts.map +1 -1
  601. package/dist/stair/documentation.js +10 -17
  602. package/dist/stair/floorplan-affordances.d.ts.map +1 -1
  603. package/dist/stair/floorplan-affordances.js +9 -25
  604. package/dist/stair/floorplan.d.ts +1 -23
  605. package/dist/stair/floorplan.d.ts.map +1 -1
  606. package/dist/stair/floorplan.js +102 -86
  607. package/dist/stair/glass-guard.d.ts +72 -0
  608. package/dist/stair/glass-guard.d.ts.map +1 -0
  609. package/dist/stair/glass-guard.js +167 -0
  610. package/dist/stair/guard-path.d.ts +119 -0
  611. package/dist/stair/guard-path.d.ts.map +1 -0
  612. package/dist/stair/guard-path.js +208 -0
  613. package/dist/stair/materials.d.ts +11 -0
  614. package/dist/stair/materials.d.ts.map +1 -0
  615. package/dist/stair/materials.js +51 -0
  616. package/dist/stair/metal-guard.d.ts +38 -0
  617. package/dist/stair/metal-guard.d.ts.map +1 -0
  618. package/dist/stair/metal-guard.js +79 -0
  619. package/dist/stair/paint.d.ts +32 -0
  620. package/dist/stair/paint.d.ts.map +1 -1
  621. package/dist/stair/paint.js +75 -36
  622. package/dist/stair/panel.d.ts.map +1 -1
  623. package/dist/stair/panel.js +154 -17
  624. package/dist/stair/parametrics.d.ts +1 -1
  625. package/dist/stair/parametrics.d.ts.map +1 -1
  626. package/dist/stair/parametrics.js +181 -1
  627. package/dist/stair/plan-entry.d.ts +31 -0
  628. package/dist/stair/plan-entry.d.ts.map +1 -0
  629. package/dist/stair/plan-entry.js +325 -0
  630. package/dist/stair/post-and-rail-guard.d.ts +31 -0
  631. package/dist/stair/post-and-rail-guard.d.ts.map +1 -0
  632. package/dist/stair/post-and-rail-guard.js +63 -0
  633. package/dist/stair/railing-controls.d.ts +9 -0
  634. package/dist/stair/railing-controls.d.ts.map +1 -0
  635. package/dist/stair/railing-controls.js +49 -0
  636. package/dist/stair/renderer.d.ts.map +1 -1
  637. package/dist/stair/renderer.js +259 -259
  638. package/dist/stair/use-stair-render-data.d.ts +317 -0
  639. package/dist/stair/use-stair-render-data.d.ts.map +1 -0
  640. package/dist/stair/use-stair-render-data.js +73 -0
  641. package/dist/stair-segment/definition.d.ts.map +1 -1
  642. package/dist/stair-segment/definition.js +52 -10
  643. package/dist/stair-segment/panel.d.ts.map +1 -1
  644. package/dist/stair-segment/panel.js +36 -42
  645. package/dist/stair-segment/parametrics.d.ts +1 -1
  646. package/dist/stair-segment/parametrics.d.ts.map +1 -1
  647. package/dist/stair-segment/parametrics.js +94 -1
  648. package/dist/stair-segment/renderer.d.ts.map +1 -1
  649. package/dist/stair-segment/renderer.js +16 -28
  650. package/dist/turbine-vent/definition.d.ts.map +1 -1
  651. package/dist/turbine-vent/definition.js +3 -5
  652. package/dist/turbine-vent/paint.d.ts.map +1 -1
  653. package/dist/turbine-vent/paint.js +2 -5
  654. package/dist/turbine-vent/panel.d.ts.map +1 -1
  655. package/dist/turbine-vent/panel.js +5 -23
  656. package/dist/unit/unit-panel.d.ts.map +1 -1
  657. package/dist/unit/unit-panel.js +6 -1
  658. package/dist/wall/actions.d.ts +3 -0
  659. package/dist/wall/actions.d.ts.map +1 -0
  660. package/dist/wall/actions.js +61 -0
  661. package/dist/wall/construction-dimensions.js +8 -5
  662. package/dist/wall/contextual-dimensions.js +4 -4
  663. package/dist/wall/curtain-opening-frame.d.ts +14 -0
  664. package/dist/wall/curtain-opening-frame.d.ts.map +1 -0
  665. package/dist/wall/curtain-opening-frame.js +111 -0
  666. package/dist/wall/curtain-panel-highlight.d.ts +3 -0
  667. package/dist/wall/curtain-panel-highlight.d.ts.map +1 -0
  668. package/dist/wall/curtain-panel-highlight.js +113 -0
  669. package/dist/wall/curtain-wall-adapter.d.ts +3 -0
  670. package/dist/wall/curtain-wall-adapter.d.ts.map +1 -0
  671. package/dist/wall/curtain-wall-adapter.js +55 -0
  672. package/dist/wall/curtain-wall-floorplan.d.ts +3 -0
  673. package/dist/wall/curtain-wall-floorplan.d.ts.map +1 -0
  674. package/dist/wall/curtain-wall-floorplan.js +91 -0
  675. package/dist/wall/curtain-wall-geometry.d.ts +4 -0
  676. package/dist/wall/curtain-wall-geometry.d.ts.map +1 -0
  677. package/dist/wall/curtain-wall-geometry.js +226 -0
  678. package/dist/wall/curtain-wall-layout.d.ts +16 -0
  679. package/dist/wall/curtain-wall-layout.d.ts.map +1 -0
  680. package/dist/wall/curtain-wall-layout.js +176 -0
  681. package/dist/wall/curtain-wall-materials.d.ts +5 -0
  682. package/dist/wall/curtain-wall-materials.d.ts.map +1 -0
  683. package/dist/wall/curtain-wall-materials.js +55 -0
  684. package/dist/wall/curtain-wall-panel.d.ts +14 -0
  685. package/dist/wall/curtain-wall-panel.d.ts.map +1 -0
  686. package/dist/wall/curtain-wall-panel.js +167 -0
  687. package/dist/wall/curtain-wall-piece-geometry.d.ts +5 -0
  688. package/dist/wall/curtain-wall-piece-geometry.d.ts.map +1 -0
  689. package/dist/wall/curtain-wall-piece-geometry.js +220 -0
  690. package/dist/wall/curtain-wall-shadow.d.ts +4 -0
  691. package/dist/wall/curtain-wall-shadow.d.ts.map +1 -0
  692. package/dist/wall/curtain-wall-shadow.js +15 -0
  693. package/dist/wall/curve-tool.d.ts.map +1 -1
  694. package/dist/wall/curve-tool.js +4 -0
  695. package/dist/wall/definition.d.ts +0 -15
  696. package/dist/wall/definition.d.ts.map +1 -1
  697. package/dist/wall/definition.js +92 -7
  698. package/dist/wall/floorplan-move.d.ts +1 -1
  699. package/dist/wall/floorplan-move.d.ts.map +1 -1
  700. package/dist/wall/floorplan-move.js +20 -83
  701. package/dist/wall/floorplan-tool.d.ts +4 -0
  702. package/dist/wall/floorplan-tool.d.ts.map +1 -0
  703. package/dist/wall/floorplan-tool.js +106 -0
  704. package/dist/wall/floorplan.d.ts +3 -1
  705. package/dist/wall/floorplan.d.ts.map +1 -1
  706. package/dist/wall/floorplan.js +189 -22
  707. package/dist/wall/length-patch.d.ts +3 -0
  708. package/dist/wall/length-patch.d.ts.map +1 -0
  709. package/dist/wall/length-patch.js +14 -0
  710. package/dist/wall/measurement.d.ts.map +1 -1
  711. package/dist/wall/measurement.js +5 -5
  712. package/dist/wall/move-endpoint-tool.d.ts.map +1 -1
  713. package/dist/wall/move-endpoint-tool.js +20 -7
  714. package/dist/wall/move-shared.d.ts.map +1 -1
  715. package/dist/wall/move-shared.js +11 -7
  716. package/dist/wall/move-tool.d.ts.map +1 -1
  717. package/dist/wall/move-tool.js +140 -148
  718. package/dist/wall/paint.d.ts +26 -15
  719. package/dist/wall/paint.d.ts.map +1 -1
  720. package/dist/wall/paint.js +339 -115
  721. package/dist/wall/panel-model.d.ts +22 -0
  722. package/dist/wall/panel-model.d.ts.map +1 -0
  723. package/dist/wall/panel-model.js +175 -0
  724. package/dist/wall/panel.d.ts.map +1 -1
  725. package/dist/wall/panel.js +233 -61
  726. package/dist/wall/parametrics.d.ts.map +1 -1
  727. package/dist/wall/parametrics.js +3 -0
  728. package/dist/wall/property-preview.d.ts +7 -0
  729. package/dist/wall/property-preview.d.ts.map +1 -0
  730. package/dist/wall/property-preview.js +31 -0
  731. package/dist/wall/rectangle-command.d.ts +248 -0
  732. package/dist/wall/rectangle-command.d.ts.map +1 -0
  733. package/dist/wall/rectangle-command.js +22 -0
  734. package/dist/wall/rectangle-tool.d.ts +2 -0
  735. package/dist/wall/rectangle-tool.d.ts.map +1 -0
  736. package/dist/wall/rectangle-tool.js +165 -0
  737. package/dist/wall/renderer.d.ts.map +1 -1
  738. package/dist/wall/renderer.js +9 -4
  739. package/dist/wall/split-floorplan-layer.d.ts +4 -0
  740. package/dist/wall/split-floorplan-layer.d.ts.map +1 -0
  741. package/dist/wall/split-floorplan-layer.js +71 -0
  742. package/dist/wall/split-pointer.d.ts +3 -0
  743. package/dist/wall/split-pointer.d.ts.map +1 -0
  744. package/dist/wall/split-pointer.js +95 -0
  745. package/dist/wall/split-preview.d.ts +55 -0
  746. package/dist/wall/split-preview.d.ts.map +1 -0
  747. package/dist/wall/split-preview.js +106 -0
  748. package/dist/wall/split-session.d.ts +16 -0
  749. package/dist/wall/split-session.d.ts.map +1 -0
  750. package/dist/wall/split-session.js +144 -0
  751. package/dist/wall/split-store.d.ts +16 -0
  752. package/dist/wall/split-store.d.ts.map +1 -0
  753. package/dist/wall/split-store.js +6 -0
  754. package/dist/wall/split-tool.d.ts +10 -0
  755. package/dist/wall/split-tool.d.ts.map +1 -0
  756. package/dist/wall/split-tool.js +95 -0
  757. package/dist/wall/system.d.ts.map +1 -1
  758. package/dist/wall/system.js +39 -5
  759. package/dist/wall/tool.d.ts.map +1 -1
  760. package/dist/wall/tool.js +114 -50
  761. package/dist/wall/treatment-level-data.d.ts +3 -2
  762. package/dist/wall/treatment-level-data.d.ts.map +1 -1
  763. package/dist/wall/treatment-level-data.js +17 -13
  764. package/dist/wall/treatments.d.ts +10 -10
  765. package/dist/wall/treatments.d.ts.map +1 -1
  766. package/dist/wall/treatments.js +67 -54
  767. package/dist/wall/wall-batch-system.d.ts +7 -0
  768. package/dist/wall/wall-batch-system.d.ts.map +1 -1
  769. package/dist/wall/wall-batch-system.js +38 -3
  770. package/dist/wall/wall-batch.d.ts +6 -0
  771. package/dist/wall/wall-batch.d.ts.map +1 -1
  772. package/dist/wall/wall-batch.js +7 -1
  773. package/dist/window/definition.d.ts.map +1 -1
  774. package/dist/window/definition.js +60 -5
  775. package/dist/window/floorplan-affordances.d.ts.map +1 -1
  776. package/dist/window/floorplan-affordances.js +8 -10
  777. package/dist/window/floorplan-move.d.ts.map +1 -1
  778. package/dist/window/floorplan-move.js +3 -1
  779. package/dist/window/floorplan.d.ts.map +1 -1
  780. package/dist/window/floorplan.js +145 -60
  781. package/dist/window/mechanism.d.ts +4 -0
  782. package/dist/window/mechanism.d.ts.map +1 -0
  783. package/dist/window/mechanism.js +20 -0
  784. package/dist/window/move-tool.d.ts.map +1 -1
  785. package/dist/window/move-tool.js +83 -54
  786. package/dist/window/paint.d.ts.map +1 -1
  787. package/dist/window/paint.js +3 -0
  788. package/dist/window/panel.d.ts.map +1 -1
  789. package/dist/window/panel.js +50 -144
  790. package/dist/window/placement.d.ts +23 -0
  791. package/dist/window/placement.d.ts.map +1 -0
  792. package/dist/window/placement.js +66 -0
  793. package/dist/window/preview.d.ts.map +1 -1
  794. package/dist/window/preview.js +2 -0
  795. package/dist/window/renderer.d.ts.map +1 -1
  796. package/dist/window/renderer.js +3 -2
  797. package/dist/window/tool.d.ts.map +1 -1
  798. package/dist/window/tool.js +130 -26
  799. package/dist/window/window-math.d.ts +6 -14
  800. package/dist/window/window-math.d.ts.map +1 -1
  801. package/dist/window/window-math.js +13 -22
  802. package/dist/zone/contextual-dimensions.d.ts.map +1 -1
  803. package/dist/zone/contextual-dimensions.js +6 -28
  804. package/dist/zone/definition.d.ts.map +1 -1
  805. package/dist/zone/definition.js +4 -3
  806. package/dist/zone/floorplan-affordances.d.ts +200 -0
  807. package/dist/zone/floorplan-affordances.d.ts.map +1 -1
  808. package/dist/zone/floorplan.d.ts +1 -1
  809. package/dist/zone/floorplan.d.ts.map +1 -1
  810. package/dist/zone/floorplan.js +93 -53
  811. package/dist/zone/quantities-panel.d.ts.map +1 -1
  812. package/dist/zone/quantities-panel.js +6 -33
  813. package/dist/zone/quick-measurement.d.ts +2 -2
  814. package/dist/zone/quick-measurement.d.ts.map +1 -1
  815. package/dist/zone/quick-measurement.js +6 -5
  816. package/dist/zone/renderer.d.ts +1 -1
  817. package/dist/zone/renderer.d.ts.map +1 -1
  818. package/dist/zone/renderer.js +58 -146
  819. package/dist/zone/room-clear-dimensions.js +3 -3
  820. package/dist/zone/room-documentation.d.ts.map +1 -1
  821. package/dist/zone/room-documentation.js +2 -6
  822. package/package.json +12 -12
  823. package/dist/block/slots.d.ts +0 -4
  824. package/dist/block/slots.d.ts.map +0 -1
  825. package/dist/block/slots.js +0 -17
  826. package/dist/cabinet/slots.d.ts +0 -4
  827. package/dist/cabinet/slots.d.ts.map +0 -1
  828. package/dist/cabinet/slots.js +0 -24
  829. package/dist/ceiling/slots.d.ts +0 -6
  830. package/dist/ceiling/slots.d.ts.map +0 -1
  831. package/dist/ceiling/slots.js +0 -7
  832. package/dist/column/slots.d.ts +0 -9
  833. package/dist/column/slots.d.ts.map +0 -1
  834. package/dist/column/slots.js +0 -19
  835. package/dist/door/slots.d.ts +0 -9
  836. package/dist/door/slots.d.ts.map +0 -1
  837. package/dist/door/slots.js +0 -20
  838. package/dist/elevator/slots.d.ts +0 -8
  839. package/dist/elevator/slots.d.ts.map +0 -1
  840. package/dist/elevator/slots.js +0 -17
  841. package/dist/fence/slots.d.ts +0 -10
  842. package/dist/fence/slots.d.ts.map +0 -1
  843. package/dist/fence/slots.js +0 -23
  844. package/dist/lean-to-extension/slots.d.ts +0 -5
  845. package/dist/lean-to-extension/slots.d.ts.map +0 -1
  846. package/dist/lean-to-extension/slots.js +0 -15
  847. package/dist/shared/opening-documentation-fields.d.ts +0 -16
  848. package/dist/shared/opening-documentation-fields.d.ts.map +0 -1
  849. package/dist/shared/opening-documentation-fields.js +0 -54
  850. package/dist/shelf/slots.d.ts +0 -9
  851. package/dist/shelf/slots.d.ts.map +0 -1
  852. package/dist/shelf/slots.js +0 -30
  853. package/dist/slab/slots.d.ts +0 -10
  854. package/dist/slab/slots.d.ts.map +0 -1
  855. package/dist/slab/slots.js +0 -20
  856. package/dist/stair/floor-stack.d.ts +0 -10
  857. package/dist/stair/floor-stack.d.ts.map +0 -1
  858. package/dist/stair/floor-stack.js +0 -115
  859. package/dist/stair/slots.d.ts +0 -7
  860. package/dist/stair/slots.d.ts.map +0 -1
  861. package/dist/stair/slots.js +0 -13
  862. package/dist/wall/slots.d.ts +0 -10
  863. package/dist/wall/slots.d.ts.map +0 -1
  864. package/dist/wall/slots.js +0 -84
  865. package/dist/window/slots.d.ts +0 -5
  866. package/dist/window/slots.d.ts.map +0 -1
  867. package/dist/window/slots.js +0 -11
@@ -0,0 +1,1144 @@
1
+ import { clampFencePicketRailProjection, getFenceCenterlineFrameAt, getFenceCenterlineLength, getFenceGateLeaves, resolveFenceFeatures, } from '@pascal-app/core';
2
+ import * as THREE from 'three';
3
+ import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
4
+ const MIN_CURVE_SEGMENT_LENGTH = 0.18;
5
+ const HORIZONTAL_FENCE_CURVE_SEGMENT_LENGTH = 0.2;
6
+ function createFencePartGeometry(part) {
7
+ if (part.geometry) {
8
+ return part.geometry;
9
+ }
10
+ const geometry = part.shape === 'pyramid'
11
+ ? new THREE.ConeGeometry(0.5, 1, 4, 1, false, Math.PI / 4)
12
+ : part.shape === 'picket'
13
+ ? createPicketGeometry(part.scale[0], part.scale[1], part.scale[2], part.picketTop)
14
+ : new THREE.BoxGeometry(1, 1, 1);
15
+ if (part.shape !== 'picket')
16
+ geometry.scale(part.scale[0], part.scale[1], part.scale[2]);
17
+ if (part.rotationZ)
18
+ geometry.rotateZ(part.rotationZ);
19
+ if (part.rotationY) {
20
+ geometry.rotateY(part.rotationY);
21
+ }
22
+ geometry.translate(part.position[0], part.position[1], part.position[2]);
23
+ applyFenceUVs(geometry);
24
+ return geometry;
25
+ }
26
+ function createPicketGeometry(width, height, depth, top = 'dog-ear') {
27
+ const shape = new THREE.Shape();
28
+ const halfWidth = width / 2;
29
+ const tip = height / 2;
30
+ const shoulder = tip - Math.min(halfWidth, height * 0.2);
31
+ shape.moveTo(-halfWidth, -tip);
32
+ shape.lineTo(halfWidth, -tip);
33
+ if (top === 'flat') {
34
+ shape.lineTo(halfWidth, tip);
35
+ shape.lineTo(-halfWidth, tip);
36
+ }
37
+ else {
38
+ shape.lineTo(halfWidth, shoulder);
39
+ if (top === 'pointed') {
40
+ shape.lineTo(0, tip);
41
+ }
42
+ else if (top === 'rounded') {
43
+ shape.absellipse(0, shoulder, halfWidth, tip - shoulder, 0, Math.PI, false, 0);
44
+ }
45
+ else {
46
+ shape.lineTo(width * 0.28, tip);
47
+ shape.lineTo(-width * 0.28, tip);
48
+ }
49
+ shape.lineTo(-halfWidth, shoulder);
50
+ }
51
+ shape.closePath();
52
+ const geometry = new THREE.ExtrudeGeometry(shape, {
53
+ bevelEnabled: false,
54
+ curveSegments: 8,
55
+ depth,
56
+ steps: 1,
57
+ });
58
+ geometry.translate(0, 0, -depth / 2);
59
+ return geometry;
60
+ }
61
+ function getInfillOffset(fence, supportDepth, infillDepth) {
62
+ const direction = fence.infillPlacement === 'front' ? 1 : fence.infillPlacement === 'back' ? -1 : 0;
63
+ // A small overlap avoids coincident faces at the attachment surface.
64
+ return direction * Math.max(0, (supportDepth + infillDepth) / 2 - 0.002);
65
+ }
66
+ function getFencePointAt(fence, t) {
67
+ const frame = getFenceCenterlineFrameAt(fence, t);
68
+ return {
69
+ point: frame.point,
70
+ tangentAngle: Math.atan2(frame.tangent.y, frame.tangent.x),
71
+ };
72
+ }
73
+ function createFenceCurveBlockPart(fence, startT, endT, centerY, height, depth, lateralOffset = 0) {
74
+ if (endT - startT <= 1e-5)
75
+ return null;
76
+ const halfHeight = height / 2;
77
+ const halfDepth = depth / 2;
78
+ const centerlineLength = getFenceCenterlineLength(fence);
79
+ const startDistance = startT * centerlineLength;
80
+ const endDistance = endT * centerlineLength;
81
+ const bottomY = centerY - halfHeight;
82
+ const topY = centerY + halfHeight;
83
+ const corners = [];
84
+ for (const t of [startT, endT]) {
85
+ const frame = getFencePointAt(fence, t);
86
+ const normalX = -Math.sin(frame.tangentAngle);
87
+ const normalZ = Math.cos(frame.tangentAngle);
88
+ const outerX = frame.point.x + normalX * (halfDepth + lateralOffset);
89
+ const outerZ = frame.point.y + normalZ * (halfDepth + lateralOffset);
90
+ const innerX = frame.point.x - normalX * (halfDepth - lateralOffset);
91
+ const innerZ = frame.point.y - normalZ * (halfDepth - lateralOffset);
92
+ corners.push([outerX, bottomY, outerZ], [innerX, bottomY, innerZ], [outerX, topY, outerZ], [innerX, topY, innerZ]);
93
+ }
94
+ const positions = [];
95
+ const uvs = [];
96
+ const pushVertex = (index, uv) => {
97
+ positions.push(...corners[index]);
98
+ uvs.push(...uv);
99
+ };
100
+ const pushQuad = (a, b, c, d, uvA, uvB, uvC, uvD) => {
101
+ pushVertex(a, uvA);
102
+ pushVertex(b, uvB);
103
+ pushVertex(c, uvC);
104
+ pushVertex(a, uvA);
105
+ pushVertex(c, uvC);
106
+ pushVertex(d, uvD);
107
+ };
108
+ const topOuterV = topY;
109
+ const topInnerV = topY + depth;
110
+ const innerTopV = topInnerV;
111
+ const innerBottomV = topInnerV + height;
112
+ const bottomInnerV = bottomY - depth;
113
+ pushQuad(0, 4, 6, 2, [startDistance, bottomY], [endDistance, bottomY], [endDistance, topY], [startDistance, topY]);
114
+ pushQuad(1, 3, 7, 5, [startDistance, innerBottomV], [startDistance, innerTopV], [endDistance, innerTopV], [endDistance, innerBottomV]);
115
+ pushQuad(2, 6, 7, 3, [startDistance, topOuterV], [endDistance, topOuterV], [endDistance, topInnerV], [startDistance, topInnerV]);
116
+ pushQuad(0, 1, 5, 4, [startDistance, bottomY], [startDistance, bottomInnerV], [endDistance, bottomInnerV], [endDistance, bottomY]);
117
+ pushQuad(0, 2, 3, 1, [0, bottomY], [0, topY], [depth, innerTopV], [depth, innerBottomV]);
118
+ pushQuad(4, 5, 7, 6, [0, bottomY], [depth, innerBottomV], [depth, innerTopV], [0, topY]);
119
+ const geometry = new THREE.BufferGeometry();
120
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute(new Float32Array(positions), 3));
121
+ geometry.setAttribute('uv', new THREE.Float32BufferAttribute(new Float32Array(uvs), 2));
122
+ geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(new Float32Array(uvs), 2));
123
+ geometry.computeVertexNormals();
124
+ return {
125
+ geometry,
126
+ position: [0, 0, 0],
127
+ scale: [1, 1, 1],
128
+ startT,
129
+ endT,
130
+ curveBlock: { centerY, height, depth, lateralOffset },
131
+ };
132
+ }
133
+ function createFenceCurveBlockParts(fence, startT, endT, centerY, height, depth, maxSegmentLength = MIN_CURVE_SEGMENT_LENGTH, lateralOffset = 0) {
134
+ const length = getFenceCenterlineLength(fence) * Math.max(1e-4, endT - startT);
135
+ const segmentCount = Math.max(1, Math.ceil(length / Math.max(1e-4, maxSegmentLength)));
136
+ const parts = [];
137
+ for (let index = 0; index < segmentCount; index += 1) {
138
+ const segmentStartT = startT + (endT - startT) * (index / segmentCount);
139
+ const segmentEndT = startT + (endT - startT) * ((index + 1) / segmentCount);
140
+ const part = createFenceCurveBlockPart(fence, segmentStartT, segmentEndT, centerY, height, depth, lateralOffset);
141
+ if (part)
142
+ parts.push(part);
143
+ }
144
+ return parts;
145
+ }
146
+ function applyFenceUVs(geometry) {
147
+ const position = geometry.getAttribute('position');
148
+ const normal = geometry.getAttribute('normal');
149
+ if (!(position && normal))
150
+ return;
151
+ // World-scale triplanar UVs: 1 UV unit = 1 metre, sampled from the part's
152
+ // local-space (already translated into fence space) coordinates with NO
153
+ // per-part origin shift. A shared origin keeps a tiled finish continuous
154
+ // across posts, rails, and infill instead of restarting the tile at each
155
+ // part's own min corner (the previous behaviour, which broke the 1 m
156
+ // contract and made adjacent parts mistile).
157
+ const uvs = new Float32Array(position.count * 2);
158
+ for (let index = 0; index < position.count; index += 1) {
159
+ const px = position.getX(index);
160
+ const py = position.getY(index);
161
+ const pz = position.getZ(index);
162
+ const nx = Math.abs(normal.getX(index));
163
+ const ny = Math.abs(normal.getY(index));
164
+ const nz = Math.abs(normal.getZ(index));
165
+ let u = 0;
166
+ let v = 0;
167
+ if (ny >= nx && ny >= nz) {
168
+ u = px;
169
+ v = pz;
170
+ }
171
+ else if (nx >= nz) {
172
+ u = pz;
173
+ v = py;
174
+ }
175
+ else {
176
+ u = px;
177
+ v = py;
178
+ }
179
+ uvs[index * 2] = u;
180
+ uvs[index * 2 + 1] = v;
181
+ }
182
+ geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
183
+ geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(uvs.slice(), 2));
184
+ }
185
+ function getStyleDefaults(style) {
186
+ if (style === 'privacy') {
187
+ return { spacingFactor: 0.42, postFactor: 1.35, baseFactor: 1.2, topFactor: 1.2 };
188
+ }
189
+ if (style === 'rail') {
190
+ return { spacingFactor: 0.68, postFactor: 0.8, baseFactor: 0.85, topFactor: 0.85 };
191
+ }
192
+ return { spacingFactor: 0.3, postFactor: 0.55, baseFactor: 1, topFactor: 0.75 };
193
+ }
194
+ function getFenceInfillVerticalBounds(fence) {
195
+ const height = fence.style === 'picket' ? Math.max(fence.height, 0.3) : fence.height;
196
+ if (fence.style === 'picket') {
197
+ const baseHeight = fence.baseStyle === 'floating' ? 0 : Math.min(Math.max(fence.baseHeight, 0.04), height * 0.5);
198
+ const bottom = Math.min(baseHeight + Math.max(fence.groundClearance, 0), height * 0.75);
199
+ const topClearance = Math.min(Math.max(fence.picketTopClearance ?? 0, 0), height - bottom - 0.02);
200
+ return { bottom, top: height - topClearance };
201
+ }
202
+ const styleDefaults = getStyleDefaults(fence.style);
203
+ const baseY = fence.baseStyle === 'floating' ? Math.max(fence.groundClearance, 0) : 0;
204
+ const baseHeight = Math.max(fence.baseHeight * (fence.style === 'horizontal' ? 1 : styleDefaults.baseFactor), 0.04);
205
+ const topRailHeight = Math.max(fence.topRailHeight * (fence.style === 'horizontal' ? 1 : styleDefaults.topFactor), 0.01);
206
+ const verticalHeight = Math.max(height - baseHeight - topRailHeight, 0.08);
207
+ const bottom = baseY + baseHeight;
208
+ return { bottom, top: bottom + verticalHeight };
209
+ }
210
+ export function getFenceFeatureDimensions(fence, feature) {
211
+ const bounds = getFenceInfillVerticalBounds(fence);
212
+ const fullPost = fence.style === 'picket' ||
213
+ fence.style === 'horizontal' ||
214
+ fence.baseStyle === 'floating' ||
215
+ !fence.showInfill;
216
+ const postBottom = fullPost ? 0 : bounds.bottom;
217
+ const defaults = getStyleDefaults(fence.style);
218
+ const railHeight = Math.max(fence.topRailHeight * (fence.style === 'horizontal' ? 1 : defaults.topFactor), 0.01);
219
+ const postTop = fence.style === 'picket'
220
+ ? Math.max(fence.height, 0.3)
221
+ : fullPost
222
+ ? bounds.top -
223
+ (fence.baseStyle === 'floating' ? Math.max(fence.groundClearance, 0) : 0) +
224
+ railHeight +
225
+ Math.max(fence.groundClearance, 0)
226
+ : bounds.top;
227
+ const matchesHeight = feature.matchFenceHeight ?? feature.matchFenceStyle !== false;
228
+ const bottom = feature.kind === 'opening'
229
+ ? postBottom
230
+ : matchesHeight
231
+ ? bounds.bottom
232
+ : (feature.clearance ?? bounds.bottom);
233
+ const defaultHeight = feature.kind === 'opening' ? postTop - postBottom : bounds.top - bounds.bottom;
234
+ const height = matchesHeight ? defaultHeight : (feature.height ?? defaultHeight);
235
+ const postHeight = Math.max(0.02, feature.kind === 'opening' ? height : postTop - postBottom);
236
+ return { bottom, height, postBottom, postHeight };
237
+ }
238
+ function distributePattern(fence, start, end, targetSpacing, minimumSpacing, minimumCount = 1) {
239
+ const length = Math.max(0, end - start);
240
+ if (length < 1e-5)
241
+ return minimumCount > 1 ? [start, end] : [(start + end) / 2];
242
+ const target = Math.max(targetSpacing, minimumSpacing);
243
+ const maxCount = Math.max(minimumCount, Math.floor(length / minimumSpacing) + 1);
244
+ const mode = fence.patternDistribution;
245
+ let count;
246
+ if (mode === 'fixed-count') {
247
+ count = THREE.MathUtils.clamp(Math.round(fence.patternCount), minimumCount, maxCount);
248
+ }
249
+ else if (mode === 'maximum-spacing') {
250
+ count = THREE.MathUtils.clamp(Math.ceil(length / target) + 1, minimumCount, maxCount);
251
+ }
252
+ else if (mode === 'equal-fit') {
253
+ count = THREE.MathUtils.clamp(Math.round(length / target) + 1, minimumCount, maxCount);
254
+ }
255
+ else {
256
+ count = THREE.MathUtils.clamp(Math.floor(length / target) + 1, minimumCount, maxCount);
257
+ }
258
+ if (count === 1) {
259
+ return [
260
+ mode === 'fixed-spacing' && fence.patternAlignment === 'start'
261
+ ? start
262
+ : mode === 'fixed-spacing' && fence.patternAlignment === 'end'
263
+ ? end
264
+ : (start + end) / 2,
265
+ ];
266
+ }
267
+ const fixed = mode === 'fixed-spacing' && fence.patternRemainder === 'leave';
268
+ const spacing = fixed ? target : length / (count - 1);
269
+ const extra = Math.max(0, length - spacing * (count - 1));
270
+ const origin = fixed
271
+ ? start +
272
+ (fence.patternAlignment === 'end'
273
+ ? extra
274
+ : fence.patternAlignment === 'center'
275
+ ? extra / 2
276
+ : 0)
277
+ : start;
278
+ return Array.from({ length: count }, (_, index) => origin + index * spacing);
279
+ }
280
+ function miterFencePartEnd(fence, part, endpoint, neighbor) {
281
+ if (!part.geometry || !part.curveBlock)
282
+ return;
283
+ const t = endpoint === 'start' ? 0 : 1;
284
+ if (Math.abs(((endpoint === 'start' ? part.startT : part.endT) ?? -1) - t) > 1e-5)
285
+ return;
286
+ const point = fence[endpoint];
287
+ const frame = getFenceCenterlineFrameAt(fence, t);
288
+ const sign = endpoint === 'start' ? 1 : -1;
289
+ const ux = frame.tangent.x * sign;
290
+ const uz = frame.tangent.y * sign;
291
+ const neighborAtStart = Math.hypot(neighbor.start[0] - point[0], neighbor.start[1] - point[1]) < 0.001;
292
+ const other = getFenceCenterlineFrameAt(neighbor, neighborAtStart ? 0 : 1);
293
+ const vx = other.tangent.x * (neighborAtStart ? 1 : -1);
294
+ const vz = other.tangent.y * (neighborAtStart ? 1 : -1);
295
+ const cross = ux * vz - uz * vx;
296
+ if (Math.abs(cross) < 0.15)
297
+ return;
298
+ const { depth, lateralOffset } = part.curveBlock;
299
+ const ratio = Math.max(neighbor.thickness, 0.03) / Math.max(fence.thickness, 0.03);
300
+ const position = part.geometry.getAttribute('position');
301
+ for (let index = 0; index < position.count; index += 1) {
302
+ const x = position.getX(index);
303
+ const z = position.getZ(index);
304
+ for (const side of [-1, 1]) {
305
+ const offset = lateralOffset + (side * depth) / 2;
306
+ const originalX = point[0] - uz * offset * sign;
307
+ const originalZ = point[1] + ux * offset * sign;
308
+ if (Math.hypot(x - originalX, z - originalZ) > 1e-4)
309
+ continue;
310
+ const neighborOffset = -offset * sign * ratio;
311
+ const nx = -uz * offset * sign;
312
+ const nz = ux * offset * sign;
313
+ const mx = -vz * neighborOffset;
314
+ const mz = vx * neighborOffset;
315
+ const reach = ((mx - nx) * vz - (mz - nz) * vx) / cross;
316
+ if (Math.abs(reach) > Math.max(depth, depth * ratio) * 4)
317
+ break;
318
+ position.setX(index, originalX + ux * reach);
319
+ position.setZ(index, originalZ + uz * reach);
320
+ break;
321
+ }
322
+ }
323
+ position.needsUpdate = true;
324
+ part.geometry.computeVertexNormals();
325
+ }
326
+ /**
327
+ * Horizontal-board fence — composite cladding boards stacked between square
328
+ * intermediate posts (each capped), instead of the vertical pickets the other
329
+ * styles draw. Posts march along the whole span at `postSpacing` (not just the
330
+ * two ends), the boards run full-length so they curve with the fence, and a
331
+ * thin reveal between boards leaves the groove shadow that reads as cladding.
332
+ */
333
+ function createHorizontalFenceParts(fence, sharedEndpoints) {
334
+ const posts = [];
335
+ const infill = [];
336
+ const base = [];
337
+ const rail = [];
338
+ const length = Math.max(getFenceCenterlineLength(fence), 0.01);
339
+ const panelDepth = Math.max(fence.thickness, 0.03);
340
+ const clearance = Math.max(fence.groundClearance, 0);
341
+ const isFloating = fence.baseStyle === 'floating';
342
+ // 'raised': the kickboard becomes a bottom rail `clearance` up; the posts still reach the ground
343
+ const isRaised = fence.baseStyle === 'raised';
344
+ const showInfill = fence.showInfill ?? true;
345
+ const baseHeight = Math.max(fence.baseHeight, 0.04);
346
+ const topRailHeight = Math.max(fence.topRailHeight, 0.01);
347
+ const verticalHeight = Math.max(fence.height - baseHeight - topRailHeight, 0.08);
348
+ const baseY = isFloating || isRaised ? clearance : 0;
349
+ // Square posts stand proud of the recessed boards on both faces.
350
+ const postWidth = Math.max(fence.postSize * 1.4, 0.04);
351
+ const postDepth = postWidth;
352
+ const boardDepth = Math.min(panelDepth, postDepth - 0.012);
353
+ const infillOffset = getInfillOffset(fence, postDepth, boardDepth);
354
+ // Stop the horizontal boards / base / rail at the inner faces of the
355
+ // end posts. Letting curved spans run all the way to t=0/1 makes them
356
+ // overlap the terminal post mesh and creates the broken seam/notch seen
357
+ // at curve ends.
358
+ const edgeInset = Math.max(fence.edgeInset ?? 0.015, postWidth * 0.5);
359
+ const startInsetT = sharedEndpoints?.has('start') ? 0 : Math.min(0.499, edgeInset / length);
360
+ const endInsetT = sharedEndpoints?.has('end') ? 1 : Math.max(0.501, 1 - edgeInset / length);
361
+ // Grounded fences get a kickboard along the bottom; floating ones don't.
362
+ if (!isFloating) {
363
+ base.push(...createFenceCurveBlockParts(fence, startInsetT, endInsetT, baseY + baseHeight / 2, baseHeight, postDepth * 0.92, HORIZONTAL_FENCE_CURVE_SEGMENT_LENGTH));
364
+ }
365
+ // Stack full-length boards between the kickboard and the top rail. The board
366
+ // height is derived to evenly fill the panel around a ~0.145 m target, with a
367
+ // constant reveal between each so the count adapts to any fence height.
368
+ if (showInfill) {
369
+ const reveal = Math.max(fence.slatGap ?? 0.01, 0);
370
+ const infillBottom = baseY + baseHeight;
371
+ if (reveal < 0.002) {
372
+ // No reveal → one flush panel, so the stacked-board edge seams don't
373
+ // read as faint lines where the user asked for a smooth surface.
374
+ infill.push(...createFenceCurveBlockParts(fence, startInsetT, endInsetT, infillBottom + verticalHeight / 2, verticalHeight, boardDepth, HORIZONTAL_FENCE_CURVE_SEGMENT_LENGTH, infillOffset));
375
+ }
376
+ else {
377
+ const boardCount = Math.max(1, Math.round(verticalHeight / (0.145 + reveal)));
378
+ const slabHeight = Math.max((verticalHeight - reveal * (boardCount - 1)) / boardCount, 0.02);
379
+ for (let index = 0; index < boardCount; index += 1) {
380
+ const centerY = infillBottom + slabHeight / 2 + index * (slabHeight + reveal);
381
+ infill.push(...createFenceCurveBlockParts(fence, startInsetT, endInsetT, centerY, slabHeight, boardDepth, HORIZONTAL_FENCE_CURVE_SEGMENT_LENGTH, infillOffset));
382
+ }
383
+ }
384
+ }
385
+ // Top rail caps the boards.
386
+ rail.push(...createFenceCurveBlockParts(fence, startInsetT, endInsetT, baseY + baseHeight + verticalHeight + topRailHeight / 2, topRailHeight, Math.max(postDepth * 0.78, 0.02), HORIZONTAL_FENCE_CURVE_SEGMENT_LENGTH));
387
+ // Posts at every `postSpacing`, anchored at both ends, each with a flat cap.
388
+ const spacing = Math.max(fence.postSpacing, postWidth * 1.4);
389
+ const postCount = Math.max(2, Math.floor(length / spacing) + 1);
390
+ const postDistances = fence.patternDistribution === 'automatic'
391
+ ? Array.from({ length: postCount }, (_, index) => (length * index) / (postCount - 1))
392
+ : distributePattern(fence, 0, length, spacing, postWidth * 1.4, 2);
393
+ const anchoredPostDistances = fence.patternDistribution === 'fixed-spacing'
394
+ ? [
395
+ 0,
396
+ ...postDistances.filter((distance) => distance > postWidth * 1.4 && length - distance > postWidth * 1.4),
397
+ length,
398
+ ]
399
+ : postDistances;
400
+ const postHeight = baseHeight + verticalHeight + topRailHeight + clearance;
401
+ const capHeight = Math.max(postWidth * 0.32, 0.03);
402
+ const cap = fence.postCap ?? 'pyramid';
403
+ for (const distance of anchoredPostDistances) {
404
+ const t = distance / length;
405
+ const frame = getFencePointAt(fence, t);
406
+ posts.push({
407
+ endpoint: distance === 0 ? 'start' : distance === length ? 'end' : undefined,
408
+ position: [frame.point.x, postHeight / 2, frame.point.y],
409
+ rotationY: -frame.tangentAngle,
410
+ scale: [postWidth, postHeight, postDepth],
411
+ });
412
+ if (cap === 'flat') {
413
+ posts.push({
414
+ endpoint: distance === 0 ? 'start' : distance === length ? 'end' : undefined,
415
+ position: [frame.point.x, postHeight + capHeight / 2, frame.point.y],
416
+ rotationY: -frame.tangentAngle,
417
+ scale: [postWidth * 1.22, capHeight, postDepth * 1.22],
418
+ });
419
+ }
420
+ else if (cap === 'pyramid') {
421
+ posts.push({
422
+ endpoint: distance === 0 ? 'start' : distance === length ? 'end' : undefined,
423
+ position: [frame.point.x, postHeight + capHeight * 0.9, frame.point.y],
424
+ rotationY: -frame.tangentAngle,
425
+ scale: [postWidth * 1.18, capHeight * 1.8, postDepth * 1.18],
426
+ shape: 'pyramid',
427
+ });
428
+ }
429
+ }
430
+ return { posts, infill, base, rail };
431
+ }
432
+ /** Dressed lumber the guard is built from, metres. */
433
+ const IN = 0.0254;
434
+ const GUARD_CAP_T = 1.5 * IN;
435
+ const GUARD_CAP_W = 5.5 * IN;
436
+ const GUARD_RAIL_T = 1.5 * IN;
437
+ const GUARD_RAIL_D = 3.5 * IN;
438
+ const GUARD_BALUSTER = 1.5 * IN;
439
+ const GUARD_BOARD_D = 5.5 * IN;
440
+ const GUARD_CABLE = 0.5 * IN;
441
+ const GUARD_POST_ABOVE_CAP = 3 * IN;
442
+ const GUARD_POST_CAP_T = 1 * IN;
443
+ /**
444
+ * A deck guard the way the AWC Deck Construction Guide (DCA 6) draws one:
445
+ * 4x4 posts (`postSize`) no more than `postSpacing` apart — the two ends
446
+ * counted as posts whether drawn or not (`startPost` / `endPost` false:
447
+ * the rails die into a post already standing there, a porch's 6x6) — a 2x6
448
+ * cap rail flat over the posts, a 2x4 top rail on edge under it, and the
449
+ * infill: balusters (2x2 on a 2x4 bottom rail `groundClearance` over the
450
+ * deck, `slatGap` clear), cable (½ in runs `slatGap` apart from
451
+ * `groundClearance` up), or boards (`thickness` × 5½ in, `slatGap` apart
452
+ * from `groundClearance` up). `postThrough` runs the posts 3 in past the
453
+ * cap under a cap of their own (`postCap`). Slots: posts / infill / base
454
+ * (the bottom rail) / rail (cap + top rail).
455
+ */
456
+ function createGuardFenceParts(fence) {
457
+ const posts = [];
458
+ const infill = [];
459
+ const base = [];
460
+ const rail = [];
461
+ const length = Math.max(getFenceCenterlineLength(fence), 0.01);
462
+ const H = Math.max(fence.height, 0.4);
463
+ const postW = Math.max(fence.postSize, 0.02);
464
+ const clearance = Math.max(fence.groundClearance, 0);
465
+ const gap = Math.max(fence.slatGap ?? 0.09, 0.01);
466
+ const infillKind = fence.guardInfill ?? 'balusters';
467
+ const through = fence.postThrough === true;
468
+ const capTop = H;
469
+ const capBottom = H - GUARD_CAP_T;
470
+ const topRailBottom = capBottom - GUARD_RAIL_D;
471
+ const bays = Math.max(1, Math.ceil(length / Math.max(fence.postSpacing, postW * 2)));
472
+ const stations = [];
473
+ for (let i = 0; i <= bays; i++)
474
+ stations.push(i / bays);
475
+ // posts: to the cap's underside, or through it with a cap
476
+ const postH = through ? capTop + GUARD_POST_ABOVE_CAP : capBottom;
477
+ for (const [i, t] of stations.entries()) {
478
+ if (i === 0 && fence.startPost === false)
479
+ continue;
480
+ if (i === stations.length - 1 && fence.endPost === false)
481
+ continue;
482
+ const frame = getFencePointAt(fence, t);
483
+ posts.push({
484
+ position: [frame.point.x, postH / 2, frame.point.y],
485
+ rotationY: -frame.tangentAngle,
486
+ scale: [postW, postH, postW],
487
+ });
488
+ if (through && (fence.postCap ?? 'flat') !== 'none') {
489
+ const cap = fence.postCap ?? 'flat';
490
+ if (cap === 'pyramid') {
491
+ posts.push({
492
+ position: [frame.point.x, postH + postW * 0.45, frame.point.y],
493
+ rotationY: -frame.tangentAngle,
494
+ scale: [postW * 1.18, postW * 0.9, postW * 1.18],
495
+ shape: 'pyramid',
496
+ });
497
+ }
498
+ else {
499
+ posts.push({
500
+ position: [frame.point.x, postH + GUARD_POST_CAP_T / 2, frame.point.y],
501
+ rotationY: -frame.tangentAngle,
502
+ scale: [postW + 2 * IN, GUARD_POST_CAP_T, postW + 2 * IN],
503
+ });
504
+ }
505
+ }
506
+ }
507
+ // the cap rail flat over the posts and the 2x4 top rail on edge under it
508
+ rail.push(...createFenceCurveBlockParts(fence, 0, 1, capBottom + GUARD_CAP_T / 2, GUARD_CAP_T, GUARD_CAP_W));
509
+ rail.push(...createFenceCurveBlockParts(fence, 0, 1, topRailBottom + GUARD_RAIL_D / 2, GUARD_RAIL_D, GUARD_RAIL_T));
510
+ const postHalfT = postW / 2 / length;
511
+ const clearOfPosts = (t, halfT) => !stations.some((s) => Math.abs(s - t) < postHalfT + halfT);
512
+ if (infillKind === 'balusters') {
513
+ // the 2x4 bottom rail `clearance` over the deck, the balusters on it
514
+ const bottomRailTop = clearance + GUARD_RAIL_D;
515
+ base.push(...createFenceCurveBlockParts(fence, 0, 1, clearance + GUARD_RAIL_D / 2, GUARD_RAIL_D, GUARD_RAIL_T));
516
+ const balH = Math.max(topRailBottom - bottomRailTop, 0.05);
517
+ const pitch = GUARD_BALUSTER + gap;
518
+ const halfT = GUARD_BALUSTER / 2 / length;
519
+ for (let s = pitch; s < length - GUARD_BALUSTER; s += pitch) {
520
+ const t = s / length;
521
+ if (!clearOfPosts(t, halfT))
522
+ continue;
523
+ const part = createFenceCurveBlockPart(fence, t - halfT, t + halfT, bottomRailTop + balH / 2, balH, GUARD_BALUSTER);
524
+ if (part)
525
+ infill.push(part);
526
+ }
527
+ }
528
+ else if (infillKind === 'cable') {
529
+ // ½ in cables `gap` apart from `clearance` up to under the top rail
530
+ for (let y = clearance; y < topRailBottom - GUARD_CABLE; y += gap) {
531
+ infill.push(...createFenceCurveBlockParts(fence, 0, 1, y + GUARD_CABLE / 2, GUARD_CABLE, GUARD_CABLE));
532
+ }
533
+ }
534
+ else {
535
+ // boards `gap` apart from `clearance` up to under the top rail
536
+ const boardT = Math.max(fence.thickness, 0.012);
537
+ for (let y = clearance; y + GUARD_BOARD_D <= topRailBottom + 1e-6; y += GUARD_BOARD_D + gap) {
538
+ infill.push(...createFenceCurveBlockParts(fence, 0, 1, y + GUARD_BOARD_D / 2, GUARD_BOARD_D, boardT));
539
+ }
540
+ }
541
+ return { posts, infill, base, rail };
542
+ }
543
+ function createFenceParts(fence, sharedEndpoints) {
544
+ if (fence.style === 'guard')
545
+ return createGuardFenceParts(fence);
546
+ if (fence.style === 'horizontal')
547
+ return createHorizontalFenceParts(fence, sharedEndpoints);
548
+ if (fence.style === 'picket')
549
+ return createPicketFenceParts(fence, sharedEndpoints);
550
+ const posts = [];
551
+ const infill = [];
552
+ const base = [];
553
+ const rail = [];
554
+ const length = Math.max(getFenceCenterlineLength(fence), 0.01);
555
+ const panelDepth = Math.max(fence.thickness, 0.03);
556
+ const clearance = Math.max(fence.groundClearance, 0);
557
+ const styleDefaults = getStyleDefaults(fence.style);
558
+ const baseHeight = Math.max(fence.baseHeight * styleDefaults.baseFactor, 0.04);
559
+ const topRailHeight = Math.max(fence.topRailHeight * styleDefaults.topFactor, 0.01);
560
+ const verticalHeight = Math.max(fence.height - baseHeight - topRailHeight, 0.08);
561
+ const postWidth = Math.max(fence.postSize * styleDefaults.postFactor, 0.01);
562
+ const spacing = Math.max(fence.postSpacing *
563
+ (fence.patternDistribution === 'automatic' ? styleDefaults.spacingFactor : 1), postWidth * 1.2);
564
+ const edgeInset = Math.max(fence.edgeInset ?? 0.015, 0.005);
565
+ const isFloating = fence.baseStyle === 'floating';
566
+ // 'raised': the base is a guard's bottom rail held `clearance` above the
567
+ // ground, the pickets ending on it; the end posts run to the ground
568
+ const isRaised = fence.baseStyle === 'raised';
569
+ const showInfill = fence.showInfill ?? true;
570
+ const baseY = isFloating || isRaised ? clearance : 0;
571
+ const effectiveBaseHeight = baseHeight;
572
+ const startInsetT = Math.min(0.499, edgeInset / length);
573
+ const endInsetT = Math.max(0.501, 1 - edgeInset / length);
574
+ if (!isFloating) {
575
+ base.push(...createFenceCurveBlockParts(fence, 0, 1, baseY + effectiveBaseHeight / 2, effectiveBaseHeight, panelDepth * 1.05));
576
+ // the grounded kickboard's thin band above it — not on a bottom rail
577
+ if (!isRaised) {
578
+ base.push(...createFenceCurveBlockParts(fence, 0, 1, baseY + effectiveBaseHeight + verticalHeight * 0.15, topRailHeight * 0.8, panelDepth * 0.35));
579
+ }
580
+ }
581
+ const count = showInfill && fence.style !== 'privacy'
582
+ ? Math.max(2, Math.floor((length - edgeInset * 2) / spacing) + 1)
583
+ : 2;
584
+ const interiorStart = edgeInset + postWidth * 1.5;
585
+ const interiorEnd = length - edgeInset - postWidth * 1.5;
586
+ const positions = !showInfill || fence.patternDistribution === 'automatic'
587
+ ? Array.from({ length: count }, (_, index) => count === 1 ? 0.5 : startInsetT + (endInsetT - startInsetT) * (index / (count - 1)))
588
+ : [
589
+ startInsetT,
590
+ ...(interiorEnd >= interiorStart
591
+ ? distributePattern(fence, interiorStart, interiorEnd, spacing, postWidth * 1.2).map((distance) => distance / length)
592
+ : []),
593
+ endInsetT,
594
+ ];
595
+ const verticalY = baseY + effectiveBaseHeight + verticalHeight / 2;
596
+ for (let index = 0; index < positions.length; index += 1) {
597
+ const t = positions[index];
598
+ const isEdgePost = index === 0 || index === positions.length - 1;
599
+ const fullHeightPost = !showInfill || ((isFloating || isRaised) && isEdgePost);
600
+ const postHeight = fullHeightPost
601
+ ? effectiveBaseHeight + verticalHeight + topRailHeight + clearance
602
+ : verticalHeight;
603
+ const postY = fullHeightPost ? postHeight / 2 : verticalY;
604
+ // End posts are the structural `posts` slot; the intermediate verticals are
605
+ // the `infill` slats (only present when showInfill adds them).
606
+ const slatHalfT = Math.max(0.0005, postWidth / (2 * length));
607
+ const slatStartT = Math.max(0, t - slatHalfT);
608
+ const slatEndT = Math.min(1, t + slatHalfT);
609
+ const slat = createFenceCurveBlockPart(fence, slatStartT, slatEndT, postY, postHeight, Math.max(panelDepth * 0.35 - 0.001, 0.011), isEdgePost
610
+ ? 0
611
+ : getInfillOffset(fence, Math.max(panelDepth * 0.55, 0.018), Math.max(panelDepth * 0.35 - 0.001, 0.011)));
612
+ if (slat) {
613
+ if (isEdgePost)
614
+ slat.endpoint = index === 0 ? 'start' : 'end';
615
+ (isEdgePost ? posts : infill).push(slat);
616
+ }
617
+ }
618
+ if (showInfill && fence.style === 'privacy') {
619
+ infill.push(...createFenceCurveBlockParts(fence, startInsetT, endInsetT, verticalY, verticalHeight, panelDepth * 0.7, MIN_CURVE_SEGMENT_LENGTH, getInfillOffset(fence, Math.max(panelDepth * 0.55, 0.018), panelDepth * 0.7)));
620
+ }
621
+ rail.push(...createFenceCurveBlockParts(fence, 0, 1, baseY + effectiveBaseHeight + verticalHeight + topRailHeight / 2, topRailHeight, Math.max(panelDepth * 0.55, 0.018)));
622
+ if (isFloating) {
623
+ rail.push(...createFenceCurveBlockParts(fence, 0, 1, baseY + effectiveBaseHeight + topRailHeight / 2, topRailHeight, Math.max(panelDepth * 0.55, 0.018)));
624
+ }
625
+ return { posts, infill, base, rail };
626
+ }
627
+ function createPicketFenceParts(fence, sharedEndpoints) {
628
+ const posts = [];
629
+ const infill = [];
630
+ const base = [];
631
+ const rail = [];
632
+ const length = Math.max(getFenceCenterlineLength(fence), 0.01);
633
+ const height = Math.max(fence.height, 0.3);
634
+ const postWidth = Math.max(fence.postSize, 0.02);
635
+ const postDepth = Math.max(fence.thickness, postWidth);
636
+ const baseHeight = fence.baseStyle === 'floating' ? 0 : Math.min(Math.max(fence.baseHeight, 0.04), height * 0.5);
637
+ const picketBottom = Math.min(baseHeight + Math.max(fence.groundClearance, 0), height * 0.75);
638
+ const topClearance = Math.min(Math.max(fence.picketTopClearance ?? 0, 0), height - picketBottom - 0.02);
639
+ const picketHeight = height - picketBottom - topClearance;
640
+ const railHeight = Math.min(Math.max(fence.topRailHeight, 0.01), picketHeight * 0.15);
641
+ // Separate the exposed faces at post/base and post/rail intersections.
642
+ const baseDepth = postDepth * 0.8;
643
+ const railDepth = postDepth + 2 * clampFencePicketRailProjection(fence.picketRailProjection, fence.postSize);
644
+ const variation = fence.picketProfile === 'level'
645
+ ? 0
646
+ : Math.min(Math.max(fence.picketVariation, 0), picketHeight * 0.3);
647
+ const shortestPicketHeight = picketHeight - variation;
648
+ const picketDepth = Math.max(Math.min(fence.thickness * 0.5, 0.052), 0.01);
649
+ const picketWidth = Math.max(fence.picketWidth, 0.02);
650
+ const isCurved = (fence.path?.length ?? 0) >= 2 || Math.abs(fence.curveOffset ?? 0) > 0.0001;
651
+ // Curved runs need more clearance as neighboring boards turn toward each other.
652
+ const curveClearance = isCurved ? 0.03 : 0;
653
+ const spacing = Math.max(fence.picketSpacing, picketWidth + 0.01) + curveClearance;
654
+ const endInset = Math.max(fence.edgeInset, 0) + postWidth / 2;
655
+ const postCount = Math.max(1, Math.ceil(length / Math.max(fence.postSpacing, postWidth * 2)));
656
+ for (let index = 0; index <= postCount; index += 1) {
657
+ const frame = getFencePointAt(fence, index / postCount);
658
+ posts.push({
659
+ endpoint: index === 0 ? 'start' : index === postCount ? 'end' : undefined,
660
+ position: [frame.point.x, height / 2, frame.point.y],
661
+ rotationY: -frame.tangentAngle,
662
+ scale: [postWidth, height, postDepth],
663
+ });
664
+ if (fence.postCap !== 'none') {
665
+ const capHeight = postWidth * (fence.postCap === 'flat' ? 0.2 : 0.5);
666
+ posts.push({
667
+ endpoint: index === 0 ? 'start' : index === postCount ? 'end' : undefined,
668
+ position: [frame.point.x, height + capHeight / 2, frame.point.y],
669
+ rotationY: -frame.tangentAngle,
670
+ scale: [postWidth * 1.5, capHeight, postDepth * 1.5],
671
+ shape: fence.postCap === 'flat' ? 'box' : 'pyramid',
672
+ });
673
+ }
674
+ }
675
+ const startInsetT = sharedEndpoints?.has('start') ? 0 : Math.min(0.499, endInset / length);
676
+ const endInsetT = sharedEndpoints?.has('end') ? 1 : Math.max(0.501, 1 - endInset / length);
677
+ const railEndInset = Math.max(0, postWidth / 2 - Math.min(postWidth * 0.2, 0.015));
678
+ const railStartT = sharedEndpoints?.has('start') ? 0 : Math.min(0.499, railEndInset / length);
679
+ const railEndT = sharedEndpoints?.has('end') ? 1 : Math.max(0.501, 1 - railEndInset / length);
680
+ if (baseHeight > 0) {
681
+ base.push(...createFenceCurveBlockParts(fence, startInsetT, endInsetT, baseHeight / 2, baseHeight, baseDepth));
682
+ }
683
+ const railCount = Math.min(3, Math.max(2, Math.round(fence.picketRailCount)));
684
+ for (let index = 0; index < railCount; index += 1) {
685
+ const centerY = picketBottom + shortestPicketHeight * (0.2 + (0.55 * index) / (railCount - 1));
686
+ rail.push(...createFenceCurveBlockParts(fence, railStartT, railEndT, centerY, railHeight, railDepth, 0.22));
687
+ }
688
+ // Lay out each bay independently so boards never intersect the structural posts.
689
+ const bayLength = length / postCount;
690
+ const usableLength = bayLength - 2 * endInset - picketWidth;
691
+ if (fence.showInfill && usableLength >= 0) {
692
+ const faceOffset = getInfillOffset(fence, railDepth, picketDepth);
693
+ for (let bay = 0; bay < postCount; bay += 1) {
694
+ const distances = fence.patternDistribution === 'automatic'
695
+ ? (() => {
696
+ const count = Math.max(1, Math.floor(usableLength / spacing) + 1);
697
+ const occupied = (count - 1) * spacing;
698
+ return Array.from({ length: count }, (_, index) => bay * bayLength + (bayLength - occupied) / 2 + index * spacing);
699
+ })()
700
+ : distributePattern(fence, bay * bayLength + endInset + picketWidth / 2, (bay + 1) * bayLength - endInset - picketWidth / 2, fence.picketSpacing, picketWidth + curveClearance + 0.01);
701
+ for (let index = 0; index < distances.length; index += 1) {
702
+ const distance = distances[index];
703
+ const frame = getFenceCenterlineFrameAt(fence, distance / length);
704
+ const u = distances.length === 1 ? 0.5 : index / (distances.length - 1);
705
+ const arch = Math.sin(Math.PI * u);
706
+ const reduction = fence.picketProfile === 'arched'
707
+ ? 1 - arch
708
+ : fence.picketProfile === 'scalloped'
709
+ ? arch
710
+ : fence.picketProfile === 'alternating'
711
+ ? index % 2
712
+ : 0;
713
+ const boardHeight = picketHeight - variation * reduction;
714
+ infill.push({
715
+ position: [
716
+ frame.point.x + frame.normal.x * faceOffset,
717
+ picketBottom + boardHeight / 2,
718
+ frame.point.y + frame.normal.y * faceOffset,
719
+ ],
720
+ rotationY: -Math.atan2(frame.tangent.y, frame.tangent.x),
721
+ scale: [picketWidth, boardHeight, picketDepth],
722
+ shape: 'picket',
723
+ picketTop: fence.picketTop,
724
+ });
725
+ }
726
+ }
727
+ }
728
+ return { posts, infill, base, rail };
729
+ }
730
+ function fenceHeightTransitions(fence, heightAt) {
731
+ const length = getFenceCenterlineLength(fence);
732
+ const count = Math.max(2, Math.ceil(length / 0.06));
733
+ const transitions = [];
734
+ let previous = getFencePointAt(fence, 0).point;
735
+ let previousHeight = heightAt(previous.x, previous.y);
736
+ for (let index = 1; index <= count; index += 1) {
737
+ const point = getFencePointAt(fence, index / count).point;
738
+ const height = heightAt(point.x, point.y);
739
+ if (Math.abs(height - previousHeight) >= 0.12) {
740
+ const t = (index - 0.5) / count;
741
+ const last = transitions.at(-1);
742
+ if (last && (t - last.t) * length < 0.18) {
743
+ last.after = height;
744
+ }
745
+ else {
746
+ transitions.push({ t, before: previousHeight, after: height });
747
+ }
748
+ }
749
+ previous = point;
750
+ previousHeight = height;
751
+ }
752
+ return transitions;
753
+ }
754
+ function createFencePathProgress(fence) {
755
+ const length = getFenceCenterlineLength(fence);
756
+ const count = Math.max(2, Math.ceil(length / 0.15));
757
+ const points = Array.from({ length: count + 1 }, (_, index) => getFencePointAt(fence, index / count).point);
758
+ return (x, z) => {
759
+ let bestT = 0;
760
+ let bestDistance = Number.POSITIVE_INFINITY;
761
+ for (let index = 1; index <= count; index += 1) {
762
+ const previous = points[index - 1];
763
+ const next = points[index];
764
+ const dx = next.x - previous.x;
765
+ const dz = next.y - previous.y;
766
+ const squared = dx * dx + dz * dz;
767
+ const fraction = squared > 1e-8
768
+ ? THREE.MathUtils.clamp(((x - previous.x) * dx + (z - previous.y) * dz) / squared, 0, 1)
769
+ : 0;
770
+ const px = previous.x + dx * fraction;
771
+ const pz = previous.y + dz * fraction;
772
+ const distance = (x - px) ** 2 + (z - pz) ** 2;
773
+ if (distance < bestDistance) {
774
+ bestDistance = distance;
775
+ bestT = (index - 1 + fraction) / count;
776
+ }
777
+ }
778
+ return bestT;
779
+ };
780
+ }
781
+ function transitionRailHeight(fence, rawHeight, transitions) {
782
+ const length = getFenceCenterlineLength(fence);
783
+ const halfSpan = Math.max(0.1, fence.transitionWidth / 2) / Math.max(length, 0.01);
784
+ const progress = createFencePathProgress(fence);
785
+ const ramps = transitions.map((transition) => {
786
+ const left = Math.max(0, transition.t - halfSpan);
787
+ const right = Math.min(1, transition.t + halfSpan);
788
+ const beforePoint = getFencePointAt(fence, left).point;
789
+ const afterPoint = getFencePointAt(fence, right).point;
790
+ return {
791
+ left,
792
+ right,
793
+ before: rawHeight(beforePoint.x, beforePoint.y),
794
+ after: rawHeight(afterPoint.x, afterPoint.y),
795
+ };
796
+ });
797
+ return (x, z) => {
798
+ const t = progress(x, z);
799
+ for (const ramp of ramps) {
800
+ if (t < ramp.left || t > ramp.right)
801
+ continue;
802
+ return THREE.MathUtils.lerp(ramp.before, ramp.after, (t - ramp.left) / Math.max(ramp.right - ramp.left, 1e-6));
803
+ }
804
+ return rawHeight(x, z);
805
+ };
806
+ }
807
+ export function createFenceRailHeightSampler(fence, heightAt) {
808
+ if (fence.surfaceMode === 'level' || fence.transitionMode !== 'slope')
809
+ return heightAt;
810
+ const transitions = fenceHeightTransitions(fence, heightAt);
811
+ return transitions.length > 0 ? transitionRailHeight(fence, heightAt, transitions) : heightAt;
812
+ }
813
+ function addTransitionPosts(fence, parts, transitions) {
814
+ const length = getFenceCenterlineLength(fence);
815
+ const width = Math.max(fence.postSize, fence.transitionMode === 'step' ? 0.22 : 0.07);
816
+ const depth = Math.max(fence.thickness, width);
817
+ for (const transition of transitions) {
818
+ if (fence.transitionMode === 'break') {
819
+ const offset = Math.min(0.08 / Math.max(length, 0.01), 0.2);
820
+ for (const t of [Math.max(0, transition.t - offset), Math.min(1, transition.t + offset)]) {
821
+ const frame = getFencePointAt(fence, t);
822
+ parts.posts.push({
823
+ position: [frame.point.x, fence.height / 2, frame.point.y],
824
+ rotationY: -frame.tangentAngle,
825
+ scale: [width, fence.height, depth],
826
+ });
827
+ }
828
+ }
829
+ else {
830
+ const frame = getFencePointAt(fence, transition.t);
831
+ const rise = Math.abs(transition.after - transition.before);
832
+ parts.posts.push({
833
+ position: [frame.point.x, (fence.height + rise) / 2, frame.point.y],
834
+ rotationY: -frame.tangentAngle,
835
+ scale: [width, fence.height + rise, depth],
836
+ heightOverride: Math.min(transition.before, transition.after),
837
+ });
838
+ }
839
+ }
840
+ }
841
+ function omitCrossingParts(parts, transitions) {
842
+ return parts.filter((part) => part.startT === undefined ||
843
+ part.endT === undefined ||
844
+ !transitions.some((transition) => transition.t > part.startT && transition.t < part.endT));
845
+ }
846
+ function cutFenceParts(fence, parts, features, heightAt, renderFeatures = true, gateLeafGeometries) {
847
+ const progress = createFencePathProgress(fence);
848
+ for (const slot of ['posts', 'infill', 'base', 'rail']) {
849
+ parts[slot] = parts[slot].flatMap((part) => {
850
+ if (part.startT === undefined || part.endT === undefined || !part.curveBlock) {
851
+ const t = progress(part.position[0], part.position[2]);
852
+ return features.some((feature) => t >= feature.startT && t <= feature.endT) ? [] : [part];
853
+ }
854
+ const ranges = [];
855
+ let cursor = part.startT;
856
+ for (const feature of features) {
857
+ if (feature.endT <= cursor || feature.startT >= part.endT)
858
+ continue;
859
+ if (feature.startT > cursor)
860
+ ranges.push([cursor, Math.min(feature.startT, part.endT)]);
861
+ cursor = Math.max(cursor, feature.endT);
862
+ if (cursor >= part.endT)
863
+ break;
864
+ }
865
+ if (cursor < part.endT)
866
+ ranges.push([cursor, part.endT]);
867
+ if (ranges.length === 1 && ranges[0][0] === part.startT && ranges[0][1] === part.endT)
868
+ return [part];
869
+ part.geometry?.dispose();
870
+ return ranges.flatMap(([start, end]) => {
871
+ const block = part.curveBlock;
872
+ const clipped = createFenceCurveBlockPart(fence, start, end, block.centerY, block.height, block.depth, block.lateralOffset);
873
+ return clipped ? [clipped] : [];
874
+ });
875
+ });
876
+ }
877
+ if (!renderFeatures)
878
+ return;
879
+ for (const feature of features) {
880
+ const matchesFence = feature.matchFenceStyle !== false;
881
+ const styleDefaults = getStyleDefaults(fence.style);
882
+ const postWidth = matchesFence
883
+ ? fence.style === 'picket'
884
+ ? Math.max(fence.postSize, 0.02)
885
+ : fence.style === 'horizontal'
886
+ ? Math.max(fence.postSize * 1.4, 0.04)
887
+ : Math.max(fence.postSize * styleDefaults.postFactor, 0.01)
888
+ : Math.max(fence.postSize, 0.05);
889
+ const postDepth = matchesFence && fence.style === 'horizontal'
890
+ ? postWidth
891
+ : matchesFence && fence.style !== 'picket'
892
+ ? Math.max(Math.max(fence.thickness, 0.03) * 0.35 - 0.001, 0.011)
893
+ : Math.max(fence.thickness, postWidth);
894
+ const { bottom, height, postBottom, postHeight } = getFenceFeatureDimensions(fence, feature);
895
+ const top = bottom + height;
896
+ if (feature.showPosts !== false)
897
+ for (const t of [feature.startT, feature.endT]) {
898
+ const frame = getFencePointAt(fence, t);
899
+ parts.posts.push({
900
+ position: [frame.point.x, postBottom + postHeight / 2, frame.point.y],
901
+ rotationY: -frame.tangentAngle,
902
+ scale: [postWidth, postHeight, postDepth],
903
+ });
904
+ if (matchesFence &&
905
+ fence.postCap !== 'none' &&
906
+ (fence.style === 'picket' || fence.style === 'horizontal')) {
907
+ const horizontal = fence.style === 'horizontal';
908
+ const capHeight = horizontal
909
+ ? Math.max(postWidth * 0.32, 0.03)
910
+ : postWidth * (fence.postCap === 'flat' ? 0.2 : 0.5);
911
+ const capScale = horizontal ? 1.22 : 1.5;
912
+ parts.posts.push({
913
+ position: [
914
+ frame.point.x,
915
+ postBottom +
916
+ postHeight +
917
+ (horizontal && fence.postCap === 'pyramid' ? capHeight * 0.9 : capHeight / 2),
918
+ frame.point.y,
919
+ ],
920
+ rotationY: -frame.tangentAngle,
921
+ scale: [
922
+ postWidth * (horizontal && fence.postCap === 'pyramid' ? 1.18 : capScale),
923
+ horizontal && fence.postCap === 'pyramid' ? capHeight * 1.8 : capHeight,
924
+ postDepth * (horizontal && fence.postCap === 'pyramid' ? 1.18 : capScale),
925
+ ],
926
+ shape: fence.postCap === 'pyramid' ? 'pyramid' : 'box',
927
+ });
928
+ }
929
+ }
930
+ if (feature.kind !== 'gate')
931
+ continue;
932
+ const style = matchesFence
933
+ ? fence.style
934
+ : !feature.style || feature.style === 'match'
935
+ ? 'picket'
936
+ : feature.style;
937
+ const hostPostWidth = fence.style === 'picket'
938
+ ? Math.max(fence.postSize, 0.02)
939
+ : fence.style === 'horizontal'
940
+ ? Math.max(fence.postSize * 1.4, 0.04)
941
+ : Math.max(fence.postSize * styleDefaults.postFactor, 0.01);
942
+ const member = Math.min(matchesFence
943
+ ? fence.style === 'rail'
944
+ ? Math.max(fence.topRailHeight * styleDefaults.topFactor, 0.01)
945
+ : Math.max(fence.picketWidth, hostPostWidth * 0.55)
946
+ : (feature.frameWidth ?? 0.055), height / 4);
947
+ const matchedDepth = fence.style === 'picket'
948
+ ? Math.max(Math.min(fence.thickness * 0.5, 0.052), 0.01)
949
+ : fence.style === 'slat'
950
+ ? Math.max(fence.thickness * 0.35, 0.011)
951
+ : Math.max(fence.thickness, 0.03);
952
+ const depth = matchesFence ? matchedDepth : (feature.thickness ?? 0.06);
953
+ const animatedLeaves = getFenceGateLeaves(fence, feature);
954
+ const fullyOpenLeaves = getFenceGateLeaves(fence, { ...feature, openAngle: 90 });
955
+ const leaves = gateLeafGeometries
956
+ ? getFenceGateLeaves(fence, { ...feature, openAngle: 0 })
957
+ : animatedLeaves;
958
+ // A swinging leaf is rigid: both leaves share the higher jamb elevation.
959
+ const support = Math.max(...[feature.startT, feature.endT].map((t) => {
960
+ const point = getFencePointAt(fence, t).point;
961
+ return heightAt?.(point.x, point.y) ?? 0;
962
+ }));
963
+ for (const [leafIndex, leaf] of leaves.entries()) {
964
+ const leafParts = [];
965
+ const width = leaf.width;
966
+ const frame = Math.min(member, width / 4);
967
+ const innerWidth = width - frame * 2;
968
+ const innerHeight = height - frame * 2;
969
+ const cos = Math.cos(leaf.rotation);
970
+ const sin = Math.sin(leaf.rotation);
971
+ const add = (x, y, z, w, h, d, rotationZ = 0, picket = false) => {
972
+ leafParts.push({
973
+ position: [leaf.hinge.x + cos * x - sin * z, y, leaf.hinge.y + sin * x + cos * z],
974
+ rotationY: -leaf.rotation,
975
+ rotationZ,
976
+ scale: [w, h, d],
977
+ heightOverride: support,
978
+ shape: picket ? 'picket' : 'box',
979
+ picketTop: fence.picketTop,
980
+ });
981
+ };
982
+ for (const x of [frame / 2, width - frame / 2])
983
+ add(x, bottom + height / 2, 0, frame, height, depth);
984
+ const railThickness = matchesFence && style === 'rail'
985
+ ? Math.max(fence.topRailHeight * styleDefaults.topFactor, 0.01)
986
+ : frame;
987
+ for (const y of [
988
+ bottom + railThickness / 2,
989
+ top + (style === 'rail' && matchesFence ? railThickness / 2 : -railThickness / 2),
990
+ ])
991
+ add(width / 2, y, 0, innerWidth, railThickness, depth);
992
+ const matchedSpacing = fence.style === 'picket'
993
+ ? fence.picketSpacing
994
+ : fence.style === 'slat'
995
+ ? fence.postSpacing * styleDefaults.spacingFactor
996
+ : fence.style === 'horizontal'
997
+ ? 0.155 + Math.max(fence.slatGap, 0)
998
+ : 0.15;
999
+ const spacing = Math.max(matchesFence ? matchedSpacing : (feature.spacing ?? 0.15), 0.04);
1000
+ const matchedBoardWidth = fence.style === 'picket'
1001
+ ? fence.picketWidth
1002
+ : fence.style === 'slat'
1003
+ ? hostPostWidth
1004
+ : fence.style === 'horizontal'
1005
+ ? 0.14
1006
+ : 0.055;
1007
+ const board = Math.min(matchesFence ? matchedBoardWidth : (feature.boardWidth ?? 0.055), innerWidth);
1008
+ if (style === 'privacy')
1009
+ add(width / 2, bottom + height / 2, 0, innerWidth, innerHeight, depth * 0.6);
1010
+ else if (style === 'horizontal') {
1011
+ const count = Math.max(1, Math.floor(innerHeight / spacing));
1012
+ const boardHeight = Math.min(board, (innerHeight / count) * 0.8);
1013
+ for (let i = 0; i < count; i++)
1014
+ add(width / 2, bottom + frame + (innerHeight * (i + 0.5)) / count, 0, innerWidth, boardHeight, depth * 0.65);
1015
+ }
1016
+ else if (style !== 'rail') {
1017
+ const count = Math.max(1, Math.floor(innerWidth / Math.max(spacing, board + 0.015)));
1018
+ for (let i = 0; i < count; i++)
1019
+ add(frame + (innerWidth * (i + 0.5)) / count, bottom + height / 2, 0, Math.min(board, (innerWidth / count) * 0.8), innerHeight, depth * 0.65, 0, style === 'picket');
1020
+ }
1021
+ if (feature.brace && feature.brace !== 'none') {
1022
+ const angle = Math.atan2(innerHeight, innerWidth);
1023
+ add(width / 2, bottom + height / 2, depth * 0.65, Math.hypot(innerWidth, innerHeight), frame * 0.7, depth * 0.35, angle);
1024
+ if (feature.brace === 'cross')
1025
+ add(width / 2, bottom + height / 2, -depth * 0.65, Math.hypot(innerWidth, innerHeight), frame * 0.7, depth * 0.35, -angle);
1026
+ }
1027
+ if (feature.showHardware !== false) {
1028
+ for (const y of [bottom + height * 0.25, bottom + height * 0.75])
1029
+ add(0, y, depth * 0.7, frame * 1.3, 0.075, depth * 0.6);
1030
+ add(width - frame, bottom + height * 0.55, depth * 0.8, Math.min(0.12, width / 4), 0.025, depth * 0.6);
1031
+ }
1032
+ if (gateLeafGeometries) {
1033
+ gateLeafGeometries.push({
1034
+ geometry: mergeFenceParts(leafParts, heightAt),
1035
+ hinge: { x: leaf.hinge.x, z: leaf.hinge.y },
1036
+ rotationY: -(animatedLeaves[leafIndex].rotation - leaf.rotation),
1037
+ openRotationY: -(fullyOpenLeaves[leafIndex].rotation - leaf.rotation),
1038
+ });
1039
+ }
1040
+ else {
1041
+ parts.infill.push(...leafParts);
1042
+ }
1043
+ }
1044
+ }
1045
+ }
1046
+ function mergeFenceParts(parts, heightAt) {
1047
+ // An empty slot group (e.g. infill with showInfill off, or base on a floating
1048
+ // fence) must not reach mergeGeometries — it throws on an empty array. The
1049
+ // empty geometry has no position attribute, so the renderer skips its mesh.
1050
+ if (parts.length === 0)
1051
+ return new THREE.BufferGeometry();
1052
+ const geometries = parts.map((part) => {
1053
+ const geometry = createFencePartGeometry(part);
1054
+ if (heightAt) {
1055
+ const positions = geometry.getAttribute('position');
1056
+ if (positions) {
1057
+ for (let index = 0; index < positions.count; index += 1) {
1058
+ const x = positions.getX(index);
1059
+ const z = positions.getZ(index);
1060
+ const lift = part.heightOverride !== undefined
1061
+ ? part.heightOverride
1062
+ : part.geometry
1063
+ ? heightAt(x, z)
1064
+ : heightAt(part.position[0], part.position[2]);
1065
+ positions.setY(index, positions.getY(index) + lift);
1066
+ }
1067
+ positions.needsUpdate = true;
1068
+ geometry.computeVertexNormals();
1069
+ }
1070
+ }
1071
+ if (!geometry.index)
1072
+ return geometry;
1073
+ const nonIndexed = geometry.toNonIndexed();
1074
+ geometry.dispose();
1075
+ return nonIndexed;
1076
+ });
1077
+ const merged = mergeGeometries(geometries, false) ?? new THREE.BufferGeometry();
1078
+ geometries.forEach((geometry) => {
1079
+ geometry.dispose();
1080
+ });
1081
+ const mergedUv = merged.getAttribute('uv');
1082
+ if (mergedUv) {
1083
+ merged.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(mergedUv.array), 2));
1084
+ }
1085
+ merged.computeVertexNormals();
1086
+ return merged;
1087
+ }
1088
+ /**
1089
+ * Geometry split by paint slot — posts, infill, base, rail — each a separate
1090
+ * merged BufferGeometry (empty ones included) so the fence renderer can give
1091
+ * each its own material + `userData.slotId`. Slots match the panel's build
1092
+ * options 1:1.
1093
+ */
1094
+ export function generateFenceSlotGeometries(fence, heightAt, mode = 'all', omitEndpointPosts, cornerNeighbors, gateLeafGeometries) {
1095
+ const parts = mode === 'features'
1096
+ ? { posts: [], infill: [], base: [], rail: [] }
1097
+ : createFenceParts(fence, omitEndpointPosts);
1098
+ if (omitEndpointPosts?.size) {
1099
+ parts.posts = parts.posts.filter((part) => !part.endpoint || !omitEndpointPosts.has(part.endpoint));
1100
+ }
1101
+ if (cornerNeighbors) {
1102
+ for (const slot of ['base', 'rail', 'infill']) {
1103
+ if (slot === 'infill' && fence.style !== 'horizontal')
1104
+ continue;
1105
+ for (const part of parts[slot]) {
1106
+ for (const endpoint of ['start', 'end']) {
1107
+ const neighbor = cornerNeighbors[endpoint];
1108
+ if (neighbor)
1109
+ miterFencePartEnd(fence, part, endpoint, neighbor);
1110
+ }
1111
+ }
1112
+ }
1113
+ }
1114
+ const features = resolveFenceFeatures(fence);
1115
+ if (features.length > 0)
1116
+ cutFenceParts(fence, parts, features, heightAt, mode !== 'body', gateLeafGeometries);
1117
+ const transitions = mode !== 'features' && heightAt && fence.surfaceMode !== 'level'
1118
+ ? fenceHeightTransitions(fence, heightAt)
1119
+ : [];
1120
+ if (heightAt && transitions.length > 0 && fence.transitionMode !== 'slope') {
1121
+ addTransitionPosts(fence, parts, transitions);
1122
+ parts.rail = omitCrossingParts(parts.rail, transitions);
1123
+ parts.base = omitCrossingParts(parts.base, transitions);
1124
+ if (fence.style === 'horizontal')
1125
+ parts.infill = omitCrossingParts(parts.infill, transitions);
1126
+ }
1127
+ const railHeightAt = heightAt && transitions.length > 0 && fence.transitionMode === 'slope'
1128
+ ? transitionRailHeight(fence, heightAt, transitions)
1129
+ : heightAt;
1130
+ return {
1131
+ posts: mergeFenceParts(parts.posts, heightAt),
1132
+ infill: mergeFenceParts(parts.infill, fence.style === 'horizontal' ? railHeightAt : heightAt),
1133
+ base: mergeFenceParts(parts.base, railHeightAt),
1134
+ rail: mergeFenceParts(parts.rail, railHeightAt),
1135
+ };
1136
+ }
1137
+ export function generateFenceGeometry(fence, heightAt) {
1138
+ const slots = generateFenceSlotGeometries(fence, heightAt);
1139
+ const geometries = Object.values(slots).filter((geometry) => geometry.getAttribute('position'));
1140
+ const merged = mergeGeometries(geometries, false) ?? new THREE.BufferGeometry();
1141
+ for (const geometry of Object.values(slots))
1142
+ geometry.dispose();
1143
+ return merged;
1144
+ }