@pascal-app/core 0.8.0 → 0.9.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 (272) hide show
  1. package/dist/events/bus.d.ts +39 -13
  2. package/dist/events/bus.d.ts.map +1 -1
  3. package/dist/hooks/scene-registry/scene-registry.d.ts +6 -22
  4. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
  5. package/dist/hooks/scene-registry/scene-registry.js +33 -26
  6. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +14 -0
  7. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -0
  8. package/dist/hooks/spatial-grid/floor-placed-elevation.js +51 -0
  9. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +2 -0
  10. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +1 -0
  11. package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +273 -0
  12. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +13 -2
  13. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  14. package/dist/hooks/spatial-grid/spatial-grid-manager.js +95 -9
  15. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +28 -1
  16. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  17. package/dist/hooks/spatial-grid/spatial-grid-sync.js +91 -18
  18. package/dist/index.d.ts +22 -4
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +19 -2
  21. package/dist/lib/polygon-relations.d.ts +10 -0
  22. package/dist/lib/polygon-relations.d.ts.map +1 -0
  23. package/dist/lib/polygon-relations.js +73 -0
  24. package/dist/lib/space-detection-pause.test.d.ts +2 -0
  25. package/dist/lib/space-detection-pause.test.d.ts.map +1 -0
  26. package/dist/lib/space-detection-pause.test.js +42 -0
  27. package/dist/lib/space-detection.d.ts +30 -1
  28. package/dist/lib/space-detection.d.ts.map +1 -1
  29. package/dist/lib/space-detection.js +215 -36
  30. package/dist/lib/space-detection.test.d.ts +2 -0
  31. package/dist/lib/space-detection.test.d.ts.map +1 -0
  32. package/dist/lib/space-detection.test.js +77 -0
  33. package/dist/material-library.d.ts +1 -1
  34. package/dist/material-library.d.ts.map +1 -1
  35. package/dist/material-library.js +1940 -143
  36. package/dist/registry/__bench__/relations-resolver.bench.d.ts +15 -0
  37. package/dist/registry/__bench__/relations-resolver.bench.d.ts.map +1 -0
  38. package/dist/registry/__bench__/relations-resolver.bench.js +170 -0
  39. package/dist/registry/handles.d.ts +321 -0
  40. package/dist/registry/handles.d.ts.map +1 -0
  41. package/dist/registry/handles.js +24 -0
  42. package/dist/registry/index.d.ts +7 -0
  43. package/dist/registry/index.d.ts.map +1 -0
  44. package/dist/registry/index.js +4 -0
  45. package/dist/registry/registry.d.ts +93 -0
  46. package/dist/registry/registry.d.ts.map +1 -0
  47. package/dist/registry/registry.js +201 -0
  48. package/dist/registry/registry.test.d.ts +2 -0
  49. package/dist/registry/registry.test.d.ts.map +1 -0
  50. package/dist/registry/registry.test.js +183 -0
  51. package/dist/registry/relations-resolver.d.ts +42 -0
  52. package/dist/registry/relations-resolver.d.ts.map +1 -0
  53. package/dist/registry/relations-resolver.js +77 -0
  54. package/dist/registry/relations-resolver.test.d.ts +2 -0
  55. package/dist/registry/relations-resolver.test.d.ts.map +1 -0
  56. package/dist/registry/relations-resolver.test.js +183 -0
  57. package/dist/registry/scene-api.d.ts +44 -0
  58. package/dist/registry/scene-api.d.ts.map +1 -0
  59. package/dist/registry/scene-api.js +111 -0
  60. package/dist/registry/scene-api.test.d.ts +2 -0
  61. package/dist/registry/scene-api.test.d.ts.map +1 -0
  62. package/dist/registry/scene-api.test.js +183 -0
  63. package/dist/registry/subtree.d.ts +67 -0
  64. package/dist/registry/subtree.d.ts.map +1 -0
  65. package/dist/registry/subtree.js +119 -0
  66. package/dist/registry/subtree.test.d.ts +2 -0
  67. package/dist/registry/subtree.test.d.ts.map +1 -0
  68. package/dist/registry/subtree.test.js +133 -0
  69. package/dist/registry/types.d.ts +1420 -0
  70. package/dist/registry/types.d.ts.map +1 -0
  71. package/dist/registry/types.js +1 -0
  72. package/dist/schema/index.d.ts +17 -2
  73. package/dist/schema/index.d.ts.map +1 -1
  74. package/dist/schema/index.js +17 -3
  75. package/dist/schema/material.d.ts +10 -0
  76. package/dist/schema/material.d.ts.map +1 -1
  77. package/dist/schema/material.js +10 -0
  78. package/dist/schema/nodes/box-vent.d.ts +74 -0
  79. package/dist/schema/nodes/box-vent.d.ts.map +1 -0
  80. package/dist/schema/nodes/box-vent.js +63 -0
  81. package/dist/schema/nodes/building.d.ts +1 -1
  82. package/dist/schema/nodes/building.d.ts.map +1 -1
  83. package/dist/schema/nodes/building.js +3 -2
  84. package/dist/schema/nodes/ceiling.d.ts +2 -0
  85. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  86. package/dist/schema/nodes/ceiling.js +1 -1
  87. package/dist/schema/nodes/chimney.d.ts +158 -0
  88. package/dist/schema/nodes/chimney.d.ts.map +1 -0
  89. package/dist/schema/nodes/chimney.js +67 -0
  90. package/dist/schema/nodes/column.d.ts +597 -5
  91. package/dist/schema/nodes/column.d.ts.map +1 -1
  92. package/dist/schema/nodes/column.js +586 -6
  93. package/dist/schema/nodes/cupola.d.ts +66 -0
  94. package/dist/schema/nodes/cupola.d.ts.map +1 -0
  95. package/dist/schema/nodes/cupola.js +28 -0
  96. package/dist/schema/nodes/dormer.d.ts +228 -0
  97. package/dist/schema/nodes/dormer.d.ts.map +1 -0
  98. package/dist/schema/nodes/dormer.js +119 -0
  99. package/dist/schema/nodes/downspout.d.ts +75 -0
  100. package/dist/schema/nodes/downspout.d.ts.map +1 -0
  101. package/dist/schema/nodes/downspout.js +63 -0
  102. package/dist/schema/nodes/elevator.d.ts +105 -0
  103. package/dist/schema/nodes/elevator.d.ts.map +1 -0
  104. package/dist/schema/nodes/elevator.js +54 -0
  105. package/dist/schema/nodes/eyebrow-vent.d.ts +68 -0
  106. package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -0
  107. package/dist/schema/nodes/eyebrow-vent.js +40 -0
  108. package/dist/schema/nodes/fence.d.ts +1 -0
  109. package/dist/schema/nodes/fence.d.ts.map +1 -1
  110. package/dist/schema/nodes/fence.js +2 -0
  111. package/dist/schema/nodes/gutter.d.ts +84 -0
  112. package/dist/schema/nodes/gutter.d.ts.map +1 -0
  113. package/dist/schema/nodes/gutter.js +76 -0
  114. package/dist/schema/nodes/item.d.ts +4 -0
  115. package/dist/schema/nodes/item.d.ts.map +1 -1
  116. package/dist/schema/nodes/item.js +9 -0
  117. package/dist/schema/nodes/level.d.ts +1 -1
  118. package/dist/schema/nodes/level.d.ts.map +1 -1
  119. package/dist/schema/nodes/level.js +2 -0
  120. package/dist/schema/nodes/ridge-vent.d.ts +67 -0
  121. package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
  122. package/dist/schema/nodes/ridge-vent.js +30 -0
  123. package/dist/schema/nodes/roof-segment.d.ts +195 -1
  124. package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
  125. package/dist/schema/nodes/roof-segment.js +254 -3
  126. package/dist/schema/nodes/roof.d.ts.map +1 -1
  127. package/dist/schema/nodes/roof.js +4 -16
  128. package/dist/schema/nodes/shelf.d.ts +105 -0
  129. package/dist/schema/nodes/shelf.d.ts.map +1 -0
  130. package/dist/schema/nodes/shelf.js +84 -0
  131. package/dist/schema/nodes/site.d.ts +1 -122
  132. package/dist/schema/nodes/site.d.ts.map +1 -1
  133. package/dist/schema/nodes/site.js +2 -6
  134. package/dist/schema/nodes/skylight.d.ts +279 -0
  135. package/dist/schema/nodes/skylight.d.ts.map +1 -0
  136. package/dist/schema/nodes/skylight.js +152 -0
  137. package/dist/schema/nodes/slab.d.ts +2 -0
  138. package/dist/schema/nodes/slab.d.ts.map +1 -1
  139. package/dist/schema/nodes/slab.js +1 -1
  140. package/dist/schema/nodes/solar-panel.d.ts +117 -0
  141. package/dist/schema/nodes/solar-panel.d.ts.map +1 -0
  142. package/dist/schema/nodes/solar-panel.js +47 -0
  143. package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -0
  144. package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -1
  145. package/dist/schema/nodes/surface-hole-metadata.js +3 -1
  146. package/dist/schema/nodes/turbine-vent.d.ts +68 -0
  147. package/dist/schema/nodes/turbine-vent.d.ts.map +1 -0
  148. package/dist/schema/nodes/turbine-vent.js +41 -0
  149. package/dist/schema/types.d.ts +1325 -125
  150. package/dist/schema/types.d.ts.map +1 -1
  151. package/dist/schema/types.js +26 -0
  152. package/dist/services/alignment-anchors.d.ts +100 -0
  153. package/dist/services/alignment-anchors.d.ts.map +1 -0
  154. package/dist/services/alignment-anchors.js +283 -0
  155. package/dist/services/alignment-anchors.test.d.ts +2 -0
  156. package/dist/services/alignment-anchors.test.d.ts.map +1 -0
  157. package/dist/services/alignment-anchors.test.js +383 -0
  158. package/dist/services/alignment.d.ts +126 -0
  159. package/dist/services/alignment.d.ts.map +1 -0
  160. package/dist/services/alignment.js +169 -0
  161. package/dist/services/alignment.test.d.ts +2 -0
  162. package/dist/services/alignment.test.d.ts.map +1 -0
  163. package/dist/services/alignment.test.js +92 -0
  164. package/dist/services/drag-session.d.ts +47 -0
  165. package/dist/services/drag-session.d.ts.map +1 -0
  166. package/dist/services/drag-session.js +100 -0
  167. package/dist/services/drag-session.test.d.ts +2 -0
  168. package/dist/services/drag-session.test.d.ts.map +1 -0
  169. package/dist/services/drag-session.test.js +219 -0
  170. package/dist/services/hosting.d.ts +80 -0
  171. package/dist/services/hosting.d.ts.map +1 -0
  172. package/dist/services/hosting.js +122 -0
  173. package/dist/services/hosting.test.d.ts +2 -0
  174. package/dist/services/hosting.test.d.ts.map +1 -0
  175. package/dist/services/hosting.test.js +199 -0
  176. package/dist/services/index.d.ts +7 -0
  177. package/dist/services/index.d.ts.map +1 -0
  178. package/dist/services/index.js +6 -0
  179. package/dist/services/movement.d.ts +48 -0
  180. package/dist/services/movement.d.ts.map +1 -0
  181. package/dist/services/movement.js +65 -0
  182. package/dist/services/movement.test.d.ts +2 -0
  183. package/dist/services/movement.test.d.ts.map +1 -0
  184. package/dist/services/movement.test.js +133 -0
  185. package/dist/services/single-undo-dance.test.d.ts +2 -0
  186. package/dist/services/single-undo-dance.test.d.ts.map +1 -0
  187. package/dist/services/single-undo-dance.test.js +210 -0
  188. package/dist/services/snap.d.ts +72 -0
  189. package/dist/services/snap.d.ts.map +1 -0
  190. package/dist/services/snap.js +120 -0
  191. package/dist/services/snap.test.d.ts +2 -0
  192. package/dist/services/snap.test.d.ts.map +1 -0
  193. package/dist/services/snap.test.js +95 -0
  194. package/dist/solar-panel-presets.d.ts +17 -0
  195. package/dist/solar-panel-presets.d.ts.map +1 -0
  196. package/dist/solar-panel-presets.js +43 -0
  197. package/dist/store/actions/node-actions.d.ts +14 -2
  198. package/dist/store/actions/node-actions.d.ts.map +1 -1
  199. package/dist/store/actions/node-actions.js +492 -15
  200. package/dist/store/actions/node-mutation-sanitize.test.d.ts +2 -0
  201. package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +1 -0
  202. package/dist/store/actions/node-mutation-sanitize.test.js +147 -0
  203. package/dist/store/actions/reparent.test.d.ts +2 -0
  204. package/dist/store/actions/reparent.test.d.ts.map +1 -0
  205. package/dist/store/actions/reparent.test.js +212 -0
  206. package/dist/store/use-interactive.d.ts +39 -0
  207. package/dist/store/use-interactive.d.ts.map +1 -1
  208. package/dist/store/use-interactive.js +75 -0
  209. package/dist/store/use-live-node-overrides.d.ts +22 -0
  210. package/dist/store/use-live-node-overrides.d.ts.map +1 -0
  211. package/dist/store/use-live-node-overrides.js +59 -0
  212. package/dist/store/use-scene-elevator-migration.test.d.ts +2 -0
  213. package/dist/store/use-scene-elevator-migration.test.d.ts.map +1 -0
  214. package/dist/store/use-scene-elevator-migration.test.js +109 -0
  215. package/dist/store/use-scene.d.ts +11 -0
  216. package/dist/store/use-scene.d.ts.map +1 -1
  217. package/dist/store/use-scene.js +196 -6
  218. package/dist/systems/elevator/elevator-dispatch.d.ts +12 -0
  219. package/dist/systems/elevator/elevator-dispatch.d.ts.map +1 -0
  220. package/dist/systems/elevator/elevator-dispatch.js +57 -0
  221. package/dist/systems/elevator/elevator-geometry.d.ts +15 -0
  222. package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -0
  223. package/dist/systems/elevator/elevator-geometry.js +50 -0
  224. package/dist/systems/elevator/elevator-opening-sync.d.ts +6 -0
  225. package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -0
  226. package/dist/systems/elevator/elevator-opening-sync.js +218 -0
  227. package/dist/systems/elevator/elevator-opening-sync.test.d.ts +2 -0
  228. package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +1 -0
  229. package/dist/systems/elevator/elevator-opening-sync.test.js +146 -0
  230. package/dist/systems/elevator/elevator-opening-system.d.ts +2 -0
  231. package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -0
  232. package/dist/systems/elevator/elevator-opening-system.js +48 -0
  233. package/dist/systems/elevator/elevator-runtime-system.d.ts +2 -0
  234. package/dist/systems/elevator/elevator-runtime-system.d.ts.map +1 -0
  235. package/dist/systems/elevator/elevator-runtime-system.js +8 -0
  236. package/dist/systems/elevator/elevator-runtime.d.ts +18 -0
  237. package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -0
  238. package/dist/systems/elevator/elevator-runtime.js +225 -0
  239. package/dist/systems/elevator/elevator-runtime.test.d.ts +2 -0
  240. package/dist/systems/elevator/elevator-runtime.test.d.ts.map +1 -0
  241. package/dist/systems/elevator/elevator-runtime.test.js +64 -0
  242. package/dist/systems/elevator/elevator-service.d.ts +19 -0
  243. package/dist/systems/elevator/elevator-service.d.ts.map +1 -0
  244. package/dist/systems/elevator/elevator-service.js +111 -0
  245. package/dist/systems/stair/stair-footprint.d.ts +44 -0
  246. package/dist/systems/stair/stair-footprint.d.ts.map +1 -0
  247. package/dist/systems/stair/stair-footprint.js +168 -0
  248. package/dist/systems/stair/stair-opening-preview.d.ts +1777 -0
  249. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -0
  250. package/dist/systems/stair/stair-opening-preview.js +93 -0
  251. package/dist/systems/stair/stair-opening-preview.test.d.ts +2 -0
  252. package/dist/systems/stair/stair-opening-preview.test.d.ts.map +1 -0
  253. package/dist/systems/stair/stair-opening-preview.test.js +80 -0
  254. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  255. package/dist/systems/stair/stair-opening-sync.js +172 -147
  256. package/dist/systems/stair/stair-opening-sync.test.js +385 -2
  257. package/dist/systems/stair/stair-opening-system.d.ts +2 -0
  258. package/dist/systems/stair/stair-opening-system.d.ts.map +1 -0
  259. package/dist/systems/stair/stair-opening-system.js +104 -0
  260. package/dist/systems/wall/wall-move.d.ts +48 -0
  261. package/dist/systems/wall/wall-move.d.ts.map +1 -0
  262. package/dist/systems/wall/wall-move.js +193 -0
  263. package/dist/utils/clone-scene-graph.d.ts +6 -4
  264. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  265. package/dist/utils/clone-scene-graph.js +6 -4
  266. package/dist/utils/clone-scene-graph.test.d.ts +2 -0
  267. package/dist/utils/clone-scene-graph.test.d.ts.map +1 -0
  268. package/dist/utils/clone-scene-graph.test.js +59 -0
  269. package/dist/validation/validate-build-json.d.ts +43 -0
  270. package/dist/validation/validate-build-json.d.ts.map +1 -0
  271. package/dist/validation/validate-build-json.js +227 -0
  272. package/package.json +9 -2
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schema/types.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAA;AAqBnB,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAoBlB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAA;AAC7C,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AACzC,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schema/types.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAA;AAkCnB,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAiClB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAA;AAC7C,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AACzC,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA"}
@@ -1,26 +1,40 @@
1
1
  import z from 'zod';
2
+ import { BoxVentNode } from './nodes/box-vent';
2
3
  import { BuildingNode } from './nodes/building';
3
4
  import { CeilingNode } from './nodes/ceiling';
5
+ import { ChimneyNode } from './nodes/chimney';
4
6
  import { ColumnNode } from './nodes/column';
7
+ import { CupolaNode } from './nodes/cupola';
5
8
  import { DoorNode } from './nodes/door';
9
+ import { DormerNode } from './nodes/dormer';
10
+ import { DownspoutNode } from './nodes/downspout';
11
+ import { ElevatorNode } from './nodes/elevator';
12
+ import { EyebrowVentNode } from './nodes/eyebrow-vent';
6
13
  import { FenceNode } from './nodes/fence';
7
14
  import { GuideNode } from './nodes/guide';
15
+ import { GutterNode } from './nodes/gutter';
8
16
  import { ItemNode } from './nodes/item';
9
17
  import { LevelNode } from './nodes/level';
18
+ import { RidgeVentNode } from './nodes/ridge-vent';
10
19
  import { RoofNode } from './nodes/roof';
11
20
  import { RoofSegmentNode } from './nodes/roof-segment';
12
21
  import { ScanNode } from './nodes/scan';
22
+ import { ShelfNode } from './nodes/shelf';
13
23
  import { SiteNode } from './nodes/site';
24
+ import { SkylightNode } from './nodes/skylight';
14
25
  import { SlabNode } from './nodes/slab';
26
+ import { SolarPanelNode } from './nodes/solar-panel';
15
27
  import { SpawnNode } from './nodes/spawn';
16
28
  import { StairNode } from './nodes/stair';
17
29
  import { StairSegmentNode } from './nodes/stair-segment';
30
+ import { TurbineVentNode } from './nodes/turbine-vent';
18
31
  import { WallNode } from './nodes/wall';
19
32
  import { WindowNode } from './nodes/window';
20
33
  import { ZoneNode } from './nodes/zone';
21
34
  export const AnyNode = z.discriminatedUnion('type', [
22
35
  SiteNode,
23
36
  BuildingNode,
37
+ ElevatorNode,
24
38
  LevelNode,
25
39
  ColumnNode,
26
40
  WallNode,
@@ -31,6 +45,7 @@ export const AnyNode = z.discriminatedUnion('type', [
31
45
  CeilingNode,
32
46
  RoofNode,
33
47
  RoofSegmentNode,
48
+ ShelfNode,
34
49
  StairNode,
35
50
  StairSegmentNode,
36
51
  ScanNode,
@@ -38,4 +53,15 @@ export const AnyNode = z.discriminatedUnion('type', [
38
53
  SpawnNode,
39
54
  WindowNode,
40
55
  DoorNode,
56
+ BoxVentNode,
57
+ RidgeVentNode,
58
+ TurbineVentNode,
59
+ CupolaNode,
60
+ EyebrowVentNode,
61
+ GutterNode,
62
+ ChimneyNode,
63
+ SolarPanelNode,
64
+ SkylightNode,
65
+ DormerNode,
66
+ DownspoutNode,
41
67
  ]);
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Node → alignment-anchor adapters.
3
+ *
4
+ * `alignment.ts` is pure geometry and knows nothing about nodes. This
5
+ * module bridges the scene graph to it: it reads a floor-placed kind's
6
+ * footprint from the registry and turns it into the bbox anchors the
7
+ * resolver matches against. Kept out of `alignment.ts` so that file stays
8
+ * registry-free.
9
+ *
10
+ * All coordinates are XZ meters in the same frame as `node.position`
11
+ * (building-local for nodes inside a building). The 3D move producer works
12
+ * entirely in that frame, so the resulting guides line up with the cursor.
13
+ */
14
+ import type { AnyNode } from '../schema/types';
15
+ import { type AlignmentAnchor } from './alignment';
16
+ export type FootprintAABB = {
17
+ minX: number;
18
+ minZ: number;
19
+ maxX: number;
20
+ maxZ: number;
21
+ };
22
+ /**
23
+ * Axis-aligned XZ bounding box of a rotated rectangle centred at
24
+ * `position`. Mirrors the rotated-corner math the spatial-grid manager
25
+ * uses (`getItemFootprint`) so alignment anchors coincide with the
26
+ * footprint used for collision / slab elevation.
27
+ */
28
+ export declare function footprintAABBFrom(position: readonly [number, number, number], dimensions: readonly [number, number, number], rotationY: number): FootprintAABB;
29
+ /** XZ footprint AABB of a floor-placed node at its current position, or
30
+ * null for kinds without a usable footprint. */
31
+ export declare function footprintAABB(node: AnyNode): FootprintAABB | null;
32
+ /** XZ footprint AABB of a floor-placed node relocated so its centre sits at
33
+ * the proposed (x, z). `rotationY` overrides the node's footprint rotation
34
+ * (R/T bumps it before the scene commit lands). Null when no footprint. */
35
+ export declare function footprintAABBAt(node: AnyNode, x: number, z: number, rotationY?: number): FootprintAABB | null;
36
+ /**
37
+ * Corner anchors for the moving node's footprint relocated so its centre
38
+ * sits at the proposed (x, z). Corners only — the moving item aligns by its
39
+ * edges, never its centreline. Returns [] when the kind has no footprint.
40
+ */
41
+ export declare function movingFootprintAnchors(node: AnyNode, x: number, z: number, rotationY?: number): AlignmentAnchor[];
42
+ /**
43
+ * Corner anchors for a moving node relocated to the proposed plan position.
44
+ * Covers both the centred-box path (`floorPlaced.footprint` /
45
+ * `alignmentFootprint: box`) and explicit AABB footprints such as stairs,
46
+ * whose occupied plan bounds depend on children or curved/spiral geometry.
47
+ */
48
+ export declare function movingAlignmentAnchors(node: AnyNode, nodes: Readonly<Record<string, AnyNode>> | undefined, x: number, z: number, rotationY?: number): AlignmentAnchor[];
49
+ /**
50
+ * Alignment anchors for a wall segment: the two centerline endpoints + chord
51
+ * midpoint, plus — when `thickness` is known — four **face** corner anchors,
52
+ * each endpoint offset by ±thickness/2 perpendicular to the wall axis.
53
+ *
54
+ * The face anchors are what let a footprint align to a wall's *face* rather
55
+ * than its centerline: for an axis-aligned wall the two same-side face
56
+ * anchors share a constant X (vertical wall) or Z (horizontal wall) running
57
+ * the wall's full length, so the point-to-point resolver snaps a moving
58
+ * corner flush to the face anywhere along the wall (the perpendicular
59
+ * tie-break connects the guide to the nearer face endpoint). A diagonal wall
60
+ * gets only its face/centerline endpoints — point-to-point can't represent a
61
+ * sloped face line; that's an accepted v1 limitation.
62
+ *
63
+ * Curve offset is ignored — endpoints are exact and the chord midpoint is
64
+ * good enough for v1. Coordinates are the wall's `start` / `end`
65
+ * (building-local XZ meters).
66
+ */
67
+ export declare function wallSegmentAnchors(id: string, start: readonly [number, number], end: readonly [number, number], thickness?: number): AlignmentAnchor[];
68
+ /** Each vertex of a polygon (slab / ceiling footprint) as a `corner` anchor. */
69
+ export declare function polygonAnchors(id: string, points: readonly (readonly [number, number])[]): AlignmentAnchor[];
70
+ /**
71
+ * Alignment anchors a node contributes to the candidate pool, dispatched by
72
+ * kind: walls / fences → segment endpoints + midpoint; slabs / ceilings →
73
+ * polygon vertices; everything else → the corners of its plan bounding box
74
+ * (`alignmentAABB`, which covers floor-placed kinds, the elevator's
75
+ * alignment box, and the stair's chain / sector footprint). Kinds with no
76
+ * usable footprint contribute nothing.
77
+ *
78
+ * `nodes` is needed only by kinds whose footprint walks siblings / children
79
+ * (a straight stair's `stair-segment` chain); every other kind derives its
80
+ * anchors from `node` alone.
81
+ */
82
+ export declare function nodeAlignmentAnchors(node: AnyNode, nodes?: Readonly<Record<string, AnyNode>>): AlignmentAnchor[];
83
+ /**
84
+ * Anchors from every alignable node except `excludeId` — the unified
85
+ * candidate pool every move / placement tool resolves against, so any
86
+ * draggable object can align to any other (items, walls, fences, slabs,
87
+ * ceilings, columns).
88
+ *
89
+ * When `levelId` is given, nodes that belong to a *different* level are
90
+ * dropped. Alignment is XZ-only, so without this a node directly below on
91
+ * another floor (e.g. the ground floor while you place on the first) would
92
+ * snap and draw a guide even though the two sit at different heights.
93
+ * Building-/site-scoped nodes with no level ancestor (e.g. an elevator
94
+ * shaft, which is parented to the building and spans every floor) resolve to
95
+ * null and stay in the pool so they align on any floor. The 2D floor-plan
96
+ * deliberately omits the filter — aligning a wall to the one directly below
97
+ * in plan is the whole point of the reference floor.
98
+ */
99
+ export declare function collectAlignmentAnchors(nodes: Readonly<Record<string, AnyNode>>, excludeId: string, levelId?: string | null): AlignmentAnchor[];
100
+ //# sourceMappingURL=alignment-anchors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alignment-anchors.d.ts","sourceRoot":"","sources":["../../src/services/alignment-anchors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAE9C,OAAO,EAAE,KAAK,eAAe,EAAqB,MAAM,aAAa,CAAA;AAErE,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtF;;;;;GAKG;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,aAAa,CA4Bf;AAwDD;iDACiD;AACjD,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAKjE;AAED;;4EAE4E;AAC5E,wBAAgB,eAAe,CAC7B,IAAI,EAAE,OAAO,EACb,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,SAAS,CAAC,EAAE,MAAM,GACjB,aAAa,GAAG,IAAI,CAItB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,OAAO,EACb,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,SAAS,CAAC,EAAE,MAAM,GACjB,eAAe,EAAE,CAInB;AAoBD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,EACpD,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,SAAS,CAAC,EAAE,MAAM,GACjB,eAAe,EAAE,CAuBnB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,GAAG,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,SAAS,CAAC,EAAE,MAAM,GACjB,eAAe,EAAE,CAwBnB;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,GAC7C,eAAe,EAAE,CAEnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,OAAO,EACb,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,eAAe,EAAE,CAkBnB;AAoBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,eAAe,EAAE,CAWnB"}
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Node → alignment-anchor adapters.
3
+ *
4
+ * `alignment.ts` is pure geometry and knows nothing about nodes. This
5
+ * module bridges the scene graph to it: it reads a floor-placed kind's
6
+ * footprint from the registry and turns it into the bbox anchors the
7
+ * resolver matches against. Kept out of `alignment.ts` so that file stays
8
+ * registry-free.
9
+ *
10
+ * All coordinates are XZ meters in the same frame as `node.position`
11
+ * (building-local for nodes inside a building). The 3D move producer works
12
+ * entirely in that frame, so the resulting guides line up with the cursor.
13
+ */
14
+ import { nodeRegistry } from '../registry';
15
+ import { DEFAULT_WALL_THICKNESS } from '../systems/wall/wall-footprint';
16
+ import { bboxCornerAnchors } from './alignment';
17
+ /**
18
+ * Axis-aligned XZ bounding box of a rotated rectangle centred at
19
+ * `position`. Mirrors the rotated-corner math the spatial-grid manager
20
+ * uses (`getItemFootprint`) so alignment anchors coincide with the
21
+ * footprint used for collision / slab elevation.
22
+ */
23
+ export function footprintAABBFrom(position, dimensions, rotationY) {
24
+ const [x, , z] = position;
25
+ const [w, , d] = dimensions;
26
+ const halfW = w / 2;
27
+ const halfD = d / 2;
28
+ const cos = Math.cos(rotationY);
29
+ const sin = Math.sin(rotationY);
30
+ let minX = Number.POSITIVE_INFINITY;
31
+ let minZ = Number.POSITIVE_INFINITY;
32
+ let maxX = Number.NEGATIVE_INFINITY;
33
+ let maxZ = Number.NEGATIVE_INFINITY;
34
+ for (const [lx, lz] of [
35
+ [-halfW, -halfD],
36
+ [halfW, -halfD],
37
+ [halfW, halfD],
38
+ [-halfW, halfD],
39
+ ]) {
40
+ const wx = x + (lx * cos - lz * sin);
41
+ const wz = z + (lx * sin + lz * cos);
42
+ if (wx < minX)
43
+ minX = wx;
44
+ if (wx > maxX)
45
+ maxX = wx;
46
+ if (wz < minZ)
47
+ minZ = wz;
48
+ if (wz > maxZ)
49
+ maxZ = wz;
50
+ }
51
+ return { minX, minZ, maxX, maxZ };
52
+ }
53
+ /** The relocatable box footprint for a node, or null when it has none
54
+ * (walls / slabs / polygon kinds) or the kind's predicate excludes it
55
+ * (e.g. a wall-attached item that doesn't rest on the floor).
56
+ *
57
+ * Box footprints come from one of two capabilities: `floorPlaced` (kinds
58
+ * whose Y is also slab-lifted — columns, items) or `alignmentFootprint`
59
+ * with a `box` shape (kinds that align by their footprint but aren't
60
+ * floor-coupled — the elevator's outer shaft). A kind whose
61
+ * `alignmentFootprint` is an `aabb` (stair) has no centred box, so it's
62
+ * resolved directly in `nodeAlignmentAnchors`, not here. */
63
+ function floorFootprint(node) {
64
+ const capabilities = nodeRegistry.get(node.type)?.capabilities;
65
+ const floorPlaced = capabilities?.floorPlaced;
66
+ // `footprint` is optional now that floor-placed kinds may instead declare
67
+ // composite `footprints` (e.g. stairs); those have no single centred box
68
+ // here, so fall through to `alignmentFootprint`.
69
+ if (floorPlaced?.footprint) {
70
+ if (floorPlaced.applies && !floorPlaced.applies(node))
71
+ return null;
72
+ return floorPlaced.footprint(node);
73
+ }
74
+ const alignment = capabilities?.alignmentFootprint?.(node);
75
+ if (alignment?.shape === 'box') {
76
+ return { dimensions: alignment.dimensions, rotation: alignment.rotation };
77
+ }
78
+ return null;
79
+ }
80
+ /**
81
+ * XZ bounding box a node occupies in plan, unifying the two non-structural
82
+ * sources: a relocatable box (`floorFootprint`, covering floor-placed kinds
83
+ * and the elevator's alignment box) and a kind that hands back an explicit
84
+ * `aabb` because its plan shape isn't a centred rectangle (stair). Returns
85
+ * null for kinds with neither.
86
+ */
87
+ function alignmentAABB(node, nodes) {
88
+ const box = footprintAABB(node);
89
+ if (box)
90
+ return box;
91
+ const alignment = nodeRegistry.get(node.type)?.capabilities?.alignmentFootprint?.(node, nodes);
92
+ if (alignment?.shape === 'aabb') {
93
+ return {
94
+ minX: alignment.minX,
95
+ minZ: alignment.minZ,
96
+ maxX: alignment.maxX,
97
+ maxZ: alignment.maxZ,
98
+ };
99
+ }
100
+ return null;
101
+ }
102
+ /** XZ footprint AABB of a floor-placed node at its current position, or
103
+ * null for kinds without a usable footprint. */
104
+ export function footprintAABB(node) {
105
+ const fp = floorFootprint(node);
106
+ if (!fp)
107
+ return null;
108
+ const position = node.position ?? [0, 0, 0];
109
+ return footprintAABBFrom(position, fp.dimensions, fp.rotation[1] ?? 0);
110
+ }
111
+ /** XZ footprint AABB of a floor-placed node relocated so its centre sits at
112
+ * the proposed (x, z). `rotationY` overrides the node's footprint rotation
113
+ * (R/T bumps it before the scene commit lands). Null when no footprint. */
114
+ export function footprintAABBAt(node, x, z, rotationY) {
115
+ const fp = floorFootprint(node);
116
+ if (!fp)
117
+ return null;
118
+ return footprintAABBFrom([x, 0, z], fp.dimensions, rotationY ?? fp.rotation[1] ?? 0);
119
+ }
120
+ /**
121
+ * Corner anchors for the moving node's footprint relocated so its centre
122
+ * sits at the proposed (x, z). Corners only — the moving item aligns by its
123
+ * edges, never its centreline. Returns [] when the kind has no footprint.
124
+ */
125
+ export function movingFootprintAnchors(node, x, z, rotationY) {
126
+ const aabb = footprintAABBAt(node, x, z, rotationY);
127
+ if (!aabb)
128
+ return [];
129
+ return bboxCornerAnchors(node.id, aabb.minX, aabb.minZ, aabb.maxX, aabb.maxZ);
130
+ }
131
+ function relocatedPlanNode(node, x, z, rotationY) {
132
+ const position = node.position;
133
+ const y = Array.isArray(position) && typeof position[1] === 'number' ? position[1] : 0;
134
+ const relocated = {
135
+ ...node,
136
+ position: [x, y, z],
137
+ };
138
+ if (rotationY !== undefined && 'rotation' in node) {
139
+ const rotation = node.rotation;
140
+ relocated.rotation = Array.isArray(rotation)
141
+ ? [rotation[0] ?? 0, rotationY, rotation[2] ?? 0]
142
+ : rotationY;
143
+ }
144
+ return relocated;
145
+ }
146
+ /**
147
+ * Corner anchors for a moving node relocated to the proposed plan position.
148
+ * Covers both the centred-box path (`floorPlaced.footprint` /
149
+ * `alignmentFootprint: box`) and explicit AABB footprints such as stairs,
150
+ * whose occupied plan bounds depend on children or curved/spiral geometry.
151
+ */
152
+ export function movingAlignmentAnchors(node, nodes, x, z, rotationY) {
153
+ const box = footprintAABBAt(node, x, z, rotationY);
154
+ if (box)
155
+ return bboxCornerAnchors(node.id, box.minX, box.minZ, box.maxX, box.maxZ);
156
+ const alignment = nodeRegistry
157
+ .get(node.type)
158
+ ?.capabilities?.alignmentFootprint?.(relocatedPlanNode(node, x, z, rotationY), nodes);
159
+ if (alignment?.shape === 'box') {
160
+ const aabb = footprintAABBFrom([x, 0, z], alignment.dimensions, alignment.rotation[1] ?? 0);
161
+ return bboxCornerAnchors(node.id, aabb.minX, aabb.minZ, aabb.maxX, aabb.maxZ);
162
+ }
163
+ if (alignment?.shape === 'aabb') {
164
+ return bboxCornerAnchors(node.id, alignment.minX, alignment.minZ, alignment.maxX, alignment.maxZ);
165
+ }
166
+ return [];
167
+ }
168
+ /**
169
+ * Alignment anchors for a wall segment: the two centerline endpoints + chord
170
+ * midpoint, plus — when `thickness` is known — four **face** corner anchors,
171
+ * each endpoint offset by ±thickness/2 perpendicular to the wall axis.
172
+ *
173
+ * The face anchors are what let a footprint align to a wall's *face* rather
174
+ * than its centerline: for an axis-aligned wall the two same-side face
175
+ * anchors share a constant X (vertical wall) or Z (horizontal wall) running
176
+ * the wall's full length, so the point-to-point resolver snaps a moving
177
+ * corner flush to the face anywhere along the wall (the perpendicular
178
+ * tie-break connects the guide to the nearer face endpoint). A diagonal wall
179
+ * gets only its face/centerline endpoints — point-to-point can't represent a
180
+ * sloped face line; that's an accepted v1 limitation.
181
+ *
182
+ * Curve offset is ignored — endpoints are exact and the chord midpoint is
183
+ * good enough for v1. Coordinates are the wall's `start` / `end`
184
+ * (building-local XZ meters).
185
+ */
186
+ export function wallSegmentAnchors(id, start, end, thickness) {
187
+ const anchors = [
188
+ { nodeId: id, kind: 'corner', x: start[0], z: start[1] },
189
+ { nodeId: id, kind: 'corner', x: end[0], z: end[1] },
190
+ { nodeId: id, kind: 'center', x: (start[0] + end[0]) / 2, z: (start[1] + end[1]) / 2 },
191
+ ];
192
+ if (thickness && thickness > 0) {
193
+ const dx = end[0] - start[0];
194
+ const dz = end[1] - start[1];
195
+ const len = Math.hypot(dx, dz);
196
+ if (len > 1e-6) {
197
+ // Perpendicular to the wall axis, scaled to half-thickness.
198
+ const half = thickness / 2;
199
+ const px = (-dz / len) * half;
200
+ const pz = (dx / len) * half;
201
+ for (const [bx, bz] of [start, end]) {
202
+ anchors.push({ nodeId: id, kind: 'corner', x: bx + px, z: bz + pz });
203
+ anchors.push({ nodeId: id, kind: 'corner', x: bx - px, z: bz - pz });
204
+ }
205
+ }
206
+ }
207
+ return anchors;
208
+ }
209
+ /** Each vertex of a polygon (slab / ceiling footprint) as a `corner` anchor. */
210
+ export function polygonAnchors(id, points) {
211
+ return points.map(([x, z]) => ({ nodeId: id, kind: 'corner', x, z }));
212
+ }
213
+ /**
214
+ * Alignment anchors a node contributes to the candidate pool, dispatched by
215
+ * kind: walls / fences → segment endpoints + midpoint; slabs / ceilings →
216
+ * polygon vertices; everything else → the corners of its plan bounding box
217
+ * (`alignmentAABB`, which covers floor-placed kinds, the elevator's
218
+ * alignment box, and the stair's chain / sector footprint). Kinds with no
219
+ * usable footprint contribute nothing.
220
+ *
221
+ * `nodes` is needed only by kinds whose footprint walks siblings / children
222
+ * (a straight stair's `stair-segment` chain); every other kind derives its
223
+ * anchors from `node` alone.
224
+ */
225
+ export function nodeAlignmentAnchors(node, nodes) {
226
+ if (node.type === 'wall' || node.type === 'fence') {
227
+ const seg = node;
228
+ // Wall thickness is schema-optional (falls back to the geometry default);
229
+ // fence always carries one. Either way, pass it through so faces align.
230
+ return wallSegmentAnchors(seg.id, seg.start, seg.end, seg.thickness ?? DEFAULT_WALL_THICKNESS);
231
+ }
232
+ if (node.type === 'slab' || node.type === 'ceiling') {
233
+ const poly = node.polygon;
234
+ return poly ? polygonAnchors(node.id, poly) : [];
235
+ }
236
+ const aabb = alignmentAABB(node, nodes);
237
+ return aabb ? bboxCornerAnchors(node.id, aabb.minX, aabb.minZ, aabb.maxX, aabb.maxZ) : [];
238
+ }
239
+ /**
240
+ * Resolve the level a node belongs to by walking its `parentId` chain, or
241
+ * null when it isn't under a level. Inlined here (rather than importing the
242
+ * spatial-grid `resolveLevelId`) to keep this services module free of
243
+ * hook / store dependencies.
244
+ */
245
+ function resolveNodeLevelId(node, nodes) {
246
+ let current = node;
247
+ while (current) {
248
+ if (current.type === 'level')
249
+ return current.id;
250
+ current = current.parentId ? nodes[current.parentId] : undefined;
251
+ }
252
+ return null;
253
+ }
254
+ /**
255
+ * Anchors from every alignable node except `excludeId` — the unified
256
+ * candidate pool every move / placement tool resolves against, so any
257
+ * draggable object can align to any other (items, walls, fences, slabs,
258
+ * ceilings, columns).
259
+ *
260
+ * When `levelId` is given, nodes that belong to a *different* level are
261
+ * dropped. Alignment is XZ-only, so without this a node directly below on
262
+ * another floor (e.g. the ground floor while you place on the first) would
263
+ * snap and draw a guide even though the two sit at different heights.
264
+ * Building-/site-scoped nodes with no level ancestor (e.g. an elevator
265
+ * shaft, which is parented to the building and spans every floor) resolve to
266
+ * null and stay in the pool so they align on any floor. The 2D floor-plan
267
+ * deliberately omits the filter — aligning a wall to the one directly below
268
+ * in plan is the whole point of the reference floor.
269
+ */
270
+ export function collectAlignmentAnchors(nodes, excludeId, levelId) {
271
+ const anchors = [];
272
+ for (const node of Object.values(nodes)) {
273
+ if (!node || node.id === excludeId)
274
+ continue;
275
+ if (levelId) {
276
+ const nodeLevelId = resolveNodeLevelId(node, nodes);
277
+ if (nodeLevelId !== null && nodeLevelId !== levelId)
278
+ continue;
279
+ }
280
+ anchors.push(...nodeAlignmentAnchors(node, nodes));
281
+ }
282
+ return anchors;
283
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=alignment-anchors.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alignment-anchors.test.d.ts","sourceRoot":"","sources":["../../src/services/alignment-anchors.test.ts"],"names":[],"mappings":""}