@pascal-app/nodes 1.0.6 → 1.0.7

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 (566) hide show
  1. package/README.md +1 -0
  2. package/dist/beam/actions/move-endpoint.d.ts +24 -0
  3. package/dist/beam/actions/move-endpoint.d.ts.map +1 -0
  4. package/dist/beam/actions/move-endpoint.js +54 -0
  5. package/dist/beam/definition.d.ts +14 -0
  6. package/dist/beam/definition.d.ts.map +1 -0
  7. package/dist/beam/definition.js +173 -0
  8. package/dist/beam/draft-settings.d.ts +17 -0
  9. package/dist/beam/draft-settings.d.ts.map +1 -0
  10. package/dist/beam/draft-settings.js +81 -0
  11. package/dist/beam/drafting.d.ts +35 -0
  12. package/dist/beam/drafting.d.ts.map +1 -0
  13. package/dist/beam/drafting.js +103 -0
  14. package/dist/beam/floorplan-affordances.d.ts +8 -0
  15. package/dist/beam/floorplan-affordances.d.ts.map +1 -0
  16. package/dist/beam/floorplan-affordances.js +40 -0
  17. package/dist/beam/floorplan-move.d.ts +11 -0
  18. package/dist/beam/floorplan-move.d.ts.map +1 -0
  19. package/dist/beam/floorplan-move.js +54 -0
  20. package/dist/beam/floorplan.d.ts +11 -0
  21. package/dist/beam/floorplan.d.ts.map +1 -0
  22. package/dist/beam/floorplan.js +91 -0
  23. package/dist/beam/geometry.d.ts +19 -0
  24. package/dist/beam/geometry.d.ts.map +1 -0
  25. package/dist/beam/geometry.js +63 -0
  26. package/dist/beam/index.d.ts +3 -0
  27. package/dist/beam/index.d.ts.map +1 -0
  28. package/dist/beam/index.js +2 -0
  29. package/dist/beam/move-endpoint-tool.d.ts +21 -0
  30. package/dist/beam/move-endpoint-tool.d.ts.map +1 -0
  31. package/dist/beam/move-endpoint-tool.js +49 -0
  32. package/dist/beam/move-tool.d.ts +12 -0
  33. package/dist/beam/move-tool.d.ts.map +1 -0
  34. package/dist/beam/move-tool.js +114 -0
  35. package/dist/beam/panel-model.d.ts +28 -0
  36. package/dist/beam/panel-model.d.ts.map +1 -0
  37. package/dist/beam/panel-model.js +123 -0
  38. package/dist/beam/panel.d.ts +13 -0
  39. package/dist/beam/panel.d.ts.map +1 -0
  40. package/dist/beam/panel.js +107 -0
  41. package/dist/beam/parametrics.d.ts +3 -0
  42. package/dist/beam/parametrics.d.ts.map +1 -0
  43. package/dist/beam/parametrics.js +4 -0
  44. package/dist/beam/renderer.d.ts +13 -0
  45. package/dist/beam/renderer.d.ts.map +1 -0
  46. package/dist/beam/renderer.js +36 -0
  47. package/dist/beam/schema.d.ts +2 -0
  48. package/dist/beam/schema.d.ts.map +1 -0
  49. package/dist/beam/schema.js +1 -0
  50. package/dist/beam/tool.d.ts +13 -0
  51. package/dist/beam/tool.d.ts.map +1 -0
  52. package/dist/beam/tool.js +152 -0
  53. package/dist/box-vent/panel.d.ts.map +1 -1
  54. package/dist/box-vent/panel.js +8 -7
  55. package/dist/cabinet/batchable.d.ts +10 -0
  56. package/dist/cabinet/batchable.d.ts.map +1 -0
  57. package/dist/cabinet/batchable.js +12 -0
  58. package/dist/cabinet/compartment-card.d.ts.map +1 -1
  59. package/dist/cabinet/compartment-card.js +142 -46
  60. package/dist/cabinet/countertop-layout.d.ts +5 -0
  61. package/dist/cabinet/countertop-layout.d.ts.map +1 -1
  62. package/dist/cabinet/definition.d.ts.map +1 -1
  63. package/dist/cabinet/definition.js +3 -0
  64. package/dist/cabinet/dimension-input.d.ts +6 -0
  65. package/dist/cabinet/dimension-input.d.ts.map +1 -0
  66. package/dist/cabinet/dimension-input.js +8 -0
  67. package/dist/cabinet/essential-controls.d.ts.map +1 -1
  68. package/dist/cabinet/essential-controls.js +82 -17
  69. package/dist/cabinet/index.d.ts +1 -1
  70. package/dist/cabinet/index.d.ts.map +1 -1
  71. package/dist/cabinet/index.js +1 -1
  72. package/dist/cabinet/kitchen-catalog.d.ts +17 -4
  73. package/dist/cabinet/kitchen-catalog.d.ts.map +1 -1
  74. package/dist/cabinet/kitchen-catalog.js +45 -7
  75. package/dist/cabinet/kitchen-collection.d.ts.map +1 -1
  76. package/dist/cabinet/kitchen-collection.js +21 -5
  77. package/dist/cabinet/kitchen-module-actions.d.ts.map +1 -1
  78. package/dist/cabinet/kitchen-module-actions.js +107 -14
  79. package/dist/cabinet/neighbor-placement.d.ts +10 -0
  80. package/dist/cabinet/neighbor-placement.d.ts.map +1 -1
  81. package/dist/cabinet/panel.d.ts.map +1 -1
  82. package/dist/cabinet/panel.js +127 -50
  83. package/dist/cabinet/run-finishing.d.ts +5 -0
  84. package/dist/cabinet/run-finishing.d.ts.map +1 -1
  85. package/dist/cabinet/run-ops.d.ts +5 -0
  86. package/dist/cabinet/run-ops.d.ts.map +1 -1
  87. package/dist/cabinet/run-panel.d.ts.map +1 -1
  88. package/dist/cabinet/run-panel.js +203 -52
  89. package/dist/cabinet/tool.d.ts.map +1 -1
  90. package/dist/cabinet/tool.js +8 -5
  91. package/dist/ceiling/build-up-section.d.ts +7 -0
  92. package/dist/ceiling/build-up-section.d.ts.map +1 -0
  93. package/dist/ceiling/build-up-section.js +57 -0
  94. package/dist/ceiling/definition.d.ts.map +1 -1
  95. package/dist/ceiling/definition.js +3 -1
  96. package/dist/ceiling/floorplan-affordances.d.ts +112 -0
  97. package/dist/ceiling/floorplan-affordances.d.ts.map +1 -1
  98. package/dist/ceiling/materials.d.ts +5 -0
  99. package/dist/ceiling/materials.d.ts.map +1 -1
  100. package/dist/ceiling/materials.js +17 -0
  101. package/dist/ceiling/panel-model.d.ts +13 -0
  102. package/dist/ceiling/panel-model.d.ts.map +1 -0
  103. package/dist/ceiling/panel-model.js +49 -0
  104. package/dist/ceiling/panel.d.ts.map +1 -1
  105. package/dist/ceiling/panel.js +82 -18
  106. package/dist/ceiling/renderer.d.ts.map +1 -1
  107. package/dist/ceiling/renderer.js +10 -4
  108. package/dist/ceiling/tool.d.ts.map +1 -1
  109. package/dist/ceiling/tool.js +24 -4
  110. package/dist/chimney/panel.d.ts.map +1 -1
  111. package/dist/chimney/panel.js +113 -41
  112. package/dist/column/definition.d.ts.map +1 -1
  113. package/dist/column/definition.js +3 -0
  114. package/dist/column/floorplan.d.ts.map +1 -1
  115. package/dist/column/floorplan.js +17 -0
  116. package/dist/column/hosted-resize.d.ts +28 -1
  117. package/dist/column/hosted-resize.d.ts.map +1 -1
  118. package/dist/column/panel.d.ts.map +1 -1
  119. package/dist/column/panel.js +125 -37
  120. package/dist/column/post-options.d.ts +10 -0
  121. package/dist/column/post-options.d.ts.map +1 -0
  122. package/dist/column/post-options.js +71 -0
  123. package/dist/column/post-placement.d.ts +78 -0
  124. package/dist/column/post-placement.d.ts.map +1 -0
  125. package/dist/column/post-placement.js +157 -0
  126. package/dist/column/post-section.d.ts +17 -0
  127. package/dist/column/post-section.d.ts.map +1 -0
  128. package/dist/column/post-section.js +95 -0
  129. package/dist/column/post-tool.d.ts +11 -0
  130. package/dist/column/post-tool.d.ts.map +1 -0
  131. package/dist/column/post-tool.js +94 -0
  132. package/dist/column/tool.d.ts +3 -10
  133. package/dist/column/tool.d.ts.map +1 -1
  134. package/dist/column/tool.js +13 -1
  135. package/dist/cupola/panel.d.ts.map +1 -1
  136. package/dist/cupola/panel.js +14 -7
  137. package/dist/door/actions.d.ts +7 -0
  138. package/dist/door/actions.d.ts.map +1 -0
  139. package/dist/door/actions.js +32 -0
  140. package/dist/door/contextual-dimensions.d.ts.map +1 -1
  141. package/dist/door/contextual-dimensions.js +11 -1
  142. package/dist/door/definition.d.ts.map +1 -1
  143. package/dist/door/definition.js +34 -7
  144. package/dist/door/floorplan-affordances.d.ts.map +1 -1
  145. package/dist/door/floorplan-affordances.js +5 -2
  146. package/dist/door/floorplan-move.d.ts.map +1 -1
  147. package/dist/door/floorplan-move.js +28 -6
  148. package/dist/door/floorplan.d.ts +2 -0
  149. package/dist/door/floorplan.d.ts.map +1 -1
  150. package/dist/door/floorplan.js +89 -16
  151. package/dist/door/move-tool.d.ts.map +1 -1
  152. package/dist/door/move-tool.js +60 -16
  153. package/dist/door/paint.d.ts +2 -7
  154. package/dist/door/paint.d.ts.map +1 -1
  155. package/dist/door/paint.js +26 -2
  156. package/dist/door/panel.d.ts.map +1 -1
  157. package/dist/door/panel.js +242 -90
  158. package/dist/door/parametrics.js +1 -1
  159. package/dist/door/placement.d.ts.map +1 -1
  160. package/dist/door/placement.js +9 -2
  161. package/dist/door/swing.d.ts +22 -0
  162. package/dist/door/swing.d.ts.map +1 -0
  163. package/dist/door/swing.js +36 -0
  164. package/dist/door/tool.d.ts.map +1 -1
  165. package/dist/door/tool.js +76 -29
  166. package/dist/dormer/panel-actions-section.d.ts.map +1 -1
  167. package/dist/dormer/panel-actions-section.js +3 -1
  168. package/dist/dormer/panel-position-section.d.ts.map +1 -1
  169. package/dist/dormer/panel-position-section.js +4 -3
  170. package/dist/dormer/panel-windows-section.d.ts.map +1 -1
  171. package/dist/dormer/panel-windows-section.js +38 -1
  172. package/dist/dormer/panel.d.ts.map +1 -1
  173. package/dist/dormer/panel.js +24 -18
  174. package/dist/downspout/inspector-editors.d.ts.map +1 -1
  175. package/dist/downspout/inspector-editors.js +3 -2
  176. package/dist/duct-fitting/inspector-editors.d.ts.map +1 -1
  177. package/dist/duct-fitting/inspector-editors.js +6 -1
  178. package/dist/duct-fitting/tool.d.ts.map +1 -1
  179. package/dist/duct-fitting/tool.js +11 -2
  180. package/dist/duct-terminal/tool.d.ts.map +1 -1
  181. package/dist/duct-terminal/tool.js +11 -2
  182. package/dist/elevator/panel.d.ts.map +1 -1
  183. package/dist/elevator/panel.js +41 -18
  184. package/dist/eyebrow-vent/panel.d.ts.map +1 -1
  185. package/dist/eyebrow-vent/panel.js +16 -6
  186. package/dist/fence/drafting.d.ts +2 -3
  187. package/dist/fence/drafting.d.ts.map +1 -1
  188. package/dist/fence/drafting.js +2 -3
  189. package/dist/fence/inspector-editors.d.ts.map +1 -1
  190. package/dist/fence/inspector-editors.js +64 -21
  191. package/dist/fence/move-endpoint-tool.d.ts.map +1 -1
  192. package/dist/fence/move-endpoint-tool.js +11 -2
  193. package/dist/fence/system.d.ts.map +1 -1
  194. package/dist/fence/system.js +13 -0
  195. package/dist/fence-feature/actions.d.ts.map +1 -1
  196. package/dist/fence-feature/actions.js +5 -2
  197. package/dist/fence-feature/inspector.d.ts.map +1 -1
  198. package/dist/fence-feature/inspector.js +150 -34
  199. package/dist/fence-feature/move-session.d.ts +1 -1
  200. package/dist/floor-opening/floorplan.d.ts +29 -6
  201. package/dist/floor-opening/floorplan.d.ts.map +1 -1
  202. package/dist/floor-opening/floorplan.js +172 -14
  203. package/dist/gutter/downspouts-panel.d.ts.map +1 -1
  204. package/dist/gutter/downspouts-panel.js +14 -2
  205. package/dist/hvac-equipment/tool.d.ts.map +1 -1
  206. package/dist/hvac-equipment/tool.js +3 -2
  207. package/dist/i18n/definition-messages.d.ts +5 -0
  208. package/dist/i18n/definition-messages.d.ts.map +1 -0
  209. package/dist/i18n/definition-messages.js +771 -0
  210. package/dist/index.d.ts +4 -1
  211. package/dist/index.d.ts.map +1 -1
  212. package/dist/index.js +6 -1
  213. package/dist/item/authored-params.d.ts.map +1 -1
  214. package/dist/item/authored-params.js +19 -4
  215. package/dist/item/floorplan.d.ts +1 -1
  216. package/dist/item/floorplan.d.ts.map +1 -1
  217. package/dist/item/panel.d.ts.map +1 -1
  218. package/dist/item/panel.js +9 -7
  219. package/dist/liquid-line/tool.d.ts.map +1 -1
  220. package/dist/liquid-line/tool.js +10 -3
  221. package/dist/measurement/tool.d.ts +1 -1
  222. package/dist/measurement/tool.d.ts.map +1 -1
  223. package/dist/measurement/tool.js +8 -8
  224. package/dist/pipe-fitting/tool.d.ts.map +1 -1
  225. package/dist/pipe-fitting/tool.js +3 -2
  226. package/dist/pipe-trap/tool.d.ts.map +1 -1
  227. package/dist/pipe-trap/tool.js +7 -2
  228. package/dist/procedural-item/renderer.d.ts.map +1 -1
  229. package/dist/procedural-item/renderer.js +8 -1
  230. package/dist/ridge-vent/panel.d.ts.map +1 -1
  231. package/dist/ridge-vent/panel.js +22 -9
  232. package/dist/roof/barrel-panel.d.ts.map +1 -1
  233. package/dist/roof/barrel-panel.js +7 -3
  234. package/dist/roof/definition.d.ts.map +1 -1
  235. package/dist/roof/definition.js +33 -3
  236. package/dist/roof/floorplan-overrides.d.ts +22 -0
  237. package/dist/roof/floorplan-overrides.d.ts.map +1 -0
  238. package/dist/roof/floorplan-overrides.js +46 -0
  239. package/dist/roof/floorplan-tool.d.ts +9 -0
  240. package/dist/roof/floorplan-tool.d.ts.map +1 -0
  241. package/dist/roof/floorplan-tool.js +168 -0
  242. package/dist/roof/floorplan.d.ts.map +1 -1
  243. package/dist/roof/floorplan.js +131 -41
  244. package/dist/roof/glazing-controls.d.ts +2 -1
  245. package/dist/roof/glazing-controls.d.ts.map +1 -1
  246. package/dist/roof/glazing-controls.js +51 -13
  247. package/dist/roof/glazing-panel-editor.d.ts.map +1 -1
  248. package/dist/roof/glazing-panel-editor.js +40 -13
  249. package/dist/roof/glazing-panel.d.ts.map +1 -1
  250. package/dist/roof/glazing-panel.js +73 -9
  251. package/dist/roof/paint.d.ts +19 -0
  252. package/dist/roof/paint.d.ts.map +1 -0
  253. package/dist/roof/paint.js +158 -0
  254. package/dist/roof/panel.d.ts.map +1 -1
  255. package/dist/roof/panel.js +90 -22
  256. package/dist/roof/renderer.d.ts.map +1 -1
  257. package/dist/roof/renderer.js +13 -2
  258. package/dist/roof/roof-footprint-source.d.ts +17 -0
  259. package/dist/roof/roof-footprint-source.d.ts.map +1 -1
  260. package/dist/roof/roof-footprint-source.js +14 -0
  261. package/dist/roof/roof-footprint.d.ts +36 -2
  262. package/dist/roof/roof-footprint.d.ts.map +1 -1
  263. package/dist/roof/roof-footprint.js +124 -2
  264. package/dist/roof/roof-materials.d.ts.map +1 -1
  265. package/dist/roof/roof-materials.js +10 -1
  266. package/dist/roof/roof-placement-mode.d.ts +0 -2
  267. package/dist/roof/roof-placement-mode.d.ts.map +1 -1
  268. package/dist/roof/roof-placement-mode.js +8 -6
  269. package/dist/roof/roof-plan-creases.d.ts +50 -0
  270. package/dist/roof/roof-plan-creases.d.ts.map +1 -0
  271. package/dist/roof/roof-plan-creases.js +194 -0
  272. package/dist/roof/roof-ridge-mode.d.ts +20 -0
  273. package/dist/roof/roof-ridge-mode.d.ts.map +1 -0
  274. package/dist/roof/roof-ridge-mode.js +16 -0
  275. package/dist/roof/tool.d.ts +17 -4
  276. package/dist/roof/tool.d.ts.map +1 -1
  277. package/dist/roof/tool.js +211 -113
  278. package/dist/roof/wall-catch.d.ts +106 -0
  279. package/dist/roof/wall-catch.d.ts.map +1 -0
  280. package/dist/roof/wall-catch.js +267 -0
  281. package/dist/roof-segment/build-up-section.d.ts +9 -0
  282. package/dist/roof-segment/build-up-section.d.ts.map +1 -0
  283. package/dist/roof-segment/build-up-section.js +191 -0
  284. package/dist/roof-segment/definition.d.ts.map +1 -1
  285. package/dist/roof-segment/definition.js +50 -5
  286. package/dist/roof-segment/floorplan-affordances.d.ts.map +1 -1
  287. package/dist/roof-segment/floorplan-affordances.js +68 -19
  288. package/dist/roof-segment/floorplan.d.ts.map +1 -1
  289. package/dist/roof-segment/floorplan.js +16 -12
  290. package/dist/roof-segment/paint.d.ts +3 -1
  291. package/dist/roof-segment/paint.d.ts.map +1 -1
  292. package/dist/roof-segment/paint.js +43 -2
  293. package/dist/roof-segment/panel-model.d.ts +23 -0
  294. package/dist/roof-segment/panel-model.d.ts.map +1 -0
  295. package/dist/roof-segment/panel-model.js +64 -0
  296. package/dist/roof-segment/panel.d.ts.map +1 -1
  297. package/dist/roof-segment/panel.js +96 -14
  298. package/dist/roof-segment/parametrics.d.ts.map +1 -1
  299. package/dist/roof-segment/parametrics.js +4 -0
  300. package/dist/roof-segment/renderer.d.ts.map +1 -1
  301. package/dist/roof-segment/renderer.js +26 -5
  302. package/dist/separator/definition.d.ts.map +1 -1
  303. package/dist/separator/definition.js +10 -12
  304. package/dist/separator/floorplan-affordances.d.ts +9 -0
  305. package/dist/separator/floorplan-affordances.d.ts.map +1 -0
  306. package/dist/separator/floorplan-affordances.js +23 -0
  307. package/dist/separator/floorplan.d.ts +9 -0
  308. package/dist/separator/floorplan.d.ts.map +1 -0
  309. package/dist/separator/floorplan.js +54 -0
  310. package/dist/separator/renderer.d.ts +4 -0
  311. package/dist/separator/renderer.d.ts.map +1 -1
  312. package/dist/separator/renderer.js +21 -6
  313. package/dist/separator/selection.d.ts +10 -0
  314. package/dist/separator/selection.d.ts.map +1 -0
  315. package/dist/separator/selection.js +35 -0
  316. package/dist/shared/build-up-controls.d.ts +88 -0
  317. package/dist/shared/build-up-controls.d.ts.map +1 -0
  318. package/dist/shared/build-up-controls.js +139 -0
  319. package/dist/shared/curtain-opening-limits.d.ts.map +1 -1
  320. package/dist/shared/curtain-opening-limits.js +44 -14
  321. package/dist/shared/item-interaction-actions.d.ts.map +1 -1
  322. package/dist/shared/item-interaction-actions.js +11 -2
  323. package/dist/shared/layer-thickness.d.ts +2 -0
  324. package/dist/shared/layer-thickness.d.ts.map +1 -0
  325. package/dist/shared/layer-thickness.js +3 -0
  326. package/dist/shared/move-roof-tool.d.ts.map +1 -1
  327. package/dist/shared/move-roof-tool.js +100 -5
  328. package/dist/shared/node-batch/batchable.d.ts +2 -0
  329. package/dist/shared/node-batch/batchable.d.ts.map +1 -1
  330. package/dist/shared/node-batch/batchable.js +4 -0
  331. package/dist/shared/node-batch/candidates.d.ts +21 -5
  332. package/dist/shared/node-batch/candidates.d.ts.map +1 -1
  333. package/dist/shared/node-batch/candidates.js +85 -50
  334. package/dist/shared/node-batch/system.d.ts.map +1 -1
  335. package/dist/shared/node-batch/system.js +24 -15
  336. package/dist/shared/opening-casing-move.d.ts +16 -0
  337. package/dist/shared/opening-casing-move.d.ts.map +1 -0
  338. package/dist/shared/opening-casing-move.js +33 -0
  339. package/dist/shared/opening-casing-section.d.ts +15 -0
  340. package/dist/shared/opening-casing-section.d.ts.map +1 -0
  341. package/dist/shared/opening-casing-section.js +38 -0
  342. package/dist/shared/opening-casing.d.ts +26 -0
  343. package/dist/shared/opening-casing.d.ts.map +1 -0
  344. package/dist/shared/opening-casing.js +80 -0
  345. package/dist/shared/opening-depth-position.d.ts +13 -0
  346. package/dist/shared/opening-depth-position.d.ts.map +1 -0
  347. package/dist/shared/opening-depth-position.js +34 -0
  348. package/dist/shared/opening-documentation.d.ts.map +1 -1
  349. package/dist/shared/opening-documentation.js +17 -8
  350. package/dist/shared/opening-guides-runtime.d.ts +2 -1
  351. package/dist/shared/opening-guides-runtime.d.ts.map +1 -1
  352. package/dist/shared/opening-guides-runtime.js +11 -7
  353. package/dist/shared/opening-labels.d.ts +11 -0
  354. package/dist/shared/opening-labels.d.ts.map +1 -0
  355. package/dist/shared/opening-labels.js +46 -0
  356. package/dist/shared/opening-placement-dimensions.d.ts.map +1 -1
  357. package/dist/shared/opening-placement-dimensions.js +11 -9
  358. package/dist/shared/opening-plane-offset.d.ts +4 -3
  359. package/dist/shared/opening-plane-offset.d.ts.map +1 -1
  360. package/dist/shared/opening-plane-offset.js +7 -4
  361. package/dist/shared/opening-swap.d.ts +8 -0
  362. package/dist/shared/opening-swap.d.ts.map +1 -0
  363. package/dist/shared/opening-swap.js +18 -0
  364. package/dist/shared/polygon-vertex-affordance.d.ts +19 -0
  365. package/dist/shared/polygon-vertex-affordance.d.ts.map +1 -1
  366. package/dist/shared/polygon-vertex-affordance.js +65 -6
  367. package/dist/shared/roof-opening-host.d.ts +2 -1
  368. package/dist/shared/roof-opening-host.d.ts.map +1 -1
  369. package/dist/shared/roof-opening-host.js +14 -5
  370. package/dist/shared/roof-wall-opening-cut.d.ts.map +1 -1
  371. package/dist/shared/roof-wall-opening-cut.js +14 -6
  372. package/dist/shared/roof-wall-opening-placement.d.ts +2 -1
  373. package/dist/shared/roof-wall-opening-placement.d.ts.map +1 -1
  374. package/dist/shared/roof-wall-opening-placement.js +6 -4
  375. package/dist/shared/run-hanger-inspector.d.ts.map +1 -1
  376. package/dist/shared/run-hanger-inspector.js +39 -5
  377. package/dist/shared/system-check-panel.d.ts.map +1 -1
  378. package/dist/shared/system-check-panel.js +94 -15
  379. package/dist/shared/wall-attach-target.d.ts.map +1 -1
  380. package/dist/shared/wall-attach-target.js +6 -3
  381. package/dist/shelf/definition.d.ts.map +1 -1
  382. package/dist/shelf/definition.js +2 -0
  383. package/dist/site/ground-hole.d.ts +3 -3
  384. package/dist/site/ground-hole.js +3 -3
  385. package/dist/site/ground-veil.d.ts +5 -12
  386. package/dist/site/ground-veil.d.ts.map +1 -1
  387. package/dist/site/ground-veil.js +9 -33
  388. package/dist/site/panel.d.ts.map +1 -1
  389. package/dist/site/panel.js +52 -12
  390. package/dist/site/renderer.d.ts.map +1 -1
  391. package/dist/site/renderer.js +52 -23
  392. package/dist/skylight/panel.d.ts.map +1 -1
  393. package/dist/skylight/panel.js +49 -19
  394. package/dist/slab/deck-geometry.d.ts +28 -0
  395. package/dist/slab/deck-geometry.d.ts.map +1 -0
  396. package/dist/slab/deck-geometry.js +320 -0
  397. package/dist/slab/definition.d.ts +2 -1
  398. package/dist/slab/definition.d.ts.map +1 -1
  399. package/dist/slab/definition.js +6 -1
  400. package/dist/slab/dependency-tracker.d.ts.map +1 -1
  401. package/dist/slab/dependency-tracker.js +3 -0
  402. package/dist/slab/floorplan-affordances.d.ts +500 -0
  403. package/dist/slab/floorplan-affordances.d.ts.map +1 -1
  404. package/dist/slab/geometry.d.ts.map +1 -1
  405. package/dist/slab/geometry.js +46 -9
  406. package/dist/slab/panel.d.ts.map +1 -1
  407. package/dist/slab/panel.js +97 -21
  408. package/dist/slab/renderer.d.ts +11 -0
  409. package/dist/slab/renderer.d.ts.map +1 -0
  410. package/dist/slab/renderer.js +19 -0
  411. package/dist/slab/system.d.ts.map +1 -1
  412. package/dist/slab/system.js +12 -1
  413. package/dist/solar-panel/panel.d.ts.map +1 -1
  414. package/dist/solar-panel/panel.js +49 -14
  415. package/dist/stair/construction-controls.d.ts +3 -1
  416. package/dist/stair/construction-controls.d.ts.map +1 -1
  417. package/dist/stair/construction-controls.js +75 -12
  418. package/dist/stair/paint.d.ts +6 -0
  419. package/dist/stair/paint.d.ts.map +1 -1
  420. package/dist/stair/panel.d.ts.map +1 -1
  421. package/dist/stair/panel.js +243 -51
  422. package/dist/stair/parametrics.d.ts.map +1 -1
  423. package/dist/stair/parametrics.js +7 -0
  424. package/dist/stair/placement-defaults.d.ts +18 -0
  425. package/dist/stair/placement-defaults.d.ts.map +1 -0
  426. package/dist/stair/placement-defaults.js +27 -0
  427. package/dist/stair/railing-controls.d.ts.map +1 -1
  428. package/dist/stair/railing-controls.js +78 -18
  429. package/dist/stair/renderer.d.ts.map +1 -1
  430. package/dist/stair/renderer.js +16 -10
  431. package/dist/stair/structure-controls.d.ts +18 -0
  432. package/dist/stair/structure-controls.d.ts.map +1 -0
  433. package/dist/stair/structure-controls.js +275 -0
  434. package/dist/stair/tool.d.ts.map +1 -1
  435. package/dist/stair/tool.js +13 -9
  436. package/dist/stair/use-stair-render-data.d.ts +41 -0
  437. package/dist/stair/use-stair-render-data.d.ts.map +1 -1
  438. package/dist/stair-segment/panel.d.ts.map +1 -1
  439. package/dist/stair-segment/panel.js +65 -18
  440. package/dist/turbine-vent/panel.d.ts.map +1 -1
  441. package/dist/turbine-vent/panel.js +14 -5
  442. package/dist/unit/unit-panel.d.ts.map +1 -1
  443. package/dist/unit/unit-panel.js +28 -12
  444. package/dist/wall/actions.d.ts +1 -1
  445. package/dist/wall/actions.d.ts.map +1 -1
  446. package/dist/wall/actions.js +17 -13
  447. package/dist/wall/construction-dimensions.d.ts +6 -0
  448. package/dist/wall/construction-dimensions.d.ts.map +1 -1
  449. package/dist/wall/construction-dimensions.js +8 -0
  450. package/dist/wall/contextual-dimensions.d.ts +6 -0
  451. package/dist/wall/contextual-dimensions.d.ts.map +1 -1
  452. package/dist/wall/contextual-dimensions.js +104 -17
  453. package/dist/wall/curtain-opening-frame.d.ts.map +1 -1
  454. package/dist/wall/curtain-opening-frame.js +6 -4
  455. package/dist/wall/curtain-wall-layout.d.ts +1 -1
  456. package/dist/wall/curtain-wall-layout.d.ts.map +1 -1
  457. package/dist/wall/curtain-wall-layout.js +6 -4
  458. package/dist/wall/curtain-wall-materials.d.ts.map +1 -1
  459. package/dist/wall/curtain-wall-materials.js +8 -3
  460. package/dist/wall/curtain-wall-panel.d.ts.map +1 -1
  461. package/dist/wall/curtain-wall-panel.js +241 -37
  462. package/dist/wall/curve-tool.d.ts.map +1 -1
  463. package/dist/wall/curve-tool.js +2 -3
  464. package/dist/wall/definition.d.ts.map +1 -1
  465. package/dist/wall/definition.js +20 -5
  466. package/dist/wall/floorplan-affordances.d.ts.map +1 -1
  467. package/dist/wall/floorplan-affordances.js +9 -6
  468. package/dist/wall/floorplan-move.d.ts.map +1 -1
  469. package/dist/wall/floorplan-move.js +66 -16
  470. package/dist/wall/floorplan.d.ts +4 -5
  471. package/dist/wall/floorplan.d.ts.map +1 -1
  472. package/dist/wall/floorplan.js +62 -46
  473. package/dist/wall/move-endpoint-tool.d.ts.map +1 -1
  474. package/dist/wall/move-endpoint-tool.js +1 -0
  475. package/dist/wall/move-tool.d.ts.map +1 -1
  476. package/dist/wall/move-tool.js +77 -12
  477. package/dist/wall/paint.d.ts +2 -2
  478. package/dist/wall/paint.d.ts.map +1 -1
  479. package/dist/wall/paint.js +11 -4
  480. package/dist/wall/panel-model.d.ts +17 -2
  481. package/dist/wall/panel-model.d.ts.map +1 -1
  482. package/dist/wall/panel-model.js +158 -18
  483. package/dist/wall/panel.d.ts +8 -0
  484. package/dist/wall/panel.d.ts.map +1 -1
  485. package/dist/wall/panel.js +228 -156
  486. package/dist/wall/parametrics.d.ts +1 -1
  487. package/dist/wall/parametrics.d.ts.map +1 -1
  488. package/dist/wall/parametrics.js +14 -1
  489. package/dist/wall/railing-wall-adapter.d.ts +7 -0
  490. package/dist/wall/railing-wall-adapter.d.ts.map +1 -0
  491. package/dist/wall/railing-wall-adapter.js +38 -0
  492. package/dist/wall/railing-wall-floorplan.d.ts +8 -0
  493. package/dist/wall/railing-wall-floorplan.d.ts.map +1 -0
  494. package/dist/wall/railing-wall-floorplan.js +27 -0
  495. package/dist/wall/railing-wall-geometry.d.ts +12 -0
  496. package/dist/wall/railing-wall-geometry.d.ts.map +1 -0
  497. package/dist/wall/railing-wall-geometry.js +51 -0
  498. package/dist/wall/railing-wall-guard.d.ts +81 -0
  499. package/dist/wall/railing-wall-guard.d.ts.map +1 -0
  500. package/dist/wall/railing-wall-guard.js +435 -0
  501. package/dist/wall/railing-wall-materials.d.ts +11 -0
  502. package/dist/wall/railing-wall-materials.d.ts.map +1 -0
  503. package/dist/wall/railing-wall-materials.js +31 -0
  504. package/dist/wall/railing-wall-panel-model.d.ts +30 -0
  505. package/dist/wall/railing-wall-panel-model.d.ts.map +1 -0
  506. package/dist/wall/railing-wall-panel-model.js +117 -0
  507. package/dist/wall/railing-wall-panel.d.ts +17 -0
  508. package/dist/wall/railing-wall-panel.d.ts.map +1 -0
  509. package/dist/wall/railing-wall-panel.js +42 -0
  510. package/dist/wall/rectangle-command.d.ts +79 -1
  511. package/dist/wall/rectangle-command.d.ts.map +1 -1
  512. package/dist/wall/rectangle-tool.d.ts.map +1 -1
  513. package/dist/wall/rectangle-tool.js +2 -0
  514. package/dist/wall/renderer.d.ts.map +1 -1
  515. package/dist/wall/renderer.js +3 -1
  516. package/dist/wall/skirting-floor-gaps.d.ts +8 -0
  517. package/dist/wall/skirting-floor-gaps.d.ts.map +1 -0
  518. package/dist/wall/skirting-floor-gaps.js +36 -0
  519. package/dist/wall/system.d.ts +0 -14
  520. package/dist/wall/system.d.ts.map +1 -1
  521. package/dist/wall/system.js +6 -1
  522. package/dist/wall/tool.d.ts +16 -0
  523. package/dist/wall/tool.d.ts.map +1 -1
  524. package/dist/wall/tool.js +160 -47
  525. package/dist/wall/treatments.d.ts +3 -0
  526. package/dist/wall/treatments.d.ts.map +1 -1
  527. package/dist/wall/treatments.js +30 -7
  528. package/dist/window/actions.d.ts +7 -0
  529. package/dist/window/actions.d.ts.map +1 -0
  530. package/dist/window/actions.js +29 -0
  531. package/dist/window/definition.d.ts.map +1 -1
  532. package/dist/window/definition.js +11 -2
  533. package/dist/window/floorplan-move.d.ts.map +1 -1
  534. package/dist/window/floorplan-move.js +5 -0
  535. package/dist/window/floorplan.d.ts +2 -0
  536. package/dist/window/floorplan.d.ts.map +1 -1
  537. package/dist/window/floorplan.js +58 -1
  538. package/dist/window/mechanism.d.ts.map +1 -1
  539. package/dist/window/mechanism.js +21 -1
  540. package/dist/window/move-tool.d.ts.map +1 -1
  541. package/dist/window/move-tool.js +10 -3
  542. package/dist/window/paint.d.ts +2 -7
  543. package/dist/window/paint.d.ts.map +1 -1
  544. package/dist/window/paint.js +31 -3
  545. package/dist/window/panel.d.ts.map +1 -1
  546. package/dist/window/panel.js +311 -69
  547. package/dist/window/placement.d.ts.map +1 -1
  548. package/dist/window/placement.js +5 -1
  549. package/dist/window/swing.d.ts +13 -0
  550. package/dist/window/swing.d.ts.map +1 -0
  551. package/dist/window/swing.js +16 -0
  552. package/dist/window/tool.d.ts.map +1 -1
  553. package/dist/window/tool.js +19 -14
  554. package/dist/zone/floorplan-affordances.d.ts +136 -0
  555. package/dist/zone/floorplan-affordances.d.ts.map +1 -1
  556. package/dist/zone/quantities-panel.d.ts.map +1 -1
  557. package/dist/zone/quantities-panel.js +34 -9
  558. package/dist/zone/renderer.d.ts.map +1 -1
  559. package/dist/zone/renderer.js +9 -5
  560. package/package.json +14 -14
  561. package/dist/site/generic-foundation-geometry.d.ts +0 -16
  562. package/dist/site/generic-foundation-geometry.d.ts.map +0 -1
  563. package/dist/site/generic-foundation-geometry.js +0 -75
  564. package/dist/site/generic-foundation.d.ts +0 -4
  565. package/dist/site/generic-foundation.d.ts.map +0 -1
  566. package/dist/site/generic-foundation.js +0 -91
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { getLevelDisplayName, isRoofOnStoreyWalls, planRoofsOffStorey, roofLevelAbove, useScene, } from '@pascal-app/core';
4
- import { ActionButton, ActionGroup, duplicateNodeAndPickUp, formatLinearMeasurement, PanelSection, PanelWrapper, parametricNodeUpdates, SegmentedControl, SliderControl, triggerSFX, useEditor, } from '@pascal-app/editor';
3
+ import { getRoofSegmentDeckReach, getSegmentSlopeFrame, isRoofOnStoreyWalls, planRoofsOffStorey, roofLevelAbove, roofTypeHasRakes, useLocale, usePascalI18n, useScene, } from '@pascal-app/core';
4
+ import { ActionButton, ActionGroup, duplicateNodeAndPickUp, formatLinearMeasurement, localizedLevelName, nodeLabel, PanelSection, PanelWrapper, parametricNodeUpdates, SegmentedControl, SliderControl, triggerSFX, useEditor, } from '@pascal-app/editor';
5
5
  import { useViewer } from '@pascal-app/viewer';
6
6
  import { Copy, Move, Plus, Trash2 } from 'lucide-react';
7
7
  import { useCallback, useState } from 'react';
@@ -9,6 +9,8 @@ import { useShallow } from 'zustand/react/shallow';
9
9
  import { RoofGlazingPanel } from '../roof/glazing-panel.js';
10
10
  import { commitSeatedRoofEdit } from './roof-level-target.js';
11
11
  export default function RoofPanel() {
12
+ const i18n = usePascalI18n();
13
+ const locale = useLocale();
12
14
  const [ventType, setVentType] = useState('box-vent');
13
15
  const selectedId = useViewer((s) => s.selection.selectedIds[0]);
14
16
  const unit = useViewer((s) => s.unit);
@@ -110,10 +112,17 @@ export default function RoofPanel() {
110
112
  const level = s.nodes[node.parentId];
111
113
  const above = roofLevelAbove(s.nodes, level.id);
112
114
  return {
113
- storey: getLevelDisplayName(level),
115
+ storey: localizedLevelName(level, i18n, locale),
114
116
  label: above.kind === 'roof'
115
- ? 'Move to the roof level'
116
- : `Move to ${getLevelDisplayName(above.level)}`,
117
+ ? i18n._({
118
+ id: 'node.roof.panel.moveToTheRoofLevel',
119
+ message: 'Move to the roof level',
120
+ })
121
+ : i18n._({
122
+ id: 'node.roof.panel.moveToLevel',
123
+ message: 'Move to {level}',
124
+ values: { level: localizedLevelName(above.level, i18n, locale) },
125
+ }),
117
126
  roofs: level.children.filter((id) => isRoofOnStoreyWalls(s.nodes, id)).length,
118
127
  };
119
128
  }));
@@ -143,6 +152,17 @@ export default function RoofPanel() {
143
152
  // Moved or turned onto the walls that carry it, it goes to the level above them.
144
153
  commitSeatedRoofEdit(parametricNodeUpdates(selectedId, updates), selectedId);
145
154
  }, [selectedId]);
155
+ // The eaves are each segment's: the roof's sliders set them on all of its segments at once.
156
+ const handleSegmentsUpdate = useCallback((patch) => {
157
+ if (!selectedId)
158
+ return;
159
+ const update = segments.flatMap((segment) => {
160
+ const data = patch(segment);
161
+ return data ? [{ id: segment.id, data: data }] : [];
162
+ });
163
+ if (update.length > 0)
164
+ commitSeatedRoofEdit(update, selectedId);
165
+ }, [segments, selectedId]);
146
166
  const handleClose = useCallback(() => {
147
167
  setSelection({ selectedIds: [] });
148
168
  }, [setSelection]);
@@ -194,14 +214,47 @@ export default function RoofPanel() {
194
214
  }, []);
195
215
  if (!(node && node.type === 'roof' && selectedId))
196
216
  return null;
197
- return (_jsxs(PanelWrapper, { icon: "/icons/roof.webp", onClose: handleClose, title: node.name || 'Roof', width: 300, children: [_jsx(RoofGlazingPanel, { segments: segments }), levelMove && (_jsx(PanelSection, { title: "Level", children: _jsxs("div", { className: "flex flex-col gap-2 px-1 pb-1", children: [_jsxs("p", { className: "text-muted-foreground text-xs", children: ["On ", levelMove.storey, " with its walls, so it hides that floor's plan in solo and exploded views.", levelMove.roofs > 1 && ` The floor's ${levelMove.roofs} roofs move together.`] }), _jsx("button", { className: "w-full rounded-full bg-foreground/[0.08] px-3 py-2 font-medium text-foreground text-xs transition-colors hover:bg-foreground/[0.14]", onClick: handleMoveOffStorey, type: "button", children: levelMove.label })] }) })), _jsxs(PanelSection, { title: "Segments", children: [_jsx("div", { className: "flex flex-col gap-1", children: segments.map((seg, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectSegment(seg.id), type: "button", children: [_jsx("span", { className: "truncate", children: seg.name || `Segment ${i + 1}` }), _jsx("span", { className: "text-muted-foreground text-xs capitalize", children: seg.roofType })] }, seg.id))) }), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Draw Segment", onClick: handleAddSegment }) })] }), _jsxs(PanelSection, { title: "Position", children: [_jsx(SliderControl, { label: "X", onChange: (v) => {
217
+ // Shown as the segment panel shows them: in plan, past the walls' outside faces.
218
+ const eaveSegment = segments.find((segment) => segment.roofType !== 'barrel');
219
+ const rakedSegment = segments.find((segment) => roofTypeHasRakes(segment.roofType));
220
+ const overhangPastWall = (segment, axis) => getRoofSegmentDeckReach(segment)[axis] - segment.wallThickness / 2;
221
+ return (_jsxs(PanelWrapper, { icon: "/icons/roof.webp", onClose: handleClose, title: node.name || i18n._({ id: 'node.roof.panel.roof', message: 'Roof' }), width: 300, children: [_jsx(RoofGlazingPanel, { segments: segments }), levelMove && (_jsx(PanelSection, { title: i18n._({ id: 'node.roof.panel.level', message: 'Level' }), children: _jsxs("div", { className: "flex flex-col gap-2 px-1 pb-1", children: [_jsx("p", { className: "text-muted-foreground text-xs", children: i18n._({
222
+ id: 'node.roof.panel.onStoreyWalls',
223
+ message: "On {storey} with its walls, so it hides that floor's plan in solo and exploded views.{roofs, plural, =0 {} =1 {} other { The floor's {roofCount} roofs move together.}}",
224
+ values: {
225
+ storey: levelMove.storey,
226
+ roofs: levelMove.roofs,
227
+ roofCount: String(levelMove.roofs),
228
+ },
229
+ }) }), _jsx("button", { className: "w-full rounded-full bg-foreground/[0.08] px-3 py-2 font-medium text-foreground text-xs transition-colors hover:bg-foreground/[0.14]", onClick: handleMoveOffStorey, type: "button", children: levelMove.label })] }) })), _jsxs(PanelSection, { title: i18n._({ id: 'node.roof.panel.segments', message: 'Segments' }), children: [_jsx("div", { className: "flex flex-col gap-1", children: segments.map((seg, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectSegment(seg.id), type: "button", children: [_jsx("span", { className: "truncate", children: seg.name ||
230
+ i18n._({
231
+ id: 'node.segmentNumber',
232
+ message: 'Segment {number}',
233
+ values: { number: i + 1 },
234
+ }) }), _jsx("span", { className: "text-muted-foreground text-xs", children: i18n._({
235
+ id: 'node.roof.panel.segmentType',
236
+ message: '{roofType, select, hip {Hip} gable {Gable} shed {Shed} gambrel {Gambrel} dutch {Dutch} mansard {Mansard} flat {Flat} conical {Conical} barrel {Barrel} other {Roof}}',
237
+ values: { roofType: seg.roofType },
238
+ }) })] }, seg.id))) }), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: i18n._({ id: 'node.roof.panel.drawSegment', message: 'Draw Segment' }), onClick: handleAddSegment }) })] }), eaveSegment && (_jsxs(PanelSection, { title: i18n._({ id: 'node.roof.panel.eaves', message: 'Eaves' }), children: [_jsx(SliderControl, { label: i18n._({ id: 'node.roof.panel.eaveOverhang', message: 'Eave overhang' }), max: 1, min: 0, onChange: (v) => handleSegmentsUpdate((segment) => segment.roofType === 'barrel'
239
+ ? null
240
+ : { overhang: v / getSegmentSlopeFrame(segment).cosTheta }), precision: 2, step: 0.05, unit: "m", value: overhangPastWall(eaveSegment, 'z') }), rakedSegment && (_jsx(SliderControl, { label: i18n._({ id: 'node.roof.panel.gableOverhang', message: 'Gable overhang' }), max: 3, min: 0, onChange: (v) => handleSegmentsUpdate((segment) => roofTypeHasRakes(segment.roofType) ? { gableOverhang: v } : null), precision: 2, step: 0.05, unit: "m", value: overhangPastWall(rakedSegment, 'x') }))] })), _jsxs(PanelSection, { title: i18n._({ id: 'node.roof.panel.position', message: 'Position' }), children: [_jsx(SliderControl, { label: "X", onChange: (v) => {
198
241
  const pos = [...node.position];
199
242
  pos[0] = v;
200
243
  handleUpdate({ position: pos });
201
244
  }, precision: 2, step: 0.05, unit: "m", value: node.position[0] }), node.support?.kind !== 'roof' && (_jsx(SegmentedControl, { onChange: (mode) => handleUpdate({ support: { kind: mode === 'walls' ? 'walls' : 'level' } }), options: [
202
- { label: 'Follows walls', value: 'walls' },
203
- { label: 'Custom', value: 'custom' },
204
- ], value: node.support?.kind === 'walls' ? 'walls' : 'custom' })), node.support?.kind === 'walls' ? (_jsxs("div", { className: "px-1 text-[11px] text-muted-foreground", children: ["Currently ", formatLinearMeasurement(node.position[1], unit, metricNotation)] })) : (_jsx(SliderControl, { label: "Y", onChange: (v) => {
245
+ {
246
+ label: i18n._({ id: 'node.roof.panel.followsWalls', message: 'Follows walls' }),
247
+ value: 'walls',
248
+ },
249
+ {
250
+ label: i18n._({ id: 'node.roof.panel.custom', message: 'Custom' }),
251
+ value: 'custom',
252
+ },
253
+ ], value: node.support?.kind === 'walls' ? 'walls' : 'custom' })), node.support?.kind === 'walls' ? (_jsx("div", { className: "px-1 text-[11px] text-muted-foreground", children: i18n._({
254
+ id: 'node.panel.currently',
255
+ message: 'Currently {value}',
256
+ values: { value: formatLinearMeasurement(node.position[1], unit, metricNotation) },
257
+ }) })) : (_jsx(SliderControl, { label: "Y", onChange: (v) => {
205
258
  const pos = [...node.position];
206
259
  pos[1] = v;
207
260
  const current = useScene.getState().nodes[node.id];
@@ -217,7 +270,7 @@ export default function RoofPanel() {
217
270
  const pos = [...node.position];
218
271
  pos[2] = v;
219
272
  handleUpdate({ position: pos });
220
- }, precision: 2, step: 0.05, unit: "m", value: node.position[2] }), _jsx(SliderControl, { label: "Rotation", max: 180, min: -180, onChange: (degrees) => {
273
+ }, precision: 2, step: 0.05, unit: "m", value: node.position[2] }), _jsx(SliderControl, { label: i18n._({ id: 'node.roof.panel.rotation', message: 'Rotation' }), max: 180, min: -180, onChange: (degrees) => {
221
274
  handleUpdate({ rotation: (degrees * Math.PI) / 180 });
222
275
  }, precision: 0, step: 1, unit: "\u00B0", value: Math.round((node.rotation * 180) / Math.PI) }), _jsxs("div", { className: "flex gap-1.5 px-1 pt-2 pb-1", children: [_jsx(ActionButton, { label: "-45\u00B0", onClick: () => {
223
276
  triggerSFX('sfx:item-rotate');
@@ -225,18 +278,33 @@ export default function RoofPanel() {
225
278
  } }), _jsx(ActionButton, { label: "+45\u00B0", onClick: () => {
226
279
  triggerSFX('sfx:item-rotate');
227
280
  handleUpdate({ rotation: node.rotation + Math.PI / 4 });
228
- } })] })] }), _jsx(PanelSection, { title: "Elements", children: _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [chimneys.map((chimney, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(chimney.id), type: "button", children: [_jsx("span", { className: "truncate", children: chimney.name || `Chimney ${i + 1}` }), _jsx("span", { className: "text-muted-foreground text-xs", children: "chimney" })] }, chimney.id))), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add Chimney", onClick: () => activateTool('chimney') }) })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [dormers.map((dormer, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(dormer.id), type: "button", children: [_jsx("span", { className: "truncate", children: dormer.name || `Dormer ${i + 1}` }), _jsx("span", { className: "text-muted-foreground text-xs", children: "dormer" })] }, dormer.id))), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add Dormer", onClick: () => activateTool('dormer') }) })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [skylights.map((skylight, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(skylight.id), type: "button", children: [_jsx("span", { className: "truncate", children: skylight.name || `Skylight ${i + 1}` }), _jsx("span", { className: "text-muted-foreground text-xs", children: "skylight" })] }, skylight.id))), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add Skylight", onClick: () => activateTool('skylight') }) })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [solarPanels.map((panel, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(panel.id), type: "button", children: [_jsx("span", { className: "truncate", children: panel.name || `Solar Panel ${i + 1}` }), _jsx("span", { className: "text-muted-foreground text-xs", children: "solar panel" })] }, panel.id))), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add Solar Panel", onClick: () => activateTool('solar-panel') }) })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [vents.map((vent, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(vent.id), type: "button", children: [_jsx("span", { className: "truncate", children: vent.name ||
229
- (vent.type === 'box-vent'
230
- ? `Box Vent ${i + 1}`
281
+ } })] })] }), _jsx(PanelSection, { title: i18n._({ id: 'node.roof.panel.elements', message: 'Elements' }), children: _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [chimneys.map((chimney, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(chimney.id), type: "button", children: [_jsx("span", { className: "truncate", children: chimney.name || numberedName(nodeLabel('chimney', 'Chimney', i18n), i + 1, i18n) }), _jsx("span", { className: "text-muted-foreground text-xs", children: i18n._({ id: 'node.roof.panel.chimney', message: 'chimney' }) })] }, chimney.id))), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: i18n._({ id: 'node.roof.panel.addChimney', message: 'Add Chimney' }), onClick: () => activateTool('chimney') }) })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [dormers.map((dormer, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(dormer.id), type: "button", children: [_jsx("span", { className: "truncate", children: dormer.name || numberedName(nodeLabel('dormer', 'Dormer', i18n), i + 1, i18n) }), _jsx("span", { className: "text-muted-foreground text-xs", children: i18n._({ id: 'node.roof.panel.dormer', message: 'dormer' }) })] }, dormer.id))), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: i18n._({ id: 'node.roof.panel.addDormer', message: 'Add Dormer' }), onClick: () => activateTool('dormer') }) })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [skylights.map((skylight, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(skylight.id), type: "button", children: [_jsx("span", { className: "truncate", children: skylight.name ||
282
+ numberedName(nodeLabel('skylight', 'Skylight', i18n), i + 1, i18n) }), _jsx("span", { className: "text-muted-foreground text-xs", children: i18n._({ id: 'node.roof.panel.skylight', message: 'skylight' }) })] }, skylight.id))), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: i18n._({ id: 'node.roof.panel.addSkylight', message: 'Add Skylight' }), onClick: () => activateTool('skylight') }) })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [solarPanels.map((panel, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(panel.id), type: "button", children: [_jsx("span", { className: "truncate", children: panel.name ||
283
+ numberedName(nodeLabel('solar-panel', 'Solar Panel', i18n), i + 1, i18n) }), _jsx("span", { className: "text-muted-foreground text-xs", children: i18n._({ id: 'node.roof.panel.solarPanel', message: 'solar panel' }) })] }, panel.id))), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: i18n._({ id: 'node.roof.panel.addSolarPanel', message: 'Add Solar Panel' }), onClick: () => activateTool('solar-panel') }) })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [vents.map((vent, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(vent.id), type: "button", children: [_jsx("span", { className: "truncate", children: vent.name ||
284
+ numberedName(vent.type === 'box-vent'
285
+ ? nodeLabel('box-vent', 'Box Vent', i18n)
231
286
  : vent.type === 'ridge-vent'
232
- ? `Ridge Vent ${i + 1}`
233
- : `Turbine Vent ${i + 1}`) }), _jsx("span", { className: "text-muted-foreground text-xs", children: vent.type === 'box-vent'
234
- ? 'box vent'
287
+ ? nodeLabel('ridge-vent', 'Ridge Vent', i18n)
288
+ : nodeLabel('turbine-vent', 'Turbine Vent', i18n), i + 1, i18n) }), _jsx("span", { className: "text-muted-foreground text-xs", children: vent.type === 'box-vent'
289
+ ? i18n._({ id: 'node.roof.panel.boxVent', message: 'box vent' })
235
290
  : vent.type === 'ridge-vent'
236
- ? 'ridge vent'
237
- : 'turbine vent' })] }, vent.id))), _jsx(SegmentedControl, { onChange: setVentType, options: [
238
- { label: 'Box', value: 'box-vent' },
239
- { label: 'Ridge', value: 'ridge-vent' },
240
- { label: 'Turbine', value: 'turbine-vent' },
241
- ], value: ventType }), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add Vent", onClick: () => activateTool(ventType) }) })] }), _jsx("div", { className: "flex flex-col gap-1", children: _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add Cupola", onClick: () => activateTool('cupola') }) }) }), _jsx("div", { className: "flex flex-col gap-1", children: _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add Eyebrow Vent", onClick: () => activateTool('eyebrow-vent') }) }) }), _jsxs("div", { className: "flex flex-col gap-1", children: [gutters.map((gutter, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(gutter.id), type: "button", children: [_jsx("span", { className: "truncate", children: gutter.name || `Gutter ${i + 1}` }), _jsx("span", { className: "text-muted-foreground text-xs", children: "gutter" })] }, gutter.id))), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: "Add Gutter", onClick: () => activateTool('gutter') }) })] })] }) }), _jsx(PanelSection, { title: "Actions", children: _jsxs(ActionGroup, { children: [_jsx(ActionButton, { icon: _jsx(Move, { className: "h-3.5 w-3.5" }), label: "Move", onClick: handleMove }), _jsx(ActionButton, { icon: _jsx(Copy, { className: "h-3.5 w-3.5" }), label: "Duplicate", onClick: handleDuplicate }), _jsx(ActionButton, { className: "hover:bg-red-500/20", icon: _jsx(Trash2, { className: "h-3.5 w-3.5 text-red-600 dark:text-red-400" }), label: "Delete", onClick: handleDelete })] }) })] }));
291
+ ? i18n._({ id: 'node.roof.panel.ridgeVent', message: 'ridge vent' })
292
+ : i18n._({ id: 'node.roof.panel.turbineVent', message: 'turbine vent' }) })] }, vent.id))), _jsx(SegmentedControl, { onChange: setVentType, options: [
293
+ { label: i18n._({ id: 'node.roof.panel.box', message: 'Box' }), value: 'box-vent' },
294
+ {
295
+ label: i18n._({ id: 'node.roof.panel.ridge', message: 'Ridge' }),
296
+ value: 'ridge-vent',
297
+ },
298
+ {
299
+ label: i18n._({ id: 'node.roof.panel.turbine', message: 'Turbine' }),
300
+ value: 'turbine-vent',
301
+ },
302
+ ], value: ventType }), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: i18n._({ id: 'node.roof.panel.addVent', message: 'Add Vent' }), onClick: () => activateTool(ventType) }) })] }), _jsx("div", { className: "flex flex-col gap-1", children: _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: i18n._({ id: 'node.roof.panel.addCupola', message: 'Add Cupola' }), onClick: () => activateTool('cupola') }) }) }), _jsx("div", { className: "flex flex-col gap-1", children: _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: i18n._({
303
+ id: 'node.roof.panel.addEyebrowVent',
304
+ message: 'Add Eyebrow Vent',
305
+ }), onClick: () => activateTool('eyebrow-vent') }) }) }), _jsxs("div", { className: "flex flex-col gap-1", children: [gutters.map((gutter, i) => (_jsxs("button", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e]", onClick: () => handleSelectElement(gutter.id), type: "button", children: [_jsx("span", { className: "truncate", children: gutter.name || numberedName(nodeLabel('gutter', 'Gutter', i18n), i + 1, i18n) }), _jsx("span", { className: "text-muted-foreground text-xs", children: i18n._({ id: 'node.roof.panel.gutter', message: 'gutter' }) })] }, gutter.id))), _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Plus, { className: "h-3.5 w-3.5" }), label: i18n._({ id: 'node.roof.panel.addGutter', message: 'Add Gutter' }), onClick: () => activateTool('gutter') }) })] })] }) }), _jsx(PanelSection, { title: i18n._({ id: 'node.roof.panel.actions', message: 'Actions' }), children: _jsxs(ActionGroup, { children: [_jsx(ActionButton, { icon: _jsx(Move, { className: "h-3.5 w-3.5" }), label: i18n._({ id: 'node.roof.panel.move', message: 'Move' }), onClick: handleMove }), _jsx(ActionButton, { icon: _jsx(Copy, { className: "h-3.5 w-3.5" }), label: i18n._({ id: 'node.roof.panel.duplicate', message: 'Duplicate' }), onClick: handleDuplicate }), _jsx(ActionButton, { className: "hover:bg-red-500/20", icon: _jsx(Trash2, { className: "h-3.5 w-3.5 text-red-600 dark:text-red-400" }), label: i18n._({ id: 'node.roof.panel.delete', message: 'Delete' }), onClick: handleDelete })] }) })] }));
306
+ }
307
+ /** "Chimney 2": a list row for an element without a name of its own. */
308
+ function numberedName(kind, number, i18n) {
309
+ return i18n._({ id: 'node.numberedName', message: '{kind} {number}', values: { kind, number } });
242
310
  }
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/roof/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,QAAQ,EAKd,MAAM,kBAAkB,CAAA;AAgBzB,eAAO,MAAM,YAAY,sBAAuB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,gCAyJjE,CAAA;eAEc,YAAY"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/roof/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,QAAQ,EAKd,MAAM,kBAAkB,CAAA;AAiBzB,eAAO,MAAM,YAAY,sBAAuB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,gCAqKjE,CAAA;eAEc,YAAY"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { hasSegmentMaterialOverride, isIndependentRoofSegment, useLiveNodeOverrides, useRegistry, useScene, } from '@pascal-app/core';
4
- import { getRoofMaterialArray, isRevealAssembling, levelWallCladdingRef, NodeRenderer, subscribeRevealAssembling, useNodeEvents, useViewer, } from '@pascal-app/viewer';
4
+ import { getRoofMaterialArray, isRevealAssembling, levelWallCladdingRef, levelWallFaceRef, NodeRenderer, subscribeRevealAssembling, useNodeEvents, useViewer, } from '@pascal-app/viewer';
5
5
  import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
6
6
  import { useShallow } from 'zustand/react/shallow';
7
7
  import { createPlaceholderGeometry } from '../shared/placeholder-geometry.js';
@@ -97,8 +97,19 @@ export const RoofRenderer = ({ node: rawNode }) => {
97
97
  const placeholderGeometry = useMemo(() => createPlaceholderGeometry(4), []);
98
98
  // The gable band is clad like the walls below it (their assemblies).
99
99
  const wallCladdingRef = useScene((state) => levelWallCladdingRef(state.nodes, node));
100
+ // The heel band, under the eaves, is wall: it shows what the walls below show outside.
101
+ const wallFaceRef = useScene((state) => levelWallFaceRef(state.nodes, node));
100
102
  const sceneMaterials = useScene((state) => state.materials);
101
- const customMaterial = useMemo(() => getRoofMaterialArray(node, shading, textures, colorPreset, sceneTheme, wallCladdingRef, sceneMaterials), [node, shading, textures, colorPreset, sceneTheme, wallCladdingRef, sceneMaterials]);
103
+ const customMaterial = useMemo(() => getRoofMaterialArray(node, shading, textures, colorPreset, sceneTheme, wallCladdingRef, sceneMaterials, wallFaceRef), [
104
+ node,
105
+ shading,
106
+ textures,
107
+ colorPreset,
108
+ sceneTheme,
109
+ wallCladdingRef,
110
+ sceneMaterials,
111
+ wallFaceRef,
112
+ ]);
102
113
  const material = debugColors
103
114
  ? getRoofDebugMaterials(shading)
104
115
  : customMaterial || getRoofMaterials(shading, textures, colorPreset);
@@ -1,7 +1,15 @@
1
1
  export type RoofFootprintSourceChoice = 'draw' | 'room';
2
+ /**
3
+ * What a Room pick finds under the cursor: a room it can roof, a room that is
4
+ * not a rectangle (Room roofs only rectangles), or no closed room at all.
5
+ */
6
+ export type RoofRoomPick = 'room' | 'irregular' | 'none';
2
7
  type RoofFootprintSourceState = {
3
8
  source: RoofFootprintSourceChoice;
4
9
  setSource: (source: RoofFootprintSourceChoice) => void;
10
+ /** The hovered Room pick, for the HUD to say why a click would do nothing; null off Room. */
11
+ roomPick: RoofRoomPick | null;
12
+ setRoomPick: (roomPick: RoofRoomPick | null) => void;
5
13
  };
6
14
  /**
7
15
  * How the user wants the next roof footprint made — draw two corners, or pick
@@ -12,5 +20,14 @@ type RoofFootprintSourceState = {
12
20
  * tool clears on unmount — both used to wipe the user's choice.
13
21
  */
14
22
  declare const useRoofFootprintSource: import("zustand").UseBoundStore<import("zustand").StoreApi<RoofFootprintSourceState>>;
23
+ declare const subscribeToRoomPick: (onChange: () => void) => () => void;
24
+ export declare const irregularRoomHintVisibility: {
25
+ subscribe: typeof subscribeToRoomPick;
26
+ value: () => boolean;
27
+ };
28
+ export declare const noRoomHintVisibility: {
29
+ subscribe: typeof subscribeToRoomPick;
30
+ value: () => boolean;
31
+ };
15
32
  export default useRoofFootprintSource;
16
33
  //# sourceMappingURL=roof-footprint-source.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"roof-footprint-source.d.ts","sourceRoot":"","sources":["../../src/roof/roof-footprint-source.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,MAAM,CAAA;AAEvD,KAAK,wBAAwB,GAAG;IAC9B,MAAM,EAAE,yBAAyB,CAAA;IACjC,SAAS,EAAE,CAAC,MAAM,EAAE,yBAAyB,KAAK,IAAI,CAAA;CACvD,CAAA;AAED;;;;;;;GAOG;AACH,QAAA,MAAM,sBAAsB,uFAGzB,CAAA;eAEY,sBAAsB"}
1
+ {"version":3,"file":"roof-footprint-source.d.ts","sourceRoot":"","sources":["../../src/roof/roof-footprint-source.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,MAAM,CAAA;AAEvD;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAA;AAExD,KAAK,wBAAwB,GAAG;IAC9B,MAAM,EAAE,yBAAyB,CAAA;IACjC,SAAS,EAAE,CAAC,MAAM,EAAE,yBAAyB,KAAK,IAAI,CAAA;IACtD,6FAA6F;IAC7F,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,CAAA;CACrD,CAAA;AAED;;;;;;;GAOG;AACH,QAAA,MAAM,sBAAsB,uFAKzB,CAAA;AAEH,QAAA,MAAM,mBAAmB,aAAc,MAAM,IAAI,eAG7C,CAAA;AAEJ,eAAO,MAAM,2BAA2B;IACtC,SAAS;IACT,KAAK;CACN,CAAA;AAED,eAAO,MAAM,oBAAoB;IAC/B,SAAS;IACT,KAAK;CACN,CAAA;eAEc,sBAAsB"}
@@ -10,5 +10,19 @@ import { create } from 'zustand';
10
10
  const useRoofFootprintSource = create((set) => ({
11
11
  source: 'draw',
12
12
  setSource: (source) => set({ source }),
13
+ roomPick: null,
14
+ setRoomPick: (roomPick) => set({ roomPick }),
13
15
  }));
16
+ const subscribeToRoomPick = (onChange) => useRoofFootprintSource.subscribe((state, previous) => {
17
+ if (state.roomPick !== previous.roomPick)
18
+ onChange();
19
+ });
20
+ export const irregularRoomHintVisibility = {
21
+ subscribe: subscribeToRoomPick,
22
+ value: () => useRoofFootprintSource.getState().roomPick === 'irregular',
23
+ };
24
+ export const noRoomHintVisibility = {
25
+ subscribe: subscribeToRoomPick,
26
+ value: () => useRoofFootprintSource.getState().roomPick === 'none',
27
+ };
14
28
  export default useRoofFootprintSource;
@@ -1,8 +1,42 @@
1
- import { type AnyNode, type LevelNode, type RoofFootprintTarget, type RoofType, type WallNode } from '@pascal-app/core';
1
+ import { type AlignmentAnchor, type AnyNode, type LevelNode, type RoofFootprintTarget, type RoofType, type WallNode } from '@pascal-app/core';
2
+ import type { RoofRoomPick } from './roof-footprint-source.js';
2
3
  export type RoofFootprintSource = 'room' | 'walls' | 'draw';
3
- export declare function isStandardRoofWallEligible(wall: WallNode): boolean;
4
+ export declare function isStandardRoofWallEligible(wall: Pick<WallNode, 'start' | 'end' | 'curveOffset'>): boolean;
5
+ /**
6
+ * The rooms' edges on `levelIds` a drawn roof snaps to, as it snaps to walls (their corners,
7
+ * middles, crossings and runs): each invisible wall's line, and each railing round a porch, deck
8
+ * or patio at its outside face, from post corner to post corner (`outdoorRoomRoofOutline`), so a
9
+ * roof drawn over the room reaches its outside edge as one drawn over the house reaches its walls'.
10
+ * `faced` holds those railings: their faces stand in for their centre lines. Kept per scene, so a
11
+ * pointer move asks once.
12
+ */
13
+ export declare function roofSnapEdges(nodes: Readonly<Record<string, AnyNode>>, levelIds: readonly string[]): {
14
+ references: WallNode[];
15
+ faced: Set<string>;
16
+ };
17
+ /**
18
+ * The rooms' edges on `levelIds` as alignment anchors for a roof drawn over them: each straight
19
+ * invisible wall's ends and middle (as a wall's), and each porch's, deck's or patio's outside
20
+ * corners, railings' post corners included.
21
+ */
22
+ export declare function roofEdgeAnchors(nodes: Readonly<Record<string, AnyNode>>, levelIds: readonly string[]): AlignmentAnchor[];
23
+ /** What a Room pick lights up while it hovers a room: its walls, a porch's edges with them. */
24
+ export declare function roofFootprintPreviewIds(target: RoofFootprintTarget | null): string[];
4
25
  export declare function isConicalRoofWallEligible(targetLevelId: LevelNode['id'], wall: WallNode, nodes: Readonly<Record<string, AnyNode>>): boolean;
26
+ /**
27
+ * The curved wall a cone would be built over from a plan point: the nearest one in reach on the
28
+ * level or the level below. The plan raises no wall events, so its tool picks the wall itself.
29
+ */
30
+ export declare function pickConicalRoofWall(levelId: LevelNode['id'], nodes: Readonly<Record<string, AnyNode>>, point: [number, number]): WallNode | null;
5
31
  export declare function parseRoofFootprintSource(value: unknown, roofType: RoofType): RoofFootprintSource;
32
+ /**
33
+ * The room a Room pick at `point` roofs (the smallest one on the level, else on the level below),
34
+ * and what the pick found: a rectangle it roofs, a room it can't (not a rectangle), or none.
35
+ */
36
+ export declare function pickRoofRoom(levelId: LevelNode['id'], nodes: Readonly<Record<string, AnyNode>>, point: [number, number]): {
37
+ target: RoofFootprintTarget | null;
38
+ pick: RoofRoomPick;
39
+ };
6
40
  export declare function subscribeToConicalRoofWallClicks(options: {
7
41
  footprintSource: RoofFootprintSource;
8
42
  currentLevelId: LevelNode['id'] | null;
@@ -1 +1 @@
1
- {"version":3,"file":"roof-footprint.d.ts","sourceRoot":"","sources":["../../src/roof/roof-footprint.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAKZ,KAAK,SAAS,EACd,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EAIb,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAA;AAEzB,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;AAI3D,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAKlE;AAED,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,EAC9B,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,OAAO,CAMT;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,mBAAmB,CAGhG;AAED,wBAAgB,gCAAgC,CAAC,OAAO,EAAE;IACxD,eAAe,EAAE,mBAAmB,CAAA;IACpC,cAAc,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACtC,QAAQ,EAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACjD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAA;IAC3C,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;IAClC,QAAQ,EAAE,QAAQ,CAAA;CACnB,GAAG,MAAM,IAAI,CA0Cb;AAED,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,EAC9B,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,MAAM,CAUR;AAED,wBAAgB,kCAAkC,CAChD,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,EAC9B,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC9C,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,EAC9B,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,UAAU,yDAAuD,GAChE,MAAM,CAKR"}
1
+ {"version":3,"file":"roof-footprint.d.ts","sourceRoot":"","sources":["../../src/roof/roof-footprint.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,OAAO,EASZ,KAAK,SAAS,EAGd,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EAMb,KAAK,QAAQ,EAGd,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE3D,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;AAI3D,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,CAAC,GACpD,OAAO,CAKT;AAgED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B;IAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAYhD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,eAAe,EAAE,CAOnB;AAED,+FAA+F;AAC/F,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,GAAG,MAAM,EAAE,CAEpF;AAED,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,EAC9B,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,OAAO,CAMT;AAKD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,EACxB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GACtB,QAAQ,GAAG,IAAI,CAajB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,mBAAmB,CAGhG;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,EACxB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GACtB;IAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,CAI5D;AAED,wBAAgB,gCAAgC,CAAC,OAAO,EAAE;IACxD,eAAe,EAAE,mBAAmB,CAAA;IACpC,cAAc,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACtC,QAAQ,EAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACjD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAA;IAC3C,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;IAClC,QAAQ,EAAE,QAAQ,CAAA;CACnB,GAAG,MAAM,IAAI,CA0Cb;AAED,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,EAC9B,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,MAAM,CAWR;AAED,wBAAgB,kCAAkC,CAChD,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,EAC9B,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC9C,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,EAC9B,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,UAAU,yDAAuD,GAChE,MAAM,CAKR"}
@@ -1,4 +1,4 @@
1
- import { emitter, getLevelBelow, getLevelElevations, isCurvedWall, resolveLevelId, resolveRoofWallTopElevation, } from '@pascal-app/core';
1
+ import { emitter, getLevelBelow, getLevelElevations, getWallCurveFrameAt, getWallCurveStationAtPoint, isCurvedWall, isOutdoorRoomType, isRailingWall, outdoorRoomRoofOutline, polygonAnchors, resolveLevelId, resolveRoofWallTopElevation, resolveRoomRoofFootprint, wallSegmentAnchors, } from '@pascal-app/core';
2
2
  const ROOF_AXIS_ALIGNMENT_EPSILON = 1e-4;
3
3
  export function isStandardRoofWallEligible(wall) {
4
4
  if (isCurvedWall(wall))
@@ -7,6 +7,94 @@ export function isStandardRoofWallEligible(wall) {
7
7
  const deltaZ = Math.abs(wall.end[1] - wall.start[1]);
8
8
  return deltaX <= ROOF_AXIS_ALIGNMENT_EPSILON || deltaZ <= ROOF_AXIS_ALIGNMENT_EPSILON;
9
9
  }
10
+ /** A room's edge as the wall snap reads a wall: its ends, its middle and its run catch a point. */
11
+ function edgeReference(id, parentId, start, end) {
12
+ return {
13
+ object: 'node',
14
+ id: id,
15
+ type: 'wall',
16
+ parentId,
17
+ visible: true,
18
+ metadata: {},
19
+ children: [],
20
+ start,
21
+ end,
22
+ frontSide: 'unknown',
23
+ backSide: 'unknown',
24
+ };
25
+ }
26
+ /** The levels' invisible walls, and their porches', decks' and patios' outside edges. */
27
+ function levelEdges(nodes, levelIds) {
28
+ return levelIds.flatMap((levelId) => {
29
+ const level = nodes[levelId];
30
+ if (level?.type !== 'level')
31
+ return [];
32
+ const children = level.children.map((id) => nodes[id]);
33
+ const walls = children.filter((node) => node?.type === 'wall');
34
+ const separators = children.filter((node) => node?.type === 'separator');
35
+ const rooms = children
36
+ .filter((node) => node?.type === 'zone' && isOutdoorRoomType(node.roomType) && node.polygon.length >= 3)
37
+ .map((zone) => ({ zone, ...outdoorRoomRoofOutline(zone, walls, separators) }));
38
+ return [{ levelId, separators, rooms }];
39
+ });
40
+ }
41
+ const snapEdgesMemo = new WeakMap();
42
+ function computeRoofSnapEdges(nodes, levelIds) {
43
+ const references = [];
44
+ const faced = new Set();
45
+ for (const { levelId, separators, rooms } of levelEdges(nodes, levelIds)) {
46
+ for (const separator of separators)
47
+ references.push(edgeReference(separator.id, levelId, separator.start, separator.end));
48
+ for (const { polygon, edges } of rooms)
49
+ edges.forEach((edge, index) => {
50
+ if (edge?.kind !== 'railing')
51
+ return;
52
+ faced.add(edge.id);
53
+ const next = polygon[(index + 1) % polygon.length];
54
+ references.push(edgeReference(edge.id, levelId, polygon[index], next));
55
+ });
56
+ }
57
+ return { references, faced };
58
+ }
59
+ /**
60
+ * The rooms' edges on `levelIds` a drawn roof snaps to, as it snaps to walls (their corners,
61
+ * middles, crossings and runs): each invisible wall's line, and each railing round a porch, deck
62
+ * or patio at its outside face, from post corner to post corner (`outdoorRoomRoofOutline`), so a
63
+ * roof drawn over the room reaches its outside edge as one drawn over the house reaches its walls'.
64
+ * `faced` holds those railings: their faces stand in for their centre lines. Kept per scene, so a
65
+ * pointer move asks once.
66
+ */
67
+ export function roofSnapEdges(nodes, levelIds) {
68
+ const key = levelIds.join('|');
69
+ let byKey = snapEdgesMemo.get(nodes);
70
+ if (!byKey) {
71
+ byKey = new Map();
72
+ snapEdgesMemo.set(nodes, byKey);
73
+ }
74
+ const cached = byKey.get(key);
75
+ if (cached)
76
+ return cached;
77
+ const edges = computeRoofSnapEdges(nodes, levelIds);
78
+ byKey.set(key, edges);
79
+ return edges;
80
+ }
81
+ /**
82
+ * The rooms' edges on `levelIds` as alignment anchors for a roof drawn over them: each straight
83
+ * invisible wall's ends and middle (as a wall's), and each porch's, deck's or patio's outside
84
+ * corners, railings' post corners included.
85
+ */
86
+ export function roofEdgeAnchors(nodes, levelIds) {
87
+ return levelEdges(nodes, levelIds).flatMap(({ separators, rooms }) => [
88
+ ...separators
89
+ .filter((separator) => isStandardRoofWallEligible(separator))
90
+ .flatMap((separator) => wallSegmentAnchors(separator.id, separator.start, separator.end)),
91
+ ...rooms.flatMap(({ zone, polygon }) => polygonAnchors(zone.id, polygon)),
92
+ ]);
93
+ }
94
+ /** What a Room pick lights up while it hovers a room: its walls, a porch's edges with them. */
95
+ export function roofFootprintPreviewIds(target) {
96
+ return target ? [...target.wallIds, ...(target.edgeIds ?? [])] : [];
97
+ }
10
98
  export function isConicalRoofWallEligible(targetLevelId, wall, nodes) {
11
99
  const completeNodes = nodes;
12
100
  const sourceLevelId = resolveLevelId(wall, completeNodes);
@@ -16,11 +104,44 @@ export function isConicalRoofWallEligible(targetLevelId, wall, nodes) {
16
104
  return true;
17
105
  return getLevelBelow(targetLevelId, completeNodes)?.id === sourceLevelId;
18
106
  }
107
+ // As far off the wall's centreline as the plan reaches for a wall a door or window goes in.
108
+ const CONICAL_WALL_PICK_DISTANCE = 0.5;
109
+ /**
110
+ * The curved wall a cone would be built over from a plan point: the nearest one in reach on the
111
+ * level or the level below. The plan raises no wall events, so its tool picks the wall itself.
112
+ */
113
+ export function pickConicalRoofWall(levelId, nodes, point) {
114
+ let picked = null;
115
+ let pickedDistance = CONICAL_WALL_PICK_DISTANCE;
116
+ for (const node of Object.values(nodes)) {
117
+ if (!(node.type === 'wall' && isCurvedWall(node)))
118
+ continue;
119
+ if (!isConicalRoofWallEligible(levelId, node, nodes))
120
+ continue;
121
+ const nearest = getWallCurveFrameAt(node, getWallCurveStationAtPoint(node, point)).point;
122
+ const distance = Math.hypot(point[0] - nearest.x, point[1] - nearest.y);
123
+ if (distance > pickedDistance)
124
+ continue;
125
+ picked = node;
126
+ pickedDistance = distance;
127
+ }
128
+ return picked;
129
+ }
19
130
  export function parseRoofFootprintSource(value, roofType) {
20
131
  if (roofType === 'conical')
21
132
  return 'walls';
22
133
  return value === 'room' ? 'room' : 'draw';
23
134
  }
135
+ /**
136
+ * The room a Room pick at `point` roofs (the smallest one on the level, else on the level below),
137
+ * and what the pick found: a rectangle it roofs, a room it can't (not a rectangle), or none.
138
+ */
139
+ export function pickRoofRoom(levelId, nodes, point) {
140
+ const found = resolveRoomRoofFootprint(levelId, nodes, point);
141
+ if (!found)
142
+ return { target: null, pick: 'none' };
143
+ return found.rectangular ? { target: found, pick: 'room' } : { target: null, pick: 'irregular' };
144
+ }
24
145
  export function subscribeToConicalRoofWallClicks(options) {
25
146
  if (!(options.roofType === 'conical' && options.footprintSource === 'walls'))
26
147
  return () => { };
@@ -66,9 +187,10 @@ export function subscribeToConicalRoofWallClicks(options) {
66
187
  export function resolveRoofFootprintElevation(targetLevelId, target, nodes) {
67
188
  const completeNodes = nodes;
68
189
  const elevations = getLevelElevations(completeNodes);
190
+ // A railing is a guard round a deck or a porch, not a wall: the roof never sits on its top.
69
191
  const tops = target.wallIds.flatMap((id) => {
70
192
  const wall = nodes[id];
71
- return wall?.type === 'wall'
193
+ return wall?.type === 'wall' && !isRailingWall(wall)
72
194
  ? [resolveRoofWallTopElevation(targetLevelId, wall, completeNodes, elevations)]
73
195
  : [];
74
196
  });
@@ -1 +1 @@
1
- {"version":3,"file":"roof-materials.d.ts","sourceRoot":"","sources":["../../src/roof/roof-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAGhB,KAAK,aAAa,EAEnB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,GAChC,KAAK,CAAC,QAAQ,EAAE,CAsBlB;AAKD,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,aAA0B,GAAG,KAAK,CAAC,QAAQ,EAAE,CAY3F"}
1
+ {"version":3,"file":"roof-materials.d.ts","sourceRoot":"","sources":["../../src/roof/roof-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAGhB,KAAK,aAAa,EAEnB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,GAChC,KAAK,CAAC,QAAQ,EAAE,CA4BlB;AAKD,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,aAA0B,GAAG,KAAK,CAAC,QAAQ,EAAE,CAe3F"}
@@ -1,7 +1,7 @@
1
1
  import { createDefaultMaterial, createSurfaceRoleMaterial, resolveSlotDefaultMaterial, } from '@pascal-app/viewer';
2
2
  import * as THREE from 'three';
3
3
  // Production materials — match the rest of the scene (white walls, light-gray slabs).
4
- // Indices: 0 = Wall/Trim, 1 = Deck, 2 = Interior, 3 = Shingle
4
+ // Indices: 0 = Wall/Trim, 1 = Deck, 2 = Interior, 3 = Shingle, 4 = Heel band, 5/6 = Gables
5
5
  const roofMaterialsCache = new Map();
6
6
  export function getRoofMaterials(shading = 'rendered', textures = true, colorPreset = 'clay') {
7
7
  const cacheKey = `${shading}-${textures}-${colorPreset}`;
@@ -16,12 +16,18 @@ export function getRoofMaterials(shading = 'rendered', textures = true, colorPre
16
16
  resolveSlotDefaultMaterial('library:preset-softwhite', shading), // 1: Deck
17
17
  resolveSlotDefaultMaterial('library:preset-softwhite', shading), // 2: Interior
18
18
  resolveSlotDefaultMaterial('library:roof-terracottatiles', shading), // 3: Shingle
19
+ resolveSlotDefaultMaterial('library:concrete-drywall', shading), // 4: Heel band
20
+ resolveSlotDefaultMaterial('library:concrete-drywall', shading), // 5: Left gable
21
+ resolveSlotDefaultMaterial('library:concrete-drywall', shading), // 6: Right gable
19
22
  ]
20
23
  : [
21
24
  createSurfaceRoleMaterial('roof', colorPreset),
22
25
  createSurfaceRoleMaterial('ceiling', colorPreset),
23
26
  createSurfaceRoleMaterial('ceiling', colorPreset),
24
27
  createSurfaceRoleMaterial('roof', colorPreset),
28
+ createSurfaceRoleMaterial('wall', colorPreset),
29
+ createSurfaceRoleMaterial('roof', colorPreset),
30
+ createSurfaceRoleMaterial('roof', colorPreset),
25
31
  ];
26
32
  roofMaterialsCache.set(cacheKey, materials);
27
33
  return materials;
@@ -37,6 +43,9 @@ export function getRoofDebugMaterials(shading = 'rendered') {
37
43
  createDefaultMaterial('#000000', 0.9, shading, THREE.FrontSide), // 1: Deck
38
44
  createDefaultMaterial('#dddddd', 0.9, shading, THREE.DoubleSide), // 2: Interior
39
45
  createDefaultMaterial('#4ade80', 0.9, shading, THREE.FrontSide), // 3: Shingle
46
+ createDefaultMaterial('#f5d0a9', 0.8, shading, THREE.DoubleSide), // 4: Heel band
47
+ createDefaultMaterial('#93c5fd', 0.8, shading, THREE.DoubleSide), // 5: Left gable
48
+ createDefaultMaterial('#c4b5fd', 0.8, shading, THREE.DoubleSide), // 6: Right gable
40
49
  ];
41
50
  roofDebugMaterialsCache.set(shading, materials);
42
51
  return materials;
@@ -1,9 +1,7 @@
1
1
  export type RoofPlacementMode = 'auto' | 'ground' | 'roof';
2
2
  type RoofPlacementModeState = {
3
- conical: boolean;
4
3
  mode: RoofPlacementMode;
5
4
  cycleMode: () => void;
6
- setConical: (conical: boolean) => void;
7
5
  };
8
6
  declare const useRoofPlacementMode: import("zustand").UseBoundStore<import("zustand").StoreApi<RoofPlacementModeState>>;
9
7
  declare const subscribeToRoofKind: (onChange: () => void) => () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"roof-placement-mode.d.ts","sourceRoot":"","sources":["../../src/roof/roof-placement-mode.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAI1D,KAAK,sBAAsB,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,iBAAiB,CAAA;IACvB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC,CAAA;AAED,QAAA,MAAM,oBAAoB,qFAQvB,CAAA;AAEH,QAAA,MAAM,mBAAmB,aAAc,MAAM,IAAI,eAG7C,CAAA;AAEJ,eAAO,MAAM,6BAA6B;IACxC,SAAS;IACT,KAAK;CACN,CAAA;AAED,eAAO,MAAM,8BAA8B;IACzC,SAAS;IACT,KAAK;CACN,CAAA;eAEc,oBAAoB"}
1
+ {"version":3,"file":"roof-placement-mode.d.ts","sourceRoot":"","sources":["../../src/roof/roof-placement-mode.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAI1D,KAAK,sBAAsB,GAAG;IAC5B,IAAI,EAAE,iBAAiB,CAAA;IACvB,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAED,QAAA,MAAM,oBAAoB,qFAMvB,CAAA;AAOH,QAAA,MAAM,mBAAmB,aAAc,MAAM,IAAI,eAG7C,CAAA;AAEJ,eAAO,MAAM,6BAA6B;IACxC,SAAS;IACT,KAAK;CACN,CAAA;AAED,eAAO,MAAM,8BAA8B;IACzC,SAAS;IACT,KAAK;CACN,CAAA;eAEc,oBAAoB"}
@@ -1,24 +1,26 @@
1
+ import { useEditor } from '@pascal-app/editor';
1
2
  import { create } from 'zustand';
2
3
  const MODES = ['auto', 'ground', 'roof'];
3
4
  const useRoofPlacementMode = create((set, get) => ({
4
- conical: false,
5
5
  mode: 'auto',
6
6
  cycleMode: () => {
7
7
  const current = MODES.indexOf(get().mode);
8
8
  set({ mode: MODES[(current + 1) % MODES.length] ?? 'auto' });
9
9
  },
10
- setConical: (conical) => set({ conical }),
11
10
  }));
12
- const subscribeToRoofKind = (onChange) => useRoofPlacementMode.subscribe((state, previous) => {
13
- if (state.conical !== previous.conical)
11
+ // Read from the roof type the tool is armed with, not from whichever roof tool mounted: in a
12
+ // 2D-only session the 3D tool never mounts, and the HUD showed a cone the standard rows.
13
+ const isConicalRoof = (state) => state.toolDefaults.roof?.roofType === 'conical';
14
+ const subscribeToRoofKind = (onChange) => useEditor.subscribe((state, previous) => {
15
+ if (isConicalRoof(state) !== isConicalRoof(previous))
14
16
  onChange();
15
17
  });
16
18
  export const conicalRoofToolHintVisibility = {
17
19
  subscribe: subscribeToRoofKind,
18
- value: () => useRoofPlacementMode.getState().conical,
20
+ value: () => isConicalRoof(useEditor.getState()),
19
21
  };
20
22
  export const standardRoofToolHintVisibility = {
21
23
  subscribe: subscribeToRoofKind,
22
- value: () => !useRoofPlacementMode.getState().conical,
24
+ value: () => !isConicalRoof(useEditor.getState()),
23
25
  };
24
26
  export default useRoofPlacementMode;