Skip to main content

ClassificationsTree

Source

Copying and pasting? We've got you covered! You can find the full source code of this tutorial here.

Displaying elements grouping 📦


One of the greatest things we can make using BIM models is to group elements based on their properties. This has many use cases! Like grouping elements to check their collisions 💥, grouping elements based on their construction activities 🔨, or grouping fininshed elements during the construction phase ✅. Other than grouping the elements, the next most important thing is to show them to your user in an easy way... well, here is where it comes the ClassificationsTree functional component!

🖖 Importing our Libraries

In this tutorial, we will import:

  • @thatopen/components to set up the barebone of our app.
  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/ui-obc to add some cool pre-made UI menus for components.
import * as OBC from "@thatopen/components";
import * as BUI from "@thatopen/ui";
// You have to import from "@thatopen/ui-obc"
import * as BUIC from "../..";

📋 Initializing the UI

As always, let's first initialize the UI library. Remember you only have to do it once in your entire app.

BUI.Manager.init();

🌎 Setting up a simple scene


We will start by creating a simple scene with a camera and a renderer. If you don't know how to set up a scene, you can check the Worlds tutorial.

const components = new OBC.Components();

const viewport = document.createElement("bim-viewport");
viewport.name = "viewer";

const worlds = components.get(OBC.Worlds);
const world = worlds.create();

const sceneComponent = new OBC.SimpleScene(components);
sceneComponent.setup();
world.scene = sceneComponent;

const rendererComponent = new OBC.SimpleRenderer(components, viewport);
world.renderer = rendererComponent;

const cameraComponent = new OBC.SimpleCamera(components);
world.camera = cameraComponent;

viewport.addEventListener("resize", () => {
rendererComponent.resize();
cameraComponent.updateAspect();
});

const viewerGrids = components.get(OBC.Grids);
viewerGrids.create(world);

components.init();

Managing your loaded models 🏢


What else can we say? The task is really simple: we need to see a list of the loaded models in the app. Let's get into it!

Setting up the components

First of all, we're going to get the FragmentIfcLoader from an existing components instance:

const ifcLoader = components.get(OBC.IfcLoader);
await ifcLoader.setup();

The step above is super important as none of the existing functional components setup any tool, they just get it as they are! So, if we don't setup the FragmentIfcLoader then the wasm path is not going to be defined and an error will arise 🤓. Just after we have setup the loader, let's then configure the FragmentManager so any time a model is loaded it gets added to some world scene created before:

const fragmentsManager = components.get(OBC.FragmentsManager);

If you want to get the resulted model every time a new model is loaded, you can subscribe to the following event anywhere in your app:

fragmentsManager.onFragmentsLoaded.add((model) => {
if (world.scene) world.scene.three.add(model);
});

Creating the classifications tree

First things first, let's create an instance of the functional component, like this:

const [classificationsTree, updateClassificationsTree] =
BUIC.tables.classificationTree({
components,
classifications: [],
});

Now that we have the classifications tree created, let's tell the FragmentsManager that each time a model is loaded it needs to classify the model based on some conditions, but more importantly is that right after those classifications are made it needs to update the classifications tree!

const classifier = components.get(OBC.Classifier);

fragmentsManager.onFragmentsLoaded.add(async (model) => {
// This creates a classification system named "entities"
classifier.byEntity(model);

// This creates a classification system named "predefinedTypes"
await classifier.byPredefinedType(model);

// This classifications in the state of the classifications tree.
// Is an array with the classification systems to be shown.
// You can pass the system name directly, or an object with system and label keys.
// The system key is the name in the classifier, and the label is how you want it to be shown in the table.
const classifications = [
{ system: "entities", label: "Entities" },
{ system: "predefinedTypes", label: "Predefined Types" },
];

updateClassificationsTree({ classifications });
});

The classifications value is just an array of the classification systems from the Classifier that you want to display in the user interface, where system is the name in classifier.list and label is the name you want to use to display in the UI. Needless to say, the classifications need to be computed before they can be used on the tree. Great! As we already told the UI when it needs to update, let's add the classifications tree to the HTML page. For it, let's create simple BIM panel component where we include the tree and also a pre-made IFC load button 👇

const panel = BUI.Component.create(() => {
const [loadIfcBtn] = BUIC.buttons.loadIfc({ components });

return BUI.html`
<bim-panel label="Classifications Tree">
<bim-panel-section label="Importing">
${loadIfcBtn}
</bim-panel-section>
<bim-panel-section label="Classifications">
${classificationsTree}
</bim-panel-section>
</bim-panel>
`;
});

Finally, let's append the BIM Panel to the page to see the classifications tree working 😉

const app = document.createElement("bim-grid");
app.layouts = {
main: {
template: `
"panel viewport"
/ 23rem 1fr
`,
elements: { panel, viewport },
},
};

app.layout = "main";
document.body.append(app);

Congratulations! You've now a ready to go user interface that let's you show your model classifications. 🥳