From 00be31ddd2a62876570305d68f7a6996c25d2ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Gonz=C3=A1lez=20Viegas?= Date: Thu, 6 Jun 2024 17:47:25 +0200 Subject: [PATCH] chore: update build --- build/404.html | 4 ++-- build/Tutorials/Components/Core/BoundingBoxer/index.html | 4 ++-- build/Tutorials/Components/Core/Classifier/index.html | 4 ++-- build/Tutorials/Components/Core/Clipper/index.html | 4 ++-- build/Tutorials/Components/Core/Cullers/index.html | 4 ++-- build/Tutorials/Components/Core/Exploder/index.html | 4 ++-- build/Tutorials/Components/Core/FragmentsManager/index.html | 4 ++-- build/Tutorials/Components/Core/Grids/index.html | 4 ++-- build/Tutorials/Components/Core/Hider/index.html | 4 ++-- build/Tutorials/Components/Core/IfcGeometryTiler/index.html | 4 ++-- build/Tutorials/Components/Core/IfcJsonExporter/index.html | 4 ++-- build/Tutorials/Components/Core/IfcLoader/index.html | 4 ++-- .../Tutorials/Components/Core/IfcPropertiesTiler/index.html | 4 ++-- .../Components/Core/IfcRelationsIndexer/index.html | 6 +++--- build/Tutorials/Components/Core/MiniMap/index.html | 4 ++-- .../Components/Core/OrthoPerspectiveCamera/index.html | 4 ++-- build/Tutorials/Components/Core/Raycasters/index.html | 4 ++-- build/Tutorials/Components/Core/Utils/index.html | 4 ++-- build/Tutorials/Components/Core/Worlds/index.html | 4 ++-- .../Tutorials/Components/Front/AngleMeasurement/index.html | 4 ++-- build/Tutorials/Components/Front/AreaMeasurement/index.html | 4 ++-- .../Tutorials/Components/Front/Civil3DNavigator/index.html | 4 ++-- .../Components/Front/CivilCrossSectionNavigator/index.html | 4 ++-- .../Components/Front/CivilElevationNavigator/index.html | 4 ++-- .../Components/Front/CivilPlanNavigator/index.html | 4 ++-- build/Tutorials/Components/Front/EdgeMeasurement/index.html | 4 ++-- build/Tutorials/Components/Front/EdgesClipper/index.html | 4 ++-- build/Tutorials/Components/Front/FaceMeasurement/index.html | 4 ++-- build/Tutorials/Components/Front/Highlighter/index.html | 4 ++-- build/Tutorials/Components/Front/IfcStreamer/index.html | 4 ++-- .../Tutorials/Components/Front/LengthMeasurement/index.html | 4 ++-- build/Tutorials/Components/Front/Marker/index.html | 4 ++-- build/Tutorials/Components/Front/Plans/index.html | 4 ++-- .../Components/Front/PostproductionRenderer/index.html | 4 ++-- build/Tutorials/Components/Front/ShadowDropper/index.html | 4 ++-- .../Tutorials/Components/Front/VolumeMeasurement/index.html | 4 ++-- build/Tutorials/Components/index.html | 4 ++-- build/Tutorials/UserInterface/Core/Component/index.html | 4 ++-- .../UserInterface/OBC/ClassificationsTree/index.html | 4 ++-- .../UserInterface/OBC/ElementProperties/index.html | 4 ++-- .../Tutorials/UserInterface/OBC/EntityAttributes/index.html | 4 ++-- build/Tutorials/UserInterface/OBC/ModelsList/index.html | 4 ++-- build/Tutorials/UserInterface/index.html | 4 ++-- build/api/classes/thatopen_components.AsyncEvent/index.html | 4 ++-- build/api/classes/thatopen_components.Base/index.html | 4 ++-- .../classes/thatopen_components.BaseWorldItem/index.html | 4 ++-- .../classes/thatopen_components.BoundingBoxer/index.html | 4 ++-- build/api/classes/thatopen_components.Clipper/index.html | 4 ++-- build/api/classes/thatopen_components.Component/index.html | 4 ++-- build/api/classes/thatopen_components.Components/index.html | 4 ++-- .../classes/thatopen_components.CullerRenderer/index.html | 4 ++-- build/api/classes/thatopen_components.Cullers/index.html | 4 ++-- build/api/classes/thatopen_components.Disposer/index.html | 4 ++-- build/api/classes/thatopen_components.Event/index.html | 4 ++-- .../classes/thatopen_components.FirstPersonMode/index.html | 4 ++-- .../classes/thatopen_components.FragmentsManager/index.html | 4 ++-- .../classes/thatopen_components.IfcJsonExporter/index.html | 4 ++-- .../thatopen_components.IfcRelationsIndexer/index.html | 4 ++-- .../thatopen_components.IfcStreamingSettings/index.html | 4 ++-- .../thatopen_components.MeshCullerRenderer/index.html | 4 ++-- build/api/classes/thatopen_components.OrbitMode/index.html | 4 ++-- .../thatopen_components.OrthoPerspectiveCamera/index.html | 4 ++-- build/api/classes/thatopen_components.PlanMode/index.html | 4 ++-- .../thatopen_components.ProjectionManager/index.html | 4 ++-- .../index.html | 4 ++-- .../api/classes/thatopen_components.SimpleCamera/index.html | 4 ++-- .../api/classes/thatopen_components.SimplePlane/index.html | 4 ++-- .../classes/thatopen_components.SimpleRenderer/index.html | 4 ++-- .../api/classes/thatopen_components.SimpleScene/index.html | 4 ++-- .../classes/thatopen_components_front.ClipEdges/index.html | 4 ++-- .../classes/thatopen_components_front.EdgesPlane/index.html | 4 ++-- .../thatopen_components_front.LengthMeasurement/index.html | 4 ++-- .../api/classes/thatopen_components_front.Marker/index.html | 4 ++-- .../api/classes/thatopen_components_front.Plans/index.html | 4 ++-- .../index.html | 4 ++-- .../thatopen_components_front.RendererWith2D/index.html | 4 ++-- build/api/classes/thatopen_fragments.Serializer/index.html | 4 ++-- build/api/classes/thatopen_ui.Button/index.html | 4 ++-- build/api/classes/thatopen_ui.Checkbox/index.html | 4 ++-- build/api/classes/thatopen_ui.ColorInput/index.html | 4 ++-- build/api/classes/thatopen_ui.Component/index.html | 4 ++-- build/api/classes/thatopen_ui.ContextMenu/index.html | 4 ++-- build/api/classes/thatopen_ui.Dropdown/index.html | 4 ++-- build/api/classes/thatopen_ui.Grid/index.html | 4 ++-- build/api/classes/thatopen_ui.Icon/index.html | 4 ++-- build/api/classes/thatopen_ui.Input/index.html | 4 ++-- build/api/classes/thatopen_ui.Label/index.html | 4 ++-- build/api/classes/thatopen_ui.NumberInput/index.html | 4 ++-- build/api/classes/thatopen_ui.Option/index.html | 4 ++-- build/api/classes/thatopen_ui.Panel/index.html | 4 ++-- build/api/classes/thatopen_ui.PanelSection/index.html | 4 ++-- build/api/classes/thatopen_ui.Selector/index.html | 4 ++-- build/api/classes/thatopen_ui.Tab/index.html | 4 ++-- build/api/classes/thatopen_ui.Table/index.html | 4 ++-- build/api/classes/thatopen_ui.Tabs/index.html | 4 ++-- build/api/classes/thatopen_ui.TextInput/index.html | 4 ++-- build/api/classes/thatopen_ui.Toolbar/index.html | 4 ++-- build/api/classes/thatopen_ui.ToolbarGroup/index.html | 4 ++-- build/api/classes/thatopen_ui.ToolbarSection/index.html | 4 ++-- build/api/classes/thatopen_ui.Viewport/index.html | 4 ++-- build/api/classes/thatopen_ui_obc.Manager/index.html | 4 ++-- build/api/classes/thatopen_ui_obc.ViewCube/index.html | 4 ++-- build/api/classes/thatopen_ui_obc.World2D/index.html | 4 ++-- build/api/index.html | 4 ++-- .../interfaces/thatopen_components.BVHGeometry/index.html | 4 ++-- .../thatopen_components.CameraControllable/index.html | 4 ++-- .../interfaces/thatopen_components.Configurable/index.html | 4 ++-- .../interfaces/thatopen_components.Createable/index.html | 4 ++-- .../interfaces/thatopen_components.Disposable/index.html | 4 ++-- .../api/interfaces/thatopen_components.Hideable/index.html | 4 ++-- .../thatopen_components.NavigationMode/index.html | 4 ++-- .../api/interfaces/thatopen_components.Progress/index.html | 4 ++-- .../interfaces/thatopen_components.Resizeable/index.html | 4 ++-- .../interfaces/thatopen_components.Updateable/index.html | 4 ++-- build/api/interfaces/thatopen_ui.ColumnData/index.html | 4 ++-- build/api/interfaces/thatopen_ui.EntryQuery/index.html | 4 ++-- build/api/interfaces/thatopen_ui.HasName/index.html | 4 ++-- build/api/interfaces/thatopen_ui.HasValue/index.html | 4 ++-- build/api/interfaces/thatopen_ui.QueryGroup/index.html | 4 ++-- build/api/modules/index.html | 4 ++-- build/api/modules/thatopen_components/index.html | 4 ++-- build/api/modules/thatopen_components_front/index.html | 4 ++-- build/api/modules/thatopen_fragments/index.html | 4 ++-- build/api/modules/thatopen_ui/index.html | 4 ++-- build/api/modules/thatopen_ui_obc/index.html | 4 ++-- .../js/{308c405f.2e284b33.js => 308c405f.91e68427.js} | 2 +- .../{runtime~main.45106ad8.js => runtime~main.28990e4f.js} | 2 +- build/components/clean-components-guide/index.html | 4 ++-- build/components/creating-components/index.html | 4 ++-- build/components/getting-started/index.html | 4 ++-- build/components/tutorial-paths/index.html | 4 ++-- build/contributing/index.html | 4 ++-- build/index.html | 4 ++-- build/intro/index.html | 4 ++-- docs/Tutorials/Components/Core/IfcRelationsIndexer.mdx | 2 +- 135 files changed, 268 insertions(+), 268 deletions(-) rename build/assets/js/{308c405f.2e284b33.js => 308c405f.91e68427.js} (73%) rename build/assets/js/{runtime~main.45106ad8.js => runtime~main.28990e4f.js} (99%) diff --git a/build/404.html b/build/404.html index cda400f6..e1361473 100644 --- a/build/404.html +++ b/build/404.html @@ -4,13 +4,13 @@ Page Not Found | That Open Docs - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/BoundingBoxer/index.html b/build/Tutorials/Components/Core/BoundingBoxer/index.html index 74ab264c..669d9adc 100644 --- a/build/Tutorials/Components/Core/BoundingBoxer/index.html +++ b/build/Tutorials/Components/Core/BoundingBoxer/index.html @@ -4,7 +4,7 @@ BoundingBoxer | That Open Docs - + @@ -12,7 +12,7 @@
Skip to main content

BoundingBoxer

Source

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

🧊 Playing with boxes


In this tutorial, you'll learn to easily create the bounding boxes of a BIM model. This can be useful for knowing the overall position and dimension of your models, which can be used, for instance, to make the camera fit a whole BIM model in the screen.

Bounding boxes?

Bounding boxes (AABB or Axis-Aligned Bounding Boxes) are the boxes aligned with the X, Y and Z axes of a 3D model that contain one or many objects. They are very common in 3D applications to make fast computations that require to know the whole dimension or position of one or many objects.

In this tutorial, we will import:

  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
  • @thatopen/ui to add some simple and cool UI menus.
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = components.get(OBC.FragmentsManager);
const file = await fetch(
"https://thatopen.github.io/engine_components/resources/small.frag",
);
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

🎲 Creation of Bounding Boxes


Now that our setup is done, lets see how you can create the bounding boxes of the model. BIM models are complex, but don't worry: creating the bounding boxes is a piece of cake thanks to the BoundingBoxer.💪 We can add models to the computation of the bounding box simply by using the add() method.

const fragmentBbox = components.get(OBC.BoundingBoxer);
fragmentBbox.add(model);

👓 Reading the Bounding Box data

After adding the model, we can now read the mesh from bounding box using getMesh() method.

Don't forget to clean up after using it! 🧹

It's a good practice to reset the bounding box after using it with the reset() method. Otherwise, if you add more models or meshes to the bounding boxer, the bounding box will compute a bounding box that includes everything (including the previously added models).

const bbox = fragmentBbox.getMesh();
fragmentBbox.reset();

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will create a new panel with an input to make the camera fit the model to the screen. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Bounding Boxes Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-button
label="Fit BIM model"
@click="${() => {
world.camera.controls.fitToSphere(bbox, true);
}}">
</bim-button>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created the bounding box of a BIM model and used it to make the camera fit the model to the screen. This also works with many models!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/Classifier/index.html b/build/Tutorials/Components/Core/Classifier/index.html index 4d093866..5116de71 100644 --- a/build/Tutorials/Components/Core/Classifier/index.html +++ b/build/Tutorials/Components/Core/Classifier/index.html @@ -4,13 +4,13 @@ Classifier | That Open Docs - +
Skip to main content

Classifier

Source

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

🔴🔵 Classifying your BIM models


In this tutorial, you'll learn how to classify your BIM models by different criterias, how to get the list of items that belong to a specific category and how to change their color.

Why classifications?

Classifications are a powerful way to organize your BIM models. They allow you to group them according to different parameters. For example: getting all the walls, or all the items that belong to a specific floor or room.

In this tutorial, we will import:

  • Three.js to get some 3D entities for our app.
  • web-ifc to get some IFC items.
  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as THREE from "three";
import * as BUI from "@thatopen/ui";
import * as WEBIFC from "web-ifc";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;
const components = new OBC.Components();
const worlds = components.get(OBC.Worlds);

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = new OBC.FragmentsManager(components);
const file = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

🗃️ Classifiying the BIM model


Next, we will set up a classifier that will help us identify the objects in the scene by their classification (e.g. their spatial structure or their category). Although you can instantiate the classifier by hand, we will use components.get() to get the classifier. All components are meant to be singletons by Components instance, and this method will make sure that this is the case.

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

Now we can classify the BIM model. The classifier includes 3 methods:

  • byEntity: classifies the model by IFC category.
  • byIfcrel: classifies the model by an indirect relationship. In this case, we'll classify the model by its spatial structure (project, site, storey an space).
  • byModel: classifies the model by model. This might seem redundant, but it's useful if you have multiple BIM models in the same scene and want to quickly select all the objects of one of them.
classifier.byEntity(model);
classifier.byIfcRel(model, WEBIFC.IFCRELCONTAINEDINSPATIALSTRUCTURE, "storeys");
classifier.byModel(model.uuid, model);

Now, to get the fragments set that belong to a certain classification, we can use the find() method. This method allows us to pass an object with filters. For example, to get all items of category "IFCWALLSTANDARDCASE", we can do:

const walls = classifier.find({
entities: ["IFCWALLSTANDARDCASE"],
});

Now, let's do that some more times. We'll gather some objects by category to later control its color from a fancy UI that we will build:

const slabs = classifier.find({
entities: ["IFCSLAB"],
});

const curtainWalls = classifier.find({
entities: ["IFCMEMBER", "IFCPLATE"],
});

const furniture = classifier.find({
entities: ["IFCFURNISHINGELEMENT"],
});

const doors = classifier.find({
entities: ["IFCDOOR"],
});

const all = classifier.find({
models: [model.uuid],
});

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to control the color of the classified elements fetched above. We'll also add a button to reset the color of all items to the original state. For more information about the UI library, you can check the specific documentation for it!

const color = new THREE.Color();

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Classifier Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-color-input
label="Walls Color" color="#202932"
@input="${({ target }: { target: BUI.ColorInput }) => {
color.set(target.color);
classifier.setColor(walls, color);
}}">
</bim-color-input>

<bim-color-input
label="Slabs Color" color="#202932"
@input="${({ target }: { target: BUI.ColorInput }) => {
color.set(target.color);
classifier.setColor(slabs, color);
}}">
</bim-color-input>

<bim-color-input
label="Curtain walls Color" color="#202932"
@input="${({ target }: { target: BUI.ColorInput }) => {
color.set(target.color);
classifier.setColor(curtainWalls, color);
}}">
</bim-color-input>

<bim-color-input
label="Furniture Color" color="#202932"
@input="${({ target }: { target: BUI.ColorInput }) => {
color.set(target.color);
classifier.setColor(furniture, color);
}}">
</bim-color-input>

<bim-color-input
label="Doors Color" color="#202932"
@input="${({ target }: { target: BUI.ColorInput }) => {
color.set(target.color);
classifier.setColor(doors, color);
}}">
</bim-color-input>

<bim-button
label="Reset walls color"
@click="${() => {
classifier.resetColor(all);
}}">
</bim-button>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have classified the items of a BIM model by IFC Category, by spatial structure and by model. You can now use the classifier to quickly access the items of one or many BIM models by specific filters.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/Clipper/index.html b/build/Tutorials/Components/Core/Clipper/index.html index d642528f..8f68ba79 100644 --- a/build/Tutorials/Components/Core/Clipper/index.html +++ b/build/Tutorials/Components/Core/Clipper/index.html @@ -4,14 +4,14 @@ Clipper | That Open Docs - +
Skip to main content

Clipper

Source

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

✂️ Cutting our scene with planes


The Clipping Tool is a powerful feature in 3D modelling that helps you dissect 3D objects. Clipping Tool is useful for inspecting and analyzing objects in detail.💪

Clipping?

Clipping is the process of "cutting" a 3D object by creating a plane. That way, we can have a bird view of the inside of a BIM model.

In this tutorial, we will import:

  • Three.js to get some 3D entities for our app.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/ui to add some simple and cool UI menus.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

const worlds = components.get(OBC.Worlds);
const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(10, 10, 10, 0, 0, 0);

world.scene.setup();

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🎲 Creating a Cube Mesh


Let's start by adding a Cube, which we can dissect. We will create a Cube with 3x3x3 dimensions and use purple color for the material.

const cubeGeometry = new THREE.BoxGeometry(3, 3, 3);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0, 1.5, 0);
world.scene.three.add(cube);
world.meshes.add(cube);

⚡ Initializing the Raycaster


We also need to initialize the Raycaster for this world so that the position of the mouse is tracked from the very first moment we use the clipping planes.

const casters = components.get(OBC.Raycasters);
casters.get(world);

⚙️ Adding the Clipper


Here comes the interesting part, we will add a Simple Clipper to our scene. You can instantiate it, but it's always better to use the components.get(OBC.Clipper) method to get it. All components are meant to be used as a singleton per components instance, and using this system to get a component makes sure this happens.

const clipper = components.get(OBC.Clipper);
Controllign the plane

Each plane generated by the clipper can be controlled using the built-in 3D arrows. You can control the activation and visibility of each plane using plane.enabled and plane.visible. To control the activation and visibility of all planes, use clipper.enabled and clipper.visible.

clipper.enabled = true;

🤝 Performing Clipping Events


Now, we want a way to create a clipping plane on demand. You can do it with a Single Click or Double Click of a mouse. For this tutorial, we will use Double Click. This will cast a ray from the mouse position to the scene and check if the ray intersects with any of the 3D objects. If it does, it will create a new clipping plane in the point of intersection.

container.ondblclick = () => clipper.create(world);

We use the Raycaster to determine if the intersection has occurred. The clipper places a plane after detecting the face on which the mouse was clicked. 😎 :::

🧹 Deleting the Clipping Planes


Now that we know how to make multiple clipping planes, we must also know how to delete them when necessary. Clipping planes can be removed using clipper.delete(world) (which will pick the first plane under the mouse using the raycaster in the specified world) or clipper.delete(world, plane) (which will delete a specific clipping plane).

window.onkeydown = (event) => {
if (event.code === "Delete" || event.code === "Backspace") {
clipper.delete(world);
}
};
Delete all the Clipping Planes

❎ If you want to safely delete all the clipping planes that were created you can simply call clipper.deleteAll().

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will create some UI elements and bind them to some of the controls of the clipper, like activation, visibility, size, color, etc. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel label="Clipper Tutorial" class="options-menu">
<bim-panel-section collapsed label="Commands">

<bim-label>Double click: Create clipping plane</bim-label>
<bim-label>Delete key: Delete clipping plane</bim-label>


</bim-panel-section>
<bim-panel-section collapsed label="Others"">

<bim-checkbox label="Clipper enabled" checked
@change="${({ target }: { target: BUI.Checkbox }) => {
clipper.enabled = target.value;
}}">
</bim-checkbox>

<bim-checkbox label="Clipper visible" checked
@change="${({ target }: { target: BUI.Checkbox }) => {
clipper.visible = target.value;
}}">
</bim-checkbox>

<bim-color-input
label="Planes Color" color="#202932"
@input="${({ target }: { target: BUI.ColorInput }) => {
clipper.material.color.set(target.color);
}}">
</bim-color-input>

<bim-number-input
slider step="0.01" label="Planes opacity" value="0.2" min="0.1" max="1"
@change="${({ target }: { target: BUI.NumberInput }) => {
clipper.material.opacity = target.value;
}}">
</bim-number-input>

<bim-number-input
slider step="0.1" label="Planes size" value="5" min="2" max="10"
@change="${({ target }: { target: BUI.NumberInput }) => {
clipper.size = target.value;
}}">
</bim-number-input>

<bim-button
label="Delete all"
@click="${() => {
clipper.deleteAll();
}}">
</bim-button>

<bim-button
label="Rotate cube"
@click="${() => {
cube.rotation.x = 2 * Math.PI * Math.random();
cube.rotation.y = 2 * Math.PI * Math.random();
cube.rotation.z = 2 * Math.PI * Math.random();
}}">
</bim-button>


</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created your first clipping planes to cut your 3D models. You can now play with the inputs to see how the planes change and adapt them to the look of your app! If you liked planes, don't forget to check out the Edges Planes tutorial, who includes styles, edges and fills and much more.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/Cullers/index.html b/build/Tutorials/Components/Core/Cullers/index.html index 3b3a66a1..f3a186f6 100644 --- a/build/Tutorials/Components/Core/Cullers/index.html +++ b/build/Tutorials/Components/Core/Cullers/index.html @@ -4,7 +4,7 @@ Cullers | That Open Docs - + @@ -12,7 +12,7 @@
Skip to main content

Cullers

Source

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

🚅 Managing Performance


There are occasions when your scene has too many objects. Multiple objects being rendered simultaneously lengthens computation time⌛️ and degrades performance.🌡️ In this tutorial, we will use ScreenCuller to improve performance by reducing unnecessary computations.🚀

What's "culling"?

Culling is a process where we hide some objects of the scene. In this case, we'll hide objects that are not visible, either because they are outside of the scope of the camera, or because there are other objects in front of them, hiding them from the camera. The goal is simple: only compute the objects visible by the camera. This is great in BIM models, because we generally don't want to see ALL the objects at the same time.

In this tutorial, we will import:

  • Three.js to get some 3D entities for our app.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import Stats from "stats.js";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

const worlds = components.get(OBC.Worlds);
const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(13, 13, 13, 0, 0, 0);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧰 Creating Screen Culler


Although adding Screen Culler to your project can appear difficult, it is actually very easy. We just need to get the Cullers component and create a new instance of ScreenCuller. Remember that although you can instance the Cullers component, it's better to get it from the components object, as all the components are meant to be singletons within a Component instance, and this ensures that.

const cullers = components.get(OBC.Cullers);
const culler = cullers.create(world);

You can use the threshold property to control the minimum size of an element in screen in order for it to be revealed by the culler. Higher numbers result in less objects visible, but more performance:

culler.threshold = 200;

Additionally, we will activate the culler.renderDebugFrame so that we can see the 2D screen of the elements that are not occluded. We will get the domElement and attach it to the body so that we can see this frame in real-time. To see it in your app, just comment out the debugFrame.style.visibility = "collapse"; line.

culler.renderDebugFrame = true;
const debugFrame = culler.renderer.domElement;
document.body.appendChild(debugFrame);
debugFrame.style.position = "fixed";
debugFrame.style.left = "0";
debugFrame.style.bottom = "0";
debugFrame.style.visibility = "collapse";

🧱 Adding a ton of cubes

We'll add the Simple 3D Cube and do it 300 times!🤯 We'll generate box geometry and use Lambert material.

const cubes: THREE.Mesh[] = [];
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshLambertMaterial({ color: "#6528D7" });
Randomising the Cubes Placement

We'll write a quick utility function that returns a random number between 0 and the specified upper limit. You can use this for a variety of purposes, but for this tutorial it will be used to generate random positions for cubes that we will add later to our scene.📌

function getRandomNumber(limit: number) {
return Math.random() * limit;
}

Now, using the getRandomNumber() method we previously created, we will add the 300 cube meshes to our scene at random positions. We'll add the cube to the scene and adjust its position between 0 and 10. Additionally, we will add meshes to the culler object, which will help the culler recognize and draw the elements that are visible to the camera, which can be done with the culler's add() method.

function regenerateCubes() {
for (let i = 0; i < 300; i++) {
const cube = new THREE.Mesh(geometry, material);
cube.position.x = getRandomNumber(10);
cube.position.y = getRandomNumber(10);
cube.position.z = getRandomNumber(10);
cube.updateMatrix();
world.scene.three.add(cube);
culler.add(cube);
cubes.push(cube);
}
}

regenerateCubes();

🔄️ Updating the Culler

Here comes the most crucial part! The core aim of ScreenCuller is to output just those components that are visible to the camera.

How often should you update the culler?

It depends on the experience you are looking for. Naturally, the most often you update it, the faster your user will discover new objects that were hidden before, but that also means that your app will be less performant.

In this tutorial we are updating it each time the camera stops moving, which generally works well for most apps.

culler.needsUpdate = true;
world.camera.controls.addEventListener("controlend", () => {
culler.needsUpdate = true;
});

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

Great job! 🎉 Now you know how to optimise your 3D scene using a Screen Culler component! Your BIM app will now have unmatched performance and can render huge scenes easily.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/Exploder/index.html b/build/Tutorials/Components/Core/Exploder/index.html index 6790ff8b..8c8b5aea 100644 --- a/build/Tutorials/Components/Core/Exploder/index.html +++ b/build/Tutorials/Components/Core/Exploder/index.html @@ -4,13 +4,13 @@ Exploder | That Open Docs - +
Skip to main content

Exploder

Source

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

🧨 Exploding BIM models


Sometimes we want to explode our BIM model by level, allowing us to see what's inside like if it was a toy. In this tutorial, we'll learn to do it.

Exploding, like bombs?

Nope! Exploding means offsetting the items of each floor a fixed distance. The effect makes the model look like it's "opened up", like a doll house. This effect is very interesting to have a bird view of both the inside and the outside of a BIM model.

In this tutorial, we will import:

  • web-ifc to get some IFC items.
  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as WEBIFC from "web-ifc";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = new OBC.FragmentsManager(components);
const file = await fetch(
"https://thatopen.github.io/engine_components/resources/small.frag",
);
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

const properties = await fetch("https://thatopen.github.io/engine_components/resources/small.json");
model.setLocalProperties(await properties.json());

🤯 Boom!


Exploding BIM models is very simple with components. The Exploder component does all the heavy lifting for us! We can get it using the get method of the components instance we are using in this app.

const exploder = components.get(OBC.Exploder);

Before being able to use it, we will need to get the classifier to classify the items of the model by storey.

Classify?

If you are not familiar with the classifier, check out its specific tutorial!

const classifier = components.get(OBC.Classifier);
classifier.byIfcRel(model, WEBIFC.IFCRELCONTAINEDINSPATIALSTRUCTURE, "storeys");

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to explode and restore our BIM model, which can be easily done with a checkbox that determines whether a model is exploded or not. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Exploder Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">
<bim-checkbox
label="Explode model"
@change="${({ target }: { target: BUI.Checkbox }) => {
exploder.set(target.value);
}}">
</bim-checkbox>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created an app that can explode and restore a BIM model, allowing to have an overview of both the inside and the outside of a BIM model!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/FragmentsManager/index.html b/build/Tutorials/Components/Core/FragmentsManager/index.html index 655bc62e..46821249 100644 --- a/build/Tutorials/Components/Core/FragmentsManager/index.html +++ b/build/Tutorials/Components/Core/FragmentsManager/index.html @@ -4,13 +4,13 @@ FragmentsManager | That Open Docs - +
Skip to main content

FragmentsManager

Source

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

🚀 Handling BIM models like a boss


In this tutorial, you'll learn how to load your BIM models in Fragment format. Fragment is an open source geometry system that we created on top of Three.js to display BIM models fast, while keeping control over the individual items of the model. The idea is simple: a BIM model is a FragmentsGroup, which is (like the name implies) a collection of fragments. A fragment is a set of identical geometries instantiated around the scene.

How do I get a BIM model in Fragment format?

The IfcLoader component does exactly that! It converts IFC models to Fragments. Check out that tutorial if you are starting out with IFC files. Of course, you can just use the IfcLoader in your app, but loading fragments is more than x10 faster than loading IFC files. Our recommendation is to convert your IFC files to fragments just once, store the fragment somewhere (frontent of backend) and then load the fragments instead of teh IFC models directly.

In this tutorial, we will import:

  • Three.js to get some 3D entities for our app.
  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import Stats from "stats.js";
import * as OBC from "@thatopen/components";
import * as BUI from "@thatopen/ui";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧶 Loading a fragments model


Let's begin by getting the FragmentsManager, which is the component to load, export, get and dispose Fragments in your app.🏭

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

Now we can load a fragment from a file. We will fetch the model data and use the load method of the FragmentsManager to get the fragment object. Then, we'll add it to the scene of the current world. We will also create an UUID of the model to later get it somewhere else.

let uuid = "";

async function loadFragments() {
if (fragments.groups.size) {
return;
}
const file = await fetch(
"https://thatopen.github.io/engine_components/resources/small.frag",
);
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const group = fragments.load(buffer);
world.scene.three.add(group);
uuid = group.uuid;
}

📤 Storing Fragments


Let's see how you can export fragments as a file. First, we'll define a function to download a file:

function download(file: File) {
const link = document.createElement("a");
link.href = URL.createObjectURL(file);
link.download = file.name;
document.body.appendChild(link);
link.click();
link.remove();
}

Fragments Manager can export fragments using the export method. The method takes the UUID of a fragment as an argument and returns a Blob, which can be used to generate a File and then download it using the function defined just before.↗️

function exportFragments() {
if (!fragments.groups.size) {
return;
}
const group = fragments.groups.get(uuid);
if (!group) {
return;
}
const data = fragments.export(group);
const blob = new Blob([data]);
const file = new File([blob], "small.frag");
download(file);
}

🧹 Discard Fragment and Clean the Scene


When your user "closes" one or many BIM models, you'll need to discard that FragmetsGroup. You can dispose a specific FragmentsGroup using the disposeGroup method, or dispose all FragmentsGroups using the dispose method.

function disposeFragments() {
fragments.dispose();
}

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will create a simple panel with a set of buttons that call the previously defined functions. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Fragments Manager Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-button
label="Load fragments"
@click="${() => {
loadFragments();
}}">
</bim-button>

<bim-button
label="Dispose fragments"
@click="${() => {
disposeFragments();
}}">
</bim-button>

<bim-button
label="Export fragments"
@click="${() => {
exportFragments();
}}">
</bim-button>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! Now you know how to load, export and dispose Fragments in your app. Fragments are much faster than raw IFC models, so you should definitely store them in your app if you want your users to have a fast loading experience. For bigger models you can use streaming, but that's another tutorial!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/Grids/index.html b/build/Tutorials/Components/Core/Grids/index.html index c21d2834..4deaefe4 100644 --- a/build/Tutorials/Components/Core/Grids/index.html +++ b/build/Tutorials/Components/Core/Grids/index.html @@ -4,13 +4,13 @@ Grids | That Open Docs - +
Skip to main content

Grids

Source

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

🕸️ Adding a fancy grid to our scene


In this tutorial you'll learn how to add a fancy grid to your scene. It's super easy and will make your app look much more professional!

Why a grid?

Grids are very common in 3D apps, and it's a great way to have a reference point for your users to navigate around, even when there are no visible objects around.

In this tutorial, we will import:

  • Three.js to get some 3D entities for our app.
  • @thatopen/components to set up the barebone of our app.
import * as THREE from "three";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

const worlds = components.get(OBC.Worlds);
const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

const cube = new THREE.Mesh(new THREE.BoxGeometry());
world.scene.three.add(cube);

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🕷️ Adding the grid to the world


To add the grid to the world, we will use the Grids component. Instead of instantiating it, we will get it directly from the components object. Remember that all components are meant to be singletons. Then, we will call the create method to add a grid to the scene.

const grids = components.get(OBC.Grids);
const grid = grids.create(world);
console.log(grid);

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


Congratulations! You have created your first infinite grid in your 3D app. As you can see, it's super easy and it looks great!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/Hider/index.html b/build/Tutorials/Components/Core/Hider/index.html index 57a9c893..48664305 100644 --- a/build/Tutorials/Components/Core/Hider/index.html +++ b/build/Tutorials/Components/Core/Hider/index.html @@ -4,7 +4,7 @@ Hider | That Open Docs - + @@ -15,7 +15,7 @@ can use the loadCached method if you had used it before: it will automatically load all the filters you created in previous sessions, even after closing the browser and opening it again:

const hider = components.get(OBC.Hider);

📕📗📘 Setting up simple filters


Next, we will classify data by category and by level using the Classifier. This will allow us to create a simple filter for both classifications.

const classifier = components.get(OBC.Classifier);
classifier.byEntity(model);
await classifier.bySpatialStructure(model);

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Next, we will create a simple object that we will use as the base for the floors filter. It will just be an object with the name of each storey as key and a boolean (true/false) as value:

const spatialStructures: Record<string, any> = {};
const structureNames = Object.keys(classifier.list.spatialStructures);
for (const name of structureNames) {
spatialStructures[name] = true;
}

Now, let's do the same for categories:

const classes: Record<string, any> = {};
const classNames = Object.keys(classifier.list.entities);
for (const name of classNames) {
classes[name] = true;
}

Now we will add some UI to control the visibility of items per category and per floor using simple checkboxes. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Hider Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-panel-section collapsed name="Floors"">
</bim-panel-section>

<bim-panel-section collapsed name="Categories"">
</bim-panel-section>

</bim-panel>
`;
});

document.body.append(panel);

const floorSection = panel.querySelector(
"bim-panel-section[name='Floors']",
) as BUI.PanelSection;

const categorySection = panel.querySelector(
"bim-panel-section[name='Categories']",
) as BUI.PanelSection;

for (const name in spatialStructures) {
const panel = BUI.Component.create<BUI.Checkbox>(() => {
return BUI.html`
<bim-checkbox checked label="${name}"
@change="${({ target }: { target: BUI.Checkbox }) => {
const found = classifier.find({ spatialStructures: [name] });
hider.set(target.value, found);
}}">
</bim-checkbox>
`;
});
floorSection.append(panel);
}

for (const name in classes) {
const checkbox = BUI.Component.create<BUI.Checkbox>(() => {
return BUI.html`
<bim-checkbox checked label="${name}"
@change="${({ target }: { target: BUI.Checkbox }) => {
const found = classifier.find({ entities: [name] });
hider.set(target.value, found);
}}">
</bim-checkbox>
`;
});
categorySection.append(checkbox);
}

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created an app with an UI that allows the user to control the visibility of items in a BIM model by floor and by category. Well done!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/IfcGeometryTiler/index.html b/build/Tutorials/Components/Core/IfcGeometryTiler/index.html index 6dd07df3..6ad1993a 100644 --- a/build/Tutorials/Components/Core/IfcGeometryTiler/index.html +++ b/build/Tutorials/Components/Core/IfcGeometryTiler/index.html @@ -4,14 +4,14 @@ IfcGeometryTiler | That Open Docs - +
Skip to main content

IfcGeometryTiler

Source

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

🧩 Tiling BIM geometry


Opening big BIM models is hard because of 2 reasons: they have a lot of data and the geometry has to be computed to implicit (e.g. extrusion) to explicit (triangles). Our library allows to tile IFC files, solving both problems. This allows to open quite big IFC models in seconds and consuming minimal resources by just opening the parts of the model that are visible to the user. In this tutorial you'll learn to convert the geometry of an IFC model into tiles.

Tiles?

Tiles are very simple. We just take a bunch of geometries within the IFC file, convert them into triangles and store them in a binary file. These files are then loaded as a stream into the scen as the user moves around and discovers them.

In this tutorial, we will import:

  • web-ifc to get some IFC items.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

This is not compulsory, as the data will come from an .ifc file, not from fragments. But at least we'll see the model whose geometry we will be converting to tiles!

const fragments = new OBC.FragmentsManager(components);
const fragFile = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");
const fragData = await fragFile.arrayBuffer();
const fragBuffer = new Uint8Array(fragData);
const model = fragments.load(fragBuffer);
world.scene.three.add(model);

🔪 Getting the geometry tiler


The way the streaming works is by fetching files based on the visible things in the viewer. Those files contain pieces of geometry information (geometry chunks) that the engine uses in order to create and display the geometry. But, where do we get those files from? Easy! From the IFC conversion to tiles. So the first step is to transform the IFC model into BIM tiles.

note

As you know, IFC files contains two things: geometries and properties. We need to convert both things if we want to take full advantage of streaming! For tiling properties, check out the Property Tiling tutorial.

So, let's start converting the IFC geometry to tiles and getting those files so the streamer can do its job:

const tiler = components.get(OBC.IfcGeometryTiler);

const wasm = {
path: "https://unpkg.com/web-ifc@0.0.53/",
absolute: true,
};

tiler.settings.wasm = wasm;
tiler.settings.minGeometrySize = 20;
tiler.settings.minAssetsSize = 1000;

This component takes IFC files and transform their geometry into tiles.

danger

The converter doesn't give you the files needed to streaming right away, just the data that must be contained in those files. Is your job to create the files! Why? Because then you can have full control over when, where and how to create them.

The first file we need is a JSON which is the entry point of the geometries streaming. That JSON must have the following structure:

interface GeometriesStreaming {
assets: {
id: number;
geometries: {
color: number[];
geometryID: number;
transformation: number[];
}[];
}[];

geometries: {
[id: number]: {
boundingBox: { [id: number]: number };
hasHoles: boolean;
geometryFile: "url-to-geometry-file-in-your-backend";
};
};

globalDataFileId: "url-to-fragments-group-file-in-your-backend";
}

📅 Setting up the events


The second file is actually not just a single file, but X number of files (depends on how big is your model) that contains the required information to generate the geometry while streaming. In order to create the JSON file and get the information with the geometry, these components, emits events that let you get the processed data from the conversion process.

info

Nedless to say, you need to set up your event listeners before triggering the conversion!

Let's start with the first event:

let files: { name: string; bits: (Uint8Array | string)[] }[] = [];
let geometriesData: OBC.StreamedGeometries = {};
let geometryFilesCount = 1;

tiler.onGeometryStreamed.add((geometry) => {
const { buffer, data } = geometry;
const bufferFileName = `small.ifc-processed-geometries-${geometryFilesCount}`;
for (const expressID in data) {
const value = data[expressID];
value.geometryFile = bufferFileName;
geometriesData[expressID] = value;
}
files.push({ name: bufferFileName, bits: [buffer] });
geometryFilesCount++;
});

One of the most important things to keep in mind is that the event we just setup will get fired as many times as per the "chunk" data generated by the converted. Simply put, the event will get fired several times ⏲ and per each time we will produce one file data that is stored in the geometryFiles array. Later on, we will download the geometry files ⏬.

note

As you see, geometriesData is not being stored as a file to be downloaded. The reason is because that is part of the information we need to create the entry JSON file 🚀.

Nice! Let's go with the second event that will give us more information to create the required files:

let assetsData: OBC.StreamedAsset[] = [];

tiler.onAssetStreamed.add((assets) => {
assetsData = [...assetsData, ...assets];
});

This one is easier as the event doesn't produce binary data, but information we need to create the JSON file.

Are you familiar with Fragments?

If you're familiar with That Open Engine (our libraries), you should recall fragments. Fragments are just a fancy word we use to refer to ThreeJS geometry efficiently created from IFC files which are the things you end up see in the viewer... one IFC file is usually composed of many fragments and all of them are grouped in a FragmentsGroup, which is the final processed IFC model.

Why do we remind you about FragmentsGroup? Because streaming also works with them! So yes, when you convert an IFC to tiles, the converter also creates a FragmentsGroup in the background, and that information is extremely important for the streamer in order to display the streamed file as everything gets grouped there. So, there is another event that gives you the FragmentsGroup binary data and we also need to create a file with that information.

tiler.onIfcLoaded.add((groupBuffer) => {
files.push({
name: "small.ifc-processed-global",
bits: [groupBuffer],
});
});
danger

You can name the file whatever you want, but is extremely important you finish the file name with -global!

↘️ Downloading the tiles


Now that we've setup the main listeners, the last thing is to download all the data once the conversion has fininshed. To do so, we can use the progress event:

function downloadFile(name: string, ...bits: (Uint8Array | string)[]) {
const file = new File(bits, name);
const anchor = document.createElement("a");
const url = URL.createObjectURL(file);
anchor.href = url;
anchor.download = file.name;
anchor.click();
URL.revokeObjectURL(url);
}

async function downloadFilesSequentially(
fileList: { name: string; bits: (Uint8Array | string)[] }[],
) {
for (const { name, bits } of fileList) {
downloadFile(name, ...bits);
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
}
}

tiler.onProgress.add((progress) => {
if (progress !== 1) return;
setTimeout(async () => {
const processedData = {
geometries: geometriesData,
assets: assetsData,
globalDataFileId: "small.ifc-processed-global",
};
files.push({
name: "small.ifc-processed.json",
bits: [JSON.stringify(processedData)],
});
await downloadFilesSequentially(files);
assetsData = [];
geometriesData = {};
files = [];
geometryFilesCount = 1;
});
});

🔥 Generating the tiles


Great! Now that we have everything setup, is time to finally convert the IFC file. In order to trigger the conversion, we can just do the following:

async function processFile() {
const fetchedIfc = await fetch("https://thatopen.github.io/engine_components/resources/small.ifc");
const ifcBuffer = await fetchedIfc.arrayBuffer();
// We will need this information later to also convert the properties
const ifcArrayBuffer = new Uint8Array(ifcBuffer);
// This triggers the conversion, so the listeners start to be called
await tiler.streamFromBuffer(ifcArrayBuffer);
}

If everything went as expected, you should now be seeing some files being downloaded from your app 🤯 Do not get scary if they're a lot, as big models tend to have many files! All of that is the information the streaming uses in order to display the geometry in the most efficient way possible 💪

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to generate and download the tiles to our computer. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Geometry tiles Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-button label="Load IFC"
@click="${() => {
processFile();
}}">
</bim-button>

</bim-panel-section>

</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created an app that can generate the geometry BIM tiles for an IFC and download them to your computer. Now you have the power to process big IFC files! Don't forget to check out the IFC property tiler tutorial. To consume these tiles, check out the IFC streamer tutorial.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/IfcJsonExporter/index.html b/build/Tutorials/Components/Core/IfcJsonExporter/index.html index 524ece50..5a5576d5 100644 --- a/build/Tutorials/Components/Core/IfcJsonExporter/index.html +++ b/build/Tutorials/Components/Core/IfcJsonExporter/index.html @@ -4,13 +4,13 @@ IfcJsonExporter | That Open Docs - +
Skip to main content

IfcJsonExporter

Source

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

💱 From IFC to JSON


IFC is great, but it's not always easy to handle for developers. JSON, on the other hand, is one of the most common formats in the programming world for exchanging data easily. In thit tutorial, you'll learn to convert an IFC file to JSON.

Is JSON better?

It's not better or worse, it's just different. In fact, IFC is a schema, not a format, so you can't compare both. What you can compare is the STEP format (what you usually as .ifc file) and JSON. The first format is better if you want to move data among different BIM apps, whereas the second is more convenient to move data programmatically (e.g. from the backend to the frontend of your app).

In this tutorial, we will import:

  • web-ifc to get some IFC items.
  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as WEBIFC from "web-ifc";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

This is not compulsory, as the data will come from an .ifc file, not from fragments. But at least we'll see the model whose properties we will be converting!

const fragments = new OBC.FragmentsManager(components);
const fragFile = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");
const fragData = await fragFile.arrayBuffer();
const fragBuffer = new Uint8Array(fragData);
const model = fragments.load(fragBuffer);
world.scene.three.add(model);

🧰 Getting the necessary tools


To convert IFC to JSON we need 2 things: web-ifc an the JSON exporter. The former is a component of this library, so we'll get it using the get method of the components instance. Next, we will create a new instance of web-ifc, which is the core of our libraries. This library can parse, read, edit and write IFC files very efficiently thanks to WebAssembly (WASM).

const exporter = components.get(OBC.IfcJsonExporter);

const webIfc = new WEBIFC.IfcAPI();
webIfc.SetWasmPath("https://unpkg.com/web-ifc@0.0.53/", true);
await webIfc.Init();

↘️ Loading the IFC data


Now we can load the IFC file data using web-ifc. This can be done in a bunch of lines:

const ifcFile = await fetch("https://thatopen.github.io/engine_components/resources/small.ifc");
const ifcData = await ifcFile.arrayBuffer();
const ifcBuffer = new Uint8Array(ifcData);
const modelID = webIfc.OpenModel(ifcBuffer);

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to export the loaded IFC file to JSON. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="IFC-JSON Exporter Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">
<bim-panel-section style="padding-top: 10px;">
<bim-button
label="Export properties JSON"
@click="${async () => {
const exported = await exporter.export(webIfc, modelID);
const serialized = JSON.stringify(exported);
const file = new File([new Blob([serialized])], "properties.json");
const url = URL.createObjectURL(file);
const link = document.createElement("a");
link.download = "properties.json";
link.href = url;
link.click();
URL.revokeObjectURL(url);
link.remove();
}}">
</bim-button>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created an app that can load IFC files and convert them to JSON! Now you can easily extract data from IFC files and move them around in your systems, regardless of the technology that you use in your stack. For bigger IFC files, exporting all the properties to a single JSON might not be feasible. In those cases, check out the properties tiler tutorial!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/IfcLoader/index.html b/build/Tutorials/Components/Core/IfcLoader/index.html index 040a3f88..3bef4f78 100644 --- a/build/Tutorials/Components/Core/IfcLoader/index.html +++ b/build/Tutorials/Components/Core/IfcLoader/index.html @@ -4,14 +4,14 @@ IfcLoader | That Open Docs - +
Skip to main content

IfcLoader

Source

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

🏢 Loading IFC files


IFC is the most common format to share BIM data openly. Our libraries are able to load, navigate and even create and edit them directly. In this tutorial, you'll learn how to open an IFC model in the 3D scene.

IFC?

If you are not famliar with the construction industry, this might be the first time you come across this term. It stands for Industry Foundation Classes, and it's the most widespread standard for sharing BIM data freely, without depending on specific software manufacturers and their propietary formats.

In this tutorial, we will import:

  • web-ifc to get some IFC items.
  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import * as WEBIFC from "web-ifc";
import * as BUI from "@thatopen/ui";
import Stats from "stats.js";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🚗🏎️ Getting IFC and fragments


When we read an IFC file, we convert it to a geometry called Fragments. Fragments are a lightweight representation of geometry built on top of THREE.js InstancedMesh to make it easy to work with BIM data efficiently. All the BIM geometry you see in our libraries are Fragments, and they are great: they are lightweight, they are fast and we have tons of tools to work with them. But fragments are not used outside our libraries. So how can we convert an IFC file to fragments? Let's check out how:

const fragments = components.get(OBC.FragmentsManager);
const fragmentIfcLoader = components.get(OBC.IfcLoader);
Why not just IFC?

IFC is nice because it lets us exchange data with many tools in the AECO industry. But your graphics card doesn't understand IFC. It only understands one thing: triangles. So we must convert IFC to triangles. There are many ways to do it, some more efficient than others. And that's exactly what Fragments are: a very efficient way to display the triangles coming from IFC files.

Once Fragments have been generated, you can export them and then load them back directly, without needing the original IFC file. Why would you do that? Well, because fragments can load +10 times faster than IFC. And the reason is very simple. When reading an IFC, we must parse the file, read the implicit geometry, convert it to triangles (Fragments) and send it to the GPU. When reading fragments, we just take the triangles and send them, so it's super fast.

How to use Fragments?

If you want to find out more about Fragments, check out the Fragments Manager tutorial.

🔭🔧 Calibrating the converter


Now, we need to configure the path of the WASM files. What's WASM? It's a technology that lets us run C++ on the browser, which means that we can load IFCs super fast! These files are the compilation of our web-ifc library. You can find them in the github repo and in NPM. These files need to be available to our app, so you have 2 options:

  • Download them and serve them statically.
  • Get them from a remote server. The easiest way is getting them from unpkg, and the cool thing is that you don't need to do it manually! It can be done directly by the tool just by writing the following:
await fragmentIfcLoader.setup();

// If you want to the path to unpkg manually, then you can skip the line
// above and set them manually as below:
// fragmentIfcLoader.settings.wasm = {
// path: "https://unpkg.com/web-ifc@0.0.53/",
// absolute: true
// }

Awesome! Optionally, we can exclude categories that we don't want to convert to fragments like very easily:

const excludedCats = [
WEBIFC.IFCTENDONANCHOR,
WEBIFC.IFCREINFORCINGBAR,
WEBIFC.IFCREINFORCINGELEMENT,
];

for (const cat of excludedCats) {
fragmentIfcLoader.settings.excludedCategories.add(cat);
}

We can further configure the conversion using the webIfc object. In this example, we will make the IFC model go to the origin of the scene (don't worry, this supports model federation):

fragmentIfcLoader.settings.webIfc.COORDINATE_TO_ORIGIN = true;

🚗🔥 Loading the IFC


Next, let's define a function to load the IFC programmatically. We have hardcoded the path to one of our IFC files, but feel free to do this with any of your own files!

Opening local IFCs

Keep in mind that the browser can't access the file of your computer directly, so you will need to use the Open File API to open local files.

async function loadIfc() {
const file = await fetch("https://thatopen.github.io/engine_components/resources/small.ifc");
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = await fragmentIfcLoader.load(buffer);
model.name = "example";
world.scene.three.add(model);
}

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:

fragments.onFragmentsLoaded.add((model) => {
console.log(model);
});

🎁 Exporting the result to fragments


Once you have your precious fragments, you might want to save them so that you don't need to open this IFC file each time your user gets into your app. Instead, the next time you can load the fragments directly. Defining a function to export fragments is as easy as this:

function download(file: File) {
const link = document.createElement("a");
link.href = URL.createObjectURL(file);
link.download = file.name;
document.body.appendChild(link);
link.click();
link.remove();
}

async function exportFragments() {
if (!fragments.groups.size) {
return;
}
const group = Array.from(fragments.groups.values())[0];
const data = fragments.export(group);
download(new File([new Blob([data])], "small.frag"));

const properties = group.getLocalProperties();
if (properties) {
download(new File([JSON.stringify(properties)], "small.json"));
}
}

🧠🧼 Cleaning memory


Now, just like in the FragmentManager tutorial, you will need to dispose the memory if your user wants to reset the state of the scene, especially if you are using Single Page Application technologies like React, Angular, Vue, etc. To do that, you can simply call the dispose method:

function disposeFragments() {
fragments.dispose();
}

That's it! Congrats, now you can load IFC files into your app, generate the 3D geometry and property data for them and navigate them in 3D. In other tutorials, you'll find tons of tools to work with them and create amazing BIM apps! See you there. 💪

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to explode and restore our BIM model, which can be easily done with a checkbox that determines whether a model is exploded or not. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="IFC Loader Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">
<bim-panel-section style="padding-top: 12px;">

<bim-button label="Load IFC"
@click="${() => {
loadIfc();
}}">
</bim-button>

<bim-button label="Export fragments"
@click="${() => {
exportFragments();
}}">
</bim-button>

<bim-button label="Dispose fragments"
@click="${() => {
disposeFragments();
}}">
</bim-button>

</bim-panel-section>

</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created an app that can load IFC files, convert them to 3D fragments and navigate them in 3D. Fantastic job! For bigger IFC files, instead of reading them directly every time, you can store the fragments and properties and load them instead of the original IFC. For even bigger files, you can use streaming, which we also cover in other tutorials!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/IfcPropertiesTiler/index.html b/build/Tutorials/Components/Core/IfcPropertiesTiler/index.html index 6e0fdf8d..7fccd1b0 100644 --- a/build/Tutorials/Components/Core/IfcPropertiesTiler/index.html +++ b/build/Tutorials/Components/Core/IfcPropertiesTiler/index.html @@ -4,7 +4,7 @@ IfcPropertiesTiler | That Open Docs - + @@ -14,7 +14,7 @@ scalable/affordable. Using this system, you'll be able to store and retrieve the data of models of any size without big cloud costs. In this tutorial, we will import:

import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

This is not compulsory, as the data will come from an .ifc file, not from fragments. But at least we'll see the model whose properties we will be converting to tiles!

const fragments = new OBC.FragmentsManager(components);
const fragFile = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");
const fragData = await fragFile.arrayBuffer();
const fragBuffer = new Uint8Array(fragData);
const model = fragments.load(fragBuffer);
world.scene.three.add(model);

↘️ Setting up downloads


Now we will define some helper download functions that will allow us to get the property tiles that we generate in our computer.

function downloadFile(name: string, bits: Blob) {
const file = new File([bits], name);
const anchor = document.createElement("a");
const url = URL.createObjectURL(file);
anchor.href = url;
anchor.download = file.name;
anchor.click();
URL.revokeObjectURL(url);
}

async function downloadFilesSequentially(
fileList: { name: string; bits: Blob }[],
) {
for (const { name, bits } of fileList) {
downloadFile(name, bits);
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
}
}

📋 Getting the streamer


Now we will get the property streamer component from the library and initialize it.

const propsStreamer = components.get(OBC.IfcPropertiesTiler);

propsStreamer.settings.wasm = {
path: "https://unpkg.com/web-ifc@0.0.53/",
absolute: true,
};

We need to generate properties JSON with the following structure:

interface StreamedProperties {
types: {
[typeID: number]: number[];
};

ids: {
[id: number]: number;
};

indexesFile: string;
}

So we will define an object where we will store the tiles as we generate them.

const jsonFile: StreamedProperties = {
types: {},
ids: {},
indexesFile: "small.ifc-processed-properties-indexes",
};

📅 Setting up the events


Similarly to geometries, here you will also get data and progress notification using events. In addition to properties, you will get indices, which is an indexation data of the properties to be able to use them effectively when streamed. Let's set up those events now!

let counter = 0;

const files: { name: string; bits: Blob }[] = [];

propsStreamer.onPropertiesStreamed.add(async (props) => {
if (!jsonFile.types[props.type]) {
jsonFile.types[props.type] = [];
}
jsonFile.types[props.type].push(counter);

for (const id in props.data) {
jsonFile.ids[id] = counter;
}

const name = `small.ifc-processed-properties-${counter}`;
const bits = new Blob([JSON.stringify(props.data)]);
files.push({ bits, name });

counter++;
});

propsStreamer.onProgress.add(async (progress) => {
console.log(progress);
});

propsStreamer.onIndicesStreamed.add(async (props) => {
files.push({
name: `small.ifc-processed-properties.json`,
bits: new Blob([JSON.stringify(jsonFile)]),
});

const relations = components.get(OBC.IfcRelationsIndexer);
const serializedRels = relations.serializeRelations(props);

files.push({
name: "small.ifc-processed-properties-indexes",
bits: new Blob([serializedRels]),
});

await downloadFilesSequentially(files);
});

🔥 Generating the tiles


Great! Now that we have everything setup, is time to finally convert the IFC file. In order to trigger the conversion, we can just do the following:

async function processFile() {
const fetchedIfc = await fetch("https://thatopen.github.io/engine_components/resources/small.ifc");
const ifcBuffer = await fetchedIfc.arrayBuffer();
// We will need this information later to also convert the properties
const ifcArrayBuffer = new Uint8Array(ifcBuffer);
// This triggers the conversion, so the listeners start to be called
await propsStreamer.streamFromBuffer(ifcArrayBuffer);
}

If everything went as expected, you should now be seeing some files being downloaded from your app 🤯 Do not get scary if they're a lot, as big models tend to have many files! All of that is the information the streaming uses in order to display the geometry in the most efficient way possible. 💪

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to generate and download the tiles to our computer. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Property Tiles Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-panel-section style="padding-top: 12px;">

<bim-button label="Load IFC"
@click="${() => {
processFile();
}}">
</bim-button>

</bim-panel-section>

</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created an app that can generate the property BIM tiles for an IFC and download them to your computer. Now you have the power to process big IFC files! To consume these tiles, check out the IFC streamer tutorial.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/IfcRelationsIndexer/index.html b/build/Tutorials/Components/Core/IfcRelationsIndexer/index.html index f7cc89bd..4fe2672b 100644 --- a/build/Tutorials/Components/Core/IfcRelationsIndexer/index.html +++ b/build/Tutorials/Components/Core/IfcRelationsIndexer/index.html @@ -4,13 +4,13 @@ IfcRelationsIndexer | That Open Docs - +
-
Skip to main content

IfcRelationsIndexer

Source

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

🔁 Getting relations (easy)


If you're aware of the IFC schema, you should know that all the possible information an entity have is not directly inside its attributes. For example, the property sets, classifications, materials, etc, of a wall (or any other element) are not directly in the wall attributes 🤯 but in other entities which are related to the wall using relations.

Why so much indirection?

Indirection is perfect for an schema like the IFC which aims to store all the building data within a single text file in the easiest way possible. However, is not that easy to work just because you need to find the relations you want to get to the element data you're looking for 😪. Luckily for you, the IfcRelationsIndexer already gives you an easy way to get the entities which are related with your elements thanks to the inverse attributes! 🔥🔥

In this tutorial, we will import:

  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding an IFC model to our scene.

Loading an IFC?

If you are not familiar with IFC loading, check out the IFC Loader tutorial first!

const ifcLoader = components.get(OBC.IfcLoader);
await ifcLoader.setup();
const file = await fetch("https://thatopen.github.io/engine_components/resources/small.ifc");
const buffer = await file.arrayBuffer();
const typedArray = new Uint8Array(buffer);
const model = await ifcLoader.load(typedArray);
world.scene.three.add(model);

📋 Indexing the model


Once the model is loaded in memory, you just need to get an instance of the IfcRelationsIndexer and process the model... it's as easy as that! 😎

const indexer = components.get(OBC.IfcRelationsIndexer);
await indexer.process(model);

The result of that is basically a map where the keys are the expressIDs and the values are other expressIDs related to the first one and grouped by the type of relation. You don't need to worry too much about the details of that, as the usage is pretty straighforward 🔝. The only thing that matters is you've now an easy way to access the entities related to your element 🙂

📄 Getting element psets


One of the most important relations between different entities is the IfcRelDefinesByProperties. That relation links together an arbitrary entity with a set of IfcPropertySet entities that applies properties. Getting them with the indexer once the model is indexed is pretty easy:

const psets = indexer.getEntityRelations(model, 6518, "IsDefinedBy");
if (psets) {
for (const expressID of psets) {
// You can get the pset attributes like this
const pset = await model.getProperties(expressID);
console.log(pset);
// You can get the pset props like this or iterate over pset.HasProperties yourself
await OBC.IfcPropertiesUtils.getPsetProps(
model,
expressID,
async (propExpressID) => {
const prop = await model.getProperties(propExpressID);
console.log(prop);
},
);
}
}
tip

IsDefinedBy is the inverse attribute name in the IFC Schema that holds the relations with property sets 😉

Awesome! really easy right?

↘️ Exporting the indexation


In bigger models, the process to calculate the relations index may take some time. There is no reason to calculate over and over the relations index every time you load a model. If the model hasn't change, their properties shouldn't neither! So, let's download the relations index to load it later.

const downloadJSON = (json: string, name: string) => {
const file = new File([json], name);
const a = document.createElement("a");
a.href = URL.createObjectURL(file);
a.download = file.name;
a.click();
URL.revokeObjectURL(a.href);
};

const json = indexer.serializeModelRelations(model);
console.log(json);
tip

As @thatopen/components can be used in either NodeJS and Browser environments, the logic to generate a JSON file may vary!

Now, in case you've loaded several models and want to get all the computed relations, there is also a handy method to do it.

const allRelationsJSON = indexer.serializeAllRelations();

↗️ Loading back the relations index


What do we gain with having a pre-processed relations index if we can't use it again, right? Well, let's use the downloaded relations index 😎

// Lets first delete the existing model relations
delete indexer.relationMaps[model.uuid];
const relationsIndexFile = await fetch("/resources/small-relations.json");
const relationsIndex = indexer.getRelationsMapFromJSON(
await relationsIndexFile.text(),
);

indexer.setRelationMap(model, relationsIndex);

Great! Now try to get again the property sets and you will see everything working nice and neat. In fact, lets try to get the building storey of one element in the IFC 👇

const buildingStorey = indexer.getEntityRelations(
model,
6518,
"ContainedInStructure",
);

if (buildingStorey && buildingStorey[0]) {
const storey = await model.getProperties(buildingStorey[0]);
console.log(storey);
}
tip

Despite there are some relations that corresponds to only one element (e.g., an element can only have one associated building storey) the getEntityRelations will always return an array. That's the reason we take the first buildingStorey relation despite it will always be the only one.

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI export the relations that we just generated. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="IFC Relations Indexer Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">
<bim-panel-section style="padding-top: 10px;">

<bim-button
label="Download relations"
@click="${async () => {
downloadJSON(allRelationsJSON, "relations-index-all.json");
}}">
</bim-button>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! Now you know how to get an easy way to get the relations of your model. Keep going with more tutorials! 💪

- +
Skip to main content

IfcRelationsIndexer

Source

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

🔁 Getting relations (easy)


If you're aware of the IFC schema, you should know that all the possible information an entity have is not directly inside its attributes. For example, the property sets, classifications, materials, etc, of a wall (or any other element) are not directly in the wall attributes 🤯 but in other entities which are related to the wall using relations.

Why so much indirection?

Indirection is perfect for an schema like the IFC which aims to store all the building data within a single text file in the easiest way possible. However, is not that easy to work just because you need to find the relations you want to get to the element data you're looking for 😪. Luckily for you, the IfcRelationsIndexer already gives you an easy way to get the entities which are related with your elements thanks to the inverse attributes! 🔥🔥

In this tutorial, we will import:

  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding an IFC model to our scene.

Loading an IFC?

If you are not familiar with IFC loading, check out the IFC Loader tutorial first!

const ifcLoader = components.get(OBC.IfcLoader);
await ifcLoader.setup();
const file = await fetch("https://thatopen.github.io/engine_components/resources/small.ifc");
const buffer = await file.arrayBuffer();
const typedArray = new Uint8Array(buffer);
const model = await ifcLoader.load(typedArray);
world.scene.three.add(model);

📋 Indexing the model


Once the model is loaded in memory, you just need to get an instance of the IfcRelationsIndexer and process the model... it's as easy as that! 😎

const indexer = components.get(OBC.IfcRelationsIndexer);
await indexer.process(model);

The result of that is basically a map where the keys are the expressIDs and the values are other expressIDs related to the first one and grouped by the type of relation. You don't need to worry too much about the details of that, as the usage is pretty straighforward 🔝. The only thing that matters is you've now an easy way to access the entities related to your element 🙂

📄 Getting element psets


One of the most important relations between different entities is the IfcRelDefinesByProperties. That relation links together an arbitrary entity with a set of IfcPropertySet entities that applies properties. Getting them with the indexer once the model is indexed is pretty easy:

const psets = indexer.getEntityRelations(model, 6518, "IsDefinedBy");
if (psets) {
for (const expressID of psets) {
// You can get the pset attributes like this
const pset = await model.getProperties(expressID);
console.log(pset);
// You can get the pset props like this or iterate over pset.HasProperties yourself
await OBC.IfcPropertiesUtils.getPsetProps(
model,
expressID,
async (propExpressID) => {
const prop = await model.getProperties(propExpressID);
console.log(prop);
},
);
}
}
tip

IsDefinedBy is the inverse attribute name in the IFC Schema that holds the relations with property sets 😉

Awesome! really easy right?

↘️ Exporting the indexation


In bigger models, the process to calculate the relations index may take some time. There is no reason to calculate over and over the relations index every time you load a model. If the model hasn't change, their properties shouldn't neither! So, let's download the relations index to load it later.

const downloadJSON = (json: string, name: string) => {
const file = new File([json], name);
const a = document.createElement("a");
a.href = URL.createObjectURL(file);
a.download = file.name;
a.click();
URL.revokeObjectURL(a.href);
};

const json = indexer.serializeModelRelations(model);
console.log(json);
tip

As @thatopen/components can be used in either NodeJS and Browser environments, the logic to generate a JSON file may vary!

Now, in case you've loaded several models and want to get all the computed relations, there is also a handy method to do it.

const allRelationsJSON = indexer.serializeAllRelations();

↗️ Loading back the relations index


What do we gain with having a pre-processed relations index if we can't use it again, right? Well, let's use the downloaded relations index 😎

// Lets first delete the existing model relations
delete indexer.relationMaps[model.uuid];
const relationsIndexFile = await fetch("https://thatopen.github.io/engine_components/resources/small-relations.json");
const relationsIndex = indexer.getRelationsMapFromJSON(
await relationsIndexFile.text(),
);

indexer.setRelationMap(model, relationsIndex);

Great! Now try to get again the property sets and you will see everything working nice and neat. In fact, lets try to get the building storey of one element in the IFC 👇

const buildingStorey = indexer.getEntityRelations(
model,
6518,
"ContainedInStructure",
);

if (buildingStorey && buildingStorey[0]) {
const storey = await model.getProperties(buildingStorey[0]);
console.log(storey);
}
tip

Despite there are some relations that corresponds to only one element (e.g., an element can only have one associated building storey) the getEntityRelations will always return an array. That's the reason we take the first buildingStorey relation despite it will always be the only one.

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI export the relations that we just generated. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="IFC Relations Indexer Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">
<bim-panel-section style="padding-top: 10px;">

<bim-button
label="Download relations"
@click="${async () => {
downloadJSON(allRelationsJSON, "relations-index-all.json");
}}">
</bim-button>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! Now you know how to get an easy way to get the relations of your model. Keep going with more tutorials! 💪

+ \ No newline at end of file diff --git a/build/Tutorials/Components/Core/MiniMap/index.html b/build/Tutorials/Components/Core/MiniMap/index.html index 82453c27..c8701f4b 100644 --- a/build/Tutorials/Components/Core/MiniMap/index.html +++ b/build/Tutorials/Components/Core/MiniMap/index.html @@ -4,14 +4,14 @@ MiniMap | That Open Docs - +
Skip to main content

MiniMap

Source

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

🗺️ Orientating your user in the scene


In this tutorial you'll learn how to use the Minimap, which is a small 2D representation of the 3D world.

Do you mean a floorplan?

Not quite. The minimap is a simple 2D representation of the 3D world. It is useful to help your user understand where they are, and to have a simple top view of their surrounding.

In this tutorial, we will import:

  • Three.js to get some 3D entities for our app.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/ui to add some simple and cool UI menus.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as THREE from "three";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

const worlds = components.get(OBC.Worlds);
const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

world.scene.setup();

components.init();

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

world.camera.controls.setLookAt(1, 2, -2, -2, 0, -5);

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🏠 Loading a model


Now that we have a scene, let's load a model. We will use the Fragment Manager for it.

Showing Fragments in the Scene

🏔️ There is a dedicated tutorial on how to use Fragment Manager to load IFC files, check it out if you haven't already!

const fragments = new OBC.FragmentsManager(components);

const file = await fetch(
"https://thatopen.github.io/engine_components/resources/small.frag",
);
const dataBlob = await file.arrayBuffer();
const buffer = new Uint8Array(dataBlob);
const model = fragments.load(buffer);
world.scene.three.add(model);

🗺 Setting up the map


Now, that we have our setup ready. Let's start with the exciting stuff. We will use MiniMap component, which does all the work for us.🔮

const maps = new OBC.MiniMaps(components);
const map = maps.create(world);

We have already created a simple DIV element with id minimap in our HTML file. We need to fetch it to add the canvas where our minimap is rendered to it. We'll also add a rounded border to make it look better.

const mapContainer = document.getElementById("minimap") as HTMLDivElement;
const canvas = map.renderer.domElement;
canvas.style.borderRadius = "12px";
mapContainer.append(canvas);
map.resize();

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

we'll also need a reference to the size of the minimap to control it:

const mapSize = map.getSize();

Now we will create a new panel with some inputs to control the different parameters of the MiniMap, like zoom, size and front offset. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel label="Minimap Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-checkbox checked="true" label="Enabled"
@change="${({ target }: { target: BUI.Checkbox }) => {
map.enabled = target.value;
}}">
</bim-checkbox>

<bim-checkbox checked label="Lock rotation"
@change="${({ target }: { target: BUI.Checkbox }) => {
map.lockRotation = target.value;
}}">
</bim-checkbox>

<bim-number-input
slider label="Zoom" value="${map.zoom}" min="0.01" max="0.5" step="0.01"
@change="${({ target }: { target: BUI.NumberInput }) => {
map.zoom = target.value;
}}">
</bim-number-input>

<bim-number-input
slider label="Front offset" value="${map.frontOffset}" min="0" max="5" step="1"
@change="${({ target }: { target: BUI.NumberInput }) => {
map.frontOffset = target.value;
}}">
</bim-number-input>

<div style="display: flex; gap: 12px">

<bim-number-input slider value="${mapSize.x}" pref="Size X" min="100" max="500" step="10"
@change="${({ target }: { target: BUI.NumberInput }) => {
const size = map.getSize();
size.x = target.value;
map.resize(size);
}}">
</bim-number-input>

<bim-number-input slider value="${mapSize.y}" pref="Size Y" min="100" max="500" step="10"
@change="${({ target }: { target: BUI.NumberInput }) => {
const size = map.getSize();
size.y = target.value;
map.resize(size);
}}">
</bim-number-input>
</div>


</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created a simple app that loads a BIM model and displays a MiniMap of it. You can play around with the different parameters of the MiniMap and see how it changes in real time.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/OrthoPerspectiveCamera/index.html b/build/Tutorials/Components/Core/OrthoPerspectiveCamera/index.html index c7b6b1f9..d88fcff5 100644 --- a/build/Tutorials/Components/Core/OrthoPerspectiveCamera/index.html +++ b/build/Tutorials/Components/Core/OrthoPerspectiveCamera/index.html @@ -4,13 +4,13 @@ OrthoPerspectiveCamera | That Open Docs - +
Skip to main content

OrthoPerspectiveCamera

Source

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

📹 How to handle a fancy camera


Sometimes, you need perspective for depth and realism. Other times, you need an orthographic camera to get precise measurements and proportions. Luckily for you, we have a camera that has both of those projections at the same time! It also has some cool functionality for navigation. In this tutorial, you'll learn to use it.

Orthographic and Perspective cameras

The difference between Orthographic and Perspective cameras is that Orthographic cameras don't see things smaller when they are further away. This has some implications, like the camera being always "outside" of your scene. You can't see the interior of a room with an orthographic camera. The most common use for orthographic cameras are 2D floor plans and sections, but they can also be used to create cool-looking 3D scenes.

In this tutorial, we will import:

  • Three.js to get some 3D entities for our app.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/ui to add some simple and cool UI menus.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as THREE from "three";
import * as BUI from "@thatopen/ui";
import * as OBC from "@thatopen/components";

🌎 Setting up the world AND the camera


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. But there's one difference: we will use the OrthoPerspectiveCamera for initializing the world.

const container = document.getElementById("container")!;
let components = new OBC.Components();
let worlds = components.get(OBC.Worlds);

let world = worlds.create<
OBC.SimpleScene,
OBC.OrthoPerspectiveCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.OrthoPerspectiveCamera(components);

world.scene.setup();

await world.camera.controls.setLookAt(3, 3, 3, 0, 0, 0);

components.init();

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

Easy, right? Believe it or not, this is all you need to use the OrthoPerspectiveCamera. Now, let's see it in action!

🧊 Creating a cube


We will start by creating a simple cube and a grid that will serve as a reference point for our camera.

let cubeGeometry = new THREE.BoxGeometry();
let cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });
let cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0, 0.5, 0);

world.scene.three.add(cube);
world.meshes.add(cube);

let grids = components.get(OBC.Grids);
let grid = grids.create(world);

🎟️ Using camera events


The OrthoPerspectiveCamera has a few events that you can use to manage the your scene. We will use the camera.projection.onChanged event to update the grid, so that when using the Orthographic camera, the grid will fade out if the camera zooms away a lot.

world.camera.projection.onChanged.add(() => {
const projection = world.camera.projection.current;
grid.fade = projection === "Perspective";
});

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Building a camera UI


Now we will use @thatopen/ui to create a simple UI for the OrthoPerspectiveCamera. It will have 4 elements:

🎛️ Navigation mode

This will control the navigation mode of the OrthoPerspectiveCamera. It will have 3 options:

  • Orbit: for 3D orbiting around the scene.
  • FirstPerson: for navigating the scene in with the mouse wheel in first person.
  • Plan: for navigating 2d plans (blocking the orbit).

📐 Projections

Like its name implies, the OrthoPerspectiveCamera has 2 projections, and it's really easy to toggle between them. The camera position will remain the same, which is really convenient when you switch between different projections!

❌ Toggling user input

Sometimes you might want to remove control from the user. For example, imagine you are animating the camera and you don't want the user to move the camera around. You can use the setUserInput method to toggle this.

🔎 Focusing objects

The OrthoPerspectiveCamera has a fit method that will fit the camera to a list of meshes. This is really useful when you want to bring attention to a specific part of the scene, or for allowing your user to navigate the scene by focusing objects.

BUI.Manager.init();

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Orthoperspective Camera Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-dropdown required label="Navigation mode"
@change="${({ target }: { target: BUI.Dropdown }) => {
const selected = target.value[0] as OBC.NavModeID;

const { current } = world.camera.projection;
const isOrtho = current === "Orthographic";
const isFirstPerson = selected === "FirstPerson";
if (isOrtho && isFirstPerson) {
alert("First person is not compatible with ortho!");
target.value[0] = world.camera.mode.id;
return;
}
world.camera.set(selected);
}}">

<bim-option checked label="Orbit"></bim-option>
<bim-option label="FirstPerson"></bim-option>
<bim-option label="Plan"></bim-option>
</bim-dropdown>


<bim-dropdown required label="Camera projection"
@change="${({ target }: { target: BUI.Dropdown }) => {
const selected = target.value[0] as OBC.CameraProjection;
const isOrtho = selected === "Orthographic";
const isFirstPerson = world.camera.mode.id === "FirstPerson";
if (isOrtho && isFirstPerson) {
alert("First person is not compatible with ortho!");
target.value[0] = world.camera.projection.current;
return;
}
world.camera.projection.set(selected);
}}">
<bim-option checked label="Perspective"></bim-option>
<bim-option label="Orthographic"></bim-option>
</bim-dropdown>

<bim-checkbox
label="Allow user input" checked
@change="${({ target }: { target: BUI.Checkbox }) => {
world.camera.setUserInput(target.checked);
}}">
</bim-checkbox>

<bim-button
label="Fit cube"
@click="${() => {
world.camera.fit([cube]);
}}">
</bim-button>

<bim-button
label="Reset scene"
@click="${async () => {
components.dispose();

components = new OBC.Components();
worlds = components.get(OBC.Worlds);

world = worlds.create<
OBC.SimpleScene,
OBC.OrthoPerspectiveCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.OrthoPerspectiveCamera(components);

world.scene.setup();

await world.camera.controls.setLookAt(3, 3, 3, 0, 0, 0);

components.init();

world.scene.three.background = null;

cubeGeometry = new THREE.BoxGeometry();
cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0, 0.5, 0);

world.scene.three.add(cube);
world.meshes.add(cube);

grids = components.get(OBC.Grids);
grid = grids.create(world);

world.camera.projection.onChanged.add(() => {
const projection = world.camera.projection.current;
grid.fade = projection === "Perspective";
});
}}">
</bim-button>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! We have created an OrthoPerspective camera that can be used to navigate a 3D scene with multiple projections and navigation modes, as well as a neat UI to control it. Great job!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/Raycasters/index.html b/build/Tutorials/Components/Core/Raycasters/index.html index 50557d9a..72c3e284 100644 --- a/build/Tutorials/Components/Core/Raycasters/index.html +++ b/build/Tutorials/Components/Core/Raycasters/index.html @@ -4,13 +4,13 @@ Raycasters | That Open Docs - +
Skip to main content

Raycasters

Source

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

🐁 Picking things with the mouse


In this tutorial you'll learn how to use the Raycaster to pick objects in the scene with the mouse.

What's ray casting?

Ray casting is the process of casting a ray from a point in space to another point in space. We will cast a ray from the mouse position to the 3D world and check if there is an object in its way. That way, when you hover or click on an object, we can know which one it is and do something with it.

In this tutorial, we will import:

  • Three.js to get some 3D entities for our app.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import Stats from "stats.js";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

const worlds = components.get(OBC.Worlds);
const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(10, 10, 10, 0, 0, 0);

world.scene.setup();

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧊 Adding some cubes to the scene


Now we will add some cubes to the scene and create some materials. The idea for this app is simple: when you click on a cube, it will change its color to green.

const cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });
const greenMaterial = new THREE.MeshStandardMaterial({ color: "#BCF124" });

const boxGeometry = new THREE.BoxGeometry(3, 3, 3);

const cube1 = new THREE.Mesh(boxGeometry, cubeMaterial);
const cube2 = new THREE.Mesh(boxGeometry, cubeMaterial);
const cube3 = new THREE.Mesh(boxGeometry, cubeMaterial);
world.scene.three.add(cube1, cube2, cube3);
const cubes = [cube1, cube2, cube3];

cube2.position.x = 5;
cube3.position.x = -5;

To make this more interesting, we will add a rotation to the cubes. We can do it by subscribing to the onBeforeUpdate event of the world, which fires 60 times per second.

const oneDegree = Math.PI / 180;

function rotateCubes() {
cube1.rotation.x += oneDegree;
cube1.rotation.y += oneDegree;
cube2.rotation.x += oneDegree;
cube2.rotation.z += oneDegree;
cube3.rotation.y += oneDegree;
cube3.rotation.z += oneDegree;
}

world.renderer.onBeforeUpdate.add(rotateCubes);

⚡ Setting up the raycaster


Next, we will set up the raycaster. We will use the Raycasters component. Instead of instantiating it, we will get it from the components object, which is usually safer, as all components are meant to be singletons.

const casters = components.get(OBC.Raycasters);

Each raycaster is bound to a specific world. In this case, we will get the raycaster from the world we are using for our scene:

const caster = casters.get(world);

Finally, we will subscribe to the mousemove event of the window. We will use the castRay method of the raycaster to find out if the mouse is over a cube. If it is, we will change its color to green. Otherwise, we will change its color to the original color.

let previousSelection: THREE.Mesh | null = null;

window.onmousemove = () => {
const result = caster.castRay(cubes);
if (previousSelection) {
previousSelection.material = cubeMaterial;
}
if (!result || !(result.object instanceof THREE.Mesh)) {
return;
}
result.object.material = greenMaterial;
previousSelection = result.object;
};

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! We have created a simple app that uses the Raycaster to pick objects in the scene with the mouse. Easy, right? Now you can allow your users to interact with your 3D world.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/Utils/index.html b/build/Tutorials/Components/Core/Utils/index.html index babe6938..88861367 100644 --- a/build/Tutorials/Components/Core/Utils/index.html +++ b/build/Tutorials/Components/Core/Utils/index.html @@ -4,13 +4,13 @@ Utils | That Open Docs - +
Skip to main content

Utils

Source

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

📏 Getting measurement information


Sometimes we need to know more about the geometry we are working with. For example, imagine you are building a takeoff and estimations app and need to give your user a tool to select the faces of different objects to take them into acccount in a specific takeoff computation. This is where the measurement utils of our libraries come in handy. In this tutorial, you'll learn to use them.

Aren't faces easy?

Not really. The geometry within an IFC file is implicit, and we convert it to triangles to be able to show it in 3D. That means that a simple wall with some windows and doors might have hundreds of triangles. How do you know which one of them, for instance, belong to the outer face? This is what we do for you.

In this tutorial, we will import:

  • three.js to get create som 3D geometry.
  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components to set up the barebone of our app.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import Stats from "stats.js";
import * as OBC from "@thatopen/components";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = new OBC.FragmentsManager(components);
const file = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

🧊 Representing the data


We're going to get the face information and represent it in 3D on top of the BIM model we just loaded when the user hover with the mouse over it. So first we need to create the 3D object that will show up on top of a hovered face. That's quite easy using three.js:

const edges = new THREE.EdgesGeometry();
const material = new THREE.LineBasicMaterial({
color: 0xff0000,
depthTest: false,
});
const line = new THREE.LineSegments(edges, material);
world.scene.three.add(line);

📐 Setting up the measurements


Now, to be able to make the user hover over the geometry, detect a face and get its ifnormation, we'll need to import 2 components: the measurements utils and the casters. We'll create a new caster in the current world.

const measurements = components.get(OBC.MeasurementUtils);
const casters = components.get(OBC.Raycasters);
const caster = casters.get(world);

And now we are going to add an event to the current renderer. The idea is quite simple:

  1. Use the raycaster to get the face under the mouse of the user (if any).
  2. Use the measurement utils to get the face data.
  3. Update the 3D object with the face data.
if (world.renderer) {
const canvas = world.renderer.three.domElement;
canvas.addEventListener("mousemove", () => {
const result = caster.castRay([model]);

if (!result) return;
if (!(result.object instanceof THREE.Mesh)) return;
if (result.faceIndex === undefined) return;

const face = measurements.getFace(
result.object,
result.faceIndex,
result.instanceId,
);

if (face) {
const points: THREE.Vector3[] = [];
for (const edge of face.edges) {
points.push(...edge.points);
}
edges.setFromPoints(points);
}
});
}

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that allows user to pick geometry faces and represent them in 3D. You can now use this data to build Takeoff and estimations apps!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Core/Worlds/index.html b/build/Tutorials/Components/Core/Worlds/index.html index 7b6198f7..8891bb88 100644 --- a/build/Tutorials/Components/Core/Worlds/index.html +++ b/build/Tutorials/Components/Core/Worlds/index.html @@ -4,13 +4,13 @@ Worlds | That Open Docs - +
Skip to main content

Worlds

Source

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

🌎 Creating our 3D world


In this tutorial you'll learn how to create a simple scene using @thatopen/components.

Hello world!

A world represents a 3D environment in your application. It consists of a scene, a camera and (optionally) a renderer. You can create multiple worlds and show them in multiple viewports at the same time.

In this tutorial, we will import:

  • Three.js to get some 3D entities for our app.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/ui to add some simple and cool UI menus.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import * as BUI from "@thatopen/ui";
import * as OBC from "@thatopen/components";
import Stats from "stats.js";

🖼️ Getting the container


Next, we need to tell the library where do we want to render the 3D scene. We have added an DIV element to this HTML page that occupies the whole width and height of the viewport. Let's fetch it by its ID:

const container = document.getElementById("container")!;

🚀 Creating a components instance


Now we will create a new instance of the Components class. This class is the main entry point of the library. It will be used to register and manage all the components in your application.

Don't forget to dispose it when you are done!

Once you are done with your application, you need to dispose the Components instance to free up the memory. This is a requirement of Three.js, which can't dispose the memory of 3D related elements automatically.

const components = new OBC.Components();

🌎 Setting up the world


Now we are ready to create our first world. We will use the Worlds component to manage all the worlds in your application. Instead of instancing it, we can get it from the Components instance. All components are singleton, so this is always a better way to get them.

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

We can create a new world by calling the create method of the Worlds component. It's a generic method, so we can specify the type of the scene, the camera and the renderer we want to use.

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

Now we can set the scene, the camera and the renderer of the world, and call the init method to start the rendering process.

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

💄 Adding things to our scene


Now we are ready to start adding some 3D entities to our scene. We will add a simple cube:

const material = new THREE.MeshLambertMaterial({ color: "#6528D7" });
const geometry = new THREE.BoxGeometry();
const cube = new THREE.Mesh(geometry, material);
world.scene.three.add(cube);

We could also add some lights, but the SimpleScene class can do that easier for us using its setup method:

world.scene.setup();

Finally, we will make the camera look at the cube:

world.camera.controls.setLookAt(3, 3, 3, 0, 0, 0);

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will create a new panel with some inputs to change the background color of the scene and the intensity of the directional and ambient lights. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel label="Worlds Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-color-input
label="Background Color" color="#202932"
@input="${({ target }: { target: BUI.ColorInput }) => {
world.scene.three.background = new THREE.Color(target.color);
}}">
</bim-color-input>

<bim-number-input
slider step="0.1" label="Directional lights intensity" value="1.5" min="0.1" max="10"
@change="${({ target }: { target: BUI.NumberInput }) => {
for (const child of world.scene.three.children) {
if (child instanceof THREE.DirectionalLight) {
child.intensity = target.value;
}
}
}}">
</bim-number-input>

<bim-number-input
slider step="0.1" label="Ambient light intensity" value="1" min="0.1" max="5"
@change="${({ target }: { target: BUI.NumberInput }) => {
for (const child of world.scene.three.children) {
if (child instanceof THREE.AmbientLight) {
child.intensity = target.value;
}
}
}}">
</bim-number-input>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created your first 3D world and added some UI elements to it. You can now play with the inputs to see how the scene changes.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/AngleMeasurement/index.html b/build/Tutorials/Components/Front/AngleMeasurement/index.html index dfc64efc..19042c8d 100644 --- a/build/Tutorials/Components/Front/AngleMeasurement/index.html +++ b/build/Tutorials/Components/Front/AngleMeasurement/index.html @@ -4,14 +4,14 @@ AngleMeasurement | That Open Docs - +
Skip to main content

AngleMeasurement

Source

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

📐 Measuring angles


Space control is one of the most important elements of BIM applications. In this tutorial, you'll learn how to expose an angle measurement tool to your end users. We will import:

  • three to create some 3D items.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";
import Stats from "stats.js";

🌎 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 container = document.getElementById("container")!;
const components = new OBC.Components();
const worlds = components.get(OBC.Worlds);

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBCF.PostproductionRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.PostproductionRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🎲 Creating a Cube Mesh


For this tutorial we will use a Cube, you can add any geometry as per your preference. We will create a Cube with 3x3x3 dimensions and use red color for the material.

const cubeGeometry = new THREE.BoxGeometry(3, 3, 3);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0, 1.5, 0);
world.scene.three.add(cube);
world.meshes.add(cube);

🛠️ Getting the angle measurements


First, let's get an instance of the angle measurement component and initialize it:

const angles = components.get(OBCF.AngleMeasurement);
angles.world = world;
angles.enabled = true;

🖱️ Setting up mouse events


Now, we'll define how to create the angle dimensions. In this case, we'll keep it simple and use the double click event of the container HTML element.

container.ondblclick = () => angles.create();

🧹 Deleting the Dimensions


Now that we know how to make multiple dimensions, we'll learn how to delete them when necessary. Dimensions can be removed using the deleteAll() method, which deletes all the created dimensions. Again, we'll keep it simple and bind this event to the keydown event. Specifically, it will fire when the user presses the Delete or Backspace key.

window.onkeydown = (event) => {
if (event.code === "Delete" || event.code === "Backspace") {
angles.deleteAll();
}
};

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that can create and delete angular dimensions on any 3D object. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/AreaMeasurement/index.html b/build/Tutorials/Components/Front/AreaMeasurement/index.html index 89da18e5..218038ca 100644 --- a/build/Tutorials/Components/Front/AreaMeasurement/index.html +++ b/build/Tutorials/Components/Front/AreaMeasurement/index.html @@ -4,14 +4,14 @@ AreaMeasurement | That Open Docs - +
Skip to main content

AreaMeasurement

Source

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

📐 Measuring areas


Space control is one of the most important elements of BIM applications. In this tutorial, you'll learn how to expose an area measurement tool to your end users. We will import:

  • three to create some 3D items.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as OBC from "@thatopen/components";
import * as THREE from "three";
import * as OBCF from "@thatopen/components-front";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBCF.PostproductionRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.PostproductionRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();
world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);
world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🎲 Creating a Cube Mesh


For this tutorial we will use a Cube, you can add any geometry as per your preference. We will create a Cube with 3x3x3 dimensions and use red color for the material.

const cubeGeometry = new THREE.BoxGeometry(3, 3, 3);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0, 1.5, 0);
world.scene.three.add(cube);
world.meshes.add(cube);

🛠️ Getting the area measurements


First, let's get an instance of the area measurement component and initialize it:

const areaDims = components.get(OBCF.AreaMeasurement);
areaDims.world = world;
areaDims.enabled = true;

🖱️ Setting up mouse events


Now, we'll define how to create the area dimensions. In this case, we'll keep it simple and use the double click event of the container HTML element. We'll also use the right mouse button to end the creation of the area.

container.ondblclick = () => areaDims.create();
container.oncontextmenu = () => areaDims.endCreation();

🧹 Deleting the Dimensions


Now that we know how to make multiple dimensions, we'll learn how to delete them when necessary. Dimensions can be removed using the deleteAll() method, which deletes all the created dimensions. Again, we'll keep it simple and bind this event to the keydown event. Specifically, it will fire when the user presses the Delete or Backspace key.

window.onkeydown = (event) => {
if (event.code === "Delete" || event.code === "Backspace") {
areaDims.deleteAll();
}
};

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that can create and delete area dimensions on any 3D object. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/Civil3DNavigator/index.html b/build/Tutorials/Components/Front/Civil3DNavigator/index.html index a476f63d..3110aa91 100644 --- a/build/Tutorials/Components/Front/Civil3DNavigator/index.html +++ b/build/Tutorials/Components/Front/Civil3DNavigator/index.html @@ -4,13 +4,13 @@ Civil3DNavigator | That Open Docs - +
Skip to main content

Civil3DNavigator

Source

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

🛣️ Navigating 3D infrastructures


Infra models are awesome, but they are usually very, very long and thin. This makes it a bit hard to navigate through them. Luckily for you, the alignment data that comes in IFC models is processed by our libraries and generated in 3D, so you can use it for navigation!

3D alignment?

The alignment data in IFC usually comes in 2D (floor plan and elevation). We use that data to regenerate the 3D curve from those 2D representations. You'll see the result in just a moment!

In this tutorial, we will import:

  • three to create some 3D items.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import Stats from "stats.js";
import * as OBCF from "@thatopen/components-front";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBCF.RendererWith2D
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.RendererWith2D(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.scene.setup();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

container.appendChild(world.renderer.three2D.domElement);

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

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

const file = await fetch(
"https://thatopen.github.io/engine_components/resources/road.frag",
);

const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = await fragments.load(buffer);
world.scene.three.add(model);

const properties = await fetch(
"https://thatopen.github.io/engine_components/resources/road.json",
);

model.setLocalProperties(await properties.json());

/*
### 🚕 Setting up Civil 3D Navigator
---

Now, we need to create an instance of the Civil 3D Navigator component. This will enable us to navigate through our 3D environment and interact with the model.
const navigator = components.get(OBCF.Civil3DNavigator);
navigator.world = world;
navigator.draw(model);

/*
### 👁️ Using the culler
---

For perfomance reasons, we will use a culler to only render the parts of the model that the camera can see. We will create a new culler for this world, add the previously loaded model to it and configure the update logic to refresh the culler every time the user stops moving the camera.
const cullers = components.get(OBC.Cullers);
const culler = cullers.create(world);
culler.threshold = 10;

for (const child of model.children) {
if (child instanceof THREE.InstancedMesh) {
culler.add(child);
}
}

culler.needsUpdate = true;

world.camera.controls.addEventListener("sleep", () => {
culler.needsUpdate = true;
});


/*
### ⚾ Navigating to the selected point
---

There are many ways to navigate to the selected point in an alignment. We will make it simple: subscribing to the highlight event, we can get some information about the highlight, such as the point that was clicked in the alignment. We will use that point to set the position of an invisible sphere that will use to move the camera with a nice animation:
const sphere = new THREE.Sphere(undefined, 20);

navigator.onHighlight.add(({ point }) => {
sphere.center.copy(point);
world.camera.controls.fitToSphere(sphere, true);
});

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created a 3D app that can load infra models, represent its alignment in 3D and use it to navigate around with a nice camera animation. Well done!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/CivilCrossSectionNavigator/index.html b/build/Tutorials/Components/Front/CivilCrossSectionNavigator/index.html index b2d10491..624b6ee9 100644 --- a/build/Tutorials/Components/Front/CivilCrossSectionNavigator/index.html +++ b/build/Tutorials/Components/Front/CivilCrossSectionNavigator/index.html @@ -4,13 +4,13 @@ CivilCrossSectionNavigator | That Open Docs - +
Skip to main content

CivilCrossSectionNavigator

Source

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

🛣️ Infra cross sections


Cross sections are one of the most important sources of information to describe civil models. In this tutorial, you'll learn how to generate and display the 2D cross section of any civil model.

Why are Cross sections important for civil?

Infra models usually have a huge difference between X and Y. This means that the cross sections is measured in meters, while the long section is kilometers long. This makes the cross section one of the most important sources of information to describe civil assets.

In this tutorial, we will import:

  • three to create some 3D items.
  • @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.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import * as BUI from "@thatopen/ui";
import * as BUIC from "@thatopen/ui-obc";
import Stats from "stats.js";
import * as OBCF from "@thatopen/components-front";

📋 Initializing the UI


The UI Components need to be initialized before we can use them. First, we will do it with both UI libraries:

BUI.Manager.init();
BUIC.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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.OrthoPerspectiveCamera,
OBCF.RendererWith2D
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.RendererWith2D(components, container);
world.camera = new OBC.OrthoPerspectiveCamera(components);

components.init();

world.scene.setup();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

container.appendChild(world.renderer.three2D.domElement);

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = components.get(OBC.FragmentsManager);
const file = await fetch("https://thatopen.github.io/engine_components/resources/road.frag");
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

const properties = await fetch("https://thatopen.github.io/engine_components/resources/road.json");
model.setLocalProperties(await properties.json());

🚗 Adding the civil plan navigator


We'll now add the civil plan navigator. The idea of this app is very simple: we will allow the user to click on the plan navigator to generate and display the 2D cross section of the model on another menu, and also navigate to that civil curve in 3D.

Civil plan navigator?

If you are not familiar with the civil plan navigator, check out that tutorial first!

So first we'll fetch the world-2d HTML element we added to this page (don't forget to check out the HTML file of this example):

const world2DLeft = document.getElementById("scene-2d-left") as BUIC.World2D;
world2DLeft.components = components;
if (!world2DLeft.world) {
throw new Error("World not found!");
}

And now we'll get a new instance of the civil plan navigator, assign it to the world-2d that we just fetched and draw the horizontal alignment on it:

const planNavigator = components.get(OBCF.CivilPlanNavigator);
planNavigator.world = world2DLeft.world;
await planNavigator.draw(model);

🚕 Adding the civil 3D navigator


Next, we'll add the civil 3D navigator, which will allow us to make the camera travel to the 3D alignment curve that the user selects in the plan navigator.

Civil 3D navigator?

If you are not familiar with the civil 3D navigator, check out that tutorial first!

We'll simply get an instance of the civil 3D navigator:

const navigator3D = components.get(OBCF.Civil3DNavigator);
navigator3D.world = world;
navigator3D.draw(model);

🚓 Setting up Civil Cross Section Navigator


The Cross Section Navigator is a tool that will allow you to clearly visualize the cross section of any civil IFC model, in any point selected in an alignment. We'll start by getting the other world-2d that we added to this page (again, don't forget to check out the HTML of this example):

const world2DRight = document.getElementById("scene-2d-right") as BUIC.World2D;
world2DRight.components = components;
if (!world2DRight.world) {
throw new Error("World not found!");
}

And now we'll get an instance of the cross section navigator and assign it to the world-2d:

const crossNavigator = components.get(OBCF.CivilCrossSectionNavigator);
crossNavigator.world = world2DRight.world;
crossNavigator.world3D = world;

🚗🚕🚓 Binding navigators


Now we'll bind the cross section navigator with the plan navigator and the 3D navigator. We'll use the marker change event for that. When the marker changes in the plan navigator:

  1. The marker of the 3D navigator will be updated.
  2. The cross navigator will be set to that point.
planNavigator.onMarkerChange.add(({ alignment, percentage, type, curve }) => {
navigator3D.setMarker(alignment, percentage, type);
if (type === "select") {
const mesh = curve.alignment.absolute[curve.index].mesh;
const point = alignment.getPointAt(percentage, "absolute");
crossNavigator.set(mesh, point);
}
});

And now we'll subscribe to the highlight event of the plan navigator, so that each time the user clicks on the horizontal alignment, we travel to that point of the road in 3D:

planNavigator.onHighlight.add(({ mesh }) => {
navigator3D.highlighter.select(mesh);
const index = mesh.curve.index;
const curve3d = mesh.curve.alignment.absolute[index];
curve3d.mesh.geometry.computeBoundingSphere();
const sphere = curve3d.mesh.geometry.boundingSphere;
if (sphere) {
world.camera.controls.fitToSphere(sphere, true);
}
});

Also, we will make sure that when the marker is hidden in the plan navigator, it's also hidden in 3D:

planNavigator.onMarkerHidden.add(({ type }) => {
navigator3D.hideMarker(type);
});

🖌️ Setting up styles


Now it's time to define how we want to see the lines of the cross section of the road. In this example, we'll give a different section color to each IFC category. First, let's fetch de classifier and classify the model by IFC category:

const classifier = components.get(OBC.Classifier);
classifier.byEntity(model);
const classifications = classifier.list;

Now, we'll create a new clipping style for each IFC category. We'll just give a random color to each category.

const clipper = components.get(OBCF.ClipEdges);
const styles = clipper.styles.list;

for (const category in classifications.entities) {
const found = classifier.find({ entities: [category] });

const color = new THREE.Color(Math.random(), Math.random(), Math.random());
const lineMaterial = new THREE.LineBasicMaterial({ color });
clipper.styles.create(category, new Set(), world2DRight.world, lineMaterial);

for (const fragID in found) {
const foundFrag = fragments.list.get(fragID);
if (!foundFrag) {
continue;
}
styles[category].fragments[fragID] = new Set(found[fragID]);
styles[category].meshes.add(foundFrag.mesh);
}
}

Don't forget to update the clipper after the styles have been created!

clipper.update(true);

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that can generate the cross section of any civil model and navigate through it smoothly. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/CivilElevationNavigator/index.html b/build/Tutorials/Components/Front/CivilElevationNavigator/index.html index 13a33cc9..1025f679 100644 --- a/build/Tutorials/Components/Front/CivilElevationNavigator/index.html +++ b/build/Tutorials/Components/Front/CivilElevationNavigator/index.html @@ -4,13 +4,13 @@ CivilElevationNavigator | That Open Docs - +
Skip to main content

CivilElevationNavigator

Source

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

🛣️ Vertical alignments


In this page, we'll learn how to represent the vertical alignment of civil models and make it interactive.

Vertical alignment?

Civil models include 2D alignments with explicit information about the asset. One of these alignments is vertical and represents the elevation of the horizontal alignment in true magnitude.

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.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";
import * as BUI from "@thatopen/ui";
import * as BUIC from "@thatopen/ui-obc";
import Stats from "stats.js";

📋 Initializing the UI


The UI Components need to be initialized before we can use them. First, we will do it with both UI libraries:

BUI.Manager.init();
BUIC.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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.OrthoPerspectiveCamera,
OBCF.RendererWith2D
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.RendererWith2D(components, container);
world.camera = new OBC.OrthoPerspectiveCamera(components);

components.init();

world.scene.setup();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

container.appendChild(world.renderer.three2D.domElement);

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = components.get(OBC.FragmentsManager);
const file = await fetch("https://thatopen.github.io/engine_components/resources/road.frag");
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

🚕 Adding the civil 3D navigator


First we'll add the civil 3D navigator to move the camera when the user interacts with the civil plan navigator, which we will add later.

Civil 3D navigator?

If you are not familiar with the civil 3D navigator, check out that tutorial first!

We'll simply get an instance of the civil 3D navigator:

const navigator = components.get(OBCF.Civil3DNavigator);
navigator.world = world;
navigator.draw(model);

🚗 Adding the civil plan navigator


We'll now add the civil plan navigator. The idea of this app is very simple: we will allow the user to click on the plan navigator to navigate across the 3D scene and the elevation alignment at the same time.

Civil plan navigator?

If you are not familiar with the civil plan navigator, check out that tutorial first!

So first we'll fetch the world-2d HTML element we added to this page (don't forget to check out the HTML file of this example):

const world2DLeft = document.getElementById("scene-2d-left") as BUIC.World2D;
world2DLeft.components = components;
if (!world2DLeft.world) {
throw new Error("World not found!");
}

And now we'll get a new instance of the civil plan navigator, assign it to the world-2d that we just fetched and draw the horizontal alignment on it:

const planNavigator = new OBCF.CivilPlanNavigator(components);
planNavigator.world = world2DLeft.world;
planNavigator.draw(model);

/*
### 🚒 Adding the civil elevation navigator
---
The Elevation Navigator is a tool that allows exploration and visualization of the vertical alignment of civil models. We'll start by fetching the other world-2d we added to this page (again, don't forget to check out the HTML file of this example):
const world2DRight = document.getElementById("scene-2d-right") as BUIC.World2D;
world2DRight.components = components;
if (!world2DRight.world) {
throw new Error("World not found!");
}

/*
And now we'll simply get the elevation navigator instance, bind it to the previous world-2d and draw the vertical alignment of the civil model we added to the scene:
const elevationNavigator = components.get(OBCF.CivilElevationNavigator);
elevationNavigator.world = world2DRight.world;
elevationNavigator.draw(model);

🚗🚕🚒 Binding navigators


Now we'll bind the elevation navigator, the plan navigator and the 3D navigator. We'll use the marker change event for that. When the marker changes in the plan navigator:

  1. The marker of the 3D navigator will be updated.
  2. The marker of the elevation navigator will be udpated.
planNavigator.onMarkerChange.add(({ alignment, percentage }) => {
elevationNavigator.setMarker(alignment, percentage, "hover");
navigator.setMarker(alignment, percentage, "hover");
});

Next, we'll subscribe to the highlight event of the plan navigator. The idea is simple: when the user highlights the plan navigator, we'll move the 3D camera to that point, and also highlight the equivalent elevation alignment curve in the elevation navigator.

planNavigator.onHighlight.add(({ mesh, point }) => {
const { index, alignment } = mesh.curve;

const percentage = alignment.getPercentageAt(point, "horizontal");
if (percentage === null) return;
const { curve } = alignment.getCurveAt(percentage, "vertical");
elevationNavigator.highlighter.select(curve.mesh);

elevationNavigator.setMarker(curve.alignment, percentage, "select");

if (world2DRight.world) {
if (!curve.mesh.geometry.boundingSphere) {
curve.mesh.geometry.computeBoundingSphere();
}
const vertSphere = curve.mesh.geometry.boundingSphere!.clone();
vertSphere.radius *= 1.5;
world2DRight.world.camera.controls.fitToSphere(vertSphere, true);
}

navigator.highlighter.select(mesh);
const curve3d = mesh.curve.alignment.absolute[index];
curve3d.mesh.geometry.computeBoundingSphere();
const sphere = curve3d.mesh.geometry.boundingSphere;
if (sphere) {
world.camera.controls.fitToSphere(sphere, true);
}
});

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that can generate the elevation alignment any civil model and navigate through it smoothly. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/CivilPlanNavigator/index.html b/build/Tutorials/Components/Front/CivilPlanNavigator/index.html index 26f880ac..920aeae8 100644 --- a/build/Tutorials/Components/Front/CivilPlanNavigator/index.html +++ b/build/Tutorials/Components/Front/CivilPlanNavigator/index.html @@ -4,13 +4,13 @@ CivilPlanNavigator | That Open Docs - +
Skip to main content

CivilPlanNavigator

Source

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

🛣️ Horizontal alignments


In this page, we'll learn how to represent the horizontal alignment of civil models and make it interactive.

Horizontal alignment?

Civil models include 2D alignments with explicit information about the asset. One of these alignments is horizontal and represents the floor plan of the alignment curves.

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.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";
import * as BUI from "@thatopen/ui";
import * as BUIC from "@thatopen/ui-obc";
import Stats from "stats.js";

📋 Initializing the UI


The UI Components need to be initialized before we can use them. First, we will do it with both UI libraries:

BUI.Manager.init();
BUIC.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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.OrthoPerspectiveCamera,
OBCF.RendererWith2D
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.RendererWith2D(components, container);
world.camera = new OBC.OrthoPerspectiveCamera(components);

components.init();

world.scene.setup();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

container.appendChild(world.renderer.three2D.domElement);

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = components.get(OBC.FragmentsManager);
const file = await fetch("https://thatopen.github.io/engine_components/resources/road.frag");
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

🚕 Adding the civil 3D navigator


First we'll add the civil 3D navigator to move the camera when the user interacts with the civil plan navigator, which we will add later.

Civil 3D navigator?

If you are not familiar with the civil 3D navigator, check out that tutorial first!

We'll simply get an instance of the civil 3D navigator:

const navigator = components.get(OBCF.Civil3DNavigator);
navigator.world = world;
navigator.draw(model);

🚗 Adding the civil plan navigator


We'll now add the civil plan navigator. The idea of this app is very simple: we will allow the user to click on the plan navigator to navigate across the 3D scene. So first we'll fetch the world-2d HTML element we added to this page (don't forget to check out the HTML file of this example):

const world2D = document.getElementById("scene-2d") as BUIC.World2D;

And now we'll get a new instance of the civil plan navigator, assign it to the world-2d that we just fetched and draw the horizontal alignment on it:

const planNavigator = components.get(OBCF.CivilPlanNavigator);
world2D.components = components;
planNavigator.world = world2D.world;
await planNavigator.draw(model);

🚗🚕 Binding navigators


Now we'll bind the elevation navigator with the 3D navigator. We'll use the highlight event for that. When the plan navigator is highlighted, the 3D navigator will be highlighted as well and the camera will travel to the selected point in 3D.

planNavigator.onHighlight.add(({ mesh }) => {
navigator.highlighter.select(mesh);
const index = mesh.curve.index;
const curve3d = mesh.curve.alignment.absolute[index];
curve3d.mesh.geometry.computeBoundingSphere();
const sphere = curve3d.mesh.geometry.boundingSphere;
if (sphere) {
world.camera.controls.fitToSphere(sphere, true);
}
});

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that can generate the horizontal alignment any civil model and navigate through it smoothly. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/EdgeMeasurement/index.html b/build/Tutorials/Components/Front/EdgeMeasurement/index.html index da6381d0..499ca100 100644 --- a/build/Tutorials/Components/Front/EdgeMeasurement/index.html +++ b/build/Tutorials/Components/Front/EdgeMeasurement/index.html @@ -4,14 +4,14 @@ EdgeMeasurement | That Open Docs - +
Skip to main content

EdgeMeasurement

Source

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

📐 Measuring edges


Space control is one of the most important elements of BIM applications. In this tutorial, you'll learn how to expose an edge measurement tool to your end users. We will import:

  • three to create some 3D items.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBCF.PostproductionRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.PostproductionRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = new OBC.FragmentsManager(components);
const file = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

for (const child of model.children) {
if (child instanceof THREE.Mesh) {
world.meshes.add(child);
}
}

🛠️ Getting the edge measurements


First, let's get an instance of the edge measurement component and initialize it:

const dimensions = components.get(OBCF.EdgeMeasurement);
dimensions.world = world;
dimensions.enabled = true;

🖱️ Setting up mouse events


Now, we'll define how to create the edge dimensions. In this case, we'll keep it simple and use the double click event of the container HTML element.

container.ondblclick = () => dimensions.create();

🧹 Deleting the Dimensions


Now that we know how to make multiple dimensions, we'll learn how to delete them when necessary. Dimensions can be removed using the deleteAll() method, which deletes all the created dimensions. You can also use the delete() method to just delete one dimension (the one under the mouse cursor). Let's set up some basic key events that allow us to delete, save and recover the dimensions:

let saved: number[][];

window.addEventListener("keydown", (event) => {
if (event.code === "KeyO") {
dimensions.delete();
} else if (event.code === "KeyS") {
saved = dimensions.get();
dimensions.deleteAll();
} else if (event.code === "KeyL") {
if (saved) {
dimensions.set(saved);
}
}
});

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that can create and delete edge dimensions on any 3D object. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/EdgesClipper/index.html b/build/Tutorials/Components/Front/EdgesClipper/index.html index fc2c52cc..14c6ee72 100644 --- a/build/Tutorials/Components/Front/EdgesClipper/index.html +++ b/build/Tutorials/Components/Front/EdgesClipper/index.html @@ -4,7 +4,7 @@ EdgesClipper | That Open Docs - + @@ -13,7 +13,7 @@ clipper.deleteAll().

Great job! 🎉 Using the Clipper Component, you can now effortlessly check BIM models or any other 3D objects with stunning edges.🧐 Let's keep it up and check out another tutorial! 🎓

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to play around with the clipping plane properties. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Edges Clipper Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-label>Double click: Create clipping plane</bim-label>
<bim-label>Delete key: Delete clipping plane</bim-label>


</bim-panel-section>
<bim-panel-section collapsed label="Others">

<bim-checkbox label="Clipper enabled" checked
@change="${({ target }: { target: BUI.Checkbox }) => {
clipper.enabled = target.value;
edges.visible = target.value;
}}">
</bim-checkbox>

<bim-checkbox label="Clipper visible" checked
@change="${({ target }: { target: BUI.Checkbox }) => {
clipper.visible = target.value;
}}">
</bim-checkbox>

<bim-color-input
label="Planes Color" color="#202932"
@input="${({ target }: { target: BUI.ColorInput }) => {
clipper.material.color.set(target.color);
}}">
</bim-color-input>

<bim-number-input
slider step="0.01" label="Planes opacity" value="0.2" min="0.1" max="1"
@change="${({ target }: { target: BUI.NumberInput }) => {
clipper.material.opacity = target.value;
}}">
</bim-number-input>

<bim-number-input
slider step="0.1" label="Planes size" value="5" min="2" max="10"
@change="${({ target }: { target: BUI.NumberInput }) => {
clipper.size = target.value;
}}">
</bim-number-input>

<bim-button
label="Delete all"
@click="${() => {
clipper.deleteAll();
}}">
</bim-button>

<bim-button
label="Rotate cubes"
@click="${() => {
cube.rotation.x = 2 * Math.PI * Math.random();
cube.rotation.y = 2 * Math.PI * Math.random();
cube.rotation.z = 2 * Math.PI * Math.random();
cube.updateMatrixWorld();

cube2.rotation.x = 2 * Math.PI * Math.random();
cube2.rotation.y = 2 * Math.PI * Math.random();
cube2.rotation.z = 2 * Math.PI * Math.random();
cube2.updateMatrixWorld();

edges.update(true);
}}">
</bim-button>


</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created an app that can create, manipulate, edit and delete clipping planes on any 3D object. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/FaceMeasurement/index.html b/build/Tutorials/Components/Front/FaceMeasurement/index.html index 82e20dcd..a4ff9473 100644 --- a/build/Tutorials/Components/Front/FaceMeasurement/index.html +++ b/build/Tutorials/Components/Front/FaceMeasurement/index.html @@ -4,14 +4,14 @@ FaceMeasurement | That Open Docs - +
Skip to main content

FaceMeasurement

Source

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

📐 Measuring faces


Space control is one of the most important elements of BIM applications. In this tutorial, you'll learn how to expose an face measurement tool to your end users. We will import:

  • three to create some 3D items.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBCF.PostproductionRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.PostproductionRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = new OBC.FragmentsManager(components);
const file = await fetch(
"https://thatopen.github.io/engine_components/resources/small.frag",
);
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

for (const child of model.children) {
if (child instanceof THREE.Mesh) {
world.meshes.add(child);
}
}

🛠️ Getting the face measurements


First, let's get an instance of the face measurement component and initialize it:

const dimensions = components.get(OBCF.FaceMeasurement);
dimensions.world = world;
dimensions.enabled = true;

🖱️ Setting up mouse events


Now, we'll define how to create the edge dimensions. In this case, we'll keep it simple and use the double click event of the container HTML element.

container.ondblclick = () => dimensions.create();

🧹 Deleting the Dimensions


Now that we know how to make multiple dimensions, we'll learn how to delete them when necessary. Dimensions can be removed using the deleteAll() method, which deletes all the created dimensions. You can also use the delete() method to just delete one dimension (the one under the mouse cursor). Let's set up some basic key events that allow us to delete, save and recover the dimensions:

let saved: OBCF.SerializedAreaMeasure[];

window.addEventListener("keydown", (event) => {
if (event.code === "KeyO") {
dimensions.delete();
} else if (event.code === "KeyS") {
saved = dimensions.get();
dimensions.deleteAll();
} else if (event.code === "KeyL") {
if (saved) {
dimensions.set(saved);
}
}
});

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that can create and delete face dimensions on any 3D object. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/Highlighter/index.html b/build/Tutorials/Components/Front/Highlighter/index.html index 449bbdd7..e5a6f31c 100644 --- a/build/Tutorials/Components/Front/Highlighter/index.html +++ b/build/Tutorials/Components/Front/Highlighter/index.html @@ -4,13 +4,13 @@ Highlighter | That Open Docs - +
Skip to main content

Highlighter

Source

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

🔦 Highlighting


In 3D apps, users get some feedback when they hover or click on an object. Generally, it changes its color or shading. In this tutorial, you'll learn how to do that with the highlighter.

Highlighting?

Highlighting means changing the color of one or many objects to make them stand out. This can be used for hovering, for selection, for bringing the attention of the user to certain items, etc.

In this tutorial, we will import:

  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";
import Stats from "stats.js";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = new OBC.FragmentsManager(components);
const file = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

💡 Getting the highlighter


Now, we will basically get the highlighter and set it up. This will create and configure 2 things:

  • Selecting: when clicking on an element.
  • Hovering: when hovering the mouse over an element.
const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });
highlighter.zoomToSelection = true;

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that can highlight items on hover and on selection. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/IfcStreamer/index.html b/build/Tutorials/Components/Front/IfcStreamer/index.html index 0423169a..c9c57b82 100644 --- a/build/Tutorials/Components/Front/IfcStreamer/index.html +++ b/build/Tutorials/Components/Front/IfcStreamer/index.html @@ -4,13 +4,13 @@ IfcStreamer | That Open Docs - +
Skip to main content

IfcStreamer

Source

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

🐳 Let's go BIG


Opening big BIM models is not easy, especially if we are in a browser or in devices that are not so powerful. In this tutorial, we'll learn how to do it using streaming, which allows to open gigabytes of data in seconds on any device.

Streaming?

Streaming consists of converting the IFC file to "tiles", and then loading only the data that the user sees. If you haven't heard of streaming before, check out the geometry tiles and property tiles tutorials first!

In this tutorial, we will import:

  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";

🌎 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. Notice how we use the PostproductionRenderer in this case.

const container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.scene.setup();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧰 Getting the streamer


Now we are ready to start streaming a BIM model. We have already a bunch of tiles prepared in our repository as example, but you can also convert your own IFC to tiles (check the geometry and property tiles tutorials for more information). First, let's get an instance of the IFC streamer:

const loader = components.get(OBCF.IfcStreamer);
loader.world = world;

Now, we need to set the base URL where the streamer needs to look for the tiles. In our case, we'll use the tiles we have prepared in our repository, but this should also work with your own backend.

loader.url = "https://thatopen.github.io/engine_components/resources/streaming/";

📺 Streaming the model


Now we'll create a function that will stream the given model. We will also allow to stream the properties optionally.

async function loadModel(geometryURL: string, propertiesURL?: string) {
const rawGeometryData = await fetch(geometryURL);
const geometryData = await rawGeometryData.json();
let propertiesData;
if (propertiesURL) {
const rawPropertiesData = await fetch(propertiesURL);
propertiesData = await rawPropertiesData.json();
}

const model = await loader.load(geometryData, true, propertiesData);
console.log(model);
}

Next, let's call this function and start streaming our model right away!

await loadModel(
"https://thatopen.github.io/engine_components/resources/streaming/small.ifc-processed.json",
"https://thatopen.github.io/engine_components/resources/streaming/small.ifc-processed-properties.json",
);

🔄️ Updating the streamer


Now, streaming works by updating the scene depending on the user's perspective and getting the necessary geometries from the backend. A simple way to achieve this is by updating the scene each time the user stops the camera:

world.camera.controls.addEventListener("sleep", () => {
loader.culler.needsUpdate = true;
});

🧠 Stream cache


As you can imagine, downloading the geometries from the server each time can take time, especially for heavier geometries. This is why the stream loader automatically caches the files locally to get them much faster. This means that the loading experience the first time might be a bit slower, but then later it will be much better. You can control this using the useCache property and clear the cache using the clearCache() method:

loader.useCache = true;

async function clearCache() {
await loader.clearCache();
window.location.reload();
}

⚙️ Streaming config


You can also customize the loader through the culler property:

  • Threshold determines how bit an object must be in the screen to stream it.
  • maxHiddenTime determines how long an object must be lost to remove it from the scene.
  • maxLostTime determines how long an object must be lost to remove it from memory.
loader.culler.threshold = 10;
loader.culler.maxHiddenTime = 1000;
loader.culler.maxLostTime = 40000;

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


This is it! Now you should be able to stream your own IFC models and open them anywhere, no matter how big they are! 💪 We will keep improving and making this API more powerful to handle any model on any device smoothly.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/LengthMeasurement/index.html b/build/Tutorials/Components/Front/LengthMeasurement/index.html index e09d8b6c..5db35685 100644 --- a/build/Tutorials/Components/Front/LengthMeasurement/index.html +++ b/build/Tutorials/Components/Front/LengthMeasurement/index.html @@ -4,14 +4,14 @@ LengthMeasurement | That Open Docs - +
Skip to main content

LengthMeasurement

Source

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

📐 Measuring lengths


Space control is one of the most important elements of BIM applications. In this tutorial, you'll learn how to expose a length measurement tool to your end users. We will import:

  • three to create some 3D items.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • @thatopen/ui to add some simple and cool UI menus.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as OBC from "@thatopen/components";
import * as THREE from "three";
import * as BUI from "@thatopen/ui";
import * as OBCF from "@thatopen/components-front";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBCF.PostproductionRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.PostproductionRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🎲 Creating a Cube Mesh


For this tutorial we will use a Cube, you can add any geometry as per your preference. We will create a Cube with 3x3x3 dimensions and use red color for the material.

const cubeGeometry = new THREE.BoxGeometry(3, 3, 3);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0, 1.5, 0);
world.scene.three.add(cube);
world.meshes.add(cube);

🛠️ Getting the length measurements


First, let's get an instance of the length measurement component and initialize it:

const dimensions = components.get(OBCF.LengthMeasurement);
dimensions.world = world;
dimensions.enabled = true;
dimensions.snapDistance = 1;

🖱️ Setting up mouse events


Now, we'll define how to create the length dimensions. In this case, we'll keep it simple and use the double click event of the container HTML element.

container.ondblclick = () => dimensions.create();

🧹 Deleting the Dimensions


Now that we know how to make multiple dimensions, we'll learn how to delete them when necessary. Dimensions can be removed using the deleteAll() method, which deletes all the created dimensions. You can also use delete to just remove the dimension under the mouse cursor. Again, we'll keep it simple and bind this event to the keydown event. Specifically, it will fire when the user presses the Delete or Backspace key.

window.onkeydown = (event) => {
if (event.code === "Delete" || event.code === "Backspace") {
dimensions.delete();
}
};

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to have some control over the dimensions we create. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Length Measurement Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">
<bim-label>Create dimension: Double click</bim-label>
<bim-label>Delete dimension: Delete</bim-label>
</bim-panel-section>

<bim-panel-section collapsed label="Others">
<bim-checkbox checked label="Dimensions enabled"
@change="${({ target }: { target: BUI.Checkbox }) => {
dimensions.enabled = target.value;
}}">
</bim-checkbox>
<bim-checkbox checked label="Dimensions visible"
@change="${({ target }: { target: BUI.Checkbox }) => {
dimensions.visible = target.value;
}}">
</bim-checkbox>

<bim-color-input
label="Dimensions Color" color="#202932"
@input="${({ target }: { target: BUI.ColorInput }) => {
dimensions.color.set(target.color);
}}">
</bim-color-input>

<bim-button label="Delete all"
@click="${() => {
dimensions.deleteAll();
}}">
</bim-button>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created an app that can create and delete length measurements on any 3D object. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/Marker/index.html b/build/Tutorials/Components/Front/Marker/index.html index 23ee57b6..6b649a6a 100644 --- a/build/Tutorials/Components/Front/Marker/index.html +++ b/build/Tutorials/Components/Front/Marker/index.html @@ -4,13 +4,13 @@ Marker | That Open Docs - +
Skip to main content

Marker

Source

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

🖼️ 2D inside 3D


Sometimes we need to see a 2D element inside our 3D world. For instance, imagine a Digital Twin application that has some icons representing a set of measurement devices inside a building. In this tutorial, we'll learn to use the Marker, which will allow us to easily create and cluster 2D elements inside the 3D scene.

2D inside 3D?

We will achieve this using Three.js CSS2DElements. They allow you to "embed" any HTML element in your 3D scene, automatically adjusting it's position to the 3D camera.

In this tutorial, we will import:

  • three to create some 3D items.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";
import Stats from "stats.js";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBCF.RendererWith2D
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.RendererWith2D(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

container.appendChild(world.renderer.three2D.domElement);

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🖌️ Creating the marker


Now we will get the marker instance. The threshold is the minimum distance that the marker will use to "cluster" the 2D elements together. You can create elements that are not clustered by defining them as static in the create method. You can disable clustering alltogether by using the autoCluster option.

const marker = components.get(OBCF.Marker);

marker.threshold = 10;

✨ Creating 2D elements


Now we will create a bunch of 2D elements in random positions using the create method.

for (let i = 0; i < 20; i++) {
const x = Math.random() * 5;
const y = Math.random() * 5;
const z = Math.random() * 5;
marker.create(world, "🚀", new THREE.Vector3(x, y, z),);
}

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that can display 2D elements inside the 3D scene. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/Plans/index.html b/build/Tutorials/Components/Front/Plans/index.html index 8f2494c5..f63c6ad7 100644 --- a/build/Tutorials/Components/Front/Plans/index.html +++ b/build/Tutorials/Components/Front/Plans/index.html @@ -4,13 +4,13 @@ Plans | That Open Docs - +
Skip to main content

Plans

Source

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

📐 Navigating floorplans


Floorplans are one of the most common ways of navigating BIM models, as they have been the most commonly used document before the digital era. In this tutorial, you'll learn how to do it with our libraries.

Floorplans?

Even though the BIM model defines the building perfectly, architects and engineers are still used to the clipped 2D representation that we have used for decades.

In this tutorial, we will import:

  • three to create some 3D items.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";
import * as BUI from "@thatopen/ui";
import Stats from "stats.js";

🌎 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. Notice how we use the PostproductionRenderer in this case.

const container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.OrthoPerspectiveCamera,
OBCF.PostproductionRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.PostproductionRenderer(components, container);
world.camera = new OBC.OrthoPerspectiveCamera(components);

// @ts-ignore
world.camera._aaaaa = "heyyyy";

world.renderer.postproduction.enabled = true;
world.renderer.postproduction.customEffects.outlineEnabled = true;

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

const grids = components.get(OBC.Grids);
grids.config.color.setHex(0x666666);
const grid = grids.create(world);
grid.three.position.y -= 1;
world.renderer.postproduction.customEffects.excludedMeshes.push(grid.three);

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = components.get(OBC.FragmentsManager);
const file = await fetch(
"https://thatopen.github.io/engine_components/resources/small.frag",
);
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

const propsFile = await fetch(
"https://thatopen.github.io/engine_components/resources/small.json",
);
const propsData = await propsFile.json();
model.setLocalProperties(propsData);

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🖼️ Getting the plans


Now, we will get an instance of the plans component and automatically generate the all the floor plans of the BIM model we just loaded.

const plans = components.get(OBCF.Plans);
plans.world = world;
await plans.generate(model);

🔦 Setting up highlighting


Now, let's set up highlighting so that the user can hover and select items on the BIM model.

Highlighter?

If you are not familiar with highlighter, check out its specific tutorial!

const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });

🐈‍⬛ Setting up culling


Now, let's set up culling so that our scene becomes even more efficient.

Culling?

If you are not familiar with culling, check out its specific tutorial!

const cullers = components.get(OBC.Cullers);
const culler = cullers.create(world);
for (const fragment of model.items) {
culler.add(fragment.mesh);
}

culler.needsUpdate = true;

world.camera.controls.addEventListener("sleep", () => {
culler.needsUpdate = true;
});

🖌️ Defining styles


Next, we need to define how we want the floorplans to look like. For that, we'll need to create a bunch of clipping styles, so that the walls and slabs have a thick section line and a filling, whereas the doors and windows have a thin section line. Of course, we also need to classifier to split the model to categories.

Clipping? Classifier?

If you are not familiar with the edges clipper or the classifier, check out that tutorial for more details about it!

So let's create both:

const classifier = components.get(OBC.Classifier);
const edges = components.get(OBCF.ClipEdges);

Now we will classify the model by model (to get all entities) and by entity (to split it by IFC category). Then we'll create some groups:

  • All the items in this model.
  • All the walls in this model.
  • All the doors, windows, plates and members in this model.
classifier.byModel(model.uuid, model);
classifier.byEntity(model);

const modelItems = classifier.find({ models: [model.uuid] });

const thickItems = classifier.find({
entities: ["IFCWALLSTANDARDCASE", "IFCWALL"],
});

const thinItems = classifier.find({
entities: ["IFCDOOR", "IFCWINDOW", "IFCPLATE", "IFCMEMBER"],
});

Awesome! Now, to create a style called "thick" for the walls, we can do the following:

const grayFill = new THREE.MeshBasicMaterial({ color: "gray", side: 2 });
const blackLine = new THREE.LineBasicMaterial({ color: "black" });
const blackOutline = new THREE.MeshBasicMaterial({
color: "black",
opacity: 0.5,
side: 2,
transparent: true,
});

edges.styles.create(
"thick",
new Set(),
world,
blackLine,
grayFill,
blackOutline,
);

for (const fragID in thickItems) {
const foundFrag = fragments.list.get(fragID);
if (!foundFrag) continue;
const { mesh } = foundFrag;
edges.styles.list.thick.fragments[fragID] = new Set(thickItems[fragID]);
edges.styles.list.thick.meshes.add(mesh);
}

Creating a style called "thin" for the rest follows the same pattern:

edges.styles.create("thin", new Set(), world);

for (const fragID in thinItems) {
const foundFrag = fragments.list.get(fragID);
if (!foundFrag) continue;
const { mesh } = foundFrag;
edges.styles.list.thin.fragments[fragID] = new Set(thinItems[fragID]);
edges.styles.list.thin.meshes.add(mesh);
}

Finally, let's update the edges to apply these changes.

await edges.update(true);

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to control the navigation across floor plans. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Plans Tutorial" class="options-menu">
<bim-panel-section collapsed name="floorPlans" label="Plan list">
</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

Next, we will add a button for each floor plan, so that when clicking on that button, we navigate to it and the look of the model becomes more "floorplan-like" (black and white with outlines):

const minGloss = world.renderer!.postproduction.customEffects.minGloss;

const whiteColor = new THREE.Color("white");

const panelSection = panel.querySelector(
"bim-panel-section[name='floorPlans']",
) as BUI.PanelSection;

for (const plan of plans.list) {
const planButton = BUI.Component.create<BUI.Checkbox>(() => {
return BUI.html`
<bim-button checked label="${plan.name}"
@click="${() => {
world.renderer!.postproduction.customEffects.minGloss = 0.1;
highlighter.backupColor = whiteColor;
classifier.setColor(modelItems, whiteColor);
world.scene.three.background = whiteColor;
plans.goTo(plan.id);
}}">
</bim-button>
`;
});
panelSection.append(planButton);
}

Finally, we will add a last button to exit the floor plan mode, going back to the 3D view and making the appearance of the scene go back to normal.

const defaultBackground = world.scene.three.background;

const exitButton = BUI.Component.create<BUI.Checkbox>(() => {
return BUI.html`
<bim-button checked label="Exit"
@click="${() => {
highlighter.backupColor = null;
world.renderer!.postproduction.customEffects.minGloss = minGloss;
classifier.resetColor(modelItems);
world.scene.three.background = defaultBackground;
plans.exitPlanView();
}}">
</bim-button>
`;
});

panelSection.append(exitButton);

🎉 Wrap up


That's it! You have created an app that can generate all the floorplans of a BIM model and navigate across them in 2D mode with a nice black and white look. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/PostproductionRenderer/index.html b/build/Tutorials/Components/Front/PostproductionRenderer/index.html index c795807f..ff7a8e91 100644 --- a/build/Tutorials/Components/Front/PostproductionRenderer/index.html +++ b/build/Tutorials/Components/Front/PostproductionRenderer/index.html @@ -4,13 +4,13 @@ PostproductionRenderer | That Open Docs - +
Skip to main content

PostproductionRenderer

Source

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

🎥 Great graphics


Postproduction effects enrich your 3D scenes. There are several post-production effects, such as adding shadows, rendering outlines, adding ambient occlusion and applying bloom, that can enhance and make your scene look cool. In this tutorial, you'll learn how to do it.

Postproduction?

The simple Three.js renderer isn't bad, but it's pretty basic. Postproduction are a collection of effects you can add to your scene to make it look much better. Of course, this means consuming more resources, but luckily for us, the power of devices is proportional to the size of its screen, so we should be able to enjoy this beauty in most scene even from our smartphones!

In this tutorial, we will import:

  • three to create some 3D items.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";

🌎 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. Notice how we use the PostproductionRenderer in this case.

const container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBCF.PostproductionRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.camera = new OBC.SimpleCamera(components);
world.renderer = new OBCF.PostproductionRenderer(components, container);

world.scene.three.background = null;

components.init();

world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);

world.scene.setup();

const grids = components.get(OBC.Grids);
grids.config.color.set(0x666666);
const grid = grids.create(world);

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = new OBC.FragmentsManager(components);
const file = await fetch(
"https://thatopen.github.io/engine_components/resources/small.frag",
);
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

🎬 Turning on the Postproduction


Now we will activate the postproduction effect and enable the visibility for postproduction layer.

const { postproduction } = world.renderer;
postproduction.enabled = true;
postproduction.customEffects.excludedMeshes.push(grid.three);
const ao = postproduction.n8ao.configuration;

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to control some of the most common postproduction parameters. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Postproduction Tutorial" class="options-menu">
<bim-panel-section collapsed label="Gamma">
<bim-checkbox checked label="Gamma Correction"
@change="${({ target }: { target: BUI.Checkbox }) => {
postproduction.setPasses({ gamma: target.value });
}}">
</bim-checkbox>
</bim-panel-section>

<bim-panel-section collapsed label="Custom effects" >
<bim-checkbox checked label="Custom effects"
@change="${({ target }: { target: BUI.Checkbox }) => {
postproduction.setPasses({ custom: target.value });
}}">
</bim-checkbox>

<bim-checkbox checked label="Gamma Correction"
@change="${({ target }: { target: BUI.Checkbox }) => {
postproduction.customEffects.glossEnabled = target.value;
}}">
</bim-checkbox>

<bim-number-input
slider step="0.01" label="Opacity"
value="${postproduction.customEffects.opacity}" min="0" max="1"
@change="${({ target }: { target: BUI.NumberInput }) => {
postproduction.customEffects.opacity = target.value;
}}">
</bim-number-input>

<bim-number-input
slider step="0.1" label="Tolerance"
value="${postproduction.customEffects.tolerance}" min="0" max="6"
@change="${({ target }: { target: BUI.NumberInput }) => {
postproduction.customEffects.tolerance = target.value;
}}">
</bim-number-input>

<bim-color-input label="Line color"
@input="${({ target }: { target: BUI.ColorInput }) => {
const color = new THREE.Color(target.value.color);
postproduction.customEffects.lineColor = color.getHex();
}}">
</bim-color-input>

<bim-number-input
slider label="Gloss exponent" step="0.1"
value="${postproduction.customEffects.glossExponent}" min="0" max="5"
@change="${({ target }: { target: BUI.NumberInput }) => {
postproduction.customEffects.glossExponent = target.value;
}}">
</bim-number-input>

<bim-number-input
slider label="Max gloss" step="0.05"
value="${postproduction.customEffects.maxGloss}" min="-2" max="2"
@change="${({ target }: { target: BUI.NumberInput }) => {
postproduction.customEffects.maxGloss = target.value;
}}">
</bim-number-input>

<bim-number-input
slider label="Min gloss" step="0.05"
value="${postproduction.customEffects.minGloss}" min="-2" max="2"
@change="${({ target }: { target: BUI.NumberInput }) => {
postproduction.customEffects.minGloss = target.value;
}}">
</bim-number-input>

</bim-panel-section>

<bim-panel-section collapsed label="Ambient Oclussion">

<bim-checkbox label="AO enabled"
@change="${({ target }: { target: BUI.Checkbox }) => {
postproduction.setPasses({ ao: target.value });
}}">
</bim-checkbox>

<bim-checkbox checked label="Half resolution"
@change="${({ target }: { target: BUI.Checkbox }) => {
ao.halfRes = target.value;
}}">
</bim-checkbox>

<bim-checkbox label="Screen space radius"
@change="${({ target }: { target: BUI.Checkbox }) => {
ao.screenSpaceRadius = target.value;
}}">
</bim-checkbox>


<bim-color-input label="AO color"
@input="${({ target }: { target: BUI.ColorInput }) => {
const color = new THREE.Color(target.value.color);
ao.color.r = color.r;
ao.color.g = color.g;
ao.color.b = color.b;
}}">
</bim-color-input>

<bim-number-input
slider label="AO Samples" step="1"
value="${ao.aoSamples}" min="1" max="16"
@change="${({ target }: { target: BUI.NumberInput }) => {
ao.aoSamples = target.value;
}}">
</bim-number-input>

<bim-number-input
slider label="Denoise Samples" step="1"
value="${ao.denoiseSamples}" min="1" max="16"
@change="${({ target }: { target: BUI.NumberInput }) => {
ao.denoiseSamples = target.value;
}}">
</bim-number-input>

<bim-number-input
slider label="Denoise Radius" step="1"
value="${ao.denoiseRadius}" min="0" max="100"
@change="${({ target }: { target: BUI.NumberInput }) => {
ao.denoiseRadius = target.value;
}}">
</bim-number-input>

<bim-number-input
slider label="AO Radius" step="1"
value="${ao.aoRadius}" min="0" max="16"
@change="${({ target }: { target: BUI.NumberInput }) => {
ao.aoRadius = target.value;
}}">
</bim-number-input>

<bim-number-input
slider label="Distance falloff" step="1"
value="${ao.distanceFalloff}" min="0" max="16"
@change="${({ target }: { target: BUI.NumberInput }) => {
ao.distanceFalloff = target.value;
}}">
</bim-number-input>

<bim-number-input
slider label="Intensity" step="1"
value="${ao.intensity}" min="0" max="16"
@change="${({ target }: { target: BUI.NumberInput }) => {
ao.intensity = target.value;
}}">
</bim-number-input>

</bim-panel-section>

</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created an app that looks great thanks to postproduction and exposes a menu to allow the user control it in real time.

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/ShadowDropper/index.html b/build/Tutorials/Components/Front/ShadowDropper/index.html index f849e39e..1bae60b0 100644 --- a/build/Tutorials/Components/Front/ShadowDropper/index.html +++ b/build/Tutorials/Components/Front/ShadowDropper/index.html @@ -4,13 +4,13 @@ ShadowDropper | That Open Docs - +
Skip to main content

ShadowDropper

Source

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

⛱️ Dropping shadows


Shadows are usually not very performant. But there's a small trick that allows us to have a neat projected shadow under our models that make our apps look great with almost zero performance impact. In this tutorial, you'll learn how to use it.

Dropped shadows?

Generally, there are 2 types of shadows: self shadows (the ones that an object project on itself) and projected shadows (the ones that are casted to other objects). Both are computationally expensive to compute, but in this tutorial we'll bake and display a very neat shadow that has no performance impact.

In this tutorial, we will import:

  • web-ifc to get some IFC items.
  • @thatopen/ui to add some simple and cool UI menus.
  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import * as THREE from "three";
import * as OBC from "@thatopen/components";
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as OBCF from "@thatopen/components-front";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBCF.RendererWith2D
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.RendererWith2D(components, container);
world.camera = new OBC.SimpleCamera(components);

world.scene.setup();

components.init();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

container.appendChild(world.renderer.three2D.domElement);

const grids = components.get(OBC.Grids);
grids.config.color.setHex(0xdddddd);
grids.create(world);

🎲 Creating a Cube Mesh


Let's start by adding a simple Cube to our scene.

const cubeGeometry = new THREE.BoxGeometry(3, 3, 3);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0, 1.5, 0);
world.scene.three.add(cube);
world.meshes.add(cube);

Next, we'll make the background of our scene white to make the shadow more visible. Of course, this is not compulsory, but shadows are a bit harder to see on darker backgrounds.

world.scene.three.background = new THREE.Color("white");

🌚 Adding Beautiful Shadow


Now, to add a shadow, we can simply get an instance of the shadow dropper, which will make all the heavy lifting for us:

const shadows = components.get(OBCF.ShadowDropper);
shadows.shadowExtraScaleFactor = 15;
shadows.shadowOffset = 0.1;

🎨 Rendering the shadow


Now, we will use shadow dropper to create shadows for the cube we created before. Of course, this would also work for your BIM models exactly the same way.

const shadowID = "example";
shadows.create([cube], shadowID, world);

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🧩 Adding some UI


We will use the @thatopen/ui library to add some simple and cool UI elements to our app. First, we need to call the init method of the BUI.Manager class to initialize the library:

BUI.Manager.init();

Now we will add some UI to control and re-render the shadow we have created. For more information about the UI library, you can check the specific documentation for it!

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Shadow dropper Tutorial" class="options-menu">
<bim-panel-section collapsed label="Controls">

<bim-number-input
slider label="Extra scale factor" step="1"
value="${shadows.shadowExtraScaleFactor}" min="0" max="20"
@change="${({ target }: { target: BUI.NumberInput }) => {
shadows.shadowExtraScaleFactor = target.value;
shadows.deleteShadow(shadowID);
shadows.create([cube], shadowID, world);
}}">
</bim-number-input>

<bim-number-input
slider label="Amount" step="1"
value="${shadows.amount}" min="0" max="20"
@change="${({ target }: { target: BUI.NumberInput }) => {
shadows.amount = target.value;
shadows.deleteShadow(shadowID);
shadows.create([cube], shadowID, world);
}}">
</bim-number-input>

<bim-number-input
slider label="Shadow offset" step="0.01"
value="${shadows.shadowOffset}" min="0" max="3"
@change="${({ target }: { target: BUI.NumberInput }) => {
shadows.shadowOffset = target.value;
shadows.deleteShadow(shadowID);
shadows.create([cube], shadowID, world);
}}">
</bim-number-input>

</bim-panel-section>
</bim-panel>
`;
});

document.body.append(panel);

And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable.

const button = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-button class="phone-menu-toggler" icon="solar:settings-bold"
@click="${() => {
if (panel.classList.contains("options-menu-visible")) {
panel.classList.remove("options-menu-visible");
} else {
panel.classList.add("options-menu-visible");
}
}}">
</bim-button>
`;
});

document.body.append(button);

🎉 Wrap up


That's it! You have created a scene where you can create a super efficient projected shadow on any object. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/Front/VolumeMeasurement/index.html b/build/Tutorials/Components/Front/VolumeMeasurement/index.html index d33ac252..59fb87c0 100644 --- a/build/Tutorials/Components/Front/VolumeMeasurement/index.html +++ b/build/Tutorials/Components/Front/VolumeMeasurement/index.html @@ -4,14 +4,14 @@ VolumeMeasurement | That Open Docs - +
Skip to main content

VolumeMeasurement

Source

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

📐 Measuring volumes


Space control is one of the most important elements of BIM applications. In this tutorial, you'll learn how to expose an volume measurement tool to your end users. We will import:

  • @thatopen/components to set up the barebone of our app.
  • @thatopen/components-front to use some frontend-oriented components.
  • Stats.js (optional) to measure the performance of our app.
import Stats from "stats.js";
import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";

🌎 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 container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBCF.PostproductionRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBCF.PostproductionRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0);

world.scene.setup();

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

We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!

world.scene.three.background = null;

🧳 Loading a BIM model


We'll start by adding a BIM model to our scene. That model is already converted to fragments, so it will load much faster than if we loaded the IFC file.

Fragments?

If you are not familiar with fragments, check out the IfcLoader tutorial!

const fragments = new OBC.FragmentsManager(components);
const file = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");
const data = await file.arrayBuffer();
const buffer = new Uint8Array(data);
const model = fragments.load(buffer);
world.scene.three.add(model);

🛠️ Getting the volume measurements


First, let's get an instance of the volume measurement component and initialize it.

const dimensions = components.get(OBCF.VolumeMeasurement);
dimensions.world = world;
dimensions.enabled = true;

🔦 Getting the highlighter


Now, let's get an instance of the highlighter component and initialize it to be able to highlight the computed volume.

Highlighter?

If you are not familiar with the highlighter component, check out the highlighter tutorial!

const highlighter = components.get(OBCF.Highlighter);
highlighter.setup({ world });

Now we'll simply take the computed volume and log it in the console. Also, when the highlighter is cleared, we'll also clear the volume dimension.

highlighter.events.select.onHighlight.add((event) => {
const volume = dimensions.getVolumeFromFragments(event);
console.log(volume);
});

highlighter.events.select.onClear.add(() => {
dimensions.clear();
});

⏱️ Measuring the performance (optional)


We'll use the Stats.js to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control.

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🎉 Wrap up


That's it! You have created an app that can create and delete volume dimensions on any 3D object. Congratulations!

- + \ No newline at end of file diff --git a/build/Tutorials/Components/index.html b/build/Tutorials/Components/index.html index a49d49ad..04ab5d16 100644 --- a/build/Tutorials/Components/index.html +++ b/build/Tutorials/Components/index.html @@ -4,7 +4,7 @@ Components | That Open Docs - + @@ -12,7 +12,7 @@
Skip to main content

Components

TOC|documentation|demo|community|npm package

cover

Open BIM Components

NPM Package NPM Package Tests

This library is a collection of BIM tools based on Three.js and other libraries. It includes pre-made features to easily build browser-based 3D BIM applications, such as postproduction, dimensions, floorplan navigation, DXF export and much more.

Packages

This library contains 2 packages:

@thatopen/components - The core functionality. Compatible both with browser and Node.js environments.

@thatopen/components-front - Features exclusive for browser environments.

Usage

You need to be familiar with Three.js API to be able to use this library effectively. In the following example, we will create a cube in a 3D scene that can be navigated with the mouse or touch events. You can see the full example here and the deployed app here.

/* eslint import/no-extraneous-dependencies: 0 */

import * as THREE from "three";
import * as OBC from "../..";

const container = document.getElementById("container")!;

const components = new OBC.Components();

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

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

const material = new THREE.MeshLambertMaterial({ color: "#6528D7" });
const geometry = new THREE.BoxGeometry();
const cube = new THREE.Mesh(geometry, material);
world.scene.three.add(cube);

world.scene.setup();

world.camera.controls.setLookAt(3, 3, 3, 0, 0, 0);
- + \ No newline at end of file diff --git a/build/Tutorials/UserInterface/Core/Component/index.html b/build/Tutorials/UserInterface/Core/Component/index.html index 5a16923f..6eca5191 100644 --- a/build/Tutorials/UserInterface/Core/Component/index.html +++ b/build/Tutorials/UserInterface/Core/Component/index.html @@ -4,7 +4,7 @@ Component | That Open Docs - + @@ -13,7 +13,7 @@ The Component class has a static method to create functional components (UI defined as a function) that can be updated anytime. The method is Component.create.

note

Despite the Component is a class that can be instantiated or extended, from a developer perspective using the library is most likely it will only use the create method.

Creating an stateless component

To start learning how to create custom components, let's create a custom component that uses the panel section:

const statelessPanelSection = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel-section label="Stateless Panel Section">
<bim-color-input label="Color"></bim-color-input>
</bim-panel-section>
`;
});
danger

Remember to first call Manager.init() before anything else!

Component.create requires you to provide a function declaration that returns an HTML string made with the html tag function, and the result of the function is the HTMLElement it self.

note

Tag functions are special declarations that are always set before a template literal to process the string.

Did you notice the component is named statelessPanelSection? Well, the reason is because components can have an optional state. Technically speaking, that makes the create method to have two overloads: one for components with state (statefull) and another for components without state (stateless). The main difference is that statefull components lets you update them with new states (so the UI component will efficiently re-render and display new data) while stateless components never needs to be updated as they are static. The component we just created is stateless, because it doesn't have any state in which its user interface depends on.

Creating a statefull component

Now, let's take a look at how to create a component that can be updated based on state changes:

interface PanelSectionUIState {
label: string;
counter: number;
}

const [statefullPanelSection, updateStatefullPanelSection] =
BUI.Component.create<BUI.PanelSection, PanelSectionUIState>(
(state: PanelSectionUIState) => {
const { label, counter } = state;
const msg = `This panel section has been updated ${counter} ${counter === 1 ? "time" : "times"}`;
return BUI.html`
<bim-panel-section label=${label}>
<bim-label>${msg}</bim-label>
</bim-panel-section>
`;
},
{ label: "Statefull Panel Section", counter: 0 },
);

When you pass an object as the argument in your create function, the component has now become statefull. As you see, there are a couple of differences between the stateless and statefull components:

  1. The statefull component requires an state object (it must be an object) to be passed in the function declaration. Think on this as the classic properties object you pass to a component in a framework like React.
  2. When the component is statefull, Component.create must have a second argument to specify the initial state of the component.
  3. Now, Component.create does not return the HTMLElement it self, but an array where the first item is the HTMLElement and second is a function to update the component based on an updated state. Think on this as when you use the useState hook in frameworks like React.
    note

    As for now, a statefull component can't update itself! However, you can nest other components that updates the state of some other.

Nesting components

Now, in order to see the two components in action, let's create a third component to integrate (nest) the two previous:

const panel = BUI.Component.create<BUI.Panel>(() => {
let counter = 0;
const onUpdateBtnClick = () => {
counter++;
if (counter >= 5) {
updateStatefullPanelSection({
label: "Powered Statefull Panel Section 💪",
counter,
});
} else {
updateStatefullPanelSection({ counter });
}
};

return BUI.html`
<bim-panel label="My Panel">
<bim-panel-section label="Update Functions">
<bim-button @click=${onUpdateBtnClick} label="Update Statefull Section"></bim-button>
</bim-panel-section>
${statelessPanelSection}
${statefullPanelSection}
</bim-panel>
`;
});

As you see, the create function doesn't need to immediately return the HTML, but you can also do any other logic you want inside. In this case, the logic adds a listener to bim-button in order to update the state of the statefullPanelSection we created earlier. A couple of things to notice here:

  1. You're not forced to update the whole component state, but just the things you need. In this case, we just updated the panel section label in case the counter is greater than or equals to 5. However, in this case the counter is always updated.
  2. Despite we updated the component inside the logic of the panel, you can update your statefull components from anywhere in your code by just using the update function.
  3. You can nest any component in any other: statefull in stateless, stateless in stateless, etc. In this case, panel is a stateless component, but it has an statefull component inside. That means contents of a stateless component can be updated but because that content is a statefull component.
  4. You see how we integrated the two previous components into the panel? Yes, its as easy as adding them as an expression (${statelessPanelSection} and ${statefullPanelSection} in this case).
    tip

    In order to know the syntax you can write inside the template literal tagged by the html function, look at the lit-html documentation.

    Finally, you can add your panel component anywhere you want as its an HTMLElement just like any other!
document.body.append(panel);

Congratulations! You already know how to create your own custom reactive components. Don't stop learning! Take a look at more tutorials in the documentation 🙂.

tip

The complementary packages of the library such as @thatopen/ui-obc have premade functional components just like the ones we've learned to create in this tutorial, so you don't need to bother to create them by yourself 😉

- + \ No newline at end of file diff --git a/build/Tutorials/UserInterface/OBC/ClassificationsTree/index.html b/build/Tutorials/UserInterface/OBC/ClassificationsTree/index.html index e4f77ebd..79457ad2 100644 --- a/build/Tutorials/UserInterface/OBC/ClassificationsTree/index.html +++ b/build/Tutorials/UserInterface/OBC/ClassificationsTree/index.html @@ -4,7 +4,7 @@ ClassificationsTree | That Open Docs - + @@ -12,7 +12,7 @@
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!

Creating the classifications tree

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

const [classificationsTree, updateClassificationsTree] =
CUI.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);

const classifications = {
Entities: ["entities"],
"Predefined Types": ["predefinedTypes"],
};

updateClassificationsTree({ classifications });
});

The classifications value is just an object where they keys are the names in the tree, and the values are the orders in which you want to group the elements. So, for example, "Entities" groups the elements based on their entities and then based on their predefined types. Needless to say, the classifications need to be computed before they can be used on the tree. You can check the system names from classifier.list. 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] = CUI.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. 🥳

- + \ No newline at end of file diff --git a/build/Tutorials/UserInterface/OBC/ElementProperties/index.html b/build/Tutorials/UserInterface/OBC/ElementProperties/index.html index 0acf3a29..2e2c5b7e 100644 --- a/build/Tutorials/UserInterface/OBC/ElementProperties/index.html +++ b/build/Tutorials/UserInterface/OBC/ElementProperties/index.html @@ -4,13 +4,13 @@ ElementProperties | That Open Docs - +
Skip to main content

ElementProperties

Source

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

Displaying data the simplest way 🔥🔥


What is a good BIM app if you don't give users a nice way to visualize its model properties, right? Well, hold tight as here you will learn all you need to know in order to use the power of UI Components to accomplish that!

Loading a model and computing it's relations

First things first... let's load a model 👇

const ifcLoader = components.get(OBC.IfcLoader);
await ifcLoader.setup();
const file = await fetch(
"https://thatopen.github.io/engine_ui-components/resources/small.ifc",
);
const buffer = await file.arrayBuffer();
const typedArray = new Uint8Array(buffer);
const model = await ifcLoader.load(typedArray);
world.scene.three.add(model);
tip

You don't need to add the model into the scene to display its properties. However, as we are going to display the properties for each selected element, then having the model into the scene is obvious, right?

Now, in order to get the most out of the properties table, you need to calculate the relations index of your model. To do it, you will need to use the IfcRelationsIndexer component from @thatopen/components to speed up the process.

const indexer = components.get(OBC.IfcRelationsIndexer);
await indexer.process(model);

Once the relations are processed, the Element Properties component has everything it needs in order to display the properties in a cool way 😎.

Creating the properties table

Let's create an instance of the functional component, like this:

const [propertiesTable, updatePropertiesTable] = CUI.tables.elementProperties({
components,
fragmentIdMap: {},
});

propertiesTable.preserveStructureOnFilter = true;
propertiesTable.indentationInText = false;
tip

The elementProperties functional component is a simplified version that shows any model entity data. However, if you like a more complete properties table, use the entityAttributes component.

Cool! properties table created. Then after, let's tell the properties table to update each time the user makes a selection over the model. For it, we will use the highlighter from @thatopen/components-front:

const highlighter = components.get(OBF.Highlighter);
highlighter.setup({ world });

highlighter.events.select.onHighlight.add((fragmentIdMap) => {
updatePropertiesTable({ fragmentIdMap });
});

highlighter.events.select.onClear.add(() =>
updatePropertiesTable({ fragmentIdMap: {} }),
);

Creating a panel to append the table

Allright! Let's now create a BIM Panel to control some aspects of the properties table and to trigger some functionalities like expanding the rows children and copying the values to TSV, so you can paste your element values inside a spreadsheet application 😉

const propertiesPanel = BUI.Component.create(() => {
const onTextInput = (e: Event) => {
const input = e.target as BUI.TextInput;
propertiesTable.queryString = input.value !== "" ? input.value : null;
};

const expandTable = (e: Event) => {
const button = e.target as BUI.Button;
propertiesTable.expanded = !propertiesTable.expanded;
button.label = propertiesTable.expanded ? "Collapse" : "Expand";
};

const copyAsTSV = async () => {
await navigator.clipboard.writeText(propertiesTable.tsv);
};

return BUI.html`
<bim-panel label="Properties">
<bim-panel-section label="Element Data">
<div style="display: flex; gap: 0.5rem;">
<bim-button @click=${expandTable} label=${propertiesTable.expanded ? "Collapse" : "Expand"}></bim-button>
<bim-button @click=${copyAsTSV} label="Copy as TSV"></bim-button>
</div>
<bim-text-input @input=${onTextInput} placeholder="Search Property" debounce="250"></bim-text-input>
${propertiesTable}
</bim-panel-section>
</bim-panel>
`;
});

Finally, let's create a BIM Grid element and provide both the panel and the viewport to display everything.

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

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

Congratulations! You have now created a fully working properties table for your app in less than 5 minutes of work. Keep going with more tutorials! 💪

- + \ No newline at end of file diff --git a/build/Tutorials/UserInterface/OBC/EntityAttributes/index.html b/build/Tutorials/UserInterface/OBC/EntityAttributes/index.html index d5488776..3078defb 100644 --- a/build/Tutorials/UserInterface/OBC/EntityAttributes/index.html +++ b/build/Tutorials/UserInterface/OBC/EntityAttributes/index.html @@ -4,13 +4,13 @@ EntityAttributes | That Open Docs - +
Skip to main content

EntityAttributes

Source

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

Displaying data the advanced way 🔥🔥


What is a good BIM app if you don't give users a nice way to visualize its model properties, right? Well, hold tight as here you will learn all you need to know in order to use the power of UI Components to accomplish that!

Loading a model and computing it's relations

First things first... let's load a model 👇

import * as WEBIFC from "web-ifc";
import * as BUI from "@thatopen/ui";
import * as OBC from "@thatopen/components";
import * as OBF from "@thatopen/components-front";
import * as CUI from "../..";

BUI.Manager.init();

const components = new OBC.Components();

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

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

const viewport = document.createElement("bim-viewport");
const rendererComponent = new OBC.SimpleRenderer(components, viewport);
world.renderer = rendererComponent;

const cameraComponent = new OBC.SimpleCamera(components);
world.camera = cameraComponent;
cameraComponent.controls.setLookAt(10, 5.5, 5, -4, -1, -6.5);

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

components.init();

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

You don't need to add the model into the scene to display its properties. However, as we are going to display the attributes for each selected element, then having the model into the scene is obvious, right?

Now, in order to get the most out of the entities table, you need to calculate the relations index of your model. To do it, you will need to use the IfcRelationsIndexer component from @thatopen/components to speed up the process.

const ifcLoader = components.get(OBC.IfcLoader);
await ifcLoader.setup();
const file = await fetch(
"https://thatopen.github.io/engine_ui-components/resources/small.ifc",
);
const buffer = await file.arrayBuffer();
const typedArray = new Uint8Array(buffer);
const model = await ifcLoader.load(typedArray);
world.scene.three.add(model);

Preconfiguring the table

The attributes table has some optional configurations. One of them is the ability to modify the styles of the cell value based on the attribute value (e.g., colorizing entities with a specific string in its name, or numeric values based on a codition ). For it, let's first create a simple base style that all our cell overwrites will share:

const indexer = components.get(OBC.IfcRelationsIndexer);
await indexer.process(model);

Then, let's create an object where the keys are the attribute values you want to overwrite its styles, and the values are functions that returns an html template result.

tip

If you want to learn more about the html template tag and how to use it, just take a look at the tutorial on how to make a custom component.

const baseStyle: Record<string, string> = {
padding: "0.25rem",
borderRadius: "0.25rem",
};

Keep in mind the step above is optional! Not needed for the table to work. Now its time to create the table using the predefine functional component that ships with the library 🙂

const tableDefinition: BUI.TableDataTransform = {
Entity: (entity) => {
let style = {};
if (entity === OBC.IfcCategoryMap[WEBIFC.IFCPROPERTYSET]) {
style = {
...baseStyle,
backgroundColor: "purple",
color: "white",
};
}
if (String(entity).includes("IFCWALL")) {
style = {
...baseStyle,
backgroundColor: "green",
color: "white",
};
}
return BUI.html`<bim-label style=${BUI.styleMap(style)}>${entity}</bim-label>`;
},
PredefinedType: (type) => {
const colors = ["#1c8d83", "#3c1c8d", "#386c19", "#837c24"];
const randomIndex = Math.floor(Math.random() * colors.length);
const backgroundColor = colors[randomIndex];
const style = { ...baseStyle, backgroundColor, color: "white" };
return BUI.html`<bim-label style=${BUI.styleMap(style)}>${type}</bim-label>`;
},
NominalValue: (value) => {
let style = {};
if (typeof value === "boolean" && value === false) {
style = { ...baseStyle, backgroundColor: "#b13535", color: "white" };
}
if (typeof value === "boolean" && value === true) {
style = { ...baseStyle, backgroundColor: "#18882c", color: "white" };
}
return BUI.html`<bim-label style=${BUI.styleMap(style)}>${value}</bim-label>`;
},
};

Cool! attributes table created. Then after, let's tell the attributes table to update each time the user makes a selection over the model. For it, we will use the Highlighter:

const [attributesTable, updateAttributesTable] = CUI.tables.entityAttributes({
components,
fragmentIdMap: {},
tableDefinition,
attributesToInclude: () => {
const attributes: any[] = [
"Name",
"ContainedInStructure",
"HasProperties",
"HasPropertySets",
(name: string) => name.includes("Value"),
(name: string) => name.startsWith("Material"),
(name: string) => name.startsWith("Relating"),
(name: string) => {
const ignore = ["IsGroupedBy", "IsDecomposedBy"];
return name.startsWith("Is") && !ignore.includes(name);
},
];
return attributes;
},
});

attributesTable.expanded = true;
attributesTable.indentationInText = true;
attributesTable.preserveStructureOnFilter = true;

Creating a panel to append the table

Allright! Let's now create a BIM Panel to control some aspects of the attributes table and to trigger some functionalities like copying the values to TSV or exporing the data to JSON 😉

const highlighter = components.get(OBF.Highlighter);
highlighter.setup({ world });

highlighter.events.select.onHighlight.add((fragmentIdMap) => {
updateAttributesTable({ fragmentIdMap });
});

highlighter.events.select.onClear.add(() =>
updateAttributesTable({ fragmentIdMap: {} }),
);

Finally, let's create a BIM Grid element and provide both the panel and the viewport to display everything.

const entityAttributesPanel = BUI.Component.create(() => {
const onSearchInput = (e: Event) => {
const input = e.target as BUI.TextInput;
attributesTable.queryString = input.value;
};

const onPreserveStructureChange = (e: Event) => {
const checkbox = e.target as BUI.Checkbox;
attributesTable.preserveStructureOnFilter = checkbox.checked;
};

const onExportJSON = () => {
attributesTable.downloadData("entities-attributes");
};

const onCopyTSV = async () => {
await navigator.clipboard.writeText(attributesTable.tsv);
alert(
"Table data copied as TSV in clipboard! Try to paste it in a spreadsheet app.",
);
};

const onAttributesChange = (e: Event) => {
const dropdown = e.target as BUI.Dropdown;
updateAttributesTable({
attributesToInclude: () => {
const attributes: any[] = [
...dropdown.value,
(name: string) => name.includes("Value"),
(name: string) => name.startsWith("Material"),
(name: string) => name.startsWith("Relating"),
(name: string) => {
const ignore = ["IsGroupedBy", "IsDecomposedBy"];
return name.startsWith("Is") && !ignore.includes(name);
},
];
return attributes;
},
});
};

return BUI.html`
<bim-panel>
<bim-panel-section label="Entity Attributes" fixed>
<div style="display: flex; gap: 0.5rem; justify-content: space-between;">
<div style="display: flex; gap: 0.5rem;">
<bim-text-input @input=${onSearchInput} type="search" placeholder="Search" debounce="250"></bim-text-input>
<bim-checkbox @change=${onPreserveStructureChange} label="Preserve Structure" inverted checked></bim-checkbox>
</div>
<div style="display: flex; gap: 0.5rem;">
<bim-dropdown @change=${onAttributesChange} multiple>
<bim-option label="Name" checked></bim-option>
<bim-option label="ContainedInStructure" checked></bim-option>
<bim-option label="ForLayerSet"></bim-option>
<bim-option label="LayerThickness"></bim-option>
<bim-option label="HasProperties" checked></bim-option>
<bim-option label="HasAssociations"></bim-option>
<bim-option label="HasAssignments"></bim-option>
<bim-option label="HasPropertySets" checked></bim-option>
<bim-option label="PredefinedType"></bim-option>
<bim-option label="Quantities"></bim-option>
<bim-option label="ReferencedSource"></bim-option>
<bim-option label="Identification"></bim-option>
<bim-option label="Prefix"></bim-option>
<bim-option label="LongName"></bim-option>
</bim-dropdown>
<bim-button @click=${onCopyTSV} icon="solar:copy-bold" tooltip-title="Copy TSV" tooltip-text="Copy the table contents as tab separated text values, so you can copy them into a spreadsheet."></bim-button>
<bim-button @click=${onExportJSON} icon="ph:export-fill" tooltip-title="Export JSON" tooltip-text="Download the table contents as a JSON file."></bim-button>
</div>
</div>
${attributesTable}
</bim-panel-section>
</bim-panel>
`;
});

Congratulations! You have now created a fully working advanced attributes table for your app in less than 10 minutes of work. Keep going with more tutorials! 💪

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

app.layout = "main";
document.body.append(app);
- + \ No newline at end of file diff --git a/build/Tutorials/UserInterface/OBC/ModelsList/index.html b/build/Tutorials/UserInterface/OBC/ModelsList/index.html index 1f95b173..53934c08 100644 --- a/build/Tutorials/UserInterface/OBC/ModelsList/index.html +++ b/build/Tutorials/UserInterface/OBC/ModelsList/index.html @@ -4,13 +4,13 @@ ModelsList | That Open Docs - +
Skip to main content

ModelsList

Source

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

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);
fragmentsManager.onFragmentsLoaded.add((model) => {
if (world.scene) world.scene.three.add(model);
});

Creating the models list component

Allright! Now that some basic events are setup, it's time to create a new fresh models list component:

const [modelsList] = CUI.tables.modelsList({ components });

Now that we have a brand new models list created, we need to add it to the HTML page. For it, let's create simple BIM panel component where we include the models list and also a pre-made IFC load button 👇

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

return BUI.html`
<bim-panel label="IFC Models">
<bim-panel-section label="Importing">
${loadIfcBtn}
</bim-panel-section>
<bim-panel-section icon="mage:box-3d-fill" label="Loaded Models">
${modelsList}
</bim-panel-section>
</bim-panel>
`;
});

Finally, let's append the BIM Panel to the page to see the models list 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 and dispose IFC models loaded into your app 🥳

- + \ No newline at end of file diff --git a/build/Tutorials/UserInterface/index.html b/build/Tutorials/UserInterface/index.html index 6aa8c5d7..39e8966a 100644 --- a/build/Tutorials/UserInterface/index.html +++ b/build/Tutorials/UserInterface/index.html @@ -4,7 +4,7 @@ UserInterface | That Open Docs - + @@ -13,7 +13,7 @@ All the implementation libraries need @thatopen/ui to be installed along with the respective packages they are giving UIs to. See the respective package.json files in each repository.

Why a monorepo? 🤷‍♀️

Easy, because we care about your final app bundle size. You see, the repositories that contains implementations of the UIComponents for different libraries, relies on the libraries to be installed in the project because they're required as peerDependencies. So, if we included all the pre-built UIComponents from @thatopen/ui-obc in the core library, you will always need to have @thatopen/components and @thatopen/components-front installed in your project even tough you're not using it.


Does these components works in my favorite framework? 🤔

Well... yes! You name it, React? Angular? Vue? Svelte? The answer is absolutely yes. Basically, you can use these componentes anywhere HTML is accepted. If you're wondering how is that possible, is becase we're using Web Components 🔥

If you're new to Web Components, no worries! Simply put, Web Components is a browser API that let's you define your own HTML tags (DOM Elements) to be used in the document. They define the look and behavior of the elements. Have you ever seen an HTML that looks something like this?

<div>
<unknown-tag />
</div>

As you may recall from your HTML knowledge, <unkown-tag /> is not somethings built-in in HTML... well, that's because is a Web Component! So the developer created it's own tag to use it in the document.

Web Components are extremely powerfull because they do mostly the same as the components you create in any framework, just they are framework agnostic and feel way more built-in. In other words, if you create a component in your framework you're not allowed to write the following directly in your HTML file:

<my-framework-component />

You always need to rely on your framework tools in order to render your component, so you must use JavaScript. However, if you create a Web Component you can use it in your HTML with nothing else needed.


[!IMPORTANT] Despite Web Components is a browser API, we used Lit to create the components as it makes the process way much easier. Also, we recommend checking your favorite framework documentation to implement web components, some of them needs a pretty basic setup to get up and running.

Getting Started

To use the UIComponents, you need to install at least the core library from your terminal like this:

npm i @thatopen/ui

Then, you need to tell the library to register the components, so you can use them in any HTML syntax. To do it, in your entry JavaScript file execute the following:

import { UIManager } from "@thatopen/ui"

UIManager.init()

Finally, in your HTML file you can start to use the components!

<bim-grid id="grid">
<bim-toolbars-container style="grid-area: header">
<bim-toolbar label="Toolbar A" active>
<bim-toolbar-section label="Build">
<bim-button vertical label="My Button" icon="solar:bookmark-square-minimalistic-bold"></bim-button>
<bim-toolbar-group>
<bim-button icon="solar:album-bold"></bim-button>
<bim-button icon="solar:archive-linear"></bim-button>
<bim-button icon="solar:battery-charge-minimalistic-broken"></bim-button>
<bim-button icon="solar:bluetooth-square-outline"></bim-button>
</bim-toolbar-group>
</bim-toolbar-section>
</bim-toolbar>
<bim-toolbar label="Toolbar B">
<bim-toolbar-section label="Section">
<bim-button vertical label="Button A" icon="bx:command"></bim-button>
<bim-button vertical label="Button B" icon="bx:fast-forward-circle"></bim-button>
<bim-button vertical label="Button C" icon="bx:support"></bim-button>
</bim-toolbar-section>
</bim-toolbar>
</bim-toolbars-container>
<div id="my-panel" style="grid-area: sidebar; background-color: var(--bim-ui_bg-base)">
<bim-panel label="Panel A">
<bim-panel-section label="Build">
<bim-text-input label="Tool Name" value="BCFManager"></bim-text-input>
<bim-input label="Position" vertical>
<bim-number-input pref="X" min="1" value="10" max="50" suffix="m" slider></bim-number-input>
<bim-number-input pref="X" min="1" value="20" max="50" suffix="m" slider></bim-number-input>
<bim-number-input pref="X" min="1" value="30" max="50" suffix="m" slider></bim-number-input>
</bim-input>
<bim-dropdown label="IFC Entity">
<bim-option label="IFCWALL"></bim-option>
<bim-option label="IFCWINDOW"></bim-option>
<bim-option label="IFCSLAB"></bim-option>
</bim-dropdown>
</bim-panel-section>
</bim-panel>
</div>
</bim-grid>

[!TIP] You can get any icon from Iconify!

And, in your JavaScript file:

const grid = document.getElementById("grid")

grid.layouts = {
main: `
"header header" auto
"sidebar content" 1fr
"sidebar content" 1fr
/ auto 1fr
`
}

grid.setLayout("main")

To know more about the UIComponents, you can explore the README files in each repository under the packages folder and also explore the documentation. You can find the link at the top of this README file.

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.AsyncEvent/index.html b/build/api/classes/thatopen_components.AsyncEvent/index.html index 763129d3..591f324f 100644 --- a/build/api/classes/thatopen_components.AsyncEvent/index.html +++ b/build/api/classes/thatopen_components.AsyncEvent/index.html @@ -4,7 +4,7 @@ Class: AsyncEvent<T> | That Open Docs - + @@ -14,7 +14,7 @@ Keep in mind that:

Type parameters

Name
T

Methods

add

add(handler): void

Add a callback to this event instance.

Parameters

NameTypeDescription
handlerT extends void ? () => Promise<void> : (data: T) => Promise<void>the callback to be added to this event.

Returns

void

Defined in

packages/core/src/core/Types/src/async-event.ts:15


remove

remove(handler): void

Removes a callback from this event instance.

Parameters

NameTypeDescription
handlerT extends void ? () => Promise<void> : (data: T) => Promise<void>the callback to be removed from this event.

Returns

void

Defined in

packages/core/src/core/Types/src/async-event.ts:27


reset

reset(): void

Gets rid of all the suscribed events.

Returns

void

Defined in

packages/core/src/core/Types/src/async-event.ts:44


trigger

trigger(data?): Promise<void>

Triggers all the callbacks assigned to this event.

Parameters

NameType
data?T

Returns

Promise<void>

Defined in

packages/core/src/core/Types/src/async-event.ts:36

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.Base/index.html b/build/api/classes/thatopen_components.Base/index.html index ab4d983a..ddf532de 100644 --- a/build/api/classes/thatopen_components.Base/index.html +++ b/build/api/classes/thatopen_components.Base/index.html @@ -4,13 +4,13 @@ Class: Base | That Open Docs - +
Skip to main content

Class: Base

@thatopen/components.Base

Base class of the library. Useful for finding out the interfaces it implements.

Hierarchy

Methods

isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Defined in

packages/core/src/core/Types/src/base.ts:27

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.BaseWorldItem/index.html b/build/api/classes/thatopen_components.BaseWorldItem/index.html index 56c9a74a..c4c8c5a3 100644 --- a/build/api/classes/thatopen_components.BaseWorldItem/index.html +++ b/build/api/classes/thatopen_components.BaseWorldItem/index.html @@ -4,14 +4,14 @@ Class: BaseWorldItem | That Open Docs - +
Skip to main content

Class: BaseWorldItem

@thatopen/components.BaseWorldItem

One of the elements that make a world. It can be either a scene, a camera or a renderer.

Hierarchy

  • Base

    BaseWorldItem

Methods

isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

Base.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

Base.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

Base.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

Base.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

Base.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.BoundingBoxer/index.html b/build/api/classes/thatopen_components.BoundingBoxer/index.html index 0820cf78..e89ec465 100644 --- a/build/api/classes/thatopen_components.BoundingBoxer/index.html +++ b/build/api/classes/thatopen_components.BoundingBoxer/index.html @@ -4,14 +4,14 @@ Class: BoundingBoxer | That Open Docs - +
Skip to main content

Class: BoundingBoxer

@thatopen/components.BoundingBoxer

A simple implementation of bounding box that works for fragments. The resulting bbox is not 100% precise, but it's fast, and should suffice for general use cases such as camera zooming or general boundary determination.

Hierarchy

Implements

Properties

enabled

enabled: boolean = true

Component.enabled

Overrides

Component.enabled

Defined in

packages/core/src/fragments/BoundingBoxer/index.ts:14


onDisposed

Readonly onDisposed: Event<unknown>

Disposable.onDisposed

Implementation of

Disposable.onDisposed

Defined in

packages/core/src/fragments/BoundingBoxer/index.ts:17

Methods

dispose

dispose(): void

Disposable.dispose

Returns

void

Implementation of

Disposable.dispose

Defined in

packages/core/src/fragments/BoundingBoxer/index.ts:68


isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

Component.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

Component.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

Component.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

Component.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

Component.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.Clipper/index.html b/build/api/classes/thatopen_components.Clipper/index.html index 518b4c6a..f706059f 100644 --- a/build/api/classes/thatopen_components.Clipper/index.html +++ b/build/api/classes/thatopen_components.Clipper/index.html @@ -4,7 +4,7 @@ Class: Clipper | That Open Docs - + @@ -18,7 +18,7 @@ will be forced to be orthogonal to the Y direction. orthogonalY has to be true for this to apply.

Defined in

packages/core/src/core/Clipper/index.ts:66

Accessors

enabled

get enabled(): boolean

Component.enabled

Returns

boolean

Overrides

Component.enabled

Defined in

packages/core/src/core/Clipper/index.ts:85

set enabled(state): void

Component.enabled

Parameters

NameType
stateboolean

Returns

void

Overrides

Component.enabled

Defined in

packages/core/src/core/Clipper/index.ts:90


material

get material(): MeshBasicMaterial

The material of the clipping plane representation.

Returns

MeshBasicMaterial

Defined in

packages/core/src/core/Clipper/index.ts:112

set material(material): void

The material of the clipping plane representation.

Parameters

NameType
materialMeshBasicMaterial

Returns

void

Defined in

packages/core/src/core/Clipper/index.ts:117


size

get size(): number

The size of the geometric representation of the clippings planes.

Returns

number

Defined in

packages/core/src/core/Clipper/index.ts:125

set size(size): void

The size of the geometric representation of the clippings planes.

Parameters

NameType
sizenumber

Returns

void

Defined in

packages/core/src/core/Clipper/index.ts:130


visible

get visible(): boolean

Hideable.visible

Returns

boolean

Implementation of

Hideable.visible

Defined in

packages/core/src/core/Clipper/index.ts:99

set visible(state): void

Hideable.visible

Parameters

NameType
stateboolean

Returns

void

Implementation of

Hideable.visible

Defined in

packages/core/src/core/Clipper/index.ts:104

Methods

create

create(world): void

Createable.create

Parameters

NameType
worldWorld

Returns

void

Implementation of

Createable.create

Defined in

packages/core/src/core/Clipper/index.ts:163


createFromNormalAndCoplanarPoint

createFromNormalAndCoplanarPoint(world, normal, point): SimplePlane

Creates a plane in a certain place and with a certain orientation, without the need of the mouse.

Parameters

NameTypeDescription
worldWorldthe world where this plane should be created.
normalVector3the orientation of the clipping plane.
pointVector3the position of the clipping plane. navigation.

Returns

SimplePlane

Defined in

packages/core/src/core/Clipper/index.ts:184


delete

delete(world, plane?): void

Createable.delete

Parameters

NameTypeDescription
worldWorldthe world where the plane to delete is.
plane?SimplePlanethe plane to delete. If undefined, the first plane found under the cursor will be deleted.

Returns

void

Implementation of

Createable.delete

Defined in

packages/core/src/core/Clipper/index.ts:201


deleteAll

deleteAll(): void

Deletes all the existing clipping planes.

Returns

void

Defined in

packages/core/src/core/Clipper/index.ts:213


dispose

dispose(): void

Disposable.dispose

Returns

void

Implementation of

Disposable.dispose

Defined in

packages/core/src/core/Clipper/index.ts:143


isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

Component.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

Component.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

Component.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

Component.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

Component.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.Component/index.html b/build/api/classes/thatopen_components.Component/index.html index 8d3656a3..07b33a95 100644 --- a/build/api/classes/thatopen_components.Component/index.html +++ b/build/api/classes/thatopen_components.Component/index.html @@ -4,7 +4,7 @@ Class: Component | That Open Docs - + @@ -18,7 +18,7 @@ on the type of component. E.g. a disabled dimension tool will stop creating dimensions, while a disabled camera will stop moving. A disabled component will not be updated automatically each frame.

Defined in

packages/core/src/core/Types/src/component.ts:19

Methods

isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

Base.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

Base.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

Base.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

Base.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

Base.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.Components/index.html b/build/api/classes/thatopen_components.Components/index.html index a45efc1c..e3050a24 100644 --- a/build/api/classes/thatopen_components.Components/index.html +++ b/build/api/classes/thatopen_components.Components/index.html @@ -4,7 +4,7 @@ Class: Components | That Open Docs - + @@ -20,7 +20,7 @@ initializing the scene, the renderer and the camera. Additionally, if any component that need a raycaster is used, the raycaster will need to be initialized.

Returns

void

Defined in

packages/core/src/core/Components/index.ts:70

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.CullerRenderer/index.html b/build/api/classes/thatopen_components.CullerRenderer/index.html index bd350deb..3708d121 100644 --- a/build/api/classes/thatopen_components.CullerRenderer/index.html +++ b/build/api/classes/thatopen_components.CullerRenderer/index.html @@ -4,7 +4,7 @@ Class: CullerRenderer | That Open Docs - + @@ -15,7 +15,7 @@ just before but not anymore.

Defined in

packages/core/src/core/Cullers/src/culler-renderer.ts:25


renderDebugFrame

renderDebugFrame: boolean = false

Render the internal scene used to determine the object visibility. Used for debugging purposes.

Defined in

packages/core/src/core/Cullers/src/culler-renderer.ts:40

Methods

dispose

dispose(): void

Disposable.dispose

Returns

void

Defined in

packages/core/src/core/Cullers/src/culler-renderer.ts:116


updateVisibility

updateVisibility(force?): Promise<void>

The function that the culler uses to reprocess the scene. Generally it's better to call needsUpdate, but you can also call this to force it.

Parameters

NameTypeDescription
force?booleanif true, it will refresh the scene even if needsUpdate is not true.

Returns

Promise<void>

Defined in

packages/core/src/core/Cullers/src/culler-renderer.ts:135

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.Cullers/index.html b/build/api/classes/thatopen_components.Cullers/index.html index 003d56ba..c159cafc 100644 --- a/build/api/classes/thatopen_components.Cullers/index.html +++ b/build/api/classes/thatopen_components.Cullers/index.html @@ -4,7 +4,7 @@ Class: Cullers | That Open Docs - + @@ -13,7 +13,7 @@ that are not visible to the camera.

Hierarchy

Implements

Properties

list

list: Map<string, MeshCullerRenderer>

A map of MeshCullerRenderer instances, keyed by their world UUIDs.

Defined in

packages/core/src/core/Cullers/index.ts:26


onDisposed

Readonly onDisposed: Event<unknown>

An event that is triggered when the Cullers component is disposed.

Implementation of

Disposable.onDisposed

Defined in

packages/core/src/core/Cullers/index.ts:31


uuid

Static Readonly uuid: "69f2a50d-c266-44fc-b1bd-fa4d34be89e6"

A unique identifier for the Cullers component.

Defined in

packages/core/src/core/Cullers/index.ts:16

Accessors

enabled

get enabled(): boolean

Gets the enabled state of the Cullers component.

Returns

boolean

The current enabled state.

Overrides

Component.enabled

Defined in

packages/core/src/core/Cullers/index.ts:38

set enabled(value): void

Sets the enabled state of the Cullers component. Also sets the enabled state of all MeshCullerRenderer instances.

Parameters

NameTypeDescription
valuebooleanThe new enabled state.

Returns

void

Overrides

Component.enabled

Defined in

packages/core/src/core/Cullers/index.ts:48

Methods

create

create(world, config?): MeshCullerRenderer

Creates a new MeshCullerRenderer for the given world. If a MeshCullerRenderer already exists for the world, it will return the existing one.

Parameters

NameTypeDescription
worldWorldThe world for which to create the MeshCullerRenderer.
config?Partial<CullerRendererSettings>Optional configuration settings for the MeshCullerRenderer.

Returns

MeshCullerRenderer

The newly created or existing MeshCullerRenderer for the given world.

Defined in

packages/core/src/core/Cullers/index.ts:69


dispose

dispose(): void

Disposable.dispose

Returns

void

Implementation of

Disposable.dispose

Defined in

packages/core/src/core/Cullers/index.ts:87


isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

Component.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

Component.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

Component.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

Component.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

Component.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.Disposer/index.html b/build/api/classes/thatopen_components.Disposer/index.html index 760af682..68852c15 100644 --- a/build/api/classes/thatopen_components.Disposer/index.html +++ b/build/api/classes/thatopen_components.Disposer/index.html @@ -4,7 +4,7 @@ Class: Disposer | That Open Docs - + @@ -13,7 +13,7 @@ prevent memory leaks.

Hierarchy

Properties

enabled

enabled: boolean = true

Component.enabled

Overrides

Component.enabled

Defined in

packages/core/src/core/Disposer/index.ts:13

Methods

destroy

destroy(object, materials?, recursive?): void

Removes a mesh, its geometry and its materials from memory. If you are using any of these in other parts of the application, make sure that you remove them from the mesh before disposing it.

Parameters

NameTypeDefault valueDescription
objectObject3D<Object3DEventMap>undefinedthe object to remove.
materialsbooleantruewhether to dispose the materials of the mesh.
recursivebooleantruewhether to recursively dispose the children of the mesh.

Returns

void

Defined in

packages/core/src/core/Disposer/index.ts:42


disposeGeometry

disposeGeometry(geometry): void

Disposes a geometry from memory.

Parameters

NameTypeDescription
geometryBufferGeometry<NormalBufferAttributes>the geometry to remove.

Returns

void

Defined in

packages/core/src/core/Disposer/index.ts:63


get

get(): Set<string>

Component.uuid.

Returns

Set<string>

the list of UUIDs of deleted components.

Defined in

packages/core/src/core/Disposer/index.ts:26


isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

Component.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

Component.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

Component.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

Component.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

Component.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.Event/index.html b/build/api/classes/thatopen_components.Event/index.html index 10e80287..6e8fecfd 100644 --- a/build/api/classes/thatopen_components.Event/index.html +++ b/build/api/classes/thatopen_components.Event/index.html @@ -4,7 +4,7 @@ Class: Event<T> | That Open Docs - + @@ -14,7 +14,7 @@ Keep in mind that:

Type parameters

Name
T

Methods

add

add(handler): void

Add a callback to this event instance.

Parameters

NameTypeDescription
handlerT extends void ? () => void : (data: T) => voidthe callback to be added to this event.

Returns

void

Defined in

packages/core/src/core/Types/src/event.ts:15


remove

remove(handler): void

Removes a callback from this event instance.

Parameters

NameTypeDescription
handlerT extends void ? () => void : (data: T) => voidthe callback to be removed from this event.

Returns

void

Defined in

packages/core/src/core/Types/src/event.ts:23


reset

reset(): void

Gets rid of all the suscribed events.

Returns

void

Defined in

packages/core/src/core/Types/src/event.ts:36


trigger

trigger(data?): void

Triggers all the callbacks assigned to this event.

Parameters

NameType
data?T

Returns

void

Defined in

packages/core/src/core/Types/src/event.ts:28

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.FirstPersonMode/index.html b/build/api/classes/thatopen_components.FirstPersonMode/index.html index 676a87ad..8072646d 100644 --- a/build/api/classes/thatopen_components.FirstPersonMode/index.html +++ b/build/api/classes/thatopen_components.FirstPersonMode/index.html @@ -4,14 +4,14 @@ Class: FirstPersonMode | That Open Docs - +
Skip to main content

Class: FirstPersonMode

@thatopen/components.FirstPersonMode

A NavigationMode that allows first person navigation, simulating FPS video games.

Implements

Properties

enabled

enabled: boolean = false

NavigationMode.enabled

Implementation of

NavigationMode.enabled

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/first-person-mode.ts:12


id

Readonly id: "FirstPerson"

NavigationMode.id

Implementation of

NavigationMode.id

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/first-person-mode.ts:15

Methods

set

set(active): void

NavigationMode.set

Parameters

NameType
activeboolean

Returns

void

Implementation of

NavigationMode.set

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/first-person-mode.ts:20

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.FragmentsManager/index.html b/build/api/classes/thatopen_components.FragmentsManager/index.html index 29cc3975..1fc137ab 100644 --- a/build/api/classes/thatopen_components.FragmentsManager/index.html +++ b/build/api/classes/thatopen_components.FragmentsManager/index.html @@ -4,14 +4,14 @@ Class: FragmentsManager | That Open Docs - +
Skip to main content

Class: FragmentsManager

@thatopen/components.FragmentsManager

Object that can efficiently load binary files that contain fragment geometry.

Hierarchy

Implements

Properties

enabled

enabled: boolean = true

Component.enabled

Overrides

Component.enabled

Defined in

packages/core/src/fragments/FragmentsManager/index.ts:31


list

Readonly list: Map<string, Fragment>

All the created fragments.

Defined in

packages/core/src/fragments/FragmentsManager/index.ts:26


onDisposed

Readonly onDisposed: Event<unknown>

Disposable.onDisposed

Implementation of

Disposable.onDisposed

Defined in

packages/core/src/fragments/FragmentsManager/index.ts:16

Accessors

meshes

get meshes(): Mesh<BufferGeometry<NormalBufferAttributes>, Material | Material[], Object3DEventMap>[]

The list of meshes of the created fragments.

Returns

Mesh<BufferGeometry<NormalBufferAttributes>, Material | Material[], Object3DEventMap>[]

Defined in

packages/core/src/fragments/FragmentsManager/index.ts:38

Methods

dispose

dispose(): void

Disposable.dispose

Returns

void

Implementation of

Disposable.dispose

Defined in

packages/core/src/fragments/FragmentsManager/index.ts:52


export

export(group): Uint8Array

Export the specified fragments.

Parameters

NameTypeDescription
groupFragmentsGroupthe fragments group to be exported.

Returns

Uint8Array

the exported data as binary buffer.

Defined in

packages/core/src/fragments/FragmentsManager/index.ts:132


isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

Component.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

Component.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

Component.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

Component.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

Component.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27


load

load(data, config?): FragmentsGroup

Loads a binar file that contain fragment geometry.

Parameters

NameTypeDescription
dataUint8ArrayThe binary data to load.
config?Partial<{ coordinate: boolean ; name: string ; properties: IfcProperties ; relationsMap: RelationsMap }>Optional configuration for loading.

Returns

FragmentsGroup

The loaded FragmentsGroup.

Defined in

packages/core/src/fragments/FragmentsManager/index.ts:89

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.IfcJsonExporter/index.html b/build/api/classes/thatopen_components.IfcJsonExporter/index.html index 517d4604..a2bd99e3 100644 --- a/build/api/classes/thatopen_components.IfcJsonExporter/index.html +++ b/build/api/classes/thatopen_components.IfcJsonExporter/index.html @@ -4,13 +4,13 @@ Class: IfcJsonExporter | That Open Docs - +
Skip to main content

Class: IfcJsonExporter

@thatopen/components.IfcJsonExporter

Object to export all the properties from an IFC to a JS object.

Hierarchy

Properties

enabled

enabled: boolean = true

Component.enabled

Overrides

Component.enabled

Defined in

packages/core/src/ifc/IfcJsonExporter/index.ts:13

Methods

export

export(webIfc, modelID, indirect?, recursiveSpatial?): Promise<IfcProperties>

Exports all the properties of an IFC into an array of JS objects.

Parameters

NameTypeDefault valueDescription
webIfcIfcAPIundefinedThe instance of [web-ifc][https://github.com/ThatOpen/engine_web-ifc](https://github.com/ThatOpen/engine_web-ifc) to use.
modelIDnumberundefinedID of the IFC model whose properties to extract.
indirectbooleanfalsewhether to get the indirect relationships as well.
recursiveSpatialbooleantruewhether to get the properties of spatial items recursively to make the location data available (e.g. absolute position of building).

Returns

Promise<IfcProperties>

Defined in

packages/core/src/ifc/IfcJsonExporter/index.ts:28


isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

Component.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

Component.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

Component.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

Component.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

Component.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.IfcRelationsIndexer/index.html b/build/api/classes/thatopen_components.IfcRelationsIndexer/index.html index 1d47d45f..99c702bc 100644 --- a/build/api/classes/thatopen_components.IfcRelationsIndexer/index.html +++ b/build/api/classes/thatopen_components.IfcRelationsIndexer/index.html @@ -4,7 +4,7 @@ Class: IfcRelationsIndexer | That Open Docs - + @@ -48,7 +48,7 @@ This method iterates through the relations in the given map, organizing them into a structured object where each key is an expressID of an entity, and its value is another object mapping relation indices to arrays of related entity expressIDs. The resulting object is then serialized into a JSON string.

Parameters

NameTypeDescription
relationMapRelationsMapThe map of relations to be serialized. The map keys are expressIDs of entities, and the values are maps where each key is a relation type ID and its value is an array of expressIDs of entities related through that relation type.

Returns

string

A JSON string representing the serialized relations of the given relation map.

Defined in

packages/core/src/ifc/IfcRelationsIndexer/index.ts:259


setRelationMap

setRelationMap(model, relationMap): void

Adds a relation map to the model's relations map.

Parameters

NameTypeDescription
modelFragmentsGroupThe FragmentsGroup model to which the relation map will be added.
relationMapRelationsMapThe RelationsMap to be added to the model's relations map.

Returns

void

Fires

onRelationsIndexed - Triggers an event with the model's UUID and the added relation map.

Defined in

packages/core/src/ifc/IfcRelationsIndexer/index.ts:95

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.IfcStreamingSettings/index.html b/build/api/classes/thatopen_components.IfcStreamingSettings/index.html index 329fa6dc..f8cdf46f 100644 --- a/build/api/classes/thatopen_components.IfcStreamingSettings/index.html +++ b/build/api/classes/thatopen_components.IfcStreamingSettings/index.html @@ -4,14 +4,14 @@ Class: IfcStreamingSettings | That Open Docs - +
Skip to main content

Class: IfcStreamingSettings

@thatopen/components.IfcStreamingSettings

Configuration of the IFC-fragment streaming.

Hierarchy

  • IfcFragmentSettings

    IfcStreamingSettings

Properties

coordinate

coordinate: boolean = true

Whether to use the coordination data coming from the IFC files.

Inherited from

IfcFragmentSettings.coordinate

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:15


excludedCategories

excludedCategories: Set<number>

List of categories that won't be converted to fragments.

Inherited from

IfcFragmentSettings.excludedCategories

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:29


includeProperties

includeProperties: boolean = true

Whether to extract the IFC properties into a JSON.

Inherited from

IfcFragmentSettings.includeProperties

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:6


optionalCategories

optionalCategories: number[]

Generate the geometry for categories that are not included by default, like IFCSPACE.

Inherited from

IfcFragmentSettings.optionalCategories

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:12


saveLocations

saveLocations: boolean = false

Whether to save the absolute location of all IFC items.

Inherited from

IfcFragmentSettings.saveLocations

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:32


wasm

wasm: Object

Path of the WASM for web-ifc.

Type declaration

NameType
absoluteboolean
logLevel?LogLevel
pathstring

Inherited from

IfcFragmentSettings.wasm

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:18


webIfc

webIfc: LoaderSettings

Loader settings for web-ifc.

Inherited from

IfcFragmentSettings.webIfc

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:35

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.MeshCullerRenderer/index.html b/build/api/classes/thatopen_components.MeshCullerRenderer/index.html index df26a7e9..8ce73713 100644 --- a/build/api/classes/thatopen_components.MeshCullerRenderer/index.html +++ b/build/api/classes/thatopen_components.MeshCullerRenderer/index.html @@ -4,7 +4,7 @@ Class: MeshCullerRenderer | That Open Docs - + @@ -13,7 +13,7 @@ You can bind this to the camera movement, to a certain interval, etc.

Inherited from

CullerRenderer.needsUpdate

Defined in

packages/core/src/core/Cullers/src/culler-renderer.ts:34


onDisposed

Readonly onDisposed: Event<string>

Disposable.onDisposed

Inherited from

CullerRenderer.onDisposed

Defined in

packages/core/src/core/Cullers/src/culler-renderer.ts:18


renderDebugFrame

renderDebugFrame: boolean = false

Render the internal scene used to determine the object visibility. Used for debugging purposes.

Inherited from

CullerRenderer.renderDebugFrame

Defined in

packages/core/src/core/Cullers/src/culler-renderer.ts:40

Methods

updateVisibility

updateVisibility(force?): Promise<void>

The function that the culler uses to reprocess the scene. Generally it's better to call needsUpdate, but you can also call this to force it.

Parameters

NameTypeDescription
force?booleanif true, it will refresh the scene even if needsUpdate is not true.

Returns

Promise<void>

Inherited from

CullerRenderer.updateVisibility

Defined in

packages/core/src/core/Cullers/src/culler-renderer.ts:135

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.OrbitMode/index.html b/build/api/classes/thatopen_components.OrbitMode/index.html index ebb1957d..779a9d62 100644 --- a/build/api/classes/thatopen_components.OrbitMode/index.html +++ b/build/api/classes/thatopen_components.OrbitMode/index.html @@ -4,14 +4,14 @@ Class: OrbitMode | That Open Docs - +
Skip to main content

Class: OrbitMode

@thatopen/components.OrbitMode

A NavigationMode that allows 3D navigation and panning like in many 3D and CAD softwares.

Implements

Properties

enabled

enabled: boolean = true

NavigationMode.enabled

Implementation of

NavigationMode.enabled

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/orbit-mode.ts:11


id

Readonly id: "Orbit"

NavigationMode.id

Implementation of

NavigationMode.id

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/orbit-mode.ts:14

Methods

set

set(active): void

NavigationMode.set

Parameters

NameType
activeboolean

Returns

void

Implementation of

NavigationMode.set

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/orbit-mode.ts:21

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.OrthoPerspectiveCamera/index.html b/build/api/classes/thatopen_components.OrthoPerspectiveCamera/index.html index 5e0b5a36..fd176497 100644 --- a/build/api/classes/thatopen_components.OrthoPerspectiveCamera/index.html +++ b/build/api/classes/thatopen_components.OrthoPerspectiveCamera/index.html @@ -4,7 +4,7 @@ Class: OrthoPerspectiveCamera | That Open Docs - + @@ -17,7 +17,7 @@ Transforming the camera directly will have no effect: you need to use this object to move, rotate, look at objects, etc.

Returns

CameraControls

Inherited from

SimpleCamera.controls

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:40


enabled

get enabled(): boolean

Component.enabled

Returns

boolean

Inherited from

SimpleCamera.enabled

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:52

set enabled(enabled): void

Component.enabled

Parameters

NameType
enabledboolean

Returns

void

Inherited from

SimpleCamera.enabled

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:60

Methods

dispose

dispose(): void

Disposable.dispose

Returns

void

Overrides

SimpleCamera.dispose

Defined in

packages/core/src/core/OrthoPerspectiveCamera/index.ts:82


fit

fit(meshes, offset?): Promise<void>

Make the camera view fit all the specified meshes.

Parameters

NameTypeDefault valueDescription
meshesIterable<Mesh<BufferGeometry<NormalBufferAttributes>, Material | Material[], Object3DEventMap>>undefinedthe meshes to fit. If it is not defined, it will evaluate Components.meshes.
offsetnumber1.5the distance to the fit object

Returns

Promise<void>

Defined in

packages/core/src/core/OrthoPerspectiveCamera/index.ts:110


hasCameraControls

hasCameraControls(): this is CameraControllable

Whether is instance is CameraControllable.

Returns

this is CameraControllable

Inherited from

SimpleCamera.hasCameraControls

Defined in

packages/core/src/core/Types/src/base-camera.ts:13


isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

SimpleCamera.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

SimpleCamera.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

SimpleCamera.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

SimpleCamera.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

SimpleCamera.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27


set

set(mode): void

Sets a new NavigationMode and disables the previous one.

Parameters

NameTypeDescription
modeNavModeIDThe NavigationMode to set.

Returns

void

Defined in

packages/core/src/core/OrthoPerspectiveCamera/index.ts:92


setUserInput

setUserInput(active): void

Allows or prevents all user input.

Parameters

NameTypeDescription
activebooleanwhether to enable or disable user inputs.

Returns

void

Defined in

packages/core/src/core/OrthoPerspectiveCamera/index.ts:144


update

update(_delta): void

Updateable.update

Parameters

NameType
_deltanumber

Returns

void

Inherited from

SimpleCamera.update

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:104


updateAspect

updateAspect(): void

Updates the aspect of the camera to match the size of the Components.renderer.

Returns

void

Inherited from

SimpleCamera.updateAspect

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:116

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.PlanMode/index.html b/build/api/classes/thatopen_components.PlanMode/index.html index 60c30471..cdb3e4d6 100644 --- a/build/api/classes/thatopen_components.PlanMode/index.html +++ b/build/api/classes/thatopen_components.PlanMode/index.html @@ -4,14 +4,14 @@ Class: PlanMode | That Open Docs - +
Skip to main content

Class: PlanMode

@thatopen/components.PlanMode

A NavigationMode that allows to navigate floorplans in 2D, like many BIM tools.

Implements

Properties

enabled

enabled: boolean = false

NavigationMode.enabled

Implementation of

NavigationMode.enabled

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/plan-mode.ts:11


id

Readonly id: "Plan"

NavigationMode.id

Implementation of

NavigationMode.id

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/plan-mode.ts:14

Methods

set

set(active): void

NavigationMode.set

Parameters

NameType
activeboolean

Returns

void

Implementation of

NavigationMode.set

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/plan-mode.ts:29

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.ProjectionManager/index.html b/build/api/classes/thatopen_components.ProjectionManager/index.html index 4bb08c23..b5a73f7e 100644 --- a/build/api/classes/thatopen_components.ProjectionManager/index.html +++ b/build/api/classes/thatopen_components.ProjectionManager/index.html @@ -4,14 +4,14 @@ Class: ProjectionManager | That Open Docs - +
Skip to main content

Class: ProjectionManager

@thatopen/components.ProjectionManager

Object to control the CameraProjection of the OrthoPerspectiveCamera.

Properties

matchOrthoDistanceEnabled

matchOrthoDistanceEnabled: boolean = false

Match Ortho zoom with Perspective distance when changing projection mode

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/projections.ts:27


onChanged

Readonly onChanged: Event<PerspectiveCamera | OrthographicCamera>

Event that fires when the CameraProjection changes.

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/projections.ts:14

Methods

set

set(projection): Promise<void>

Sets the CameraProjection of the OrthoPerspectiveCamera.

Parameters

NameTypeDescription
projectionCameraProjectionthe new projection to set. If it is the current projection, it will have no effect.

Returns

Promise<void>

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/projections.ts:40


toggle

toggle(): Promise<void>

Changes the current CameraProjection from Ortographic to Perspective and vice versa.

Returns

Promise<void>

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/projections.ts:54

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.PropertiesStreamingSettings/index.html b/build/api/classes/thatopen_components.PropertiesStreamingSettings/index.html index aed3a719..c0cc4e77 100644 --- a/build/api/classes/thatopen_components.PropertiesStreamingSettings/index.html +++ b/build/api/classes/thatopen_components.PropertiesStreamingSettings/index.html @@ -4,14 +4,14 @@ Class: PropertiesStreamingSettings | That Open Docs - +
Skip to main content

Class: PropertiesStreamingSettings

@thatopen/components.PropertiesStreamingSettings

Configuration of the IFC-fragment streaming.

Hierarchy

  • IfcFragmentSettings

    PropertiesStreamingSettings

Properties

coordinate

coordinate: boolean = true

Whether to use the coordination data coming from the IFC files.

Inherited from

IfcFragmentSettings.coordinate

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:15


excludedCategories

excludedCategories: Set<number>

List of categories that won't be converted to fragments.

Inherited from

IfcFragmentSettings.excludedCategories

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:29


includeProperties

includeProperties: boolean = true

Whether to extract the IFC properties into a JSON.

Inherited from

IfcFragmentSettings.includeProperties

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:6


optionalCategories

optionalCategories: number[]

Generate the geometry for categories that are not included by default, like IFCSPACE.

Inherited from

IfcFragmentSettings.optionalCategories

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:12


saveLocations

saveLocations: boolean = false

Whether to save the absolute location of all IFC items.

Inherited from

IfcFragmentSettings.saveLocations

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:32


wasm

wasm: Object

Path of the WASM for web-ifc.

Type declaration

NameType
absoluteboolean
logLevel?LogLevel
pathstring

Inherited from

IfcFragmentSettings.wasm

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:18


webIfc

webIfc: LoaderSettings

Loader settings for web-ifc.

Inherited from

IfcFragmentSettings.webIfc

Defined in

packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts:35

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.SimpleCamera/index.html b/build/api/classes/thatopen_components.SimpleCamera/index.html index 96f1751c..7d6ae088 100644 --- a/build/api/classes/thatopen_components.SimpleCamera/index.html +++ b/build/api/classes/thatopen_components.SimpleCamera/index.html @@ -4,7 +4,7 @@ Class: SimpleCamera | That Open Docs - + @@ -17,7 +17,7 @@ Transforming the camera directly will have no effect: you need to use this object to move, rotate, look at objects, etc.

Returns

CameraControls

Overrides

BaseCamera.controls

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:40


enabled

get enabled(): boolean

Component.enabled

Returns

boolean

Overrides

BaseCamera.enabled

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:52

set enabled(enabled): void

Component.enabled

Parameters

NameType
enabledboolean

Returns

void

Overrides

BaseCamera.enabled

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:60

Methods

dispose

dispose(): void

Disposable.dispose

Returns

void

Implementation of

Disposable.dispose

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:90


hasCameraControls

hasCameraControls(): this is CameraControllable

Whether is instance is CameraControllable.

Returns

this is CameraControllable

Inherited from

BaseCamera.hasCameraControls

Defined in

packages/core/src/core/Types/src/base-camera.ts:13


isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

BaseCamera.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

BaseCamera.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

BaseCamera.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

BaseCamera.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

BaseCamera.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27


update

update(_delta): void

Updateable.update

Parameters

NameType
_deltanumber

Returns

void

Implementation of

Updateable.update

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:104


updateAspect

updateAspect(): void

Updates the aspect of the camera to match the size of the Components.renderer.

Returns

void

Defined in

packages/core/src/core/Worlds/src/simple-camera.ts:116

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.SimplePlane/index.html b/build/api/classes/thatopen_components.SimplePlane/index.html index b9527204..38bda540 100644 --- a/build/api/classes/thatopen_components.SimplePlane/index.html +++ b/build/api/classes/thatopen_components.SimplePlane/index.html @@ -4,13 +4,13 @@ Class: SimplePlane | That Open Docs - +
Skip to main content

Class: SimplePlane

@thatopen/components.SimplePlane

Each of the planes created by SimpleClipper.

Implements

Properties

onDisposed

Readonly onDisposed: Event<unknown>

Disposable.onDisposed

Implementation of

Disposable.onDisposed

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:17


onDraggingEnded

Readonly onDraggingEnded: Event<unknown>

Event that fires when the user stops dragging a clipping plane.

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:14


onDraggingStarted

Readonly onDraggingStarted: Event<unknown>

Event that fires when the user starts dragging a clipping plane.

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:11

Accessors

enabled

set enabled(state): void

Component.enabled

Parameters

NameType
stateboolean

Returns

void

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:52


meshes

get meshes(): Mesh<BufferGeometry<NormalBufferAttributes>, Material | Material[], Object3DEventMap>[]

The meshes used for raycasting

Returns

Mesh<BufferGeometry<NormalBufferAttributes>, Material | Material[], Object3DEventMap>[]

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:74


planeMaterial

get planeMaterial(): Material | Material[]

The material of the clipping plane representation.

Returns

Material | Material[]

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:79

set planeMaterial(material): void

The material of the clipping plane representation.

Parameters

NameType
materialMaterial | Material[]

Returns

void

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:84


size

get size(): number

The size of the clipping plane representation.

Returns

number

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:89

set size(size): void

Sets the size of the clipping plane representation.

Parameters

NameType
sizenumber

Returns

void

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:94


visible

get visible(): boolean

Hideable.visible

Returns

boolean

Implementation of

Hideable.visible

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:61

set visible(state): void

Hideable.visible

Parameters

NameType
stateboolean

Returns

void

Implementation of

Hideable.visible

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:66

Methods

dispose

dispose(): void

Disposable.dispose

Returns

void

Implementation of

Disposable.dispose

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:155


update

update(): void

Updateable.update

Returns

void

Defined in

packages/core/src/core/Clipper/src/simple-plane.ts:146

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.SimpleRenderer/index.html b/build/api/classes/thatopen_components.SimpleRenderer/index.html index 99114f1d..b3c2b74b 100644 --- a/build/api/classes/thatopen_components.SimpleRenderer/index.html +++ b/build/api/classes/thatopen_components.SimpleRenderer/index.html @@ -4,7 +4,7 @@ Class: SimpleRenderer | That Open Docs - + @@ -16,7 +16,7 @@ clipping plane to the renderer.

Parameters

NameType
activeboolean
planePlane
isLocal?boolean

Returns

void

Inherited from

BaseRenderer.setPlane

Defined in

packages/core/src/core/Types/src/base-renderer.ts:57


update

update(): void

Updateable.update

Returns

void

Overrides

BaseRenderer.update

Defined in

packages/core/src/core/Worlds/src/simple-renderer.ts:61


updateClippingPlanes

updateClippingPlanes(): void

Forces the update of the clipping planes and all components that depend on them that are subscribed to onClippingPlanesUpdated.

Returns

void

Inherited from

BaseRenderer.updateClippingPlanes

Defined in

packages/core/src/core/Types/src/base-renderer.ts:48

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components.SimpleScene/index.html b/build/api/classes/thatopen_components.SimpleScene/index.html index b3b3b88d..479d164a 100644 --- a/build/api/classes/thatopen_components.SimpleScene/index.html +++ b/build/api/classes/thatopen_components.SimpleScene/index.html @@ -4,14 +4,14 @@ Class: SimpleScene | That Open Docs - +
Skip to main content

Class: SimpleScene

@thatopen/components.SimpleScene

A basic 3D scene to add objects hierarchically, and easily dispose them when you are finished with it.

No Inherit Doc

Hierarchy

  • BaseScene

    SimpleScene

Implements

Properties

isSetup

isSetup: boolean = false

Configurable.isSetup

Implementation of

Configurable.isSetup

Defined in

packages/core/src/core/Worlds/src/simple-scene.ts:24


onDisposed

Readonly onDisposed: Event<unknown>

Disposable.onDisposed

Inherited from

BaseScene.onDisposed

Defined in

packages/core/src/core/Types/src/base-scene.ts:10


onSetup

Readonly onSetup: Event<SimpleScene>

Configurable.onSetup

Implementation of

Configurable.onSetup

Defined in

packages/core/src/core/Worlds/src/simple-scene.ts:29

Methods

dispose

dispose(): void

Disposable.dispose

Returns

void

Inherited from

BaseScene.dispose

Defined in

packages/core/src/core/Types/src/base-scene.ts:19


isConfigurable

isConfigurable(): this is Configurable<any>

Whether is component is Configurable.

Returns

this is Configurable<any>

Inherited from

BaseScene.isConfigurable

Defined in

packages/core/src/core/Types/src/base.ts:39


isDisposeable

isDisposeable(): this is Disposable

Whether is component is Disposable.

Returns

this is Disposable

Inherited from

BaseScene.isDisposeable

Defined in

packages/core/src/core/Types/src/base.ts:17


isHideable

isHideable(): this is Hideable

Whether is component is Hideable.

Returns

this is Hideable

Inherited from

BaseScene.isHideable

Defined in

packages/core/src/core/Types/src/base.ts:34


isResizeable

isResizeable(): this is Resizeable

Whether is component is Resizeable.

Returns

this is Resizeable

Inherited from

BaseScene.isResizeable

Defined in

packages/core/src/core/Types/src/base.ts:22


isUpdateable

isUpdateable(): this is Updateable

Whether is component is Updateable.

Returns

this is Updateable

Inherited from

BaseScene.isUpdateable

Defined in

packages/core/src/core/Types/src/base.ts:27


setup

setup(config?): void

Creates a simple and nice default set up for the scene (e.g. lighting).

Parameters

NameType
config?Partial<SimpleSceneConfig>

Returns

void

Implementation of

Configurable.setup

Defined in

packages/core/src/core/Worlds/src/simple-scene.ts:50

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components_front.ClipEdges/index.html b/build/api/classes/thatopen_components_front.ClipEdges/index.html index 03960f56..f05ed4c0 100644 --- a/build/api/classes/thatopen_components_front.ClipEdges/index.html +++ b/build/api/classes/thatopen_components_front.ClipEdges/index.html @@ -4,14 +4,14 @@ Class: ClipEdges | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components_front.EdgesPlane/index.html b/build/api/classes/thatopen_components_front.EdgesPlane/index.html index 2380147c..b34ab162 100644 --- a/build/api/classes/thatopen_components_front.EdgesPlane/index.html +++ b/build/api/classes/thatopen_components_front.EdgesPlane/index.html @@ -4,7 +4,7 @@ Class: EdgesPlane | That Open Docs - + @@ -12,7 +12,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components_front.LengthMeasurement/index.html b/build/api/classes/thatopen_components_front.LengthMeasurement/index.html index 5b996527..e68aac29 100644 --- a/build/api/classes/thatopen_components_front.LengthMeasurement/index.html +++ b/build/api/classes/thatopen_components_front.LengthMeasurement/index.html @@ -4,14 +4,14 @@ Class: LengthMeasurement | That Open Docs - +
Skip to main content

Class: LengthMeasurement

@thatopen/components-front.LengthMeasurement

A basic dimension tool to measure distances between 2 points in 3D and display a 3D symbol displaying the numeric value.

Hierarchy

  • Component

    LengthMeasurement

Implements

  • Createable
  • Hideable
  • Disposable
  • Updateable

Properties

snapDistance

snapDistance: number = 0.25

The minimum distance to force the dimension cursor to a vertex.

Defined in

packages/front/src/measurement/LengthMeasurement/index.ts:24

Methods

cancelCreation

cancelCreation(): void

Cancels the drawing of the current dimension.

Returns

void

Implementation of

OBC.Createable.cancelCreation

Defined in

packages/front/src/measurement/LengthMeasurement/index.ts:178


create

create(data?): void

Starts or finishes drawing a new dimension line.

Parameters

NameTypeDescription
data?anyforces the dimension to be drawn on a plane. Use this if you are drawing dimensions in floor plan navigation.

Returns

void

Implementation of

OBC.Createable.create

Defined in

packages/front/src/measurement/LengthMeasurement/index.ts:119


delete

delete(): void

Deletes the dimension that the user is hovering over with the mouse or touch event.

Returns

void

Implementation of

OBC.Createable.delete

Defined in

packages/front/src/measurement/LengthMeasurement/index.ts:138


deleteAll

deleteAll(): void

Deletes all the dimensions that have been previously created.

Returns

void

Defined in

packages/front/src/measurement/LengthMeasurement/index.ts:170


dispose

dispose(): void

Disposable.dispose

Returns

void

Implementation of

OBC.Disposable.dispose

Defined in

packages/front/src/measurement/LengthMeasurement/index.ts:93

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components_front.Marker/index.html b/build/api/classes/thatopen_components_front.Marker/index.html index 0fb4422e..844d0105 100644 --- a/build/api/classes/thatopen_components_front.Marker/index.html +++ b/build/api/classes/thatopen_components_front.Marker/index.html @@ -4,7 +4,7 @@ Class: Marker | That Open Docs - + @@ -12,7 +12,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components_front.Plans/index.html b/build/api/classes/thatopen_components_front.Plans/index.html index 9ca711c7..ae133cf5 100644 --- a/build/api/classes/thatopen_components_front.Plans/index.html +++ b/build/api/classes/thatopen_components_front.Plans/index.html @@ -4,13 +4,13 @@ Class: Plans | That Open Docs - +
Skip to main content

Class: Plans

@thatopen/components-front.Plans

Helper to control the camera and easily define and navigate 2D floor plans.

Hierarchy

  • Component

    Plans

Implements

  • Disposable

Properties

currentPlan

currentPlan: null | PlanView = null

The floorplan that is currently selected.

Defined in

packages/front/src/fragments/Plans/index.ts:25


defaultCameraOffset

defaultCameraOffset: number = 30

The offset of the 2D camera to the floor plan elevation.

Defined in

packages/front/src/fragments/Plans/index.ts:31


defaultSectionOffset

defaultSectionOffset: number = 1.5

The offset from the clipping planes to their respective floor plan elevation.

Defined in

packages/front/src/fragments/Plans/index.ts:28


onDisposed

Readonly onDisposed: Event<unknown>

Disposable.onDisposed

Implementation of

OBC.Disposable.onDisposed

Defined in

packages/front/src/fragments/Plans/index.ts:16

Methods

create

create(config): void

Creates a new floor plan in the navigator.

Parameters

NameTypeDescription
configPlanViewNecessary data to initialize the floor plan.

Returns

void

Defined in

packages/front/src/fragments/Plans/index.ts:97


dispose

dispose(): void

Disposable.dispose

Returns

void

Implementation of

OBC.Disposable.dispose

Defined in

packages/front/src/fragments/Plans/index.ts:48


exitPlanView

exitPlanView(animate?): Promise<void>

Deactivate navigator and go back to the previous view.

Parameters

NameTypeDefault valueDescription
animatebooleanfalseWhether to animate the camera transition.

Returns

Promise<void>

Defined in

packages/front/src/fragments/Plans/index.ts:141


goTo

goTo(id, animate?): Promise<void>

Make the navigator go to the specified floor plan.

Parameters

NameTypeDefault valueDescription
idstringundefinedFloor plan to go to.
animatebooleanfalseWhether to animate the camera transition.

Returns

Promise<void>

Defined in

packages/front/src/fragments/Plans/index.ts:119

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components_front.PostproductionRenderer/index.html b/build/api/classes/thatopen_components_front.PostproductionRenderer/index.html index 79c9ea35..b5dff473 100644 --- a/build/api/classes/thatopen_components_front.PostproductionRenderer/index.html +++ b/build/api/classes/thatopen_components_front.PostproductionRenderer/index.html @@ -4,13 +4,13 @@ Class: PostproductionRenderer | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_components_front.RendererWith2D/index.html b/build/api/classes/thatopen_components_front.RendererWith2D/index.html index 5edaf51e..1b9d0ac2 100644 --- a/build/api/classes/thatopen_components_front.RendererWith2D/index.html +++ b/build/api/classes/thatopen_components_front.RendererWith2D/index.html @@ -4,7 +4,7 @@ Class: RendererWith2D | That Open Docs - + @@ -13,7 +13,7 @@ (Objec3Ds and CSS2DObjects respectively).

Hierarchy

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_fragments.Serializer/index.html b/build/api/classes/thatopen_fragments.Serializer/index.html index 091779d5..44b68c51 100644 --- a/build/api/classes/thatopen_fragments.Serializer/index.html +++ b/build/api/classes/thatopen_fragments.Serializer/index.html @@ -4,14 +4,14 @@ Class: Serializer | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Button/index.html b/build/api/classes/thatopen_ui.Button/index.html index 06ef3dcc..7d5db1da 100644 --- a/build/api/classes/thatopen_ui.Button/index.html +++ b/build/api/classes/thatopen_ui.Button/index.html @@ -4,13 +4,13 @@ Class: Button | That Open Docs - +
Skip to main content

Class: Button

@thatopen/ui.Button

Heloooooooooo

Hierarchy

  • LitElement

    Button

Properties

active

active: boolean = false

A boolean attribute which, if present, indicates that the button is active.

Default

false

Example

<bim-button label="Click me" active></bim-button>

Example

const button = document.createElement('bim-button');
button.label = 'Click me';
button.active = true;

Defined in

packages/core/src/components/Button/index.ts:162


disabled

disabled: boolean = false

A boolean attribute which, if present, indicates that the button is disabled.

Default

false

Example

<bim-button label="Click me" disabled></bim-button>

Example

const button = document.createElement('bim-button');
button.label = 'Click me';
button.disabled = true;

Defined in

packages/core/src/components/Button/index.ts:173


icon

Optional icon: string

The icon to be displayed on the button.

Default

undefined

Example

<bim-button icon="my-icon"></bim-button>

Example

const button = document.createElement('bim-button');
button.icon = 'my-icon';

Defined in

packages/core/src/components/Button/index.ts:184


label

Optional label: string

The label to be displayed on the button.

Default

undefined

Example

<bim-button label="Click me"></bim-button>

Example

const button = document.createElement('bim-button');
button.label = 'Click me';

Defined in

packages/core/src/components/Button/index.ts:140


labelHidden

labelHidden: boolean = false

A boolean attribute which, if present, indicates that the label should be hidden.

Default

false

Example

<bim-button label="Click me" label-hidden></bim-button>

Example

const button = document.createElement('bim-button');
button.label = 'Click me';
button.labelHidden = true;

Defined in

packages/core/src/components/Button/index.ts:151


tooltipText

Optional tooltipText: string

The text of the tooltip to be displayed when hovering over the button.

Default

undefined

Example

<bim-button label="Click me" tooltip-text="This is a tooltip"></bim-button>

Example

const button = document.createElement('bim-button');
button.label = 'Click me';
button.tooltipText = 'This is a tooltip';

Defined in

packages/core/src/components/Button/index.ts:242


tooltipTime

Optional tooltipTime: number

The time (in milliseconds) to wait before showing the tooltip when hovering over the button.

Default

700

Example

<bim-button label="Click me" tooltip-time="1000"></bim-button>

Example

const button = document.createElement('bim-button');
button.label = 'Click me';
button.tooltipTime = 1000;

Defined in

packages/core/src/components/Button/index.ts:207


tooltipTitle

Optional tooltipTitle: string

The title of the tooltip to be displayed when hovering over the button.

Default

undefined

Example

<bim-button label="Click me" tooltip-title="Button Tooltip"></bim-button>

Example

const button = document.createElement('bim-button');
button.label = 'Click me';
button.tooltipTitle = 'Button Tooltip';

Defined in

packages/core/src/components/Button/index.ts:230


tooltipVisible

tooltipVisible: boolean = false

A boolean attribute which, if present, indicates that the tooltip should be visible.

Default

false

Example

<bim-button label="Click me" tooltip-visible></bim-button>

Example

const button = document.createElement('bim-button');
button.label = 'Click me';
button.tooltipVisible = true;

Defined in

packages/core/src/components/Button/index.ts:218


vertical

vertical: boolean = false

A boolean attribute which, if present, indicates that the button should be displayed vertically.

Default

false

Example

<bim-button label="Click me" vertical></bim-button>

Example

const button = document.createElement('bim-button');
button.label = 'Click me';
button.vertical = true;

Defined in

packages/core/src/components/Button/index.ts:195

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Checkbox/index.html b/build/api/classes/thatopen_ui.Checkbox/index.html index be002bb3..3f530cf6 100644 --- a/build/api/classes/thatopen_ui.Checkbox/index.html +++ b/build/api/classes/thatopen_ui.Checkbox/index.html @@ -4,7 +4,7 @@ Class: Checkbox | That Open Docs - + @@ -12,7 +12,7 @@
Skip to main content

Class: Checkbox

@thatopen/ui.Checkbox

Heloooooooooo

Hierarchy

  • LitElement

    Checkbox

Implements

Properties

checked

checked: boolean = false

Indicates whether the checkbox is checked or not. This property reflects the checked state of the internal \<input> element and can be used to set or get the checkbox's state. Changing this property dynamically updates the checkbox's visual state and its checked attribute.

Default

false

Example

<bim-checkbox checked></bim-checkbox>

Example

const checkbox = document.createElement('bim-checkbox');
checkbox.checked = true;
document.body.appendChild(checkbox);

Defined in

packages/core/src/components/Checkbox/index.ts:93


icon

Optional icon: string

Represents the icon associated with the checkbox label. This icon is displayed next to the label text if provided. Changing this property dynamically updates the displayed icon if the label is present. It is used to visually enhance the checkbox by adding an icon.

Default

undefined

Example

<bim-checkbox icon="check"></bim-checkbox>

Example

const checkbox = document.createElement('bim-checkbox');
checkbox.icon = 'check';
document.body.appendChild(checkbox);

Defined in

packages/core/src/components/Checkbox/index.ts:55


inverted

inverted: boolean = false

Indicates whether the checkbox is displayed with an inverted disposition.

Default

false

Example

<bim-checkbox inverted></bim-checkbox>

Example

const checkbox = document.createElement('bim-checkbox');
checkbox.inverted = true;
document.body.appendChild(checkbox);

Defined in

packages/core/src/components/Checkbox/index.ts:106


label

Optional label: string

The label text associated with the checkbox. This text is displayed next to the checkbox itself. Changing this property dynamically updates the displayed label. If an icon is also specified, it will be displayed alongside this label.

Default

undefined

Example

<bim-checkbox label="Accept Terms"></bim-checkbox>

Example

const checkbox = document.createElement('bim-checkbox');
checkbox.label = 'Accept Terms';
document.body.appendChild(checkbox);

Defined in

packages/core/src/components/Checkbox/index.ts:81


name

Optional name: string

The name attribute of the checkbox. It can be used to identify the checkbox when submitting a form or to reference the checkbox in JavaScript. Changing this property dynamically updates the name attribute of the internal \<input> element.

Default

undefined

Example

<bim-checkbox name="agreement"></bim-checkbox>

Example

const checkbox = document.createElement('bim-checkbox');
checkbox.name = 'agreement';
document.body.appendChild(checkbox);

Defined in

packages/core/src/components/Checkbox/index.ts:68

Accessors

value

get value(): boolean

A getter that returns the current checked state of the checkbox. This is useful for retrieving the checkbox's value in form submissions or JavaScript interactions as it provides a consistent value property as many other components.

Returns

boolean

Default

false

Example

<script>console.log(document.querySelector('bim-checkbox').value);</script>

Example

const checkbox = document.createElement('bim-checkbox');
document.body.appendChild(checkbox);
console.log(checkbox.value); // false initially

Implementation of

HasValue.value

Defined in

packages/core/src/components/Checkbox/index.ts:118

Events

onValueChange

Readonly onValueChange: Event

Event that is dispatched when the checkbox's checked state changes. This event can be used to listen for changes to the checkbox's value and perform necessary actions when the value changes.

change

Example

checkbox.addEventListener('change', (event) => {
console.log('Checkbox value changed:', event.target.checked);
});

Implementation of

HasValue.onValueChange

Defined in

packages/core/src/components/Checkbox/index.ts:133

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.ColorInput/index.html b/build/api/classes/thatopen_ui.ColorInput/index.html index ef5c0417..433b1681 100644 --- a/build/api/classes/thatopen_ui.ColorInput/index.html +++ b/build/api/classes/thatopen_ui.ColorInput/index.html @@ -4,14 +4,14 @@ Class: ColorInput | That Open Docs - +
Skip to main content

Class: ColorInput

@thatopen/ui.ColorInput

Heloooooooooo

Hierarchy

  • LitElement

    ColorInput

Implements

Properties

color

color: string = "#bcf124"

The color value of the color input in hexadecimal format.

Default

#bcf124

Example

<bim-color-input color="#ff0000"></bim-color-input>

Example

const colorInput = document.createElement('bim-color-input');
colorInput.color = '#ff0000';

Defined in

packages/core/src/components/ColorInput/index.ts:148


icon

Optional icon: string

The icon for the color input.

Default

undefined

Example

<bim-color-input icon="palette"></bim-color-input>

Example

const colorInput = document.createElement('bim-color-input');
colorInput.icon = 'palette';

Defined in

packages/core/src/components/ColorInput/index.ts:111


label

Optional label: string

The label for the color input.

Default

undefined

Example

<bim-color-input label="Select a color"></bim-color-input>

Example

const colorInput = document.createElement('bim-color-input');
colorInput.label = 'Select a color';

Implementation of

HasName.label

Defined in

packages/core/src/components/ColorInput/index.ts:98


name

Optional name: string

The name of the color input.

Default

undefined

Example

<bim-color-input name="colorInput"></bim-color-input>

Example

const colorInput = document.createElement('bim-color-input');
colorInput.name = 'colorInput';

Implementation of

HasName.name

Defined in

packages/core/src/components/ColorInput/index.ts:85


opacity

Optional opacity: number

The opacity of the color input.

Default

undefined

Example

<bim-color-input opacity="0.5"></bim-color-input>

Example

const colorInput = document.createElement('bim-color-input');
colorInput.opacity = 0.5;

Defined in

packages/core/src/components/ColorInput/index.ts:136


vertical

vertical: boolean = false

A boolean attribute which, if present, indicates that the color input should be displayed vertically.

Default

false

Example

<bim-color-input vertical></bim-color-input>

Example

const colorInput = document.createElement('bim-color-input');
colorInput.vertical = true;

Defined in

packages/core/src/components/ColorInput/index.ts:123

Accessors

value

set value(_value): void

Represents both the color and opacity values combined into a single object. This is an instance property, not an HTMLElement attribute.

Parameters

NameType
_valueObject
_value.colorstring
_value.opacity?number

Returns

void

Example

const colorInput = document.createElement('bim-color-input');
colorInput.value = { color: '#ff0000', opacity: 0.5 };

Implementation of

HasValue.value

Defined in

packages/core/src/components/ColorInput/index.ts:162

Methods

focus

focus(): void

Focuses on the color input by programmatically triggering a click event on the underlying color input element. If the color input element is not available, the function does nothing.

Returns

void

Overrides

LitElement.focus

Defined in

packages/core/src/components/ColorInput/index.ts:208

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Component/index.html b/build/api/classes/thatopen_ui.Component/index.html index 8b62bebe..2dcfae32 100644 --- a/build/api/classes/thatopen_ui.Component/index.html +++ b/build/api/classes/thatopen_ui.Component/index.html @@ -4,13 +4,13 @@ Class: Component | That Open Docs - +
Skip to main content

Class: Component

@thatopen/ui.Component

Heloooooooooo

Hierarchy

Methods

create

create<T, S>(template, state): [element: T, update: UpdateFunction<S>]

Creates a new UI component instance based on the provided template and initial state.

Type parameters

NameTypeDescription
Textends HTMLElementThe type of the UI component element.
Sextends Record<string, any>The type of the component state.

Parameters

NameTypeDescription
templateStatefullComponent<S>The stateful component template function.
stateSThe initial state of the component.

Returns

[element: T, update: UpdateFunction<S>]

An array containing the created UI component element and a function to update its state.

Defined in

packages/core/src/core/Component/index.ts:92

create<T>(template): T

Creates a new UI component instance based on the provided template and initial state.

Type parameters

NameTypeDescription
Textends HTMLElementThe type of the UI component element.

Parameters

NameTypeDescription
templateStatelessComponentThe stateless component template function.

Returns

T

The created UI component element.

Defined in

packages/core/src/core/Component/index.ts:106

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.ContextMenu/index.html b/build/api/classes/thatopen_ui.ContextMenu/index.html index 43620b12..ba818b20 100644 --- a/build/api/classes/thatopen_ui.ContextMenu/index.html +++ b/build/api/classes/thatopen_ui.ContextMenu/index.html @@ -4,7 +4,7 @@ Class: ContextMenu | That Open Docs - + @@ -16,7 +16,7 @@ to ensure the context menu is properly placed relative to the target element.

Parameters

NameTypeDescription
target?HTMLElementThe target element relative to which the context menu should be positioned. If not provided, the parent node is used as the target.

Returns

Promise<void>

A promise that resolves once the position has been updated. This method does not explicitly return a value but updates the context menu's style properties to position it on the screen.

Defined in

packages/core/src/components/ContextMenu/index.ts:85

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Dropdown/index.html b/build/api/classes/thatopen_ui.Dropdown/index.html index 95d3b042..f6f5c7fc 100644 --- a/build/api/classes/thatopen_ui.Dropdown/index.html +++ b/build/api/classes/thatopen_ui.Dropdown/index.html @@ -4,14 +4,14 @@ Class: Dropdown | That Open Docs - +
Skip to main content

Class: Dropdown

@thatopen/ui.Dropdown

Heloooooooooo

Hierarchy

Implements

Properties

icon

Optional icon: string

The icon to be displayed in the dropdown.

Default

undefined

Example

<bim-dropdown icon="exampleIcon"></bim-dropdown>

Example

const dropdown = document.createElement('bim-dropdown');
dropdown.icon = 'exampleIcon';

Defined in

packages/core/src/components/Dropdown/index.ts:82


label

Optional label: string

The label to be displayed in the dropdown.

Default

undefined

Example

<bim-dropdown label="Example Label"></bim-dropdown>

Example

const dropdown = document.createElement('bim-dropdown');
dropdown.label = 'Example Label';

Implementation of

HasName.label

Defined in

packages/core/src/components/Dropdown/index.ts:95


multiple

multiple: boolean = false

Indicates whether multiple options can be selected in the dropdown.

Default

false

Example

<bim-dropdown multiple></bim-dropdown>

Example

const dropdown = document.createElement('bim-dropdown');
dropdown.multiple = true;

Defined in

packages/core/src/components/Dropdown/index.ts:107


name

Optional name: string

The name of the dropdown.

Default

undefined

Example

<bim-dropdown name="exampleName"></bim-dropdown>

Example

const dropdown = document.createElement('bim-dropdown');
dropdown.name = 'exampleName';

Implementation of

HasName.name

Defined in

packages/core/src/components/Dropdown/index.ts:69


required

required: boolean = false

Indicates whether a selection is required in the dropdown.

Default

false

Example

<bim-dropdown required></bim-dropdown>

Example

const dropdown = document.createElement('bim-dropdown');
dropdown.required = true;

Defined in

packages/core/src/components/Dropdown/index.ts:119


vertical

vertical: boolean = false

Indicates whether the dropdown should be displayed vertically.

Default

false

Example

<bim-dropdown vertical></bim-dropdown>

Example

const dropdown = document.createElement('bim-dropdown');
dropdown.vertical = true;

Defined in

packages/core/src/components/Dropdown/index.ts:131

Accessors

value

set value(value): void

The selected values in the dropdown.

Parameters

NameType
valueany[]

Returns

void

Example

const dropdown = document.createElement('bim-dropdown');
dropdown.value = ['option1', 'option2'];

Implementation of

HasValue.value

Defined in

packages/core/src/components/Dropdown/index.ts:170


visible

set visible(value): void

Indicates whether the dropdown it-self (not the component) is visible.

Parameters

NameType
valueboolean

Returns

void

Default

false

Example

<bim-dropdown visible></bim-dropdown>

Example

const dropdown = document.createElement('bim-dropdown');
dropdown.visible = true;

Defined in

packages/core/src/components/Dropdown/index.ts:150

Methods

create

create<T, S>(template, state): [element: T, update: UpdateFunction<S>]

Creates a new UI component instance based on the provided template and initial state.

Type parameters

NameTypeDescription
Textends HTMLElementThe type of the UI component element.
Sextends Record<string, any>The type of the component state.

Parameters

NameTypeDescription
templateStatefullComponent<S>The stateful component template function.
stateSThe initial state of the component.

Returns

[element: T, update: UpdateFunction<S>]

An array containing the created UI component element and a function to update its state.

Inherited from

Component.create

Defined in

packages/core/src/core/Component/index.ts:92

create<T>(template): T

Creates a new UI component instance based on the provided template and initial state.

Type parameters

NameTypeDescription
Textends HTMLElementThe type of the UI component element.

Parameters

NameTypeDescription
templateStatelessComponentThe stateless component template function.

Returns

T

The created UI component element.

Inherited from

Component.create

Defined in

packages/core/src/core/Component/index.ts:106

Events

onValueChange

onValueChange: Event

Event that is fired when the value of the dropdown changes. This event is fired when the user selects or deselects an option.

change

Example

dropdown.addEventListener('change', (event) => {
console.log('Dropdown value changed:', event.target.value);
});

Implementation of

HasValue.onValueChange

Defined in

packages/core/src/components/Dropdown/index.ts:209

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Grid/index.html b/build/api/classes/thatopen_ui.Grid/index.html index 7dbb2dcc..880d575a 100644 --- a/build/api/classes/thatopen_ui.Grid/index.html +++ b/build/api/classes/thatopen_ui.Grid/index.html @@ -4,7 +4,7 @@ Class: Grid | That Open Docs - + @@ -13,7 +13,7 @@ Each layout is defined by a unique name, a grid template string, and a map of area names to HTMLElement instances. The grid template string defines the structure of the grid, and the area names correspond to the grid-area property of the HTMLElement instances. The HTMLElement instances are used to populate the grid with content.

Defined in

packages/core/src/components/Grid/index.ts:79

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Icon/index.html b/build/api/classes/thatopen_ui.Icon/index.html index 89ded686..2bfdc8cc 100644 --- a/build/api/classes/thatopen_ui.Icon/index.html +++ b/build/api/classes/thatopen_ui.Icon/index.html @@ -4,13 +4,13 @@ Class: Icon | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Input/index.html b/build/api/classes/thatopen_ui.Input/index.html index 13c862b0..fa945f5e 100644 --- a/build/api/classes/thatopen_ui.Input/index.html +++ b/build/api/classes/thatopen_ui.Input/index.html @@ -4,13 +4,13 @@ Class: Input | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Label/index.html b/build/api/classes/thatopen_ui.Label/index.html index 7b36d520..3802a996 100644 --- a/build/api/classes/thatopen_ui.Label/index.html +++ b/build/api/classes/thatopen_ui.Label/index.html @@ -4,7 +4,7 @@ Class: Label | That Open Docs - + @@ -18,7 +18,7 @@ When the label property changes, the displayed text updates to reflect the new value. If the label is hidden (controlled by labelHidden), changing this property will not affect the visibility of the label.

Default

undefined

Example

<bim-label label="Example Label"></bim-label>

Example

const labelComponent = document.createElement('bim-label');
labelComponent.label = 'Example Label';
document.body.appendChild(labelComponent);

Defined in

packages/core/src/components/Label/index.ts:71


labelHidden

labelHidden: boolean = false

Controls the visibility of the label text. When true, the label text is not rendered to the user. Changing this property to true hides the label text if it was previously visible. Setting it to false will show the label text if it is defined.

Default

false

Example

<bim-label label-hidden></bim-label>

Example

const labelComponent = document.createElement('bim-label');
labelComponent.labelHidden = true;
document.body.appendChild(labelComponent);

Defined in

packages/core/src/components/Label/index.ts:98


vertical

vertical: boolean = false

Determines the orientation of the component. When true, the component's contents (label, image, and icon) are stacked vertically. Changing this property affects the layout of the component, switching between a horizontal and vertical arrangement of its contents.

Default

false

Example

<bim-label vertical></bim-label>

Example

const labelComponent = document.createElement('bim-label');
labelComponent.vertical = true;
document.body.appendChild(labelComponent);

Defined in

packages/core/src/components/Label/index.ts:140

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.NumberInput/index.html b/build/api/classes/thatopen_ui.NumberInput/index.html index dd20c761..554e2c23 100644 --- a/build/api/classes/thatopen_ui.NumberInput/index.html +++ b/build/api/classes/thatopen_ui.NumberInput/index.html @@ -4,7 +4,7 @@ Class: NumberInput | That Open Docs - + @@ -39,7 +39,7 @@ This method is useful for programmatically focusing the input element, for example, in response to a user action or to emphasize the input in the UI.

If the input element reference is not available (not yet rendered or disconnected), this method will do nothing.

Returns

void

Overrides

LitElement.focus

Defined in

packages/core/src/components/NumberInput/index.ts:408

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Option/index.html b/build/api/classes/thatopen_ui.Option/index.html index 470b99e6..d108ced2 100644 --- a/build/api/classes/thatopen_ui.Option/index.html +++ b/build/api/classes/thatopen_ui.Option/index.html @@ -4,7 +4,7 @@ Class: Option | That Open Docs - + @@ -27,7 +27,7 @@ The value property does not reflect, meaning if you change the value using JavaScript, you won't find the same data in the attributes. However, if you have set the value in the property and then you change the attribute, the value will be set at the data from the attribute. By default, if no value is set, value will return the label value in case there is one defined.

Returns

any

Example

<bim-option value="10"></bim-option>

Example

const option = document.createElement('bim-option');
// option.setAttribute('value', 'true');
// option.setAttribute('value', '10');
// option.setAttribute('value', 'some string');
document.body.appendChild(option);

Example

const option = document.createElement('bim-option');
option.label = "At origin"
option.value = {x: 0, y: 0, z: 0} // As this is an object, it must be set in the property and not in the attribute.
document.body.appendChild(option);

Defined in

packages/core/src/components/Option/index.ts:191

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Panel/index.html b/build/api/classes/thatopen_ui.Panel/index.html index e3f03f72..ea966208 100644 --- a/build/api/classes/thatopen_ui.Panel/index.html +++ b/build/api/classes/thatopen_ui.Panel/index.html @@ -4,7 +4,7 @@ Class: Panel | That Open Docs - + @@ -26,7 +26,7 @@ This method iterates over each bim-panel-section found within the panel's DOM and sets their collapsed property to false, effectively showing their content. This can be used to programmatically reveal the content of sections within the panel, making the panel more informative or to display details that are necessary for the user.

Returns

void

Defined in

packages/core/src/components/Panel/src/Panel.ts:198

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.PanelSection/index.html b/build/api/classes/thatopen_ui.PanelSection/index.html index 7cd3fa20..46f6b937 100644 --- a/build/api/classes/thatopen_ui.PanelSection/index.html +++ b/build/api/classes/thatopen_ui.PanelSection/index.html @@ -4,13 +4,13 @@ Class: PanelSection | That Open Docs - +
Skip to main content

Class: PanelSection

@thatopen/ui.PanelSection

Heloooooooooo

Hierarchy

  • LitElement

    PanelSection

Implements

Properties

collapsed

Optional collapsed: boolean

Controls the collapsed state of the panel section. When collapsed is true, the content of the section is hidden, and only the header is visible. This property can be toggled to show or hide the section's content, and is reflected to an attribute for easy HTML or JavaScript manipulation. Note that sections marked as fixed ignore changes to this property.

Default

undefined

Example

<bim-panel-section collapsed></bim-panel-section>

Example

const section = document.createElement('bim-panel-section');
section.collapsed = true;
document.body.appendChild(section);

Defined in

packages/core/src/components/Panel/src/Section.ts:145


fixed

Optional fixed: boolean

Determines whether the panel section is fixed, meaning it cannot be collapsed or expanded. This is useful for sections that should always remain visible. When fixed is true, the collapse/expand icon is hidden, and clicking the header does not toggle the collapsed state. This property is reflected to an attribute, allowing it to be set declaratively in HTML or programmatically via JavaScript.

Default

undefined

Example

<bim-panel-section fixed></bim-panel-section>

Example

const section = document.createElement('bim-panel-section');
section.fixed = true;
document.body.appendChild(section);

Defined in

packages/core/src/components/Panel/src/Section.ts:132


icon

Optional icon: string

Represents the icon to be displayed within the panel section. This icon is a visual cue that can be used alongside the label to provide additional context or to represent the section's content visually. When the icon property changes, the displayed icon updates accordingly. This property is reflected to an attribute, allowing for declarative usage in HTML as well as programmatic control in JavaScript.

Default

undefined

Example

<bim-panel-section icon="settings"></bim-panel-section>

Example

const section = document.createElement('bim-panel-section');
section.icon = 'settings';
document.body.appendChild(section);

Defined in

packages/core/src/components/Panel/src/Section.ts:93


label

Optional label: string

Specifies the label for the panel section. This label is displayed prominently at the top of the section and serves as a title or heading. When the label property changes, the section's header updates to reflect the new label. This property takes precedence over the name property for display purposes and is also reflected to an attribute for HTML declaration or JavaScript manipulation.

Default

undefined

Example

<bim-panel-section label="User Settings"></bim-panel-section>

Example

const section = document.createElement('bim-panel-section');
section.label = 'User Settings';
document.body.appendChild(section);

Implementation of

HasName.label

Defined in

packages/core/src/components/Panel/src/Section.ts:106


name

Optional name: string

Defines the name of the panel section, acting as an identifier. While similar to label, name is more suited for identification purposes rather than display. If label is not set, name can be displayed as a fallback in the section's header. The name property is reflected to an attribute, enabling both HTML and JavaScript interactions.

Default

undefined

Example

<bim-panel-section name="user-settings"></bim-panel-section>

Example

const section = document.createElement('bim-panel-section');
section.name = 'user-settings';
document.body.appendChild(section);

Implementation of

HasName.name

Defined in

packages/core/src/components/Panel/src/Section.ts:119

Accessors

value

get value(): Record<string, any>

The value getter computes and returns the current state of the panel section's form elements as an object. This object's keys are the name or label attributes of the child elements, and the values are the corresponding values of these elements. This property is particularly useful for retrieving a consolidated view of the user's input or selections within the panel section. When the value of any child element changes, the returned object from this getter will reflect those changes, providing a dynamic snapshot of the panel section's state. Note that this property does not have a default value as it dynamically reflects the current state of the panel section's form elements.

Returns

Record<string, any>

Example

<bim-panel-section></bim-panel-section> <!-- Usage in HTML not directly applicable as this is a getter -->

Example

const section = document.createElement('bim-panel-section');
console.log(section.value); // Logs the current value object

Implementation of

HasValue.value

Defined in

packages/core/src/components/Panel/src/Section.ts:157

set value(data): void

The value setter allows programmatically updating the values of the panel section's child elements. It accepts an object where keys correspond to the name or label attributes of the child elements, and the values are the new values to be set for these elements. This property is useful for initializing the panel section with specific values or updating its state based on external data. When the property changes, the corresponding child elements' values are updated to reflect the new state. This does not have a default value as it is a method for updating child elements' values.

Parameters

NameType
dataRecord<string, any>

Returns

void

Default

undefined

Example

<bim-panel-section></bim-panel-section> <!-- Usage in HTML not directly applicable as this is a setter -->

Example

const section = document.createElement('bim-panel-section');
section.value = { 'user-settings': 'John Doe' }; // Programmatically sets the value of a child element named 'user-settings'

Implementation of

HasValue.value

Defined in

packages/core/src/components/Panel/src/Section.ts:171

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Selector/index.html b/build/api/classes/thatopen_ui.Selector/index.html index 82a95edc..901ce468 100644 --- a/build/api/classes/thatopen_ui.Selector/index.html +++ b/build/api/classes/thatopen_ui.Selector/index.html @@ -4,7 +4,7 @@ Class: Selector | That Open Docs - + @@ -12,7 +12,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Tab/index.html b/build/api/classes/thatopen_ui.Tab/index.html index 6b9b5819..5fc3d449 100644 --- a/build/api/classes/thatopen_ui.Tab/index.html +++ b/build/api/classes/thatopen_ui.Tab/index.html @@ -4,13 +4,13 @@ Class: Tab | That Open Docs - +
Skip to main content

Class: Tab

@thatopen/ui.Tab

Heloooooooooo

Hierarchy

  • LitElement

    Tab

Properties

icon

Optional icon: string

The icon of the tab. This property is optional and can be used to display an icon next to the tab's label or name.

Defined in

packages/core/src/components/Tabs/src/Tab.ts:38


label

Optional label: string

The label of the tab. This property is optional and can be used to display a custom label instead of the tab's name.

Defined in

packages/core/src/components/Tabs/src/Tab.ts:32


name

name: string

The name of the tab. If not provided, a default name will be assigned based on its position in the parent element.

Defined in

packages/core/src/components/Tabs/src/Tab.ts:26

Accessors

hidden

set hidden(value): void

Sets the hidden state of the tab.

Parameters

NameTypeDescription
valuebooleanThe new hidden state. If true, the tab will be hidden. If false, the tab will be visible.

Returns

void

Fires

hiddenchange - Dispatched when the hidden state changes.

Example

const tab = document.querySelector('bim-tab');
tab.hidden = true; // hides the tab

Overrides

LitElement.hidden

Defined in

packages/core/src/components/Tabs/src/Tab.ts:55

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Table/index.html b/build/api/classes/thatopen_ui.Table/index.html index bea473a7..0ebf7569 100644 --- a/build/api/classes/thatopen_ui.Table/index.html +++ b/build/api/classes/thatopen_ui.Table/index.html @@ -4,7 +4,7 @@ Class: Table | That Open Docs - + @@ -31,7 +31,7 @@ If a simple string is provided, the table will filter rows based on the string's presence in any column. If a complex query is provided, the table will filter rows based on the query's conditions and values.

Parameters

NameTypeDescription
_valuenull | stringThe search string or null to clear the search.

Returns

void

Example

table.queryString = "example";

Example

table.queryString = "column1="Jhon Doe" & column2=20";

Defined in

packages/core/src/components/Table/index.ts:191


tsv

get tsv(): string

A getter function that generates a Tab Separated Values (TSV) representation of the table data.

Returns

string

A string containing the TSV representation of the table data.

Example

const tsvData = table.tsv;
console.log(tsvData); // Output: "Column 1\tColumn 2\nValue 1\tValue 2\nValue 3\tValue 4"

Defined in

packages/core/src/components/Table/index.ts:397


value

get value(): TableGroupData[]

Getter for the value property. Returns the filtered data if a search string is provided, otherwise returns the original data.

Returns

TableGroupData[]

Example

const tableValue = table.value;
console.log(tableValue); // Output: The filtered or original data.

Defined in

packages/core/src/components/Table/index.ts:162

Methods

downloadData

downloadData(fileName?, format?): void

The downloadData method is used to download the table data in different formats.

Parameters

NameTypeDefault valueDescription
fileNamestring"BIM Table Data"The name of the downloaded file. Default is "BIM Table Data".
format"json" | "tsv" | "csv""json"The format of the downloaded file. Can be "json", "tsv", or "csv". Default is "json".

Returns

void

Example

table.downloadData("MyTableData", "tsv");

Defined in

packages/core/src/components/Table/index.ts:427

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Tabs/index.html b/build/api/classes/thatopen_ui.Tabs/index.html index 95e5065c..1ee29f26 100644 --- a/build/api/classes/thatopen_ui.Tabs/index.html +++ b/build/api/classes/thatopen_ui.Tabs/index.html @@ -4,7 +4,7 @@ Class: Tabs | That Open Docs - + @@ -13,7 +13,7 @@ and sets its hidden property to false. It also updates the corresponding tab switcher's data-active attribute to reflect the active state.

If the provided value does not match any tab name, no tab will be selected.

If the tab property is already set to the provided value, this method will deselect all tabs by setting the tab property to undefined.

Example

// Set the active tab to "tab1"
tabs.tab = "tab1";

// Deselect all tabs
tabs.tab = undefined;

Defined in

packages/core/src/components/Tabs/src/Tabs.ts:169

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.TextInput/index.html b/build/api/classes/thatopen_ui.TextInput/index.html index 1c235b2e..63ab6bb8 100644 --- a/build/api/classes/thatopen_ui.TextInput/index.html +++ b/build/api/classes/thatopen_ui.TextInput/index.html @@ -4,7 +4,7 @@ Class: TextInput | That Open Docs - + @@ -25,7 +25,7 @@ The type property determines the behavior of the input field. It can be any of the following: "date", "datetime-local", "email", "month", "password", "search", "tel", "text", "time", "url", "week". If an invalid type is provided, the type will not be changed.

Parameters

NameType
valuestring

Returns

void

Example

// Set the type to "email"
textInput.type = "email";

Defined in

packages/core/src/components/TextInput/index.ts:148

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Toolbar/index.html b/build/api/classes/thatopen_ui.Toolbar/index.html index bcdbf0a4..6afefe8f 100644 --- a/build/api/classes/thatopen_ui.Toolbar/index.html +++ b/build/api/classes/thatopen_ui.Toolbar/index.html @@ -4,7 +4,7 @@ Class: Toolbar | That Open Docs - + @@ -14,7 +14,7 @@ When labelsHidden is false, labels in the toolbar sections will be visible.

Default Value

false

Defined in

packages/core/src/components/Toolbar/src/Toolbar.ts:59

Accessors

vertical

set vertical(value): void

Sets the vertical property of the toolbar. When vertical is true, the toolbar will be displayed in a vertical layout. When vertical is false, the toolbar will be displayed in a horizontal layout.

Parameters

NameType
valueboolean

Returns

void

Defined in

packages/core/src/components/Toolbar/src/Toolbar.ts:69

- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.ToolbarGroup/index.html b/build/api/classes/thatopen_ui.ToolbarGroup/index.html index 45069e14..7e0f1d90 100644 --- a/build/api/classes/thatopen_ui.ToolbarGroup/index.html +++ b/build/api/classes/thatopen_ui.ToolbarGroup/index.html @@ -4,7 +4,7 @@ Class: ToolbarGroup | That Open Docs - + @@ -12,7 +12,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.ToolbarSection/index.html b/build/api/classes/thatopen_ui.ToolbarSection/index.html index 21e07f87..152d189f 100644 --- a/build/api/classes/thatopen_ui.ToolbarSection/index.html +++ b/build/api/classes/thatopen_ui.ToolbarSection/index.html @@ -4,13 +4,13 @@ Class: ToolbarSection | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui.Viewport/index.html b/build/api/classes/thatopen_ui.Viewport/index.html index 3dcbe1e0..d6a0ee2d 100644 --- a/build/api/classes/thatopen_ui.Viewport/index.html +++ b/build/api/classes/thatopen_ui.Viewport/index.html @@ -4,13 +4,13 @@ Class: Viewport | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui_obc.Manager/index.html b/build/api/classes/thatopen_ui_obc.Manager/index.html index e844f437..d621de59 100644 --- a/build/api/classes/thatopen_ui_obc.Manager/index.html +++ b/build/api/classes/thatopen_ui_obc.Manager/index.html @@ -4,13 +4,13 @@ Class: Manager | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui_obc.ViewCube/index.html b/build/api/classes/thatopen_ui_obc.ViewCube/index.html index 1b0647ed..e3e937d6 100644 --- a/build/api/classes/thatopen_ui_obc.ViewCube/index.html +++ b/build/api/classes/thatopen_ui_obc.ViewCube/index.html @@ -4,13 +4,13 @@ Class: ViewCube | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/classes/thatopen_ui_obc.World2D/index.html b/build/api/classes/thatopen_ui_obc.World2D/index.html index 807cd6d0..68037c21 100644 --- a/build/api/classes/thatopen_ui_obc.World2D/index.html +++ b/build/api/classes/thatopen_ui_obc.World2D/index.html @@ -4,13 +4,13 @@ Class: World2D | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/index.html b/build/api/index.html index 1462613b..46554a25 100644 --- a/build/api/index.html +++ b/build/api/index.html @@ -4,13 +4,13 @@ Open BIM Docs | That Open Docs - +
Skip to main content

Open BIM Docs

TOC|documentation|community|npm package

cover

That Open Docs

This library contains the official docs for all the libraries of That Open Company.

  • It uses docusaurus to build them.
  • It gathers code from our repos and build the API docs using TypeDoc.
  • It gathers the HTML examples from our repos and build the tutorials.

If you see anything outdated in the docs page, feel free to open an issue. If the issue is specific to a specific repository, please open the issue in that repository!

If you have any questions, you can also ask around in our community.

Local development

Requirements

Install all dependencies

yarn install

Run docusaurus local devserver

yarn start

Generating tutorials and api docs

This script clones both components and fragments repos into a temp/ folder and generates the api docs.

yarn build:remote
- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_components.BVHGeometry/index.html b/build/api/interfaces/thatopen_components.BVHGeometry/index.html index b4d3b75f..8f2b0513 100644 --- a/build/api/interfaces/thatopen_components.BVHGeometry/index.html +++ b/build/api/interfaces/thatopen_components.BVHGeometry/index.html @@ -4,13 +4,13 @@ Interface: BVHGeometry | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_components.CameraControllable/index.html b/build/api/interfaces/thatopen_components.CameraControllable/index.html index 8917a0e9..02090c4a 100644 --- a/build/api/interfaces/thatopen_components.CameraControllable/index.html +++ b/build/api/interfaces/thatopen_components.CameraControllable/index.html @@ -4,13 +4,13 @@ Interface: CameraControllable | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_components.Configurable/index.html b/build/api/interfaces/thatopen_components.Configurable/index.html index ffe23b39..f07aef3b 100644 --- a/build/api/interfaces/thatopen_components.Configurable/index.html +++ b/build/api/interfaces/thatopen_components.Configurable/index.html @@ -4,14 +4,14 @@ Interface: Configurable<T> | That Open Docs - +
Skip to main content

Interface: Configurable<T>

@thatopen/components.Configurable

Whether this component supports to be configured.

Type parameters

NameType
Textends Record<string, any>

Implemented by

Properties

config

config: Required<T>

Object holding the tool configuration. Is not meant to be edited directly, if you need to make changes to this object, use () just after the tool is instantiated.

Defined in

packages/core/src/core/Types/src/interfaces.ts:128


isSetup

isSetup: boolean

Wether this components has been already configured.

Defined in

packages/core/src/core/Types/src/interfaces.ts:117


onSetup

Readonly onSetup: Event<any>

Fired after successfully calling ()

Defined in

packages/core/src/core/Types/src/interfaces.ts:123


setup

setup: (config?: Partial<T>) => void | Promise<void>

Type declaration

▸ (config?): void | Promise<void>

Use the provided configuration to setup the tool.

Parameters
NameType
config?Partial<T>
Returns

void | Promise<void>

Defined in

packages/core/src/core/Types/src/interfaces.ts:120

- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_components.Createable/index.html b/build/api/interfaces/thatopen_components.Createable/index.html index 810dab42..446fa65a 100644 --- a/build/api/interfaces/thatopen_components.Createable/index.html +++ b/build/api/interfaces/thatopen_components.Createable/index.html @@ -4,7 +4,7 @@ Interface: Createable | That Open Docs - + @@ -14,7 +14,7 @@ dimensions.

Implemented by

Properties

cancelCreation

Optional cancelCreation: (data: any) => void

Type declaration

▸ (data): void

Cancels the creation process of the component, going back to the state before starting to create.

Parameters
NameType
dataany
Returns

void

Defined in

packages/core/src/core/Types/src/interfaces.ts:106


create

create: (data: any) => void

Type declaration

▸ (data): void

Creates a new instance of an element (e.g. a new Dimension).

Parameters
NameType
dataany
Returns

void

Defined in

packages/core/src/core/Types/src/interfaces.ts:94


delete

delete: (data: any) => void

Type declaration

▸ (data): void

Deletes an existing instance of an element (e.g. a Dimension).

Parameters
NameType
dataany
Returns

void

Defined in

packages/core/src/core/Types/src/interfaces.ts:109


endCreation

Optional endCreation: (data: any) => void

Type declaration

▸ (data): void

Finish the creation process of the component, successfully creating an instance of whatever the component creates.

Parameters
NameType
dataany
Returns

void

Defined in

packages/core/src/core/Types/src/interfaces.ts:100

- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_components.Disposable/index.html b/build/api/interfaces/thatopen_components.Disposable/index.html index 3f4e47ed..cd034ca4 100644 --- a/build/api/interfaces/thatopen_components.Disposable/index.html +++ b/build/api/interfaces/thatopen_components.Disposable/index.html @@ -4,7 +4,7 @@ Interface: Disposable | That Open Docs - + @@ -15,7 +15,7 @@ This also ensures that the DOM events created by that component will be cleaned up.

Implemented by

Properties

dispose

dispose: () => void | Promise<void>

Type declaration

▸ (): void | Promise<void>

Destroys the object from memory to prevent a memory leak.

Returns

void | Promise<void>

Defined in

packages/core/src/core/Types/src/interfaces.ts:17


onDisposed

Readonly onDisposed: Event<any>

Fired after the tool has been ()

Defined in

packages/core/src/core/Types/src/interfaces.ts:20

- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_components.Hideable/index.html b/build/api/interfaces/thatopen_components.Hideable/index.html index 1e4f7fdf..bec03fd4 100644 --- a/build/api/interfaces/thatopen_components.Hideable/index.html +++ b/build/api/interfaces/thatopen_components.Hideable/index.html @@ -4,7 +4,7 @@ Interface: Hideable | That Open Docs - + @@ -14,7 +14,7 @@ Three.js scene.

Implemented by

Properties

visible

visible: boolean

Whether the geometric representation of this component is currently visible or not in the Three.js scene.

Defined in

packages/core/src/core/Types/src/interfaces.ts:34

- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_components.NavigationMode/index.html b/build/api/interfaces/thatopen_components.NavigationMode/index.html index 03903dda..9463d6ff 100644 --- a/build/api/interfaces/thatopen_components.NavigationMode/index.html +++ b/build/api/interfaces/thatopen_components.NavigationMode/index.html @@ -4,7 +4,7 @@ Interface: NavigationMode | That Open Docs - + @@ -13,7 +13,7 @@ and the user input (e.g. 2D floor plan mode, first person mode, etc).

Implemented by

Properties

enabled

enabled: boolean

Whether this navigation mode is active or not.

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/types.ts:30


id

id: NavModeID

The unique ID of this navigation mode.

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/types.ts:17


set

set: (active: boolean, options?: any) => void

Type declaration

▸ (active, options?): void

Enable or disable this navigation mode. When a new navigation mode is enabled, the previous navigation mode must be disabled.

Parameters
NameTypeDescription
activebooleanwhether to enable or disable this mode.
options?anyany additional data required to enable or disable it.
Returns

void

Defined in

packages/core/src/core/OrthoPerspectiveCamera/src/types.ts:27

- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_components.Progress/index.html b/build/api/interfaces/thatopen_components.Progress/index.html index 30b35335..b98780e9 100644 --- a/build/api/interfaces/thatopen_components.Progress/index.html +++ b/build/api/interfaces/thatopen_components.Progress/index.html @@ -4,13 +4,13 @@ Interface: Progress | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_components.Resizeable/index.html b/build/api/interfaces/thatopen_components.Resizeable/index.html index 412be7bb..cea9bfff 100644 --- a/build/api/interfaces/thatopen_components.Resizeable/index.html +++ b/build/api/interfaces/thatopen_components.Resizeable/index.html @@ -4,7 +4,7 @@ Interface: Resizeable | That Open Docs - + @@ -18,7 +18,7 @@ component.

Returns

Vector2

Defined in

packages/core/src/core/Types/src/interfaces.ts:60


onResize

onResize: Event<Vector2>

Event that fires when the component has been resized.

Defined in

packages/core/src/core/Types/src/interfaces.ts:53


resize

resize: (size?: Vector2) => void

Type declaration

▸ (size?): void

Sets size of this component (e.g. the resolution of a Renderer component.

Parameters
NameType
size?Vector2
Returns

void

Defined in

packages/core/src/core/Types/src/interfaces.ts:50

- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_components.Updateable/index.html b/build/api/interfaces/thatopen_components.Updateable/index.html index 61a1d689..affb3c2d 100644 --- a/build/api/interfaces/thatopen_components.Updateable/index.html +++ b/build/api/interfaces/thatopen_components.Updateable/index.html @@ -4,14 +4,14 @@ Interface: Updateable | That Open Docs - +
Skip to main content

Interface: Updateable

@thatopen/components.Updateable

Whether this component should be updated each frame.

Implemented by

Properties

onAfterUpdate

onAfterUpdate: Event<any>

Actions that should be executed after updating the component.

Defined in

packages/core/src/core/Types/src/interfaces.ts:66


onBeforeUpdate

onBeforeUpdate: Event<any>

Actions that should be executed before updating the component.

Defined in

packages/core/src/core/Types/src/interfaces.ts:69

Methods

update

update(delta?): void

Function used to update the state of this component each frame. For instance, a renderer component will make a render each frame.

Parameters

NameType
delta?number

Returns

void

Defined in

packages/core/src/core/Types/src/interfaces.ts:75

- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_ui.ColumnData/index.html b/build/api/interfaces/thatopen_ui.ColumnData/index.html index 80d3144d..03737b44 100644 --- a/build/api/interfaces/thatopen_ui.ColumnData/index.html +++ b/build/api/interfaces/thatopen_ui.ColumnData/index.html @@ -4,13 +4,13 @@ Interface: ColumnData | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_ui.EntryQuery/index.html b/build/api/interfaces/thatopen_ui.EntryQuery/index.html index b254996a..fa693ecd 100644 --- a/build/api/interfaces/thatopen_ui.EntryQuery/index.html +++ b/build/api/interfaces/thatopen_ui.EntryQuery/index.html @@ -4,13 +4,13 @@ Interface: EntryQuery | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_ui.HasName/index.html b/build/api/interfaces/thatopen_ui.HasName/index.html index 55266a73..1a4305fb 100644 --- a/build/api/interfaces/thatopen_ui.HasName/index.html +++ b/build/api/interfaces/thatopen_ui.HasName/index.html @@ -4,13 +4,13 @@ Interface: HasName | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_ui.HasValue/index.html b/build/api/interfaces/thatopen_ui.HasValue/index.html index 029c5356..f73dbbe1 100644 --- a/build/api/interfaces/thatopen_ui.HasValue/index.html +++ b/build/api/interfaces/thatopen_ui.HasValue/index.html @@ -4,13 +4,13 @@ Interface: HasValue | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/interfaces/thatopen_ui.QueryGroup/index.html b/build/api/interfaces/thatopen_ui.QueryGroup/index.html index 441fbaab..1cc1d5da 100644 --- a/build/api/interfaces/thatopen_ui.QueryGroup/index.html +++ b/build/api/interfaces/thatopen_ui.QueryGroup/index.html @@ -4,13 +4,13 @@ Interface: QueryGroup | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/modules/index.html b/build/api/modules/index.html index a310ef71..22323e27 100644 --- a/build/api/modules/index.html +++ b/build/api/modules/index.html @@ -4,13 +4,13 @@ Open BIM Docs | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/modules/thatopen_components/index.html b/build/api/modules/thatopen_components/index.html index c7021dc4..01471c56 100644 --- a/build/api/modules/thatopen_components/index.html +++ b/build/api/modules/thatopen_components/index.html @@ -4,13 +4,13 @@ Module: @thatopen/components | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/modules/thatopen_components_front/index.html b/build/api/modules/thatopen_components_front/index.html index 1042d9a0..06970091 100644 --- a/build/api/modules/thatopen_components_front/index.html +++ b/build/api/modules/thatopen_components_front/index.html @@ -4,13 +4,13 @@ Module: @thatopen/components-front | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/modules/thatopen_fragments/index.html b/build/api/modules/thatopen_fragments/index.html index 263a8bc0..6ff6995d 100644 --- a/build/api/modules/thatopen_fragments/index.html +++ b/build/api/modules/thatopen_fragments/index.html @@ -4,13 +4,13 @@ Module: @thatopen/fragments | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/modules/thatopen_ui/index.html b/build/api/modules/thatopen_ui/index.html index c11b10bd..1d070f3a 100644 --- a/build/api/modules/thatopen_ui/index.html +++ b/build/api/modules/thatopen_ui/index.html @@ -4,13 +4,13 @@ Module: @thatopen/ui | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/api/modules/thatopen_ui_obc/index.html b/build/api/modules/thatopen_ui_obc/index.html index a3b9cac2..b80514fd 100644 --- a/build/api/modules/thatopen_ui_obc/index.html +++ b/build/api/modules/thatopen_ui_obc/index.html @@ -4,13 +4,13 @@ Module: @thatopen/ui-obc | That Open Docs - +
Skip to main content
- + \ No newline at end of file diff --git a/build/assets/js/308c405f.2e284b33.js b/build/assets/js/308c405f.91e68427.js similarity index 73% rename from build/assets/js/308c405f.2e284b33.js rename to build/assets/js/308c405f.91e68427.js index 065c4faa..01a559bd 100644 --- a/build/assets/js/308c405f.2e284b33.js +++ b/build/assets/js/308c405f.91e68427.js @@ -1 +1 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[8491],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>h});var o=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=o.createContext({}),p=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},d=function(e){var t=p(e.components);return o.createElement(s.Provider,{value:t},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},u=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=p(n),u=a,h=c["".concat(s,".").concat(u)]||c[u]||m[u]||r;return n?o.createElement(h,i(i({ref:t},d),{},{components:n})):o.createElement(h,i({ref:t},d))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,i=new Array(r);i[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[c]="string"==typeof e?e:a,i[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>c});var o=n(7462),a=n(3366),r=(n(7294),n(3905)),i=["components"],l={},s=void 0,p={unversionedId:"Tutorials/Components/Core/IfcRelationsIndexer",id:"Tutorials/Components/Core/IfcRelationsIndexer",title:"IfcRelationsIndexer",description:"Copying and pasting? We've got you covered! You can find the full source code of this tutorial here.",source:"@site/docs/Tutorials/Components/Core/IfcRelationsIndexer.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/IfcRelationsIndexer",permalink:"/Tutorials/Components/Core/IfcRelationsIndexer",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"IfcPropertiesTiler",permalink:"/Tutorials/Components/Core/IfcPropertiesTiler"},next:{title:"MiniMap",permalink:"/Tutorials/Components/Core/MiniMap"}},d={},c=[{value:"\ud83d\udd01 Getting relations (easy)",id:"-getting-relations-easy",level:3},{value:"\ud83c\udf0e Setting up a simple scene",id:"-setting-up-a-simple-scene",level:3},{value:"\ud83e\uddf3 Loading a BIM model",id:"-loading-a-bim-model",level:3},{value:"\ud83d\udccb Indexing the model",id:"-indexing-the-model",level:3},{value:"\ud83d\udcc4 Getting element psets",id:"-getting-element-psets",level:3},{value:"\u2198\ufe0f Exporting the indexation",id:"\ufe0f-exporting-the-indexation",level:3},{value:"\u2197\ufe0f Loading back the relations index",id:"\ufe0f-loading-back-the-relations-index",level:3},{value:"\u23f1\ufe0f Measuring the performance (optional)",id:"\ufe0f-measuring-the-performance-optional",level:3},{value:"\ud83e\udde9 Adding some UI",id:"-adding-some-ui",level:3},{value:"\ud83c\udf89 Wrap up",id:"-wrap-up",level:3}],m={toc:c},u="wrapper";function h(e){var t=e.components,n=(0,a.Z)(e,i);return(0,r.kt)(u,(0,o.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("admonition",{title:"Source",type:"info"},(0,r.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/packages/core/src/ifc/IfcRelationsIndexer/example.ts"},"here"),".")),(0,r.kt)("h3",{id:"-getting-relations-easy"},"\ud83d\udd01 Getting relations (easy)"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"If you're aware of the IFC schema, you should know that all the possible information an entity have is not directly inside its attributes. For example, the property sets, classifications, materials, etc, of a wall (or any other element) are not directly in the wall attributes \ud83e\udd2f but in other entities which are related to the wall using relations."),(0,r.kt)("admonition",{title:"Why so much indirection?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"Indirection is perfect for an schema like the IFC which aims to store all the building data within a single text file in the easiest way possible. However, is not that easy to work just because you need to find the relations you want to get to the element data you're looking for \ud83d\ude2a. Luckily for you, the ",(0,r.kt)("inlineCode",{parentName:"p"},"IfcRelationsIndexer")," already gives you an easy way to get the entities which are related with your elements thanks to the inverse attributes! \ud83d\udd25\ud83d\udd25")),(0,r.kt)("p",null,"In this tutorial, we will import:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"@thatopen/ui")," to add some simple and cool UI menus."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"@thatopen/components")," to set up the barebone of our app."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"Stats.js")," (optional) to measure the performance of our app.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'import Stats from "stats.js";\nimport * as BUI from "@thatopen/ui";\nimport * as OBC from "@thatopen/components";\n')),(0,r.kt)("h3",{id:"-setting-up-a-simple-scene"},"\ud83c\udf0e Setting up a simple scene"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"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."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const container = document.getElementById("container")!;\n\nconst components = new OBC.Components();\n\nconst worlds = components.get(OBC.Worlds);\n\nconst world = worlds.create<\n OBC.SimpleScene,\n OBC.SimpleCamera,\n OBC.SimpleRenderer\n>();\n\nworld.scene = new OBC.SimpleScene(components);\nworld.renderer = new OBC.SimpleRenderer(components, container);\nworld.camera = new OBC.SimpleCamera(components);\n\ncomponents.init();\n\nworld.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);\n\nworld.scene.setup();\n\nconst grids = components.get(OBC.Grids);\ngrids.create(world);\n')),(0,r.kt)("p",null,"We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"world.scene.three.background = null;\n")),(0,r.kt)("h3",{id:"-loading-a-bim-model"},"\ud83e\uddf3 Loading a BIM model"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"We'll start by adding an IFC model to our scene."),(0,r.kt)("admonition",{title:"Loading an IFC?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"If you are not familiar with IFC loading, check out the IFC Loader tutorial first!")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const ifcLoader = components.get(OBC.IfcLoader);\nawait ifcLoader.setup();\nconst file = await fetch("https://thatopen.github.io/engine_components/resources/small.ifc");\nconst buffer = await file.arrayBuffer();\nconst typedArray = new Uint8Array(buffer);\nconst model = await ifcLoader.load(typedArray);\nworld.scene.three.add(model);\n')),(0,r.kt)("h3",{id:"-indexing-the-model"},"\ud83d\udccb Indexing the model"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Once the model is loaded in memory, you just need to get an instance of the IfcRelationsIndexer and process the model... it's as easy as that! \ud83d\ude0e"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const indexer = components.get(OBC.IfcRelationsIndexer);\nawait indexer.process(model);\n")),(0,r.kt)("p",null,"The result of that is basically a map where the keys are the expressIDs and the values are other expressIDs related to the first one and grouped by the type of relation. You don't need to worry too much about the details of that, as the usage is pretty straighforward \ud83d\udd1d. The only thing that matters is you've now an easy way to access the entities related to your element \ud83d\ude42"),(0,r.kt)("h3",{id:"-getting-element-psets"},"\ud83d\udcc4 Getting element psets"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"One of the most important relations between different entities is the ",(0,r.kt)("inlineCode",{parentName:"p"},"IfcRelDefinesByProperties"),". That relation links together an arbitrary entity with a set of ",(0,r.kt)("inlineCode",{parentName:"p"},"IfcPropertySet")," entities that applies properties. Getting them with the indexer once the model is indexed is pretty easy:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const psets = indexer.getEntityRelations(model, 6518, "IsDefinedBy");\nif (psets) {\n for (const expressID of psets) {\n // You can get the pset attributes like this\n const pset = await model.getProperties(expressID);\n console.log(pset);\n // You can get the pset props like this or iterate over pset.HasProperties yourself\n await OBC.IfcPropertiesUtils.getPsetProps(\n model,\n expressID,\n async (propExpressID) => {\n const prop = await model.getProperties(propExpressID);\n console.log(prop);\n },\n );\n }\n}\n')),(0,r.kt)("admonition",{type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"IsDefinedBy is the inverse attribute name in the IFC Schema that holds the relations with property sets \ud83d\ude09")),(0,r.kt)("p",null,"Awesome! really easy right?"),(0,r.kt)("h3",{id:"\ufe0f-exporting-the-indexation"},"\u2198\ufe0f Exporting the indexation"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"In bigger models, the process to calculate the relations index may take some time. There is no reason to calculate over and over the relations index every time you load a model. If the model hasn't change, their properties shouldn't neither! So, let's download the relations index to load it later."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const downloadJSON = (json: string, name: string) => {\n const file = new File([json], name);\n const a = document.createElement("a");\n a.href = URL.createObjectURL(file);\n a.download = file.name;\n a.click();\n URL.revokeObjectURL(a.href);\n};\n\nconst json = indexer.serializeModelRelations(model);\nconsole.log(json);\n')),(0,r.kt)("admonition",{type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"As ",(0,r.kt)("inlineCode",{parentName:"p"},"@thatopen/components")," can be used in either NodeJS and Browser environments, the logic to generate a JSON file may vary!")),(0,r.kt)("p",null,"Now, in case you've loaded several models and want to get all the computed relations, there is also a handy method to do it."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const allRelationsJSON = indexer.serializeAllRelations();\n")),(0,r.kt)("h3",{id:"\ufe0f-loading-back-the-relations-index"},"\u2197\ufe0f Loading back the relations index"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"What do we gain with having a pre-processed relations index if we can't use it again, right? Well, let's use the downloaded relations index \ud83d\ude0e"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'// Lets first delete the existing model relations\ndelete indexer.relationMaps[model.uuid];\nconst relationsIndexFile = await fetch("/resources/small-relations.json");\nconst relationsIndex = indexer.getRelationsMapFromJSON(\n await relationsIndexFile.text(),\n);\n\nindexer.setRelationMap(model, relationsIndex);\n')),(0,r.kt)("p",null,"Great! Now try to get again the property sets and you will see everything working nice and neat. In fact, lets try to get the building storey of one element in the IFC \ud83d\udc47"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const buildingStorey = indexer.getEntityRelations(\n model,\n 6518,\n "ContainedInStructure",\n);\n\nif (buildingStorey && buildingStorey[0]) {\n const storey = await model.getProperties(buildingStorey[0]);\n console.log(storey);\n}\n')),(0,r.kt)("admonition",{type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"Despite there are some relations that corresponds to only one element (e.g., an element can only have one associated building storey) the ",(0,r.kt)("inlineCode",{parentName:"p"},"getEntityRelations")," will always return an array. That's the reason we take the first buildingStorey relation despite it will always be the only one.")),(0,r.kt)("h3",{id:"\ufe0f-measuring-the-performance-optional"},"\u23f1\ufe0f Measuring the performance (optional)"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"We'll use the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/mrdoob/stats.js"},"Stats.js")," to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const stats = new Stats();\nstats.showPanel(2);\ndocument.body.append(stats.dom);\nstats.dom.style.left = "0px";\nstats.dom.style.zIndex = "unset";\nworld.renderer.onBeforeUpdate.add(() => stats.begin());\nworld.renderer.onAfterUpdate.add(() => stats.end());\n')),(0,r.kt)("h3",{id:"-adding-some-ui"},"\ud83e\udde9 Adding some UI"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"We will use the ",(0,r.kt)("inlineCode",{parentName:"p"},"@thatopen/ui")," library to add some simple and cool UI elements to our app. First, we need to call the ",(0,r.kt)("inlineCode",{parentName:"p"},"init")," method of the ",(0,r.kt)("inlineCode",{parentName:"p"},"BUI.Manager")," class to initialize the library:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"BUI.Manager.init();\n")),(0,r.kt)("p",null,"Now we will add some UI export the relations that we just generated. For more information about the UI library, you can check the specific documentation for it!"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const panel = BUI.Component.create(() => {\n return BUI.html`\n \n \n \n \n \n \n\n \n \n `;\n});\n\ndocument.body.append(panel);\n')),(0,r.kt)("p",null,"And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const button = BUI.Component.create(() => {\n return BUI.html`\n \n \n `;\n});\n\ndocument.body.append(button);\n')),(0,r.kt)("h3",{id:"-wrap-up"},"\ud83c\udf89 Wrap up"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"That's it! Now you know how to get an easy way to get the relations of your model. Keep going with more tutorials! \ud83d\udcaa"),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/IfcRelationsIndexer"}))}h.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[8491],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>h});var o=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=o.createContext({}),p=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},d=function(e){var t=p(e.components);return o.createElement(s.Provider,{value:t},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},u=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=p(n),u=a,h=c["".concat(s,".").concat(u)]||c[u]||m[u]||r;return n?o.createElement(h,i(i({ref:t},d),{},{components:n})):o.createElement(h,i({ref:t},d))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,i=new Array(r);i[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[c]="string"==typeof e?e:a,i[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>c});var o=n(7462),a=n(3366),r=(n(7294),n(3905)),i=["components"],l={},s=void 0,p={unversionedId:"Tutorials/Components/Core/IfcRelationsIndexer",id:"Tutorials/Components/Core/IfcRelationsIndexer",title:"IfcRelationsIndexer",description:"Copying and pasting? We've got you covered! You can find the full source code of this tutorial here.",source:"@site/docs/Tutorials/Components/Core/IfcRelationsIndexer.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/IfcRelationsIndexer",permalink:"/Tutorials/Components/Core/IfcRelationsIndexer",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"IfcPropertiesTiler",permalink:"/Tutorials/Components/Core/IfcPropertiesTiler"},next:{title:"MiniMap",permalink:"/Tutorials/Components/Core/MiniMap"}},d={},c=[{value:"\ud83d\udd01 Getting relations (easy)",id:"-getting-relations-easy",level:3},{value:"\ud83c\udf0e Setting up a simple scene",id:"-setting-up-a-simple-scene",level:3},{value:"\ud83e\uddf3 Loading a BIM model",id:"-loading-a-bim-model",level:3},{value:"\ud83d\udccb Indexing the model",id:"-indexing-the-model",level:3},{value:"\ud83d\udcc4 Getting element psets",id:"-getting-element-psets",level:3},{value:"\u2198\ufe0f Exporting the indexation",id:"\ufe0f-exporting-the-indexation",level:3},{value:"\u2197\ufe0f Loading back the relations index",id:"\ufe0f-loading-back-the-relations-index",level:3},{value:"\u23f1\ufe0f Measuring the performance (optional)",id:"\ufe0f-measuring-the-performance-optional",level:3},{value:"\ud83e\udde9 Adding some UI",id:"-adding-some-ui",level:3},{value:"\ud83c\udf89 Wrap up",id:"-wrap-up",level:3}],m={toc:c},u="wrapper";function h(e){var t=e.components,n=(0,a.Z)(e,i);return(0,r.kt)(u,(0,o.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("admonition",{title:"Source",type:"info"},(0,r.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/packages/core/src/ifc/IfcRelationsIndexer/example.ts"},"here"),".")),(0,r.kt)("h3",{id:"-getting-relations-easy"},"\ud83d\udd01 Getting relations (easy)"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"If you're aware of the IFC schema, you should know that all the possible information an entity have is not directly inside its attributes. For example, the property sets, classifications, materials, etc, of a wall (or any other element) are not directly in the wall attributes \ud83e\udd2f but in other entities which are related to the wall using relations."),(0,r.kt)("admonition",{title:"Why so much indirection?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"Indirection is perfect for an schema like the IFC which aims to store all the building data within a single text file in the easiest way possible. However, is not that easy to work just because you need to find the relations you want to get to the element data you're looking for \ud83d\ude2a. Luckily for you, the ",(0,r.kt)("inlineCode",{parentName:"p"},"IfcRelationsIndexer")," already gives you an easy way to get the entities which are related with your elements thanks to the inverse attributes! \ud83d\udd25\ud83d\udd25")),(0,r.kt)("p",null,"In this tutorial, we will import:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"@thatopen/ui")," to add some simple and cool UI menus."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"@thatopen/components")," to set up the barebone of our app."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"Stats.js")," (optional) to measure the performance of our app.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'import Stats from "stats.js";\nimport * as BUI from "@thatopen/ui";\nimport * as OBC from "@thatopen/components";\n')),(0,r.kt)("h3",{id:"-setting-up-a-simple-scene"},"\ud83c\udf0e Setting up a simple scene"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"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."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const container = document.getElementById("container")!;\n\nconst components = new OBC.Components();\n\nconst worlds = components.get(OBC.Worlds);\n\nconst world = worlds.create<\n OBC.SimpleScene,\n OBC.SimpleCamera,\n OBC.SimpleRenderer\n>();\n\nworld.scene = new OBC.SimpleScene(components);\nworld.renderer = new OBC.SimpleRenderer(components, container);\nworld.camera = new OBC.SimpleCamera(components);\n\ncomponents.init();\n\nworld.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);\n\nworld.scene.setup();\n\nconst grids = components.get(OBC.Grids);\ngrids.create(world);\n')),(0,r.kt)("p",null,"We'll make the background of the scene transparent so that it looks good in our docs page, but you don't have to do that in your app!"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"world.scene.three.background = null;\n")),(0,r.kt)("h3",{id:"-loading-a-bim-model"},"\ud83e\uddf3 Loading a BIM model"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"We'll start by adding an IFC model to our scene."),(0,r.kt)("admonition",{title:"Loading an IFC?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"If you are not familiar with IFC loading, check out the IFC Loader tutorial first!")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const ifcLoader = components.get(OBC.IfcLoader);\nawait ifcLoader.setup();\nconst file = await fetch("https://thatopen.github.io/engine_components/resources/small.ifc");\nconst buffer = await file.arrayBuffer();\nconst typedArray = new Uint8Array(buffer);\nconst model = await ifcLoader.load(typedArray);\nworld.scene.three.add(model);\n')),(0,r.kt)("h3",{id:"-indexing-the-model"},"\ud83d\udccb Indexing the model"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Once the model is loaded in memory, you just need to get an instance of the IfcRelationsIndexer and process the model... it's as easy as that! \ud83d\ude0e"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const indexer = components.get(OBC.IfcRelationsIndexer);\nawait indexer.process(model);\n")),(0,r.kt)("p",null,"The result of that is basically a map where the keys are the expressIDs and the values are other expressIDs related to the first one and grouped by the type of relation. You don't need to worry too much about the details of that, as the usage is pretty straighforward \ud83d\udd1d. The only thing that matters is you've now an easy way to access the entities related to your element \ud83d\ude42"),(0,r.kt)("h3",{id:"-getting-element-psets"},"\ud83d\udcc4 Getting element psets"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"One of the most important relations between different entities is the ",(0,r.kt)("inlineCode",{parentName:"p"},"IfcRelDefinesByProperties"),". That relation links together an arbitrary entity with a set of ",(0,r.kt)("inlineCode",{parentName:"p"},"IfcPropertySet")," entities that applies properties. Getting them with the indexer once the model is indexed is pretty easy:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const psets = indexer.getEntityRelations(model, 6518, "IsDefinedBy");\nif (psets) {\n for (const expressID of psets) {\n // You can get the pset attributes like this\n const pset = await model.getProperties(expressID);\n console.log(pset);\n // You can get the pset props like this or iterate over pset.HasProperties yourself\n await OBC.IfcPropertiesUtils.getPsetProps(\n model,\n expressID,\n async (propExpressID) => {\n const prop = await model.getProperties(propExpressID);\n console.log(prop);\n },\n );\n }\n}\n')),(0,r.kt)("admonition",{type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"IsDefinedBy is the inverse attribute name in the IFC Schema that holds the relations with property sets \ud83d\ude09")),(0,r.kt)("p",null,"Awesome! really easy right?"),(0,r.kt)("h3",{id:"\ufe0f-exporting-the-indexation"},"\u2198\ufe0f Exporting the indexation"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"In bigger models, the process to calculate the relations index may take some time. There is no reason to calculate over and over the relations index every time you load a model. If the model hasn't change, their properties shouldn't neither! So, let's download the relations index to load it later."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const downloadJSON = (json: string, name: string) => {\n const file = new File([json], name);\n const a = document.createElement("a");\n a.href = URL.createObjectURL(file);\n a.download = file.name;\n a.click();\n URL.revokeObjectURL(a.href);\n};\n\nconst json = indexer.serializeModelRelations(model);\nconsole.log(json);\n')),(0,r.kt)("admonition",{type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"As ",(0,r.kt)("inlineCode",{parentName:"p"},"@thatopen/components")," can be used in either NodeJS and Browser environments, the logic to generate a JSON file may vary!")),(0,r.kt)("p",null,"Now, in case you've loaded several models and want to get all the computed relations, there is also a handy method to do it."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const allRelationsJSON = indexer.serializeAllRelations();\n")),(0,r.kt)("h3",{id:"\ufe0f-loading-back-the-relations-index"},"\u2197\ufe0f Loading back the relations index"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"What do we gain with having a pre-processed relations index if we can't use it again, right? Well, let's use the downloaded relations index \ud83d\ude0e"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'// Lets first delete the existing model relations\ndelete indexer.relationMaps[model.uuid];\nconst relationsIndexFile = await fetch("https://thatopen.github.io/engine_components/resources/small-relations.json");\nconst relationsIndex = indexer.getRelationsMapFromJSON(\n await relationsIndexFile.text(),\n);\n\nindexer.setRelationMap(model, relationsIndex);\n')),(0,r.kt)("p",null,"Great! Now try to get again the property sets and you will see everything working nice and neat. In fact, lets try to get the building storey of one element in the IFC \ud83d\udc47"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const buildingStorey = indexer.getEntityRelations(\n model,\n 6518,\n "ContainedInStructure",\n);\n\nif (buildingStorey && buildingStorey[0]) {\n const storey = await model.getProperties(buildingStorey[0]);\n console.log(storey);\n}\n')),(0,r.kt)("admonition",{type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"Despite there are some relations that corresponds to only one element (e.g., an element can only have one associated building storey) the ",(0,r.kt)("inlineCode",{parentName:"p"},"getEntityRelations")," will always return an array. That's the reason we take the first buildingStorey relation despite it will always be the only one.")),(0,r.kt)("h3",{id:"\ufe0f-measuring-the-performance-optional"},"\u23f1\ufe0f Measuring the performance (optional)"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"We'll use the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/mrdoob/stats.js"},"Stats.js")," to measure the performance of our app. We will add it to the top left corner of the viewport. This way, we'll make sure that the memory consumption and the FPS of our app are under control."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const stats = new Stats();\nstats.showPanel(2);\ndocument.body.append(stats.dom);\nstats.dom.style.left = "0px";\nstats.dom.style.zIndex = "unset";\nworld.renderer.onBeforeUpdate.add(() => stats.begin());\nworld.renderer.onAfterUpdate.add(() => stats.end());\n')),(0,r.kt)("h3",{id:"-adding-some-ui"},"\ud83e\udde9 Adding some UI"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"We will use the ",(0,r.kt)("inlineCode",{parentName:"p"},"@thatopen/ui")," library to add some simple and cool UI elements to our app. First, we need to call the ",(0,r.kt)("inlineCode",{parentName:"p"},"init")," method of the ",(0,r.kt)("inlineCode",{parentName:"p"},"BUI.Manager")," class to initialize the library:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"BUI.Manager.init();\n")),(0,r.kt)("p",null,"Now we will add some UI export the relations that we just generated. For more information about the UI library, you can check the specific documentation for it!"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const panel = BUI.Component.create(() => {\n return BUI.html`\n \n \n \n \n \n \n\n \n \n `;\n});\n\ndocument.body.append(panel);\n')),(0,r.kt)("p",null,"And we will make some logic that adds a button to the screen when the user is visiting our app from their phone, allowing to show or hide the menu. Otherwise, the menu would make the app unusable."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const button = BUI.Component.create(() => {\n return BUI.html`\n \n \n `;\n});\n\ndocument.body.append(button);\n')),(0,r.kt)("h3",{id:"-wrap-up"},"\ud83c\udf89 Wrap up"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"That's it! Now you know how to get an easy way to get the relations of your model. Keep going with more tutorials! \ud83d\udcaa"),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/IfcRelationsIndexer"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/runtime~main.45106ad8.js b/build/assets/js/runtime~main.28990e4f.js similarity index 99% rename from build/assets/js/runtime~main.45106ad8.js rename to build/assets/js/runtime~main.28990e4f.js index 96ec5850..ac34d721 100644 --- a/build/assets/js/runtime~main.45106ad8.js +++ b/build/assets/js/runtime~main.28990e4f.js @@ -1 +1 @@ -(()=>{"use strict";var e,a,b,d,f,c={},t={};function r(e){var a=t[e];if(void 0!==a)return a.exports;var b=t[e]={id:e,loaded:!1,exports:{}};return c[e].call(b.exports,b,b.exports,r),b.loaded=!0,b.exports}r.m=c,r.c=t,e=[],r.O=(a,b,d,f)=>{if(!b){var c=1/0;for(i=0;i=f)&&Object.keys(r.O).every((e=>r.O[e](b[o])))?b.splice(o--,1):(t=!1,f0&&e[i-1][2]>f;i--)e[i]=e[i-1];e[i]=[b,d,f]},r.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return r.d(a,{a:a}),a},b=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,d){if(1&d&&(e=this(e)),8&d)return e;if("object"==typeof e&&e){if(4&d&&e.__esModule)return e;if(16&d&&"function"==typeof e.then)return e}var f=Object.create(null);r.r(f);var c={};a=a||[null,b({}),b([]),b(b)];for(var t=2&d&&e;"object"==typeof t&&!~a.indexOf(t);t=b(t))Object.getOwnPropertyNames(t).forEach((a=>c[a]=()=>e[a]));return c.default=()=>e,r.d(f,c),f},r.d=(e,a)=>{for(var b in a)r.o(a,b)&&!r.o(e,b)&&Object.defineProperty(e,b,{enumerable:!0,get:a[b]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((a,b)=>(r.f[b](e,a),a)),[])),r.u=e=>"assets/js/"+({53:"935f2afb",94:"111feb22",155:"d9bc4fdf",261:"4f749076",282:"2aba4d5d",321:"ee29e6ad",327:"60d286c5",350:"14b2fcb7",372:"a19b4c2c",400:"23f0a572",440:"02b3ccc6",549:"4c4c2199",638:"8e79a5c7",761:"1c5bde81",835:"a599bc11",984:"3834acf5",1129:"255d81eb",1160:"bb95e244",1221:"1c46db60",1393:"0f647618",1513:"60f3e948",1625:"746b65d1",1667:"929d4cd2",1694:"93d93575",1732:"e4857e4d",1764:"44d0ab4a",1842:"f45379a9",1860:"2a9a2072",2011:"b1f8f8a6",2154:"da19a474",2232:"e3702cd4",2238:"1567e004",2394:"1b39a4e0",2609:"9417cdd2",2720:"733d79ef",2837:"89d80fa0",3045:"c2bbc440",3132:"fba9713b",3149:"2895e020",3156:"5251865f",3208:"9cc62e14",3386:"140c5f61",3501:"7899e7db",3659:"0849f8a0",3738:"9064555a",3771:"8a5feacb",3893:"7887f5d5",3941:"9d7eea47",3991:"98508be6",4125:"80bb5c14",4238:"a6a8c80c",4283:"5c7b714e",4321:"15cde90e",4335:"dc2a58ea",4376:"f77615d9",4394:"ca57013d",4591:"7f370fb4",4646:"d05ab5b9",4718:"753b1b55",4847:"7d516a78",4872:"f71f0617",4914:"3faecb4d",5243:"17d65022",5260:"6aecd34e",5264:"bf390c76",5269:"035e0bbb",5282:"8f0e23c6",5336:"413ac913",5362:"232409b2",5446:"ac8bbf91",5447:"c62bb28c",5499:"9b7b7fda",5648:"b86baa83",5662:"be95f796",5936:"4b44f7a2",6015:"6ff4b68b",6038:"5813db83",6061:"9bd74671",6162:"5a3844a3",6383:"36f30aba",6569:"07285760",6631:"d60cd624",6635:"35bc646f",6644:"519cde36",6811:"72d46929",6812:"c1b3b982",7027:"57f3dce9",7054:"9dd8a0d2",7080:"4d54d076",7116:"0dcd76a4",7154:"73803af1",7283:"5416c1e0",7306:"f6aebfbf",7389:"dbc536f0",7592:"e8a4ea3d",7597:"5e8c322a",7605:"2e22e314",7621:"5094df0e",7709:"fa23a143",7784:"f7901ade",7918:"17896441",7942:"6b4facaa",8046:"f70643aa",8092:"070f01c0",8094:"65e97039",8100:"3eac639e",8141:"16dd699a",8220:"d3dcb6ee",8230:"26cef902",8239:"a711233c",8400:"b46e04f8",8470:"416b2475",8491:"308c405f",8509:"0d0787b6",8553:"f0d87bf4",8604:"82c425bb",8645:"10d63750",8648:"63ea579c",8655:"87654e65",8892:"8004d10c",8985:"8368d781",8995:"4bf8fd60",9018:"96c1a8ad",9065:"cd5a87c7",9098:"02cb068a",9170:"072af5be",9231:"bbb18ae5",9262:"01976200",9311:"9f1bf482",9514:"1be78505",9595:"7cc95c1b",9671:"0e384e19",9772:"c9909429",9773:"c6f50999",9908:"27876b76",9926:"38ed86ac"}[e]||e)+"."+{53:"5c42a705",94:"625cb4a1",155:"52599afa",261:"70333dd8",282:"76a4b932",321:"4d7f9c3a",327:"6fb4eca5",350:"1d7d8e2a",372:"a7410515",400:"308a90a9",440:"27a59e49",549:"63b1a199",638:"7f895758",761:"a2f41d95",835:"68952c38",984:"e9463fde",1129:"9cdf638c",1160:"265de2d8",1221:"db000c7b",1393:"5e6582a3",1513:"ad52528c",1625:"e16aac08",1667:"3431c4f6",1694:"a2123b2a",1732:"8c5e3dd2",1764:"149e314f",1842:"3f70e0f1",1860:"252e9c72",2011:"2a9e1e58",2154:"5bb218d4",2232:"ef6024b3",2238:"a7aca9b5",2394:"0b12ca46",2609:"99414796",2720:"e67b3c6a",2837:"8d88c9ba",3045:"9d5623a9",3132:"1f342564",3149:"792c2304",3156:"bf90fefc",3208:"5bb0c3ea",3386:"51f4100f",3501:"1dee1e61",3659:"b43a357c",3738:"61c8cf75",3771:"dcf04dc3",3893:"5c09b8c4",3941:"6e9aa767",3991:"d737f6b6",4125:"94e9dde9",4238:"cdbe9d16",4283:"ff7650b5",4321:"872dbdbb",4335:"8db72f50",4376:"b3fe716b",4394:"9ea94f80",4591:"61c48188",4646:"a01219f1",4718:"8dc4e862",4847:"dc507a44",4872:"1d3933cd",4914:"0adb9858",4972:"96c55074",5243:"e8e9d8f8",5260:"294afd81",5264:"7a9f3ee4",5269:"1bdb4d2e",5282:"071d91da",5336:"4ef3fee1",5362:"72bb1df4",5446:"9cf08345",5447:"5874442d",5499:"d81d0725",5648:"36e2f0f2",5662:"abadc49f",5936:"ad4f089a",6015:"c77637ca",6038:"5f3ddfd6",6061:"349df549",6162:"5e2d2c06",6383:"83655606",6569:"af4cfae0",6631:"53fa4f66",6635:"ed9c3a44",6644:"eb5b24a7",6811:"7ed16a36",6812:"7526063b",7027:"e22ef023",7054:"32165b2d",7080:"5d8b1de3",7116:"8f965752",7154:"fafcc52c",7283:"3e04c7f9",7306:"3b73aec4",7389:"92266a31",7592:"e3ed6a35",7597:"2da5ba5a",7605:"3f34bfb2",7621:"44606bb6",7709:"2d855b48",7784:"87a29880",7918:"2a0ca96b",7942:"610133c5",8046:"5458bee7",8092:"a754c109",8094:"a7d77ebb",8100:"3e4a42e9",8141:"38a45d17",8220:"7aeea05b",8230:"66f10c57",8239:"9db8fa68",8400:"92a3a2f2",8470:"b4b37b74",8491:"2e284b33",8509:"e56c9ead",8553:"d23cb3bf",8604:"0309c69e",8645:"75f3528c",8648:"85ca868d",8655:"9537dcd2",8892:"3082051c",8985:"68c27569",8995:"1bda64d4",9018:"f6ed53b8",9065:"e1ccdbbb",9098:"8dad5714",9170:"0acc5b4e",9231:"8bb2f74f",9262:"18bc0972",9311:"cbc76717",9514:"c3b88fa2",9595:"bfd584ef",9671:"4a14e1bd",9772:"478ddff5",9773:"7ea4406d",9908:"36af7355",9926:"e4c0d4c1"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),d={},f="docs:",r.l=(e,a,b,c)=>{if(d[e])d[e].push(a);else{var t,o;if(void 0!==b)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var f=d[e];if(delete d[e],t.parentNode&&t.parentNode.removeChild(t),f&&f.forEach((e=>e(b))),a)return a(b)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={17896441:"7918","935f2afb":"53","111feb22":"94",d9bc4fdf:"155","4f749076":"261","2aba4d5d":"282",ee29e6ad:"321","60d286c5":"327","14b2fcb7":"350",a19b4c2c:"372","23f0a572":"400","02b3ccc6":"440","4c4c2199":"549","8e79a5c7":"638","1c5bde81":"761",a599bc11:"835","3834acf5":"984","255d81eb":"1129",bb95e244:"1160","1c46db60":"1221","0f647618":"1393","60f3e948":"1513","746b65d1":"1625","929d4cd2":"1667","93d93575":"1694",e4857e4d:"1732","44d0ab4a":"1764",f45379a9:"1842","2a9a2072":"1860",b1f8f8a6:"2011",da19a474:"2154",e3702cd4:"2232","1567e004":"2238","1b39a4e0":"2394","9417cdd2":"2609","733d79ef":"2720","89d80fa0":"2837",c2bbc440:"3045",fba9713b:"3132","2895e020":"3149","5251865f":"3156","9cc62e14":"3208","140c5f61":"3386","7899e7db":"3501","0849f8a0":"3659","9064555a":"3738","8a5feacb":"3771","7887f5d5":"3893","9d7eea47":"3941","98508be6":"3991","80bb5c14":"4125",a6a8c80c:"4238","5c7b714e":"4283","15cde90e":"4321",dc2a58ea:"4335",f77615d9:"4376",ca57013d:"4394","7f370fb4":"4591",d05ab5b9:"4646","753b1b55":"4718","7d516a78":"4847",f71f0617:"4872","3faecb4d":"4914","17d65022":"5243","6aecd34e":"5260",bf390c76:"5264","035e0bbb":"5269","8f0e23c6":"5282","413ac913":"5336","232409b2":"5362",ac8bbf91:"5446",c62bb28c:"5447","9b7b7fda":"5499",b86baa83:"5648",be95f796:"5662","4b44f7a2":"5936","6ff4b68b":"6015","5813db83":"6038","9bd74671":"6061","5a3844a3":"6162","36f30aba":"6383","07285760":"6569",d60cd624:"6631","35bc646f":"6635","519cde36":"6644","72d46929":"6811",c1b3b982:"6812","57f3dce9":"7027","9dd8a0d2":"7054","4d54d076":"7080","0dcd76a4":"7116","73803af1":"7154","5416c1e0":"7283",f6aebfbf:"7306",dbc536f0:"7389",e8a4ea3d:"7592","5e8c322a":"7597","2e22e314":"7605","5094df0e":"7621",fa23a143:"7709",f7901ade:"7784","6b4facaa":"7942",f70643aa:"8046","070f01c0":"8092","65e97039":"8094","3eac639e":"8100","16dd699a":"8141",d3dcb6ee:"8220","26cef902":"8230",a711233c:"8239",b46e04f8:"8400","416b2475":"8470","308c405f":"8491","0d0787b6":"8509",f0d87bf4:"8553","82c425bb":"8604","10d63750":"8645","63ea579c":"8648","87654e65":"8655","8004d10c":"8892","8368d781":"8985","4bf8fd60":"8995","96c1a8ad":"9018",cd5a87c7:"9065","02cb068a":"9098","072af5be":"9170",bbb18ae5:"9231","01976200":"9262","9f1bf482":"9311","1be78505":"9514","7cc95c1b":"9595","0e384e19":"9671",c9909429:"9772",c6f50999:"9773","27876b76":"9908","38ed86ac":"9926"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,532:0};r.f.j=(a,b)=>{var d=r.o(e,a)?e[a]:void 0;if(0!==d)if(d)b.push(d[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var f=new Promise(((b,f)=>d=e[a]=[b,f]));b.push(d[2]=f);var c=r.p+r.u(a),t=new Error;r.l(c,(b=>{if(r.o(e,a)&&(0!==(d=e[a])&&(e[a]=void 0),d)){var f=b&&("load"===b.type?"missing":b.type),c=b&&b.target&&b.target.src;t.message="Loading chunk "+a+" failed.\n("+f+": "+c+")",t.name="ChunkLoadError",t.type=f,t.request=c,d[1](t)}}),"chunk-"+a,a)}},r.O.j=a=>0===e[a];var a=(a,b)=>{var d,f,[c,t,o]=b,n=0;if(c.some((a=>0!==e[a]))){for(d in t)r.o(t,d)&&(r.m[d]=t[d]);if(o)var i=o(r)}for(a&&a(b);n{"use strict";var e,a,b,d,f,c={},t={};function r(e){var a=t[e];if(void 0!==a)return a.exports;var b=t[e]={id:e,loaded:!1,exports:{}};return c[e].call(b.exports,b,b.exports,r),b.loaded=!0,b.exports}r.m=c,r.c=t,e=[],r.O=(a,b,d,f)=>{if(!b){var c=1/0;for(i=0;i=f)&&Object.keys(r.O).every((e=>r.O[e](b[o])))?b.splice(o--,1):(t=!1,f0&&e[i-1][2]>f;i--)e[i]=e[i-1];e[i]=[b,d,f]},r.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return r.d(a,{a:a}),a},b=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,d){if(1&d&&(e=this(e)),8&d)return e;if("object"==typeof e&&e){if(4&d&&e.__esModule)return e;if(16&d&&"function"==typeof e.then)return e}var f=Object.create(null);r.r(f);var c={};a=a||[null,b({}),b([]),b(b)];for(var t=2&d&&e;"object"==typeof t&&!~a.indexOf(t);t=b(t))Object.getOwnPropertyNames(t).forEach((a=>c[a]=()=>e[a]));return c.default=()=>e,r.d(f,c),f},r.d=(e,a)=>{for(var b in a)r.o(a,b)&&!r.o(e,b)&&Object.defineProperty(e,b,{enumerable:!0,get:a[b]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((a,b)=>(r.f[b](e,a),a)),[])),r.u=e=>"assets/js/"+({53:"935f2afb",94:"111feb22",155:"d9bc4fdf",261:"4f749076",282:"2aba4d5d",321:"ee29e6ad",327:"60d286c5",350:"14b2fcb7",372:"a19b4c2c",400:"23f0a572",440:"02b3ccc6",549:"4c4c2199",638:"8e79a5c7",761:"1c5bde81",835:"a599bc11",984:"3834acf5",1129:"255d81eb",1160:"bb95e244",1221:"1c46db60",1393:"0f647618",1513:"60f3e948",1625:"746b65d1",1667:"929d4cd2",1694:"93d93575",1732:"e4857e4d",1764:"44d0ab4a",1842:"f45379a9",1860:"2a9a2072",2011:"b1f8f8a6",2154:"da19a474",2232:"e3702cd4",2238:"1567e004",2394:"1b39a4e0",2609:"9417cdd2",2720:"733d79ef",2837:"89d80fa0",3045:"c2bbc440",3132:"fba9713b",3149:"2895e020",3156:"5251865f",3208:"9cc62e14",3386:"140c5f61",3501:"7899e7db",3659:"0849f8a0",3738:"9064555a",3771:"8a5feacb",3893:"7887f5d5",3941:"9d7eea47",3991:"98508be6",4125:"80bb5c14",4238:"a6a8c80c",4283:"5c7b714e",4321:"15cde90e",4335:"dc2a58ea",4376:"f77615d9",4394:"ca57013d",4591:"7f370fb4",4646:"d05ab5b9",4718:"753b1b55",4847:"7d516a78",4872:"f71f0617",4914:"3faecb4d",5243:"17d65022",5260:"6aecd34e",5264:"bf390c76",5269:"035e0bbb",5282:"8f0e23c6",5336:"413ac913",5362:"232409b2",5446:"ac8bbf91",5447:"c62bb28c",5499:"9b7b7fda",5648:"b86baa83",5662:"be95f796",5936:"4b44f7a2",6015:"6ff4b68b",6038:"5813db83",6061:"9bd74671",6162:"5a3844a3",6383:"36f30aba",6569:"07285760",6631:"d60cd624",6635:"35bc646f",6644:"519cde36",6811:"72d46929",6812:"c1b3b982",7027:"57f3dce9",7054:"9dd8a0d2",7080:"4d54d076",7116:"0dcd76a4",7154:"73803af1",7283:"5416c1e0",7306:"f6aebfbf",7389:"dbc536f0",7592:"e8a4ea3d",7597:"5e8c322a",7605:"2e22e314",7621:"5094df0e",7709:"fa23a143",7784:"f7901ade",7918:"17896441",7942:"6b4facaa",8046:"f70643aa",8092:"070f01c0",8094:"65e97039",8100:"3eac639e",8141:"16dd699a",8220:"d3dcb6ee",8230:"26cef902",8239:"a711233c",8400:"b46e04f8",8470:"416b2475",8491:"308c405f",8509:"0d0787b6",8553:"f0d87bf4",8604:"82c425bb",8645:"10d63750",8648:"63ea579c",8655:"87654e65",8892:"8004d10c",8985:"8368d781",8995:"4bf8fd60",9018:"96c1a8ad",9065:"cd5a87c7",9098:"02cb068a",9170:"072af5be",9231:"bbb18ae5",9262:"01976200",9311:"9f1bf482",9514:"1be78505",9595:"7cc95c1b",9671:"0e384e19",9772:"c9909429",9773:"c6f50999",9908:"27876b76",9926:"38ed86ac"}[e]||e)+"."+{53:"5c42a705",94:"625cb4a1",155:"52599afa",261:"70333dd8",282:"76a4b932",321:"4d7f9c3a",327:"6fb4eca5",350:"1d7d8e2a",372:"a7410515",400:"308a90a9",440:"27a59e49",549:"63b1a199",638:"7f895758",761:"a2f41d95",835:"68952c38",984:"e9463fde",1129:"9cdf638c",1160:"265de2d8",1221:"db000c7b",1393:"5e6582a3",1513:"ad52528c",1625:"e16aac08",1667:"3431c4f6",1694:"a2123b2a",1732:"8c5e3dd2",1764:"149e314f",1842:"3f70e0f1",1860:"252e9c72",2011:"2a9e1e58",2154:"5bb218d4",2232:"ef6024b3",2238:"a7aca9b5",2394:"0b12ca46",2609:"99414796",2720:"e67b3c6a",2837:"8d88c9ba",3045:"9d5623a9",3132:"1f342564",3149:"792c2304",3156:"bf90fefc",3208:"5bb0c3ea",3386:"51f4100f",3501:"1dee1e61",3659:"b43a357c",3738:"61c8cf75",3771:"dcf04dc3",3893:"5c09b8c4",3941:"6e9aa767",3991:"d737f6b6",4125:"94e9dde9",4238:"cdbe9d16",4283:"ff7650b5",4321:"872dbdbb",4335:"8db72f50",4376:"b3fe716b",4394:"9ea94f80",4591:"61c48188",4646:"a01219f1",4718:"8dc4e862",4847:"dc507a44",4872:"1d3933cd",4914:"0adb9858",4972:"96c55074",5243:"e8e9d8f8",5260:"294afd81",5264:"7a9f3ee4",5269:"1bdb4d2e",5282:"071d91da",5336:"4ef3fee1",5362:"72bb1df4",5446:"9cf08345",5447:"5874442d",5499:"d81d0725",5648:"36e2f0f2",5662:"abadc49f",5936:"ad4f089a",6015:"c77637ca",6038:"5f3ddfd6",6061:"349df549",6162:"5e2d2c06",6383:"83655606",6569:"af4cfae0",6631:"53fa4f66",6635:"ed9c3a44",6644:"eb5b24a7",6811:"7ed16a36",6812:"7526063b",7027:"e22ef023",7054:"32165b2d",7080:"5d8b1de3",7116:"8f965752",7154:"fafcc52c",7283:"3e04c7f9",7306:"3b73aec4",7389:"92266a31",7592:"e3ed6a35",7597:"2da5ba5a",7605:"3f34bfb2",7621:"44606bb6",7709:"2d855b48",7784:"87a29880",7918:"2a0ca96b",7942:"610133c5",8046:"5458bee7",8092:"a754c109",8094:"a7d77ebb",8100:"3e4a42e9",8141:"38a45d17",8220:"7aeea05b",8230:"66f10c57",8239:"9db8fa68",8400:"92a3a2f2",8470:"b4b37b74",8491:"91e68427",8509:"e56c9ead",8553:"d23cb3bf",8604:"0309c69e",8645:"75f3528c",8648:"85ca868d",8655:"9537dcd2",8892:"3082051c",8985:"68c27569",8995:"1bda64d4",9018:"f6ed53b8",9065:"e1ccdbbb",9098:"8dad5714",9170:"0acc5b4e",9231:"8bb2f74f",9262:"18bc0972",9311:"cbc76717",9514:"c3b88fa2",9595:"bfd584ef",9671:"4a14e1bd",9772:"478ddff5",9773:"7ea4406d",9908:"36af7355",9926:"e4c0d4c1"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),d={},f="docs:",r.l=(e,a,b,c)=>{if(d[e])d[e].push(a);else{var t,o;if(void 0!==b)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var f=d[e];if(delete d[e],t.parentNode&&t.parentNode.removeChild(t),f&&f.forEach((e=>e(b))),a)return a(b)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={17896441:"7918","935f2afb":"53","111feb22":"94",d9bc4fdf:"155","4f749076":"261","2aba4d5d":"282",ee29e6ad:"321","60d286c5":"327","14b2fcb7":"350",a19b4c2c:"372","23f0a572":"400","02b3ccc6":"440","4c4c2199":"549","8e79a5c7":"638","1c5bde81":"761",a599bc11:"835","3834acf5":"984","255d81eb":"1129",bb95e244:"1160","1c46db60":"1221","0f647618":"1393","60f3e948":"1513","746b65d1":"1625","929d4cd2":"1667","93d93575":"1694",e4857e4d:"1732","44d0ab4a":"1764",f45379a9:"1842","2a9a2072":"1860",b1f8f8a6:"2011",da19a474:"2154",e3702cd4:"2232","1567e004":"2238","1b39a4e0":"2394","9417cdd2":"2609","733d79ef":"2720","89d80fa0":"2837",c2bbc440:"3045",fba9713b:"3132","2895e020":"3149","5251865f":"3156","9cc62e14":"3208","140c5f61":"3386","7899e7db":"3501","0849f8a0":"3659","9064555a":"3738","8a5feacb":"3771","7887f5d5":"3893","9d7eea47":"3941","98508be6":"3991","80bb5c14":"4125",a6a8c80c:"4238","5c7b714e":"4283","15cde90e":"4321",dc2a58ea:"4335",f77615d9:"4376",ca57013d:"4394","7f370fb4":"4591",d05ab5b9:"4646","753b1b55":"4718","7d516a78":"4847",f71f0617:"4872","3faecb4d":"4914","17d65022":"5243","6aecd34e":"5260",bf390c76:"5264","035e0bbb":"5269","8f0e23c6":"5282","413ac913":"5336","232409b2":"5362",ac8bbf91:"5446",c62bb28c:"5447","9b7b7fda":"5499",b86baa83:"5648",be95f796:"5662","4b44f7a2":"5936","6ff4b68b":"6015","5813db83":"6038","9bd74671":"6061","5a3844a3":"6162","36f30aba":"6383","07285760":"6569",d60cd624:"6631","35bc646f":"6635","519cde36":"6644","72d46929":"6811",c1b3b982:"6812","57f3dce9":"7027","9dd8a0d2":"7054","4d54d076":"7080","0dcd76a4":"7116","73803af1":"7154","5416c1e0":"7283",f6aebfbf:"7306",dbc536f0:"7389",e8a4ea3d:"7592","5e8c322a":"7597","2e22e314":"7605","5094df0e":"7621",fa23a143:"7709",f7901ade:"7784","6b4facaa":"7942",f70643aa:"8046","070f01c0":"8092","65e97039":"8094","3eac639e":"8100","16dd699a":"8141",d3dcb6ee:"8220","26cef902":"8230",a711233c:"8239",b46e04f8:"8400","416b2475":"8470","308c405f":"8491","0d0787b6":"8509",f0d87bf4:"8553","82c425bb":"8604","10d63750":"8645","63ea579c":"8648","87654e65":"8655","8004d10c":"8892","8368d781":"8985","4bf8fd60":"8995","96c1a8ad":"9018",cd5a87c7:"9065","02cb068a":"9098","072af5be":"9170",bbb18ae5:"9231","01976200":"9262","9f1bf482":"9311","1be78505":"9514","7cc95c1b":"9595","0e384e19":"9671",c9909429:"9772",c6f50999:"9773","27876b76":"9908","38ed86ac":"9926"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,532:0};r.f.j=(a,b)=>{var d=r.o(e,a)?e[a]:void 0;if(0!==d)if(d)b.push(d[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var f=new Promise(((b,f)=>d=e[a]=[b,f]));b.push(d[2]=f);var c=r.p+r.u(a),t=new Error;r.l(c,(b=>{if(r.o(e,a)&&(0!==(d=e[a])&&(e[a]=void 0),d)){var f=b&&("load"===b.type?"missing":b.type),c=b&&b.target&&b.target.src;t.message="Loading chunk "+a+" failed.\n("+f+": "+c+")",t.name="ChunkLoadError",t.type=f,t.request=c,d[1](t)}}),"chunk-"+a,a)}},r.O.j=a=>0===e[a];var a=(a,b)=>{var d,f,[c,t,o]=b,n=0;if(c.some((a=>0!==e[a]))){for(d in t)r.o(t,d)&&(r.m[d]=t[d]);if(o)var i=o(r)}for(a&&a(b);n 🧹 Keeping them clean | That Open Docs - +

🧹 Keeping them clean

🧽 Basics

Always extend from the base Component class. 👪

import * as OBC from "@thatopen/components"

export MyComponent extends OBC.Component {
// ...
}

Always name the base file of your component index.ts and store it in a folder with the component name. If you need to include other supporting files, create a src folder in the component folder. You can call those supporting file whatever you want. The folder should have another index.ts file exporting all the elements that have to be exported from the folder. This is a basic folder structure: 🗃️

  • MyComponent

    • index.ts

    • src

      • supporting-file-1.ts

      • supporting-file-2.ts

      • index.ts

🧼 TypeScript

Follow the Single Responsibility Principle. 🥇

Always name private members with underscore. 🥷🏻

Avoid using ! in property fields. If a property element is not initialized in the constructor, you can either use ?, or create a getter to assert that it exists before getting it, like this:

private _customProperty?: string;

get customProperty(): string {
if(!this._customProperty) {
throw new Error("Custom property not initialized!");
}
return this._customProperty;
}

Avoid using any as much as possible. ❌

Never define private properties in the constructor. Make them explicit beforehand: 📋

/*Incorrect*/
constructor(private _components: Components)

/*Correct*/
private _components: Components
constructor(components: Components) {
this._components = components;
}

Always make events readonly and initialize them directly. ⚡

readonly onCreated = new OBC.Event<number>()

Always make sure to know the interfaces you can implement when creating your component (i.e. Creatable, Hideable, UI, etc), that way we keep things uniform in terms of properties and methods naming and types.

📚 Documentation

In tutorials, try to not reference specifics inside paragraphs. That allows to easily update the tutorial code without need to also update the paragraphs. 👇🏻

/*❌ Incorrect */
/*MD
To add a cube to the scene, you need to call scene.add()
*/

scene.add(cube)

/*✅ Correct*/
/*MD
To add a cube to the scene, just add the following code line!
*/

scene.add(cube)

🧠 Memory management

Memory management is critical when using Open BIM components. Not paying attention to this can result in applications that consume more and more memory, up to a point in which it freezes / crashes. This is especially relevant when using SPA (Single Page Application) libraries and frameworks, like React, Angular, Vue, etc. 🛑

To make sure your component doesn’t cause memory leaks and stays efficient, you need to make sure that:

  • It is added to the components instance in the constructor like specified in the component creation guide.

  • It implements the Disposable interface. This will force you to implement a dispose() method that will be called automatically by the library when components.dispose() is called. You can add all the clean up logic inside this method. You also need to add a onDisposed event so that you can subscribe to the deletion of that component to clean up.

There are some things that you want to clean up inside the dispose method:

🥎 3D objects and materials

Three.js needs to manually release the memory of the geometry and materials that are not used anymore. If your component creates any new geometry or material, you need to keep track of it and get rid of it. You can do this in 2 ways:

  1. 🧹 Using the Three.js dispose method to delete all geometries and materials, including their children.

  2. 🧹 Using the Disposer component provided by the components library, which does everything for you.

To make sure that the browser gets rid of this memory, you should also leave this data out of scope (e.g. emptying the array where they are after disposing it). For instance, if you are keeping track of all your meshes in an array called meshes, you can get rid of it like this: 👇🏻

import * as THREE from "three";
import * as OBC from "@thatopen/components";

class YourComponent extends Component implements Disposable {

// ...

readonly onDisposed = new OBC.Event();

private _meshes: Mesh[];

dispose() {
// ...
const disposer = this.components.get(OBC.Disposer);
for(const mesh of this.meshes) {
// The disposer gets rid of geometries and materials
// including children
disposer.dispose(mesh);
}
// Removing all references to them
// in arrays an object is critical for this to work
this._meshes = [];
this.onDisposed.trigger();
this.onDisposed.reset();
}

}

📅 Events

Events are a nice way of binding HTML elements to JS logic. A common way of doing that is using addEventListener. That’s fine if all the events are bound to HTML elements that you create inside your component and are destroyed when your component is disposed. 👌🏻

But in some situations you’ll need to add events to HTML elements outside your components, or even to the global window object. In those cases, you will need to make sure that you get rid of these events when your component is disposed. You can do that with removeEventListener, and making sure that you keep a reference to the logic as an arrow function. 🏹

To make sure you don’t forget about getting rid of your events, it’s a good practice to create a setupEvents method that allows you to toggle them like this: 👇🏻

import * as THREE from "three";
import * as OBC from "@thatopen/components";

class YourComponent extends Component implements Disposable {

// ...

constructor() {
this.setupEvents(true);
}

dispose() {
// ...
this.setupEvents(false);
// ...
}

private setupEvents(active: boolean) {
if(active) {
window.addEventListener("mousemove", this.logMessage);
} else {
window.removeEventListener("mousemove", this.logMessage);
}
}

private logMessage = () => {
console.log("Hey!");
}
}

🐘 Huge objects / arrays

Some components are data-heavy. JavaScript has an automatic garbage collector that should take care of these, but that can take some time. To accelerate this release of memory, you can just assign them an empty value:

import * as THREE from "three";
import * as OBC from "@thatopen/components";

class YourComponent extends Component implements Disposable {

dataArray: any = [];
dataObject: any = {};

dispose() {
// ...
this.dataArray= [];
this.dataObject= {};
}
}
- + \ No newline at end of file diff --git a/build/components/creating-components/index.html b/build/components/creating-components/index.html index 44d23528..a4f65cff 100644 --- a/build/components/creating-components/index.html +++ b/build/components/creating-components/index.html @@ -4,13 +4,13 @@ 🦾 Making your own | That Open Docs - +

🦾 Making your own

👪 Create it

Our libraries have many components, but the BIM world is vast and it's impossible to cover all use cases. But that's the biggest power of components: they are flexible and can be extended to cover all use cases.

We like types, but you don't have to

We are going to do the examples in TypeScript, but don't worry if you only know JavaScript! Translating these examples to JavaScript would be exactly the same but removing all the type definitions. 🤝

Creating a component is as simple as creating a class that extends the basic Component class. For example, let's create a "Hello world" component, whose only mission is to log a greeting message in the console. 👋🏻

import * as OBC from "@thatopen/components";

/**
* A basic component to say hello in the console.
*/
export class HelloWorldComponent extends OBC.Component {

static readonly uuid = "60bd6763-f9ff-4820-a04f-2054922c0297" as const;

enabled = true;

private readonly _message = "Hello";

constructor(components: OBC.Components) {
super(components);
components.add(HelloWorldComponent.uuid, this);
}

greet(name: string) {
const message = `${this._message} ${name}!`;
console.log(message);
}
}

As you can see, the structure of the component is very simple. The Component base class requires certain things:

  • A static UUID (v4) that identifies it uniquely within the Components instance that is used to create it.

  • A constructor where the only parameter is the object Components. This Components object will be available as a public property of the class automatically. Also, you need to add the component to the Components instance in the constructor.

  • An enabled property that indicates whether the component is active or not.

Now, you can use this component just as any of the components you will get to know in the tutorials! 👩🏻‍🏫

🌍 Availability

As you can see, components have a static UUID (v4) and are registered in the Components instance. That way, we make sure that they are unique and globally available through your entire application (or Components instance). You can access that component instance anywhere else in your application like this:

// Somewhere in your app:

const helloWorldComponent = new HelloWorldComponent(components);

// Somewhere else in your app:

const hwComponent = await components.get(HelloWorldComponent);

We strongly recommend that you always get the library built-in components using the get method directly, never instantiating them yourself. Don't worry, if they are not instanced, they library will do it for you automatically! 🤖

♻️ Lifecycle

The library will take care of updating your components and releasing their memory when your 3D app is diposed, preventing memory leaks. You can set up your component easily using the interfaces provided by the library. For example:

  • With Updateable, the library will automatically update your component every frame. 🔄️

  • With Disposable, the library will release the memory of your component when the application is disposed, preventing memory leaks. 🧹

Let's see them in action!

import * as THREE from "three";
import * as OBC from "@thatopen/components";

/**
* A basic component to say hello in the console.
*/
export class HelloWorldComponent extends OBC.Component
implements OBC.Disposable, OBC.Updateable {
static readonly uuid = "0f89b34b-fc6b-4b97-b56d-1edeb0a308a2";

readonly onAfterUpdate = new OBC.Event();

readonly onBeforeUpdate = new OBC.Event();

readonly onDisposed = new OBC.Event();

enabled = true;

someMesh = new THREE.Mesh();

private readonly _message = "Hello";

constructor(components: OBC.Components) {
super(components);
components.add(HelloWorldComponent.uuid, this);
}

greet(name: string) {
const message = `${this._message} ${name}!`;
console.log(message);
}

dispose() {
this.enabled = false;
// Make sure to clean up the events
this.onBeforeUpdate.reset();
this.onAfterUpdate.reset();
// Use the disposer component to easily dispose THREE.js objects
const disposer = this.components.get(OBC.Disposer);
disposer.destroy(this.someMesh);
this.onDisposed.trigger();
this.onDisposed.reset();
}

async update(delta?: number) {
this.onBeforeUpdate.trigger();
console.log("Updated! Delta: " + delta);
this.onAfterUpdate.trigger();
}
}

This is it! Now, the library will take care of updating and disposing your component automatically. 💪🏻🤖

🧹 Cleanliness

It's important that you keep your components clean and tidy! To do that, check out the short guide for avoiding smells when creating components. 🪐

💅🏻 UI

We strongly recommend that you don't include UI logic in your components. Rather, we have a UI Components library that is specific for building BIM UIs super easily. The code of both libraries (components and UI components) is completely decoupled. We encourage you to do the same. 🍐🍎

Check out the UI Components tutorials for more details about how to build your own UI elements.

- + \ No newline at end of file diff --git a/build/components/getting-started/index.html b/build/components/getting-started/index.html index ade1c817..30d7b28c 100644 --- a/build/components/getting-started/index.html +++ b/build/components/getting-started/index.html @@ -4,13 +4,13 @@ 🚀 Getting started | That Open Docs - +

🚀 Getting started

👩🏻‍🏫 Component ABC

Components are the building blocks of all our libraries. In short, components have 2 things:

  • 🌍 Globally availability: BIM applications have many parts, and in many occasions we will need to access logic from several places in our app easily and in a decoupled manner.

  • 🧹 Lifecycle management: 3D applications need to be able to update their parts at every frame. In addition, Three.js applications need to manage memory to ensure that we don't have memory leaks, especially when using technologies like React, Angular, etc.

Why components?

Our goal is to allow everyone to create BIM software. But that's not enough; creating a BIM software is no easy task, especially when doing it alone (as a company or individual).

Wouldn't it be nice if all the software that we develop spoke the same language? That way, we could all share, buy and sell it to solve each other's problem, creating a decentralised ecosystem. That's exactly what Components are: a very basic set of rules that allows everyone to build their own tools on top of the same foundations, so that everything is compatible. 👞👟🥿👠

The cool thing about components is that they are extensible. We provide a wide set of components that cover the basic features of any BIM app, but buildings are complex and there are many use cases. Using our technology as a basis, you'll be able to create your own components in no time. We also cover that here. 😉

But first, let's get our feet wet with the basics: start using components in one of your projects!

⛷️ Try them!

We have many libraries of components. The reason is that some of those components are big, and having everything in a single repository would be a mess! However, the main one is this one. You can import it in your project using npm, yarn or any other package manager of your choice:

npm i @thatopen/components

Most of our libraries are based on Three.js, so you'll also need to import it. Make sure it's the same version as the one used by our libraries!

npm i three

Finally, you also need to install some peer dependencies. These are other libraries we made and didn't include as regular dependencies to enable more flexible bundling scenarios. Again, make sure it's the same version as the one used by our libraries:

npm i @thatopen/fragments
npm i web-ifc

That's it! Now you are ready to start using components. But where to start? Here you have a nice tutorial to make your first steps and build a 3D app that looks like this in less than 5 minutes: 👇🏻

Cool, right? But of course, as you can imagine, there's a long way between this simple app and a full-fledged BIM application. If you need some guidance in your journey to discover all the components that we offer, check out the tutorial paths. If you want to know more about components, keep reading! 👇

🔌 Compatibility

You might be wondering where you can use these components. In short, anywhere that runs JavaScript! You can use them on vanilla web apps, on any library/framework (Vue, React, Angular, Svelte, etc). Many of them are also compatible with Node.js (for backend apps), React Native (for mobile apps) and Electron (for desktop apps).

What about types?

We write all our code in TypeScript and document it with TSDoc. That means that our code is natively type-safe and that you'll get the same documentation that you'll find in the API section of these docs. 📖📖

👩🏻‍🔬 Testability

🧪 Community tests

All the running 3D apps that you see in these docs are not hardcoded: they are automatically deployed from the latest version of the code of our repositories and imported here. Any change in the repositories will automatically show up here.

Each component has a minimal, self-contained tutorial app exposing all its features. That means that each person looking at a tutorial of a specific component and trying it in the 3D app is actually testing that component. Having thousands of users navigating through these docs, if anything breaks, we'll know it right away!

🧪 Unit tests

We are goint to implement the unit tests of all the code of all our libraries using AI, Jest, Typescript and human beings 🙂. It's a work in progress, but we hope to have it ready soon!

- + \ No newline at end of file diff --git a/build/components/tutorial-paths/index.html b/build/components/tutorial-paths/index.html index 012ce469..dbfcd5b3 100644 --- a/build/components/tutorial-paths/index.html +++ b/build/components/tutorial-paths/index.html @@ -4,13 +4,13 @@ 🧭 Tutorial paths | That Open Docs - +

🧭 Tutorial paths

As you can see, we have tons of tutorials in this documentations, and we will keep adding more as new features come out. If you're not looking for anything specific, it can be a little difficult to know where to start. For that reason, here are some interesting itineraries to take your first steps in the library! 🔥🔥🚀

First steps in the library

The best place to start is playing with the core components of our library. These are the basic building blocks you will need in all the apps you build on top of this! 👶🏻

Making great 3D apps

Basics are great, but that's not enough to build professional 3D apps. Let's discover some components that will bring our BIM software to the next level: 🆙

Loading and editing BIM data

We are here to make BIM apps, and the library has tons of components to make it super easy! Our library is based on fragments, an open source geometric format that can load display big amounts of BIM data super efficiently. Let's learn how to build BIM apps with it! 💪🏻

Keep in mind that this documentation assumes some basic level of web development and Three.js. If you are just starting or have never coded before in the web, don't worry! Get around our community, say hi, meet other aspiring BIM software developers and start your coding journey with us. 🚀

- + \ No newline at end of file diff --git a/build/contributing/index.html b/build/contributing/index.html index 935f5cd3..57022c3b 100644 --- a/build/contributing/index.html +++ b/build/contributing/index.html @@ -4,13 +4,13 @@ 🤝 Get involved | That Open Docs - +

🤝 Get involved

Want to help us make this project even more amazing? Great! Contributing is easy, and on this page you'll find a quick guide on how to do it. 👇🏻

There are basically 3 places where you can help:

🐞 Spotting bugs

Have you found a bug / something to improve? Create an issue in the corresponding repository (if it doesn't exist yet) so that we can start working on it! 💪🏻

If you are not sure where the issue belongs, you can just create it in the components repository and we'll redirect it to the right place!

🍻 Visiting the community

Our community is the heart of our project. It's the place where all BIM software developers meet, share their wins and learn from each other.

You can be part of it by:

  • Showing us what you built with our libraries!

  • Answering questions of other BIM software developers.

  • Sharing resources / tutorials.

  • Starting interesting debates and conversations.

👨🏻‍💻 Coding

What you'll need

The knowledge you need to help us depend on which part of the libraries you want to help us with. In general, basic knowledge of web development, TypeScript and Three.js should suffice. If you are not sure, don't hesitate to ask us!

This includes adding features and enhancing existing ones, fixing bugs or writing docs. The steps to contribute are the following:

🙏🏻 1. Ask first

We have been creating and maintaining our libraries for years for free, and there is nothing we appreciate more than people who want to help us. 💕

Meritocracy over anarchy

At That Open Company we believe in a merit-based governance model over the libraries. That means that the people who have helped the most and for the longest have the most decision-making power over them.

Therefore, if you use the libraries and miss something, or just want to be part of the project, we strongly recommend that you ask first. It can save yourself days of work doing a PR that might be rejected by the maintainers. Also, if you ask, we'll gladly help you out in everything you need to start developing. 🚀

You can ask by opening a feature issue in any of our repositories or adding a comment to any existing one. That way, you open a friendly discussion where people can participate and maintainers decide. If you are not sure where the issue belongs, you can just create it in the components repository and we'll redirect it to the right place!

🚀 2. Start coding

Once you have asked, and got a positive answer from one of the maintainers, you can start coding! To add / edit code of the library, you will need to:

  1. Create a fork of the repository.

  2. Create a branch to work on that specific issue, and link that branch to the issue.

  3. Work on your fork of the repository locally. Please follow the basic clean rules!

  4. Create a pull request. The name should follow the conventional commits convention. If you are not sure, check out the title past pull requests!

Then, someone from our team will reviewed it and, if everything is ok, merge it. That's it! Easy, right? 😋 We'll help you get started and give you anything you needs, so don't hesitate to reach out!

- + \ No newline at end of file diff --git a/build/index.html b/build/index.html index 6caa8b2a..b0b67f39 100644 --- a/build/index.html +++ b/build/index.html @@ -4,13 +4,13 @@ That Open Docs - +
- + \ No newline at end of file diff --git a/build/intro/index.html b/build/intro/index.html index 0a3c7a59..28f8448e 100644 --- a/build/intro/index.html +++ b/build/intro/index.html @@ -4,13 +4,13 @@ 👨🏻‍💻 Introduction | That Open Docs - +

👨🏻‍💻 Introduction

Welcome to That Open Docs! Have you ever wanted to create your own BIM software, but don't know where to start? Here you will find everything you need to go from zero to hero! 🏢👩‍💻

🚀 Getting Started

💪🏻 What you can do

You can use all the libraries covered in this documentation to create your own 3D BIM software in minutes. All the libraries are free and open source, so you are free to distribute and sell anything you create.

These libraries are written in JavaScript / TypeScript, so you can use them to build BIM applications for:

  • 🌍 Web: using HTML, CSS and (optionally) React, Angular, Vue, etc.
  • 📁Servers: using Node.js.
  • 💻 Desktop: using Electron.
  • 📱 Mobile: using React Native or importing them as iframes (e.g. in Flutter).

In terms of features, these libraries offer you a wide set of handy tools to create BIM software, such as:

  • 🏢 IFC import, display, navigate, edit and export.
  • 🌳 Import, display and navigate other 3D formats.
  • 🚀 3D+2D modelling and editing (work in progress!).
  • 📦 Store, process and distribute BIM models and files.
  • 🔎 3D+2D navigation tools
  • ✍ 3D+2D annotations.
  • 📏 BIM models measurement.
  • 📋 Documentation import and export.
  • 🤝 Integration with data systems (e.g. Sharepoint, PowerBI, Google Drive, etc).
  • 🌎 Integration with GIS systems (e.g. Cesium, Mapbox).
Do you need another features?

Feel free to open an issue in our repos or ask around in our community. 🚀

📋 What you'll need

The concept BIM software has two words, and you'll need some familiarity with both before you can jump into the pool and have fun. 🏊‍♂️

🏢 BIM...

You have probably seen architects and engineers in movies drawing 2D blueprints and handling them to construction companies to make things like buildings and bridges. That's the way we have built for a long time, but now there's a better way: BIM.

BIM stands for Building Information Model. In a nutshell, instead of drawing each blueprint one by one, we make a 3D model (M) with information (I) of the object to build (B). We can then generate all the 2D blueprints automatically from that 3D model. This has many advantages: errors in the design can be detected earlier, the design process is more traceable, we can make simulations, etc.

Never heard of BIM before?

Don't worry! You don't be to be an architect/building engineer to follow these docs and create BIM software, but keep in mind that this is the domain that we will constantly refer to. Feel free to get to our open community and ask around! We'll be happy to help. 🍻

🧑🏻‍💻 ...Software

Programming is a broad topic, so we can't just start from scratch. This documentation assumes some basic knowledge of web development technologies. You will need to be familiar with basic concepts like frontend, backend, server, library and debugging, as well as some experience with:

TechnologyDescription
HTMLMarkdown langage used to define the structure of any web application.
CSSStyling sheets to define the appearance of all the HTML elements within a web app.
JavaScriptProgramming language of all frontend web applications, used to define their logic. Naturally, knowing TypeScript is even better, but it's not a requirement!
NPMNode Package Manager is the most popular repository to import and publish JavaScript libraries.
BundlingTools used to pack the code in bundles. Examples are Rollup and Webpack. Alternatively, if you are familiar with a frontend library/framework like React, Angular or Vue, that's more than enough!
Three.jsThe most popular web 3D library in the world.

If you have never programmed before, this might sound overwhelming, but it's actually quite easy to learn. Anyone can learn to code, and web programming is one of the best places to start. Look around for some tutorials, get your feet wet there first, and then come back here!

How to become a BIM software developer?

There are many free resources out there to learn to code for the web, although they are not specific to BIM or the construction industry. For that reason, we have made some courses that cover all these topics, starting from scratch and oriented to BIM software developers.

🐞 Reporting bugs

We work very hard to make our libraries better every day, and you can help us! If you find any error while using it, don't hesitate to let us know and we will take care of it as soon as possible.

This is how you can report a bug:

  1. Go to the Github library that has the code you are using. If you are not sure, don't worry: just go to the components repository.

  2. Go to the issues tab.

  3. Create a new issue. Try to ellaborate as much as possible, explaining what you are trying to do, the behavior you expect and what the library is doing. Providing a minimal code example we can test is also super useful!

That's it! If you have questions, you can also get into our community, meet everyone and ask anything! We'll be super happy to see you there. 😊

📚 Navigating the docs

These docs are organized in 3 different sections. This is a lot of information and it can be hard to find just what you are looking for, so here's a short guide of where to go from here:


🧩 Components

Everything in these libraries are Components. Here, you will learn the basics and how to import, use and even create your own custom components. You will also find some useful tutorial paths that will help you progress throughout these docs!


📋 API

Here you will find all the descriptions of the classes, properties, methods and interfaces of the libraries. It's automatically generated and corresponds to the docs that you will see in your IDE when using any of the libraries.


👩🏻‍🏫 Tutorials

Step by step tutorials covering how to use different components, including a live application where you can see it in action. This is the best way to get your feet wet with the libraries. Check out the recommended tutorial paths!

- + \ No newline at end of file diff --git a/docs/Tutorials/Components/Core/IfcRelationsIndexer.mdx b/docs/Tutorials/Components/Core/IfcRelationsIndexer.mdx index 5bdae757..67dbb2d8 100644 --- a/docs/Tutorials/Components/Core/IfcRelationsIndexer.mdx +++ b/docs/Tutorials/Components/Core/IfcRelationsIndexer.mdx @@ -137,7 +137,7 @@ What do we gain with having a pre-processed relations index if we can't use it a ```js // Lets first delete the existing model relations delete indexer.relationMaps[model.uuid]; -const relationsIndexFile = await fetch("/resources/small-relations.json"); +const relationsIndexFile = await fetch("https://thatopen.github.io/engine_components/resources/small-relations.json"); const relationsIndex = indexer.getRelationsMapFromJSON( await relationsIndexFile.text(), );