@ifc-lite/export 1.7.0 → 1.8.0

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.
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Reference collector for IFC STEP export filtering.
3
+ *
4
+ * Walks #ID references transitively from a set of root entities to build
5
+ * the complete closure of all entities that must be included for a valid
6
+ * STEP file. Used for visible-only export and merged export.
7
+ *
8
+ * KEY DESIGN: In IFC STEP files, the reference graph is:
9
+ * - Products reference geometry (Product → Placement → CartesianPoint)
10
+ * - Relationships reference products (Rel → Product, NOT Product → Rel)
11
+ * - Properties are reached via relationships (Rel → PropertySet → Property)
12
+ *
13
+ * For visible-only export, we need:
14
+ * 1. Infrastructure + spatial structure (always included)
15
+ * 2. Visible product entities (checked against hidden/isolated)
16
+ * 3. Relationship entities (always included as roots — they reference products)
17
+ * 4. Forward closure from the above roots pulls in geometry, properties, etc.
18
+ * 5. Hidden product IDs are BLOCKED during the closure walk so their
19
+ * exclusively-referenced geometry doesn't get pulled in.
20
+ * 6. IfcStyledItem entities are collected in a reverse pass after the closure
21
+ * because they reference geometry but nothing references them back.
22
+ * 7. Openings whose parent element is hidden are also excluded
23
+ * (via IfcRelVoidsElement propagation).
24
+ */
25
+ import type { IfcDataStore } from '@ifc-lite/parser';
26
+ /**
27
+ * Collect all entity IDs transitively referenced from a set of root entities.
28
+ *
29
+ * Starting from `rootIds`, reads each entity's raw bytes from the source buffer
30
+ * and extracts all `#ID` references via byte-level scanning (no string
31
+ * allocation). Recursively follows references to build a complete closure
32
+ * that guarantees referential integrity.
33
+ *
34
+ * @param rootIds - Seed entity IDs to start the walk from
35
+ * @param source - The original STEP file source buffer
36
+ * @param entityIndex - Map of expressId → byte position in source
37
+ * @param excludeIds - Entity IDs to NEVER follow during the walk.
38
+ *
39
+ * Performance: O(total bytes of included entities). Each entity visited once.
40
+ * Uses byte-level scanning — no TextDecoder, no regex, no string allocation.
41
+ */
42
+ export declare function collectReferencedEntityIds(rootIds: Set<number>, source: Uint8Array, entityIndex: Map<number, {
43
+ byteOffset: number;
44
+ byteLength: number;
45
+ }>, excludeIds?: Set<number>): Set<number>;
46
+ /**
47
+ * Compute the root entity set and hidden product IDs for a visible-only export.
48
+ *
49
+ * Returns:
50
+ * - `roots`: Entity IDs that form the seed set for the reference closure.
51
+ * Includes infrastructure, spatial structure, relationship entities, and
52
+ * visible product entities.
53
+ * - `hiddenProductIds`: Product entity IDs that are hidden/not isolated.
54
+ * These should be passed as `excludeIds` to `collectReferencedEntityIds`
55
+ * to prevent the closure from walking into hidden products' geometry.
56
+ *
57
+ * Also propagates hidden status from building elements to their openings
58
+ * via IfcRelVoidsElement, so orphaned openings are excluded.
59
+ */
60
+ export declare function getVisibleEntityIds(dataStore: IfcDataStore, hiddenIds: Set<number>, isolatedIds: Set<number> | null): {
61
+ roots: Set<number>;
62
+ hiddenProductIds: Set<number>;
63
+ };
64
+ /**
65
+ * Collect style entities (IFCSTYLEDITEM, etc.) that reference geometry already
66
+ * in the closure, then transitively follow their style references.
67
+ *
68
+ * In IFC STEP, IFCSTYLEDITEM references a geometry RepresentationItem, but
69
+ * nothing references the StyledItem back. So the forward closure walk misses
70
+ * them entirely. This function does a reverse pass using the byType index:
71
+ * for each styled item, check if any referenced ID is in the closure. If yes,
72
+ * add the styled item and walk its style chain into the closure.
73
+ *
74
+ * Uses byType for O(styledItems) instead of O(allEntities), and byte-level
75
+ * scanning for #ID extraction.
76
+ *
77
+ * Must be called AFTER collectReferencedEntityIds so the closure is complete.
78
+ *
79
+ * @param closure - The existing closure set (mutated in place)
80
+ * @param source - The original STEP file source buffer
81
+ * @param entityIndex - Full entity index with type info and byType lookup
82
+ */
83
+ export declare function collectStyleEntities(closure: Set<number>, source: Uint8Array, entityIndex: {
84
+ byId: Map<number, {
85
+ type: string;
86
+ byteOffset: number;
87
+ byteLength: number;
88
+ }>;
89
+ byType: Map<string, number[]>;
90
+ }): void;
91
+ //# sourceMappingURL=reference-collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-collector.d.ts","sourceRoot":"","sources":["../src/reference-collector.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA+MrD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EACpB,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,EACpE,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACvB,GAAG,CAAC,MAAM,CAAC,CAqCb;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EACtB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAC9B;IAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAkEvD;AAkDD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EACpB,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE;IACX,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC/B,GACA,IAAI,CAoDN"}
@@ -0,0 +1,428 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /** ASCII code points for byte-level scanning. */
5
+ const HASH = 0x23; // '#'
6
+ const ZERO = 0x30; // '0'
7
+ const NINE = 0x39; // '9'
8
+ /** Entity types that form the shared file infrastructure and must always be included. */
9
+ const INFRASTRUCTURE_TYPES = new Set([
10
+ 'IFCOWNERHISTORY',
11
+ 'IFCAPPLICATION',
12
+ 'IFCPERSON',
13
+ 'IFCORGANIZATION',
14
+ 'IFCPERSONANDORGANIZATION',
15
+ 'IFCUNITASSIGNMENT',
16
+ 'IFCSIUNIT',
17
+ 'IFCDERIVEDUNIT',
18
+ 'IFCDERIVEDUNITELEMENT',
19
+ 'IFCCONVERSIONBASEDUNIT',
20
+ 'IFCMEASUREWITHUNIT',
21
+ 'IFCDIMENSIONALEXPONENTS',
22
+ 'IFCMONETARYUNIT',
23
+ 'IFCGEOMETRICREPRESENTATIONCONTEXT',
24
+ 'IFCGEOMETRICREPRESENTATIONSUBCONTEXT',
25
+ ]);
26
+ /**
27
+ * Spatial structure entity types — always included as roots.
28
+ * Covers IFC4 and IFC4X3 (bridges, roads, railways, marine facilities).
29
+ * Derived from all subtypes of IfcSpatialElement in the IFC schema,
30
+ * excluding IfcSpace (which users can toggle visibility on).
31
+ */
32
+ const SPATIAL_STRUCTURE_TYPES = new Set([
33
+ 'IFCPROJECT',
34
+ // IFC4 spatial structure
35
+ 'IFCSITE', 'IFCBUILDING', 'IFCBUILDINGSTOREY',
36
+ // IFC4X3 spatial structure (subtypes of IfcFacility / IfcSpatialElement)
37
+ 'IFCBRIDGE', 'IFCBRIDGEPART',
38
+ 'IFCFACILITY', 'IFCFACILITYPART', 'IFCFACILITYPARTCOMMON',
39
+ 'IFCMARINEFACILITY', 'IFCMARINEPART',
40
+ 'IFCRAILWAY', 'IFCRAILWAYPART',
41
+ 'IFCROAD', 'IFCROADPART',
42
+ // Abstract spatial types (rarely instantiated but handle gracefully)
43
+ 'IFCSPATIALELEMENT', 'IFCSPATIALSTRUCTUREELEMENT', 'IFCSPATIALZONE',
44
+ 'IFCEXTERNALSPATIALELEMENT', 'IFCEXTERNALSPATIALSTRUCTUREELEMENT',
45
+ ]);
46
+ /**
47
+ * Complete set of all IfcProduct subtypes from the IFC4 + IFC4X3 schemas,
48
+ * excluding spatial structure types (handled above). Generated from the
49
+ * schema registry's inheritanceChain metadata.
50
+ *
51
+ * 202 types — full IFC schema coverage. The hiddenIds fallback below
52
+ * catches any types that may exist in future schema versions.
53
+ */
54
+ const PRODUCT_TYPES = new Set([
55
+ // IfcElement > IfcBuildingElement
56
+ 'IFCBEAM', 'IFCBEAMSTANDARDCASE', 'IFCBUILDINGELEMENT',
57
+ 'IFCBUILDINGELEMENTPART', 'IFCBUILDINGELEMENTPROXY', 'IFCBUILTELEMENT',
58
+ 'IFCCHIMNEY', 'IFCCOLUMN', 'IFCCOLUMNSTANDARDCASE',
59
+ 'IFCCOVERING', 'IFCCURTAINWALL',
60
+ 'IFCDEEPFOUNDATION', 'IFCDOOR', 'IFCDOORSTANDARDCASE',
61
+ 'IFCFOOTING', 'IFCMEMBER', 'IFCMEMBERSTANDARDCASE',
62
+ 'IFCPILE', 'IFCPLATE', 'IFCPLATESTANDARDCASE',
63
+ 'IFCRAILING', 'IFCRAMP', 'IFCRAMPFLIGHT',
64
+ 'IFCROOF', 'IFCSHADINGDEVICE',
65
+ 'IFCSLAB', 'IFCSLABELEMENTEDCASE', 'IFCSLABSTANDARDCASE',
66
+ 'IFCSTAIR', 'IFCSTAIRFLIGHT',
67
+ 'IFCWALL', 'IFCWALLELEMENTEDCASE', 'IFCWALLSTANDARDCASE',
68
+ 'IFCWINDOW', 'IFCWINDOWSTANDARDCASE',
69
+ // IfcElement > IfcDistributionElement
70
+ 'IFCDISTRIBUTIONELEMENT', 'IFCDISTRIBUTIONCONTROLELEMENT',
71
+ 'IFCDISTRIBUTIONFLOWELEMENT', 'IFCDISTRIBUTIONCHAMBERELEMENT',
72
+ 'IFCDISTRIBUTIONPORT', 'IFCDISTRIBUTIONBOARD',
73
+ // IfcDistributionControlElement subtypes
74
+ 'IFCACTUATOR', 'IFCALARM', 'IFCCONTROLLER',
75
+ 'IFCFLOWINSTRUMENT', 'IFCPROTECTIVEDEVICETRIPPINGUNIT',
76
+ 'IFCSENSOR', 'IFCUNITARYCONTROLELEMENT',
77
+ // IfcFlowController subtypes
78
+ 'IFCAIRTERMINALBOX', 'IFCDAMPER', 'IFCELECTRICDISTRIBUTIONBOARD',
79
+ 'IFCELECTRICTIMECONTROL', 'IFCFLOWCONTROLLER', 'IFCFLOWMETER',
80
+ 'IFCPROTECTIVEDEVICE', 'IFCSWITCHINGDEVICE', 'IFCVALVE',
81
+ // IfcFlowFitting subtypes
82
+ 'IFCCABLECARRIERFITTING', 'IFCCABLEFITTING',
83
+ 'IFCDUCTFITTING', 'IFCFLOWFITTING', 'IFCJUNCTIONBOX', 'IFCPIPEFITTING',
84
+ // IfcFlowMovingDevice subtypes
85
+ 'IFCCOMPRESSOR', 'IFCFAN', 'IFCFLOWMOVINGDEVICE', 'IFCPUMP',
86
+ // IfcFlowSegment subtypes
87
+ 'IFCCABLECARRIERSEGMENT', 'IFCCABLESEGMENT', 'IFCCONVEYORSEGMENT',
88
+ 'IFCDUCTSEGMENT', 'IFCFLOWSEGMENT', 'IFCPIPESEGMENT',
89
+ // IfcFlowStorageDevice subtypes
90
+ 'IFCELECTRICFLOWSTORAGEDEVICE', 'IFCFLOWSTORAGEDEVICE', 'IFCTANK',
91
+ // IfcFlowTerminal subtypes
92
+ 'IFCAIRTERMINAL', 'IFCAUDIOVISUALAPPLIANCE', 'IFCCOMMUNICATIONSAPPLIANCE',
93
+ 'IFCELECTRICAPPLIANCE', 'IFCFIRESUPPRESSIONTERMINAL', 'IFCFLOWTERMINAL',
94
+ 'IFCLAMP', 'IFCLIGHTFIXTURE', 'IFCLIQUIDTERMINAL',
95
+ 'IFCMEDICALDEVICE', 'IFCMOBILETELECOMMUNICATIONSAPPLIANCE',
96
+ 'IFCOUTLET', 'IFCSANITARYTERMINAL', 'IFCSPACEHEATER',
97
+ 'IFCSTACKTERMINAL', 'IFCWASTETERMINAL',
98
+ // IfcFlowTreatmentDevice subtypes
99
+ 'IFCDUCTSILENCER', 'IFCELECTRICFLOWTREATMENTDEVICE',
100
+ 'IFCFILTER', 'IFCFLOWTREATMENTDEVICE', 'IFCINTERCEPTOR',
101
+ // IfcEnergyConversionDevice subtypes
102
+ 'IFCAIRTOAIRHEATRECOVERY', 'IFCBOILER', 'IFCBURNER',
103
+ 'IFCCHILLER', 'IFCCOIL', 'IFCCONDENSER',
104
+ 'IFCCOOLEDBEAM', 'IFCCOOLINGTOWER',
105
+ 'IFCELECTRICGENERATOR', 'IFCELECTRICMOTOR',
106
+ 'IFCENERGYCONVERSIONDEVICE', 'IFCENGINE',
107
+ 'IFCEVAPORATIVECOOLER', 'IFCEVAPORATOR',
108
+ 'IFCHEATEXCHANGER', 'IFCHUMIDIFIER', 'IFCMOTORCONNECTION',
109
+ 'IFCSOLARDEVICE', 'IFCTRANSFORMER', 'IFCTUBEBUNDLE',
110
+ 'IFCUNITARYEQUIPMENT',
111
+ // IfcElement > IfcElementAssembly
112
+ 'IFCELEMENT', 'IFCELEMENTASSEMBLY',
113
+ // IfcElement > IfcElementComponent
114
+ 'IFCELEMENTCOMPONENT', 'IFCFASTENER',
115
+ 'IFCMECHANICALFASTENER', 'IFCDISCRETEACCESSORY',
116
+ 'IFCVIBRATIONDAMPER', 'IFCVIBRATIONISOLATOR',
117
+ 'IFCIMPACTPROTECTIONDEVICE',
118
+ // IfcElement > IfcFeatureElement
119
+ 'IFCFEATUREELEMENT', 'IFCFEATUREELEMENTADDITION', 'IFCFEATUREELEMENTSUBTRACTION',
120
+ 'IFCOPENINGELEMENT', 'IFCOPENINGSTANDARDCASE',
121
+ 'IFCPROJECTIONELEMENT', 'IFCSURFACEFEATURE', 'IFCVOIDINGFEATURE',
122
+ // IfcElement > IfcFurnishingElement
123
+ 'IFCFURNISHINGELEMENT', 'IFCFURNITURE', 'IFCSYSTEMFURNITUREELEMENT',
124
+ // IfcElement > IfcGeographicElement / IfcCivilElement
125
+ 'IFCGEOGRAPHICELEMENT', 'IFCCIVILELEMENT',
126
+ // IfcElement > IfcTransportElement / IfcTransportationDevice / IfcVehicle
127
+ 'IFCTRANSPORTELEMENT', 'IFCTRANSPORTATIONDEVICE', 'IFCVEHICLE',
128
+ // IfcElement > IfcReinforcingElement
129
+ 'IFCREINFORCINGELEMENT', 'IFCREINFORCINGBAR', 'IFCREINFORCINGMESH',
130
+ 'IFCTENDON', 'IFCTENDONANCHOR', 'IFCTENDONCONDUIT',
131
+ // IfcElement > IFC4X3 additions
132
+ 'IFCBEARING', 'IFCCAISSONFOUNDATION', 'IFCCOURSE',
133
+ 'IFCEARTHWORKSCUT', 'IFCEARTHWORKSELEMENT', 'IFCEARTHWORKSFILL',
134
+ 'IFCKERB', 'IFCMOORINGDEVICE', 'IFCNAVIGATIONELEMENT',
135
+ 'IFCPAVEMENT', 'IFCRAIL', 'IFCREINFORCEDSOIL', 'IFCSIGN', 'IFCSIGNAL',
136
+ 'IFCTRACKELEMENT',
137
+ // IFC4X3 alignment and positioning
138
+ 'IFCALIGNMENT', 'IFCALIGNMENTCANT', 'IFCALIGNMENTHORIZONTAL',
139
+ 'IFCALIGNMENTSEGMENT', 'IFCALIGNMENTVERTICAL',
140
+ 'IFCLINEARELEMENT', 'IFCLINEARPOSITIONINGELEMENT',
141
+ 'IFCPOSITIONINGELEMENT', 'IFCREFERENT',
142
+ // IFC4X3 geotechnical
143
+ 'IFCBOREHOLE', 'IFCGEOMODEL', 'IFCGEOSLICE',
144
+ 'IFCGEOTECHNICALASSEMBLY', 'IFCGEOTECHNICALELEMENT', 'IFCGEOTECHNICALSTRATUM',
145
+ // IfcProduct (non-element)
146
+ 'IFCANNOTATION', 'IFCGRID', 'IFCPORT', 'IFCPROXY',
147
+ 'IFCSPACE', 'IFCVIRTUALELEMENT',
148
+ // IfcStructuralItem / IfcStructuralActivity
149
+ 'IFCSTRUCTURALACTION', 'IFCSTRUCTURALACTIVITY',
150
+ 'IFCSTRUCTURALCONNECTION', 'IFCSTRUCTURALCURVEACTION',
151
+ 'IFCSTRUCTURALCURVECONNECTION', 'IFCSTRUCTURALCURVEMEMBER',
152
+ 'IFCSTRUCTURALCURVEMEMBERVARYING', 'IFCSTRUCTURALCURVEREACTION',
153
+ 'IFCSTRUCTURALITEM', 'IFCSTRUCTURALLINEARACTION',
154
+ 'IFCSTRUCTURALMEMBER', 'IFCSTRUCTURALPLANARACTION',
155
+ 'IFCSTRUCTURALPOINTACTION', 'IFCSTRUCTURALPOINTCONNECTION',
156
+ 'IFCSTRUCTURALPOINTREACTION', 'IFCSTRUCTURALREACTION',
157
+ 'IFCSTRUCTURALSURFACEACTION', 'IFCSTRUCTURALSURFACECONNECTION',
158
+ 'IFCSTRUCTURALSURFACEMEMBER', 'IFCSTRUCTURALSURFACEMEMBERVARYING',
159
+ 'IFCSTRUCTURALSURFACEREACTION',
160
+ ]);
161
+ // ---------------------------------------------------------------------------
162
+ // Byte-level #ID reference extraction
163
+ // ---------------------------------------------------------------------------
164
+ /**
165
+ * Extract all #ID references from a raw STEP entity byte range.
166
+ *
167
+ * Scans the Uint8Array directly for '#' (0x23) followed by ASCII digits,
168
+ * avoiding TextDecoder string allocation and regex overhead. Each entity
169
+ * is visited at most once, and IDs are parsed inline from bytes.
170
+ *
171
+ * ~4-15x faster than TextDecoder + regex for large closures.
172
+ */
173
+ function extractRefsFromBytes(source, byteOffset, byteLength, out) {
174
+ const end = byteOffset + byteLength;
175
+ let i = byteOffset;
176
+ while (i < end) {
177
+ if (source[i] === HASH) {
178
+ i++;
179
+ // Check if followed by at least one digit
180
+ if (i < end && source[i] >= ZERO && source[i] <= NINE) {
181
+ let id = source[i] - ZERO;
182
+ i++;
183
+ while (i < end && source[i] >= ZERO && source[i] <= NINE) {
184
+ id = id * 10 + (source[i] - ZERO);
185
+ i++;
186
+ }
187
+ out.push(id);
188
+ }
189
+ }
190
+ else {
191
+ i++;
192
+ }
193
+ }
194
+ }
195
+ // ---------------------------------------------------------------------------
196
+ // Core closure walk
197
+ // ---------------------------------------------------------------------------
198
+ /**
199
+ * Collect all entity IDs transitively referenced from a set of root entities.
200
+ *
201
+ * Starting from `rootIds`, reads each entity's raw bytes from the source buffer
202
+ * and extracts all `#ID` references via byte-level scanning (no string
203
+ * allocation). Recursively follows references to build a complete closure
204
+ * that guarantees referential integrity.
205
+ *
206
+ * @param rootIds - Seed entity IDs to start the walk from
207
+ * @param source - The original STEP file source buffer
208
+ * @param entityIndex - Map of expressId → byte position in source
209
+ * @param excludeIds - Entity IDs to NEVER follow during the walk.
210
+ *
211
+ * Performance: O(total bytes of included entities). Each entity visited once.
212
+ * Uses byte-level scanning — no TextDecoder, no regex, no string allocation.
213
+ */
214
+ export function collectReferencedEntityIds(rootIds, source, entityIndex, excludeIds) {
215
+ const visited = new Set();
216
+ const queue = [];
217
+ // Seed the queue with roots that exist in the entity index
218
+ for (const id of rootIds) {
219
+ if (entityIndex.has(id) && !visited.has(id)) {
220
+ visited.add(id);
221
+ queue.push(id);
222
+ }
223
+ }
224
+ // Reusable buffer for extracted refs (avoids per-entity allocation)
225
+ const refs = [];
226
+ while (queue.length > 0) {
227
+ const entityId = queue.pop();
228
+ const ref = entityIndex.get(entityId);
229
+ if (!ref)
230
+ continue;
231
+ // Extract #ID references directly from bytes
232
+ refs.length = 0;
233
+ extractRefsFromBytes(source, ref.byteOffset, ref.byteLength, refs);
234
+ for (let i = 0; i < refs.length; i++) {
235
+ const referencedId = refs[i];
236
+ if (!visited.has(referencedId) && entityIndex.has(referencedId)) {
237
+ if (excludeIds && excludeIds.has(referencedId)) {
238
+ continue;
239
+ }
240
+ visited.add(referencedId);
241
+ queue.push(referencedId);
242
+ }
243
+ }
244
+ }
245
+ return visited;
246
+ }
247
+ // ---------------------------------------------------------------------------
248
+ // Visibility classification
249
+ // ---------------------------------------------------------------------------
250
+ /**
251
+ * Compute the root entity set and hidden product IDs for a visible-only export.
252
+ *
253
+ * Returns:
254
+ * - `roots`: Entity IDs that form the seed set for the reference closure.
255
+ * Includes infrastructure, spatial structure, relationship entities, and
256
+ * visible product entities.
257
+ * - `hiddenProductIds`: Product entity IDs that are hidden/not isolated.
258
+ * These should be passed as `excludeIds` to `collectReferencedEntityIds`
259
+ * to prevent the closure from walking into hidden products' geometry.
260
+ *
261
+ * Also propagates hidden status from building elements to their openings
262
+ * via IfcRelVoidsElement, so orphaned openings are excluded.
263
+ */
264
+ export function getVisibleEntityIds(dataStore, hiddenIds, isolatedIds) {
265
+ const roots = new Set();
266
+ const hiddenProductIds = new Set();
267
+ for (const [expressId, entityRef] of dataStore.entityIndex.byId) {
268
+ const typeUpper = entityRef.type.toUpperCase();
269
+ // Always include infrastructure entities (units, contexts, owner history)
270
+ if (INFRASTRUCTURE_TYPES.has(typeUpper)) {
271
+ roots.add(expressId);
272
+ continue;
273
+ }
274
+ // Always include spatial structure (project, site, building, storey, facility)
275
+ if (SPATIAL_STRUCTURE_TYPES.has(typeUpper)) {
276
+ roots.add(expressId);
277
+ continue;
278
+ }
279
+ // Always include relationship entities as roots.
280
+ // Relationships reference products (not vice versa), so they must be roots
281
+ // for properties, materials, and type definitions to be reachable.
282
+ if (typeUpper.startsWith('IFCREL')) {
283
+ roots.add(expressId);
284
+ continue;
285
+ }
286
+ // For product/element entities: check visibility
287
+ if (PRODUCT_TYPES.has(typeUpper)) {
288
+ const isHidden = hiddenIds.has(expressId);
289
+ const isNotIsolated = isolatedIds !== null && !isolatedIds.has(expressId);
290
+ if (isHidden || isNotIsolated) {
291
+ hiddenProductIds.add(expressId);
292
+ }
293
+ else {
294
+ roots.add(expressId);
295
+ }
296
+ continue;
297
+ }
298
+ // Fallback: if the entity ID is explicitly hidden by the viewer, block it
299
+ // even if its type isn't in PRODUCT_TYPES (catches future schema additions)
300
+ if (hiddenIds.has(expressId)) {
301
+ hiddenProductIds.add(expressId);
302
+ continue;
303
+ }
304
+ // Fallback: if isolation is active and this entity IS isolated, it must be
305
+ // a product the user wants to see — make it a root
306
+ if (isolatedIds !== null && isolatedIds.has(expressId)) {
307
+ roots.add(expressId);
308
+ continue;
309
+ }
310
+ // All other entity types (geometry, properties, materials, type objects, etc.)
311
+ // are NOT roots. They will only be included if transitively referenced by
312
+ // a root entity during the closure walk. This ensures hidden products'
313
+ // exclusively-referenced geometry is excluded.
314
+ }
315
+ // Propagate hidden status to openings whose parent element is hidden.
316
+ // IfcRelVoidsElement(_, _, _, _, #RelatingElement, #RelatedOpening) — if
317
+ // the relating element is hidden, the opening must be excluded too.
318
+ propagateOpeningExclusions(dataStore, roots, hiddenProductIds);
319
+ return { roots, hiddenProductIds };
320
+ }
321
+ /**
322
+ * Propagate hidden status from building elements to their openings.
323
+ *
324
+ * Uses byte-level scanning on IfcRelVoidsElement entities (via byType index)
325
+ * to extract the last two #ID refs (RelatingBuildingElement, RelatedOpening).
326
+ */
327
+ function propagateOpeningExclusions(dataStore, roots, hiddenProductIds) {
328
+ const source = dataStore.source;
329
+ if (!source)
330
+ return;
331
+ const relVoidsIds = dataStore.entityIndex.byType.get('IFCRELVOIDSELEMENT') ?? [];
332
+ if (relVoidsIds.length === 0)
333
+ return;
334
+ const refs = [];
335
+ for (const relId of relVoidsIds) {
336
+ const entityRef = dataStore.entityIndex.byId.get(relId);
337
+ if (!entityRef)
338
+ continue;
339
+ // Find the opening paren to skip the leading #ID=TYPE(
340
+ let parenPos = entityRef.byteOffset;
341
+ const end = entityRef.byteOffset + entityRef.byteLength;
342
+ while (parenPos < end && source[parenPos] !== 0x28 /* '(' */)
343
+ parenPos++;
344
+ if (parenPos >= end)
345
+ continue;
346
+ refs.length = 0;
347
+ extractRefsFromBytes(source, parenPos, end - parenPos, refs);
348
+ if (refs.length < 2)
349
+ continue;
350
+ const relatingElementId = refs[refs.length - 2];
351
+ const relatedOpeningId = refs[refs.length - 1];
352
+ if (hiddenProductIds.has(relatingElementId)) {
353
+ hiddenProductIds.add(relatedOpeningId);
354
+ roots.delete(relId);
355
+ roots.delete(relatedOpeningId);
356
+ }
357
+ }
358
+ }
359
+ // ---------------------------------------------------------------------------
360
+ // Style entity collection (reverse pass)
361
+ // ---------------------------------------------------------------------------
362
+ /**
363
+ * Collect style entities (IFCSTYLEDITEM, etc.) that reference geometry already
364
+ * in the closure, then transitively follow their style references.
365
+ *
366
+ * In IFC STEP, IFCSTYLEDITEM references a geometry RepresentationItem, but
367
+ * nothing references the StyledItem back. So the forward closure walk misses
368
+ * them entirely. This function does a reverse pass using the byType index:
369
+ * for each styled item, check if any referenced ID is in the closure. If yes,
370
+ * add the styled item and walk its style chain into the closure.
371
+ *
372
+ * Uses byType for O(styledItems) instead of O(allEntities), and byte-level
373
+ * scanning for #ID extraction.
374
+ *
375
+ * Must be called AFTER collectReferencedEntityIds so the closure is complete.
376
+ *
377
+ * @param closure - The existing closure set (mutated in place)
378
+ * @param source - The original STEP file source buffer
379
+ * @param entityIndex - Full entity index with type info and byType lookup
380
+ */
381
+ export function collectStyleEntities(closure, source, entityIndex) {
382
+ const queue = [];
383
+ const refs = [];
384
+ // Use byType index for direct lookup — O(styledItems) not O(allEntities)
385
+ const styledItemIds = entityIndex.byType.get('IFCSTYLEDITEM') ?? [];
386
+ const styledRepIds = entityIndex.byType.get('IFCSTYLEDREPRESENTATION') ?? [];
387
+ for (const ids of [styledItemIds, styledRepIds]) {
388
+ for (const expressId of ids) {
389
+ if (closure.has(expressId))
390
+ continue;
391
+ const entityRef = entityIndex.byId.get(expressId);
392
+ if (!entityRef)
393
+ continue;
394
+ // Check if any referenced ID is in the closure
395
+ refs.length = 0;
396
+ extractRefsFromBytes(source, entityRef.byteOffset, entityRef.byteLength, refs);
397
+ let referencesClosureEntity = false;
398
+ for (let i = 0; i < refs.length; i++) {
399
+ if (closure.has(refs[i])) {
400
+ referencesClosureEntity = true;
401
+ break;
402
+ }
403
+ }
404
+ if (referencesClosureEntity) {
405
+ closure.add(expressId);
406
+ queue.push(expressId);
407
+ }
408
+ }
409
+ }
410
+ // Walk forward from newly added style entities to pull in their style chain
411
+ // (IfcPresentationStyleAssignment → IfcSurfaceStyle → IfcSurfaceStyleRendering → IfcColourRgb)
412
+ while (queue.length > 0) {
413
+ const entityId = queue.pop();
414
+ const ref = entityIndex.byId.get(entityId);
415
+ if (!ref)
416
+ continue;
417
+ refs.length = 0;
418
+ extractRefsFromBytes(source, ref.byteOffset, ref.byteLength, refs);
419
+ for (let i = 0; i < refs.length; i++) {
420
+ const referencedId = refs[i];
421
+ if (!closure.has(referencedId) && entityIndex.byId.has(referencedId)) {
422
+ closure.add(referencedId);
423
+ queue.push(referencedId);
424
+ }
425
+ }
426
+ }
427
+ }
428
+ //# sourceMappingURL=reference-collector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-collector.js","sourceRoot":"","sources":["../src/reference-collector.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,12 @@ export interface StepExportOptions {
34
34
  applyMutations?: boolean;
35
35
  /** Only export entities with mutations (delta export) */
36
36
  deltaOnly?: boolean;
37
+ /** Only export entities currently visible in the viewer */
38
+ visibleOnly?: boolean;
39
+ /** Hidden entity IDs (local expressIds) — required when visibleOnly is true */
40
+ hiddenEntityIds?: Set<number>;
41
+ /** Isolated entity IDs (local expressIds, null = no isolation active) */
42
+ isolatedEntityIds?: Set<number> | null;
37
43
  }
38
44
  /**
39
45
  * Result of STEP export
@@ -1 +1 @@
1
- {"version":3,"file":"step-exporter.d.ts","sourceRoot":"","sources":["../src/step-exporter.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AASrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAI/D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,yBAAyB;IACzB,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IACrC,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,yDAAyD;IACzD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,4CAA4C;IAC5C,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,yDAAyD;IACzD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,KAAK,EAAE;QACL,8BAA8B;QAC9B,WAAW,EAAE,MAAM,CAAC;QACpB,yCAAyC;QACzC,cAAc,EAAE,MAAM,CAAC;QACvB,qCAAqC;QACrC,mBAAmB,EAAE,MAAM,CAAC;QAC5B,yBAAyB;QACzB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,aAAa,CAAS;gBAElB,SAAS,EAAE,YAAY,EAAE,YAAY,CAAC,EAAE,mBAAmB;IAOvE;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB;IAmJpD;;OAEG;IACH,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,GAAG,gBAAgB;IAQ3F;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAgDnC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IASxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;OAEG;IACH,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAqCxB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAe7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAe1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAqB5B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,YAAY,EACvB,OAAO,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GACnC,MAAM,CAOR"}
1
+ {"version":3,"file":"step-exporter.d.ts","sourceRoot":"","sources":["../src/step-exporter.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AASrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAK/D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,yBAAyB;IACzB,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IACrC,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,yDAAyD;IACzD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,4CAA4C;IAC5C,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,yDAAyD;IACzD,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,KAAK,EAAE;QACL,8BAA8B;QAC9B,WAAW,EAAE,MAAM,CAAC;QACpB,yCAAyC;QACzC,cAAc,EAAE,MAAM,CAAC;QACvB,qCAAqC;QACrC,mBAAmB,EAAE,MAAM,CAAC;QAC5B,yBAAyB;QACzB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,aAAa,CAAS;gBAElB,SAAS,EAAE,YAAY,EAAE,YAAY,CAAC,EAAE,mBAAmB;IAOvE;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB;IAgLpD;;OAEG;IACH,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,GAAG,gBAAgB;IAQ3F;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAgDnC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IASxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;OAEG;IACH,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAqCxB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAe7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAe1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAqB5B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,YAAY,EACvB,OAAO,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GACnC,MAAM,CAOR"}
@@ -3,6 +3,7 @@
3
3
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
4
  import { generateHeader, serializeValue, ref, } from '@ifc-lite/parser';
5
5
  import { PropertyValueType } from '@ifc-lite/data';
6
+ import { collectReferencedEntityIds, getVisibleEntityIds, collectStyleEntities } from './reference-collector.js';
6
7
  /**
7
8
  * IFC STEP file exporter
8
9
  */
@@ -102,12 +103,26 @@ export class StepExporter {
102
103
  },
103
104
  };
104
105
  }
106
+ // Build visible-only closure if requested
107
+ let allowedEntityIds = null;
108
+ if (options.visibleOnly && this.dataStore.source) {
109
+ const { roots, hiddenProductIds } = getVisibleEntityIds(this.dataStore, options.hiddenEntityIds ?? new Set(), options.isolatedEntityIds ?? null);
110
+ allowedEntityIds = collectReferencedEntityIds(roots, this.dataStore.source, this.dataStore.entityIndex.byId, hiddenProductIds);
111
+ // Second pass: collect IFCSTYLEDITEM entities that reference included
112
+ // geometry. Styled items reference geometry items but nothing references
113
+ // them back, so the forward closure misses them.
114
+ collectStyleEntities(allowedEntityIds, this.dataStore.source, this.dataStore.entityIndex);
115
+ }
105
116
  // Export original entities from source buffer, SKIPPING modified property sets
106
117
  if (!options.deltaOnly && this.dataStore.source) {
107
118
  const decoder = new TextDecoder();
108
119
  const source = this.dataStore.source;
109
120
  // Extract existing entities from source
110
121
  for (const [expressId, entityRef] of this.dataStore.entityIndex.byId) {
122
+ // Skip entities outside the visible closure
123
+ if (allowedEntityIds !== null && !allowedEntityIds.has(expressId)) {
124
+ continue;
125
+ }
111
126
  // Skip property sets/relationships that are being replaced
112
127
  if (skipPropertySetIds.has(expressId) || skipRelationshipIds.has(expressId)) {
113
128
  continue;