@ifc-lite/viewer 1.6.0 → 1.7.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/CHANGELOG.md +78 -0
- package/dist/assets/{Arrow.dom-BjDQoB2M.js → Arrow.dom-BGPQieQQ.js} +1 -1
- package/dist/assets/ifc-lite_bg-DyIN_nBM.wasm +0 -0
- package/dist/assets/{index-YBtrHPu3.js → index-dgdgiQ9p.js} +40212 -30008
- package/dist/assets/index-yTqs8kgX.css +1 -0
- package/dist/assets/{native-bridge-CULtTDX3.js → native-bridge-DD0SNyQ5.js} +1 -1
- package/dist/assets/{wasm-bridge-CjL-lSak.js → wasm-bridge-D54YMO7X.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +18 -15
- package/src/components/viewer/BCFPanel.tsx +7 -789
- package/src/components/viewer/Drawing2DCanvas.tsx +1048 -0
- package/src/components/viewer/DrawingSettingsPanel.tsx +3 -3
- package/src/components/viewer/HierarchyPanel.tsx +110 -842
- package/src/components/viewer/IDSExportDialog.tsx +281 -0
- package/src/components/viewer/IDSPanel.tsx +126 -17
- package/src/components/viewer/KeyboardShortcutsDialog.tsx +9 -0
- package/src/components/viewer/LensPanel.tsx +603 -0
- package/src/components/viewer/MainToolbar.tsx +188 -21
- package/src/components/viewer/PropertiesPanel.tsx +171 -663
- package/src/components/viewer/PropertyEditor.tsx +866 -77
- package/src/components/viewer/Section2DPanel.tsx +76 -2648
- package/src/components/viewer/ToolOverlays.tsx +3 -1097
- package/src/components/viewer/ViewerLayout.tsx +132 -45
- package/src/components/viewer/Viewport.tsx +237 -1659
- package/src/components/viewer/ViewportContainer.tsx +11 -3
- package/src/components/viewer/bcf/BCFCreateTopicForm.tsx +134 -0
- package/src/components/viewer/bcf/BCFTopicDetail.tsx +388 -0
- package/src/components/viewer/bcf/BCFTopicList.tsx +239 -0
- package/src/components/viewer/bcf/bcfHelpers.tsx +109 -0
- package/src/components/viewer/hierarchy/HierarchyNode.tsx +328 -0
- package/src/components/viewer/hierarchy/treeDataBuilder.ts +464 -0
- package/src/components/viewer/hierarchy/types.ts +54 -0
- package/src/components/viewer/hierarchy/useHierarchyTree.ts +280 -0
- package/src/components/viewer/lists/ListBuilder.tsx +486 -0
- package/src/components/viewer/lists/ListPanel.tsx +540 -0
- package/src/components/viewer/lists/ListResultsTable.tsx +193 -0
- package/src/components/viewer/properties/ClassificationCard.tsx +70 -0
- package/src/components/viewer/properties/CoordinateDisplay.tsx +49 -0
- package/src/components/viewer/properties/DocumentCard.tsx +89 -0
- package/src/components/viewer/properties/MaterialCard.tsx +201 -0
- package/src/components/viewer/properties/ModelMetadataPanel.tsx +335 -0
- package/src/components/viewer/properties/PropertySetCard.tsx +132 -0
- package/src/components/viewer/properties/QuantitySetCard.tsx +79 -0
- package/src/components/viewer/properties/RelationshipsCard.tsx +100 -0
- package/src/components/viewer/properties/encodingUtils.ts +29 -0
- package/src/components/viewer/tools/MeasurePanel.tsx +218 -0
- package/src/components/viewer/tools/MeasurementVisuals.tsx +644 -0
- package/src/components/viewer/tools/SectionPanel.tsx +183 -0
- package/src/components/viewer/tools/SectionVisualization.tsx +78 -0
- package/src/components/viewer/tools/formatDistance.ts +18 -0
- package/src/components/viewer/tools/sectionConstants.ts +14 -0
- package/src/components/viewer/useAnimationLoop.ts +166 -0
- package/src/components/viewer/useGeometryStreaming.ts +398 -0
- package/src/components/viewer/useKeyboardControls.ts +221 -0
- package/src/components/viewer/useMouseControls.ts +1009 -0
- package/src/components/viewer/useRenderUpdates.ts +165 -0
- package/src/components/viewer/useTouchControls.ts +245 -0
- package/src/hooks/ids/idsColorSystem.ts +125 -0
- package/src/hooks/ids/idsDataAccessor.ts +237 -0
- package/src/hooks/ids/idsExportService.ts +444 -0
- package/src/hooks/useBCF.ts +7 -0
- package/src/hooks/useDrawingExport.ts +627 -0
- package/src/hooks/useDrawingGeneration.ts +627 -0
- package/src/hooks/useFloorplanView.ts +108 -0
- package/src/hooks/useIDS.ts +270 -463
- package/src/hooks/useIfc.ts +26 -1628
- package/src/hooks/useIfcFederation.ts +803 -0
- package/src/hooks/useIfcLoader.ts +508 -0
- package/src/hooks/useIfcServer.ts +465 -0
- package/src/hooks/useKeyboardShortcuts.ts +1 -1
- package/src/hooks/useLens.ts +129 -0
- package/src/hooks/useMeasure2D.ts +365 -0
- package/src/hooks/useViewControls.ts +218 -0
- package/src/lib/ifc4-pset-definitions.test.ts +161 -0
- package/src/lib/ifc4-pset-definitions.ts +621 -0
- package/src/lib/ifc4-qto-definitions.ts +315 -0
- package/src/lib/lens/adapter.ts +138 -0
- package/src/lib/lens/index.ts +5 -0
- package/src/lib/lists/adapter.ts +69 -0
- package/src/lib/lists/index.ts +28 -0
- package/src/lib/lists/persistence.ts +64 -0
- package/src/services/fs-cache.ts +1 -1
- package/src/services/tauri-modules.d.ts +25 -0
- package/src/store/index.ts +38 -2
- package/src/store/slices/cameraSlice.ts +14 -1
- package/src/store/slices/dataSlice.ts +14 -1
- package/src/store/slices/lensSlice.ts +184 -0
- package/src/store/slices/listSlice.ts +74 -0
- package/src/store/slices/pinboardSlice.ts +114 -0
- package/src/store/types.ts +5 -0
- package/src/utils/ifcConfig.ts +16 -3
- package/src/utils/serverDataModel.ts +64 -101
- package/src/vite-env.d.ts +3 -0
- package/dist/assets/ifc-lite_bg-C6kblxf9.wasm +0 -0
- package/dist/assets/index-v3mcCUPN.css +0 -1
|
@@ -0,0 +1,315 @@
|
|
|
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
|
+
/**
|
|
6
|
+
* IFC4 Quantity Set Definitions (Qto_)
|
|
7
|
+
*
|
|
8
|
+
* Maps IFC entity types to their standard base quantities
|
|
9
|
+
* according to the IFC4 standard (ISO 16739-1:2018).
|
|
10
|
+
*
|
|
11
|
+
* Used by the quantity editor to validate and suggest additions.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { QuantityType } from '@ifc-lite/data';
|
|
15
|
+
|
|
16
|
+
export interface QtoQuantityDef {
|
|
17
|
+
name: string;
|
|
18
|
+
type: QuantityType;
|
|
19
|
+
description: string;
|
|
20
|
+
unit: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface QtoDefinition {
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
applicableTypes: string[];
|
|
27
|
+
quantities: QtoQuantityDef[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// IFC4 Standard Quantity Set Definitions
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
const QTO_DEFINITIONS: QtoDefinition[] = [
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Wall Base Quantities
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
{
|
|
39
|
+
name: 'Qto_WallBaseQuantities',
|
|
40
|
+
description: 'Base quantities for walls',
|
|
41
|
+
applicableTypes: ['IfcWall', 'IfcWallStandardCase', 'IfcCurtainWall'],
|
|
42
|
+
quantities: [
|
|
43
|
+
{ name: 'Length', type: QuantityType.Length, description: 'Length along the wall path', unit: 'm' },
|
|
44
|
+
{ name: 'Width', type: QuantityType.Length, description: 'Wall thickness', unit: 'm' },
|
|
45
|
+
{ name: 'Height', type: QuantityType.Length, description: 'Wall height', unit: 'm' },
|
|
46
|
+
{ name: 'GrossFootprintArea', type: QuantityType.Area, description: 'Gross footprint area including openings', unit: 'm²' },
|
|
47
|
+
{ name: 'NetFootprintArea', type: QuantityType.Area, description: 'Net footprint area excluding openings', unit: 'm²' },
|
|
48
|
+
{ name: 'GrossSideArea', type: QuantityType.Area, description: 'Gross side area including openings', unit: 'm²' },
|
|
49
|
+
{ name: 'NetSideArea', type: QuantityType.Area, description: 'Net side area excluding openings', unit: 'm²' },
|
|
50
|
+
{ name: 'GrossVolume', type: QuantityType.Volume, description: 'Gross volume including openings', unit: 'm³' },
|
|
51
|
+
{ name: 'NetVolume', type: QuantityType.Volume, description: 'Net volume excluding openings', unit: 'm³' },
|
|
52
|
+
{ name: 'GrossWeight', type: QuantityType.Weight, description: 'Gross weight', unit: 'kg' },
|
|
53
|
+
{ name: 'NetWeight', type: QuantityType.Weight, description: 'Net weight', unit: 'kg' },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Slab Base Quantities
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
{
|
|
61
|
+
name: 'Qto_SlabBaseQuantities',
|
|
62
|
+
description: 'Base quantities for slabs',
|
|
63
|
+
applicableTypes: ['IfcSlab'],
|
|
64
|
+
quantities: [
|
|
65
|
+
{ name: 'Width', type: QuantityType.Length, description: 'Slab thickness', unit: 'm' },
|
|
66
|
+
{ name: 'Perimeter', type: QuantityType.Length, description: 'Slab perimeter', unit: 'm' },
|
|
67
|
+
{ name: 'GrossArea', type: QuantityType.Area, description: 'Gross area including openings', unit: 'm²' },
|
|
68
|
+
{ name: 'NetArea', type: QuantityType.Area, description: 'Net area excluding openings', unit: 'm²' },
|
|
69
|
+
{ name: 'GrossVolume', type: QuantityType.Volume, description: 'Gross volume including openings', unit: 'm³' },
|
|
70
|
+
{ name: 'NetVolume', type: QuantityType.Volume, description: 'Net volume excluding openings', unit: 'm³' },
|
|
71
|
+
{ name: 'GrossWeight', type: QuantityType.Weight, description: 'Gross weight', unit: 'kg' },
|
|
72
|
+
{ name: 'NetWeight', type: QuantityType.Weight, description: 'Net weight', unit: 'kg' },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Door Base Quantities
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
{
|
|
80
|
+
name: 'Qto_DoorBaseQuantities',
|
|
81
|
+
description: 'Base quantities for doors',
|
|
82
|
+
applicableTypes: ['IfcDoor'],
|
|
83
|
+
quantities: [
|
|
84
|
+
{ name: 'Width', type: QuantityType.Length, description: 'Overall width', unit: 'm' },
|
|
85
|
+
{ name: 'Height', type: QuantityType.Length, description: 'Overall height', unit: 'm' },
|
|
86
|
+
{ name: 'Perimeter', type: QuantityType.Length, description: 'Perimeter of door opening', unit: 'm' },
|
|
87
|
+
{ name: 'Area', type: QuantityType.Area, description: 'Total area of door opening', unit: 'm²' },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Window Base Quantities
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
{
|
|
95
|
+
name: 'Qto_WindowBaseQuantities',
|
|
96
|
+
description: 'Base quantities for windows',
|
|
97
|
+
applicableTypes: ['IfcWindow'],
|
|
98
|
+
quantities: [
|
|
99
|
+
{ name: 'Width', type: QuantityType.Length, description: 'Overall width', unit: 'm' },
|
|
100
|
+
{ name: 'Height', type: QuantityType.Length, description: 'Overall height', unit: 'm' },
|
|
101
|
+
{ name: 'Perimeter', type: QuantityType.Length, description: 'Perimeter of window opening', unit: 'm' },
|
|
102
|
+
{ name: 'Area', type: QuantityType.Area, description: 'Total area of window opening', unit: 'm²' },
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Column Base Quantities
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
{
|
|
110
|
+
name: 'Qto_ColumnBaseQuantities',
|
|
111
|
+
description: 'Base quantities for columns',
|
|
112
|
+
applicableTypes: ['IfcColumn'],
|
|
113
|
+
quantities: [
|
|
114
|
+
{ name: 'Length', type: QuantityType.Length, description: 'Column length (typically height)', unit: 'm' },
|
|
115
|
+
{ name: 'CrossSectionArea', type: QuantityType.Area, description: 'Cross section area', unit: 'm²' },
|
|
116
|
+
{ name: 'OuterSurfaceArea', type: QuantityType.Area, description: 'Outer surface area', unit: 'm²' },
|
|
117
|
+
{ name: 'GrossVolume', type: QuantityType.Volume, description: 'Gross volume', unit: 'm³' },
|
|
118
|
+
{ name: 'NetVolume', type: QuantityType.Volume, description: 'Net volume', unit: 'm³' },
|
|
119
|
+
{ name: 'GrossWeight', type: QuantityType.Weight, description: 'Gross weight', unit: 'kg' },
|
|
120
|
+
{ name: 'NetWeight', type: QuantityType.Weight, description: 'Net weight', unit: 'kg' },
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Beam Base Quantities
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
{
|
|
128
|
+
name: 'Qto_BeamBaseQuantities',
|
|
129
|
+
description: 'Base quantities for beams',
|
|
130
|
+
applicableTypes: ['IfcBeam'],
|
|
131
|
+
quantities: [
|
|
132
|
+
{ name: 'Length', type: QuantityType.Length, description: 'Beam length', unit: 'm' },
|
|
133
|
+
{ name: 'CrossSectionArea', type: QuantityType.Area, description: 'Cross section area', unit: 'm²' },
|
|
134
|
+
{ name: 'OuterSurfaceArea', type: QuantityType.Area, description: 'Outer surface area', unit: 'm²' },
|
|
135
|
+
{ name: 'GrossVolume', type: QuantityType.Volume, description: 'Gross volume', unit: 'm³' },
|
|
136
|
+
{ name: 'NetVolume', type: QuantityType.Volume, description: 'Net volume', unit: 'm³' },
|
|
137
|
+
{ name: 'GrossWeight', type: QuantityType.Weight, description: 'Gross weight', unit: 'kg' },
|
|
138
|
+
{ name: 'NetWeight', type: QuantityType.Weight, description: 'Net weight', unit: 'kg' },
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Stair Base Quantities
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
{
|
|
146
|
+
name: 'Qto_StairFlightBaseQuantities',
|
|
147
|
+
description: 'Base quantities for stair flights',
|
|
148
|
+
applicableTypes: ['IfcStair'],
|
|
149
|
+
quantities: [
|
|
150
|
+
{ name: 'Length', type: QuantityType.Length, description: 'Stair flight length', unit: 'm' },
|
|
151
|
+
{ name: 'GrossVolume', type: QuantityType.Volume, description: 'Gross volume', unit: 'm³' },
|
|
152
|
+
{ name: 'NetVolume', type: QuantityType.Volume, description: 'Net volume', unit: 'm³' },
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
// Ramp Base Quantities
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
{
|
|
160
|
+
name: 'Qto_RampFlightBaseQuantities',
|
|
161
|
+
description: 'Base quantities for ramp flights',
|
|
162
|
+
applicableTypes: ['IfcRamp'],
|
|
163
|
+
quantities: [
|
|
164
|
+
{ name: 'Length', type: QuantityType.Length, description: 'Ramp flight length', unit: 'm' },
|
|
165
|
+
{ name: 'Width', type: QuantityType.Length, description: 'Ramp width', unit: 'm' },
|
|
166
|
+
{ name: 'GrossArea', type: QuantityType.Area, description: 'Gross area', unit: 'm²' },
|
|
167
|
+
{ name: 'NetArea', type: QuantityType.Area, description: 'Net area', unit: 'm²' },
|
|
168
|
+
{ name: 'GrossVolume', type: QuantityType.Volume, description: 'Gross volume', unit: 'm³' },
|
|
169
|
+
{ name: 'NetVolume', type: QuantityType.Volume, description: 'Net volume', unit: 'm³' },
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Roof Base Quantities
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
{
|
|
177
|
+
name: 'Qto_RoofBaseQuantities',
|
|
178
|
+
description: 'Base quantities for roofs',
|
|
179
|
+
applicableTypes: ['IfcRoof'],
|
|
180
|
+
quantities: [
|
|
181
|
+
{ name: 'GrossArea', type: QuantityType.Area, description: 'Total gross area of the roof', unit: 'm²' },
|
|
182
|
+
{ name: 'NetArea', type: QuantityType.Area, description: 'Net area excluding openings', unit: 'm²' },
|
|
183
|
+
{ name: 'ProjectedArea', type: QuantityType.Area, description: 'Projected horizontal area', unit: 'm²' },
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
// Covering Base Quantities
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
{
|
|
191
|
+
name: 'Qto_CoveringBaseQuantities',
|
|
192
|
+
description: 'Base quantities for coverings',
|
|
193
|
+
applicableTypes: ['IfcCovering'],
|
|
194
|
+
quantities: [
|
|
195
|
+
{ name: 'Width', type: QuantityType.Length, description: 'Covering thickness', unit: 'm' },
|
|
196
|
+
{ name: 'GrossArea', type: QuantityType.Area, description: 'Gross area', unit: 'm²' },
|
|
197
|
+
{ name: 'NetArea', type: QuantityType.Area, description: 'Net area', unit: 'm²' },
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// Space Base Quantities
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
{
|
|
205
|
+
name: 'Qto_SpaceBaseQuantities',
|
|
206
|
+
description: 'Base quantities for spaces',
|
|
207
|
+
applicableTypes: ['IfcSpace'],
|
|
208
|
+
quantities: [
|
|
209
|
+
{ name: 'Height', type: QuantityType.Length, description: 'Net height of the space', unit: 'm' },
|
|
210
|
+
{ name: 'FinishCeilingHeight', type: QuantityType.Length, description: 'Height from floor to ceiling finish', unit: 'm' },
|
|
211
|
+
{ name: 'FinishFloorHeight', type: QuantityType.Length, description: 'Floor finish height', unit: 'm' },
|
|
212
|
+
{ name: 'GrossPerimeter', type: QuantityType.Length, description: 'Gross floor perimeter', unit: 'm' },
|
|
213
|
+
{ name: 'NetPerimeter', type: QuantityType.Length, description: 'Net floor perimeter', unit: 'm' },
|
|
214
|
+
{ name: 'GrossFloorArea', type: QuantityType.Area, description: 'Gross floor area', unit: 'm²' },
|
|
215
|
+
{ name: 'NetFloorArea', type: QuantityType.Area, description: 'Net floor area', unit: 'm²' },
|
|
216
|
+
{ name: 'GrossWallArea', type: QuantityType.Area, description: 'Gross wall area', unit: 'm²' },
|
|
217
|
+
{ name: 'NetWallArea', type: QuantityType.Area, description: 'Net wall area', unit: 'm²' },
|
|
218
|
+
{ name: 'GrossCeilingArea', type: QuantityType.Area, description: 'Gross ceiling area', unit: 'm²' },
|
|
219
|
+
{ name: 'NetCeilingArea', type: QuantityType.Area, description: 'Net ceiling area', unit: 'm²' },
|
|
220
|
+
{ name: 'GrossVolume', type: QuantityType.Volume, description: 'Gross volume', unit: 'm³' },
|
|
221
|
+
{ name: 'NetVolume', type: QuantityType.Volume, description: 'Net volume', unit: 'm³' },
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
// Building Storey Base Quantities
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
{
|
|
229
|
+
name: 'Qto_BuildingStoreyBaseQuantities',
|
|
230
|
+
description: 'Base quantities for building storeys',
|
|
231
|
+
applicableTypes: ['IfcBuildingStorey'],
|
|
232
|
+
quantities: [
|
|
233
|
+
{ name: 'GrossHeight', type: QuantityType.Length, description: 'Gross height (floor-to-floor)', unit: 'm' },
|
|
234
|
+
{ name: 'NetHeight', type: QuantityType.Length, description: 'Net height (floor-to-ceiling)', unit: 'm' },
|
|
235
|
+
{ name: 'GrossFloorArea', type: QuantityType.Area, description: 'Gross floor area', unit: 'm²' },
|
|
236
|
+
{ name: 'NetFloorArea', type: QuantityType.Area, description: 'Net floor area', unit: 'm²' },
|
|
237
|
+
{ name: 'GrossVolume', type: QuantityType.Volume, description: 'Gross volume', unit: 'm³' },
|
|
238
|
+
{ name: 'NetVolume', type: QuantityType.Volume, description: 'Net volume', unit: 'm³' },
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
// Railing Base Quantities
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
{
|
|
246
|
+
name: 'Qto_RailingBaseQuantities',
|
|
247
|
+
description: 'Base quantities for railings',
|
|
248
|
+
applicableTypes: ['IfcRailing'],
|
|
249
|
+
quantities: [
|
|
250
|
+
{ name: 'Length', type: QuantityType.Length, description: 'Railing length', unit: 'm' },
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// Lookup Functions
|
|
257
|
+
// ============================================================================
|
|
258
|
+
|
|
259
|
+
// Pre-computed uppercase -> PascalCase lookup map for all known types
|
|
260
|
+
const KNOWN_TYPE_MAP = new Map<string, string>();
|
|
261
|
+
for (const qto of QTO_DEFINITIONS) {
|
|
262
|
+
for (const t of qto.applicableTypes) {
|
|
263
|
+
KNOWN_TYPE_MAP.set(t.toUpperCase(), t);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function normalizeTypeName(type: string): string {
|
|
268
|
+
if (type.startsWith('Ifc')) return type;
|
|
269
|
+
const upper = type.toUpperCase();
|
|
270
|
+
if (!upper.startsWith('IFC')) return type;
|
|
271
|
+
const known = KNOWN_TYPE_MAP.get(upper);
|
|
272
|
+
if (known) return known;
|
|
273
|
+
const rest = type.slice(3).toLowerCase();
|
|
274
|
+
return 'Ifc' + rest.charAt(0).toUpperCase() + rest.slice(1);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Get all valid quantity set definitions for a given IFC entity type.
|
|
279
|
+
*/
|
|
280
|
+
export function getQtoDefinitionsForType(entityType: string): QtoDefinition[] {
|
|
281
|
+
const normalized = normalizeTypeName(entityType);
|
|
282
|
+
return QTO_DEFINITIONS.filter(qto =>
|
|
283
|
+
qto.applicableTypes.some(t => t === normalized)
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get a specific quantity set definition by name.
|
|
289
|
+
*/
|
|
290
|
+
export function getQtoDefinition(qtoName: string): QtoDefinition | undefined {
|
|
291
|
+
return QTO_DEFINITIONS.find(q => q.name === qtoName);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Get the quantity definitions for a specific quantity set.
|
|
296
|
+
*/
|
|
297
|
+
export function getQuantitiesForQto(qtoName: string): QtoQuantityDef[] {
|
|
298
|
+
const qto = QTO_DEFINITIONS.find(q => q.name === qtoName);
|
|
299
|
+
return qto ? qto.quantities : [];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Get the display unit suffix for a quantity type.
|
|
304
|
+
*/
|
|
305
|
+
export function getQuantityUnit(type: QuantityType): string {
|
|
306
|
+
switch (type) {
|
|
307
|
+
case QuantityType.Length: return 'm';
|
|
308
|
+
case QuantityType.Area: return 'm²';
|
|
309
|
+
case QuantityType.Volume: return 'm³';
|
|
310
|
+
case QuantityType.Weight: return 'kg';
|
|
311
|
+
case QuantityType.Count: return '';
|
|
312
|
+
case QuantityType.Time: return 's';
|
|
313
|
+
default: return '';
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
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
|
+
/**
|
|
6
|
+
* Creates a {@link LensDataProvider} from the viewer's data sources.
|
|
7
|
+
*
|
|
8
|
+
* Bridges the abstract provider interface to IfcDataStore + federation:
|
|
9
|
+
* - Multi-model: iterates all models, translates global IDs
|
|
10
|
+
* - Legacy single-model: uses offset = 0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { LensDataProvider, PropertySetInfo } from '@ifc-lite/lens';
|
|
14
|
+
import type { IfcDataStore } from '@ifc-lite/parser';
|
|
15
|
+
import type { FederatedModel } from '@/store/types';
|
|
16
|
+
|
|
17
|
+
interface ModelEntry {
|
|
18
|
+
id: string;
|
|
19
|
+
ifcDataStore: IfcDataStore;
|
|
20
|
+
idOffset: number;
|
|
21
|
+
maxExpressId: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Scan entity array to find the actual maximum expressId */
|
|
25
|
+
function computeMaxExpressId(dataStore: IfcDataStore): number {
|
|
26
|
+
const entities = dataStore.entities;
|
|
27
|
+
if (!entities || entities.count === 0) return 0;
|
|
28
|
+
let max = 0;
|
|
29
|
+
for (let i = 0; i < entities.count; i++) {
|
|
30
|
+
if (entities.expressId[i] > max) max = entities.expressId[i];
|
|
31
|
+
}
|
|
32
|
+
return max;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create a LensDataProvider for the viewer's federated models.
|
|
37
|
+
*
|
|
38
|
+
* @param models - Loaded federated models (may be empty in legacy mode)
|
|
39
|
+
* @param legacyDataStore - Single-model data store (fallback)
|
|
40
|
+
*/
|
|
41
|
+
export function createLensDataProvider(
|
|
42
|
+
models: Map<string, FederatedModel>,
|
|
43
|
+
legacyDataStore: IfcDataStore | null,
|
|
44
|
+
): LensDataProvider {
|
|
45
|
+
// Build a flat array for fast iteration
|
|
46
|
+
const entries: ModelEntry[] = [];
|
|
47
|
+
if (models.size > 0) {
|
|
48
|
+
for (const [, model] of models) {
|
|
49
|
+
if (model.ifcDataStore) {
|
|
50
|
+
entries.push({
|
|
51
|
+
id: model.id,
|
|
52
|
+
ifcDataStore: model.ifcDataStore,
|
|
53
|
+
idOffset: model.idOffset ?? 0,
|
|
54
|
+
maxExpressId: model.maxExpressId ?? 0,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} else if (legacyDataStore) {
|
|
59
|
+
entries.push({
|
|
60
|
+
id: 'legacy',
|
|
61
|
+
ifcDataStore: legacyDataStore,
|
|
62
|
+
idOffset: 0,
|
|
63
|
+
maxExpressId: computeMaxExpressId(legacyDataStore),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
getEntityCount(): number {
|
|
69
|
+
let count = 0;
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
count += entry.ifcDataStore.entities?.count ?? 0;
|
|
72
|
+
}
|
|
73
|
+
return count;
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
forEachEntity(callback: (globalId: number, modelId: string) => void): void {
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
const entities = entry.ifcDataStore.entities;
|
|
79
|
+
if (!entities) continue;
|
|
80
|
+
for (let i = 0; i < entities.count; i++) {
|
|
81
|
+
const expressId = entities.expressId[i];
|
|
82
|
+
callback(expressId + entry.idOffset, entry.id);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
getEntityType(globalId: number): string | undefined {
|
|
88
|
+
const resolved = resolveGlobalId(globalId, entries);
|
|
89
|
+
if (!resolved) return undefined;
|
|
90
|
+
return resolved.entry.ifcDataStore.entities?.getTypeName?.(resolved.expressId);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
getPropertyValue(
|
|
94
|
+
globalId: number,
|
|
95
|
+
propertySetName: string,
|
|
96
|
+
propertyName: string,
|
|
97
|
+
): unknown {
|
|
98
|
+
const resolved = resolveGlobalId(globalId, entries);
|
|
99
|
+
if (!resolved) return undefined;
|
|
100
|
+
return resolved.entry.ifcDataStore.properties?.getPropertyValue?.(
|
|
101
|
+
resolved.expressId,
|
|
102
|
+
propertySetName,
|
|
103
|
+
propertyName,
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
getPropertySets(globalId: number): PropertySetInfo[] {
|
|
108
|
+
const resolved = resolveGlobalId(globalId, entries);
|
|
109
|
+
if (!resolved) return [];
|
|
110
|
+
const psets = resolved.entry.ifcDataStore.properties?.getForEntity?.(resolved.expressId);
|
|
111
|
+
if (!psets) return [];
|
|
112
|
+
return psets as PropertySetInfo[];
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Resolve a global ID to (entry, local expressId).
|
|
119
|
+
* O(m) where m = model count (typically 1–5).
|
|
120
|
+
* Reuses a single result object to avoid per-call allocation during
|
|
121
|
+
* hot-loop lens evaluation (100k+ calls).
|
|
122
|
+
*/
|
|
123
|
+
const _resolved = { entry: null as unknown as ModelEntry, expressId: 0 };
|
|
124
|
+
|
|
125
|
+
function resolveGlobalId(
|
|
126
|
+
globalId: number,
|
|
127
|
+
entries: ModelEntry[],
|
|
128
|
+
): typeof _resolved | null {
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
const localId = globalId - entry.idOffset;
|
|
131
|
+
if (localId >= 0 && localId <= entry.maxExpressId) {
|
|
132
|
+
_resolved.entry = entry;
|
|
133
|
+
_resolved.expressId = localId;
|
|
134
|
+
return _resolved;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
/**
|
|
6
|
+
* Adapter that bridges IfcDataStore (parser output) to the
|
|
7
|
+
* ListDataProvider interface used by @ifc-lite/lists.
|
|
8
|
+
*
|
|
9
|
+
* Handles on-demand property/quantity extraction via WASM when needed.
|
|
10
|
+
* Also handles on-demand attribute extraction for Description, ObjectType,
|
|
11
|
+
* and Tag which are not stored during the fast initial parse.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { IfcDataStore } from '@ifc-lite/parser';
|
|
15
|
+
import { extractPropertiesOnDemand, extractQuantitiesOnDemand, extractEntityAttributesOnDemand } from '@ifc-lite/parser';
|
|
16
|
+
import type { PropertySet, QuantitySet } from '@ifc-lite/data';
|
|
17
|
+
import type { ListDataProvider } from '@ifc-lite/lists';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create a ListDataProvider backed by an IfcDataStore.
|
|
21
|
+
* The provider handles on-demand WASM extraction transparently.
|
|
22
|
+
*/
|
|
23
|
+
export function createListDataProvider(store: IfcDataStore): ListDataProvider {
|
|
24
|
+
// Cache for on-demand attribute extraction (description, objectType, tag)
|
|
25
|
+
// These are not stored during initial parse to keep load times fast,
|
|
26
|
+
// but are needed for list display. Cache avoids re-parsing per column.
|
|
27
|
+
const attrCache = new Map<number, { description: string; objectType: string; tag: string }>();
|
|
28
|
+
|
|
29
|
+
function getOnDemandAttrs(id: number): { description: string; objectType: string; tag: string } {
|
|
30
|
+
const cached = attrCache.get(id);
|
|
31
|
+
if (cached) return cached;
|
|
32
|
+
|
|
33
|
+
if (store.source?.length > 0 && store.entityIndex) {
|
|
34
|
+
const attrs = extractEntityAttributesOnDemand(store, id);
|
|
35
|
+
const result = { description: attrs.description, objectType: attrs.objectType, tag: attrs.tag };
|
|
36
|
+
attrCache.set(id, result);
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const empty = { description: '', objectType: '', tag: '' };
|
|
41
|
+
attrCache.set(id, empty);
|
|
42
|
+
return empty;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
getEntitiesByType: (type) => store.entities.getByType(type),
|
|
47
|
+
|
|
48
|
+
getEntityName: (id) => store.entities.getName(id),
|
|
49
|
+
getEntityGlobalId: (id) => store.entities.getGlobalId(id),
|
|
50
|
+
getEntityDescription: (id) => store.entities.getDescription(id) || getOnDemandAttrs(id).description,
|
|
51
|
+
getEntityObjectType: (id) => store.entities.getObjectType(id) || getOnDemandAttrs(id).objectType,
|
|
52
|
+
getEntityTag: (id) => getOnDemandAttrs(id).tag,
|
|
53
|
+
getEntityTypeName: (id) => store.entities.getTypeName(id),
|
|
54
|
+
|
|
55
|
+
getPropertySets(entityId: number): PropertySet[] {
|
|
56
|
+
if (store.onDemandPropertyMap && store.source?.length > 0) {
|
|
57
|
+
return extractPropertiesOnDemand(store, entityId) as PropertySet[];
|
|
58
|
+
}
|
|
59
|
+
return store.properties?.getForEntity(entityId) ?? [];
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
getQuantitySets(entityId: number): QuantitySet[] {
|
|
63
|
+
if (store.onDemandQuantityMap && store.source?.length > 0) {
|
|
64
|
+
return extractQuantitiesOnDemand(store, entityId) as QuantitySet[];
|
|
65
|
+
}
|
|
66
|
+
return store.quantities?.getForEntity(entityId) ?? [];
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
// Re-export from the @ifc-lite/lists package for convenient viewer imports
|
|
6
|
+
export type {
|
|
7
|
+
ListDataProvider,
|
|
8
|
+
ListDefinition,
|
|
9
|
+
ListResult,
|
|
10
|
+
ListRow,
|
|
11
|
+
CellValue,
|
|
12
|
+
ColumnDefinition,
|
|
13
|
+
PropertyCondition,
|
|
14
|
+
ConditionOperator,
|
|
15
|
+
DiscoveredColumns,
|
|
16
|
+
EntityAttribute,
|
|
17
|
+
} from '@ifc-lite/lists';
|
|
18
|
+
export {
|
|
19
|
+
ENTITY_ATTRIBUTES,
|
|
20
|
+
executeList,
|
|
21
|
+
listResultToCSV,
|
|
22
|
+
discoverColumns,
|
|
23
|
+
LIST_PRESETS,
|
|
24
|
+
} from '@ifc-lite/lists';
|
|
25
|
+
|
|
26
|
+
// Viewer-specific: persistence (browser APIs) and adapter
|
|
27
|
+
export { loadListDefinitions, saveListDefinitions, exportListDefinition, importListDefinition } from './persistence.js';
|
|
28
|
+
export { createListDataProvider } from './adapter.js';
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
/**
|
|
6
|
+
* Persistence for list definitions via localStorage
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ListDefinition } from '@ifc-lite/lists';
|
|
10
|
+
|
|
11
|
+
const STORAGE_KEY = 'ifc-lite-lists';
|
|
12
|
+
|
|
13
|
+
export function loadListDefinitions(): ListDefinition[] {
|
|
14
|
+
try {
|
|
15
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
16
|
+
if (!raw) return [];
|
|
17
|
+
return JSON.parse(raw) as ListDefinition[];
|
|
18
|
+
} catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function saveListDefinitions(definitions: ListDefinition[]): void {
|
|
24
|
+
try {
|
|
25
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(definitions));
|
|
26
|
+
} catch {
|
|
27
|
+
console.warn('[Lists] Failed to save list definitions to localStorage');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function exportListDefinition(definition: ListDefinition): void {
|
|
32
|
+
const json = JSON.stringify(definition, null, 2);
|
|
33
|
+
const blob = new Blob([json], { type: 'application/json' });
|
|
34
|
+
const url = URL.createObjectURL(blob);
|
|
35
|
+
const a = document.createElement('a');
|
|
36
|
+
a.href = url;
|
|
37
|
+
a.download = `${definition.name.replace(/[^a-zA-Z0-9-_]/g, '_')}.list.json`;
|
|
38
|
+
a.click();
|
|
39
|
+
URL.revokeObjectURL(url);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function importListDefinition(file: File): Promise<ListDefinition> {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const reader = new FileReader();
|
|
45
|
+
reader.onload = () => {
|
|
46
|
+
try {
|
|
47
|
+
const def = JSON.parse(reader.result as string) as ListDefinition;
|
|
48
|
+
if (!def.id || !def.name || !def.entityTypes || !def.columns) {
|
|
49
|
+
reject(new Error('Invalid list definition file'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Generate a new ID to avoid collisions
|
|
53
|
+
def.id = crypto.randomUUID();
|
|
54
|
+
def.createdAt = Date.now();
|
|
55
|
+
def.updatedAt = Date.now();
|
|
56
|
+
resolve(def);
|
|
57
|
+
} catch {
|
|
58
|
+
reject(new Error('Failed to parse list definition file'));
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
reader.onerror = () => reject(new Error('Failed to read file'));
|
|
62
|
+
reader.readAsText(file);
|
|
63
|
+
});
|
|
64
|
+
}
|
package/src/services/fs-cache.ts
CHANGED
|
@@ -66,7 +66,7 @@ export async function getCached(key: string): Promise<ArrayBuffer | null> {
|
|
|
66
66
|
|
|
67
67
|
const data = await fs.readFile(cachePath);
|
|
68
68
|
// Slice to actual data range - Uint8Array.buffer may contain extra bytes
|
|
69
|
-
const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
69
|
+
const buffer = (data.buffer as ArrayBuffer).slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
70
70
|
|
|
71
71
|
console.log(`[FS Cache] Cache hit for key ${key} (${(buffer.byteLength / 1024 / 1024).toFixed(2)}MB)`);
|
|
72
72
|
return buffer;
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
/**
|
|
6
|
+
* Ambient type declarations for Tauri-only modules.
|
|
7
|
+
* These packages are only available at runtime in desktop (Tauri) builds
|
|
8
|
+
* and are not installed in the web viewer's node_modules.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
declare module '@tauri-apps/plugin-fs' {
|
|
12
|
+
export function mkdir(path: string, options?: { recursive?: boolean }): Promise<void>;
|
|
13
|
+
export function writeFile(path: string, data: Uint8Array): Promise<void>;
|
|
14
|
+
export function writeTextFile(path: string, data: string): Promise<void>;
|
|
15
|
+
export function readFile(path: string): Promise<Uint8Array>;
|
|
16
|
+
export function readTextFile(path: string): Promise<string>;
|
|
17
|
+
export function exists(path: string): Promise<boolean>;
|
|
18
|
+
export function remove(path: string): Promise<void>;
|
|
19
|
+
export function readDir(path: string): Promise<Array<{ name: string | null }>>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare module '@tauri-apps/api/path' {
|
|
23
|
+
export function appDataDir(): Promise<string>;
|
|
24
|
+
export function join(...paths: string[]): Promise<string>;
|
|
25
|
+
}
|