babylon-ifc-loader 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/API.md ADDED
@@ -0,0 +1,619 @@
1
+ # API Reference
2
+
3
+ This document provides detailed API reference for the IFC Viewer library. The codebase follows a strict layered architecture with clear separation between the IFC data layer (web-ifc) and the rendering layer (Babylon.js).
4
+
5
+ ## Table of Contents
6
+
7
+ - [IFC Data Layer (ifcInit.ts)](#ifc-data-layer-ifcinitts)
8
+ - [initializeWebIFC](#initializewebifc)
9
+ - [loadIfcModel](#loadifcmodel)
10
+ - [closeIfcModel](#closeifcmodel)
11
+ - [getProjectInfo](#getprojectinfo)
12
+ - [Types](#types-ifcinit)
13
+ - [Rendering Layer (ifcModel.ts)](#rendering-layer-ifcmodelts)
14
+ - [buildIfcModel](#buildifcmodel)
15
+ - [disposeIfcModel](#disposeifcmodel)
16
+ - [getModelBounds](#getmodelbounds)
17
+ - [centerModelAtOrigin](#centermodelatorigin)
18
+ - [Types](#types-ifcmodel)
19
+
20
+ ---
21
+
22
+ ## IFC Data Layer (ifcInit.ts)
23
+
24
+ All web-ifc interaction. **Zero Babylon.js dependencies.**
25
+
26
+ ### initializeWebIFC
27
+
28
+ Initialize the web-ifc API. This should be called once at application startup.
29
+
30
+ ```typescript
31
+ async function initializeWebIFC(wasmPath?: string, logLevel?: WebIFC.LogLevel): Promise<WebIFC.IfcAPI>;
32
+ ```
33
+
34
+ #### Parameters
35
+
36
+ | Parameter | Type | Required | Default | Description |
37
+ | ---------- | ----------------- | -------- | ----------------- | ----------------------------------------------------------------------- |
38
+ | `wasmPath` | `string` | No | - | Custom path to the web-ifc.wasm file. Use `"./"` for production builds. |
39
+ | `logLevel` | `WebIFC.LogLevel` | No | `LOG_LEVEL_ERROR` | Logging level for web-ifc. |
40
+
41
+ #### Returns
42
+
43
+ `Promise<WebIFC.IfcAPI>` - Initialized web-ifc API instance.
44
+
45
+ #### Example
46
+
47
+ ```typescript
48
+ import { initializeWebIFC } from "./ifcInit";
49
+
50
+ // Initialize with default settings
51
+ const ifcAPI = await initializeWebIFC();
52
+
53
+ // Initialize with custom WASM path (for production)
54
+ const ifcAPI = await initializeWebIFC("./");
55
+
56
+ // Initialize with custom log level
57
+ import * as WebIFC from "web-ifc";
58
+ const ifcAPI = await initializeWebIFC("./", WebIFC.LogLevel.LOG_LEVEL_WARNING);
59
+ ```
60
+
61
+ #### Console Output
62
+
63
+ ```
64
+ ✓ Web-IFC initialized in Xms
65
+ ```
66
+
67
+ ---
68
+
69
+ ### loadIfcModel
70
+
71
+ Load an IFC file and extract raw geometry data. Returns a `RawIfcModel` with no Babylon.js dependencies.
72
+
73
+ ```typescript
74
+ async function loadIfcModel(
75
+ ifcAPI: WebIFC.IfcAPI,
76
+ source: string | File,
77
+ options?: IfcInitOptions,
78
+ ): Promise<RawIfcModel>;
79
+ ```
80
+
81
+ #### Parameters
82
+
83
+ | Parameter | Type | Required | Default | Description |
84
+ | --------- | ---------------- | -------- | ------- | ------------------------------------------------------- |
85
+ | `ifcAPI` | `WebIFC.IfcAPI` | Yes | - | Initialized web-ifc API instance. |
86
+ | `source` | `string \| File` | Yes | - | URL path to IFC file or File object from drag-and-drop. |
87
+ | `options` | `IfcInitOptions` | No | `{}` | Configuration options for loading. |
88
+
89
+ #### IfcInitOptions
90
+
91
+ | Property | Type | Default | Description |
92
+ | -------------------- | --------- | ------- | ------------------------------------------------------------------ |
93
+ | `coordinateToOrigin` | `boolean` | `true` | Move model coordinates to origin (web-ifc `COORDINATE_TO_ORIGIN`). |
94
+ | `verbose` | `boolean` | `true` | Enable console logging during loading. |
95
+
96
+ #### Returns
97
+
98
+ `Promise<RawIfcModel>` - Raw IFC model data with geometry parts, storey map, and statistics.
99
+
100
+ #### Example
101
+
102
+ ```typescript
103
+ import { loadIfcModel } from "./ifcInit";
104
+
105
+ // Load from URL
106
+ const model = await loadIfcModel(ifcAPI, "/path/to/model.ifc");
107
+
108
+ // Load from File object (drag-and-drop)
109
+ const model = await loadIfcModel(ifcAPI, fileObject);
110
+
111
+ // Load with options
112
+ const model = await loadIfcModel(ifcAPI, "/model.ifc", {
113
+ coordinateToOrigin: true,
114
+ verbose: true,
115
+ });
116
+
117
+ // Access model data
118
+ console.log(model.modelID); // IFC model identifier
119
+ console.log(model.parts.length); // Number of geometry parts
120
+ console.log(model.rawStats); // Statistics
121
+ ```
122
+
123
+ #### Console Output (verbose mode)
124
+
125
+ ```
126
+ 📥 Fetching IFC from URL: /test.ifc
127
+ 📥 Received 2.45 MB
128
+ 📥 Opening IFC model (2.45 MB)...
129
+ 📥 OpenModel returned modelID: 0
130
+
131
+ 📦 Collected 1234 geometry parts
132
+
133
+ 📊 Raw Model Statistics:
134
+ Parts extracted: 1234
135
+ Vertices: 156,789
136
+ Triangles: 52,263
137
+ Storey relationships: 5
138
+ ```
139
+
140
+ ---
141
+
142
+ ### closeIfcModel
143
+
144
+ Close an IFC model and free WASM memory. Call this when disposing of a model.
145
+
146
+ ```typescript
147
+ function closeIfcModel(ifcAPI: WebIFC.IfcAPI, modelID: number): void;
148
+ ```
149
+
150
+ #### Parameters
151
+
152
+ | Parameter | Type | Required | Description |
153
+ | --------- | --------------- | -------- | -------------------------------------- |
154
+ | `ifcAPI` | `WebIFC.IfcAPI` | Yes | Initialized web-ifc API instance. |
155
+ | `modelID` | `number` | Yes | Model ID returned from `loadIfcModel`. |
156
+
157
+ #### Example
158
+
159
+ ```typescript
160
+ import { closeIfcModel } from "./ifcInit";
161
+
162
+ // Close model and free memory
163
+ closeIfcModel(ifcAPI, model.modelID);
164
+ ```
165
+
166
+ #### Console Output
167
+
168
+ ```
169
+ ✓ Model 0 closed and memory freed
170
+ ```
171
+
172
+ ---
173
+
174
+ ### getProjectInfo
175
+
176
+ Extract high-level IFC project metadata (project name, application, author, organization).
177
+
178
+ ```typescript
179
+ function getProjectInfo(ifcAPI: WebIFC.IfcAPI, modelID: number): ProjectInfoResult;
180
+ ```
181
+
182
+ #### Parameters
183
+
184
+ | Parameter | Type | Required | Description |
185
+ | --------- | --------------- | -------- | -------------------------------------- |
186
+ | `ifcAPI` | `WebIFC.IfcAPI` | Yes | Initialized web-ifc API instance. |
187
+ | `modelID` | `number` | Yes | Model ID returned from `loadIfcModel`. |
188
+
189
+ #### Returns
190
+
191
+ `ProjectInfoResult` - Project metadata object.
192
+
193
+ #### Example
194
+
195
+ ```typescript
196
+ import { getProjectInfo } from "./ifcInit";
197
+
198
+ const projectInfo = getProjectInfo(ifcAPI, model.modelID);
199
+
200
+ console.log(projectInfo.projectName); // "My Building Project"
201
+ console.log(projectInfo.projectDescription); // "A sample building"
202
+ console.log(projectInfo.application); // "Revit 2024"
203
+ console.log(projectInfo.author); // "John Doe"
204
+ console.log(projectInfo.organization); // "ACME Corp"
205
+ ```
206
+
207
+ ---
208
+
209
+ ### Types (ifcInit)
210
+
211
+ #### RawIfcModel
212
+
213
+ Complete raw model returned by `loadIfcModel`.
214
+
215
+ ```typescript
216
+ interface RawIfcModel {
217
+ modelID: number; // IFC model identifier
218
+ parts: RawGeometryPart[]; // Array of geometry parts
219
+ storeyMap: Map<number, number>; // Element ID to storey ID mapping
220
+ rawStats: {
221
+ partCount: number; // Number of geometry parts
222
+ vertexCount: number; // Total vertices
223
+ triangleCount: number; // Total triangles
224
+ };
225
+ }
226
+ ```
227
+
228
+ #### RawGeometryPart
229
+
230
+ Single piece of placed geometry from web-ifc.
231
+
232
+ ```typescript
233
+ interface RawGeometryPart {
234
+ expressID: number; // IFC element express ID
235
+ geometryExpressID: number; // Geometry express ID
236
+ positions: Float32Array; // Vertex positions (x, y, z)
237
+ normals: Float32Array; // Vertex normals (nx, ny, nz)
238
+ indices: Uint32Array; // Triangle indices
239
+ flatTransform: number[]; // 4x4 transformation matrix (16 values)
240
+ color: {
241
+ // RGBA color (normalized 0-1)
242
+ x: number; // R
243
+ y: number; // G
244
+ z: number; // B
245
+ w: number; // A
246
+ } | null;
247
+ colorId: number; // Unique color identifier
248
+ }
249
+ ```
250
+
251
+ #### IfcInitOptions
252
+
253
+ Configuration for IFC loader.
254
+
255
+ ```typescript
256
+ interface IfcInitOptions {
257
+ coordinateToOrigin?: boolean; // Move to origin (default: true)
258
+ verbose?: boolean; // Console logging (default: true)
259
+ }
260
+ ```
261
+
262
+ #### ProjectInfoResult
263
+
264
+ Project metadata extracted from IFC file.
265
+
266
+ ```typescript
267
+ interface ProjectInfoResult {
268
+ projectName: string | null; // IFC project name
269
+ projectDescription: string | null; // Project description
270
+ application: string | null; // Creating application
271
+ author: string | null; // Author name
272
+ organization: string | null; // Organization name
273
+ }
274
+ ```
275
+
276
+ ---
277
+
278
+ ## Rendering Layer (ifcModel.ts)
279
+
280
+ All Babylon.js scene construction. **Zero web-ifc dependencies.**
281
+
282
+ ### buildIfcModel
283
+
284
+ Build a Babylon.js scene from raw IFC model data.
285
+
286
+ ```typescript
287
+ function buildIfcModel(model: RawIfcModel, scene: Scene, options?: SceneBuildOptions): SceneBuildResult;
288
+ ```
289
+
290
+ #### Parameters
291
+
292
+ | Parameter | Type | Required | Description |
293
+ | --------- | ------------------- | -------- | ----------------------------------------- |
294
+ | `model` | `RawIfcModel` | Yes | Raw model data from `loadIfcModel`. |
295
+ | `scene` | `Scene` | Yes | Babylon.js scene instance. |
296
+ | `options` | `SceneBuildOptions` | No | Configuration options for scene building. |
297
+
298
+ #### SceneBuildOptions
299
+
300
+ | Property | Type | Default | Description |
301
+ | ------------------ | --------- | ------- | ------------------------------------------------ |
302
+ | `mergeMeshes` | `boolean` | `true` | Merge meshes with same material for performance. |
303
+ | `autoCenter` | `boolean` | `true` | Center model at origin. |
304
+ | `doubleSided` | `boolean` | `true` | Disable backface culling (render both sides). |
305
+ | `generateNormals` | `boolean` | `false` | Generate normals if missing. |
306
+ | `verbose` | `boolean` | `true` | Enable console logging during building. |
307
+ | `freezeAfterBuild` | `boolean` | `true` | Freeze meshes and materials for performance. |
308
+
309
+ #### Returns
310
+
311
+ `SceneBuildResult` - Object containing meshes, root node, and statistics.
312
+
313
+ #### Example
314
+
315
+ ```typescript
316
+ import { buildIfcModel } from "./ifcModel";
317
+
318
+ const { meshes, rootNode, stats } = buildIfcModel(model, scene, {
319
+ mergeMeshes: true,
320
+ autoCenter: true,
321
+ doubleSided: true,
322
+ generateNormals: false,
323
+ verbose: true,
324
+ freezeAfterBuild: true,
325
+ });
326
+
327
+ console.log(`Created ${meshes.length} meshes`);
328
+ console.log(`Build time: ${stats.buildTimeMs}ms`);
329
+ ```
330
+
331
+ #### Console Output (verbose mode)
332
+
333
+ ```
334
+ 🏗️ Building Babylon.js scene from 1234 raw parts...
335
+ Created 1234 initial meshes
336
+ Grouped into 567 unique (expressID + material) combinations
337
+
338
+ 📍 Model auto-centered at origin (offset: 10.50, 0.00, -5.25)
339
+
340
+ ✅ Scene building complete:
341
+ Original parts: 1234
342
+ Merged groups: 100
343
+ Skipped groups: 5
344
+ Final meshes: 1129
345
+ Materials created: 15
346
+ Build time: 234.56ms
347
+ IFC meshes and materials frozen for optimal performance
348
+ ```
349
+
350
+ ---
351
+
352
+ ### disposeIfcModel
353
+
354
+ Dispose all IFC meshes, materials, and the root node. Call this before loading a new model.
355
+
356
+ ```typescript
357
+ function disposeIfcModel(scene: Scene): void;
358
+ ```
359
+
360
+ #### Parameters
361
+
362
+ | Parameter | Type | Required | Description |
363
+ | --------- | ------- | -------- | -------------------------- |
364
+ | `scene` | `Scene` | Yes | Babylon.js scene instance. |
365
+
366
+ #### Example
367
+
368
+ ```typescript
369
+ import { disposeIfcModel } from "./ifcModel";
370
+
371
+ // Clean up before loading new model
372
+ disposeIfcModel(scene);
373
+ ```
374
+
375
+ #### Console Output
376
+
377
+ ```
378
+ ✓ ifc-root node and all child meshes disposed
379
+ ✓ 15 IFC materials disposed
380
+ ```
381
+
382
+ ---
383
+
384
+ ### getModelBounds
385
+
386
+ Calculate bounding box for a set of meshes. Useful for camera positioning.
387
+
388
+ ```typescript
389
+ function getModelBounds(meshes: AbstractMesh[]): BoundsInfo | null;
390
+ ```
391
+
392
+ #### Parameters
393
+
394
+ | Parameter | Type | Required | Description |
395
+ | --------- | ---------------- | -------- | ---------------------------------------- |
396
+ | `meshes` | `AbstractMesh[]` | Yes | Array of meshes to calculate bounds for. |
397
+
398
+ #### Returns
399
+
400
+ `BoundsInfo | null` - Bounding box information, or `null` if no valid meshes.
401
+
402
+ #### Example
403
+
404
+ ```typescript
405
+ import { getModelBounds } from "./ifcModel";
406
+
407
+ const bounds = getModelBounds(meshes);
408
+
409
+ if (bounds) {
410
+ console.log(`Center: ${bounds.center}`);
411
+ console.log(`Size: ${bounds.size}`);
412
+ console.log(`Diagonal: ${bounds.diagonal}`);
413
+
414
+ // Position camera
415
+ camera.target = bounds.center;
416
+ camera.radius = bounds.diagonal * 1.5;
417
+ }
418
+ ```
419
+
420
+ ---
421
+
422
+ ### centerModelAtOrigin
423
+
424
+ Manually center a model at the origin. Useful when `autoCenter` option is disabled.
425
+
426
+ ```typescript
427
+ function centerModelAtOrigin(meshes: AbstractMesh[], rootNode?: TransformNode): Vector3;
428
+ ```
429
+
430
+ #### Parameters
431
+
432
+ | Parameter | Type | Required | Description |
433
+ | ---------- | ---------------- | -------- | ----------------------------- |
434
+ | `meshes` | `AbstractMesh[]` | Yes | Array of meshes to center. |
435
+ | `rootNode` | `TransformNode` | No | Root node to apply offset to. |
436
+
437
+ #### Returns
438
+
439
+ `Vector3` - The offset vector that was applied.
440
+
441
+ #### Example
442
+
443
+ ```typescript
444
+ import { centerModelAtOrigin } from "./ifcModel";
445
+
446
+ const offset = centerModelAtOrigin(meshes, rootNode);
447
+ console.log(`Model centered with offset: ${offset}`);
448
+ ```
449
+
450
+ ---
451
+
452
+ ### Types (ifcModel)
453
+
454
+ #### SceneBuildOptions
455
+
456
+ Configuration for scene building.
457
+
458
+ ```typescript
459
+ interface SceneBuildOptions {
460
+ mergeMeshes?: boolean; // Merge meshes with same material (default: true)
461
+ autoCenter?: boolean; // Center model at origin (default: true)
462
+ doubleSided?: boolean; // Disable backface culling (default: true)
463
+ generateNormals?: boolean; // Generate normals if missing (default: false)
464
+ verbose?: boolean; // Console logging (default: true)
465
+ freezeAfterBuild?: boolean; // Freeze for performance (default: true)
466
+ }
467
+ ```
468
+
469
+ #### SceneBuildResult
470
+
471
+ Result of building a scene.
472
+
473
+ ```typescript
474
+ interface SceneBuildResult {
475
+ meshes: AbstractMesh[]; // Created meshes
476
+ rootNode: TransformNode; // Root transform node
477
+ stats: BuildStats; // Build statistics
478
+ }
479
+ ```
480
+
481
+ #### BuildStats
482
+
483
+ Statistics from scene building.
484
+
485
+ ```typescript
486
+ interface BuildStats {
487
+ originalPartCount: number; // Original geometry parts
488
+ finalMeshCount: number; // Final mesh count after merging
489
+ mergedGroupCount: number; // Successfully merged groups
490
+ skippedGroupCount: number; // Groups that couldn't be merged
491
+ materialCount: number; // Materials created
492
+ buildTimeMs: number; // Build time in milliseconds
493
+ }
494
+ ```
495
+
496
+ #### BoundsInfo
497
+
498
+ Bounds information for camera framing.
499
+
500
+ ```typescript
501
+ interface BoundsInfo {
502
+ min: Vector3; // Minimum corner
503
+ max: Vector3; // Maximum corner
504
+ center: Vector3; // Center point
505
+ size: Vector3; // Size (width, height, depth)
506
+ diagonal: number; // Diagonal length
507
+ }
508
+ ```
509
+
510
+ ---
511
+
512
+ ## Complete Usage Example
513
+
514
+ ```typescript
515
+ import { initializeWebIFC, loadIfcModel, closeIfcModel, getProjectInfo } from "./ifcInit";
516
+ import { buildIfcModel, disposeIfcModel, getModelBounds } from "./ifcModel";
517
+ import { Engine, Scene, ArcRotateCamera, HemisphericLight, Vector3 } from "@babylonjs/core";
518
+
519
+ // Step 1: Initialize web-ifc
520
+ const ifcAPI = await initializeWebIFC("./");
521
+
522
+ // Step 2: Create Babylon.js scene
523
+ const canvas = document.getElementById("canvas") as HTMLCanvasElement;
524
+ const engine = new Engine(canvas, true);
525
+ const scene = new Scene(engine);
526
+
527
+ // Step 3: Setup camera and lighting
528
+ const camera = new ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 10, Vector3.Zero(), scene);
529
+ camera.attachControl(canvas, true);
530
+ const light = new HemisphericLight("light", new Vector3(0, 1, 0), scene);
531
+
532
+ // Step 4: Load IFC model
533
+ const model = await loadIfcModel(ifcAPI, "/model.ifc", {
534
+ coordinateToOrigin: true,
535
+ verbose: true,
536
+ });
537
+
538
+ // Step 5: Get project info
539
+ const projectInfo = getProjectInfo(ifcAPI, model.modelID);
540
+ console.log(`Project: ${projectInfo.projectName}`);
541
+
542
+ // Step 6: Build Babylon.js scene
543
+ const { meshes, rootNode, stats } = buildIfcModel(model, scene, {
544
+ autoCenter: true,
545
+ mergeMeshes: true,
546
+ doubleSided: true,
547
+ verbose: true,
548
+ });
549
+
550
+ // Step 7: Position camera
551
+ const bounds = getModelBounds(meshes);
552
+ if (bounds) {
553
+ camera.target = bounds.center;
554
+ camera.radius = bounds.diagonal * 1.5;
555
+ }
556
+
557
+ // Step 8: Render loop
558
+ engine.runRenderLoop(() => scene.render());
559
+
560
+ // Cleanup when loading new model
561
+ function cleanup() {
562
+ disposeIfcModel(scene);
563
+ closeIfcModel(ifcAPI, model.modelID);
564
+ }
565
+ ```
566
+
567
+ ---
568
+
569
+ ## Mesh Metadata
570
+
571
+ All meshes created by `buildIfcModel` have metadata attached:
572
+
573
+ ```typescript
574
+ mesh.metadata = {
575
+ expressID: number, // IFC element express ID
576
+ modelID: number, // IFC model ID
577
+ };
578
+ ```
579
+
580
+ Use this for element picking and querying:
581
+
582
+ ```typescript
583
+ scene.onPointerDown = (evt, pickResult) => {
584
+ if (pickResult.hit && pickResult.pickedMesh?.metadata) {
585
+ const { expressID, modelID } = pickResult.pickedMesh.metadata;
586
+ const element = ifcAPI.GetLine(modelID, expressID, true);
587
+ const typeName = ifcAPI.GetNameFromTypeCode(element.type);
588
+ console.log(`Picked: ${typeName} (ID: ${expressID})`);
589
+ }
590
+ };
591
+ ```
592
+
593
+ ---
594
+
595
+ ## Performance Notes
596
+
597
+ 1. **Mesh Merging**: Enabled by default, significantly reduces draw calls
598
+ 2. **Freezing**: Meshes and materials are frozen after build for optimal performance
599
+ 3. **Memory**: Always call `closeIfcModel` when done with a model to free WASM memory
600
+ 4. **Z-Fighting**: Materials have incremental `zOffset` to mitigate visual artifacts
601
+ 5. **Coordinate System**: Z-axis flip is applied via root node scaling for IFC-to-Babylon conversion
602
+
603
+ ---
604
+
605
+ ## Error Handling
606
+
607
+ ```typescript
608
+ try {
609
+ const model = await loadIfcModel(ifcAPI, "/model.ifc");
610
+ } catch (error) {
611
+ console.error("Failed to load IFC:", error);
612
+ // Handle error (show user message, etc.)
613
+ }
614
+
615
+ // Check if model is open before closing
616
+ if (ifcAPI.IsModelOpen(modelID)) {
617
+ closeIfcModel(ifcAPI, modelID);
618
+ }
619
+ ```