@pascal-app/core 1.0.0 → 1.0.3

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 (345) 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 +263 -84
  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 +77 -72
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +74 -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/node-visibility.d.ts +12 -0
  35. package/dist/lib/node-visibility.d.ts.map +1 -0
  36. package/dist/lib/node-visibility.js +12 -0
  37. package/dist/lib/plan-footprint.d.ts +41 -0
  38. package/dist/lib/plan-footprint.d.ts.map +1 -0
  39. package/dist/lib/plan-footprint.js +58 -0
  40. package/dist/lib/room-topology-index.d.ts +1 -1
  41. package/dist/lib/room-topology-index.js +1 -1
  42. package/dist/lib/selection-proxy.d.ts +1 -1
  43. package/dist/lib/slab-polygon.d.ts +2 -2
  44. package/dist/lib/slab-polygon.js +2 -2
  45. package/dist/lib/space-detection.d.ts +1 -1
  46. package/dist/lib/space-detection.d.ts.map +1 -1
  47. package/dist/lib/space-detection.js +16 -11
  48. package/dist/lib/terrain-brush.d.ts +1 -1
  49. package/dist/lib/terrain-brush.js +1 -1
  50. package/dist/lib/terrain-codec.d.ts +2 -2
  51. package/dist/lib/terrain-codec.js +1 -1
  52. package/dist/lib/terrain-raycast.d.ts +1 -1
  53. package/dist/lib/terrain-raycast.js +1 -1
  54. package/dist/lib/terrain-source.d.ts +3 -3
  55. package/dist/lib/terrain-source.js +3 -3
  56. package/dist/lib/terrain-support.d.ts +1 -1
  57. package/dist/lib/terrain-support.js +3 -3
  58. package/dist/lib/unit-containment.d.ts +19 -0
  59. package/dist/lib/unit-containment.d.ts.map +1 -0
  60. package/dist/lib/unit-containment.js +158 -0
  61. package/dist/lib/unit-report.d.ts +19 -0
  62. package/dist/lib/unit-report.d.ts.map +1 -0
  63. package/dist/lib/unit-report.js +36 -0
  64. package/dist/lib/wall-distance.d.ts +2 -2
  65. package/dist/lib/wall-distance.js +1 -1
  66. package/dist/lib/zone-quantities.d.ts +1 -1
  67. package/dist/lib/zone-quantities.js +7 -7
  68. package/dist/material-library.d.ts +1 -1
  69. package/dist/material-library.js +1 -1
  70. package/dist/procedural-items/fixtures.d.ts +261 -0
  71. package/dist/procedural-items/fixtures.d.ts.map +1 -0
  72. package/dist/procedural-items/fixtures.js +349 -0
  73. package/dist/procedural-items/index.d.ts +10 -0
  74. package/dist/procedural-items/index.d.ts.map +1 -0
  75. package/dist/procedural-items/index.js +9 -0
  76. package/dist/procedural-items/integration.d.ts +92 -0
  77. package/dist/procedural-items/integration.d.ts.map +1 -0
  78. package/dist/procedural-items/integration.js +19 -0
  79. package/dist/procedural-items/library-colors.d.ts +56 -0
  80. package/dist/procedural-items/library-colors.d.ts.map +1 -0
  81. package/dist/procedural-items/library-colors.js +84 -0
  82. package/dist/procedural-items/materials.d.ts +5 -0
  83. package/dist/procedural-items/materials.d.ts.map +1 -0
  84. package/dist/procedural-items/materials.js +47 -0
  85. package/dist/procedural-items/node.d.ts +280 -0
  86. package/dist/procedural-items/node.d.ts.map +1 -0
  87. package/dist/procedural-items/node.js +71 -0
  88. package/dist/procedural-items/placement.d.ts +7 -0
  89. package/dist/procedural-items/placement.d.ts.map +1 -0
  90. package/dist/procedural-items/placement.js +48 -0
  91. package/dist/procedural-items/query.d.ts +51 -0
  92. package/dist/procedural-items/query.d.ts.map +1 -0
  93. package/dist/procedural-items/query.js +342 -0
  94. package/dist/procedural-items/recipe.d.ts +144 -0
  95. package/dist/procedural-items/recipe.d.ts.map +1 -0
  96. package/dist/procedural-items/recipe.js +372 -0
  97. package/dist/procedural-items/spatial.d.ts +19 -0
  98. package/dist/procedural-items/spatial.d.ts.map +1 -0
  99. package/dist/procedural-items/spatial.js +39 -0
  100. package/dist/registry/__bench__/relations-resolver.bench.js +2 -2
  101. package/dist/registry/handles.d.ts +40 -5
  102. package/dist/registry/handles.d.ts.map +1 -1
  103. package/dist/registry/index.d.ts +6 -6
  104. package/dist/registry/index.d.ts.map +1 -1
  105. package/dist/registry/index.js +4 -4
  106. package/dist/registry/registry.d.ts +1 -1
  107. package/dist/registry/relations-resolver.d.ts +2 -2
  108. package/dist/registry/relations-resolver.js +1 -1
  109. package/dist/registry/scene-api.d.ts +2 -2
  110. package/dist/registry/scene-api.js +2 -2
  111. package/dist/registry/subtree.d.ts +1 -1
  112. package/dist/registry/subtree.d.ts.map +1 -1
  113. package/dist/registry/subtree.js +8 -2
  114. package/dist/registry/types.d.ts +32 -10
  115. package/dist/registry/types.d.ts.map +1 -1
  116. package/dist/registry/use-registry-version.js +1 -1
  117. package/dist/registry/validate-relations.d.ts +4 -0
  118. package/dist/registry/validate-relations.d.ts.map +1 -0
  119. package/dist/registry/validate-relations.js +22 -0
  120. package/dist/schema/__bench__/node-parsers.bench.js +3 -3
  121. package/dist/schema/__fixtures__/node-fixtures.d.ts +1 -1
  122. package/dist/schema/__fixtures__/node-fixtures.d.ts.map +1 -1
  123. package/dist/schema/__fixtures__/node-fixtures.js +4 -2
  124. package/dist/schema/base.js +1 -1
  125. package/dist/schema/collections.d.ts +1 -1
  126. package/dist/schema/collections.js +1 -1
  127. package/dist/schema/compiled-node-parsers.d.ts +1 -1
  128. package/dist/schema/compiled-node-parsers.js +1 -1
  129. package/dist/schema/index.d.ts +74 -70
  130. package/dist/schema/index.d.ts.map +1 -1
  131. package/dist/schema/index.js +65 -61
  132. package/dist/schema/material.d.ts +1 -1
  133. package/dist/schema/material.js +1 -1
  134. package/dist/schema/nodes/automatic-downspout.d.ts +4 -4
  135. package/dist/schema/nodes/automatic-downspout.js +5 -5
  136. package/dist/schema/nodes/block.d.ts +3 -3
  137. package/dist/schema/nodes/block.d.ts.map +1 -1
  138. package/dist/schema/nodes/block.js +24 -12
  139. package/dist/schema/nodes/box-vent.js +2 -2
  140. package/dist/schema/nodes/building.d.ts +1 -1
  141. package/dist/schema/nodes/building.d.ts.map +1 -1
  142. package/dist/schema/nodes/building.js +8 -5
  143. package/dist/schema/nodes/cabinet.d.ts +4 -4
  144. package/dist/schema/nodes/cabinet.js +4 -4
  145. package/dist/schema/nodes/ceiling.d.ts +1 -1
  146. package/dist/schema/nodes/ceiling.js +5 -5
  147. package/dist/schema/nodes/chimney.js +2 -2
  148. package/dist/schema/nodes/column.d.ts +1 -0
  149. package/dist/schema/nodes/column.d.ts.map +1 -1
  150. package/dist/schema/nodes/column.js +3 -2
  151. package/dist/schema/nodes/construction-dimension.d.ts +3 -3
  152. package/dist/schema/nodes/construction-dimension.js +2 -2
  153. package/dist/schema/nodes/cupola.d.ts +1 -1
  154. package/dist/schema/nodes/cupola.js +2 -2
  155. package/dist/schema/nodes/curtain-wall.d.ts +129 -0
  156. package/dist/schema/nodes/curtain-wall.d.ts.map +1 -0
  157. package/dist/schema/nodes/curtain-wall.js +49 -0
  158. package/dist/schema/nodes/door.d.ts +2 -2
  159. package/dist/schema/nodes/door.js +2 -2
  160. package/dist/schema/nodes/dormer.d.ts +3 -3
  161. package/dist/schema/nodes/dormer.js +4 -4
  162. package/dist/schema/nodes/downspout.js +2 -2
  163. package/dist/schema/nodes/duct-fitting.js +1 -1
  164. package/dist/schema/nodes/duct-segment.js +2 -2
  165. package/dist/schema/nodes/duct-terminal.js +1 -1
  166. package/dist/schema/nodes/elevator.js +2 -2
  167. package/dist/schema/nodes/eyebrow-vent.js +2 -2
  168. package/dist/schema/nodes/fence.js +2 -2
  169. package/dist/schema/nodes/guide.js +2 -2
  170. package/dist/schema/nodes/gutter.d.ts +1 -1
  171. package/dist/schema/nodes/gutter.js +4 -4
  172. package/dist/schema/nodes/hvac-equipment.js +1 -1
  173. package/dist/schema/nodes/imported-mesh.d.ts +40 -0
  174. package/dist/schema/nodes/imported-mesh.d.ts.map +1 -0
  175. package/dist/schema/nodes/imported-mesh.js +27 -0
  176. package/dist/schema/nodes/item.d.ts +3 -3
  177. package/dist/schema/nodes/item.js +3 -3
  178. package/dist/schema/nodes/lean-to-extension.js +4 -4
  179. package/dist/schema/nodes/level.d.ts +1 -1
  180. package/dist/schema/nodes/level.d.ts.map +1 -1
  181. package/dist/schema/nodes/level.js +1 -1
  182. package/dist/schema/nodes/lineset.js +1 -1
  183. package/dist/schema/nodes/liquid-line.js +1 -1
  184. package/dist/schema/nodes/measurement.js +2 -2
  185. package/dist/schema/nodes/pipe-fitting.d.ts +1 -1
  186. package/dist/schema/nodes/pipe-fitting.js +1 -1
  187. package/dist/schema/nodes/pipe-segment.d.ts +1 -1
  188. package/dist/schema/nodes/pipe-segment.js +2 -2
  189. package/dist/schema/nodes/pipe-trap.d.ts +1 -1
  190. package/dist/schema/nodes/pipe-trap.js +1 -1
  191. package/dist/schema/nodes/ridge-vent.d.ts +1 -1
  192. package/dist/schema/nodes/ridge-vent.js +3 -3
  193. package/dist/schema/nodes/roof-segment-shape.d.ts +1 -1
  194. package/dist/schema/nodes/roof-segment-shape.js +1 -1
  195. package/dist/schema/nodes/roof-segment-walls.d.ts +1 -1
  196. package/dist/schema/nodes/roof-segment-walls.js +1 -1
  197. package/dist/schema/nodes/roof-segment.d.ts +2 -2
  198. package/dist/schema/nodes/roof-segment.js +2 -2
  199. package/dist/schema/nodes/roof.d.ts +1 -1
  200. package/dist/schema/nodes/roof.js +3 -3
  201. package/dist/schema/nodes/scan.js +3 -3
  202. package/dist/schema/nodes/shelf.d.ts +1 -1
  203. package/dist/schema/nodes/shelf.d.ts.map +1 -1
  204. package/dist/schema/nodes/shelf.js +3 -4
  205. package/dist/schema/nodes/site.js +2 -2
  206. package/dist/schema/nodes/skylight.d.ts +4 -4
  207. package/dist/schema/nodes/skylight.js +2 -2
  208. package/dist/schema/nodes/slab.js +3 -3
  209. package/dist/schema/nodes/solar-panel.js +3 -3
  210. package/dist/schema/nodes/spawn.js +1 -1
  211. package/dist/schema/nodes/stair-segment.js +2 -2
  212. package/dist/schema/nodes/stair.d.ts +1 -1
  213. package/dist/schema/nodes/stair.js +3 -3
  214. package/dist/schema/nodes/structural-grid.js +1 -1
  215. package/dist/schema/nodes/turbine-vent.d.ts +1 -1
  216. package/dist/schema/nodes/turbine-vent.js +2 -2
  217. package/dist/schema/nodes/unit.d.ts +33 -0
  218. package/dist/schema/nodes/unit.d.ts.map +1 -0
  219. package/dist/schema/nodes/unit.js +13 -0
  220. package/dist/schema/nodes/wall.d.ts +75 -2
  221. package/dist/schema/nodes/wall.d.ts.map +1 -1
  222. package/dist/schema/nodes/wall.js +10 -6
  223. package/dist/schema/nodes/window.js +2 -2
  224. package/dist/schema/nodes/zone.js +1 -1
  225. package/dist/schema/scene-material.js +2 -2
  226. package/dist/schema/types.d.ts +417 -22
  227. package/dist/schema/types.d.ts.map +1 -1
  228. package/dist/schema/types.js +56 -49
  229. package/dist/services/__fixtures__/hosting.d.ts +3 -0
  230. package/dist/services/__fixtures__/hosting.d.ts.map +1 -0
  231. package/dist/services/__fixtures__/hosting.js +9 -0
  232. package/dist/services/__fixtures__/shelf-surface-rows.d.ts +4 -0
  233. package/dist/services/__fixtures__/shelf-surface-rows.d.ts.map +1 -0
  234. package/dist/services/__fixtures__/shelf-surface-rows.js +18 -0
  235. package/dist/services/alignment-anchors.d.ts +6 -11
  236. package/dist/services/alignment-anchors.d.ts.map +1 -1
  237. package/dist/services/alignment-anchors.js +7 -34
  238. package/dist/services/drag-session.d.ts +4 -4
  239. package/dist/services/drag-session.js +1 -1
  240. package/dist/services/hosting.d.ts +5 -8
  241. package/dist/services/hosting.d.ts.map +1 -1
  242. package/dist/services/hosting.js +25 -18
  243. package/dist/services/index.d.ts +16 -13
  244. package/dist/services/index.d.ts.map +1 -1
  245. package/dist/services/index.js +16 -13
  246. package/dist/services/level-height.d.ts +2 -2
  247. package/dist/services/level-height.js +3 -3
  248. package/dist/services/movement.d.ts +3 -3
  249. package/dist/services/movement.js +2 -2
  250. package/dist/services/port-connectivity.d.ts +1 -1
  251. package/dist/services/port-connectivity.js +1 -1
  252. package/dist/services/riser-diagram.d.ts +1 -1
  253. package/dist/services/shelf-board.d.ts +5 -0
  254. package/dist/services/shelf-board.d.ts.map +1 -0
  255. package/dist/services/shelf-board.js +15 -0
  256. package/dist/services/snap.d.ts +3 -0
  257. package/dist/services/snap.d.ts.map +1 -1
  258. package/dist/services/snap.js +11 -0
  259. package/dist/services/storey.d.ts +2 -2
  260. package/dist/services/storey.js +2 -2
  261. package/dist/services/surface-hosting-procedural-parity.fixtures.d.ts +13 -0
  262. package/dist/services/surface-hosting-procedural-parity.fixtures.d.ts.map +1 -0
  263. package/dist/services/surface-hosting-procedural-parity.fixtures.js +4691 -0
  264. package/dist/services/surface-hosting-shelf-parity.fixtures.d.ts +1060 -0
  265. package/dist/services/surface-hosting-shelf-parity.fixtures.d.ts.map +1 -0
  266. package/dist/services/surface-hosting-shelf-parity.fixtures.js +3127 -0
  267. package/dist/services/surface-hosting.d.ts +92 -0
  268. package/dist/services/surface-hosting.d.ts.map +1 -0
  269. package/dist/services/surface-hosting.js +305 -0
  270. package/dist/services/surface-region.d.ts +8 -0
  271. package/dist/services/surface-region.d.ts.map +1 -0
  272. package/dist/services/surface-region.js +44 -0
  273. package/dist/services/system-graph.d.ts +2 -2
  274. package/dist/services/system-graph.js +2 -2
  275. package/dist/services/validate-dwv.d.ts +1 -1
  276. package/dist/services/validate-dwv.js +1 -1
  277. package/dist/store/actions/node-actions.d.ts +2 -2
  278. package/dist/store/actions/node-actions.d.ts.map +1 -1
  279. package/dist/store/actions/node-actions.js +50 -104
  280. package/dist/store/history-control.d.ts +3 -3
  281. package/dist/store/history-invalidation.d.ts +1 -1
  282. package/dist/store/history-invalidation.js +1 -1
  283. package/dist/store/use-interactive.d.ts +2 -2
  284. package/dist/store/use-live-terrain.d.ts +1 -1
  285. package/dist/store/use-scene.d.ts +4 -4
  286. package/dist/store/use-scene.d.ts.map +1 -1
  287. package/dist/store/use-scene.js +50 -32
  288. package/dist/systems/elevator/elevator-dispatch.d.ts +2 -2
  289. package/dist/systems/elevator/elevator-dispatch.js +1 -1
  290. package/dist/systems/elevator/elevator-geometry.d.ts +1 -1
  291. package/dist/systems/elevator/elevator-opening-sync.d.ts +1 -1
  292. package/dist/systems/elevator/elevator-opening-sync.js +2 -2
  293. package/dist/systems/elevator/elevator-opening-system.js +3 -3
  294. package/dist/systems/elevator/elevator-runtime.d.ts +3 -3
  295. package/dist/systems/elevator/elevator-runtime.js +3 -3
  296. package/dist/systems/elevator/elevator-service.d.ts +1 -1
  297. package/dist/systems/elevator/elevator-service.js +1 -1
  298. package/dist/systems/fence/fence-centerline.d.ts +2 -2
  299. package/dist/systems/fence/fence-centerline.js +2 -2
  300. package/dist/systems/fence/fence-spline.d.ts +2 -2
  301. package/dist/systems/roof/roof-elevation-system.js +3 -3
  302. package/dist/systems/roof/roof-elevation.d.ts +2 -2
  303. package/dist/systems/roof/roof-elevation.js +6 -6
  304. package/dist/systems/roof/roof-footprint.d.ts +1 -1
  305. package/dist/systems/roof/roof-footprint.js +3 -3
  306. package/dist/systems/slab/slab-placement.d.ts +1 -1
  307. package/dist/systems/slab/slab-support.d.ts +1 -1
  308. package/dist/systems/slab/slab-support.js +4 -4
  309. package/dist/systems/stair/stair-flight.d.ts +2 -2
  310. package/dist/systems/stair/stair-flight.js +2 -2
  311. package/dist/systems/stair/stair-footprint.d.ts +1 -1
  312. package/dist/systems/stair/stair-opening-preview.d.ts +248 -87
  313. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
  314. package/dist/systems/stair/stair-opening-preview.js +2 -2
  315. package/dist/systems/stair/stair-opening-sync.d.ts +1 -1
  316. package/dist/systems/stair/stair-opening-sync.js +6 -6
  317. package/dist/systems/stair/stair-opening-system.js +8 -8
  318. package/dist/systems/stair/stair-rise.d.ts +1 -1
  319. package/dist/systems/stair/stair-rise.js +3 -3
  320. package/dist/systems/wall/wall-curve.d.ts +2 -2
  321. package/dist/systems/wall/wall-footprint.d.ts +3 -3
  322. package/dist/systems/wall/wall-footprint.js +3 -3
  323. package/dist/systems/wall/wall-merge.d.ts +38 -0
  324. package/dist/systems/wall/wall-merge.d.ts.map +1 -0
  325. package/dist/systems/wall/wall-merge.js +240 -0
  326. package/dist/systems/wall/wall-mitering.d.ts +1 -1
  327. package/dist/systems/wall/wall-mitering.js +1 -1
  328. package/dist/systems/wall/wall-move.d.ts +1 -1
  329. package/dist/systems/wall/wall-operations.d.ts +28 -0
  330. package/dist/systems/wall/wall-operations.d.ts.map +1 -0
  331. package/dist/systems/wall/wall-operations.js +145 -0
  332. package/dist/systems/wall/wall-top.d.ts +1 -1
  333. package/dist/systems/wall/wall-topology.d.ts +9 -2
  334. package/dist/systems/wall/wall-topology.d.ts.map +1 -1
  335. package/dist/systems/wall/wall-topology.js +57 -9
  336. package/dist/utils/clone-scene-graph.d.ts +3 -3
  337. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  338. package/dist/utils/clone-scene-graph.js +9 -3
  339. package/dist/utils/scene-migrations.d.ts +3 -3
  340. package/dist/utils/scene-migrations.js +3 -3
  341. package/dist/utils/vertical-scene-migration.js +5 -5
  342. package/dist/validation/validate-build-json.d.ts +5 -2
  343. package/dist/validation/validate-build-json.d.ts.map +1 -1
  344. package/dist/validation/validate-build-json.js +57 -4
  345. package/package.json +15 -4
@@ -1 +1 @@
1
- {"version":3,"file":"stair-opening-preview.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7E,OAA6B,EAAE,KAAK,iBAAiB,EAAE,MAAM,qCAAqC,CAAA;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAGpE,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,CAAA;CACtC,CAAA;AAoBD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,WAYvC;AAED,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBvC;AAED,wBAAgB,qCAAqC;;mBAUlC,oBAAoB,EAAE;;EAiCxC"}
1
+ {"version":3,"file":"stair-opening-preview.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7E,OAA6B,EAAE,KAAK,iBAAiB,EAAE,MAAM,qCAAqC,CAAA;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAGpE,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,CAAA;CACtC,CAAA;AAoBD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,WAYvC;AAED,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBvC;AAED,wBAAgB,qCAAqC;;mBAUlC,oBAAoB,EAAE;;EAiCxC"}
@@ -1,5 +1,5 @@
1
- import useLiveNodeOverrides, {} from '../../store/use-live-node-overrides';
2
- import useScene from '../../store/use-scene';
1
+ import useLiveNodeOverrides, {} from '../../store/use-live-node-overrides.js';
2
+ import useScene from '../../store/use-scene.js';
3
3
  const SURFACE_OPENING_FIELDS = ['holes', 'holeMetadata'];
4
4
  function isSurface(node) {
5
5
  return node?.type === 'slab' || node?.type === 'ceiling';
@@ -1,4 +1,4 @@
1
- import type { AnyNode, AnyNodeId, CeilingNode, SlabNode } from '../../schema';
1
+ import type { AnyNode, AnyNodeId, CeilingNode, SlabNode } from '../../schema/index.js';
2
2
  export declare function syncAutoStairOpenings(nodes: Record<string, AnyNode>): {
3
3
  id: AnyNodeId;
4
4
  data: Partial<SlabNode | CeilingNode>;
@@ -1,9 +1,9 @@
1
- import { resolveBuildingForLevel, resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync';
2
- import { polygonContainsPolygon, polygonsOverlap } from '../../lib/polygon-relations';
3
- import { resolveCeilingHeight } from '../../services/level-height';
4
- import { getLevelElevations } from '../../services/storey';
5
- import { computeSegmentTransforms, rotateXZ } from './stair-footprint';
6
- import { resolveStairTotalRise } from './stair-rise';
1
+ import { resolveBuildingForLevel, resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync.js';
2
+ import { polygonContainsPolygon, polygonsOverlap } from '../../lib/polygon-relations.js';
3
+ import { resolveCeilingHeight } from '../../services/level-height.js';
4
+ import { getLevelElevations } from '../../services/storey.js';
5
+ import { computeSegmentTransforms, rotateXZ } from './stair-footprint.js';
6
+ import { resolveStairTotalRise } from './stair-rise.js';
7
7
  const CURVED_STAIR_SLAB_OPENING_RATIO = 0.8;
8
8
  const STRAIGHT_STAIR_TARGET_THRESHOLD_MIN = 0.35;
9
9
  const STAIR_SLAB_OPENING_TIGHTENING = 0;
@@ -1,13 +1,13 @@
1
1
  'use client';
2
2
  import { useEffect } from 'react';
3
- import { pauseSceneHistory, resumeSceneHistory } from '../../store/history-control';
4
- import { queueSceneNormalization } from '../../store/scene-hydration';
5
- import useLiveNodeOverrides from '../../store/use-live-node-overrides';
6
- import useLiveTransforms from '../../store/use-live-transforms';
7
- import useScene from '../../store/use-scene';
8
- import { createSurfaceOpeningPreviewController, getNodesWithLiveStairOpeningInputs, hasLiveStairOpeningInputs, } from './stair-opening-preview';
9
- import { syncAutoStairOpenings } from './stair-opening-sync';
10
- import { syncStairRises } from './stair-rise';
3
+ import { pauseSceneHistory, resumeSceneHistory } from '../../store/history-control.js';
4
+ import { queueSceneNormalization } from '../../store/scene-hydration.js';
5
+ import useLiveNodeOverrides from '../../store/use-live-node-overrides.js';
6
+ import useLiveTransforms from '../../store/use-live-transforms.js';
7
+ import useScene from '../../store/use-scene.js';
8
+ import { createSurfaceOpeningPreviewController, getNodesWithLiveStairOpeningInputs, hasLiveStairOpeningInputs, } from './stair-opening-preview.js';
9
+ import { syncAutoStairOpenings } from './stair-opening-sync.js';
10
+ import { syncStairRises } from './stair-rise.js';
11
11
  function isOpeningRelevantNode(node) {
12
12
  return (node?.type === 'building' ||
13
13
  node?.type === 'ceiling' ||
@@ -1,4 +1,4 @@
1
- import type { AnyNode, AnyNodeId, StairNode } from '../../schema';
1
+ import type { AnyNode, AnyNodeId, StairNode } from '../../schema/index.js';
2
2
  export declare function resolveStairTotalRise(stair: StairNode, nodes: Record<string, AnyNode>): number;
3
3
  /**
4
4
  * Keeps straight stairs' flight segments in step with the resolved rise.
@@ -1,6 +1,6 @@
1
- import { getFloorStackedPosition } from '../../hooks/spatial-grid/floor-placed-elevation';
2
- import { DEFAULT_LEVEL_HEIGHT } from '../../services/level-height';
3
- import { getLevelFloorToFloorHeight } from '../../services/storey';
1
+ import { getFloorStackedPosition } from '../../hooks/spatial-grid/floor-placed-elevation.js';
2
+ import { DEFAULT_LEVEL_HEIGHT } from '../../services/level-height.js';
3
+ import { getLevelFloorToFloorHeight } from '../../services/storey.js';
4
4
  export function resolveStairTotalRise(stair, nodes) {
5
5
  if (stair.totalRise !== undefined)
6
6
  return stair.totalRise;
@@ -1,5 +1,5 @@
1
- import type { FenceNode, WallNode } from '../../schema';
2
- import type { Point2D } from './wall-mitering';
1
+ import type { FenceNode, WallNode } from '../../schema/index.js';
2
+ import type { Point2D } from './wall-mitering.js';
3
3
  type WallCurveLike = Pick<WallNode | FenceNode, 'start' | 'end' | 'curveOffset'>;
4
4
  type CurveFrame = {
5
5
  point: Point2D;
@@ -1,6 +1,6 @@
1
- import type { WallNode } from '../../schema';
2
- import { type Point2D, type WallMiterData } from './wall-mitering';
3
- export { calculateLevelMiters, type Point2D, type WallMiterData } from './wall-mitering';
1
+ import type { WallNode } from '../../schema/index.js';
2
+ import { type Point2D, type WallMiterData } from './wall-mitering.js';
3
+ export { calculateLevelMiters, type Point2D, type WallMiterData } from './wall-mitering.js';
4
4
  export declare const DEFAULT_WALL_THICKNESS = 0.1;
5
5
  export declare const DEFAULT_WALL_HEIGHT = 2.5;
6
6
  export declare function getWallThickness(wallNode: WallNode): number;
@@ -1,6 +1,6 @@
1
- import { getWallSurfacePolygon, isCurvedWall } from './wall-curve';
2
- import { getWallMiterBoundaryPoints, pointToKey, } from './wall-mitering';
3
- export { calculateLevelMiters } from './wall-mitering';
1
+ import { getWallSurfacePolygon, isCurvedWall } from './wall-curve.js';
2
+ import { getWallMiterBoundaryPoints, pointToKey, } from './wall-mitering.js';
3
+ export { calculateLevelMiters } from './wall-mitering.js';
4
4
  export const DEFAULT_WALL_THICKNESS = 0.1;
5
5
  export const DEFAULT_WALL_HEIGHT = 2.5;
6
6
  const CURVED_WALL_SURFACE_SEGMENTS = 24;
@@ -0,0 +1,38 @@
1
+ import { type AnyNode, type AnyNodeId, type WallNode } from '../../schema/index.js';
2
+ import type { WallTopologyChanges } from './wall-topology.js';
3
+ export type WallAttachmentUpdate = {
4
+ id: AnyNodeId;
5
+ data: Partial<AnyNode>;
6
+ };
7
+ export declare function getWallEndpointAtPoint(wall: Pick<WallNode, 'start' | 'end'>, point: [number, number]): 'start' | 'end' | null;
8
+ /**
9
+ * The first thing two walls disagree on (for the merge's explanation), or null.
10
+ * The delete heal is strict: room sides must match, and a wall following the
11
+ * storey never joins one with an explicit height. An explicit merge compares
12
+ * what is visible instead — `heightOf` resolves each wall's actual height — and
13
+ * leaves the sides to room detection, which reclassifies the merged wall.
14
+ */
15
+ export declare function wallStyleMismatch(a: WallNode, b: WallNode, options: {
16
+ sides: boolean;
17
+ heightOf?: (wall: WallNode) => number;
18
+ }): string | null;
19
+ export declare function areWallStylesCompatible(a: WallNode, b: WallNode): boolean;
20
+ export declare function areWallsCollinearAcrossPoint(a: WallNode, b: WallNode, sharedPoint: [number, number]): boolean;
21
+ export declare function resolveMergedWallEndpoints(primary: WallNode, secondary: WallNode, sharedPoint: [number, number]): {
22
+ start: [number, number];
23
+ end: [number, number];
24
+ };
25
+ export declare function buildMergedWallAttachmentUpdates(primary: WallNode, secondary: WallNode, mergedWallId: AnyNodeId, mergedStart: [number, number], mergedEnd: [number, number], nodes: Record<AnyNodeId, AnyNode>): WallAttachmentUpdate[];
26
+ /**
27
+ * Merges a straight run of adjoining walls into one — the inverse of a split.
28
+ * Every joint must hold exactly these walls (a T or a cross stays split), and
29
+ * neighbours must continue in line and look alike (`wallStyleMismatch`: same
30
+ * thickness, visible height and finish). The wall with the most attachments
31
+ * keeps its id and height mode; openings and wall items keep their world
32
+ * position on it.
33
+ */
34
+ export declare function planWallMerge(nodes: Record<AnyNodeId, AnyNode>, wallIds: readonly AnyNodeId[]): {
35
+ changes: WallTopologyChanges;
36
+ wallId: WallNode['id'];
37
+ };
38
+ //# sourceMappingURL=wall-merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wall-merge.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-merge.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EAGd,KAAK,QAAQ,EACd,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAK1D,MAAM,MAAM,oBAAoB,GAAG;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAA;AAY5E,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC,EACrC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GACtB,OAAO,GAAG,KAAK,GAAG,IAAI,CAIxB;AAMD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,QAAQ,EACX,CAAC,EAAE,QAAQ,EACX,OAAO,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,MAAM,CAAA;CAAE,GACjE,MAAM,GAAG,IAAI,CAuBf;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,WAE/D;AAED,wBAAgB,4BAA4B,CAC1C,CAAC,EAAE,QAAQ,EACX,CAAC,EAAE,QAAQ,EACX,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,WAgB9B;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,QAAQ,EACjB,SAAS,EAAE,QAAQ,EACnB,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B;IAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAkBpD;AAyBD,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,QAAQ,EACjB,SAAS,EAAE,QAAQ,EACnB,YAAY,EAAE,SAAS,EACvB,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAChC,oBAAoB,EAAE,CAyDxB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,OAAO,EAAE,SAAS,SAAS,EAAE,GAC5B;IAAE,OAAO,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;CAAE,CA4F1D"}
@@ -0,0 +1,240 @@
1
+ import { getWallEffectiveHeightForNodes } from '../../hooks/spatial-grid/spatial-grid-manager.js';
2
+ import { getEffectiveWallSurfaceMaterial, getWallSurfaceMaterialSignature, } from '../../schema/index.js';
3
+ function pointsEqual(a, b, tolerance = 1e-6) {
4
+ const dx = a[0] - b[0];
5
+ const dz = a[1] - b[1];
6
+ return dx * dx + dz * dz <= tolerance * tolerance;
7
+ }
8
+ function wallLength(wall) {
9
+ return Math.hypot(wall.end[0] - wall.start[0], wall.end[1] - wall.start[1]);
10
+ }
11
+ export function getWallEndpointAtPoint(wall, point) {
12
+ if (pointsEqual(wall.start, point))
13
+ return 'start';
14
+ if (pointsEqual(wall.end, point))
15
+ return 'end';
16
+ return null;
17
+ }
18
+ function getWallFreeEndpoint(wall, sharedPoint) {
19
+ return pointsEqual(wall.start, sharedPoint) ? wall.end : wall.start;
20
+ }
21
+ /**
22
+ * The first thing two walls disagree on (for the merge's explanation), or null.
23
+ * The delete heal is strict: room sides must match, and a wall following the
24
+ * storey never joins one with an explicit height. An explicit merge compares
25
+ * what is visible instead — `heightOf` resolves each wall's actual height — and
26
+ * leaves the sides to room detection, which reclassifies the merged wall.
27
+ */
28
+ export function wallStyleMismatch(a, b, options) {
29
+ if ((a.parentId ?? null) !== (b.parentId ?? null))
30
+ return 'floor';
31
+ if (Math.abs((a.curveOffset ?? 0) - (b.curveOffset ?? 0)) > 1e-6)
32
+ return 'curve';
33
+ if (Math.abs((a.thickness ?? 0.2) - (b.thickness ?? 0.2)) > 1e-6)
34
+ return 'thickness';
35
+ const { heightOf } = options;
36
+ if (heightOf
37
+ ? Math.abs(heightOf(a) - heightOf(b)) > 1e-6
38
+ : (a.height == null) !== (b.height == null) ||
39
+ Math.abs((a.height ?? 0) - (b.height ?? 0)) > 1e-6)
40
+ return 'height';
41
+ for (const side of ['interior', 'exterior']) {
42
+ if (getWallSurfaceMaterialSignature(getEffectiveWallSurfaceMaterial(a, side)) !==
43
+ getWallSurfaceMaterialSignature(getEffectiveWallSurfaceMaterial(b, side)))
44
+ return `${side} finish`;
45
+ }
46
+ if (options.sides && (a.frontSide !== b.frontSide || a.backSide !== b.backSide))
47
+ return 'room sides';
48
+ if (a.visible !== b.visible)
49
+ return 'visibility';
50
+ return null;
51
+ }
52
+ export function areWallStylesCompatible(a, b) {
53
+ return wallStyleMismatch(a, b, { sides: true }) === null;
54
+ }
55
+ export function areWallsCollinearAcrossPoint(a, b, sharedPoint) {
56
+ const freeA = getWallFreeEndpoint(a, sharedPoint);
57
+ const freeB = getWallFreeEndpoint(b, sharedPoint);
58
+ const ax = freeA[0] - sharedPoint[0];
59
+ const az = freeA[1] - sharedPoint[1];
60
+ const bx = freeB[0] - sharedPoint[0];
61
+ const bz = freeB[1] - sharedPoint[1];
62
+ const lenA = Math.hypot(ax, az);
63
+ const lenB = Math.hypot(bx, bz);
64
+ if (lenA < 1e-6 || lenB < 1e-6)
65
+ return false;
66
+ const cross = (ax * bz - az * bx) / (lenA * lenB);
67
+ const dot = (ax * bx + az * bz) / (lenA * lenB);
68
+ return Math.abs(cross) <= 1e-4 && dot < -0.999;
69
+ }
70
+ export function resolveMergedWallEndpoints(primary, secondary, sharedPoint) {
71
+ const primaryEndpoint = getWallEndpointAtPoint(primary, sharedPoint);
72
+ const secondaryEndpoint = getWallEndpointAtPoint(secondary, sharedPoint);
73
+ if (primaryEndpoint === 'end' && secondaryEndpoint === 'start') {
74
+ return { start: primary.start, end: secondary.end };
75
+ }
76
+ if (primaryEndpoint === 'start' && secondaryEndpoint === 'end') {
77
+ return { start: secondary.start, end: primary.end };
78
+ }
79
+ // The kept wall never turns around: its sides, finishes and hosted faces stay
80
+ // where they are. Meeting start-to-start or end-to-end, the absorbed wall is
81
+ // the one read backwards.
82
+ if (primaryEndpoint === 'start' && secondaryEndpoint === 'start') {
83
+ return { start: secondary.end, end: primary.end };
84
+ }
85
+ return { start: primary.start, end: secondary.start };
86
+ }
87
+ /**
88
+ * Hosted nodes live in the wall's local frame (+X along it, +Z its front, yaw
89
+ * 0 front / π back). A wall read backwards turns that frame by 180° about Y,
90
+ * so the face and the yaw swap and the caller mirrors depth. Hinges, handles
91
+ * and swing hang off the node's own yaw (the plan symbol flips them from it),
92
+ * so they stay as stored.
93
+ */
94
+ function reversedWallChildPatch(child) {
95
+ const patch = {};
96
+ if ('side' in child && (child.side === 'front' || child.side === 'back')) {
97
+ patch.side = child.side === 'front' ? 'back' : 'front';
98
+ }
99
+ if ('rotation' in child && Array.isArray(child.rotation)) {
100
+ const yaw = child.rotation[1] + Math.PI;
101
+ patch.rotation = [
102
+ child.rotation[0],
103
+ Math.atan2(Math.sin(yaw), Math.cos(yaw)),
104
+ child.rotation[2],
105
+ ];
106
+ }
107
+ return patch;
108
+ }
109
+ export function buildMergedWallAttachmentUpdates(primary, secondary, mergedWallId, mergedStart, mergedEnd, nodes) {
110
+ const mergedLength = Math.max(Math.hypot(mergedEnd[0] - mergedStart[0], mergedEnd[1] - mergedStart[1]), 1e-6);
111
+ const tangentX = (mergedEnd[0] - mergedStart[0]) / mergedLength;
112
+ const tangentZ = (mergedEnd[1] - mergedStart[1]) / mergedLength;
113
+ const updates = [];
114
+ const wallChildren = [...(primary.children ?? []), ...(secondary.children ?? [])];
115
+ for (const childId of wallChildren) {
116
+ const child = nodes[childId];
117
+ if (!child)
118
+ continue;
119
+ // Every child moves to the kept wall: deleting the absorbed wall would
120
+ // otherwise take a child left on it along. Only positioned ones re-place.
121
+ const rehost = { parentId: mergedWallId, wallId: mergedWallId };
122
+ if (!('position' in child && Array.isArray(child.position))) {
123
+ updates.push({ id: childId, data: rehost });
124
+ continue;
125
+ }
126
+ const sourceWall = child.parentId === secondary.id ? secondary : primary;
127
+ const sourceLength = Math.max(wallLength(sourceWall), 1e-6);
128
+ const reversed = (sourceWall.end[0] - sourceWall.start[0]) * tangentX +
129
+ (sourceWall.end[1] - sourceWall.start[1]) * tangentZ <
130
+ 0;
131
+ const mirrored = reversed ? reversedWallChildPatch(child) : {};
132
+ const localX = typeof child.position[0] === 'number' ? child.position[0] : 0;
133
+ const worldX = sourceWall.start[0] + ((sourceWall.end[0] - sourceWall.start[0]) * localX) / sourceLength;
134
+ const worldZ = sourceWall.start[1] + ((sourceWall.end[1] - sourceWall.start[1]) * localX) / sourceLength;
135
+ const nextLocalX = Math.max(0, Math.min(mergedLength, (worldX - mergedStart[0]) * tangentX + (worldZ - mergedStart[1]) * tangentZ));
136
+ updates.push({
137
+ id: childId,
138
+ data: {
139
+ ...rehost,
140
+ ...mirrored,
141
+ position: [
142
+ nextLocalX,
143
+ child.position[1],
144
+ reversed ? -child.position[2] : child.position[2],
145
+ ],
146
+ ...('wallT' in child ? { wallT: nextLocalX / mergedLength } : {}),
147
+ },
148
+ });
149
+ }
150
+ return updates;
151
+ }
152
+ /**
153
+ * Merges a straight run of adjoining walls into one — the inverse of a split.
154
+ * Every joint must hold exactly these walls (a T or a cross stays split), and
155
+ * neighbours must continue in line and look alike (`wallStyleMismatch`: same
156
+ * thickness, visible height and finish). The wall with the most attachments
157
+ * keeps its id and height mode; openings and wall items keep their world
158
+ * position on it.
159
+ */
160
+ export function planWallMerge(nodes, wallIds) {
161
+ const walls = [...new Set(wallIds)]
162
+ .map((id) => nodes[id])
163
+ .filter((node) => node?.type === 'wall');
164
+ if (walls.length < 2 || walls.length !== new Set(wallIds).size)
165
+ throw Error('Select two or more walls to merge.');
166
+ if (walls.some((wall) => (wall.curveOffset ?? 0) !== 0))
167
+ throw Error('Only straight walls can be merged.');
168
+ const levelId = walls[0].parentId ?? null;
169
+ if (walls.some((wall) => (wall.parentId ?? null) !== levelId))
170
+ throw Error('Merge walls on the same floor.');
171
+ const virtual = { ...nodes };
172
+ const [primary, ...rest] = [...walls].sort((a, b) => (b.children?.length ?? 0) - (a.children?.length ?? 0) || a.id.localeCompare(b.id));
173
+ let merged = primary;
174
+ let remaining = rest;
175
+ while (remaining.length > 0) {
176
+ let next;
177
+ let joint;
178
+ for (const wall of remaining) {
179
+ joint = [merged.start, merged.end].find((end) => getWallEndpointAtPoint(wall, end) !== null);
180
+ if (joint) {
181
+ next = wall;
182
+ break;
183
+ }
184
+ }
185
+ if (!(next && joint))
186
+ throw Error('Merge walls that touch end to end.');
187
+ const atJoint = Object.values(virtual).filter((node) => node?.type === 'wall' &&
188
+ (node.parentId ?? null) === levelId &&
189
+ getWallEndpointAtPoint(node, joint) !== null);
190
+ if (atJoint.length !== 2)
191
+ throw Error('Another wall meets this joint, so merging would disconnect it.');
192
+ if (!areWallsCollinearAcrossPoint(merged, next, joint))
193
+ throw Error('Merge walls that continue in a straight line.');
194
+ const mismatch = wallStyleMismatch(merged, next, {
195
+ sides: false,
196
+ heightOf: (wall) => getWallEffectiveHeightForNodes(wall, virtual),
197
+ });
198
+ if (mismatch)
199
+ throw Error(`These walls have a different ${mismatch}.`);
200
+ const { start, end } = resolveMergedWallEndpoints(merged, next, joint);
201
+ for (const update of buildMergedWallAttachmentUpdates(merged, next, merged.id, start, end, virtual)) {
202
+ virtual[update.id] = { ...virtual[update.id], ...update.data };
203
+ }
204
+ merged = {
205
+ ...merged,
206
+ start,
207
+ end,
208
+ // Ids without a node are dropped rather than carried onto the kept wall.
209
+ children: [
210
+ ...new Set([
211
+ ...(merged.children ?? []),
212
+ ...(next.children ?? []).filter((id) => virtual[id]),
213
+ ]),
214
+ ],
215
+ };
216
+ virtual[merged.id] = merged;
217
+ delete virtual[next.id];
218
+ for (const node of Object.values(virtual)) {
219
+ if (node?.type !== 'zone' || !node.boundaryWallIds?.includes(next.id))
220
+ continue;
221
+ virtual[node.id] = {
222
+ ...node,
223
+ boundaryWallIds: [
224
+ ...new Set(node.boundaryWallIds.map((id) => (id === next.id ? merged.id : id))),
225
+ ],
226
+ };
227
+ }
228
+ remaining = remaining.filter((wall) => wall !== next);
229
+ }
230
+ return {
231
+ changes: {
232
+ create: [],
233
+ update: Object.values(virtual)
234
+ .filter((node) => nodes[node.id] && node !== nodes[node.id])
235
+ .map((node) => ({ id: node.id, data: node })),
236
+ delete: walls.filter((wall) => !virtual[wall.id]).map((wall) => wall.id),
237
+ },
238
+ wallId: merged.id,
239
+ };
240
+ }
@@ -1,4 +1,4 @@
1
- import type { WallNode } from '../../schema';
1
+ import type { WallNode } from '../../schema/index.js';
2
2
  export interface Point2D {
3
3
  x: number;
4
4
  y: number;
@@ -1,4 +1,4 @@
1
- import { getWallCurveFrameAt, isCurvedWall } from './wall-curve';
1
+ import { getWallCurveFrameAt, isCurvedWall } from './wall-curve.js';
2
2
  // ============================================================================
3
3
  // UTILITY FUNCTIONS
4
4
  // ============================================================================
@@ -1,4 +1,4 @@
1
- import type { WallNode } from '../../schema';
1
+ import type { WallNode } from '../../schema/index.js';
2
2
  export type WallPlanPoint = [number, number];
3
3
  export type WallMoveAxis = [number, number];
4
4
  export type WallMoveEndpoint = 'start' | 'end';
@@ -0,0 +1,28 @@
1
+ import type { AnyNode, AnyNodeId, WallNode } from '../../schema/index.js';
2
+ import type { WallPlanPoint } from './wall-move.js';
3
+ import { type WallTopologyChanges } from './wall-topology.js';
4
+ export declare function wallRectangleCorners(a: WallPlanPoint, b: WallPlanPoint): WallPlanPoint[];
5
+ /** Plan against a scratch graph so four sides commit atomically, including junction splits. */
6
+ export declare function planWallRectangle(nodes: Record<AnyNodeId, AnyNode>, args: {
7
+ levelId: AnyNodeId;
8
+ start: WallPlanPoint;
9
+ end: WallPlanPoint;
10
+ wallDefaults?: Partial<WallNode>;
11
+ }): {
12
+ changes: WallTopologyChanges;
13
+ walls: WallNode[];
14
+ };
15
+ /** Explicit split keeps the first wall's identity and reuses the junction planner's host migration. */
16
+ export declare function planWallDivision(nodes: Record<AnyNodeId, AnyNode>, wallId: WallNode['id'], distance: number): {
17
+ changes: WallTopologyChanges;
18
+ point: WallPlanPoint;
19
+ };
20
+ /**
21
+ * Several cuts as one edit. Cutting farthest first keeps the first wall's
22
+ * identity at every step, so the nearer distances stay valid on it.
23
+ */
24
+ export declare function planWallDivisions(nodes: Record<AnyNodeId, AnyNode>, wallId: WallNode['id'], distances: readonly number[]): {
25
+ changes: WallTopologyChanges;
26
+ points: WallPlanPoint[];
27
+ };
28
+ //# sourceMappingURL=wall-operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wall-operations.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,iBAAiB,CAAA;AAExB,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,aAAa,EAAE,CAiBxF;AAED,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,IAAI,EAAE;IACJ,OAAO,EAAE,SAAS,CAAA;IAClB,KAAK,EAAE,aAAa,CAAA;IACpB,GAAG,EAAE,aAAa,CAAA;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;CACjC,GACA;IAAE,OAAO,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,QAAQ,EAAE,CAAA;CAAE,CAyCrD;AAED,uGAAuG;AACvG,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,EACtB,QAAQ,EAAE,MAAM;;WAsD+B,aAAa;EAC7D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,EACtB,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B;IAAE,OAAO,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,aAAa,EAAE,CAAA;CAAE,CAyB3D"}
@@ -0,0 +1,145 @@
1
+ import { getWallCurveFrameAt, getWallCurveLength } from './wall-curve.js';
2
+ import { planWallInsertion, planWallSplitAtPoint, uncoveredWallSegments, } from './wall-topology.js';
3
+ export function wallRectangleCorners(a, b) {
4
+ if (![...a, ...b].every(Number.isFinite) ||
5
+ Math.abs(a[0] - b[0]) < 0.05 ||
6
+ Math.abs(a[1] - b[1]) < 0.05)
7
+ return [];
8
+ const x = Math.min(a[0], b[0]);
9
+ const z = Math.min(a[1], b[1]);
10
+ const right = Math.max(a[0], b[0]);
11
+ const top = Math.max(a[1], b[1]);
12
+ return [
13
+ [x, z],
14
+ [right, z],
15
+ [right, top],
16
+ [x, top],
17
+ ];
18
+ }
19
+ /** Plan against a scratch graph so four sides commit atomically, including junction splits. */
20
+ export function planWallRectangle(nodes, args) {
21
+ if (nodes[args.levelId]?.type !== 'level')
22
+ throw Error('Select an editable floor.');
23
+ const corners = wallRectangleCorners(args.start, args.end);
24
+ if (!corners.length)
25
+ throw Error('Both rectangle dimensions must be at least 5 cm.');
26
+ const virtual = { ...nodes };
27
+ const added = new Set();
28
+ for (let i = 0; i < 4; i++) {
29
+ // A side that runs along existing walls only adds what they don't cover,
30
+ // so snapping to a wall and dragging past its end reuses it rather than
31
+ // doubling it.
32
+ const levelWalls = Object.values(virtual).filter((node) => node.type === 'wall' && node.parentId === args.levelId);
33
+ for (const [start, end] of uncoveredWallSegments(corners[i], corners[(i + 1) % 4], levelWalls)) {
34
+ const result = planWallInsertion(virtual, { ...args, start, end, joinRadius: 0 });
35
+ if (!result.ok)
36
+ throw Error('The rectangle cannot be drawn here.');
37
+ for (const id of result.plan.changes.delete)
38
+ delete virtual[id];
39
+ for (const { node, parentId } of result.plan.changes.create) {
40
+ virtual[node.id] = { ...node, ...(parentId ? { parentId } : {}) };
41
+ }
42
+ for (const wall of result.plan.insertedWalls)
43
+ added.add(wall.id);
44
+ for (const { id, data } of result.plan.changes.update) {
45
+ if (virtual[id])
46
+ virtual[id] = { ...virtual[id], ...data };
47
+ }
48
+ }
49
+ }
50
+ const created = Object.values(virtual).filter((n) => !nodes[n.id]);
51
+ return {
52
+ changes: {
53
+ create: created.map((node) => ({ node, parentId: node.parentId })),
54
+ update: Object.values(virtual)
55
+ .filter((n) => nodes[n.id] && n !== nodes[n.id])
56
+ .map((n) => ({ id: n.id, data: n })),
57
+ delete: Object.keys(nodes).filter((id) => !virtual[id]),
58
+ },
59
+ walls: created.filter((n) => n.type === 'wall' && added.has(n.id)),
60
+ };
61
+ }
62
+ /** Explicit split keeps the first wall's identity and reuses the junction planner's host migration. */
63
+ export function planWallDivision(nodes, wallId, distance) {
64
+ const wall = nodes[wallId];
65
+ if (wall?.type !== 'wall' || !wall.parentId)
66
+ throw Error('Select a wall.');
67
+ const length = getWallCurveLength(wall);
68
+ if (!Number.isFinite(distance) || distance < 0.05 || distance > length - 0.05)
69
+ throw Error('Keep at least 5 cm on each side of the split.');
70
+ const point = getWallCurveFrameAt(wall, distance / length).point;
71
+ const result = planWallSplitAtPoint(nodes, {
72
+ levelId: wall.parentId,
73
+ point: [point.x, point.y],
74
+ radius: 0.001,
75
+ ignoreWallIds: Object.values(nodes)
76
+ .filter((n) => n.type === 'wall' && n.id !== wallId)
77
+ .map((n) => n.id),
78
+ });
79
+ if (!result.ok || result.plan.changes.create.length !== 2)
80
+ throw Error('Move the split away from doors, windows and mounted objects.');
81
+ const [first, second] = result.plan.changes.create;
82
+ const firstId = first.node.id;
83
+ const changes = {
84
+ create: [second],
85
+ delete: [],
86
+ update: [
87
+ {
88
+ id: wallId,
89
+ data: { ...first.node, id: wallId, parentId: wall.parentId },
90
+ },
91
+ ...result.plan.changes.update.map((op) => ({
92
+ ...op,
93
+ data: {
94
+ ...op.data,
95
+ ...(op.data.parentId === firstId ? { parentId: wallId } : {}),
96
+ ...('wallId' in op.data && op.data.wallId === firstId ? { wallId } : {}),
97
+ },
98
+ })),
99
+ ],
100
+ };
101
+ for (const node of Object.values(nodes)) {
102
+ if (node.type === 'zone' &&
103
+ node.parentId === wall.parentId &&
104
+ node.boundaryWallIds?.includes(wallId)) {
105
+ changes.update.push({
106
+ id: node.id,
107
+ data: {
108
+ boundaryWallIds: node.boundaryWallIds.flatMap((id) => id === wallId ? [wallId, second.node.id] : [id]),
109
+ },
110
+ });
111
+ }
112
+ }
113
+ return { changes, point: [point.x, point.y] };
114
+ }
115
+ /**
116
+ * Several cuts as one edit. Cutting farthest first keeps the first wall's
117
+ * identity at every step, so the nearer distances stay valid on it.
118
+ */
119
+ export function planWallDivisions(nodes, wallId, distances) {
120
+ const virtual = { ...nodes };
121
+ const points = [];
122
+ for (const distance of [...distances].sort((a, b) => b - a)) {
123
+ const plan = planWallDivision(virtual, wallId, distance);
124
+ for (const { node, parentId } of plan.changes.create) {
125
+ virtual[node.id] = { ...node, ...(parentId ? { parentId } : {}) };
126
+ }
127
+ for (const { id, data } of plan.changes.update) {
128
+ if (virtual[id])
129
+ virtual[id] = { ...virtual[id], ...data };
130
+ }
131
+ points.push(plan.point);
132
+ }
133
+ return {
134
+ changes: {
135
+ create: Object.values(virtual)
136
+ .filter((n) => !nodes[n.id])
137
+ .map((node) => ({ node, parentId: node.parentId })),
138
+ update: Object.values(virtual)
139
+ .filter((n) => nodes[n.id] && n !== nodes[n.id])
140
+ .map((n) => ({ id: n.id, data: n })),
141
+ delete: [],
142
+ },
143
+ points: points.reverse(),
144
+ };
145
+ }
@@ -1,4 +1,4 @@
1
- import type { WallNode } from '../../schema/nodes/wall';
1
+ import type { WallNode } from '../../schema/nodes/wall.js';
2
2
  /**
3
3
  * Minimum wall body height in meters. Governs both the wall height
4
4
  * arrow's lower drag bound and the slab-elevation clamp: a slab may not
@@ -1,5 +1,5 @@
1
- import { type AnyNode, type AnyNodeId, type WallNode } from '../../schema';
2
- import type { WallPlanPoint } from './wall-move';
1
+ import { type AnyNode, type AnyNodeId, type WallNode } from '../../schema/index.js';
2
+ import type { WallPlanPoint } from './wall-move.js';
3
3
  export type WallTopologyChanges = {
4
4
  create: Array<{
5
5
  node: AnyNode;
@@ -37,6 +37,13 @@ export type WallPointSplitResult = {
37
37
  ok: false;
38
38
  reason: 'no-host';
39
39
  };
40
+ /**
41
+ * The parts of `start→end` that no straight wall already runs along, in order.
42
+ * A side drawn along an existing wall and past its end yields the overhang
43
+ * only, so the existing wall is reused instead of doubled. Gaps too short to
44
+ * be a wall are treated as covered.
45
+ */
46
+ export declare function uncoveredWallSegments(start: WallPlanPoint, end: WallPlanPoint, walls: WallNode[]): Array<[WallPlanPoint, WallPlanPoint]>;
40
47
  export declare function planWallSplitAtPoint(nodes: Record<AnyNodeId, AnyNode>, args: {
41
48
  levelId: AnyNodeId | null;
42
49
  point: WallPlanPoint;