@pascal-app/core 1.0.0 → 1.0.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 (320) hide show
  1. package/dist/capture/index.d.ts +2 -2
  2. package/dist/capture/index.js +2 -2
  3. package/dist/capture/schema.d.ts.map +1 -1
  4. package/dist/capture/schema.js +2 -0
  5. package/dist/capture/source.d.ts +1 -1
  6. package/dist/capture/source.js +1 -1
  7. package/dist/events/bus.d.ts +6 -3
  8. package/dist/events/bus.d.ts.map +1 -1
  9. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +4 -4
  10. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -1
  11. package/dist/hooks/spatial-grid/floor-placed-elevation.js +7 -12
  12. package/dist/hooks/spatial-grid/floor-placed-footprints.d.ts +4 -0
  13. package/dist/hooks/spatial-grid/floor-placed-footprints.d.ts.map +1 -0
  14. package/dist/hooks/spatial-grid/floor-placed-footprints.js +7 -0
  15. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +5 -8
  16. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  17. package/dist/hooks/spatial-grid/spatial-grid-manager.js +23 -106
  18. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +221 -76
  19. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  20. package/dist/hooks/spatial-grid/spatial-grid-sync.js +77 -13
  21. package/dist/hooks/spatial-grid/support-host-patch.d.ts +1 -1
  22. package/dist/hooks/spatial-grid/support-host-patch.js +6 -6
  23. package/dist/hooks/spatial-grid/use-spatial-query.d.ts +1 -1
  24. package/dist/hooks/spatial-grid/use-spatial-query.js +1 -1
  25. package/dist/index.d.ts +74 -72
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +71 -69
  28. package/dist/lib/door-operation.d.ts +1 -1
  29. package/dist/lib/item-polygon-overlap.d.ts +6 -0
  30. package/dist/lib/item-polygon-overlap.d.ts.map +1 -0
  31. package/dist/lib/item-polygon-overlap.js +67 -0
  32. package/dist/lib/level-name.d.ts +1 -1
  33. package/dist/lib/measurement-geometry.d.ts +4 -4
  34. package/dist/lib/plan-footprint.d.ts +41 -0
  35. package/dist/lib/plan-footprint.d.ts.map +1 -0
  36. package/dist/lib/plan-footprint.js +58 -0
  37. package/dist/lib/room-topology-index.d.ts +1 -1
  38. package/dist/lib/room-topology-index.js +1 -1
  39. package/dist/lib/selection-proxy.d.ts +1 -1
  40. package/dist/lib/slab-polygon.d.ts +2 -2
  41. package/dist/lib/slab-polygon.js +2 -2
  42. package/dist/lib/space-detection.d.ts +1 -1
  43. package/dist/lib/space-detection.d.ts.map +1 -1
  44. package/dist/lib/space-detection.js +16 -11
  45. package/dist/lib/terrain-brush.d.ts +1 -1
  46. package/dist/lib/terrain-brush.js +1 -1
  47. package/dist/lib/terrain-codec.d.ts +2 -2
  48. package/dist/lib/terrain-codec.js +1 -1
  49. package/dist/lib/terrain-raycast.d.ts +1 -1
  50. package/dist/lib/terrain-raycast.js +1 -1
  51. package/dist/lib/terrain-source.d.ts +3 -3
  52. package/dist/lib/terrain-source.js +3 -3
  53. package/dist/lib/terrain-support.d.ts +1 -1
  54. package/dist/lib/terrain-support.js +3 -3
  55. package/dist/lib/unit-containment.d.ts +19 -0
  56. package/dist/lib/unit-containment.d.ts.map +1 -0
  57. package/dist/lib/unit-containment.js +158 -0
  58. package/dist/lib/unit-report.d.ts +19 -0
  59. package/dist/lib/unit-report.d.ts.map +1 -0
  60. package/dist/lib/unit-report.js +36 -0
  61. package/dist/lib/wall-distance.d.ts +2 -2
  62. package/dist/lib/wall-distance.js +1 -1
  63. package/dist/lib/zone-quantities.d.ts +1 -1
  64. package/dist/lib/zone-quantities.js +7 -7
  65. package/dist/material-library.d.ts +1 -1
  66. package/dist/material-library.js +1 -1
  67. package/dist/procedural-items/fixtures.d.ts +261 -0
  68. package/dist/procedural-items/fixtures.d.ts.map +1 -0
  69. package/dist/procedural-items/fixtures.js +349 -0
  70. package/dist/procedural-items/index.d.ts +10 -0
  71. package/dist/procedural-items/index.d.ts.map +1 -0
  72. package/dist/procedural-items/index.js +9 -0
  73. package/dist/procedural-items/integration.d.ts +92 -0
  74. package/dist/procedural-items/integration.d.ts.map +1 -0
  75. package/dist/procedural-items/integration.js +19 -0
  76. package/dist/procedural-items/library-colors.d.ts +56 -0
  77. package/dist/procedural-items/library-colors.d.ts.map +1 -0
  78. package/dist/procedural-items/library-colors.js +84 -0
  79. package/dist/procedural-items/materials.d.ts +5 -0
  80. package/dist/procedural-items/materials.d.ts.map +1 -0
  81. package/dist/procedural-items/materials.js +47 -0
  82. package/dist/procedural-items/node.d.ts +280 -0
  83. package/dist/procedural-items/node.d.ts.map +1 -0
  84. package/dist/procedural-items/node.js +71 -0
  85. package/dist/procedural-items/placement.d.ts +7 -0
  86. package/dist/procedural-items/placement.d.ts.map +1 -0
  87. package/dist/procedural-items/placement.js +48 -0
  88. package/dist/procedural-items/query.d.ts +50 -0
  89. package/dist/procedural-items/query.d.ts.map +1 -0
  90. package/dist/procedural-items/query.js +298 -0
  91. package/dist/procedural-items/recipe.d.ts +144 -0
  92. package/dist/procedural-items/recipe.d.ts.map +1 -0
  93. package/dist/procedural-items/recipe.js +372 -0
  94. package/dist/procedural-items/spatial.d.ts +19 -0
  95. package/dist/procedural-items/spatial.d.ts.map +1 -0
  96. package/dist/procedural-items/spatial.js +39 -0
  97. package/dist/registry/__bench__/relations-resolver.bench.js +2 -2
  98. package/dist/registry/handles.d.ts +12 -4
  99. package/dist/registry/handles.d.ts.map +1 -1
  100. package/dist/registry/index.d.ts +6 -6
  101. package/dist/registry/index.js +4 -4
  102. package/dist/registry/registry.d.ts +1 -1
  103. package/dist/registry/relations-resolver.d.ts +2 -2
  104. package/dist/registry/relations-resolver.js +1 -1
  105. package/dist/registry/scene-api.d.ts +2 -2
  106. package/dist/registry/scene-api.js +2 -2
  107. package/dist/registry/subtree.d.ts +1 -1
  108. package/dist/registry/subtree.js +2 -2
  109. package/dist/registry/types.d.ts +21 -9
  110. package/dist/registry/types.d.ts.map +1 -1
  111. package/dist/registry/use-registry-version.js +1 -1
  112. package/dist/registry/validate-relations.d.ts +4 -0
  113. package/dist/registry/validate-relations.d.ts.map +1 -0
  114. package/dist/registry/validate-relations.js +22 -0
  115. package/dist/schema/__bench__/node-parsers.bench.js +3 -3
  116. package/dist/schema/__fixtures__/node-fixtures.d.ts +1 -1
  117. package/dist/schema/__fixtures__/node-fixtures.d.ts.map +1 -1
  118. package/dist/schema/__fixtures__/node-fixtures.js +4 -2
  119. package/dist/schema/base.js +1 -1
  120. package/dist/schema/collections.d.ts +1 -1
  121. package/dist/schema/collections.js +1 -1
  122. package/dist/schema/compiled-node-parsers.d.ts +1 -1
  123. package/dist/schema/compiled-node-parsers.js +1 -1
  124. package/dist/schema/index.d.ts +73 -70
  125. package/dist/schema/index.d.ts.map +1 -1
  126. package/dist/schema/index.js +64 -61
  127. package/dist/schema/material.d.ts +1 -1
  128. package/dist/schema/material.js +1 -1
  129. package/dist/schema/nodes/automatic-downspout.d.ts +4 -4
  130. package/dist/schema/nodes/automatic-downspout.js +5 -5
  131. package/dist/schema/nodes/block.d.ts +1 -1
  132. package/dist/schema/nodes/block.d.ts.map +1 -1
  133. package/dist/schema/nodes/block.js +3 -4
  134. package/dist/schema/nodes/box-vent.js +2 -2
  135. package/dist/schema/nodes/building.d.ts +1 -1
  136. package/dist/schema/nodes/building.d.ts.map +1 -1
  137. package/dist/schema/nodes/building.js +8 -5
  138. package/dist/schema/nodes/cabinet.d.ts +2 -2
  139. package/dist/schema/nodes/cabinet.js +4 -4
  140. package/dist/schema/nodes/ceiling.d.ts +1 -1
  141. package/dist/schema/nodes/ceiling.js +5 -5
  142. package/dist/schema/nodes/chimney.js +2 -2
  143. package/dist/schema/nodes/column.d.ts +1 -0
  144. package/dist/schema/nodes/column.d.ts.map +1 -1
  145. package/dist/schema/nodes/column.js +3 -2
  146. package/dist/schema/nodes/construction-dimension.d.ts +3 -3
  147. package/dist/schema/nodes/construction-dimension.js +2 -2
  148. package/dist/schema/nodes/cupola.d.ts +1 -1
  149. package/dist/schema/nodes/cupola.js +2 -2
  150. package/dist/schema/nodes/door.js +2 -2
  151. package/dist/schema/nodes/dormer.d.ts +3 -3
  152. package/dist/schema/nodes/dormer.js +4 -4
  153. package/dist/schema/nodes/downspout.js +2 -2
  154. package/dist/schema/nodes/duct-fitting.js +1 -1
  155. package/dist/schema/nodes/duct-segment.js +2 -2
  156. package/dist/schema/nodes/duct-terminal.js +1 -1
  157. package/dist/schema/nodes/elevator.js +2 -2
  158. package/dist/schema/nodes/eyebrow-vent.js +2 -2
  159. package/dist/schema/nodes/fence.js +2 -2
  160. package/dist/schema/nodes/guide.js +2 -2
  161. package/dist/schema/nodes/gutter.d.ts +1 -1
  162. package/dist/schema/nodes/gutter.js +4 -4
  163. package/dist/schema/nodes/hvac-equipment.js +1 -1
  164. package/dist/schema/nodes/imported-mesh.d.ts +40 -0
  165. package/dist/schema/nodes/imported-mesh.d.ts.map +1 -0
  166. package/dist/schema/nodes/imported-mesh.js +27 -0
  167. package/dist/schema/nodes/item.d.ts +3 -3
  168. package/dist/schema/nodes/item.js +3 -3
  169. package/dist/schema/nodes/lean-to-extension.js +4 -4
  170. package/dist/schema/nodes/level.d.ts +1 -1
  171. package/dist/schema/nodes/level.d.ts.map +1 -1
  172. package/dist/schema/nodes/level.js +1 -1
  173. package/dist/schema/nodes/lineset.js +1 -1
  174. package/dist/schema/nodes/liquid-line.js +1 -1
  175. package/dist/schema/nodes/measurement.js +2 -2
  176. package/dist/schema/nodes/pipe-fitting.d.ts +1 -1
  177. package/dist/schema/nodes/pipe-fitting.js +1 -1
  178. package/dist/schema/nodes/pipe-segment.d.ts +1 -1
  179. package/dist/schema/nodes/pipe-segment.js +2 -2
  180. package/dist/schema/nodes/pipe-trap.d.ts +1 -1
  181. package/dist/schema/nodes/pipe-trap.js +1 -1
  182. package/dist/schema/nodes/ridge-vent.d.ts +1 -1
  183. package/dist/schema/nodes/ridge-vent.js +3 -3
  184. package/dist/schema/nodes/roof-segment-shape.d.ts +1 -1
  185. package/dist/schema/nodes/roof-segment-shape.js +1 -1
  186. package/dist/schema/nodes/roof-segment-walls.d.ts +1 -1
  187. package/dist/schema/nodes/roof-segment-walls.js +1 -1
  188. package/dist/schema/nodes/roof-segment.d.ts +2 -2
  189. package/dist/schema/nodes/roof-segment.js +2 -2
  190. package/dist/schema/nodes/roof.d.ts +1 -1
  191. package/dist/schema/nodes/roof.js +3 -3
  192. package/dist/schema/nodes/scan.js +3 -3
  193. package/dist/schema/nodes/shelf.d.ts +1 -1
  194. package/dist/schema/nodes/shelf.d.ts.map +1 -1
  195. package/dist/schema/nodes/shelf.js +3 -4
  196. package/dist/schema/nodes/site.js +2 -2
  197. package/dist/schema/nodes/skylight.d.ts +4 -4
  198. package/dist/schema/nodes/skylight.js +2 -2
  199. package/dist/schema/nodes/slab.js +3 -3
  200. package/dist/schema/nodes/solar-panel.js +3 -3
  201. package/dist/schema/nodes/spawn.js +1 -1
  202. package/dist/schema/nodes/stair-segment.js +2 -2
  203. package/dist/schema/nodes/stair.d.ts +1 -1
  204. package/dist/schema/nodes/stair.js +3 -3
  205. package/dist/schema/nodes/structural-grid.js +1 -1
  206. package/dist/schema/nodes/turbine-vent.d.ts +1 -1
  207. package/dist/schema/nodes/turbine-vent.js +2 -2
  208. package/dist/schema/nodes/unit.d.ts +33 -0
  209. package/dist/schema/nodes/unit.d.ts.map +1 -0
  210. package/dist/schema/nodes/unit.js +13 -0
  211. package/dist/schema/nodes/wall.d.ts +2 -2
  212. package/dist/schema/nodes/wall.d.ts.map +1 -1
  213. package/dist/schema/nodes/wall.js +7 -6
  214. package/dist/schema/nodes/window.js +2 -2
  215. package/dist/schema/nodes/zone.js +1 -1
  216. package/dist/schema/scene-material.js +2 -2
  217. package/dist/schema/types.d.ts +341 -19
  218. package/dist/schema/types.d.ts.map +1 -1
  219. package/dist/schema/types.js +56 -49
  220. package/dist/services/alignment-anchors.d.ts +6 -11
  221. package/dist/services/alignment-anchors.d.ts.map +1 -1
  222. package/dist/services/alignment-anchors.js +7 -34
  223. package/dist/services/drag-session.d.ts +4 -4
  224. package/dist/services/drag-session.js +1 -1
  225. package/dist/services/hosting.d.ts +2 -8
  226. package/dist/services/hosting.d.ts.map +1 -1
  227. package/dist/services/hosting.js +2 -12
  228. package/dist/services/index.d.ts +16 -13
  229. package/dist/services/index.d.ts.map +1 -1
  230. package/dist/services/index.js +16 -13
  231. package/dist/services/level-height.d.ts +2 -2
  232. package/dist/services/level-height.js +3 -3
  233. package/dist/services/movement.d.ts +3 -3
  234. package/dist/services/movement.js +2 -2
  235. package/dist/services/port-connectivity.d.ts +1 -1
  236. package/dist/services/port-connectivity.js +1 -1
  237. package/dist/services/riser-diagram.d.ts +1 -1
  238. package/dist/services/shelf-board.d.ts +5 -0
  239. package/dist/services/shelf-board.d.ts.map +1 -0
  240. package/dist/services/shelf-board.js +15 -0
  241. package/dist/services/storey.d.ts +2 -2
  242. package/dist/services/storey.js +2 -2
  243. package/dist/services/surface-hosting-procedural-parity.fixtures.d.ts +13 -0
  244. package/dist/services/surface-hosting-procedural-parity.fixtures.d.ts.map +1 -0
  245. package/dist/services/surface-hosting-procedural-parity.fixtures.js +4691 -0
  246. package/dist/services/surface-hosting-shelf-parity.fixtures.d.ts +1060 -0
  247. package/dist/services/surface-hosting-shelf-parity.fixtures.d.ts.map +1 -0
  248. package/dist/services/surface-hosting-shelf-parity.fixtures.js +3127 -0
  249. package/dist/services/surface-hosting.d.ts +88 -0
  250. package/dist/services/surface-hosting.d.ts.map +1 -0
  251. package/dist/services/surface-hosting.js +268 -0
  252. package/dist/services/surface-region.d.ts +8 -0
  253. package/dist/services/surface-region.d.ts.map +1 -0
  254. package/dist/services/surface-region.js +44 -0
  255. package/dist/services/system-graph.d.ts +2 -2
  256. package/dist/services/system-graph.js +2 -2
  257. package/dist/services/validate-dwv.d.ts +1 -1
  258. package/dist/services/validate-dwv.js +1 -1
  259. package/dist/store/actions/node-actions.d.ts +2 -2
  260. package/dist/store/actions/node-actions.d.ts.map +1 -1
  261. package/dist/store/actions/node-actions.js +48 -9
  262. package/dist/store/history-control.d.ts +3 -3
  263. package/dist/store/history-invalidation.d.ts +1 -1
  264. package/dist/store/history-invalidation.js +1 -1
  265. package/dist/store/use-interactive.d.ts +2 -2
  266. package/dist/store/use-live-terrain.d.ts +1 -1
  267. package/dist/store/use-scene.d.ts +4 -4
  268. package/dist/store/use-scene.d.ts.map +1 -1
  269. package/dist/store/use-scene.js +37 -31
  270. package/dist/systems/elevator/elevator-dispatch.d.ts +2 -2
  271. package/dist/systems/elevator/elevator-dispatch.js +1 -1
  272. package/dist/systems/elevator/elevator-geometry.d.ts +1 -1
  273. package/dist/systems/elevator/elevator-opening-sync.d.ts +1 -1
  274. package/dist/systems/elevator/elevator-opening-sync.js +2 -2
  275. package/dist/systems/elevator/elevator-opening-system.js +3 -3
  276. package/dist/systems/elevator/elevator-runtime.d.ts +3 -3
  277. package/dist/systems/elevator/elevator-runtime.js +3 -3
  278. package/dist/systems/elevator/elevator-service.d.ts +1 -1
  279. package/dist/systems/elevator/elevator-service.js +1 -1
  280. package/dist/systems/fence/fence-centerline.d.ts +2 -2
  281. package/dist/systems/fence/fence-centerline.js +2 -2
  282. package/dist/systems/fence/fence-spline.d.ts +2 -2
  283. package/dist/systems/roof/roof-elevation-system.js +3 -3
  284. package/dist/systems/roof/roof-elevation.d.ts +2 -2
  285. package/dist/systems/roof/roof-elevation.js +6 -6
  286. package/dist/systems/roof/roof-footprint.d.ts +1 -1
  287. package/dist/systems/roof/roof-footprint.js +3 -3
  288. package/dist/systems/slab/slab-placement.d.ts +1 -1
  289. package/dist/systems/slab/slab-support.d.ts +1 -1
  290. package/dist/systems/slab/slab-support.js +4 -4
  291. package/dist/systems/stair/stair-flight.d.ts +2 -2
  292. package/dist/systems/stair/stair-flight.js +2 -2
  293. package/dist/systems/stair/stair-footprint.d.ts +1 -1
  294. package/dist/systems/stair/stair-opening-preview.d.ts +206 -79
  295. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
  296. package/dist/systems/stair/stair-opening-preview.js +2 -2
  297. package/dist/systems/stair/stair-opening-sync.d.ts +1 -1
  298. package/dist/systems/stair/stair-opening-sync.js +6 -6
  299. package/dist/systems/stair/stair-opening-system.js +8 -8
  300. package/dist/systems/stair/stair-rise.d.ts +1 -1
  301. package/dist/systems/stair/stair-rise.js +3 -3
  302. package/dist/systems/wall/wall-curve.d.ts +2 -2
  303. package/dist/systems/wall/wall-footprint.d.ts +3 -3
  304. package/dist/systems/wall/wall-footprint.js +3 -3
  305. package/dist/systems/wall/wall-mitering.d.ts +1 -1
  306. package/dist/systems/wall/wall-mitering.js +1 -1
  307. package/dist/systems/wall/wall-move.d.ts +1 -1
  308. package/dist/systems/wall/wall-top.d.ts +1 -1
  309. package/dist/systems/wall/wall-topology.d.ts +2 -2
  310. package/dist/systems/wall/wall-topology.js +4 -4
  311. package/dist/utils/clone-scene-graph.d.ts +3 -3
  312. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  313. package/dist/utils/clone-scene-graph.js +9 -3
  314. package/dist/utils/scene-migrations.d.ts +3 -3
  315. package/dist/utils/scene-migrations.js +3 -3
  316. package/dist/utils/vertical-scene-migration.js +5 -5
  317. package/dist/validation/validate-build-json.d.ts +5 -2
  318. package/dist/validation/validate-build-json.d.ts.map +1 -1
  319. package/dist/validation/validate-build-json.js +45 -4
  320. package/package.json +14 -3
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Test if an item's footprint overlaps with a polygon.
3
+ * Checks: any item corner inside polygon, or any polygon vertex inside item AABB, or edges intersect.
4
+ */
5
+ export declare function itemOverlapsPolygon(position: [number, number, number], dimensions: [number, number, number], rotation: [number, number, number], polygon: Array<[number, number]>, inset?: number): boolean;
6
+ //# sourceMappingURL=item-polygon-overlap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-polygon-overlap.d.ts","sourceRoot":"","sources":["../../src/lib/item-polygon-overlap.ts"],"names":[],"mappings":"AA0EA;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAClC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACpC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAChC,KAAK,SAAI,GACR,OAAO,CA8BT"}
@@ -0,0 +1,67 @@
1
+ import { pointInPolygon } from '../systems/slab/slab-support.js';
2
+ import { planFootprintCorners } from './plan-footprint.js';
3
+ /**
4
+ * Test if two line segments (a1->a2) and (b1->b2) intersect.
5
+ */
6
+ function segmentsIntersect(ax1, az1, ax2, az2, bx1, bz1, bx2, bz2) {
7
+ const cross = (ox, oz, ax, az, bx, bz) => (ax - ox) * (bz - oz) - (az - oz) * (bx - ox);
8
+ const d1 = cross(bx1, bz1, bx2, bz2, ax1, az1);
9
+ const d2 = cross(bx1, bz1, bx2, bz2, ax2, az2);
10
+ const d3 = cross(ax1, az1, ax2, az2, bx1, bz1);
11
+ const d4 = cross(ax1, az1, ax2, az2, bx2, bz2);
12
+ if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) {
13
+ return true;
14
+ }
15
+ // Collinear touching cases
16
+ const onSeg = (px, pz, qx, qz, rx, rz) => Math.min(px, qx) <= rx &&
17
+ rx <= Math.max(px, qx) &&
18
+ Math.min(pz, qz) <= rz &&
19
+ rz <= Math.max(pz, qz);
20
+ if (d1 === 0 && onSeg(bx1, bz1, bx2, bz2, ax1, az1))
21
+ return true;
22
+ if (d2 === 0 && onSeg(bx1, bz1, bx2, bz2, ax2, az2))
23
+ return true;
24
+ if (d3 === 0 && onSeg(ax1, az1, ax2, az2, bx1, bz1))
25
+ return true;
26
+ if (d4 === 0 && onSeg(ax1, az1, ax2, az2, bx2, bz2))
27
+ return true;
28
+ return false;
29
+ }
30
+ /**
31
+ * Test if a line segment intersects any edge of a polygon.
32
+ */
33
+ function segmentIntersectsPolygon(sx1, sz1, sx2, sz2, polygon) {
34
+ const n = polygon.length;
35
+ for (let i = 0; i < n; i++) {
36
+ const j = (i + 1) % n;
37
+ if (segmentsIntersect(sx1, sz1, sx2, sz2, polygon[i][0], polygon[i][1], polygon[j][0], polygon[j][1])) {
38
+ return true;
39
+ }
40
+ }
41
+ return false;
42
+ }
43
+ /**
44
+ * Test if an item's footprint overlaps with a polygon.
45
+ * Checks: any item corner inside polygon, or any polygon vertex inside item AABB, or edges intersect.
46
+ */
47
+ export function itemOverlapsPolygon(position, dimensions, rotation, polygon, inset = 0) {
48
+ const corners = planFootprintCorners(position, dimensions, rotation[1], inset);
49
+ // Check if any item corner is inside the polygon
50
+ for (const [cx, cz] of corners) {
51
+ if (pointInPolygon(cx, cz, polygon))
52
+ return true;
53
+ }
54
+ // Check if any polygon vertex is inside the item footprint
55
+ // (handles case where slab is fully inside a large item)
56
+ for (const [px, pz] of polygon) {
57
+ if (pointInPolygon(px, pz, corners))
58
+ return true;
59
+ }
60
+ // Check if any item edge intersects any polygon edge
61
+ for (let i = 0; i < 4; i++) {
62
+ const j = (i + 1) % 4;
63
+ if (segmentIntersectsPolygon(corners[i][0], corners[i][1], corners[j][0], corners[j][1], polygon))
64
+ return true;
65
+ }
66
+ return false;
67
+ }
@@ -1,4 +1,4 @@
1
- import type { LevelNode } from '../schema';
1
+ import type { LevelNode } from '../schema/index.js';
2
2
  export declare function getDefaultLevelName(level: number): string;
3
3
  export declare function getLevelDisplayName(level: Pick<LevelNode, 'name' | 'level'>): string;
4
4
  //# sourceMappingURL=level-name.d.ts.map
@@ -1,7 +1,7 @@
1
- import type { MeasurementFeature, MeasurementFeatureBinding } from '../registry/types';
2
- import type { ConstructionDimensionNode } from '../schema/nodes/construction-dimension';
3
- import type { MeasurementAnchor, MeasurementPayload, MeasurementPoint } from '../schema/nodes/measurement';
4
- import type { AnyNodeId } from '../schema/types';
1
+ import type { MeasurementFeature, MeasurementFeatureBinding } from '../registry/types.js';
2
+ import type { ConstructionDimensionNode } from '../schema/nodes/construction-dimension.js';
3
+ import type { MeasurementAnchor, MeasurementPayload, MeasurementPoint } from '../schema/nodes/measurement.js';
4
+ import type { AnyNodeId } from '../schema/types.js';
5
5
  export declare const MEASUREMENT_PLANAR_TOLERANCE = 0.01;
6
6
  export declare function measurementDistance(start: MeasurementPoint, end: MeasurementPoint): number;
7
7
  export declare function measurementAnchorFallback(anchor: MeasurementAnchor): MeasurementPoint;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Pure plan (XZ) footprint math — one source for spatial-grid collision and
3
+ * alignment anchors. The `@pascal-app/core/plan-footprint` subpath is the seam
4
+ * for a follow-up that consolidates MCP layout clearance onto these helpers.
5
+ *
6
+ * ## Invariant
7
+ * Callers share `planFootprintCorners` / `planFootprintAABB`. Do not invent a
8
+ * third rotation-aware plan AABB path beside this module.
9
+ *
10
+ * ## Gap call-site meanings (for the follow-up overlap helper)
11
+ * When an expand-then-intersect overlap check lands here, treat `gap` as
12
+ * **minimum free space** (expand each box by `gap`, then intersect):
13
+ * - Packing / furnish: typically `gap ≈ 0.08` for breathing room between items
14
+ * - check / verify collision: use `gap = 0` for true interpenetration only
15
+ * Do not share one default blindly across both questions.
16
+ *
17
+ * ## Scope
18
+ * Foundation only. Does not move door keep-outs, level ancestry, or furnish
19
+ * search into core. Item-scaled / attach-aware wrappers stay with callers
20
+ * until the MCP consolidation follow-up.
21
+ */
22
+ export type PlanAabb = {
23
+ minX: number;
24
+ maxX: number;
25
+ minZ: number;
26
+ maxZ: number;
27
+ };
28
+ export type PlanVec2 = [number, number];
29
+ /**
30
+ * Four XZ corners of a centred footprint at `position`, rotated by Y
31
+ * rotation. Matches spatial-grid `getItemFootprint` convention:
32
+ * local +X maps with (cos, sin), local +Z with (-sin, cos) terms as used
33
+ * in the existing corner formula.
34
+ */
35
+ export declare function planFootprintCorners(position: readonly [number, number, number], dimensions: readonly [number, number, number], rotationY: number, inset?: number): PlanVec2[];
36
+ /**
37
+ * Axis-aligned XZ extent of a footprint. Equivalent to the AABB of
38
+ * `planFootprintCorners` (no inset) and to spatial-grid `footprintBoundsXZ`.
39
+ */
40
+ export declare function planFootprintAABB(position: readonly [number, number, number], dimensions: readonly [number, number, number], rotationY: number): PlanAabb;
41
+ //# sourceMappingURL=plan-footprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-footprint.d.ts","sourceRoot":"","sources":["../../src/lib/plan-footprint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEvC;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC3C,UAAU,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC7C,SAAS,EAAE,MAAM,EACjB,KAAK,SAAI,GACR,QAAQ,EAAE,CAcZ;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC3C,UAAU,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC7C,SAAS,EAAE,MAAM,GAChB,QAAQ,CAYV"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Pure plan (XZ) footprint math — one source for spatial-grid collision and
3
+ * alignment anchors. The `@pascal-app/core/plan-footprint` subpath is the seam
4
+ * for a follow-up that consolidates MCP layout clearance onto these helpers.
5
+ *
6
+ * ## Invariant
7
+ * Callers share `planFootprintCorners` / `planFootprintAABB`. Do not invent a
8
+ * third rotation-aware plan AABB path beside this module.
9
+ *
10
+ * ## Gap call-site meanings (for the follow-up overlap helper)
11
+ * When an expand-then-intersect overlap check lands here, treat `gap` as
12
+ * **minimum free space** (expand each box by `gap`, then intersect):
13
+ * - Packing / furnish: typically `gap ≈ 0.08` for breathing room between items
14
+ * - check / verify collision: use `gap = 0` for true interpenetration only
15
+ * Do not share one default blindly across both questions.
16
+ *
17
+ * ## Scope
18
+ * Foundation only. Does not move door keep-outs, level ancestry, or furnish
19
+ * search into core. Item-scaled / attach-aware wrappers stay with callers
20
+ * until the MCP consolidation follow-up.
21
+ */
22
+ /**
23
+ * Four XZ corners of a centred footprint at `position`, rotated by Y
24
+ * rotation. Matches spatial-grid `getItemFootprint` convention:
25
+ * local +X maps with (cos, sin), local +Z with (-sin, cos) terms as used
26
+ * in the existing corner formula.
27
+ */
28
+ export function planFootprintCorners(position, dimensions, rotationY, inset = 0) {
29
+ const [x, , z] = position;
30
+ const [w, , d] = dimensions;
31
+ const halfW = Math.max(0, w / 2 - inset);
32
+ const halfD = Math.max(0, d / 2 - inset);
33
+ const cos = Math.cos(rotationY);
34
+ const sin = Math.sin(rotationY);
35
+ return [
36
+ [x + (-halfW * cos + halfD * sin), z + (-halfW * sin - halfD * cos)],
37
+ [x + (halfW * cos + halfD * sin), z + (halfW * sin - halfD * cos)],
38
+ [x + (halfW * cos - halfD * sin), z + (halfW * sin + halfD * cos)],
39
+ [x + (-halfW * cos - halfD * sin), z + (-halfW * sin + halfD * cos)],
40
+ ];
41
+ }
42
+ /**
43
+ * Axis-aligned XZ extent of a footprint. Equivalent to the AABB of
44
+ * `planFootprintCorners` (no inset) and to spatial-grid `footprintBoundsXZ`.
45
+ */
46
+ export function planFootprintAABB(position, dimensions, rotationY) {
47
+ const [width, , depth] = dimensions;
48
+ const cos = Math.abs(Math.cos(rotationY));
49
+ const sin = Math.abs(Math.sin(rotationY));
50
+ const rotatedW = width * cos + depth * sin;
51
+ const rotatedD = width * sin + depth * cos;
52
+ return {
53
+ minX: position[0] - rotatedW / 2,
54
+ maxX: position[0] + rotatedW / 2,
55
+ minZ: position[2] - rotatedD / 2,
56
+ maxZ: position[2] + rotatedD / 2,
57
+ };
58
+ }
@@ -1,4 +1,4 @@
1
- import type { WallNode } from '../schema';
1
+ import type { WallNode } from '../schema/index.js';
2
2
  type SceneNodes = Record<string, any>;
3
3
  type Point = [number, number];
4
4
  type IndexedRoom = {
@@ -1,4 +1,4 @@
1
- import { getClampedWallCurveOffset } from '../systems/wall/wall-curve';
1
+ import { getClampedWallCurveOffset } from '../systems/wall/wall-curve.js';
2
2
  const CELL_SIZE = 2;
3
3
  function bboxOf(points) {
4
4
  let minX = Number.POSITIVE_INFINITY;
@@ -1,4 +1,4 @@
1
- import type { AnyNode, AnyNodeId } from '../schema/types';
1
+ import type { AnyNode, AnyNodeId } from '../schema/types.js';
2
2
  export declare function selectionProxyIdFromMetadata(metadata: unknown): AnyNodeId | null;
3
3
  export declare function resolveSelectionProxyId(node: AnyNode, nodes: Readonly<Record<string, AnyNode | undefined>>): AnyNodeId;
4
4
  //# sourceMappingURL=selection-proxy.d.ts.map
@@ -1,5 +1,5 @@
1
- import type { GeometryContext } from '../registry/types';
2
- import type { AnyNode, AnyNodeId, SlabNode, WallNode } from '../schema';
1
+ import type { GeometryContext } from '../registry/types.js';
2
+ import type { AnyNode, AnyNodeId, SlabNode, WallNode } from '../schema/index.js';
3
3
  export type SlabPolygonContext = {
4
4
  /** Walls on the slab's level. */
5
5
  walls: WallNode[];
@@ -1,5 +1,5 @@
1
- import { isCurvedWall, sampleWallCenterline } from '../systems/wall/wall-curve';
2
- import { getWallThickness } from '../systems/wall/wall-footprint';
1
+ import { isCurvedWall, sampleWallCenterline } from '../systems/wall/wall-curve.js';
2
+ import { getWallThickness } from '../systems/wall/wall-footprint.js';
3
3
  /**
4
4
  * Render-time slab polygon rules.
5
5
  *
@@ -1,4 +1,4 @@
1
- import { type AnyNodeId, type CeilingNode as CeilingNodeType, type SlabNode as SlabNodeType, type WallNode, type ZoneNode as ZoneNodeType } from '../schema';
1
+ import { type AnyNodeId, type CeilingNode as CeilingNodeType, type SlabNode as SlabNodeType, type WallNode, type ZoneNode as ZoneNodeType } from '../schema/index.js';
2
2
  type Point2D = {
3
3
  x: number;
4
4
  y: number;
@@ -1 +1 @@
1
- {"version":3,"file":"space-detection.d.ts","sourceRoot":"","sources":["../../src/lib/space-detection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"space-detection.d.ts","sourceRoot":"","sources":["../../src/lib/space-detection.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,14 @@
1
- import { GROUND_SUPPORT_ID } from '../hooks/spatial-grid/support-host-id';
2
- import { CeilingNode, SlabNode, ZoneNode, } from '../schema';
3
- import { DEFAULT_LEVEL_HEIGHT } from '../services/level-height';
4
- import { CEILING_CLAMP_MARGIN, findLevelAboveId, getCeilingClampBound, getLevelBelow, getLevelElevations, getStoredLevelHeight, } from '../services/storey';
5
- import { activeSceneCommitNodeIds, getSceneHistoryPauseDepth, pauseSceneHistory, resumeSceneHistory, subscribeSceneCommits, } from '../store/history-control';
6
- import { computeWallSlabSupport } from '../systems/slab/slab-support';
7
- import { getClampedWallCurveOffset, getWallCurveFrameAt, isCurvedWall, } from '../systems/wall/wall-curve';
8
- import { resolveWallTop } from '../systems/wall/wall-top';
9
- import { simplifyClosedPolygon } from './polygon-geometry';
10
- import { distanceToSegment, RoomTopologyIndex, } from './room-topology-index';
11
- import { levelBaseElevationAt } from './terrain-support';
1
+ import { GROUND_SUPPORT_ID } from '../hooks/spatial-grid/support-host-id.js';
2
+ import { CeilingNode, SlabNode, ZoneNode, } from '../schema/index.js';
3
+ import { DEFAULT_LEVEL_HEIGHT } from '../services/level-height.js';
4
+ import { CEILING_CLAMP_MARGIN, findLevelAboveId, getCeilingClampBound, getLevelBelow, getLevelElevations, getStoredLevelHeight, } from '../services/storey.js';
5
+ import { activeSceneCommitNodeIds, getSceneHistoryPauseDepth, pauseSceneHistory, resumeSceneHistory, subscribeSceneCommits, } from '../store/history-control.js';
6
+ import { computeWallSlabSupport } from '../systems/slab/slab-support.js';
7
+ import { getClampedWallCurveOffset, getWallCurveFrameAt, isCurvedWall, } from '../systems/wall/wall-curve.js';
8
+ import { resolveWallTop } from '../systems/wall/wall-top.js';
9
+ import { simplifyClosedPolygon } from './polygon-geometry.js';
10
+ import { distanceToSegment, RoomTopologyIndex, } from './room-topology-index.js';
11
+ import { levelBaseElevationAt } from './terrain-support.js';
12
12
  const DEFAULT_AUTO_SLAB_ELEVATION = 0.05;
13
13
  const CEILING_HEIGHT_EPSILON = 1e-6;
14
14
  const ROOM_CURVE_TOLERANCE = 0.04;
@@ -1858,6 +1858,11 @@ export function initSpaceDetectionSync(sceneStore, editorStore, options = {}) {
1858
1858
  return;
1859
1859
  adoptSceneBaseline(commit.current.nodes);
1860
1860
  });
1861
+ // Keep reconciliation in this synchronous store subscription. Zundo emits
1862
+ // the originating local SceneCommit only after subscribers return, so the
1863
+ // history-paused derived writes below join that commit's current snapshot
1864
+ // and undo step. Running from subscribeSceneCommits would cross the snapshot
1865
+ // boundary, and the paused writes would emit no replacement commit.
1861
1866
  const unsubscribe = sceneStore.subscribe((state) => {
1862
1867
  if (isProcessing)
1863
1868
  return;
@@ -30,7 +30,7 @@
30
30
  * one in-flight gesture and dies with it, which is cheaper and clearer than
31
31
  * rebuilding a coverage map per pointer-move.
32
32
  */
33
- import { type HeightPatch, type TerrainField } from './terrain-field';
33
+ import { type HeightPatch, type TerrainField } from './terrain-field.js';
34
34
  export type BrushShape = 'round' | 'square';
35
35
  /**
36
36
  * `raise`/`lower` are one verb with an inverted sign at the call site, matching
@@ -30,7 +30,7 @@
30
30
  * one in-flight gesture and dies with it, which is cheaper and clearer than
31
31
  * rebuilding a coverage map per pointer-move.
32
32
  */
33
- import { heightAt, heightAtSample, quantize, } from './terrain-field';
33
+ import { heightAt, heightAtSample, quantize, } from './terrain-field.js';
34
34
  export const DEFAULT_BRUSH_SETTINGS = {
35
35
  radius: 2,
36
36
  strength: 0.6,
@@ -24,8 +24,8 @@
24
24
  * the wire format must not depend on that being true of the machine that saved
25
25
  * the scene.
26
26
  */
27
- import { type TerrainData } from '../schema/terrain';
28
- import type { HeightPatch, TerrainField } from './terrain-field';
27
+ import { type TerrainData } from '../schema/terrain.js';
28
+ import type { HeightPatch, TerrainField } from './terrain-field.js';
29
29
  export type EncodedHeightPatch = Omit<HeightPatch, 'heights'> & {
30
30
  heights: string;
31
31
  };
@@ -24,7 +24,7 @@
24
24
  * the wire format must not depend on that being true of the machine that saved
25
25
  * the scene.
26
26
  */
27
- import { MAX_TERRAIN_SIDE } from '../schema/terrain';
27
+ import { MAX_TERRAIN_SIDE } from '../schema/terrain.js';
28
28
  const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
29
29
  function encodeBase64(bytes) {
30
30
  let out = '';
@@ -20,7 +20,7 @@
20
20
  * ladder, so the returned point is on the same rendered triangle plane
21
21
  * `heightAt` reports.
22
22
  */
23
- import { type TerrainField } from './terrain-field';
23
+ import { type TerrainField } from './terrain-field.js';
24
24
  export type TerrainHit = {
25
25
  /** Distance along the (unnormalized) direction vector. */
26
26
  readonly t: number;
@@ -20,7 +20,7 @@
20
20
  * ladder, so the returned point is on the same rendered triangle plane
21
21
  * `heightAt` reports.
22
22
  */
23
- import { heightAt } from './terrain-field';
23
+ import { heightAt } from './terrain-field.js';
24
24
  /** Bisection depth. 20 halvings of one cell is sub-micrometre at any sane spacing. */
25
25
  const REFINE_ITERATIONS = 20;
26
26
  /**
@@ -17,9 +17,9 @@
17
17
  * the commit, the next read is a cache hit, so a stroke never decodes base64 even
18
18
  * once, let alone per dab.
19
19
  */
20
- import type { SiteNode } from '../schema/nodes/site';
21
- import type { TerrainData } from '../schema/terrain';
22
- import { createTerrainField, type TerrainField } from './terrain-field';
20
+ import type { SiteNode } from '../schema/nodes/site.js';
21
+ import type { TerrainData } from '../schema/terrain.js';
22
+ import { createTerrainField, type TerrainField } from './terrain-field.js';
23
23
  /**
24
24
  * The terrain field for a site, or `null` when the site has no sculpted terrain.
25
25
  *
@@ -17,9 +17,9 @@
17
17
  * the commit, the next read is a cache hit, so a stroke never decodes base64 even
18
18
  * once, let alone per dab.
19
19
  */
20
- import useLiveTerrain from '../store/use-live-terrain';
21
- import { decodeTerrainField, encodeTerrainField } from './terrain-codec';
22
- import { createTerrainField } from './terrain-field';
20
+ import useLiveTerrain from '../store/use-live-terrain.js';
21
+ import { decodeTerrainField, encodeTerrainField } from './terrain-codec.js';
22
+ import { createTerrainField } from './terrain-field.js';
23
23
  const fieldCache = new WeakMap();
24
24
  /**
25
25
  * Sites whose `terrain` failed to decode. Prevents re-attempting a doomed decode
@@ -11,7 +11,7 @@
11
11
  * The whole file is pure — nodes in, metres out — which is what lets the stacking
12
12
  * matrix be tested per kind without a canvas.
13
13
  */
14
- import type { AnyNode } from '../schema';
14
+ import type { AnyNode } from '../schema/index.js';
15
15
  /**
16
16
  * World Y of the site datum — the plane terrain heights are measured from.
17
17
  *
@@ -11,9 +11,9 @@
11
11
  * The whole file is pure — nodes in, metres out — which is what lets the stacking
12
12
  * matrix be tested per kind without a canvas.
13
13
  */
14
- import { getLevelElevations } from '../services/storey';
15
- import { heightAt } from './terrain-field';
16
- import { terrainFieldOf } from './terrain-source';
14
+ import { getLevelElevations } from '../services/storey.js';
15
+ import { heightAt } from './terrain-field.js';
16
+ import { terrainFieldOf } from './terrain-source.js';
17
17
  /**
18
18
  * World Y of the site datum — the plane terrain heights are measured from.
19
19
  *
@@ -0,0 +1,19 @@
1
+ import type { AnyNode, AnyNodeId, BuildingNode, LevelNode, UnitNode, WallNode, ZoneNode } from '../schema/index.js';
2
+ export type UnitDerivation = {
3
+ unitId: UnitNode['id'];
4
+ memberZoneIds: ZoneNode['id'][];
5
+ levelIds: LevelNode['id'][];
6
+ containedNodeIds: AnyNodeId[];
7
+ boundaryWallIds: WallNode['id'][];
8
+ supportIds: AnyNodeId[];
9
+ visibleNodeIds: AnyNodeId[];
10
+ };
11
+ type Nodes = Readonly<Record<AnyNodeId, AnyNode>>;
12
+ export declare function deriveUnit(unit: UnitNode, nodes: Nodes): UnitDerivation;
13
+ export declare function unitsForZone(zoneId: ZoneNode['id'], nodes: Nodes): UnitNode[];
14
+ export declare function unassignedZoneIds(buildingId: BuildingNode['id'], nodes: Nodes): ZoneNode['id'][];
15
+ export declare function unitWarnings(unit: UnitNode, nodes: Nodes): Array<{
16
+ code: 'empty' | 'non-adjacent-levels';
17
+ }>;
18
+ export {};
19
+ //# sourceMappingURL=unit-containment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unit-containment.d.ts","sourceRoot":"","sources":["../../src/lib/unit-containment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,QAAQ,EACT,MAAM,WAAW,CAAA;AAMlB,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IACtB,aAAa,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAA;IAC/B,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAA;IAC3B,gBAAgB,EAAE,SAAS,EAAE,CAAA;IAC7B,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAA;IACjC,UAAU,EAAE,SAAS,EAAE,CAAA;IACvB,cAAc,EAAE,SAAS,EAAE,CAAA;CAC5B,CAAA;AAED,KAAK,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAA;AA8CjD,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,GAAG,cAAc,CAuFvE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,QAAQ,EAAE,CAI7E;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAqBhG;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,KAAK,GACX,KAAK,CAAC;IACP,IAAI,EAAE,OAAO,GAAG,qBAAqB,CAAA;CACtC,CAAC,CAiBD"}
@@ -0,0 +1,158 @@
1
+ import { pointOnPolygonBoundary, wallOverlapsSlabFootprint } from '../systems/slab/slab-support.js';
2
+ import { measurementCentroid } from './measurement-geometry.js';
3
+ import { pointInPolygon } from './polygon-relations.js';
4
+ import { detectSpacesForLevel, resolveAutoZonePolygon } from './space-detection.js';
5
+ function footprintCentroid(node) {
6
+ if ('position' in node && Array.isArray(node.position) && node.position.length === 3) {
7
+ return [node.position[0], node.position[2]];
8
+ }
9
+ if ('polygon' in node && Array.isArray(node.polygon)) {
10
+ const centroid = measurementCentroid(node.polygon.map(([x, z]) => [x, 0, z]));
11
+ return centroid ? [centroid[0], centroid[2]] : null;
12
+ }
13
+ return null;
14
+ }
15
+ function polygonCentroid(polygon) {
16
+ if (polygon.length < 3)
17
+ return null;
18
+ const centroid = measurementCentroid(polygon.map(([x, z]) => [x, 0, z]));
19
+ return centroid ? [centroid[0], centroid[2]] : null;
20
+ }
21
+ function coversPoint(point, polygon, holes = []) {
22
+ return (polygon.length >= 3 &&
23
+ (pointInPolygon(point, polygon) || pointOnPolygonBoundary(point[0], point[1], polygon)) &&
24
+ !holes.some((hole) => pointInPolygon(point, hole, { includeBoundary: false }) &&
25
+ !pointOnPolygonBoundary(point[0], point[1], hole)));
26
+ }
27
+ function overlapsSupport(polygon, support) {
28
+ if (polygon.length < 3 || support.polygon.length < 3)
29
+ return false;
30
+ return (polygon.some((point) => coversPoint(point, support.polygon, support.holes)) ||
31
+ support.polygon.some((point) => coversPoint(point, polygon)) ||
32
+ polygon.some((start, index) => wallOverlapsSlabFootprint({ start, end: polygon[(index + 1) % polygon.length], thickness: 0 }, support.polygon, support.holes)));
33
+ }
34
+ export function deriveUnit(unit, nodes) {
35
+ const members = [...new Set(unit.members)].flatMap((id) => {
36
+ const zone = nodes[id];
37
+ return zone?.type === 'zone' ? [zone] : [];
38
+ });
39
+ const levels = new Map();
40
+ const polygonsByLevel = new Map();
41
+ for (const zone of members) {
42
+ const level = zone.parentId ? nodes[zone.parentId] : undefined;
43
+ if (level?.type !== 'level')
44
+ continue;
45
+ levels.set(level.id, level);
46
+ const polygons = polygonsByLevel.get(level.id) ?? [];
47
+ polygons.push(resolveAutoZonePolygon(zone, (id) => nodes[id]));
48
+ polygonsByLevel.set(level.id, polygons);
49
+ }
50
+ const levelIds = [...levels.values()]
51
+ .sort((a, b) => a.level - b.level || a.id.localeCompare(b.id))
52
+ .map((level) => level.id);
53
+ const containedNodeIds = [];
54
+ const boundaryWalls = new Set();
55
+ const supportIds = [];
56
+ const wallsByLevel = new Map();
57
+ for (const node of Object.values(nodes)) {
58
+ const polygons = node.parentId
59
+ ? polygonsByLevel.get(node.parentId)
60
+ : undefined;
61
+ if (!polygons)
62
+ continue;
63
+ if (node.type === 'wall') {
64
+ const levelId = node.parentId;
65
+ wallsByLevel.set(levelId, [...(wallsByLevel.get(levelId) ?? []), node]);
66
+ if (polygons.some((polygon) => polygon.length >= 3 && wallOverlapsSlabFootprint(node, polygon))) {
67
+ boundaryWalls.add(node.id);
68
+ }
69
+ }
70
+ else if (node.type === 'slab' || node.type === 'ceiling') {
71
+ if (polygons.some((polygon) => overlapsSupport(polygon, node)))
72
+ supportIds.push(node.id);
73
+ }
74
+ else if (node.type !== 'zone' &&
75
+ node.type !== 'level' &&
76
+ node.type !== 'building' &&
77
+ node.type !== 'site' &&
78
+ node.type !== 'unit') {
79
+ const point = footprintCentroid(node);
80
+ if (point && polygons.some((polygon) => coversPoint(point, polygon)))
81
+ containedNodeIds.push(node.id);
82
+ }
83
+ }
84
+ // A zone drawn inside a room, short of the wall centerlines, still means
85
+ // that room: the walls of every detected space that encloses a member
86
+ // centroid, or whose own centroid the member encloses, are boundary walls.
87
+ for (const [levelId, walls] of wallsByLevel) {
88
+ const polygons = polygonsByLevel.get(levelId) ?? [];
89
+ for (const space of detectSpacesForLevel(levelId, walls).spaces) {
90
+ if (space.isExterior)
91
+ continue;
92
+ const spaceCentroid = polygonCentroid(space.polygon);
93
+ const matches = polygons.some((polygon) => {
94
+ const memberCentroid = polygonCentroid(polygon);
95
+ return ((memberCentroid !== null && coversPoint(memberCentroid, space.polygon)) ||
96
+ (spaceCentroid !== null && coversPoint(spaceCentroid, polygon)));
97
+ });
98
+ if (matches)
99
+ for (const wallId of space.wallIds)
100
+ boundaryWalls.add(wallId);
101
+ }
102
+ }
103
+ const boundaryWallIds = [...boundaryWalls];
104
+ const memberZoneIds = members.map((zone) => zone.id);
105
+ // Ancestors (levels, building, site, the unit itself) are left out: the
106
+ // viewer's isolation filter keeps every descendant of a listed id, so
107
+ // listing a level would show the whole storey.
108
+ const visible = new Set([
109
+ ...memberZoneIds,
110
+ ...containedNodeIds,
111
+ ...boundaryWallIds,
112
+ ...supportIds,
113
+ ]);
114
+ return {
115
+ unitId: unit.id,
116
+ memberZoneIds,
117
+ levelIds,
118
+ containedNodeIds,
119
+ boundaryWallIds,
120
+ supportIds,
121
+ visibleNodeIds: [...visible],
122
+ };
123
+ }
124
+ export function unitsForZone(zoneId, nodes) {
125
+ return Object.values(nodes).filter((node) => node.type === 'unit' && node.members.includes(zoneId));
126
+ }
127
+ export function unassignedZoneIds(buildingId, nodes) {
128
+ const assigned = new Set(Object.values(nodes).flatMap((node) => (node.type === 'unit' ? node.members : [])));
129
+ const building = nodes[buildingId];
130
+ if (building?.type !== 'building')
131
+ return [];
132
+ const levels = new Set(Object.values(nodes).flatMap((node) => node.type === 'level' && (node.parentId === buildingId || building.children.includes(node.id))
133
+ ? [node.id]
134
+ : []));
135
+ return Object.values(nodes).flatMap((node) => node.type === 'zone' &&
136
+ node.parentId &&
137
+ levels.has(node.parentId) &&
138
+ !assigned.has(node.id)
139
+ ? [node.id]
140
+ : []);
141
+ }
142
+ export function unitWarnings(unit, nodes) {
143
+ const members = [...new Set(unit.members)].filter((id) => nodes[id]?.type === 'zone');
144
+ const warnings = [];
145
+ if (members.length === 0)
146
+ warnings.push({ code: 'empty' });
147
+ const ordinals = [
148
+ ...new Set(members.flatMap((id) => {
149
+ const parentId = nodes[id]?.parentId;
150
+ const level = parentId ? nodes[parentId] : undefined;
151
+ return level?.type === 'level' ? [level.level] : [];
152
+ })),
153
+ ].sort((a, b) => a - b);
154
+ if (ordinals.some((ordinal, index) => index > 0 && ordinal - ordinals[index - 1] > 1)) {
155
+ warnings.push({ code: 'non-adjacent-levels' });
156
+ }
157
+ return warnings;
158
+ }
@@ -0,0 +1,19 @@
1
+ import type { AnyNode, AnyNodeId, LevelNode, UnitNode, ZoneNode } from '../schema/index.js';
2
+ export type UnitReport = {
3
+ memberCount: number;
4
+ levelSpan: {
5
+ minOrdinal: number;
6
+ maxOrdinal: number;
7
+ count: number;
8
+ } | null;
9
+ grossAreaM2: number;
10
+ members: Array<{
11
+ zoneId: ZoneNode['id'];
12
+ name: string;
13
+ levelId: LevelNode['id'] | null;
14
+ levelOrdinal: number | null;
15
+ areaM2: number;
16
+ }>;
17
+ };
18
+ export declare function buildUnitReport(unit: UnitNode, nodes: Readonly<Record<AnyNodeId, AnyNode>>): UnitReport;
19
+ //# sourceMappingURL=unit-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unit-report.d.ts","sourceRoot":"","sources":["../../src/lib/unit-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAIlF,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC3E,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,KAAK,CAAC;QACb,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;QACtB,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;QAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,MAAM,EAAE,MAAM,CAAA;KACf,CAAC,CAAA;CACH,CAAA;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,GAC1C,UAAU,CAgCZ"}