/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ export {} // module boundary for type checking /** * Create IFC from scratch — generate a fully attributed building * * Demonstrates the bim.create API: build a complete IFC file with * walls, slab, columns, beams, stair, and a parametric timber gridshell * roof — each with material colours, standard IFC property sets, and * base quantities. The roof is a doubly-curved diamond lattice of * small-diameter timber laths generated from a mathematical surface. */ // ─── 1. Project ───────────────────────────────────────────────────────── const h = bim.create.project({ Name: 'Sample Building', Description: 'Demonstration of ifc-lite IFC creation from scratch', Author: 'ifc-lite', Organization: 'ifc-lite', }); // ─── 2. Storey ────────────────────────────────────────────────────────── const gf = bim.create.addIfcBuildingStorey(h, { Name: 'Ground Floor', Elevation: 0 }); // ─── 3. Walls — 5 × 8 m footprint, 3 m high, 0.2 m thick ───────────── const southWall = bim.create.addIfcWall(h, gf, { Name: 'South Wall', Description: 'Exterior south façade', ObjectType: 'Basic Wall:Exterior - 200mm', Start: [0, 0, 0], End: [5, 0, 0], Thickness: 0.2, Height: 3, Openings: [ { Name: 'W-01 Window', Width: 1.2, Height: 1.5, Position: [1.5, 0, 0.9] }, ], }); const eastWall = bim.create.addIfcWall(h, gf, { Name: 'East Wall', Description: 'Exterior east façade', ObjectType: 'Basic Wall:Exterior - 200mm', Start: [5, 0, 0], End: [5, 8, 0], Thickness: 0.2, Height: 3, Openings: [ { Name: 'D-01 Entrance Door', Width: 0.9, Height: 2.1, Position: [3, 0, 0] }, ], }); const northWall = bim.create.addIfcWall(h, gf, { Name: 'North Wall', Description: 'Exterior north façade', ObjectType: 'Basic Wall:Exterior - 200mm', Start: [5, 8, 0], End: [0, 8, 0], Thickness: 0.2, Height: 3, Openings: [ { Name: 'W-02 Window', Width: 1.4, Height: 1.5, Position: [1.8, 0, 0.9] }, ], }); const westWall = bim.create.addIfcWall(h, gf, { Name: 'West Wall', Description: 'Exterior west façade', ObjectType: 'Basic Wall:Exterior - 200mm', Start: [0, 8, 0], End: [0, 0, 0], Thickness: 0.2, Height: 3, }); // Wall colours and materials for (const wId of [southWall, eastWall, northWall, westWall]) { bim.create.setColor(h, wId, 'Plaster - Beige', [0.92, 0.88, 0.80]); bim.create.addIfcMaterial(h, wId, { Name: 'Exterior Wall Assembly', Layers: [ { Name: 'Gypsum Board', Thickness: 0.013, Category: 'Finish' }, { Name: 'Mineral Wool Insulation', Thickness: 0.08, Category: 'Insulation' }, { Name: 'Concrete C30/37', Thickness: 0.2, Category: 'Structural' }, { Name: 'External Render', Thickness: 0.015, Category: 'Finish' }, ], }); } // Wall properties & quantities (all walls share the same spec) for (const [wId, wName, wLen] of [ [southWall, 'South Wall', 5], [eastWall, 'East Wall', 8], [northWall, 'North Wall', 5], [westWall, 'West Wall', 8], ] as [number, string, number][]) { bim.create.addIfcPropertySet(h, wId, { Name: 'Pset_WallCommon', Properties: [ { Name: 'Reference', NominalValue: 'Exterior - 200mm', Type: 'IfcIdentifier' }, { Name: 'IsExternal', NominalValue: true, Type: 'IfcBoolean' }, { Name: 'LoadBearing', NominalValue: true, Type: 'IfcBoolean' }, { Name: 'FireRating', NominalValue: 'REI60', Type: 'IfcLabel' }, { Name: 'AcousticRating', NominalValue: 'STC 45', Type: 'IfcLabel' }, { Name: 'ThermalTransmittance', NominalValue: 0.25, Type: 'IfcReal' }, ], }); bim.create.addIfcElementQuantity(h, wId, { Name: 'Qto_WallBaseQuantities', Quantities: [ { Name: 'Length', Value: wLen, Kind: 'IfcQuantityLength' }, { Name: 'Height', Value: 3, Kind: 'IfcQuantityLength' }, { Name: 'Width', Value: 0.2, Kind: 'IfcQuantityLength' }, { Name: 'GrossSideArea', Value: wLen * 3, Kind: 'IfcQuantityArea' }, { Name: 'GrossVolume', Value: wLen * 3 * 0.2, Kind: 'IfcQuantityVolume' }, ], }); } // ─── 4. Floor slab ────────────────────────────────────────────────────── const slab = bim.create.addIfcSlab(h, gf, { Name: 'Ground Floor Slab', Description: 'Reinforced concrete floor slab', ObjectType: 'Floor:Concrete - 300mm', Position: [0, 0, -0.3], Thickness: 0.3, Width: 5, Depth: 8, }); bim.create.setColor(h, slab, 'Concrete - Grey', [0.65, 0.65, 0.65]); bim.create.addIfcMaterial(h, slab, { Name: 'Floor Slab Assembly', Layers: [ { Name: 'Ceramic Tile', Thickness: 0.01, Category: 'Finish' }, { Name: 'Screed', Thickness: 0.05, Category: 'Finish' }, { Name: 'Reinforced Concrete C30/37', Thickness: 0.24, Category: 'Structural' }, ], }); bim.create.addIfcPropertySet(h, slab, { Name: 'Pset_SlabCommon', Properties: [ { Name: 'Reference', NominalValue: 'Concrete - 300mm', Type: 'IfcIdentifier' }, { Name: 'IsExternal', NominalValue: false, Type: 'IfcBoolean' }, { Name: 'LoadBearing', NominalValue: true, Type: 'IfcBoolean' }, { Name: 'FireRating', NominalValue: 'REI90', Type: 'IfcLabel' }, { Name: 'AcousticRating', NominalValue: 'STC 52', Type: 'IfcLabel' }, { Name: 'Combustible', NominalValue: false, Type: 'IfcBoolean' }, ], }); bim.create.addIfcElementQuantity(h, slab, { Name: 'Qto_SlabBaseQuantities', Quantities: [ { Name: 'Width', Value: 0.3, Kind: 'IfcQuantityLength' }, { Name: 'GrossArea', Value: 40, Kind: 'IfcQuantityArea' }, { Name: 'NetArea', Value: 40, Kind: 'IfcQuantityArea' }, { Name: 'GrossVolume', Value: 12, Kind: 'IfcQuantityVolume' }, { Name: 'GrossWeight', Value: 28800, Kind: 'IfcQuantityWeight' }, ], }); // ─── 5. Columns at corners ────────────────────────────────────────────── const columnPositions: [string, number, number][] = [ ['C-01 SW', 0.1, 0.1], ['C-02 SE', 4.7, 0.1], ['C-03 NE', 4.7, 7.7], ['C-04 NW', 0.1, 7.7], ]; for (const [cName, cx, cy] of columnPositions) { const colId = bim.create.addIfcColumn(h, gf, { Name: cName, Description: 'Reinforced concrete column', ObjectType: 'Column:Concrete 300x300', Position: [cx, cy, 0], Width: 0.3, Depth: 0.3, Height: 3, }); bim.create.setColor(h, colId, 'Concrete - Light', [0.72, 0.72, 0.74]); bim.create.addIfcMaterial(h, colId, { Name: 'Reinforced Concrete C30/37', Category: 'Concrete' }); bim.create.addIfcPropertySet(h, colId, { Name: 'Pset_ColumnCommon', Properties: [ { Name: 'Reference', NominalValue: 'Concrete 300x300', Type: 'IfcIdentifier' }, { Name: 'LoadBearing', NominalValue: true, Type: 'IfcBoolean' }, { Name: 'IsExternal', NominalValue: false, Type: 'IfcBoolean' }, { Name: 'FireRating', NominalValue: 'R120', Type: 'IfcLabel' }, { Name: 'Slope', NominalValue: 0, Type: 'IfcInteger' }, ], }); bim.create.addIfcElementQuantity(h, colId, { Name: 'Qto_ColumnBaseQuantities', Quantities: [ { Name: 'Length', Value: 3, Kind: 'IfcQuantityLength' }, { Name: 'CrossSectionArea', Value: 0.09, Kind: 'IfcQuantityArea' }, { Name: 'GrossVolume', Value: 0.27, Kind: 'IfcQuantityVolume' }, { Name: 'GrossWeight', Value: 648, Kind: 'IfcQuantityWeight' }, ], }); } // ─── 6. Beams along the top ───────────────────────────────────────────── const beamDefs: [string, [number, number, number], [number, number, number]][] = [ ['B-01 South Beam', [0, 0, 3], [5, 0, 3]], ['B-02 North Beam', [0, 8, 3], [5, 8, 3]], ]; for (const [bName, bStart, bEnd] of beamDefs) { const bLen = Math.sqrt( (bEnd[0] - bStart[0]) ** 2 + (bEnd[1] - bStart[1]) ** 2 + (bEnd[2] - bStart[2]) ** 2, ); const beamId = bim.create.addIfcBeam(h, gf, { Name: bName, Description: 'Steel I-beam', ObjectType: 'Beam:IPE 200', Start: bStart, End: bEnd, Width: 0.2, Height: 0.4, }); bim.create.setColor(h, beamId, 'Steel - Grey', [0.55, 0.55, 0.58]); bim.create.addIfcMaterial(h, beamId, { Name: 'Structural Steel S235', Category: 'Steel' }); bim.create.addIfcPropertySet(h, beamId, { Name: 'Pset_BeamCommon', Properties: [ { Name: 'Reference', NominalValue: 'IPE 200', Type: 'IfcIdentifier' }, { Name: 'LoadBearing', NominalValue: true, Type: 'IfcBoolean' }, { Name: 'IsExternal', NominalValue: false, Type: 'IfcBoolean' }, { Name: 'FireRating', NominalValue: 'R60', Type: 'IfcLabel' }, { Name: 'Span', NominalValue: bLen, Type: 'IfcReal' }, ], }); bim.create.addIfcElementQuantity(h, beamId, { Name: 'Qto_BeamBaseQuantities', Quantities: [ { Name: 'Length', Value: bLen, Kind: 'IfcQuantityLength' }, { Name: 'CrossSectionArea', Value: 0.08, Kind: 'IfcQuantityArea' }, { Name: 'GrossVolume', Value: bLen * 0.08, Kind: 'IfcQuantityVolume' }, { Name: 'GrossWeight', Value: bLen * 0.08 * 7850, Kind: 'IfcQuantityWeight' }, ], }); } // ─── 7. Stair ─────────────────────────────────────────────────────────── const numRisers = 17; const riserH = 0.176; const treadL = 0.28; const stairW = 1.0; // Run along +Y (the 8 m axis) so the 4.76 m run fits comfortably. // Position is stair origin; with Direction = PI/2 the width extends toward −X. // Place at x = stairW so the stair sits flush against the west wall (x = 0). const stairId = bim.create.addIfcStair(h, gf, { Name: 'ST-01 Main Stair', Description: 'Straight-run concrete stair', ObjectType: 'Stair:Concrete - Straight Run', Position: [stairW, 1, 0], Direction: Math.PI / 2, NumberOfRisers: numRisers, RiserHeight: riserH, TreadLength: treadL, Width: stairW, }); bim.create.setColor(h, stairId, 'Concrete - Warm', [0.80, 0.78, 0.74]); bim.create.addIfcMaterial(h, stairId, { Name: 'Reinforced Concrete C25/30', Category: 'Concrete' }); bim.create.addIfcPropertySet(h, stairId, { Name: 'Pset_StairCommon', Properties: [ { Name: 'Reference', NominalValue: 'Concrete - Straight Run', Type: 'IfcIdentifier' }, { Name: 'FireRating', NominalValue: 'REI60', Type: 'IfcLabel' }, { Name: 'NumberOfRiser', NominalValue: numRisers, Type: 'IfcInteger' }, { Name: 'NumberOfTreads', NominalValue: numRisers - 1, Type: 'IfcInteger' }, { Name: 'RiserHeight', NominalValue: riserH, Type: 'IfcReal' }, { Name: 'TreadLength', NominalValue: treadL, Type: 'IfcReal' }, { Name: 'IsExternal', NominalValue: false, Type: 'IfcBoolean' }, { Name: 'HandicapAccessible', NominalValue: false, Type: 'IfcBoolean' }, ], }); bim.create.addIfcElementQuantity(h, stairId, { Name: 'Qto_StairBaseQuantities', Quantities: [ { Name: 'Length', Value: numRisers * treadL, Kind: 'IfcQuantityLength' }, { Name: 'GrossVolume', Value: numRisers * treadL * stairW * riserH * 0.5, Kind: 'IfcQuantityVolume' }, ], }); // ─── 8. Second floor ───────────────────────────────────────────────── const ff = bim.create.addIfcBuildingStorey(h, { Name: 'First Floor', Elevation: 3 }); // Floor slab — position is relative to storey elevation (3m) // Opening must be long enough for headroom (≥ 2.1 m) over the ascending stair. // At the slab level the stair is still rising, so the opening needs to extend // back far enough that a person standing on a lower step has full clearance. const stairRunEnd = 1 + numRisers * treadL; // y ≈ 5.76 const stairOpenLen = 4.2; // ~1.5 × run ensures headroom const stairOpenY = stairRunEnd - stairOpenLen / 2; // center near arrival end const ffSlab = bim.create.addIfcSlab(h, ff, { Name: 'First Floor Slab', Description: 'Reinforced concrete floor slab', ObjectType: 'Floor:Concrete - 300mm', Position: [0, 0, -0.3], Thickness: 0.3, Width: 5, Depth: 8, Openings: [ { Name: 'Stair Opening', Width: stairW + 0.2, Height: stairOpenLen, Position: [stairW / 2, stairOpenY, 0] }, ], }); bim.create.setColor(h, ffSlab, 'Concrete - Grey', [0.65, 0.65, 0.65]); bim.create.addIfcMaterial(h, ffSlab, { Name: 'Floor Slab Assembly', Layers: [ { Name: 'Ceramic Tile', Thickness: 0.01, Category: 'Finish' }, { Name: 'Screed', Thickness: 0.05, Category: 'Finish' }, { Name: 'Reinforced Concrete C30/37', Thickness: 0.24, Category: 'Structural' }, ], }); bim.create.addIfcPropertySet(h, ffSlab, { Name: 'Pset_SlabCommon', Properties: [ { Name: 'Reference', NominalValue: 'Concrete - 300mm', Type: 'IfcIdentifier' }, { Name: 'IsExternal', NominalValue: false, Type: 'IfcBoolean' }, { Name: 'LoadBearing', NominalValue: true, Type: 'IfcBoolean' }, { Name: 'FireRating', NominalValue: 'REI90', Type: 'IfcLabel' }, ], }); // Second floor walls — same footprint, Z=0 relative to storey elevation const ffSouthWall = bim.create.addIfcWall(h, ff, { Name: 'South Wall', Description: 'Exterior south façade', ObjectType: 'Basic Wall:Exterior - 200mm', Start: [0, 0, 0], End: [5, 0, 0], Thickness: 0.2, Height: 3, Openings: [ { Name: 'W-03 Window', Width: 1.2, Height: 1.5, Position: [1.5, 0, 0.9] }, ], }); const ffEastWall = bim.create.addIfcWall(h, ff, { Name: 'East Wall', Description: 'Exterior east façade', ObjectType: 'Basic Wall:Exterior - 200mm', Start: [5, 0, 0], End: [5, 8, 0], Thickness: 0.2, Height: 3, Openings: [ { Name: 'W-04 Window', Width: 1.4, Height: 1.5, Position: [3, 0, 0.9] }, ], }); const ffNorthWall = bim.create.addIfcWall(h, ff, { Name: 'North Wall', Description: 'Exterior north façade', ObjectType: 'Basic Wall:Exterior - 200mm', Start: [5, 8, 0], End: [0, 8, 0], Thickness: 0.2, Height: 3, Openings: [ { Name: 'W-05 Window', Width: 1.4, Height: 1.5, Position: [1.8, 0, 0.9] }, ], }); const ffWestWall = bim.create.addIfcWall(h, ff, { Name: 'West Wall', Description: 'Exterior west façade', ObjectType: 'Basic Wall:Exterior - 200mm', Start: [0, 8, 0], End: [0, 0, 0], Thickness: 0.2, Height: 3, }); for (const wId of [ffSouthWall, ffEastWall, ffNorthWall, ffWestWall]) { bim.create.setColor(h, wId, 'Plaster - Beige', [0.92, 0.88, 0.80]); bim.create.addIfcMaterial(h, wId, { Name: 'Exterior Wall Assembly', Layers: [ { Name: 'Gypsum Board', Thickness: 0.013, Category: 'Finish' }, { Name: 'Mineral Wool Insulation', Thickness: 0.08, Category: 'Insulation' }, { Name: 'Concrete C30/37', Thickness: 0.2, Category: 'Structural' }, { Name: 'External Render', Thickness: 0.015, Category: 'Finish' }, ], }); } for (const [wId, wName, wLen] of [ [ffSouthWall, 'South Wall', 5], [ffEastWall, 'East Wall', 8], [ffNorthWall, 'North Wall', 5], [ffWestWall, 'West Wall', 8], ] as [number, string, number][]) { bim.create.addIfcPropertySet(h, wId, { Name: 'Pset_WallCommon', Properties: [ { Name: 'Reference', NominalValue: 'Exterior - 200mm', Type: 'IfcIdentifier' }, { Name: 'IsExternal', NominalValue: true, Type: 'IfcBoolean' }, { Name: 'LoadBearing', NominalValue: true, Type: 'IfcBoolean' }, { Name: 'FireRating', NominalValue: 'REI60', Type: 'IfcLabel' }, { Name: 'ThermalTransmittance', NominalValue: 0.25, Type: 'IfcReal' }, ], }); bim.create.addIfcElementQuantity(h, wId, { Name: 'Qto_WallBaseQuantities', Quantities: [ { Name: 'Length', Value: wLen, Kind: 'IfcQuantityLength' }, { Name: 'Height', Value: 3, Kind: 'IfcQuantityLength' }, { Name: 'Width', Value: 0.2, Kind: 'IfcQuantityLength' }, { Name: 'GrossSideArea', Value: wLen * 3, Kind: 'IfcQuantityArea' }, { Name: 'GrossVolume', Value: wLen * 3 * 0.2, Kind: 'IfcQuantityVolume' }, ], }); } // Second floor columns — Z=0 relative to storey elevation for (const [cName, cx, cy] of columnPositions) { const colId = bim.create.addIfcColumn(h, ff, { Name: cName, Description: 'Reinforced concrete column', ObjectType: 'Column:Concrete 300x300', Position: [cx, cy, 0], Width: 0.3, Depth: 0.3, Height: 3, }); bim.create.setColor(h, colId, 'Concrete - Light', [0.72, 0.72, 0.74]); bim.create.addIfcMaterial(h, colId, { Name: 'Reinforced Concrete C30/37', Category: 'Concrete' }); } // Second floor beams — Z=3 relative to storey elevation (→ world Z=6) for (const [bName, bStart, bEnd] of beamDefs) { const beamId = bim.create.addIfcBeam(h, ff, { Name: bName, Description: 'Steel I-beam', ObjectType: 'Beam:IPE 200', Start: [bStart[0], bStart[1], 3], End: [bEnd[0], bEnd[1], 3], Width: 0.2, Height: 0.4, }); bim.create.setColor(h, beamId, 'Steel - Grey', [0.55, 0.55, 0.58]); bim.create.addIfcMaterial(h, beamId, { Name: 'Structural Steel S235', Category: 'Steel' }); } // ─── 9. Parametric timber gridshell roof ───────────────────────────── // // A doubly-curved gridshell spanning the 5 × 8 m footprint. // Two families of diagonal timber laths form a diamond lattice. // Three surface shapes are provided — uncomment one at a time. const ROOF_W = 5; // building width (X) const ROOF_D = 8; // building depth (Y) const WALL_H = 3; // wall-top relative to first floor storey (elevation 3) const CROWN = 1.8; // rise above walls const NU = 10; // grid divisions along X const NV = 16; // grid divisions along Y const LATH_W = 0.06; // lath width 60 mm const LATH_D = 0.08; // lath depth 80 mm // ── Shape A: sinusoidal dome — smooth symmetric shell ────────────── // function roofZ(u: number, v: number): number { // return WALL_H + CROWN * Math.sin(Math.PI * u) * Math.sin(Math.PI * v); // } // ── Shape B: rolling dunes — three asymmetric peaks ──────────────── // function roofZ(u: number, v: number): number { // const env = Math.sin(Math.PI * u) * Math.sin(Math.PI * v); // const p1 = 1.0 * Math.exp(-((u - 0.3) ** 2 + (v - 0.30) ** 2) / 0.04); // const p2 = 0.65 * Math.exp(-((u - 0.72) ** 2 + (v - 0.55) ** 2) / 0.06); // const p3 = 0.45 * Math.exp(-((u - 0.40) ** 2 + (v - 0.80) ** 2) / 0.03); // return WALL_H + CROWN * env * (0.25 + p1 + p2 + p3); // } // ── Shape C: twisted hypar — saddle with raised diagonal corners ─── function roofZ(u: number, v: number): number { const env = Math.sin(Math.PI * u) * Math.sin(Math.PI * v); const twist = (2 * u - 1) * (2 * v - 1); return WALL_H + CROWN * env * (1.0 + 0.8 * twist); } // Build grid of surface points const grid: [number, number, number][][] = []; for (let i = 0; i <= NU; i++) { const row: [number, number, number][] = []; for (let j = 0; j <= NV; j++) { const u = i / NU, v = j / NV; row.push([u * ROOF_W, v * ROOF_D, roofZ(u, v)]); } grid.push(row); } let lathCount = 0; // Family A — diagonal (i+1, j+1): warm larch tone for (let d = -NV; d <= NU; d++) { const i0 = Math.max(0, d); const i1 = Math.min(NU, NV + d); for (let i = i0; i < i1; i++) { const j = i - d; if (j < 0 || j >= NV) continue; const s = grid[i][j], e = grid[i + 1][j + 1]; const id = bim.create.addIfcBeam(h, ff, { Name: `GL-A${(d + NV).toString().padStart(2, '0')}/${i - i0}`, Description: 'Gridshell lath family A', ObjectType: 'Timber Lath:GL24h 60x80', Start: s, End: e, Width: LATH_W, Height: LATH_D, }); bim.create.setColor(h, id, 'Timber - Larch', [0.82, 0.62, 0.38]); bim.create.addIfcMaterial(h, id, { Name: 'Glulam GL24h Spruce', Category: 'Timber' }); lathCount++; } } // Family B — diagonal (i+1, j-1): darker oak tone for (let s = 0; s <= NU + NV; s++) { const i0 = Math.max(0, s - NV); const i1 = Math.min(NU, s); for (let i = i0; i < i1; i++) { const j = s - i; if (j <= 0 || j > NV) continue; const p0 = grid[i][j], p1 = grid[i + 1][j - 1]; const id = bim.create.addIfcBeam(h, ff, { Name: `GL-B${s.toString().padStart(2, '0')}/${i - i0}`, Description: 'Gridshell lath family B', ObjectType: 'Timber Lath:GL24h 60x80', Start: p0, End: p1, Width: LATH_W, Height: LATH_D, }); bim.create.setColor(h, id, 'Timber - Oak', [0.72, 0.52, 0.30]); bim.create.addIfcMaterial(h, id, { Name: 'Glulam GL24h Spruce', Category: 'Timber' }); lathCount++; } } // Perimeter ring beam at wall top — ties laths together const RING_N = 2 * (NU + NV); const ringPts: [number, number, number][] = []; for (let i = 0; i <= NU; i++) ringPts.push(grid[i][0]); // south edge for (let j = 1; j <= NV; j++) ringPts.push(grid[NU][j]); // east edge for (let i = NU - 1; i >= 0; i--) ringPts.push(grid[i][NV]); // north edge for (let j = NV - 1; j >= 1; j--) ringPts.push(grid[0][j]); // west edge for (let k = 0; k < ringPts.length; k++) { const rStart = ringPts[k]; const rEnd = ringPts[(k + 1) % ringPts.length]; const rId = bim.create.addIfcBeam(h, ff, { Name: `GL-Ring/${k.toString().padStart(2, '0')}`, Description: 'Gridshell perimeter ring beam', ObjectType: 'Beam:GL28h 120x200', Start: rStart, End: rEnd, Width: 0.12, Height: 0.20, }); bim.create.setColor(h, rId, 'Timber - Dark', [0.45, 0.32, 0.20]); bim.create.addIfcMaterial(h, rId, { Name: 'Glulam GL28h Larch', Category: 'Timber' }); } console.log(`Gridshell roof: ${lathCount} laths + ${ringPts.length} ring segments`); // ─── 10. Generate, preview, download ──────────────────────────────────── const result = bim.create.toIfc(h); console.log( `Created ${result.entities.length} entities, ` + `${result.stats.entityCount} STEP lines, ` + `${(result.stats.fileSize / 1024).toFixed(1)} KB`, ); bim.model.loadIfc(result.content, 'sample-building.ifc'); bim.export.download(result.content, 'sample-building.ifc', 'application/x-step');