Skip to main content

πŸ‘“ Hiding

Introduction#

πŸ‘©β€πŸ« With what we have seen in other tutorials we already know how to select elements in 3D, access their properties and highlight them using subsets, which are parts of the whole model.

πŸšͺ🧱 However, in many BIM applications it is also possible to hide and isolate elements. A common use case is to hide all elements that do not comply with a certain filter or show only those objects belonging to a floor of the building.

🍰 Of course, this is a piece of cake using IFC.js. In this example we are going to create filters by category, so that the user can show or hide items using checkboxes.

As with the other tutorials, you can find the full example here.

How to do it#

Get subsets of categories#

πŸ‘©β€πŸ« Visibility in IFC.js is based on subset operations. This allows complex visualisations to be created with minimal memory usage. The first thing we are going to do is to define which IFC categories we are going to allow the user to show or hide.

πŸ“‹πŸ“‹πŸ“‹ To save memory, categories in IFC.js are defined as numeric constants. So let's create an object that maps the name of those constants to their numeric value, and a function to retrieve them:

import { IFCWALLSTANDARDCASE, IFCSLAB, IFCDOOR, IFCWINDOW, IFCFURNISHINGELEMENT, IFCMEMBER, IFCPLATE } from "web-ifc";
// List of categories names
const categories = {
IFCWALLSTANDARDCASE,
IFCSLAB,
IFCFURNISHINGELEMENT,
IFCDOOR,
IFCWINDOW,
IFCPLATE,
IFCMEMBER,
};
// Gets the name of a category
function getName(category) {
const names = Object.keys(categories);
return names.find((name) => categories[name] === category);
}

🏠⏩πŸšͺ Now let's create a couple of functions to get all the IDs of the elements belonging to a category and create a subset with those IDs.

πŸ‘©β€πŸ« You can also use removeFromSubset() to remove a single item from a subset (e.g. hide a single item). If you combine that with createSubset() with removePrevious = false, you'll have full control of what is added to which subset and its visibility.

// Gets the IDs of all the items of a specific category
async function getAll(category) {
const manager = ifcLoader.ifcManager;
return manager.getAllItemsOfType(0, category, false);
}
// Creates a new subset containing all elements of a category
async function newSubsetOfType(category) {
const ids = await getAll(category);
return ifcLoader.ifcManager.createSubset({
modelID: 0,
scene,
ids,
removePrevious: true,
customID: category.toString(),
});
}

Set up GUI#

πŸ’»πŸ§‘ We will now create a simple GUI to allow the user to control which categories are visible or invisible. We are going to create a checkbox for each category of the BIM model we are working with.

πŸ’… This can easily be done with a little HTML and CSS:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="../../../favicon.ico" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="styles.css" />
<title>IFC.js</title>
</head>
<body>
<canvas id="three-canvas"></canvas>
<div class="checkboxes">
<div>
<input checked="true" id="IFCWALLSTANDARDCASE" type="checkbox" />
Walls
</div>
<div>
<input checked="true" id="IFCSLAB" type="checkbox" />
Slabs
</div>
<div>
<input checked="true" id="IFCWINDOW" type="checkbox" />
Windows
</div>
<div>
<input checked="true" id="IFCFURNISHINGELEMENT" type="checkbox" />
Furniture
</div>
<div>
<input checked="true" id="IFCDOOR" type="checkbox" />
Doors
</div>
<div>
<input checked="true" id="IFCMEMBER" type="checkbox" />
Curtain wall structure
</div>
<div>
<input checked="true" id="IFCPLATE" type="checkbox" />
Curtain wall plates
</div>
</div>
<script src="bundle.js"></script>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
overflow: hidden;
}
#three-canvas {
position: fixed;
top: 0;
left: 0;
outline: none;
}
#file-input {
z-index: 1;
position: absolute;
}
.checkboxes {
position: absolute;
left: 1rem;
top: 1rem;
}

Bind GUI with visibility#

✨ Now it only remains to link the click event of each checkbox with the visibility of the corresponding category, so that when the checkbox is activated the category becomes visible and when it is deactivated it disappears.

πŸŽ¨πŸ‘¨β€πŸŽ¨ Here it is worth noting that to make the code more concise we have given each checkbox an ID with the same name as the category it links to.

// Stores the created subsets
const subsets = {};
async function setupAllCategories() {
const allCategories = Object.values(categories);
for (let i = 0; i < allCategories.length; i++) {
const category = allCategories[i];
await setupCategory(category);
}
}
// Creates a new subset and configures the checkbox
async function setupCategory(category) {
subsets[category] = await newSubsetOfType(category);
setupCheckBox(category);
}
// Sets up the checkbox event to hide / show elements
function setupCheckBox(category) {
const name = getName(category);
const checkBox = document.getElementById(name);
checkBox.addEventListener("change", (event) => {
const checked = event.target.checked;
const subset = subsets[category];
if (checked) scene.add(subset);
else subset.removeFromParent();
});
}

😎 And here is the result:

Next steps#

πŸŽ‰πŸŽ‰πŸŽ‰ Congratulations! Now you know how to control the visibility of elements using any filter.

πŸ‘©β€πŸ« However, web-ifc-three has even more functionality. For example, what if we want to open and close the viewer? Let's take a look at it next.