@pascal-app/core 0.8.0 → 0.9.2

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 (471) hide show
  1. package/dist/events/bus.d.ts +67 -13
  2. package/dist/events/bus.d.ts.map +1 -1
  3. package/dist/hooks/scene-registry/item-clip-registry.d.ts +18 -0
  4. package/dist/hooks/scene-registry/item-clip-registry.d.ts.map +1 -0
  5. package/dist/hooks/scene-registry/item-clip-registry.js +10 -0
  6. package/dist/hooks/scene-registry/scene-registry.d.ts +14 -23
  7. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
  8. package/dist/hooks/scene-registry/scene-registry.js +59 -26
  9. package/dist/hooks/scene-registry/scene-registry.test.d.ts +2 -0
  10. package/dist/hooks/scene-registry/scene-registry.test.d.ts.map +1 -0
  11. package/dist/hooks/scene-registry/scene-registry.test.js +26 -0
  12. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +14 -0
  13. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -0
  14. package/dist/hooks/spatial-grid/floor-placed-elevation.js +51 -0
  15. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +2 -0
  16. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +1 -0
  17. package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +366 -0
  18. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +79 -11
  19. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  20. package/dist/hooks/spatial-grid/spatial-grid-manager.js +508 -141
  21. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +28 -1
  22. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  23. package/dist/hooks/spatial-grid/spatial-grid-sync.js +109 -18
  24. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts +2 -0
  25. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts.map +1 -0
  26. package/dist/hooks/spatial-grid/wall-slab-overlap.test.js +363 -0
  27. package/dist/index.d.ts +36 -8
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +33 -6
  30. package/dist/lib/level-name.d.ts +4 -0
  31. package/dist/lib/level-name.d.ts.map +1 -0
  32. package/dist/lib/level-name.js +10 -0
  33. package/dist/lib/measurement-geometry.d.ts +19 -0
  34. package/dist/lib/measurement-geometry.d.ts.map +1 -0
  35. package/dist/lib/measurement-geometry.js +232 -0
  36. package/dist/lib/measurement-geometry.test.d.ts +2 -0
  37. package/dist/lib/measurement-geometry.test.d.ts.map +1 -0
  38. package/dist/lib/measurement-geometry.test.js +102 -0
  39. package/dist/lib/polygon-geometry.d.ts +0 -1
  40. package/dist/lib/polygon-geometry.d.ts.map +1 -1
  41. package/dist/lib/polygon-geometry.js +0 -18
  42. package/dist/lib/polygon-relations.d.ts +10 -0
  43. package/dist/lib/polygon-relations.d.ts.map +1 -0
  44. package/dist/lib/polygon-relations.js +73 -0
  45. package/dist/lib/selection-proxy.d.ts +4 -0
  46. package/dist/lib/selection-proxy.d.ts.map +1 -0
  47. package/dist/lib/selection-proxy.js +12 -0
  48. package/dist/lib/slab-polygon.d.ts +31 -2
  49. package/dist/lib/slab-polygon.d.ts.map +1 -1
  50. package/dist/lib/slab-polygon.js +525 -31
  51. package/dist/lib/slab-polygon.test.d.ts +2 -0
  52. package/dist/lib/slab-polygon.test.d.ts.map +1 -0
  53. package/dist/lib/slab-polygon.test.js +635 -0
  54. package/dist/lib/slots.d.ts +14 -0
  55. package/dist/lib/slots.d.ts.map +1 -0
  56. package/dist/lib/slots.js +26 -0
  57. package/dist/lib/space-detection-pause.test.d.ts +2 -0
  58. package/dist/lib/space-detection-pause.test.d.ts.map +1 -0
  59. package/dist/lib/space-detection-pause.test.js +42 -0
  60. package/dist/lib/space-detection.d.ts +54 -2
  61. package/dist/lib/space-detection.d.ts.map +1 -1
  62. package/dist/lib/space-detection.js +599 -124
  63. package/dist/lib/space-detection.test.d.ts +2 -0
  64. package/dist/lib/space-detection.test.d.ts.map +1 -0
  65. package/dist/lib/space-detection.test.js +376 -0
  66. package/dist/lib/wall-distance.d.ts +60 -0
  67. package/dist/lib/wall-distance.d.ts.map +1 -0
  68. package/dist/lib/wall-distance.js +85 -0
  69. package/dist/lib/zone-quantities.d.ts +21 -0
  70. package/dist/lib/zone-quantities.d.ts.map +1 -0
  71. package/dist/lib/zone-quantities.js +183 -0
  72. package/dist/lib/zone-quantities.test.d.ts +2 -0
  73. package/dist/lib/zone-quantities.test.d.ts.map +1 -0
  74. package/dist/lib/zone-quantities.test.js +82 -0
  75. package/dist/material-library.d.ts +20 -1
  76. package/dist/material-library.d.ts.map +1 -1
  77. package/dist/material-library.js +3737 -200
  78. package/dist/registry/__bench__/relations-resolver.bench.d.ts +15 -0
  79. package/dist/registry/__bench__/relations-resolver.bench.d.ts.map +1 -0
  80. package/dist/registry/__bench__/relations-resolver.bench.js +170 -0
  81. package/dist/registry/handles.d.ts +394 -0
  82. package/dist/registry/handles.d.ts.map +1 -0
  83. package/dist/registry/handles.js +24 -0
  84. package/dist/registry/index.d.ts +7 -0
  85. package/dist/registry/index.d.ts.map +1 -0
  86. package/dist/registry/index.js +4 -0
  87. package/dist/registry/registry.d.ts +136 -0
  88. package/dist/registry/registry.d.ts.map +1 -0
  89. package/dist/registry/registry.js +282 -0
  90. package/dist/registry/registry.test.d.ts +2 -0
  91. package/dist/registry/registry.test.d.ts.map +1 -0
  92. package/dist/registry/registry.test.js +196 -0
  93. package/dist/registry/relations-resolver.d.ts +42 -0
  94. package/dist/registry/relations-resolver.d.ts.map +1 -0
  95. package/dist/registry/relations-resolver.js +77 -0
  96. package/dist/registry/relations-resolver.test.d.ts +2 -0
  97. package/dist/registry/relations-resolver.test.d.ts.map +1 -0
  98. package/dist/registry/relations-resolver.test.js +183 -0
  99. package/dist/registry/scene-api.d.ts +44 -0
  100. package/dist/registry/scene-api.d.ts.map +1 -0
  101. package/dist/registry/scene-api.js +111 -0
  102. package/dist/registry/scene-api.test.d.ts +2 -0
  103. package/dist/registry/scene-api.test.d.ts.map +1 -0
  104. package/dist/registry/scene-api.test.js +183 -0
  105. package/dist/registry/subtree.d.ts +67 -0
  106. package/dist/registry/subtree.d.ts.map +1 -0
  107. package/dist/registry/subtree.js +123 -0
  108. package/dist/registry/subtree.test.d.ts +2 -0
  109. package/dist/registry/subtree.test.d.ts.map +1 -0
  110. package/dist/registry/subtree.test.js +163 -0
  111. package/dist/registry/types.d.ts +2099 -0
  112. package/dist/registry/types.d.ts.map +1 -0
  113. package/dist/registry/types.js +1 -0
  114. package/dist/schema/index.d.ts +35 -4
  115. package/dist/schema/index.d.ts.map +1 -1
  116. package/dist/schema/index.js +32 -4
  117. package/dist/schema/material.d.ts +13 -2
  118. package/dist/schema/material.d.ts.map +1 -1
  119. package/dist/schema/material.js +17 -5
  120. package/dist/schema/material.test.d.ts +2 -0
  121. package/dist/schema/material.test.d.ts.map +1 -0
  122. package/dist/schema/material.test.js +43 -0
  123. package/dist/schema/nodes/box-vent.d.ts +74 -0
  124. package/dist/schema/nodes/box-vent.d.ts.map +1 -0
  125. package/dist/schema/nodes/box-vent.js +63 -0
  126. package/dist/schema/nodes/building.d.ts +1 -1
  127. package/dist/schema/nodes/building.d.ts.map +1 -1
  128. package/dist/schema/nodes/building.js +3 -2
  129. package/dist/schema/nodes/cabinet.d.ts +534 -0
  130. package/dist/schema/nodes/cabinet.d.ts.map +1 -0
  131. package/dist/schema/nodes/cabinet.js +137 -0
  132. package/dist/schema/nodes/ceiling.d.ts +5 -2
  133. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  134. package/dist/schema/nodes/ceiling.js +5 -1
  135. package/dist/schema/nodes/chimney.d.ts +158 -0
  136. package/dist/schema/nodes/chimney.d.ts.map +1 -0
  137. package/dist/schema/nodes/chimney.js +67 -0
  138. package/dist/schema/nodes/column.d.ts +600 -7
  139. package/dist/schema/nodes/column.d.ts.map +1 -1
  140. package/dist/schema/nodes/column.js +589 -6
  141. package/dist/schema/nodes/cupola.d.ts +66 -0
  142. package/dist/schema/nodes/cupola.d.ts.map +1 -0
  143. package/dist/schema/nodes/cupola.js +28 -0
  144. package/dist/schema/nodes/door.d.ts +13 -5
  145. package/dist/schema/nodes/door.d.ts.map +1 -1
  146. package/dist/schema/nodes/door.js +12 -0
  147. package/dist/schema/nodes/dormer.d.ts +228 -0
  148. package/dist/schema/nodes/dormer.d.ts.map +1 -0
  149. package/dist/schema/nodes/dormer.js +119 -0
  150. package/dist/schema/nodes/downspout.d.ts +75 -0
  151. package/dist/schema/nodes/downspout.d.ts.map +1 -0
  152. package/dist/schema/nodes/downspout.js +63 -0
  153. package/dist/schema/nodes/duct-fitting.d.ts +91 -0
  154. package/dist/schema/nodes/duct-fitting.d.ts.map +1 -0
  155. package/dist/schema/nodes/duct-fitting.js +92 -0
  156. package/dist/schema/nodes/duct-segment.d.ts +61 -0
  157. package/dist/schema/nodes/duct-segment.d.ts.map +1 -0
  158. package/dist/schema/nodes/duct-segment.js +73 -0
  159. package/dist/schema/nodes/duct-terminal.d.ts +57 -0
  160. package/dist/schema/nodes/duct-terminal.d.ts.map +1 -0
  161. package/dist/schema/nodes/duct-terminal.js +51 -0
  162. package/dist/schema/nodes/elevator.d.ts +106 -0
  163. package/dist/schema/nodes/elevator.d.ts.map +1 -0
  164. package/dist/schema/nodes/elevator.js +57 -0
  165. package/dist/schema/nodes/eyebrow-vent.d.ts +68 -0
  166. package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -0
  167. package/dist/schema/nodes/eyebrow-vent.js +40 -0
  168. package/dist/schema/nodes/fence.d.ts +19 -2
  169. package/dist/schema/nodes/fence.d.ts.map +1 -1
  170. package/dist/schema/nodes/fence.js +27 -2
  171. package/dist/schema/nodes/gutter.d.ts +84 -0
  172. package/dist/schema/nodes/gutter.d.ts.map +1 -0
  173. package/dist/schema/nodes/gutter.js +76 -0
  174. package/dist/schema/nodes/hvac-equipment.d.ts +63 -0
  175. package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -0
  176. package/dist/schema/nodes/hvac-equipment.js +55 -0
  177. package/dist/schema/nodes/item.d.ts +12 -0
  178. package/dist/schema/nodes/item.d.ts.map +1 -1
  179. package/dist/schema/nodes/item.js +20 -0
  180. package/dist/schema/nodes/level.d.ts +1 -1
  181. package/dist/schema/nodes/level.d.ts.map +1 -1
  182. package/dist/schema/nodes/level.js +4 -0
  183. package/dist/schema/nodes/lineset.d.ts +42 -0
  184. package/dist/schema/nodes/lineset.d.ts.map +1 -0
  185. package/dist/schema/nodes/lineset.js +38 -0
  186. package/dist/schema/nodes/liquid-line.d.ts +34 -0
  187. package/dist/schema/nodes/liquid-line.d.ts.map +1 -0
  188. package/dist/schema/nodes/liquid-line.js +24 -0
  189. package/dist/schema/nodes/measurement.d.ts +306 -0
  190. package/dist/schema/nodes/measurement.d.ts.map +1 -0
  191. package/dist/schema/nodes/measurement.js +87 -0
  192. package/dist/schema/nodes/measurement.test.d.ts +2 -0
  193. package/dist/schema/nodes/measurement.test.d.ts.map +1 -0
  194. package/dist/schema/nodes/measurement.test.js +139 -0
  195. package/dist/schema/nodes/pipe-fitting.d.ts +56 -0
  196. package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -0
  197. package/dist/schema/nodes/pipe-fitting.js +45 -0
  198. package/dist/schema/nodes/pipe-segment.d.ts +48 -0
  199. package/dist/schema/nodes/pipe-segment.d.ts.map +1 -0
  200. package/dist/schema/nodes/pipe-segment.js +36 -0
  201. package/dist/schema/nodes/pipe-trap.d.ts +43 -0
  202. package/dist/schema/nodes/pipe-trap.d.ts.map +1 -0
  203. package/dist/schema/nodes/pipe-trap.js +36 -0
  204. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts +2 -0
  205. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts.map +1 -0
  206. package/dist/schema/nodes/ridge-vent-defaults.test.js +205 -0
  207. package/dist/schema/nodes/ridge-vent.d.ts +82 -0
  208. package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
  209. package/dist/schema/nodes/ridge-vent.js +248 -0
  210. package/dist/schema/nodes/roof-segment-shape.d.ts +90 -0
  211. package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -0
  212. package/dist/schema/nodes/roof-segment-shape.js +318 -0
  213. package/dist/schema/nodes/roof-segment-shape.test.d.ts +2 -0
  214. package/dist/schema/nodes/roof-segment-shape.test.d.ts.map +1 -0
  215. package/dist/schema/nodes/roof-segment-shape.test.js +175 -0
  216. package/dist/schema/nodes/roof-segment-surface.test.d.ts +2 -0
  217. package/dist/schema/nodes/roof-segment-surface.test.d.ts.map +1 -0
  218. package/dist/schema/nodes/roof-segment-surface.test.js +48 -0
  219. package/dist/schema/nodes/roof-segment-trim.test.d.ts +2 -0
  220. package/dist/schema/nodes/roof-segment-trim.test.d.ts.map +1 -0
  221. package/dist/schema/nodes/roof-segment-trim.test.js +107 -0
  222. package/dist/schema/nodes/roof-segment-walls.d.ts +101 -0
  223. package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -0
  224. package/dist/schema/nodes/roof-segment-walls.js +330 -0
  225. package/dist/schema/nodes/roof-segment-walls.test.d.ts +2 -0
  226. package/dist/schema/nodes/roof-segment-walls.test.d.ts.map +1 -0
  227. package/dist/schema/nodes/roof-segment-walls.test.js +92 -0
  228. package/dist/schema/nodes/roof-segment.d.ts +273 -3
  229. package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
  230. package/dist/schema/nodes/roof-segment.js +539 -3
  231. package/dist/schema/nodes/roof.d.ts +8 -8
  232. package/dist/schema/nodes/roof.d.ts.map +1 -1
  233. package/dist/schema/nodes/roof.js +4 -16
  234. package/dist/schema/nodes/shelf.d.ts +106 -0
  235. package/dist/schema/nodes/shelf.d.ts.map +1 -0
  236. package/dist/schema/nodes/shelf.js +90 -0
  237. package/dist/schema/nodes/site.d.ts +1 -122
  238. package/dist/schema/nodes/site.d.ts.map +1 -1
  239. package/dist/schema/nodes/site.js +2 -6
  240. package/dist/schema/nodes/skylight.d.ts +279 -0
  241. package/dist/schema/nodes/skylight.d.ts.map +1 -0
  242. package/dist/schema/nodes/skylight.js +152 -0
  243. package/dist/schema/nodes/slab.d.ts +5 -2
  244. package/dist/schema/nodes/slab.d.ts.map +1 -1
  245. package/dist/schema/nodes/slab.js +5 -1
  246. package/dist/schema/nodes/solar-panel.d.ts +117 -0
  247. package/dist/schema/nodes/solar-panel.d.ts.map +1 -0
  248. package/dist/schema/nodes/solar-panel.js +47 -0
  249. package/dist/schema/nodes/stair-segment.d.ts +4 -4
  250. package/dist/schema/nodes/stair.d.ts +13 -12
  251. package/dist/schema/nodes/stair.d.ts.map +1 -1
  252. package/dist/schema/nodes/stair.js +3 -0
  253. package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -0
  254. package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -1
  255. package/dist/schema/nodes/surface-hole-metadata.js +3 -1
  256. package/dist/schema/nodes/turbine-vent.d.ts +68 -0
  257. package/dist/schema/nodes/turbine-vent.d.ts.map +1 -0
  258. package/dist/schema/nodes/turbine-vent.js +41 -0
  259. package/dist/schema/nodes/wall.d.ts +245 -6
  260. package/dist/schema/nodes/wall.d.ts.map +1 -1
  261. package/dist/schema/nodes/wall.js +257 -0
  262. package/dist/schema/nodes/wall.test.d.ts +2 -0
  263. package/dist/schema/nodes/wall.test.d.ts.map +1 -0
  264. package/dist/schema/nodes/wall.test.js +208 -0
  265. package/dist/schema/nodes/window.d.ts +11 -3
  266. package/dist/schema/nodes/window.d.ts.map +1 -1
  267. package/dist/schema/nodes/window.js +12 -0
  268. package/dist/schema/nodes/zone.d.ts +2 -0
  269. package/dist/schema/nodes/zone.d.ts.map +1 -1
  270. package/dist/schema/nodes/zone.js +6 -0
  271. package/dist/schema/scene-material.d.ts +41 -0
  272. package/dist/schema/scene-material.d.ts.map +1 -0
  273. package/dist/schema/scene-material.js +9 -0
  274. package/dist/schema/types.d.ts +2850 -655
  275. package/dist/schema/types.d.ts.map +1 -1
  276. package/dist/schema/types.js +49 -0
  277. package/dist/services/alignment-anchors.d.ts +100 -0
  278. package/dist/services/alignment-anchors.d.ts.map +1 -0
  279. package/dist/services/alignment-anchors.js +312 -0
  280. package/dist/services/alignment-anchors.test.d.ts +2 -0
  281. package/dist/services/alignment-anchors.test.d.ts.map +1 -0
  282. package/dist/services/alignment-anchors.test.js +383 -0
  283. package/dist/services/alignment.d.ts +131 -0
  284. package/dist/services/alignment.d.ts.map +1 -0
  285. package/dist/services/alignment.js +171 -0
  286. package/dist/services/alignment.test.d.ts +2 -0
  287. package/dist/services/alignment.test.d.ts.map +1 -0
  288. package/dist/services/alignment.test.js +92 -0
  289. package/dist/services/drag-session.d.ts +47 -0
  290. package/dist/services/drag-session.d.ts.map +1 -0
  291. package/dist/services/drag-session.js +100 -0
  292. package/dist/services/drag-session.test.d.ts +2 -0
  293. package/dist/services/drag-session.test.d.ts.map +1 -0
  294. package/dist/services/drag-session.test.js +219 -0
  295. package/dist/services/hosting.d.ts +88 -0
  296. package/dist/services/hosting.d.ts.map +1 -0
  297. package/dist/services/hosting.js +131 -0
  298. package/dist/services/hosting.test.d.ts +2 -0
  299. package/dist/services/hosting.test.d.ts.map +1 -0
  300. package/dist/services/hosting.test.js +230 -0
  301. package/dist/services/index.d.ts +13 -0
  302. package/dist/services/index.d.ts.map +1 -0
  303. package/dist/services/index.js +12 -0
  304. package/dist/services/level-height.d.ts +28 -0
  305. package/dist/services/level-height.d.ts.map +1 -0
  306. package/dist/services/level-height.js +62 -0
  307. package/dist/services/movement.d.ts +48 -0
  308. package/dist/services/movement.d.ts.map +1 -0
  309. package/dist/services/movement.js +65 -0
  310. package/dist/services/movement.test.d.ts +2 -0
  311. package/dist/services/movement.test.d.ts.map +1 -0
  312. package/dist/services/movement.test.js +133 -0
  313. package/dist/services/opening-guides.d.ts +141 -0
  314. package/dist/services/opening-guides.d.ts.map +1 -0
  315. package/dist/services/opening-guides.js +267 -0
  316. package/dist/services/opening-guides.test.d.ts +2 -0
  317. package/dist/services/opening-guides.test.d.ts.map +1 -0
  318. package/dist/services/opening-guides.test.js +208 -0
  319. package/dist/services/port-connectivity.d.ts +115 -0
  320. package/dist/services/port-connectivity.d.ts.map +1 -0
  321. package/dist/services/port-connectivity.js +300 -0
  322. package/dist/services/port-connectivity.test.d.ts +2 -0
  323. package/dist/services/port-connectivity.test.d.ts.map +1 -0
  324. package/dist/services/port-connectivity.test.js +297 -0
  325. package/dist/services/riser-diagram.d.ts +40 -0
  326. package/dist/services/riser-diagram.d.ts.map +1 -0
  327. package/dist/services/riser-diagram.js +110 -0
  328. package/dist/services/riser-diagram.test.d.ts +2 -0
  329. package/dist/services/riser-diagram.test.d.ts.map +1 -0
  330. package/dist/services/riser-diagram.test.js +66 -0
  331. package/dist/services/single-undo-dance.test.d.ts +2 -0
  332. package/dist/services/single-undo-dance.test.d.ts.map +1 -0
  333. package/dist/services/single-undo-dance.test.js +210 -0
  334. package/dist/services/snap.d.ts +80 -0
  335. package/dist/services/snap.d.ts.map +1 -0
  336. package/dist/services/snap.js +140 -0
  337. package/dist/services/snap.test.d.ts +2 -0
  338. package/dist/services/snap.test.d.ts.map +1 -0
  339. package/dist/services/snap.test.js +137 -0
  340. package/dist/services/system-graph.d.ts +30 -0
  341. package/dist/services/system-graph.d.ts.map +1 -0
  342. package/dist/services/system-graph.js +164 -0
  343. package/dist/services/system-graph.test.d.ts +2 -0
  344. package/dist/services/system-graph.test.d.ts.map +1 -0
  345. package/dist/services/system-graph.test.js +138 -0
  346. package/dist/services/validate-dwv.d.ts +32 -0
  347. package/dist/services/validate-dwv.d.ts.map +1 -0
  348. package/dist/services/validate-dwv.js +118 -0
  349. package/dist/services/validate-dwv.test.d.ts +2 -0
  350. package/dist/services/validate-dwv.test.d.ts.map +1 -0
  351. package/dist/services/validate-dwv.test.js +109 -0
  352. package/dist/solar-panel-presets.d.ts +17 -0
  353. package/dist/solar-panel-presets.d.ts.map +1 -0
  354. package/dist/solar-panel-presets.js +43 -0
  355. package/dist/store/actions/node-actions.d.ts +14 -2
  356. package/dist/store/actions/node-actions.d.ts.map +1 -1
  357. package/dist/store/actions/node-actions.js +585 -15
  358. package/dist/store/actions/node-mutation-sanitize.test.d.ts +2 -0
  359. package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +1 -0
  360. package/dist/store/actions/node-mutation-sanitize.test.js +147 -0
  361. package/dist/store/actions/reparent.test.d.ts +2 -0
  362. package/dist/store/actions/reparent.test.d.ts.map +1 -0
  363. package/dist/store/actions/reparent.test.js +212 -0
  364. package/dist/store/actions/ridge-vent-update.test.d.ts +2 -0
  365. package/dist/store/actions/ridge-vent-update.test.d.ts.map +1 -0
  366. package/dist/store/actions/ridge-vent-update.test.js +264 -0
  367. package/dist/store/history-control.d.ts +11 -0
  368. package/dist/store/history-control.d.ts.map +1 -1
  369. package/dist/store/history-control.js +33 -0
  370. package/dist/store/use-interactive.d.ts +39 -0
  371. package/dist/store/use-interactive.d.ts.map +1 -1
  372. package/dist/store/use-interactive.js +75 -0
  373. package/dist/store/use-live-node-overrides.d.ts +22 -0
  374. package/dist/store/use-live-node-overrides.d.ts.map +1 -0
  375. package/dist/store/use-live-node-overrides.js +59 -0
  376. package/dist/store/use-scene-dirty-tracking.test.d.ts +2 -0
  377. package/dist/store/use-scene-dirty-tracking.test.d.ts.map +1 -0
  378. package/dist/store/use-scene-dirty-tracking.test.js +68 -0
  379. package/dist/store/use-scene-elevator-migration.test.d.ts +2 -0
  380. package/dist/store/use-scene-elevator-migration.test.d.ts.map +1 -0
  381. package/dist/store/use-scene-elevator-migration.test.js +109 -0
  382. package/dist/store/use-scene-measurement-roundtrip.test.d.ts +2 -0
  383. package/dist/store/use-scene-measurement-roundtrip.test.d.ts.map +1 -0
  384. package/dist/store/use-scene-measurement-roundtrip.test.js +97 -0
  385. package/dist/store/use-scene-plugins.test.d.ts +2 -0
  386. package/dist/store/use-scene-plugins.test.d.ts.map +1 -0
  387. package/dist/store/use-scene-plugins.test.js +51 -0
  388. package/dist/store/use-scene-wall-slot-migration.test.d.ts +2 -0
  389. package/dist/store/use-scene-wall-slot-migration.test.d.ts.map +1 -0
  390. package/dist/store/use-scene-wall-slot-migration.test.js +210 -0
  391. package/dist/store/use-scene-window-migration.test.d.ts +2 -0
  392. package/dist/store/use-scene-window-migration.test.d.ts.map +1 -0
  393. package/dist/store/use-scene-window-migration.test.js +85 -0
  394. package/dist/store/use-scene.d.ts +28 -2
  395. package/dist/store/use-scene.d.ts.map +1 -1
  396. package/dist/store/use-scene.js +546 -49
  397. package/dist/systems/elevator/elevator-dispatch.d.ts +12 -0
  398. package/dist/systems/elevator/elevator-dispatch.d.ts.map +1 -0
  399. package/dist/systems/elevator/elevator-dispatch.js +57 -0
  400. package/dist/systems/elevator/elevator-geometry.d.ts +15 -0
  401. package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -0
  402. package/dist/systems/elevator/elevator-geometry.js +50 -0
  403. package/dist/systems/elevator/elevator-opening-sync.d.ts +6 -0
  404. package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -0
  405. package/dist/systems/elevator/elevator-opening-sync.js +218 -0
  406. package/dist/systems/elevator/elevator-opening-sync.test.d.ts +2 -0
  407. package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +1 -0
  408. package/dist/systems/elevator/elevator-opening-sync.test.js +146 -0
  409. package/dist/systems/elevator/elevator-opening-system.d.ts +2 -0
  410. package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -0
  411. package/dist/systems/elevator/elevator-opening-system.js +48 -0
  412. package/dist/systems/elevator/elevator-runtime-system.d.ts +2 -0
  413. package/dist/systems/elevator/elevator-runtime-system.d.ts.map +1 -0
  414. package/dist/systems/elevator/elevator-runtime-system.js +8 -0
  415. package/dist/systems/elevator/elevator-runtime.d.ts +18 -0
  416. package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -0
  417. package/dist/systems/elevator/elevator-runtime.js +225 -0
  418. package/dist/systems/elevator/elevator-runtime.test.d.ts +2 -0
  419. package/dist/systems/elevator/elevator-runtime.test.d.ts.map +1 -0
  420. package/dist/systems/elevator/elevator-runtime.test.js +64 -0
  421. package/dist/systems/elevator/elevator-service.d.ts +19 -0
  422. package/dist/systems/elevator/elevator-service.d.ts.map +1 -0
  423. package/dist/systems/elevator/elevator-service.js +111 -0
  424. package/dist/systems/fence/fence-centerline.d.ts +12 -0
  425. package/dist/systems/fence/fence-centerline.d.ts.map +1 -0
  426. package/dist/systems/fence/fence-centerline.js +37 -0
  427. package/dist/systems/fence/fence-spline.d.ts +39 -0
  428. package/dist/systems/fence/fence-spline.d.ts.map +1 -0
  429. package/dist/systems/fence/fence-spline.js +196 -0
  430. package/dist/systems/fence/fence-spline.test.d.ts +2 -0
  431. package/dist/systems/fence/fence-spline.test.d.ts.map +1 -0
  432. package/dist/systems/fence/fence-spline.test.js +136 -0
  433. package/dist/systems/stair/stair-footprint.d.ts +44 -0
  434. package/dist/systems/stair/stair-footprint.d.ts.map +1 -0
  435. package/dist/systems/stair/stair-footprint.js +178 -0
  436. package/dist/systems/stair/stair-opening-preview.d.ts +2447 -0
  437. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -0
  438. package/dist/systems/stair/stair-opening-preview.js +93 -0
  439. package/dist/systems/stair/stair-opening-preview.test.d.ts +2 -0
  440. package/dist/systems/stair/stair-opening-preview.test.d.ts.map +1 -0
  441. package/dist/systems/stair/stair-opening-preview.test.js +80 -0
  442. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  443. package/dist/systems/stair/stair-opening-sync.js +155 -147
  444. package/dist/systems/stair/stair-opening-sync.test.js +424 -2
  445. package/dist/systems/stair/stair-opening-system.d.ts +2 -0
  446. package/dist/systems/stair/stair-opening-system.d.ts.map +1 -0
  447. package/dist/systems/stair/stair-opening-system.js +104 -0
  448. package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
  449. package/dist/systems/wall/wall-mitering.js +38 -2
  450. package/dist/systems/wall/wall-mitering.test.d.ts +2 -0
  451. package/dist/systems/wall/wall-mitering.test.d.ts.map +1 -0
  452. package/dist/systems/wall/wall-mitering.test.js +63 -0
  453. package/dist/systems/wall/wall-move.d.ts +48 -0
  454. package/dist/systems/wall/wall-move.d.ts.map +1 -0
  455. package/dist/systems/wall/wall-move.js +193 -0
  456. package/dist/utils/clone-scene-graph.d.ts +7 -4
  457. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  458. package/dist/utils/clone-scene-graph.js +29 -7
  459. package/dist/utils/clone-scene-graph.test.d.ts +2 -0
  460. package/dist/utils/clone-scene-graph.test.d.ts.map +1 -0
  461. package/dist/utils/clone-scene-graph.test.js +62 -0
  462. package/dist/utils/heal-scene-graph.d.ts +18 -0
  463. package/dist/utils/heal-scene-graph.d.ts.map +1 -0
  464. package/dist/utils/heal-scene-graph.js +90 -0
  465. package/dist/utils/heal-scene-graph.test.d.ts +2 -0
  466. package/dist/utils/heal-scene-graph.test.d.ts.map +1 -0
  467. package/dist/utils/heal-scene-graph.test.js +88 -0
  468. package/dist/validation/validate-build-json.d.ts +44 -0
  469. package/dist/validation/validate-build-json.d.ts.map +1 -0
  470. package/dist/validation/validate-build-json.js +256 -0
  471. package/package.json +11 -4
@@ -0,0 +1,101 @@
1
+ import type { RoofSegmentNode } from './roof-segment';
2
+ /**
3
+ * Wall-face math for roof segments — the vertical surfaces a wall-mounted
4
+ * opening (door / window) can attach to. A segment's generated volume has
5
+ * four vertical faces; on gable-family roofs the end faces extend past the
6
+ * eave line into the gable (rect + triangle/pentagon, coplanar with the
7
+ * base wall). These helpers describe each face as a 2D frame
8
+ * (`u` along the face, `v` height above the segment base) plus the
9
+ * placeable profile polygon, so placement tools, renderers, and CSG cut
10
+ * builders all share one definition of "the wall under the roof".
11
+ *
12
+ * The numbers MUST mirror the outer wall volume built by
13
+ * `getRoofSegmentBrushes` in the viewer's roof system
14
+ * (`getVol(wallThickness / 2, 0, 0, …)`): the volume is the segment
15
+ * footprint extended outward by `wallThickness / 2`, which drops the eave
16
+ * line by `(wallThickness / 2) · tanθ` and raises the ridge by the same
17
+ * amount so the apex stays at `wallHeight + activeRh`.
18
+ */
19
+ export type RoofWallFaceId = 'front' | 'back' | 'right' | 'left';
20
+ export type RoofSegmentWallFace = {
21
+ id: RoofWallFaceId;
22
+ /** Outward normal in segment-local space. */
23
+ normal: [number, number, number];
24
+ /**
25
+ * Yaw (radians, rotation-y) mapping opening-local +Z to the outward
26
+ * normal and opening-local +X to the face's +U direction — the same
27
+ * frame a wall-hosted door/window uses relative to its wall mesh.
28
+ */
29
+ yaw: number;
30
+ /** Face length along U. */
31
+ length: number;
32
+ /**
33
+ * Placeable region, CCW polygon in face coords. `u ∈ [0, length]`,
34
+ * `v` is height above the segment base (segment-local Y).
35
+ */
36
+ profile: [number, number][];
37
+ };
38
+ type SegmentWallInputs = Pick<RoofSegmentNode, 'roofType' | 'width' | 'depth' | 'wallHeight' | 'wallThickness' | 'pitch'> & Partial<Pick<RoofSegmentNode, 'gambrelLowerWidthRatio' | 'gambrelLowerHeightRatio' | 'mansardSteepWidthRatio' | 'mansardSteepHeightRatio' | 'dutchHipWidthRatio' | 'dutchHipHeightRatio' | 'dutchWaistLengthRatio'>>;
39
+ export declare function getRoofSegmentWallFace(node: SegmentWallInputs, id: RoofWallFaceId): RoofSegmentWallFace;
40
+ export declare function getRoofSegmentWallFaces(node: SegmentWallInputs): RoofSegmentWallFace[];
41
+ /**
42
+ * Segment-local point → face coords. `dist` is the signed offset off the
43
+ * outer wall plane along the face normal (0 = on the plane, positive =
44
+ * outside the volume).
45
+ */
46
+ export declare function segmentPointToRoofWallFace(node: SegmentWallInputs, id: RoofWallFaceId, point: [number, number, number]): {
47
+ u: number;
48
+ v: number;
49
+ dist: number;
50
+ };
51
+ /**
52
+ * The face's render frame in segment-local space: a group placed at
53
+ * `origin` and yawed by `yaw` maps face coords to segment space —
54
+ * frame X = U (along the face), frame Y = V (height), frame Z = the
55
+ * outward normal, with z = 0 on the WALL MID-PLANE. The mid-plane of
56
+ * the generated wall volume lands exactly on the nominal footprint
57
+ * (`±width/2` / `±depth/2`), so hosted children use the same position
58
+ * conventions as wall children (openings at z = 0, wall-side items
59
+ * pushed +thickness/2 at render time). Renderers derive this from the
60
+ * live-override-merged segment, which is what makes hosted children
61
+ * track segment edits live instead of jumping on commit.
62
+ */
63
+ export declare function getRoofWallFaceFrame(node: SegmentWallInputs, id: RoofWallFaceId): {
64
+ origin: [number, number, number];
65
+ yaw: number;
66
+ };
67
+ /** Face-frame point ([u, v, z-from-mid-plane]) → segment-local point. */
68
+ export declare function roofFacePointToSegment(node: SegmentWallInputs, id: RoofWallFaceId, point: [number, number, number]): [number, number, number];
69
+ /**
70
+ * Max width of a rect growing from an anchored vertical edge (`anchorU`)
71
+ * in direction `growSign` (±1 along U) while staying inside the face
72
+ * profile at the fixed vertical center `vCenter`. Resize-handle limit:
73
+ * the anchored-edge model matches the handles' apply math (opposite
74
+ * edge stays put, center re-derives).
75
+ */
76
+ export declare function getMaxRoofRectWidthFromAnchor(face: RoofSegmentWallFace, anchorU: number, growSign: number, vCenter: number, height: number): number;
77
+ /**
78
+ * Max height of a rect growing from an anchored horizontal edge
79
+ * (`anchorV`) in direction `growSign` (+1 = bottom anchored, grows up)
80
+ * while staying inside the face profile at the fixed horizontal center
81
+ * `uCenter`.
82
+ */
83
+ export declare function getMaxRoofRectHeightFromAnchor(face: RoofSegmentWallFace, uCenter: number, width: number, anchorV: number, growSign: number): number;
84
+ /**
85
+ * Clamp a rect center so the rect fits inside the face profile.
86
+ *
87
+ * - `lockV: true` (doors): `v` is fixed; only `u` slides. Returns null
88
+ * when no `u` keeps the rect inside at that height.
89
+ * - otherwise (windows): the center is projected into the eroded convex
90
+ * region (cyclic projection — profiles are convex by construction).
91
+ *
92
+ * Returns null when the rect cannot fit anywhere on the face.
93
+ */
94
+ export declare function clampRectToRoofWallFace(face: RoofSegmentWallFace, u: number, v: number, width: number, height: number, opts?: {
95
+ lockV?: boolean;
96
+ }): {
97
+ u: number;
98
+ v: number;
99
+ } | null;
100
+ export {};
101
+ //# sourceMappingURL=roof-segment-walls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roof-segment-walls.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/roof-segment-walls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAGrD;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;AAEhE,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,cAAc,CAAA;IAClB,6CAA6C;IAC7C,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAA;IACX,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;CAC5B,CAAA;AAED,KAAK,iBAAiB,GAAG,IAAI,CAC3B,eAAe,EACf,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,YAAY,GAAG,eAAe,GAAG,OAAO,CAC1E,GACC,OAAO,CACL,IAAI,CACF,eAAe,EACb,wBAAwB,GACxB,yBAAyB,GACzB,wBAAwB,GACxB,yBAAyB,GACzB,oBAAoB,GACpB,qBAAqB,GACrB,uBAAuB,CAC1B,CACF,CAAA;AAqJH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,iBAAiB,EACvB,EAAE,EAAE,cAAc,GACjB,mBAAmB,CAUrB;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,EAAE,CAStF;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,iBAAiB,EACvB,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAC9B;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAaxC;AA+CD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,iBAAiB,EACvB,EAAE,EAAE,cAAc,GACjB;IAAE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAYnD;AAED,yEAAyE;AACzE,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,iBAAiB,EACvB,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAC9B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAO1B;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,CAWR;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,MAAM,CASR;AAID;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,mBAAmB,EACzB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACzB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAwCjC"}
@@ -0,0 +1,330 @@
1
+ import { getDutchRoofMetrics, getSegmentSlopeFrame } from './roof-segment';
2
+ function getWallVolumeFrame(node) {
3
+ const { activeRh, tanTheta } = getSegmentSlopeFrame(node);
4
+ const wallThickness = node.wallThickness ?? 0.1;
5
+ const autoDrop = (wallThickness / 2) * tanTheta;
6
+ const wV = Math.max(0.01, node.width + wallThickness);
7
+ const dV = Math.max(0.01, node.depth + wallThickness);
8
+ const eaveY = Math.max(0.01, node.wallHeight - autoDrop);
9
+ let rh = activeRh;
10
+ if (activeRh > 0) {
11
+ rh = activeRh + autoDrop;
12
+ if (node.roofType === 'shed')
13
+ rh = activeRh + 2 * autoDrop;
14
+ }
15
+ return {
16
+ wV,
17
+ dV,
18
+ eaveY,
19
+ peakY: eaveY + Math.max(0.001, rh),
20
+ tanTheta,
21
+ hasSlope: activeRh > 0,
22
+ };
23
+ }
24
+ const FACE_NORMALS = {
25
+ front: [0, 0, 1],
26
+ back: [0, 0, -1],
27
+ right: [1, 0, 0],
28
+ left: [-1, 0, 0],
29
+ };
30
+ const FACE_YAWS = {
31
+ front: 0,
32
+ back: Math.PI,
33
+ right: Math.PI / 2,
34
+ left: -Math.PI / 2,
35
+ };
36
+ function rectProfile(length, top) {
37
+ return [
38
+ [0, 0],
39
+ [length, 0],
40
+ [length, top],
41
+ [0, top],
42
+ ];
43
+ }
44
+ function buildFaceProfile(node, frame, id) {
45
+ const { wV, dV, eaveY, peakY, tanTheta, hasSlope } = frame;
46
+ const isEnd = id === 'right' || id === 'left';
47
+ const length = isEnd ? dV : wV;
48
+ if (!hasSlope)
49
+ return rectProfile(length, eaveY);
50
+ switch (node.roofType) {
51
+ case 'gable': {
52
+ if (!isEnd)
53
+ return rectProfile(length, eaveY);
54
+ return [
55
+ [0, 0],
56
+ [length, 0],
57
+ [length, eaveY],
58
+ [length / 2, peakY],
59
+ [0, eaveY],
60
+ ];
61
+ }
62
+ case 'gambrel': {
63
+ if (!isEnd)
64
+ return rectProfile(length, eaveY);
65
+ // Kink ring sits at z = ±mz on the nominal footprint (see
66
+ // getModuleFaces); both end faces are symmetric about u = length/2.
67
+ const ratio = node.gambrelLowerWidthRatio ?? 0.5;
68
+ const mz = Math.min((node.depth / 2) * ratio, length / 2);
69
+ const kinkY = eaveY + (length / 2 - mz) * tanTheta;
70
+ return [
71
+ [0, 0],
72
+ [length, 0],
73
+ [length, eaveY],
74
+ [length / 2 + mz, kinkY],
75
+ [length / 2, peakY],
76
+ [length / 2 - mz, kinkY],
77
+ [0, eaveY],
78
+ ];
79
+ }
80
+ case 'shed': {
81
+ // Slope falls toward +Z: 'back' is the full-height wall, the end
82
+ // faces are right trapezoids rising toward the back edge.
83
+ if (id === 'front')
84
+ return rectProfile(length, eaveY);
85
+ if (id === 'back')
86
+ return rectProfile(length, peakY);
87
+ if (id === 'right') {
88
+ return [
89
+ [0, 0],
90
+ [length, 0],
91
+ [length, peakY],
92
+ [0, eaveY],
93
+ ];
94
+ }
95
+ return [
96
+ [0, 0],
97
+ [length, 0],
98
+ [length, eaveY],
99
+ [0, peakY],
100
+ ];
101
+ }
102
+ case 'dutch': {
103
+ const metrics = getDutchRoofMetrics(node);
104
+ const isDutchGableFace = (metrics.axis === 'x' && isEnd) ||
105
+ (metrics.axis === 'z' && (id === 'front' || id === 'back'));
106
+ if (!isDutchGableFace)
107
+ return rectProfile(length, eaveY);
108
+ const shoulderInset = metrics.axis === 'x' ? metrics.shoulderInsetAlongDepth : metrics.shoulderInsetAlongWidth;
109
+ if (!(shoulderInset > 0.001))
110
+ return rectProfile(length, eaveY);
111
+ const shoulderLo = Math.max(0, shoulderInset);
112
+ const shoulderHi = Math.min(length, length - shoulderInset);
113
+ if (!(shoulderHi - shoulderLo > 0.02))
114
+ return rectProfile(length, eaveY);
115
+ return [
116
+ [0, 0],
117
+ [length, 0],
118
+ [length, eaveY],
119
+ [shoulderHi, eaveY],
120
+ [length / 2, peakY],
121
+ [shoulderLo, eaveY],
122
+ [0, eaveY],
123
+ ];
124
+ }
125
+ default:
126
+ return rectProfile(length, eaveY);
127
+ }
128
+ }
129
+ export function getRoofSegmentWallFace(node, id) {
130
+ const frame = getWallVolumeFrame(node);
131
+ const isEnd = id === 'right' || id === 'left';
132
+ return {
133
+ id,
134
+ normal: FACE_NORMALS[id],
135
+ yaw: FACE_YAWS[id],
136
+ length: isEnd ? frame.dV : frame.wV,
137
+ profile: buildFaceProfile(node, frame, id),
138
+ };
139
+ }
140
+ export function getRoofSegmentWallFaces(node) {
141
+ const frame = getWallVolumeFrame(node);
142
+ return ['front', 'back', 'right', 'left'].map((id) => ({
143
+ id,
144
+ normal: FACE_NORMALS[id],
145
+ yaw: FACE_YAWS[id],
146
+ length: id === 'right' || id === 'left' ? frame.dV : frame.wV,
147
+ profile: buildFaceProfile(node, frame, id),
148
+ }));
149
+ }
150
+ /**
151
+ * Segment-local point → face coords. `dist` is the signed offset off the
152
+ * outer wall plane along the face normal (0 = on the plane, positive =
153
+ * outside the volume).
154
+ */
155
+ export function segmentPointToRoofWallFace(node, id, point) {
156
+ const { wV, dV } = getWallVolumeFrame(node);
157
+ const [x, y, z] = point;
158
+ switch (id) {
159
+ case 'front':
160
+ return { u: x + wV / 2, v: y, dist: z - dV / 2 };
161
+ case 'back':
162
+ return { u: wV / 2 - x, v: y, dist: -z - dV / 2 };
163
+ case 'right':
164
+ return { u: dV / 2 - z, v: y, dist: x - wV / 2 };
165
+ case 'left':
166
+ return { u: z + dV / 2, v: y, dist: -x - wV / 2 };
167
+ }
168
+ }
169
+ /**
170
+ * Inward half-plane constraints of the raw profile polygon (CCW →
171
+ * interior is to the left of each edge): a point p is inside when
172
+ * `nu·p.u + nv·p.v ≥ c` for every constraint.
173
+ */
174
+ function getProfileConstraints(face) {
175
+ const constraints = [];
176
+ const pts = face.profile;
177
+ for (let i = 0; i < pts.length; i++) {
178
+ const a = pts[i];
179
+ const b = pts[(i + 1) % pts.length];
180
+ const du = b[0] - a[0];
181
+ const dv = b[1] - a[1];
182
+ const len = Math.hypot(du, dv);
183
+ if (len < 1e-9)
184
+ continue;
185
+ const nu = -dv / len;
186
+ const nv = du / len;
187
+ constraints.push({ nu, nv, c: nu * a[0] + nv * a[1] });
188
+ }
189
+ return constraints;
190
+ }
191
+ /**
192
+ * Half-plane constraints for the CENTER of a `width × height` rect that
193
+ * must fit inside the face profile — the raw constraints eroded by the
194
+ * rect's half-extents projected on each edge normal.
195
+ */
196
+ function getRectCenterConstraints(face, width, height) {
197
+ return getProfileConstraints(face).map(({ nu, nv, c }) => ({
198
+ nu,
199
+ nv,
200
+ c: c + (Math.abs(nu) * width) / 2 + (Math.abs(nv) * height) / 2,
201
+ }));
202
+ }
203
+ /**
204
+ * The face's render frame in segment-local space: a group placed at
205
+ * `origin` and yawed by `yaw` maps face coords to segment space —
206
+ * frame X = U (along the face), frame Y = V (height), frame Z = the
207
+ * outward normal, with z = 0 on the WALL MID-PLANE. The mid-plane of
208
+ * the generated wall volume lands exactly on the nominal footprint
209
+ * (`±width/2` / `±depth/2`), so hosted children use the same position
210
+ * conventions as wall children (openings at z = 0, wall-side items
211
+ * pushed +thickness/2 at render time). Renderers derive this from the
212
+ * live-override-merged segment, which is what makes hosted children
213
+ * track segment edits live instead of jumping on commit.
214
+ */
215
+ export function getRoofWallFaceFrame(node, id) {
216
+ const { wV, dV } = getWallVolumeFrame(node);
217
+ switch (id) {
218
+ case 'front':
219
+ return { origin: [-wV / 2, 0, node.depth / 2], yaw: FACE_YAWS.front };
220
+ case 'back':
221
+ return { origin: [wV / 2, 0, -node.depth / 2], yaw: FACE_YAWS.back };
222
+ case 'right':
223
+ return { origin: [node.width / 2, 0, dV / 2], yaw: FACE_YAWS.right };
224
+ case 'left':
225
+ return { origin: [-node.width / 2, 0, -dV / 2], yaw: FACE_YAWS.left };
226
+ }
227
+ }
228
+ /** Face-frame point ([u, v, z-from-mid-plane]) → segment-local point. */
229
+ export function roofFacePointToSegment(node, id, point) {
230
+ const { origin, yaw } = getRoofWallFaceFrame(node, id);
231
+ const cos = Math.cos(yaw);
232
+ const sin = Math.sin(yaw);
233
+ const [u, v, z] = point;
234
+ // rotation-y: +x → (cos, 0, -sin), +z → (sin, 0, cos)
235
+ return [origin[0] + u * cos + z * sin, origin[1] + v, origin[2] - u * sin + z * cos];
236
+ }
237
+ /**
238
+ * Max width of a rect growing from an anchored vertical edge (`anchorU`)
239
+ * in direction `growSign` (±1 along U) while staying inside the face
240
+ * profile at the fixed vertical center `vCenter`. Resize-handle limit:
241
+ * the anchored-edge model matches the handles' apply math (opposite
242
+ * edge stays put, center re-derives).
243
+ */
244
+ export function getMaxRoofRectWidthFromAnchor(face, anchorU, growSign, vCenter, height) {
245
+ let max = Number.POSITIVE_INFINITY;
246
+ for (const { nu, nv, c } of getProfileConstraints(face)) {
247
+ // Center at anchorU + growSign·w/2, eroded by |nu|·w/2 + |nv|·h/2:
248
+ // base + k·w ≥ 0 with k ≤ 0 only when growth approaches the edge.
249
+ const k = (nu * growSign - Math.abs(nu)) / 2;
250
+ if (k >= -1e-9)
251
+ continue;
252
+ const base = nu * anchorU + nv * vCenter - c - (Math.abs(nv) * height) / 2;
253
+ max = Math.min(max, Math.max(0, base / -k));
254
+ }
255
+ return max;
256
+ }
257
+ /**
258
+ * Max height of a rect growing from an anchored horizontal edge
259
+ * (`anchorV`) in direction `growSign` (+1 = bottom anchored, grows up)
260
+ * while staying inside the face profile at the fixed horizontal center
261
+ * `uCenter`.
262
+ */
263
+ export function getMaxRoofRectHeightFromAnchor(face, uCenter, width, anchorV, growSign) {
264
+ let max = Number.POSITIVE_INFINITY;
265
+ for (const { nu, nv, c } of getProfileConstraints(face)) {
266
+ const k = (nv * growSign - Math.abs(nv)) / 2;
267
+ if (k >= -1e-9)
268
+ continue;
269
+ const base = nu * uCenter + nv * anchorV - c - (Math.abs(nu) * width) / 2;
270
+ max = Math.min(max, Math.max(0, base / -k));
271
+ }
272
+ return max;
273
+ }
274
+ const CLAMP_EPSILON = 1e-4;
275
+ /**
276
+ * Clamp a rect center so the rect fits inside the face profile.
277
+ *
278
+ * - `lockV: true` (doors): `v` is fixed; only `u` slides. Returns null
279
+ * when no `u` keeps the rect inside at that height.
280
+ * - otherwise (windows): the center is projected into the eroded convex
281
+ * region (cyclic projection — profiles are convex by construction).
282
+ *
283
+ * Returns null when the rect cannot fit anywhere on the face.
284
+ */
285
+ export function clampRectToRoofWallFace(face, u, v, width, height, opts) {
286
+ const constraints = getRectCenterConstraints(face, width, height);
287
+ if (constraints.length < 3)
288
+ return null;
289
+ if (opts?.lockV) {
290
+ let lo = Number.NEGATIVE_INFINITY;
291
+ let hi = Number.POSITIVE_INFINITY;
292
+ for (const { nu, nv, c } of constraints) {
293
+ const rhs = c - nv * v;
294
+ if (Math.abs(nu) < 1e-9) {
295
+ if (rhs > CLAMP_EPSILON)
296
+ return null;
297
+ continue;
298
+ }
299
+ if (nu > 0)
300
+ lo = Math.max(lo, rhs / nu);
301
+ else
302
+ hi = Math.min(hi, rhs / nu);
303
+ }
304
+ if (lo > hi + CLAMP_EPSILON)
305
+ return null;
306
+ return { u: Math.min(Math.max(u, lo), hi), v };
307
+ }
308
+ let pu = u;
309
+ let pv = v;
310
+ for (let iter = 0; iter < 32; iter++) {
311
+ let worst = null;
312
+ let worstViolation = CLAMP_EPSILON;
313
+ for (const constraint of constraints) {
314
+ const violation = constraint.c - (constraint.nu * pu + constraint.nv * pv);
315
+ if (violation > worstViolation) {
316
+ worstViolation = violation;
317
+ worst = constraint;
318
+ }
319
+ }
320
+ if (!worst)
321
+ return { u: pu, v: pv };
322
+ pu += worst.nu * worstViolation;
323
+ pv += worst.nv * worstViolation;
324
+ }
325
+ for (const { nu, nv, c } of constraints) {
326
+ if (nu * pu + nv * pv < c - 1e-3)
327
+ return null;
328
+ }
329
+ return { u: pu, v: pv };
330
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=roof-segment-walls.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roof-segment-walls.test.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/roof-segment-walls.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,92 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { RoofSegmentNode } from './roof-segment';
3
+ import { getRoofSegmentWallFace, getRoofSegmentWallFaces, getRoofWallFaceFrame, roofFacePointToSegment, segmentPointToRoofWallFace, } from './roof-segment-walls';
4
+ function segment(overrides = {}) {
5
+ return RoofSegmentNode.parse({
6
+ id: 'rseg_test',
7
+ type: 'roof-segment',
8
+ roofType: 'gable',
9
+ width: 8,
10
+ depth: 6,
11
+ wallHeight: 2.6,
12
+ wallThickness: 0.1,
13
+ pitch: 40,
14
+ ...overrides,
15
+ });
16
+ }
17
+ describe('roof wall face frames', () => {
18
+ test('frame z = 0 lands on the nominal footprint (wall mid-plane)', () => {
19
+ const seg = segment();
20
+ // front face, u at the face middle, v = 1, mid-plane.
21
+ const point = roofFacePointToSegment(seg, 'front', [(8 + 0.1) / 2, 1, 0]);
22
+ expect(point[0]).toBeCloseTo(0);
23
+ expect(point[1]).toBeCloseTo(1);
24
+ expect(point[2]).toBeCloseTo(3); // depth / 2 — the footprint plane
25
+ });
26
+ test('frame +z is the outward normal on every face', () => {
27
+ const seg = segment();
28
+ for (const [faceId, axis, sign] of [
29
+ ['front', 2, 1],
30
+ ['back', 2, -1],
31
+ ['right', 0, 1],
32
+ ['left', 0, -1],
33
+ ]) {
34
+ const onPlane = roofFacePointToSegment(seg, faceId, [1, 1, 0]);
35
+ const pushed = roofFacePointToSegment(seg, faceId, [1, 1, 0.5]);
36
+ expect(pushed[axis] - onPlane[axis]).toBeCloseTo(0.5 * sign);
37
+ // The other horizontal axis is unaffected by the push.
38
+ const other = axis === 2 ? 0 : 2;
39
+ expect(pushed[other] - onPlane[other]).toBeCloseTo(0);
40
+ }
41
+ });
42
+ test('face frame agrees with the hit resolver coordinates', () => {
43
+ const seg = segment();
44
+ // A point on the outer surface (z = +thickness/2 off the mid-plane)
45
+ // must read back with the same u/v and dist ≈ 0 off the outer plane.
46
+ const segLocal = roofFacePointToSegment(seg, 'right', [2.5, 1.25, 0.05]);
47
+ const { u, v, dist } = segmentPointToRoofWallFace(seg, 'right', segLocal);
48
+ expect(u).toBeCloseTo(2.5);
49
+ expect(v).toBeCloseTo(1.25);
50
+ expect(dist).toBeCloseTo(0);
51
+ });
52
+ test('resizing the segment moves the frame, not the stored coords', () => {
53
+ // The core live-tracking property: the same face-local point maps to
54
+ // the new plane after a depth change — children follow by re-render.
55
+ const before = roofFacePointToSegment(segment(), 'front', [2, 1, 0]);
56
+ const after = roofFacePointToSegment(segment({ depth: 8 }), 'front', [2, 1, 0]);
57
+ expect(before[2]).toBeCloseTo(3);
58
+ expect(after[2]).toBeCloseTo(4);
59
+ expect(after[1]).toBeCloseTo(before[1]);
60
+ });
61
+ test('frame yaw matches the face descriptor yaw', () => {
62
+ const seg = segment();
63
+ for (const faceId of ['front', 'back', 'right', 'left']) {
64
+ expect(getRoofWallFaceFrame(seg, faceId).yaw).toBe(getRoofSegmentWallFace(seg, faceId).yaw);
65
+ }
66
+ });
67
+ test('dutch width-axis roofs expose hostable gable-end wall profiles on the short ends', () => {
68
+ const faces = getRoofSegmentWallFaces(segment({
69
+ roofType: 'dutch',
70
+ width: 8,
71
+ depth: 6,
72
+ dutchHipWidthRatio: 0.25,
73
+ dutchWaistLengthRatio: 1,
74
+ }));
75
+ const left = faces.find((face) => face.id === 'left');
76
+ const right = faces.find((face) => face.id === 'right');
77
+ expect(left?.profile.length).toBeGreaterThan(5);
78
+ expect(right?.profile.length).toBeGreaterThan(5);
79
+ expect(left?.profile[3]?.[1]).toBeCloseTo(left?.profile[2]?.[1] ?? 0);
80
+ });
81
+ test('dutch long-side faces stay rectangular while only the gable ends rise above the eave', () => {
82
+ const faces = getRoofSegmentWallFaces(segment({
83
+ roofType: 'dutch',
84
+ width: 8,
85
+ depth: 6,
86
+ }));
87
+ const front = faces.find((face) => face.id === 'front');
88
+ const back = faces.find((face) => face.id === 'back');
89
+ expect(front?.profile).toHaveLength(4);
90
+ expect(back?.profile).toHaveLength(4);
91
+ });
92
+ });