@ifc-lite/viewer 1.17.1 → 1.17.3
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/.turbo/turbo-build.log +30 -28
- package/.turbo/turbo-typecheck.log +1 -41
- package/CHANGELOG.md +23 -0
- package/dist/assets/arrow-DJf2ErbF.js +20 -0
- package/dist/assets/basketViewActivator-aojwdomq.js +1 -0
- package/dist/assets/bcf-D5-QWGO9.js +281 -0
- package/dist/assets/{browser-BQdwnOUt.js → browser-CKs-FY1P.js} +1 -1
- package/dist/assets/drawing-2d-gWfpdfYe.js +257 -0
- package/dist/assets/epsg-index.generated-BjJrt_0S.js +1 -0
- package/dist/assets/exporters-C_6J153K.js +79896 -0
- package/dist/assets/geometry.worker-Nz9_YIqh.js +1 -0
- package/dist/assets/ids-B4jTqB1O.js +1 -0
- package/dist/assets/ifc-lite_bg-eSkBTizQ.wasm +0 -0
- package/dist/assets/index-jhBr1wbn.js +101666 -0
- package/dist/assets/index-pbE7itQS.css +1 -0
- package/dist/assets/lens-CSASnhAL.js +1 -0
- package/dist/assets/maplibre-gl-BpvwNKKy.js +811 -0
- package/dist/assets/{native-bridge-CN0ZMR2t.js → native-bridge-DSIyEYXG.js} +6 -4
- package/dist/assets/{arrow2-bb-jcVEo.js → parquet-CEXmQNRO.js} +2 -2
- package/dist/assets/sandbox-B79eavQ3.js +5933 -0
- package/dist/assets/server-client-D3bUPJJc.js +626 -0
- package/dist/assets/wasm-bridge-B0J07fZZ.js +1 -0
- package/dist/assets/zip-B-jFFAGa.js +12 -0
- package/dist/index.html +11 -2
- package/package.json +24 -19
- package/src/components/viewer/ExportChangesButton.tsx +18 -3
- package/src/components/viewer/ExportDialog.tsx +16 -3
- package/src/components/viewer/HierarchyPanel.tsx +6 -6
- package/src/components/viewer/PropertiesPanel.tsx +96 -60
- package/src/components/viewer/Section2DPanel.tsx +3 -2
- package/src/components/viewer/ViewportContainer.tsx +5 -4
- package/src/components/viewer/hierarchy/treeDataBuilder.ts +2 -1
- package/src/components/viewer/properties/EpsgLookupDialog.tsx +418 -0
- package/src/components/viewer/properties/GeoreferencingPanel.tsx +591 -0
- package/src/components/viewer/properties/LocationMap.tsx +289 -0
- package/src/components/viewer/properties/ModelMetadataPanel.tsx +3 -70
- package/src/hooks/bcfIdLookup.ts +13 -11
- package/src/hooks/ids/idsColorSystem.ts +3 -8
- package/src/hooks/useIDS.ts +31 -16
- package/src/hooks/useIfcFederation.ts +2 -2
- package/src/lib/geo/kmz-exporter.ts +112 -0
- package/src/lib/geo/reproject.ts +244 -0
- package/src/lib/lens/adapter.ts +3 -1
- package/src/main.tsx +1 -0
- package/src/sdk/adapters/export-adapter.ts +14 -1
- package/src/sdk/adapters/viewer-adapter.ts +5 -9
- package/src/sdk/adapters/visibility-adapter.ts +6 -9
- package/src/store/basketVisibleSet.ts +3 -4
- package/src/store/globalId.ts +79 -0
- package/src/store/index.ts +1 -0
- package/src/store/slices/mutationSlice.ts +178 -0
- package/src/store/slices/pinboardSlice.ts +4 -8
- package/vite.config.ts +17 -0
- package/dist/assets/Arrow.dom-DuPUrOxJ.js +0 -20
- package/dist/assets/arrow2_bg-BlXl-cSQ.js +0 -1
- package/dist/assets/basketViewActivator-DetjPnvt.js +0 -1
- package/dist/assets/geometry.worker-Bjm-ukng.js +0 -1
- package/dist/assets/ifc-lite_bg-DD0A7Yow.wasm +0 -0
- package/dist/assets/index-B3X21yXA.js +0 -229
- package/dist/assets/index-Ba4eoTe7.css +0 -1
- package/dist/assets/index-BybGZJTW.js +0 -189478
- package/dist/assets/module-6F3E5H7Y-tx0BadV3.js +0 -6
- package/dist/assets/wasm-bridge-D0bALkma.js +0 -1
|
@@ -11,6 +11,13 @@ import type { ViewerState } from '../index.js';
|
|
|
11
11
|
import type { MutablePropertyView } from '@ifc-lite/mutations';
|
|
12
12
|
import type { Mutation, ChangeSet, PropertyValue } from '@ifc-lite/mutations';
|
|
13
13
|
import { PropertyValueType, QuantityType } from '@ifc-lite/data';
|
|
14
|
+
import type { MapConversion, ProjectedCRS } from '@ifc-lite/parser';
|
|
15
|
+
|
|
16
|
+
/** Tracks georeferencing field mutations per model */
|
|
17
|
+
export interface GeorefMutationData {
|
|
18
|
+
projectedCRS?: Partial<ProjectedCRS>;
|
|
19
|
+
mapConversion?: Partial<MapConversion>;
|
|
20
|
+
}
|
|
14
21
|
|
|
15
22
|
export interface MutationSlice {
|
|
16
23
|
// State
|
|
@@ -28,6 +35,26 @@ export interface MutationSlice {
|
|
|
28
35
|
dirtyModels: Set<string>;
|
|
29
36
|
/** Version counter to trigger re-renders when mutations change */
|
|
30
37
|
mutationVersion: number;
|
|
38
|
+
/** Georeferencing mutations per model */
|
|
39
|
+
georefMutations: Map<string, GeorefMutationData>;
|
|
40
|
+
|
|
41
|
+
// Actions - Georeferencing Mutations
|
|
42
|
+
/** Set a georeferencing field value */
|
|
43
|
+
setGeorefField: (
|
|
44
|
+
modelId: string,
|
|
45
|
+
entity: 'projectedCRS' | 'mapConversion',
|
|
46
|
+
field: string,
|
|
47
|
+
value: string | number,
|
|
48
|
+
oldValue?: string | number
|
|
49
|
+
) => void;
|
|
50
|
+
/** Set multiple georeferencing field values atomically */
|
|
51
|
+
setGeorefFields: (
|
|
52
|
+
modelId: string,
|
|
53
|
+
entity: 'projectedCRS' | 'mapConversion',
|
|
54
|
+
fields: Array<{ field: string; value: string | number; oldValue?: string | number }>
|
|
55
|
+
) => void;
|
|
56
|
+
/** Get merged georef mutations for a model */
|
|
57
|
+
getGeorefMutations: (modelId: string) => GeorefMutationData | undefined;
|
|
31
58
|
|
|
32
59
|
// Actions - Mutation View Management
|
|
33
60
|
/** Get or create mutation view for a model */
|
|
@@ -154,6 +181,60 @@ export const createMutationSlice: StateCreator<
|
|
|
154
181
|
redoStacks: new Map(),
|
|
155
182
|
dirtyModels: new Set(),
|
|
156
183
|
mutationVersion: 0,
|
|
184
|
+
georefMutations: new Map(),
|
|
185
|
+
|
|
186
|
+
// Georeferencing Mutations
|
|
187
|
+
setGeorefField: (modelId, entity, field, value, oldValue) => {
|
|
188
|
+
get().setGeorefFields(modelId, entity, [{ field, value, oldValue }]);
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
setGeorefFields: (modelId, entity, fields) => {
|
|
192
|
+
if (fields.length === 0) return;
|
|
193
|
+
set((state) => {
|
|
194
|
+
const newGeorefMuts = new Map(state.georefMutations);
|
|
195
|
+
const modelMuts = { ...(newGeorefMuts.get(modelId) || {}) };
|
|
196
|
+
const entityMuts = { ...(modelMuts[entity] || {}) } as Record<string, unknown>;
|
|
197
|
+
for (const entry of fields) {
|
|
198
|
+
entityMuts[entry.field] = entry.value;
|
|
199
|
+
}
|
|
200
|
+
newGeorefMuts.set(modelId, { ...modelMuts, [entity]: entityMuts });
|
|
201
|
+
|
|
202
|
+
// Track undo
|
|
203
|
+
const newUndoStacks = new Map(state.undoStacks);
|
|
204
|
+
const stack = newUndoStacks.get(modelId) || [];
|
|
205
|
+
const nextMutations: Mutation[] = fields.map(entry => ({
|
|
206
|
+
id: `mut_georef_${entity}_${entry.field}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
207
|
+
type: 'UPDATE_ATTRIBUTE',
|
|
208
|
+
timestamp: Date.now(),
|
|
209
|
+
modelId,
|
|
210
|
+
entityId: 0, // georef entities don't map to a specific element
|
|
211
|
+
attributeName: `georef.${entity}.${entry.field}`,
|
|
212
|
+
oldValue: entry.oldValue,
|
|
213
|
+
newValue: entry.value,
|
|
214
|
+
propName: entry.field,
|
|
215
|
+
psetName: entity,
|
|
216
|
+
}));
|
|
217
|
+
newUndoStacks.set(modelId, [...stack, ...nextMutations]);
|
|
218
|
+
|
|
219
|
+
const newRedoStacks = new Map(state.redoStacks);
|
|
220
|
+
newRedoStacks.set(modelId, []);
|
|
221
|
+
|
|
222
|
+
const newDirty = new Set(state.dirtyModels);
|
|
223
|
+
newDirty.add(modelId);
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
georefMutations: newGeorefMuts,
|
|
227
|
+
undoStacks: newUndoStacks,
|
|
228
|
+
redoStacks: newRedoStacks,
|
|
229
|
+
dirtyModels: newDirty,
|
|
230
|
+
mutationVersion: state.mutationVersion + 1,
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
getGeorefMutations: (modelId) => {
|
|
236
|
+
return get().georefMutations.get(modelId);
|
|
237
|
+
},
|
|
157
238
|
|
|
158
239
|
// Mutation View Management
|
|
159
240
|
getMutationView: (modelId) => {
|
|
@@ -388,6 +469,48 @@ export const createMutationSlice: StateCreator<
|
|
|
388
469
|
if (undoStack.length === 0) return;
|
|
389
470
|
|
|
390
471
|
const mutation = undoStack[undoStack.length - 1];
|
|
472
|
+
|
|
473
|
+
// Handle georef mutations directly on georefMutations map
|
|
474
|
+
if (mutation.type === 'UPDATE_ATTRIBUTE' && mutation.attributeName?.startsWith('georef.')) {
|
|
475
|
+
const parts = mutation.attributeName.split('.');
|
|
476
|
+
const entity = parts[1] as 'projectedCRS' | 'mapConversion';
|
|
477
|
+
const field = parts[2];
|
|
478
|
+
set((s) => {
|
|
479
|
+
const newGeorefMuts = new Map(s.georefMutations);
|
|
480
|
+
const modelMuts = { ...(newGeorefMuts.get(modelId) || {}) };
|
|
481
|
+
const entityMuts = { ...(modelMuts[entity] || {}) } as Record<string, unknown>;
|
|
482
|
+
if (mutation.oldValue !== undefined && mutation.oldValue !== null) {
|
|
483
|
+
entityMuts[field] = mutation.oldValue;
|
|
484
|
+
} else {
|
|
485
|
+
delete entityMuts[field];
|
|
486
|
+
}
|
|
487
|
+
if (Object.keys(entityMuts).length === 0) {
|
|
488
|
+
delete modelMuts[entity];
|
|
489
|
+
} else {
|
|
490
|
+
modelMuts[entity] = entityMuts as typeof modelMuts[typeof entity];
|
|
491
|
+
}
|
|
492
|
+
if (Object.keys(modelMuts).length === 0) {
|
|
493
|
+
newGeorefMuts.delete(modelId);
|
|
494
|
+
} else {
|
|
495
|
+
newGeorefMuts.set(modelId, modelMuts);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const newUndoStacks = new Map(s.undoStacks);
|
|
499
|
+
newUndoStacks.set(modelId, undoStack.slice(0, -1));
|
|
500
|
+
const newRedoStacks = new Map(s.redoStacks);
|
|
501
|
+
const redoStack = newRedoStacks.get(modelId) || [];
|
|
502
|
+
newRedoStacks.set(modelId, [...redoStack, mutation]);
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
georefMutations: newGeorefMuts,
|
|
506
|
+
undoStacks: newUndoStacks,
|
|
507
|
+
redoStacks: newRedoStacks,
|
|
508
|
+
mutationVersion: s.mutationVersion + 1,
|
|
509
|
+
};
|
|
510
|
+
});
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
391
514
|
const view = state.mutationViews.get(modelId);
|
|
392
515
|
if (!view) return;
|
|
393
516
|
|
|
@@ -465,6 +588,48 @@ export const createMutationSlice: StateCreator<
|
|
|
465
588
|
if (redoStack.length === 0) return;
|
|
466
589
|
|
|
467
590
|
const mutation = redoStack[redoStack.length - 1];
|
|
591
|
+
|
|
592
|
+
// Handle georef mutations directly
|
|
593
|
+
if (mutation.type === 'UPDATE_ATTRIBUTE' && mutation.attributeName?.startsWith('georef.')) {
|
|
594
|
+
const parts = mutation.attributeName.split('.');
|
|
595
|
+
const entity = parts[1] as 'projectedCRS' | 'mapConversion';
|
|
596
|
+
const field = parts[2];
|
|
597
|
+
set((s) => {
|
|
598
|
+
const newGeorefMuts = new Map(s.georefMutations);
|
|
599
|
+
const modelMuts = { ...(newGeorefMuts.get(modelId) || {}) };
|
|
600
|
+
const entityMuts = { ...(modelMuts[entity] || {}) } as Record<string, unknown>;
|
|
601
|
+
if (mutation.newValue !== undefined && mutation.newValue !== null) {
|
|
602
|
+
entityMuts[field] = mutation.newValue;
|
|
603
|
+
} else {
|
|
604
|
+
delete entityMuts[field];
|
|
605
|
+
}
|
|
606
|
+
if (Object.keys(entityMuts).length === 0) {
|
|
607
|
+
delete modelMuts[entity];
|
|
608
|
+
} else {
|
|
609
|
+
modelMuts[entity] = entityMuts as typeof modelMuts[typeof entity];
|
|
610
|
+
}
|
|
611
|
+
if (Object.keys(modelMuts).length === 0) {
|
|
612
|
+
newGeorefMuts.delete(modelId);
|
|
613
|
+
} else {
|
|
614
|
+
newGeorefMuts.set(modelId, modelMuts);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const newRedoStacks = new Map(s.redoStacks);
|
|
618
|
+
newRedoStacks.set(modelId, redoStack.slice(0, -1));
|
|
619
|
+
const newUndoStacks = new Map(s.undoStacks);
|
|
620
|
+
const undoStack = newUndoStacks.get(modelId) || [];
|
|
621
|
+
newUndoStacks.set(modelId, [...undoStack, mutation]);
|
|
622
|
+
|
|
623
|
+
return {
|
|
624
|
+
georefMutations: newGeorefMuts,
|
|
625
|
+
undoStacks: newUndoStacks,
|
|
626
|
+
redoStacks: newRedoStacks,
|
|
627
|
+
mutationVersion: s.mutationVersion + 1,
|
|
628
|
+
};
|
|
629
|
+
});
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
468
633
|
const view = state.mutationViews.get(modelId);
|
|
469
634
|
if (!view) return;
|
|
470
635
|
|
|
@@ -606,6 +771,14 @@ export const createMutationSlice: StateCreator<
|
|
|
606
771
|
for (const view of get().mutationViews.values()) {
|
|
607
772
|
count += view.getModifiedEntityCount();
|
|
608
773
|
}
|
|
774
|
+
// Include models with georef-only edits
|
|
775
|
+
for (const [modelId, gm] of get().georefMutations) {
|
|
776
|
+
const hasGeoref = (gm.projectedCRS && Object.keys(gm.projectedCRS).length > 0)
|
|
777
|
+
|| (gm.mapConversion && Object.keys(gm.mapConversion).length > 0);
|
|
778
|
+
if (hasGeoref && !get().mutationViews.has(modelId)) {
|
|
779
|
+
count += 1; // count the model as having modifications
|
|
780
|
+
}
|
|
781
|
+
}
|
|
609
782
|
return count;
|
|
610
783
|
},
|
|
611
784
|
|
|
@@ -626,10 +799,14 @@ export const createMutationSlice: StateCreator<
|
|
|
626
799
|
const newDirty = new Set(state.dirtyModels);
|
|
627
800
|
newDirty.delete(modelId);
|
|
628
801
|
|
|
802
|
+
const newGeorefMuts = new Map(state.georefMutations);
|
|
803
|
+
newGeorefMuts.delete(modelId);
|
|
804
|
+
|
|
629
805
|
return {
|
|
630
806
|
undoStacks: newUndoStacks,
|
|
631
807
|
redoStacks: newRedoStacks,
|
|
632
808
|
dirtyModels: newDirty,
|
|
809
|
+
georefMutations: newGeorefMuts,
|
|
633
810
|
mutationVersion: state.mutationVersion + 1,
|
|
634
811
|
};
|
|
635
812
|
});
|
|
@@ -644,6 +821,7 @@ export const createMutationSlice: StateCreator<
|
|
|
644
821
|
undoStacks: new Map(),
|
|
645
822
|
redoStacks: new Map(),
|
|
646
823
|
dirtyModels: new Set(),
|
|
824
|
+
georefMutations: new Map(),
|
|
647
825
|
mutationVersion: state.mutationVersion + 1,
|
|
648
826
|
}));
|
|
649
827
|
},
|
|
@@ -20,6 +20,7 @@ import type { StateCreator } from 'zustand';
|
|
|
20
20
|
import type { Drawing2D } from '@ifc-lite/drawing-2d';
|
|
21
21
|
import type { CameraCallbacks, CameraViewpoint, EntityRef, SectionPlane } from '../types.js';
|
|
22
22
|
import { entityRefToString, stringToEntityRef } from '../types.js';
|
|
23
|
+
import { toGlobalIdForRef } from '../globalId.js';
|
|
23
24
|
|
|
24
25
|
export type BasketSource = 'selection' | 'visible' | 'hierarchy' | 'manual';
|
|
25
26
|
|
|
@@ -148,17 +149,14 @@ function basketToGlobalIds(
|
|
|
148
149
|
const globalIds = new Set<number>();
|
|
149
150
|
for (const str of basketEntities) {
|
|
150
151
|
const ref = stringToEntityRef(str);
|
|
151
|
-
|
|
152
|
-
const offset = model?.idOffset ?? 0;
|
|
153
|
-
globalIds.add(ref.expressId + offset);
|
|
152
|
+
globalIds.add(toGlobalIdForRef(models, ref));
|
|
154
153
|
}
|
|
155
154
|
return globalIds;
|
|
156
155
|
}
|
|
157
156
|
|
|
158
157
|
/** Compute a single EntityRef's global ID */
|
|
159
158
|
function refToGlobalId(ref: EntityRef, models: Map<string, { idOffset: number }>): number {
|
|
160
|
-
|
|
161
|
-
return ref.expressId + (model?.idOffset ?? 0);
|
|
159
|
+
return toGlobalIdForRef(models, ref);
|
|
162
160
|
}
|
|
163
161
|
|
|
164
162
|
function refsToEntityKeySet(refs: EntityRef[]): Set<string> {
|
|
@@ -195,9 +193,7 @@ function computeBasketVisibility(
|
|
|
195
193
|
}
|
|
196
194
|
const hiddenEntities = new Set<number>(currentHidden);
|
|
197
195
|
for (const ref of unhideRefs) {
|
|
198
|
-
|
|
199
|
-
const offset = model?.idOffset ?? 0;
|
|
200
|
-
hiddenEntities.delete(ref.expressId + offset);
|
|
196
|
+
hiddenEntities.delete(toGlobalIdForRef(models, ref));
|
|
201
197
|
}
|
|
202
198
|
return { isolatedEntities, hiddenEntities };
|
|
203
199
|
}
|
package/vite.config.ts
CHANGED
|
@@ -275,6 +275,23 @@ export default defineConfig({
|
|
|
275
275
|
build: {
|
|
276
276
|
target: 'esnext',
|
|
277
277
|
chunkSizeWarningLimit: 6000,
|
|
278
|
+
rollupOptions: {
|
|
279
|
+
output: {
|
|
280
|
+
manualChunks(id) {
|
|
281
|
+
if (id.includes('/packages/sandbox/')) return 'sandbox';
|
|
282
|
+
if (id.includes('/packages/export/')) return 'exporters';
|
|
283
|
+
if (id.includes('/packages/server-client/')) return 'server-client';
|
|
284
|
+
if (id.includes('/packages/bcf/')) return 'bcf';
|
|
285
|
+
if (id.includes('/packages/ids/')) return 'ids';
|
|
286
|
+
if (id.includes('/packages/lens/')) return 'lens';
|
|
287
|
+
if (id.includes('/packages/drawing-2d/')) return 'drawing-2d';
|
|
288
|
+
if (id.includes('/node_modules/jszip/')) return 'zip';
|
|
289
|
+
if (id.includes('/node_modules/apache-arrow/')) return 'arrow';
|
|
290
|
+
if (id.includes('/node_modules/parquet-wasm/')) return 'parquet';
|
|
291
|
+
return undefined;
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
},
|
|
278
295
|
},
|
|
279
296
|
optimizeDeps: {
|
|
280
297
|
exclude: [
|