@ifc-lite/viewer 1.28.0 → 1.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/.turbo/turbo-build.log +34 -41
  2. package/CHANGELOG.md +10 -0
  3. package/dist/assets/{basketViewActivator-BNRDNuUJ.js → basketViewActivator-Ce38DhXd.js} +7 -7
  4. package/dist/assets/{bcf-DCwCuP7n.js → bcf-Cv_O3JfD.js} +1 -1
  5. package/dist/assets/{deflate-DNGgs8Ur.js → deflate-HbyMq59o.js} +1 -1
  6. package/dist/assets/drawing-2d-DW98umlt.js +257 -0
  7. package/dist/assets/{exporters-B9v81gi9.js → exporters-BuD3XRzB.js} +463 -416
  8. package/dist/assets/geometry.worker-TH3fCCoY.js +1 -0
  9. package/dist/assets/{geotiff-D-YCLS4g.js → geotiff-B2HA8Bwm.js} +10 -10
  10. package/dist/assets/{ids-CCpq-5d3.js → ids-DYUFMd5f.js} +4 -4
  11. package/dist/assets/{ifc-lite_bg-DbgS5EUA.wasm → ifc-lite_bg-BEA5DLmg.wasm} +0 -0
  12. package/dist/assets/index-E9wB0zWt.css +1 -0
  13. package/dist/assets/{index-Bgb3_Pu_.js → index-n5O1QJMM.js} +36808 -39415
  14. package/dist/assets/{index.es-CWfqZyyr.js → index.es-BKVIpZgL.js} +8 -8
  15. package/dist/assets/{jpeg-DGOAeUqU.js → jpeg-C7hjKjPX.js} +1 -1
  16. package/dist/assets/{jspdf.es.min-XPLU2Wkq.js → jspdf.es.min-oWlFc42Y.js} +4 -4
  17. package/dist/assets/{lerc-1PMSCHwX.js → lerc-BfIOGhQz.js} +1 -1
  18. package/dist/assets/{lzw-C65U9lNM.js → lzw-B0jRuuW5.js} +1 -1
  19. package/dist/assets/{native-bridge-XxXos6yI.js → native-bridge-DpB-dtEn.js} +5 -2
  20. package/dist/assets/{packbits-BdMWXC3m.js → packbits-DVvBTC39.js} +1 -1
  21. package/dist/assets/{parser.worker-Ddwo3_06.js → parser.worker-BDsWQ6rc.js} +1 -1
  22. package/dist/assets/{pdf-CRwaZf3s.js → pdf-dVIqI5ac.js} +9 -9
  23. package/dist/assets/raw-C0ZJYGmN.js +1 -0
  24. package/dist/assets/{sandbox-0sDo3g3m.js → sandbox-qpJlrNN0.js} +8 -8
  25. package/dist/assets/{server-client-cTCJ-853.js → server-client-DVZ2huNS.js} +1 -1
  26. package/dist/assets/{webimage-BtakWX7W.js → webimage-B394g0Tw.js} +1 -1
  27. package/dist/assets/{xlsx-B1YOg2QB.js → xlsx-D-oHO76J.js} +7 -7
  28. package/dist/assets/{zstd-CmwsbxmM.js → zstd-Bf38MwV2.js} +1 -1
  29. package/dist/index.html +8 -8
  30. package/package.json +5 -5
  31. package/src/App.tsx +1 -3
  32. package/src/components/viewer/BCFPanel.tsx +1 -16
  33. package/src/components/viewer/ChatPanel.tsx +11 -46
  34. package/src/components/viewer/HierarchyPanel.tsx +2 -176
  35. package/src/components/viewer/IDSPanel.tsx +1 -26
  36. package/src/components/viewer/MainToolbar.tsx +75 -185
  37. package/src/components/viewer/MobileToolbar.tsx +1 -9
  38. package/src/components/viewer/PropertiesPanel.tsx +28 -126
  39. package/src/components/viewer/ScriptPanel.tsx +8 -34
  40. package/src/components/viewer/Section2DPanel.tsx +32 -1
  41. package/src/components/viewer/ViewerLayout.tsx +0 -2
  42. package/src/components/viewer/ViewportContainer.tsx +24 -42
  43. package/src/components/viewer/ViewportOverlays.tsx +1 -4
  44. package/src/components/viewer/useGeometryStreaming.ts +0 -2
  45. package/src/hooks/ingest/federationAlign.ts +7 -0
  46. package/src/hooks/useDrawingGeneration.ts +211 -13
  47. package/src/hooks/useIfcCache.ts +94 -41
  48. package/src/hooks/useIfcFederation.ts +2 -3
  49. package/src/hooks/useIfcLoader.ts +10 -1051
  50. package/src/services/cacheService.ts +9 -25
  51. package/src/services/desktop-export.ts +2 -59
  52. package/src/services/file-dialog.ts +8 -142
  53. package/src/store/constants.ts +23 -0
  54. package/src/store/index.ts +3 -5
  55. package/src/store/slices/drawing2DSlice.ts +8 -0
  56. package/src/store/slices/visibilitySlice.ts +22 -1
  57. package/src/store/types.ts +1 -71
  58. package/src/utils/ifcConfig.ts +0 -12
  59. package/vite.config.ts +6 -3
  60. package/DESKTOP_CONTRACT_VERSION +0 -1
  61. package/dist/assets/drawing-2d-D0dDf6Lh.js +0 -257
  62. package/dist/assets/event-B0kAzHa-.js +0 -1
  63. package/dist/assets/geometry.worker-Bpa3115V.js +0 -1
  64. package/dist/assets/index-BtbXFKsX.css +0 -1
  65. package/dist/assets/raw-CJgQdyuZ.js +0 -1
  66. package/dist/assets/tauri-core-stub-D8Fa-u43.js +0 -1
  67. package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +0 -1
  68. package/dist/assets/tauri-fs-stub-BdeRC7aK.js +0 -1
  69. package/src/components/viewer/DesktopEntitlementBanner.tsx +0 -74
  70. package/src/components/viewer/SettingsPage.tsx +0 -581
  71. package/src/lib/desktop/desktopEntitlementEvents.ts +0 -39
  72. package/src/lib/desktop-entitlement.ts +0 -43
  73. package/src/lib/desktop-product.ts +0 -130
  74. package/src/lib/platform.ts +0 -23
  75. package/src/services/desktop-cache.ts +0 -186
  76. package/src/services/desktop-harness.ts +0 -196
  77. package/src/services/desktop-logger.ts +0 -20
  78. package/src/services/desktop-native-metadata.ts +0 -230
  79. package/src/services/desktop-panel-actions.ts +0 -43
  80. package/src/services/desktop-preferences.ts +0 -44
  81. package/src/services/fs-cache.ts +0 -212
  82. package/src/services/tauri-core-stub.ts +0 -7
  83. package/src/services/tauri-dialog-stub.ts +0 -7
  84. package/src/services/tauri-fs-stub.ts +0 -7
  85. package/src/services/tauri-modules.d.ts +0 -50
  86. package/src/store/slices/desktopEntitlementSlice.ts +0 -86
  87. package/src/utils/desktopModelSnapshot.ts +0 -359
  88. package/src/utils/nativeSpatialDataStore.ts +0 -277
  89. package/src-tauri/Cargo.toml +0 -29
  90. package/src-tauri/build.rs +0 -7
  91. package/src-tauri/capabilities/default.json +0 -18
  92. package/src-tauri/icons/128x128.png +0 -0
  93. package/src-tauri/icons/128x128@2x.png +0 -0
  94. package/src-tauri/icons/32x32.png +0 -0
  95. package/src-tauri/icons/Square107x107Logo.png +0 -0
  96. package/src-tauri/icons/Square142x142Logo.png +0 -0
  97. package/src-tauri/icons/Square150x150Logo.png +0 -0
  98. package/src-tauri/icons/Square284x284Logo.png +0 -0
  99. package/src-tauri/icons/Square30x30Logo.png +0 -0
  100. package/src-tauri/icons/Square310x310Logo.png +0 -0
  101. package/src-tauri/icons/Square44x44Logo.png +0 -0
  102. package/src-tauri/icons/Square71x71Logo.png +0 -0
  103. package/src-tauri/icons/Square89x89Logo.png +0 -0
  104. package/src-tauri/icons/StoreLogo.png +0 -0
  105. package/src-tauri/icons/icon.icns +0 -0
  106. package/src-tauri/icons/icon.ico +0 -0
  107. package/src-tauri/icons/icon.png +0 -0
  108. package/src-tauri/src/lib.rs +0 -21
  109. package/src-tauri/src/main.rs +0 -10
  110. package/src-tauri/tauri.conf.json +0 -39
@@ -1,359 +0,0 @@
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
- import {
6
- IfcTypeEnum,
7
- IfcTypeEnumToString,
8
- PropertyTableBuilder,
9
- PropertyValueType,
10
- QuantityTableBuilder,
11
- type PropertyTable,
12
- type QuantityTable,
13
- type SpatialHierarchy,
14
- type SpatialNode,
15
- } from '@ifc-lite/data';
16
- import {
17
- BinaryCacheReader,
18
- BinaryCacheWriter,
19
- SchemaVersion as CacheSchemaVersion,
20
- type IfcDataStore as CacheDataStore,
21
- } from '@ifc-lite/cache';
22
- import { extractPropertiesOnDemand, extractQuantitiesOnDemand, type IfcDataStore } from '@ifc-lite/parser';
23
- import { rebuildOnDemandMaps } from './spatialHierarchy.js';
24
-
25
- interface SerializedSpatialNode {
26
- expressId: number;
27
- type: number;
28
- name: string;
29
- elevation?: number;
30
- children: SerializedSpatialNode[];
31
- elements: number[];
32
- }
33
-
34
- interface SerializedSpatialHierarchy {
35
- project: SerializedSpatialNode;
36
- byStorey: Array<[number, number[]]>;
37
- byBuilding: Array<[number, number[]]>;
38
- bySite: Array<[number, number[]]>;
39
- bySpace: Array<[number, number[]]>;
40
- storeyElevations: Array<[number, number]>;
41
- storeyHeights: Array<[number, number]>;
42
- elementToStorey: Array<[number, number]>;
43
- }
44
-
45
- interface SerializedDesktopMetadataSnapshot {
46
- version: number;
47
- schemaVersion: IfcDataStore['schemaVersion'];
48
- fileSize: number;
49
- entityCount: number;
50
- parseTime: number;
51
- spatialHierarchy: SerializedSpatialHierarchy | null;
52
- propertySetIds: number[];
53
- quantitySetIds: number[];
54
- }
55
-
56
- interface DecodedDesktopMetadataSnapshot {
57
- cacheBuffer: ArrayBuffer;
58
- metadata: SerializedDesktopMetadataSnapshot;
59
- }
60
-
61
- const SNAPSHOT_VERSION = 2;
62
-
63
- function toCacheSchemaVersion(schemaVersion: IfcDataStore['schemaVersion']): CacheSchemaVersion {
64
- switch (schemaVersion) {
65
- case 'IFC4':
66
- return CacheSchemaVersion.IFC4;
67
- case 'IFC4X3':
68
- return CacheSchemaVersion.IFC4X3;
69
- default:
70
- return CacheSchemaVersion.IFC2X3;
71
- }
72
- }
73
-
74
- function serializeSpatialNode(node: SpatialNode): SerializedSpatialNode {
75
- return {
76
- expressId: node.expressId,
77
- type: node.type,
78
- name: node.name,
79
- elevation: node.elevation,
80
- children: node.children.map(serializeSpatialNode),
81
- elements: [...node.elements],
82
- };
83
- }
84
-
85
- function deserializeSpatialNode(node: SerializedSpatialNode): SpatialNode {
86
- return {
87
- expressId: node.expressId,
88
- type: node.type as IfcTypeEnum,
89
- name: node.name,
90
- elevation: node.elevation,
91
- children: node.children.map(deserializeSpatialNode),
92
- elements: [...node.elements],
93
- };
94
- }
95
-
96
- function serializeSpatialHierarchy(spatialHierarchy: SpatialHierarchy | undefined): SerializedSpatialHierarchy | null {
97
- if (!spatialHierarchy) return null;
98
- return {
99
- project: serializeSpatialNode(spatialHierarchy.project),
100
- byStorey: [...spatialHierarchy.byStorey.entries()].map(([id, elements]) => [id, [...elements]]),
101
- byBuilding: [...spatialHierarchy.byBuilding.entries()].map(([id, elements]) => [id, [...elements]]),
102
- bySite: [...spatialHierarchy.bySite.entries()].map(([id, elements]) => [id, [...elements]]),
103
- bySpace: [...spatialHierarchy.bySpace.entries()].map(([id, elements]) => [id, [...elements]]),
104
- storeyElevations: [...spatialHierarchy.storeyElevations.entries()],
105
- storeyHeights: [...spatialHierarchy.storeyHeights.entries()],
106
- elementToStorey: [...spatialHierarchy.elementToStorey.entries()],
107
- };
108
- }
109
-
110
- function deserializeSpatialHierarchy(
111
- serialized: SerializedSpatialHierarchy | null | undefined,
112
- ): SpatialHierarchy | undefined {
113
- if (!serialized) return undefined;
114
-
115
- const project = deserializeSpatialNode(serialized.project);
116
- const byStorey = new Map<number, number[]>(serialized.byStorey.map(([id, elements]) => [id, [...elements]]));
117
- const byBuilding = new Map<number, number[]>(serialized.byBuilding.map(([id, elements]) => [id, [...elements]]));
118
- const bySite = new Map<number, number[]>(serialized.bySite.map(([id, elements]) => [id, [...elements]]));
119
- const bySpace = new Map<number, number[]>(serialized.bySpace.map(([id, elements]) => [id, [...elements]]));
120
- const storeyElevations = new Map<number, number>(serialized.storeyElevations);
121
- const storeyHeights = new Map<number, number>(serialized.storeyHeights);
122
- const elementToStorey = new Map<number, number>(serialized.elementToStorey);
123
- const elementToSpace = new Map<number, number>();
124
-
125
- for (const [spaceId, elementIds] of bySpace) {
126
- for (const elementId of elementIds) {
127
- elementToSpace.set(elementId, spaceId);
128
- }
129
- }
130
-
131
- return {
132
- project,
133
- byStorey,
134
- byBuilding,
135
- bySite,
136
- bySpace,
137
- storeyElevations,
138
- storeyHeights,
139
- elementToStorey,
140
- getStoreyElements(storeyId: number): number[] {
141
- return byStorey.get(storeyId) ?? [];
142
- },
143
- getStoreyByElevation(z: number): number | null {
144
- let bestStoreyId: number | null = null;
145
- let bestDistance = Number.POSITIVE_INFINITY;
146
- for (const [storeyId, elevation] of storeyElevations) {
147
- const distance = Math.abs(elevation - z);
148
- if (distance < bestDistance) {
149
- bestDistance = distance;
150
- bestStoreyId = storeyId;
151
- }
152
- }
153
- return bestStoreyId;
154
- },
155
- getContainingSpace(elementId: number): number | null {
156
- return elementToSpace.get(elementId) ?? null;
157
- },
158
- getPath(elementId: number): SpatialNode[] {
159
- const path: SpatialNode[] = [];
160
- const walk = (node: SpatialNode): boolean => {
161
- path.push(node);
162
- if (node.elements.includes(elementId)) {
163
- return true;
164
- }
165
- for (const child of node.children) {
166
- if (walk(child)) {
167
- return true;
168
- }
169
- }
170
- path.pop();
171
- return false;
172
- };
173
- walk(project);
174
- return path;
175
- },
176
- };
177
- }
178
-
179
- function encodeSnapshotEnvelope(cacheBuffer: ArrayBuffer, metadata: SerializedDesktopMetadataSnapshot): ArrayBuffer {
180
- const metadataBytes = new TextEncoder().encode(JSON.stringify(metadata));
181
- const cacheBytes = new Uint8Array(cacheBuffer);
182
- const out = new Uint8Array(4 + metadataBytes.length + cacheBytes.length);
183
- const view = new DataView(out.buffer);
184
- view.setUint32(0, metadataBytes.length, true);
185
- out.set(metadataBytes, 4);
186
- out.set(cacheBytes, 4 + metadataBytes.length);
187
- return out.buffer;
188
- }
189
-
190
- function collectSnapshotIndexIds(dataStore: IfcDataStore): { propertySetIds: number[]; quantitySetIds: number[] } {
191
- return {
192
- propertySetIds: [
193
- ...(dataStore.entityIndex.byType.get('IFCPROPERTYSET') ?? dataStore.entityIndex.byType.get('IfcPropertySet') ?? []),
194
- ],
195
- quantitySetIds: [
196
- ...(dataStore.entityIndex.byType.get('IFCELEMENTQUANTITY') ?? dataStore.entityIndex.byType.get('IfcElementQuantity') ?? []),
197
- ],
198
- };
199
- }
200
-
201
- function buildEntityIndexByType(
202
- dataStore: Pick<IfcDataStore, 'entities'>,
203
- propertySetIds: number[],
204
- quantitySetIds: number[],
205
- ): Map<string, number[]> {
206
- const byType = new Map<string, number[]>();
207
- for (let i = 0; i < dataStore.entities.count; i++) {
208
- const typeName = IfcTypeEnumToString(dataStore.entities.typeEnum[i]).toUpperCase();
209
- const expressId = dataStore.entities.expressId[i];
210
- let ids = byType.get(typeName);
211
- if (!ids) {
212
- ids = [];
213
- byType.set(typeName, ids);
214
- }
215
- ids.push(expressId);
216
- }
217
- if (propertySetIds.length > 0) {
218
- byType.set('IFCPROPERTYSET', [...propertySetIds]);
219
- }
220
- if (quantitySetIds.length > 0) {
221
- byType.set('IFCELEMENTQUANTITY', [...quantitySetIds]);
222
- }
223
- return byType;
224
- }
225
-
226
- function decodeSnapshotEnvelope(buffer: ArrayBuffer): DecodedDesktopMetadataSnapshot {
227
- const bytes = new Uint8Array(buffer);
228
- const view = new DataView(buffer);
229
- const metadataLength = view.getUint32(0, true);
230
- const metadataBytes = bytes.subarray(4, 4 + metadataLength);
231
- const cacheBytes = bytes.subarray(4 + metadataLength);
232
- const metadata = JSON.parse(new TextDecoder().decode(metadataBytes)) as SerializedDesktopMetadataSnapshot;
233
- return {
234
- cacheBuffer: cacheBytes.slice().buffer,
235
- metadata,
236
- };
237
- }
238
-
239
- function materializeSnapshotPropertyTable(dataStore: IfcDataStore): PropertyTable {
240
- if (dataStore.properties.count > 0 || !dataStore.source?.length || !dataStore.onDemandPropertyMap?.size) {
241
- return dataStore.properties;
242
- }
243
-
244
- const builder = new PropertyTableBuilder(dataStore.strings);
245
- for (const entityId of dataStore.onDemandPropertyMap.keys()) {
246
- const propertySets = extractPropertiesOnDemand(dataStore, entityId);
247
- for (const propertySet of propertySets) {
248
- for (const property of propertySet.properties) {
249
- builder.add({
250
- entityId,
251
- psetName: propertySet.name,
252
- psetGlobalId: propertySet.globalId ?? '',
253
- propName: property.name,
254
- propType: property.type as PropertyValueType,
255
- value: property.value,
256
- });
257
- }
258
- }
259
- }
260
- return builder.build();
261
- }
262
-
263
- function materializeSnapshotQuantityTable(dataStore: IfcDataStore): QuantityTable {
264
- if (dataStore.quantities.count > 0 || !dataStore.source?.length || !dataStore.onDemandQuantityMap?.size) {
265
- return dataStore.quantities;
266
- }
267
-
268
- const builder = new QuantityTableBuilder(dataStore.strings);
269
- for (const entityId of dataStore.onDemandQuantityMap.keys()) {
270
- const quantitySets = extractQuantitiesOnDemand(dataStore, entityId);
271
- for (const quantitySet of quantitySets) {
272
- for (const quantity of quantitySet.quantities) {
273
- builder.add({
274
- entityId,
275
- qsetName: quantitySet.name,
276
- quantityName: quantity.name,
277
- quantityType: quantity.type,
278
- value: quantity.value,
279
- });
280
- }
281
- }
282
- }
283
- return builder.build();
284
- }
285
-
286
- export async function buildDesktopMetadataSnapshot(
287
- dataStore: IfcDataStore,
288
- sourceBuffer: ArrayBuffer,
289
- ): Promise<ArrayBuffer> {
290
- const writer = new BinaryCacheWriter();
291
- const properties = materializeSnapshotPropertyTable(dataStore);
292
- const quantities = materializeSnapshotQuantityTable(dataStore);
293
- const cacheDataStore: CacheDataStore = {
294
- schema: toCacheSchemaVersion(dataStore.schemaVersion),
295
- entityCount: dataStore.entityCount || dataStore.entities?.count || 0,
296
- strings: dataStore.strings,
297
- entities: dataStore.entities,
298
- properties,
299
- quantities,
300
- relationships: dataStore.relationships,
301
- spatialHierarchy: dataStore.spatialHierarchy,
302
- };
303
-
304
- const cacheBuffer = await writer.write(cacheDataStore, undefined, sourceBuffer, {
305
- includeGeometry: false,
306
- includeSpatialHierarchy: false,
307
- });
308
- const { propertySetIds, quantitySetIds } = collectSnapshotIndexIds(dataStore);
309
-
310
- return encodeSnapshotEnvelope(cacheBuffer, {
311
- version: SNAPSHOT_VERSION,
312
- schemaVersion: dataStore.schemaVersion,
313
- fileSize: dataStore.fileSize,
314
- entityCount: dataStore.entityCount,
315
- parseTime: dataStore.parseTime,
316
- spatialHierarchy: serializeSpatialHierarchy(dataStore.spatialHierarchy),
317
- propertySetIds,
318
- quantitySetIds,
319
- });
320
- }
321
-
322
- export async function restoreDesktopMetadataSnapshot(
323
- snapshotBuffer: ArrayBuffer,
324
- ): Promise<IfcDataStore> {
325
- const { cacheBuffer, metadata } = decodeSnapshotEnvelope(snapshotBuffer);
326
- if (metadata.version !== SNAPSHOT_VERSION) {
327
- throw new Error(`unsupported-desktop-metadata-snapshot:${metadata.version}`);
328
- }
329
- const reader = new BinaryCacheReader();
330
- const result = await reader.read(cacheBuffer, { skipGeometry: true });
331
- const dataStore = result.dataStore as unknown as IfcDataStore;
332
- const byType = buildEntityIndexByType(
333
- dataStore,
334
- metadata.propertySetIds ?? [],
335
- metadata.quantitySetIds ?? [],
336
- );
337
-
338
- dataStore.fileSize = metadata.fileSize;
339
- dataStore.schemaVersion = metadata.schemaVersion;
340
- dataStore.entityCount = metadata.entityCount;
341
- dataStore.parseTime = metadata.parseTime;
342
- dataStore.source = new Uint8Array(0);
343
- dataStore.entityIndex = {
344
- byId: new Map() as IfcDataStore['entityIndex']['byId'],
345
- byType,
346
- };
347
- dataStore.spatialHierarchy = deserializeSpatialHierarchy(metadata.spatialHierarchy);
348
-
349
- const { onDemandPropertyMap, onDemandQuantityMap, onDemandMaterialMap } = rebuildOnDemandMaps(
350
- dataStore.entities,
351
- dataStore.relationships,
352
- dataStore.entityIndex,
353
- );
354
- dataStore.onDemandPropertyMap = onDemandPropertyMap;
355
- dataStore.onDemandQuantityMap = onDemandQuantityMap;
356
- dataStore.onDemandMaterialMap = onDemandMaterialMap;
357
-
358
- return dataStore;
359
- }
@@ -1,277 +0,0 @@
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
- import {
6
- IfcTypeEnumFromString,
7
- IfcTypeEnumToString,
8
- isBuildingLikeSpatialType,
9
- isStoreyLikeSpatialType,
10
- type SpatialHierarchy,
11
- type SpatialNode,
12
- } from '@ifc-lite/data';
13
- import type { IfcDataStore } from '@ifc-lite/parser';
14
- import type { NativeMetadataEntitySummary, NativeMetadataSnapshot, NativeMetadataSpatialNode } from '@/store/types';
15
-
16
- function normalizeSummary(summary: Partial<NativeMetadataEntitySummary> & { typeName?: string; summary?: Partial<NativeMetadataEntitySummary> }): NativeMetadataEntitySummary | null {
17
- const source = summary.summary ? { ...summary.summary, ...summary } : summary;
18
- const expressId = typeof source.expressId === 'number' ? source.expressId : null;
19
- const type = typeof source.type === 'string'
20
- ? source.type
21
- : typeof source.typeName === 'string'
22
- ? source.typeName
23
- : null;
24
- if (expressId === null || !type) {
25
- return null;
26
- }
27
- return {
28
- expressId,
29
- type,
30
- name: typeof source.name === 'string' ? source.name : `${type} #${expressId}`,
31
- globalId: typeof source.globalId === 'string' ? source.globalId : null,
32
- kind: source.kind === 'spatial' ? 'spatial' : 'element',
33
- hasChildren: Boolean(source.hasChildren),
34
- elementCount: typeof source.elementCount === 'number' ? source.elementCount : undefined,
35
- elevation: typeof source.elevation === 'number' ? source.elevation : undefined,
36
- };
37
- }
38
-
39
- function normalizeSpatialNode(
40
- node: NativeMetadataSpatialNode | (Partial<NativeMetadataSpatialNode> & { typeName?: string; summary?: Partial<NativeMetadataEntitySummary> }),
41
- ): (NativeMetadataSpatialNode & { elements: NativeMetadataEntitySummary[] }) | null {
42
- const summary = normalizeSummary(node as Partial<NativeMetadataEntitySummary> & { typeName?: string; summary?: Partial<NativeMetadataEntitySummary> });
43
- if (!summary) {
44
- return null;
45
- }
46
- const children = Array.isArray(node.children)
47
- ? node.children
48
- .map((child) => normalizeSpatialNode(child as Partial<NativeMetadataSpatialNode> & { typeName?: string; summary?: Partial<NativeMetadataEntitySummary> }))
49
- .filter((child): child is NativeMetadataSpatialNode & { elements: NativeMetadataEntitySummary[] } => child !== null)
50
- : [];
51
- const elements = Array.isArray((node as { elements?: unknown[] }).elements)
52
- ? ((node as { elements?: unknown[] }).elements ?? [])
53
- .map((entry) => normalizeSummary(entry as Partial<NativeMetadataEntitySummary> & { typeName?: string; summary?: Partial<NativeMetadataEntitySummary> }))
54
- .filter((entry): entry is NativeMetadataEntitySummary => entry !== null)
55
- : [];
56
- return {
57
- ...summary,
58
- children,
59
- elements,
60
- };
61
- }
62
-
63
- function buildEntityLookup() {
64
- const ids: number[] = [];
65
- const typeEnums: number[] = [];
66
- const flags: number[] = [];
67
- const nameById = new Map<number, string>();
68
- const typeNameById = new Map<number, string>();
69
- const byType = new Map<string, number[]>();
70
-
71
- const addSummary = (summary: NativeMetadataEntitySummary) => {
72
- if (!summary.type) {
73
- return;
74
- }
75
- if (nameById.has(summary.expressId)) {
76
- return;
77
- }
78
- ids.push(summary.expressId);
79
- typeEnums.push(IfcTypeEnumFromString(summary.type));
80
- flags.push(0);
81
- nameById.set(summary.expressId, summary.name);
82
- typeNameById.set(summary.expressId, summary.type);
83
- const typeKey = summary.type.toUpperCase();
84
- const existing = byType.get(typeKey);
85
- if (existing) {
86
- existing.push(summary.expressId);
87
- } else {
88
- byType.set(typeKey, [summary.expressId]);
89
- }
90
- };
91
-
92
- return {
93
- addSummary,
94
- buildEntities() {
95
- return {
96
- count: ids.length,
97
- expressId: Uint32Array.from(ids),
98
- typeEnum: Uint32Array.from(typeEnums),
99
- flags: Uint8Array.from(flags),
100
- getName(expressId: number) {
101
- return nameById.get(expressId);
102
- },
103
- getTypeName(expressId: number) {
104
- return typeNameById.get(expressId) ?? 'Unknown';
105
- },
106
- getByType(type: string | number) {
107
- const key = typeof type === 'string'
108
- ? type.toUpperCase()
109
- : IfcTypeEnumToString(type).toUpperCase();
110
- return byType.get(key) ?? [];
111
- },
112
- };
113
- },
114
- buildByType() {
115
- return byType;
116
- },
117
- };
118
- }
119
-
120
- function buildPathResolver(project: SpatialNode) {
121
- return (elementId: number): SpatialNode[] => {
122
- const path: SpatialNode[] = [];
123
- const visit = (node: SpatialNode): boolean => {
124
- path.push(node);
125
- if (node.elements.includes(elementId)) {
126
- return true;
127
- }
128
- for (const child of node.children) {
129
- if (visit(child)) {
130
- return true;
131
- }
132
- }
133
- path.pop();
134
- return false;
135
- };
136
- visit(project);
137
- return path;
138
- };
139
- }
140
-
141
- export function buildIfcDataStoreFromNativeMetadata(snapshot: NativeMetadataSnapshot): IfcDataStore | null {
142
- const root = snapshot.spatialTree ? normalizeSpatialNode(snapshot.spatialTree) : null;
143
- if (!root) {
144
- return null;
145
- }
146
-
147
- const byStorey = new Map<number, number[]>();
148
- const byBuilding = new Map<number, number[]>();
149
- const bySite = new Map<number, number[]>();
150
- const bySpace = new Map<number, number[]>();
151
- const storeyElevations = new Map<number, number>();
152
- const storeyHeights = new Map<number, number>();
153
- const elementToStorey = new Map<number, number>();
154
- const elementToSpace = new Map<number, number>();
155
- const entityLookup = buildEntityLookup();
156
-
157
- const buildNode = (
158
- node: NativeMetadataSpatialNode & { elements: NativeMetadataEntitySummary[] },
159
- currentStoreyId: number | null,
160
- currentSpaceId: number | null,
161
- ): SpatialNode => {
162
- entityLookup.addSummary(node);
163
- const typeEnum = IfcTypeEnumFromString(node.type);
164
- const nextStoreyId = isStoreyLikeSpatialType(typeEnum) ? node.expressId : currentStoreyId;
165
- const nextSpaceId = node.type === 'IfcSpace' ? node.expressId : currentSpaceId;
166
- const elements = node.elements.map((summary) => {
167
- entityLookup.addSummary(summary);
168
- if (nextStoreyId !== null) {
169
- // Direct storey containment wins — only set if absent. Mirrors the
170
- // descendant-walk path in `spatialHierarchy.ts` where direct
171
- // IfcRelContainedInSpatialStructure entries take precedence over
172
- // inherited aggregate-descendant assignments.
173
- if (!elementToStorey.has(summary.expressId)) {
174
- elementToStorey.set(summary.expressId, nextStoreyId);
175
- }
176
- // NOTE: aggregate descendants of an element (e.g. IfcBuildingElementPart
177
- // children of an IfcWall) are NOT represented locally in the native
178
- // metadata snapshot — `NativeMetadataSpatialNode.children` only contains
179
- // spatial sub-nodes and `node.elements` is a flat list of
180
- // directly-contained elements (no `children` field on
181
- // `NativeMetadataEntitySummary`). They are fetched lazily through
182
- // `getNativeMetadataChildren`. The aggregate-descendant-walk fix that
183
- // `spatialHierarchy.ts` performs via `relationships.getRelated` cannot
184
- // be replicated here without an additional native bootstrap payload
185
- // change (see issue #540 follow-up).
186
- }
187
- if (nextSpaceId !== null) {
188
- if (!elementToSpace.has(summary.expressId)) {
189
- elementToSpace.set(summary.expressId, nextSpaceId);
190
- }
191
- }
192
- return summary.expressId;
193
- });
194
-
195
- const childNodes = node.children.map((child) => buildNode(child, nextStoreyId, nextSpaceId));
196
- const spatialNode: SpatialNode = {
197
- expressId: node.expressId,
198
- type: typeEnum,
199
- name: node.name,
200
- elevation: node.elevation ?? undefined,
201
- children: childNodes,
202
- elements,
203
- };
204
-
205
- if (isStoreyLikeSpatialType(typeEnum)) {
206
- byStorey.set(node.expressId, elements);
207
- if (typeof node.elevation === 'number') {
208
- storeyElevations.set(node.expressId, node.elevation);
209
- }
210
- } else if (isBuildingLikeSpatialType(typeEnum)) {
211
- byBuilding.set(node.expressId, elements);
212
- } else if (node.type === 'IfcSite') {
213
- bySite.set(node.expressId, elements);
214
- } else if (node.type === 'IfcSpace') {
215
- bySpace.set(node.expressId, elements);
216
- }
217
-
218
- return spatialNode;
219
- };
220
-
221
- const project = buildNode(root, null, null);
222
- const getPath = buildPathResolver(project);
223
- const hierarchy: SpatialHierarchy = {
224
- project,
225
- byStorey,
226
- byBuilding,
227
- bySite,
228
- bySpace,
229
- storeyElevations,
230
- storeyHeights,
231
- elementToStorey,
232
- getStoreyElements(storeyId: number) {
233
- return byStorey.get(storeyId) ?? [];
234
- },
235
- getStoreyByElevation(z: number) {
236
- let closestStoreyId: number | null = null;
237
- let closestDistance = Number.POSITIVE_INFINITY;
238
- for (const [storeyId, elevation] of storeyElevations) {
239
- const distance = Math.abs(elevation - z);
240
- if (distance < closestDistance) {
241
- closestDistance = distance;
242
- closestStoreyId = storeyId;
243
- }
244
- }
245
- return closestStoreyId;
246
- },
247
- getContainingSpace(elementId: number) {
248
- return elementToSpace.get(elementId) ?? null;
249
- },
250
- getPath,
251
- };
252
-
253
- const entities = entityLookup.buildEntities();
254
- return {
255
- fileSize: 0,
256
- schemaVersion: snapshot.schemaVersion,
257
- entityCount: snapshot.entityCount,
258
- parseTime: 0,
259
- source: new Uint8Array(0),
260
- entityIndex: {
261
- byId: new Map(),
262
- byType: entityLookup.buildByType(),
263
- },
264
- strings: {} as IfcDataStore['strings'],
265
- entities: entities as unknown as IfcDataStore['entities'],
266
- properties: undefined as unknown as IfcDataStore['properties'],
267
- quantities: undefined as unknown as IfcDataStore['quantities'],
268
- relationships: undefined as unknown as IfcDataStore['relationships'],
269
- spatialHierarchy: hierarchy,
270
- // Native spatial snapshots are metadata-only (no source buffer / property
271
- // or quantity tables), so the IfcStoreBase accessors return empties.
272
- getEntity: () => null,
273
- getEntitiesByType: () => [],
274
- getProperties: () => [],
275
- getQuantities: () => [],
276
- } as IfcDataStore;
277
- }
@@ -1,29 +0,0 @@
1
- [package]
2
- name = "app"
3
- version = "0.1.0"
4
- description = "A Tauri App"
5
- authors = ["you"]
6
- license = ""
7
- repository = ""
8
- edition = "2021"
9
- rust-version = "1.77.2"
10
-
11
- # Exclude from parent workspace
12
- [workspace]
13
-
14
- # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
15
-
16
- [lib]
17
- name = "app_lib"
18
- crate-type = ["staticlib", "cdylib", "rlib"]
19
-
20
- [build-dependencies]
21
- tauri-build = { version = "2.5.3", features = [] }
22
-
23
- [dependencies]
24
- serde_json = "1.0"
25
- serde = { version = "1.0", features = ["derive"] }
26
- log = "0.4"
27
- tauri = { version = "2.9.5", features = [] }
28
- tauri-plugin-log = "2"
29
- tauri-plugin-fs = "2"
@@ -1,7 +0,0 @@
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
- fn main() {
6
- tauri_build::build()
7
- }
@@ -1,18 +0,0 @@
1
- {
2
- "$schema": "../gen/schemas/desktop-schema.json",
3
- "identifier": "default",
4
- "description": "enables the default permissions",
5
- "windows": [
6
- "main"
7
- ],
8
- "permissions": [
9
- "core:default",
10
- "fs:allow-read-file",
11
- "fs:allow-write-file",
12
- "fs:allow-read-dir",
13
- "fs:allow-create",
14
- "fs:allow-exists",
15
- "fs:allow-remove",
16
- "fs:scope-appdata"
17
- ]
18
- }
Binary file
Binary file
Binary file