@ifc-lite/export 3.1.0 → 4.0.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.
- package/README.md +3 -1
- package/dist/anonymize-export.d.ts.map +1 -1
- package/dist/anonymize-export.js +108 -8
- package/dist/anonymize-export.js.map +1 -1
- package/dist/anonymize-placement.js +4 -4
- package/dist/anonymize-scrub.d.ts.map +1 -1
- package/dist/anonymize-scrub.js +5 -2
- package/dist/anonymize-scrub.js.map +1 -1
- package/dist/anonymize-types.d.ts +30 -7
- package/dist/anonymize-types.d.ts.map +1 -1
- package/dist/declared-property-type.d.ts +15 -0
- package/dist/declared-property-type.d.ts.map +1 -1
- package/dist/declared-property-type.js +43 -0
- package/dist/declared-property-type.js.map +1 -1
- package/dist/georef-closure.d.ts +55 -0
- package/dist/georef-closure.d.ts.map +1 -0
- package/dist/georef-closure.js +95 -0
- package/dist/georef-closure.js.map +1 -0
- package/dist/glb.d.ts.map +1 -1
- package/dist/glb.js +26 -2
- package/dist/glb.js.map +1 -1
- package/dist/ifc5-classification.d.ts +36 -0
- package/dist/ifc5-classification.d.ts.map +1 -0
- package/dist/ifc5-classification.js +46 -0
- package/dist/ifc5-classification.js.map +1 -0
- package/dist/ifc5-export-helpers.d.ts +32 -0
- package/dist/ifc5-export-helpers.d.ts.map +1 -0
- package/dist/ifc5-export-helpers.js +84 -0
- package/dist/ifc5-export-helpers.js.map +1 -0
- package/dist/ifc5-exporter.d.ts.map +1 -1
- package/dist/ifc5-exporter.js +14 -86
- package/dist/ifc5-exporter.js.map +1 -1
- package/dist/ifc5-material.d.ts +38 -0
- package/dist/ifc5-material.d.ts.map +1 -0
- package/dist/ifc5-material.js +32 -0
- package/dist/ifc5-material.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/merged-context.d.ts +100 -0
- package/dist/merged-context.d.ts.map +1 -0
- package/dist/merged-context.js +211 -0
- package/dist/merged-context.js.map +1 -0
- package/dist/merged-empty-containers.d.ts +63 -0
- package/dist/merged-empty-containers.d.ts.map +1 -0
- package/dist/merged-empty-containers.js +354 -0
- package/dist/merged-empty-containers.js.map +1 -0
- package/dist/merged-exporter.d.ts +28 -67
- package/dist/merged-exporter.d.ts.map +1 -1
- package/dist/merged-exporter.js +134 -258
- package/dist/merged-exporter.js.map +1 -1
- package/dist/merged-guid.d.ts +48 -0
- package/dist/merged-guid.d.ts.map +1 -0
- package/dist/merged-guid.js +128 -0
- package/dist/merged-guid.js.map +1 -0
- package/dist/merged-rel-aggregates.d.ts +63 -0
- package/dist/merged-rel-aggregates.d.ts.map +1 -0
- package/dist/merged-rel-aggregates.js +122 -0
- package/dist/merged-rel-aggregates.js.map +1 -0
- package/dist/merged-remap.d.ts +18 -0
- package/dist/merged-remap.d.ts.map +1 -0
- package/dist/merged-remap.js +66 -0
- package/dist/merged-remap.js.map +1 -0
- package/dist/merged-subcontext.d.ts +41 -0
- package/dist/merged-subcontext.d.ts.map +1 -0
- package/dist/merged-subcontext.js +99 -0
- package/dist/merged-subcontext.js.map +1 -0
- package/dist/parquet-exporter-ondemand.d.ts +34 -0
- package/dist/parquet-exporter-ondemand.d.ts.map +1 -0
- package/dist/parquet-exporter-ondemand.js +89 -0
- package/dist/parquet-exporter-ondemand.js.map +1 -0
- package/dist/parquet-exporter.d.ts +1 -29
- package/dist/parquet-exporter.d.ts.map +1 -1
- package/dist/parquet-exporter.js +59 -81
- package/dist/parquet-exporter.js.map +1 -1
- package/dist/parquet-type-strings.d.ts +11 -0
- package/dist/parquet-type-strings.d.ts.map +1 -0
- package/dist/parquet-type-strings.js +40 -0
- package/dist/parquet-type-strings.js.map +1 -0
- package/dist/parquet-uint32-columns.d.ts +30 -0
- package/dist/parquet-uint32-columns.d.ts.map +1 -0
- package/dist/parquet-uint32-columns.js +38 -0
- package/dist/parquet-uint32-columns.js.map +1 -0
- package/dist/reference-collector.d.ts +0 -31
- package/dist/reference-collector.d.ts.map +1 -1
- package/dist/reference-collector.js +27 -82
- package/dist/reference-collector.js.map +1 -1
- package/dist/related-entities.d.ts.map +1 -1
- package/dist/related-entities.js +19 -4
- package/dist/related-entities.js.map +1 -1
- package/dist/schema-converter-attr-remap.d.ts +53 -0
- package/dist/schema-converter-attr-remap.d.ts.map +1 -0
- package/dist/schema-converter-attr-remap.js +106 -0
- package/dist/schema-converter-attr-remap.js.map +1 -0
- package/dist/schema-converter.d.ts.map +1 -1
- package/dist/schema-converter.js +24 -1
- package/dist/schema-converter.js.map +1 -1
- package/dist/schema-untranslatable.d.ts +35 -0
- package/dist/schema-untranslatable.d.ts.map +1 -0
- package/dist/schema-untranslatable.js +60 -0
- package/dist/schema-untranslatable.js.map +1 -0
- package/dist/step-argument-parser.d.ts.map +1 -1
- package/dist/step-argument-parser.js +11 -1
- package/dist/step-argument-parser.js.map +1 -1
- package/dist/step-attribute-mutations.js +14 -11
- package/dist/step-attribute-mutations.js.map +1 -1
- package/dist/step-collection.d.ts.map +1 -1
- package/dist/step-collection.js +20 -6
- package/dist/step-collection.js.map +1 -1
- package/dist/step-export-types.d.ts +15 -0
- package/dist/step-export-types.d.ts.map +1 -1
- package/dist/step-export-types.js +10 -0
- package/dist/step-export-types.js.map +1 -1
- package/dist/step-exporter.d.ts.map +1 -1
- package/dist/step-exporter.js +9 -1
- package/dist/step-exporter.js.map +1 -1
- package/dist/step-map-unit.d.ts +2 -1
- package/dist/step-map-unit.d.ts.map +1 -1
- package/dist/step-map-unit.js.map +1 -1
- package/dist/step-serialization.d.ts +10 -30
- package/dist/step-serialization.d.ts.map +1 -1
- package/dist/step-serialization.js +10 -60
- package/dist/step-serialization.js.map +1 -1
- package/dist/step-source-iteration.d.ts.map +1 -1
- package/dist/step-source-iteration.js +21 -0
- package/dist/step-source-iteration.js.map +1 -1
- package/dist/style-closure.d.ts +91 -0
- package/dist/style-closure.d.ts.map +1 -0
- package/dist/style-closure.js +164 -0
- package/dist/style-closure.js.map +1 -0
- package/dist/subset-entity-reader.d.ts +10 -9
- package/dist/subset-entity-reader.d.ts.map +1 -1
- package/dist/subset-entity-reader.js +21 -11
- package/dist/subset-entity-reader.js.map +1 -1
- package/dist/subset-roots.d.ts +17 -1
- package/dist/subset-roots.d.ts.map +1 -1
- package/dist/subset-roots.js +67 -2
- package/dist/subset-roots.js.map +1 -1
- package/dist/type-owned-psets.d.ts +19 -0
- package/dist/type-owned-psets.d.ts.map +1 -1
- package/dist/type-owned-psets.js +31 -0
- package/dist/type-owned-psets.js.map +1 -1
- package/dist/unit-normalize.d.ts.map +1 -1
- package/dist/unit-normalize.js +10 -2
- package/dist/unit-normalize.js.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* Express-id remapping for the merged exporter: rewrite every `#N` reference of
|
|
6
|
+
* one model's STEP line into the merged file's id space.
|
|
7
|
+
*
|
|
8
|
+
* Split out of `merged-exporter.ts` (it never used `this`) so the id-rewriting
|
|
9
|
+
* rule lives in one small module next to the other merge-time line surgery
|
|
10
|
+
* (`merged-empty-containers.ts`, `merged-subcontext.ts`).
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Remap all #ID references in a STEP entity line.
|
|
14
|
+
* Applies offset to all IDs, then overrides with specific remappings.
|
|
15
|
+
*
|
|
16
|
+
* Only `#<digits>` tokens in code positions are rewritten; tokens inside
|
|
17
|
+
* single-quoted STEP strings (e.g. a 'Room #205' Name or a 'http://x#42'
|
|
18
|
+
* URL) are left untouched so string attribute values are not corrupted.
|
|
19
|
+
*/
|
|
20
|
+
export function remapEntityText(entityText, offset, sharedRemap) {
|
|
21
|
+
const remapId = (originalId) => {
|
|
22
|
+
// Check if this ID has a specific remap (project, shared infrastructure)
|
|
23
|
+
const remapped = sharedRemap.get(originalId);
|
|
24
|
+
if (remapped !== undefined) {
|
|
25
|
+
return `#${remapped}`;
|
|
26
|
+
}
|
|
27
|
+
// Apply offset
|
|
28
|
+
return `#${originalId + offset}`;
|
|
29
|
+
};
|
|
30
|
+
let out = '';
|
|
31
|
+
let inString = false;
|
|
32
|
+
for (let i = 0; i < entityText.length; i++) {
|
|
33
|
+
const char = entityText[i];
|
|
34
|
+
if (inString) {
|
|
35
|
+
out += char;
|
|
36
|
+
if (char === "'") {
|
|
37
|
+
// STEP escapes a literal quote by doubling it ('').
|
|
38
|
+
if (entityText[i + 1] === "'") {
|
|
39
|
+
out += entityText[i + 1];
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
inString = false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (char === "'") {
|
|
49
|
+
inString = true;
|
|
50
|
+
out += char;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (char === '#' && entityText[i + 1] >= '0' && entityText[i + 1] <= '9') {
|
|
54
|
+
let j = i + 1;
|
|
55
|
+
while (j < entityText.length && entityText[j] >= '0' && entityText[j] <= '9')
|
|
56
|
+
j++;
|
|
57
|
+
const originalId = parseInt(entityText.slice(i + 1, j), 10);
|
|
58
|
+
out += remapId(originalId);
|
|
59
|
+
i = j - 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
out += char;
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=merged-remap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merged-remap.js","sourceRoot":"","sources":["../src/merged-remap.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAkB,EAClB,MAAc,EACd,WAAwC;IAExC,MAAM,OAAO,GAAG,CAAC,UAAkB,EAAU,EAAE;QAC7C,yEAAyE;QACzE,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,IAAI,QAAQ,EAAE,CAAC;QACxB,CAAC;QACD,eAAe;QACf,OAAO,IAAI,UAAU,GAAG,MAAM,EAAE,CAAC;IACnC,CAAC,CAAC;IAEF,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAE3B,IAAI,QAAQ,EAAE,CAAC;YACb,GAAG,IAAI,IAAI,CAAC;YACZ,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;gBACjB,oDAAoD;gBACpD,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBAC9B,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBACzB,CAAC,EAAE,CAAC;gBACN,CAAC;qBAAM,CAAC;oBACN,QAAQ,GAAG,KAAK,CAAC;gBACnB,CAAC;YACH,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,QAAQ,GAAG,IAAI,CAAC;YAChB,GAAG,IAAI,IAAI,CAAC;YACZ,SAAS;QACX,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC;YACzE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,GAAG;gBAAE,CAAC,EAAE,CAAC;YAClF,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5D,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;YAC3B,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACV,SAAS;QACX,CAAC;QAED,GAAG,IAAI,IAAI,CAAC;IACd,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kind-aware `IfcGeometricRepresentationSubContext` matching for
|
|
3
|
+
* {@link MergedExporter} (merge invariant lens pass 3, item 1).
|
|
4
|
+
*
|
|
5
|
+
* `MergedExporter` deduplicates a unit-compatible model's shared
|
|
6
|
+
* infrastructure — `IfcUnitAssignment`, `IfcGeometricRepresentationContext`
|
|
7
|
+
* and subcontexts — onto the primary model's instance. For subcontexts that
|
|
8
|
+
* used to mean taking the two models' subcontext id lists **positionally**:
|
|
9
|
+
* this model's first subcontext (in whatever order `entityIndex.byType`
|
|
10
|
+
* returns them) was unified onto the primary model's first subcontext, with
|
|
11
|
+
* no check that the two are the same kind.
|
|
12
|
+
*
|
|
13
|
+
* A model normally declares more than one subcontext ('Body' for solid
|
|
14
|
+
* geometry, 'Axis' for centreline geometry, sometimes 'FootPrint'/'Box'), and
|
|
15
|
+
* nothing in the IFC schema or common exporter behaviour guarantees they are
|
|
16
|
+
* always written in the same order across two different authoring tools. A
|
|
17
|
+
* positional match can therefore unify this model's 'Body' subcontext onto
|
|
18
|
+
* the primary model's 'Axis' subcontext (or vice versa): every
|
|
19
|
+
* `IfcShapeRepresentation.ContextOfItems` that pointed at the dropped 'Body'
|
|
20
|
+
* subcontext now resolves, after the remap, to a context tagged 'Axis' — the
|
|
21
|
+
* wrong kind, which many viewers filter out of the 3D view entirely (the
|
|
22
|
+
* geometry silently vanishes), with no dangling reference to reveal it.
|
|
23
|
+
*
|
|
24
|
+
* `planSubContextUnify` fixes this by matching on the subcontext's *kind*
|
|
25
|
+
* (its `ContextIdentifier` *and* `TargetView`, plus `UserDefinedTargetView`
|
|
26
|
+
* where applicable) rather than on raw array position.
|
|
27
|
+
*/
|
|
28
|
+
import type { IfcDataStore } from '@ifc-lite/parser';
|
|
29
|
+
/** Group `ids` (a model's subcontext express ids) by {@link subContextKey}. */
|
|
30
|
+
export declare function groupSubContextsByKey(dataStore: IfcDataStore, ids: readonly number[]): Map<string, number[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Plan this model's subcontext dedup against the primary model, key by key:
|
|
33
|
+
* a subcontext in `thisIds` is remapped+skipped only against an unclaimed
|
|
34
|
+
* primary-model subcontext with the SAME key (never a differently ordered one
|
|
35
|
+
* of a different kind or TargetView). A subcontext with no same-key match in
|
|
36
|
+
* the primary model is left un-remapped — kept as its own (offset-only)
|
|
37
|
+
* entity, exactly like today's "no shared infrastructure of this type"
|
|
38
|
+
* fallback. Mutates `sharedRemap`/`skipEntityIds`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function planSubContextUnify(dataStore: IfcDataStore, thisIds: readonly number[], firstModelSubContextsByKey: ReadonlyMap<string, number[]>, firstModelOffset: number, sharedRemap: Map<number, number>, skipEntityIds: Set<number>): void;
|
|
41
|
+
//# sourceMappingURL=merged-subcontext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merged-subcontext.d.ts","sourceRoot":"","sources":["../src/merged-subcontext.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA8DrD,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAQ5G;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,0BAA0B,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACzD,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GACzB,IAAI,CAYN"}
|
|
@@ -0,0 +1,99 @@
|
|
|
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
|
+
import { asSourceBytes, STEP_TRIVIA } from '@ifc-lite/parser';
|
|
5
|
+
import { splitTopLevelStepArguments } from './step-argument-parser.js';
|
|
6
|
+
/**
|
|
7
|
+
* `#N=TYPE(...)` record, with STEP trivia (whitespace and/or a
|
|
8
|
+
* `/* ... */` comment, #3789) tolerated between the type name and `(` —
|
|
9
|
+
* same adjacency fix as `entity-extractor.ts`'s `extractEntity`. Without it
|
|
10
|
+
* a wrapped record's attribute is silently unreadable here (`getStepAttr`
|
|
11
|
+
* returns `null`), which reads identically to a genuinely-absent attribute.
|
|
12
|
+
*/
|
|
13
|
+
const RECORD_RE = new RegExp(`^#\\d+\\s*=\\s*\\w+${STEP_TRIVIA}\\(([\\s\\S]*)\\)\\s*;?\\s*$`);
|
|
14
|
+
/** 0-based attribute index of `IfcGeometricRepresentationSubContext.ContextIdentifier`. */
|
|
15
|
+
const CONTEXT_IDENTIFIER_ATTR = 0;
|
|
16
|
+
/** 0-based attribute index of `IfcGeometricRepresentationSubContext.TargetView`. */
|
|
17
|
+
const TARGET_VIEW_ATTR = 8;
|
|
18
|
+
/** 0-based attribute index of `IfcGeometricRepresentationSubContext.UserDefinedTargetView`. */
|
|
19
|
+
const USER_DEFINED_TARGET_VIEW_ATTR = 9;
|
|
20
|
+
function decodeEntity(dataStore, expressId) {
|
|
21
|
+
const source = dataStore.source;
|
|
22
|
+
if (!source)
|
|
23
|
+
return null;
|
|
24
|
+
const ref = dataStore.entityIndex.byId.get(expressId);
|
|
25
|
+
if (!ref)
|
|
26
|
+
return null;
|
|
27
|
+
return asSourceBytes(source).decodeUtf8(ref.byteOffset, ref.byteOffset + ref.byteLength);
|
|
28
|
+
}
|
|
29
|
+
/** Extract one 0-based STEP attribute of `expressId`'s entity, or null if unreadable. */
|
|
30
|
+
function getStepAttr(dataStore, expressId, index) {
|
|
31
|
+
const text = decodeEntity(dataStore, expressId);
|
|
32
|
+
const match = text?.match(RECORD_RE);
|
|
33
|
+
if (!match)
|
|
34
|
+
return null;
|
|
35
|
+
const args = splitTopLevelStepArguments(match[1]);
|
|
36
|
+
const raw = args?.[index];
|
|
37
|
+
return raw === undefined ? null : raw.trim();
|
|
38
|
+
}
|
|
39
|
+
/** Normalize a STEP string/enum token to a comparison key; `''` for unset/unreadable. */
|
|
40
|
+
function normalizeLabel(raw) {
|
|
41
|
+
if (!raw || raw === '$' || raw === '*')
|
|
42
|
+
return '';
|
|
43
|
+
const quoted = raw.match(/^'([\s\S]*)'$/);
|
|
44
|
+
const inner = quoted ? quoted[1] : raw;
|
|
45
|
+
return inner.replace(/\./g, '').trim().toUpperCase();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The matching key of one `IfcGeometricRepresentationSubContext` combines its
|
|
49
|
+
* `ContextIdentifier` and `TargetView`: a Body representation for MODEL_VIEW
|
|
50
|
+
* is not interchangeable with one for PLAN_VIEW. `UserDefinedTargetView`
|
|
51
|
+
* distinguishes two USERDEFINED views. Empty pieces deliberately remain part
|
|
52
|
+
* of the key so identifier-less subcontexts still group by their view.
|
|
53
|
+
*/
|
|
54
|
+
function subContextKey(dataStore, expressId) {
|
|
55
|
+
const identifier = normalizeLabel(getStepAttr(dataStore, expressId, CONTEXT_IDENTIFIER_ATTR));
|
|
56
|
+
const targetView = normalizeLabel(getStepAttr(dataStore, expressId, TARGET_VIEW_ATTR));
|
|
57
|
+
const userDefinedTargetView = targetView === 'USERDEFINED'
|
|
58
|
+
? normalizeLabel(getStepAttr(dataStore, expressId, USER_DEFINED_TARGET_VIEW_ATTR))
|
|
59
|
+
: '';
|
|
60
|
+
return `${identifier}\u0000${targetView}\u0000${userDefinedTargetView}`;
|
|
61
|
+
}
|
|
62
|
+
/** Group `ids` (a model's subcontext express ids) by {@link subContextKey}. */
|
|
63
|
+
export function groupSubContextsByKey(dataStore, ids) {
|
|
64
|
+
const map = new Map();
|
|
65
|
+
for (const id of ids) {
|
|
66
|
+
const key = subContextKey(dataStore, id);
|
|
67
|
+
const bucket = map.get(key);
|
|
68
|
+
if (bucket)
|
|
69
|
+
bucket.push(id);
|
|
70
|
+
else
|
|
71
|
+
map.set(key, [id]);
|
|
72
|
+
}
|
|
73
|
+
return map;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Plan this model's subcontext dedup against the primary model, key by key:
|
|
77
|
+
* a subcontext in `thisIds` is remapped+skipped only against an unclaimed
|
|
78
|
+
* primary-model subcontext with the SAME key (never a differently ordered one
|
|
79
|
+
* of a different kind or TargetView). A subcontext with no same-key match in
|
|
80
|
+
* the primary model is left un-remapped — kept as its own (offset-only)
|
|
81
|
+
* entity, exactly like today's "no shared infrastructure of this type"
|
|
82
|
+
* fallback. Mutates `sharedRemap`/`skipEntityIds`.
|
|
83
|
+
*/
|
|
84
|
+
export function planSubContextUnify(dataStore, thisIds, firstModelSubContextsByKey, firstModelOffset, sharedRemap, skipEntityIds) {
|
|
85
|
+
const nextIndex = new Map();
|
|
86
|
+
for (const id of thisIds) {
|
|
87
|
+
const key = subContextKey(dataStore, id);
|
|
88
|
+
const pool = firstModelSubContextsByKey.get(key);
|
|
89
|
+
if (!pool || pool.length === 0)
|
|
90
|
+
continue;
|
|
91
|
+
const idx = nextIndex.get(key) ?? 0;
|
|
92
|
+
if (idx >= pool.length)
|
|
93
|
+
continue; // every same-kind primary target already claimed
|
|
94
|
+
nextIndex.set(key, idx + 1);
|
|
95
|
+
sharedRemap.set(id, pool[idx] + firstModelOffset);
|
|
96
|
+
skipEntityIds.add(id);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=merged-subcontext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merged-subcontext.js","sourceRoot":"","sources":["../src/merged-subcontext.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Properties.parquet` / `Quantities.parquet` writers for the on-demand parse
|
|
3
|
+
* path, extracted from `ParquetExporter` (`parquet-exporter.ts`) to keep that
|
|
4
|
+
* file under its module-size budget.
|
|
5
|
+
*
|
|
6
|
+
* `IfcParser.parseColumnar` (`packages/parser`'s only parse path, used by
|
|
7
|
+
* every real caller) never calls `.add()` on the `PropertyTableBuilder`/
|
|
8
|
+
* `QuantityTableBuilder` it constructs — properties and quantities are served
|
|
9
|
+
* lazily through `onDemandPropertyMap`/`onDemandQuantityMap` +
|
|
10
|
+
* `store.getProperties()`/`store.getQuantities()` instead
|
|
11
|
+
* (`extractPropertiesOnDemand`/`extractQuantitiesOnDemand`, re-parsing the
|
|
12
|
+
* source buffer on access). `ParquetExporter`'s bulk-table writers read
|
|
13
|
+
* `store.properties`/`store.quantities` directly, which is only populated
|
|
14
|
+
* when a caller builds a store some other way (e.g. this package's own
|
|
15
|
+
* hand-built test fixtures) — every store from a real parse left those two
|
|
16
|
+
* tables at zero rows, silently, alongside a fully-populated
|
|
17
|
+
* `Entities.parquet`/`Relationships.parquet`. These two functions are the
|
|
18
|
+
* on-demand fallback `ParquetExporter` calls when the bulk table is empty:
|
|
19
|
+
* one flat row per property/quantity, read through the same
|
|
20
|
+
* `store.getProperties()`/`store.getQuantities()` accessor every other
|
|
21
|
+
* consumer (the query package, the viewer's property panel) uses — never a
|
|
22
|
+
* second copy of the pset/property walk.
|
|
23
|
+
*/
|
|
24
|
+
import type { IfcDataStore } from '@ifc-lite/parser';
|
|
25
|
+
import type { EffectiveEntityIndex } from './effective-index.js';
|
|
26
|
+
export declare function writePropertiesOnDemand(store: IfcDataStore, effective: EffectiveEntityIndex | null): Promise<Uint8Array>;
|
|
27
|
+
/**
|
|
28
|
+
* `Formula` is always NULL here: the on-demand quantity reader
|
|
29
|
+
* (`readQuantitySet` in `@ifc-lite/parser`) reports only
|
|
30
|
+
* `{name, type, value}` per quantity, so there is no formula string to carry
|
|
31
|
+
* — `ParquetExporter`'s bulk-table path is the only one that can populate it.
|
|
32
|
+
*/
|
|
33
|
+
export declare function writeQuantitiesOnDemand(store: IfcDataStore, effective: EffectiveEntityIndex | null): Promise<Uint8Array>;
|
|
34
|
+
//# sourceMappingURL=parquet-exporter-ondemand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parquet-exporter-ondemand.d.ts","sourceRoot":"","sources":["../src/parquet-exporter-ondemand.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAKjE,wBAAsB,uBAAuB,CACzC,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,oBAAoB,GAAG,IAAI,GACvC,OAAO,CAAC,UAAU,CAAC,CA4CrB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CACzC,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,oBAAoB,GAAG,IAAI,GACvC,OAAO,CAAC,UAAU,CAAC,CA+BrB"}
|
|
@@ -0,0 +1,89 @@
|
|
|
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
|
+
import { PropertyValueType } from '@ifc-lite/data';
|
|
5
|
+
import { columnsToParquet } from './columns-to-parquet.js';
|
|
6
|
+
import { PARQUET_UINT32_COLUMNS } from './parquet-uint32-columns.js';
|
|
7
|
+
import { propertyValueTypeToString, quantityTypeToString } from './parquet-type-strings.js';
|
|
8
|
+
export async function writePropertiesOnDemand(store, effective) {
|
|
9
|
+
const entityIdCol = [];
|
|
10
|
+
const psetNameCol = [];
|
|
11
|
+
const psetGlobalIdCol = [];
|
|
12
|
+
const propNameCol = [];
|
|
13
|
+
const propTypeCol = [];
|
|
14
|
+
const valueStringCol = [];
|
|
15
|
+
// `IfcPropertyBoundedValue` is surfaced as a display string but retains
|
|
16
|
+
// the REAL type tag. Only a scalar numeric value belongs in ValueReal;
|
|
17
|
+
// writing 0 for the display-string form fabricates a measurement.
|
|
18
|
+
const valueRealCol = [];
|
|
19
|
+
const valueIntCol = [];
|
|
20
|
+
const valueBoolCol = [];
|
|
21
|
+
for (const id of store.onDemandPropertyMap.keys()) {
|
|
22
|
+
if (effective?.isDeleted(id))
|
|
23
|
+
continue;
|
|
24
|
+
const psets = store.getProperties(id);
|
|
25
|
+
for (const pset of psets) {
|
|
26
|
+
for (const prop of pset.properties) {
|
|
27
|
+
entityIdCol.push(id);
|
|
28
|
+
psetNameCol.push(pset.name);
|
|
29
|
+
psetGlobalIdCol.push(pset.globalId ?? '');
|
|
30
|
+
propNameCol.push(prop.name);
|
|
31
|
+
propTypeCol.push(prop.type);
|
|
32
|
+
const v = prop.value;
|
|
33
|
+
valueStringCol.push(typeof v === 'string' ? v : null);
|
|
34
|
+
valueRealCol.push(prop.type === PropertyValueType.Real && typeof v === 'number' ? v : null);
|
|
35
|
+
valueIntCol.push(prop.type === PropertyValueType.Integer && typeof v === 'number' ? v : 0);
|
|
36
|
+
valueBoolCol.push(typeof v === 'boolean' ? v : null);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return columnsToParquet({
|
|
41
|
+
EntityId: entityIdCol,
|
|
42
|
+
PsetName: psetNameCol,
|
|
43
|
+
PsetGlobalId: psetGlobalIdCol,
|
|
44
|
+
PropName: propNameCol,
|
|
45
|
+
PropType: propTypeCol.map(t => propertyValueTypeToString(t)),
|
|
46
|
+
ValueString: valueStringCol,
|
|
47
|
+
ValueReal: valueRealCol,
|
|
48
|
+
ValueInt: valueIntCol,
|
|
49
|
+
ValueBool: valueBoolCol,
|
|
50
|
+
}, new Set(['ValueReal']), PARQUET_UINT32_COLUMNS);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* `Formula` is always NULL here: the on-demand quantity reader
|
|
54
|
+
* (`readQuantitySet` in `@ifc-lite/parser`) reports only
|
|
55
|
+
* `{name, type, value}` per quantity, so there is no formula string to carry
|
|
56
|
+
* — `ParquetExporter`'s bulk-table path is the only one that can populate it.
|
|
57
|
+
*/
|
|
58
|
+
export async function writeQuantitiesOnDemand(store, effective) {
|
|
59
|
+
const entityIdCol = [];
|
|
60
|
+
const qsetNameCol = [];
|
|
61
|
+
const quantityNameCol = [];
|
|
62
|
+
const quantityTypeCol = [];
|
|
63
|
+
const valueCol = [];
|
|
64
|
+
const formulaCol = [];
|
|
65
|
+
for (const id of store.onDemandQuantityMap.keys()) {
|
|
66
|
+
if (effective?.isDeleted(id))
|
|
67
|
+
continue;
|
|
68
|
+
const qsets = store.getQuantities(id);
|
|
69
|
+
for (const qset of qsets) {
|
|
70
|
+
for (const q of qset.quantities) {
|
|
71
|
+
entityIdCol.push(id);
|
|
72
|
+
qsetNameCol.push(qset.name);
|
|
73
|
+
quantityNameCol.push(q.name);
|
|
74
|
+
quantityTypeCol.push(q.type);
|
|
75
|
+
valueCol.push(q.value);
|
|
76
|
+
formulaCol.push(q.formula ?? null);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return columnsToParquet({
|
|
81
|
+
EntityId: entityIdCol,
|
|
82
|
+
QsetName: qsetNameCol,
|
|
83
|
+
QuantityName: quantityNameCol,
|
|
84
|
+
QuantityType: quantityTypeCol.map(t => quantityTypeToString(t)),
|
|
85
|
+
Value: valueCol,
|
|
86
|
+
Formula: formulaCol,
|
|
87
|
+
}, new Set(['Value']), PARQUET_UINT32_COLUMNS);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=parquet-exporter-ondemand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parquet-exporter-ondemand.js","sourceRoot":"","sources":["../src/parquet-exporter-ondemand.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import type { IfcDataStore } from '@ifc-lite/parser';
|
|
3
3
|
import type { GeometryResult } from '@ifc-lite/geometry';
|
|
4
4
|
import type { MutablePropertyView } from '@ifc-lite/mutations';
|
|
5
|
-
import { QuantityType } from '@ifc-lite/data';
|
|
6
5
|
export interface ParquetExportOptions {
|
|
7
6
|
includeGeometry?: boolean;
|
|
8
7
|
}
|
|
@@ -58,35 +57,8 @@ export declare class ParquetExporter {
|
|
|
58
57
|
private writeMeshes;
|
|
59
58
|
private writeSpatialHierarchy;
|
|
60
59
|
private writeMetadata;
|
|
61
|
-
/**
|
|
62
|
-
* Column names whose domain is an unsigned 32-bit integer.
|
|
63
|
-
*
|
|
64
|
-
* Every one carries an IFC EXPRESS ID or an index into a geometry buffer,
|
|
65
|
-
* and both are `u32` everywhere else in this codebase - `Uint32Array` in
|
|
66
|
-
* the parser's entity index and its transports, `u32` in the Rust crates.
|
|
67
|
-
* Arrow's content inference reaches for Int32 on any whole number, so an
|
|
68
|
-
* express id at or above 2_147_483_648 came out NEGATIVE: an id-shaped
|
|
69
|
-
* number that joins to nothing. STEP puts no upper bound on an entity id
|
|
70
|
-
* below the `u32` the readers use, so that is reachable input rather than a
|
|
71
|
-
* hypothetical.
|
|
72
|
-
*/
|
|
73
|
-
private static readonly UINT32_COLUMNS;
|
|
74
|
-
/**
|
|
75
|
-
* NOT in the set above, deliberately: `BuildingId`, `SiteId` and `SpaceId`
|
|
76
|
-
* in `SpatialHierarchy.parquet` carry **-1 as "none"** (see
|
|
77
|
-
* `writeSpatialHierarchy` - a storey directly under the project has no
|
|
78
|
-
* building). Declaring those unsigned turned that sentinel into
|
|
79
|
-
* 4294967295: an id-shaped number where an obviously-absent marker used to
|
|
80
|
-
* be, which is the exact failure this class of change exists to prevent.
|
|
81
|
-
*
|
|
82
|
-
* The residual gap is the narrower one: a building or site id at or above
|
|
83
|
-
* 2^31 still wraps negative in those three columns. Fixing that properly
|
|
84
|
-
* means writing NULL rather than -1 for "none", which changes what every
|
|
85
|
-
* consumer reads for an absent parent and is a separate decision from the
|
|
86
|
-
* id width.
|
|
87
|
-
*/
|
|
88
60
|
private toParquet;
|
|
89
61
|
private createZipArchive;
|
|
90
62
|
}
|
|
91
|
-
export
|
|
63
|
+
export { quantityTypeToString as QuantityTypeToString } from './parquet-type-strings.js';
|
|
92
64
|
//# sourceMappingURL=parquet-exporter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parquet-exporter.d.ts","sourceRoot":"","sources":["../src/parquet-exporter.ts"],"names":[],"mappings":"AAIA,uDAAuD;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"parquet-exporter.d.ts","sourceRoot":"","sources":["../src/parquet-exporter.ts"],"names":[],"mappings":"AAIA,uDAAuD;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAQ/D,MAAM,WAAW,oBAAoB;IACjC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;GAGG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,YAAY,CAA6B;IAEjD;;;;;;;;;;;;;;;;;OAiBG;gBACS,KAAK,EAAE,YAAY,EAAE,cAAc,CAAC,EAAE,cAAc,EAAE,YAAY,CAAC,EAAE,mBAAmB;IAMpG;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACG,SAAS,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,UAAU,CAAC;IA4BxE;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;YAkB3C,aAAa;YAoDb,eAAe;YAuCf,eAAe;YA0Bf,kBAAkB;YAwClB,YAAY;YAkBZ,iBAAiB;YA+DjB,gBAAgB;YA6BhB,WAAW;YAyCX,qBAAqB;IAuFnC,OAAO,CAAC,aAAa;YA6BP,SAAS;YAIT,gBAAgB;CAUjC;AAgCD,OAAO,EAAE,oBAAoB,IAAI,oBAAoB,EAAE,MAAM,2BAA2B,CAAC"}
|
package/dist/parquet-exporter.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
-
import { IfcTypeEnum, EntityFlags,
|
|
4
|
+
import { IfcTypeEnum, EntityFlags, RelationshipType, IFC_ENTITY_NAMES, exactTypeName, flattenRelationshipEdges } from '@ifc-lite/data';
|
|
5
5
|
import { getEffectiveEntityIndex } from './effective-index.js';
|
|
6
6
|
import { columnsToParquet } from './columns-to-parquet.js';
|
|
7
|
+
import { PARQUET_UINT32_COLUMNS } from './parquet-uint32-columns.js';
|
|
8
|
+
import { writePropertiesOnDemand, writeQuantitiesOnDemand } from './parquet-exporter-ondemand.js';
|
|
9
|
+
import { propertyValueTypeToString, quantityTypeToString } from './parquet-type-strings.js';
|
|
7
10
|
/**
|
|
8
11
|
* Export to ara3d BIM Open Schema compatible Parquet files.
|
|
9
12
|
* Creates a .bos archive (ZIP of Parquet files).
|
|
@@ -148,6 +151,22 @@ export class ParquetExporter {
|
|
|
148
151
|
async writeProperties() {
|
|
149
152
|
const { properties, strings } = this.store;
|
|
150
153
|
const effective = this.getEffective();
|
|
154
|
+
// `IfcParser.parseColumnar` (the sole parse path every real caller of
|
|
155
|
+
// this exporter goes through — `packages/parser`'s `parseLite`) never
|
|
156
|
+
// populates `store.properties`: it builds a `PropertyTableBuilder` but
|
|
157
|
+
// never calls `.add()` on it, relying instead on lazy per-entity
|
|
158
|
+
// extraction via `onDemandPropertyMap` + `store.getProperties()`
|
|
159
|
+
// (`extractPropertiesOnDemand`, re-parsing the source buffer on
|
|
160
|
+
// access). Reading `store.properties` directly here — the bulk table
|
|
161
|
+
// — silently wrote a zero-row `Properties.parquet` for every model
|
|
162
|
+
// parsed the normal way, geometry/relationships/entities tables full
|
|
163
|
+
// alongside it. `store.properties` only carries real rows when a
|
|
164
|
+
// caller builds a store some other way (e.g. hand-built fixtures in
|
|
165
|
+
// this file's own tests). Route through the on-demand path first;
|
|
166
|
+
// fall back to the bulk table when it was actually populated.
|
|
167
|
+
if (properties.entityId.length === 0 && this.store.onDemandPropertyMap && this.store.onDemandPropertyMap.size > 0) {
|
|
168
|
+
return writePropertiesOnDemand(this.store, effective);
|
|
169
|
+
}
|
|
151
170
|
const entityId = Array.from(properties.entityId);
|
|
152
171
|
// A property row belongs to the entity named in its own EntityId
|
|
153
172
|
// column, not to its own row index — filter on that, not on ExpressId.
|
|
@@ -157,7 +176,7 @@ export class ParquetExporter {
|
|
|
157
176
|
PsetName: mapTypedArray(properties.psetName, i => strings.get(i)),
|
|
158
177
|
PsetGlobalId: mapTypedArray(properties.psetGlobalId, i => strings.get(i)),
|
|
159
178
|
PropName: mapTypedArray(properties.propName, i => strings.get(i)),
|
|
160
|
-
PropType: mapTypedArray(properties.propType, t =>
|
|
179
|
+
PropType: mapTypedArray(properties.propType, t => propertyValueTypeToString(t)),
|
|
161
180
|
ValueString: mapTypedArray(properties.valueString, i => i >= 0 && i < strings.count ? strings.get(i) : null),
|
|
162
181
|
ValueReal: Array.from(properties.valueReal),
|
|
163
182
|
ValueInt: Array.from(properties.valueInt),
|
|
@@ -167,40 +186,54 @@ export class ParquetExporter {
|
|
|
167
186
|
async writeQuantities() {
|
|
168
187
|
const { quantities, strings } = this.store;
|
|
169
188
|
const effective = this.getEffective();
|
|
189
|
+
// Same gap as `writeProperties`: `store.quantities` is only ever
|
|
190
|
+
// populated when a caller bulk-builds it directly (this file's own
|
|
191
|
+
// tests); the real `parseColumnar` path leaves it empty and serves
|
|
192
|
+
// quantities lazily through `onDemandQuantityMap` +
|
|
193
|
+
// `store.getQuantities()`.
|
|
194
|
+
if (quantities.entityId.length === 0 && this.store.onDemandQuantityMap && this.store.onDemandQuantityMap.size > 0) {
|
|
195
|
+
return writeQuantitiesOnDemand(this.store, effective);
|
|
196
|
+
}
|
|
170
197
|
const entityId = Array.from(quantities.entityId);
|
|
171
198
|
const keep = effective ? entityId.map((id) => !effective.isDeleted(id)) : null;
|
|
172
199
|
return this.toParquet(filterColumns({
|
|
173
200
|
EntityId: entityId,
|
|
174
201
|
QsetName: mapTypedArray(quantities.qsetName, i => strings.get(i)),
|
|
175
202
|
QuantityName: mapTypedArray(quantities.quantityName, i => strings.get(i)),
|
|
176
|
-
QuantityType: mapTypedArray(quantities.quantityType, t =>
|
|
203
|
+
QuantityType: mapTypedArray(quantities.quantityType, t => quantityTypeToString(t)),
|
|
177
204
|
Value: Array.from(quantities.value),
|
|
178
205
|
Formula: mapTypedArray(quantities.formula, i => i > 0 ? strings.get(i) : null),
|
|
179
206
|
}, keep), new Set(['Value']));
|
|
180
207
|
}
|
|
181
208
|
async writeRelationships() {
|
|
182
209
|
const { relationships } = this.store;
|
|
183
|
-
const edges = relationships.forward;
|
|
184
210
|
const effective = this.getEffective();
|
|
185
|
-
//
|
|
211
|
+
// One row per `IfcRel*` STEP record, including any collapsed into
|
|
212
|
+
// an edge's `shadowedRelationshipIds` (#3760/#3782) — not one row
|
|
213
|
+
// per deduped edge; see `flattenRelationshipEdges`'s doc comment.
|
|
186
214
|
const sourceIds = [];
|
|
187
215
|
const targetIds = [];
|
|
188
216
|
const relTypes = [];
|
|
189
217
|
const relIds = [];
|
|
190
|
-
for (const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
218
|
+
for (const row of flattenRelationshipEdges(relationships.forward)) {
|
|
219
|
+
// An edge naming a tombstoned entity on either end no longer
|
|
220
|
+
// has a live entity to relate — drop the row rather than
|
|
221
|
+
// leave a dangling SourceId/TargetId in the export.
|
|
222
|
+
//
|
|
223
|
+
// `relationshipId` gets the same treatment: an `IfcRel*` record
|
|
224
|
+
// is itself a row in Entities.parquet (the columnar parser
|
|
225
|
+
// indexes it like any other line), so emitting a RelId for a
|
|
226
|
+
// deleted one leaves a dangling reference too. Because each
|
|
227
|
+
// shadowed id is its own row here, this drops exactly the
|
|
228
|
+
// deleted record and keeps a live sibling — the same rule
|
|
229
|
+
// `edgeSurvives` applies to the CLI/MCP `related()` path
|
|
230
|
+
// (#3782 review).
|
|
231
|
+
if (effective && (effective.isDeleted(row.sourceId) || effective.isDeleted(row.targetId) || effective.isDeleted(row.relationshipId)))
|
|
232
|
+
continue;
|
|
233
|
+
sourceIds.push(row.sourceId);
|
|
234
|
+
targetIds.push(row.targetId);
|
|
235
|
+
relTypes.push(RelationshipTypeToString(row.type));
|
|
236
|
+
relIds.push(row.relationshipId);
|
|
204
237
|
}
|
|
205
238
|
return this.toParquet({
|
|
206
239
|
SourceId: sourceIds,
|
|
@@ -437,40 +470,8 @@ export class ParquetExporter {
|
|
|
437
470
|
// ═══════════════════════════════════════════════════════════════
|
|
438
471
|
// UTILITIES
|
|
439
472
|
// ═══════════════════════════════════════════════════════════════
|
|
440
|
-
/**
|
|
441
|
-
* Column names whose domain is an unsigned 32-bit integer.
|
|
442
|
-
*
|
|
443
|
-
* Every one carries an IFC EXPRESS ID or an index into a geometry buffer,
|
|
444
|
-
* and both are `u32` everywhere else in this codebase - `Uint32Array` in
|
|
445
|
-
* the parser's entity index and its transports, `u32` in the Rust crates.
|
|
446
|
-
* Arrow's content inference reaches for Int32 on any whole number, so an
|
|
447
|
-
* express id at or above 2_147_483_648 came out NEGATIVE: an id-shaped
|
|
448
|
-
* number that joins to nothing. STEP puts no upper bound on an entity id
|
|
449
|
-
* below the `u32` the readers use, so that is reachable input rather than a
|
|
450
|
-
* hypothetical.
|
|
451
|
-
*/
|
|
452
|
-
static UINT32_COLUMNS = new Set([
|
|
453
|
-
'ExpressId', 'EntityId', 'SourceId', 'TargetId', 'RelId',
|
|
454
|
-
'ElementId', 'StoreyId',
|
|
455
|
-
'Index0', 'Index1', 'Index2',
|
|
456
|
-
'VertexStart', 'VertexCount', 'IndexStart', 'IndexCount',
|
|
457
|
-
]);
|
|
458
|
-
/**
|
|
459
|
-
* NOT in the set above, deliberately: `BuildingId`, `SiteId` and `SpaceId`
|
|
460
|
-
* in `SpatialHierarchy.parquet` carry **-1 as "none"** (see
|
|
461
|
-
* `writeSpatialHierarchy` - a storey directly under the project has no
|
|
462
|
-
* building). Declaring those unsigned turned that sentinel into
|
|
463
|
-
* 4294967295: an id-shaped number where an obviously-absent marker used to
|
|
464
|
-
* be, which is the exact failure this class of change exists to prevent.
|
|
465
|
-
*
|
|
466
|
-
* The residual gap is the narrower one: a building or site id at or above
|
|
467
|
-
* 2^31 still wraps negative in those three columns. Fixing that properly
|
|
468
|
-
* means writing NULL rather than -1 for "none", which changes what every
|
|
469
|
-
* consumer reads for an absent parent and is a separate decision from the
|
|
470
|
-
* id width.
|
|
471
|
-
*/
|
|
472
473
|
async toParquet(columns, floatColumns) {
|
|
473
|
-
return columnsToParquet(columns, floatColumns,
|
|
474
|
+
return columnsToParquet(columns, floatColumns, PARQUET_UINT32_COLUMNS);
|
|
474
475
|
}
|
|
475
476
|
async createZipArchive(files) {
|
|
476
477
|
const JSZip = (await import('jszip')).default;
|
|
@@ -503,35 +504,12 @@ function filterColumns(columns, keep) {
|
|
|
503
504
|
}
|
|
504
505
|
return out;
|
|
505
506
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
[PropertyValueType.Logical]: 'Logical',
|
|
513
|
-
[PropertyValueType.Label]: 'Label',
|
|
514
|
-
[PropertyValueType.Identifier]: 'Identifier',
|
|
515
|
-
[PropertyValueType.Text]: 'Text',
|
|
516
|
-
[PropertyValueType.Enum]: 'Enum',
|
|
517
|
-
[PropertyValueType.Reference]: 'Reference',
|
|
518
|
-
[PropertyValueType.List]: 'List',
|
|
519
|
-
};
|
|
520
|
-
return names[type] || 'Unknown';
|
|
521
|
-
}
|
|
522
|
-
// Quantity type conversion - exported for future use when quantities are implemented
|
|
523
|
-
export function QuantityTypeToString(type) {
|
|
524
|
-
const names = {
|
|
525
|
-
[QuantityType.Length]: 'Length',
|
|
526
|
-
[QuantityType.Area]: 'Area',
|
|
527
|
-
[QuantityType.Volume]: 'Volume',
|
|
528
|
-
[QuantityType.Count]: 'Count',
|
|
529
|
-
[QuantityType.Weight]: 'Weight',
|
|
530
|
-
[QuantityType.Time]: 'Time',
|
|
531
|
-
[QuantityType.Number]: 'Number',
|
|
532
|
-
};
|
|
533
|
-
return names[type] || 'Unknown';
|
|
534
|
-
}
|
|
507
|
+
// Kept exported under its original name here (moved to `parquet-type-strings.ts`
|
|
508
|
+
// so the on-demand writers can share it without importing this file, which
|
|
509
|
+
// imports them): nothing in-repo imports it from this path — index.ts never
|
|
510
|
+
// re-exported it — but it was public `export function` surface before the
|
|
511
|
+
// module split, so a re-export costs nothing to keep.
|
|
512
|
+
export { quantityTypeToString as QuantityTypeToString } from './parquet-type-strings.js';
|
|
535
513
|
function RelationshipTypeToString(type) {
|
|
536
514
|
const names = {
|
|
537
515
|
[RelationshipType.ContainsElements]: 'IfcRelContainedInSpatialStructure',
|