@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
@@ -1,21 +1,65 @@
1
1
  import { ConwayGeometry, } from "../../index.js";
2
+ import IfcStepModel from "../../ifc/ifc_step_model.js";
3
+ import { WindowedStepBufferProvider } from "../../step/step_buffer_provider.js";
2
4
  import * as glmatrix from "gl-matrix";
5
+ import { IDENTITY_MAT4, LARGE_COORDINATE_BUDGET_M, TRANSLATION_X, TRANSLATION_Y, TRANSLATION_Z, composeTransformF64, deriveCoordinationF64 } from "./coordination_f64.js";
3
6
  import { IfcProperties } from "./ifc_properties.js";
4
7
  import Logger from "../../logging/logger.js";
5
8
  import { ProgressTracker } from "../../core/progress.js";
6
9
  import { formatModelLine } from "../../core/progress_log.js";
10
+ import { wasmHeapView, } from "../../core/wasm_heap.js";
7
11
  import { extractModelInfo } from "../../loaders/loading_utilities.js";
8
12
  import IfcStepParser from "../../ifc/ifc_step_parser.js";
9
13
  import ParsingBuffer from "../../parsing/parsing_buffer.js";
14
+ import { BufferByteSource, StoreByteSource } from "../../step/parsing/byte_source.js";
15
+ import { buildIndexStreamingAsync, } from "../../step/parsing/streaming_index_builder.js";
16
+ import { ColumnarIndexSink } from "../../step/parsing/columnar_index.js";
17
+ import { StorePreviewChannel } from "./store_preview_channel.js";
18
+ import { ifcPreviewAdapter, StreamedPreviewChannel, } from "./streamed_preview_channel.js";
19
+ import { emitSpatialStructureImposters } from "./spatial_imposter.js";
10
20
  import { ExtractResult } from "../../index.js";
11
21
  import { IfcGeometryExtraction } from "../../ifc/ifc_geometry_extraction.js";
12
22
  import { ParseResult } from "../../index.js";
23
+ import { releaseScratchParsingBuffer } from "../../step/parsing/step_deserialization_functions.js";
13
24
  import Memory from "../../memory/memory.js";
14
25
  import { FromRawLineData } from "./ifc2x4_helper.js";
15
26
  import { shimIfcEntityMap, shimIfcEntityReverseMap } from "./shim_schema_mapping.js";
16
27
  import { EntityTypesIfcCount } from "../../ifc/ifc4_gen/entity_types_ifc.gen.js";
17
- import { IfcProduct, IfcRoot } from "../../ifc/ifc4_gen/index.js";
28
+ import { IfcProduct, IfcRelAggregates, IfcRoot } from "../../ifc/ifc4_gen/index.js";
18
29
  import { CanonicalMeshType } from "../../index.js";
30
+ // Batch size used when a whole-model consumer (streamAllMeshes) drains
31
+ // a deferred model's remaining products synchronously.
32
+ const DEFERRED_DRAIN_BATCH = 256;
33
+ /* Moving-window size for the streamed columnar parse (matches the
34
+ * ifc_stream_open default; the window bounds parse-time scratch, not
35
+ * the source buffer, which the model keeps resident here). */
36
+ // eslint-disable-next-line no-magic-numbers
37
+ const STREAMED_PARSE_POOL_BYTES = 1024 * 1024;
38
+ /* Store-backed open pays an await per window slide (`File.slice`).
39
+ * 1 MiB on an 860 MB IFC is ~1700 trips; 16 MiB is ~100. The window
40
+ * is scratch — it does not stay resident after parse. */
41
+ // eslint-disable-next-line no-magic-numbers
42
+ const STORE_PARSE_POOL_BYTES = 16 * 1024 * 1024;
43
+ // eslint-disable-next-line no-magic-numbers
44
+ const BYTES_PER_MIB = 1024 * 1024;
45
+ /**
46
+ * The coordination frame spatial-structure imposters compose under.
47
+ *
48
+ * With COORDINATE_TO_ORIGIN on, that is whatever frame the preview
49
+ * channel latched (undefined when it never captured an instance — the
50
+ * imposter walk then derives an equivalent one itself). With it OFF the
51
+ * durable capture composes against a bare identity, so the plates must
52
+ * too, or they would be the only thing in the scene in metres and Y-up.
53
+ *
54
+ * @param settings The open's loader settings.
55
+ * @param previewCoordination The preview channel's latched frame.
56
+ * @return {ArrayLike<number> | undefined} The frame, or undefined to let
57
+ * the imposter walk derive one.
58
+ */
59
+ function imposterCoordination(settings, previewCoordination) {
60
+ return settings?.COORDINATE_TO_ORIGIN === true ?
61
+ previewCoordination : IDENTITY_MAT4;
62
+ }
19
63
  /**
20
64
  * The proxy for IFC from the shim.
21
65
  */
@@ -31,6 +75,44 @@ export class IfcApiProxyIfc {
31
75
  this.wasmModule = wasmModule;
32
76
  this.settings = settings;
33
77
  this.fs = undefined;
78
+ /** Was this model opened without extraction (DEFER_GEOMETRY)? */
79
+ this.deferredMode_ = false;
80
+ /** True once beginPhase('geometry') has fired for this deferred pump. */
81
+ this.geometryPhaseStarted_ = false;
82
+ /**
83
+ * Deferred capture watermarks: entity localID -> how many of its
84
+ * placed instances (in scene-walk order) have been captured. Shared
85
+ * (mapped) geometry attributes instances to an entity from OTHER
86
+ * products' extractions, so an entity's instance set grows across
87
+ * batches - the watermark makes each instance captured exactly once,
88
+ * in the same order the classic single walk would process it.
89
+ */
90
+ this.demandCapturedCounts_ = new Map();
91
+ /** Cursor into demandProducts_ — products before it are extracted. */
92
+ this.demandCursor_ = 0;
93
+ /** Wall-clock split of the store-backed batch pump (profile script). */
94
+ this.extractProfile_ = {
95
+ prefetchMs: 0,
96
+ extractMs: 0,
97
+ releaseMs: 0,
98
+ batches: 0,
99
+ lastPins: 0,
100
+ pinMax: 0,
101
+ };
102
+ /** Cursor into demandAggregates_ — items before it are extracted. */
103
+ this.demandAggregatesCursor_ = 0;
104
+ /**
105
+ * True while an adopted preview-channel coordination frame is still
106
+ * unvalidated against the durable walk's first geometry. A model can
107
+ * carry geometry in more than one frame (ISSUE_129: a local-scale
108
+ * annotation previews first while the body is georeferenced at
109
+ * ~8.3e6 m), so a frame anchored on the wrong unit leaves the body
110
+ * raw — the Share#1634 browser-vs-node divergence, which was really
111
+ * preview-vs-no-preview. streamNewMeshes_ clears this on its first
112
+ * geometry, re-deriving from it when the adopted frame fails the
113
+ * large-coordinate budget.
114
+ */
115
+ this.demandCoordinationFromPreview_ = false;
34
116
  this._isCoordinated = false;
35
117
  this.linearScalingFactor = 1;
36
118
  this.identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
@@ -43,12 +125,18 @@ export class IfcApiProxyIfc {
43
125
  * Contains all the logic and methods regarding properties, psets, qsets, etc.
44
126
  */
45
127
  this.properties = new IfcProperties(this);
128
+ /** Native geometry freed (releaseGeometry) — scene walks would touch
129
+ * freed objects, so mesh serving degrades to the accumulated maps. */
130
+ this.released_ = false;
46
131
  // The cooperative path (createAsync) parses/extracts before construction
47
132
  // so it can await mid-load; the classic OpenModel path does it here,
48
133
  // synchronously. Both share the tail below (mesh vectors, statistics).
49
134
  const loadState = precomputed ??
50
135
  IfcApiProxyIfc.parseAndExtract(modelID, data, new ConwayGeometry(wasmModule), settings);
51
136
  this.conwaywasm = loadState.conwaywasm;
137
+ this.conwayGeometry_ = loadState.conwayGeometry;
138
+ this.deferredMode_ = loadState.deferred === true;
139
+ this.progressTracker_ = loadState.tracker;
52
140
  const statistics = Logger.getStatistics(modelID);
53
141
  const { allTimeStart, stepHeader, model, scene, conwayGeometry, geometryTimeInMs: executionTimeInMs, } = loadState;
54
142
  // get linear scaling factor
@@ -124,6 +212,18 @@ export class IfcApiProxyIfc {
124
212
  ];
125
213
  // save settings
126
214
  this.settings = settings;
215
+ // Deferred opens whose preview channel already established the
216
+ // coordination frame: adopt it, so the durable capture skips its own
217
+ // derivation and places exactly where the preview did. (Internal
218
+ // only — getCoordinationMatrix stays identity, see
219
+ // demandCoordination_.)
220
+ if (this.deferredMode_ && loadState.previewCoordinationMatrix !== void 0) {
221
+ this.demandCoordination_ = loadState.previewCoordinationMatrix;
222
+ this._isCoordinated = true;
223
+ // Adopted, not derived: validate against the durable walk's first
224
+ // geometry before trusting it (see demandCoordinationFromPreview_).
225
+ this.demandCoordinationFromPreview_ = true;
226
+ }
127
227
  let FILE_NAME = stepHeader.headers.get("FILE_NAME");
128
228
  if (FILE_NAME !== void 0) {
129
229
  // strip start / end parenthesis
@@ -167,6 +267,62 @@ export class IfcApiProxyIfc {
167
267
  const loadState = await IfcApiProxyIfc.parseAndExtractAsync(modelID, data, new ConwayGeometry(wasmModule), settings);
168
268
  return new IfcApiProxyIfc(modelID, data, wasmModule, settings, loadState);
169
269
  }
270
+ /**
271
+ * Streamed-open construction (conway extension, used by
272
+ * OpenModelStreamed): the parse runs through the streaming columnar
273
+ * indexer, so the model's index is columnar from birth and the
274
+ * per-record object phase — the dominant JS-heap cost of the classic
275
+ * parse on large models — never exists. Geometry extraction is the
276
+ * same cooperative path OpenModelAsync uses, and everything
277
+ * downstream (meshes, properties, SpillModelSource) behaves
278
+ * identically to a classic open.
279
+ *
280
+ * @param modelID The model ID being opened.
281
+ * @param data The IFC data buffer.
282
+ * @param wasmModule The wasm module.
283
+ * @param settings Loader settings (ON_PROGRESS is honored).
284
+ * @return {Promise<IfcApiProxyIfc>} The constructed proxy.
285
+ */
286
+ static async createStreamed(modelID, data, wasmModule, settings) {
287
+ const loadState = await IfcApiProxyIfc.parseColumnarAndExtractAsync(modelID, data, new ConwayGeometry(wasmModule), settings);
288
+ return new IfcApiProxyIfc(modelID, data, wasmModule, settings, loadState);
289
+ }
290
+ /**
291
+ * Deferred-geometry streamed open (conway extension; slice A of
292
+ * Share's demand/tiled rendering — design doc
293
+ * demand-tiled-rendering.md): identical streamed columnar parse, but
294
+ * NO geometry extraction happens at open. The proxy registers with an
295
+ * empty scene wired to the demand-extraction seam; callers then pump
296
+ * {@link extractGeometryBatch} to extract products in file-order
297
+ * batches, receiving each batch's meshes incrementally — the scene,
298
+ * properties, and spatial structure work from the first batch.
299
+ *
300
+ * @param modelID The model ID being opened.
301
+ * @param data The IFC data buffer.
302
+ * @param wasmModule The wasm module.
303
+ * @param settings Loader settings (ON_PROGRESS is honored for parse).
304
+ * @return {Promise<IfcApiProxyIfc>} The constructed proxy.
305
+ */
306
+ static async createDeferred(modelID, data, wasmModule, settings) {
307
+ const loadState = await IfcApiProxyIfc.parseColumnarAndExtractAsync(modelID, data, new ConwayGeometry(wasmModule), settings, true);
308
+ return new IfcApiProxyIfc(modelID, data, wasmModule, settings, loadState);
309
+ }
310
+ /**
311
+ * M1b store-backed open: parse through a moving window over `store`
312
+ * and keep the model windowed from birth — the source is never held
313
+ * as one `ArrayBuffer`. Geometry extract pages product closures
314
+ * through {@link extractGeometryBatchAsync}.
315
+ *
316
+ * @param modelID The model ID being opened.
317
+ * @param store External store holding the source bytes.
318
+ * @param wasmModule The wasm module.
319
+ * @param settings Loader settings (ON_PROGRESS is honored).
320
+ * @return {Promise<IfcApiProxyIfc>} The constructed proxy.
321
+ */
322
+ static async createFromStore(modelID, store, wasmModule, settings) {
323
+ const loadState = await IfcApiProxyIfc.parseColumnarFromStore(modelID, store, new ConwayGeometry(wasmModule), settings, settings?.DEFER_GEOMETRY === true);
324
+ return new IfcApiProxyIfc(modelID, new Uint8Array(0), wasmModule, settings, loadState);
325
+ }
170
326
  /**
171
327
  * Log + record the header parse result on the model statistics.
172
328
  *
@@ -342,6 +498,334 @@ export class IfcApiProxyIfc {
342
498
  geometryTimeInMs: endTime - startTime,
343
499
  };
344
500
  }
501
+ /**
502
+ * Streamed twin of parseAndExtractAsync: the data parse runs through
503
+ * the streaming columnar indexer over a moving window instead of the
504
+ * per-record object parse, so the index is columnar from birth (no
505
+ * object phase). The source buffer stays resident behind the model —
506
+ * extraction and synchronous property reads behave exactly like a
507
+ * classic open, and `spillSourceToExternalStore` works afterwards as
508
+ * usual.
509
+ *
510
+ * The columnar build is cooperative (periodic event-loop yields, like
511
+ * the classic parseDataBlockAsync) with absolute byte-cursor progress
512
+ * ticks, and extraction is cooperative too — the streamed open keeps
513
+ * the repaint/no-stall property of OpenModelAsync (#301 §2). Throws
514
+ * when the streamed parse is anything but COMPLETE — the caller
515
+ * (OpenModelStreamed) falls back to the classic path, which tolerates
516
+ * recoverable parses.
517
+ *
518
+ * @param modelID The model ID being opened.
519
+ * @param data The IFC data buffer.
520
+ * @param conwaywasm The conway geometry wasm wrapper.
521
+ * @param settings Loader settings (ON_PROGRESS is honored).
522
+ * @return {Promise<IfcProxyLoadState>} Everything the constructor tail needs.
523
+ */
524
+ static async parseColumnarAndExtractAsync(modelID, data, conwaywasm, settings, deferGeometry = false) {
525
+ const tracker = IfcApiProxyIfc.makeTracker(settings);
526
+ const allTimeStart = Date.now();
527
+ const parser = IfcStepParser.Instance;
528
+ const bufferInput = new ParsingBuffer(data);
529
+ tracker?.beginPhase("headerParse", "bytes", data.length);
530
+ // Header parsed standalone first so the model line fires before the
531
+ // full parse, exactly like the classic path (the columnar build
532
+ // re-reads the tiny header internally; the cost is negligible).
533
+ const [stepHeader, result0] = parser.parseHeader(bufferInput);
534
+ Logger.createStatistics(modelID);
535
+ const statistics = Logger.getStatistics(modelID);
536
+ IfcApiProxyIfc.reportHeaderParseResult(result0, bufferInput, modelID);
537
+ const modelInfo = extractModelInfo(stepHeader, data.length);
538
+ Logger.info(formatModelLine(modelInfo));
539
+ settings?.ON_MODEL_INFO?.(modelInfo);
540
+ tracker?.beginPhase("dataParse", "bytes", data.length);
541
+ const parseTick = tracker !== void 0 ?
542
+ (cursorBytes) => tracker.update(cursorBytes) : void 0;
543
+ const parseStartTime = Date.now();
544
+ // Inline twin of buildColumnarIndexStreamingAsync — the sink is created
545
+ // here so the parse-time preview channel (slice A2) can watch it grow
546
+ // and snapshot prefix models between the parse's cooperative yields.
547
+ const sink = new ColumnarIndexSink();
548
+ const previewChannel = deferGeometry && settings?.ON_PREVIEW_MESH !== void 0 ?
549
+ new StreamedPreviewChannel(data, conwaywasm, sink, ifcPreviewAdapter(), settings.COORDINATE_TO_ORIGIN === true, settings.ON_PREVIEW_MESH) : void 0;
550
+ previewChannel?.start();
551
+ // Channel ticks ride the parse's own progress callback (see
552
+ // maybeTickInline) — timer ticks alone starve under the parse's
553
+ // scheduler-priority yields in browsers.
554
+ const parseProgress = previewChannel !== void 0 ?
555
+ (cursorBytes) => {
556
+ parseTick?.(cursorBytes);
557
+ previewChannel.maybeTickInline();
558
+ } : parseTick;
559
+ let result;
560
+ try {
561
+ ({ result } = await buildIndexStreamingAsync(new BufferByteSource(data), parser, STREAMED_PARSE_POOL_BYTES, void 0, sink, parseProgress));
562
+ }
563
+ finally {
564
+ previewChannel?.stop();
565
+ }
566
+ const columns = sink.finalize();
567
+ const parseEndTime = Date.now();
568
+ tracker?.endPhase(data.length);
569
+ if (result !== ParseResult.COMPLETE) {
570
+ Logger.warning(`[OpenModelStreamed]: streamed parse result ${result}`);
571
+ statistics?.setLoadStatus("PARSE_FAIL");
572
+ throw new Error("Streamed parse did not complete");
573
+ }
574
+ const model = new IfcStepModel(data, columns);
575
+ statistics?.setParseTime(parseEndTime - parseStartTime);
576
+ const conwayGeometry = new IfcGeometryExtraction(conwaywasm, model);
577
+ // Deferred mode (createDeferred): no extraction now — prime the
578
+ // per-product demand seam and hand back the (empty) live scene the
579
+ // batch pump populates. `scene` is the same object streamAllMeshes
580
+ // walks, so meshes appear to consumers as batches extract.
581
+ if (deferGeometry) {
582
+ conwayGeometry.prepareDemandExtraction();
583
+ if (settings?.ON_PREVIEW_MESH !== void 0) {
584
+ try {
585
+ // After prepareDemandExtraction, not before: the plates are
586
+ // composed under the coordination frame, whose scaling comes
587
+ // from getLinearScalingFactor() — which reads 1 until the
588
+ // extraction maps (and with them the unit assignment) are
589
+ // prepared. Both calls run in the same synchronous stretch
590
+ // after the parse, so nothing reaches the screen any later.
591
+ await emitSpatialStructureImposters(model, settings.ON_PREVIEW_MESH, imposterCoordination(settings, previewChannel?.coordinationMatrix), conwayGeometry.getLinearScalingFactor());
592
+ }
593
+ catch {
594
+ // Spatial imposters must never break a deferred open.
595
+ }
596
+ }
597
+ statistics?.setProductCount(model.typeCount(IfcProduct));
598
+ return {
599
+ conwaywasm,
600
+ deferred: true,
601
+ // Pin the durable pump's coordination to the preview channel's
602
+ // (derived from the same first instance with the same math), so
603
+ // preview payloads and durable meshes share one frame.
604
+ previewCoordinationMatrix: previewChannel?.coordinationMatrix,
605
+ allTimeStart,
606
+ stepHeader,
607
+ model,
608
+ scene: conwayGeometry.scene,
609
+ conwayGeometry,
610
+ geometryTimeInMs: 0,
611
+ tracker,
612
+ };
613
+ }
614
+ tracker?.beginPhase("geometry", "products");
615
+ const geometryTick = tracker !== void 0 ?
616
+ (completed, total) => {
617
+ tracker.setPhaseTotal(total);
618
+ tracker.update(completed);
619
+ } : void 0;
620
+ const startTime = Date.now();
621
+ const [extractionResult, scene] = await conwayGeometry.extractIFCGeometryDataAsync(geometryTick);
622
+ const endTime = Date.now();
623
+ tracker?.endPhase();
624
+ if (extractionResult !== ExtractResult.COMPLETE) {
625
+ Logger.error("[OpenModelStreamed]: Error extracting geometry, exiting...");
626
+ statistics?.setLoadStatus("FAIL");
627
+ throw new Error("Couldn't extract model");
628
+ }
629
+ statistics?.setProductCount(model.typeCount(IfcProduct));
630
+ statistics?.setGeometryTypeCounts(conwayGeometry.geometryTypeCounts);
631
+ return {
632
+ conwaywasm,
633
+ allTimeStart,
634
+ stepHeader,
635
+ model,
636
+ scene,
637
+ conwayGeometry,
638
+ geometryTimeInMs: endTime - startTime,
639
+ };
640
+ }
641
+ /**
642
+ * Windowed-from-birth twin of parseColumnarAndExtractAsync: the
643
+ * source stays in `store`, parse windows are filled through it, and
644
+ * the model never holds a resident copy. During parse a bounded
645
+ * prefix extract pages product closures from the store and emits
646
+ * placed meshes (same COORDINATE_TO_ORIGIN frame as durable). After
647
+ * the index exists, spatial-structure AABB plates go out too.
648
+ * Deferred opens page prep closures before prepareDemandExtraction;
649
+ * non-deferred opens drain the demand pump with per-product residency.
650
+ *
651
+ * @param modelID The model ID being opened.
652
+ * @param store External store holding the source bytes.
653
+ * @param conwaywasm The conway geometry wasm wrapper.
654
+ * @param settings Loader settings (ON_PROGRESS is honored).
655
+ * @param deferGeometry Skip extraction (Share demand pump).
656
+ * @return {Promise<IfcProxyLoadState>} Everything the constructor tail needs.
657
+ */
658
+ static async parseColumnarFromStore(modelID, store, conwaywasm, settings, deferGeometry = false) {
659
+ const tracker = IfcApiProxyIfc.makeTracker(settings);
660
+ const allTimeStart = Date.now();
661
+ const parser = IfcStepParser.Instance;
662
+ const fileSize = store.byteLength;
663
+ Logger.createStatistics(modelID);
664
+ const statistics = Logger.getStatistics(modelID);
665
+ tracker?.beginPhase("dataParse", "bytes", fileSize);
666
+ const parseTick = tracker !== void 0 ?
667
+ (cursorBytes) => tracker.update(cursorBytes) : void 0;
668
+ const parseStartTime = Date.now();
669
+ // Live sink so the store preview channel can snapshot a prefix
670
+ // mid-parse without a resident source buffer.
671
+ const sink = new ColumnarIndexSink();
672
+ const storePreview = settings?.ON_PREVIEW_MESH !== void 0 ?
673
+ new StorePreviewChannel(store, sink, conwaywasm, settings.COORDINATE_TO_ORIGIN === true, settings.ON_PREVIEW_MESH) : void 0;
674
+ const parseProgress = async (cursorBytes) => {
675
+ parseTick?.(cursorBytes);
676
+ await storePreview?.maybeTickAsync();
677
+ };
678
+ // One windowed pass: header comes out of the same slide as the
679
+ // data block so we do not hold a second 16 MiB prefix copy.
680
+ let result;
681
+ let stepHeader;
682
+ try {
683
+ ({ result, header: stepHeader } = await buildIndexStreamingAsync(new StoreByteSource(store), parser, STORE_PARSE_POOL_BYTES, void 0, sink, parseProgress));
684
+ if (storePreview !== void 0) {
685
+ await storePreview.flushAsync();
686
+ }
687
+ }
688
+ finally {
689
+ storePreview?.stop();
690
+ }
691
+ const columns = sink.finalize();
692
+ // Parse window is out of scope; drop the module scratch in case a
693
+ // numeric read during the slide left it pointed at that 16 MiB view.
694
+ releaseScratchParsingBuffer();
695
+ IfcApiProxyIfc.reportHeaderParseResult(result, new ParsingBuffer(new Uint8Array(0)), modelID);
696
+ const modelInfo = extractModelInfo(stepHeader, fileSize);
697
+ Logger.info(formatModelLine(modelInfo));
698
+ settings?.ON_MODEL_INFO?.(modelInfo);
699
+ const parseEndTime = Date.now();
700
+ tracker?.endPhase(fileSize);
701
+ if (result !== ParseResult.COMPLETE) {
702
+ Logger.warning(`[OpenModelStream]: streamed parse result ${result}`);
703
+ statistics?.setLoadStatus("PARSE_FAIL");
704
+ throw new Error("Streamed parse did not complete");
705
+ }
706
+ const provider = new WindowedStepBufferProvider(store);
707
+ const model = new IfcStepModel(void 0, columns, provider);
708
+ statistics?.setParseTime(parseEndTime - parseStartTime);
709
+ const conwayGeometry = new IfcGeometryExtraction(conwaywasm, model);
710
+ const prepPins = await conwayGeometry.ensureResidentForDemandPrep();
711
+ try {
712
+ conwayGeometry.prepareDemandExtraction();
713
+ }
714
+ finally {
715
+ model.releaseSourceViews(prepPins);
716
+ model.unpinLocalIDs(prepPins);
717
+ }
718
+ if (settings?.ON_PREVIEW_MESH !== void 0) {
719
+ try {
720
+ // Emitted after demand prep so getLinearScalingFactor() reports
721
+ // the model's real units — see the resident path's note.
722
+ await emitSpatialStructureImposters(model, settings.ON_PREVIEW_MESH, imposterCoordination(settings, storePreview?.coordinationMatrix), conwayGeometry.getLinearScalingFactor());
723
+ }
724
+ catch {
725
+ // Spatial imposters must never break a store-backed open.
726
+ }
727
+ }
728
+ if (deferGeometry) {
729
+ statistics?.setProductCount(model.typeCount(IfcProduct));
730
+ return {
731
+ conwaywasm,
732
+ deferred: true,
733
+ previewCoordinationMatrix: storePreview?.coordinationMatrix,
734
+ allTimeStart,
735
+ stepHeader,
736
+ model,
737
+ scene: conwayGeometry.scene,
738
+ conwayGeometry,
739
+ geometryTimeInMs: 0,
740
+ tracker,
741
+ };
742
+ }
743
+ tracker?.beginPhase("geometry", "products");
744
+ const startTime = Date.now();
745
+ const aggregateTargets = conwayGeometry.aggregateTargetLocalIDs();
746
+ let completed = 0;
747
+ tracker?.setPhaseTotal(model.typeCount(IfcProduct));
748
+ // eslint-disable-next-line no-magic-numbers
749
+ const EXTRACT_BATCH = 8;
750
+ const pending = [];
751
+ const flushProductBatch = async () => {
752
+ if (pending.length === 0) {
753
+ return;
754
+ }
755
+ const pins = new Set();
756
+ const leafSpans = [];
757
+ try {
758
+ await Promise.all(pending.map((localID) => conwayGeometry.ensureResidentForProductExtract(localID, pins, leafSpans)));
759
+ for (const localID of pending) {
760
+ try {
761
+ conwayGeometry.extractProductGeometryByLocalID(localID);
762
+ }
763
+ catch (error) {
764
+ Logger.error(`Error extracting product localID ${localID}: ` +
765
+ `${error instanceof Error ? error.message : String(error)}`);
766
+ }
767
+ model.releaseSourceViews(pins);
768
+ ++completed;
769
+ }
770
+ }
771
+ finally {
772
+ model.releaseSourceViews(pins);
773
+ model.unpinLocalIDs(pins);
774
+ for (const span of leafSpans) {
775
+ model.unpinAddressRange(span.address, span.length);
776
+ }
777
+ pending.length = 0;
778
+ }
779
+ tracker?.update(completed, {
780
+ residentSourceMb: model.residentSourceBytes / BYTES_PER_MIB,
781
+ });
782
+ };
783
+ for (const product of model.types(IfcProduct)) {
784
+ if (aggregateTargets.has(product.localID)) {
785
+ continue;
786
+ }
787
+ pending.push(product.localID);
788
+ if (pending.length >= EXTRACT_BATCH) {
789
+ await flushProductBatch();
790
+ }
791
+ }
792
+ await flushProductBatch();
793
+ for (const relAggregate of model.types(IfcRelAggregates)) {
794
+ const leafSpans = [];
795
+ const pins = await conwayGeometry.ensureResidentForAggregateExtract(relAggregate, leafSpans);
796
+ try {
797
+ conwayGeometry.extractRelAggregateGeometry(relAggregate);
798
+ }
799
+ catch (error) {
800
+ Logger.error(`Error extracting aggregate ${relAggregate.expressID}: ` +
801
+ `${error instanceof Error ? error.message : String(error)}`);
802
+ }
803
+ finally {
804
+ model.releaseSourceViews(pins);
805
+ model.unpinLocalIDs(pins);
806
+ for (const span of leafSpans) {
807
+ model.unpinAddressRange(span.address, span.length);
808
+ }
809
+ }
810
+ ++completed;
811
+ tracker?.update(completed, {
812
+ residentSourceMb: model.residentSourceBytes / BYTES_PER_MIB,
813
+ });
814
+ }
815
+ const endTime = Date.now();
816
+ tracker?.endPhase();
817
+ statistics?.setProductCount(model.typeCount(IfcProduct));
818
+ statistics?.setGeometryTypeCounts(conwayGeometry.geometryTypeCounts);
819
+ return {
820
+ conwaywasm,
821
+ allTimeStart,
822
+ stepHeader,
823
+ model,
824
+ scene: conwayGeometry.scene,
825
+ conwayGeometry,
826
+ geometryTimeInMs: endTime - startTime,
827
+ };
828
+ }
345
829
  /**
346
830
  *
347
831
  * @param input - FILE_HEADER from step header
@@ -412,8 +896,7 @@ export class IfcApiProxyIfc {
412
896
  return clone;
413
897
  }
414
898
  else {
415
- Logger.error(`[GetGeometry]: Geometry Object not found for expressID:
416
- ${geometryExpressID}`);
899
+ Logger.error(`[GetGeometry]: Geometry Object not found for expressID: \n ${geometryExpressID}`);
417
900
  }
418
901
  }
419
902
  }
@@ -506,6 +989,14 @@ export class IfcApiProxyIfc {
506
989
  get sourceIsExternal() {
507
990
  return this.model[0].isSourceExternal;
508
991
  }
992
+ /**
993
+ * Store-backed pump split (prefetch / extract / release).
994
+ *
995
+ * @return {object} Cumulative milliseconds and pin telemetry.
996
+ */
997
+ get extractProfile() {
998
+ return this.extractProfile_;
999
+ }
509
1000
  /**
510
1001
  * Release the resident source buffer and serve subsequent record
511
1002
  * reads through fixed-size windows paged in from the given external
@@ -767,6 +1258,16 @@ export class IfcApiProxyIfc {
767
1258
  /* eslint-enable no-unused-vars */
768
1259
  return Array.from(coordinationMatrix);
769
1260
  }
1261
+ /**
1262
+ * The coordination frame actually applied to emitted placements —
1263
+ * the derived (or validated adopted) recenter, identity when no
1264
+ * recenter ran. See ifc_api_model_passthrough.getAppliedCoordination.
1265
+ *
1266
+ * @return {Array<number>} column-major mat4
1267
+ */
1268
+ getAppliedCoordination() {
1269
+ return [...(this.demandCoordination_ ?? this.identity)];
1270
+ }
770
1271
  /**
771
1272
  *
772
1273
  * @param ptr
@@ -793,8 +1294,15 @@ export class IfcApiProxyIfc {
793
1294
  * @return {Float32Array | Uint32Array}
794
1295
  */
795
1296
  getSubArray(heap, startPtr, sizeBytes) {
796
- // eslint-disable-next-line no-magic-numbers, no-mixed-operators
797
- return heap.subarray(startPtr / 4, startPtr / 4 + sizeBytes).slice(0);
1297
+ // `heap` stays in the signature because web-ifc's API puts it there, but
1298
+ // it is now read only for its element type. The view itself is rebuilt
1299
+ // over the module's CURRENT heap buffer: a caller-held HEAPF32/HEAPU32 can
1300
+ // be bound to a pre-growth buffer on the MT build, and subarray() on such
1301
+ // a view clamps the window silently and returns short data rather than
1302
+ // failing (#485, and see core/wasm_heap.ts).
1303
+ const arrayType = heap.constructor;
1304
+ // slice(0) as before, so the result outlives the next call into wasm.
1305
+ return wasmHeapView(this.wasmModule, arrayType, startPtr, sizeBytes).slice(0);
798
1306
  }
799
1307
  /**
800
1308
  * Closes a model and frees all related memory
@@ -804,12 +1312,467 @@ export class IfcApiProxyIfc {
804
1312
  closeModel() {
805
1313
  // Null operation.
806
1314
  }
1315
+ /**
1316
+ * Free this model's native geometry (conway extension): every
1317
+ * canonical mesh the extraction produced plus the GetGeometry map.
1318
+ * Call AFTER the consumer has built its own scene from the meshes —
1319
+ * subsequent GetGeometry calls return an empty dummy. On a deferred
1320
+ * model whose pump has not drained, this is a no-op (releasing would
1321
+ * break the remaining extraction).
1322
+ *
1323
+ * The wasm heap never shrinks, but freed pages are reused: repeated
1324
+ * loads in one tab plateau instead of stacking whole model scenes
1325
+ * (the multi-load crash).
1326
+ *
1327
+ * @return {boolean} True when geometry was released.
1328
+ */
1329
+ releaseGeometry() {
1330
+ if (this.deferredMode_ &&
1331
+ this.demandProducts_ !== void 0 &&
1332
+ (this.demandCursor_ < this.demandProducts_.length ||
1333
+ this.demandAggregatesCursor_ <
1334
+ (this.demandAggregates_?.length ?? 0))) {
1335
+ Logger.warning("[ReleaseModelGeometry]: deferred pump not drained \u2014 not releasing");
1336
+ return false;
1337
+ }
1338
+ const model = this.model[0];
1339
+ const localIDs = [];
1340
+ for (const mesh of model.geometry) {
1341
+ localIDs.push(mesh.localID);
1342
+ }
1343
+ for (const localID of localIDs) {
1344
+ try {
1345
+ model.geometry.delete(localID);
1346
+ }
1347
+ catch {
1348
+ // Never let a free break a loaded model.
1349
+ }
1350
+ }
1351
+ // The GetGeometry map holds references to the now-freed natives —
1352
+ // clear it so lookups degrade to the dummy instead of touching them.
1353
+ this.model[3].clear();
1354
+ this.released_ = true;
1355
+ return true;
1356
+ }
1357
+ /**
1358
+ * Deferred-mode batch pump (conway extension; Share demand/tiled
1359
+ * rendering slice A): extract the next `batchSize` products (file
1360
+ * order) through the per-product demand seam and emit THIS BATCH's
1361
+ * meshes through `meshCallback` — the incremental twin of
1362
+ * streamAllMeshes. Placed-geometry math (coordination, scaling,
1363
+ * centering) is identical; the shared meshMap is updated so
1364
+ * getFlatMesh keeps working. Call repeatedly until `remaining` is 0.
1365
+ *
1366
+ * Requires a model opened with deferred geometry
1367
+ * (`OpenModelStreamed(data, {..., DEFER_GEOMETRY: true})`); on a
1368
+ * fully-extracted model this is a no-op returning remaining 0.
1369
+ *
1370
+ * @param batchSize Max products to extract this call (min 1).
1371
+ * @param meshCallback Receives each newly-extracted product's mesh.
1372
+ * @return {object} `{extracted, remaining}` — products processed this
1373
+ * call and products still pending.
1374
+ */
1375
+ extractGeometryBatch(batchSize, meshCallback) {
1376
+ // Fully-extracted opens have nothing to pump — re-running the
1377
+ // per-product extraction on them would duplicate scene work.
1378
+ if (!this.deferredMode_) {
1379
+ return { extracted: 0, remaining: 0 };
1380
+ }
1381
+ if (this.model[0].isSourceExternal) {
1382
+ throw new Error("ExtractGeometryBatch is synchronous and cannot page a windowed " +
1383
+ "source \u2014 use ExtractGeometryBatchAsync");
1384
+ }
1385
+ this.ensureDemandWorklists_();
1386
+ return this.pumpGeometryBatch_(batchSize, meshCallback);
1387
+ }
1388
+ /**
1389
+ * Async twin of {@link extractGeometryBatch}: pages each product's
1390
+ * `#ref` closure before extracting it, so a model opened through
1391
+ * {@link IfcApiProxyIfc.createFromStore} (windowed from birth) can
1392
+ * be pumped. Safe on a resident source (prefetch is a no-op).
1393
+ *
1394
+ * @param batchSize Max products to extract this call (min 1).
1395
+ * @param meshCallback Receives each newly-extracted product's mesh.
1396
+ * @return {Promise<object>} `{extracted, remaining}`.
1397
+ */
1398
+ async extractGeometryBatchAsync(batchSize, meshCallback) {
1399
+ if (!this.deferredMode_) {
1400
+ return { extracted: 0, remaining: 0 };
1401
+ }
1402
+ this.ensureDemandWorklists_();
1403
+ if (!this.model[0].isSourceExternal) {
1404
+ return this.pumpGeometryBatch_(batchSize, meshCallback);
1405
+ }
1406
+ const products = this.demandProducts_ ?? [];
1407
+ const aggregates = this.demandAggregates_ ?? [];
1408
+ const totalWork = products.length + aggregates.length;
1409
+ const budget = Math.max(batchSize, 1);
1410
+ let extracted = 0;
1411
+ if (this.progressTracker_ !== void 0 && !this.geometryPhaseStarted_) {
1412
+ this.progressTracker_.beginPhase("geometry", "products", totalWork);
1413
+ this.geometryPhaseStarted_ = true;
1414
+ }
1415
+ const productEnd = Math.min(this.demandCursor_ + budget, products.length);
1416
+ const batchIDs = products.slice(this.demandCursor_, productEnd);
1417
+ if (batchIDs.length > 0) {
1418
+ // One pin set for the batch so mapped geometry stays resident
1419
+ // across the instances that share it.
1420
+ const pins = new Set();
1421
+ const leafSpans = [];
1422
+ const profile = this.extractProfile_;
1423
+ try {
1424
+ const prefetchStart = Date.now();
1425
+ await Promise.all(batchIDs.map((localID) => this.conwayGeometry_.ensureResidentForProductExtract(localID, pins, leafSpans)));
1426
+ profile.prefetchMs += Date.now() - prefetchStart;
1427
+ if (pins.size > profile.pinMax) {
1428
+ profile.pinMax = pins.size;
1429
+ }
1430
+ profile.lastPins = pins.size;
1431
+ const extractStart = Date.now();
1432
+ for (const localID of batchIDs) {
1433
+ try {
1434
+ if (this.conwayGeometry_.extractProductGeometryByLocalID(localID)) {
1435
+ ++extracted;
1436
+ }
1437
+ }
1438
+ catch (error) {
1439
+ Logger.error(`Error extracting product localID ${localID}: ` +
1440
+ `${error instanceof Error ? error.message : String(error)}`);
1441
+ }
1442
+ // Drop JS views after each product; chunks stay pinned for
1443
+ // the rest of the batch so rematerialise is a cache hit.
1444
+ this.model[0].releaseSourceViews(pins);
1445
+ }
1446
+ profile.extractMs += Date.now() - extractStart;
1447
+ this.demandCursor_ = productEnd;
1448
+ }
1449
+ finally {
1450
+ const releaseStart = Date.now();
1451
+ this.model[0].releaseSourceViews(pins);
1452
+ this.model[0].unpinLocalIDs(pins);
1453
+ for (const span of leafSpans) {
1454
+ this.model[0].unpinAddressRange(span.address, span.length);
1455
+ }
1456
+ profile.releaseMs += Date.now() - releaseStart;
1457
+ profile.batches++;
1458
+ }
1459
+ }
1460
+ if (this.demandCursor_ >= products.length &&
1461
+ this.demandAggregatesCursor_ < aggregates.length) {
1462
+ const aggregatesEnd = Math.min(this.demandAggregatesCursor_ + (budget - extracted), aggregates.length);
1463
+ for (; this.demandAggregatesCursor_ < aggregatesEnd; ++this.demandAggregatesCursor_) {
1464
+ const relAggregate = aggregates[this.demandAggregatesCursor_];
1465
+ const leafSpans = [];
1466
+ const pins = await this.conwayGeometry_.ensureResidentForAggregateExtract(relAggregate, leafSpans);
1467
+ try {
1468
+ this.conwayGeometry_.extractRelAggregateGeometry(relAggregate);
1469
+ ++extracted;
1470
+ }
1471
+ catch (error) {
1472
+ Logger.error(`Error extracting aggregate ${relAggregate.expressID}: ` +
1473
+ `${error instanceof Error ? error.message : String(error)}`);
1474
+ }
1475
+ finally {
1476
+ this.model[0].releaseSourceViews(pins);
1477
+ this.model[0].unpinLocalIDs(pins);
1478
+ for (const span of leafSpans) {
1479
+ this.model[0].unpinAddressRange(span.address, span.length);
1480
+ }
1481
+ }
1482
+ }
1483
+ }
1484
+ if (meshCallback !== void 0) {
1485
+ this.streamNewMeshes_(meshCallback);
1486
+ }
1487
+ const remaining = (products.length - this.demandCursor_) +
1488
+ (aggregates.length - this.demandAggregatesCursor_);
1489
+ const windowMb = this.model[0].residentSourceBytes / BYTES_PER_MIB;
1490
+ if (this.progressTracker_ !== void 0) {
1491
+ const completed = totalWork - remaining;
1492
+ if (remaining === 0) {
1493
+ this.progressTracker_.update(completed, { residentSourceMb: windowMb });
1494
+ this.progressTracker_.endPhase(totalWork);
1495
+ }
1496
+ else {
1497
+ this.progressTracker_.update(completed, { residentSourceMb: windowMb });
1498
+ }
1499
+ }
1500
+ return { extracted, remaining };
1501
+ }
1502
+ /**
1503
+ * Enumerate the deferred worklists once (file-order products, then
1504
+ * rel-aggregates). Shared by the sync and async pumps.
1505
+ */
1506
+ ensureDemandWorklists_() {
1507
+ if (this.demandProducts_ !== void 0) {
1508
+ return;
1509
+ }
1510
+ // Aggregate-target products are extracted ONLY by the
1511
+ // rel-aggregates pass below (with the relating object's master
1512
+ // rel-voids), never by the per-product pass: extracting them here
1513
+ // first would emit their instances with the uncut/placeholder
1514
+ // content and the pass's later replacement would never reach an
1515
+ // incremental consumer (it copies at delivery), while the pass's
1516
+ // second scene instance would draw over the first (see
1517
+ // aggregateTargetLocalIDs).
1518
+ const aggregateTargets = this.conwayGeometry_.aggregateTargetLocalIDs();
1519
+ const products = [];
1520
+ for (const product of this.model[0].types(IfcProduct)) {
1521
+ if (aggregateTargets.has(product.localID)) {
1522
+ continue;
1523
+ }
1524
+ products.push(product.localID);
1525
+ }
1526
+ const aggregates = [];
1527
+ for (const relAggregate of this.model[0].types(IfcRelAggregates)) {
1528
+ aggregates.push(relAggregate);
1529
+ }
1530
+ this.demandProducts_ = products;
1531
+ this.demandAggregates_ = aggregates;
1532
+ this.demandCursor_ = 0;
1533
+ this.demandAggregatesCursor_ = 0;
1534
+ }
1535
+ /**
1536
+ * Synchronous extract of the next batch. Caller must have paged
1537
+ * ranges if the source is windowed.
1538
+ *
1539
+ * @param batchSize Max products this call.
1540
+ * @param meshCallback Optional mesh consumer.
1541
+ * @return {object} `{extracted, remaining}`.
1542
+ */
1543
+ pumpGeometryBatch_(batchSize, meshCallback) {
1544
+ const products = this.demandProducts_ ?? [];
1545
+ const aggregates = this.demandAggregates_ ?? [];
1546
+ const totalWork = products.length + aggregates.length;
1547
+ if (this.progressTracker_ !== void 0 && !this.geometryPhaseStarted_) {
1548
+ this.progressTracker_.beginPhase("geometry", "products", totalWork);
1549
+ this.geometryPhaseStarted_ = true;
1550
+ }
1551
+ const budget = Math.max(batchSize, 1);
1552
+ const end = Math.min(this.demandCursor_ + budget, products.length);
1553
+ let extracted = 0;
1554
+ for (; this.demandCursor_ < end; ++this.demandCursor_) {
1555
+ const localID = products[this.demandCursor_];
1556
+ if (this.conwayGeometry_.extractProductGeometryByLocalID(localID)) {
1557
+ ++extracted;
1558
+ }
1559
+ }
1560
+ // Classic parity: once the product walk completes, pump the
1561
+ // whole-model walk's second (rel-aggregates master-voids) pass with
1562
+ // the same per-call budget — batch-by-batch, not as one end-of-load
1563
+ // stall — so aggregate parts stream in cut, exactly once, with
1564
+ // final content (see demandAggregates_).
1565
+ if (this.demandCursor_ >= products.length &&
1566
+ this.demandAggregatesCursor_ < aggregates.length) {
1567
+ const aggregatesEnd = Math.min(this.demandAggregatesCursor_ + (budget - extracted), aggregates.length);
1568
+ for (; this.demandAggregatesCursor_ < aggregatesEnd; ++this.demandAggregatesCursor_) {
1569
+ this.conwayGeometry_.extractRelAggregateGeometry(aggregates[this.demandAggregatesCursor_]);
1570
+ ++extracted;
1571
+ }
1572
+ }
1573
+ if (meshCallback !== void 0) {
1574
+ this.streamNewMeshes_(meshCallback);
1575
+ }
1576
+ const remaining = (products.length - this.demandCursor_) +
1577
+ (aggregates.length - this.demandAggregatesCursor_);
1578
+ if (this.progressTracker_ !== void 0) {
1579
+ const completed = totalWork - remaining;
1580
+ if (remaining === 0) {
1581
+ this.progressTracker_.endPhase(totalWork);
1582
+ }
1583
+ else {
1584
+ this.progressTracker_.update(completed);
1585
+ }
1586
+ }
1587
+ return {
1588
+ extracted,
1589
+ remaining,
1590
+ };
1591
+ }
1592
+ /**
1593
+ * Walk the scene and emit every not-yet-captured placed instance as
1594
+ * per-entity DELTA FlatMeshes — the incremental core of
1595
+ * streamAllMeshes with identical placed-geometry math, processed in
1596
+ * walk order exactly once per instance (per-entity watermarks). An
1597
+ * entity re-emits with only its NEW instances when shared/mapped
1598
+ * geometry attributes more to it in later batches; consumers render
1599
+ * deltas additively (the shared meshMap still accumulates each
1600
+ * entity's FULL vector, so getFlatMesh stays whole-model correct).
1601
+ *
1602
+ * Also fixes a latent multi-call bug: the derived coordination
1603
+ * matrix is remembered (demandCoordination_), so later batches place
1604
+ * with the SAME coordination the first batch established
1605
+ * (streamAllMeshes never needed this — it runs once). It is NOT
1606
+ * exposed through getCoordinationMatrix, which keeps the classic
1607
+ * identity contract consumers stamp onto assembled models.
1608
+ *
1609
+ * @param meshCallback Receives one delta FlatMesh per entity that
1610
+ * gained instances this call.
1611
+ */
1612
+ streamNewMeshes_(meshCallback) {
1613
+ // Released models: the scene's natives are freed — nothing new can
1614
+ // exist to capture, and walking would touch freed objects.
1615
+ if (this.released_) {
1616
+ return;
1617
+ }
1618
+ const [model, scene, meshMap, geometryMaterialTransformMap] = this.model;
1619
+ let coordinationMatrix = this.demandCoordination_ ?? glmatrix.mat4.create();
1620
+ const seenThisPass = new Map();
1621
+ const deltas = new Map();
1622
+ // eslint-disable-next-line no-unused-vars
1623
+ for (const [_, nativeTransform, geometry, material, entity] of scene.walk()) {
1624
+ if (entity?.localID === void 0 || entity.expressID === void 0) {
1625
+ continue;
1626
+ }
1627
+ // Per-entity walk position vs watermark: instances before the
1628
+ // watermark were captured in an earlier call (append-only walk
1629
+ // order makes the count a stable cursor).
1630
+ const walkIndex = seenThisPass.get(entity.localID) ?? 0;
1631
+ seenThisPass.set(entity.localID, walkIndex + 1);
1632
+ if (walkIndex < (this.demandCapturedCounts_.get(entity.localID) ?? 0)) {
1633
+ continue;
1634
+ }
1635
+ if (geometry.type !== CanonicalMeshType.BUFFER_GEOMETRY || geometry.temporary) {
1636
+ continue;
1637
+ }
1638
+ const material_ = material ?? {
1639
+ name: "",
1640
+ // eslint-disable-next-line no-magic-numbers
1641
+ baseColor: [0.8, 0.8, 0.8, 1],
1642
+ // eslint-disable-next-line no-magic-numbers
1643
+ legacyColor: [0.8, 0.8, 0.8, 1],
1644
+ doubleSided: true,
1645
+ blend: 0,
1646
+ };
1647
+ const validatePreviewFrame = this.demandCoordinationFromPreview_ &&
1648
+ this.settings?.COORDINATE_TO_ORIGIN === true;
1649
+ let nativePt;
1650
+ if ((!this._isCoordinated || validatePreviewFrame) &&
1651
+ this.settings?.COORDINATE_TO_ORIGIN) {
1652
+ nativePt = geometry.geometry.getPoint(0);
1653
+ }
1654
+ // normalize() recenters the shared geometry buffer (side effect)
1655
+ // and returns the local centre used to place it.
1656
+ const center = geometry.geometry.normalize();
1657
+ const expressID = model.getElementByLocalID(geometry.localID)?.expressID;
1658
+ // Full-precision float64 placement straight from the wasm boundary
1659
+ // (glm::dmat4). NOT re-truncated through a gl-matrix Float32Array —
1660
+ // the recentre math runs in double precision (see coordination_f64).
1661
+ const geometryTransform = nativeTransform?.getValues();
1662
+ if (!this._isCoordinated && this.settings?.COORDINATE_TO_ORIGIN) {
1663
+ const derived = deriveCoordinationF64(geometryTransform, nativePt, this.NormalizeMat, this.linearScalingFactor);
1664
+ coordinationMatrix = derived;
1665
+ // Persist for every later batch (internal only — see
1666
+ // demandCoordination_'s identity-contract note).
1667
+ this.demandCoordination_ = derived;
1668
+ this._isCoordinated = true;
1669
+ }
1670
+ else if (validatePreviewFrame) {
1671
+ // One-shot check of an adopted preview frame against the durable
1672
+ // walk's first geometry: when the composed placement still lands
1673
+ // beyond the large-coordinate budget, the preview anchored in a
1674
+ // different frame than the model body (Share#1634) — re-derive
1675
+ // from this geometry, the classic path's anchor. The throwaway
1676
+ // preview scene may briefly disagree; the durable model must
1677
+ // place like the classic open.
1678
+ this.demandCoordinationFromPreview_ = false;
1679
+ const probe = composeTransformF64(coordinationMatrix, geometryTransform, center);
1680
+ const magnitude = Math.max(Math.abs(probe[TRANSLATION_X]), Math.abs(probe[TRANSLATION_Y]), Math.abs(probe[TRANSLATION_Z]));
1681
+ if (magnitude > LARGE_COORDINATE_BUDGET_M) {
1682
+ const derived = deriveCoordinationF64(geometryTransform, nativePt, this.NormalizeMat, this.linearScalingFactor);
1683
+ coordinationMatrix = derived;
1684
+ this.demandCoordination_ = derived;
1685
+ Logger.info(`[deferred] preview coordination frame rejected (first durable ` +
1686
+ `placement at ${Math.round(magnitude)}m); re-derived from the durable anchor`);
1687
+ }
1688
+ }
1689
+ const newTransformArr = composeTransformF64(coordinationMatrix, geometryTransform, center);
1690
+ geometryMaterialTransformMap.set(expressID, [geometry.geometry, material_, newTransformArr]);
1691
+ const color = {
1692
+ x: material_.legacyColor[0],
1693
+ y: material_.legacyColor[1],
1694
+ z: material_.legacyColor[2],
1695
+ w: material_.legacyColor[3],
1696
+ };
1697
+ const placed = {
1698
+ color,
1699
+ geometryExpressID: expressID,
1700
+ flatTransformation: newTransformArr,
1701
+ };
1702
+ let mesh = meshMap.get(entity.expressID);
1703
+ if (mesh === void 0) {
1704
+ const placedArray = new Array();
1705
+ const placedVector = {
1706
+ get: (index) => placedArray[index] ?? placed,
1707
+ size: () => placedArray.length,
1708
+ push: (parameter) => {
1709
+ placedArray.push(parameter);
1710
+ },
1711
+ };
1712
+ const flatMesh = {
1713
+ geometries: placedVector,
1714
+ expressID: entity.expressID,
1715
+ };
1716
+ mesh = [placedVector, flatMesh];
1717
+ meshMap.set(entity.expressID, mesh);
1718
+ }
1719
+ mesh[0].push(placed);
1720
+ mesh[1].geometries = mesh[0];
1721
+ this.demandCapturedCounts_.set(entity.localID, (this.demandCapturedCounts_.get(entity.localID) ?? 0) + 1);
1722
+ let delta = deltas.get(entity.expressID);
1723
+ if (delta === void 0) {
1724
+ delta = [];
1725
+ deltas.set(entity.expressID, delta);
1726
+ }
1727
+ delta.push(placed);
1728
+ }
1729
+ const vectorFlatMesh = this.model[4];
1730
+ for (const [expressID, placedList] of deltas) {
1731
+ const placedVector = {
1732
+ get: (index) => placedList[index] ?? placedList[0],
1733
+ size: () => placedList.length,
1734
+ push: (parameter) => {
1735
+ placedList.push(parameter);
1736
+ },
1737
+ };
1738
+ const deltaMesh = { geometries: placedVector, expressID };
1739
+ vectorFlatMesh.push(deltaMesh);
1740
+ meshCallback(deltaMesh);
1741
+ }
1742
+ }
807
1743
  /**
808
1744
  *
809
1745
  * @param modelID
810
1746
  * @param meshCallback
811
1747
  */
812
1748
  streamAllMeshes(meshCallback) {
1749
+ // Released models: the natives behind the scene are freed — serve
1750
+ // the accumulated per-entity meshes instead of re-walking.
1751
+ if (this.released_ && !this.deferredMode_) {
1752
+ const [, , meshMap, , vectorFlatMesh] = this.model;
1753
+ meshMap.forEach((mesh) => {
1754
+ vectorFlatMesh.push(mesh[1]);
1755
+ meshCallback(mesh[1]);
1756
+ });
1757
+ return;
1758
+ }
1759
+ // Deferred models: the delta capture has already populated (or will
1760
+ // populate) the shared meshMap — re-running the classic walk would
1761
+ // push every instance a second time. Pump any remainder to
1762
+ // completion and serve the accumulated full per-entity meshes.
1763
+ if (this.deferredMode_) {
1764
+ const noCallback = void 0;
1765
+ while (this.extractGeometryBatch(DEFERRED_DRAIN_BATCH, noCallback).remaining > 0) {
1766
+ // draining
1767
+ }
1768
+ this.streamNewMeshes_(() => { });
1769
+ const [, , meshMap, , vectorFlatMesh] = this.model;
1770
+ meshMap.forEach((mesh) => {
1771
+ vectorFlatMesh.push(mesh[1]);
1772
+ meshCallback(mesh[1]);
1773
+ });
1774
+ return;
1775
+ }
813
1776
  const [model, scene, meshMap, geometryMaterialTransformMap, vectorFlatMesh] = this.model;
814
1777
  let coordinationMatrix = this.model[5];
815
1778
  // eslint-disable-next-line no-unused-vars
@@ -834,62 +1797,23 @@ export class IfcApiProxyIfc {
834
1797
  if (!this._isCoordinated && this.settings?.COORDINATE_TO_ORIGIN) {
835
1798
  nativePt = geometry.geometry.getPoint(0);
836
1799
  }
837
- // extract center
838
- const geomCenter = glmatrix.vec3.create();
1800
+ // normalize() recenters the geometry buffer (side effect) and
1801
+ // returns the local centre used to place it.
839
1802
  const center = geometry.geometry.normalize();
840
- geomCenter[0] = center.x;
841
- geomCenter[1] = center.y;
842
- geomCenter[2] = center.z;
843
- // Create a translation matrix from geom.min
844
- const translationMatrixGeomMin = glmatrix.mat4.create();
845
- glmatrix.mat4.fromTranslation(translationMatrixGeomMin, geomCenter);
846
1803
  // create PlacedGeometry
847
1804
  const expressID = model.getElementByLocalID(geometry.localID)?.expressID;
1805
+ // Full-precision float64 placement straight from the wasm boundary
1806
+ // (glm::dmat4) — never truncated through a gl-matrix Float32Array;
1807
+ // the recentre math runs in double precision (see coordination_f64).
848
1808
  const geometryTransform = nativeTransform?.getValues();
849
- let newMatrix;
850
- if (geometryTransform !== void 0) {
851
- newMatrix = glmatrix.mat4.fromValues(geometryTransform[0], geometryTransform[1], geometryTransform[2], geometryTransform[3], geometryTransform[4], geometryTransform[5], geometryTransform[6], geometryTransform[7], geometryTransform[8], geometryTransform[9], geometryTransform[10], geometryTransform[11], geometryTransform[12], geometryTransform[13], geometryTransform[14], geometryTransform[15]);
852
- }
853
- else {
854
- // set to identity if no transform found
855
- newMatrix = glmatrix.mat4.create();
856
- }
857
1809
  if (!this._isCoordinated && this.settings?.COORDINATE_TO_ORIGIN) {
858
- // coordinate the geometry to the origin
859
- const pt = [nativePt.x, nativePt.y, nativePt.z];
860
- // Transform the point by the matrix.
861
- const transformedPt = glmatrix.vec4.create();
862
- glmatrix.vec4.transformMat4(transformedPt, [pt[0], pt[1], pt[2], 1], newMatrix);
863
- // Create the translation matrix.
864
- coordinationMatrix = glmatrix.mat4.create();
865
- glmatrix.mat4.fromTranslation(coordinationMatrix, [-transformedPt[0], -transformedPt[1], -transformedPt[2]]);
866
- const scaleMatrix = glmatrix.mat4.create();
867
- // Create a 3D vector for scaling factors
868
- const scaleVec = glmatrix.vec3.fromValues(this.linearScalingFactor, this.linearScalingFactor, this.linearScalingFactor);
869
- // Scale the matrix
870
- glmatrix.mat4.scale(scaleMatrix, scaleMatrix, scaleVec);
871
- glmatrix.mat4.multiply(coordinationMatrix, this.NormalizeMat, coordinationMatrix);
872
- glmatrix.mat4.multiply(coordinationMatrix, scaleMatrix, coordinationMatrix);
1810
+ coordinationMatrix = deriveCoordinationF64(geometryTransform, nativePt, this.NormalizeMat, this.linearScalingFactor);
1811
+ // Persisted for getAppliedCoordination (Share#1634): report
1812
+ // the real applied frame, not the classic silent identity.
1813
+ this.demandCoordination_ = Array.from(coordinationMatrix);
873
1814
  this._isCoordinated = true;
874
1815
  }
875
- // extract color
876
- const newTransform = glmatrix.mat4.create();
877
- // Create a 4x4 identity matrix
878
- const scaleMatrix = glmatrix.mat4.create();
879
- // Create a 3D vector for scaling factors
880
- const scaleVec = glmatrix.vec3.fromValues(this.linearScalingFactor, this.linearScalingFactor, this.linearScalingFactor);
881
- // Scale the matrix
882
- glmatrix.mat4.scale(scaleMatrix, scaleMatrix, scaleVec);
883
- // Perform the matrix multiplications
884
- if (newMatrix !== void 0) {
885
- glmatrix.mat4.multiply(newTransform, coordinationMatrix, newMatrix);
886
- glmatrix.mat4.multiply(newTransform, newTransform, translationMatrixGeomMin);
887
- }
888
- else {
889
- glmatrix.mat4.multiply(newTransform, coordinationMatrix, newTransform);
890
- glmatrix.mat4.multiply(newTransform, newTransform, translationMatrixGeomMin);
891
- }
892
- const newTransformArr = Array.from(newTransform);
1816
+ const newTransformArr = composeTransformF64(coordinationMatrix, geometryTransform, center);
893
1817
  geometryMaterialTransformMap.set(expressID, [geometry.geometry, material_, newTransformArr]);
894
1818
  if (entity?.localID !== void 0) {
895
1819
  if (entity?.expressID !== void 0) {
@@ -1003,62 +1927,23 @@ export class IfcApiProxyIfc {
1003
1927
  if (!this._isCoordinated && this.settings?.COORDINATE_TO_ORIGIN) {
1004
1928
  nativePt = geometry.geometry.getPoint(0);
1005
1929
  }
1006
- // extract center
1007
- const geomCenter = glmatrix.vec3.create();
1930
+ // normalize() recenters the geometry buffer (side effect) and
1931
+ // returns the local centre used to place it.
1008
1932
  const center = geometry.geometry.normalize();
1009
- geomCenter[0] = center.x;
1010
- geomCenter[1] = center.y;
1011
- geomCenter[2] = center.z;
1012
- // Create a translation matrix from geom.min
1013
- const translationMatrixGeomMin = glmatrix.mat4.create();
1014
- glmatrix.mat4.fromTranslation(translationMatrixGeomMin, geomCenter);
1015
1933
  // create PlacedGeometry
1016
1934
  const expressID = model.getElementByLocalID(geometry.localID)?.expressID;
1935
+ // Full-precision float64 placement straight from the wasm boundary
1936
+ // (glm::dmat4) — never truncated through a gl-matrix Float32Array;
1937
+ // the recentre math runs in double precision (see coordination_f64).
1017
1938
  const geometryTransform = nativeTransform?.getValues();
1018
- let newMatrix;
1019
- if (geometryTransform !== void 0) {
1020
- newMatrix = glmatrix.mat4.fromValues(geometryTransform[0], geometryTransform[1], geometryTransform[2], geometryTransform[3], geometryTransform[4], geometryTransform[5], geometryTransform[6], geometryTransform[7], geometryTransform[8], geometryTransform[9], geometryTransform[10], geometryTransform[11], geometryTransform[12], geometryTransform[13], geometryTransform[14], geometryTransform[15]);
1021
- }
1022
1939
  if (!this._isCoordinated && this.settings?.COORDINATE_TO_ORIGIN) {
1023
- // coordinate the geometry to the origin
1024
- // Assuming geom.GetPoint(0) returns a glm::dvec3, i.e., a 3D vector.
1025
- // In TypeScript, you can represent it as number[] or Float64Array.
1026
1940
  Logger.info("Setting up coordinationMatrix");
1027
- // const nativePt = geometry.geometry.GetPoint(0)
1028
- const pt = [nativePt.x, nativePt.y, nativePt.z];
1029
- // Transform the point by the matrix.
1030
- const transformedPt = glmatrix.vec4.create();
1031
- glmatrix.vec4.transformMat4(transformedPt, [pt[0], pt[1], pt[2], 1], newMatrix);
1032
- // Create the translation matrix.
1033
- coordinationMatrix = glmatrix.mat4.create();
1034
- glmatrix.mat4.fromTranslation(coordinationMatrix, [-transformedPt[0], -transformedPt[1], -transformedPt[2]]);
1035
- const scaleMatrix = glmatrix.mat4.create();
1036
- // Create a 3D vector for scaling factors
1037
- const scaleVec = glmatrix.vec3.fromValues(this.linearScalingFactor, this.linearScalingFactor, this.linearScalingFactor);
1038
- // Scale the matrix
1039
- glmatrix.mat4.scale(scaleMatrix, scaleMatrix, scaleVec);
1040
- glmatrix.mat4.multiply(coordinationMatrix, this.NormalizeMat, coordinationMatrix);
1041
- glmatrix.mat4.multiply(coordinationMatrix, scaleMatrix, coordinationMatrix);
1941
+ coordinationMatrix = deriveCoordinationF64(geometryTransform, nativePt, this.NormalizeMat, this.linearScalingFactor);
1942
+ // Persisted for getAppliedCoordination (Share#1634).
1943
+ this.demandCoordination_ = Array.from(coordinationMatrix);
1042
1944
  this._isCoordinated = true;
1043
1945
  }
1044
- // extract color
1045
- const newTransform = glmatrix.mat4.create();
1046
- // Create a 4x4 identity matrix
1047
- const scaleMatrix = glmatrix.mat4.create();
1048
- // Create a 3D vector for scaling factors
1049
- const scaleVec = glmatrix.vec3.fromValues(this.linearScalingFactor, this.linearScalingFactor, this.linearScalingFactor);
1050
- // Scale the matrix
1051
- glmatrix.mat4.scale(scaleMatrix, scaleMatrix, scaleVec);
1052
- // Perform the matrix multiplications
1053
- if (newMatrix !== void 0) {
1054
- glmatrix.mat4.multiply(newTransform, coordinationMatrix, newMatrix);
1055
- glmatrix.mat4.multiply(newTransform, newTransform, translationMatrixGeomMin);
1056
- }
1057
- else {
1058
- glmatrix.mat4.multiply(newTransform, coordinationMatrix, newTransform);
1059
- glmatrix.mat4.multiply(newTransform, newTransform, translationMatrixGeomMin);
1060
- }
1061
- const newTransformArr = Array.from(newTransform);
1946
+ const newTransformArr = composeTransformF64(coordinationMatrix, geometryTransform, center);
1062
1947
  geometryMaterialTransformMap.set(expressID, [geometry.geometry, material_, newTransformArr]);
1063
1948
  if (entity?.localID !== void 0) {
1064
1949
  if (entity?.expressID !== void 0) {
@@ -1158,61 +2043,23 @@ export class IfcApiProxyIfc {
1158
2043
  if (!this._isCoordinated && this.settings?.COORDINATE_TO_ORIGIN) {
1159
2044
  nativePt = geometry.geometry.getPoint(0);
1160
2045
  }
1161
- // extract center
1162
- const geomCenter = glmatrix.vec3.create();
2046
+ // normalize() recenters the geometry buffer (side effect) and
2047
+ // returns the local centre used to place it.
1163
2048
  const center = geometry.geometry.normalize();
1164
- geomCenter[0] = center.x;
1165
- geomCenter[1] = center.y;
1166
- geomCenter[2] = center.z;
1167
- // Create a translation matrix from geom.min
1168
- const translationMatrixGeomMin = glmatrix.mat4.create();
1169
- glmatrix.mat4.fromTranslation(translationMatrixGeomMin, geomCenter);
1170
2049
  // create PlacedGeometry
1171
2050
  const expressID = model.getElementByLocalID(geometry.localID)?.expressID;
2051
+ // Full-precision float64 placement straight from the wasm boundary
2052
+ // (glm::dmat4) — never truncated through a gl-matrix Float32Array;
2053
+ // the recentre math runs in double precision (see coordination_f64).
1172
2054
  const geometryTransform = nativeTransform?.getValues();
1173
- let newMatrix;
1174
- if (geometryTransform !== void 0) {
1175
- newMatrix = glmatrix.mat4.fromValues(geometryTransform[0], geometryTransform[1], geometryTransform[2], geometryTransform[3], geometryTransform[4], geometryTransform[5], geometryTransform[6], geometryTransform[7], geometryTransform[8], geometryTransform[9], geometryTransform[10], geometryTransform[11], geometryTransform[12], geometryTransform[13], geometryTransform[14], geometryTransform[15]);
1176
- }
1177
2055
  if (!this._isCoordinated && this.settings?.COORDINATE_TO_ORIGIN) {
1178
- // coordinate the geometry to the origin
1179
- // Assuming geom.GetPoint(0) returns a glm::dvec3, i.e., a 3D vector.
1180
- // In TypeScript, you can represent it as number[] or Float64Array.
1181
2056
  Logger.info("Setting up coordinationMatrix");
1182
- const pt = [nativePt.x, nativePt.y, nativePt.z];
1183
- // Transform the point by the matrix.
1184
- const transformedPt = glmatrix.vec4.create();
1185
- glmatrix.vec4.transformMat4(transformedPt, [pt[0], pt[1], pt[2], 1], newMatrix);
1186
- // Create the translation matrix.
1187
- coordinationMatrix = glmatrix.mat4.create();
1188
- glmatrix.mat4.fromTranslation(coordinationMatrix, [-transformedPt[0], -transformedPt[1], -transformedPt[2]]);
1189
- const scaleMatrix = glmatrix.mat4.create();
1190
- // Create a 3D vector for scaling factors
1191
- const scaleVec = glmatrix.vec3.fromValues(this.linearScalingFactor, this.linearScalingFactor, this.linearScalingFactor);
1192
- // Scale the matrix
1193
- glmatrix.mat4.scale(scaleMatrix, scaleMatrix, scaleVec);
1194
- glmatrix.mat4.multiply(coordinationMatrix, this.NormalizeMat, coordinationMatrix);
1195
- glmatrix.mat4.multiply(coordinationMatrix, scaleMatrix, coordinationMatrix);
2057
+ coordinationMatrix = deriveCoordinationF64(geometryTransform, nativePt, this.NormalizeMat, this.linearScalingFactor);
2058
+ // Persisted for getAppliedCoordination (Share#1634).
2059
+ this.demandCoordination_ = Array.from(coordinationMatrix);
1196
2060
  this._isCoordinated = true;
1197
2061
  }
1198
- // extract color
1199
- const newTransform = glmatrix.mat4.create();
1200
- // Create a 4x4 identity matrix
1201
- const scaleMatrix = glmatrix.mat4.create();
1202
- // Create a 3D vector for scaling factors
1203
- const scaleVec = glmatrix.vec3.fromValues(this.linearScalingFactor, this.linearScalingFactor, this.linearScalingFactor);
1204
- // Scale the matrix
1205
- glmatrix.mat4.scale(scaleMatrix, scaleMatrix, scaleVec);
1206
- // Perform the matrix multiplications
1207
- if (newMatrix !== void 0) {
1208
- glmatrix.mat4.multiply(newTransform, coordinationMatrix, newMatrix);
1209
- glmatrix.mat4.multiply(newTransform, newTransform, translationMatrixGeomMin);
1210
- }
1211
- else {
1212
- glmatrix.mat4.multiply(newTransform, coordinationMatrix, newTransform);
1213
- glmatrix.mat4.multiply(newTransform, newTransform, translationMatrixGeomMin);
1214
- }
1215
- const newTransformArr = Array.from(newTransform);
2062
+ const newTransformArr = composeTransformF64(coordinationMatrix, geometryTransform, center);
1216
2063
  geometryMaterialTransformMap.set(expressID, [geometry.geometry, material_, newTransformArr]);
1217
2064
  if (entity?.localID !== void 0) {
1218
2065
  if (entity?.expressID !== void 0) {