Skip to main content

IFC building models

IFC is the open file format of building models (BIM): not just the shapes of a building but what each part is, a wall, a door or a slab, on which storey it stands, what it is made of and what its properties are. Architects' and engineers' tools read and write it. bitbybit.ifc writes such models from a script, reads existing files, changes them and builds their geometry.

The IFC functions are experimental: what they cover grows, and an input may still change. The editors mark them as beta and say so in the console when a script uses them.

A whole building is written the same way: a family house in IFC is a two-storey house of some 700 elements, in a project you can open, run and change.

The family house written as IFC in a TypeScript project, drawn in its materials' colours on an afternoon, its shadows on the garden

A model, step by step​

A model is built by a chain of calls. model.create starts one with a project, a site and one building; spatial.addStorey adds the storeys; then walls, slabs, roofs, columns and beams go on the storeys, and doors and windows into the walls.

Every call that changes a model gives back the changed model and leaves the one it was given as it was, so each call takes the model the call before returned. In Rete and Blockly that is one chain of components or blocks; in TypeScript, one variable you keep reassigning.

Here a wall gets a window. A window type is added first, and the window is placed from it, cutting its opening in the wall:

Bitbybit Platform

A wall with a window, written as IFC in Rete

rete logoRete
Script Source (rete)
{
"id": "rete-v2-json",
"nodes": {
"model": {
"id": "model",
"name": "bitbybit.ifc.model.create",
"customName": "create model",
"async": true,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"name": "Project",
"siteName": "Site",
"buildingName": "Building",
"lengthUnit": "metre",
"seed": "learn",
"author": "",
"organization": ""
},
"inputs": {},
"position": [
40,
40
]
},
"storey": {
"id": "storey",
"name": "bitbybit.ifc.spatial.addStorey",
"customName": "add storey",
"async": true,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"id": "ground",
"name": "Storey",
"elevation": 0
},
"inputs": {
"model": {
"connections": [
{
"node": "model",
"output": "result",
"data": {}
}
]
}
},
"position": [
460,
40
]
},
"wall": {
"id": "wall",
"name": "bitbybit.ifc.walls.add",
"customName": "add wall",
"async": true,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"storey": "ground",
"id": "wall",
"name": "",
"height": 2.8,
"thickness": 0.2,
"layerSet": "",
"alignment": "center",
"baseOffset": 0,
"wallType": "",
"predefinedType": "STANDARD"
},
"inputs": {
"model": {
"connections": [
{
"node": "storey",
"output": "result",
"data": {}
}
]
},
"start": {
"connections": [
{
"node": "start",
"output": "result",
"data": {}
}
]
},
"end": {
"connections": [
{
"node": "end",
"output": "result",
"data": {}
}
]
}
},
"position": [
880,
40
]
},
"start": {
"id": "start",
"name": "bitbybit.point.pointXY",
"customName": "point xy",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0
},
"inputs": {},
"position": [
460,
520
]
},
"end": {
"id": "end",
"name": "bitbybit.point.pointXY",
"customName": "point xy",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 4,
"y": 0
},
"inputs": {},
"position": [
460,
860
]
},
"type": {
"id": "type",
"name": "bitbybit.ifc.windows.addType",
"customName": "add window type",
"async": true,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"id": "window-type",
"name": "Window",
"width": 1.2,
"height": 1.2,
"frameMaterial": "",
"glassMaterial": ""
},
"inputs": {
"model": {
"connections": [
{
"node": "wall",
"output": "result",
"data": {}
}
]
}
},
"position": [
1300,
40
]
},
"window": {
"id": "window",
"name": "bitbybit.ifc.windows.add",
"customName": "add window",
"async": true,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"wall": "wall",
"windowType": "window-type",
"id": "",
"name": "",
"offset": 1.4,
"sill": 0.9
},
"inputs": {
"model": {
"connections": [
{
"node": "type",
"output": "result",
"data": {}
}
]
}
},
"position": [
1720,
40
]
},
"shapes": {
"id": "shapes",
"name": "bitbybit.ifc.geometry.toShapes",
"customName": "to shapes",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"adjustZtoY": true,
"emptyWhenFailed": true,
"facetedMeshes": false,
"maxTriangles": 100000
},
"inputs": {
"model": {
"connections": [
{
"node": "window",
"output": "result",
"data": {}
}
]
}
},
"position": [
2140,
40
]
}
},
"modules": {}
}

Ids of your own​

Give a storey, a wall or a type an id such as ground or north-wall, and later calls name it by that id: the wall stands on the storey ground, the window goes into the wall wall. The model turns each id into the GlobalId IFC requires. With a seed on model.create, the same calls always give the same GlobalIds, so a script writes the same file every run.

Units and axes​

Lengths are in millimetres unless model.create is given another lengthUnit, and a length left out takes its default converted to the model's unit: a wall left without a height is 3000 mm high, or 3 in a model in metres. The examples here work in metres, so a building is drawn at the scale of the editor's view. IFC is Z-up: positions on a storey are [x, y] in its plan, and heights go up from its floor.

What a model can hold​

  • Walls by thickness, by a layer set of materials or by a wall type, joined at corners with walls.connect and clipped under roofs; walls.parameters reads one back and walls.edit changes it, its doors and windows moving with it.
  • Openings, doors and windows: rectangular openings in walls, any outline through a slab, and door and window types placed many times.
  • Slabs, roofs, columns, beams and members, and spaces, the rooms and areas of a storey.
  • The site: terrain and trees.
  • Materials, property sets and quantities: colours and layer sets, property values set one by one, and base quantities measured from the geometry.

Reading and writing files​

model.read reads an .ifc file, given as text or bytes: in a project, load it with bitbybit.asset.getFile and bitbybit.asset.toUint8Array (Assets). model.summary says what it holds, and model.elements lists the elements of a type. Unchanged parts of a file are written back exactly as they were read. model.write gives the text of the file, which bitbybit.asset.download saves. An IFC2X3 file can be read and drawn; model.upgradeToIfc4 makes it editable.

Geometry​

A model describes its geometry; a kernel builds it:

  • geometry.toShapes builds every element as an exact OpenCascade shape, one per element, standing up along Y for drawing. They measure, cut and export like any other shape.
  • geometry.toStep writes the model as a STEP file of such shapes, each element a named product, and geometry.toDocument gives the assembly document instead.
  • geometry.recipe describes the geometry as data, a recipe, which bitbybit.manifold.recipes.build builds as fast mesh solids for large models, and bitbybit.occt.recipes.build exactly.

Building needs the OpenCascade kernel beside the IFC one; the editors on bitbybit.dev start both.

More​

  • A family house in IFC: a whole two-storey house written with these functions, in a project of several files, under a sky with a time-of-day slider.
  • IFC in the npm packages: writing a house in Node, drawing a model in your own app and what the first version covers.
  • IFC models with an agent: the order to write a building in, and the mistakes to avoid.

Every function​

FunctionWhat it does
assemblies.addMakes elements the parts of a new assembly standing in their storey in their place.
beams.addAdds a beam from start to end in a storey, its section centred on the line between them.
columns.addAdds a column standing on position in a storey's plan, its section centred on that point and extruded height up from baseOffset above the floor.
doors.addPlaces a door of a door type in a wall, offset along the wall's axis from its start, cutting an opening as wide and high as the type.
doors.addTypeAdds a door type: a lining around one panel, width by height, which doors are then placed from.
geometry.recipeDescribes the geometry of a model's elements as a recipe, ready for a kernel to build.
geometry.toDocumentBuilds a model's elements into an OpenCascade assembly document for STEP or glTF: a product per element with its GlobalId and IFC type as properties, nested in its site, building and storey, by IFC class unless groupByClass is false, one part per type placed many times.
geometry.toShapesBuilds a model's elements as OpenCascade shapes, one per element in the order geometry.recipe lists them, each placed where the model puts it, in the model's units.
geometry.toStepWrites a model's elements as a STEP file of OpenCascade shapes, as toDocument builds them, and gives back its bytes.
geometry.unsupportedLists the elements whose geometry geometry.recipe leaves out, each with what it could not describe, such as a profile type that is not supported yet.
materials.addAdds a material, which layer sets, columns and beams then refer to by its name.
materials.addLayerSetAdds a layer set: materials stacked in order, each with its thickness, which walls, wall types, slabs and roofs are then made of.
members.addAdds a member from start to end in a storey, its section centred on the line between them, as a brace, a rafter, a stud, a mullion or another kind.
model.createStarts a new IFC4 model with a project, its site and one building, ready for storeys.
model.elementDescribes one object of a model: its GlobalId, its type, its name and the spatial element it stands in.
model.elementsLists the objects of one IFC type, its subtypes included, optionally only those standing in one storey or other spatial element.
model.georeferenceReads where a model sits on the Earth: the map conversion that places its coordinates in a projected coordinate reference system, and its site's reference latitude, longitude and elevation.
model.getAttributeReads one attribute of an object, such as a wall's Name, Description or Tag.
model.globalIdOfSays which GlobalId an id stands for in a model: the GlobalId itself, or the one the model derives from an id of your own such as north-wall.
model.moveMoves an element or a space, and turns it about the vertical through its own origin; what is placed on it, such as a wall's openings, moves with it.
model.readReads an IFC file into a model to inspect, change and write back.
model.removeRemoves an element, an empty storey or space, or a type nothing is of, with what only it uses.
model.setAttributeSets one attribute of an object to a text, a number, true or false, such as a wall's Name, Description or Tag.
model.summaryDescribes what a model holds: its schema, its project, its unit, its storeys and how many elements of each type.
model.upgradeToIfc4Turns a model read from an IFC2X3 file into an IFC4 model, which can be changed and is written as IFC4.
model.writeWrites a model as the text of an .ifc file.
model.writeBytesWrites a model as the bytes of an .ifc file, at any size.
openings.addCuts a rectangular opening through a wall, offset along its axis from its start and sill above its base.
openings.addInSlabCuts an opening of any outline, such as a stairwell or a shaft, through a slab's whole thickness.
openings.editMoves an opening along its wall or up and down it, or resizes it.
properties.addSetAttaches a property set to one or more objects: walls, doors, types or any object with a GlobalId.
properties.getSetsReads the property sets attached to an object, with each single value by name.
properties.removeSetTakes a property set away from one object.
properties.removeValuesTakes properties out of one object's property set; the set goes when none are left.
properties.setValuesSets property values of one object, in its property set of the given name, and makes the set when the object has none.
quantities.computeMeasures elements and writes their base quantity sets, replacing any set of the same name: walls, slabs, columns, beams, members, doors, windows, wall openings, spaces and roofs.
quantities.getReads the quantity sets of an element, with each quantity's value by name.
roofs.addAdds a roof over a rectangle of a storey: flat, sloping one way, gabled, or hipped on all four sides.
site.addTerrainAdds terrain to the site: the ground as an outline in the site's plan, with holes where the building or paving meets it, its top at elevation and depth deep.
site.addTreeAdds a tree to the site: a tapering trunk and a rounded crown at position, height tall, each part in its own material, with its species written as the element's object type.
slabs.addAdds a slab: an outline in a storey's plan, with optional holes, its top topOffset above the storey's floor and its thickness below that.
spaces.addAdds a space to a storey: a room or an area, from an outline in the storey's plan, height high.
spaces.listLists the spaces of the model, or of one storey, with their numbers, names, areas and heights.
spatial.addStoreyAdds a storey to the model's building at an elevation above the building's origin.
spatial.setElevationRaises or lowers a storey to a new elevation.
spatial.storeysLists the storeys of the model's buildings, lowest first, with their GlobalIds, names and elevations.
walls.addAdds a straight wall on a storey, from start to end in the storey's plan, height high.
walls.addTypeAdds a wall type: a named kind of wall made of a layer set.
walls.clipByPlaneClips a wall with a plane, removing the part on the side the plane's normal points to, as a wall is cut under a sloping roof.
walls.clipByRoofClips a wall under a roof by each of the roof's planes that cuts it: under the eaves level or along the slope, under a gable to the gable's triangle.
walls.connectJoins two walls where they meet, trimming both: two walls meeting at their ends are mitred into a corner, and a wall ending against another's side is cut back to that side.
walls.disconnectTakes away the join between two walls, so the ends it trimmed are square again.
walls.editChanges a wall's axis, height, base, layers or alignment.
walls.parametersReads a wall's parameters back from the model: its axis, height, base, thickness, layers and alignment, the walls it is joined to, its openings and its clippings.
windows.addPlaces a window of a window type in a wall, offset along the wall's axis from its start and sill above its base, cutting an opening as wide and high as the type.
windows.addTypeAdds a window type: a frame around one pane, width by height, which windows are then placed from.

Recipes​

FunctionWhat it does
buildBuilds each root of a recipe as a shape placed by its matrix, in root order.
buildDocumentBuilds the roots of a recipe into an assembly document, ready to export to STEP.