@bldrs-ai/conway 1.389.1221 → 1.394.1504

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 (382) hide show
  1. package/README.md +36 -22
  2. package/compiled/Dist/ConwayGeomWasmNode.js +0 -0
  3. package/compiled/Dist/ConwayGeomWasmNodeMT.js +0 -0
  4. package/compiled/Dist/ConwayGeomWasmWeb.js +0 -0
  5. package/compiled/Dist/ConwayGeomWasmWebMT.js +1 -1
  6. package/compiled/Dist/ConwayGeomWasmWebMT.wasm +0 -0
  7. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNode.js +0 -0
  8. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNodeMT.js +0 -0
  9. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWeb.js +0 -0
  10. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.js +1 -1
  11. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.wasm +0 -0
  12. package/compiled/dependencies/conway-geom/interface/conway_geometry.d.ts +19 -0
  13. package/compiled/dependencies/conway-geom/interface/conway_geometry.d.ts.map +1 -1
  14. package/compiled/dependencies/conway-geom/interface/conway_geometry.js +17 -0
  15. package/compiled/dependencies/conway-geom/interface/parameters/surface_object.d.ts +6 -0
  16. package/compiled/dependencies/conway-geom/interface/parameters/surface_object.d.ts.map +1 -1
  17. package/compiled/examples/browser-bundled.cjs +1569 -51
  18. package/compiled/examples/cli-bundled.cjs +2790 -436
  19. package/compiled/examples/cli-step-bundled.cjs +1904 -229
  20. package/compiled/examples/validator-bundled.cjs +1569 -51
  21. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_curve.gen.d.ts +2 -2
  22. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_curve.gen.d.ts.map +1 -1
  23. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_curve.gen.js +2 -2
  24. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_surface.gen.d.ts +3 -3
  25. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_surface.gen.d.ts.map +1 -1
  26. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_surface.gen.js +3 -3
  27. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/composite_curve.gen.d.ts +1 -1
  28. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/composite_curve.gen.d.ts.map +1 -1
  29. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/composite_curve.gen.js +1 -1
  30. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_2d.gen.d.ts +1 -1
  31. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_2d.gen.d.ts.map +1 -1
  32. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_2d.gen.js +1 -1
  33. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_3d.gen.d.ts +1 -1
  34. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_3d.gen.d.ts.map +1 -1
  35. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_3d.gen.js +1 -1
  36. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_surface.gen.d.ts +1 -1
  37. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_surface.gen.d.ts.map +1 -1
  38. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_surface.gen.js +1 -1
  39. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/presentation_style_assignment.gen.d.ts.map +1 -1
  40. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/presentation_style_assignment.gen.js +2 -2
  41. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/schema_ap214.gen.js +11 -11
  42. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/shape_aspect.gen.d.ts +1 -1
  43. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/shape_aspect.gen.d.ts.map +1 -1
  44. package/compiled/src/AP214E3_2010/AP214E3_2010_gen/shape_aspect.gen.js +1 -1
  45. package/compiled/src/AP214E3_2010/ap214_command_line_main.js +7 -3
  46. package/compiled/src/AP214E3_2010/ap214_geometry_extraction.d.ts +181 -7
  47. package/compiled/src/AP214E3_2010/ap214_geometry_extraction.d.ts.map +1 -1
  48. package/compiled/src/AP214E3_2010/ap214_geometry_extraction.js +906 -137
  49. package/compiled/src/AP214E3_2010/ap214_geometry_extraction.test.js +10 -2
  50. package/compiled/src/AP214E3_2010/ap214_logical_attribute.test.d.ts +2 -0
  51. package/compiled/src/AP214E3_2010/ap214_logical_attribute.test.d.ts.map +1 -0
  52. package/compiled/src/AP214E3_2010/ap214_logical_attribute.test.js +114 -0
  53. package/compiled/src/AP214E3_2010/ap214_model_geometry.d.ts +6 -0
  54. package/compiled/src/AP214E3_2010/ap214_model_geometry.d.ts.map +1 -1
  55. package/compiled/src/AP214E3_2010/ap214_model_geometry.js +9 -0
  56. package/compiled/src/AP214E3_2010/ap214_pcurve_basis_surfaces.test.d.ts +2 -0
  57. package/compiled/src/AP214E3_2010/ap214_pcurve_basis_surfaces.test.d.ts.map +1 -0
  58. package/compiled/src/AP214E3_2010/ap214_pcurve_basis_surfaces.test.js +233 -0
  59. package/compiled/src/AP214E3_2010/ap214_sphere_vertex_loop.test.d.ts +2 -0
  60. package/compiled/src/AP214E3_2010/ap214_sphere_vertex_loop.test.d.ts.map +1 -0
  61. package/compiled/src/AP214E3_2010/ap214_sphere_vertex_loop.test.js +147 -0
  62. package/compiled/src/AP214E3_2010/ap214_step_model.d.ts +2 -1
  63. package/compiled/src/AP214E3_2010/ap214_step_model.d.ts.map +1 -1
  64. package/compiled/src/AP214E3_2010/ap214_step_model.js +1 -1
  65. package/compiled/src/AP214E3_2010/ap214_typed_select_enum.test.d.ts +2 -0
  66. package/compiled/src/AP214E3_2010/ap214_typed_select_enum.test.d.ts.map +1 -0
  67. package/compiled/src/AP214E3_2010/ap214_typed_select_enum.test.js +65 -0
  68. package/compiled/src/AP214E3_2010/ap214_unit_scale.test.d.ts +2 -0
  69. package/compiled/src/AP214E3_2010/ap214_unit_scale.test.d.ts.map +1 -0
  70. package/compiled/src/AP214E3_2010/ap214_unit_scale.test.js +111 -0
  71. package/compiled/src/compat/web-ifc/ap214_streamed_open.test.d.ts +2 -0
  72. package/compiled/src/compat/web-ifc/ap214_streamed_open.test.d.ts.map +1 -0
  73. package/compiled/src/compat/web-ifc/ap214_streamed_open.test.js +190 -0
  74. package/compiled/src/compat/web-ifc/coordination_export_order.test.d.ts +2 -0
  75. package/compiled/src/compat/web-ifc/coordination_export_order.test.d.ts.map +1 -0
  76. package/compiled/src/compat/web-ifc/coordination_export_order.test.js +110 -0
  77. package/compiled/src/compat/web-ifc/coordination_f64.d.ts +165 -0
  78. package/compiled/src/compat/web-ifc/coordination_f64.d.ts.map +1 -0
  79. package/compiled/src/compat/web-ifc/coordination_f64.js +286 -0
  80. package/compiled/src/compat/web-ifc/coordination_f64.test.d.ts +2 -0
  81. package/compiled/src/compat/web-ifc/coordination_f64.test.d.ts.map +1 -0
  82. package/compiled/src/compat/web-ifc/coordination_f64.test.js +225 -0
  83. package/compiled/src/compat/web-ifc/ifc_api.d.ts +154 -0
  84. package/compiled/src/compat/web-ifc/ifc_api.d.ts.map +1 -1
  85. package/compiled/src/compat/web-ifc/ifc_api.js +211 -3
  86. package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.d.ts +2 -0
  87. package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.d.ts.map +1 -0
  88. package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.js +288 -0
  89. package/compiled/src/compat/web-ifc/ifc_api_model_passthrough.d.ts +39 -0
  90. package/compiled/src/compat/web-ifc/ifc_api_model_passthrough.d.ts.map +1 -1
  91. package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.d.ts +30 -0
  92. package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.d.ts.map +1 -1
  93. package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.js +83 -0
  94. package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.d.ts +2 -0
  95. package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.d.ts.map +1 -0
  96. package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.js +383 -0
  97. package/compiled/src/compat/web-ifc/ifc_api_preview_coordination.test.d.ts +2 -0
  98. package/compiled/src/compat/web-ifc/ifc_api_preview_coordination.test.d.ts.map +1 -0
  99. package/compiled/src/compat/web-ifc/ifc_api_preview_coordination.test.js +124 -0
  100. package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.d.ts +132 -0
  101. package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.d.ts.map +1 -1
  102. package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.js +488 -92
  103. package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts +268 -0
  104. package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts.map +1 -1
  105. package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.js +996 -149
  106. package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.d.ts +2 -0
  107. package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.d.ts.map +1 -0
  108. package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.js +129 -0
  109. package/compiled/src/compat/web-ifc/spatial_imposter.d.ts +134 -0
  110. package/compiled/src/compat/web-ifc/spatial_imposter.d.ts.map +1 -0
  111. package/compiled/src/compat/web-ifc/spatial_imposter.js +933 -0
  112. package/compiled/src/compat/web-ifc/spatial_imposter.test.d.ts +2 -0
  113. package/compiled/src/compat/web-ifc/spatial_imposter.test.d.ts.map +1 -0
  114. package/compiled/src/compat/web-ifc/spatial_imposter.test.js +467 -0
  115. package/compiled/src/compat/web-ifc/store_preview_channel.d.ts +187 -0
  116. package/compiled/src/compat/web-ifc/store_preview_channel.d.ts.map +1 -0
  117. package/compiled/src/compat/web-ifc/store_preview_channel.js +524 -0
  118. package/compiled/src/compat/web-ifc/store_preview_channel.test.d.ts +2 -0
  119. package/compiled/src/compat/web-ifc/store_preview_channel.test.d.ts.map +1 -0
  120. package/compiled/src/compat/web-ifc/store_preview_channel.test.js +234 -0
  121. package/compiled/src/compat/web-ifc/streamed_preview_channel.d.ts +306 -0
  122. package/compiled/src/compat/web-ifc/streamed_preview_channel.d.ts.map +1 -0
  123. package/compiled/src/compat/web-ifc/streamed_preview_channel.js +580 -0
  124. package/compiled/src/compat/web-ifc/web_wasm_directory.test.d.ts +2 -0
  125. package/compiled/src/compat/web-ifc/web_wasm_directory.test.d.ts.map +1 -0
  126. package/compiled/src/compat/web-ifc/web_wasm_directory.test.js +22 -0
  127. package/compiled/src/core/composed_model_skeleton.d.ts +63 -0
  128. package/compiled/src/core/composed_model_skeleton.d.ts.map +1 -0
  129. package/compiled/src/core/composed_model_skeleton.js +66 -0
  130. package/compiled/src/core/composed_model_skeleton.test.d.ts +2 -0
  131. package/compiled/src/core/composed_model_skeleton.test.d.ts.map +1 -0
  132. package/compiled/src/core/composed_model_skeleton.test.js +69 -0
  133. package/compiled/src/core/demand_geometry_queue.d.ts +147 -0
  134. package/compiled/src/core/demand_geometry_queue.d.ts.map +1 -0
  135. package/compiled/src/core/demand_geometry_queue.js +223 -0
  136. package/compiled/src/core/demand_geometry_queue.test.d.ts +2 -0
  137. package/compiled/src/core/demand_geometry_queue.test.d.ts.map +1 -0
  138. package/compiled/src/core/demand_geometry_queue.test.js +133 -0
  139. package/compiled/src/core/demand_residency_pump.d.ts +93 -0
  140. package/compiled/src/core/demand_residency_pump.d.ts.map +1 -0
  141. package/compiled/src/core/demand_residency_pump.js +120 -0
  142. package/compiled/src/core/demand_residency_pump.test.d.ts +2 -0
  143. package/compiled/src/core/demand_residency_pump.test.d.ts.map +1 -0
  144. package/compiled/src/core/demand_residency_pump.test.js +148 -0
  145. package/compiled/src/core/geometry_tile_bindings.d.ts +110 -0
  146. package/compiled/src/core/geometry_tile_bindings.d.ts.map +1 -0
  147. package/compiled/src/core/geometry_tile_bindings.js +76 -0
  148. package/compiled/src/core/geometry_tile_bindings.test.d.ts +2 -0
  149. package/compiled/src/core/geometry_tile_bindings.test.d.ts.map +1 -0
  150. package/compiled/src/core/geometry_tile_bindings.test.js +211 -0
  151. package/compiled/src/core/geometry_tile_pool.d.ts +99 -0
  152. package/compiled/src/core/geometry_tile_pool.d.ts.map +1 -0
  153. package/compiled/src/core/geometry_tile_pool.js +104 -0
  154. package/compiled/src/core/geometry_tile_pool.test.d.ts +2 -0
  155. package/compiled/src/core/geometry_tile_pool.test.d.ts.map +1 -0
  156. package/compiled/src/core/geometry_tile_pool.test.js +180 -0
  157. package/compiled/src/core/mem/chunked_pool.d.ts +116 -0
  158. package/compiled/src/core/mem/chunked_pool.d.ts.map +1 -0
  159. package/compiled/src/core/mem/chunked_pool.js +149 -0
  160. package/compiled/src/core/mem/chunked_pool.test.d.ts +2 -0
  161. package/compiled/src/core/mem/chunked_pool.test.d.ts.map +1 -0
  162. package/compiled/src/core/mem/chunked_pool.test.js +63 -0
  163. package/compiled/src/core/mem/shared_asset_pool.d.ts +82 -0
  164. package/compiled/src/core/mem/shared_asset_pool.d.ts.map +1 -0
  165. package/compiled/src/core/mem/shared_asset_pool.js +113 -0
  166. package/compiled/src/core/mem/shared_asset_pool.test.d.ts +2 -0
  167. package/compiled/src/core/mem/shared_asset_pool.test.d.ts.map +1 -0
  168. package/compiled/src/core/mem/shared_asset_pool.test.js +49 -0
  169. package/compiled/src/core/model_registry.d.ts +87 -0
  170. package/compiled/src/core/model_registry.d.ts.map +1 -0
  171. package/compiled/src/core/model_registry.js +101 -0
  172. package/compiled/src/core/model_registry.test.d.ts +2 -0
  173. package/compiled/src/core/model_registry.test.d.ts.map +1 -0
  174. package/compiled/src/core/model_registry.test.js +60 -0
  175. package/compiled/src/core/model_uri.d.ts +57 -0
  176. package/compiled/src/core/model_uri.d.ts.map +1 -0
  177. package/compiled/src/core/model_uri.js +101 -0
  178. package/compiled/src/core/model_uri.test.d.ts +2 -0
  179. package/compiled/src/core/model_uri.test.d.ts.map +1 -0
  180. package/compiled/src/core/model_uri.test.js +49 -0
  181. package/compiled/src/core/progress.d.ts +16 -2
  182. package/compiled/src/core/progress.d.ts.map +1 -1
  183. package/compiled/src/core/progress.js +31 -5
  184. package/compiled/src/core/progress.test.js +11 -2
  185. package/compiled/src/core/progress_log.d.ts +1 -0
  186. package/compiled/src/core/progress_log.d.ts.map +1 -1
  187. package/compiled/src/core/progress_log.js +7 -2
  188. package/compiled/src/core/progress_log.test.js +21 -0
  189. package/compiled/src/core/shared_byte_budget.d.ts +60 -0
  190. package/compiled/src/core/shared_byte_budget.d.ts.map +1 -0
  191. package/compiled/src/core/shared_byte_budget.js +85 -0
  192. package/compiled/src/core/shared_byte_budget.test.d.ts +2 -0
  193. package/compiled/src/core/shared_byte_budget.test.d.ts.map +1 -0
  194. package/compiled/src/core/shared_byte_budget.test.js +55 -0
  195. package/compiled/src/core/wasm_heap.d.ts +208 -0
  196. package/compiled/src/core/wasm_heap.d.ts.map +1 -0
  197. package/compiled/src/core/wasm_heap.js +417 -0
  198. package/compiled/src/core/wasm_heap.test.d.ts +2 -0
  199. package/compiled/src/core/wasm_heap.test.d.ts.map +1 -0
  200. package/compiled/src/core/wasm_heap.test.js +546 -0
  201. package/compiled/src/demand/index.d.ts +23 -0
  202. package/compiled/src/demand/index.d.ts.map +1 -0
  203. package/compiled/src/demand/index.js +22 -0
  204. package/compiled/src/ifc/ifc4_gen/IfcLogical.gen.d.ts +1 -1
  205. package/compiled/src/ifc/ifc4_gen/IfcLogical.gen.d.ts.map +1 -1
  206. package/compiled/src/ifc/ifc4_gen/IfcLogical.gen.js +1 -1
  207. package/compiled/src/ifc/ifc4_gen/IfcPresentationStyleAssignment.gen.d.ts.map +1 -1
  208. package/compiled/src/ifc/ifc4_gen/IfcPresentationStyleAssignment.gen.js +2 -2
  209. package/compiled/src/ifc/ifc4_gen/IfcRelInterferesElements.gen.d.ts +1 -1
  210. package/compiled/src/ifc/ifc4_gen/IfcRelInterferesElements.gen.d.ts.map +1 -1
  211. package/compiled/src/ifc/ifc4_gen/IfcRelInterferesElements.gen.js +1 -1
  212. package/compiled/src/ifc/ifc4_gen/schema_ifc.gen.js +2 -2
  213. package/compiled/src/ifc/ifc_demand_extraction.test.d.ts +2 -0
  214. package/compiled/src/ifc/ifc_demand_extraction.test.d.ts.map +1 -0
  215. package/compiled/src/ifc/ifc_demand_extraction.test.js +251 -0
  216. package/compiled/src/ifc/ifc_geometry_extraction.d.ts +367 -6
  217. package/compiled/src/ifc/ifc_geometry_extraction.d.ts.map +1 -1
  218. package/compiled/src/ifc/ifc_geometry_extraction.js +1322 -389
  219. package/compiled/src/ifc/ifc_grid_placement.test.d.ts +2 -0
  220. package/compiled/src/ifc/ifc_grid_placement.test.d.ts.map +1 -0
  221. package/compiled/src/ifc/ifc_grid_placement.test.js +261 -0
  222. package/compiled/src/ifc/ifc_model_geometry.d.ts +12 -0
  223. package/compiled/src/ifc/ifc_model_geometry.d.ts.map +1 -1
  224. package/compiled/src/ifc/ifc_model_geometry.js +15 -0
  225. package/compiled/src/ifc/ifc_regression_batch_main.js +210 -12
  226. package/compiled/src/ifc/ifc_spatial_skeleton.d.ts +145 -0
  227. package/compiled/src/ifc/ifc_spatial_skeleton.d.ts.map +1 -0
  228. package/compiled/src/ifc/ifc_spatial_skeleton.js +266 -0
  229. package/compiled/src/ifc/ifc_spatial_skeleton.test.d.ts +2 -0
  230. package/compiled/src/ifc/ifc_spatial_skeleton.test.d.ts.map +1 -0
  231. package/compiled/src/ifc/ifc_spatial_skeleton.test.js +199 -0
  232. package/compiled/src/ifc/ifc_step_model.d.ts +6 -2
  233. package/compiled/src/ifc/ifc_step_model.d.ts.map +1 -1
  234. package/compiled/src/ifc/ifc_step_model.js +6 -4
  235. package/compiled/src/ifc/ifc_step_parser.d.ts +58 -0
  236. package/compiled/src/ifc/ifc_step_parser.d.ts.map +1 -1
  237. package/compiled/src/ifc/ifc_step_parser.js +64 -0
  238. package/compiled/src/ifc/ifc_stream_model.test.d.ts +2 -0
  239. package/compiled/src/ifc/ifc_stream_model.test.d.ts.map +1 -0
  240. package/compiled/src/ifc/ifc_stream_model.test.js +70 -0
  241. package/compiled/src/ifc/ifc_stream_open.d.ts +131 -0
  242. package/compiled/src/ifc/ifc_stream_open.d.ts.map +1 -0
  243. package/compiled/src/ifc/ifc_stream_open.js +116 -0
  244. package/compiled/src/ifc/ifc_stream_open.test.d.ts +2 -0
  245. package/compiled/src/ifc/ifc_stream_open.test.d.ts.map +1 -0
  246. package/compiled/src/ifc/ifc_stream_open.test.js +230 -0
  247. package/compiled/src/ifc/ifc_tile_extractor.d.ts +84 -0
  248. package/compiled/src/ifc/ifc_tile_extractor.d.ts.map +1 -0
  249. package/compiled/src/ifc/ifc_tile_extractor.js +134 -0
  250. package/compiled/src/ifc/ifc_tile_extractor.test.d.ts +2 -0
  251. package/compiled/src/ifc/ifc_tile_extractor.test.d.ts.map +1 -0
  252. package/compiled/src/ifc/ifc_tile_extractor.test.js +118 -0
  253. package/compiled/src/ifc/ifc_typed_select_enum.test.d.ts +2 -0
  254. package/compiled/src/ifc/ifc_typed_select_enum.test.d.ts.map +1 -0
  255. package/compiled/src/ifc/ifc_typed_select_enum.test.js +60 -0
  256. package/compiled/src/index.d.ts +21 -1
  257. package/compiled/src/index.d.ts.map +1 -1
  258. package/compiled/src/index.js +28 -1
  259. package/compiled/src/logging/logger.d.ts +16 -2
  260. package/compiled/src/logging/logger.d.ts.map +1 -1
  261. package/compiled/src/logging/logger.js +34 -7
  262. package/compiled/src/logging/logger.test.js +30 -0
  263. package/compiled/src/mem/index.d.ts +17 -0
  264. package/compiled/src/mem/index.d.ts.map +1 -0
  265. package/compiled/src/mem/index.js +16 -0
  266. package/compiled/src/namespace_surface.test.d.ts +2 -0
  267. package/compiled/src/namespace_surface.test.d.ts.map +1 -0
  268. package/compiled/src/namespace_surface.test.js +43 -0
  269. package/compiled/src/parsing/parsing_buffer.d.ts +23 -0
  270. package/compiled/src/parsing/parsing_buffer.d.ts.map +1 -1
  271. package/compiled/src/parsing/parsing_buffer.js +31 -0
  272. package/compiled/src/scripts/model_report_displacement.test.d.ts +2 -0
  273. package/compiled/src/scripts/model_report_displacement.test.d.ts.map +1 -0
  274. package/compiled/src/scripts/model_report_displacement.test.js +104 -0
  275. package/compiled/src/scripts/render_glb_paths.test.d.ts +2 -0
  276. package/compiled/src/scripts/render_glb_paths.test.d.ts.map +1 -0
  277. package/compiled/src/scripts/render_glb_paths.test.js +115 -0
  278. package/compiled/src/step/indexing/step_type_indexer.d.ts +20 -0
  279. package/compiled/src/step/indexing/step_type_indexer.d.ts.map +1 -1
  280. package/compiled/src/step/indexing/step_type_indexer.js +67 -0
  281. package/compiled/src/step/parsing/byte_source.d.ts +153 -0
  282. package/compiled/src/step/parsing/byte_source.d.ts.map +1 -0
  283. package/compiled/src/step/parsing/byte_source.js +106 -0
  284. package/compiled/src/step/parsing/byte_source.test.d.ts +2 -0
  285. package/compiled/src/step/parsing/byte_source.test.d.ts.map +1 -0
  286. package/compiled/src/step/parsing/byte_source.test.js +47 -0
  287. package/compiled/src/step/parsing/byte_source_node.d.ts +41 -0
  288. package/compiled/src/step/parsing/byte_source_node.d.ts.map +1 -0
  289. package/compiled/src/step/parsing/byte_source_node.js +54 -0
  290. package/compiled/src/step/parsing/columnar_index.d.ts +103 -0
  291. package/compiled/src/step/parsing/columnar_index.d.ts.map +1 -0
  292. package/compiled/src/step/parsing/columnar_index.js +224 -0
  293. package/compiled/src/step/parsing/columnar_index.test.d.ts +2 -0
  294. package/compiled/src/step/parsing/columnar_index.test.d.ts.map +1 -0
  295. package/compiled/src/step/parsing/columnar_index.test.js +155 -0
  296. package/compiled/src/step/parsing/cross_reference_registry.d.ts +95 -0
  297. package/compiled/src/step/parsing/cross_reference_registry.d.ts.map +1 -0
  298. package/compiled/src/step/parsing/cross_reference_registry.js +91 -0
  299. package/compiled/src/step/parsing/cross_reference_registry.test.d.ts +2 -0
  300. package/compiled/src/step/parsing/cross_reference_registry.test.d.ts.map +1 -0
  301. package/compiled/src/step/parsing/cross_reference_registry.test.js +66 -0
  302. package/compiled/src/step/parsing/decode_utf8.d.ts +15 -0
  303. package/compiled/src/step/parsing/decode_utf8.d.ts.map +1 -1
  304. package/compiled/src/step/parsing/decode_utf8.js +28 -0
  305. package/compiled/src/step/parsing/express_ref_scan.d.ts +20 -0
  306. package/compiled/src/step/parsing/express_ref_scan.d.ts.map +1 -0
  307. package/compiled/src/step/parsing/express_ref_scan.js +91 -0
  308. package/compiled/src/step/parsing/express_ref_scan.test.d.ts +2 -0
  309. package/compiled/src/step/parsing/express_ref_scan.test.d.ts.map +1 -0
  310. package/compiled/src/step/parsing/express_ref_scan.test.js +28 -0
  311. package/compiled/src/step/parsing/index_sidecar.d.ts +81 -0
  312. package/compiled/src/step/parsing/index_sidecar.d.ts.map +1 -0
  313. package/compiled/src/step/parsing/index_sidecar.js +283 -0
  314. package/compiled/src/step/parsing/index_sidecar.test.d.ts +2 -0
  315. package/compiled/src/step/parsing/index_sidecar.test.d.ts.map +1 -0
  316. package/compiled/src/step/parsing/index_sidecar.test.js +122 -0
  317. package/compiled/src/step/parsing/inf_recovery_index.test.d.ts +2 -0
  318. package/compiled/src/step/parsing/inf_recovery_index.test.d.ts.map +1 -0
  319. package/compiled/src/step/parsing/inf_recovery_index.test.js +108 -0
  320. package/compiled/src/step/parsing/prefix_type_index.d.ts +124 -0
  321. package/compiled/src/step/parsing/prefix_type_index.d.ts.map +1 -0
  322. package/compiled/src/step/parsing/prefix_type_index.js +207 -0
  323. package/compiled/src/step/parsing/prefix_type_index.test.d.ts +2 -0
  324. package/compiled/src/step/parsing/prefix_type_index.test.d.ts.map +1 -0
  325. package/compiled/src/step/parsing/prefix_type_index.test.js +298 -0
  326. package/compiled/src/step/parsing/range_byte_source.d.ts +69 -0
  327. package/compiled/src/step/parsing/range_byte_source.d.ts.map +1 -0
  328. package/compiled/src/step/parsing/range_byte_source.js +78 -0
  329. package/compiled/src/step/parsing/range_byte_source.test.d.ts +2 -0
  330. package/compiled/src/step/parsing/range_byte_source.test.d.ts.map +1 -0
  331. package/compiled/src/step/parsing/range_byte_source.test.js +70 -0
  332. package/compiled/src/step/parsing/record_event.d.ts +23 -0
  333. package/compiled/src/step/parsing/record_event.d.ts.map +1 -0
  334. package/compiled/src/step/parsing/record_event.js +1 -0
  335. package/compiled/src/step/parsing/record_field_cursor.d.ts +94 -0
  336. package/compiled/src/step/parsing/record_field_cursor.d.ts.map +1 -0
  337. package/compiled/src/step/parsing/record_field_cursor.js +189 -0
  338. package/compiled/src/step/parsing/step_deserialization_functions.d.ts +29 -0
  339. package/compiled/src/step/parsing/step_deserialization_functions.d.ts.map +1 -1
  340. package/compiled/src/step/parsing/step_deserialization_functions.js +45 -0
  341. package/compiled/src/step/parsing/step_deserialization_functions.test.js +59 -1
  342. package/compiled/src/step/parsing/step_parser.d.ts +66 -1
  343. package/compiled/src/step/parsing/step_parser.d.ts.map +1 -1
  344. package/compiled/src/step/parsing/step_parser.js +164 -10
  345. package/compiled/src/step/parsing/step_string_parser.js +8 -8
  346. package/compiled/src/step/parsing/streaming_index_builder.d.ts +141 -0
  347. package/compiled/src/step/parsing/streaming_index_builder.d.ts.map +1 -0
  348. package/compiled/src/step/parsing/streaming_index_builder.js +280 -0
  349. package/compiled/src/step/parsing/streaming_index_builder.test.d.ts +2 -0
  350. package/compiled/src/step/parsing/streaming_index_builder.test.d.ts.map +1 -0
  351. package/compiled/src/step/parsing/streaming_index_builder.test.js +107 -0
  352. package/compiled/src/step/parsing/streaming_index_builder_async.test.d.ts +2 -0
  353. package/compiled/src/step/parsing/streaming_index_builder_async.test.d.ts.map +1 -0
  354. package/compiled/src/step/parsing/streaming_index_builder_async.test.js +109 -0
  355. package/compiled/src/step/parsing/streaming_record_dispatcher.d.ts +71 -0
  356. package/compiled/src/step/parsing/streaming_record_dispatcher.d.ts.map +1 -0
  357. package/compiled/src/step/parsing/streaming_record_dispatcher.js +88 -0
  358. package/compiled/src/step/parsing/streaming_record_dispatcher.test.d.ts +2 -0
  359. package/compiled/src/step/parsing/streaming_record_dispatcher.test.d.ts.map +1 -0
  360. package/compiled/src/step/parsing/streaming_record_dispatcher.test.js +66 -0
  361. package/compiled/src/step/parsing/uint32_sink.d.ts +72 -0
  362. package/compiled/src/step/parsing/uint32_sink.d.ts.map +1 -0
  363. package/compiled/src/step/parsing/uint32_sink.js +180 -0
  364. package/compiled/src/step/parsing/uint32_sink.test.d.ts +2 -0
  365. package/compiled/src/step/parsing/uint32_sink.test.d.ts.map +1 -0
  366. package/compiled/src/step/parsing/uint32_sink.test.js +199 -0
  367. package/compiled/src/step/step_buffer_provider.d.ts +48 -3
  368. package/compiled/src/step/step_buffer_provider.d.ts.map +1 -1
  369. package/compiled/src/step/step_buffer_provider.js +60 -11
  370. package/compiled/src/step/step_buffer_provider.test.js +14 -0
  371. package/compiled/src/step/step_entity_base.d.ts +65 -3
  372. package/compiled/src/step/step_entity_base.d.ts.map +1 -1
  373. package/compiled/src/step/step_entity_base.js +135 -3
  374. package/compiled/src/step/step_model_base.d.ts +221 -8
  375. package/compiled/src/step/step_model_base.d.ts.map +1 -1
  376. package/compiled/src/step/step_model_base.js +542 -7
  377. package/compiled/src/stream/index.d.ts +34 -0
  378. package/compiled/src/stream/index.d.ts.map +1 -0
  379. package/compiled/src/stream/index.js +32 -0
  380. package/compiled/src/version/version.js +1 -1
  381. package/compiled/tsconfig.tsbuildinfo +1 -1
  382. package/package.json +16 -1
@@ -0,0 +1,933 @@
1
+ import EntityTypesIfc from "../../ifc/ifc4_gen/entity_types_ifc.gen.js";
2
+ import { IfcBuilding, IfcBuildingStorey, IfcProject, IfcRelAggregates, IfcRelContainedInSpatialStructure, IfcSite, IfcSpace, } from "../../ifc/ifc4_gen/index.js";
3
+ import { deriveCoordinationF64, mat4MultiplyF64, NORMALIZE_MAT_F64, } from "./coordination_f64.js";
4
+ /* eslint-disable no-magic-numbers */
5
+ /** Black, almost transparent — Share honours `w` as opacity. */
6
+ export const SPATIAL_IMPOSTER_COLOR = { x: 0, y: 0, z: 0, w: 0.3 };
7
+ /** Sample this many contained-product placements per spatial node. */
8
+ const PRODUCT_SAMPLE = 32;
9
+ /** Collapse a parent whose box matches its only child this closely. */
10
+ const COLLAPSE_REL = 0.15;
11
+ /**
12
+ * Minimum box edge IN METRES so a point origin still draws. Every box in
13
+ * this module is in raw IFC source units, so callers convert this by the
14
+ * model's linear scaling factor before use ({@link minEdgeSourceUnits}) —
15
+ * a bare `1` is one metre on a metre model but one millimetre on a
16
+ * millimetre one, which made the padding invisible on mm files.
17
+ */
18
+ const MIN_EDGE_M = 1;
19
+ /** Degenerate-scale floor for a box matrix (source units). */
20
+ const MIN_BOX_SCALE = 0.001;
21
+ /**
22
+ * MIN_EDGE_M expressed in the model's own source units.
23
+ *
24
+ * @param linearScalingFactor Source units -> metres.
25
+ * @return {number} The minimum edge in source units.
26
+ */
27
+ function minEdgeSourceUnits(linearScalingFactor) {
28
+ return MIN_EDGE_M / sanitizeScale_(linearScalingFactor);
29
+ }
30
+ /**
31
+ * @param linearScalingFactor Source units -> metres, possibly garbage.
32
+ * @return {number} The factor, or 1 when it is not usable.
33
+ */
34
+ function sanitizeScale_(linearScalingFactor) {
35
+ return Number.isFinite(linearScalingFactor) && linearScalingFactor > 0 ?
36
+ linearScalingFactor : 1;
37
+ }
38
+ /**
39
+ * Column-major 4x4 mapping a centred unit cube onto `aabb`, entirely in
40
+ * RAW IFC space: `translate(centre) * scale(size)`.
41
+ *
42
+ * Deliberately carries NO Z-up -> Y-up flip and NO unit scaling: both of
43
+ * those live in the coordination matrix this is composed under (see
44
+ * {@link emitSpatialStructureImposters}), exactly as they do for real
45
+ * meshes. Baking the flip here — which the parse-time point-list
46
+ * imposter this replaced used to do — put the plates in a frame the
47
+ * durable geometry never shares (conway#515).
48
+ *
49
+ * @param aabb The IFC-space bounds.
50
+ * @return {number[]} 16-element matrix.
51
+ */
52
+ export function aabbBoxMatrix(aabb) {
53
+ const sx = Math.max(aabb.max[0] - aabb.min[0], MIN_BOX_SCALE);
54
+ const sy = Math.max(aabb.max[1] - aabb.min[1], MIN_BOX_SCALE);
55
+ const sz = Math.max(aabb.max[2] - aabb.min[2], MIN_BOX_SCALE);
56
+ const cx = (aabb.min[0] + aabb.max[0]) * 0.5;
57
+ const cy = (aabb.min[1] + aabb.max[1]) * 0.5;
58
+ const cz = (aabb.min[2] + aabb.max[2]) * 0.5;
59
+ return [
60
+ sx,
61
+ 0,
62
+ 0,
63
+ 0,
64
+ 0,
65
+ sy,
66
+ 0,
67
+ 0,
68
+ 0,
69
+ 0,
70
+ sz,
71
+ 0,
72
+ cx,
73
+ cy,
74
+ cz,
75
+ 1,
76
+ ];
77
+ }
78
+ const REL_AGGREGATES_RELATING = [4, 4, 3];
79
+ const REL_AGGREGATES_RELATED = [5, 4, 3];
80
+ const REL_CONTAINED_RELATED = [4, 4, 3];
81
+ const REL_CONTAINED_RELATING = [5, 4, 3];
82
+ const PRODUCT_PLACEMENT = [5, 5, 3];
83
+ const LOCAL_PLACEMENT_REL_TO = [0, 0, 1];
84
+ const LOCAL_PLACEMENT_RELATIVE = [1, 0, 1];
85
+ const PLACEMENT_LOCATION = [0, 0, 2];
86
+ const PLACEMENT_AXIS = [1, 1, 3];
87
+ const PLACEMENT_REF_DIRECTION = [2, 1, 3];
88
+ const STOREY_ELEVATION = [9, 9, 6];
89
+ const IDENTITY_PLACEMENT = [
90
+ 1,
91
+ 0,
92
+ 0,
93
+ 0,
94
+ 0,
95
+ 1,
96
+ 0,
97
+ 0,
98
+ 0,
99
+ 0,
100
+ 1,
101
+ 0,
102
+ 0,
103
+ 0,
104
+ 0,
105
+ 1,
106
+ ];
107
+ /** Column-major indices of a 4x4's translation component. */
108
+ const TRANSLATION = [12, 13, 14];
109
+ /** Column-major index of the local +Z axis' world Z component. */
110
+ const LOCAL_Z_WORLD_Z = 10;
111
+ /** Cross-product magnitude below which two unit axes are parallel. */
112
+ const MIN_AXIS_CROSS = 1e-9;
113
+ const SPACE_TYPES = new Set([
114
+ EntityTypesIfc.IFCSPACE,
115
+ EntityTypesIfc.IFCSPATIALZONE,
116
+ EntityTypesIfc.IFCEXTERNALSPATIALELEMENT,
117
+ ]);
118
+ const STOREY_TYPES = new Set([
119
+ EntityTypesIfc.IFCBUILDINGSTOREY,
120
+ ]);
121
+ /**
122
+ * Deepest spatial depth we still emit (never Spaces). Halfway down the
123
+ * tree, plus every BuildingStorey — floor plates are the useful cut.
124
+ *
125
+ * @param maxDepth Deepest spatial node (spaces included).
126
+ * @return {number} Inclusive depth cap for non-storey nodes.
127
+ */
128
+ export function spatialImposterDepthCap(maxDepth) {
129
+ return Math.max(0, Math.ceil(maxDepth / 2));
130
+ }
131
+ /**
132
+ * Whether this spatial node should become a box.
133
+ *
134
+ * @param depth Node depth from the project root.
135
+ * @param maxDepth Tree depth.
136
+ * @param typeID Schema type.
137
+ * @return {boolean} True to emit.
138
+ */
139
+ export function shouldEmitSpatialNode(depth, maxDepth, typeID) {
140
+ if (SPACE_TYPES.has(typeID)) {
141
+ return false;
142
+ }
143
+ if (STOREY_TYPES.has(typeID)) {
144
+ return true;
145
+ }
146
+ return depth <= spatialImposterDepthCap(maxDepth);
147
+ }
148
+ /**
149
+ * True when two AABBs are within `rel` of each other on every axis
150
+ * (used to drop Project/Site wrappers that match the Building).
151
+ *
152
+ * @param a First box.
153
+ * @param b Second box.
154
+ * @param rel Relative slack.
155
+ * @param minEdge Span floor, in the same source units as `a` and `b`.
156
+ * The walk always passes {@link minEdgeSourceUnits}' converted value;
157
+ * the default is the unconverted MIN_EDGE_M, which is only correct on a
158
+ * metre model — a caller that omits it on a millimetre one gets a
159
+ * one-millimetre floor, the very bug MIN_EDGE_M removed.
160
+ * @return {boolean} True if they match.
161
+ */
162
+ export function aabbMostlyEqual(a, b, rel = COLLAPSE_REL, minEdge = MIN_EDGE_M) {
163
+ for (let axis = 0; axis < 3; ++axis) {
164
+ const span = Math.max(a.max[axis] - a.min[axis], b.max[axis] - b.min[axis], minEdge);
165
+ const slack = span * rel;
166
+ if (Math.abs(a.min[axis] - b.min[axis]) > slack ||
167
+ Math.abs(a.max[axis] - b.max[axis]) > slack) {
168
+ return false;
169
+ }
170
+ }
171
+ return true;
172
+ }
173
+ /**
174
+ * Union of boxes; ignores undefined.
175
+ *
176
+ * @param boxes Boxes to join.
177
+ * @return {Aabb3 | undefined} The union.
178
+ */
179
+ export function unionAabb(...boxes) {
180
+ let min;
181
+ let max;
182
+ for (const box of boxes) {
183
+ if (box === void 0) {
184
+ continue;
185
+ }
186
+ if (min === void 0 || max === void 0) {
187
+ min = [box.min[0], box.min[1], box.min[2]];
188
+ max = [box.max[0], box.max[1], box.max[2]];
189
+ continue;
190
+ }
191
+ for (let axis = 0; axis < 3; ++axis) {
192
+ if (box.min[axis] < min[axis]) {
193
+ min[axis] = box.min[axis];
194
+ }
195
+ if (box.max[axis] > max[axis]) {
196
+ max[axis] = box.max[axis];
197
+ }
198
+ }
199
+ }
200
+ return min === void 0 || max === void 0 ? void 0 : { min, max };
201
+ }
202
+ function padAabb_(box, minEdge) {
203
+ const min = [box.min[0], box.min[1], box.min[2]];
204
+ const max = [box.max[0], box.max[1], box.max[2]];
205
+ for (let axis = 0; axis < 3; ++axis) {
206
+ if (max[axis] - min[axis] < minEdge) {
207
+ const mid = (min[axis] + max[axis]) * 0.5;
208
+ min[axis] = mid - minEdge * 0.5;
209
+ max[axis] = mid + minEdge * 0.5;
210
+ }
211
+ }
212
+ return { min, max };
213
+ }
214
+ function pointAabb_(x, y, z) {
215
+ return { min: [x, y, z], max: [x, y, z] };
216
+ }
217
+ async function ensure_(model, localID) {
218
+ await model.ensureResidentByLocalID(localID);
219
+ }
220
+ /**
221
+ * @param v A vector.
222
+ * @return {[number, number, number] | undefined} `v` scaled to unit
223
+ * length, or undefined when it has none (zero / non-finite input).
224
+ */
225
+ function normalize3_(v) {
226
+ const length = Math.hypot(v[0], v[1], v[2]);
227
+ return Number.isFinite(length) && length > 0 ?
228
+ [v[0] / length, v[1] / length, v[2] / length] : void 0;
229
+ }
230
+ /**
231
+ * @param a Left vector.
232
+ * @param b Right vector.
233
+ * @return {[number, number, number]} `a` x `b`.
234
+ */
235
+ function cross3_(a, b) {
236
+ return [
237
+ a[1] * b[2] - a[2] * b[1],
238
+ a[2] * b[0] - a[0] * b[2],
239
+ a[0] * b[1] - a[1] * b[0],
240
+ ];
241
+ }
242
+ /**
243
+ * The `DirectionRatios` of an IfcDirection referenced from `field` of
244
+ * `entity`, or undefined when the field is absent.
245
+ *
246
+ * @param model The model.
247
+ * @param entity The referring entity.
248
+ * @param field The (index, min, max) field triple.
249
+ * @return {Promise<[number, number, number] | undefined>} The ratios.
250
+ */
251
+ async function directionRatios_(model, entity, field) {
252
+ const directionID = entity.extractReferenceLocalID(field[0], field[1], field[2], true);
253
+ if (directionID === null) {
254
+ return;
255
+ }
256
+ await ensure_(model, directionID);
257
+ const direction = model.getElementByLocalID(directionID);
258
+ const ratios = direction?.DirectionRatios;
259
+ if (ratios === void 0 || ratios.length < 2) {
260
+ return;
261
+ }
262
+ return [ratios[0], ratios[1], ratios[2] ?? 0];
263
+ }
264
+ /**
265
+ * The local +X reference to use when a placement states an `Axis` but no
266
+ * `RefDirection`: world +X, unless that is (nearly) parallel to the
267
+ * stated axis, in which case world +Y.
268
+ *
269
+ * IFC's own `IfcFirstProjAxis` makes exactly this substitution, and
270
+ * without it a lie-down placement — `Axis = (1,0,0)`, RefDirection
271
+ * omitted, which is how a wall-mounted or rotated-into-plan element is
272
+ * commonly written — has its Axis cross the defaulted (1,0,0) to zero
273
+ * and falls all the way back to the world frame, discarding a perfectly
274
+ * well-formed rotation. conway-geom divides by that zero cross product
275
+ * and returns NaN, so matching the C++ settles nothing here; keeping the
276
+ * stated Axis is strictly better than dropping it.
277
+ *
278
+ * @param zAxis The normalized local +Z.
279
+ * @return {[number, number, number]} A reference not parallel to it.
280
+ */
281
+ function defaultRefDirection_(zAxis) {
282
+ // |cross(zAxis, worldX)| — world +X stays the default (which is what
283
+ // the IFC default says, and what the overwhelmingly common
284
+ // zAxis = (0,0,1) needs) until it degenerates.
285
+ return Math.hypot(zAxis[1], zAxis[2]) > MIN_AXIS_CROSS ?
286
+ [1, 0, 0] : [0, 1, 0];
287
+ }
288
+ /**
289
+ * The 4x4 an IfcAxis2Placement denotes: Location as the translation,
290
+ * and — on the 3D form only — Axis as local +Z and RefDirection as local
291
+ * +X, both defaulting to the world axes when absent per the IFC
292
+ * defaults.
293
+ *
294
+ * The 2D form has to be handled, not assumed away.
295
+ * `IfcLocalPlacement.RelativePlacement` is
296
+ * `IfcAxis2Placement2D | IfcAxis2Placement3D`, and the two disagree on
297
+ * every field past Location: offset 1 is `Axis` on the 3D entity but
298
+ * `RefDirection` on the 2D one, and offset 2 does not exist there at
299
+ * all, so reading it throws "too few fields in record". That throw would
300
+ * escape to {@link placementTransform_}'s catch, which memoizes
301
+ * `undefined` for the placement — and a memoized undefined is not merely
302
+ * a lost sample: every descendant placement then takes the
303
+ * no-parent branch and is composed in LOCAL coordinates, silently
304
+ * reparenting a whole subtree to the origin. So the direction fields are
305
+ * read only once the type is confirmed 3D, which also restores the
306
+ * pre-#517 behaviour for 2D links (translation only).
307
+ *
308
+ * Deliberately the same construction as conway-geom's
309
+ * `GetAxis2Placement3D` (ConwayGeometryProcessor.cpp) — normalize the
310
+ * two references, `y = normalize(z x x)`, then re-derive
311
+ * `x = normalize(y x z)` so a non-perpendicular RefDirection is
312
+ * projected rather than skewing the frame. It has to match: the plates
313
+ * are previewing meshes the durable pipeline places with that function,
314
+ * and a second convention here (Gram-Schmidt on x, say) would rotate
315
+ * them apart on any file whose RefDirection is off-perpendicular.
316
+ *
317
+ * One deviation, on degenerate input: where the C++ divides by a zero
318
+ * cross product and yields NaN, this falls back to the identity axes. A
319
+ * NaN transform reaching Share is an invisible or scene-destroying box,
320
+ * and the imposter path's standing rule is that a preview must never
321
+ * break open.
322
+ *
323
+ * @param model The model.
324
+ * @param axisLocalID The IfcAxis2Placement3D's local ID.
325
+ * @return {Promise<Mat4>} The placement, identity if unreadable.
326
+ */
327
+ async function axisPlacementMatrix_(model, axisLocalID) {
328
+ await ensure_(model, axisLocalID);
329
+ const placement = model.getElementByLocalID(axisLocalID);
330
+ if (placement === void 0) {
331
+ return IDENTITY_PLACEMENT.slice();
332
+ }
333
+ let translation = [0, 0, 0];
334
+ const locationID = placement.extractReferenceLocalID(PLACEMENT_LOCATION[0], PLACEMENT_LOCATION[1], PLACEMENT_LOCATION[2], false);
335
+ if (locationID !== null) {
336
+ await ensure_(model, locationID);
337
+ const point = model.getElementByLocalID(locationID);
338
+ const coords = point?.Coordinates;
339
+ if (coords !== void 0 && coords.length >= 2) {
340
+ translation = [coords[0], coords[1], coords[2] ?? 0];
341
+ }
342
+ }
343
+ const is3D = model.typeIDOf(axisLocalID) === EntityTypesIfc.IFCAXIS2PLACEMENT3D;
344
+ const axisRatios = is3D ?
345
+ await directionRatios_(model, placement, PLACEMENT_AXIS) : void 0;
346
+ const refRatios = is3D ?
347
+ await directionRatios_(model, placement, PLACEMENT_REF_DIRECTION) : void 0;
348
+ let zAxis = (axisRatios !== void 0 ? normalize3_(axisRatios) : void 0) ?? [0, 0, 1];
349
+ let refAxis = (refRatios !== void 0 ? normalize3_(refRatios) : void 0) ??
350
+ defaultRefDirection_(zAxis);
351
+ let yAxis = normalize3_(cross3_(zAxis, refAxis));
352
+ if (yAxis === void 0) {
353
+ // Axis and RefDirection are parallel (or garbage): nothing here
354
+ // determines a frame, so keep the translation and drop back to world
355
+ // axes. Only reachable with an explicit RefDirection now — an Axis
356
+ // on its own gets a perpendicular default instead of being thrown
357
+ // away with it (see defaultRefDirection_).
358
+ zAxis = [0, 0, 1];
359
+ refAxis = [1, 0, 0];
360
+ yAxis = [0, 1, 0];
361
+ }
362
+ const xAxis = normalize3_(cross3_(yAxis, zAxis)) ?? refAxis;
363
+ return [
364
+ xAxis[0],
365
+ xAxis[1],
366
+ xAxis[2],
367
+ 0,
368
+ yAxis[0],
369
+ yAxis[1],
370
+ yAxis[2],
371
+ 0,
372
+ zAxis[0],
373
+ zAxis[1],
374
+ zAxis[2],
375
+ 0,
376
+ translation[0],
377
+ translation[1],
378
+ translation[2],
379
+ 1,
380
+ ];
381
+ }
382
+ /**
383
+ * The composed world transform of an IfcLocalPlacement chain: every
384
+ * link's IfcAxis2Placement3D built out in full and multiplied
385
+ * `parent * local`, which is exactly conway-geom's `GetLocalPlacement`.
386
+ *
387
+ * Rotation used to be dropped here — the chain summed Location
388
+ * translations and nothing else (conway#514's shortcut). That is correct
389
+ * only while every ancestor placement is axis-aligned, and Revit
390
+ * routinely rotates the site or building placement to true north, so on
391
+ * a real export every sampled origin landed unrotated in raw model space
392
+ * while the durable meshes beside it did not: the plate cloud read as a
393
+ * rotated ghost of the building (conway#517).
394
+ *
395
+ * @param model The model.
396
+ * @param placementLocalID IfcObjectPlacement local ID.
397
+ * @param cache Memo of composed transforms, keyed by placement local ID.
398
+ * A placement re-entered while it is being composed (a cyclic RelativeTo)
399
+ * reads back undefined and stops the recursion.
400
+ * @return {Promise<Mat4 | undefined>} The world transform.
401
+ */
402
+ async function placementTransform_(model, placementLocalID, cache) {
403
+ if (cache.has(placementLocalID)) {
404
+ return cache.get(placementLocalID);
405
+ }
406
+ cache.set(placementLocalID, void 0);
407
+ try {
408
+ await ensure_(model, placementLocalID);
409
+ const typeID = model.typeIDOf(placementLocalID);
410
+ if (typeID !== EntityTypesIfc.IFCLOCALPLACEMENT) {
411
+ return;
412
+ }
413
+ const entity = model.getElementByLocalID(placementLocalID);
414
+ if (entity === void 0) {
415
+ return;
416
+ }
417
+ const relativeID = entity.extractReferenceLocalID(LOCAL_PLACEMENT_RELATIVE[0], LOCAL_PLACEMENT_RELATIVE[1], LOCAL_PLACEMENT_RELATIVE[2], false);
418
+ const local = relativeID !== null ?
419
+ await axisPlacementMatrix_(model, relativeID) :
420
+ IDENTITY_PLACEMENT.slice();
421
+ const parentID = entity.extractReferenceLocalID(LOCAL_PLACEMENT_REL_TO[0], LOCAL_PLACEMENT_REL_TO[1], LOCAL_PLACEMENT_REL_TO[2], true);
422
+ if (parentID === null) {
423
+ cache.set(placementLocalID, local);
424
+ return local;
425
+ }
426
+ const parent = await placementTransform_(model, parentID, cache);
427
+ const world = parent === void 0 ? local : mat4MultiplyF64(parent, local);
428
+ cache.set(placementLocalID, world);
429
+ return world;
430
+ }
431
+ catch {
432
+ return;
433
+ }
434
+ }
435
+ /**
436
+ * @param transform A composed world transform.
437
+ * @return {[number, number, number]} Its translation column, i.e. where
438
+ * the placement's own origin lands in world space.
439
+ */
440
+ function originOf_(transform) {
441
+ return [
442
+ transform[TRANSLATION[0]],
443
+ transform[TRANSLATION[1]],
444
+ transform[TRANSLATION[2]],
445
+ ];
446
+ }
447
+ async function productTransform_(model, localID, cache) {
448
+ await ensure_(model, localID);
449
+ const entity = model.getElementByLocalID(localID);
450
+ if (entity === void 0) {
451
+ return;
452
+ }
453
+ const placementID = entity.extractReferenceLocalID(PRODUCT_PLACEMENT[0], PRODUCT_PLACEMENT[1], PRODUCT_PLACEMENT[2], true);
454
+ if (placementID === null) {
455
+ return;
456
+ }
457
+ return placementTransform_(model, placementID, cache);
458
+ }
459
+ /**
460
+ * Walk the IFC spatial tree and emit AABB imposters for Share.
461
+ *
462
+ * Project → Site → Building → Storey → Space. Spaces are never
463
+ * drawn (too dense). Everything else is drawn down to half the tree
464
+ * depth, and storeys are always drawn when present. Nested wrappers
465
+ * whose box matches their only child are collapsed.
466
+ *
467
+ * **Frame.** Every box is measured in raw IFC source units and Z-up,
468
+ * because that is what the placement chains this walk reads carry. The
469
+ * emitted `flatTransformation` is `C * M_box`, where `M_box` is that raw
470
+ * box ({@link aabbBoxMatrix}) and `C` is the durable coordination frame —
471
+ * metres, Y-up, recentred per the quantized model-zero policy. So the
472
+ * plates land exactly where the real meshes do, which they did not
473
+ * before (raw units, a locally baked axis flip, no coordination: on a
474
+ * millimetre model the plates came out 1000x too big, and a
475
+ * georeferenced one put them a site-offset away — conway#515).
476
+ * `payload.aabb` stays in raw IFC space as the consumer's reference.
477
+ *
478
+ * **Call it after `prepareDemandExtraction()`, never before.** The frame
479
+ * needs `getLinearScalingFactor()`, which reads 1 until the extraction
480
+ * maps — and with them the unit assignment — are prepared, and
481
+ * `extractLinearScalingFactor()` is `*=`-accumulating, so it cannot be
482
+ * pulled forward on its own without squaring the unit prefix. The cost
483
+ * is latency on the model's first visible feedback: on the resident path
484
+ * the plates now follow the prep instead of preceding it, and on the
485
+ * store-backed path they also follow an awaited
486
+ * `ensureResidentForDemandPrep()`, so on a large model they land
487
+ * measurably later than they used to. Accepted deliberately — a plate at
488
+ * 1000x the model's scale is worse than a plate slightly late.
489
+ *
490
+ * **The latched frame is adopted, not guaranteed.** The deferred durable
491
+ * pump re-derives its own when the composed placement overruns
492
+ * LARGE_COORDINATE_BUDGET_M (`validatePreviewFrame`, Share#1634). Plates
493
+ * emitted here are not re-emitted, so on that path they keep the
494
+ * rejected frame and sit a site-offset from the durable meshes — the
495
+ * conway#515 symptom again, now confined to a case the preview meshes
496
+ * beside them share, and to scenery Share tears down at load end.
497
+ * Re-emitting on rejection is the fix if it ever shows up live.
498
+ *
499
+ * @param model Parsed IFC model (windowed or resident).
500
+ * @param onMesh Preview consumer.
501
+ * @param coordinationMatrix The frame already latched by the preview
502
+ * channel (the one the durable pump adopts). When it is `undefined` — no
503
+ * preview instance was ever captured — the walk derives the same frame
504
+ * from an identity placement anchored on the root spatial box's centre.
505
+ * That is best-effort by construction: below LARGE_COORDINATE_BUDGET_M
506
+ * the policy recentres nothing, so it agrees with the durable derivation
507
+ * exactly; above it, both snap to COORDINATION_SNAP_M and agree as long
508
+ * as the spatial root and the first geometry share a grid cell.
509
+ * @param linearScalingFactor Source units -> metres, from
510
+ * `IfcGeometryExtraction.getLinearScalingFactor()`. Only consulted for
511
+ * the fallback derivation and the minimum-edge padding; when
512
+ * `coordinationMatrix` is supplied its own scaling is already baked in.
513
+ * @return {Promise<number>} Boxes emitted.
514
+ */
515
+ export async function emitSpatialStructureImposters(model, onMesh, coordinationMatrix, linearScalingFactor = 1) {
516
+ const minEdge = minEdgeSourceUnits(linearScalingFactor);
517
+ const nodes = new Map();
518
+ const childrenOf = new Map();
519
+ const parentOf = new Map();
520
+ const addNode = (localID, expressID, typeID) => {
521
+ if (!nodes.has(localID)) {
522
+ nodes.set(localID, {
523
+ localID,
524
+ expressID,
525
+ typeID,
526
+ depth: 0,
527
+ children: [],
528
+ });
529
+ }
530
+ };
531
+ const spatialTypes = [
532
+ IfcProject,
533
+ IfcSite,
534
+ IfcBuilding,
535
+ IfcBuildingStorey,
536
+ IfcSpace,
537
+ ];
538
+ for (const type of spatialTypes) {
539
+ for (const expressID of model.expressIDsOfTypes(type)) {
540
+ const localID = model.resolveExpressID(expressID);
541
+ if (localID === void 0) {
542
+ continue;
543
+ }
544
+ addNode(localID, expressID, model.typeIDOf(localID) ?? -1);
545
+ }
546
+ }
547
+ if (nodes.size === 0) {
548
+ return 0;
549
+ }
550
+ for (const expressID of model.expressIDsOfTypes(IfcRelAggregates)) {
551
+ const relLocalID = model.resolveExpressID(expressID);
552
+ if (relLocalID === void 0) {
553
+ continue;
554
+ }
555
+ try {
556
+ await ensure_(model, relLocalID);
557
+ const rel = model.getElementByLocalID(relLocalID);
558
+ if (rel === void 0) {
559
+ continue;
560
+ }
561
+ const relatingID = rel.extractReferenceLocalID(REL_AGGREGATES_RELATING[0], REL_AGGREGATES_RELATING[1], REL_AGGREGATES_RELATING[2], false);
562
+ if (relatingID === null || !nodes.has(relatingID)) {
563
+ continue;
564
+ }
565
+ rel.forEachReferenceInField(REL_AGGREGATES_RELATED[0], REL_AGGREGATES_RELATED[1], REL_AGGREGATES_RELATED[2], (relatedExpressID) => {
566
+ if (relatedExpressID === void 0) {
567
+ return true;
568
+ }
569
+ const relatedID = model.resolveExpressID(relatedExpressID);
570
+ if (relatedID === void 0 || !nodes.has(relatedID)) {
571
+ return true;
572
+ }
573
+ const list = childrenOf.get(relatingID) ?? [];
574
+ list.push(relatedID);
575
+ childrenOf.set(relatingID, list);
576
+ parentOf.set(relatedID, relatingID);
577
+ return true;
578
+ });
579
+ }
580
+ catch {
581
+ // One bad relationship must not kill the preview.
582
+ }
583
+ }
584
+ const contained = new Map();
585
+ for (const expressID of model.expressIDsOfTypes(IfcRelContainedInSpatialStructure)) {
586
+ const relLocalID = model.resolveExpressID(expressID);
587
+ if (relLocalID === void 0) {
588
+ continue;
589
+ }
590
+ try {
591
+ await ensure_(model, relLocalID);
592
+ const rel = model.getElementByLocalID(relLocalID);
593
+ if (rel === void 0) {
594
+ continue;
595
+ }
596
+ const structureID = rel.extractReferenceLocalID(REL_CONTAINED_RELATING[0], REL_CONTAINED_RELATING[1], REL_CONTAINED_RELATING[2], false);
597
+ if (structureID === null || !nodes.has(structureID)) {
598
+ continue;
599
+ }
600
+ const products = [];
601
+ rel.forEachReferenceInField(REL_CONTAINED_RELATED[0], REL_CONTAINED_RELATED[1], REL_CONTAINED_RELATED[2], (productExpressID) => {
602
+ if (productExpressID === void 0) {
603
+ return true;
604
+ }
605
+ const productID = model.resolveExpressID(productExpressID);
606
+ if (productID !== void 0) {
607
+ products.push(productID);
608
+ }
609
+ return true;
610
+ });
611
+ if (products.length > 0) {
612
+ const existing = contained.get(structureID) ?? [];
613
+ existing.push(...products);
614
+ contained.set(structureID, existing);
615
+ }
616
+ }
617
+ catch {
618
+ // Preview must never break open.
619
+ }
620
+ }
621
+ for (const [parent, kids] of childrenOf) {
622
+ const node = nodes.get(parent);
623
+ if (node !== void 0) {
624
+ node.children = kids;
625
+ }
626
+ }
627
+ const roots = [];
628
+ for (const localID of nodes.keys()) {
629
+ if (!parentOf.has(localID)) {
630
+ roots.push(localID);
631
+ }
632
+ }
633
+ if (roots.length === 0) {
634
+ roots.push(...nodes.keys());
635
+ }
636
+ const queue = roots.slice();
637
+ const queued = new Set(roots);
638
+ let maxDepth = 0;
639
+ while (queue.length > 0) {
640
+ const id = queue.shift();
641
+ const node = nodes.get(id);
642
+ if (node === void 0) {
643
+ continue;
644
+ }
645
+ const parentID = parentOf.get(id);
646
+ node.depth = parentID === void 0 ? 0 : (nodes.get(parentID)?.depth ?? 0) + 1;
647
+ maxDepth = Math.max(maxDepth, node.depth);
648
+ for (const childID of node.children) {
649
+ if (queued.has(childID)) {
650
+ continue;
651
+ }
652
+ queued.add(childID);
653
+ queue.push(childID);
654
+ }
655
+ }
656
+ const placementCache = new Map();
657
+ for (const node of nodes.values()) {
658
+ try {
659
+ // IfcProject is an IfcContext, not an IfcProduct — field 5 is
660
+ // not ObjectPlacement.
661
+ if (node.typeID !== EntityTypesIfc.IFCPROJECT) {
662
+ node.placement =
663
+ await productTransform_(model, node.localID, placementCache);
664
+ node.origin = node.placement !== void 0 ?
665
+ originOf_(node.placement) : void 0;
666
+ }
667
+ if (STOREY_TYPES.has(node.typeID)) {
668
+ await ensure_(model, node.localID);
669
+ const entity = model.getElementByLocalID(node.localID);
670
+ if (entity !== void 0) {
671
+ const elevation = entity.extractNumber(STOREY_ELEVATION[0], STOREY_ELEVATION[1], STOREY_ELEVATION[2], true);
672
+ if (elevation !== null) {
673
+ node.elevation = elevation;
674
+ }
675
+ }
676
+ }
677
+ }
678
+ catch {
679
+ // Keep walking.
680
+ }
681
+ }
682
+ const productOrigins = new Map();
683
+ for (const [structureID, products] of contained) {
684
+ const stride = Math.max(1, Math.floor(products.length / PRODUCT_SAMPLE));
685
+ const origins = [];
686
+ for (let i = 0; i < products.length && origins.length < PRODUCT_SAMPLE; i += stride) {
687
+ try {
688
+ const transform = await productTransform_(model, products[i], placementCache);
689
+ if (transform !== void 0) {
690
+ origins.push(originOf_(transform));
691
+ }
692
+ }
693
+ catch {
694
+ // Skip one product.
695
+ }
696
+ }
697
+ if (origins.length > 0) {
698
+ productOrigins.set(structureID, origins);
699
+ }
700
+ }
701
+ const storeysByParent = new Map();
702
+ for (const node of nodes.values()) {
703
+ if (!STOREY_TYPES.has(node.typeID) || node.elevation === void 0) {
704
+ continue;
705
+ }
706
+ const parentKey = parentOf.get(node.localID) ?? "root";
707
+ const group = storeysByParent.get(parentKey) ?? [];
708
+ group.push(node);
709
+ storeysByParent.set(parentKey, group);
710
+ }
711
+ const storeyHeight = new Map();
712
+ for (const group of storeysByParent.values()) {
713
+ group.sort((a, b) => (a.elevation ?? 0) - (b.elevation ?? 0));
714
+ for (let i = 0; i < group.length; ++i) {
715
+ const here = group[i].elevation ?? 0;
716
+ const next = group[i + 1]?.elevation;
717
+ let height = next !== void 0 && next > here ? next - here : void 0;
718
+ if (height === void 0) {
719
+ const samples = productOrigins.get(group[i].localID);
720
+ if (samples !== void 0 && samples.length > 0) {
721
+ let minZ = samples[0][2];
722
+ let maxZ = samples[0][2];
723
+ for (const origin of samples) {
724
+ if (origin[2] < minZ) {
725
+ minZ = origin[2];
726
+ }
727
+ if (origin[2] > maxZ) {
728
+ maxZ = origin[2];
729
+ }
730
+ }
731
+ height = Math.max(maxZ - minZ, minEdge);
732
+ }
733
+ else {
734
+ height = minEdge;
735
+ }
736
+ }
737
+ storeyHeight.set(group[i].localID, height);
738
+ }
739
+ }
740
+ const aabbOf = (localID) => {
741
+ const node = nodes.get(localID);
742
+ if (node === void 0) {
743
+ return;
744
+ }
745
+ if (node.aabb !== void 0) {
746
+ return node.aabb;
747
+ }
748
+ if (node.computing === true) {
749
+ return;
750
+ }
751
+ node.computing = true;
752
+ try {
753
+ const pieces = [];
754
+ // Where a storey states an Elevation, its floor is the building
755
+ // datum plus that — see the banding block below, which this has to
756
+ // agree with. Under the ordinary authoring it equals the storey's
757
+ // own placement Z; the two only part on files that leave every
758
+ // storey placement on the datum.
759
+ const storeyZ0 = STOREY_TYPES.has(node.typeID) && node.elevation !== void 0 ?
760
+ storeyFloorZ_(localID, node.elevation, nodes, parentOf) : void 0;
761
+ if (node.origin !== void 0) {
762
+ // The placement point contributes this node's XY footprint. For
763
+ // a storey with an Elevation its Z is the DATUM, not its
764
+ // contents, so seeding the band with it would drag every
765
+ // storey's floor down onto the building origin — substitute the
766
+ // banded floor. Non-storeys keep their own Z.
767
+ pieces.push(pointAabb_(node.origin[0], node.origin[1], storeyZ0 ?? node.origin[2]));
768
+ }
769
+ const samples = productOrigins.get(localID);
770
+ if (samples !== void 0) {
771
+ for (const origin of samples) {
772
+ pieces.push(pointAabb_(...origin));
773
+ }
774
+ }
775
+ for (const childID of node.children) {
776
+ const childBox = aabbOf(childID);
777
+ if (childBox !== void 0) {
778
+ pieces.push(childBox);
779
+ }
780
+ }
781
+ let box = unionAabb(...pieces);
782
+ if (box !== void 0 && STOREY_TYPES.has(node.typeID)) {
783
+ const height = storeyHeight.get(localID);
784
+ // IfcBuildingStorey.Elevation is measured from the BUILDING
785
+ // datum, not from world zero, so a building placed at z=100
786
+ // drew its plates 100 units low. Add the datum back rather than
787
+ // letting the storey's own placement Z replace elevation
788
+ // outright: the two agree under the ordinary authoring (the
789
+ // storey placement is offset from the building BY the
790
+ // elevation), but a file that parks every storey placement on
791
+ // the building datum and carries the height only in Elevation
792
+ // would then collapse all of its plates onto one Z. Storey
793
+ // HEIGHTS stay on elevation deltas — datum-invariant either way,
794
+ // though on a datum tilted out of plumb they over-thicken the
795
+ // plate by 1/cos(tilt) (the floor Z is tilt-correct; see
796
+ // storeyFloorZ_). Left alone: the sample-derived height fallback
797
+ // is already a world-Z span, so one scale cannot serve both, and
798
+ // a tilted building has no horizontal storeys to plate anyway.
799
+ const z0 = storeyZ0 ?? node.origin?.[2] ?? box.min[2];
800
+ const z1 = height !== void 0 ? z0 + height : box.max[2];
801
+ box = {
802
+ min: [box.min[0], box.min[1], Math.min(box.min[2], z0)],
803
+ max: [box.max[0], box.max[1], Math.max(box.max[2], z1)],
804
+ };
805
+ }
806
+ node.aabb = box;
807
+ return box;
808
+ }
809
+ finally {
810
+ node.computing = false;
811
+ }
812
+ };
813
+ for (const root of roots) {
814
+ aabbOf(root);
815
+ }
816
+ const emitIDs = [];
817
+ for (const node of nodes.values()) {
818
+ if (!shouldEmitSpatialNode(node.depth, maxDepth, node.typeID)) {
819
+ continue;
820
+ }
821
+ if (node.aabb === void 0) {
822
+ continue;
823
+ }
824
+ if (node.children.length === 1) {
825
+ const child = nodes.get(node.children[0]);
826
+ if (child?.aabb !== void 0 &&
827
+ aabbMostlyEqual(node.aabb, child.aabb, COLLAPSE_REL, minEdge) &&
828
+ shouldEmitSpatialNode(child.depth, maxDepth, child.typeID)) {
829
+ continue;
830
+ }
831
+ }
832
+ emitIDs.push(node.localID);
833
+ }
834
+ const coordination = coordinationMatrix ??
835
+ fallbackCoordination_(roots, nodes, linearScalingFactor);
836
+ let emitted = 0;
837
+ for (const localID of emitIDs) {
838
+ const node = nodes.get(localID);
839
+ if (node?.aabb === void 0) {
840
+ continue;
841
+ }
842
+ const aabb = padAabb_(node.aabb, minEdge);
843
+ try {
844
+ onMesh({
845
+ expressID: node.expressID,
846
+ geometryExpressID: -1,
847
+ color: SPATIAL_IMPOSTER_COLOR,
848
+ flatTransformation: mat4MultiplyF64(coordination, aabbBoxMatrix(aabb)),
849
+ aabb,
850
+ });
851
+ ++emitted;
852
+ }
853
+ catch {
854
+ // Preview must never break open.
855
+ }
856
+ }
857
+ return emitted;
858
+ }
859
+ /**
860
+ * World Z of an IfcBuildingStorey's floor: `Elevation` measured up the
861
+ * datum's own +Z from the datum's world origin, where the datum is the
862
+ * nearest ancestor with a resolved placement (the IfcBuilding in a
863
+ * well-formed file).
864
+ *
865
+ * Elevation is a length along the datum's Z, not along world Z, so the
866
+ * datum's whole transform is needed and not just its height — on a
867
+ * building tilted out of plumb the two differ by `cos(tilt)`. Only the
868
+ * Z component is taken, because that is all the banding uses: the plate
869
+ * keeps the XY footprint its placement and contained samples give it.
870
+ * A tilted building's storeys are not horizontal anyway, so the
871
+ * axis-aligned plate is an approximation there by construction — this
872
+ * just stops it being an approximation on the *height* as well.
873
+ *
874
+ * Falls back to a world-relative Elevation (datum at zero, +Z up) when
875
+ * nothing up the chain resolved a placement: the pre-fix behaviour, and
876
+ * the only thing left to anchor on.
877
+ *
878
+ * @param localID The storey.
879
+ * @param elevation The storey's `Elevation`, in source units.
880
+ * @param nodes All walked nodes.
881
+ * @param parentOf Child -> parent.
882
+ * @return {number} The floor's world Z.
883
+ */
884
+ function storeyFloorZ_(localID, elevation, nodes, parentOf) {
885
+ let id = parentOf.get(localID);
886
+ // Bounded rather than while-truthy: parentOf is built by a BFS and so
887
+ // should be acyclic, but this module treats the aggregate graph as
888
+ // untrusted everywhere else too (see `computing` in aabbOf).
889
+ for (let hops = 0; id !== void 0 && hops <= nodes.size; ++hops) {
890
+ const placement = nodes.get(id)?.placement;
891
+ if (placement !== void 0) {
892
+ return placement[TRANSLATION[2]] +
893
+ elevation * placement[LOCAL_Z_WORLD_Z];
894
+ }
895
+ id = parentOf.get(id);
896
+ }
897
+ return elevation;
898
+ }
899
+ /**
900
+ * The durable coordination frame, derived the way the preview channels
901
+ * derive theirs but anchored on the spatial root's centre instead of the
902
+ * first extracted geometry's first point: identity placement + that
903
+ * anchor is exactly `deriveCoordinationF64`'s input contract, so the
904
+ * quantized model-zero policy (no recentre inside
905
+ * LARGE_COORDINATE_BUDGET_M, snap to COORDINATION_SNAP_M beyond) applies
906
+ * unchanged. Used only when no preview instance latched a frame first.
907
+ *
908
+ * Best-effort by construction, and the failure has two shapes. Beyond
909
+ * LARGE_COORDINATE_BUDGET_M both sides snap to COORDINATION_SNAP_M and
910
+ * agree as long as the spatial root and the first geometry share a grid
911
+ * cell. Worse: the anchor comes from `placementTransform_`, which reads
912
+ * IfcLocalPlacement chains and never looks at geometry — so a file that
913
+ * keeps identity placements and bakes absolute coordinates into the
914
+ * vertices (civil/GIS exports do this) has a root box near zero and gets
915
+ * no recentre at all, while the durable walk anchors tens of km out and
916
+ * does. That is a full site-offset, not a one-cell disagreement. Whenever
917
+ * the preview channel latched a frame we use it instead, precisely
918
+ * because it was derived from real geometry.
919
+ *
920
+ * @param roots Root spatial node local IDs.
921
+ * @param nodes The walked spatial nodes.
922
+ * @param linearScalingFactor Source units -> metres.
923
+ * @return {number[]} The coordination matrix.
924
+ */
925
+ function fallbackCoordination_(roots, nodes, linearScalingFactor) {
926
+ const rootBox = unionAabb(...roots.map((id) => nodes.get(id)?.aabb));
927
+ const anchor = rootBox !== void 0 ? {
928
+ x: (rootBox.min[0] + rootBox.max[0]) * 0.5,
929
+ y: (rootBox.min[1] + rootBox.max[1]) * 0.5,
930
+ z: (rootBox.min[2] + rootBox.max[2]) * 0.5,
931
+ } : { x: 0, y: 0, z: 0 };
932
+ return deriveCoordinationF64(void 0, anchor, NORMALIZE_MAT_F64, sanitizeScale_(linearScalingFactor));
933
+ }