@pascal-app/core 0.9.1 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/README.md +17 -10
  2. package/dist/events/bus.d.ts +49 -2
  3. package/dist/events/bus.d.ts.map +1 -1
  4. package/dist/hooks/scene-registry/item-clip-registry.d.ts +18 -0
  5. package/dist/hooks/scene-registry/item-clip-registry.d.ts.map +1 -0
  6. package/dist/hooks/scene-registry/item-clip-registry.js +10 -0
  7. package/dist/hooks/scene-registry/scene-registry.d.ts +8 -1
  8. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
  9. package/dist/hooks/scene-registry/scene-registry.js +27 -1
  10. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +17 -1
  11. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -1
  12. package/dist/hooks/spatial-grid/floor-placed-elevation.js +61 -8
  13. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +135 -28
  14. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  15. package/dist/hooks/spatial-grid/spatial-grid-manager.js +427 -271
  16. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +46 -1
  17. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  18. package/dist/hooks/spatial-grid/spatial-grid-sync.js +208 -9
  19. package/dist/hooks/spatial-grid/support-host-id.d.ts +3 -0
  20. package/dist/hooks/spatial-grid/support-host-id.d.ts.map +1 -0
  21. package/dist/hooks/spatial-grid/support-host-id.js +2 -0
  22. package/dist/hooks/spatial-grid/support-host-patch.d.ts +51 -0
  23. package/dist/hooks/spatial-grid/support-host-patch.d.ts.map +1 -0
  24. package/dist/hooks/spatial-grid/support-host-patch.js +164 -0
  25. package/dist/index.d.ts +33 -11
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +32 -10
  28. package/dist/lib/level-name.d.ts +4 -0
  29. package/dist/lib/level-name.d.ts.map +1 -0
  30. package/dist/lib/level-name.js +10 -0
  31. package/dist/lib/measurement-geometry.d.ts +23 -0
  32. package/dist/lib/measurement-geometry.d.ts.map +1 -0
  33. package/dist/lib/measurement-geometry.js +249 -0
  34. package/dist/lib/polygon-geometry.d.ts +0 -1
  35. package/dist/lib/polygon-geometry.d.ts.map +1 -1
  36. package/dist/lib/polygon-geometry.js +0 -18
  37. package/dist/lib/selection-proxy.d.ts +4 -0
  38. package/dist/lib/selection-proxy.d.ts.map +1 -0
  39. package/dist/lib/selection-proxy.js +12 -0
  40. package/dist/lib/slab-polygon.d.ts +31 -2
  41. package/dist/lib/slab-polygon.d.ts.map +1 -1
  42. package/dist/lib/slab-polygon.js +557 -31
  43. package/dist/lib/slots.d.ts +14 -0
  44. package/dist/lib/slots.d.ts.map +1 -0
  45. package/dist/lib/slots.js +26 -0
  46. package/dist/lib/space-detection.d.ts +40 -6
  47. package/dist/lib/space-detection.d.ts.map +1 -1
  48. package/dist/lib/space-detection.js +574 -192
  49. package/dist/lib/terrain-brush.d.ts +163 -0
  50. package/dist/lib/terrain-brush.d.ts.map +1 -0
  51. package/dist/lib/terrain-brush.js +359 -0
  52. package/dist/lib/terrain-codec.d.ts +51 -0
  53. package/dist/lib/terrain-codec.d.ts.map +1 -0
  54. package/dist/lib/terrain-codec.js +167 -0
  55. package/dist/lib/terrain-field.d.ts +150 -0
  56. package/dist/lib/terrain-field.d.ts.map +1 -0
  57. package/dist/lib/terrain-field.js +248 -0
  58. package/dist/lib/terrain-raycast.d.ts +44 -0
  59. package/dist/lib/terrain-raycast.d.ts.map +1 -0
  60. package/dist/lib/terrain-raycast.js +188 -0
  61. package/dist/lib/terrain-source.d.ts +60 -0
  62. package/dist/lib/terrain-source.d.ts.map +1 -0
  63. package/dist/lib/terrain-source.js +90 -0
  64. package/dist/lib/terrain-support.d.ts +60 -0
  65. package/dist/lib/terrain-support.d.ts.map +1 -0
  66. package/dist/lib/terrain-support.js +150 -0
  67. package/dist/lib/wall-distance.d.ts +60 -0
  68. package/dist/lib/wall-distance.d.ts.map +1 -0
  69. package/dist/lib/wall-distance.js +85 -0
  70. package/dist/lib/zone-quantities.d.ts +21 -0
  71. package/dist/lib/zone-quantities.d.ts.map +1 -0
  72. package/dist/lib/zone-quantities.js +428 -0
  73. package/dist/material-library.d.ts +28 -1
  74. package/dist/material-library.d.ts.map +1 -1
  75. package/dist/material-library.js +2233 -441
  76. package/dist/registry/handles.d.ts +91 -3
  77. package/dist/registry/handles.d.ts.map +1 -1
  78. package/dist/registry/index.d.ts +3 -3
  79. package/dist/registry/index.d.ts.map +1 -1
  80. package/dist/registry/index.js +1 -1
  81. package/dist/registry/registry.d.ts +46 -3
  82. package/dist/registry/registry.d.ts.map +1 -1
  83. package/dist/registry/registry.js +82 -1
  84. package/dist/registry/subtree.d.ts.map +1 -1
  85. package/dist/registry/subtree.js +8 -1
  86. package/dist/registry/types.d.ts +781 -39
  87. package/dist/registry/types.d.ts.map +1 -1
  88. package/dist/schema/index.d.ts +27 -8
  89. package/dist/schema/index.d.ts.map +1 -1
  90. package/dist/schema/index.js +23 -6
  91. package/dist/schema/material.d.ts +3 -2
  92. package/dist/schema/material.d.ts.map +1 -1
  93. package/dist/schema/material.js +7 -5
  94. package/dist/schema/nodes/box-vent.d.ts +2 -2
  95. package/dist/schema/nodes/cabinet.d.ts +536 -0
  96. package/dist/schema/nodes/cabinet.d.ts.map +1 -0
  97. package/dist/schema/nodes/cabinet.js +139 -0
  98. package/dist/schema/nodes/ceiling.d.ts +4 -3
  99. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  100. package/dist/schema/nodes/ceiling.js +11 -1
  101. package/dist/schema/nodes/chimney.d.ts +4 -4
  102. package/dist/schema/nodes/column.d.ts +4 -2
  103. package/dist/schema/nodes/column.d.ts.map +1 -1
  104. package/dist/schema/nodes/column.js +5 -0
  105. package/dist/schema/nodes/construction-dimension.d.ts +195 -0
  106. package/dist/schema/nodes/construction-dimension.d.ts.map +1 -0
  107. package/dist/schema/nodes/construction-dimension.js +151 -0
  108. package/dist/schema/nodes/cupola.d.ts +2 -2
  109. package/dist/schema/nodes/door.d.ts +42 -5
  110. package/dist/schema/nodes/door.d.ts.map +1 -1
  111. package/dist/schema/nodes/door.js +32 -0
  112. package/dist/schema/nodes/dormer.d.ts +8 -8
  113. package/dist/schema/nodes/dormer.js +1 -1
  114. package/dist/schema/nodes/downspout.d.ts +2 -2
  115. package/dist/schema/nodes/duct-fitting.d.ts +91 -0
  116. package/dist/schema/nodes/duct-fitting.d.ts.map +1 -0
  117. package/dist/schema/nodes/duct-fitting.js +92 -0
  118. package/dist/schema/nodes/duct-segment.d.ts +61 -0
  119. package/dist/schema/nodes/duct-segment.d.ts.map +1 -0
  120. package/dist/schema/nodes/duct-segment.js +73 -0
  121. package/dist/schema/nodes/duct-terminal.d.ts +58 -0
  122. package/dist/schema/nodes/duct-terminal.d.ts.map +1 -0
  123. package/dist/schema/nodes/duct-terminal.js +53 -0
  124. package/dist/schema/nodes/elevator.d.ts +3 -2
  125. package/dist/schema/nodes/elevator.d.ts.map +1 -1
  126. package/dist/schema/nodes/elevator.js +3 -0
  127. package/dist/schema/nodes/eyebrow-vent.d.ts +2 -2
  128. package/dist/schema/nodes/fence.d.ts +20 -2
  129. package/dist/schema/nodes/fence.d.ts.map +1 -1
  130. package/dist/schema/nodes/fence.js +33 -2
  131. package/dist/schema/nodes/gutter.d.ts +2 -2
  132. package/dist/schema/nodes/hvac-equipment.d.ts +64 -0
  133. package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -0
  134. package/dist/schema/nodes/hvac-equipment.js +57 -0
  135. package/dist/schema/nodes/item.d.ts +9 -0
  136. package/dist/schema/nodes/item.d.ts.map +1 -1
  137. package/dist/schema/nodes/item.js +24 -0
  138. package/dist/schema/nodes/level.d.ts +2 -1
  139. package/dist/schema/nodes/level.d.ts.map +1 -1
  140. package/dist/schema/nodes/level.js +12 -31
  141. package/dist/schema/nodes/lineset.d.ts +42 -0
  142. package/dist/schema/nodes/lineset.d.ts.map +1 -0
  143. package/dist/schema/nodes/lineset.js +38 -0
  144. package/dist/schema/nodes/liquid-line.d.ts +34 -0
  145. package/dist/schema/nodes/liquid-line.d.ts.map +1 -0
  146. package/dist/schema/nodes/liquid-line.js +24 -0
  147. package/dist/schema/nodes/measurement.d.ts +306 -0
  148. package/dist/schema/nodes/measurement.d.ts.map +1 -0
  149. package/dist/schema/nodes/measurement.js +87 -0
  150. package/dist/schema/nodes/pipe-fitting.d.ts +56 -0
  151. package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -0
  152. package/dist/schema/nodes/pipe-fitting.js +45 -0
  153. package/dist/schema/nodes/pipe-segment.d.ts +48 -0
  154. package/dist/schema/nodes/pipe-segment.d.ts.map +1 -0
  155. package/dist/schema/nodes/pipe-segment.js +36 -0
  156. package/dist/schema/nodes/pipe-trap.d.ts +43 -0
  157. package/dist/schema/nodes/pipe-trap.d.ts.map +1 -0
  158. package/dist/schema/nodes/pipe-trap.js +36 -0
  159. package/dist/schema/nodes/ridge-vent.d.ts +18 -3
  160. package/dist/schema/nodes/ridge-vent.d.ts.map +1 -1
  161. package/dist/schema/nodes/ridge-vent.js +221 -3
  162. package/dist/schema/nodes/roof-segment-shape.d.ts +90 -0
  163. package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -0
  164. package/dist/schema/nodes/roof-segment-shape.js +318 -0
  165. package/dist/schema/nodes/roof-segment-walls.d.ts +101 -0
  166. package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -0
  167. package/dist/schema/nodes/roof-segment-walls.js +330 -0
  168. package/dist/schema/nodes/roof-segment.d.ts +84 -8
  169. package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
  170. package/dist/schema/nodes/roof-segment.js +290 -5
  171. package/dist/schema/nodes/roof.d.ts +8 -8
  172. package/dist/schema/nodes/shelf.d.ts +4 -2
  173. package/dist/schema/nodes/shelf.d.ts.map +1 -1
  174. package/dist/schema/nodes/shelf.js +8 -0
  175. package/dist/schema/nodes/site.d.ts +9 -0
  176. package/dist/schema/nodes/site.d.ts.map +1 -1
  177. package/dist/schema/nodes/site.js +8 -6
  178. package/dist/schema/nodes/skylight.d.ts +10 -10
  179. package/dist/schema/nodes/slab.d.ts +8 -2
  180. package/dist/schema/nodes/slab.d.ts.map +1 -1
  181. package/dist/schema/nodes/slab.js +18 -2
  182. package/dist/schema/nodes/solar-panel.d.ts +4 -4
  183. package/dist/schema/nodes/spawn.d.ts +1 -0
  184. package/dist/schema/nodes/spawn.d.ts.map +1 -1
  185. package/dist/schema/nodes/spawn.js +2 -0
  186. package/dist/schema/nodes/stair-segment.d.ts +4 -4
  187. package/dist/schema/nodes/stair.d.ts +16 -13
  188. package/dist/schema/nodes/stair.d.ts.map +1 -1
  189. package/dist/schema/nodes/stair.js +11 -1
  190. package/dist/schema/nodes/structural-grid.d.ts +27 -0
  191. package/dist/schema/nodes/structural-grid.d.ts.map +1 -0
  192. package/dist/schema/nodes/structural-grid.js +17 -0
  193. package/dist/schema/nodes/turbine-vent.d.ts +2 -2
  194. package/dist/schema/nodes/wall.d.ts +248 -6
  195. package/dist/schema/nodes/wall.d.ts.map +1 -1
  196. package/dist/schema/nodes/wall.js +266 -0
  197. package/dist/schema/nodes/window.d.ts +40 -3
  198. package/dist/schema/nodes/window.d.ts.map +1 -1
  199. package/dist/schema/nodes/window.js +30 -0
  200. package/dist/schema/nodes/zone.d.ts +22 -0
  201. package/dist/schema/nodes/zone.d.ts.map +1 -1
  202. package/dist/schema/nodes/zone.js +21 -0
  203. package/dist/schema/scene-material.d.ts +41 -0
  204. package/dist/schema/scene-material.d.ts.map +1 -0
  205. package/dist/schema/scene-material.js +9 -0
  206. package/dist/schema/terrain.d.ts +28 -0
  207. package/dist/schema/terrain.d.ts.map +1 -0
  208. package/dist/schema/terrain.js +30 -0
  209. package/dist/schema/types.d.ts +1300 -97
  210. package/dist/schema/types.d.ts.map +1 -1
  211. package/dist/schema/types.js +27 -0
  212. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts +80 -0
  213. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts.map +1 -0
  214. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.js +89 -0
  215. package/dist/services/alignment-anchors.d.ts.map +1 -1
  216. package/dist/services/alignment-anchors.js +30 -1
  217. package/dist/services/alignment.d.ts +5 -0
  218. package/dist/services/alignment.d.ts.map +1 -1
  219. package/dist/services/alignment.js +2 -0
  220. package/dist/services/hosting.d.ts +8 -0
  221. package/dist/services/hosting.d.ts.map +1 -1
  222. package/dist/services/hosting.js +13 -4
  223. package/dist/services/index.d.ts +9 -2
  224. package/dist/services/index.d.ts.map +1 -1
  225. package/dist/services/index.js +9 -2
  226. package/dist/services/level-height.d.ts +27 -0
  227. package/dist/services/level-height.d.ts.map +1 -0
  228. package/dist/services/level-height.js +94 -0
  229. package/dist/services/opening-guides.d.ts +141 -0
  230. package/dist/services/opening-guides.d.ts.map +1 -0
  231. package/dist/services/opening-guides.js +267 -0
  232. package/dist/services/port-connectivity.d.ts +115 -0
  233. package/dist/services/port-connectivity.d.ts.map +1 -0
  234. package/dist/services/port-connectivity.js +300 -0
  235. package/dist/services/riser-diagram.d.ts +40 -0
  236. package/dist/services/riser-diagram.d.ts.map +1 -0
  237. package/dist/services/riser-diagram.js +110 -0
  238. package/dist/services/snap.d.ts +9 -1
  239. package/dist/services/snap.d.ts.map +1 -1
  240. package/dist/services/snap.js +21 -1
  241. package/dist/services/storey.d.ts +103 -0
  242. package/dist/services/storey.d.ts.map +1 -0
  243. package/dist/services/storey.js +296 -0
  244. package/dist/services/system-graph.d.ts +30 -0
  245. package/dist/services/system-graph.d.ts.map +1 -0
  246. package/dist/services/system-graph.js +164 -0
  247. package/dist/services/validate-dwv.d.ts +32 -0
  248. package/dist/services/validate-dwv.d.ts.map +1 -0
  249. package/dist/services/validate-dwv.js +118 -0
  250. package/dist/store/actions/node-actions.d.ts.map +1 -1
  251. package/dist/store/actions/node-actions.js +141 -4
  252. package/dist/store/history-control.d.ts +31 -0
  253. package/dist/store/history-control.d.ts.map +1 -1
  254. package/dist/store/history-control.js +130 -0
  255. package/dist/store/use-live-terrain.d.ts +65 -0
  256. package/dist/store/use-live-terrain.d.ts.map +1 -0
  257. package/dist/store/use-live-terrain.js +31 -0
  258. package/dist/store/use-live-transforms.d.ts +8 -0
  259. package/dist/store/use-live-transforms.d.ts.map +1 -1
  260. package/dist/store/use-scene.d.ts +47 -3
  261. package/dist/store/use-scene.d.ts.map +1 -1
  262. package/dist/store/use-scene.js +992 -55
  263. package/dist/systems/elevator/elevator-opening-system.d.ts +1 -0
  264. package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -1
  265. package/dist/systems/elevator/elevator-opening-system.js +27 -21
  266. package/dist/systems/elevator/elevator-service.d.ts +0 -2
  267. package/dist/systems/elevator/elevator-service.d.ts.map +1 -1
  268. package/dist/systems/elevator/elevator-service.js +2 -24
  269. package/dist/systems/fence/fence-centerline.d.ts +12 -0
  270. package/dist/systems/fence/fence-centerline.d.ts.map +1 -0
  271. package/dist/systems/fence/fence-centerline.js +37 -0
  272. package/dist/systems/fence/fence-spline.d.ts +39 -0
  273. package/dist/systems/fence/fence-spline.d.ts.map +1 -0
  274. package/dist/systems/fence/fence-spline.js +196 -0
  275. package/dist/systems/slab/slab-placement.d.ts +11 -0
  276. package/dist/systems/slab/slab-placement.d.ts.map +1 -0
  277. package/dist/systems/slab/slab-placement.js +14 -0
  278. package/dist/systems/slab/slab-support.d.ts +137 -0
  279. package/dist/systems/slab/slab-support.d.ts.map +1 -0
  280. package/dist/systems/slab/slab-support.js +557 -0
  281. package/dist/systems/stair/stair-footprint.d.ts.map +1 -1
  282. package/dist/systems/stair/stair-footprint.js +21 -11
  283. package/dist/systems/stair/stair-opening-preview.d.ts +1300 -505
  284. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
  285. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  286. package/dist/systems/stair/stair-opening-sync.js +19 -33
  287. package/dist/systems/stair/stair-opening-system.d.ts.map +1 -1
  288. package/dist/systems/stair/stair-opening-system.js +32 -4
  289. package/dist/systems/stair/stair-rise.d.ts +22 -0
  290. package/dist/systems/stair/stair-rise.d.ts.map +1 -0
  291. package/dist/systems/stair/stair-rise.js +79 -0
  292. package/dist/systems/wall/wall-curve.d.ts +10 -0
  293. package/dist/systems/wall/wall-curve.d.ts.map +1 -1
  294. package/dist/systems/wall/wall-curve.js +1 -1
  295. package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
  296. package/dist/systems/wall/wall-mitering.js +44 -8
  297. package/dist/systems/wall/wall-top.d.ts +40 -0
  298. package/dist/systems/wall/wall-top.d.ts.map +1 -0
  299. package/dist/systems/wall/wall-top.js +46 -0
  300. package/dist/utils/clone-scene-graph.d.ts +1 -0
  301. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  302. package/dist/utils/clone-scene-graph.js +56 -5
  303. package/dist/utils/heal-scene-graph.d.ts +18 -0
  304. package/dist/utils/heal-scene-graph.d.ts.map +1 -0
  305. package/dist/utils/heal-scene-graph.js +106 -0
  306. package/dist/validation/validate-build-json.d.ts +3 -0
  307. package/dist/validation/validate-build-json.d.ts.map +1 -1
  308. package/dist/validation/validate-build-json.js +90 -5
  309. package/package.json +4 -4
  310. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +0 -2
  311. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +0 -1
  312. package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +0 -273
  313. package/dist/lib/space-detection-pause.test.d.ts +0 -2
  314. package/dist/lib/space-detection-pause.test.d.ts.map +0 -1
  315. package/dist/lib/space-detection-pause.test.js +0 -42
  316. package/dist/lib/space-detection.test.d.ts +0 -2
  317. package/dist/lib/space-detection.test.d.ts.map +0 -1
  318. package/dist/lib/space-detection.test.js +0 -77
  319. package/dist/registry/registry.test.d.ts +0 -2
  320. package/dist/registry/registry.test.d.ts.map +0 -1
  321. package/dist/registry/registry.test.js +0 -183
  322. package/dist/registry/relations-resolver.test.d.ts +0 -2
  323. package/dist/registry/relations-resolver.test.d.ts.map +0 -1
  324. package/dist/registry/relations-resolver.test.js +0 -183
  325. package/dist/registry/scene-api.test.d.ts +0 -2
  326. package/dist/registry/scene-api.test.d.ts.map +0 -1
  327. package/dist/registry/scene-api.test.js +0 -183
  328. package/dist/registry/subtree.test.d.ts +0 -2
  329. package/dist/registry/subtree.test.d.ts.map +0 -1
  330. package/dist/registry/subtree.test.js +0 -133
  331. package/dist/schema/asset-url.test.d.ts +0 -2
  332. package/dist/schema/asset-url.test.d.ts.map +0 -1
  333. package/dist/schema/asset-url.test.js +0 -134
  334. package/dist/services/alignment-anchors.test.d.ts +0 -2
  335. package/dist/services/alignment-anchors.test.d.ts.map +0 -1
  336. package/dist/services/alignment-anchors.test.js +0 -383
  337. package/dist/services/alignment.test.d.ts +0 -2
  338. package/dist/services/alignment.test.d.ts.map +0 -1
  339. package/dist/services/alignment.test.js +0 -92
  340. package/dist/services/drag-session.test.d.ts +0 -2
  341. package/dist/services/drag-session.test.d.ts.map +0 -1
  342. package/dist/services/drag-session.test.js +0 -219
  343. package/dist/services/hosting.test.d.ts +0 -2
  344. package/dist/services/hosting.test.d.ts.map +0 -1
  345. package/dist/services/hosting.test.js +0 -199
  346. package/dist/services/movement.test.d.ts +0 -2
  347. package/dist/services/movement.test.d.ts.map +0 -1
  348. package/dist/services/movement.test.js +0 -133
  349. package/dist/services/single-undo-dance.test.d.ts +0 -2
  350. package/dist/services/single-undo-dance.test.d.ts.map +0 -1
  351. package/dist/services/single-undo-dance.test.js +0 -210
  352. package/dist/services/snap.test.d.ts +0 -2
  353. package/dist/services/snap.test.d.ts.map +0 -1
  354. package/dist/services/snap.test.js +0 -95
  355. package/dist/store/actions/node-mutation-sanitize.test.d.ts +0 -2
  356. package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +0 -1
  357. package/dist/store/actions/node-mutation-sanitize.test.js +0 -147
  358. package/dist/store/actions/reparent.test.d.ts +0 -2
  359. package/dist/store/actions/reparent.test.d.ts.map +0 -1
  360. package/dist/store/actions/reparent.test.js +0 -212
  361. package/dist/store/use-scene-elevator-migration.test.d.ts +0 -2
  362. package/dist/store/use-scene-elevator-migration.test.d.ts.map +0 -1
  363. package/dist/store/use-scene-elevator-migration.test.js +0 -109
  364. package/dist/systems/elevator/elevator-opening-sync.test.d.ts +0 -2
  365. package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +0 -1
  366. package/dist/systems/elevator/elevator-opening-sync.test.js +0 -146
  367. package/dist/systems/elevator/elevator-runtime.test.d.ts +0 -2
  368. package/dist/systems/elevator/elevator-runtime.test.d.ts.map +0 -1
  369. package/dist/systems/elevator/elevator-runtime.test.js +0 -64
  370. package/dist/systems/stair/stair-opening-preview.test.d.ts +0 -2
  371. package/dist/systems/stair/stair-opening-preview.test.d.ts.map +0 -1
  372. package/dist/systems/stair/stair-opening-preview.test.js +0 -80
  373. package/dist/systems/stair/stair-opening-sync.test.d.ts +0 -2
  374. package/dist/systems/stair/stair-opening-sync.test.d.ts.map +0 -1
  375. package/dist/systems/stair/stair-opening-sync.test.js +0 -446
  376. package/dist/utils/clone-scene-graph.test.d.ts +0 -2
  377. package/dist/utils/clone-scene-graph.test.d.ts.map +0 -1
  378. package/dist/utils/clone-scene-graph.test.js +0 -59
@@ -0,0 +1,85 @@
1
+ import { isCurvedWall } from '../systems/wall/wall-curve';
2
+ /**
3
+ * Pure plan-space wall-distance math shared by the 2D opening snap
4
+ * (`findClosestWallInPlan` in @pascal-app/nodes) and the editor's 2D
5
+ * Voronoi debug overlay. One source of truth means the overlay is a
6
+ * faithful picture of what the snap actually decides.
7
+ *
8
+ * A wall segment's Voronoi cell is exactly "the points whose nearest wall
9
+ * is this segment", so nearest-segment classification == the segment
10
+ * Voronoi diagram. Curved walls are excluded (the opening snap rejects
11
+ * them — mitering + arc + opening tears in 3D).
12
+ */
13
+ /**
14
+ * Max cursor-to-wall plan distance (metres) for a 2D opening to snap onto a
15
+ * wall. Tight, because plan walls are thin and often close together — a large
16
+ * radius would let a far wall's region reach across a nearer one. Shared so the
17
+ * snap and the Voronoi debug overlay clip to the exact same range.
18
+ */
19
+ export const WALL_SNAP_DISTANCE_M = 0.4;
20
+ /**
21
+ * Collect the straight (non-curved) wall segments that are direct children
22
+ * of a level — the candidates an opening can snap onto.
23
+ */
24
+ export function collectLevelWallSegments(nodes, levelId) {
25
+ if (!levelId)
26
+ return [];
27
+ const level = nodes[levelId];
28
+ const childIds = level?.children;
29
+ if (!Array.isArray(childIds))
30
+ return [];
31
+ const segments = [];
32
+ for (const childId of childIds) {
33
+ const node = nodes[childId];
34
+ if (node?.type !== 'wall')
35
+ continue;
36
+ const wall = node;
37
+ if (isCurvedWall(wall))
38
+ continue;
39
+ const dx = wall.end[0] - wall.start[0];
40
+ const dy = wall.end[1] - wall.start[1];
41
+ const length = Math.hypot(dx, dy);
42
+ if (length < 1e-6)
43
+ continue;
44
+ segments.push({
45
+ wall,
46
+ start: wall.start,
47
+ end: wall.end,
48
+ dirX: dx / length,
49
+ dirY: dy / length,
50
+ length,
51
+ });
52
+ }
53
+ return segments;
54
+ }
55
+ /** Closest point + signed offset of one query point against one segment. */
56
+ export function closestOnSegment(segment, pointX, pointY) {
57
+ const px = pointX - segment.start[0];
58
+ const py = pointY - segment.start[1];
59
+ const along = Math.max(0, Math.min(segment.length, px * segment.dirX + py * segment.dirY));
60
+ const perp = px * -segment.dirY + py * segment.dirX;
61
+ const closestX = segment.start[0] + segment.dirX * along;
62
+ const closestY = segment.start[1] + segment.dirY * along;
63
+ const distance = Math.hypot(pointX - closestX, pointY - closestY);
64
+ return { distance, along, perp };
65
+ }
66
+ /**
67
+ * The single nearest wall segment to a plan point — its Voronoi cell. Returns
68
+ * null when `segments` is empty or (when `maxDistance` is given) nothing is
69
+ * within range. Ties resolve to the first segment scanned; callers pass an
70
+ * already-curved-filtered list from `collectLevelWallSegments`.
71
+ */
72
+ export function nearestWallSegment(segments, pointX, pointY, maxDistance = Number.POSITIVE_INFINITY, excludeWallId) {
73
+ let best = null;
74
+ for (const segment of segments) {
75
+ if (excludeWallId && segment.wall.id === excludeWallId)
76
+ continue;
77
+ const { distance, along, perp } = closestOnSegment(segment, pointX, pointY);
78
+ if (distance > maxDistance)
79
+ continue;
80
+ if (best && distance >= best.distance)
81
+ continue;
82
+ best = { segment, distance, along, perp };
83
+ }
84
+ return best;
85
+ }
@@ -0,0 +1,21 @@
1
+ import type { AnyNode, ZoneNode } from '../schema';
2
+ export type ZoneQuantityValue = {
3
+ status: 'available';
4
+ value: number;
5
+ note?: string;
6
+ } | {
7
+ status: 'unavailable';
8
+ reason: string;
9
+ };
10
+ export type ZoneQuantityReport = {
11
+ classification: 'footprint' | 'enclosed-room';
12
+ footprintArea: number;
13
+ perimeter: number;
14
+ edgeLengths: number[];
15
+ boundaryWallIds: string[];
16
+ wallSurface: ZoneQuantityValue;
17
+ floorSurface: ZoneQuantityValue;
18
+ volume: ZoneQuantityValue;
19
+ };
20
+ export declare function deriveZoneQuantityReport(zone: ZoneNode, sceneNodes: Readonly<Record<string, AnyNode>>): ZoneQuantityReport;
21
+ //# sourceMappingURL=zone-quantities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zone-quantities.d.ts","sourceRoot":"","sources":["../../src/lib/zone-quantities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAmC,QAAQ,EAAE,MAAM,WAAW,CAAA;AAYnF,MAAM,MAAM,iBAAiB,GACzB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE7C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,WAAW,GAAG,eAAe,CAAA;IAC7C,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,WAAW,EAAE,iBAAiB,CAAA;IAC9B,YAAY,EAAE,iBAAiB,CAAA;IAC/B,MAAM,EAAE,iBAAiB,CAAA;CAC1B,CAAA;AA8bD,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC5C,kBAAkB,CAmGpB"}
@@ -0,0 +1,428 @@
1
+ import { DEFAULT_LEVEL_HEIGHT, resolveCeilingHeight } from '../services/level-height';
2
+ import { getWallPlaneTop } from '../services/storey';
3
+ import { computeWallSlabSupport } from '../systems/slab/slab-support';
4
+ import { sampleWallCenterline } from '../systems/wall/wall-curve';
5
+ import { DEFAULT_WALL_THICKNESS } from '../systems/wall/wall-footprint';
6
+ import { resolveWallEffectiveHeight } from '../systems/wall/wall-top';
7
+ import { detectSpacesForLevel } from './space-detection';
8
+ const BOUNDARY_TOLERANCE = 0.08;
9
+ const SURFACE_COVERAGE_THRESHOLD = 0.95;
10
+ const SPACE_CONTAINMENT_THRESHOLD = 0.95;
11
+ const COVERAGE_SAMPLE_STEPS = 24;
12
+ const SURFACE_DATUM_EPSILON = 1e-4;
13
+ function pointDistance(a, b) {
14
+ return Math.hypot(a[0] - b[0], a[1] - b[1]);
15
+ }
16
+ function pointToSegmentDistance(point, start, end) {
17
+ const dx = end[0] - start[0];
18
+ const dy = end[1] - start[1];
19
+ const lengthSquared = dx * dx + dy * dy;
20
+ if (lengthSquared <= 1e-12)
21
+ return pointDistance(point, start);
22
+ const t = Math.max(0, Math.min(1, ((point[0] - start[0]) * dx + (point[1] - start[1]) * dy) / lengthSquared));
23
+ return pointDistance(point, [start[0] + t * dx, start[1] + t * dy]);
24
+ }
25
+ function pointToPolygonBoundaryDistance(point, polygon) {
26
+ let best = Number.POSITIVE_INFINITY;
27
+ for (let index = 0; index < polygon.length; index += 1) {
28
+ const start = polygon[index];
29
+ const end = polygon[(index + 1) % polygon.length];
30
+ if (!(start && end))
31
+ continue;
32
+ best = Math.min(best, pointToSegmentDistance(point, start, end));
33
+ }
34
+ return best;
35
+ }
36
+ function signedPolygonArea(polygon) {
37
+ let area = 0;
38
+ for (let index = 0; index < polygon.length; index += 1) {
39
+ const start = polygon[index];
40
+ const end = polygon[(index + 1) % polygon.length];
41
+ if (!(start && end))
42
+ continue;
43
+ area += start[0] * end[1] - end[0] * start[1];
44
+ }
45
+ return area / 2;
46
+ }
47
+ function polygonArea(polygon) {
48
+ return Math.abs(signedPolygonArea(polygon));
49
+ }
50
+ function polygonsDescribeSameRegion(a, b) {
51
+ if (a.length < 3 || b.length < 3)
52
+ return false;
53
+ const aArea = polygonArea(a);
54
+ const bArea = polygonArea(b);
55
+ const areaTolerance = Math.max(0.02, Math.max(aArea, bArea) * 0.01);
56
+ if (Math.abs(aArea - bArea) > areaTolerance)
57
+ return false;
58
+ return (a.every((point) => pointToPolygonBoundaryDistance(point, b) <= BOUNDARY_TOLERANCE) &&
59
+ b.every((point) => pointToPolygonBoundaryDistance(point, a) <= BOUNDARY_TOLERANCE));
60
+ }
61
+ function pointInPolygon(point, polygon, includeBoundary = true) {
62
+ if (polygon.length < 3)
63
+ return false;
64
+ if (pointToPolygonBoundaryDistance(point, polygon) <= 1e-6)
65
+ return includeBoundary;
66
+ let inside = false;
67
+ for (let index = 0, previous = polygon.length - 1; index < polygon.length; previous = index++) {
68
+ const currentPoint = polygon[index];
69
+ const previousPoint = polygon[previous];
70
+ if (currentPoint[1] > point[1] !== previousPoint[1] > point[1] &&
71
+ point[0] <
72
+ ((previousPoint[0] - currentPoint[0]) * (point[1] - currentPoint[1])) /
73
+ (previousPoint[1] - currentPoint[1]) +
74
+ currentPoint[0]) {
75
+ inside = !inside;
76
+ }
77
+ }
78
+ return inside;
79
+ }
80
+ function segmentsCrossProperly(a, b, c, d) {
81
+ const cross = (start, end, point) => (end[0] - start[0]) * (point[1] - start[1]) - (end[1] - start[1]) * (point[0] - start[0]);
82
+ const abC = cross(a, b, c);
83
+ const abD = cross(a, b, d);
84
+ const cdA = cross(c, d, a);
85
+ const cdB = cross(c, d, b);
86
+ return abC * abD < -1e-12 && cdA * cdB < -1e-12;
87
+ }
88
+ function polygonContainsRegion(outer, inner) {
89
+ if (outer.length < 3 || inner.length < 3)
90
+ return false;
91
+ if (polygonsDescribeSameRegion(outer, inner))
92
+ return true;
93
+ if (!inner.every((point) => pointInPolygon(point, outer) ||
94
+ pointToPolygonBoundaryDistance(point, outer) <= BOUNDARY_TOLERANCE)) {
95
+ return false;
96
+ }
97
+ for (let innerIndex = 0; innerIndex < inner.length; innerIndex += 1) {
98
+ const innerStart = inner[innerIndex];
99
+ const innerEnd = inner[(innerIndex + 1) % inner.length];
100
+ for (let outerIndex = 0; outerIndex < outer.length; outerIndex += 1) {
101
+ if (segmentsCrossProperly(innerStart, innerEnd, outer[outerIndex], outer[(outerIndex + 1) % outer.length])) {
102
+ return false;
103
+ }
104
+ }
105
+ }
106
+ return inner.some((start, index) => {
107
+ const end = inner[(index + 1) % inner.length];
108
+ const midpoint = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2];
109
+ return pointInPolygon(start, outer, false) || pointInPolygon(midpoint, outer, false);
110
+ });
111
+ }
112
+ function polygonsHaveInteriorOverlap(a, b) {
113
+ if (polygonsDescribeSameRegion(a, b))
114
+ return true;
115
+ if (a.some((point, index) => {
116
+ const end = a[(index + 1) % a.length];
117
+ return (pointInPolygon(point, b, false) ||
118
+ pointInPolygon([(point[0] + end[0]) / 2, (point[1] + end[1]) / 2], b, false));
119
+ })) {
120
+ return true;
121
+ }
122
+ if (b.some((point, index) => {
123
+ const end = b[(index + 1) % b.length];
124
+ return (pointInPolygon(point, a, false) ||
125
+ pointInPolygon([(point[0] + end[0]) / 2, (point[1] + end[1]) / 2], a, false));
126
+ })) {
127
+ return true;
128
+ }
129
+ for (let aIndex = 0; aIndex < a.length; aIndex += 1) {
130
+ for (let bIndex = 0; bIndex < b.length; bIndex += 1) {
131
+ if (segmentsCrossProperly(a[aIndex], a[(aIndex + 1) % a.length], b[bIndex], b[(bIndex + 1) % b.length])) {
132
+ return true;
133
+ }
134
+ }
135
+ }
136
+ return false;
137
+ }
138
+ function polygonCoverageRatio(subject, covers) {
139
+ if (subject.length < 3 || covers.length === 0)
140
+ return 0;
141
+ const xs = subject.map((point) => point[0]);
142
+ const ys = subject.map((point) => point[1]);
143
+ const minX = Math.min(...xs);
144
+ const maxX = Math.max(...xs);
145
+ const minY = Math.min(...ys);
146
+ const maxY = Math.max(...ys);
147
+ if (maxX - minX <= 1e-9 || maxY - minY <= 1e-9)
148
+ return 0;
149
+ let inside = 0;
150
+ let covered = 0;
151
+ for (let xIndex = 0; xIndex < COVERAGE_SAMPLE_STEPS; xIndex += 1) {
152
+ for (let yIndex = 0; yIndex < COVERAGE_SAMPLE_STEPS; yIndex += 1) {
153
+ const point = [
154
+ minX + ((xIndex + 0.5) / COVERAGE_SAMPLE_STEPS) * (maxX - minX),
155
+ minY + ((yIndex + 0.5) / COVERAGE_SAMPLE_STEPS) * (maxY - minY),
156
+ ];
157
+ if (!pointInPolygon(point, subject))
158
+ continue;
159
+ inside += 1;
160
+ if (covers.some((cover) => pointInPolygon(point, cover)))
161
+ covered += 1;
162
+ }
163
+ }
164
+ return inside > 0 ? covered / inside : 0;
165
+ }
166
+ function pointToPolylineDistance(point, polyline) {
167
+ let best = Number.POSITIVE_INFINITY;
168
+ for (let index = 0; index < polyline.length - 1; index += 1) {
169
+ const start = polyline[index];
170
+ const end = polyline[index + 1];
171
+ if (!(start && end))
172
+ continue;
173
+ best = Math.min(best, pointToSegmentDistance(point, start, end));
174
+ }
175
+ return best;
176
+ }
177
+ function wallForBoundarySegment(start, end, wallPaths) {
178
+ const midpoint = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2];
179
+ let best = null;
180
+ for (const { wall, points } of wallPaths) {
181
+ const distances = [start, midpoint, end].map((point) => pointToPolylineDistance(point, points));
182
+ if (distances.some((distance) => distance > BOUNDARY_TOLERANCE))
183
+ continue;
184
+ const distance = distances.reduce((sum, value) => sum + value, 0);
185
+ if (!best || distance < best.distance)
186
+ best = { wall, distance };
187
+ }
188
+ return best?.wall ?? null;
189
+ }
190
+ function wallPathsFor(walls) {
191
+ return walls.map((wall) => ({
192
+ wall,
193
+ points: sampleWallCenterline(wall, 32).map((point) => [point.x, point.y]),
194
+ }));
195
+ }
196
+ function pointAlongSegment(start, end, t) {
197
+ return [start[0] + (end[0] - start[0]) * t, start[1] + (end[1] - start[1]) * t];
198
+ }
199
+ function segmentParameter(point, start, end) {
200
+ const dx = end[0] - start[0];
201
+ const dy = end[1] - start[1];
202
+ const lengthSquared = dx * dx + dy * dy;
203
+ return lengthSquared <= 1e-12
204
+ ? 0
205
+ : ((point[0] - start[0]) * dx + (point[1] - start[1]) * dy) / lengthSquared;
206
+ }
207
+ function segmentLengthInsideOrNearPolygon(start, end, polygon, tolerance) {
208
+ const dx = end[0] - start[0];
209
+ const dy = end[1] - start[1];
210
+ const length = Math.hypot(dx, dy);
211
+ if (length <= 1e-9)
212
+ return 0;
213
+ const breaks = [0, 1];
214
+ for (let index = 0; index < polygon.length; index += 1) {
215
+ const polygonStart = polygon[index];
216
+ const polygonEnd = polygon[(index + 1) % polygon.length];
217
+ const edgeX = polygonEnd[0] - polygonStart[0];
218
+ const edgeY = polygonEnd[1] - polygonStart[1];
219
+ const denominator = dx * edgeY - dy * edgeX;
220
+ if (Math.abs(denominator) > 1e-12) {
221
+ const t = ((polygonStart[0] - start[0]) * edgeY - (polygonStart[1] - start[1]) * edgeX) / denominator;
222
+ const edgeT = ((polygonStart[0] - start[0]) * dy - (polygonStart[1] - start[1]) * dx) / denominator;
223
+ if (t > 0 && t < 1 && edgeT >= -1e-9 && edgeT <= 1 + 1e-9)
224
+ breaks.push(t);
225
+ }
226
+ if (pointToSegmentDistance(polygonStart, start, end) <= tolerance) {
227
+ const projected = segmentParameter(polygonStart, start, end);
228
+ if (projected > 0 && projected < 1)
229
+ breaks.push(projected);
230
+ }
231
+ }
232
+ breaks.sort((a, b) => a - b);
233
+ const uniqueBreaks = breaks.filter((value, index) => index === 0 || value - breaks[index - 1] > 1e-7);
234
+ let insideLength = 0;
235
+ for (let index = 0; index < uniqueBreaks.length - 1; index += 1) {
236
+ const t0 = uniqueBreaks[index];
237
+ const t1 = uniqueBreaks[index + 1];
238
+ const midpoint = pointAlongSegment(start, end, (t0 + t1) / 2);
239
+ if (pointInPolygon(midpoint, polygon) ||
240
+ pointToPolygonBoundaryDistance(midpoint, polygon) <= tolerance) {
241
+ insideLength += length * (t1 - t0);
242
+ }
243
+ }
244
+ return insideLength;
245
+ }
246
+ function boundaryFaceKey(boundary) {
247
+ const pointKey = (point) => `${point[0].toFixed(6)},${point[1].toFixed(6)}`;
248
+ const forward = boundary.points.map(pointKey).join('|');
249
+ const reverse = [...boundary.points].reverse().map(pointKey).join('|');
250
+ return `${boundary.wallId}:${boundary.face}:${forward < reverse ? forward : reverse}`;
251
+ }
252
+ function spansFromSpaces(spaces, zone, wallsById) {
253
+ const spans = [];
254
+ const seen = new Set();
255
+ for (const space of spaces) {
256
+ for (const boundary of space.boundaryFaces) {
257
+ const key = boundaryFaceKey(boundary);
258
+ if (seen.has(key))
259
+ continue;
260
+ seen.add(key);
261
+ const wall = wallsById.get(boundary.wallId);
262
+ if (!wall)
263
+ return null;
264
+ const tolerance = (wall.thickness ?? DEFAULT_WALL_THICKNESS) / 2 + BOUNDARY_TOLERANCE;
265
+ let length = 0;
266
+ for (let index = 0; index < boundary.points.length - 1; index += 1) {
267
+ length += segmentLengthInsideOrNearPolygon(boundary.points[index], boundary.points[index + 1], zone.polygon, tolerance);
268
+ }
269
+ if (length > 1e-6)
270
+ spans.push({ wall, length });
271
+ }
272
+ }
273
+ return spans.length > 0 ? spans : null;
274
+ }
275
+ function spansFromZoneBoundary(zone, wallPaths) {
276
+ const spans = [];
277
+ for (let edgeIndex = 0; edgeIndex < zone.polygon.length; edgeIndex += 1) {
278
+ const start = zone.polygon[edgeIndex];
279
+ const end = zone.polygon[(edgeIndex + 1) % zone.polygon.length];
280
+ const edgeLength = pointDistance(start, end);
281
+ if (edgeLength <= 1e-6)
282
+ continue;
283
+ const breaks = [0, 1];
284
+ for (const path of wallPaths) {
285
+ for (const point of path.points) {
286
+ if (pointToSegmentDistance(point, start, end) > BOUNDARY_TOLERANCE)
287
+ continue;
288
+ const t = segmentParameter(point, start, end);
289
+ if (t > 0 && t < 1)
290
+ breaks.push(t);
291
+ }
292
+ }
293
+ breaks.sort((a, b) => a - b);
294
+ const uniqueBreaks = breaks.filter((value, index) => index === 0 || value - breaks[index - 1] > 1e-6);
295
+ for (let index = 0; index < uniqueBreaks.length - 1; index += 1) {
296
+ const t0 = uniqueBreaks[index];
297
+ const t1 = uniqueBreaks[index + 1];
298
+ if (t1 - t0 <= 1e-6)
299
+ continue;
300
+ const spanStart = pointAlongSegment(start, end, t0);
301
+ const spanEnd = pointAlongSegment(start, end, t1);
302
+ const wall = wallForBoundarySegment(spanStart, spanEnd, wallPaths);
303
+ if (!wall)
304
+ return null;
305
+ spans.push({ wall, length: edgeLength * (t1 - t0) });
306
+ }
307
+ }
308
+ return spans.length > 0 ? spans : null;
309
+ }
310
+ function proveSurfaceCoverage(zone, nodes, getDatum, labels) {
311
+ const candidates = nodes.filter((node) => polygonsHaveInteriorOverlap(zone.polygon, node.polygon) &&
312
+ polygonCoverageRatio(zone.polygon, [node.polygon]) > 0);
313
+ if (candidates.length === 0 ||
314
+ polygonCoverageRatio(zone.polygon, candidates.map((node) => node.polygon)) < SURFACE_COVERAGE_THRESHOLD) {
315
+ return { status: 'unavailable', reason: `No ${labels.singular} coverage proves this zone.` };
316
+ }
317
+ const datum = getDatum(candidates[0]);
318
+ if (!Number.isFinite(datum) ||
319
+ candidates.some((node) => Math.abs(getDatum(node) - datum) > SURFACE_DATUM_EPSILON)) {
320
+ return {
321
+ status: 'unavailable',
322
+ reason: `${labels.plural} covering this zone have different ${labels.datum}.`,
323
+ };
324
+ }
325
+ let area = polygonArea(zone.polygon);
326
+ const seenHoles = new Set();
327
+ for (const node of candidates) {
328
+ for (const hole of node.holes) {
329
+ const forward = hole.map((point) => `${point[0].toFixed(6)},${point[1].toFixed(6)}`).join('|');
330
+ const reverse = [...hole]
331
+ .reverse()
332
+ .map((point) => `${point[0].toFixed(6)},${point[1].toFixed(6)}`)
333
+ .join('|');
334
+ const key = forward < reverse ? forward : reverse;
335
+ if (seenHoles.has(key))
336
+ continue;
337
+ seenHoles.add(key);
338
+ if (polygonContainsRegion(hole, zone.polygon)) {
339
+ return { status: 'unavailable', reason: 'A surface opening removes this zone.' };
340
+ }
341
+ if (polygonContainsRegion(zone.polygon, hole)) {
342
+ area -= polygonArea(hole);
343
+ }
344
+ else if (polygonsHaveInteriorOverlap(zone.polygon, hole)) {
345
+ return { status: 'unavailable', reason: 'A surface opening crosses the zone boundary.' };
346
+ }
347
+ }
348
+ }
349
+ return { status: 'available', area: Math.max(0, area), datum };
350
+ }
351
+ function unavailable(reason) {
352
+ return { status: 'unavailable', reason };
353
+ }
354
+ export function deriveZoneQuantityReport(zone, sceneNodes) {
355
+ const levelId = zone.parentId;
356
+ const levelNodes = levelId
357
+ ? Object.values(sceneNodes).filter((node) => node.parentId === levelId)
358
+ : [];
359
+ const walls = levelNodes.filter((node) => node.type === 'wall');
360
+ const slabs = levelNodes.filter((node) => node.type === 'slab');
361
+ const wallEffectiveHeight = (wall) => {
362
+ const support = computeWallSlabSupport(wall, slabs, walls, wall.supportSlabId);
363
+ const planeTop = levelId ? getWallPlaneTop(wall, levelId, sceneNodes) : DEFAULT_LEVEL_HEIGHT;
364
+ return resolveWallEffectiveHeight(wall, planeTop, support.elevation);
365
+ };
366
+ const edgeLengths = zone.polygon.map((start, index) => {
367
+ const end = zone.polygon[(index + 1) % zone.polygon.length];
368
+ return end ? pointDistance(start, end) : 0;
369
+ });
370
+ const footprintArea = polygonArea(zone.polygon);
371
+ const perimeter = edgeLengths.reduce((sum, length) => sum + length, 0);
372
+ const slabCoverage = proveSurfaceCoverage(zone, levelNodes.filter((node) => node.type === 'slab'), (node) => node.elevation, { singular: 'slab', plural: 'Slabs', datum: 'elevations' });
373
+ const ceilingCoverage = proveSurfaceCoverage(zone, levelNodes.filter((node) => node.type === 'ceiling'), (node) => resolveCeilingHeight(node, sceneNodes), { singular: 'ceiling', plural: 'Ceilings', datum: 'heights' });
374
+ const spaces = levelId ? detectSpacesForLevel(levelId, walls).spaces : [];
375
+ const overlappingSpaces = spaces.filter((space) => polygonsHaveInteriorOverlap(zone.polygon, space.polygon));
376
+ const topologyEnclosesZone = overlappingSpaces.length > 0 &&
377
+ polygonCoverageRatio(zone.polygon, overlappingSpaces.map((space) => space.polygon)) >= SPACE_CONTAINMENT_THRESHOLD &&
378
+ overlappingSpaces.every((space) => polygonCoverageRatio(space.polygon, [zone.polygon]) >= SPACE_CONTAINMENT_THRESHOLD);
379
+ const wallsById = new Map(walls.map((wall) => [wall.id, wall]));
380
+ const wallPaths = wallPathsFor(walls);
381
+ const topologyWallSpans = spansFromSpaces(overlappingSpaces, zone, wallsById);
382
+ const boundaryWallSpans = spansFromZoneBoundary(zone, wallPaths);
383
+ const wallSpans = topologyWallSpans ?? boundaryWallSpans;
384
+ const allWallsProven = Boolean(wallSpans);
385
+ const boundaryWallIds = wallSpans ? [...new Set(wallSpans.map((span) => span.wall.id))] : [];
386
+ const wallSurface = allWallsProven
387
+ ? {
388
+ status: 'available',
389
+ value: wallSpans.reduce((sum, span) => sum + span.length * wallEffectiveHeight(span.wall), 0),
390
+ note: 'Gross indoor-facing wall surface within this zone, including both sides of interior partitions.',
391
+ }
392
+ : unavailable('No indoor-facing wall surface is proven within this zone.');
393
+ const floorSurface = slabCoverage.status === 'available'
394
+ ? {
395
+ status: 'available',
396
+ value: slabCoverage.area,
397
+ note: 'Zone floor surface proven by compatible slab coverage, after openings.',
398
+ }
399
+ : unavailable(slabCoverage.reason);
400
+ let volume;
401
+ if (slabCoverage.status === 'unavailable') {
402
+ volume = unavailable(slabCoverage.reason);
403
+ }
404
+ else if (ceilingCoverage.status === 'unavailable') {
405
+ volume = unavailable(ceilingCoverage.reason);
406
+ }
407
+ else {
408
+ const clearHeight = ceilingCoverage.datum - slabCoverage.datum;
409
+ volume =
410
+ Number.isFinite(clearHeight) && clearHeight > 0
411
+ ? {
412
+ status: 'available',
413
+ value: slabCoverage.area * clearHeight,
414
+ note: 'Proven zone floor area multiplied by clear ceiling height.',
415
+ }
416
+ : unavailable('The matching ceiling is not above the slab surface.');
417
+ }
418
+ return {
419
+ classification: topologyEnclosesZone || boundaryWallSpans ? 'enclosed-room' : 'footprint',
420
+ footprintArea,
421
+ perimeter,
422
+ edgeLengths,
423
+ boundaryWallIds,
424
+ wallSurface,
425
+ floorSurface,
426
+ volume,
427
+ };
428
+ }
@@ -1,20 +1,47 @@
1
1
  import { type MaterialPresetPayload } from './schema/material';
2
+ export type MaterialSource = 'pascal' | 'community' | 'mine' | 'workspace';
2
3
  export type MaterialCatalogItem = {
3
4
  id: string;
4
5
  label: string;
5
6
  category: MaterialCategory;
7
+ /** Origin of the entry. Absent = 'pascal' (all static catalog entries). */
8
+ source?: MaterialSource;
9
+ /**
10
+ * Where this finish is appropriate. Absent = universal (e.g. flat colors).
11
+ * The paint picker may filter by the slot being painted; v1 shows everything.
12
+ */
13
+ surfaces?: MaterialSurface[];
6
14
  description?: string;
7
15
  previewThumbnailUrl?: string;
8
16
  previewColor?: string;
9
17
  preset: MaterialPresetPayload;
10
18
  };
11
- export declare const MATERIAL_CATEGORIES: readonly ["wood", "flooring", "roof", "other"];
19
+ export declare const MATERIAL_CATEGORIES: readonly ["colors", "wood", "stone", "brick", "tile", "wallpaper", "concrete", "metal", "plastic", "fabric", "carpet", "leather", "roofing", "ground", "glass", "other"];
12
20
  export type MaterialCategory = (typeof MATERIAL_CATEGORIES)[number];
21
+ export declare const MATERIAL_SURFACES: readonly ["floor", "wall", "ceiling", "roof", "furniture", "outdoor"];
22
+ export type MaterialSurface = (typeof MATERIAL_SURFACES)[number];
13
23
  export declare const MATERIAL_CATALOG: MaterialCatalogItem[];
24
+ export declare function registerLibraryMaterials(items: MaterialCatalogItem[]): void;
25
+ export declare function unregisterLibraryMaterials(ids: string[]): void;
26
+ export declare function getDynamicLibraryMaterials(): MaterialCatalogItem[];
27
+ export declare function subscribeLibraryMaterials(listener: () => void): () => void;
28
+ export declare function getLibraryMaterialsVersion(): number;
14
29
  export declare function getMaterialsForCategory(category: MaterialCategory): MaterialCatalogItem[];
15
30
  export declare function getCatalogMaterialById(id?: string): MaterialCatalogItem | undefined;
16
31
  export declare const LIBRARY_MATERIAL_REF_PREFIX = "library:";
32
+ export declare const SCENE_MATERIAL_REF_PREFIX = "scene:";
17
33
  export declare function toLibraryMaterialRef(id: string): string;
34
+ export declare function toSceneMaterialRef(id: string): string;
18
35
  export declare function getLibraryMaterialIdFromRef(materialRef?: string | null): string | null;
36
+ export declare function getSceneMaterialIdFromRef(materialRef?: string | null): string | null;
37
+ export type MaterialRef = string;
38
+ export type ParsedMaterialRef = {
39
+ kind: 'library';
40
+ id: string;
41
+ } | {
42
+ kind: 'scene';
43
+ id: string;
44
+ };
45
+ export declare function parseMaterialRef(ref?: string | null): ParsedMaterialRef | null;
19
46
  export declare function getMaterialPresetByRef(materialRef?: string | null): MaterialPresetPayload | null;
20
47
  //# sourceMappingURL=material-library.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"material-library.d.ts","sourceRoot":"","sources":["../src/material-library.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,mBAAmB,CAAA;AAE1B,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,qBAAqB,CAAA;CAC9B,CAAA;AAuCD,eAAO,MAAM,mBAAmB,gDAAiD,CAAA;AACjF,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,eAAO,MAAM,gBAAgB,EAAE,mBAAmB,EAqzEjD,CAAA;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,mBAAmB,EAAE,CAEzF;AAED,wBAAgB,sBAAsB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAGnF;AAED,eAAO,MAAM,2BAA2B,aAAa,CAAA;AAErD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,UAE9C;AAED,wBAAgB,2BAA2B,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,iBAItE;AAED,wBAAgB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,qBAAqB,GAAG,IAAI,CAIhG"}
1
+ {"version":3,"file":"material-library.d.ts","sourceRoot":"","sources":["../src/material-library.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,mBAAmB,CAAA;AAE1B,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,WAAW,CAAA;AAE1E,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,2EAA2E;IAC3E,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAA;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,qBAAqB,CAAA;CAC9B,CAAA;AAuCD,eAAO,MAAM,mBAAmB,0KAiBtB,CAAA;AACV,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,eAAO,MAAM,iBAAiB,uEAOpB,CAAA;AACV,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhE,eAAO,MAAM,gBAAgB,EAAE,mBAAmB,EAi+HjD,CAAA;AAkBD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,mBAAmB,EAAE,GAAG,IAAI,CAM3E;AAED,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAM9D;AAED,wBAAgB,0BAA0B,IAAI,mBAAmB,EAAE,CAElE;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAK1E;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,mBAAmB,EAAE,CAMzF;AAED,wBAAgB,sBAAsB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAGnF;AAED,eAAO,MAAM,2BAA2B,aAAa,CAAA;AACrD,eAAO,MAAM,yBAAyB,WAAW,CAAA;AAEjD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,UAE9C;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,UAE5C;AAED,wBAAgB,2BAA2B,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,iBAItE;AAED,wBAAgB,yBAAyB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAIpF;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,CAAA;AAEhC,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/F,wBAAgB,gBAAgB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB,GAAG,IAAI,CAM9E;AAED,wBAAgB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,qBAAqB,GAAG,IAAI,CAIhG"}