@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.
- package/README.md +36 -22
- package/compiled/Dist/ConwayGeomWasmNode.js +0 -0
- package/compiled/Dist/ConwayGeomWasmNodeMT.js +0 -0
- package/compiled/Dist/ConwayGeomWasmWeb.js +0 -0
- package/compiled/Dist/ConwayGeomWasmWebMT.js +1 -1
- package/compiled/Dist/ConwayGeomWasmWebMT.wasm +0 -0
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNode.js +0 -0
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNodeMT.js +0 -0
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWeb.js +0 -0
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.js +1 -1
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.wasm +0 -0
- package/compiled/dependencies/conway-geom/interface/conway_geometry.d.ts +19 -0
- package/compiled/dependencies/conway-geom/interface/conway_geometry.d.ts.map +1 -1
- package/compiled/dependencies/conway-geom/interface/conway_geometry.js +17 -0
- package/compiled/dependencies/conway-geom/interface/parameters/surface_object.d.ts +6 -0
- package/compiled/dependencies/conway-geom/interface/parameters/surface_object.d.ts.map +1 -1
- package/compiled/examples/browser-bundled.cjs +1569 -51
- package/compiled/examples/cli-bundled.cjs +2790 -436
- package/compiled/examples/cli-step-bundled.cjs +1904 -229
- package/compiled/examples/validator-bundled.cjs +1569 -51
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_curve.gen.d.ts +2 -2
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_curve.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_curve.gen.js +2 -2
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_surface.gen.d.ts +3 -3
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_surface.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_surface.gen.js +3 -3
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/composite_curve.gen.d.ts +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/composite_curve.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/composite_curve.gen.js +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_2d.gen.d.ts +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_2d.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_2d.gen.js +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_3d.gen.d.ts +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_3d.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_3d.gen.js +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_surface.gen.d.ts +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_surface.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_surface.gen.js +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/presentation_style_assignment.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/presentation_style_assignment.gen.js +2 -2
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/schema_ap214.gen.js +11 -11
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/shape_aspect.gen.d.ts +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/shape_aspect.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/shape_aspect.gen.js +1 -1
- package/compiled/src/AP214E3_2010/ap214_command_line_main.js +7 -3
- package/compiled/src/AP214E3_2010/ap214_geometry_extraction.d.ts +181 -7
- package/compiled/src/AP214E3_2010/ap214_geometry_extraction.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/ap214_geometry_extraction.js +906 -137
- package/compiled/src/AP214E3_2010/ap214_geometry_extraction.test.js +10 -2
- package/compiled/src/AP214E3_2010/ap214_logical_attribute.test.d.ts +2 -0
- package/compiled/src/AP214E3_2010/ap214_logical_attribute.test.d.ts.map +1 -0
- package/compiled/src/AP214E3_2010/ap214_logical_attribute.test.js +114 -0
- package/compiled/src/AP214E3_2010/ap214_model_geometry.d.ts +6 -0
- package/compiled/src/AP214E3_2010/ap214_model_geometry.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/ap214_model_geometry.js +9 -0
- package/compiled/src/AP214E3_2010/ap214_pcurve_basis_surfaces.test.d.ts +2 -0
- package/compiled/src/AP214E3_2010/ap214_pcurve_basis_surfaces.test.d.ts.map +1 -0
- package/compiled/src/AP214E3_2010/ap214_pcurve_basis_surfaces.test.js +233 -0
- package/compiled/src/AP214E3_2010/ap214_sphere_vertex_loop.test.d.ts +2 -0
- package/compiled/src/AP214E3_2010/ap214_sphere_vertex_loop.test.d.ts.map +1 -0
- package/compiled/src/AP214E3_2010/ap214_sphere_vertex_loop.test.js +147 -0
- package/compiled/src/AP214E3_2010/ap214_step_model.d.ts +2 -1
- package/compiled/src/AP214E3_2010/ap214_step_model.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/ap214_step_model.js +1 -1
- package/compiled/src/AP214E3_2010/ap214_typed_select_enum.test.d.ts +2 -0
- package/compiled/src/AP214E3_2010/ap214_typed_select_enum.test.d.ts.map +1 -0
- package/compiled/src/AP214E3_2010/ap214_typed_select_enum.test.js +65 -0
- package/compiled/src/AP214E3_2010/ap214_unit_scale.test.d.ts +2 -0
- package/compiled/src/AP214E3_2010/ap214_unit_scale.test.d.ts.map +1 -0
- package/compiled/src/AP214E3_2010/ap214_unit_scale.test.js +111 -0
- package/compiled/src/compat/web-ifc/ap214_streamed_open.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ap214_streamed_open.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ap214_streamed_open.test.js +190 -0
- package/compiled/src/compat/web-ifc/coordination_export_order.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/coordination_export_order.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/coordination_export_order.test.js +110 -0
- package/compiled/src/compat/web-ifc/coordination_f64.d.ts +165 -0
- package/compiled/src/compat/web-ifc/coordination_f64.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/coordination_f64.js +286 -0
- package/compiled/src/compat/web-ifc/coordination_f64.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/coordination_f64.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/coordination_f64.test.js +225 -0
- package/compiled/src/compat/web-ifc/ifc_api.d.ts +154 -0
- package/compiled/src/compat/web-ifc/ifc_api.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api.js +211 -3
- package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.js +288 -0
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough.d.ts +39 -0
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.d.ts +30 -0
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.js +83 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.js +383 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_coordination.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_coordination.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_coordination.test.js +124 -0
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.d.ts +132 -0
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.js +488 -92
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts +268 -0
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.js +996 -149
- package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.js +129 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.d.ts +134 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.js +933 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.test.js +467 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.d.ts +187 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.js +524 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.test.js +234 -0
- package/compiled/src/compat/web-ifc/streamed_preview_channel.d.ts +306 -0
- package/compiled/src/compat/web-ifc/streamed_preview_channel.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/streamed_preview_channel.js +580 -0
- package/compiled/src/compat/web-ifc/web_wasm_directory.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/web_wasm_directory.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/web_wasm_directory.test.js +22 -0
- package/compiled/src/core/composed_model_skeleton.d.ts +63 -0
- package/compiled/src/core/composed_model_skeleton.d.ts.map +1 -0
- package/compiled/src/core/composed_model_skeleton.js +66 -0
- package/compiled/src/core/composed_model_skeleton.test.d.ts +2 -0
- package/compiled/src/core/composed_model_skeleton.test.d.ts.map +1 -0
- package/compiled/src/core/composed_model_skeleton.test.js +69 -0
- package/compiled/src/core/demand_geometry_queue.d.ts +147 -0
- package/compiled/src/core/demand_geometry_queue.d.ts.map +1 -0
- package/compiled/src/core/demand_geometry_queue.js +223 -0
- package/compiled/src/core/demand_geometry_queue.test.d.ts +2 -0
- package/compiled/src/core/demand_geometry_queue.test.d.ts.map +1 -0
- package/compiled/src/core/demand_geometry_queue.test.js +133 -0
- package/compiled/src/core/demand_residency_pump.d.ts +93 -0
- package/compiled/src/core/demand_residency_pump.d.ts.map +1 -0
- package/compiled/src/core/demand_residency_pump.js +120 -0
- package/compiled/src/core/demand_residency_pump.test.d.ts +2 -0
- package/compiled/src/core/demand_residency_pump.test.d.ts.map +1 -0
- package/compiled/src/core/demand_residency_pump.test.js +148 -0
- package/compiled/src/core/geometry_tile_bindings.d.ts +110 -0
- package/compiled/src/core/geometry_tile_bindings.d.ts.map +1 -0
- package/compiled/src/core/geometry_tile_bindings.js +76 -0
- package/compiled/src/core/geometry_tile_bindings.test.d.ts +2 -0
- package/compiled/src/core/geometry_tile_bindings.test.d.ts.map +1 -0
- package/compiled/src/core/geometry_tile_bindings.test.js +211 -0
- package/compiled/src/core/geometry_tile_pool.d.ts +99 -0
- package/compiled/src/core/geometry_tile_pool.d.ts.map +1 -0
- package/compiled/src/core/geometry_tile_pool.js +104 -0
- package/compiled/src/core/geometry_tile_pool.test.d.ts +2 -0
- package/compiled/src/core/geometry_tile_pool.test.d.ts.map +1 -0
- package/compiled/src/core/geometry_tile_pool.test.js +180 -0
- package/compiled/src/core/mem/chunked_pool.d.ts +116 -0
- package/compiled/src/core/mem/chunked_pool.d.ts.map +1 -0
- package/compiled/src/core/mem/chunked_pool.js +149 -0
- package/compiled/src/core/mem/chunked_pool.test.d.ts +2 -0
- package/compiled/src/core/mem/chunked_pool.test.d.ts.map +1 -0
- package/compiled/src/core/mem/chunked_pool.test.js +63 -0
- package/compiled/src/core/mem/shared_asset_pool.d.ts +82 -0
- package/compiled/src/core/mem/shared_asset_pool.d.ts.map +1 -0
- package/compiled/src/core/mem/shared_asset_pool.js +113 -0
- package/compiled/src/core/mem/shared_asset_pool.test.d.ts +2 -0
- package/compiled/src/core/mem/shared_asset_pool.test.d.ts.map +1 -0
- package/compiled/src/core/mem/shared_asset_pool.test.js +49 -0
- package/compiled/src/core/model_registry.d.ts +87 -0
- package/compiled/src/core/model_registry.d.ts.map +1 -0
- package/compiled/src/core/model_registry.js +101 -0
- package/compiled/src/core/model_registry.test.d.ts +2 -0
- package/compiled/src/core/model_registry.test.d.ts.map +1 -0
- package/compiled/src/core/model_registry.test.js +60 -0
- package/compiled/src/core/model_uri.d.ts +57 -0
- package/compiled/src/core/model_uri.d.ts.map +1 -0
- package/compiled/src/core/model_uri.js +101 -0
- package/compiled/src/core/model_uri.test.d.ts +2 -0
- package/compiled/src/core/model_uri.test.d.ts.map +1 -0
- package/compiled/src/core/model_uri.test.js +49 -0
- package/compiled/src/core/progress.d.ts +16 -2
- package/compiled/src/core/progress.d.ts.map +1 -1
- package/compiled/src/core/progress.js +31 -5
- package/compiled/src/core/progress.test.js +11 -2
- package/compiled/src/core/progress_log.d.ts +1 -0
- package/compiled/src/core/progress_log.d.ts.map +1 -1
- package/compiled/src/core/progress_log.js +7 -2
- package/compiled/src/core/progress_log.test.js +21 -0
- package/compiled/src/core/shared_byte_budget.d.ts +60 -0
- package/compiled/src/core/shared_byte_budget.d.ts.map +1 -0
- package/compiled/src/core/shared_byte_budget.js +85 -0
- package/compiled/src/core/shared_byte_budget.test.d.ts +2 -0
- package/compiled/src/core/shared_byte_budget.test.d.ts.map +1 -0
- package/compiled/src/core/shared_byte_budget.test.js +55 -0
- package/compiled/src/core/wasm_heap.d.ts +208 -0
- package/compiled/src/core/wasm_heap.d.ts.map +1 -0
- package/compiled/src/core/wasm_heap.js +417 -0
- package/compiled/src/core/wasm_heap.test.d.ts +2 -0
- package/compiled/src/core/wasm_heap.test.d.ts.map +1 -0
- package/compiled/src/core/wasm_heap.test.js +546 -0
- package/compiled/src/demand/index.d.ts +23 -0
- package/compiled/src/demand/index.d.ts.map +1 -0
- package/compiled/src/demand/index.js +22 -0
- package/compiled/src/ifc/ifc4_gen/IfcLogical.gen.d.ts +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcLogical.gen.d.ts.map +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcLogical.gen.js +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcPresentationStyleAssignment.gen.d.ts.map +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcPresentationStyleAssignment.gen.js +2 -2
- package/compiled/src/ifc/ifc4_gen/IfcRelInterferesElements.gen.d.ts +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcRelInterferesElements.gen.d.ts.map +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcRelInterferesElements.gen.js +1 -1
- package/compiled/src/ifc/ifc4_gen/schema_ifc.gen.js +2 -2
- package/compiled/src/ifc/ifc_demand_extraction.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_demand_extraction.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_demand_extraction.test.js +251 -0
- package/compiled/src/ifc/ifc_geometry_extraction.d.ts +367 -6
- package/compiled/src/ifc/ifc_geometry_extraction.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_geometry_extraction.js +1322 -389
- package/compiled/src/ifc/ifc_grid_placement.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_grid_placement.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_grid_placement.test.js +261 -0
- package/compiled/src/ifc/ifc_model_geometry.d.ts +12 -0
- package/compiled/src/ifc/ifc_model_geometry.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_model_geometry.js +15 -0
- package/compiled/src/ifc/ifc_regression_batch_main.js +210 -12
- package/compiled/src/ifc/ifc_spatial_skeleton.d.ts +145 -0
- package/compiled/src/ifc/ifc_spatial_skeleton.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_spatial_skeleton.js +266 -0
- package/compiled/src/ifc/ifc_spatial_skeleton.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_spatial_skeleton.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_spatial_skeleton.test.js +199 -0
- package/compiled/src/ifc/ifc_step_model.d.ts +6 -2
- package/compiled/src/ifc/ifc_step_model.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_step_model.js +6 -4
- package/compiled/src/ifc/ifc_step_parser.d.ts +58 -0
- package/compiled/src/ifc/ifc_step_parser.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_step_parser.js +64 -0
- package/compiled/src/ifc/ifc_stream_model.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_stream_model.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_stream_model.test.js +70 -0
- package/compiled/src/ifc/ifc_stream_open.d.ts +131 -0
- package/compiled/src/ifc/ifc_stream_open.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_stream_open.js +116 -0
- package/compiled/src/ifc/ifc_stream_open.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_stream_open.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_stream_open.test.js +230 -0
- package/compiled/src/ifc/ifc_tile_extractor.d.ts +84 -0
- package/compiled/src/ifc/ifc_tile_extractor.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_tile_extractor.js +134 -0
- package/compiled/src/ifc/ifc_tile_extractor.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_tile_extractor.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_tile_extractor.test.js +118 -0
- package/compiled/src/ifc/ifc_typed_select_enum.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_typed_select_enum.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_typed_select_enum.test.js +60 -0
- package/compiled/src/index.d.ts +21 -1
- package/compiled/src/index.d.ts.map +1 -1
- package/compiled/src/index.js +28 -1
- package/compiled/src/logging/logger.d.ts +16 -2
- package/compiled/src/logging/logger.d.ts.map +1 -1
- package/compiled/src/logging/logger.js +34 -7
- package/compiled/src/logging/logger.test.js +30 -0
- package/compiled/src/mem/index.d.ts +17 -0
- package/compiled/src/mem/index.d.ts.map +1 -0
- package/compiled/src/mem/index.js +16 -0
- package/compiled/src/namespace_surface.test.d.ts +2 -0
- package/compiled/src/namespace_surface.test.d.ts.map +1 -0
- package/compiled/src/namespace_surface.test.js +43 -0
- package/compiled/src/parsing/parsing_buffer.d.ts +23 -0
- package/compiled/src/parsing/parsing_buffer.d.ts.map +1 -1
- package/compiled/src/parsing/parsing_buffer.js +31 -0
- package/compiled/src/scripts/model_report_displacement.test.d.ts +2 -0
- package/compiled/src/scripts/model_report_displacement.test.d.ts.map +1 -0
- package/compiled/src/scripts/model_report_displacement.test.js +104 -0
- package/compiled/src/scripts/render_glb_paths.test.d.ts +2 -0
- package/compiled/src/scripts/render_glb_paths.test.d.ts.map +1 -0
- package/compiled/src/scripts/render_glb_paths.test.js +115 -0
- package/compiled/src/step/indexing/step_type_indexer.d.ts +20 -0
- package/compiled/src/step/indexing/step_type_indexer.d.ts.map +1 -1
- package/compiled/src/step/indexing/step_type_indexer.js +67 -0
- package/compiled/src/step/parsing/byte_source.d.ts +153 -0
- package/compiled/src/step/parsing/byte_source.d.ts.map +1 -0
- package/compiled/src/step/parsing/byte_source.js +106 -0
- package/compiled/src/step/parsing/byte_source.test.d.ts +2 -0
- package/compiled/src/step/parsing/byte_source.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/byte_source.test.js +47 -0
- package/compiled/src/step/parsing/byte_source_node.d.ts +41 -0
- package/compiled/src/step/parsing/byte_source_node.d.ts.map +1 -0
- package/compiled/src/step/parsing/byte_source_node.js +54 -0
- package/compiled/src/step/parsing/columnar_index.d.ts +103 -0
- package/compiled/src/step/parsing/columnar_index.d.ts.map +1 -0
- package/compiled/src/step/parsing/columnar_index.js +224 -0
- package/compiled/src/step/parsing/columnar_index.test.d.ts +2 -0
- package/compiled/src/step/parsing/columnar_index.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/columnar_index.test.js +155 -0
- package/compiled/src/step/parsing/cross_reference_registry.d.ts +95 -0
- package/compiled/src/step/parsing/cross_reference_registry.d.ts.map +1 -0
- package/compiled/src/step/parsing/cross_reference_registry.js +91 -0
- package/compiled/src/step/parsing/cross_reference_registry.test.d.ts +2 -0
- package/compiled/src/step/parsing/cross_reference_registry.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/cross_reference_registry.test.js +66 -0
- package/compiled/src/step/parsing/decode_utf8.d.ts +15 -0
- package/compiled/src/step/parsing/decode_utf8.d.ts.map +1 -1
- package/compiled/src/step/parsing/decode_utf8.js +28 -0
- package/compiled/src/step/parsing/express_ref_scan.d.ts +20 -0
- package/compiled/src/step/parsing/express_ref_scan.d.ts.map +1 -0
- package/compiled/src/step/parsing/express_ref_scan.js +91 -0
- package/compiled/src/step/parsing/express_ref_scan.test.d.ts +2 -0
- package/compiled/src/step/parsing/express_ref_scan.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/express_ref_scan.test.js +28 -0
- package/compiled/src/step/parsing/index_sidecar.d.ts +81 -0
- package/compiled/src/step/parsing/index_sidecar.d.ts.map +1 -0
- package/compiled/src/step/parsing/index_sidecar.js +283 -0
- package/compiled/src/step/parsing/index_sidecar.test.d.ts +2 -0
- package/compiled/src/step/parsing/index_sidecar.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/index_sidecar.test.js +122 -0
- package/compiled/src/step/parsing/inf_recovery_index.test.d.ts +2 -0
- package/compiled/src/step/parsing/inf_recovery_index.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/inf_recovery_index.test.js +108 -0
- package/compiled/src/step/parsing/prefix_type_index.d.ts +124 -0
- package/compiled/src/step/parsing/prefix_type_index.d.ts.map +1 -0
- package/compiled/src/step/parsing/prefix_type_index.js +207 -0
- package/compiled/src/step/parsing/prefix_type_index.test.d.ts +2 -0
- package/compiled/src/step/parsing/prefix_type_index.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/prefix_type_index.test.js +298 -0
- package/compiled/src/step/parsing/range_byte_source.d.ts +69 -0
- package/compiled/src/step/parsing/range_byte_source.d.ts.map +1 -0
- package/compiled/src/step/parsing/range_byte_source.js +78 -0
- package/compiled/src/step/parsing/range_byte_source.test.d.ts +2 -0
- package/compiled/src/step/parsing/range_byte_source.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/range_byte_source.test.js +70 -0
- package/compiled/src/step/parsing/record_event.d.ts +23 -0
- package/compiled/src/step/parsing/record_event.d.ts.map +1 -0
- package/compiled/src/step/parsing/record_event.js +1 -0
- package/compiled/src/step/parsing/record_field_cursor.d.ts +94 -0
- package/compiled/src/step/parsing/record_field_cursor.d.ts.map +1 -0
- package/compiled/src/step/parsing/record_field_cursor.js +189 -0
- package/compiled/src/step/parsing/step_deserialization_functions.d.ts +29 -0
- package/compiled/src/step/parsing/step_deserialization_functions.d.ts.map +1 -1
- package/compiled/src/step/parsing/step_deserialization_functions.js +45 -0
- package/compiled/src/step/parsing/step_deserialization_functions.test.js +59 -1
- package/compiled/src/step/parsing/step_parser.d.ts +66 -1
- package/compiled/src/step/parsing/step_parser.d.ts.map +1 -1
- package/compiled/src/step/parsing/step_parser.js +164 -10
- package/compiled/src/step/parsing/step_string_parser.js +8 -8
- package/compiled/src/step/parsing/streaming_index_builder.d.ts +141 -0
- package/compiled/src/step/parsing/streaming_index_builder.d.ts.map +1 -0
- package/compiled/src/step/parsing/streaming_index_builder.js +280 -0
- package/compiled/src/step/parsing/streaming_index_builder.test.d.ts +2 -0
- package/compiled/src/step/parsing/streaming_index_builder.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/streaming_index_builder.test.js +107 -0
- package/compiled/src/step/parsing/streaming_index_builder_async.test.d.ts +2 -0
- package/compiled/src/step/parsing/streaming_index_builder_async.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/streaming_index_builder_async.test.js +109 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.d.ts +71 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.d.ts.map +1 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.js +88 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.test.d.ts +2 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.test.js +66 -0
- package/compiled/src/step/parsing/uint32_sink.d.ts +72 -0
- package/compiled/src/step/parsing/uint32_sink.d.ts.map +1 -0
- package/compiled/src/step/parsing/uint32_sink.js +180 -0
- package/compiled/src/step/parsing/uint32_sink.test.d.ts +2 -0
- package/compiled/src/step/parsing/uint32_sink.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/uint32_sink.test.js +199 -0
- package/compiled/src/step/step_buffer_provider.d.ts +48 -3
- package/compiled/src/step/step_buffer_provider.d.ts.map +1 -1
- package/compiled/src/step/step_buffer_provider.js +60 -11
- package/compiled/src/step/step_buffer_provider.test.js +14 -0
- package/compiled/src/step/step_entity_base.d.ts +65 -3
- package/compiled/src/step/step_entity_base.d.ts.map +1 -1
- package/compiled/src/step/step_entity_base.js +135 -3
- package/compiled/src/step/step_model_base.d.ts +221 -8
- package/compiled/src/step/step_model_base.d.ts.map +1 -1
- package/compiled/src/step/step_model_base.js +542 -7
- package/compiled/src/stream/index.d.ts +34 -0
- package/compiled/src/stream/index.d.ts.map +1 -0
- package/compiled/src/stream/index.js +32 -0
- package/compiled/src/version/version.js +1 -1
- package/compiled/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -1
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
import { BlendMode, } from "../../dependencies/conway-geom/index.js";
|
|
2
|
+
import { Uint32Sink } from "../step/parsing/uint32_sink.js";
|
|
3
|
+
import { StepBufferNotResidentError } from "../step/step_buffer_provider.js";
|
|
2
4
|
import { exponentToRoughness } from "../core/canonical_material.js";
|
|
3
5
|
import { CanonicalMeshType } from "../core/canonical_mesh.js";
|
|
4
6
|
import { yieldToEventLoop } from "../core/progress.js";
|
|
5
7
|
import { ObjectPool } from "../core/native_pool.js";
|
|
6
8
|
import { ExtractResult } from "../core/shared_constants.js";
|
|
7
|
-
import { IfcArbitraryClosedProfileDef, IfcAxis2Placement2D, IfcAxis2Placement3D, IfcBooleanResult, IfcCartesianPointList2D, IfcCartesianPointList3D, IfcCartesianTransformationOperator3D, IfcCircleProfileDef, IfcColourRgb, IfcCompositeProfileDef, IfcExtrudedAreaSolid, IfcGridPlacement, IfcIndexedPolyCurve, IfcIndexedPolygonalFaceWithVoids, IfcLocalPlacement, IfcMappedItem, IfcOpeningElement, IfcOpeningStandardCase, IfcPolygonalFaceSet, IfcProduct, IfcReflectanceMethodEnum, IfcSpace, IfcSpecularExponent, IfcStyledItem, IfcSurfaceStyle, IfcSurfaceStyleRefraction, IfcSurfaceStyleRendering, IfcSurfaceStyleShading, IfcPresentationStyleAssignment, IfcSurfaceSide, IfcBooleanClippingResult, IfcCompositeCurve, IfcPolyline, IfcTrimmedCurve, IfcCartesianPoint, IfcParameterValue, IfcTrimmingPreference, IfcCircle, IfcHalfSpaceSolid, IfcPlane, IfcBoundingBox, IfcShellBasedSurfaceModel, IfcPolyLoop, IfcFacetedBrep, IfcRelAssociatesMaterial, IfcShapeRepresentation, IfcMaterialList, IfcMaterialDefinitionRepresentation, IfcMaterialLayerSetUsage, IfcMaterial, IfcRelVoidsElement, IfcRectangleProfileDef, IfcMaterialProfileSet, IfcMaterialConstituentSet, IfcMaterialConstituent, IfcMaterialProfile, IfcProject, IfcSIUnit, IfcSIPrefix, IfcUnitEnum, IfcSIUnitName, IfcConversionBasedUnit, IfcFaceBasedSurfaceModel, IfcArbitraryProfileDefWithVoids, IfcAdvancedBrep, IfcBSplineCurve, IfcBSplineSurface, IfcBSplineSurfaceWithKnots, IfcRationalBSplineSurfaceWithKnots, IfcCylindricalSurface, IfcSurfaceOfRevolution, IfcSurfaceOfLinearExtrusion, IfcRationalBSplineCurveWithKnots, IfcArbitraryOpenProfileDef, IfcBSplineCurveWithKnots, IfcEdgeLoop, IfcEdgeCurve, IfcVertexPoint, IfcRectangleHollowProfileDef, IfcEllipseProfileDef, IfcCircleHollowProfileDef, IfcIShapeProfileDef, IfcUShapeProfileDef, IfcDerivedProfileDef, IfcCartesianTransformationOperator2DnonUniform, IfcLShapeProfileDef, IfcCShapeProfileDef, IfcTShapeProfileDef, IfcZShapeProfileDef, IfcRelAggregates, IfcTriangulatedFaceSet, IfcPolygonalBoundedHalfSpace, IfcLine, IfcEllipse, IfcMaterialLayerSet, IfcSweptDiskSolid, IfcBlock, IfcRoundedRectangleProfileDef, IfcMaterialProfileSetUsage, IfcCompositeCurveSegment, IfcRevolvedAreaSolid, IfcFaceSurface, } from "./ifc4_gen/index.js";
|
|
9
|
+
import { IfcArbitraryClosedProfileDef, IfcAxis2Placement2D, IfcAxis2Placement3D, IfcBooleanResult, IfcCartesianPointList2D, IfcCartesianPointList3D, IfcCartesianTransformationOperator3D, IfcCircleProfileDef, IfcColourRgb, IfcCompositeProfileDef, IfcDirection, IfcExtrudedAreaSolid, IfcGrid, IfcGridPlacement, IfcIndexedPolyCurve, IfcIndexedPolygonalFaceWithVoids, IfcLocalPlacement, IfcMappedItem, IfcOpeningElement, IfcOpeningStandardCase, IfcPolygonalFaceSet, IfcProduct, IfcReflectanceMethodEnum, IfcSpace, IfcSpecularExponent, IfcStyledItem, IfcSurfaceStyle, IfcSurfaceStyleRefraction, IfcSurfaceStyleRendering, IfcSurfaceStyleShading, IfcPresentationStyleAssignment, IfcSurfaceSide, IfcBooleanClippingResult, IfcCompositeCurve, IfcPolyline, IfcTrimmedCurve, IfcCartesianPoint, IfcParameterValue, IfcTrimmingPreference, IfcCircle, IfcHalfSpaceSolid, IfcPlane, IfcBoundingBox, IfcShellBasedSurfaceModel, IfcPolyLoop, IfcFacetedBrep, IfcRelAssociatesMaterial, IfcShapeRepresentation, IfcMaterialList, IfcMaterialDefinitionRepresentation, IfcMaterialLayerSetUsage, IfcMaterial, IfcRelVoidsElement, IfcRectangleProfileDef, IfcMaterialProfileSet, IfcMaterialConstituentSet, IfcMaterialConstituent, IfcMaterialProfile, IfcProject, IfcSIUnit, IfcSIPrefix, IfcUnitEnum, IfcSIUnitName, IfcConversionBasedUnit, IfcFaceBasedSurfaceModel, IfcArbitraryProfileDefWithVoids, IfcAdvancedBrep, IfcBSplineCurve, IfcBSplineSurface, IfcBSplineSurfaceWithKnots, IfcRationalBSplineSurfaceWithKnots, IfcCylindricalSurface, IfcSurfaceOfRevolution, IfcSurfaceOfLinearExtrusion, IfcRationalBSplineCurveWithKnots, IfcArbitraryOpenProfileDef, IfcBSplineCurveWithKnots, IfcEdgeLoop, IfcEdgeCurve, IfcVertexPoint, IfcRectangleHollowProfileDef, IfcEllipseProfileDef, IfcCircleHollowProfileDef, IfcIShapeProfileDef, IfcUShapeProfileDef, IfcDerivedProfileDef, IfcCartesianTransformationOperator2DnonUniform, IfcLShapeProfileDef, IfcCShapeProfileDef, IfcTShapeProfileDef, IfcZShapeProfileDef, IfcRelAggregates, IfcTriangulatedFaceSet, IfcPolygonalBoundedHalfSpace, IfcLine, IfcEllipse, IfcMaterialLayerSet, IfcSweptDiskSolid, IfcBlock, IfcRoundedRectangleProfileDef, IfcMaterialProfileSetUsage, IfcCompositeCurveSegment, IfcRevolvedAreaSolid, IfcFaceSurface, IfcVirtualGridIntersection, } from "./ifc4_gen/index.js";
|
|
8
10
|
import EntityTypesIfc from "./ifc4_gen/entity_types_ifc.gen.js";
|
|
9
11
|
import { IfcSceneBuilder } from "./ifc_scene_builder.js";
|
|
10
12
|
import Logger from "../logging/logger.js";
|
|
13
|
+
import { arraysToWasmHeap, arrayToWasmHeap, freeAll, wasmHeapView, withRelease, } from "../core/wasm_heap.js";
|
|
11
14
|
import { REFLECTANCE_METHOD_PERMISSIVE, MATERIAL_RELATED_OBJECTS_PERMISSIVE } from "./ifc_parser_quirks.js";
|
|
12
15
|
import { CsgOperationType } from "../core/csg_operations.js";
|
|
13
16
|
import { MemoizationCapture, RegressionCaptureState } from "../core/regression_capture_state.js";
|
|
14
17
|
// Await cadence for extractIFCGeometryDataAsync: long enough that yielding
|
|
15
18
|
// doesn't dominate, short enough that browser paint + watchdogs stay live.
|
|
16
19
|
const DEFAULT_GEOMETRY_YIELD_INTERVAL_MS = 50;
|
|
20
|
+
// IfcVirtualGridIntersection.IntersectingAxes is LIST [2:2] and
|
|
21
|
+
// OffsetDistances LIST [2:3], the third entry displacing the intersection
|
|
22
|
+
// along the grid placement's z axis.
|
|
23
|
+
const GRID_INTERSECTION_AXIS_COUNT = 2;
|
|
24
|
+
const GRID_INTERSECTION_Z_OFFSET = 2;
|
|
25
|
+
// Sine of the smallest angle two grid axes may cross at and still be treated
|
|
26
|
+
// as intersecting. Grid axes cross at architectural angles, so this only has
|
|
27
|
+
// to separate "crossing" from "drawn along each other" (or from an axis with
|
|
28
|
+
// no length at all).
|
|
29
|
+
const GRID_AXIS_PARALLEL_EPSILON = 1e-9;
|
|
17
30
|
/**
|
|
18
31
|
* Extract a specular highlight, converting specular exponents to a roughness value.
|
|
19
32
|
*
|
|
@@ -108,23 +121,44 @@ export function extractColorOrFactorMultiply(from, surfaceColor, alpha = 1) {
|
|
|
108
121
|
* Handles Geometry data extraction from a populated IfcStepModel
|
|
109
122
|
* Can export to OBJ, GLTF (Draco), GLB (Draco)
|
|
110
123
|
*/
|
|
124
|
+
/**
|
|
125
|
+
* Starting capacities for the reused polygonal-faceset sinks: enough that a
|
|
126
|
+
* typical faceset never reallocates, while still growing for outliers.
|
|
127
|
+
*/
|
|
128
|
+
/**
|
|
129
|
+
* Vtable coordinates of IfcPolygonalFaceSet.Faces and
|
|
130
|
+
* IfcIndexedPolygonalFace.CoordIndex, mirroring their generated
|
|
131
|
+
* getters. The equivalence test in uint32_sink.test.ts fails loudly if
|
|
132
|
+
* codegen ever moves them.
|
|
133
|
+
*/
|
|
134
|
+
const FACES_FIELD_OFFSET = 2;
|
|
135
|
+
const FACES_FIELD_BASE_OFFSET = 1;
|
|
136
|
+
const FACES_FIELD_DEPTH = 4;
|
|
137
|
+
/** IfcRelAggregates.RelatedObjects — see IfcRelAggregates.gen.ts. */
|
|
138
|
+
const REL_AGGREGATES_RELATED_OFFSET = 5;
|
|
139
|
+
const REL_AGGREGATES_RELATED_BASE = 4;
|
|
140
|
+
const REL_AGGREGATES_RELATED_DEPTH = 3;
|
|
141
|
+
/** IfcRelAssociates.RelatedObjects — see IfcRelAssociates.gen.ts. */
|
|
142
|
+
const REL_ASSOCIATES_RELATED_OFFSET = 4;
|
|
143
|
+
const REL_ASSOCIATES_RELATED_BASE = 4;
|
|
144
|
+
const REL_ASSOCIATES_RELATED_DEPTH = 2;
|
|
145
|
+
const COORD_INDEX_FIELD_OFFSET = 0;
|
|
146
|
+
const POLYGONAL_INDEX_SINK_CAPACITY = 65536;
|
|
147
|
+
const POLYGONAL_FACE_SINK_CAPACITY = 16384;
|
|
111
148
|
export class IfcGeometryExtraction {
|
|
112
|
-
/**
|
|
113
|
-
* Construct a geometry extraction from an IFC step model and conway model
|
|
114
|
-
*
|
|
115
|
-
* @param conwayModel The ConwayGeometry instance to use for geometry operations.
|
|
116
|
-
* @param model The IfcStepModel instance representing the IFC model.
|
|
117
|
-
* @param limitCSGDepth Whether to limit the depth of CSG operations.
|
|
118
|
-
* @param csgDepthLimit The maximum depth for CSG operations when limit CSG depth is used,
|
|
119
|
-
* or the maximum level for CSG memoization if it is not.
|
|
120
|
-
* @param lowMemoryMode Whether to enable low memory mode for geometry extraction.
|
|
121
|
-
*/
|
|
122
149
|
constructor(conwayModel, model, limitCSGDepth = true, csgDepthLimit = 20, lowMemoryMode = false) {
|
|
123
150
|
this.conwayModel = conwayModel;
|
|
124
151
|
this.model = model;
|
|
125
152
|
this.limitCSGDepth = limitCSGDepth;
|
|
126
153
|
this.csgDepthLimit = csgDepthLimit;
|
|
127
154
|
this.lowMemoryMode = lowMemoryMode;
|
|
155
|
+
/* Reused across polygonal-faceset extractions; see extractPolygonalFaceSet. */
|
|
156
|
+
this.polygonalIndexSink_ = new Uint32Sink(POLYGONAL_INDEX_SINK_CAPACITY);
|
|
157
|
+
this.polygonalStartIndexSink_ = new Uint32Sink(POLYGONAL_FACE_SINK_CAPACITY);
|
|
158
|
+
this.polygonalFaceOffsetSink_ = new Uint32Sink(POLYGONAL_FACE_SINK_CAPACITY);
|
|
159
|
+
this.polygonalStartOffsetSink_ = new Uint32Sink(POLYGONAL_FACE_SINK_CAPACITY);
|
|
160
|
+
/* Guards the sinks against a re-entrant faceset extraction. */
|
|
161
|
+
this.polygonalSinksInUse_ = false;
|
|
128
162
|
this.TWO_DIMENSIONS = 2;
|
|
129
163
|
this.THREE_DIMENSIONS = 3;
|
|
130
164
|
this.circleSegments = 12;
|
|
@@ -138,8 +172,28 @@ export class IfcGeometryExtraction {
|
|
|
138
172
|
*/
|
|
139
173
|
this.geometryTypeCounts = new Map();
|
|
140
174
|
this.csgDepth = 0;
|
|
175
|
+
/** When true, a product whose ObjectPlacement — or any record in its
|
|
176
|
+
* placement chain — is a dangling forward reference is treated as not
|
|
177
|
+
* yet extractable (the extraction throws, and the caller skips the
|
|
178
|
+
* product) instead of being silently extracted UNPLACED.
|
|
179
|
+
*
|
|
180
|
+
* Set by the parse-time preview channel's PREFIX extractions. A
|
|
181
|
+
* mid-parse prefix nulls dangling optional references
|
|
182
|
+
* (`nullOnErrors`), so a product whose placement record sits beyond
|
|
183
|
+
* the prefix — Revit writes placements near the end of the file, e.g.
|
|
184
|
+
* Snowdon's first door is record #5014 with its IFCLOCALPLACEMENT at
|
|
185
|
+
* #789283 — read as "no placement" and extracted at the origin. On a
|
|
186
|
+
* georeferenced model the preview then composed
|
|
187
|
+
* coordination x identity, emitting geometry ~425km from the model
|
|
188
|
+
* (Share#1744). The durable extraction runs on the full model, where
|
|
189
|
+
* nothing dangles, and keeps the classic lenient read. */
|
|
190
|
+
this.deferDanglingPlacements = false;
|
|
141
191
|
this.freeVectorPolygonalFaces_ = [];
|
|
192
|
+
// Guards prepareExtractionMaps_ so the demand path and the whole-model
|
|
193
|
+
// walk can both trigger it without duplicating work.
|
|
194
|
+
this.extractionMapsPrepared_ = false;
|
|
142
195
|
this.csgMemoization = !this.lowMemoryMode;
|
|
196
|
+
this.quietRecoverableLogging = false;
|
|
143
197
|
this.materials = model.materials;
|
|
144
198
|
this.scene = new IfcSceneBuilder(model, conwayModel, this.materials);
|
|
145
199
|
this.voidMaterials = model.voidMaterials;
|
|
@@ -523,20 +577,173 @@ export class IfcGeometryExtraction {
|
|
|
523
577
|
this.model.voidGeometry.add(canonicalMesh);
|
|
524
578
|
}
|
|
525
579
|
}
|
|
580
|
+
/**
|
|
581
|
+
* Hand a faceset's accumulated indices to wasm and register the
|
|
582
|
+
* resulting mesh. Shared by the fast reference-level path and the
|
|
583
|
+
* generated-getter fallback so they cannot diverge.
|
|
584
|
+
*
|
|
585
|
+
* @param entity The faceset.
|
|
586
|
+
* @param temporary Is this a temporary (preview//operand) extraction?
|
|
587
|
+
* @param isRelVoid Is this geometry a rel-void operand?
|
|
588
|
+
* @param allIndices Face indices, concatenated.
|
|
589
|
+
* @param allStartIndices Per-face start indices.
|
|
590
|
+
* @param polygonalFaceBufferOffsets Per-face offsets into allIndices.
|
|
591
|
+
* @param startIndicesBufferOffsets Per-face offsets into allStartIndices.
|
|
592
|
+
* @return {ExtractResult} The extraction result.
|
|
593
|
+
*/
|
|
594
|
+
finishPolygonalFaceSet_(entity, temporary, isRelVoid, allIndices, allStartIndices, polygonalFaceBufferOffsets, startIndicesBufferOffsets) {
|
|
595
|
+
// Add the final entry
|
|
596
|
+
polygonalFaceBufferOffsets.push(allIndices.length);
|
|
597
|
+
startIndicesBufferOffsets.push(allStartIndices.length);
|
|
598
|
+
// Views over exactly the appended elements — no intermediate copy.
|
|
599
|
+
const indicesArray = allIndices.view;
|
|
600
|
+
const startIndicesArray = allStartIndices.view;
|
|
601
|
+
const polygonalFaceBufferOffsetsArray = polygonalFaceBufferOffsets.view;
|
|
602
|
+
const startIndicesBufferOffsetsArray = startIndicesBufferOffsets.view;
|
|
603
|
+
// All four together, so a failed allocation part-way through cannot strand
|
|
604
|
+
// the ones already taken. Everything after this point is inside the
|
|
605
|
+
// try/finally below, which is what actually guarantees they come back.
|
|
606
|
+
const [indicesArrayPtr, startIndicesArrayPtr, polygonalFaceBufferOffsetsArrayPtr, startIndicesBufferOffsetsArrayPtr,] = arraysToWasmHeap(this.wasmModule, [
|
|
607
|
+
indicesArray,
|
|
608
|
+
startIndicesArray,
|
|
609
|
+
polygonalFaceBufferOffsetsArray,
|
|
610
|
+
startIndicesBufferOffsetsArray,
|
|
611
|
+
]);
|
|
612
|
+
// A throw here used to strand all four heap pointers plus the pooled
|
|
613
|
+
// parse buffer, once per faceset (15,777 on PSB — a leak large enough
|
|
614
|
+
// to cause the exhaustion it would then be blamed on). withRelease
|
|
615
|
+
// reclaims them; it suppresses a release failure only while something
|
|
616
|
+
// is already unwinding, so a wasm-runtime fault is not replaced by a
|
|
617
|
+
// generic teardown error in the per-record catch upstream.
|
|
618
|
+
const geometry = withRelease(() => {
|
|
619
|
+
const pointsParseBuffer = this.conwayModel.nativeParseBuffer();
|
|
620
|
+
// Packed triangulation: the four heap buffers are already the
|
|
621
|
+
// face index layout. Unpacking them into 9.1M IndexedPolygonalFace
|
|
622
|
+
// std::vectors was the rest of the PSB pump after #448, and is
|
|
623
|
+
// why feeding the ThreadPool lost (the pool was paying to copy
|
|
624
|
+
// those vectors).
|
|
625
|
+
const pointsArrayNative = withRelease(() => {
|
|
626
|
+
if (!entity.Coordinates.extractParseBuffer(0, 0, 0, pointsParseBuffer, this.wasmModule, true)) {
|
|
627
|
+
pointsParseBuffer.resize(0);
|
|
628
|
+
}
|
|
629
|
+
return this.wasmModule.parseVertexVector(pointsParseBuffer);
|
|
630
|
+
}, () => this.conwayModel.freeParseBuffer(pointsParseBuffer));
|
|
631
|
+
return withRelease(() => this.conwayModel.getPolygonalFaceSetGeometryPacked(pointsArrayNative, indicesArrayPtr, polygonalFaceBufferOffsetsArrayPtr, polygonalFaceBufferOffsetsArray.length, startIndicesArrayPtr, startIndicesBufferOffsetsArrayPtr), () => pointsArrayNative.delete());
|
|
632
|
+
},
|
|
633
|
+
// freeAll, not four statements in one closure: there the first
|
|
634
|
+
// failure would abandon the other three.
|
|
635
|
+
() => freeAll(this.wasmModule, [
|
|
636
|
+
indicesArrayPtr,
|
|
637
|
+
startIndicesArrayPtr,
|
|
638
|
+
polygonalFaceBufferOffsetsArrayPtr,
|
|
639
|
+
startIndicesBufferOffsetsArrayPtr,
|
|
640
|
+
]));
|
|
641
|
+
const canonicalMesh = {
|
|
642
|
+
type: CanonicalMeshType.BUFFER_GEOMETRY,
|
|
643
|
+
geometry: geometry,
|
|
644
|
+
localID: entity.localID,
|
|
645
|
+
model: this.model,
|
|
646
|
+
temporary: temporary,
|
|
647
|
+
};
|
|
648
|
+
if (!isRelVoid) {
|
|
649
|
+
this.model.geometry.add(canonicalMesh);
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
this.model.voidGeometry.add(canonicalMesh);
|
|
653
|
+
}
|
|
654
|
+
return ExtractResult.COMPLETE;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Extract a polygonal faceset.
|
|
658
|
+
*
|
|
659
|
+
* The index sinks are extractor-scoped, so a nested faceset extraction
|
|
660
|
+
* reached from inside this one would silently clobber them. Nothing
|
|
661
|
+
* recurses there today, so this guards the invariant rather than a
|
|
662
|
+
* live bug — but the release must survive a throw: per-record
|
|
663
|
+
* extraction errors are caught upstream and are expected on malformed
|
|
664
|
+
* models, and a leaked flag would turn one bad record into a failure
|
|
665
|
+
* for every remaining faceset.
|
|
666
|
+
*
|
|
667
|
+
* @param entity The faceset.
|
|
668
|
+
* @param temporary Is this a temporary (preview/operand) extraction?
|
|
669
|
+
* @param isRelVoid Is this geometry a rel-void operand?
|
|
670
|
+
* @return {ExtractResult} The extraction result.
|
|
671
|
+
*/
|
|
672
|
+
extractPolygonalFaceSet(entity, temporary = false, isRelVoid = false) {
|
|
673
|
+
if (this.polygonalSinksInUse_) {
|
|
674
|
+
throw new Error("extractPolygonalFaceSet re-entered: the shared index sinks are already in use");
|
|
675
|
+
}
|
|
676
|
+
this.polygonalSinksInUse_ = true;
|
|
677
|
+
try {
|
|
678
|
+
return this.extractPolygonalFaceSetGuarded_(entity, temporary, isRelVoid);
|
|
679
|
+
}
|
|
680
|
+
finally {
|
|
681
|
+
this.polygonalSinksInUse_ = false;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
526
684
|
/**
|
|
527
685
|
* @param entity
|
|
528
686
|
* @param temporary
|
|
529
687
|
* @param isRelVoid
|
|
530
688
|
* @return {ExtractResult}
|
|
531
689
|
*/
|
|
532
|
-
|
|
690
|
+
extractPolygonalFaceSetGuarded_(entity, temporary, isRelVoid) {
|
|
533
691
|
const result = ExtractResult.COMPLETE;
|
|
534
|
-
//
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
692
|
+
// Reusable typed sinks. A tessellated model can carry millions of
|
|
693
|
+
// IfcIndexedPolygonalFace records, and the boxed `Array< number >`
|
|
694
|
+
// this loop used to build per face — which the generated CoordIndex
|
|
695
|
+
// getter also cached on the face, keeping it alive — dominated both
|
|
696
|
+
// GC time and retained heap during extraction. Indices are parsed
|
|
697
|
+
// straight from the STEP buffer into these instead, reused across
|
|
698
|
+
// facesets.
|
|
699
|
+
const allIndices = this.polygonalIndexSink_;
|
|
700
|
+
const allStartIndices = this.polygonalStartIndexSink_;
|
|
701
|
+
const polygonalFaceBufferOffsets = this.polygonalFaceOffsetSink_;
|
|
702
|
+
const startIndicesBufferOffsets = this.polygonalStartOffsetSink_;
|
|
703
|
+
allIndices.reset();
|
|
704
|
+
allStartIndices.reset();
|
|
705
|
+
polygonalFaceBufferOffsets.reset();
|
|
706
|
+
startIndicesBufferOffsets.reset();
|
|
707
|
+
// Fast path: read each face's CoordIndex straight from its record,
|
|
708
|
+
// never constructing the 9M+ IfcIndexedPolygonalFace entities a
|
|
709
|
+
// large tessellated model would otherwise materialise (and retain,
|
|
710
|
+
// since the generated Faces getter caches them). It handles only
|
|
711
|
+
// plain, single-class IFCINDEXEDPOLYGONALFACE records; anything
|
|
712
|
+
// else (voided faces, inline elements, multi-mapped records)
|
|
713
|
+
// abandons the whole faceset back to the getter path below, so
|
|
714
|
+
// correctness never depends on the fast path's coverage.
|
|
715
|
+
let fastPathOk = true;
|
|
716
|
+
let hintLocalID;
|
|
717
|
+
const facesWalked = entity.forEachReferenceInField(FACES_FIELD_OFFSET, FACES_FIELD_BASE_OFFSET, FACES_FIELD_DEPTH, (expressID) => {
|
|
718
|
+
if (expressID === void 0) {
|
|
719
|
+
fastPathOk = false;
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
const localID = this.model.resolveExpressID(expressID, hintLocalID);
|
|
723
|
+
if (localID === void 0) {
|
|
724
|
+
fastPathOk = false;
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
const count = this.model.extractIntegerArrayByLocalIDInto(localID, COORD_INDEX_FIELD_OFFSET, EntityTypesIfc.IFCINDEXEDPOLYGONALFACE, allIndices);
|
|
728
|
+
if (count === void 0) {
|
|
729
|
+
fastPathOk = false;
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
hintLocalID = localID;
|
|
733
|
+
polygonalFaceBufferOffsets.push(allIndices.length - count);
|
|
734
|
+
startIndicesBufferOffsets.push(allStartIndices.length);
|
|
735
|
+
allStartIndices.push(0);
|
|
736
|
+
return true;
|
|
737
|
+
});
|
|
738
|
+
if (fastPathOk && facesWalked) {
|
|
739
|
+
this.finishPolygonalFaceSet_(entity, temporary, isRelVoid, allIndices, allStartIndices, polygonalFaceBufferOffsets, startIndicesBufferOffsets);
|
|
740
|
+
return result;
|
|
741
|
+
}
|
|
742
|
+
// Fallback: something in this faceset needs the generated getters.
|
|
743
|
+
allIndices.reset();
|
|
744
|
+
allStartIndices.reset();
|
|
745
|
+
polygonalFaceBufferOffsets.reset();
|
|
746
|
+
startIndicesBufferOffsets.reset();
|
|
540
747
|
// Prepare indices and start indices for all faces
|
|
541
748
|
entity.Faces.forEach((polygonalFace) => {
|
|
542
749
|
// set the offets for the memory buffers so we can rebuild the
|
|
@@ -545,68 +752,29 @@ export class IfcGeometryExtraction {
|
|
|
545
752
|
startIndicesBufferOffsets.push(allStartIndices.length);
|
|
546
753
|
let coordIndex = 0;
|
|
547
754
|
if (polygonalFace instanceof IfcIndexedPolygonalFaceWithVoids) {
|
|
548
|
-
|
|
755
|
+
// Voided faces are rare; keep the straightforward getter path.
|
|
756
|
+
const indicesPerFace = polygonalFace.CoordIndex.length;
|
|
549
757
|
allStartIndices.push(0);
|
|
550
758
|
coordIndex += indicesPerFace;
|
|
551
|
-
|
|
759
|
+
for (const index of polygonalFace.CoordIndex) {
|
|
760
|
+
allIndices.push(index);
|
|
761
|
+
}
|
|
552
762
|
polygonalFace.InnerCoordIndices.forEach((innerIndices) => {
|
|
553
763
|
allStartIndices.push(coordIndex);
|
|
554
|
-
|
|
764
|
+
for (const index of innerIndices) {
|
|
765
|
+
allIndices.push(index);
|
|
766
|
+
}
|
|
555
767
|
coordIndex += innerIndices.length;
|
|
556
768
|
});
|
|
557
769
|
}
|
|
558
770
|
else {
|
|
559
|
-
|
|
771
|
+
// CoordIndex is vtable offset 0 on IfcIndexedPolygonalFace (see
|
|
772
|
+
// its generated getter) — parsed in place, never allocated or cached.
|
|
773
|
+
polygonalFace.extractIntegerArrayInto(0, 0, 3, allIndices);
|
|
560
774
|
allStartIndices.push(0);
|
|
561
|
-
allIndices.push(...polygonalFace.CoordIndex);
|
|
562
775
|
}
|
|
563
776
|
});
|
|
564
|
-
|
|
565
|
-
polygonalFaceBufferOffsets.push(allIndices.length);
|
|
566
|
-
startIndicesBufferOffsets.push(allStartIndices.length);
|
|
567
|
-
// Convert to typed arrays for transfer to WebAssembly
|
|
568
|
-
const indicesArray = new Uint32Array(allIndices);
|
|
569
|
-
const indicesArrayPtr = this.arrayToWasmHeap(indicesArray);
|
|
570
|
-
const startIndicesArray = new Uint32Array(allStartIndices);
|
|
571
|
-
const startIndicesArrayPtr = this.arrayToWasmHeap(startIndicesArray);
|
|
572
|
-
const polygonalFaceBufferOffsetsArray = new Uint32Array(polygonalFaceBufferOffsets);
|
|
573
|
-
const polygonalFaceBufferOffsetsArrayPtr = this.arrayToWasmHeap(polygonalFaceBufferOffsetsArray);
|
|
574
|
-
const startIndicesBufferOffsetsArray = new Uint32Array(startIndicesBufferOffsets);
|
|
575
|
-
const startIndicesBufferOffsetsArrayPtr = this.arrayToWasmHeap(startIndicesBufferOffsetsArray);
|
|
576
|
-
const polygonalFaceVector = this.wasmModule.buildIndexedPolygonalFaceVector(indicesArrayPtr, indicesArray.length, startIndicesArrayPtr, polygonalFaceBufferOffsetsArrayPtr, polygonalFaceBufferOffsets.length, startIndicesBufferOffsetsArrayPtr, startIndicesBufferOffsets.length);
|
|
577
|
-
const pointsParseBuffer = this.conwayModel.nativeParseBuffer();
|
|
578
|
-
if (!entity.Coordinates.extractParseBuffer(0, 0, 0, pointsParseBuffer, this.wasmModule, true)) {
|
|
579
|
-
pointsParseBuffer.resize(0);
|
|
580
|
-
}
|
|
581
|
-
const pointsArrayNative = this.wasmModule.parseVertexVector(pointsParseBuffer);
|
|
582
|
-
this.conwayModel.freeParseBuffer(pointsParseBuffer);
|
|
583
|
-
const parameters = {
|
|
584
|
-
indicesPerFace: indicesPerFace,
|
|
585
|
-
points: pointsArrayNative,
|
|
586
|
-
faces: polygonalFaceVector,
|
|
587
|
-
};
|
|
588
|
-
const geometry = this.conwayModel.getPolygonalFaceSetGeometry(parameters);
|
|
589
|
-
// free allocated wasm vectors
|
|
590
|
-
pointsArrayNative.delete();
|
|
591
|
-
this.wasmModule._free(indicesArrayPtr);
|
|
592
|
-
this.wasmModule._free(startIndicesArrayPtr);
|
|
593
|
-
this.wasmModule._free(polygonalFaceBufferOffsetsArrayPtr);
|
|
594
|
-
this.wasmModule._free(startIndicesBufferOffsetsArrayPtr);
|
|
595
|
-
polygonalFaceVector.delete();
|
|
596
|
-
const canonicalMesh = {
|
|
597
|
-
type: CanonicalMeshType.BUFFER_GEOMETRY,
|
|
598
|
-
geometry: geometry,
|
|
599
|
-
localID: entity.localID,
|
|
600
|
-
model: this.model,
|
|
601
|
-
temporary: temporary,
|
|
602
|
-
};
|
|
603
|
-
// add mesh to the list of mesh objects
|
|
604
|
-
if (!isRelVoid) {
|
|
605
|
-
this.model.geometry.add(canonicalMesh);
|
|
606
|
-
}
|
|
607
|
-
else {
|
|
608
|
-
this.model.voidGeometry.add(canonicalMesh);
|
|
609
|
-
}
|
|
777
|
+
this.finishPolygonalFaceSet_(entity, temporary, isRelVoid, allIndices, allStartIndices, polygonalFaceBufferOffsets, startIndicesBufferOffsets);
|
|
610
778
|
return result;
|
|
611
779
|
}
|
|
612
780
|
/**
|
|
@@ -614,14 +782,7 @@ export class IfcGeometryExtraction {
|
|
|
614
782
|
* @return {number} Pointer/memory address
|
|
615
783
|
*/
|
|
616
784
|
arrayToWasmHeap(array) {
|
|
617
|
-
|
|
618
|
-
const bytesPerElement = array.BYTES_PER_ELEMENT;
|
|
619
|
-
const numBytes = array.length * bytesPerElement;
|
|
620
|
-
const arrayPtr = this.wasmModule._malloc(numBytes);
|
|
621
|
-
// Create a new Uint8Array view on the Wasm memory buffer, then set the array to it
|
|
622
|
-
const arrayWasm = new Uint8Array(this.wasmModule.HEAPU8.buffer, arrayPtr, numBytes);
|
|
623
|
-
arrayWasm.set(new Uint8Array(array.buffer));
|
|
624
|
-
return arrayPtr;
|
|
785
|
+
return arrayToWasmHeap(this.wasmModule, array);
|
|
625
786
|
}
|
|
626
787
|
/**
|
|
627
788
|
* @param array
|
|
@@ -2646,9 +2807,13 @@ export class IfcGeometryExtraction {
|
|
|
2646
2807
|
pointer = this.wasmModule._malloc(numBytes);
|
|
2647
2808
|
capacity = maxPossibleFloats;
|
|
2648
2809
|
}
|
|
2649
|
-
// 2) Create a Float64Array view into WASM memory
|
|
2650
|
-
//
|
|
2651
|
-
|
|
2810
|
+
// 2) Create a Float64Array view into WASM memory, over the heap as it is
|
|
2811
|
+
// after the _malloc above rather than over whatever view the module had
|
|
2812
|
+
// cached before it - see wasm_heap.ts for why those can differ. Built
|
|
2813
|
+
// rather than subarray'd off HEAPF64 for the same reason: subarray clamps
|
|
2814
|
+
// an out-of-range window into a short one, and the set() loop below would
|
|
2815
|
+
// then quietly write somewhere other than the allocation (#485).
|
|
2816
|
+
const wasmFloat64View = wasmHeapView(this.wasmModule, Float64Array, pointer, capacity);
|
|
2652
2817
|
// 3) Single pass to skip consecutive duplicates, fill up the wasm array
|
|
2653
2818
|
let offset = 0;
|
|
2654
2819
|
let prevLocalID = -1;
|
|
@@ -3893,6 +4058,341 @@ export class IfcGeometryExtraction {
|
|
|
3893
4058
|
.getAxis2Placement3D(axis2Placement3DParameters);
|
|
3894
4059
|
this.voidScene.addTransform(parentLocalId, axis2PlacementTransform.getValues(), axis2PlacementTransform);
|
|
3895
4060
|
}
|
|
4061
|
+
/**
|
|
4062
|
+
* Resolve and extract a product's placement with dangling references
|
|
4063
|
+
* treated as errors (deferDanglingPlacements mode).
|
|
4064
|
+
*
|
|
4065
|
+
* The model's `nullOnErrors` (default true) turns a reference to an
|
|
4066
|
+
* unindexed record into a silent null, which is indistinguishable from
|
|
4067
|
+
* a genuinely absent (`$`) optional attribute. On a mid-parse prefix
|
|
4068
|
+
* that conflation is exactly wrong: "placement record not indexed
|
|
4069
|
+
* yet" must defer the product, while "product has no placement" may
|
|
4070
|
+
* extract unplaced, matching the classic path. Flipping
|
|
4071
|
+
* `nullOnErrors` off for the duration makes the step layer throw on
|
|
4072
|
+
* the dangling read (`extractElement`), anywhere in the chain —
|
|
4073
|
+
* IFCLOCALPLACEMENT ancestors and their axis/point records included,
|
|
4074
|
+
* since `extractPlacement` resolves them through the same layer.
|
|
4075
|
+
*
|
|
4076
|
+
* The throw intentionally propagates: the demand seam
|
|
4077
|
+
* (`extractProductGeometryByLocalID` callers, the preview adapter's
|
|
4078
|
+
* per-unit catch) already treats a throwing product as
|
|
4079
|
+
* not-yet-extractable and leaves it to the durable pump — the IFC
|
|
4080
|
+
* channel's forward-only unit cursor does not re-run passed
|
|
4081
|
+
* ordinals. Nothing is memoized on the throwing path — the generated
|
|
4082
|
+
* getters only cache on successful extraction — so an adapter mode
|
|
4083
|
+
* that does retry (AP214's retryEmptyUnits, or a future IFC
|
|
4084
|
+
* equivalent) re-reads cleanly against its longer prefix.
|
|
4085
|
+
*
|
|
4086
|
+
* @param product The product whose placement to resolve.
|
|
4087
|
+
*/
|
|
4088
|
+
extractPlacementStrict_(product) {
|
|
4089
|
+
const model = this.model;
|
|
4090
|
+
const priorNullOnErrors = model.nullOnErrors;
|
|
4091
|
+
model.nullOnErrors = false;
|
|
4092
|
+
try {
|
|
4093
|
+
const objectPlacement = product.ObjectPlacement;
|
|
4094
|
+
if (objectPlacement !== null) {
|
|
4095
|
+
this.extractPlacement(objectPlacement);
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
finally {
|
|
4099
|
+
model.nullOnErrors = priorNullOnErrors;
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
/**
|
|
4103
|
+
* Reduce a grid axis to an unbounded line in the plane of the grid's
|
|
4104
|
+
* placement: a point on it, and its tangent there.
|
|
4105
|
+
*
|
|
4106
|
+
* Only the straight axis curves are handled — an IfcLine, or the single
|
|
4107
|
+
* segment of a two-point IfcPolyline — which is what grid axes are in
|
|
4108
|
+
* practice. Anything else returns undefined after warning, so the
|
|
4109
|
+
* placement lands in errors.csv rather than being dropped in silence.
|
|
4110
|
+
*
|
|
4111
|
+
* `SameSense` is folded into the tangent because everything downstream is
|
|
4112
|
+
* measured from it: the offset in IfcVirtualGridIntersection is normal to
|
|
4113
|
+
* the tangent, and the tangent is also an IfcGridPlacement's default
|
|
4114
|
+
* x-axis, so a reversed axis offsets to the other side and points the
|
|
4115
|
+
* other way.
|
|
4116
|
+
*
|
|
4117
|
+
* @param axis The grid axis to reduce.
|
|
4118
|
+
* @return {{x, y, dx, dy} | undefined} A point and (unnormalised) tangent
|
|
4119
|
+
* in the grid's coordinate system, or undefined for an axis curve this
|
|
4120
|
+
* does not handle.
|
|
4121
|
+
*/
|
|
4122
|
+
static gridAxisLine(axis) {
|
|
4123
|
+
const curve = axis.AxisCurve;
|
|
4124
|
+
let x;
|
|
4125
|
+
let y;
|
|
4126
|
+
let dx;
|
|
4127
|
+
let dy;
|
|
4128
|
+
if (curve instanceof IfcPolyline) {
|
|
4129
|
+
const points = curve.Points;
|
|
4130
|
+
if (points.length !== 2) {
|
|
4131
|
+
// A multi-segment axis needs the segment nearest the intersection
|
|
4132
|
+
// picked per axis pair, which this does not do.
|
|
4133
|
+
Logger.warning("IfcGridPlacement: grid axis IfcPolyline is not a single segment.", axis.expressID);
|
|
4134
|
+
return void 0;
|
|
4135
|
+
}
|
|
4136
|
+
const start = points[0].Coordinates;
|
|
4137
|
+
const end = points[1].Coordinates;
|
|
4138
|
+
x = start[0];
|
|
4139
|
+
y = start[1];
|
|
4140
|
+
dx = end[0] - start[0];
|
|
4141
|
+
dy = end[1] - start[1];
|
|
4142
|
+
}
|
|
4143
|
+
else if (curve instanceof IfcLine) {
|
|
4144
|
+
const origin = curve.Pnt.Coordinates;
|
|
4145
|
+
const direction = curve.Dir.Orientation.DirectionRatios;
|
|
4146
|
+
// IfcVector.Magnitude scales the direction; the axis is unbounded
|
|
4147
|
+
// here, so it changes neither the line nor the normalised tangent.
|
|
4148
|
+
x = origin[0];
|
|
4149
|
+
y = origin[1];
|
|
4150
|
+
dx = direction[0];
|
|
4151
|
+
dy = direction[1];
|
|
4152
|
+
}
|
|
4153
|
+
else {
|
|
4154
|
+
Logger.warning(`IfcGridPlacement: unsupported grid axis curve, type: ${EntityTypesIfc[curve.type]}`, axis.expressID);
|
|
4155
|
+
return void 0;
|
|
4156
|
+
}
|
|
4157
|
+
if (!axis.SameSense) {
|
|
4158
|
+
dx = -dx;
|
|
4159
|
+
dy = -dy;
|
|
4160
|
+
}
|
|
4161
|
+
return { x: x, y: y, dx: dx, dy: dy };
|
|
4162
|
+
}
|
|
4163
|
+
/**
|
|
4164
|
+
* Resolve an IfcVirtualGridIntersection to a point in the coordinate system
|
|
4165
|
+
* of the IfcGrid that owns its axes, along with the tangent of its first
|
|
4166
|
+
* axis — which is the default x-axis of an IfcGridPlacement located here.
|
|
4167
|
+
*
|
|
4168
|
+
* Per IFC4 each axis is replaced by its offset curve before intersecting:
|
|
4169
|
+
* OffsetDistances[n] is measured from axis n normal to its tangent,
|
|
4170
|
+
* positive towards the tangent turned a quarter turn anti-clockwise. Both
|
|
4171
|
+
* axes here are straight, so their offset curves are parallel to them and
|
|
4172
|
+
* the tangents are unchanged. A third offset, when the file gives one,
|
|
4173
|
+
* displaces the result along the grid placement's z axis.
|
|
4174
|
+
*
|
|
4175
|
+
* @param from The intersection to resolve.
|
|
4176
|
+
* @return {{position, tangent} | undefined} The intersection point and the
|
|
4177
|
+
* first axis' unit tangent, or undefined when the axes cannot be reduced to
|
|
4178
|
+
* two lines that meet (warned at the point of failure).
|
|
4179
|
+
*/
|
|
4180
|
+
resolveVirtualGridIntersection(from) {
|
|
4181
|
+
const axes = from.IntersectingAxes;
|
|
4182
|
+
if (axes.length < GRID_INTERSECTION_AXIS_COUNT) {
|
|
4183
|
+
// LIST [2:2] in the schema, so this is a malformed or absent list.
|
|
4184
|
+
Logger.warning("IfcGridPlacement: virtual grid intersection has fewer than two axes.", from.expressID);
|
|
4185
|
+
return void 0;
|
|
4186
|
+
}
|
|
4187
|
+
const line0 = IfcGeometryExtraction.gridAxisLine(axes[0]);
|
|
4188
|
+
const line1 = IfcGeometryExtraction.gridAxisLine(axes[1]);
|
|
4189
|
+
if (line0 === void 0 || line1 === void 0) {
|
|
4190
|
+
return void 0;
|
|
4191
|
+
}
|
|
4192
|
+
const length0 = Math.sqrt(line0.dx * line0.dx + line0.dy * line0.dy);
|
|
4193
|
+
const length1 = Math.sqrt(line1.dx * line1.dx + line1.dy * line1.dy);
|
|
4194
|
+
const cross = line0.dx * line1.dy - line0.dy * line1.dx;
|
|
4195
|
+
// |cross| / (length0 * length1) is the sine of the angle between the
|
|
4196
|
+
// axes, so the test is independent of the model's units. A zero-length
|
|
4197
|
+
// tangent (a polyline whose two points coincide) also lands here,
|
|
4198
|
+
// before it can divide its way into a NaN below.
|
|
4199
|
+
if (Math.abs(cross) <= GRID_AXIS_PARALLEL_EPSILON * length0 * length1) {
|
|
4200
|
+
Logger.warning("IfcGridPlacement: intersecting grid axes are parallel or degenerate.", from.expressID);
|
|
4201
|
+
return void 0;
|
|
4202
|
+
}
|
|
4203
|
+
const offsets = from.OffsetDistances;
|
|
4204
|
+
const offset0 = offsets[0] ?? 0;
|
|
4205
|
+
const offset1 = offsets[1] ?? 0;
|
|
4206
|
+
// Offset curve origins: the axis point pushed along the tangent's
|
|
4207
|
+
// anti-clockwise normal, (-dy, dx) normalised.
|
|
4208
|
+
const x0 = line0.x - (offset0 * line0.dy) / length0;
|
|
4209
|
+
const y0 = line0.y + (offset0 * line0.dx) / length0;
|
|
4210
|
+
const x1 = line1.x - (offset1 * line1.dy) / length1;
|
|
4211
|
+
const y1 = line1.y + (offset1 * line1.dx) / length1;
|
|
4212
|
+
// Parameter along the first offset curve where the two meet, from
|
|
4213
|
+
// crossing (p1 - p0) + t * d0 = s * d1 with d1.
|
|
4214
|
+
const t = ((x1 - x0) * line1.dy - (y1 - y0) * line1.dx) / cross;
|
|
4215
|
+
return {
|
|
4216
|
+
position: {
|
|
4217
|
+
x: x0 + t * line0.dx,
|
|
4218
|
+
y: y0 + t * line0.dy,
|
|
4219
|
+
z: offsets[GRID_INTERSECTION_Z_OFFSET] ?? 0,
|
|
4220
|
+
},
|
|
4221
|
+
tangent: { x: line0.dx / length0, y: line0.dy / length0 },
|
|
4222
|
+
};
|
|
4223
|
+
}
|
|
4224
|
+
/**
|
|
4225
|
+
* The IfcGrid that lists a given axis, or undefined if no grid does.
|
|
4226
|
+
*
|
|
4227
|
+
* IfcGridAxis's route back to its grid is the PartOfU/PartOfV/PartOfW
|
|
4228
|
+
* inverse attributes, and the generated schema layer carries no inverses,
|
|
4229
|
+
* so this is a scan of every IfcGrid's axis lists, memoised. It is built on
|
|
4230
|
+
* the first grid placement extracted and never at all for a model without
|
|
4231
|
+
* one.
|
|
4232
|
+
*
|
|
4233
|
+
* @param axis The axis to look up.
|
|
4234
|
+
* @return {IfcGrid | undefined} The grid listing the axis.
|
|
4235
|
+
*/
|
|
4236
|
+
gridByAxis(axis) {
|
|
4237
|
+
let gridByAxis = this.gridByAxis_;
|
|
4238
|
+
if (gridByAxis === void 0) {
|
|
4239
|
+
gridByAxis = new Map();
|
|
4240
|
+
for (const grid of this.model.types(IfcGrid)) {
|
|
4241
|
+
for (const gridAxis of grid.UAxes) {
|
|
4242
|
+
gridByAxis.set(gridAxis.localID, grid);
|
|
4243
|
+
}
|
|
4244
|
+
for (const gridAxis of grid.VAxes) {
|
|
4245
|
+
gridByAxis.set(gridAxis.localID, grid);
|
|
4246
|
+
}
|
|
4247
|
+
for (const gridAxis of grid.WAxes ?? []) {
|
|
4248
|
+
gridByAxis.set(gridAxis.localID, grid);
|
|
4249
|
+
}
|
|
4250
|
+
}
|
|
4251
|
+
this.gridByAxis_ = gridByAxis;
|
|
4252
|
+
}
|
|
4253
|
+
return gridByAxis.get(axis.localID);
|
|
4254
|
+
}
|
|
4255
|
+
/**
|
|
4256
|
+
* Extract an IfcGridPlacement, registering a transform against its local ID
|
|
4257
|
+
* the same way the IfcLocalPlacement arm does — same scene, same
|
|
4258
|
+
* memoisation, same composition onto the current parent.
|
|
4259
|
+
*
|
|
4260
|
+
* The origin is the virtual intersection of two grid axes and the x-axis
|
|
4261
|
+
* comes from PlacementRefDirection (defaulting to the first axis' tangent),
|
|
4262
|
+
* both expressed in the coordinate system of the IfcGrid that owns the
|
|
4263
|
+
* axes. IfcGridPlacement has no PlacementRelTo of its own in IFC4, so that
|
|
4264
|
+
* grid's object placement IS the parent: extracting it first leaves it as
|
|
4265
|
+
* the scene's current transform, which `addTransform` then composes onto.
|
|
4266
|
+
*
|
|
4267
|
+
* @param from The grid placement to extract.
|
|
4268
|
+
* @param isRelVoid Whether this placement is being resolved into the void
|
|
4269
|
+
* scene rather than the main one.
|
|
4270
|
+
*/
|
|
4271
|
+
extractGridPlacement(from, isRelVoid) {
|
|
4272
|
+
const placementLocation = from.PlacementLocation;
|
|
4273
|
+
const location = this.resolveVirtualGridIntersection(placementLocation);
|
|
4274
|
+
if (location === void 0) {
|
|
4275
|
+
// Warned where it failed, with the axis or curve responsible.
|
|
4276
|
+
return;
|
|
4277
|
+
}
|
|
4278
|
+
let xAxisRef = { x: location.tangent.x, y: location.tangent.y, z: 0 };
|
|
4279
|
+
const refDirection = from.PlacementRefDirection;
|
|
4280
|
+
if (refDirection instanceof IfcDirection) {
|
|
4281
|
+
const ratios = refDirection.DirectionRatios;
|
|
4282
|
+
xAxisRef = { x: ratios[0], y: ratios[1], z: ratios[2] ?? 0 };
|
|
4283
|
+
}
|
|
4284
|
+
else if (refDirection instanceof IfcVirtualGridIntersection) {
|
|
4285
|
+
// The x-axis runs from this placement's intersection to the reference
|
|
4286
|
+
// one. If the reference cannot be resolved the resolver has already
|
|
4287
|
+
// said why, and the schema's own default — the first axis' tangent —
|
|
4288
|
+
// stands in.
|
|
4289
|
+
const reference = this.resolveVirtualGridIntersection(refDirection);
|
|
4290
|
+
if (reference !== void 0) {
|
|
4291
|
+
xAxisRef = {
|
|
4292
|
+
x: reference.position.x - location.position.x,
|
|
4293
|
+
y: reference.position.y - location.position.y,
|
|
4294
|
+
z: reference.position.z - location.position.z,
|
|
4295
|
+
};
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
// getAxis2Placement3D below is called with a forced z of (0,0,1) and
|
|
4299
|
+
// normalizeX, and the native side (ConwayGeometryProcessor's
|
|
4300
|
+
// getAxis2Placement3D) has no degeneracy guard of its own: an xAxisRef
|
|
4301
|
+
// with nothing off that z has no component left after the projection,
|
|
4302
|
+
// normalises to NaN, and addTransform then bakes the NaN into this
|
|
4303
|
+
// placement and every descendant of it. Measured on data/grid_placement.ifc
|
|
4304
|
+
// product E, whose x axis came out (NaN,NaN,NaN) before this guard.
|
|
4305
|
+
//
|
|
4306
|
+
// Two schema-reachable states produce it, and because z is exactly +Z both
|
|
4307
|
+
// reduce to one test - the xy magnitude vanishing relative to the vector's
|
|
4308
|
+
// own, in the same units-independent form as the axis-parallel check in
|
|
4309
|
+
// resolveVirtualGridIntersection (a zero vector fails it too, 0 <= 0):
|
|
4310
|
+
//
|
|
4311
|
+
// - PlacementRefDirection is an IfcVirtualGridIntersection resolving to
|
|
4312
|
+
// the same point as PlacementLocation, so the difference above is the
|
|
4313
|
+
// zero vector;
|
|
4314
|
+
// - PlacementRefDirection is IFCDIRECTION((0.,0.,1.)), parallel to the
|
|
4315
|
+
// forced z.
|
|
4316
|
+
//
|
|
4317
|
+
// The fallback is the schema's own default for an absent
|
|
4318
|
+
// PlacementRefDirection, and it is already in hand and already known
|
|
4319
|
+
// non-degenerate: resolveVirtualGridIntersection returns the first axis'
|
|
4320
|
+
// tangent normalised, and only after rejecting a zero-length one.
|
|
4321
|
+
const xAxisRefPlanar = Math.sqrt(xAxisRef.x * xAxisRef.x + xAxisRef.y * xAxisRef.y);
|
|
4322
|
+
const xAxisRefLength = Math.sqrt(xAxisRefPlanar * xAxisRefPlanar + xAxisRef.z * xAxisRef.z);
|
|
4323
|
+
if (xAxisRefPlanar <= GRID_AXIS_PARALLEL_EPSILON * xAxisRefLength) {
|
|
4324
|
+
Logger.warning("IfcGridPlacement: the placement reference direction is parallel " +
|
|
4325
|
+
"to the grid placement's z axis, or has no length - falling back " +
|
|
4326
|
+
"to the schema default, the first axis' tangent.", from.expressID);
|
|
4327
|
+
xAxisRef = { x: location.tangent.x, y: location.tangent.y, z: 0 };
|
|
4328
|
+
}
|
|
4329
|
+
// Two guarded regions rather than one, because the two reads degrade to
|
|
4330
|
+
// DIFFERENT states and a single catch could only describe one of them
|
|
4331
|
+
// truthfully. Neither may fail the product: dropping it entirely is
|
|
4332
|
+
// strictly worse than the pre-implementation rendered-at-origin.
|
|
4333
|
+
//
|
|
4334
|
+
// Region 1 - the gridByAxis scan. It reads IfcGrid records, which are
|
|
4335
|
+
// outside the forward-reference closure a windowed source pages for a
|
|
4336
|
+
// product (the grid references the axes, not vice versa - see
|
|
4337
|
+
// ensureResidentForProductExtract), so this is the read most likely to
|
|
4338
|
+
// miss. Nothing has been applied when it throws, so the intersection
|
|
4339
|
+
// lands in the current frame - the same degraded state the no-grid arm
|
|
4340
|
+
// gives. gridByAxis memoises only a COMPLETE scan, so a later product on
|
|
4341
|
+
// a fully resident model rebuilds rather than trusting a partial index.
|
|
4342
|
+
let grid;
|
|
4343
|
+
let gridScanned = false;
|
|
4344
|
+
try {
|
|
4345
|
+
grid = this.gridByAxis(placementLocation.IntersectingAxes[0]);
|
|
4346
|
+
gridScanned = true;
|
|
4347
|
+
}
|
|
4348
|
+
catch (error) {
|
|
4349
|
+
if (!(error instanceof StepBufferNotResidentError)) {
|
|
4350
|
+
throw error;
|
|
4351
|
+
}
|
|
4352
|
+
Logger.warning("IfcGridPlacement: grid records are not resident on this " +
|
|
4353
|
+
"windowed source - placing at the grid intersection in the " +
|
|
4354
|
+
"current frame, without the grid's own placement.", from.expressID);
|
|
4355
|
+
}
|
|
4356
|
+
if (gridScanned && grid === void 0) {
|
|
4357
|
+
// Axes belonging to no grid: the intersection is still a point, but
|
|
4358
|
+
// there is no grid placement to measure it from, so it lands in
|
|
4359
|
+
// whatever frame is current.
|
|
4360
|
+
Logger.warning("IfcGridPlacement: no IfcGrid lists the axes of this placement.", from.expressID);
|
|
4361
|
+
}
|
|
4362
|
+
else if (grid !== void 0) {
|
|
4363
|
+
// Region 2 - the grid's own placement chain. extractPlacement walks
|
|
4364
|
+
// PlacementRelTo upwards and pushes a transform per level as it
|
|
4365
|
+
// returns, so a residency miss PART WAY UP leaves some ancestors
|
|
4366
|
+
// already applied. Region 1's message would be a lie about this state
|
|
4367
|
+
// (it says the grid's placement was not applied at all), and the frame
|
|
4368
|
+
// this placement then composes onto is partial rather than current.
|
|
4369
|
+
try {
|
|
4370
|
+
const gridPlacement = grid.ObjectPlacement;
|
|
4371
|
+
if (gridPlacement !== null) {
|
|
4372
|
+
this.extractPlacement(gridPlacement, isRelVoid);
|
|
4373
|
+
}
|
|
4374
|
+
}
|
|
4375
|
+
catch (error) {
|
|
4376
|
+
if (!(error instanceof StepBufferNotResidentError)) {
|
|
4377
|
+
throw error;
|
|
4378
|
+
}
|
|
4379
|
+
Logger.warning("IfcGridPlacement: the grid's own placement chain is not fully " +
|
|
4380
|
+
"resident on this windowed source - it was applied as far as it " +
|
|
4381
|
+
"reads, so this placement composes onto a partial frame.", from.expressID);
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
const placementTransform = this.conwayModel.getAxis2Placement3D({
|
|
4385
|
+
position: location.position,
|
|
4386
|
+
// A grid is planar in its own placement, so this placement's z axis is
|
|
4387
|
+
// the grid's z axis; only the x axis is steered here.
|
|
4388
|
+
zAxisRef: { x: 0, y: 0, z: 1 },
|
|
4389
|
+
xAxisRef: xAxisRef,
|
|
4390
|
+
normalizeZ: false,
|
|
4391
|
+
normalizeX: true,
|
|
4392
|
+
});
|
|
4393
|
+
const scene = isRelVoid ? this.voidScene : this.scene;
|
|
4394
|
+
scene.addTransform(from.localID, placementTransform.getValues(), placementTransform);
|
|
4395
|
+
}
|
|
3896
4396
|
/**
|
|
3897
4397
|
*
|
|
3898
4398
|
* @param from
|
|
@@ -3931,8 +4431,7 @@ export class IfcGeometryExtraction {
|
|
|
3931
4431
|
}
|
|
3932
4432
|
}
|
|
3933
4433
|
else if (from instanceof IfcGridPlacement) {
|
|
3934
|
-
|
|
3935
|
-
// Logger.warning('IfcGridPlacement: unimplemented.')
|
|
4434
|
+
this.extractGridPlacement(from, isRelVoid);
|
|
3936
4435
|
}
|
|
3937
4436
|
}
|
|
3938
4437
|
/**
|
|
@@ -4211,14 +4710,53 @@ export class IfcGeometryExtraction {
|
|
|
4211
4710
|
}
|
|
4212
4711
|
return styledItemID;
|
|
4213
4712
|
}
|
|
4713
|
+
/**
|
|
4714
|
+
* Handle an error raised while building the cross-product extraction
|
|
4715
|
+
* maps. Each map sweep reads references off relationship entities
|
|
4716
|
+
* (materials, styled items, rel-voids); a single malformed reference in
|
|
4717
|
+
* a large real-world model (STEP where an entity reference is expected)
|
|
4718
|
+
* must not abort the whole model's extraction. Both the whole-model walk
|
|
4719
|
+
* and the demand path depend on map prep being tolerant here — the
|
|
4720
|
+
* browser demand pump swallows per-batch extraction throws, so a model
|
|
4721
|
+
* that renders in prod would otherwise hard-fail the headless regression
|
|
4722
|
+
* batch. Honours the strict-mode escape hatch so a strict run still
|
|
4723
|
+
* surfaces the error.
|
|
4724
|
+
*
|
|
4725
|
+
* @param context Short label for the relationship being processed.
|
|
4726
|
+
* @param expressID The offending record's express ID (may be undefined).
|
|
4727
|
+
* @param ex The caught value.
|
|
4728
|
+
*/
|
|
4729
|
+
handleMapPrepError_(context, expressID, ex) {
|
|
4730
|
+
if (ex instanceof Error) {
|
|
4731
|
+
if (MATERIAL_RELATED_OBJECTS_PERMISSIVE) {
|
|
4732
|
+
Logger.error(`Error processing ${context} expressID: ${expressID}, error: ${ex.message}`);
|
|
4733
|
+
}
|
|
4734
|
+
else {
|
|
4735
|
+
throw ex;
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
else {
|
|
4739
|
+
Logger.error(`Unknown exception processing ${context}.`);
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4214
4742
|
/**
|
|
4215
4743
|
*
|
|
4216
4744
|
*/
|
|
4217
4745
|
populateStyledItemsMap() {
|
|
4218
4746
|
const styledItems = this.model.types(IfcStyledItem);
|
|
4219
4747
|
for (const styledItem of styledItems) {
|
|
4220
|
-
|
|
4221
|
-
|
|
4748
|
+
try {
|
|
4749
|
+
// IfcStyledItem.Item — same vtable slot as the generated
|
|
4750
|
+
// getter's extractElement(0, 0, 1, true, ...). Hydrating Item
|
|
4751
|
+
// would acquire the faceset record (often the packed geometry
|
|
4752
|
+
// itself) for every styled item at once.
|
|
4753
|
+
const itemLocalID = styledItem.extractReferenceLocalID(0, 0, 1, true);
|
|
4754
|
+
if (itemLocalID !== null) {
|
|
4755
|
+
this.materials.styledItemMap.set(itemLocalID, styledItem.localID);
|
|
4756
|
+
}
|
|
4757
|
+
}
|
|
4758
|
+
catch (ex) {
|
|
4759
|
+
this.handleMapPrepError_("IfcStyledItem", styledItem.expressID, ex);
|
|
4222
4760
|
}
|
|
4223
4761
|
}
|
|
4224
4762
|
}
|
|
@@ -4229,12 +4767,17 @@ export class IfcGeometryExtraction {
|
|
|
4229
4767
|
// populate MaterialDefinitionsMap
|
|
4230
4768
|
const materialDefinitionRepresentations = this.model.types(IfcMaterialDefinitionRepresentation);
|
|
4231
4769
|
for (const materialDefinitionRep of materialDefinitionRepresentations) {
|
|
4232
|
-
|
|
4233
|
-
for (
|
|
4234
|
-
|
|
4235
|
-
|
|
4770
|
+
try {
|
|
4771
|
+
for (const representation of materialDefinitionRep.Representations) {
|
|
4772
|
+
for (let itemIndex = 0; itemIndex < representation.Items.length; ++itemIndex) {
|
|
4773
|
+
// save mapping of IfcMaterial --> IfcStyledItem
|
|
4774
|
+
this.materials.materialDefinitionsMap.set(materialDefinitionRep.RepresentedMaterial.localID, representation.Items[itemIndex].localID);
|
|
4775
|
+
}
|
|
4236
4776
|
}
|
|
4237
4777
|
}
|
|
4778
|
+
catch (ex) {
|
|
4779
|
+
this.handleMapPrepError_("IfcMaterialDefinitionRepresentation", materialDefinitionRep.expressID, ex);
|
|
4780
|
+
}
|
|
4238
4781
|
}
|
|
4239
4782
|
}
|
|
4240
4783
|
/**
|
|
@@ -4244,17 +4787,22 @@ export class IfcGeometryExtraction {
|
|
|
4244
4787
|
// populate relvoids map
|
|
4245
4788
|
const relVoids = this.model.types(IfcRelVoidsElement);
|
|
4246
4789
|
for (const relVoid of relVoids) {
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4790
|
+
try {
|
|
4791
|
+
// map product --> relvoids opening elements
|
|
4792
|
+
const geometryLocalIDArray = this.productToVoidGeometryMap.get(relVoid.RelatingBuildingElement.localID);
|
|
4793
|
+
if (geometryLocalIDArray === void 0) {
|
|
4794
|
+
const localIDArray = [];
|
|
4795
|
+
localIDArray.push(relVoid.RelatedOpeningElement.localID);
|
|
4796
|
+
this.productToVoidGeometryMap.set(relVoid.RelatingBuildingElement.localID, localIDArray);
|
|
4797
|
+
}
|
|
4798
|
+
else {
|
|
4799
|
+
// append to it
|
|
4800
|
+
geometryLocalIDArray.push(relVoid.RelatedOpeningElement.localID);
|
|
4801
|
+
this.productToVoidGeometryMap.set(relVoid.RelatingBuildingElement.localID, geometryLocalIDArray);
|
|
4802
|
+
}
|
|
4253
4803
|
}
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
geometryLocalIDArray.push(relVoid.RelatedOpeningElement.localID);
|
|
4257
|
-
this.productToVoidGeometryMap.set(relVoid.RelatingBuildingElement.localID, geometryLocalIDArray);
|
|
4804
|
+
catch (ex) {
|
|
4805
|
+
this.handleMapPrepError_("IfcRelVoidsElement", relVoid.expressID, ex);
|
|
4258
4806
|
}
|
|
4259
4807
|
}
|
|
4260
4808
|
}
|
|
@@ -4364,6 +4912,657 @@ export class IfcGeometryExtraction {
|
|
|
4364
4912
|
fs.writeFileSync(outputFilePath, objContent, 'utf8')
|
|
4365
4913
|
} */
|
|
4366
4914
|
}
|
|
4915
|
+
/**
|
|
4916
|
+
* Local IDs of every IfcProduct that is a RelatedObject of any
|
|
4917
|
+
* IfcRelAggregates — the exact set the rel-aggregates pass
|
|
4918
|
+
* ({@link extractRelAggregateGeometry}) re-extracts with the relating
|
|
4919
|
+
* object's master rel-voids.
|
|
4920
|
+
*
|
|
4921
|
+
* Both product walks SKIP these in their first pass and extract them
|
|
4922
|
+
* only in the aggregates pass, for two invariants consumers rely on:
|
|
4923
|
+
* 1. one scene instance per placement (the first-pass node plus the
|
|
4924
|
+
* re-extraction node used to coincide exactly — a duplicate draw
|
|
4925
|
+
* that z-fights), and
|
|
4926
|
+
* 2. a part's canonical mesh content is never REPLACED after its
|
|
4927
|
+
* instances were already emitted — incremental consumers copy
|
|
4928
|
+
* content at delivery time, so a first-pass emission served the
|
|
4929
|
+
* uncut (or empty) content and the later replacement never
|
|
4930
|
+
* reached the screen (the ILNA missing-facades class).
|
|
4931
|
+
*
|
|
4932
|
+
* @return {Set<number>} product localIDs deferred to the aggregates
|
|
4933
|
+
* pass; empty for models without IfcRelAggregates.
|
|
4934
|
+
*/
|
|
4935
|
+
aggregateTargetLocalIDs() {
|
|
4936
|
+
if (this.aggregateTargetLocalIDs_ !== void 0) {
|
|
4937
|
+
return this.aggregateTargetLocalIDs_;
|
|
4938
|
+
}
|
|
4939
|
+
const targets = new Set();
|
|
4940
|
+
const productTypes = new Set(IfcProduct.query);
|
|
4941
|
+
for (const relAggregate of this.model.types(IfcRelAggregates)) {
|
|
4942
|
+
try {
|
|
4943
|
+
// RelatedObjects as express IDs — do not hydrate every child
|
|
4944
|
+
// product (that paged most of the file during demand prep).
|
|
4945
|
+
relAggregate.forEachReferenceInField(REL_AGGREGATES_RELATED_OFFSET, REL_AGGREGATES_RELATED_BASE, REL_AGGREGATES_RELATED_DEPTH, (expressID) => {
|
|
4946
|
+
if (expressID === void 0) {
|
|
4947
|
+
return true;
|
|
4948
|
+
}
|
|
4949
|
+
const relatedLocalID = this.model.resolveExpressID(expressID);
|
|
4950
|
+
if (relatedLocalID === void 0) {
|
|
4951
|
+
return true;
|
|
4952
|
+
}
|
|
4953
|
+
const typeID = this.model.typeIDOf(relatedLocalID);
|
|
4954
|
+
if (typeID !== void 0 && productTypes.has(typeID)) {
|
|
4955
|
+
targets.add(relatedLocalID);
|
|
4956
|
+
}
|
|
4957
|
+
return true;
|
|
4958
|
+
});
|
|
4959
|
+
}
|
|
4960
|
+
catch {
|
|
4961
|
+
// Malformed relationship rows are tolerated exactly like the
|
|
4962
|
+
// aggregates pass itself tolerates them (permissive catch) —
|
|
4963
|
+
// their targets simply stay in the first-pass walk.
|
|
4964
|
+
}
|
|
4965
|
+
}
|
|
4966
|
+
this.aggregateTargetLocalIDs_ = targets;
|
|
4967
|
+
return targets;
|
|
4968
|
+
}
|
|
4969
|
+
/**
|
|
4970
|
+
* One-time extraction setup shared by the whole-model walk and the
|
|
4971
|
+
* per-product demand path (Phase B2): linear scaling factor and the
|
|
4972
|
+
* cross-product maps (materials, material definitions, rel-voids, styled
|
|
4973
|
+
* items). Idempotent.
|
|
4974
|
+
*/
|
|
4975
|
+
prepareExtractionMaps_() {
|
|
4976
|
+
if (this.extractionMapsPrepared_) {
|
|
4977
|
+
return;
|
|
4978
|
+
}
|
|
4979
|
+
this.extractionMapsPrepared_ = true;
|
|
4980
|
+
this.extractLinearScalingFactor();
|
|
4981
|
+
// populate relMaterialsMap
|
|
4982
|
+
const relAssociatesMaterials = this.model.types(IfcRelAssociatesMaterial);
|
|
4983
|
+
const productTypes = new Set(IfcProduct.query);
|
|
4984
|
+
for (const relAssociateMaterial of relAssociatesMaterials) {
|
|
4985
|
+
try {
|
|
4986
|
+
// Read RelatingMaterial INSIDE the guard: a malformed material
|
|
4987
|
+
// reference (e.g. a value where an entity ref is expected) throws
|
|
4988
|
+
// "Value in STEP was incorrectly typed", and pulling this read above
|
|
4989
|
+
// the try let one bad relationship abort the whole model's extraction
|
|
4990
|
+
// instead of being tolerated like every other malformed relationship
|
|
4991
|
+
// here. The rel-aggregates loop below already reads its RelatingObject
|
|
4992
|
+
// inside its own try for exactly this reason.
|
|
4993
|
+
const relatingMaterial = relAssociateMaterial.RelatingMaterial;
|
|
4994
|
+
const materialLocalID = relatingMaterial.localID;
|
|
4995
|
+
relAssociateMaterial.forEachReferenceInField(REL_ASSOCIATES_RELATED_OFFSET, REL_ASSOCIATES_RELATED_BASE, REL_ASSOCIATES_RELATED_DEPTH, (expressID) => {
|
|
4996
|
+
if (expressID === void 0) {
|
|
4997
|
+
return true;
|
|
4998
|
+
}
|
|
4999
|
+
const productLocalID = this.model.resolveExpressID(expressID);
|
|
5000
|
+
if (productLocalID === void 0) {
|
|
5001
|
+
return true;
|
|
5002
|
+
}
|
|
5003
|
+
const typeID = this.model.typeIDOf(productLocalID);
|
|
5004
|
+
if (typeID === EntityTypesIfc.IFCOPENINGELEMENT ||
|
|
5005
|
+
typeID === EntityTypesIfc.IFCSPACE ||
|
|
5006
|
+
typeID === EntityTypesIfc.IFCOPENINGSTANDARDCASE) {
|
|
5007
|
+
return true;
|
|
5008
|
+
}
|
|
5009
|
+
if (typeID !== void 0 && productTypes.has(typeID)) {
|
|
5010
|
+
this.materials.relMaterialsMap.set(productLocalID, materialLocalID);
|
|
5011
|
+
}
|
|
5012
|
+
return true;
|
|
5013
|
+
});
|
|
5014
|
+
}
|
|
5015
|
+
catch (ex) {
|
|
5016
|
+
// Reference the relationship record, not RelatingMaterial: the
|
|
5017
|
+
// latter may be exactly what threw and would be undefined here.
|
|
5018
|
+
this.handleMapPrepError_("IfcRelAssociatesMaterial", relAssociateMaterial.expressID, ex);
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
5021
|
+
// populate MaterialDefinitionsMap
|
|
5022
|
+
this.populateMaterialDefinitionsMap();
|
|
5023
|
+
// populate relvoids map
|
|
5024
|
+
this.populateRelVoidsMap();
|
|
5025
|
+
// populate styled items map
|
|
5026
|
+
this.populateStyledItemsMap();
|
|
5027
|
+
}
|
|
5028
|
+
/**
|
|
5029
|
+
* Prepare for per-product demand extraction (Phase B2) without running the
|
|
5030
|
+
* whole-model walk: populates the shared extraction maps so
|
|
5031
|
+
* {@link extractProductGeometryByLocalID} can extract any single product.
|
|
5032
|
+
* Idempotent; the whole-model walk shares the same preparation.
|
|
5033
|
+
*
|
|
5034
|
+
* @param lightweightPreview Preview-only preparation (parse-time
|
|
5035
|
+
* preview generations): skips the relationship sweeps
|
|
5036
|
+
* (rel-materials, rel-voids) whose entity materialization dominates
|
|
5037
|
+
* preparation cost on large models — extracted products then miss
|
|
5038
|
+
* openings and rel-bound materials, which preview consumers accept
|
|
5039
|
+
* by contract. Styled-item and material-definition maps are kept
|
|
5040
|
+
* (colors), as is the linear scaling factor.
|
|
5041
|
+
*/
|
|
5042
|
+
prepareDemandExtraction(lightweightPreview = false) {
|
|
5043
|
+
if (lightweightPreview && !this.extractionMapsPrepared_) {
|
|
5044
|
+
this.extractionMapsPrepared_ = true;
|
|
5045
|
+
this.extractLinearScalingFactor();
|
|
5046
|
+
this.populateMaterialDefinitionsMap();
|
|
5047
|
+
this.populateStyledItemsMap();
|
|
5048
|
+
return;
|
|
5049
|
+
}
|
|
5050
|
+
this.prepareExtractionMaps_();
|
|
5051
|
+
}
|
|
5052
|
+
/**
|
|
5053
|
+
* Extract one product's geometry into the scene by local ID — the demand
|
|
5054
|
+
* path's entry (Phase B2). Requires {@link prepareDemandExtraction} (or a
|
|
5055
|
+
* prior whole-model walk) so the shared maps exist.
|
|
5056
|
+
*
|
|
5057
|
+
* Note: this extracts a single product with its OWN voids. Products that
|
|
5058
|
+
* are targets of IfcRelAggregates whose relating object carries rel-voids
|
|
5059
|
+
* additionally need the whole-model walk's second pass
|
|
5060
|
+
* ({@link extractRelAggregatesGeometry}) — the demand pump runs it once
|
|
5061
|
+
* after the last product batch so pumped extractions converge to the
|
|
5062
|
+
* classic walk exactly.
|
|
5063
|
+
*
|
|
5064
|
+
* @param localID The product's local ID.
|
|
5065
|
+
* @return {boolean} True if the local ID was an IfcProduct and was
|
|
5066
|
+
* extracted; false if it wasn't a product.
|
|
5067
|
+
*/
|
|
5068
|
+
extractProductGeometryByLocalID(localID) {
|
|
5069
|
+
this.prepareExtractionMaps_();
|
|
5070
|
+
const element = this.model.getElementByLocalID(localID);
|
|
5071
|
+
if (!(element instanceof IfcProduct)) {
|
|
5072
|
+
return false;
|
|
5073
|
+
}
|
|
5074
|
+
this.extractProductGeometry(element);
|
|
5075
|
+
return true;
|
|
5076
|
+
}
|
|
5077
|
+
/**
|
|
5078
|
+
* Page in every record {@link prepareDemandExtraction} will read, so
|
|
5079
|
+
* the relationship sweeps succeed on a windowed source. Call before
|
|
5080
|
+
* the first prepare on a model whose source is external.
|
|
5081
|
+
*
|
|
5082
|
+
* Pins the prep-type records themselves (and a 1-hop of product /
|
|
5083
|
+
* material records the relationship getters hydrate). Does **not**
|
|
5084
|
+
* BFS-pin styled-item Item closures — those are facesets, and
|
|
5085
|
+
* walking them here pulled most of the file into the windowed LRU
|
|
5086
|
+
* before the first product extract.
|
|
5087
|
+
*
|
|
5088
|
+
* @return {Promise<Set<number>>} Pinned local IDs — caller must
|
|
5089
|
+
* {@link StepModelBase.unpinLocalIDs} after prepare/extract.
|
|
5090
|
+
*/
|
|
5091
|
+
async ensureResidentForDemandPrep() {
|
|
5092
|
+
const pinned = new Set();
|
|
5093
|
+
if (!this.model.isSourceExternal) {
|
|
5094
|
+
return pinned;
|
|
5095
|
+
}
|
|
5096
|
+
// Small trees: project units, material-definition style stacks.
|
|
5097
|
+
for (const type of [IfcProject, IfcMaterialDefinitionRepresentation]) {
|
|
5098
|
+
for (const expressID of this.model.expressIDsOfTypes(type)) {
|
|
5099
|
+
await this.model.ensureResidentClosureByExpressID(expressID, void 0, pinned);
|
|
5100
|
+
}
|
|
5101
|
+
}
|
|
5102
|
+
const recordOnlyTypes = [
|
|
5103
|
+
IfcRelAssociatesMaterial,
|
|
5104
|
+
IfcRelVoidsElement,
|
|
5105
|
+
IfcStyledItem,
|
|
5106
|
+
IfcRelAggregates,
|
|
5107
|
+
];
|
|
5108
|
+
for (const type of recordOnlyTypes) {
|
|
5109
|
+
for (const expressID of this.model.expressIDsOfTypes(type)) {
|
|
5110
|
+
const localID = this.model.resolveExpressID(expressID);
|
|
5111
|
+
if (localID === void 0 || pinned.has(localID)) {
|
|
5112
|
+
continue;
|
|
5113
|
+
}
|
|
5114
|
+
await this.model.ensureResidentByLocalID(localID);
|
|
5115
|
+
this.model.pinByLocalID(localID);
|
|
5116
|
+
pinned.add(localID);
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
// Relationship getters hydrate the related product / material
|
|
5120
|
+
// records (not their geometry). Pin that 1-hop so populate can
|
|
5121
|
+
// read `.localID` without a full closure walk.
|
|
5122
|
+
const oneHopTypes = [
|
|
5123
|
+
IfcRelAssociatesMaterial,
|
|
5124
|
+
IfcRelVoidsElement,
|
|
5125
|
+
];
|
|
5126
|
+
for (const type of oneHopTypes) {
|
|
5127
|
+
for (const expressID of this.model.expressIDsOfTypes(type)) {
|
|
5128
|
+
const localID = this.model.resolveExpressID(expressID);
|
|
5129
|
+
if (localID === void 0) {
|
|
5130
|
+
continue;
|
|
5131
|
+
}
|
|
5132
|
+
for (const refExpressID of this.model.referencedExpressIDs(localID)) {
|
|
5133
|
+
const refLocalID = this.model.resolveExpressID(refExpressID);
|
|
5134
|
+
if (refLocalID === void 0 || pinned.has(refLocalID)) {
|
|
5135
|
+
continue;
|
|
5136
|
+
}
|
|
5137
|
+
await this.model.ensureResidentByLocalID(refLocalID);
|
|
5138
|
+
this.model.pinByLocalID(refLocalID);
|
|
5139
|
+
pinned.add(refLocalID);
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
return pinned;
|
|
5144
|
+
}
|
|
5145
|
+
/**
|
|
5146
|
+
* Page in the `#ref` closure a per-product extract will acquire,
|
|
5147
|
+
* including this product's voids, bound materials and styled items
|
|
5148
|
+
* (those are inverse — not written in the product record).
|
|
5149
|
+
*
|
|
5150
|
+
* @param localID The product's local ID.
|
|
5151
|
+
* @param seen Optional set to extend.
|
|
5152
|
+
* @param leafSpans Coalesced Faces[] pin ranges — caller unpins.
|
|
5153
|
+
* @return {Promise<Set<number>>} Pinned local IDs — caller must unpin
|
|
5154
|
+
* after extract.
|
|
5155
|
+
*/
|
|
5156
|
+
async ensureResidentForProductExtract(localID, seen, leafSpans) {
|
|
5157
|
+
if (!this.model.isSourceExternal) {
|
|
5158
|
+
return seen ?? new Set();
|
|
5159
|
+
}
|
|
5160
|
+
// Paging failures are contained to their product (conway#526).
|
|
5161
|
+
// `extractGeometryBatchAsync` prefetches a batch through one
|
|
5162
|
+
// Promise.all, so a rejection here used to escape the pump and
|
|
5163
|
+
// abort the entire load — a whole model lost to one unreadable
|
|
5164
|
+
// product. The extract that follows already tolerates a product it
|
|
5165
|
+
// cannot read (its own per-product catch logs and moves on), so
|
|
5166
|
+
// degrading to "this product is missing" is both the smaller
|
|
5167
|
+
// failure and the one the pipeline is already built for.
|
|
5168
|
+
//
|
|
5169
|
+
// Deliberately no retry: a second attempt sharing `seen` would
|
|
5170
|
+
// re-claim nothing and re-ensure nothing, and giving it a fresh set
|
|
5171
|
+
// would double-pin every record it re-walked — pins are refcounted
|
|
5172
|
+
// but the caller unpins once per unique ID, so the surplus would
|
|
5173
|
+
// hold those chunks resident for the model's life and grow the
|
|
5174
|
+
// window without bound.
|
|
5175
|
+
try {
|
|
5176
|
+
return await this.ensureResidentForProductExtractUnguarded_(localID, seen, leafSpans);
|
|
5177
|
+
}
|
|
5178
|
+
catch (error) {
|
|
5179
|
+
Logger.error(`Error paging product localID ${localID} for extract: ` +
|
|
5180
|
+
`${error instanceof Error ? error.message : String(error)}`);
|
|
5181
|
+
return seen ?? new Set();
|
|
5182
|
+
}
|
|
5183
|
+
}
|
|
5184
|
+
/**
|
|
5185
|
+
* {@link ensureResidentForProductExtract} without the per-product
|
|
5186
|
+
* fault containment — split out so the guard cannot accidentally
|
|
5187
|
+
* swallow a failure raised by its own bookkeeping.
|
|
5188
|
+
*
|
|
5189
|
+
* @param localID The product's local ID.
|
|
5190
|
+
* @param seen Optional set to extend.
|
|
5191
|
+
* @param leafSpans Coalesced Faces[] pin ranges — caller unpins.
|
|
5192
|
+
* @return {Promise<Set<number>>} Pinned local IDs.
|
|
5193
|
+
*/
|
|
5194
|
+
async ensureResidentForProductExtractUnguarded_(localID, seen, leafSpans) {
|
|
5195
|
+
const extra = [];
|
|
5196
|
+
const voids = this.productToVoidGeometryMap.get(localID);
|
|
5197
|
+
if (voids !== void 0) {
|
|
5198
|
+
extra.push(...voids);
|
|
5199
|
+
}
|
|
5200
|
+
const material = this.materials.relMaterialsMap.get(localID);
|
|
5201
|
+
if (material !== void 0) {
|
|
5202
|
+
extra.push(material);
|
|
5203
|
+
}
|
|
5204
|
+
// Facesets are visited but not scanned (Faces[] is a later span).
|
|
5205
|
+
// Face records are spanned, not visited — 78k pins / 15s otherwise.
|
|
5206
|
+
const isFace = (id) => {
|
|
5207
|
+
const typeID = this.model.typeIDOf(id);
|
|
5208
|
+
return typeID === EntityTypesIfc.IFCINDEXEDPOLYGONALFACE ||
|
|
5209
|
+
typeID === EntityTypesIfc.IFCINDEXEDPOLYGONALFACEWITHVOIDS;
|
|
5210
|
+
};
|
|
5211
|
+
const descend = (id) => {
|
|
5212
|
+
const typeID = this.model.typeIDOf(id);
|
|
5213
|
+
return !isFace(id) &&
|
|
5214
|
+
typeID !== EntityTypesIfc.IFCPOLYGONALFACESET &&
|
|
5215
|
+
typeID !== EntityTypesIfc.IFCTRIANGULATEDFACESET;
|
|
5216
|
+
};
|
|
5217
|
+
// IDs THIS call claimed and awaited, as opposed to `visited`, which
|
|
5218
|
+
// is the caller's shared set when a batch prefetches concurrently.
|
|
5219
|
+
// Only the former may be read synchronously afterwards (conway#526).
|
|
5220
|
+
const claimed = new Set();
|
|
5221
|
+
const visited = await this.model.ensureResidentClosureByLocalID(localID, extra, seen, descend, leafSpans, isFace, claimed);
|
|
5222
|
+
const styleExtra = [];
|
|
5223
|
+
for (const visitedID of visited) {
|
|
5224
|
+
const styled = this.materials.styledItemMap.get(visitedID);
|
|
5225
|
+
if (styled !== void 0) {
|
|
5226
|
+
styleExtra.push(styled);
|
|
5227
|
+
}
|
|
5228
|
+
const definition = this.materials.materialDefinitionsMap.get(visitedID);
|
|
5229
|
+
if (definition !== void 0) {
|
|
5230
|
+
styleExtra.push(definition);
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
if (styleExtra.length > 0) {
|
|
5234
|
+
await this.model.ensureResidentClosureByLocalID(localID, styleExtra, visited, descend, leafSpans, isFace, claimed);
|
|
5235
|
+
}
|
|
5236
|
+
await this.ensureResidentVisitedFacesetPayloads_(claimed, visited, leafSpans);
|
|
5237
|
+
return visited;
|
|
5238
|
+
}
|
|
5239
|
+
/**
|
|
5240
|
+
* Page a faceset's Coordinates record and its Faces[] run as one
|
|
5241
|
+
* or more tight address spans. The generic closure visits the
|
|
5242
|
+
* faceset but does not scan Faces[].
|
|
5243
|
+
*
|
|
5244
|
+
* **Iterates `claimed`, not `visited`, and re-ensures before the
|
|
5245
|
+
* scan** — both halves of conway#526. `referencedExpressIDs` is a
|
|
5246
|
+
* SYNCHRONOUS acquire of the faceset's own bytes, and when a batch
|
|
5247
|
+
* prefetches products concurrently through one shared `visited` set
|
|
5248
|
+
* (`extractGeometryBatchAsync`'s Promise.all over 8 products), the
|
|
5249
|
+
* closure walk skips any ID a sibling product already claimed. That
|
|
5250
|
+
* sibling has pinned the range but its read may still be in flight,
|
|
5251
|
+
* so scanning the shared set threw
|
|
5252
|
+
* `StepBufferNotResidentError: STEP source range [...] is not
|
|
5253
|
+
* resident` out of the prefetch and killed the whole load — a pin
|
|
5254
|
+
* reserves a chunk against eviction, it does not make it resident.
|
|
5255
|
+
* Iterating this call's own claims fixes that (and drops the
|
|
5256
|
+
* quadratic re-scan of every sibling's facesets); the `await` below
|
|
5257
|
+
* makes the guarantee local rather than inherited, since a resident
|
|
5258
|
+
* chunk costs only a microtask there.
|
|
5259
|
+
*
|
|
5260
|
+
* @param claimed Faceset local IDs this call claimed and awaited.
|
|
5261
|
+
* @param visited The (possibly shared) visited set — read for
|
|
5262
|
+
* Coordinates de-duplication and extended with the ones pinned here,
|
|
5263
|
+
* so the caller's unpin covers them.
|
|
5264
|
+
* @param leafSpans Coalesced Faces[] pin ranges — caller unpins.
|
|
5265
|
+
*/
|
|
5266
|
+
async ensureResidentVisitedFacesetPayloads_(claimed, visited, leafSpans) {
|
|
5267
|
+
for (const localID of claimed) {
|
|
5268
|
+
const typeID = this.model.typeIDOf(localID);
|
|
5269
|
+
if (typeID !== EntityTypesIfc.IFCPOLYGONALFACESET &&
|
|
5270
|
+
typeID !== EntityTypesIfc.IFCTRIANGULATEDFACESET) {
|
|
5271
|
+
continue;
|
|
5272
|
+
}
|
|
5273
|
+
// Cheap when already resident (a Map hit and a microtask), and
|
|
5274
|
+
// the difference between a guarantee and an assumption when the
|
|
5275
|
+
// window has churned since this record was claimed.
|
|
5276
|
+
await this.model.ensureResidentByLocalID(localID);
|
|
5277
|
+
const refs = this.model.referencedExpressIDs(localID);
|
|
5278
|
+
if (refs.length === 0) {
|
|
5279
|
+
continue;
|
|
5280
|
+
}
|
|
5281
|
+
// First #ref is Coordinates (IfcTessellatedFaceSet). Pin it as a
|
|
5282
|
+
// normal record — one IfcCartesianPointList3D, no child #refs.
|
|
5283
|
+
const coordsLocalID = this.model.resolveExpressID(refs[0]);
|
|
5284
|
+
if (coordsLocalID !== void 0 && !visited.has(coordsLocalID)) {
|
|
5285
|
+
visited.add(coordsLocalID);
|
|
5286
|
+
this.model.pinByLocalID(coordsLocalID);
|
|
5287
|
+
await this.model.ensureResidentByLocalID(coordsLocalID);
|
|
5288
|
+
}
|
|
5289
|
+
if (refs.length < 2) {
|
|
5290
|
+
continue;
|
|
5291
|
+
}
|
|
5292
|
+
const spans = this.model.spansOfExpressIDs(refs, 1);
|
|
5293
|
+
for (const span of spans) {
|
|
5294
|
+
this.model.pinAddressRange(span.address, span.length);
|
|
5295
|
+
leafSpans?.push(span);
|
|
5296
|
+
await this.model.ensureResidentRange(span.address, span.length);
|
|
5297
|
+
}
|
|
5298
|
+
}
|
|
5299
|
+
}
|
|
5300
|
+
/**
|
|
5301
|
+
* Page in the records {@link extractRelAggregateGeometry} will
|
|
5302
|
+
* acquire: the relationship, the relating product (and its voids)
|
|
5303
|
+
* and every related product.
|
|
5304
|
+
*
|
|
5305
|
+
* @param relAggregate The relationship to prefetch.
|
|
5306
|
+
* @return {Promise<Set<number>>} Pinned local IDs — caller must unpin
|
|
5307
|
+
* after extract.
|
|
5308
|
+
*/
|
|
5309
|
+
async ensureResidentForAggregateExtract(relAggregate, leafSpans) {
|
|
5310
|
+
if (!this.model.isSourceExternal) {
|
|
5311
|
+
return new Set();
|
|
5312
|
+
}
|
|
5313
|
+
// Pin the rel record, page its 1-hop so RelatingObject /
|
|
5314
|
+
// RelatedObjects getters can hydrate, then walk each product
|
|
5315
|
+
// through the bounded extract. The 1-hop IDs must NOT go into
|
|
5316
|
+
// `pinned`/`seen` — that would skip their geometry closures.
|
|
5317
|
+
// A full BFS of the rel used to follow every RelatedObject into
|
|
5318
|
+
// Faces[] (the 78k-pin path) before descend could skip them.
|
|
5319
|
+
const pinned = new Set();
|
|
5320
|
+
const hop = [];
|
|
5321
|
+
this.model.pinByLocalID(relAggregate.localID);
|
|
5322
|
+
pinned.add(relAggregate.localID);
|
|
5323
|
+
try {
|
|
5324
|
+
await this.model.ensureResidentByLocalID(relAggregate.localID);
|
|
5325
|
+
for (const expressID of this.model.referencedExpressIDs(relAggregate.localID)) {
|
|
5326
|
+
const localID = this.model.resolveExpressID(expressID);
|
|
5327
|
+
if (localID === void 0 || pinned.has(localID)) {
|
|
5328
|
+
continue;
|
|
5329
|
+
}
|
|
5330
|
+
this.model.pinByLocalID(localID);
|
|
5331
|
+
hop.push(localID);
|
|
5332
|
+
await this.model.ensureResidentByLocalID(localID);
|
|
5333
|
+
}
|
|
5334
|
+
const relating = relAggregate.RelatingObject;
|
|
5335
|
+
if (relating !== null && relating !== void 0) {
|
|
5336
|
+
await this.ensureResidentForProductExtract(relating.localID, pinned, leafSpans);
|
|
5337
|
+
}
|
|
5338
|
+
const related = relAggregate.RelatedObjects;
|
|
5339
|
+
if (related !== null && related !== void 0) {
|
|
5340
|
+
for (const product of related) {
|
|
5341
|
+
await this.ensureResidentForProductExtract(product.localID, pinned, leafSpans);
|
|
5342
|
+
}
|
|
5343
|
+
}
|
|
5344
|
+
return pinned;
|
|
5345
|
+
}
|
|
5346
|
+
finally {
|
|
5347
|
+
for (const localID of hop) {
|
|
5348
|
+
this.model.unpinByLocalID(localID);
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5351
|
+
}
|
|
5352
|
+
/**
|
|
5353
|
+
* The whole-model walk's SECOND product pass, shared with the demand
|
|
5354
|
+
* pump: for every IfcRelAggregates, re-extract the related products
|
|
5355
|
+
* with the relating object's rel-voids as a master override. This
|
|
5356
|
+
* re-extraction REPLACES the canonical mesh cached under each
|
|
5357
|
+
* representation item's localID (the scene resolves geometry by
|
|
5358
|
+
* localID at walk time), so aggregate parts whose parent carries
|
|
5359
|
+
* openings end up CUT — a demand pump that only ran the per-product
|
|
5360
|
+
* pass would leave them uncut and serve different GetGeometry content
|
|
5361
|
+
* than the classic walk (the vyzn romana/DOWA scatter). Call exactly
|
|
5362
|
+
* once, after every product has been extracted.
|
|
5363
|
+
*/
|
|
5364
|
+
extractRelAggregatesGeometry() {
|
|
5365
|
+
const relAggregates = this.model.types(IfcRelAggregates);
|
|
5366
|
+
for (const relAggregate of relAggregates) {
|
|
5367
|
+
this.extractRelAggregateGeometry(relAggregate);
|
|
5368
|
+
}
|
|
5369
|
+
}
|
|
5370
|
+
/**
|
|
5371
|
+
* Extract one IfcRelAggregates' related products with the relating
|
|
5372
|
+
* object's ("master") rel-voids — the shared per-item body of the
|
|
5373
|
+
* whole-model walk's rel-aggregates loop and
|
|
5374
|
+
* {@link extractRelAggregatesGeometry}. Public so the deferred pump
|
|
5375
|
+
* can run the pass incrementally (batch-budgeted) instead of as one
|
|
5376
|
+
* end-of-load stall.
|
|
5377
|
+
*
|
|
5378
|
+
* @param relAggregate The rel-aggregates relationship to process.
|
|
5379
|
+
*/
|
|
5380
|
+
extractRelAggregateGeometry(relAggregate) {
|
|
5381
|
+
try {
|
|
5382
|
+
// Note, this is required because the relating object
|
|
5383
|
+
// can be where the rel-void is applied, so we
|
|
5384
|
+
// used this as a top level over-ride for a rel-void.
|
|
5385
|
+
// for all objects in the aggregate - CS
|
|
5386
|
+
const relatingObject = relAggregate.RelatingObject;
|
|
5387
|
+
const masterRelVoids = relatingObject instanceof IfcProduct ?
|
|
5388
|
+
this.extractRelVoids(relatingObject) :
|
|
5389
|
+
void 0;
|
|
5390
|
+
const relatedObjects = relAggregate.RelatedObjects;
|
|
5391
|
+
for (const productRepresentation of relatedObjects) {
|
|
5392
|
+
if (productRepresentation instanceof IfcProduct) {
|
|
5393
|
+
this.extractProductGeometry(productRepresentation, masterRelVoids);
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5396
|
+
if (masterRelVoids !== void 0) {
|
|
5397
|
+
masterRelVoids[0].delete();
|
|
5398
|
+
}
|
|
5399
|
+
}
|
|
5400
|
+
catch (ex) {
|
|
5401
|
+
if (ex instanceof Error) {
|
|
5402
|
+
if (MATERIAL_RELATED_OBJECTS_PERMISSIVE) {
|
|
5403
|
+
Logger.error(`Error processing relAggregate\n\terror: ${ex.message}`, relAggregate.expressID);
|
|
5404
|
+
}
|
|
5405
|
+
else {
|
|
5406
|
+
throw ex;
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5409
|
+
else {
|
|
5410
|
+
Logger.error("Unknown exception processing IfcRelAssociateMaterials.");
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
/**
|
|
5415
|
+
* Extract one product's geometry into the scene: placement, material /
|
|
5416
|
+
* style extraction, representation items (including mapped items) and
|
|
5417
|
+
* rel-void application. The deduplicated body of the whole-model walk's
|
|
5418
|
+
* two product loops (plain products + rel-aggregates), shared with the
|
|
5419
|
+
* per-product demand path.
|
|
5420
|
+
*
|
|
5421
|
+
* @param product The product to extract.
|
|
5422
|
+
* @param precomputedRelVoids Optional caller-owned rel-voids (the
|
|
5423
|
+
* rel-aggregates "master" case): when supplied the caller retains
|
|
5424
|
+
* ownership and this method does not delete the mesh vector; when
|
|
5425
|
+
* omitted, voids are extracted per product and freed here.
|
|
5426
|
+
*/
|
|
5427
|
+
extractProductGeometry(product, precomputedRelVoids) {
|
|
5428
|
+
this.scene.clearParentStack();
|
|
5429
|
+
const isSpace = product instanceof IfcOpeningElement ||
|
|
5430
|
+
product instanceof IfcSpace ||
|
|
5431
|
+
product instanceof IfcOpeningStandardCase;
|
|
5432
|
+
if (this.deferDanglingPlacements) {
|
|
5433
|
+
// Strict placement resolution for prefix extractions: dangling
|
|
5434
|
+
// references throw here (see extractPlacementStrict_) and the
|
|
5435
|
+
// demand seam reports the product as not-yet-extractable —
|
|
5436
|
+
// instead of extracting the product unplaced. The IFC preview
|
|
5437
|
+
// channel's forward-only cursor does not re-run a passed unit, so
|
|
5438
|
+
// a deferred product simply never previews and the durable pump
|
|
5439
|
+
// renders it; that is the existing contract for every product
|
|
5440
|
+
// whose extraction throws on an unparsed forward reference, and
|
|
5441
|
+
// an empty slot beats geometry at the wrong placement. This read
|
|
5442
|
+
// must be the entity's FIRST ObjectPlacement access: the
|
|
5443
|
+
// generated getters memoize, and a lenient read would cache null
|
|
5444
|
+
// for a dangling reference.
|
|
5445
|
+
this.extractPlacementStrict_(product);
|
|
5446
|
+
}
|
|
5447
|
+
else {
|
|
5448
|
+
const objectPlacement = product.ObjectPlacement;
|
|
5449
|
+
if (objectPlacement !== null) {
|
|
5450
|
+
this.extractPlacement(objectPlacement);
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
const representations = product.Representation;
|
|
5454
|
+
if (representations === null) {
|
|
5455
|
+
return;
|
|
5456
|
+
}
|
|
5457
|
+
// extract styledItem material
|
|
5458
|
+
const styledItemID = this.extractMaterialStyle(product);
|
|
5459
|
+
let hasRelVoid = false;
|
|
5460
|
+
const extractRelVoidsResult = precomputedRelVoids ?? this.extractRelVoids(product);
|
|
5461
|
+
let relVoidsMeshVector;
|
|
5462
|
+
let relVoidLocalIDs;
|
|
5463
|
+
if (extractRelVoidsResult !== void 0) {
|
|
5464
|
+
[relVoidsMeshVector, relVoidLocalIDs] = extractRelVoidsResult;
|
|
5465
|
+
}
|
|
5466
|
+
if (relVoidsMeshVector !== void 0) {
|
|
5467
|
+
hasRelVoid = true;
|
|
5468
|
+
}
|
|
5469
|
+
if (styledItemID !== void 0) {
|
|
5470
|
+
// optimization: extract the first representation item and cache
|
|
5471
|
+
// the styleID to apply to the rest of the product geometry
|
|
5472
|
+
const styledItem = this.model.getElementByLocalID(styledItemID);
|
|
5473
|
+
let reusableStyleID;
|
|
5474
|
+
for (const representation of representations.Representations) {
|
|
5475
|
+
if (representation instanceof IfcShapeRepresentation) {
|
|
5476
|
+
// this check is essential -
|
|
5477
|
+
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
5478
|
+
if (representation.RepresentationIdentifier !== "Body" &&
|
|
5479
|
+
representation.RepresentationIdentifier !== "Facetation") {
|
|
5480
|
+
continue;
|
|
5481
|
+
}
|
|
5482
|
+
}
|
|
5483
|
+
for (const item of representation.Items) {
|
|
5484
|
+
if (item instanceof IfcMappedItem) {
|
|
5485
|
+
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
5486
|
+
}
|
|
5487
|
+
else {
|
|
5488
|
+
try {
|
|
5489
|
+
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
5490
|
+
if (hasRelVoid) {
|
|
5491
|
+
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
5492
|
+
}
|
|
5493
|
+
}
|
|
5494
|
+
catch (error) {
|
|
5495
|
+
if (this.quietRecoverableLogging) {
|
|
5496
|
+
// Preview prefix: expected on a truncated tail — skip.
|
|
5497
|
+
}
|
|
5498
|
+
else if (error instanceof Error) {
|
|
5499
|
+
Logger.error(`Error extracting representation item\n\t${error.message}\n\tExpress ID: #${item.expressID}`);
|
|
5500
|
+
}
|
|
5501
|
+
else {
|
|
5502
|
+
Logger.error(`Error extracting representation item\n\tExpress ID: #${item.expressID}`);
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
5507
|
+
if (styledItemLocalID_ !== undefined) {
|
|
5508
|
+
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
5509
|
+
this.extractStyledItem(styledItem_);
|
|
5510
|
+
}
|
|
5511
|
+
else if (reusableStyleID !== void 0) {
|
|
5512
|
+
this.materials.addGeometryMapping(item.localID, reusableStyleID);
|
|
5513
|
+
}
|
|
5514
|
+
else if (styledItem instanceof IfcStyledItem) {
|
|
5515
|
+
// here we have the styled item, apply it to all geometry in this IfcProduct
|
|
5516
|
+
reusableStyleID = this.extractStyledItem(styledItem, item);
|
|
5517
|
+
}
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5521
|
+
else {
|
|
5522
|
+
for (const representation of representations.Representations) {
|
|
5523
|
+
if (representation instanceof IfcShapeRepresentation) {
|
|
5524
|
+
// this check is essential -
|
|
5525
|
+
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
5526
|
+
if (representation.RepresentationIdentifier !== "Body" &&
|
|
5527
|
+
representation.RepresentationIdentifier !== "Facetation") {
|
|
5528
|
+
continue;
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5531
|
+
for (const item of representation.Items) {
|
|
5532
|
+
if (item instanceof IfcMappedItem) {
|
|
5533
|
+
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
5534
|
+
}
|
|
5535
|
+
else {
|
|
5536
|
+
try {
|
|
5537
|
+
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
5538
|
+
if (hasRelVoid) {
|
|
5539
|
+
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
5540
|
+
}
|
|
5541
|
+
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
5542
|
+
if (styledItemLocalID_ !== void 0) {
|
|
5543
|
+
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
5544
|
+
this.extractStyledItem(styledItem_);
|
|
5545
|
+
}
|
|
5546
|
+
}
|
|
5547
|
+
catch (error) {
|
|
5548
|
+
if (this.quietRecoverableLogging) {
|
|
5549
|
+
// Preview prefix: expected on a truncated tail — skip.
|
|
5550
|
+
}
|
|
5551
|
+
else if (error instanceof Error) {
|
|
5552
|
+
Logger.error(`Error extracting representation item\n\t${error.message}\n\t${error.stack}\n\tExpress ID: #${item.localID}`);
|
|
5553
|
+
}
|
|
5554
|
+
else {
|
|
5555
|
+
Logger.error(`Unknown error extracting representation item Express ID: #${item.localID}`);
|
|
5556
|
+
}
|
|
5557
|
+
}
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
}
|
|
5561
|
+
}
|
|
5562
|
+
if (precomputedRelVoids === void 0) {
|
|
5563
|
+
relVoidsMeshVector?.delete();
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
4367
5566
|
/**
|
|
4368
5567
|
* Extract the geometry data from the IFC
|
|
4369
5568
|
*
|
|
@@ -4444,10 +5643,7 @@ export class IfcGeometryExtraction {
|
|
|
4444
5643
|
*/
|
|
4445
5644
|
*extractIFCGeometryDataIncremental() {
|
|
4446
5645
|
let result = ExtractResult.INCOMPLETE;
|
|
4447
|
-
this.extractLinearScalingFactor();
|
|
4448
5646
|
const previousMemoizationState = this.model.elementMemoization;
|
|
4449
|
-
// populate relMaterialsMap
|
|
4450
|
-
const relAssociatesMaterials = this.model.types(IfcRelAssociatesMaterial);
|
|
4451
5647
|
try {
|
|
4452
5648
|
// 256 meg limit for memoization - smaller models get a big
|
|
4453
5649
|
// win from memoization, but much larger models it uses far too much heap.
|
|
@@ -4456,317 +5652,54 @@ export class IfcGeometryExtraction {
|
|
|
4456
5652
|
if (this.lowMemoryMode || this.model.bufferBytesize > MEMOIZATION_THRESHOLD) {
|
|
4457
5653
|
this.model.elementMemoization = false;
|
|
4458
5654
|
}
|
|
4459
|
-
|
|
4460
|
-
const relatingMaterial = relAssociateMaterial.RelatingMaterial;
|
|
4461
|
-
try {
|
|
4462
|
-
const relatedObjects = relAssociateMaterial.RelatedObjects;
|
|
4463
|
-
for (const relatedObject of relatedObjects) {
|
|
4464
|
-
const product = relatedObject;
|
|
4465
|
-
if (product instanceof IfcProduct) {
|
|
4466
|
-
if (product instanceof IfcOpeningElement ||
|
|
4467
|
-
product instanceof IfcSpace ||
|
|
4468
|
-
product instanceof IfcOpeningStandardCase) {
|
|
4469
|
-
continue;
|
|
4470
|
-
}
|
|
4471
|
-
// save mapping of IfcProduct --> IfcMaterial
|
|
4472
|
-
this.materials.relMaterialsMap.set(product.localID, relatingMaterial.localID);
|
|
4473
|
-
}
|
|
4474
|
-
else {
|
|
4475
|
-
// Logger.warning(`type other than IfcProduct: ${EntityTypesIfc[product.type]}`)
|
|
4476
|
-
}
|
|
4477
|
-
}
|
|
4478
|
-
}
|
|
4479
|
-
catch (ex) {
|
|
4480
|
-
if (ex instanceof Error) {
|
|
4481
|
-
if (MATERIAL_RELATED_OBJECTS_PERMISSIVE) {
|
|
4482
|
-
Logger.error("Error processing relatingMaterial expressID: " +
|
|
4483
|
-
`${relatingMaterial.expressID}, error: ${ex.message}`);
|
|
4484
|
-
}
|
|
4485
|
-
else {
|
|
4486
|
-
throw ex;
|
|
4487
|
-
}
|
|
4488
|
-
}
|
|
4489
|
-
else {
|
|
4490
|
-
Logger.error("Unknown exception processing IfcRelAssociateMaterials.");
|
|
4491
|
-
}
|
|
4492
|
-
}
|
|
4493
|
-
}
|
|
4494
|
-
// populate MaterialDefinitionsMap
|
|
4495
|
-
this.populateMaterialDefinitionsMap();
|
|
4496
|
-
// populate relvoids map
|
|
4497
|
-
this.populateRelVoidsMap();
|
|
4498
|
-
// populate styled items map
|
|
4499
|
-
this.populateStyledItemsMap();
|
|
5655
|
+
this.prepareExtractionMaps_();
|
|
4500
5656
|
const products = this.model.types(IfcProduct);
|
|
4501
5657
|
// Prefix-sum counts (no iteration/materialization) — see typeCount.
|
|
4502
5658
|
// Slight over-count from multi-mapped elements just leaves the bar
|
|
4503
5659
|
// shy of 100% until endPhase; fine for progress.
|
|
4504
5660
|
const totalItems = this.model.typeCount(IfcProduct) + this.model.typeCount(IfcRelAggregates);
|
|
4505
5661
|
let completedItems = 0;
|
|
5662
|
+
// Products the aggregates pass re-extracts are SKIPPED here and
|
|
5663
|
+
// extracted exactly once, below, with the master rel-voids — one
|
|
5664
|
+
// scene instance per placement and no content replacement after
|
|
5665
|
+
// emission (see aggregateTargetLocalIDs).
|
|
5666
|
+
const aggregateTargets = this.aggregateTargetLocalIDs();
|
|
4506
5667
|
for (const product of products) {
|
|
4507
5668
|
yield [++completedItems, totalItems];
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
product instanceof IfcSpace ||
|
|
4511
|
-
product instanceof IfcOpeningStandardCase;
|
|
4512
|
-
const objectPlacement = product.ObjectPlacement;
|
|
4513
|
-
if (objectPlacement !== null) {
|
|
4514
|
-
this.extractPlacement(objectPlacement);
|
|
4515
|
-
}
|
|
4516
|
-
const representations = product.Representation;
|
|
4517
|
-
if (representations !== null) {
|
|
4518
|
-
// extract styledItem material
|
|
4519
|
-
const styledItemID = this.extractMaterialStyle(product);
|
|
4520
|
-
let hasRelVoid = false;
|
|
4521
|
-
const extractRelVoidsResult = this.extractRelVoids(product);
|
|
4522
|
-
let relVoidsMeshVector;
|
|
4523
|
-
let relVoidLocalIDs;
|
|
4524
|
-
if (extractRelVoidsResult !== void 0) {
|
|
4525
|
-
[relVoidsMeshVector, relVoidLocalIDs] = extractRelVoidsResult;
|
|
4526
|
-
}
|
|
4527
|
-
if (relVoidsMeshVector !== void 0) {
|
|
4528
|
-
hasRelVoid = true;
|
|
4529
|
-
}
|
|
4530
|
-
if (styledItemID !== void 0) {
|
|
4531
|
-
// optimization: extract the first representation item and cache
|
|
4532
|
-
// the styleID to apply to the rest of the product geometry
|
|
4533
|
-
const styledItem = this.model.getElementByLocalID(styledItemID);
|
|
4534
|
-
let reusableStyleID;
|
|
4535
|
-
for (const representation of representations.Representations) {
|
|
4536
|
-
if (representation instanceof IfcShapeRepresentation) {
|
|
4537
|
-
// this check is essential -
|
|
4538
|
-
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
4539
|
-
if (representation.RepresentationIdentifier !== "Body" &&
|
|
4540
|
-
representation.RepresentationIdentifier !== "Facetation") {
|
|
4541
|
-
continue;
|
|
4542
|
-
}
|
|
4543
|
-
}
|
|
4544
|
-
for (const item of representation.Items) {
|
|
4545
|
-
if (item instanceof IfcMappedItem) {
|
|
4546
|
-
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
4547
|
-
}
|
|
4548
|
-
else {
|
|
4549
|
-
try {
|
|
4550
|
-
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
4551
|
-
if (hasRelVoid) {
|
|
4552
|
-
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
4553
|
-
}
|
|
4554
|
-
}
|
|
4555
|
-
catch (error) {
|
|
4556
|
-
if (error instanceof Error) {
|
|
4557
|
-
Logger.error(`Error extracting representation item\n\t${error.message}\n\tExpress ID: #${item.expressID}`);
|
|
4558
|
-
}
|
|
4559
|
-
else {
|
|
4560
|
-
Logger.error(`Error extracting representation item\n\tExpress ID: #${item.expressID}`);
|
|
4561
|
-
}
|
|
4562
|
-
}
|
|
4563
|
-
}
|
|
4564
|
-
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
4565
|
-
if (styledItemLocalID_ !== undefined) {
|
|
4566
|
-
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
4567
|
-
this.extractStyledItem(styledItem_);
|
|
4568
|
-
}
|
|
4569
|
-
else if (reusableStyleID !== void 0) {
|
|
4570
|
-
this.materials.addGeometryMapping(item.localID, reusableStyleID);
|
|
4571
|
-
}
|
|
4572
|
-
else if (styledItem instanceof IfcStyledItem) {
|
|
4573
|
-
// here we have the styled item, apply it to all geometry in this IfcProduct
|
|
4574
|
-
reusableStyleID = this.extractStyledItem(styledItem, item);
|
|
4575
|
-
}
|
|
4576
|
-
}
|
|
4577
|
-
}
|
|
4578
|
-
}
|
|
4579
|
-
else {
|
|
4580
|
-
for (const representation of representations.Representations) {
|
|
4581
|
-
if (representation instanceof IfcShapeRepresentation) {
|
|
4582
|
-
// this check is essential -
|
|
4583
|
-
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
4584
|
-
if (representation.RepresentationIdentifier !== "Body" &&
|
|
4585
|
-
representation.RepresentationIdentifier !== "Facetation") {
|
|
4586
|
-
continue;
|
|
4587
|
-
}
|
|
4588
|
-
}
|
|
4589
|
-
for (const item of representation.Items) {
|
|
4590
|
-
if (item instanceof IfcMappedItem) {
|
|
4591
|
-
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
4592
|
-
}
|
|
4593
|
-
else {
|
|
4594
|
-
try {
|
|
4595
|
-
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
4596
|
-
if (hasRelVoid) {
|
|
4597
|
-
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
4598
|
-
}
|
|
4599
|
-
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
4600
|
-
if (styledItemLocalID_ !== void 0) {
|
|
4601
|
-
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
4602
|
-
this.extractStyledItem(styledItem_);
|
|
4603
|
-
}
|
|
4604
|
-
}
|
|
4605
|
-
catch (error) {
|
|
4606
|
-
if (error instanceof Error) {
|
|
4607
|
-
Logger.error(`Error extracting representation item\n\t${error.message}\n\t${error.stack}\n\tExpress ID: #${item.localID}`);
|
|
4608
|
-
}
|
|
4609
|
-
else {
|
|
4610
|
-
Logger.error(`Unknown error extracting representation item Express ID: #${item.localID}`);
|
|
4611
|
-
}
|
|
4612
|
-
}
|
|
4613
|
-
}
|
|
4614
|
-
}
|
|
4615
|
-
}
|
|
4616
|
-
}
|
|
4617
|
-
relVoidsMeshVector?.delete();
|
|
5669
|
+
if (aggregateTargets.has(product.localID)) {
|
|
5670
|
+
continue;
|
|
4618
5671
|
}
|
|
4619
|
-
}
|
|
4620
|
-
const relAggregates = this.model.types(IfcRelAggregates);
|
|
4621
|
-
for (const relAggregate of relAggregates) {
|
|
4622
|
-
yield [++completedItems, totalItems];
|
|
4623
5672
|
try {
|
|
4624
|
-
|
|
4625
|
-
// can be where the rel-void is applied, so we
|
|
4626
|
-
// used this as a top level over-ride for a rel-void.
|
|
4627
|
-
// for all objects in the aggregate - CS
|
|
4628
|
-
const relatingObject = relAggregate.RelatingObject;
|
|
4629
|
-
const masterRelVoids = relatingObject instanceof IfcProduct ?
|
|
4630
|
-
this.extractRelVoids(relatingObject) :
|
|
4631
|
-
void 0;
|
|
4632
|
-
const relatedObjects = relAggregate.RelatedObjects;
|
|
4633
|
-
for (const productRepresentation of relatedObjects) {
|
|
4634
|
-
if (productRepresentation instanceof IfcProduct) {
|
|
4635
|
-
const isSpace = productRepresentation instanceof IfcOpeningElement ||
|
|
4636
|
-
productRepresentation instanceof IfcSpace ||
|
|
4637
|
-
productRepresentation instanceof IfcOpeningStandardCase;
|
|
4638
|
-
const product = productRepresentation;
|
|
4639
|
-
this.scene.clearParentStack();
|
|
4640
|
-
const objectPlacement = product.ObjectPlacement;
|
|
4641
|
-
if (objectPlacement !== null) {
|
|
4642
|
-
this.extractPlacement(objectPlacement);
|
|
4643
|
-
}
|
|
4644
|
-
const representations = product.Representation;
|
|
4645
|
-
if (representations !== null) {
|
|
4646
|
-
// extract styledItem material
|
|
4647
|
-
const styledItemID = this.extractMaterialStyle(product);
|
|
4648
|
-
const extractRelVoidsResult = masterRelVoids ?? this.extractRelVoids(product);
|
|
4649
|
-
let hasRelVoid = false;
|
|
4650
|
-
let relVoidsMeshVector;
|
|
4651
|
-
let relVoidLocalIDs;
|
|
4652
|
-
if (extractRelVoidsResult !== void 0) {
|
|
4653
|
-
[relVoidsMeshVector, relVoidLocalIDs] = extractRelVoidsResult;
|
|
4654
|
-
}
|
|
4655
|
-
if (relVoidsMeshVector !== void 0) {
|
|
4656
|
-
hasRelVoid = true;
|
|
4657
|
-
}
|
|
4658
|
-
if (styledItemID !== void 0) {
|
|
4659
|
-
// optimization: extract the first representation item and cache
|
|
4660
|
-
// the styleID to apply to the rest of the product geometry
|
|
4661
|
-
const styledItem = this.model.getElementByLocalID(styledItemID);
|
|
4662
|
-
let reusableStyleID;
|
|
4663
|
-
for (const representation of representations.Representations) {
|
|
4664
|
-
if (representation instanceof IfcShapeRepresentation) {
|
|
4665
|
-
// this check is essential -
|
|
4666
|
-
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
4667
|
-
if (representation.RepresentationIdentifier !== "Body" &&
|
|
4668
|
-
representation.RepresentationIdentifier !== "Facetation") {
|
|
4669
|
-
continue;
|
|
4670
|
-
}
|
|
4671
|
-
}
|
|
4672
|
-
for (const item of representation.Items) {
|
|
4673
|
-
if (item instanceof IfcMappedItem) {
|
|
4674
|
-
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
4675
|
-
}
|
|
4676
|
-
else {
|
|
4677
|
-
try {
|
|
4678
|
-
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
4679
|
-
if (hasRelVoid) {
|
|
4680
|
-
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
4681
|
-
}
|
|
4682
|
-
}
|
|
4683
|
-
catch (error) {
|
|
4684
|
-
if (error instanceof Error) {
|
|
4685
|
-
Logger.error(`Error extracting rel aggregate representation item\n\t${error.message}\n\t${error.stack}\n\t Express ID: #${item.expressID}`);
|
|
4686
|
-
}
|
|
4687
|
-
else {
|
|
4688
|
-
Logger.error(`Unknown error extracting rel aggregate representation item Express ID: #${item.expressID}`);
|
|
4689
|
-
}
|
|
4690
|
-
}
|
|
4691
|
-
}
|
|
4692
|
-
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
4693
|
-
if (styledItemLocalID_ !== undefined) {
|
|
4694
|
-
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
4695
|
-
this.extractStyledItem(styledItem_);
|
|
4696
|
-
}
|
|
4697
|
-
else if (reusableStyleID !== void 0) {
|
|
4698
|
-
this.materials.addGeometryMapping(item.localID, reusableStyleID);
|
|
4699
|
-
}
|
|
4700
|
-
else if (styledItem instanceof IfcStyledItem) {
|
|
4701
|
-
// here we have the styled item, apply it to all geometry in this IfcProduct
|
|
4702
|
-
reusableStyleID = this.extractStyledItem(styledItem, item);
|
|
4703
|
-
}
|
|
4704
|
-
}
|
|
4705
|
-
}
|
|
4706
|
-
}
|
|
4707
|
-
else {
|
|
4708
|
-
for (const representation of representations.Representations) {
|
|
4709
|
-
if (representation instanceof IfcShapeRepresentation) {
|
|
4710
|
-
// this check is essential -
|
|
4711
|
-
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
4712
|
-
if (representation.RepresentationIdentifier !== "Body" &&
|
|
4713
|
-
representation.RepresentationIdentifier !== "Facetation") {
|
|
4714
|
-
continue;
|
|
4715
|
-
}
|
|
4716
|
-
}
|
|
4717
|
-
for (const item of representation.Items) {
|
|
4718
|
-
if (item instanceof IfcMappedItem) {
|
|
4719
|
-
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
4720
|
-
}
|
|
4721
|
-
else {
|
|
4722
|
-
try {
|
|
4723
|
-
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
4724
|
-
if (hasRelVoid) {
|
|
4725
|
-
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
4726
|
-
}
|
|
4727
|
-
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
4728
|
-
if (styledItemLocalID_ !== void 0) {
|
|
4729
|
-
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
4730
|
-
this.extractStyledItem(styledItem_);
|
|
4731
|
-
}
|
|
4732
|
-
}
|
|
4733
|
-
catch (error) {
|
|
4734
|
-
if (error instanceof Error) {
|
|
4735
|
-
Logger.error(`Error extracting rel aggregate representation item\n\t${error.message}\n\t${error.stack}\n\t Express ID: #${item.expressID}`);
|
|
4736
|
-
}
|
|
4737
|
-
else {
|
|
4738
|
-
Logger.error(`Unknown error extracting rel aggregate representation item Express ID: #${item.expressID}`);
|
|
4739
|
-
}
|
|
4740
|
-
}
|
|
4741
|
-
}
|
|
4742
|
-
}
|
|
4743
|
-
}
|
|
4744
|
-
}
|
|
4745
|
-
if (masterRelVoids === void 0) {
|
|
4746
|
-
relVoidsMeshVector?.delete();
|
|
4747
|
-
}
|
|
4748
|
-
}
|
|
4749
|
-
}
|
|
4750
|
-
}
|
|
4751
|
-
if (masterRelVoids !== void 0) {
|
|
4752
|
-
masterRelVoids[0].delete();
|
|
4753
|
-
}
|
|
5673
|
+
this.extractProductGeometry(product);
|
|
4754
5674
|
}
|
|
4755
5675
|
catch (ex) {
|
|
5676
|
+
// A single malformed product must not abort the whole model. A
|
|
5677
|
+
// reference field carrying a value where an entity is expected
|
|
5678
|
+
// throws "Value in STEP was incorrectly typed" deep in material/
|
|
5679
|
+
// representation extraction; the rel-aggregates pass below already
|
|
5680
|
+
// tolerates this per-relationship, and the browser demand pump
|
|
5681
|
+
// swallows per-product throws, so a model that renders in prod
|
|
5682
|
+
// would otherwise hard-fail the headless regression batch. Skip
|
|
5683
|
+
// the offending product and continue; strict mode re-throws.
|
|
4756
5684
|
if (ex instanceof Error) {
|
|
4757
5685
|
if (MATERIAL_RELATED_OBJECTS_PERMISSIVE) {
|
|
4758
|
-
Logger.error(
|
|
4759
|
-
`error: ${ex.message}
|
|
5686
|
+
Logger.error(`Error extracting product expressID: ${product.expressID}, ` +
|
|
5687
|
+
`error: ${ex.message}`);
|
|
4760
5688
|
}
|
|
4761
5689
|
else {
|
|
4762
5690
|
throw ex;
|
|
4763
5691
|
}
|
|
4764
5692
|
}
|
|
4765
5693
|
else {
|
|
4766
|
-
Logger.error("Unknown exception
|
|
5694
|
+
Logger.error("Unknown exception extracting product.");
|
|
4767
5695
|
}
|
|
4768
5696
|
}
|
|
4769
5697
|
}
|
|
5698
|
+
const relAggregates = this.model.types(IfcRelAggregates);
|
|
5699
|
+
for (const relAggregate of relAggregates) {
|
|
5700
|
+
yield [++completedItems, totalItems];
|
|
5701
|
+
this.extractRelAggregateGeometry(relAggregate);
|
|
5702
|
+
}
|
|
4770
5703
|
if (RegressionCaptureState.memoization !== MemoizationCapture.FULL) {
|
|
4771
5704
|
this.model.geometry.deleteTemporaries();
|
|
4772
5705
|
this.model.voidGeometry.deleteTemporaries();
|