
Thatopen Core Web Ifc
- 5 installs
- 17 repo stars
- Updated July 8, 2026
- openaec-foundation/thatopen-claude-skill-package
Helps with ai & agent building tasks.
About
thatopen-core-web-ifc is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- thatopen-core-web-ifc
- AI & Agent Building
- AI-coding skill
Thatopen Core Web Ifc by the numbers
- 5 all-time installs (skills.sh)
- Ranked #13,065 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 26, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/thatopen-claude-skill-package --skill thatopen-core-web-ifcAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5 |
|---|---|
| repo stars | ★ 17 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/thatopen-claude-skill-package ↗ |
What it does
Helps with ai & agent building tasks.
Files
web-ifc Engine: Direct WASM IFC Parser
Overview
web-ifc is the WASM-powered IFC parsing engine beneath the ThatOpen component stack. It reads and writes IFC files at native speed in browser and Node.js environments. The central class is IfcAPI.
- Package:
web-ifc(npm) - Source: https://github.com/ThatOpen/engine_web-ifc
- Environments: Browser, Node.js (single-threaded and multi-threaded WASM)
- License: MPL-2.0
When using ThatOpen components (@thatopen/components), you rarely call web-ifc directly — theIfcLoadercomponent wraps it. Use this skill when you need low-level IFC access, custom geometry extraction, or bulk property queries outside the component framework.
---
Critical Warnings
1. ALWAYS call `Init()` before any other method (except SetWasmPath). Every method silently fails or throws without WASM initialization. 2. ALWAYS call `CloseModel(modelID)` when done — each open model holds significant WASM heap memory. Forgetting this causes memory leaks that crash browser tabs. 3. ALWAYS use `.size()` and `.get(i)` for `Vector<T>` access — NEVER use array indexing ([]). WASM vectors are not JavaScript arrays. 4. ALWAYS call `Dispose()` when the IfcAPI instance is no longer needed — this releases the entire WASM module. 5. NEVER create multiple `IfcAPI` instances — one instance handles multiple models. Extra instances waste memory by loading duplicate WASM modules. 6. Vertex format is 6 floats per vertex: [x, y, z, nx, ny, nz] — position followed by normal. ALWAYS account for this interleaved layout when extracting geometry. 7. All 4x4 matrices are column-major (16 floats) — directly compatible with THREE.Matrix4.fromArray().
---
Quick Start
import * as WebIFC from "web-ifc";
const ifcApi = new WebIFC.IfcAPI();
ifcApi.SetWasmPath("/wasm/"); // MUST be called before Init()
await ifcApi.Init();
const data = new Uint8Array(buffer); // from fetch or fs.readFile
const modelID = ifcApi.OpenModel(data);
// Query walls
const wallIDs = ifcApi.GetLineIDsWithType(modelID, WebIFC.IFCWALL);
for (let i = 0; i < wallIDs.size(); i++) {
const wall = ifcApi.GetLine(modelID, wallIDs.get(i));
console.log(wall.Name?.value);
}
// Get geometry
const mesh = ifcApi.GetFlatMesh(modelID, wallIDs.get(0));
// Get coordination matrix
const matrix = ifcApi.GetCoordinationMatrix(modelID);
ifcApi.CloseModel(modelID); // ALWAYS free memory---
Initialization
SetWasmPath(path: string, absolute?: boolean): void
Sets the directory containing WASM files. MUST be called before Init(). The directory MUST contain web-ifc.wasm (and web-ifc-mt.wasm for multi-threaded mode).
ifcApi.SetWasmPath("/static/wasm/"); // relative
ifcApi.SetWasmPath("https://cdn.example.com/wasm/", true); // absolute URLNEVER hardcode a version in the WASM path — ALWAYS match the installed web-ifc npm version.
Init(customLocateFileHandler?, forceSingleThread?): Promise<void>
Initializes the WASM module. MUST be awaited before calling any other API method.
await ifcApi.Init(); // default (auto-detect threading)
await ifcApi.Init(undefined, true); // force single-threaded
await ifcApi.Init((path, prefix) => "/custom/" + path); // custom file locatorDispose(): void
Releases the entire WASM module and all resources. Call when the IfcAPI instance is no longer needed.
---
Model Lifecycle
| Method | Signature | Purpose |
|---|---|---|
OpenModel | (data: Uint8Array, settings?: LoaderSettings) => number | Load IFC from buffer, returns modelID |
OpenModels | (dataSets: Uint8Array[], settings?) => number[] | Load multiple IFC files at once |
OpenModelFromCallback | (callback: ModelLoadCallback, settings?) => number | Stream-load without full buffer in memory |
CreateModel | (model: NewIfcModel, settings?) => number | Create empty IFC model |
SaveModel | (modelID: number) => Uint8Array | Serialize model to IFC bytes |
CloseModel | (modelID: number) => void | Free all WASM memory for this model |
IsModelOpen | (modelID: number) => boolean | Check if model is open |
LoaderSettings
interface LoaderSettings {
COORDINATE_TO_ORIGIN?: boolean; // translate model to origin (recommended)
USE_FAST_BOOLS?: boolean; // faster but less accurate boolean ops
CIRCLE_SEGMENTS_LOW?: number; // tessellation for small curves
CIRCLE_SEGMENTS_MEDIUM?: number; // tessellation for medium curves
CIRCLE_SEGMENTS_HIGH?: number; // tessellation for large curves
BOOL_ABORT_THRESHOLD?: number; // timeout (ms) for boolean operations
MEMORY_LIMIT?: number; // WASM memory limit in bytes
}ALWAYS use COORDINATE_TO_ORIGIN: true for models with large world coordinates — prevents floating-point precision issues in rendering.
---
Data Queries
Core Query Methods
| Method | Returns | Purpose |
|---|---|---|
GetAllLines(modelID) | Vector<number> | All expressIDs in the model |
GetLineIDsWithType(modelID, type, includeInherited?) | Vector<number> | ExpressIDs by IFC type |
GetLine(modelID, expressID, flatten?, inverse?, inversePropKey?) | any | Single entity by expressID |
GetLines(modelID, expressIDs, flatten?, inverse?, inversePropKey?) | any[] | Batch entity retrieval |
GetRawLineData(modelID, expressID) | RawLineData | Raw unparsed data (faster) |
GetLineType(modelID, expressID) | number | IFC type code only |
GetMaxExpressID(modelID) | number | Highest expressID |
GetNextExpressID(modelID, expressID) | number | Next valid expressID |
GetLine Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
flatten | boolean | false | Recursively resolve all references inline |
inverse | boolean | false | Include inverse relationships |
inversePropKey | string? | null | Filter inverse props to specific key |
NEVER use flatten: true on large models without limiting scope — it recursively resolves every reference and causes severe performance degradation.
Schema & Type Information
| Method | Returns | Purpose |
|---|---|---|
GetModelSchema(modelID) | string | Schema version ("IFC2X3", "IFC4", "IFC4X3") |
GetAllTypesOfModel(modelID) | IfcType[] | All IFC types present in model |
GetHeaderLine(modelID, headerType) | any | IFC header info |
---
Geometry Extraction
Single-Element Geometry
const flatMesh = ifcApi.GetFlatMesh(modelID, expressID);
for (let i = 0; i < flatMesh.geometries.size(); i++) {
const pg = flatMesh.geometries.get(i);
const geom = ifcApi.GetGeometry(modelID, pg.geometryExpressID);
const verts = ifcApi.GetVertexArray(geom.GetVertexData(), geom.GetVertexDataSize());
const indices = ifcApi.GetIndexArray(geom.GetIndexData(), geom.GetIndexDataSize());
// verts: Float32Array — 6 floats per vertex [x, y, z, nx, ny, nz]
// indices: Uint32Array — triangle indices
// pg.color: { x, y, z, w } — RGBA (w = alpha)
// pg.flatTransformation: number[] — 4x4 column-major transform matrix
}Geometry Streaming (Memory-Efficient)
ALWAYS prefer streaming over LoadAllGeometry for models with more than a few hundred elements.
| Method | Purpose |
|---|---|
StreamAllMeshes(modelID, callback) | Stream ALL meshable entities |
StreamAllMeshesWithTypes(modelID, types[], callback) | Stream filtered by IFC types |
StreamMeshes(modelID, expressIDs[], callback) | Stream specific elements |
LoadAllGeometry(modelID) | Load all at once (AVOID for large models) |
Callback signature: (mesh: FlatMesh, index: number, total: number) => void
Coordination & Transforms
| Method | Purpose |
|---|---|
GetCoordinationMatrix(modelID) | 4x4 matrix for multi-model alignment |
SetGeometryTransformation(modelID, matrix) | Apply global transform to all geometry output |
GetWorldTransformMatrix(modelID, placementExpressId) | Transform for specific placement |
---
Properties Helper
The ifcApi.properties object provides high-level async methods for property queries.
| Method | Returns | Purpose |
|---|---|---|
getItemProperties(modelID, id, recursive?, inverse?) | Promise<any> | All properties for an element |
getPropertySets(modelID, elementID?, recursive?) | Promise<any[]> | Property sets (Psets) |
getTypeProperties(modelID, elementID?, recursive?) | Promise<any[]> | Type object properties |
getMaterialsProperties(modelID, elementID?, recursive?) | Promise<any[]> | Material definitions |
getSpatialStructure(modelID, includeProperties?) | Promise<Node> | Full spatial hierarchy tree |
The spatial structure returns a tree: { expressID, type, children: [...] }.
---
GUID Utilities
| Method | Purpose |
|---|---|
GetExpressIdFromGuid(modelID, guid) | Convert IFC GUID string to expressID |
GetGuidFromExpressId(modelID, expressID) | Convert expressID to IFC GUID string |
CreateIfcGuidToExpressIdMapping(modelID) | Pre-build mapping for faster lookups |
ALWAYS call CreateIfcGuidToExpressIdMapping first if performing many GUID lookups — it builds an index that makes subsequent calls faster.
---
Writing Data
| Method | Purpose |
|---|---|
WriteLine(modelID, lineObject) | Write or update a single entity |
WriteLines(modelID, lineObjects[]) | Batch write |
DeleteLine(modelID, expressID) | Remove entity from model |
CreateIfcEntity(modelID, type, ...args) | Create new IFC entity |
After modifications, use SaveModel(modelID) to serialize back to IFC format.
---
Logging
import { LogLevel } from "web-ifc";
ifcApi.SetLogLevel(LogLevel.LOG_LEVEL_OFF); // silent (recommended for production)
ifcApi.SetLogLevel(LogLevel.LOG_LEVEL_ERROR); // errors only
ifcApi.SetLogLevel(LogLevel.LOG_LEVEL_DEBUG); // verbose debugging---
Key Types
See references/methods.md for complete type definitions.
| Type | Description |
|---|---|
FlatMesh | Triangulated geometry result: { expressID, geometries: Vector<PlacedGeometry> } |
PlacedGeometry | Single geometry piece: color (RGBA), transform (4x4), geometryExpressID |
IfcGeometry | Raw WASM geometry: GetVertexData/Size(), GetIndexData/Size() |
Vector<T> | WASM vector: access via .size() and .get(i) ONLY |
RawLineData | Unparsed entity: { ID, type, arguments } |
LoaderSettings | Model loading configuration |
IfcType | Type descriptor: { typeID, typeName } |
---
Common IFC Type Constants
import {
// Structural
IFCWALL, IFCWALLSTANDARDCASE, IFCSLAB, IFCBEAM, IFCCOLUMN,
// Openings
IFCDOOR, IFCWINDOW, IFCOPENINGELEMENT,
// Building elements
IFCROOF, IFCSTAIR, IFCFURNISHINGELEMENT,
// Spatial hierarchy
IFCPROJECT, IFCSITE, IFCBUILDING, IFCBUILDINGSTOREY, IFCSPACE,
// Properties & relations
IFCPROPERTYSET, IFCPROPERTYSINGLEVALUE,
IFCRELDEFINESBYPROPERTIES, IFCRELCONTAINEDINSPATIALSTRUCTURE,
IFCRELAGGREGATES, IFCRELVOIDSELEMENT,
} from "web-ifc";---
References
references/methods.md— Complete IfcAPI method signatures and type definitionsreferences/examples.md— Full working examples: init, load, query, geometry, propertiesreferences/anti-patterns.md— Common failures: WASM init, memory leaks, Vector access
Sources
- GitHub: https://github.com/ThatOpen/engine_web-ifc
- Docs: https://thatopen.github.io/engine_web-ifc/docs/
- npm: https://www.npmjs.com/package/web-ifc
web-ifc Anti-Patterns and Common Failures
1. WASM Initialization Failures
Missing or Wrong WASM Path
WRONG:
const ifcApi = new WebIFC.IfcAPI();
await ifcApi.Init(); // Fails silently or throws — WASM files not foundCORRECT:
const ifcApi = new WebIFC.IfcAPI();
ifcApi.SetWasmPath("/wasm/"); // Directory containing web-ifc.wasm
await ifcApi.Init();The WASM path directory MUST contain the .wasm files matching the installed web-ifc version. NEVER hardcode a version-specific path — when the package updates, the WASM files change.
Calling Methods Before Init
WRONG:
const ifcApi = new WebIFC.IfcAPI();
const modelID = ifcApi.OpenModel(data); // CRASH: WASM not initializedCORRECT:
const ifcApi = new WebIFC.IfcAPI();
ifcApi.SetWasmPath("/wasm/");
await ifcApi.Init(); // ALWAYS await Init() first
const modelID = ifcApi.OpenModel(data);Setting WASM Path After Init
WRONG:
await ifcApi.Init();
ifcApi.SetWasmPath("/wasm/"); // Too late — Init already loaded WASMCORRECT:
ifcApi.SetWasmPath("/wasm/"); // ALWAYS before Init
await ifcApi.Init();---
2. Memory Leaks
Forgetting to Close Models
WRONG:
async function processIfc(data: Uint8Array) {
const modelID = ifcApi.OpenModel(data);
const walls = ifcApi.GetLineIDsWithType(modelID, IFCWALL);
return walls; // Model stays open — WASM memory leaked
}CORRECT:
async function processIfc(data: Uint8Array) {
const modelID = ifcApi.OpenModel(data);
try {
const walls = ifcApi.GetLineIDsWithType(modelID, IFCWALL);
// Process walls...
return result;
} finally {
ifcApi.CloseModel(modelID); // ALWAYS close in finally block
}
}Creating Multiple IfcAPI Instances
WRONG:
// Each instance loads a separate WASM module (~10MB+)
const api1 = new WebIFC.IfcAPI();
const api2 = new WebIFC.IfcAPI();
const api3 = new WebIFC.IfcAPI();CORRECT:
// One instance handles all models
const ifcApi = new WebIFC.IfcAPI();
await ifcApi.Init();
const model1 = ifcApi.OpenModel(data1);
const model2 = ifcApi.OpenModel(data2);Not Calling Dispose
WRONG:
// Application shutdown — IfcAPI still holds WASM memory
window.removeEventListener("unload", cleanup);CORRECT:
function cleanup() {
for (const modelID of openModels) {
ifcApi.CloseModel(modelID);
}
ifcApi.Dispose(); // Release entire WASM module
}---
3. Vector Access Errors
Using Array Indexing on WASM Vectors
WRONG:
const walls = ifcApi.GetLineIDsWithType(modelID, IFCWALL);
for (let i = 0; i < walls.length; i++) { // .length is undefined
const wall = ifcApi.GetLine(modelID, walls[i]); // [] returns undefined
}CORRECT:
const walls = ifcApi.GetLineIDsWithType(modelID, IFCWALL);
for (let i = 0; i < walls.size(); i++) { // .size() for length
const wall = ifcApi.GetLine(modelID, walls.get(i)); // .get(i) for access
}This applies to ALL Vector<T> returns: GetAllLines, GetLineIDsWithType, LoadAllGeometry, and FlatMesh.geometries.
Spreading WASM Vectors
WRONG:
const ids = [...ifcApi.GetAllLines(modelID)]; // Spread does not work on WASM vectorsCORRECT:
const vec = ifcApi.GetAllLines(modelID);
const ids: number[] = [];
for (let i = 0; i < vec.size(); i++) {
ids.push(vec.get(i));
}---
4. Geometry Extraction Mistakes
Wrong Vertex Data Layout
WRONG:
const verts = ifcApi.GetVertexArray(geom.GetVertexData(), geom.GetVertexDataSize());
// Treating as 3 floats per vertex — WRONG, it is 6
const positions = new THREE.BufferAttribute(verts, 3);CORRECT:
const verts = ifcApi.GetVertexArray(geom.GetVertexData(), geom.GetVertexDataSize());
// 6 floats per vertex: [x, y, z, nx, ny, nz]
const vertexCount = verts.length / 6;
const positions = new Float32Array(vertexCount * 3);
const normals = new Float32Array(vertexCount * 3);
for (let i = 0; i < verts.length; i += 6) {
const j = (i / 6) * 3;
positions[j] = verts[i]; positions[j+1] = verts[i+1]; positions[j+2] = verts[i+2];
normals[j] = verts[i+3]; normals[j+1] = verts[i+4]; normals[j+2] = verts[i+5];
}Ignoring the Transform Matrix
WRONG:
// Placing geometry without its transform — ends up at wrong position
const mesh = new THREE.Mesh(bufferGeometry, material);
scene.add(mesh);CORRECT:
const mesh = new THREE.Mesh(bufferGeometry, material);
mesh.applyMatrix4(new THREE.Matrix4().fromArray(pg.flatTransformation));
scene.add(mesh);Ignoring the Coordination Matrix
WRONG:
// Loading multiple models without coordination — they overlap incorrectly
const group1 = loadModel(data1);
const group2 = loadModel(data2);CORRECT:
const group1 = loadModel(data1);
group1.applyMatrix4(new THREE.Matrix4().fromArray(ifcApi.GetCoordinationMatrix(modelID1)));
const group2 = loadModel(data2);
group2.applyMatrix4(new THREE.Matrix4().fromArray(ifcApi.GetCoordinationMatrix(modelID2)));---
5. Performance Anti-Patterns
Flattening on Large Models
WRONG:
// flatten=true recursively resolves ALL references — extremely slow on large models
const allLines = ifcApi.GetAllLines(modelID);
for (let i = 0; i < allLines.size(); i++) {
const entity = ifcApi.GetLine(modelID, allLines.get(i), true); // DO NOT flatten every entity
}CORRECT:
// Use flatten only for specific entities you need fully resolved
const wallIDs = ifcApi.GetLineIDsWithType(modelID, IFCWALL);
for (let i = 0; i < wallIDs.size(); i++) {
const wall = ifcApi.GetLine(modelID, wallIDs.get(i)); // no flatten
// Resolve specific references manually if needed
}Using LoadAllGeometry Instead of Streaming
WRONG:
// Loads ALL geometry into memory at once — crashes on large models
const allMeshes = ifcApi.LoadAllGeometry(modelID);CORRECT:
// Stream geometry one element at a time — constant memory usage
ifcApi.StreamAllMeshes(modelID, (mesh, index, total) => {
processGeometry(mesh);
});Not Using COORDINATE_TO_ORIGIN
WRONG:
const modelID = ifcApi.OpenModel(data);
// Model at real-world coordinates (e.g., x=500000, y=6000000)
// Causes floating-point precision issues in Three.js renderingCORRECT:
const modelID = ifcApi.OpenModel(data, { COORDINATE_TO_ORIGIN: true });
// Model translated to origin — safe for GPU rendering---
6. Schema-Related Mistakes
Assuming Schema Version
WRONG:
// Assuming IFC4 — will fail on IFC2X3 files
const storeys = ifcApi.GetLineIDsWithType(modelID, IFCBUILDINGSTOREY);CORRECT:
const schema = ifcApi.GetModelSchema(modelID);
console.log(`Loading ${schema} model`);
// Some type constants may differ between IFC2X3 and IFC4
// ALWAYS check schema when handling schema-specific typesNot Checking Available Types
WRONG:
// Assumes the model contains walls — may return empty vector
const walls = ifcApi.GetLineIDsWithType(modelID, IFCWALL);
const firstWall = ifcApi.GetLine(modelID, walls.get(0)); // Crashes if size() == 0CORRECT:
const walls = ifcApi.GetLineIDsWithType(modelID, IFCWALL);
if (walls.size() === 0) {
console.log("No walls in this model");
return;
}
const firstWall = ifcApi.GetLine(modelID, walls.get(0));---
7. Property Access Mistakes
Accessing Property Values Directly
WRONG:
const wall = ifcApi.GetLine(modelID, expressID);
console.log(wall.Name); // Logs { type: 1, value: "Wall-001" } — not the stringCORRECT:
const wall = ifcApi.GetLine(modelID, expressID);
console.log(wall.Name?.value); // Logs "Wall-001"IFC property values are wrapped objects with type and value fields. ALWAYS access .value to get the actual data. ALWAYS use optional chaining (?.) because properties may be null.
Unresolved References
WRONG:
const wall = ifcApi.GetLine(modelID, expressID);
console.log(wall.OwnerHistory.OwningUser); // OwnerHistory is { type: 5, value: 42 } — a referenceCORRECT:
const wall = ifcApi.GetLine(modelID, expressID);
// Option 1: Resolve manually
const ownerHistory = ifcApi.GetLine(modelID, wall.OwnerHistory.value);
// Option 2: Use flatten (only for small, targeted queries)
const wallFlat = ifcApi.GetLine(modelID, expressID, true);
console.log(wallFlat.OwnerHistory.OwningUser); // Now fully resolvedReferences have type: 5 and a value containing the expressID of the referenced entity.
web-ifc Examples
1. Initialize and Load an IFC File
import * as WebIFC from "web-ifc";
async function loadIfc(url: string): Promise<{ ifcApi: WebIFC.IfcAPI; modelID: number }> {
const ifcApi = new WebIFC.IfcAPI();
ifcApi.SetWasmPath("/wasm/");
await ifcApi.Init();
const response = await fetch(url);
const buffer = await response.arrayBuffer();
const data = new Uint8Array(buffer);
const modelID = ifcApi.OpenModel(data, {
COORDINATE_TO_ORIGIN: true,
USE_FAST_BOOLS: true,
});
console.log("Schema:", ifcApi.GetModelSchema(modelID));
return { ifcApi, modelID };
}2. Query IFC Entities by Type
function getWalls(ifcApi: WebIFC.IfcAPI, modelID: number) {
const wallIDs = ifcApi.GetLineIDsWithType(modelID, WebIFC.IFCWALL);
const walls = [];
for (let i = 0; i < wallIDs.size(); i++) {
const wall = ifcApi.GetLine(modelID, wallIDs.get(i));
walls.push({
expressID: wall.expressID,
globalId: wall.GlobalId?.value,
name: wall.Name?.value,
description: wall.Description?.value,
});
}
return walls;
}
// Include subtypes (e.g., IFCWALLSTANDARDCASE is a subtype of IFCWALL)
function getAllWallTypes(ifcApi: WebIFC.IfcAPI, modelID: number) {
const wallIDs = ifcApi.GetLineIDsWithType(modelID, WebIFC.IFCWALL, true);
// Returns both IFCWALL and IFCWALLSTANDARDCASE entities
return wallIDs;
}3. Walk the Spatial Structure
async function printSpatialTree(ifcApi: WebIFC.IfcAPI, modelID: number) {
const tree = await ifcApi.properties.getSpatialStructure(modelID);
function walk(node: any, depth: number = 0) {
const indent = " ".repeat(depth);
const entity = ifcApi.GetLine(modelID, node.expressID);
console.log(`${indent}[${node.type}] ${entity.Name?.value ?? "unnamed"} (#${node.expressID})`);
if (node.children) {
for (const child of node.children) {
walk(child, depth + 1);
}
}
}
walk(tree);
}4. Extract Properties for an Element
async function getElementProperties(ifcApi: WebIFC.IfcAPI, modelID: number, expressID: number) {
// Basic item properties
const item = await ifcApi.properties.getItemProperties(modelID, expressID);
console.log("Item:", item);
// Property sets (Psets)
const psets = await ifcApi.properties.getPropertySets(modelID, expressID, true);
for (const ps of psets) {
console.log(`\nPset: ${ps.Name?.value}`);
if (ps.HasProperties) {
for (const prop of ps.HasProperties) {
console.log(` ${prop.Name?.value}: ${prop.NominalValue?.value}`);
}
}
}
// Materials
const materials = await ifcApi.properties.getMaterialsProperties(modelID, expressID, true);
console.log("\nMaterials:", materials);
// Type properties
const typeProps = await ifcApi.properties.getTypeProperties(modelID, expressID, true);
console.log("Type:", typeProps);
}5. Extract Geometry for a Single Element
function extractGeometry(ifcApi: WebIFC.IfcAPI, modelID: number, expressID: number) {
const flatMesh = ifcApi.GetFlatMesh(modelID, expressID);
const geometries = [];
for (let i = 0; i < flatMesh.geometries.size(); i++) {
const pg = flatMesh.geometries.get(i);
const geom = ifcApi.GetGeometry(modelID, pg.geometryExpressID);
const verts = ifcApi.GetVertexArray(geom.GetVertexData(), geom.GetVertexDataSize());
const indices = ifcApi.GetIndexArray(geom.GetIndexData(), geom.GetIndexDataSize());
// Split interleaved vertex data: 6 floats per vertex [x,y,z,nx,ny,nz]
const vertexCount = verts.length / 6;
const positions = new Float32Array(vertexCount * 3);
const normals = new Float32Array(vertexCount * 3);
for (let v = 0; v < verts.length; v += 6) {
const outIdx = (v / 6) * 3;
positions[outIdx] = verts[v];
positions[outIdx + 1] = verts[v + 1];
positions[outIdx + 2] = verts[v + 2];
normals[outIdx] = verts[v + 3];
normals[outIdx + 1] = verts[v + 4];
normals[outIdx + 2] = verts[v + 5];
}
geometries.push({
positions,
normals,
indices,
color: { r: pg.color.x, g: pg.color.y, b: pg.color.z, a: pg.color.w },
transform: pg.flatTransformation, // 4x4 column-major
});
}
return geometries;
}6. Stream All Geometry to Three.js
import * as WebIFC from "web-ifc";
import * as THREE from "three";
async function loadIfcToThreeJs(url: string): Promise<THREE.Group> {
const ifcApi = new WebIFC.IfcAPI();
ifcApi.SetWasmPath("/wasm/");
await ifcApi.Init();
const data = new Uint8Array(await (await fetch(url)).arrayBuffer());
const modelID = ifcApi.OpenModel(data, { COORDINATE_TO_ORIGIN: true });
const group = new THREE.Group();
const coordMatrix = ifcApi.GetCoordinationMatrix(modelID);
group.applyMatrix4(new THREE.Matrix4().fromArray(coordMatrix));
ifcApi.StreamAllMeshes(modelID, (flatMesh, index, total) => {
for (let i = 0; i < flatMesh.geometries.size(); i++) {
const pg = flatMesh.geometries.get(i);
const geom = ifcApi.GetGeometry(modelID, pg.geometryExpressID);
const v = ifcApi.GetVertexArray(geom.GetVertexData(), geom.GetVertexDataSize());
const idx = ifcApi.GetIndexArray(geom.GetIndexData(), geom.GetIndexDataSize());
// Deinterleave: 6 floats per vertex
const pos = new Float32Array(v.length / 2);
const norm = new Float32Array(v.length / 2);
for (let j = 0; j < v.length; j += 6) {
const k = (j / 6) * 3;
pos[k] = v[j]; pos[k + 1] = v[j + 1]; pos[k + 2] = v[j + 2];
norm[k] = v[j + 3]; norm[k + 1] = v[j + 4]; norm[k + 2] = v[j + 5];
}
const bufGeom = new THREE.BufferGeometry();
bufGeom.setAttribute("position", new THREE.BufferAttribute(pos, 3));
bufGeom.setAttribute("normal", new THREE.BufferAttribute(norm, 3));
bufGeom.setIndex(new THREE.BufferAttribute(idx, 1));
const { x, y, z, w } = pg.color;
const mat = new THREE.MeshPhongMaterial({
color: new THREE.Color(x, y, z),
opacity: w,
transparent: w < 1,
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(bufGeom, mat);
mesh.applyMatrix4(new THREE.Matrix4().fromArray(pg.flatTransformation));
group.add(mesh);
}
});
ifcApi.CloseModel(modelID);
return group;
}7. Stream Specific Types Only
function streamWallsAndSlabs(ifcApi: WebIFC.IfcAPI, modelID: number) {
const targetTypes = [WebIFC.IFCWALL, WebIFC.IFCSLAB, WebIFC.IFCBEAM, WebIFC.IFCCOLUMN];
ifcApi.StreamAllMeshesWithTypes(modelID, targetTypes, (mesh, index, total) => {
console.log(`Processing ${index + 1}/${total}: expressID=${mesh.expressID}`);
// Process geometry same as StreamAllMeshes callback
});
}8. Multi-Model Federation
async function loadMultipleModels(urls: string[]) {
const ifcApi = new WebIFC.IfcAPI();
ifcApi.SetWasmPath("/wasm/");
await ifcApi.Init();
const models: Array<{ modelID: number; matrix: number[] }> = [];
for (const url of urls) {
const data = new Uint8Array(await (await fetch(url)).arrayBuffer());
const modelID = ifcApi.OpenModel(data, { COORDINATE_TO_ORIGIN: true });
const matrix = ifcApi.GetCoordinationMatrix(modelID);
models.push({ modelID, matrix });
console.log(`Loaded model ${modelID}: schema=${ifcApi.GetModelSchema(modelID)}`);
}
// Process all models...
// ALWAYS close all models when done
for (const { modelID } of models) {
ifcApi.CloseModel(modelID);
}
}9. GUID Lookups
function guidExamples(ifcApi: WebIFC.IfcAPI, modelID: number) {
// Pre-build mapping for performance (do this once)
ifcApi.CreateIfcGuidToExpressIdMapping(modelID);
// GUID to expressID
const expressID = ifcApi.GetExpressIdFromGuid(modelID, "3Dr$t5gOX0AxMQNRw6q2K$");
console.log("ExpressID:", expressID);
// expressID to GUID
const guid = ifcApi.GetGuidFromExpressId(modelID, expressID);
console.log("GUID:", guid);
}10. Create and Save a New IFC Model
async function createNewModel(ifcApi: WebIFC.IfcAPI) {
const modelID = ifcApi.CreateModel({
schema: "IFC4",
name: "New Model",
description: "Created with web-ifc",
});
// Create entities using CreateIfcEntity...
// Write data using WriteLine...
const ifcData = ifcApi.SaveModel(modelID);
const blob = new Blob([ifcData], { type: "application/octet-stream" });
ifcApi.CloseModel(modelID);
return blob;
}11. Discover Model Contents
function discoverModel(ifcApi: WebIFC.IfcAPI, modelID: number) {
// What schema?
console.log("Schema:", ifcApi.GetModelSchema(modelID));
// What types are present?
const types = ifcApi.GetAllTypesOfModel(modelID);
for (const t of types) {
const count = ifcApi.GetLineIDsWithType(modelID, t.typeID).size();
console.log(` ${t.typeName}: ${count} entities`);
}
// Total entities
const allLines = ifcApi.GetAllLines(modelID);
console.log(`Total entities: ${allLines.size()}`);
// Header info
const fileName = ifcApi.GetHeaderLine(modelID, WebIFC.FILE_NAME);
console.log("File name:", fileName);
}IfcAPI Complete Method Signatures
Initialization
SetWasmPath(path: string, absolute?: boolean): void
Init(customLocateFileHandler?: LocateFileHandlerFn, forceSingleThread?: boolean): Promise<void>
Dispose(): voidModel Lifecycle
OpenModel(data: Uint8Array, settings?: LoaderSettings): number
OpenModels(dataSets: Array<Uint8Array>, settings?: LoaderSettings): Array<number>
OpenModelFromCallback(callback: ModelLoadCallback, settings?: LoaderSettings): number
CreateModel(model: NewIfcModel, settings?: LoaderSettings): number
SaveModel(modelID: number): Uint8Array
CloseModel(modelID: number): void
IsModelOpen(modelID: number): booleanData Queries
GetAllLines(modelID: number): Vector<number>
GetLineIDsWithType(modelID: number, type: number, includeInherited?: boolean): Vector<number>
GetLine(modelID: number, expressID: number, flatten?: boolean, inverse?: boolean, inversePropKey?: string): any
GetLines(modelID: number, expressIDs: number[], flatten?: boolean, inverse?: boolean, inversePropKey?: string): any[]
GetRawLineData(modelID: number, expressID: number): RawLineData
GetHeaderLine(modelID: number, headerType: number): any
GetLineType(modelID: number, expressID: number): number
GetMaxExpressID(modelID: number): number
GetNextExpressID(modelID: number, expressID: number): numberSchema & Type Information
GetModelSchema(modelID: number): string
GetAllTypesOfModel(modelID: number): IfcType[]Writing Data
WriteLine<T extends IfcLineObject>(modelID: number, lineObject: T): void
WriteLines<T extends IfcLineObject>(modelID: number, lineObjects: T[]): void
DeleteLine(modelID: number, expressID: number): void
CreateIfcEntity(modelID: number, type: number, ...args: any[]): IfcLineObjectGeometry Extraction
GetFlatMesh(modelID: number, expressID: number): FlatMesh
GetGeometry(modelID: number, geometryExpressID: number): IfcGeometry
GetVertexArray(ptr: number, size: number): Float32Array
GetIndexArray(ptr: number, size: number): Uint32Array
LoadAllGeometry(modelID: number): Vector<FlatMesh>Geometry Streaming
StreamAllMeshes(modelID: number, meshCallback: (mesh: FlatMesh, index: number, total: number) => void): void
StreamAllMeshesWithTypes(modelID: number, types: number[], meshCallback: (mesh: FlatMesh, index: number, total: number) => void): void
StreamMeshes(modelID: number, expressIDs: number[], meshCallback: (mesh: FlatMesh, index: number, total: number) => void): voidCoordination & Transforms
GetCoordinationMatrix(modelID: number): Array<number>
GetWorldTransformMatrix(modelID: number, placementExpressId: number): Array<number>
SetGeometryTransformation(modelID: number, transformationMatrix: Array<number>): voidProperties Helper (ifcApi.properties)
getItemProperties(modelID: number, id: number, recursive?: boolean, inverse?: boolean): Promise<any>
getPropertySets(modelID: number, elementID?: number, recursive?: boolean): Promise<any[]>
getTypeProperties(modelID: number, elementID?: number, recursive?: boolean): Promise<any[]>
getMaterialsProperties(modelID: number, elementID?: number, recursive?: boolean): Promise<any[]>
getSpatialStructure(modelID: number, includeProperties?: boolean): Promise<Node>GUID Utilities
GetExpressIdFromGuid(modelID: number, guid: string): number
GetGuidFromExpressId(modelID: number, expressID: number): string
CreateIfcGuidToExpressIdMapping(modelID: number): voidLogging
SetLogLevel(level: LogLevel): void---
Type Definitions
interface LoaderSettings {
COORDINATE_TO_ORIGIN?: boolean; // move model geometry to origin
USE_FAST_BOOLS?: boolean; // faster but less accurate boolean operations
CIRCLE_SEGMENTS_LOW?: number; // tessellation segments for small curves
CIRCLE_SEGMENTS_MEDIUM?: number; // tessellation segments for medium curves
CIRCLE_SEGMENTS_HIGH?: number; // tessellation segments for large curves
BOOL_ABORT_THRESHOLD?: number; // timeout in ms for boolean operations
MEMORY_LIMIT?: number; // WASM memory limit in bytes
}
interface FlatMesh {
expressID: number;
geometries: Vector<PlacedGeometry>;
}
interface PlacedGeometry {
color: { x: number; y: number; z: number; w: number }; // RGBA
flatTransformation: Array<number>; // 4x4 column-major matrix (16 floats)
geometryExpressID: number;
}
interface IfcGeometry {
GetVertexData(): number; // WASM pointer to vertex data
GetVertexDataSize(): number; // size of vertex data
GetIndexData(): number; // WASM pointer to index data
GetIndexDataSize(): number; // size of index data
}
// WASM vector — NEVER use array indexing on this type
interface Vector<T> {
get(index: number): T;
size(): number;
}
interface RawLineData {
ID: number;
type: number;
arguments: any[];
}
interface IfcType {
typeID: number;
typeName: string;
}
interface NewIfcModel {
schema: string; // "IFC2X3" | "IFC4" | "IFC4X3"
name?: string;
description?: string;
}
enum LogLevel {
LOG_LEVEL_DEBUG = 0,
LOG_LEVEL_INFO = 1,
LOG_LEVEL_WARN = 2,
LOG_LEVEL_ERROR = 3,
LOG_LEVEL_OFF = 4,
}
// Header line type constants
const FILE_DESCRIPTION: number;
const FILE_NAME: number;
const FILE_SCHEMA: number;Vertex Data Layout
Each vertex consists of 6 floats (24 bytes):
[x, y, z, nx, ny, nz]
^-position-^ ^-normal-^GetVertexArrayreturns aFloat32ArraywithvertexCount * 6elementsGetIndexArrayreturns aUint32Arraywith triangle indices (3 per triangle)- Vertex count =
verts.length / 6 - Triangle count =
indices.length / 3