How to create a basic wall using IfcOpenShell WebAssembly
December 19, 2022 · 5 min
In this tutorial, I’m going to show you how to create a simple wall using IfcOpenShell WebAssembly.
We will be using some Python, Javascript, HTML and a dash of CSS. I won’t be explaining the complete basics of these technologies in this video because I want to focus on IfcOpenShell WebAssembly. However, if you would like me to create content showing how Python, Javascript and other programming languages can be used with OpenBIM, please let me know.
If you would like to follow along and play with the code, you can find the example here on Github.
First, let’s look at the final result and then we will go back and explain the different parts. Using Visual Studio Code and the Live Server extension I launch the example. Here’s the final result:
“But where is my AI-generated super automatic digital twin BIM model?!” you ask. Well, baby step… we have to start somewhere.
Let’s take a look at what’s happening behind the scenes to makes this happen.
If you’ve never seen HTML source before this may seem intimidating, but let’s look at the most relevant parts in bite-size chunks. At the top of the document we find the <head> tag which contains a <link> tag that points to style.css. This is simply loading our CSS file. As mentioned, we will not go in-depth into the CSS or styling of our page in this tutorial.
The next major tag is the <body> tag, which contains the rest of the page really. We will skip down to the <script> tags. These here are fetching a library called Pyodide which allows us to run Python code inside the browser:
Pretty neat! These next lines fetch the Three.js library which we use to show stuff in 3D in the browser:
22
23
24
25
26
27
<scripttype="importmap">
{"imports": {"three":"https://unpkg.com/three@0.141.0/build/three.module.js",
"OrbitControls":"https://unpkg.com/three@0.141.0/examples/jsm/controls/OrbitControls.js"}}
</script>
<scripttype="module">
import * as THREE from 'three';
If we skip ahead a little to the more interesting bits, we find the main function. It first loads and initializes Pyodide and then loads IfcOpenShell as a python package.
34
35
36
37
38
39
40
41
42
asyncfunctionmain() {
document.querySelector("#status2").innerHTML="Initializing pyodide";
pyodide=awaitloadPyodide();
document.querySelector("#status2").innerHTML="Loading package manager";
awaitpyodide.loadPackage("micropip");
awaitpyodide.loadPackage("numpy");
constmicropip=pyodide.pyimport("micropip");
document.querySelector("#status2").innerHTML="Loading IfcOpenShell (this may take a while)";
awaitmicropip.install("../IfcOpenShell-0.7.0-py3-none-any.whl");
Then we define a function that is bound to the onclick event of the only button on our page. This is where things get really interesting. On line 51 we run a python file called create_basic_wall.py. Let’s jump into that file:
import ifcopenshell
from ifcopenshell.api import run
# Create a blank modelmodel = ifcopenshell.file()
# All projects must have one IFC Project elementproject = run("root.create_entity", model, ifc_class="IfcProject", name="My Project")
# Geometry is optional in IFC, but because we want to use geometry in this example, let's define units# Assigning without arguments defaults to metric unitsrun("unit.assign_unit", model)
# Let's create a modeling geometry context, so we can store 3D geometry (note: IFC supports 2D too!)context = run("context.add_context", model, context_type="Model")
# In particular, in this example we want to store the 3D "body" geometry of objects, i.e. the body shapebody = run(
"context.add_context", model,
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context
)
# Create a site, building, and storey. Many hierarchies are possible.site = run("root.create_entity", model, ifc_class="IfcSite", name="My Site")
building = run("root.create_entity", model, ifc_class="IfcBuilding", name="Building A")
storey = run("root.create_entity", model, ifc_class="IfcBuildingStorey", name="Ground Floor")
# Since the site is our top level location, assign it to the project# Then place our building on the site, and our storey in the buildingrun("aggregate.assign_object", model, relating_object=project, product=site)
run("aggregate.assign_object", model, relating_object=site, product=building)
run("aggregate.assign_object", model, relating_object=building, product=storey)
# Let's create a new wallwall = run("root.create_entity", model, ifc_class="IfcWall")
# Add a new wall-like body geometry, 5 meters long, 3 meters high, and 200mm thickrepresentation = run("geometry.add_wall_representation", model, context=body, length=5, height=3, thickness=0.2)
# Assign our new body geometry back to our wallrun("geometry.assign_representation", model, product=wall, representation=representation)
# Place our wall in the ground floorrun("spatial.assign_container", model, relating_structure=storey, product=wall)
# Return ifc modelmodel
This is a pure python file. Again, it’s running in the browser thanks to Pyodide. Here we can use IfcOpenShell using it’s Python interface to create a simple wall. Each line is commented, but essentially we create all the basic elements an IFC model needs, including a Project, Site, Building, Storey, etc. We add a wall to it and at the very end return the model variable, which in this case is an IFC file.
Going back to the HTML file, more precisely in the javascript code, we grab the model that was returned and generate mesh geometry we can load into Three.js. That’s basically what the rest of the code does.
So, what do you think? Could we build our own open-source browser-based BIM modeller using this technology? In a future tutorial, we will dive even deeper into an example of interactively drawing IFC walls in the browser.