@bitbybit-dev/ifc 0.0.0-stage → 1.5.0-rc.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/LICENSE +21 -0
- package/NOTICE +42 -0
- package/README.md +92 -2
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/lib/api/dto-registry.d.ts +8 -0
- package/lib/api/dto-registry.js +109 -0
- package/lib/api/ifc-service.d.ts +109 -0
- package/lib/api/ifc-service.js +55 -0
- package/lib/api/inputs/base-inputs.d.ts +5 -0
- package/lib/api/inputs/base-inputs.js +5 -0
- package/lib/api/inputs/ifc-inputs.d.ts +2727 -0
- package/lib/api/inputs/ifc-inputs.js +2498 -0
- package/lib/api/inputs/index.d.ts +3 -0
- package/lib/api/inputs/index.js +3 -0
- package/lib/api/resolved-inputs/index.d.ts +70 -0
- package/lib/api/resolved-inputs/index.js +1 -0
- package/lib/api/services/assemblies.d.ts +28 -0
- package/lib/api/services/assemblies.js +49 -0
- package/lib/api/services/beams.d.ts +33 -0
- package/lib/api/services/beams.js +59 -0
- package/lib/api/services/columns.d.ts +26 -0
- package/lib/api/services/columns.js +56 -0
- package/lib/api/services/defaults.constants.d.ts +44 -0
- package/lib/api/services/defaults.constants.js +44 -0
- package/lib/api/services/doors.d.ts +45 -0
- package/lib/api/services/doors.js +94 -0
- package/lib/api/services/geometry.d.ts +51 -0
- package/lib/api/services/geometry.js +87 -0
- package/lib/api/services/index.d.ts +17 -0
- package/lib/api/services/index.js +17 -0
- package/lib/api/services/materials.d.ts +46 -0
- package/lib/api/services/materials.js +86 -0
- package/lib/api/services/member-support.d.ts +7 -0
- package/lib/api/services/member-support.js +29 -0
- package/lib/api/services/members.d.ts +24 -0
- package/lib/api/services/members.js +51 -0
- package/lib/api/services/model.d.ts +244 -0
- package/lib/api/services/model.js +409 -0
- package/lib/api/services/openings.d.ts +57 -0
- package/lib/api/services/openings.js +102 -0
- package/lib/api/services/properties.d.ts +89 -0
- package/lib/api/services/properties.js +142 -0
- package/lib/api/services/quantities.d.ts +38 -0
- package/lib/api/services/quantities.js +51 -0
- package/lib/api/services/roofs.d.ts +24 -0
- package/lib/api/services/roofs.js +52 -0
- package/lib/api/services/service-support.d.ts +8 -0
- package/lib/api/services/service-support.js +37 -0
- package/lib/api/services/service-types.d.ts +3 -0
- package/lib/api/services/service-types.js +1 -0
- package/lib/api/services/site.d.ts +37 -0
- package/lib/api/services/site.js +80 -0
- package/lib/api/services/slabs.d.ts +31 -0
- package/lib/api/services/slabs.js +59 -0
- package/lib/api/services/spaces.d.ts +36 -0
- package/lib/api/services/spaces.js +64 -0
- package/lib/api/services/spatial.d.ts +54 -0
- package/lib/api/services/spatial.js +75 -0
- package/lib/api/services/walls.d.ts +140 -0
- package/lib/api/services/walls.js +268 -0
- package/lib/api/services/windows.d.ts +46 -0
- package/lib/api/services/windows.js +76 -0
- package/lib/build/assemblies.d.ts +4 -0
- package/lib/build/assemblies.js +45 -0
- package/lib/build/build-types.d.ts +345 -0
- package/lib/build/build-types.js +1 -0
- package/lib/build/checks.d.ts +6 -0
- package/lib/build/checks.js +32 -0
- package/lib/build/constants.d.ts +27 -0
- package/lib/build/constants.js +43 -0
- package/lib/build/contexts.d.ts +9 -0
- package/lib/build/contexts.js +52 -0
- package/lib/build/curves.d.ts +3 -0
- package/lib/build/curves.js +23 -0
- package/lib/build/entity-writer.d.ts +23 -0
- package/lib/build/entity-writer.js +97 -0
- package/lib/build/fillings.d.ts +7 -0
- package/lib/build/fillings.js +182 -0
- package/lib/build/georeference.d.ts +5 -0
- package/lib/build/georeference.js +44 -0
- package/lib/build/materials.d.ts +18 -0
- package/lib/build/materials.js +166 -0
- package/lib/build/math.d.ts +10 -0
- package/lib/build/math.js +47 -0
- package/lib/build/measure.d.ts +9 -0
- package/lib/build/measure.js +195 -0
- package/lib/build/moving.d.ts +4 -0
- package/lib/build/moving.js +61 -0
- package/lib/build/openings.d.ts +12 -0
- package/lib/build/openings.js +156 -0
- package/lib/build/outlines.d.ts +4 -0
- package/lib/build/outlines.js +40 -0
- package/lib/build/placement.d.ts +16 -0
- package/lib/build/placement.js +106 -0
- package/lib/build/profiles.d.ts +4 -0
- package/lib/build/profiles.js +35 -0
- package/lib/build/project.d.ts +11 -0
- package/lib/build/project.js +134 -0
- package/lib/build/properties.d.ts +10 -0
- package/lib/build/properties.js +182 -0
- package/lib/build/quantities.d.ts +7 -0
- package/lib/build/quantities.js +262 -0
- package/lib/build/relationships.d.ts +9 -0
- package/lib/build/relationships.js +67 -0
- package/lib/build/removal.d.ts +3 -0
- package/lib/build/removal.js +106 -0
- package/lib/build/representations.d.ts +6 -0
- package/lib/build/representations.js +46 -0
- package/lib/build/roof-clipping.d.ts +5 -0
- package/lib/build/roof-clipping.js +79 -0
- package/lib/build/roofs.d.ts +4 -0
- package/lib/build/roofs.js +131 -0
- package/lib/build/site.d.ts +7 -0
- package/lib/build/site.js +112 -0
- package/lib/build/slab-openings.d.ts +4 -0
- package/lib/build/slab-openings.js +71 -0
- package/lib/build/slabs-columns-beams.d.ts +8 -0
- package/lib/build/slabs-columns-beams.js +73 -0
- package/lib/build/solids.d.ts +5 -0
- package/lib/build/solids.js +33 -0
- package/lib/build/spaces.d.ts +6 -0
- package/lib/build/spaces.js +55 -0
- package/lib/build/spatial.d.ts +17 -0
- package/lib/build/spatial.js +111 -0
- package/lib/build/type-objects.d.ts +4 -0
- package/lib/build/type-objects.js +9 -0
- package/lib/build/wall-edits.d.ts +5 -0
- package/lib/build/wall-edits.js +138 -0
- package/lib/build/wall-geometry.d.ts +15 -0
- package/lib/build/wall-geometry.js +159 -0
- package/lib/build/wall-parameters.d.ts +3 -0
- package/lib/build/wall-parameters.js +47 -0
- package/lib/build/walls.d.ts +12 -0
- package/lib/build/walls.js +238 -0
- package/lib/geometry/advanced.d.ts +27 -0
- package/lib/geometry/advanced.js +292 -0
- package/lib/geometry/colours.d.ts +3 -0
- package/lib/geometry/colours.js +109 -0
- package/lib/geometry/curves.d.ts +32 -0
- package/lib/geometry/curves.js +370 -0
- package/lib/geometry/edges.constants.d.ts +5 -0
- package/lib/geometry/edges.constants.js +5 -0
- package/lib/geometry/edges.d.ts +21 -0
- package/lib/geometry/edges.js +164 -0
- package/lib/geometry/elements.d.ts +3 -0
- package/lib/geometry/elements.js +152 -0
- package/lib/geometry/errors.d.ts +3 -0
- package/lib/geometry/errors.js +6 -0
- package/lib/geometry/faceted.d.ts +31 -0
- package/lib/geometry/faceted.js +214 -0
- package/lib/geometry/geometry-types.d.ts +103 -0
- package/lib/geometry/geometry-types.js +1 -0
- package/lib/geometry/groups.d.ts +21 -0
- package/lib/geometry/groups.js +106 -0
- package/lib/geometry/items.d.ts +34 -0
- package/lib/geometry/items.js +313 -0
- package/lib/geometry/operators.d.ts +3 -0
- package/lib/geometry/operators.js +65 -0
- package/lib/geometry/profiles.d.ts +4 -0
- package/lib/geometry/profiles.js +277 -0
- package/lib/geometry/recipe-builder.d.ts +14 -0
- package/lib/geometry/recipe-builder.js +48 -0
- package/lib/geometry/references.d.ts +4 -0
- package/lib/geometry/references.js +17 -0
- package/lib/geometry/regions.d.ts +4 -0
- package/lib/geometry/regions.js +26 -0
- package/lib/geometry/surfaces.constants.d.ts +17 -0
- package/lib/geometry/surfaces.constants.js +17 -0
- package/lib/geometry/surfaces.d.ts +6 -0
- package/lib/geometry/surfaces.js +306 -0
- package/lib/geometry/swept-solids.constants.d.ts +7 -0
- package/lib/geometry/swept-solids.constants.js +6 -0
- package/lib/geometry/swept-solids.d.ts +25 -0
- package/lib/geometry/swept-solids.js +243 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +6 -0
- package/lib/model/constants.d.ts +30 -0
- package/lib/model/constants.js +30 -0
- package/lib/model/entity-lookup.d.ts +9 -0
- package/lib/model/entity-lookup.js +20 -0
- package/lib/model/guid.d.ts +7 -0
- package/lib/model/guid.js +60 -0
- package/lib/model/id-map.d.ts +10 -0
- package/lib/model/id-map.js +80 -0
- package/lib/model/index.d.ts +4 -0
- package/lib/model/index.js +2 -0
- package/lib/model/io.d.ts +6 -0
- package/lib/model/io.js +136 -0
- package/lib/model/is-model.d.ts +9 -0
- package/lib/model/is-model.js +11 -0
- package/lib/model/model-types.d.ts +111 -0
- package/lib/model/model-types.js +1 -0
- package/lib/model/overlay-indexes.d.ts +17 -0
- package/lib/model/overlay-indexes.js +69 -0
- package/lib/model/references.d.ts +2 -0
- package/lib/model/references.js +51 -0
- package/lib/model/rows-by-id.d.ts +8 -0
- package/lib/model/rows-by-id.js +39 -0
- package/lib/model/snapshot.d.ts +36 -0
- package/lib/model/snapshot.js +144 -0
- package/lib/model/source.d.ts +29 -0
- package/lib/model/source.js +172 -0
- package/lib/model/transaction.d.ts +39 -0
- package/lib/model/transaction.js +250 -0
- package/lib/model/upgrade.d.ts +2 -0
- package/lib/model/upgrade.js +78 -0
- package/lib/schema/constants.d.ts +3 -0
- package/lib/schema/constants.js +3 -0
- package/lib/schema/generated/ifc4.d.ts +2 -0
- package/lib/schema/generated/ifc4.js +1204 -0
- package/lib/schema/generated/ifc4x3.d.ts +2 -0
- package/lib/schema/generated/ifc4x3.js +1325 -0
- package/lib/schema/registry.d.ts +7 -0
- package/lib/schema/registry.js +37 -0
- package/lib/schema/schema-types.d.ts +45 -0
- package/lib/schema/schema-types.js +1 -0
- package/lib/schema/schema.d.ts +23 -0
- package/lib/schema/schema.js +136 -0
- package/lib/step/constants.d.ts +48 -0
- package/lib/step/constants.js +48 -0
- package/lib/step/encoder.d.ts +21 -0
- package/lib/step/encoder.js +321 -0
- package/lib/step/errors.d.ts +39 -0
- package/lib/step/errors.js +45 -0
- package/lib/step/file-index.d.ts +2 -0
- package/lib/step/file-index.js +221 -0
- package/lib/step/header.d.ts +4 -0
- package/lib/step/header.js +52 -0
- package/lib/step/numbers.d.ts +3 -0
- package/lib/step/numbers.js +73 -0
- package/lib/step/reader.d.ts +2 -0
- package/lib/step/reader.js +160 -0
- package/lib/step/scanner.d.ts +13 -0
- package/lib/step/scanner.js +203 -0
- package/lib/step/step-types.d.ts +170 -0
- package/lib/step/step-types.js +1 -0
- package/lib/step/strings.d.ts +2 -0
- package/lib/step/strings.js +150 -0
- package/lib/step/values.d.ts +13 -0
- package/lib/step/values.js +51 -0
- package/package.json +51 -5
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { ROOT_ENTITY } from "../schema/constants";
|
|
2
|
+
import { StepSyntaxError } from "../step/errors";
|
|
3
|
+
import { decodeArguments } from "../step/reader";
|
|
4
|
+
import { firstStringIn, plainText, referenceCapacity, referenceList, referencesInto, textOf } from "../step/scanner";
|
|
5
|
+
import { decodeString } from "../step/strings";
|
|
6
|
+
import { RowsById } from "./rows-by-id";
|
|
7
|
+
const NO_IDS = [];
|
|
8
|
+
const UNSEEN = -1;
|
|
9
|
+
export class IfcSource {
|
|
10
|
+
constructor(index, schema) {
|
|
11
|
+
this.decoded = new Map();
|
|
12
|
+
this.index = index;
|
|
13
|
+
this.schema = schema;
|
|
14
|
+
this.rows = new RowsById(index);
|
|
15
|
+
this.maxId = this.rows.maxId;
|
|
16
|
+
this.typeOfCode = index.typeTable.map((name) => schema.entityName(name));
|
|
17
|
+
this.spell = (typeName) => { var _a; return (_a = schema.typeName(typeName)) !== null && _a !== void 0 ? _a : typeName; };
|
|
18
|
+
}
|
|
19
|
+
has(id) {
|
|
20
|
+
return this.rows.rowOf(id) >= 0;
|
|
21
|
+
}
|
|
22
|
+
typeOf(id) {
|
|
23
|
+
const row = this.rows.rowOf(id);
|
|
24
|
+
return row < 0 ? undefined : this.typeOfRow(row);
|
|
25
|
+
}
|
|
26
|
+
rawTypeOf(id) {
|
|
27
|
+
const row = this.rows.rowOf(id);
|
|
28
|
+
return row < 0 ? undefined : this.index.typeTable[this.index.typeCodes[row]] || "complex instance";
|
|
29
|
+
}
|
|
30
|
+
get(id) {
|
|
31
|
+
const known = this.decoded.get(id);
|
|
32
|
+
if (known) {
|
|
33
|
+
return known;
|
|
34
|
+
}
|
|
35
|
+
const entity = this.decode(id);
|
|
36
|
+
if (entity) {
|
|
37
|
+
this.decoded.set(id, entity);
|
|
38
|
+
}
|
|
39
|
+
return entity;
|
|
40
|
+
}
|
|
41
|
+
peek(id) {
|
|
42
|
+
var _a;
|
|
43
|
+
return (_a = this.decoded.get(id)) !== null && _a !== void 0 ? _a : this.decode(id);
|
|
44
|
+
}
|
|
45
|
+
referenceList(id) {
|
|
46
|
+
const row = this.rows.rowOf(id);
|
|
47
|
+
return row < 0 ? undefined : referenceList(this.index.bytes, this.index.argStart[row], this.index.argEnd[row]);
|
|
48
|
+
}
|
|
49
|
+
rawArguments(id) {
|
|
50
|
+
const row = this.rows.rowOf(id);
|
|
51
|
+
return row < 0 ? undefined : decodeArguments(this.index.bytes, this.index.argStart[row], this.index.argEnd[row], this.spell);
|
|
52
|
+
}
|
|
53
|
+
decode(id) {
|
|
54
|
+
const row = this.rows.rowOf(id);
|
|
55
|
+
if (row < 0) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const type = this.typeOfRow(row);
|
|
59
|
+
if (type === undefined) {
|
|
60
|
+
throw new StepSyntaxError(`Entity #${id} is a ${this.rawTypeOf(id)}, which ${this.schema.name} does not define`, this.index.rowStart[row]);
|
|
61
|
+
}
|
|
62
|
+
return { id, type, args: decodeArguments(this.index.bytes, this.index.argStart[row], this.index.argEnd[row], this.spell) };
|
|
63
|
+
}
|
|
64
|
+
idsOfType(type) {
|
|
65
|
+
var _a;
|
|
66
|
+
if (!this.typeIds) {
|
|
67
|
+
const lists = this.typeOfCode.map(() => []);
|
|
68
|
+
for (let row = 0; row < this.index.count; row++) {
|
|
69
|
+
lists[this.index.typeCodes[row]].push(this.index.ids[row]);
|
|
70
|
+
}
|
|
71
|
+
const index = new Map();
|
|
72
|
+
this.typeOfCode.forEach((name, code) => {
|
|
73
|
+
var _a;
|
|
74
|
+
if (name !== undefined && lists[code].length) {
|
|
75
|
+
index.set(name, [...((_a = index.get(name)) !== null && _a !== void 0 ? _a : []), ...lists[code]]);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
this.typeIds = index;
|
|
79
|
+
}
|
|
80
|
+
return (_a = this.typeIds.get(type)) !== null && _a !== void 0 ? _a : NO_IDS;
|
|
81
|
+
}
|
|
82
|
+
ownersOfGlobalId(globalId) {
|
|
83
|
+
var _a, _b;
|
|
84
|
+
(_a = this.globalIdOwners) !== null && _a !== void 0 ? _a : (this.globalIdOwners = this.indexGlobalIds());
|
|
85
|
+
const first = this.globalIdOwners.first.get(globalId);
|
|
86
|
+
return first === undefined ? NO_IDS : [first, ...((_b = this.globalIdOwners.more.get(globalId)) !== null && _b !== void 0 ? _b : NO_IDS)];
|
|
87
|
+
}
|
|
88
|
+
usersOf(id) {
|
|
89
|
+
const row = this.rows.rowOf(id);
|
|
90
|
+
if (row < 0) {
|
|
91
|
+
return NO_IDS;
|
|
92
|
+
}
|
|
93
|
+
if (!this.users) {
|
|
94
|
+
this.users = this.indexUsers();
|
|
95
|
+
}
|
|
96
|
+
return Array.from(this.users.users.subarray(this.users.offsets[row], this.users.offsets[row + 1]));
|
|
97
|
+
}
|
|
98
|
+
indexGlobalIds() {
|
|
99
|
+
var _a;
|
|
100
|
+
const first = new Map();
|
|
101
|
+
const more = new Map();
|
|
102
|
+
const { bytes, argStart, argEnd, ids, count, typeCodes } = this.index;
|
|
103
|
+
const rooted = this.typeOfCode.map((type) => type !== undefined && this.schema.isSubtypeOf(type, ROOT_ENTITY));
|
|
104
|
+
for (let row = 0; row < count; row++) {
|
|
105
|
+
const span = rooted[typeCodes[row]] ? firstStringIn(bytes, argStart[row], argEnd[row]) : undefined;
|
|
106
|
+
if (!span) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const globalId = (_a = plainText(bytes, span[0], span[1])) !== null && _a !== void 0 ? _a : decodeString(textOf(bytes, span[0], span[1]), span[0]);
|
|
110
|
+
if (!first.has(globalId)) {
|
|
111
|
+
first.set(globalId, ids[row]);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const list = more.get(globalId);
|
|
115
|
+
if (list) {
|
|
116
|
+
list.push(ids[row]);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
more.set(globalId, [ids[row]]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { first, more };
|
|
123
|
+
}
|
|
124
|
+
typeOfRow(row) {
|
|
125
|
+
return this.typeOfCode[this.index.typeCodes[row]];
|
|
126
|
+
}
|
|
127
|
+
indexUsers() {
|
|
128
|
+
const { ids, count } = this.index;
|
|
129
|
+
const offsets = new Int32Array(count + 1);
|
|
130
|
+
const seen = new Int32Array(count).fill(UNSEEN);
|
|
131
|
+
let scratch = new Int32Array(0);
|
|
132
|
+
const targetRows = (row, stamp) => {
|
|
133
|
+
const start = this.index.argStart[row];
|
|
134
|
+
const end = this.index.argEnd[row];
|
|
135
|
+
const capacity = referenceCapacity(this.index.bytes, start, end);
|
|
136
|
+
if (capacity === 0) {
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
if (scratch.length < capacity) {
|
|
140
|
+
scratch = new Int32Array(Math.max(capacity, 2 * scratch.length));
|
|
141
|
+
}
|
|
142
|
+
const found = referencesInto(this.index.bytes, start, end, scratch);
|
|
143
|
+
let kept = 0;
|
|
144
|
+
for (let at = 0; at < found; at++) {
|
|
145
|
+
const target = this.rows.rowOf(scratch[at]);
|
|
146
|
+
if (target >= 0 && seen[target] !== stamp) {
|
|
147
|
+
seen[target] = stamp;
|
|
148
|
+
scratch[kept++] = target;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return kept;
|
|
152
|
+
};
|
|
153
|
+
for (let row = 0; row < count; row++) {
|
|
154
|
+
const kept = targetRows(row, row);
|
|
155
|
+
for (let at = 0; at < kept; at++) {
|
|
156
|
+
offsets[scratch[at] + 1]++;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (let row = 0; row < count; row++) {
|
|
160
|
+
offsets[row + 1] = offsets[row + 1] + offsets[row];
|
|
161
|
+
}
|
|
162
|
+
const users = new Int32Array(offsets[count]);
|
|
163
|
+
const filled = offsets.slice(0, count);
|
|
164
|
+
for (let row = 0; row < count; row++) {
|
|
165
|
+
const kept = targetRows(row, row + count);
|
|
166
|
+
for (let at = 0; at < kept; at++) {
|
|
167
|
+
users[filled[scratch[at]]++] = ids[row];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { offsets, users };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IfcEntity, IfcValue } from "../step/step-types";
|
|
2
|
+
import type { IfcSchema } from "../schema/schema";
|
|
3
|
+
import { EntityLookup } from "./entity-lookup";
|
|
4
|
+
import type { IfcAttributes } from "./model-types";
|
|
5
|
+
import { ModelSnapshot } from "./snapshot";
|
|
6
|
+
export declare class IfcTransaction extends EntityLookup {
|
|
7
|
+
readonly base: ModelSnapshot;
|
|
8
|
+
private readonly changes;
|
|
9
|
+
private readonly changedUsers;
|
|
10
|
+
private readonly keySeed;
|
|
11
|
+
private readonly usedGlobalIds;
|
|
12
|
+
private readonly dropCandidates;
|
|
13
|
+
private nextId;
|
|
14
|
+
private issued;
|
|
15
|
+
private committed;
|
|
16
|
+
constructor(base: ModelSnapshot, keySeed?: string);
|
|
17
|
+
get schema(): IfcSchema;
|
|
18
|
+
get(id: number): IfcEntity | undefined;
|
|
19
|
+
typeOf(id: number): string | undefined;
|
|
20
|
+
byType(type: string): IfcEntity[];
|
|
21
|
+
referencesTo(id: number): readonly number[];
|
|
22
|
+
argsFrom(type: string, attributes: IfcAttributes): IfcValue[];
|
|
23
|
+
create(type: string, attributes?: IfcAttributes): number;
|
|
24
|
+
update(id: number, attributes: IfcAttributes): void;
|
|
25
|
+
delete(id: number): void;
|
|
26
|
+
globalId(key: string | undefined): string;
|
|
27
|
+
givenGlobalId(globalId: string): string;
|
|
28
|
+
structureGlobalId(name: string): string;
|
|
29
|
+
dropIfUnused(ids: readonly number[]): void;
|
|
30
|
+
commit(): ModelSnapshot;
|
|
31
|
+
private record;
|
|
32
|
+
private automaticGlobalId;
|
|
33
|
+
private claim;
|
|
34
|
+
private assertOpen;
|
|
35
|
+
private checkGlobalIds;
|
|
36
|
+
private pruneUnused;
|
|
37
|
+
private onlyPresents;
|
|
38
|
+
private letGo;
|
|
39
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { MAX_EXPRESS_ID } from "../step/constants";
|
|
2
|
+
import { encoderFor } from "../step/encoder";
|
|
3
|
+
import { DERIVED, isDerived, isList, isReference, referencesIn } from "../step/values";
|
|
4
|
+
import { PRUNABLE_TYPES } from "./constants";
|
|
5
|
+
import { EntityLookup } from "./entity-lookup";
|
|
6
|
+
import { automaticGlobalId, keyGlobalId, randomGlobalId, structureGlobalId } from "./guid";
|
|
7
|
+
import { checkReferences } from "./references";
|
|
8
|
+
import { ModelSnapshot } from "./snapshot";
|
|
9
|
+
const ascending = (a, b) => a - b;
|
|
10
|
+
const STYLED_ITEM = "IfcStyledItem";
|
|
11
|
+
const LAYER_ASSIGNMENT = "IfcPresentationLayerAssignment";
|
|
12
|
+
export class IfcTransaction extends EntityLookup {
|
|
13
|
+
constructor(base, keySeed = base.keySeed) {
|
|
14
|
+
super();
|
|
15
|
+
this.changes = new Map();
|
|
16
|
+
this.changedUsers = new Map();
|
|
17
|
+
this.usedGlobalIds = new Set();
|
|
18
|
+
this.dropCandidates = new Set();
|
|
19
|
+
this.issued = 0;
|
|
20
|
+
this.committed = false;
|
|
21
|
+
if (!base.editable) {
|
|
22
|
+
throw new Error(base.schemaName === base.schema.name
|
|
23
|
+
? `This ${base.schemaName} model can be shown and queried but not changed; changes are made to IFC4 models`
|
|
24
|
+
: `This ${base.schemaName} file is read through the ${base.schema.name} schema, so it can be shown and queried but not changed`);
|
|
25
|
+
}
|
|
26
|
+
this.base = base;
|
|
27
|
+
this.nextId = base.nextId;
|
|
28
|
+
this.keySeed = keySeed;
|
|
29
|
+
}
|
|
30
|
+
get schema() {
|
|
31
|
+
return this.base.schema;
|
|
32
|
+
}
|
|
33
|
+
get(id) {
|
|
34
|
+
var _a;
|
|
35
|
+
if (this.changes.has(id)) {
|
|
36
|
+
return (_a = this.changes.get(id)) !== null && _a !== void 0 ? _a : undefined;
|
|
37
|
+
}
|
|
38
|
+
return this.base.get(id);
|
|
39
|
+
}
|
|
40
|
+
typeOf(id) {
|
|
41
|
+
var _a;
|
|
42
|
+
if (this.changes.has(id)) {
|
|
43
|
+
return (_a = this.changes.get(id)) === null || _a === void 0 ? void 0 : _a.type;
|
|
44
|
+
}
|
|
45
|
+
return this.base.typeOf(id);
|
|
46
|
+
}
|
|
47
|
+
byType(type) {
|
|
48
|
+
const found = [];
|
|
49
|
+
for (const entity of this.base.byType(type)) {
|
|
50
|
+
if (!this.changes.has(entity.id)) {
|
|
51
|
+
found.push(entity);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const entity of this.changes.values()) {
|
|
55
|
+
if (entity && this.schema.isSubtypeOf(entity.type, type)) {
|
|
56
|
+
found.push(entity);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return found.sort((a, b) => a.id - b.id);
|
|
60
|
+
}
|
|
61
|
+
referencesTo(id) {
|
|
62
|
+
var _a;
|
|
63
|
+
const users = this.base.referencesTo(id).filter((user) => !this.changes.has(user));
|
|
64
|
+
users.push(...((_a = this.changedUsers.get(id)) !== null && _a !== void 0 ? _a : []));
|
|
65
|
+
return users.sort(ascending);
|
|
66
|
+
}
|
|
67
|
+
argsFrom(type, attributes) {
|
|
68
|
+
const info = this.schema.entity(type);
|
|
69
|
+
const unknown = Object.keys(attributes).filter((name) => !info.positions.has(name));
|
|
70
|
+
if (unknown.length) {
|
|
71
|
+
throw new Error(`${info.name} has no attribute ${unknown.join(", ")}`);
|
|
72
|
+
}
|
|
73
|
+
const misplaced = info.attributes.find((attribute) => attribute.derived && attributes[attribute.name] !== undefined && !isDerived(attributes[attribute.name]));
|
|
74
|
+
if (misplaced) {
|
|
75
|
+
throw new Error(`${info.name}.${misplaced.name} is derived and takes no value`);
|
|
76
|
+
}
|
|
77
|
+
return info.attributes.map((attribute) => { var _a; return (attribute.derived ? DERIVED : (_a = attributes[attribute.name]) !== null && _a !== void 0 ? _a : null); });
|
|
78
|
+
}
|
|
79
|
+
create(type, attributes = {}) {
|
|
80
|
+
this.assertOpen();
|
|
81
|
+
const info = this.schema.entity(type);
|
|
82
|
+
const id = this.nextId;
|
|
83
|
+
if (id > MAX_EXPRESS_ID) {
|
|
84
|
+
throw new Error(`The model has no express id left below ${MAX_EXPRESS_ID}`);
|
|
85
|
+
}
|
|
86
|
+
const args = encoderFor(this.schema).normalizeArguments(info.name, this.argsFrom(info.name, attributes), `#${id}`);
|
|
87
|
+
this.nextId++;
|
|
88
|
+
this.record(id, { id, type: info.name, args });
|
|
89
|
+
return id;
|
|
90
|
+
}
|
|
91
|
+
update(id, attributes) {
|
|
92
|
+
this.assertOpen();
|
|
93
|
+
const entity = this.entity(id);
|
|
94
|
+
const info = this.schema.entity(entity.type);
|
|
95
|
+
const encoder = encoderFor(this.schema);
|
|
96
|
+
const args = [...entity.args];
|
|
97
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
98
|
+
const position = info.positions.get(name);
|
|
99
|
+
if (position === undefined) {
|
|
100
|
+
throw new Error(`${info.name} has no attribute ${name}`);
|
|
101
|
+
}
|
|
102
|
+
if (info.attributes[position].derived) {
|
|
103
|
+
throw new Error(`${info.name}.${name} is derived and takes no value`);
|
|
104
|
+
}
|
|
105
|
+
args[position] = encoder.normalizeAttribute(info, position, value !== null && value !== void 0 ? value : null, `#${id}`);
|
|
106
|
+
}
|
|
107
|
+
this.record(id, { id, type: info.name, args });
|
|
108
|
+
}
|
|
109
|
+
delete(id) {
|
|
110
|
+
this.assertOpen();
|
|
111
|
+
this.entity(id);
|
|
112
|
+
this.record(id, null);
|
|
113
|
+
}
|
|
114
|
+
globalId(key) {
|
|
115
|
+
const globalId = key === undefined ? this.automaticGlobalId() : keyGlobalId(this.keySeed, key);
|
|
116
|
+
this.claim(globalId, key === undefined ? `the GlobalId ${globalId}` : `the id '${key}'`);
|
|
117
|
+
return globalId;
|
|
118
|
+
}
|
|
119
|
+
givenGlobalId(globalId) {
|
|
120
|
+
this.claim(globalId, `the GlobalId ${globalId}`);
|
|
121
|
+
return globalId;
|
|
122
|
+
}
|
|
123
|
+
structureGlobalId(name) {
|
|
124
|
+
const globalId = structureGlobalId(this.keySeed, name);
|
|
125
|
+
this.claim(globalId, `the GlobalId ${globalId}`);
|
|
126
|
+
return globalId;
|
|
127
|
+
}
|
|
128
|
+
dropIfUnused(ids) {
|
|
129
|
+
ids.forEach((id) => this.dropCandidates.add(id));
|
|
130
|
+
}
|
|
131
|
+
commit() {
|
|
132
|
+
this.assertOpen();
|
|
133
|
+
this.committed = true;
|
|
134
|
+
this.pruneUnused();
|
|
135
|
+
const overlay = this.base.overlay.withChanges([...this.changes].map(([id, entity]) => { var _a; return [id, entity === null && !((_a = this.base.source) === null || _a === void 0 ? void 0 : _a.has(id)) ? undefined : entity]; }));
|
|
136
|
+
const next = new ModelSnapshot(Object.assign(Object.assign({}, this.base.parts), { overlay, nextId: this.nextId }));
|
|
137
|
+
const inherited = this.base.releaseIndexes();
|
|
138
|
+
if (inherited) {
|
|
139
|
+
for (const [id, entity] of this.changes) {
|
|
140
|
+
const before = this.base.overlay.get(id);
|
|
141
|
+
if (before) {
|
|
142
|
+
inherited.remove(before);
|
|
143
|
+
}
|
|
144
|
+
if (entity) {
|
|
145
|
+
inherited.add(entity);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
next.adoptIndexes(inherited);
|
|
149
|
+
}
|
|
150
|
+
const changed = [...this.changes.keys()].filter((id) => this.changes.get(id) !== null);
|
|
151
|
+
const deleted = [...this.changes.keys()].filter((id) => this.changes.get(id) === null);
|
|
152
|
+
checkReferences(next, changed, deleted);
|
|
153
|
+
this.checkGlobalIds(next, changed);
|
|
154
|
+
return next;
|
|
155
|
+
}
|
|
156
|
+
record(id, entity) {
|
|
157
|
+
var _a;
|
|
158
|
+
const before = this.changes.get(id);
|
|
159
|
+
for (const target of new Set(before ? before.args.flatMap((arg) => referencesIn(arg)) : [])) {
|
|
160
|
+
(_a = this.changedUsers.get(target)) === null || _a === void 0 ? void 0 : _a.delete(id);
|
|
161
|
+
}
|
|
162
|
+
for (const target of new Set(entity ? entity.args.flatMap((arg) => referencesIn(arg)) : [])) {
|
|
163
|
+
let users = this.changedUsers.get(target);
|
|
164
|
+
if (!users) {
|
|
165
|
+
users = new Set();
|
|
166
|
+
this.changedUsers.set(target, users);
|
|
167
|
+
}
|
|
168
|
+
users.add(id);
|
|
169
|
+
}
|
|
170
|
+
this.changes.set(id, entity);
|
|
171
|
+
}
|
|
172
|
+
automaticGlobalId() {
|
|
173
|
+
return this.base.seededIds ? automaticGlobalId(this.keySeed, `${this.base.nextId}.${this.issued++}`) : randomGlobalId();
|
|
174
|
+
}
|
|
175
|
+
claim(globalId, what) {
|
|
176
|
+
if (this.usedGlobalIds.has(globalId) || this.base.byGlobalId(globalId) !== undefined) {
|
|
177
|
+
throw new Error(`The model already holds an object with ${what}`);
|
|
178
|
+
}
|
|
179
|
+
this.usedGlobalIds.add(globalId);
|
|
180
|
+
}
|
|
181
|
+
assertOpen() {
|
|
182
|
+
if (this.committed) {
|
|
183
|
+
throw new Error("The transaction has been committed; start a new one from the model it made");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
checkGlobalIds(next, changed) {
|
|
187
|
+
const indexes = next.overlayIndexes();
|
|
188
|
+
for (const id of changed) {
|
|
189
|
+
const globalId = indexes.globalIdOf(next.entity(id));
|
|
190
|
+
const before = this.base.get(id);
|
|
191
|
+
if (globalId === undefined || (before && indexes.globalIdOf(before) === globalId)) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const owners = next.globalIdOwners(globalId);
|
|
195
|
+
if (owners.length > 1) {
|
|
196
|
+
throw new Error(`#${owners[0]} and #${owners[1]} have the same GlobalId ${globalId}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
pruneUnused() {
|
|
201
|
+
if (!this.dropCandidates.size) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const removed = new Set();
|
|
205
|
+
const work = [...this.dropCandidates];
|
|
206
|
+
while (work.length) {
|
|
207
|
+
const id = work.pop();
|
|
208
|
+
const entity = this.get(id);
|
|
209
|
+
if (!entity || removed.has(id) || !PRUNABLE_TYPES.some((type) => this.schema.isSubtypeOf(entity.type, type))) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const users = this.referencesTo(id).filter((user) => !removed.has(user));
|
|
213
|
+
if (!users.every((user) => this.onlyPresents(user, id, removed))) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
users.forEach((user) => this.letGo(user, id, removed));
|
|
217
|
+
removed.add(id);
|
|
218
|
+
this.record(id, null);
|
|
219
|
+
work.push(...entity.args.flatMap((arg) => referencesIn(arg)));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
onlyPresents(user, id, removed) {
|
|
223
|
+
const entity = this.get(user);
|
|
224
|
+
if (!entity) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
if (this.schema.isSubtypeOf(entity.type, STYLED_ITEM)) {
|
|
228
|
+
const item = this.attribute(user, "Item");
|
|
229
|
+
return isReference(item) && item.ref === id && this.referencesTo(user).every((other) => removed.has(other));
|
|
230
|
+
}
|
|
231
|
+
return this.schema.isSubtypeOf(entity.type, LAYER_ASSIGNMENT);
|
|
232
|
+
}
|
|
233
|
+
letGo(user, id, removed) {
|
|
234
|
+
const entity = this.get(user);
|
|
235
|
+
if (this.schema.isSubtypeOf(entity.type, STYLED_ITEM)) {
|
|
236
|
+
removed.add(user);
|
|
237
|
+
this.record(user, null);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const position = this.schema.entity(entity.type).positions.get("AssignedItems");
|
|
241
|
+
const assigned = entity.args[position];
|
|
242
|
+
const kept = (isList(assigned) ? assigned : []).filter((item) => !(isReference(item) && item.ref === id));
|
|
243
|
+
if (!kept.length) {
|
|
244
|
+
removed.add(user);
|
|
245
|
+
this.record(user, null);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
this.record(user, Object.assign(Object.assign({}, entity), { args: entity.args.map((arg, index) => (index === position ? kept : arg)) }));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { encoderFor } from "../step/encoder";
|
|
2
|
+
import { DERIVED, enumValue, isDerived, isList, isReference } from "../step/values";
|
|
3
|
+
import { ModelSnapshot } from "./snapshot";
|
|
4
|
+
const IFC4 = "IFC4";
|
|
5
|
+
const NOT_DEFINED = "NOTDEFINED";
|
|
6
|
+
const SET = "SET";
|
|
7
|
+
const SUCCESSORS = new Map([["IFCRELOCCUPIESSPACES", "IfcRelAssignsToActor"]]);
|
|
8
|
+
function notDefinedFor(schema, attribute) {
|
|
9
|
+
const type = typeof attribute.type === "string" ? schema.type(attribute.type) : undefined;
|
|
10
|
+
return type && "e" in type && type.e.includes(NOT_DEFINED) ? enumValue(NOT_DEFINED) : undefined;
|
|
11
|
+
}
|
|
12
|
+
function keyOf(value) {
|
|
13
|
+
return isReference(value) ? `#${value.ref}` : JSON.stringify(value);
|
|
14
|
+
}
|
|
15
|
+
function withoutRepeats(values) {
|
|
16
|
+
const seen = new Set();
|
|
17
|
+
const kept = values.filter((value) => {
|
|
18
|
+
const key = keyOf(value);
|
|
19
|
+
const fresh = !seen.has(key);
|
|
20
|
+
seen.add(key);
|
|
21
|
+
return fresh;
|
|
22
|
+
});
|
|
23
|
+
return kept.length === values.length ? values : kept;
|
|
24
|
+
}
|
|
25
|
+
function upgradedValue(schema, info, entity, index, value) {
|
|
26
|
+
const attribute = info.attributes[index];
|
|
27
|
+
if (attribute.derived) {
|
|
28
|
+
return isDerived(value) ? value : DERIVED;
|
|
29
|
+
}
|
|
30
|
+
if (value === null || isDerived(value)) {
|
|
31
|
+
if (attribute.optional) {
|
|
32
|
+
return value === null ? value : null;
|
|
33
|
+
}
|
|
34
|
+
const fallback = notDefinedFor(schema, attribute);
|
|
35
|
+
if (fallback === undefined) {
|
|
36
|
+
throw new Error(`#${entity.id} ${info.name}.${attribute.name} is required by IFC4 and the file leaves it out, so the file cannot be upgraded`);
|
|
37
|
+
}
|
|
38
|
+
return fallback;
|
|
39
|
+
}
|
|
40
|
+
return Array.isArray(attribute.type) && attribute.type[0] === SET && isList(value) ? withoutRepeats(value) : value;
|
|
41
|
+
}
|
|
42
|
+
function upgradedEntity(model, id) {
|
|
43
|
+
var _a, _b;
|
|
44
|
+
const known = model.typeOf(id);
|
|
45
|
+
if (known !== undefined) {
|
|
46
|
+
return model.peek(id);
|
|
47
|
+
}
|
|
48
|
+
const raw = (_b = (_a = model.source) === null || _a === void 0 ? void 0 : _a.rawTypeOf(id)) !== null && _b !== void 0 ? _b : "";
|
|
49
|
+
const successor = SUCCESSORS.get(raw.toUpperCase());
|
|
50
|
+
if (successor === undefined) {
|
|
51
|
+
throw new Error(`#${id} is an ${raw}, which IFC4 has no counterpart for, so the file cannot be upgraded`);
|
|
52
|
+
}
|
|
53
|
+
return { id, type: successor, args: model.source.rawArguments(id) };
|
|
54
|
+
}
|
|
55
|
+
export function upgradeToIfc4(model) {
|
|
56
|
+
if (model.editable && model.schemaName === IFC4) {
|
|
57
|
+
return model;
|
|
58
|
+
}
|
|
59
|
+
if (model.schema.name !== IFC4) {
|
|
60
|
+
throw new Error(`Only an IFC2X3 model is upgraded to IFC4, and this one is ${model.schemaName}`);
|
|
61
|
+
}
|
|
62
|
+
const encoder = encoderFor(model.schema);
|
|
63
|
+
const changes = [];
|
|
64
|
+
for (const id of model.ids()) {
|
|
65
|
+
const entity = upgradedEntity(model, id);
|
|
66
|
+
const info = model.schema.entity(entity.type);
|
|
67
|
+
if (entity.args.length > info.attributes.length) {
|
|
68
|
+
throw new Error(`#${id} gives ${entity.args.length} values where IFC4's ${info.name} takes ${info.attributes.length}, so the file cannot be upgraded`);
|
|
69
|
+
}
|
|
70
|
+
const args = info.attributes.map((_, index) => { var _a; return upgradedValue(model.schema, info, entity, index, (_a = entity.args[index]) !== null && _a !== void 0 ? _a : null); });
|
|
71
|
+
const normalized = encoder.normalizeArguments(info.name, args, `#${id}`);
|
|
72
|
+
const changed = entity.type !== model.typeOf(id) || entity.args.length !== args.length || args.some((value, index) => value !== entity.args[index]);
|
|
73
|
+
if (changed) {
|
|
74
|
+
changes.push([id, { id, type: info.name, args: normalized }]);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return new ModelSnapshot(Object.assign(Object.assign({}, model.parts), { header: Object.assign(Object.assign({}, model.header), { schemaIdentifiers: [IFC4] }), overlay: model.overlay.withChanges(changes) }));
|
|
78
|
+
}
|